VirtualApk跟其他的插件化框架有何区别? 看官方介绍
支持几乎所有的Android特性,支持加载加载四大组件。
兼容性好
兼容市面上几乎所有的Android手机,这一点已经在滴滴出行客户端中得到验证;
资源方面适配小米、Vivo、Nubia等,对未知机型采用自适应适配方案;
极少的Binder Hook,目前仅仅hook了两个Binder:AMS
和IContentProvider
,hook过程做了充分的兼容性适配;
插件运行逻辑和宿主隔离,确保框架的任何问题都不会影响宿主的正常运行。
入侵性极低
插件开发等同于原生开发,四大组件无需继承特定的基类;
精简的插件包,插件可以依赖宿主中的代码和资源,也可以不依赖;
插件的构建过程简单,通过Gradle插件来完成插件的构建,整个过程对开发者透明。
程序入口在哪? 在Application的attachBaseContext()中。
PluginManager.getInstance(context).init()
PluginManager.getInstance(context)中创建了PluginManager对象
PluginManager构造函数里做了什么? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 protected PluginManager (Context context) { if (context instanceof Application) { this .mApplication = (Application) context; this .mContext = mApplication.getBaseContext(); } else { final Context app = context.getApplicationContext(); if (app == null ) { this .mContext = context; this .mApplication = ActivityThread.currentApplication(); } else { this .mApplication = (Application) app; this .mContext = mApplication.getBaseContext(); } } mComponentsHandler = createComponentsHandler(); hookCurrentProcess(); }
创建了ComponentsHandler
hookCurrentProcess()
hookCurrentProcess()做了什么? 1 2 3 hookInstrumentationAndHandler(); hookSystemServices(); hookDataBindingUtil();
hookInstrumentationAndHandler()做了什么? hook Instrumentation
通过ActivityThread.currentActivityThread()获取当前App进程的ActivityThread对象。
通过ActivityThread获取Instrumentation对象。
创建一个VAInstrumentation对象,把原始的Instrumentation对象传进去。
反射替换ActivityThread的mInstrumentation属性为创建的VAInstrumentation对象。
hook Handler
反射获取ActivityThread的getHandler()。
反射替换Handler的mCallback为创建的VAInstrumentation对象。
VAInstrumentation实现了Handler.Callback。
VAInstrumentation 为什么要替换ActivityThread的Instrumentation为自定义的VAInstrumentation? VAInstrumentation做了以下几件事:
覆写了execStartActivity()方法,并在执行原逻辑之前,调用了injectIntent(intent)来处理execStartActivity()传递过来的intent参数。
覆写了newActivity()。
覆写了callActivityOnCreate(),并在执行原逻辑之前,调用了injectActivity(activity)来修改callActivityOnCreate()传递过来的activity。
Instrumentation的execStartActivity()是做什么的?什么时候被调用? Instrumentation.execStartActivity()内部会调用ActivityManagerService.startActivity()来启动Activity
调用链:
Activity.startActivity() -> Activity.startActivityForResult() -> Instrumentation.execStartActivity()
startActivity()为什么要交给ActivityManagerService来做? 如果启动是另外一个应用程序的Activity,需要创建新的进程,创建新的进程只能由Zygote来创建,AMS负责把创建进程的工作通过Socket转发给Zygote,AMS相当于是个门面,接管所有四大组件的操作,向应用程序屏蔽内部细节
为什么ActivtyManagerService跟Zygote通信是socket,而不是Binder? 因为Zygote中会fork进程,如果是Binder通信,Zygote代码会运行在Zygote进程的Binder线程池中的某个线程中,fork()一个进程容易产生死锁,并且fork()的子进程只有一个线程,其他线程会消失。
为什么多线程下fork()容易产生死锁? fork()的行为是复制整个用户空间的数据(copy on write策略,复制会很快),并且fork()进程只会把父进程中当前线程拷贝到子进程。
然后锁的实现一般也是存储在用户空间,如果父进程中某个线程lock了锁,在另外一个线程fork()子进程,会把锁也带到子进程中,但是持有锁的线程蒸发了,子进程中的fork()过来的线程的锁永远不会释放,就发生了死锁。
Binder线程池什么时候创建? 无论是system_server进程,还是app进程,都是在进程fork完成后,便会在新进程中执行onZygoteInit()的过程中,启动binder线程池。
injectIntent()做了什么? 1 2 3 4 5 6 7 8 9 protected void injectIntent (Intent intent) { mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent); if (intent.getComponent() != null ) { Log.i(TAG, String.format("execStartActivity[%s : %s]" , intent.getComponent().getPackageName(), intent.getComponent().getClassName())); this .mPluginManager.getComponentsHandler().markIntentIfNeeded(intent); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public Intent transformIntentToExplicitAsNeeded (Intent intent) { ComponentName component = intent.getComponent(); if (component == null || component.getPackageName().equals(mContext.getPackageName())) { ResolveInfo info = mPluginManager.resolveActivity(intent); if (info != null && info.activityInfo != null ) { component = new ComponentName (info.activityInfo.packageName, info.activityInfo.name); intent.setComponent(component); } } return intent; } public ResolveInfo resolveActivity (Intent intent) { return this .resolveActivity(intent, 0 ); } public ResolveInfo resolveActivity (Intent intent, int flags) { for (LoadedPlugin plugin : this .mPlugins.values()) { ResolveInfo resolveInfo = plugin.resolveActivity(intent, flags); if (null != resolveInfo) { return resolveInfo; } } return null ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public void markIntentIfNeeded (Intent intent) { if (intent.getComponent() == null ) { return ; } String targetPackageName = intent.getComponent().getPackageName(); String targetClassName = intent.getComponent().getClassName(); if (!targetPackageName.equals(mContext.getPackageName()) && mPluginManager.getLoadedPlugin(targetPackageName) != null ) { intent.putExtra(Constants.KEY_IS_PLUGIN, true ); intent.putExtra(Constants.KEY_TARGET_PACKAGE, targetPackageName); intent.putExtra(Constants.KEY_TARGET_ACTIVITY, targetClassName); dispatchStubActivity(intent); } } private void dispatchStubActivity (Intent intent) { ComponentName component = intent.getComponent(); String targetClassName = intent.getComponent().getClassName(); LoadedPlugin loadedPlugin = mPluginManager.getLoadedPlugin(intent); ActivityInfo info = loadedPlugin.getActivityInfo(component); if (info == null ) { throw new RuntimeException ("can not find " + component); } int launchMode = info.launchMode; Resources.Theme themeObj = loadedPlugin.getResources().newTheme(); themeObj.applyStyle(info.theme, true ); String stubActivity = mStubActivityInfo.getStubActivity(targetClassName, launchMode, themeObj); Log.i(TAG, String.format("dispatchStubActivity,[%s -> %s]" , targetClassName, stubActivity)); intent.setClassName(mContext, stubActivity); }
如果startActivity()中的Intent中没有Component说明是隐式的Activity启动,去插件中寻找有没有匹配的Activity,有的话创建一个CompoenentName设置给Intent。这个设置有什么用呢?
接下来在markIntentIfNeed()中用到了,如果要启动的Activity不是宿主的并且是插件中的,给Intent增加extra参数,设置Activity的类名和目标包名。
这里设置有什么用呢? 在VAInstrumentation的newActivity()和injectActivity()中会用到。
如何绕过AMS对Activity是否在AndroidManifest.xml注册过的校验? 然后会再调用dispatchStubActivity()把intent中指定要启动的Activity类名替换为预先在AndroidManifest里占坑的StubActivity的类名,会根据不同的launchMode来选用用哪个占坑的。这样会让AMS以为要启动的是一个正常的Activity,可以绕过安全检查,不会出现ActivityNotFound。
newActivity()常规情况是什么时候被调用的? Instrumentation.newActivity()
的调用链:
ApplicationThread.scheduleTransaction() -> ActivityThread.scheduleTransaction() -> ActivityThread.H.handleMessage(): EXECUTE_TRANSACTION -> TransactionExecutor.execute() -> TransactionExecutor.executeCallbacks() -> LaunchActivityItem.execute() -> ActivityThread.handleLaunchActivity() -> ActivityThread.performLaunchActivity() -> Instrumentation.newActivity()
VAInstrumentation.newActivity()做了什么? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 @Override public Activity newActivity (ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException { try { cl.loadClass(className); Log.i(TAG, String.format("newActivity[%s]" , className)); } catch (ClassNotFoundException e) { ComponentName component = PluginUtil.getComponent(intent); if (component == null ) { return newActivity(mBase.newActivity(cl, className, intent)); } String targetClassName = component.getClassName(); Log.i(TAG, String.format("newActivity[%s : %s/%s]" , className, component.getPackageName(), targetClassName)); LoadedPlugin plugin = this .mPluginManager.getLoadedPlugin(component); if (plugin == null ) { boolean debuggable = false ; try { Context context = this .mPluginManager.getHostContext(); debuggable = (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 ; } catch (Throwable ex) { } if (debuggable) { throw new ActivityNotFoundException ("error intent: " + intent.toURI()); } Log.i(TAG, "Not found. starting the stub activity: " + StubActivity.class); return newActivity(mBase.newActivity(cl, StubActivity.class.getName(), intent)); } Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent); activity.setIntent(intent); Reflector.QuietReflector.with(activity).field("mResources" ).set(plugin.getResources()); return newActivity(activity); } return newActivity(mBase.newActivity(cl, className, intent)); }
Instrumentation中的newActivity()原逻辑就是用ClassLoader根据指定的类名创建Activity实例并返回。
但如果是插件中的Activity,宿主的ClassLoader就会找不到类,因为Android中的ClassLoader是绑定dex文件位置的,类都存储在dex文件中,那应该怎么办?
用插件的ClassLoader去加载类,插件的ClassLoader中的dex。这就是catch到ClassNotFoundException异常后的逻辑了。
callActivityOnCreate()什么时候被调用?是做什么的? 调用链:
ApplicationThread.scheduleTransaction() -> ActivityThread.scheduleTransaction() -> ActivityThread.H.handleMessage():EXECUTE_TRANSACTION -> TransactionExecutor.execute() -> TransactionExecutor.executeCallbacks() -> LaunchActivityItem.execute() -> ActivityThread.handleLaunchActivity() -> ActivityThread.performLaunchActivity() -> Instrumentation.callActivityOnCreate()
1 2 3 4 5 6 public void callActivityOnCreate (Activity activity, Bundle icicle) { prePerformCreate(activity); activity.performCreate(icicle); postPerformCreate(activity); }
Instrumentation中的原逻辑会调用Activity的onCreate()。
VAInstrumentation.callActivityOnCreate()做了什么? 1 2 3 4 5 @Override public void callActivityOnCreate (Activity activity, Bundle icicle) { injectActivity(activity); mBase.callActivityOnCreate(activity, icicle); }
调用了一下injectActivity()。
injectActivity()做了什么? 主要是将Activity中的Resource,Context等对象替换成了插件的相应对象,保证插件Activity在调用涉及到Context的方法时能够正确运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 protected void injectActivity (Activity activity) { final Intent intent = activity.getIntent(); if (PluginUtil.isIntentFromPlugin(intent)) { Context base = activity.getBaseContext(); try { LoadedPlugin plugin = this .mPluginManager.getLoadedPlugin(intent); Reflector.with(base).field("mResources" ).set(plugin.getResources()); Reflector reflector = Reflector.with(activity); reflector.field("mBase" ).set(plugin.createPluginContext(activity.getBaseContext())); reflector.field("mApplication" ).set(plugin.getApplication()); ActivityInfo activityInfo = plugin.getActivityInfo(PluginUtil.getComponent(intent)); if (activityInfo.screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) { activity.setRequestedOrientation(activityInfo.screenOrientation); } ComponentName component = PluginUtil.getComponent(intent); Intent wrapperIntent = new Intent (intent); wrapperIntent.setClassName(component.getPackageName(), component.getClassName()); activity.setIntent(wrapperIntent); } catch (Exception e) { Log.w(TAG, e); } } }
之后Activity的onResume、onStop等生命周期怎么处理? 所有和Activity相关的生命周期函数,系统都会自动调用插件中的Activity的生命周期函数。
原因在于AMS在处理Activity时,通过一个token表示具体Activity对象,而这个token正是和启动Activity时创建的对象对应的,而这个Activity被我们替换成了插件中的Activity,所以之后AMS的所有调用都会传给插件中的Activity。
为什么要替换ActivityThread的Handler的Callback为自定义的VAInstrumentation? 获取LAUNCH_ACTIVITY的msg,获取到ActivityClientRecord,可以:
给Intent设置插件的ClassLoader。
反射获取activityInfo,替换Activity主题。
hookSystemServices()做了什么? hook了ActivtyManager,对其进行动态代理拦截。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 protected void hookSystemServices () { try { Singleton<IActivityManager> defaultSingleton; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { defaultSingleton = Reflector.on(ActivityManager.class).field("IActivityManagerSingleton" ).get(); } else { defaultSingleton = Reflector.on(ActivityManagerNative.class).field("gDefault" ).get(); } IActivityManager origin = defaultSingleton.get(); IActivityManager activityManagerProxy = (IActivityManager) Proxy.newProxyInstance(mContext.getClassLoader(), new Class [] { IActivityManager.class }, createActivityManagerProxy(origin)); Reflector.with(defaultSingleton).field("mInstance" ).set(activityManagerProxy); if (defaultSingleton.get() == activityManagerProxy) { this .mActivityManager = activityManagerProxy; Log.d(TAG, "hookSystemServices succeed : " + mActivityManager); } } catch (Exception e) { Log.w(TAG, e); } }
动态代理ActivtyManager干什么? 看动态代理的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 package com.didi.virtualapk.delegate;import android.app.ActivityManagerNative;import android.app.IActivityManager;import android.app.IApplicationThread;import android.app.Service;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.pm.ResolveInfo;import android.content.pm.ServiceInfo;import android.os.Bundle;import android.os.DeadObjectException;import android.os.IBinder;import android.os.RemoteException;import android.os.ServiceManager;import android.util.Log;import com.didi.virtualapk.PluginManager;import com.didi.virtualapk.internal.Constants;import com.didi.virtualapk.internal.utils.PluginUtil;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class ActivityManagerProxy implements InvocationHandler { private static final String TAG = Constants.TAG_PREFIX + "IActivityManagerProxy" ; public static final int INTENT_SENDER_BROADCAST = 1 ; public static final int INTENT_SENDER_ACTIVITY = 2 ; public static final int INTENT_SENDER_ACTIVITY_RESULT = 3 ; public static final int INTENT_SENDER_SERVICE = 4 ; private PluginManager mPluginManager; private IActivityManager mActivityManager; public ActivityManagerProxy (PluginManager pluginManager, IActivityManager activityManager) { this .mPluginManager = pluginManager; this .mActivityManager = activityManager; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { if ("startService" .equals(method.getName())) { try { return startService(proxy, method, args); } catch (Throwable e) { Log.e(TAG, "Start service error" , e); } } else if ("stopService" .equals(method.getName())) { try { return stopService(proxy, method, args); } catch (Throwable e) { Log.e(TAG, "Stop Service error" , e); } } else if ("stopServiceToken" .equals(method.getName())) { try { return stopServiceToken(proxy, method, args); } catch (Throwable e) { Log.e(TAG, "Stop service token error" , e); } } else if ("bindService" .equals(method.getName())) { try { return bindService(proxy, method, args); } catch (Throwable e) { Log.w(TAG, e); } } else if ("unbindService" .equals(method.getName())) { try { return unbindService(proxy, method, args); } catch (Throwable e) { Log.w(TAG, e); } } else if ("getIntentSender" .equals(method.getName())) { try { getIntentSender(method, args); } catch (Exception e) { Log.w(TAG, e); } } else if ("overridePendingTransition" .equals(method.getName())){ try { overridePendingTransition(method, args); } catch (Exception e){ Log.w(TAG, e); } } try { return method.invoke(this .mActivityManager, args); } catch (Throwable th) { Throwable c = th.getCause(); if (c != null && c instanceof DeadObjectException) { IBinder ams = ServiceManager.getService(Context.ACTIVITY_SERVICE); if (ams != null ) { IActivityManager am = ActivityManagerNative.asInterface(ams); mActivityManager = am; } } Throwable cause = th; do { if (cause instanceof RemoteException) { throw cause; } } while ((cause = cause.getCause()) != null ); throw c != null ? c : th; } } protected Object startService (Object proxy, Method method, Object[] args) throws Throwable { IApplicationThread appThread = (IApplicationThread) args[0 ]; Intent target = (Intent) args[1 ]; ResolveInfo resolveInfo = this .mPluginManager.resolveService(target, 0 ); if (null == resolveInfo || null == resolveInfo.serviceInfo) { return method.invoke(this .mActivityManager, args); } return startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null , RemoteService.EXTRA_COMMAND_START_SERVICE); } protected Object stopService (Object proxy, Method method, Object[] args) throws Throwable { Intent target = (Intent) args[1 ]; ResolveInfo resolveInfo = this .mPluginManager.resolveService(target, 0 ); if (null == resolveInfo || null == resolveInfo.serviceInfo) { return method.invoke(this .mActivityManager, args); } startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null , RemoteService.EXTRA_COMMAND_STOP_SERVICE); return 1 ; } protected Object stopServiceToken (Object proxy, Method method, Object[] args) throws Throwable { ComponentName component = (ComponentName) args[0 ]; Intent target = new Intent ().setComponent(component); ResolveInfo resolveInfo = this .mPluginManager.resolveService(target, 0 ); if (null == resolveInfo || null == resolveInfo.serviceInfo) { return method.invoke(this .mActivityManager, args); } startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null , RemoteService.EXTRA_COMMAND_STOP_SERVICE); return true ; } protected Object bindService (Object proxy, Method method, Object[] args) throws Throwable { Intent target = (Intent) args[2 ]; ResolveInfo resolveInfo = this .mPluginManager.resolveService(target, 0 ); if (null == resolveInfo || null == resolveInfo.serviceInfo) { return method.invoke(this .mActivityManager, args); } Bundle bundle = new Bundle (); PluginUtil.putBinder(bundle, "sc" , (IBinder) args[4 ]); startDelegateServiceForTarget(target, resolveInfo.serviceInfo, bundle, RemoteService.EXTRA_COMMAND_BIND_SERVICE); mPluginManager.getComponentsHandler().remberIServiceConnection((IBinder) args[4 ], target); return 1 ; } protected Object unbindService (Object proxy, Method method, Object[] args) throws Throwable { IBinder iServiceConnection = (IBinder)args[0 ]; Intent target = mPluginManager.getComponentsHandler().forgetIServiceConnection(iServiceConnection); if (target == null ) { return method.invoke(this .mActivityManager, args); } ResolveInfo resolveInfo = this .mPluginManager.resolveService(target, 0 ); startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null , RemoteService.EXTRA_COMMAND_UNBIND_SERVICE); return true ; } protected ComponentName startDelegateServiceForTarget (Intent target, ServiceInfo serviceInfo, Bundle extras, int command) { Intent wrapperIntent = wrapperTargetIntent(target, serviceInfo, extras, command); return mPluginManager.getHostContext().startService(wrapperIntent); } protected Intent wrapperTargetIntent (Intent target, ServiceInfo serviceInfo, Bundle extras, int command) { target.setComponent(new ComponentName (serviceInfo.packageName, serviceInfo.name)); String pluginLocation = mPluginManager.getLoadedPlugin(target.getComponent()).getLocation(); boolean local = PluginUtil.isLocalService(serviceInfo); Class<? extends Service > delegate = local ? LocalService.class : RemoteService.class; Intent intent = new Intent (); intent.setClass(mPluginManager.getHostContext(), delegate); intent.putExtra(RemoteService.EXTRA_TARGET, target); intent.putExtra(RemoteService.EXTRA_COMMAND, command); intent.putExtra(RemoteService.EXTRA_PLUGIN_LOCATION, pluginLocation); if (extras != null ) { intent.putExtras(extras); } return intent; } protected void getIntentSender (Method method, Object[] args) { String hostPackageName = mPluginManager.getHostContext().getPackageName(); args[1 ] = hostPackageName; Intent target = ((Intent[]) args[5 ])[0 ]; int intentSenderType = (int )args[0 ]; if (intentSenderType == INTENT_SENDER_ACTIVITY) { mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(target); mPluginManager.getComponentsHandler().markIntentIfNeeded(target); } else if (intentSenderType == INTENT_SENDER_SERVICE) { ResolveInfo resolveInfo = this .mPluginManager.resolveService(target, 0 ); if (resolveInfo != null && resolveInfo.serviceInfo != null ) { Intent wrapperIntent = wrapperTargetIntent(target, resolveInfo.serviceInfo, null , RemoteService.EXTRA_COMMAND_START_SERVICE); ((Intent[]) args[5 ])[0 ] = wrapperIntent; } } else if (intentSenderType == INTENT_SENDER_BROADCAST) { } } protected void overridePendingTransition (Method method, Object[] args) { String hostPackageName = mPluginManager.getHostContext().getPackageName(); args[1 ] = hostPackageName; } }
主要是拦截Service的启动。
以startService为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 protected Object startService (Object proxy, Method method, Object[] args) throws Throwable { IApplicationThread appThread = (IApplicationThread) args[0 ]; Intent target = (Intent) args[1 ]; ResolveInfo resolveInfo = this .mPluginManager.resolveService(target, 0 ); if (null == resolveInfo || null == resolveInfo.serviceInfo) { return method.invoke(this .mActivityManager, args); } return startDelegateServiceForTarget(target, resolveInfo.serviceInfo, null , RemoteService.EXTRA_COMMAND_START_SERVICE); } protected ComponentName startDelegateServiceForTarget (Intent target, ServiceInfo serviceInfo, Bundle extras, int command) { Intent wrapperIntent = wrapperTargetIntent(target, serviceInfo, extras, command); return mPluginManager.getHostContext().startService(wrapperIntent); } protected Intent wrapperTargetIntent (Intent target, ServiceInfo serviceInfo, Bundle extras, int command) { target.setComponent(new ComponentName (serviceInfo.packageName, serviceInfo.name)); String pluginLocation = mPluginManager.getLoadedPlugin(target.getComponent()).getLocation(); boolean local = PluginUtil.isLocalService(serviceInfo); Class<? extends Service > delegate = local ? LocalService.class : RemoteService.class; Intent intent = new Intent (); intent.setClass(mPluginManager.getHostContext(), delegate); intent.putExtra(RemoteService.EXTRA_TARGET, target); intent.putExtra(RemoteService.EXTRA_COMMAND, command); intent.putExtra(RemoteService.EXTRA_PLUGIN_LOCATION, pluginLocation); if (extras != null ) { intent.putExtras(extras); } return intent; }
如果发现是宿主的Service类,直接按原来的方式启动。
如果发现是插件的Service类,启动在AndroidManifest.xml里预埋的LocalService或RemoteService,把插件的Service类名等信息添加到Intent中。
LocalService里做了什么? 在LocalService或RemoteService中,会解析出插件的Service类名,通过反射创建实例,然后调用生命周期方法,实现插件Service的生命周期的响应。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 public class LocalService extends Service { @Override public void onCreate () { super .onCreate(); mPluginManager = PluginManager.getInstance(this ); } @Override public int onStartCommand (Intent intent, int flags, int startId) { if (null == intent || !intent.hasExtra(EXTRA_TARGET) || !intent.hasExtra(EXTRA_COMMAND)) { return START_STICKY; } Intent target = intent.getParcelableExtra(EXTRA_TARGET); int command = intent.getIntExtra(EXTRA_COMMAND, 0 ); if (null == target || command <= 0 ) { return START_STICKY; } ComponentName component = target.getComponent(); LoadedPlugin plugin = mPluginManager.getLoadedPlugin(component); if (plugin == null ) { Log.w(TAG, "Error target: " + target.toURI()); return START_STICKY; } target.setExtrasClassLoader(plugin.getClassLoader()); switch (command) { case EXTRA_COMMAND_START_SERVICE: { ActivityThread mainThread = ActivityThread.currentActivityThread(); IApplicationThread appThread = mainThread.getApplicationThread(); Service service; if (this .mPluginManager.getComponentsHandler().isServiceAvailable(component)) { service = this .mPluginManager.getComponentsHandler().getService(component); } else { try { service = (Service) plugin.getClassLoader().loadClass(component.getClassName()).newInstance(); Application app = plugin.getApplication(); IBinder token = appThread.asBinder(); Method attach = service.getClass().getMethod("attach" , Context.class, ActivityThread.class, String.class, IBinder.class, Application.class, Object.class); IActivityManager am = mPluginManager.getActivityManager(); attach.invoke(service, plugin.getPluginContext(), mainThread, component.getClassName(), token, app, am); service.onCreate(); this .mPluginManager.getComponentsHandler().rememberService(component, service); } catch (Throwable t) { return START_STICKY; } } service.onStartCommand(target, 0 , this .mPluginManager.getComponentsHandler().getServiceCounter(service).getAndIncrement()); break ; } } } }
hookDataBindingUtil()做了什么? databinding相关,不是主要逻辑,暂不关心。
插件里的BroadcastReceiver是怎么让其生效的? 在LoadedPlugin
的构造函数里:
读取插件AndroidManifest里所有的广播接收器。
通过插件的ClassLoader加载 广播接收器类,再通过反射创建实例。
用宿主Context动态注册广播接收器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public LoadedPlugin (PluginManager pluginManager, Context context, File apk) throws Exception { this .mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK); Map<ComponentName, ActivityInfo> receivers = new HashMap <ComponentName, ActivityInfo>(); for (PackageParser.Activity receiver : this .mPackage.receivers) { receivers.put(receiver.getComponentName(), receiver.info); BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance()); for (PackageParser.ActivityIntentInfo aii : receiver.intents) { this .mHostContext.registerReceiver(br, aii); } } }
插件里的ContentProvider如何运行? 首先得看常规访问ContentProvider是什么手段。然后在访问ContentProvider的路径上看能否找到切入点,加载插件的ContentProvider。
常规访问ContentProvider是通过Context对象调用getContentResolver()获取ContentResolver对象来间接访问ContentProvider的。
以query()为例,ContentResolver.query()调用流程:
ContentResolver.query()函数主要做了两件事:
获取ContentProvider的Binder代理。
远程调用ContentProvider的query方法。1 2 3 4 5 6 7 8 9 10 11 public final Cursor query (final @RequiresPermission .Read @NonNull Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) { IContentProvider unstableProvider = acquireUnstableProvider(uri); unstableProvider.query(mPackageName, uri, projection, queryArgs, remoteCancellationSignal); }
我们只能在ContentResolver的acquireUnstableProvider上下功夫,因为这一步还是处于App内部的逻辑,到别的进程就没法控制了。
如何拦截处理ContentResolver.acquireUnstableProvider()? 跟Service一个套路,如果发现启动的是插件里的ContentProvider:
就启动一个AndroidManifest里预埋的ContentProvider。
把插件的ContentProvider的类信息、查询的信息,都放在Intent里,转发给预埋的ContentProvider。
在预埋的ContentProvider手动创建插件里的ContentProvider对象,并调用其方法,传递正确的参数,获取返回结果再向上返回。
VirtualApk如何覆写ContentResolver.acquireUnstableProvider()? VirtualApk定义了一个PluginContentResolver类继承ContentResolver,覆写了acquireUnstableProvider方法。
1 2 3 4 5 6 protected IContentProvider acquireUnstableProvider (Context context, String auth) { if (mPluginManager.resolveContentProvider(auth, 0 ) != null ) { return mPluginManager.getIContentProvider(); } return super .acquireUnstableProvider(context, auth); }
PluginContentResolver在什么时候被创建使用? 这个PluginContentResolver在PluginContext类的getContentResolver()方法中创建。
1 2 3 4 @Override public ContentResolver getContentResolver () { return new PluginContentResolver (getHostContext()); }
PluginContext在什么时候被使用? LoadedPlugin的构造函数里会创建,并一直缓存。
在创建插件的Activity、Service、ContentProvider类时,都会传这个Context对象,以保证插件里获取的Context对象都是PluginContext,好让自定义的行为发生作用。
query()时怎么判断是不是要访问插件里的ContentProvider? query()会传一个Uri参数,然后把Uri传给acquireUnstableProvider(uri)
1 2 3 4 5 6 7 8 9 10 public final IContentProvider acquireUnstableProvider (Uri uri) { if (!SCHEME_CONTENT.equals(uri.getScheme())) { return null ; } String auth = uri.getAuthority(); if (auth != null ) { return acquireUnstableProvider(mContext, uri.getAuthority()); } return null ; }
Uri中的Authority部分可以唯一标识一个ContentProvider。
PluginContentResolver.acquireUnstableProvider()中,通过判断mPluginManager.resolveContentProvider(auth, 0) != null
断定要加载的是不是插件中的ContentProvider。
PluginManager.resolveContentProvider()内部,也就是遍历插件Apk的AndroidManifest,然后把ContentProvider的信息都保存一个集合类里,然后遍历集合对比authority寻找目标ContentProvider。
怎么获取预埋的ContentProvider对象实例? query()时发现要加载插件里的ContentProvider,然后要把消息转发给预埋的ContentProvider,但是预埋的ContentProvider对象怎么获取?
看上面的源码,可以知道会调用mPluginManager.getIContentProvider()
,并返回IContentProvider。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public synchronized IContentProvider getIContentProvider () { if (mIContentProvider == null ) { hookIContentProviderAsNeeded(); } return mIContentProvider; } protected void hookIContentProviderAsNeeded () { Uri uri = Uri.parse(RemoteContentProvider.getUri(mContext)); mContext.getContentResolver().call(uri, "wakeup" , null , null ); try { Field authority = null ; Field provider = null ; ActivityThread activityThread = ActivityThread.currentActivityThread(); Map providerMap = Reflector.with(activityThread).field("mProviderMap" ).get(); Iterator iter = providerMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); Object key = entry.getKey(); Object val = entry.getValue(); String auth; if (key instanceof String) { auth = (String) key; } else { if (authority == null ) { authority = key.getClass().getDeclaredField("authority" ); authority.setAccessible(true ); } auth = (String) authority.get(key); } if (auth.equals(RemoteContentProvider.getAuthority(mContext))) { if (provider == null ) { provider = val.getClass().getDeclaredField("mProvider" ); provider.setAccessible(true ); } IContentProvider rawProvider = (IContentProvider) provider.get(val); IContentProvider proxy = IContentProviderProxy.newInstance(mContext, rawProvider); mIContentProvider = proxy; Log.d(TAG, "hookIContentProvider succeed : " + mIContentProvider); break ; } } } catch (Exception e) { Log.w(TAG, e); } }
hookIContentProviderAsNeeded()就干两件事:
mContext.getContentResolver().call(uri, "wakeup", null, null)
触发AndroidManifest里预埋的RemoteContentProvider启动和创建。
RemoteContentProvider创建后必然保存在当前进程的ActivityThread里的mProviderMap里。
去ActivityThread里找到RemoteContentProvider的实例并返回。
RemoteContentProvider.query()做了什么? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 @Override public Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { ContentProvider provider = getContentProvider(uri); Uri pluginUri = Uri.parse(uri.getQueryParameter(KEY_URI)); if (provider != null ) { return provider.query(pluginUri, projection, selection, selectionArgs, sortOrder); } return null ; } private ContentProvider getContentProvider (final Uri uri) { final PluginManager pluginManager = PluginManager.getInstance(getContext()); Uri pluginUri = Uri.parse(uri.getQueryParameter(KEY_URI)); final String auth = pluginUri.getAuthority(); ContentProvider cachedProvider = sCachedProviders.get(auth); if (cachedProvider != null ) { return cachedProvider; } synchronized (sCachedProviders) { LoadedPlugin plugin = pluginManager.getLoadedPlugin(uri.getQueryParameter(KEY_PKG)); if (plugin == null ) { try { pluginManager.loadPlugin(new File (uri.getQueryParameter(KEY_PLUGIN))); } catch (Exception e) { Log.w(TAG, e); } } final ProviderInfo providerInfo = pluginManager.resolveContentProvider(auth, 0 ); if (providerInfo != null ) { RunUtil.runOnUiThread(new Runnable () { @Override public void run () { try { LoadedPlugin loadedPlugin = pluginManager.getLoadedPlugin(uri.getQueryParameter(KEY_PKG)); ContentProvider contentProvider = (ContentProvider) Class.forName(providerInfo.name).newInstance(); contentProvider.attachInfo(loadedPlugin.getPluginContext(), providerInfo); sCachedProviders.put(auth, contentProvider); } catch (Exception e) { Log.w(TAG, e); } } }, true ); return sCachedProviders.get(auth); } } return null ; }
代码很清晰,主要两件事:
创建插件里的ContentProvider对象实例并缓存。
调用插件ContentProvider的query()方法,把参数正确的传递。
至此插件的ContentProvider代理过程完成。
参考资料