栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Android插件化开发指南——Hook技术(三)版本适配问题

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android插件化开发指南——Hook技术(三)版本适配问题

文章目录
  • 1. 前言
  • 2. API版本大于26后的AMS
  • 3. API版本大于28后的ActivityThread

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方法。简略版如下:

// API 26 ( >= 26)
// Instrumentation
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版本也放置在这里:

// API 25 ( >= 23)
// Instrumentation
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。

// ActivityManager.java (API 26)

public static IActivityManager getService(){
	return IActivityManagerSingleton.get();
}

这里按照语义还是使用了一个单例对象来进行存储,接着继续看这个单例对象IActivityManagerSingleton的源码。

// ActivityManager.java (API 26)
private static final Singleton IActivityManagerSingleton = 
	new Singleton() {
		@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 {
    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);
    // 获取静态的gDefault对象
    iActivityManagerSingletonValue = getDefault.get(null);
}

// 而实际上AMS在单例Singleton中
Class singletonClazz = Class.forName("android.util.Singleton");
Field mInstance = singletonClazz.getDeclaredField("mInstance");
mInstance.setAccessible(true);
Object amsObj = mInstance.get(iActivityManagerSingletonValue); // AMS
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至于怎么来的,这里就不追踪源码了。现在的为:

// ActivityThread.java  (API 28)
case EXECUTE_TRANSACTION: // 159
    final ClientTransaction transaction = (ClientTransaction) msg.obj;
    mTransactionExecutor.execute(transaction);
    if (isSystem()) {
        // Client transactions inside system process are recycled on the client side
        // instead of ClientLifecycleManager to avoid being cleared before this
        // message is handled.
        transaction.recycle();
    }
    // TODO(lifecycler): Recycle locally scheduled transactions.
    break;

之前的为:

// ActivityThread.java  (API 25)
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这个方法会执行什么?

// TransactionExecutor.java
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相关的东西。

// TransactionExecutor.java
public void executeCallbacks(ClientTransaction transaction) {
        final List callbacks = transaction.getCallbacks();
        if (callbacks == null) {
            // No callbacks to execute, return early.
            return;
        }
       	...
        final int size = callbacks.size();
        for (int i = 0; i < size; ++i) {
            final ClientTransactionItem item = callbacks.get(i);
            ...

但是在executeCallbacks中进行了List callbacks的一个遍历。所以可以看看ClientTransactionItem中存放的什么内容。很遗憾只是一个抽闲的类,没有具体的实现。所以我们可通过ClientTransaction,找找addCallback方法。

// ClientTransaction.java
public void addCallback(ClientTransactionItem activityCallback) {
    if (mActivityCallbacks == null) {
        mActivityCallbacks = new ArrayList<>();
    }
    mActivityCallbacks.add(activityCallback);
}

所以我们只要找到调用addCallback的地方就可以知道ClientTransactionItem中存放的是什么东西。很不幸不太了解AMS的启动,所以这里还是直接给出答案:ActivityStackSupervisor。

// ActivityStackSupervisor.java
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
        System.identityHashCode(r), r.info,
        // TODO: Have this take the merged configuration instead of separate global
        // and override configs.
        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; // ClientTransaction
        Field mActivityCallbacksField = clientTransactionObj.getClass().getDeclaredField("mActivityCallbacks");
        mActivityCallbacksField.setAccessible(true);
        List mActivityCallbacksValue = (List) mActivityCallbacksField.get(clientTransactionObj);

        // 遍历mActivityCallbacks
        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);
                // 从这个newIntent得到真正的意图
                Intent oldIntent = newIntent.getParcelableExtra(ORIGIN_INTENT);
                if(oldIntent != null){
                    // 设置r中的intent为当前的这个oldIntent
                    mIntentField.set(o, oldIntent);
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}


最后测试效果也成功进入到了插件的Activity。

代码地址:https://github.com/baiyazi/pluginLearn/tree/main/demo2


References

  • 代号、标记和细分版本号
  • http://androidxref.com/
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/605978.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号