滴滴插件化框架(VirtualApk)分析

相关分析连接:https://juejin.im/post/5d7a0d0551882523ee70a3ac

ClassLoader 介绍: https://www.jianshu.com/p/a620e368389a

上一篇文章我们分析了DynamicLoadApk的实现原理,它采用的是一种代理机制,用ProxyActivity代理要启动的TargetActivity,

而今天我们要分析的VirtualApk 的实现思路是采用了一种Hook机制,通过hook ActivityManagerNative,Instrumentation去实现启动插件apk中的Activity,Service,BroadcastReceiver,ContentProvider.

今天我们首先来分析启动插件Apk总的Activity的流程

先来学习了解以下Activity的启动流程  https://blog.csdn.net/yyh352091626/article/details/51086117

我们先来了解以下几个角色:

PluginManager: 作为插件管理者,包含了init, prepare , loadPlugin等操作

ActivityManagerProxy:  实现了对IActivityManagerService的代理行为

VAInstrumentation:  继承了Instrumentation,重写了启动activity,以及对TargetActivity的实例化

StubActivityInfo: 维护了所有了StubActivity(作为要targetActivity的代理)

1 首先分析PluginManager

step1: 初始化

 

public void init() {
    mComponentsHandler = new ComponentsHandler(this);
    RunUtil.getThreadPool().execute(new Runnable() {
        @Override
        public void run() {
            doInWorkThread();
        }
    });
}

这个流程里只是创建了ComponentsHandler,如果要处理启动插件Apk中的Activity,它的作用是用来处理intent,并将targetActivity作为参数封装到intent中,然后启动StubActivity.

 

public void markIntentIfNeeded(Intent intent) {
    if (intent.getComponent() == null) {
        return;
    }

    String targetPackageName = intent.getComponent().getPackageName();
    String targetClassName = intent.getComponent().getClassName();
    // search map and return specific launchmode stub activity
    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);
}
 

 

Step2:  prepare操作

 

private void prepare() {
    Systems.sHostContext = getHostContext();
    this.hookInstrumentationAndHandler();
    if (Build.VERSION.SDK_INT >= 26) {
        this.hookAMSForO();
    } else {
        this.hookSystemServices();
    }
}

 

private void hookInstrumentationAndHandler() {
    try {
        Instrumentation baseInstrumentation = ReflectUtil.getInstrumentation(this.mContext);
        if (baseInstrumentation.getClass().getName().contains("lbe")) {
            // reject executing in paralell space, for example, lbe.
            System.exit(0);
        }

        final VAInstrumentation instrumentation = new VAInstrumentation(this, baseInstrumentation);
        Object activityThread = ReflectUtil.getActivityThread(this.mContext);
        ReflectUtil.setInstrumentation(activityThread, instrumentation);
        ReflectUtil.setHandlerCallback(this.mContext, instrumentation);
        this.mInstrumentation = instrumentation;
    } catch (Exception e) {
        e.printStackTrace();
    }
}

 

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
        ReflectUtil.setField(defaultSingleton.getClass().getSuperclass(), defaultSingleton, "mInstance", activityManagerProxy);

        if (defaultSingleton.get() == activityManagerProxy) {
            this.mActivityManager = activityManagerProxy;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

 

从上面代码中可知,prepare 包含两部分:

 

 1 Hook Instrumentation, 用VAInstrumentation代理了ActivityThread中的mInstrumentation,  这里重新了execStartActivity 和 newActivity

 2 通过AOP 模式,使用了ActivityManagerProxy 来 hook IActivityManager,

 

@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) {
            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 system binder
            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;
    }

}

这里针对startService,stopService,bindService,unbindService等操作进行拦截

step3  插件apk的load 过程 loadPlugin

 

public void loadPlugin(File apk) throws Exception {
    if (null == apk) {
        throw new IllegalArgumentException("error : apk is null.");
    }

    if (!apk.exists()) {
        throw new FileNotFoundException(apk.getAbsolutePath());
    }

    LoadedPlugin plugin = LoadedPlugin.create(this, this.mContext, apk);
    if (null != plugin) {
        this.mPlugins.put(plugin.getPackageName(), plugin);
        // try to invoke plugin's application
        plugin.invokeApplication();
    } else {
        throw  new RuntimeException("Can't load plugin which is invalid: " + apk.getAbsolutePath());
    }
}

