|
| 1 | +package com.plugin.core; |
| 2 | + |
| 3 | +import android.annotation.TargetApi; |
| 4 | +import android.app.Activity; |
| 5 | +import android.app.Application; |
| 6 | +import android.app.Instrumentation; |
| 7 | +import android.content.Context; |
| 8 | +import android.content.ContextWrapper; |
| 9 | +import android.content.Intent; |
| 10 | +import android.content.pm.ActivityInfo; |
| 11 | +import android.content.pm.ProviderInfo; |
| 12 | +import android.os.Build; |
| 13 | +import android.os.Handler; |
| 14 | +import android.view.ContextThemeWrapper; |
| 15 | +import android.view.LayoutInflater; |
| 16 | + |
| 17 | +import com.plugin.content.PluginProviderInfo; |
| 18 | +import com.plugin.core.ui.PluginNormalFragmentActivity; |
| 19 | +import com.plugin.core.ui.stub.PluginStubActivity; |
| 20 | +import com.plugin.util.ClassLoaderUtil; |
| 21 | +import com.plugin.util.FragmentHelper; |
| 22 | +import com.plugin.util.LogUtil; |
| 23 | +import com.plugin.util.RefInvoker; |
| 24 | + |
| 25 | +import java.util.ArrayList; |
| 26 | +import java.util.Collection; |
| 27 | +import java.util.List; |
| 28 | + |
| 29 | +public class PluginInjector { |
| 30 | + |
| 31 | + private static final String android_app_ActivityThread = "android.app.ActivityThread"; |
| 32 | + private static final String android_app_ActivityThread_currentActivityThread = "currentActivityThread"; |
| 33 | + private static final String android_app_ActivityThread_mInstrumentation = "mInstrumentation"; |
| 34 | + private static final String android_app_ActivityThread_getHandler = "getHandler"; |
| 35 | + private static final String android_app_ActivityThread_installContentProviders = "installContentProviders"; |
| 36 | + |
| 37 | + private static final String android_content_ContextWrapper_mBase = "mBase"; |
| 38 | + |
| 39 | + private static final String android_os_Handler_mCallback = "mCallback"; |
| 40 | + |
| 41 | + private static final String android_app_Activity_mInstrumentation = "mInstrumentation"; |
| 42 | + private static final String android_app_Activity_mActivityInfo = "mActivityInfo"; |
| 43 | + |
| 44 | + private static final String android_content_ContextThemeWrapper_attachBaseContext = "attachBaseContext"; |
| 45 | + private static final String android_content_ContextThemeWrapper_mResources = "mResources"; |
| 46 | + private static final String android_content_ContextThemeWrapper_mTheme = "mTheme"; |
| 47 | + |
| 48 | + |
| 49 | + /** |
| 50 | + * 替换宿主程序Application对象的mBase是为了修改它的几个StartActivity、 |
| 51 | + * StartService和SendBroadcast方法 |
| 52 | + */ |
| 53 | + static void injectBaseContext(Application application) { |
| 54 | + LogUtil.d("替换宿主程序Application对象的mBase"); |
| 55 | + Context base = (Context)RefInvoker.getFieldObject(application, ContextWrapper.class.getName(), |
| 56 | + android_content_ContextWrapper_mBase); |
| 57 | + Context newBase = new PluginBaseContextWrapper(base); |
| 58 | + RefInvoker.setFieldObject(application, ContextWrapper.class.getName(), |
| 59 | + android_content_ContextWrapper_mBase, newBase); |
| 60 | + } |
| 61 | + |
| 62 | + static Object getActivityThread() { |
| 63 | + // 从ThreadLocal中取出来的 |
| 64 | + LogUtil.d("从宿主程序中取出ActivityThread对象备用"); |
| 65 | + Object activityThread = RefInvoker.invokeStaticMethod(android_app_ActivityThread, |
| 66 | + android_app_ActivityThread_currentActivityThread, |
| 67 | + (Class[]) null, (Object[]) null); |
| 68 | + return activityThread; |
| 69 | + } |
| 70 | + |
| 71 | + /** |
| 72 | + * 注入Instrumentation主要是为了支持Activity |
| 73 | + */ |
| 74 | + static void injectInstrumentation(Object activityThread) { |
| 75 | + // 给Instrumentation添加一层代理,用来实现隐藏api的调用 |
| 76 | + LogUtil.d("替换宿主程序Intstrumentation"); |
| 77 | + Instrumentation originalInstrumentation = (Instrumentation) RefInvoker.getFieldObject(activityThread, |
| 78 | + android_app_ActivityThread, android_app_ActivityThread_mInstrumentation); |
| 79 | + RefInvoker.setFieldObject(activityThread, android_app_ActivityThread, |
| 80 | + android_app_ActivityThread_mInstrumentation, |
| 81 | + new PluginInstrumentionWrapper(originalInstrumentation)); |
| 82 | + } |
| 83 | + |
| 84 | + static void injectHandlerCallback(Object activityThread) { |
| 85 | + LogUtil.d("向插入宿主程序消息循环插入回调器"); |
| 86 | + Handler handler = (Handler) RefInvoker.invokeMethod(activityThread, |
| 87 | + android_app_ActivityThread, android_app_ActivityThread_getHandler, |
| 88 | + (Class[]) null, (Object[]) null); |
| 89 | + RefInvoker.setFieldObject(handler, Handler.class.getName(), android_os_Handler_mCallback, |
| 90 | + new PluginAppTrace(handler)); |
| 91 | + } |
| 92 | + |
| 93 | + static void installContentProviders(Context context, Collection<PluginProviderInfo> pluginProviderInfos) { |
| 94 | + LogUtil.d("安装插件ContentProvider", pluginProviderInfos.size()); |
| 95 | + Object activityThread = PluginInjector.getActivityThread(); |
| 96 | + if (activityThread != null) { |
| 97 | + ClassLoaderUtil.hackClassLoaderIfNeeded(); |
| 98 | + List<ProviderInfo> providers = new ArrayList<ProviderInfo>(); |
| 99 | + for (PluginProviderInfo pluginProviderInfo : pluginProviderInfos) { |
| 100 | + ProviderInfo p = new ProviderInfo(); |
| 101 | + p.name = pluginProviderInfo.getName(); |
| 102 | + p.authority = pluginProviderInfo.getAuthority(); |
| 103 | + p.applicationInfo = context.getApplicationInfo(); |
| 104 | + p.exported = pluginProviderInfo.isExported(); |
| 105 | + p.packageName = context.getApplicationInfo().packageName; |
| 106 | + providers.add(p); |
| 107 | + } |
| 108 | + RefInvoker.invokeMethod(activityThread, |
| 109 | + android_app_ActivityThread, android_app_ActivityThread_installContentProviders, |
| 110 | + new Class[]{Context.class, List.class}, new Object[]{context, providers}); |
| 111 | + } |
| 112 | + } |
| 113 | + |
| 114 | + static void injectInstrumetionFor360Safe(Activity activity, Instrumentation pluginInstrumentation) { |
| 115 | + // 检查mInstrumention是否已经替换成功。 |
| 116 | + // 之所以要检查,是因为如果手机上安装了360手机卫士等app,它们可能会劫持用户app的ActivityThread对象, |
| 117 | + // 导致在PluginApplication的onCreate方法里面替换mInstrumention可能会失败 |
| 118 | + // 所以这里再做一次检查 |
| 119 | + Instrumentation instrumention = (Instrumentation) RefInvoker.getFieldObject(activity, Activity.class.getName(), |
| 120 | + android_app_Activity_mInstrumentation); |
| 121 | + if (!(instrumention instanceof PluginInstrumentionWrapper)) { |
| 122 | + // 说明被360还原了,这里再次尝试替换 |
| 123 | + RefInvoker.setFieldObject(activity, Activity.class.getName(), android_app_Activity_mInstrumentation, pluginInstrumentation); |
| 124 | + } |
| 125 | + } |
| 126 | + |
| 127 | + @TargetApi(Build.VERSION_CODES.HONEYCOMB) |
| 128 | + static void injectActivityContext(Activity activity) { |
| 129 | + Intent intent = activity.getIntent(); |
| 130 | + // 如果是打开插件中的activity |
| 131 | + if (intent.getComponent() != null |
| 132 | + && (intent.getComponent().getClassName().equals(PluginStubActivity.class.getName()) || intent |
| 133 | + .getComponent().getClassName().equals(PluginNormalFragmentActivity.class.getName()))) { |
| 134 | + // 为了不需要重写插件Activity的attachBaseContext方法为: |
| 135 | + // @Override |
| 136 | + // protected void attachBaseContext(Context newBase) { |
| 137 | + // super.attachBaseContext(PluginLoader.getDefaultPluginContext(PluginNotInManifestActivity.class)); |
| 138 | + // } |
| 139 | + // 我们在activityoncreate之前去完成attachBaseContext的事情 |
| 140 | + |
| 141 | + |
| 142 | + Context pluginContext = null; |
| 143 | + if (activity.getClass().getName().equals(PluginNormalFragmentActivity.class.getName())) { |
| 144 | + // 为了能够在宿主中的Activiy里面展示来自插件的普通Fragment, |
| 145 | + // 我们将宿主程序中用来展示插件普通Fragment的Activity的Context也替换掉 |
| 146 | + String classId = activity.getIntent().getStringExtra(FragmentHelper.FRAGMENT_ID_IN_PLUGIN); |
| 147 | + @SuppressWarnings("rawtypes") |
| 148 | + Class clazz = PluginLoader.loadPluginFragmentClassById(classId); |
| 149 | + pluginContext = PluginLoader.getNewPluginContext(clazz); |
| 150 | + } else { |
| 151 | + pluginContext = PluginLoader.getNewPluginContext(activity.getClass()); |
| 152 | + } |
| 153 | + |
| 154 | + // 重设BaseContext |
| 155 | + RefInvoker.setFieldObject(activity, ContextWrapper.class.getName(), android_content_ContextWrapper_mBase, null); |
| 156 | + RefInvoker.invokeMethod(activity, ContextThemeWrapper.class.getName(), android_content_ContextThemeWrapper_attachBaseContext, |
| 157 | + new Class[]{Context.class }, new Object[] { pluginContext }); |
| 158 | + |
| 159 | + // 重设LayoutInflater |
| 160 | + LogUtil.d(activity.getWindow().getClass().getName()); |
| 161 | + RefInvoker.setFieldObject(activity.getWindow(), activity.getWindow().getClass().getName(), |
| 162 | + "mLayoutInflater", LayoutInflater.from(pluginContext)); |
| 163 | + |
| 164 | + // 如果api>=11,还要重设factory2 |
| 165 | + if (Build.VERSION.SDK_INT >= 11) { |
| 166 | + RefInvoker.invokeMethod(activity.getWindow().getLayoutInflater(), LayoutInflater.class.getName(), |
| 167 | + "setPrivateFactory", new Class[] { LayoutInflater.Factory2.class }, new Object[] { activity }); |
| 168 | + } |
| 169 | + |
| 170 | + // 由于在attach的时候Resource已经被初始化了,所以还需要重置Resource |
| 171 | + RefInvoker.setFieldObject(activity, ContextThemeWrapper.class.getName(), android_content_ContextThemeWrapper_mResources, null); |
| 172 | + |
| 173 | + // 重设theme |
| 174 | + ActivityInfo activityInfo = (ActivityInfo) RefInvoker.getFieldObject(activity, Activity.class.getName(), |
| 175 | + android_app_Activity_mActivityInfo); |
| 176 | + int theme = activityInfo.getThemeResource(); |
| 177 | + if (theme != 0) { |
| 178 | + RefInvoker.setFieldObject(activity, ContextThemeWrapper.class.getName(), android_content_ContextThemeWrapper_mTheme, null); |
| 179 | + activity.setTheme(theme); |
| 180 | + } |
| 181 | + } else { |
| 182 | + // 如果是打开宿主程序的activity,注入一个无害的Context,用来在宿主程序中startService和sendBroadcast时检查打开的对象是否是插件中的对象 |
| 183 | + // 插入Context |
| 184 | + Context mainContext = new PluginBaseContextWrapper(activity.getBaseContext()); |
| 185 | + RefInvoker.setFieldObject(activity, ContextWrapper.class.getName(), android_content_ContextWrapper_mBase, null); |
| 186 | + RefInvoker.invokeMethod(activity, ContextThemeWrapper.class.getName(), android_content_ContextThemeWrapper_attachBaseContext, |
| 187 | + new Class[]{Context.class }, new Object[] { mainContext }); |
| 188 | + } |
| 189 | + } |
| 190 | + |
| 191 | + |
| 192 | +} |
0 commit comments