Virtual apk插件化架构分析

其基本原理,就是hook系统的activity service等关键组件,当需要启动插件内的一些组件式,走自定义逻辑,当启动本地apk中的组件时,走原生逻辑。

一、系统相关。

在分析滴滴插件化框架之前,需要弄清楚android系统中应用的启动流程,以及和系统的通信调度相关。

有几个关键类需要了解下:ActivityThread.java ApplicationThread.java ActivityManagerService.java Instrumentation.java

启动流程如下:

在laucher里面启动一个新的app时,流程参考老罗文章:

整个应用程序的启动过程要执行很多步骤,但是整体来看,主要分为以下五个阶段:

   一. Step1 -Step 11:Launcher通过Binder进程间通信机制通知ActivityManagerService,它要启动一个Activity;

   二. Step 12 -Step 16:ActivityManagerService通过Binder进程间通信机制通知Launcher进入Paused状态;

   三. Step 17 -Step 24:Launcher通过Binder进程间通信机制通知ActivityManagerService,它已经准备就绪进入Paused状态,于是ActivityManagerService就创建一个新的进程,用来启动一个ActivityThread实例,即将要启动的Activity就是在这个ActivityThread实例中运行;

   四. Step 25 -Step 27:ActivityThread通过Binder进程间通信机制将一个ApplicationThread类型的Binder对象传递给ActivityManagerService,以便以后ActivityManagerService能够通过这个Binder对象和它进行通信;

   五. Step 28 -Step 35:ActivityManagerService通过Binder进程间通信机制通知ActivityThread,现在一切备就绪,它可以真正执行Activity的启动

ActivityManagerService:

这个是系统核心类,几乎掌管了整个系统启动相关的事宜。

ActivityThread:

它是一个应用进程的主线程,有一个死循环来分发进程需要处理的工作。

ApplicationThread:

用途参照启动流程中的step4.

Instrumentation:

里面的方法如下截图,基本上管理了所有与Activity的相关交互工作。So,这个类需要被hook



二、源码分析。

如果想从主apk里面启动插件apk相关的activity或service,那么首先必须先把插件中的相关activity和service加载进来,整个加载流程如下。

 

针对hook的这几个系统类,Instrumentation使用了一个子类对象(VAInstrumentation)来代替原本的instrumentation,这个子类里面做的事情很简单。复写了父类的newActivity()(就是当我们创建activity时调用的方法),利用try catch捕捉classnotfoundexception,正常情况下说明我们想加载的是插件apk中的activity,但是默认情况,我们的应用是没有把插件apk中的类加载进来的,所以报找不到类的错误。So,这个时候,我们就可以去插件apk中加载对应的activity了。

当需要启动一个activity时,一般使用startActivity(),那么就跟随这个方法,看看其调用链。

public void startActivityForResult(Intentintent, int requestCode, @Nullable Bundle options) {

        if(mParent == null) {

//在这里看到调用了instrumentation的execStartActivity(),那么我们就需要针对instrumentation的这个方法进行处理,可以看到在virtualapk代码中,确实是这样处理的。

           Instrumentation.ActivityResult ar =

               mInstrumentation.execStartActivity(

                    this,mMainThread.getApplicationThread(), mToken, this,

                    intent, requestCode, options);

          …………

    }

Instrumentation.java

 publicActivityResult execStartActivity(

           Context who, IBinder contextThread, IBinder token, Activity target,

           Intent intent, int requestCode, Bundle options) {

       IApplicationThread whoThread = (IApplicationThread) contextThread;

       Uri referrer = target != null ? target.onProvideReferrer() : null;

        if(referrer != null) {

           intent.putExtra(Intent.EXTRA_REFERRER, referrer);

        }

    …………

       try {

           intent.migrateExtraStreamToClipData();

           intent.prepareToLeaveProcess();

//实际调用的是ActivityManagerNative,其实就是系统调用ActivityManagerService来启动activity.

//这个就不深究了。既然启动activity需要调用Instrumentation的这个方法,so 我们需要处理这个方法。

            int result =ActivityManagerNative.getDefault()

                .startActivity(whoThread,who.getBasePackageName(), intent,

                       intent.resolveTypeIfNeeded(who.getContentResolver()),

                        token, target != null ?target.mEmbeddedID : null,

                        requestCode, 0, null,options);

           checkStartActivityResult(result, intent);

        }catch (RemoteException e) {

           throw new RuntimeException("Failure from system", e);

        }

       return null;

    }

Instrumentation.java

//复写的父类中的方法.

    publicActivityResult execStartActivity(

           Context who, IBinder contextThread, IBinder token, Activity target,

           Intent intent, int requestCode, Bundle options) {

       mPluginManager.getComponentsHandler().transformIntentToExplicitAsNeeded(intent);

        //null component is an implicitly intent

        if(intent.getComponent() != null) {

           Log.i(TAG, String.format("execStartActivity[%s : %s]",intent.getComponent().getPackageName(),

                   intent.getComponent().getClassName()));

           // resolve intent with Stub Activity if needed

           //启动一个本地预置的activity,不过在其intent中加入了 plugin标志位,以及目标包名,目标类名

           //接下来继续走正常的activity启动流程,直到newActivity()

           this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent);

        }

       ActivityResult result = realExecStartActivity(who, contextThread, token,target,

                   intent, requestCode, options);

       return result;

    }