创建并解析插件apk并封装为LoadedPlugin, 下面我们看一下LoadedPlugin

它的作用: 创建createClassloader(用来加载插件apk中的java类),初始化AssetsManager和Resources(用来控制资源访问),  复制so, 使用PackageParser解析并缓存插件apk中所用的Activity,Service,BroadcastReceiver,ContentProvider

 

private static ClassLoader createClassLoader(Context context, File apk, File libsDir, ClassLoader parent) {
    File dexOutputDir = context.getDir(Constants.OPTIMIZE_DIR, Context.MODE_PRIVATE);
    String dexOutputPath = dexOutputDir.getAbsolutePath();
    DexClassLoader loader = new DexClassLoader(apk.getAbsolutePath(), dexOutputPath, libsDir.getAbsolutePath(), parent);

    if (Constants.COMBINE_CLASSLOADER) {
        try {
            DexUtil.insertDex(loader);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    return loader;
}

private static AssetManager createAssetManager(Context context, File apk) {
    try {
        AssetManager am = AssetManager.class.newInstance();
        ReflectUtil.invoke(AssetManager.class, am, "addAssetPath", apk.getAbsolutePath());
        return am;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

@WorkerThread
private static Resources createResources(Context context, File apk) {
    if (Constants.COMBINE_RESOURCES) {
        Resources resources = ResourcesManager.createResources(context, apk.getAbsolutePath());
        ResourcesManager.hookResources(context, resources);
        return resources;
    } else {
        Resources hostResources = context.getResources();
        AssetManager assetManager = createAssetManager(context, apk);
        return new Resources(assetManager, hostResources.getDisplayMetrics(), hostResources.getConfiguration());
    }
}

 

下面我们来整体梳理启动插件Apk中Activity的流程

1 client端 的启动

 

Intent intent = new Intent();
intent.setClassName(this, "com.didi.virtualapk.demo.aidl.BookManagerActivity");
startActivity(intent);

2 从Activity的启动流程中可知,activity的具体启动是通过调用ActivityThread中的Instrumentation类型的mInstrumentation的execStartActivity, 之前PluginManager在prepare的是,已经通过hookInstrumentationAndHandler ,用VAInstrumentation 代替了父类的Instrumentation,即调用到VAInstrumentation的execStartActivity,代码如下:

 

public ActivityResult 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
        this.mPluginManager.getComponentsHandler().markIntentIfNeeded(intent);
    }

    ActivityResult result = realExecStartActivity(who, contextThread, token, target,
                intent, requestCode, options);

    return result;

}

private ActivityResult realExecStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    ActivityResult result = null;
    try {
        Class[] parameterTypes = {Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class,
        int.class, Bundle.class};
        result = (ActivityResult)ReflectUtil.invoke(Instrumentation.class, mBase,
                "execStartActivity", parameterTypes,
                who, contextThread, token, target, intent, requestCode, options);
    } catch (Exception e) {
        if (e.getCause() instanceof ActivityNotFoundException) {
            throw (ActivityNotFoundException) e.getCause();
        }
        e.printStackTrace();
    }

    return result;
}
 
 

execStartActivity中做了两件事:

1 transformIntentToExplicitAsNeed ,

2 markIntentIfNeed  如果是插件apk的activity, 进行标记,

 

public void markIntentIfNeeded(Intent intent) {
    if (intent.getComponent() == null) {
        return;
    }

    String targetPackageName = intent.getComponent().getPackageName();
    String targetClassName = intent.getComponent().getClassName();
    // search map and return specific launchmode stub activity
    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);
}

第一步,将targetActivity作为参数封装到intent中, 然后根据launchMode 启动stubActivity,

根据activity启动流程,通过调用activityThread中的performlaunchActivity 去通过调用Instrumentation.newActivity()初始化Activity实例,用于我们已经hook了Instrumentation,所以会调用到VAInstrumentation的的newActivity,代码如下:

 

