源码分析 — Activity的清单注册校验及动态注入

一、概述

一般情况下,Activity的启动都需要先在清单文件AndroidManifest.xml中注册后,才能使用。而目前流行的插件化则是通过在底座清单文件中提前占坑的方式来达到启动插件中Activity的目的。但是,插件中的Activity并没有在底座的清单文件中注册。那么,他们是如何达到这个目的的呢?

说明:本文是从插件化中剥离出来的一个知识点;

本文涉及到的技术点:
PackageManagerService(二)之resolveIntent()

二、示例

new TextView(context).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // MainActivity注册过,SecondActivity没有注册过;
        Intent intent = new Intent(MainActivity.this, SecondActivity.class);
        startActivity(intent);
    }
});

结果:

Process: com.example.demo, PID: 3773
android.content.ActivityNotFoundException:
Unable to find explicit activity class {com.example.demo/com.example.demo.SecondActivity};
have you declared this activity in your AndroidManifest.xml?
…略…

三、时序图

这里写图片描述

四、源码分析

// Activity.class
// 调用startActivity(intent),经多次跳转后,最终调用此方法;
public void startActivityForResult(
            @RequiresPermission Intent intent, 
            int requestCode, @Nullable Bundle options) {

    options = transferSpringboardActivityOptions(options);
    // 注意:Instrumentation该类是Activity的实际操作类
    Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(
            this, mMainThread.getApplicationThread(), mToken, this,
            intent, requestCode, options);

    // ...代码省略...
}

// Instrumentation.class
public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    // ...代码省略...
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess(who);

        // ActivityManagerNative涉及到IBinder通信机制 (看下面小结)
        // 结论:最终返回 result = = ActivityManager.START_CLASS_NOT_FOUND;
        int result = ActivityManagerNative.getDefault()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);

        // 校验:根据返回的result值校验当前Activity是否有异常;
        checkStartActivityResult(result, intent);

    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

// Instrumentation.class
public static void checkStartActivityResult(int res, Object intent) {
    // 启动成功,就直接return
    if (res >= ActivityManager.START_SUCCESS) {
        return;
    }

    switch (res) {
        case ActivityManager.START_INTENT_NOT_RESOLVED:
        case ActivityManager.START_CLASS_NOT_FOUND:
            if (intent instanceof Intent && ((Intent)intent).getComponent() != null) {
                // 抛出这一行异常
                throw new ActivityNotFoundException(
                        "Unable to find explicit activity class "
                        + ((Intent)intent).getComponent().toShortString()
                        + "; have you declared this activity 
                        + in your AndroidManifest.xml?");
            }
        // ...代码省略...
    }
}

小结:

  1. 涉及三个类:ActivityManagerNative、ActivityManagerProxy、ActivityManagerService(AMS) ,这三个类都实现了IActivityManager接口;
  2. 记住:调用ActivityManagerNative.getDefault().startActivity(),最终会调用Service(AMS)端的startActivity()方法;
/*
 * 由上面的小结可知,ActivityManagerNative.getDefault().startActivity()最终调用了 
 * ActivityManagerService.startActivity()方法;
 */

// ActivityManagerService.class
public final int startActivity(IApplicationThread caller, String callingPackage,
     Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
     int startFlags, ProfilerInfo profilerInfo, Bundle options) {
        //调用startActivityAsUser方法
    return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
        resultWho, requestCode, startFlags, profilerInfo, options,
        UserHandle.getCallingUserId());
}

// ActivityManagerService.class
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
    Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
    int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {

    enforceNotIsolatedCaller("startActivity");
    userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
            false, ALLOW_FULL_ONLY, "startActivity", null);
    // TODO: Switch to user app stacks here.
    // 调用ActivityStackSupervisor.startActivityMayWait()方法
    return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
            resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
            profilerInfo, null, null, options, false, userId, null, null);
}