整个调用流程如下图片:

 

@Override

    publicActivity newActivity(ClassLoader cl, String className, Intent intent) throwsInstantiationException, IllegalAccessException, ClassNotFoundException {

       try {

//先从主apkclassloader中加载类,注意此时的类名是<activityandroid:name=".B$1" android:launchMode="singleTop"/>,又因为本地是没有创建这个类的,所以肯定会报找不到类的错误           cl.loadClass(className);

        }catch (ClassNotFoundException e) {

//可以看下LoadedPlugin的实现,它是怎样把插件apk中的类加载进来的。

           LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent);

//从加载的插件资源中,找到对应的activity

           String targetClassName = PluginUtil.getTargetActivity(intent);

           Log.i(TAG, String.format("newActivity[%s : %s]", className,targetClassName));

           if (targetClassName != null) {

//plugin.getClassLoader() 已经是加载过插件资源的classloader,这里就创建了一个插件activity实例

                //相当于这个时候就用插件apk中的activity代替了预置的<activity android:name=".B$1"android:launchMode="singleTop"/>

                //之所以要这样操作,是因为如果不预置activity,即不在manifest中声明,那么就会报activityNotFound的错误。

                //所以采取的策略是,先骗过系统,防止出现activityNotFound的错误                Activity activity =mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);

               activity.setIntent(intent);

               try {

                    // for 4.1+

//通过反射修改mResources为加载了插件资源的resource

                   ReflectUtil.setField(ContextThemeWrapper.class, activity,"mResources", plugin.getResources());

               } catch (Exception ignored) {

                    // ignored.

               }

               return activity;

           }

        }

       return mBase.newActivity(cl, className, intent);

    }

至此,插件apk中的activity的启动就分析完毕了。

针对service,是hook了系统的利用动态代理实现的。

 /**

     *hookSystemServices, but need to compatible with Android O in future.

     */

   private void hookSystemServices() {

       try {

           Singleton<IActivityManager> defaultSingleton =(Singleton<IActivityManager>)ReflectUtil.getField(ActivityManagerNative.class, null, "gDefault");

           IActivityManager activityManagerProxy =ActivityManagerProxy.newInstance(this, defaultSingleton.get());

           // Hook IActivityManager from ActivityManagerNative

            //把singleton的内部的对象用代理对象代替。

           ReflectUtil.setField(defaultSingleton.getClass().getSuperclass(),defaultSingleton, "mInstance", activityManagerProxy);

           if (defaultSingleton.get() == activityManagerProxy) {

               this.mActivityManager = activityManagerProxy;

           }

        }catch (Exception e) {

           e.printStackTrace();

        }

    }

看下自己定义的动态代理类。

ActivityManagerProxy.java

@Override

    publicObject 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 serviceerror", 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) {

               e.printStackTrace();

           }

        }else if ("unbindService".equals(method.getName())) {

           try {

               return unbindService(proxy, method, args);

           } catch (Throwable e) {

               e.printStackTrace();

            }

        }else if ("getIntentSender".equals(method.getName())) {

           try {

               getIntentSender(method, args);

           } catch (Exception e) {

               e.printStackTrace();

           }

        }else if ("overridePendingTransition".equals(method.getName())){

           try {

               overridePendingTransition(method, args);

           } catch (Exception e){

               e.printStackTrace();

           }

        }

       try {

           // sometimes system binder has problems.

           return method.invoke(this.mActivityManager, args);

        }catch (Throwable th) {

           Throwable c = th.getCause();

           if (c != null && c instanceof DeadObjectException) {

                // retry connect to systembinder

               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;

        }

    }

