您的位置:首页 > 其它

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();

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初始化过程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  插件