1. 前言
在Android插件化开发指南——Hook技术(一)【长文】对AMS 进行Hook 的时候,我重新创建了一个低版本的项目,并创建了对应的模拟器来运行程序,以得到AMS 对象。当时所使用的API 版本为30 ,所以其实可以知道25 其实和30 的AMS 的Hook 会略有不同。实际上两个分水岭为23 和26 。前面几篇博客中所给出的Hook 得到AMS对象的为23 到26 之间的版本。对于大于等于26 的版本需要重新做适配。
再次查看Android 版本和API 级别的对应关系:代号、标记和细分版本号 我们知道从Oreo 即Android 版本为8.0.0 开始,API 级别就为 26 。且http://androidxref.com/也支持查看对应的高版本系统的源码,所以这里就尝试以源码网站中的Oreo - 8.1.0_r33 为例开始。
2. API版本大于26后的AMS
对于前面的流程从startActivity->startActivityForResult->Instrumentation->ams.startActivity 的这个流程这里不再继续看源码进行追踪。这里直接找到Instrumentation 这个类,对应的execStartActivity 方法。简略版如下:
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
...
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
不妨将之前博客中写的API 25 版本也放置在这里:
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
...
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
很明显这里得到AMS 对象不再是使用ActivityManagerNative.getDefault() ,而是ActivityManager.getService() 方法。所以这里需要继续追踪一下ActivityManager 这个类,ActivityManager.java。
public static IActivityManager getService(){
return IActivityManagerSingleton.get();
}
这里按照语义还是使用了一个单例对象来进行存储,接着继续看这个单例对象IActivityManagerSingleton 的源码。
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
portected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
对于这个单例属性只需要执行它的create 方法就可以创建对应的AMS 。所以我们需要得到这个单例对象。类似的,由于其包装在Singleton 类中,所以还是需要继续查看Singleton 的源码:
public abstract class Singleton<T> {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
这个类和前面的没有什么区别。所以这里还是首先得到这个单例类对象,然后通过create 方法来获取到AMS 对象。即:
Object iActivityManagerSingletonValue = null;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
Field iActivityManagerSingletonField = ActivityManager.class.getDeclaredField("IActivityManagerSingleton");
iActivityManagerSingletonField.setAccessible(true);
iActivityManagerSingletonValue = iActivityManagerSingletonField.get(null);
}else{
Class<?> aClass = Class.forName("android.app.ActivityManagerNative");
Field getDefault = aClass.getDeclaredField("gDefault");
getDefault.setAccessible(true);
iActivityManagerSingletonValue = getDefault.get(null);
}
Class<?> singletonClazz = Class.forName("android.util.Singleton");
Field mInstance = singletonClazz.getDeclaredField("mInstance");
mInstance.setAccessible(true);
Object amsObj = mInstance.get(iActivityManagerSingletonValue);
3. API版本大于28后的ActivityThread
需要注意的是在API 版本大于28 后,ActivityThread 中对消息的处理也不一样。注意到API 级别28 对应的Android 版本为9 。刚好在http://androidxref.com/中提供了Pie - 9.0.0_r3 系统的源码。但是这个网站用来查看代码实在是太卡了,所以这里还是修改grdle 文件,然后直接在本地查看:
android {
compileSdkVersion 28
buildToolsVersion "28.0.6"
defaultConfig {
applicationId "com.weizu.myapplication"
minSdkVersion 28
targetSdkVersion 28
versionCode 1
versionName "1.0"
...
在我本地其实是下载了对应的SDK ,如下图: 如果需要下载对应版本的SDK ,直接打开Seetings->Android SDK 然后选择下载即可。这里我选择28 。
找到ActivityThread 的消息处理对象mH ,可以发现消息标识的编码已经没有了之前的100 ,也就是没有了LAUNCH_ACTIVITY 。这里对应的修改为了EXECUTE_TRANSACTION 至于怎么来的,这里就不追踪源码了。现在的为:
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
if (isSystem()) {
transaction.recycle();
}
break;
之前的为:
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);
} break;
且在之前的代码中我们做Hanlder.Callback 的时候,反射的是ActivityClientRecord 这个对象,然后从r 对象中得到其属性intent ,然后将这个intent 进行替换。
这里类似的首先需要追踪一下TransactionExecutor 这个类,然后看下execute 这个方法会执行什么?
public void execute(ClientTransaction transaction) {
final IBinder token = transaction.getActivityToken();
log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);
executeCallbacks(transaction);
executeLifecycleState(transaction);
mPendingActions.clear();
log("End resolving transaction");
}
继续查看executeCallbacks 方法,但是在这个方法也没有找到和Intent 相关的东西。
public void executeCallbacks(ClientTransaction transaction) {
final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
if (callbacks == null) {
return;
}
...
final int size = callbacks.size();
for (int i = 0; i < size; ++i) {
final ClientTransactionItem item = callbacks.get(i);
...
但是在executeCallbacks 中进行了List<ClientTransactionItem> callbacks 的一个遍历。所以可以看看ClientTransactionItem 中存放的什么内容。很遗憾只是一个抽闲的类,没有具体的实现。所以我们可通过ClientTransaction ,找找addCallback 方法。
public void addCallback(ClientTransactionItem activityCallback) {
if (mActivityCallbacks == null) {
mActivityCallbacks = new ArrayList<>();
}
mActivityCallbacks.add(activityCallback);
}
所以我们只要找到调用addCallback 的地方就可以知道ClientTransactionItem 中存放的是什么东西。很不幸不太了解AMS 的启动,所以这里还是直接给出答案:ActivityStackSupervisor 。
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
r.persistentState, results, newIntents, mService.isNextTransitionForward(),
profilerInfo));
对应的LaunchActivityItem 的类中就有一个mIntent 属性:
public class LaunchActivityItem extends ClientTransactionItem {
private Intent mIntent;
...
}
所以流程为:
- 获取到
mActivityCallbacks 集合; - 遍历
mActivityCallbacks ,得到LaunchActivityItem - 替换
LaunchActivityItem 中的Intent
因为mActivityCallbacks 属于ClientTransaction ,首先是拿到ClientTransaction 实例对象。在ActivityThread 的case EXECUTE_TRANSACTION: 处理中给了从消息中得到ClientTransaction 对象的方法,即:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
最终的代码如下:
private void handleLaunchActivity28(Message message) {
try {
Object clientTransactionObj = message.obj;
Field mActivityCallbacksField = clientTransactionObj.getClass().getDeclaredField("mActivityCallbacks");
mActivityCallbacksField.setAccessible(true);
List mActivityCallbacksValue = (List) mActivityCallbacksField.get(clientTransactionObj);
for (Object o : mActivityCallbacksValue) {
if(o.getClass().getName().equals("android.app.servertransaction.LaunchActivityItem")){
Field mIntentField = o.getClass().getDeclaredField("mIntent");
mIntentField.setAccessible(true);
Intent newIntent = (Intent) mIntentField.get(o);
Intent oldIntent = newIntent.getParcelableExtra(ORIGIN_INTENT);
if(oldIntent != null){
mIntentField.set(o, oldIntent);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
最后测试效果也成功进入到了插件的Activity 。
代码地址:https://github.com/baiyazi/pluginLearn/tree/main/demo2
References
|