ActivityManagerProxy.java

private Object startService(Object proxy, Methodmethod, Object[] args) throws Throwable {

       IApplicationThread appThread = (IApplicationThread) args[0];

       Intent target = (Intent) args[1];

        //先在插件service集合中查找,找不到就说明是主apk中的service

       ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0);

        //启动的是主apk中的service

        if(null == resolveInfo || null == resolveInfo.serviceInfo) {

           // is host service

           return method.invoke(this.mActivityManager, args);

        }

       return startDelegateServiceForTarget(target, resolveInfo.serviceInfo,null, RemoteService.EXTRA_COMMAND_START_SERVICE);

}

wrapperTargetIntent():

private Intent wrapperTargetIntent(Intenttarget, ServiceInfo serviceInfo, Bundle extras, int command) {

        //fill in service with ComponentName

       target.setComponent(new ComponentName(serviceInfo.packageName,serviceInfo.name));

       String pluginLocation =mPluginManager.getLoadedPlugin(target.getComponent()).getLocation();

        //start delegate service to run plugin service inside

       boolean local = PluginUtil.isLocalService(serviceInfo);

       Class<? extends Service> delegate = local ? LocalService.class :RemoteService.class;

       Intent intent = new Intent();

//启动本地代理service

       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作为例子来看:

/*

 *Copyright (C) 2017 Beijing Didi Infinity Technology and Development Co.,Ltd.All rights reserved.

 *

 *Licensed under the Apache License, Version 2.0 (the "License");

 * you maynot use this file except in compliance with the License.

 * You mayobtain a copy of the License at

 *

 *http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unlessrequired by applicable law or agreed to in writing, software

 *distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUTWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See theLicense for the specific language governing permissions and

 *limitations under the License.

 */

package com.didi.virtualapk.delegate;

import android.app.ActivityThread;

import android.app.Application;

import android.app.IActivityManager;

import android.app.IApplicationThread;

import android.app.IServiceConnection;

import android.app.Service;

import android.content.ComponentName;

import android.content.Context;

import android.content.Intent;

import android.os.Binder;

import android.os.Build;

import android.os.IBinder;

import android.util.Log;

import com.didi.virtualapk.PluginManager;

importcom.didi.virtualapk.internal.LoadedPlugin;

import com.didi.virtualapk.utils.PluginUtil;

import com.didi.virtualapk.utils.ReflectUtil;

import java.lang.reflect.Method;

/**

 * @authorjohnsonlee

 */

public class LocalService extends Service {

   private static final String TAG = "LocalService";

    /**

     * Thetarget service, usually it's a plugin service intent

     */

    publicstatic final String EXTRA_TARGET = "target";

    publicstatic final String EXTRA_COMMAND = "command";

    publicstatic final String EXTRA_PLUGIN_LOCATION = "plugin_location";

    publicstatic final int EXTRA_COMMAND_START_SERVICE = 1;

    publicstatic final int EXTRA_COMMAND_STOP_SERVICE = 2;

    publicstatic final int EXTRA_COMMAND_BIND_SERVICE = 3;

    publicstatic final int EXTRA_COMMAND_UNBIND_SERVICE = 4;

   private PluginManager mPluginManager;

   @Override

    publicIBinder onBind(Intent intent) {

       return new Binder();

    }

   @Override

    publicvoid onCreate() {

       super.onCreate();

       mPluginManager = PluginManager.getInstance(this);

    }

   @Override

    publicint onStartCommand(Intent intent, int flags, int startId) {

        if(null == intent || !intent.hasExtra(EXTRA_TARGET) ||!intent.hasExtra(EXTRA_COMMAND)) {

           return START_STICKY;

        }

//获取目标service

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

        //ClassNotFoundException when unmarshalling in Android 5.1

       target.setExtrasClassLoader(plugin.getClassLoader());

       switch (command) {

           case EXTRA_COMMAND_START_SERVICE: {

               ActivityThread mainThread =(ActivityThread)ReflectUtil.getActivityThread(getBaseContext());

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

                        //反射调用serviceattach()

                        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;

           }

           …………

}


猜你喜欢

转载自blog.csdn.net/HelloMagina/article/details/79533895