@Override
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    try {
     cl.loadClass(className);
    } catch (ClassNotFoundException e) {
        LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent);
        String targetClassName = PluginUtil.getTargetActivity(intent);

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

        if (targetClassName != null) {
            Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);
            activity.setIntent(intent);

            try {
                // for 4.1+
                ReflectUtil.setField(ContextThemeWrapper.class, activity, "mResources", plugin.getResources());
            } catch (Exception ignored) {
                // ignored.
            }

            return activity;
        }
    }

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

当启动插件apk 中的activity时,由于我们用了stubActivity代理,所以这个类是不存在的,上面的loadclass会出现异常,然后通过插件的classloader去实例化targetActivity,此时activity已经启动成功。

 

不同于Activity(受launch mode影响,以及 theme等相关,另外activity生命周期的复杂性,所以需要hook Instrumention 以及 manifest占坑的方式来实现 欺骗系统的过程),

Service 插件化:由于Service 的性质决定了 需要在manifest注册,但是不像Activity那样收到launch mode 影响,所以可用 代理方式

在VirtualApk中,使用LocalService来代理同进程内Service,RemoteService来非同进程Service  ,这两个Service 只存在 是否和 主 Dex 在同进程的区别,所以RemoteService 需要额外去执行插件的加载,而LocalService则不需要

ActivityManagerProxy

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) {
        // is host service
        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) {
    // 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();
    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来启动targetService,

public class RemoteService extends LocalService {
    
    private static final String TAG = Constants.TAG_PREFIX + "RemoteService";

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent == null) {
            return super.onStartCommand(intent, flags, startId);
        }

        Intent target = intent.getParcelableExtra(EXTRA_TARGET);
        if (target != null) {
            String pluginLocation = intent.getStringExtra(EXTRA_PLUGIN_LOCATION);
            ComponentName component = target.getComponent();
           //在单独的进程中,需要第一次的时候,需要重新加载插件
            LoadedPlugin plugin = PluginManager.getInstance(this).getLoadedPlugin(component);
            if (plugin == null && pluginLocation != null) {
                try {
                    PluginManager.getInstance(this).loadPlugin(new File(pluginLocation));
                } catch (Exception e) {
                    Log.w(TAG, e);
                }
            }
        }

        return super.onStartCommand(intent, flags, startId);
    }

}

LocalService: 所有的代理都是在onStartCommand中 处理,针对targetService: targetService的初始化,onCreate,onStartCommond,onBind,onUnbind,onDestroy的调用

 

ublic class LocalService extends Service {
    private static final String TAG = Constants.TAG_PREFIX + "LocalService";

    /**
     * The target service, usually it's a plugin service intent
     */
    public static final String EXTRA_TARGET = "target";
    public static final String EXTRA_COMMAND = "command";
    public static final String EXTRA_PLUGIN_LOCATION = "plugin_location";

    public static final int EXTRA_COMMAND_START_SERVICE = 1;
    public static final int EXTRA_COMMAND_STOP_SERVICE = 2;
    public static final int EXTRA_COMMAND_BIND_SERVICE = 3;
    public static final int EXTRA_COMMAND_UNBIND_SERVICE = 4;


    private PluginManager mPluginManager;

    @Override
    public IBinder onBind(Intent intent) {
        return new Binder();
    }

    @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;
        }
        // ClassNotFoundException when unmarshalling in Android 5.1
        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;
            }
            case EXTRA_COMMAND_BIND_SERVICE: {
                ActivityThread mainThread = ActivityThread.currentActivityThread();
                IApplicationThread appThread = mainThread.getApplicationThread();
                Service service = null;

                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) {
                        Log.w(TAG, t);
                    }
                }
                try {
                    IBinder binder = service.onBind(target);
                    IBinder serviceConnection = PluginUtil.getBinder(intent.getExtras(), "sc");
                    IServiceConnection iServiceConnection = IServiceConnection.Stub.asInterface(serviceConnection);
                    if (Build.VERSION.SDK_INT >= 26) {
                        iServiceConnection.connected(component, binder, false);
                    } else {
                        Reflector.QuietReflector.with(iServiceConnection).method("connected", ComponentName.class, IBinder.class).call(component, binder);
                    }
                } catch (Exception e) {
                    Log.w(TAG, e);
                }
                break;
            }
            case EXTRA_COMMAND_STOP_SERVICE: {
                Service service = this.mPluginManager.getComponentsHandler().forgetService(component);
                if (null != service) {
                    try {
                        service.onDestroy();
                    } catch (Exception e) {
                        Log.e(TAG, "Unable to stop service " + service + ": " + e.toString());
                    }
                } else {
                    Log.i(TAG, component + " not found");
                }
                break;
            }
            case EXTRA_COMMAND_UNBIND_SERVICE: {
                Service service = this.mPluginManager.getComponentsHandler().forgetService(component);
                if (null != service) {
                    try {
                        service.onUnbind(target);
                        service.onDestroy();
                    } catch (Exception e) {
                        Log.e(TAG, "Unable to unbind service " + service + ": " + e.toString());
                    }
                } else {
                    Log.i(TAG, component + " not found");
                }
                break;
            }
        }

        return START_STICKY;
    }

}

