DroidPlugin源码分析插件运行环境初始化
2016-07-23 01:27
751 查看
从DroidPlugin的官方文档中我们知道。
2 在AndroidManifest.xml中使用插件的com.morgoo.droidplugin.PluginApplication:
或者在自定义的Application的onCreate()函数中,调用PluginHelper.getInstance().applicationOnCreate(getBaseContext());
在Application的attachBaseContext()函数中,调用
PluginHelper.getInstance().applicationAttachBaseContext(base);
接下来就先分析PluginHelper.applicationOnCreate();
第一步PluginHelper.applicationOnCreate();
这个函数比较简单,只是保存传入的Context对象,然后调用PluginHelper的initPlugin这个函数。
第二步PluginHelper. initPlugin()函数如下:
A 适配小米系统 fixMiUiLbeSecurity
首先调用fixMiUiLbeSecurity函数如下:
其实这个函数的作用注释已经说得非常清楚了,小米某一款手机自带了一款Lbe的安全软件,
而这个软件内部自定义MonitoredLoaderMap类应该是继承自Map类,替换了ApplicationLoaders类中的原本的成员变量mLoaders来管理Android系统中的应用程序的类加载器。LoadedApk内一个成员变量mClassLoader就是通过ApplicationLoaders来创建的。这里卸载掉钩子就是把mLoaders替换成原生的HashMap对象。
另外一个类MonitoredPackageMap替换了ActivityThread中原本的mPackages对象,mPackages是HashMap类对象以应用报名为key,LoadedApk对象实例为Value。这里卸掉钩子也就是把mPackages对象替换成原生的HashMap对象。
在Activity启动过程中,我们的插件Activity实例化,就是通过预先把插件Apk包名对应的LoadedApk对象保存到ActivityThread的成员变量mPackages中,顺便用PluginClassLoader 替换LoadedApk中的mClassLoader.这样PluginClassLoader就可以加载插件中的类了。(这里会在插件Activity启动中详细分析)
也许是因为影响到了插件Activity的启动过程,所以需要处理一下。
最后把主线程里面所有lbe的消息都删除。
B 插件异常处理PluginPatchManager.getInstance().init()
这个初始化就是把context保存到PluginPatchManager的成员变量mContext中,这里保存Context对象主要就是为了延迟启动插件Activity。这个类主要是在启动插件Activity过程中,会调用PluginPatchManager的函数canStartPluginActivity判断PluginManagerService这个插件管理服务是否已经启动,如果没有启动则会调用PluginPatchManager的函数startPluginActivity延迟启动插件Activity等待PluginManagerService启动完成。后面会在Activity相关文章中分析。
C 初始化Hook系统插件需要用到的相关服务
PluginProcessManager.installHook(baseContext);
这个函数主要是调用HookFactory.getInstance().installHook(hostContext, null); Hook系统相关服务。(Hook的过程稍后会详细分析)
D 宿主进程关闭hook开关等待PluginManagerService启动后在打开
通过PluginProcessManager.isPluginProcess判断,如果是插件进程则打开Hook开关,如果是宿主进程暂时关闭Hook开关。它会等到PluginManagerService启动完成后,再打开Hook开关。
首先获取当前进程的名字再判断是否和宿主进程包名相同,如果相同说明不是插件进程。如果不同还需要继续判断,是否是宿主应用开出的其他进程(除去DroidPlugin为插件预定义了N个进程)initProcessList就会把宿主应用开出的其他进程保存在sProcessList。
在这里为什么如果是宿主进程需要先关闭Hook?因为Hook主要是为了瞒过系统保证插件Apk在未安装的情况下也能正常运行, PluginManagerService还没有启动的时候,已经安装的插件Apk是还没有装载进来的。另外,如果是在插件进程中,那么Hook完成之后可以直接打开Hook开关,因为PluginManagerService是运行在宿主进程中的。
E 注册服务连接Callback启动PluginManagerService
PluginManager.getInstance().addServiceConnection
bba3
(PluginHelper.this);
PluginManager.getInstance().init(baseContext);
首先注册服务连接,等待服务启动完成后,调用PluginProcessManager.setHookEnable(true)函数打开Hook开关。
然后调用PluginManager的init函数,这个函数主要就是启动PluginManagerService。
F 启动PluginManagerService初始化IPluginManagerImpl对象。
首先提升service所在进程优先级。
创建IPluginManagerImpl对象, IPluginManagerImpl的构造函数比较简单,只是创建了一个MyActivityManagerService 对象,他主要是用于插件进程管理的。
接着调用IPluginManagerImpl的onCreate函数。
onCreate函数中主要是启动一个线程,运行onCreateInner函数。
G 加载已经安装的插件,获取宿主进程申明的权限
函数loadAllPlugin()主要是从/data/data/com.HOST.PACKAGE/Plugin目录下面找到已经安装的插件包,创建PluginPackageParser对象并缓存,然后缓存签名。这些基本都在DroidPlugin源码分析安装和卸载中有分析。
函数loadHostRequestedPermission()主要是搜集宿主进程的申明的权限信息。
H 创建并保存IPluginManagerImpl Proxy对象,创建线程,等待IPluginManagerImpl初始化完成后,分发服务连接成功监听,注册服务死亡回调。
函数首先保存IPluginManagerImpl Proxy 对象到mPluginManger中,
然后调用mPluginManager.waitForReady()查询服务是否初始化完成,如果没有完成则等待。
完成后,则分发这服务连接成功监听。在E步中收到服务连接成功回调,设置hook开关为True。
最后注册Binder死亡毁掉,当IPluginManagerImpl本地binder对象销毁时,我们会收到Binder死亡通知,然后在通知中重新启动PluginManagerService。
第三步:在Application的attachBaseContext()函数中,调用
这个函数主要是注册一个UncaughtExceptionHandler在应用异常退出时,保存Crash异常信息。
到此插件运行环境初始化过程结束,总结一下:
1 适配小米手机预装LBE,替换ApplicationLoaders成员变量mLoaders,替换ActivityThread成员变量mPackages,然后从主线程队列里面移除所有lbe相关的消息。
2 初始化插件异常处理和初始化Hook系统相关服务。如果是在宿主进程中先关闭Hook开关,如果不是则打开Hook开关。
3 注册PluginManagerService的服务连接监听对象,然后启动PluginManagerService服务,在服务中创建MyActivityManagerService的对象,管理所有的插件进程。然后装载所有已经安装的插件Apk。服务启动完成以后,服务监听收到回调,打开Hook开关。
由此我们可以知道PluginManagerService主要职责,装载已经安装的插件Apk,插件Apk的安装和卸载,插件Apk信息的查询,以及插件进程管理等。
下一篇博客我们再来分析,Hook初始化过程。
2 在AndroidManifest.xml中使用插件的com.morgoo.droidplugin.PluginApplication:
或者在自定义的Application的onCreate()函数中,调用PluginHelper.getInstance().applicationOnCreate(getBaseContext());
在Application的attachBaseContext()函数中,调用
PluginHelper.getInstance().applicationAttachBaseContext(base);
接下来就先分析PluginHelper.applicationOnCreate();
第一步PluginHelper.applicationOnCreate();
public void applicationOnCreate(final Context baseContext) { mContext = baseContext; initPlugin(baseContext); }
这个函数比较简单,只是保存传入的Context对象,然后调用PluginHelper的initPlugin这个函数。
第二步PluginHelper. initPlugin()函数如下:
private void initPlugin(Context baseContext) { fixMiUiLbeSecurity(); PluginPatchManager.getInstance().init(baseContext); PluginProcessManager.installHook(baseContext); if (PluginProcessManager.isPluginProcess(baseContext)) { PluginProcessManager.setHookEnable(true); } else { PluginProcessManager.setHookEnable(false); } PluginManager.getInstance().addServiceConnection(PluginHelper.this); PluginManager.getInstance().init(baseContext); }
A 适配小米系统 fixMiUiLbeSecurity
首先调用fixMiUiLbeSecurity函数如下:
//解决小米JLB22.0 4.1.1系统自带的小米安全中心(lbe.security.miui)广告拦截组件导致的插件白屏问题 private void fixMiUiLbeSecurity(){ //卸载掉LBE安全的ApplicationLoaders.mLoaders钩子 Class ApplicationLoaders = Class.forName("android.app.ApplicationLoaders"); Object applicationLoaders = MethodUtils.invokeStaticMethod(ApplicationLoaders, "getDefault"); Object mLoaders = FieldUtils.readField(applicationLoaders, "mLoaders", true); if (mLoaders instanceof HashMap) { HashMap oldValue = ((HashMap) mLoaders); if ("com.lbe.security.client.ClientContainer$MonitoredLoaderMap".equals(mLoaders.getClass().getName())) { HashMap value = new HashMap(); value.putAll(oldValue); FieldUtils.writeField(applicationLoaders, "mLoaders", value, true); } } //卸载掉LBE安全的ActivityThread.mPackages钩子 Object currentActivityThread = ActivityThreadCompat.currentActivityThread(); Object mPackages = FieldUtils.readField(currentActivityThread, "mPackages", true); if (mPackages instanceof HashMap) { HashMap oldValue = ((HashMap) mPackages); if ("com.lbe.security.client.ClientContainer$MonitoredPackageMap".equals(mPackages.getClass().getName())) { HashMap value = new HashMap(); value.putAll(oldValue); FieldUtils.writeField(currentActivityThread, "mPackages", value, true); } } //当前已经处在主线程消息队列中的所有消息,找出lbe消息并remove之 if (Looper.getMainLooper() == Looper.myLooper()) { final MessageQueue queue = Looper.myQueue(); Object mMessages = FieldUtils.readField(queue, "mMessages", true); if (mMessages instanceof Message) { findLbeMessageAndRemoveIt((Message) mMessages); } }
其实这个函数的作用注释已经说得非常清楚了,小米某一款手机自带了一款Lbe的安全软件,
而这个软件内部自定义MonitoredLoaderMap类应该是继承自Map类,替换了ApplicationLoaders类中的原本的成员变量mLoaders来管理Android系统中的应用程序的类加载器。LoadedApk内一个成员变量mClassLoader就是通过ApplicationLoaders来创建的。这里卸载掉钩子就是把mLoaders替换成原生的HashMap对象。
另外一个类MonitoredPackageMap替换了ActivityThread中原本的mPackages对象,mPackages是HashMap类对象以应用报名为key,LoadedApk对象实例为Value。这里卸掉钩子也就是把mPackages对象替换成原生的HashMap对象。
在Activity启动过程中,我们的插件Activity实例化,就是通过预先把插件Apk包名对应的LoadedApk对象保存到ActivityThread的成员变量mPackages中,顺便用PluginClassLoader 替换LoadedApk中的mClassLoader.这样PluginClassLoader就可以加载插件中的类了。(这里会在插件Activity启动中详细分析)
也许是因为影响到了插件Activity的启动过程,所以需要处理一下。
最后把主线程里面所有lbe的消息都删除。
B 插件异常处理PluginPatchManager.getInstance().init()
public void init(Context context){ mContext = context; }
这个初始化就是把context保存到PluginPatchManager的成员变量mContext中,这里保存Context对象主要就是为了延迟启动插件Activity。这个类主要是在启动插件Activity过程中,会调用PluginPatchManager的函数canStartPluginActivity判断PluginManagerService这个插件管理服务是否已经启动,如果没有启动则会调用PluginPatchManager的函数startPluginActivity延迟启动插件Activity等待PluginManagerService启动完成。后面会在Activity相关文章中分析。
C 初始化Hook系统插件需要用到的相关服务
PluginProcessManager.installHook(baseContext);
这个函数主要是调用HookFactory.getInstance().installHook(hostContext, null); Hook系统相关服务。(Hook的过程稍后会详细分析)
D 宿主进程关闭hook开关等待PluginManagerService启动后在打开
if (PluginProcessManager.isPluginProcess(baseContext)) { PluginProcessManager.setHookEnable(true); } else { PluginProcessManager.setHookEnable(false); }
通过PluginProcessManager.isPluginProcess判断,如果是插件进程则打开Hook开关,如果是宿主进程暂时关闭Hook开关。它会等到PluginManagerService启动完成后,再打开Hook开关。
PluginProcessManager.isPluginProcess函数如下: public static final boolean isPluginProcess(Context context) { String currentProcessName = getCurrentProcessName(context); if (TextUtils.equals(currentProcessName, context.getPackageName())) return false; initProcessList(context); return !sProcessList.contains(currentProcessName); }
首先获取当前进程的名字再判断是否和宿主进程包名相同,如果相同说明不是插件进程。如果不同还需要继续判断,是否是宿主应用开出的其他进程(除去DroidPlugin为插件预定义了N个进程)initProcessList就会把宿主应用开出的其他进程保存在sProcessList。
在这里为什么如果是宿主进程需要先关闭Hook?因为Hook主要是为了瞒过系统保证插件Apk在未安装的情况下也能正常运行, PluginManagerService还没有启动的时候,已经安装的插件Apk是还没有装载进来的。另外,如果是在插件进程中,那么Hook完成之后可以直接打开Hook开关,因为PluginManagerService是运行在宿主进程中的。
E 注册服务连接Callback启动PluginManagerService
PluginManager.getInstance().addServiceConnection
bba3
(PluginHelper.this);
PluginManager.getInstance().init(baseContext);
首先注册服务连接,等待服务启动完成后,调用PluginProcessManager.setHookEnable(true)函数打开Hook开关。
然后调用PluginManager的init函数,这个函数主要就是启动PluginManagerService。
F 启动PluginManagerService初始化IPluginManagerImpl对象。
PluginManagerService的onCreate函数。 public void onCreate() { keepAlive(); mPluginPackageManager = new IPluginManagerImpl(this); mPluginPackageManager.onCreate(); }
首先提升service所在进程优先级。
创建IPluginManagerImpl对象, IPluginManagerImpl的构造函数比较简单,只是创建了一个MyActivityManagerService 对象,他主要是用于插件进程管理的。
接着调用IPluginManagerImpl的onCreate函数。
onCreate函数中主要是启动一个线程,运行onCreateInner函数。
G 加载已经安装的插件,获取宿主进程申明的权限
private void onCreateInner() { loadAllPlugin(mContext); loadHostRequestedPermission(); mHasLoadedOk.set(true); synchronized (mLock) { mLock.notifyAll(); } }
函数loadAllPlugin()主要是从/data/data/com.HOST.PACKAGE/Plugin目录下面找到已经安装的插件包,创建PluginPackageParser对象并缓存,然后缓存签名。这些基本都在DroidPlugin源码分析安装和卸载中有分析。
函数loadHostRequestedPermission()主要是搜集宿主进程的申明的权限信息。
H 创建并保存IPluginManagerImpl Proxy对象,创建线程,等待IPluginManagerImpl初始化完成后,分发服务连接成功监听,注册服务死亡回调。
public void onServiceConnected(final ComponentName componentName, final IBinder iBinder) { mPluginManager = IPluginManager.Stub.asInterface(iBinder); new Thread() { @Override public void run() { mPluginManager.waitForReady(); mPluginManager.registerApplicationCallback(new IApplicationCallback.Stub() { @Override public Bundle onCallback(Bundle extra) throws RemoteException { return extra; } }); Iterator<WeakReference<ServiceConnection>> iterator = sServiceConnection.iterator(); while (iterator.hasNext()) { WeakReference<ServiceConnection> wsc = iterator.next(); ServiceConnection sc = wsc != null ? wsc.get() : null; if (sc != null) { sc.onServiceConnected(componentName, iBinder); } else { iterator.remove(); } } mPluginManager.asBinder().linkToDeath(new IBinder.DeathRecipient() { @Override public void binderDied() { onServiceDisconnected(componentName); } }, 0); } } }.start(); }
函数首先保存IPluginManagerImpl Proxy 对象到mPluginManger中,
然后调用mPluginManager.waitForReady()查询服务是否初始化完成,如果没有完成则等待。
完成后,则分发这服务连接成功监听。在E步中收到服务连接成功回调,设置hook开关为True。
最后注册Binder死亡毁掉,当IPluginManagerImpl本地binder对象销毁时,我们会收到Binder死亡通知,然后在通知中重新启动PluginManagerService。
第三步:在Application的attachBaseContext()函数中,调用
PluginHelper.getInstance().applicationAttachBaseContext(base) public void applicationAttachBaseContext(Context baseContext) { MyCrashHandler.getInstance().register(baseContext); }
这个函数主要是注册一个UncaughtExceptionHandler在应用异常退出时,保存Crash异常信息。
到此插件运行环境初始化过程结束,总结一下:
1 适配小米手机预装LBE,替换ApplicationLoaders成员变量mLoaders,替换ActivityThread成员变量mPackages,然后从主线程队列里面移除所有lbe相关的消息。
2 初始化插件异常处理和初始化Hook系统相关服务。如果是在宿主进程中先关闭Hook开关,如果不是则打开Hook开关。
3 注册PluginManagerService的服务连接监听对象,然后启动PluginManagerService服务,在服务中创建MyActivityManagerService的对象,管理所有的插件进程。然后装载所有已经安装的插件Apk。服务启动完成以后,服务监听收到回调,打开Hook开关。
由此我们可以知道PluginManagerService主要职责,装载已经安装的插件Apk,插件Apk的安装和卸载,插件Apk信息的查询,以及插件进程管理等。
下一篇博客我们再来分析,Hook初始化过程。
相关文章推荐
- java自动生成验证码插件-kaptcha
- jQuery插件实现文字无缝向上滚动效果代码
- jQuery菜单插件用法实例
- 加载flash9.ocx出现错误的解决方法
- jquery实现的代替传统checkbox样式插件
- 10款新鲜出炉的 jQuery 插件(Ajax 插件,有幻灯片、图片画廊、菜单等)
- jquery插件autocomplete用法示例
- 推荐40个非常优秀的jQuery插件和教程【系列三】
- Node.js插件的正确编写方式
- 推荐十款免费 WordPress 插件
- NopCommerce架构分析之(四)基于路由实现灵活的插件机制
- Bootstrap教程JS插件弹出框学习笔记分享
- Bootstrap插件全集
- 使用JavaScript开发IE浏览器本地插件实例
- jQuery实现的简单提示信息插件
- 推荐25个超炫的jQuery网格插件
- 纯JavaScript实现的分页插件实例
- JQuery插件jcarousellite的参数中文说明
- Bootstrap Paginator分页插件使用方法详解
- Bootstrap每天必学之弹出框(Popover)插件