// ActivityStackSupervisor.class
final int startActivityMayWait(IApplicationThread caller, int callingUid,
        String callingPackage, Intent intent, String resolvedType,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
        IBinder resultTo, String resultWho, int requestCode, int startFlags,
        ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
        Bundle options, boolean ignoreTargetSecurity, int userId,
        IActivityContainer iContainer, TaskRecord inTask) {
    // ...代码省略...

    // Collect information about the target of the Intent.
    // 这一步是关键,去解析目标Activity的相关Intent信息;(由下面一部分的代码可知,aInfo=null)
    ActivityInfo aInfo = resolveActivity(intent, resolvedType, 
                                    startFlags, profilerInfo, userId);

    // ...代码省略...
    synchronized (mService) {     
        // ...省略一大段代码...

        // 将aInfo=null传入startActivityLocked(),最终返回 res = ActivityManager.START_CLASS_NOT_FOUND;
        int res = startActivityLocked(caller, intent, resolvedType, aInfo,
                voiceSession, voiceInteractor, resultTo, resultWho,
                requestCode, callingPid, callingUid, callingPackage,
                realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
                componentSpecified, null, container, inTask);

        // ...省略一大段代码...

        return res; //res = ActivityManager.START_CLASS_NOT_FOUND
    }
}

// ActivityStackSupervisor.class
ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
            ProfilerInfo profilerInfo, int userId) {
    // Collect information about the target of the Intent.
    ActivityInfo aInfo;
    try {
        /* 
         * 这里AppGlobals.getPackageManager()获取的是IPackageManager实例对象,
         * 真正的实现类是PackageManagerService;
         * 
         * 关于IPackageManager.resolveIntent(),
         * 请参考《PackageManagerService(二)之resolveIntent()》
         * http://blog.csdn.net/love667767/article/details/79585080
         */
        ResolveInfo rInfo = AppGlobals.getPackageManager()
                .resolveIntent(intent, resolvedType,
                        PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS,
                        userId);
        // 由于Activity没有在清单文件注册过,因此最终返回的aInfo=null
        aInfo = rInfo != null ? rInfo.activityInfo : null;
    } catch (RemoteException e) {
        aInfo = null;
    }
    // ...省略一大段代码...

    return aInfo;
}

// ActivityStackSupervisor.class
final int startActivityLocked(IApplicationThread caller,
        Intent intent, String resolvedType, ActivityInfo aInfo,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
        IBinder resultTo, String resultWho, int requestCode,
        int callingPid, int callingUid, String callingPackage,
        int realCallingPid, int realCallingUid, int startFlags, Bundle options,
        boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
        ActivityContainer container, TaskRecord inTask) {

    // 初始状态是:START_SUCCESS
    int err = ActivityManager.START_SUCCESS;

    // ...省略一大段代码...

    // intent.getComponent()是有值的
    if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
        // We couldn't find a class that can handle the given Intent.
        // That's the end of that!
        err = ActivityManager.START_INTENT_NOT_RESOLVED;
    }
    // 在这里aInfo == null 满足条件,因此最终返回err = ActivityManager.START_CLASS_NOT_FOUND 
    if (err == ActivityManager.START_SUCCESS && aInfo == null) {
        // We couldn't find the specific class specified in the Intent.
        // Also the end of the line.
        err = ActivityManager.START_CLASS_NOT_FOUND;
    }

    err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
            startFlags, true, options, inTask);

    // 最终返回ActivityManager.START_CLASS_NOT_FOUND 
    return err;
}

五、实践:如何绕过校验,动态注入Activity?

5.1 原理分析

由上面的源码分析可知:

  1. Activity的清单校验逻辑在Instrumentation.execStartActivity() 中进行的;
  2. Instrumentation 的实例在ActivityThread中;
  3. Activity 必须要在清单文件注册的;

