在真正分析插件化技术前,我们必须了解一些必要的关于Android四大组件的相关知识。
以Activity为例,我们需要了解Activity启动过程,才能有效的进行Hook实现插件化。
以Android 8.1为例
我们启动一个Activity通常会使用startActivity方法,但是在Activity内部最终都会调用startActivityForResult方法
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
......
} else {
......
}
}
在这个方法的内部实际是调用Instrumentation类的execStartActivity方法。
在这里我们遇到了三个非常重要的类。
- Instrumentation : 随着Activity内部会持有一个对Instrumentation的引用,
- ActivityThread : 主线程,在app启动的时候创建的,代表了Android应用程序,里面包含了Main函数
- ApplicationThread : App端在system进程的代理类(涉及到Binder机制)
接着会调用Instrumentation内部的execStartAcivitiesAsUser方法
public void execStartActivitiesAsUser(Context who, IBinder contextThread,
IBinder token, Activity target, Intent[] intents, Bundle options,
int userId) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
......
try {
.......
int result = ActivityManager.getService()
.startActivities(whoThread, who.getBasePackageName(), intents, resolvedTypes,
token, options, userId);
checkStartActivityResult(result, intents[0]);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
然后我们进入ActivityManager内部,getService实际返回的是一个单例对象,持有AMS在app端的远程代理对象。
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
所以从这里就从app进程进入到system进程。
AMS里的代码很多,逻辑非常复杂,只要知道AMS会对传递过来的Intent进行检查,如果需要启动的Activity没有在manifest里申明,就会把错误码返回给Instrumentation,从而Instrumentation就会抛出经典的ActivityNofFoundException异常。
如果通过AMS的检查,那么会回调ApplicationThread的scheduleLauncherActivity方法
@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
ActivityClientRecord r = new ActivityClientRecord();
......
sendMessage(H.LAUNCH_ACTIVITY, r);
}
给H发送了一个LAUNCH_ACTIVITY的消息
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);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
就正式开始了Activity的启动过程。
从以上的分析,我们是无法启动一个没有manifest注册的Activity,但是插件化技术却可以绕过AMS的检查达到这样一个目的。