VirtualAPK分析
VirtualAPK是由滴滴公司发布的一款插件化的Android APK框架。 其使用的也是动态代理的方式实现的。请看下面官方给的框架图:
从图中可以发现,在Core和Manager之间添加了一层代理。
先说宿主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的时候做了什么:
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()
的实现:
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);
}
}
这里是先获取了ActivityThread
的Instrumenttation
的实例,然后创建自己的VAInstrumentation
,并将系统的Instrumenttation
作为自己的变量,然后再把自己的VAInstrumentation
设置到系统里面:
Reflector.with(activityThread).field("mInstrumentation").set(instrumentation);
Reflector.with(mainHandler).field("mCallback").set(instrumentation);
VAInstrumentation
已经继承了系统的Instrumentation
以及Handler.Callback
,并在继承的类里面重写了插件apk需要的方法:
@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
注入到系统里面:
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,可以自己独立运行。但是在这里它并没有安装,只是存放在外部存储器上。