VirtualAPK分析

VirtualAPK分析

VirtualAPK是由滴滴公司發佈的一款插件化的Android APK框架。 其使用的也是動態代理的方式實現的。請看下面官方給的框架圖: avatarjava

從圖中能夠發現,在Core和Manager之間添加了一層代理。android

先說宿主apk

public class VAApplication extends Application {

	    @Override
	    protected void attachBaseContext(Context base) {
	        super.attachBaseContext(base);
	        long start = System.currentTimeMillis();
	        PluginManager.getInstance(base).init();
	        Log.d("ryg", "use time:" + (System.currentTimeMillis() - start));
	    }
	
	    @Override
	    public void onCreate() {
	        super.onCreate();
	    }
	}

首先宿主APK重寫了Application,而且重寫了attachBaseContext方法,此方法是在onCreate以前調用的。在attachBaseContext裏面初始化了PluginManager,先看看在建立PluginManager的時候作了什麼:app

protected PluginManager(Context context) {
        if (context instanceof Application) {
            this.mApplication = (Application) context;
            this.mContext = mApplication.getBaseContext();
        } else {
            final Context app = context.getApplicationContext();
            if (app == null) {
                this.mContext = context;
                this.mApplication = ActivityThread.currentApplication();
            } else {
                this.mApplication = (Application) app;
                this.mContext = mApplication.getBaseContext();
            }
        }
        
        mComponentsHandler = createComponentsHandler();
        hookCurrentProcess();
    }

剛開始是對mApplication和mContext的賦值,這個就很少說了。咱們主要說說hookCurrentProcess()的實現:框架

protected void hookCurrentProcess() {
        hookInstrumentationAndHandler();
        hookSystemServices();
        hookDataBindingUtil();
    }

這裏調用了三個方法,你們該猜到這是代理初始化的地方,先看hookInstrumentationAndHandler()的實現:ide

protected void hookInstrumentationAndHandler() {
        try {
            ActivityThread activityThread = ActivityThread.currentActivityThread();
            Instrumentation baseInstrumentation = activityThread.getInstrumentation();

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

這裏是先獲取了ActivityThreadInstrumenttation的實例,而後建立本身的VAInstrumentation,並將系統的Instrumenttation做爲本身的變量,而後再把本身的VAInstrumentation設置到系統裏面:ui

Reflector.with(activityThread).field("mInstrumentation").set(instrumentation);
Reflector.with(mainHandler).field("mCallback").set(instrumentation);

VAInstrumentation已經繼承了系統的Instrumentation以及Handler.Callback,並在繼承的類裏面重寫了插件apk須要的方法:this

@Override
    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode) {
        injectIntent(intent);
        return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode);
    }

    @Override
    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
        injectIntent(intent);
        return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode, options);
    }

    @Override
    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Fragment target, Intent intent, int requestCode, Bundle options) {
        injectIntent(intent);
        return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode, options);
    }

    @Override
    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, String target, Intent intent, int requestCode, Bundle options) {
        injectIntent(intent);
        return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode, options);
    }
    
    protected void injectIntent(Intent intent) {
        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);
        }
    }

相比系統的調用多了injectIntent(Intent intent)的調用,這個方法主要是尋找插件裏面相應的component,並將intent填寫完整。 這一就能夠完成插件裏面組件的無感知調用。插件

下面再看對service的代理:代理

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

方法其實和上面是相似的,首先獲取系統的ActivityManager,而後再將代理的activityManagerProxy注入到系統裏面:code

Reflector.with(defaultSingleton).field("mInstance").set(activityManagerProxy);

這樣要是再啓動組件的時候就會調用到咱們的代理類裏面,代理類裏面能夠去尋找插件裏面的類。以·startService爲例:

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

再看startDelegateServiceForTarget的實現:

protected ComponentName startDelegateServiceForTarget(Intent target, ServiceInfo serviceInfo, Bundle extras, int command) {
        Intent wrapperIntent = wrapperTargetIntent(target, serviceInfo, extras, command);
        return mPluginManager.getHostContext().startService(wrapperIntent);
    }

這個會啓動插件裏面的Service。

最後一個是數據的綁定,主要對插件裏面資源的注入:

protected void hookDataBindingUtil() {
        Reflector.QuietReflector reflector = Reflector.QuietReflector.on("android.databinding.DataBindingUtil").field("sMapper");
        Object old = reflector.get();
        if (old != null) {
            try {
                Callback callback = Reflector.on("android.databinding.DataBinderMapperProxy").constructor().newInstance();
                reflector.set(callback);
                addCallback(callback);
                Log.d(TAG, "hookDataBindingUtil succeed : " + callback);
            } catch (Reflector.ReflectedException e) {
                Log.w(TAG, e);
            }
        }
    }

將代理的DataBinderMapperProxy設置到系統裏面:

reflector.set(callback);

經過主動的將插件的資源路徑插入到系統的map裏面:

@Override
    public void onAddedLoadedPlugin(LoadedPlugin plugin) {
        try {
            String clsName = "android.databinding.DataBinderMapper_" + plugin.getPackageName().replace('.', '_');
            Log.d(TAG, "Try to find the class: " + clsName);
            Class cls = Class.forName(clsName, true, plugin.getClassLoader());
            Object obj = cls.newInstance();
    
            addMapper((DataBinderMapper) obj);
            
        } catch (Exception e) {
            Log.w(TAG, e);
        }
    }

在獲取的過程當中就能夠從這裏取到資源信息:

@Override
    public int getLayoutId(String tag) {
        int layoutId;
    
        for (DataBinderMapper mapper : getCache()) {
            layoutId = mapper.getLayoutId(tag);
            if (layoutId != 0) {
                return layoutId;
            }
        }
    
        return 0;
    }

插件的加載

插件的加載是在主Activity啓動的時候作了加載:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView = (TextView)findViewById(R.id.textView);
        String cpuArch;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            cpuArch = Build.SUPPORTED_ABIS[0];
        } else {
            cpuArch = Build.CPU_ABI;
        }
        textView.setText(cpuArch);
        Log.d("ryg", "onCreate cpu arch is "+ cpuArch);
        Log.d("ryg", "onCreate classloader is "+ getClassLoader());

        if (hasPermission()) {
            Log.d(TAG,"loadPlugin");

            this.loadPlugin(this);
        } else {
            requestPermission();
        }
    }
private void loadPlugin(Context base) {
        if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
            Toast.makeText(this, "sdcard was NOT MOUNTED!", Toast.LENGTH_SHORT).show();
        }

        PluginManager pluginManager = PluginManager.getInstance(base);
        File apk = new File(Environment.getExternalStorageDirectory(), "Test.apk");
        
        if (apk.exists()) {
            try {
                pluginManager.loadPlugin(apk);
                Log.i(TAG, "Loaded plugin from apk: " + apk);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            try {
                File file = new File(base.getFilesDir(), "Test.apk");
                java.io.InputStream inputStream = base.getAssets().open("Test.apk", 2);
                java.io.FileOutputStream outputStream = new java.io.FileOutputStream(file);
                byte[] buf = new byte[1024];
                int len;
    
                while ((len = inputStream.read(buf)) > 0) {
                    outputStream.write(buf, 0, len);
                }
    
                outputStream.close();
                inputStream.close();
                pluginManager.loadPlugin(file);
                Log.i(TAG, "Loaded plugin from assets: " + file);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

從上面兩段代碼能夠看出,從外部存儲器加載了一個叫Test.apk的文件。這個就是插件的apk,這個APK是一個完成的APK,能夠本身獨立運行。可是在這裏它並無安裝,只是存放在外部存儲器上。

相關文章
相關標籤/搜索