13.源码阅读(启动一个没有注册的Activity为何会抛出异常-have you declared this activity in your AndroidManifest.xml?--android api 23)

简介: app中每一个activity都要在AndroidManifest文件中配置,否则启动会抛出异常Unable to find explicit activity class .

app中每一个activity都要在AndroidManifest文件中配置,否则启动会抛出异常

Unable to find explicit activity class ..; have you declared this activity in your AndroidManifest.xml?

那么我们是否可以启动一个没有注册的activity呢?这就是今天看源码的目的

系统如何检查AndroidManifest中是否注册有一个activity?

在文章03.源码阅读(Activity启动流程--android api 23)中我们已经通过源码知道,启动一个activity调用startActivity后,会进入Instrumentation的方法中

public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
            ......
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            //通过result值进行一些列的异常抛出,上边的那个就是这里抛出的,
            checkStartActivityResult(result, intent);
            ......
    }

已经知道ActivityManagerNative.getDefault()(api26叫ActivityManager.getService())这行代码已经看过多次,但是这次还是要贴出来说一说,因为很重要,它得到的是IActivityManager这个接口的一个实现类----ActivityMangagerService,最终还是要去看ActivityMangagerService中的startActivity

/**
     * Retrieve the system's default/global activity manager.
     */
    static public IActivityManager getDefault() {
        return gDefault.get();
    }

    private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };

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;
        }
    }
}

IActivityManager这个接口是hook拦截activity创建的关键,这里先放着
进入ActivityManagerService的startActivity中,一路点进入会来到ActivityStackSupervisor类中的startActivityLocked方法

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) {
            //activity启动的返回结果
            int err = ActivityManager.START_SUCCESS;
            ......
            if (err == ActivityManager.START_SUCCESS && aInfo == null) {
                    // 就是在这个返回值的情况下会抛出activity未注册的异常
                    err = ActivityManager.START_CLASS_NOT_FOUND;
            }
            ......
}

看一下什么情况下会返回这个,err == ActivityManager.START_SUCCESS 一上来就设置了,所以关键看aInfo,它什么时候会为null,那么现在要回退过去了,看看aInfo是何时赋值的,找到ActivityStackSupervisor中的方法startActivityMayWait

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) {
            ......
            ActivityInfo aInfo =
                resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);
            ......
}
ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
            ProfilerInfo profilerInfo, int userId) {
        ......
        ActivityInfo aInfo;
        try {
            ResolveInfo rInfo =
                AppGlobals.getPackageManager().resolveIntent(
                        intent, resolvedType,
                        PackageManager.MATCH_DEFAULT_ONLY
                                    | ActivityManagerService.STOCK_PM_FLAGS, userId);
            //ActivityInfo是从ResolveInfo中的activityInfo赋值得到的,所以需要找到ResolveInfo如何获取的
            aInfo = rInfo != null ? rInfo.activityInfo : null;
        } catch (RemoteException e) {
            aInfo = null;
        }
        ......
    }

AppGlobals.getPackageManager()最终得到的是IPackageManager的实现类PackageManagerService,来到这里的resolveIntent方法->chooseBestActivity->findPreferredActivity->getActivityInfo

@Override
    public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
        if (!sUserManager.exists(userId)) return null;
        //检查users-permission 
        enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get activity info");
        synchronized (mPackages) {
            PackageParser.Activity a = mActivities.mActivities.get(component);

            if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
            if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
                PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                if (ps == null) return null;
                return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
                        userId);
            }
            if (mResolveComponentName.equals(component)) {
                return PackageParser.generateActivityInfo(mResolveActivity, flags,
                        new PackageUserState(), userId);
            }
        }
        return null;
    }

进入PackageParser中,可以看到,只要Activity a不为null,ActivityInfo就不会为null,那么到了这里又要回退过去看看Activity什么时候为null的

public static final ActivityInfo generateActivityInfo(Activity a, int flags,
            PackageUserState state, int userId) {
        if (a == null) return null;
        if (!checkUseInstalledOrHidden(flags, state)) {
            return null;
        }
        if (!copyNeeded(flags, a.owner, state, a.metaData, userId)) {
            return a.info;
        }
        // Make shallow copies so we can store the metadata safely
        ActivityInfo ai = new ActivityInfo(a.info);
        ai.metaData = a.metaData;
        ai.applicationInfo = generateApplicationInfo(a.owner, flags, state, userId);
        return ai;
    }

来到PackageManagerService类中,可以看到activity是从集合中取出来的