ContentProvider:

ActivityThread中保存了所有的Provider, 通过反射,hook IContentProvider,

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);
    }
}
/**
 * Created by renyugang on 16/12/8.
 */

public class IContentProviderProxy implements InvocationHandler {
    private static final String TAG = Constants.TAG_PREFIX + "IContentProviderProxy";

    private IContentProvider mBase;
    private Context mContext;

    private IContentProviderProxy(Context context, IContentProvider iContentProvider) {
        mBase = iContentProvider;
        mContext = context;
    }

    public static IContentProvider newInstance(Context context, IContentProvider iContentProvider) {
        return (IContentProvider) Proxy.newProxyInstance(iContentProvider.getClass().getClassLoader(),
                new Class[] { IContentProvider.class }, new IContentProviderProxy(context, iContentProvider));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.v(TAG, method.toGenericString() + " : " + Arrays.toString(args));
        wrapperUri(method, args);

        try {
            return method.invoke(mBase, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

    private void wrapperUri(Method method, Object[] args) {
        Uri uri = null;
        int index = 0;
        if (args != null) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof Uri) {
                    uri = (Uri) args[i];
                    index = i;
                    break;
                }
            }
        }

        Bundle bundleInCallMethod = null;
        if (method.getName().equals("call")) {
            bundleInCallMethod = getBundleParameter(args);
            if (bundleInCallMethod != null) {
                String uriString = bundleInCallMethod.getString(RemoteContentProvider.KEY_WRAPPER_URI);
                if (uriString != null) {
                    uri = Uri.parse(uriString);
                }
            }
        }

        if (uri == null) {
            return;
        }

        PluginManager pluginManager = PluginManager.getInstance(mContext);
        ProviderInfo info = pluginManager.resolveContentProvider(uri.getAuthority(), 0);
        if (info != null) {
            String pkg = info.packageName;
            LoadedPlugin plugin = pluginManager.getLoadedPlugin(pkg);
            Uri wrapperUri = PluginContentResolver.wrapperUri(plugin, uri);
            if (method.getName().equals("call")) {
                bundleInCallMethod.putString(RemoteContentProvider.KEY_WRAPPER_URI, wrapperUri.toString());
            } else {
                args[index] = wrapperUri;
            }
        }
    }

    private Bundle getBundleParameter(Object[] args) {
        Bundle bundle = null;
        if (args != null) {
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof Bundle) {
                    bundle = (Bundle) args[i];
                    break;
                }
            }
        }

        return bundle;
    }

}
PluginContentResolver 负责Uri的pluginUri 转换为 代理proxyUri

 context.getContentResolve().query(uri,....) 流程如下:

ContextImpl.java                               1  getContentResolver()---query() --> acquireUnstableProvider() ---> 

ApplicationContentResolver.java       2 acquireUnstableProvider --> 

