码迷,mamicode.com
首页 > 其他好文 > 详细

DroidPlugin源码分析插件运行环境初始化

时间:2016-07-23 07:32:59      阅读:212      评论:0      收藏:0      [点我收藏+]

标签:

从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(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初始化过程。

DroidPlugin源码分析插件运行环境初始化

标签:

原文地址:http://blog.csdn.net/zhangyawen1i/article/details/52001803

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!