Hook Context.startActivity和Activity.startActivity
- Context的startActivity其实具体是由ContextImpl去实现的,首先来看下framework的相关源码ContextImpl:
@Override
public void startActivity(Intent intent) {
warnIfCallingFromSystemProcess();
startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
// Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
// generally not allowed, except if the caller specifies the task id the activity should
// be launched in.
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
&& options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity) null, intent, -1, options);
}
由上可知,最终是通过ActivityThread里面的mInstrumentation对象来执行execStartActivity,而ActivityThread是在Android APP启动执行Java main方法的时候创建的ActivityThread:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
上面的ActivityThread thread = new ActivityThread();thread.attach(false);这两句就是创建了一个ActivityThread对象,再看attach方法,这里代码太多,只贴出有用的部分:
private void attach(boolean system) {
sCurrentActivityThread = this;
// 余下省略...
}
sCurrentActivityThread就是ActivityThread对象,一个APP进程只对应一个ActivityThread对象,同时这个对象可由Activity 自身提供的一个静态公有方法来获取到,这个方法在应用层是没办法调用到的,但是可以动过反射method来获取到这个方法并invoke,因为这是一个静态的方法:
private static volatile ActivityThread sCurrentActivityThread;
public static ActivityThread currentActivityThread() {
return sCurrentActivityThread;
}
当然,你也可以通过反射Field来获取到sCurrentActivityThread对象:
private static Object getActivityThread() {
Object target = null;
try {
Class<?> activityThreadCls = Class.forName("android.app.ActivityThread");
Method method = activityThreadCls.getDeclaredMethod("currentActivityThread");
method.setAccessible(true);
// Field sCurrentActivityThreadField = activityThreadCls.getDeclaredField("sCurrentActivityThread");
// sCurrentActivityThreadField.setAccessible(true);
// target = sCurrentActivityThreadField.get(null);
target = method.invoke(null);
return target;
} catch (Exception e) {
throw new RuntimeException(e.toString());
}
}
现在我们来思考一下,想要在startActivity之前做一些事情,然后在startActivity之后再做一些事情其实可以hook startActivity 里面的execStartActivity这个点,因为一个APP进程只对应一个ActivityThread,而且这个对象还是static类型的,比较好通过反射直接获取到具体对象sCurrentActivityThreadField.get(null),这里传null即可,如果不是静态类型的对象,那反射的话这里还要填入具体的对象,由于execStartActivity这个方法是Instrumentation类里面的,而这个类的对象是ActivityThread里面的一个全局变量:
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity) null, intent, -1, options);
你可通过反射方法来获取这个对象,也可通过反射Field来获取到这个对象,然后把这个对象设置成我们自己的代理对象,代理对象其实就是代理Instrumentation,这里用的是静态代理:
public class BaseInstrumentationProxy extends Instrumentation {
private static final String TAG = "BaseInstrumentationProxy";
private Instrumentation mBase;
public final void setInstrumentation(Instrumentation mBase) {
this.mBase = mBase;
}
protected void before() {
}
protected void after() {
}
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
if (mBase == null) {
throw new RuntimeException("mBase is null");
}
before();
try {
Method execStartActivity = Instrumentation.class.getDeclaredMethod(
"execStartActivity",
Context.class, IBinder.class, IBinder.class, Activity.class,
Intent.class, int.class, Bundle.class);
execStartActivity.setAccessible(true);
ActivityResult activityResult = (ActivityResult) execStartActivity.invoke(mBase, who, contextThread, token, target, intent, requestCode, options);
after();
return activityResult;
} catch (Exception e) {
throw new RuntimeException("no support hook:" + e.toString());
}
}
}
这个代理对象其实就是在execStartActivity之前做一些自定义的操作,然后在execStartActivity之后做一些自定义的操作,而它实际上是通过反射来执行真正的Instrumentation对象的execStartActivity方法:
public static void hookStartActivityFormContext(BaseInstrumentationProxy baseInstrumentationProxy) {
Object activityThread = InstrumentationHooker.getActivityThread();
try {
Field mInstrumentationField = activityThread.getClass().getDeclaredField("mInstrumentation");
mInstrumentationField.setAccessible(true);
baseInstrumentationProxy.setInstrumentation((Instrumentation) mInstrumentationField.get(activityThread));
mInstrumentationField.set(activityThread, baseInstrumentationProxy);
} catch (Exception e) {
e.printStackTrace();
}
}
这里注意一点,ContextImpl是调用ActivityThread对象里面的Instrumentation来执行execStartActivity,而Activity是调用自身的Instrumentation来执行execStartActivity,实际上Activity里面的Instrumentation是在Activity创建的时候由ActivityThread对象里面的Instrumentation传进来的,Activity里面的Instrumentation对象引用也是指向ActivityThread里面的Instrumentation对象,但是我们现在是把ActivityThread对象引用替换成我们自定义的代理对象,而Activity里面的Instrumentation依然还是原来的,并没有被替换,所以如果要Hook Activity的startActivity是要把Activity里面的Instrumentation对象设置成我们自定义的代理对象:
public static void hookStartActivityFormContext(BaseInstrumentationProxy baseInstrumentationProxy) {
Object activityThread = InstrumentationHooker.getActivityThread();
try {
Field mInstrumentationField = activityThread.getClass().getDeclaredField("mInstrumentation");
mInstrumentationField.setAccessible(true);
baseInstrumentationProxy.setInstrumentation((Instrumentation) mInstrumentationField.get(activityThread));
mInstrumentationField.set(activityThread, baseInstrumentationProxy);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void hookStartActivityFromActivity(Activity activity, BaseInstrumentationProxy baseInstrumentationProxy) {
Field mInstrumentationField = null;
try {
mInstrumentationField =Activity.class.getDeclaredField("mInstrumentation");
mInstrumentationField.setAccessible(true);
baseInstrumentationProxy.setInstrumentation((Instrumentation) mInstrumentationField.get(activity));
mInstrumentationField.set(activity, baseInstrumentationProxy);
} catch (Exception e) {
e.printStackTrace();
}
}
这样就hook成功了,然后在startActivity之前调用一下以上两个方法就可以了:
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InstrumentationHooker.hookStartActivityFromActivity(this, new BaseInstrumentationProxy(){
@Override
protected void before() {
Log.d(TAG, "hookStartActivityFromActivity before");
}
@Override
protected void after() {
Log.d(TAG, "hookStartActivityFromActivity after");
}
});
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
}
}
public class SecondActivity extends Activity {
private static final String TAG = "SecondActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
InstrumentationHooker.hookStartActivityFormContext(new BaseInstrumentationProxy(){
@Override
protected void before() {
Log.d(TAG, "hookStartActivityFromActivity before");
}
@Override
protected void after() {
Log.d(TAG, "hookStartActivityFromActivity after");
}
});
Intent intent = new Intent(this, ThirdActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplicationContext().startActivity(intent);
}
}
最后运行的结果如下:
09-28 23:15:52.843 22148-22148/com.lhd.hooktest D/MainActivity: hookStartActivityFromActivity before
09-28 23:15:52.849 22148-22148/com.lhd.hooktest D/MainActivity: hookStartActivityFromActivity after
09-28 23:15:52.935 22148-22148/com.lhd.hooktest D/SecondActivity: hookStartActivityFormContext before
09-28 23:15:52.943 22148-22148/com.lhd.hooktest D/SecondActivity: hookStartActivityFormContext after
网友评论