方案:
占坑方案:既然Activity要注册过,那我们就在清单文件中预先注册一个占坑的Activity;
作用:在Activity校验的时候,将真正被启动的Activity替换成这个占坑的Activity,从而完成Activity校验的过程。
技术实现:利用清单文件合并的特点,预先在一个清单文件中写入占坑的Activity;

问题:
在校验之前,将被启动的Activity替换成占坑的Activity后,那真正启动的不就是占坑的Activity了吗?
如何启动我们真正需要的Activity呢?

分析:
其实,Activity的创建过程分为两部分:

  1. Activity的校验;(Instrumentation.execStartActivity() )
  2. Activity实例的创建;(Instrumentation.newActivity() )

方案:(Hook技术)

  1. 通过反射技术来替换 ActivityThread 中的 Instrumentation 实例对象,并重写其内部的 execStartActivity()、newActivity() 方法;
  2. 在重写的execStartActivity() 方法中将目标Activity替换成占坑的Activity,并调用系统自身的Instrumentation.execStartActivity(),从而完成Activity的校验过程;
  3. 在重写的newActivity() 方法中将占坑Activity的还原成目标Activity,并调用系统自身的Instrumentation.newActivity() ,最终完成目标Activity的创建;
5.2 实践

说明:

  1. MainActivity、PlaceHolderActivity 注册、SecondActivity未注册 ;
  2. PlaceHolderActivity 是占坑的Activity ;
  3. HookInstrumentation 继承Instrumentation,并重写execStartActivity()、newActivity() 方法;
  4. Application.attachBaseContext() 中将HookInstrumentation通过反射赋值给ActivityThread.mInstrumentation 字段 ;

代码实现:

public class VirtualApkApplication extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        init();
    }

    /**
     * 1.反射获取ActivityThread对象;
     * 2.反射获取ActivityThread对象中的mInstrumentation对象;
     * 3.创建自定义的HookInstrumentation对象,
     *   并通过反射将自定义的HookInstrumentation对象赋值给ActivityThread.mInstrumentation;
     */
    private void init() {
        try {
            Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
            Object activityThread = ReflectUtil.getField(activityThreadClazz, 
                                                null, "sCurrentActivityThread");

            Instrumentation sInstrumentation = (Instrumentation) invoke(
                                                activityThread.getClass(), 
                                                activityThread, 
                                                "getInstrumentation");

            final HookInstrumentation instrumentation = new HookInstrumentation(sInstrumentation);
            ReflectUtil.setField(activityThread.getClass(), activityThread, 
                                                "mInstrumentation", instrumentation);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Object invoke(Class clazz, Object target, String name, Object... args)
            throws Exception {
        Class[] parameterTypes = null;
        if (args != null) {
            parameterTypes = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                parameterTypes[i] = args[i].getClass();
            }
        }
        Method method = clazz.getDeclaredMethod(name, parameterTypes);
        method.setAccessible(true);
        return method.invoke(target, args);
    }
}
public class HookInstrumentation extends Instrumentation {

    private static final String PACKAGE_NAME = "package_name";
    private static final String CLASS_NAME = "class_name";

    Instrumentation mBase;

    public HookInstrumentation(Instrumentation base) {
        this.mBase = base;
    }

