Activity啓動過程重點是應用進程跟AMS進行通訊,處理完成後AMS再交給應用進程繼續處理。須要Hook的點就是在AMS調用以前跟MAS調用完成以後。java
#Instrumentation
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, String resultWho,
Intent intent, int requestCode, Bundle options, UserHandle user) {
.....
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
//調用AMS繼續啓動Activity
int result = ActivityManager.getService()
.startActivityAsUser(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, resultWho,
requestCode, 0, null, options, user.getIdentifier());
//檢查啓動Activity的結果
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
複製代碼
在Activity啓動時,經過Instrumentation的checkStartActivityResult去檢查啓動的Activity的結果,若是插件的Activity未在清單文件中註冊,則會拋出ActivityNotFoundException。須要解決的就是如何經過驗證?android
#ActivityThread
private class H extends Handler {
...
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
//調用了performLaunchActivity方法
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
...
}
...
}
複製代碼
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
//建立要啓動Activity的上下文環境
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
//用類加載器來建立Activity的實例
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);//1
...
} catch (Exception e) {
...
}
...
return activity;
}
複製代碼
須要解決的是將須要加載的插件Activity建立出來git
VirtualApkgithub
在Application進行初始化操做緩存
PluginManager.getInstance(base).init();
複製代碼
在初始化操做時Hook了Instrumentation、ActivityThread的mH類的Callback、IActivityManager、DataBindingUtilbash
#PluginManager
protected PluginManager(Context context) {
......
hookCurrentProcess();
}
protected void hookCurrentProcess() {
hookInstrumentationAndHandler();
hookSystemServices();
hookDataBindingUtil();
}
複製代碼
(1)hookInstrumentationAndHandler架構
#PluginManager
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);
}
}
public class VAInstrumentation extends Instrumentation implements Handler.Callback {......}
複製代碼
(2)hookSystemServicesapp
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);
}
}
public class ActivityManagerProxy implements InvocationHandler {......}
複製代碼
(1)loadPlugin
通常會將某個功能插件生成jar或者apk文件,而後交給主工程經過PluginManager的loadPlugin進行加載框架
#PluginManager
public void loadPlugin(File apk) throws Exception {
......
//將插件文件轉換爲一個LoadedPlugin對象
LoadedPlugin plugin = createLoadedPlugin(apk);
if (null == plugin) {
throw new RuntimeException("Can't load plugin which is invalid: " + apk.getAbsolutePath());
}
//將插件LoadedPlugin存入
this.mPlugins.put(plugin.getPackageName(), plugin);
......
}
複製代碼
(2)構建LoadedPlugin對象ide
#LoadedPlugin
public LoadedPlugin(PluginManager pluginManager, Context context, File apk) throws Exception {
this.mPluginManager = pluginManager;
this.mHostContext = context;
this.mLocation = apk.getAbsolutePath();
this.mPackage = PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
this.mPackage.applicationInfo.metaData = this.mPackage.mAppMetaData;
//建立PackageInfo對象
this.mPackageInfo = new PackageInfo();
this.mPackageInfo.applicationInfo = this.mPackage.applicationInfo;
this.mPackageInfo.applicationInfo.sourceDir = apk.getAbsolutePath();
......
this.mPackageManager = createPluginPackageManager();
this.mPluginContext = createPluginContext(null);
this.mNativeLibDir = getDir(context, Constants.NATIVE_DIR);
this.mPackage.applicationInfo.nativeLibraryDir = this.mNativeLibDir.getAbsolutePath();
//建立Resource
this.mResources = createResources(context, getPackageName(), apk);
//建立ClassLoader
this.mClassLoader = createClassLoader(context, apk, this.mNativeLibDir, context.getClassLoader());
//拷貝so
tryToCopyNativeLib(apk);
// 緩存instrumentations
Map<ComponentName, InstrumentationInfo> instrumentations = new HashMap<ComponentName, InstrumentationInfo>();
for (PackageParser.Instrumentation instrumentation : this.mPackage.instrumentation) {
instrumentations.put(instrumentation.getComponentName(), instrumentation.info);
}
this.mInstrumentationInfos = Collections.unmodifiableMap(instrumentations);
this.mPackageInfo.instrumentation = instrumentations.values().toArray(new InstrumentationInfo[instrumentations.size()]);
// 緩存activities
Map<ComponentName, ActivityInfo> activityInfos = new HashMap<ComponentName, ActivityInfo>();
for (PackageParser.Activity activity : this.mPackage.activities) {
activity.info.metaData = activity.metaData;
activityInfos.put(activity.getComponentName(), activity.info);
}
this.mActivityInfos = Collections.unmodifiableMap(activityInfos);
this.mPackageInfo.activities = activityInfos.values().toArray(new ActivityInfo[activityInfos.size()]);
// 緩存services
Map<ComponentName, ServiceInfo> serviceInfos = new HashMap<ComponentName, ServiceInfo>();
for (PackageParser.Service service : this.mPackage.services) {
serviceInfos.put(service.getComponentName(), service.info);
}
this.mServiceInfos = Collections.unmodifiableMap(serviceInfos);
this.mPackageInfo.services = serviceInfos.values().toArray(new ServiceInfo[serviceInfos.size()]);
// 緩存providers
Map<String, ProviderInfo> providers = new HashMap<String, ProviderInfo>();
Map<ComponentName, ProviderInfo> providerInfos = new HashMap<ComponentName, ProviderInfo>();
for (PackageParser.Provider provider : this.mPackage.providers) {
providers.put(provider.info.authority, provider.info);
providerInfos.put(provider.getComponentName(), provider.info);
}
this.mProviders = Collections.unmodifiableMap(providers);
this.mProviderInfos = Collections.unmodifiableMap(providerInfos);
this.mPackageInfo.providers = providerInfos.values().toArray(new ProviderInfo[providerInfos.size()]);
// Register broadcast receivers dynamically
Map<ComponentName, ActivityInfo> receivers = new HashMap<ComponentName, ActivityInfo>();
for (PackageParser.Activity receiver : this.mPackage.receivers) {
receivers.put(receiver.getComponentName(), receiver.info);
BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance());
for (PackageParser.ActivityIntentInfo aii : receiver.intents) {
this.mHostContext.registerReceiver(br, aii);
}
}
this.mReceiverInfos = Collections.unmodifiableMap(receivers);
this.mPackageInfo.receivers = receivers.values().toArray(new ActivityInfo[receivers.size()]);
// try to invoke plugin's application invokeApplication(); } 複製代碼
建立PackageInfo、Resouces、ClassLoader對象,存儲Instrumentation、Activity、Service、Content Provider等信息
(3)建立ClassLoader對象
#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用來加載插件
DexClassLoader loader = new DexClassLoader(apk.getAbsolutePath(), dexOutputPath, libsDir.getAbsolutePath(), parent);
if (Constants.COMBINE_CLASSLOADER) {
DexUtil.insertDex(loader, parent, libsDir);
}
return loader;
}
#DexUtil
public static void insertDex(DexClassLoader dexClassLoader, ClassLoader baseClassLoader, File nativeLibsDir) throws Exception {
Object baseDexElements = getDexElements(getPathList(baseClassLoader));
Object newDexElements = getDexElements(getPathList(dexClassLoader));
//將宿主自身的dex文件和插件的dex文件合併
Object allDexElements = combineArray(baseDexElements, newDexElements);
Object pathList = getPathList(baseClassLoader);
//經過反射將合併後的dex文件賦值給dexElements
Reflector.with(pathList).field("dexElements").set(allDexElements);
insertNativeLibrary(dexClassLoader, baseClassLoader, nativeLibsDir);
}
複製代碼
<activity android:exported="false" android:name="com.didi.virtualapk.delegate.StubActivity" android:launchMode="standard"/>
<!-- Stub Activities -->
<activity android:exported="false" android:name=".A$1" android:launchMode="standard"/>
<activity android:exported="false" android:name=".A$2" android:launchMode="standard"
android:theme="@android:style/Theme.Translucent" />
......
<!-- Local Service running in main process -->
<service android:exported="false" android:name="com.didi.virtualapk.delegate.LocalService" />
<!-- Daemon Service running in child process -->
<service android:exported="false" android:name="com.didi.virtualapk.delegate.RemoteService" android:process=":daemon">
<intent-filter>
<action android:name="${applicationId}.intent.ACTION_DAEMON_SERVICE" />
</intent-filter>
</service>
<provider
android:exported="false"
android:name="com.didi.virtualapk.delegate.RemoteContentProvider"
android:authorities="${applicationId}.VirtualAPK.Provider"
android:process=":daemon" />
複製代碼
在清單文件中定了各類啓動模式的佔位Activity、Service、ContentProvider
(1)啓動Activity時會走到VAInstrumentation的execStartActivity方法
#VAInstrumentation
@Override
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode) {
//替換爲佔坑的Activity
injectIntent(intent);
//繼續走Instrumentation的execStartActivity方法
return mBase.execStartActivity(who, contextThread, token, target, intent, requestCode);
}
protected void injectIntent(Intent intent) {
//經過intent去匹配PluginManager中Activity的坑位
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);
}
}
複製代碼
(2)將插件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);
}
}
複製代碼
(3)將插件Activity替換爲佔位的Activity進行啓動
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);
//經過launchMode等信息找到合適的佔位Activity
String stubActivity = mStubActivityInfo.getStubActivity(targetClassName, launchMode, themeObj);
Log.i(TAG, String.format("dispatchStubActivity,[%s -> %s]", targetClassName, stubActivity));
intent.setClassName(mContext, stubActivity);
}
複製代碼
接下來拿着佔位的Activity繼續跟AMS進行通訊
(1)VAInstrumentation接收到ApplicationThread發送的消息
#VAInstrumentation
@Override
public boolean handleMessage(Message msg) {
if (msg.what == LAUNCH_ACTIVITY) {
// ActivityClientRecord r
Object r = msg.obj;
try {
Reflector reflector = Reflector.with(r);
Intent intent = reflector.field("intent").get();
intent.setExtrasClassLoader(mPluginManager.getHostContext().getClassLoader());
//獲取ActivityInfo
ActivityInfo activityInfo = reflector.field("activityInfo").get();
if (PluginUtil.isIntentFromPlugin(intent)) {
int theme = PluginUtil.getTheme(mPluginManager.getHostContext(), intent);
if (theme != 0) {
Log.i(TAG, "resolve theme, current theme:" + activityInfo.theme + " after :0x" + Integer.toHexString(theme));
//更換thme
activityInfo.theme = theme;
}
}
} catch (Exception e) {
Log.w(TAG, e);
}
}
return false;
}
複製代碼
(2)經過VAInstrumentation的newActivity建立一個Activity對象
#VAInstrumentation
@Override
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
try {
cl.loadClass(className);
Log.i(TAG, String.format("newActivity[%s]", className));
} catch (ClassNotFoundException e) {
//佔位的Activity不存在,進入catch處理
ComponentName component = PluginUtil.getComponent(intent);
if (component == null) {
return newActivity(mBase.newActivity(cl, className, intent));
}
//獲取目標插件的Activity
String targetClassName = component.getClassName();
LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(component);
if (plugin == null) {
// Not found then goto stub activity.
boolean debuggable = false;
try {
Context context = this.mPluginManager.getHostContext();
debuggable = (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Throwable ex) {
}
if (debuggable) {
throw new ActivityNotFoundException("error intent: " + intent.toURI());
}
Log.i(TAG, "Not found. starting the stub activity: " + StubActivity.class);
return newActivity(mBase.newActivity(cl, StubActivity.class.getName(), intent));
}
//經過Instrumentation的newActivity實現目標Activity的建立
Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);
activity.setIntent(intent);
// for 4.1+
Reflector.QuietReflector.with(activity).field("mResources").set(plugin.getResources());
return newActivity(activity);
}
return newActivity(mBase.newActivity(cl, className, intent));
}
複製代碼
獲取目標Activity的ComponentName
#PluginUtil
public static ComponentName getComponent(Intent intent) {
if (intent == null) {
return null;
}
if (isIntentFromPlugin(intent)) {
return new ComponentName(intent.getStringExtra(Constants.KEY_TARGET_PACKAGE),
intent.getStringExtra(Constants.KEY_TARGET_ACTIVITY));
}
return intent.getComponent();
}
複製代碼
獲取以前存儲到佔位Activity的相關參數信息,並返回ComponentName,繼續執行Activity啓動操做
@Override
public void callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) {
injectActivity(activity);
mBase.callActivityOnCreate(activity, icicle, persistentState);
}
protected void injectActivity(Activity activity) {
final Intent intent = activity.getIntent();
if (PluginUtil.isIntentFromPlugin(intent)) {
Context base = activity.getBaseContext();
try {
LoadedPlugin plugin = this.mPluginManager.getLoadedPlugin(intent);
Reflector.with(base).field("mResources").set(plugin.getResources());
Reflector reflector = Reflector.with(activity);
reflector.field("mBase").set(plugin.createPluginContext(activity.getBaseContext()));
reflector.field("mApplication").set(plugin.getApplication());
// set screenOrientation
ActivityInfo activityInfo = plugin.getActivityInfo(PluginUtil.getComponent(intent));
if (activityInfo.screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
activity.setRequestedOrientation(activityInfo.screenOrientation);
}
// for native activity
ComponentName component = PluginUtil.getComponent(intent);
Intent wrapperIntent = new Intent(intent);
wrapperIntent.setClassName(component.getPackageName(), component.getClassName());
activity.setIntent(wrapperIntent);
} catch (Exception e) {
Log.w(TAG, e);
}
}
}
複製代碼
設置了修改了mResources、mBase(Context)、mApplication對象,最終執行了Activity的onCreate方法
(1)初始化時Hook住Instrumentation、ActivityThread.mH的Callback回調 (2)在宿主工程的清單文件中定義佔位Activity (3)加載插件時,將插件dex文件和宿主dex文件合併,反射賦值給PathList的dexElements,以便被ClassLoader加載 (4)啓動目標Activity過程當中,VAInstrumentation將目標Activity替換爲佔位Activity,並將目標Activity信息做爲參數存儲。從而經過對Activity的校驗,繼而繼續與AMS進行通訊。 (5)AMS處理完成後,傳遞到ApplicationThread中,經過VAInstrumentation攔截到該消息,將佔位Activity替換爲目標Activity,並將目標Activity進行建立,繼而進行後續操做。 (6)設置mResources、mBase(Context)、mApplication對象,最終調用到了Activity的onCreate方法
參考資料: