要把正常的apk当做一个插件启动起来,首先我们拿到这个插件的Launcher Activity,然后启动宿主APP中的Proxy Activity,在这里面调用插件的Activity的生命周期回调。结果会报Null Pointer Exception,原因在于调用插件Activity的onCreate时会调用到super.onCreate,里面需要用到的一些上下文没有初始化过,所以为null。这里确实没有初始化过,我们拿到插件的Activity类后只是调用了其构造函数,并没有初始化上下文。但是如果插件的Activity都是继承自宿主APP提供的一个PluginBaseActivity就不会有这种问题,因为调用super.onCreate时会调到宿主的PluginBaseActivity的onCreate,里面是空的什么也没有做,所以不会崩溃。仔细想想确实该如此,因为super.onCreate在Proxy Activity中已经做过了,所以插件的Activity就不用再做一次了。但是我们这里插件的Activity并没有继承自PluginBaseActivity,所以还是会调到super.onCreate的,怎么解决这个问题呢?
要解决问题需要对Activity启动过程非常熟悉,所以接下来我们仔细研究一下Activity的启动过程。
首先入口是startActivity函数,里面调到了startActivityForResult,接下来会调到mInstrumentation.execStartActivity:
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode) {
..........
ActivityManagerNative.getDefault()
.startActivity(whoThread, intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
null, 0, token, target != null ? target.mEmbeddedID : null,
requestCode, false, false);
..........
}
这里又调到了ActivityManagerNative的startActivity,我们先看看ActivityManagerNative.getDefault是个什么东西:
private static IActivityManager gDefault;
static public IActivityManager getDefault() {
if (gDefault != null) {
return gDefault;
}
IBinder b = ServiceManager.getService("activity");
gDefault = asInterface(b);
return gDefault;
}
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager) obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
原来是先从ServiceManager中查到ActivityManagerService的IBinder,然后封装了一层ActivityManagerProxy,这个IBinder就保存在ActivityManagerProxy的mRemote中。IBinder负责直接与/dev/binder交互,属于”物理层”,而ActivityManagerProxy是在“物理层”上封装的“业务层”,只要准备好业务相关的数据即可,至于怎么给数据发出去是“物理层”的事。
我们看看ActivityManagerProxy中的startActivity的实现:
public int startActivity(IApplicationThread caller, Intent intent,
String resolvedType, Uri[] grantedUriPermissions, int grantedMode,
IBinder resultTo, String resultWho,
int requestCode, boolean onlyIfNeeded,
boolean debug) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeTypedArray(grantedUriPermissions, 0);
data.writeInt(grantedMode);
data.writeStrongBinder(resultTo);
data.writeString(resultWho);
data.writeInt(requestCode);
data.writeInt(onlyIfNeeded ? 1 : 0);
data.writeInt(debug ? 1 : 0);
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
reply.recycle();
data.recycle();
return result;
}
可见这里就是准备好两个parcel,一个是发出去的,一个是接收返回值的,具体和/dev/binder交互是通过mRemote。我们来看看这里发出的数据是在哪里处理的,由于这是ActivityManagerService的IBinder,所以处理肯定是在ActivityManagerService的onTransact中:
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
try {
return super.onTransact(code, data, reply, flags);
} catch (RuntimeException e) {
// The activity manager only throws security exceptions, so let's
// log all others.
if (!(e instanceof SecurityException)) {
Slog.e(TAG, "Activity Manager Crash", e);
}
throw e;
}
}
这里调到了父类的onTransact,而ActivityManagerService的父类是ActivityManagerNative,搞了半天又绕回来了。
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
case START_ACTIVITY_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app = ApplicationThreadNative.asInterface(b);
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
Uri[] grantedUriPermissions = data.createTypedArray(Uri.CREATOR);
int grantedMode = data.readInt();
IBinder resultTo = data.readStrongBinder();
String resultWho = data.readString();
int requestCode = data.readInt();
boolean onlyIfNeeded = data.readInt() != 0;
boolean debug = data.readInt() != 0;
int result = startActivity(app, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug);
reply.writeNoException();
reply.writeInt(result);
return true;
}
..........
}
这里首先从parcel中取出各类数据,然后调用startActivity,这个startActivity的实现又回到了ActivityManagerService中:
public final int startActivity(IApplicationThread caller,
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
return mMainStack.startActivityMayWait(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, null, null);
}
这个mMainStack是个ActivityStack:
final int startActivityMayWait(IApplicationThread caller,
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug, WaitResult outResult, Configuration config) {
intent = new Intent(intent);
ResolveInfo rInfo =
AppGlobals.getPackageManager().resolveIntent(
intent, resolvedType,
PackageManager.MATCH_DEFAULT_ONLY
| ActivityManagerService.STOCK_PM_FLAGS);
ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
if (aInfo != null) {
intent.setComponent(new ComponentName(
aInfo.applicationInfo.packageName, aInfo.name));
}
..........
int res = startActivityLocked(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, aInfo,
resultTo, resultWho, requestCode, callingPid, callingUid,
onlyIfNeeded, componentSpecified);
..........
return res;
}
这里resolveIntent找到匹配的Activity,然后调用startActivityLocked启动Activity:
final int startActivityLocked(IApplicationThread caller,
Intent intent, String resolvedType,
Uri[] grantedUriPermissions,
int grantedMode, ActivityInfo aInfo, IBinder resultTo,
String resultWho, int requestCode,
int callingPid, int callingUid, boolean onlyIfNeeded,
boolean componentSpecified) {
..........
return startActivityUncheckedLocked(r, sourceRecord,
grantedUriPermissions, grantedMode, onlyIfNeeded, true);
}
final int startActivityUncheckedLocked(ActivityRecord r,
ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
int grantedMode, boolean onlyIfNeeded, boolean doResume) {
..........
startActivityLocked(r, newTask, doResume);
return START_SUCCESS;
}
private final void startActivityLocked(ActivityRecord r, boolean newTask,
boolean doResume) {
..........
if (doResume) {
resumeTopActivityLocked(null);
}
}
final boolean resumeTopActivityLocked(ActivityRecord prev) {
..........
startSpecificActivityLocked(next, true, true);
..........
}
private final void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
..........
if (app != null && app.thread != null) {
realStartActivityLocked(r, app, andResume, checkConfig);
return;
}
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false);
}
为了启动一个Activity真是费尽周折,这里最后调到了realStartActivityLocked,如果进程还没启动则还要调用startProcessLocked启动一个新进程。先来看看realStartActivityLocked:
final boolean realStartActivityLocked(ActivityRecord r,
ProcessRecord app, boolean andResume, boolean checkConfig)
throws RemoteException {
..........
app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
System.identityHashCode(r),
r.info, r.icicle, results, newIntents, !andResume,
mService.isNextTransitionForward());
return true;
}
这里scheduleLaunchActivity调到了ApplicationThreadProxy中,这是ApplicationThreadNative的内部类。
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward)
throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
intent.writeToParcel(data, 0);
data.writeStrongBinder(token);
data.writeInt(ident);
info.writeToParcel(data, 0);
data.writeBundle(state);
data.writeTypedList(pendingResults);
data.writeTypedList(pendingNewIntents);
data.writeInt(notResumed ? 1 : 0);
data.writeInt(isForward ? 1 : 0);
mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
}
看来又涉及到了Binder,这里应该是ApplicationThread的service,我们找对应的onTranct,在ApplicationThreadNative中:
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
case SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
Intent intent = Intent.CREATOR.createFromParcel(data);
IBinder b = data.readStrongBinder();
int ident = data.readInt();
ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
Bundle state = data.readBundle();
List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
List<Intent> pi = data.createTypedArrayList(Intent.CREATOR);
boolean notResumed = data.readInt() != 0;
boolean isForward = data.readInt() != 0;
scheduleLaunchActivity(intent, b, ident, info, state, ri, pi,
notResumed, isForward);
return true;
}
..........
}
这里又调到了scheduleLaunchActivity,实现在ApplicationThread中,这是ActivityThread的内部类,继承自ApplicationThreadNative。
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) {
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.activityInfo = info;
r.state = state;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
}
看来是丢到Message Queue中处理了,我们看看handleMessage:
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo);
handleLaunchActivity(r, null);
} break;
.........
}
private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
Activity a = performLaunchActivity(r, customIntent);
..........
}
想必这个performLaunchActivity就是最终启动Activity的地方了,前面的那些过程不是我们关注的重点,这个函数我们需要仔细看看了,因为和插件非常相关。
private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
Activity activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
r.intent.setExtrasClassLoader(cl);
if (r.state != null) {
r.state.setClassLoader(cl);
}
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
ContextImpl appContext = new ContextImpl();
appContext.init(r.packageInfo, r.token, this);
appContext.setOuterContext(activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mConfiguration);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstance,
r.lastNonConfigurationChildInstances, config);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstance = null;
r.lastNonConfigurationChildInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
mInstrumentation.callActivityOnCreate(activity, r.state);
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
}
r.paused = true;
mActivities.put(r.token, r);
return activity;
}
这里通过Instrumentation.newActivity创建一个Activity:
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}
可见,其实就是传入DexClassLoader,先去加载class,然后调用构造函数创建一个Activity,和我们调起插件的Activity的过程是一样的。接下来就是要为这个Activity赋予上下文了,这些我们调起插件的时候是无法做到的,只能用Proxy Activity来代为完成。
好了,回到本文开始时提出的问题,如何解决插件Activity在onCreate时调用super.onCreate由于上下文未初始化导致的NPR。这个问题的解决关键就是为插件Activity赋予上下文,解决方案很简单,首先我们hook掉AMS,将要启动的intent改变为指向ProxyActivity,然后整个流程都会无比顺畅地走下去了,接下来我们要hook掉ActivityThread中的Handler,赋予Handler一个callback就可以拦截到消息,我们在这里判断当消息为LAUNCH_ACTIVITY时来处理我们自己的逻辑,首先通过msg.obj拿到ActivityClientRecord,里面有Intent,ActivityInfo之类的,不过最重要的是LoadedApk,因为新建Activity时就是用这个LoadedApk中的mClassLoader来加载的,而这个LoadedApk是通过getPackageInfoNoCheck函数来获取的,需要反射调用。最后通过反射给插件Apk的DexClassLoader写入LoadedApk中,则万事大吉了。
总的说来,所有的一切都是欺骗系统,在一些关键节点截获数据,精心地修改一番,再顺着正常的流程走下去,最终达到我们需要的效果,而系统毫不知情。不过缺陷就是需要适配不同的sdk版本。每次发布新的sdk,我们都要进行适配,很麻烦。