@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
    return mMainThread.acquireProvider(c,
            ContentProvider.getAuthorityWithoutUserId(auth),
            resolveUserIdFromAuthority(auth), false);
}

ActivityThread.java   (app中所有的ContentProvider在app冷启动时,被安装(优先于application的onCreate(),然后保存在mProviderMap 中),如果本地app没有要查询的authority,则通过ActivityManagerService 去查询

public final IContentProvider acquireProvider(
        Context c, String auth, int userId, boolean stable) {
    final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
    if (provider != null) {
        return provider;
    }

    // There is a possible race here.  Another thread may try to acquire
    // the same provider at the same time.  When this happens, we want to ensure
    // that the first one wins.
    // Note that we cannot hold the lock while acquiring and installing the
    // provider since it might take a long time to run and it could also potentially
    // be re-entrant in the case where the provider is in the same process.
    ContentProviderHolder holder = null;
    try {
        synchronized (getGetProviderLock(auth, userId)) {
            holder = ActivityManager.getService().getContentProvider(
                    getApplicationThread(), auth, userId, stable);
        }
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
    if (holder == null) {
        Slog.e(TAG, "Failed to find provider info for " + auth);
        return null;
    }

    // Install provider will increment the reference count for us, and break
    // any ties in the race.
    holder = installProvider(c, holder, holder.info,
            true /*noisy*/, holder.noReleaseNeeded, stable);
    return holder.provider;
}

最终通过 RemoteProvider 实现代理

如果插件访问插件的ContentProvider,会用到 PluginContentResolver ,此时不会调用RemoteContentProvider,, ; 如果插件访问宿主的ContentProvider,使用的是原生的ContentResover,此时不会调用RemoteContentProvider, 只有宿主访问插件的ContentProvider时,才会调用ContentProvider。

 

BroadcastReceiver: 插件中静态注册的receiver都是通过applicaiton 的 context 来进行动态注册实现的 ,动态广播不需要插件化

 

宿主与插件,插件与插件,宿主与宿主 之间的通讯都是通过PluginManger.java 中的下面两个方法实现

  /**
     * hookSystemServices, but need to compatible with Android O in future.
     */
    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));

            // Hook IActivityManager from ActivityManagerNative
            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);
        }
    }
    
    protected void hookInstrumentationAndHandler() {
        try {
            ActivityThread activityThread = ActivityThread.currentActivityThread();
            Instrumentation baseInstrumentation = activityThread.getInstrumentation();
//            if (baseInstrumentation.getClass().getName().contains("lbe")) {
//                // reject executing in paralell space, for example, lbe.
//                System.exit(0);
//            }
    
            final VAInstrumentation instrumentation = createInstrumentation(baseInstrumentation);
            
            Reflector.with(activityThread).field("mInstrumentation").set(instrumentation);
            Handler mainHandler = Reflector.with(activityThread).method("getHandler").call();
            Reflector.with(mainHandler).field("mCallback").set(instrumentation);
            this.mInstrumentation = instrumentation;
            Log.d(TAG, "hookInstrumentationAndHandler succeed : " + mInstrumentation);
        } catch (Exception e) {
            Log.w(TAG, e);
        }
    }

插件application的Context 都是PluginContext  , PluginContext.getContentResolver()得到的是 PluginContentResolver

VirtualApk支持宿主和插件中类的互相访问,无法做到插件和插件中类的访问

 

 

LoadedPlugin

 

protected ClassLoader createClassLoader(Context context, File apk, File libsDir, ClassLoader parent) throws Exception {
    File dexOutputDir = getDir(context, Constants.OPTIMIZE_DIR);
    String dexOutputPath = dexOutputDir.getAbsolutePath();
    DexClassLoader loader = new DexClassLoader(apk.getAbsolutePath(), dexOutputPath, libsDir.getAbsolutePath(), parent); // 将宿主的pathClassLoader设为插件dexClassLoader的parent,利用双亲委派机制实现插件和宿主之间类的互相访问

    if (Constants.COMBINE_CLASSLOADER) {
        DexUtil.insertDex(loader, parent, libsDir);
    }

    return loader;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值