    // 这个方法被隐藏了
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {

        if (intent.getComponent() != null) {
            intent.putExtra(PACKAGE_NAME, intent.getComponent().getPackageName());
            intent.putExtra(CLASS_NAME, intent.getComponent().getClassName());
            // 将目标Activity替换成占位Activity;
            intent.setClassName(intent.getComponent().getPackageName(), 
                                    PlaceHolderActivity.class.getName());
        }

        ActivityResult result = null;
        try {
            Class[] parameterTypes = {Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class,
                    int.class, Bundle.class};

            result = (ActivityResult) invoke(Instrumentation.class, mBase,
                    "execStartActivity", parameterTypes,
                    who, contextThread, token, target, intent, requestCode, options);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }


    @Override
    public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {

        String targetClassName = intent.getStringExtra(CLASS_NAME);
        if (!TextUtils.isEmpty(targetClassName)) {
            // 打开目标Activity
            Activity activity = mBase.newActivity(cl, targetClassName, intent);
            activity.setIntent(intent);
            return activity;
        }
        return mBase.newActivity(cl, className, intent);
    }

    public static Object invoke(Class clazz, Object target, String name, Class[] parameterTypes, Object... args)
            throws Exception {
        Method method = clazz.getDeclaredMethod(name, parameterTypes);
        method.setAccessible(true);
        return method.invoke(target, args);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Activity启动模式是Android应用程序中非常重要的概念,它决定了Activity的启动方式和生命周期的管理方式。在Android中,Activity启动模式主要有以下几种: 1. standard:标准模式。默认情况下,每次启动Activity时都会创建一个新实例,并放入任务栈中。如果该Activity已经存在任务栈中,则会将该Activity放到栈顶,并重新调用其onCreate()方法。 2. singleTop:栈顶复用模式。如果新启动的Activity已经存在任务栈的栈顶,则不会创建新实例,而是将已有的实例作为当前任务的Activity,并调用其onNewIntent()方法。如果新启动的Activity不在栈顶,则会创建新实例,并将其放到任务栈的栈顶。 3. singleTask:栈内复用模式。如果新启动的Activity已经存在任务栈中,则不会创建新实例,而是将已有的实例作为当前任务的Activity,并将其上面的Activity全部出栈,调用其onNewIntent()方法。如果新启动的Activity不存在任务栈中,则会创建新实例,并放到任务栈的栈顶。 4. singleInstance:单例模式。在一个新的任务栈中创建Activity,并且该任务栈中只有该Activity实例。如果该Activity已经存在于其他任务栈中,则会将该任务栈中的该Activity实例移动到新的任务栈中。 下面是Activity源码分析: 1. standard模式 在Activity源码中,标准模式是默认的启动模式。当我们使用startActivity()方法启动一个Activity时,会调用ActivityStackSupervisor类中的startActivityLocked()方法。在该方法中,会通过ActivityStack类的findTaskLocked()方法查找是否存在当前Activity所在的任务栈。如果存在,则会将当前Activity放到该任务栈的栈顶,并调用其onCreate()方法。如果不存在,则会创建一个新的任务栈,并将当前Activity放到该任务栈的栈顶。 2. singleTop模式 当我们在Manifest文件中设置Activity的启动模式为singleTop时,会在ActivityInfo中保存该信息。在ActivityStackSupervisor类的startActivityLocked()方法中,会通过ActivityStack类的findTaskLocked()方法查找是否存在当前Activity所在的任务栈,并判断当前Activity是否在栈顶。如果在栈顶,则会调用其onNewIntent()方法。如果不在栈顶,则会创建一个新的实例,并放到该任务栈的栈顶。 3. singleTask模式 当我们在Manifest文件中设置Activity的启动模式为singleTask时,会在ActivityInfo中保存该信息。在ActivityStackSupervisor类的startActivityLocked()方法中,会通过ActivityStack类的findTaskLocked()方法查找是否存在当前Activity所在的任务栈。如果存在,则会找到该任务栈中的栈顶Activity,并将其上面的所有Activity出栈。然后将当前Activity放到该任务栈的栈顶,并调用其onNewIntent()方法。如果不存在,则会创建一个新的任务栈,并将当前Activity放到该任务栈的栈顶。 4. singleInstance模式 当我们在Manifest文件中设置Activity的启动模式为singleInstance时,会在ActivityInfo中保存该信息。在ActivityStackSupervisor类的startActivityLocked()方法中,会创建一个新的任务栈,并将当前Activity放到该任务栈的栈顶。如果该Activity已经存在于其他任务栈中,则会将该任务栈中的该Activity实例移动到新的任务栈中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值