@Override
    public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
        if (!sUserManager.exists(userId)) return null;
        enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get activity info");
        synchronized (mPackages) {
            PackageParser.Activity a = mActivities.mActivities.get(component);

            if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
            if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
                PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                if (ps == null) return null;
                return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId),
                        userId);
            }
            if (mResolveComponentName.equals(component)) {
                return PackageParser.generateActivityInfo(mResolveActivity, flags,
                        new PackageUserState(), userId);
            }
        }
        return null;
    }

PackageParser.Activity a = mActivities.mActivities.get(component);这个集合以ComponentName为key,以这个Activity为value,ComponentName肯定是类的全类名,包名+类名的形式,那么能不能从这个集合中取出这个activity关键就是看这个activity有没有被加入集合了

private final ArrayMap<ComponentName, PackageParser.Activity> mActivities
                = new ArrayMap<ComponentName, PackageParser.Activity>();

可以在PackageManagerService类中找到这个方法

public final void addActivity(PackageParser.Activity a, String type) {
            final boolean systemApp = a.info.applicationInfo.isSystemApp();
            mActivities.put(a.getComponentName(), a);
            ......
        }

这里可以猜测一下,app启动后(或者app安装时就已经扫描保存了)应该会扫描AndroidManifest文件,然后将activity都加入到这个集合,然后启动一个activity的时候如果从这个集合中找不到这个activity,就抛出异常activity未在AndroidManifest中注册,所以肯定有一个扫描AndroidManifest文件的过程,这个过程我们放到下一篇博客中说,今天看到这里基本上已经达到我们的目的了,总结一下

所有在AndroidManifest中注册的activity会被加入一个集合中,这个集合以这个activity的包名+类型为key值存储这个activity,当调用startActivity启动一个activity的时候,会根据这个activity的全类名去这个集合中查找,如果查找不到就表明这个activity没有在AndroidManifest中注册,抛出异常(这里省略了系统扫描AndroidManifest配置文件的过程,以app启动时,activity已经被加入集合为前提)

再次回到我们最初的问题,我们是否可以启动一个没有注册的activity?通过看源码也了解到了,之所以没有注册的activity启动会被抛出异常,是因为有一个监测的过程,如果能绕过这个监测过程,岂不是就可以启动这个activity了

这里采用偷梁换柱的方式来实现,通过动态代理hook Activity的启动过程,启动activity时,系统通过Intent中传递的Activity的ComponentName去集合中查找,只要查找得到就会躲过检查,那么我们的思路是这样的,当我们要启动一个未注册的activity时,传递一个已经注册的activity的componentname作为傀儡,当监测通过的时候,真正要启动这个activity的时候再将这个未注册的替换过去,达到偷梁换柱的目的

具体实现见下一篇博客

相关文章
|
8天前
|
JSON 编译器 开发工具
VS Code阅读Android源码
VS Code阅读Android源码
13 1
|
28天前
|
XML Java Android开发
Android实现自定义进度条(源码+解析)
Android实现自定义进度条(源码+解析)
55 1
|
28天前
|
Java Android开发
Android反编译查看源码
Android反编译查看源码
28 0
|
7天前
|
Java Android开发
Android系统 修改无源码普通应用为默认Launcher和隐藏Settings中应用信息图标
Android系统 修改无源码普通应用为默认Launcher和隐藏Settings中应用信息图标
22 0
|
1天前
|
数据采集 DataWorks 安全
DataWorks产品使用合集之在DataWorks数据服务中,生成API和注册API的主要区别如何解决
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
15 2
|
7天前
|
Linux 开发工具 Android开发
Docker系列(1)安装Linux系统编译Android源码
Docker系列(1)安装Linux系统编译Android源码
14 0
|
7天前
|
安全 Java Shell
Android13 adb input 调试命令使用和源码解析
Android13 adb input 调试命令使用和源码解析
16 0
|
8天前
|
Java API Android开发
[NDK/JNI系列04] JNI接口方法表、基础API与异常API
[NDK/JNI系列04] JNI接口方法表、基础API与异常API
11 0
|
8天前
|
API Android开发
Android Framework增加API 报错 Missing nullability on parameter
Android Framework增加API 报错 Missing nullability on parameter
10 1
|
14天前
|
数据采集 小程序 数据可视化
Java Android原生智慧校园管理系统源码
对班牌的考试模式、班牌模式上课模式进行设置及管理,设置成功后,班牌端将同步应用。
21 0