PMS工作原理解析——app目录的解析流程(Android12)

1,897 阅读17分钟

Android的知识体系搭建

一 概述

上一篇 PackageManagerService 源码解析 详细解析了 PackageManagerService 的启动流程。我们也大致知道了 PMS 启动时的工作原理。

但是,里面有一块非常重要的部分我们当时忽略了,那就是 PMS 解析安装目录的过程。

  • PMS 是如何解析我们安装到 Android 系统中的应用的?
  • 我们写的那些代码文件,PMS 是如何将它变成四大组件启动的?

今天,我们就带着这两个问题,来了解一下 PMS 启动的,解析应用的原理。

二 PMS 的启动

在上一篇启动篇中,我们介绍了 PMS 的初始化分为 5 个流程。其中的流程 2 和流程 3 分别是解析系统文件目录和解析用户安装目录 /data/app。

如下图,就是一个安卓手机的目录情况,今天我们要学习的,就是 PMS 解析 /data/app 目录的原理。这个目录就是我们应用安装的位置。

Android目录.png

2.1 构造函数

书接上回,我们介绍了 PMS 构造函数中的五个步骤,说到了 scanDirTracedLI 这个函数,它的作用就是扫描文件目录。

[frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]
// 如果不是只加载核心apk,一般情况都是false
if (!mOnlyCore) {
	EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
			SystemClock.uptimeMillis());
        // 扫描 App 安装目录
	scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
			packageParser, executorService);

}

我们继续说说这个函数扫描的流程。

2.2 scanDirTracedLI

注意,这里传入了一个参数 packageParser,类型是 PackageParser2。具体的构造可以看 PackageManagerService 源码解析

[frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
		long currentTime, PackageParser2 packageParser, ExecutorService executorService) {

    try {
        scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
    }
}

2.3 scanDirLI

ParallelPackageParser 的作用其实就是对 PackageParser2 的包装,真正工作的还是 PackageParser2。

class ParallelPackageParser {
    private final PackageParser2 mPackageParser;
    private final ExecutorService mExecutorService;

    ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService) {
        mPackageParser = packageParser;
        mExecutorService = executorService;
    }
}

这种包装的方式主要是用于解耦,后续如果想对 PackageParser2 进行升级,例如改成 PackageParser3 也是非常容易的。

2.4 scanDirLI

[frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
            PackageParser2 packageParser, ExecutorService executorService) {

    // 要扫描的文件数组
    final File[] files = scanDir.listFiles();
    if (ArrayUtils.isEmpty(files)) {
        return;
    }

    // 包解析器
    // 注意,这里的 packageParser 是 PackageParser2,
    // 这是用一个 ParallelPackageParser 包裹 PackageParser2
ParallelPackageParser parallelPackageParser = new ParallelPackageParser(packageParser, executorService);

    // 开始扫描
    int fileCount = 0;
    for (File file : files) {
        final boolean isPackage = (isApkFile(file) || file.isDirectory())
                && !PackageInstallerService.isStageName(file.getName());
        if (!isPackage) {
            // 如果不满足 isPackage 的条件就不扫描
            // 如果是安装的 apk,那么一定满足条件
            continue;
        }

        // 把apk 文件添加到了 PackageInstallerService 的扫描队列中
        // PackageInstallerService 中有一个 mQueue 对象
        parallelPackageParser.submit(file, parseFlags);
        fileCount++;
    }

    // 逐一处理结果
    for (; fileCount > 0; fileCount--) {
        ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
        Throwable throwable = parseResult.throwable;
        int errorCode = PackageManager.INSTALL_SUCCEEDED;
        String errorMsg = null;

        if (throwable == null) {
            // TODO(toddke): move lower in the scan chain
            // Static shared libraries have synthetic package names
            if (parseResult.parsedPackage.isStaticSharedLibrary()) {
                renameStaticSharedLibraryPackage(parseResult.parsedPackage);
            }
            try {
                addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
                        currentTime, null);
            } catch (PackageManagerException e) {
            }

        if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
            mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
        }

        // 删除无效的用户应用程序
        if ((scanFlags & SCAN_AS_SYSTEM) == 0
                && errorCode != PackageManager.INSTALL_SUCCEEDED) {
            removeCodePathLI(parseResult.scanFile);
        }
    }
}

调用 ParallelPackageParser 的 submit 提交扫描文件。

三 ParallelPackageParser

3.1 submit

public void submit(File scanFile, int parseFlags) {
    mExecutorService.submit(() -> {
            ParseResult pr = new ParseResult();

            try {
                pr.scanFile = scanFile;
                // 解析文件
                pr.parsedPackage = parsePackage(scanFile, parseFlags);
            } ...

            try {
                mQueue.put(pr);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                // 将结果传播给take()的调用者。
                // 有助于防止主线程在interrupt 时等待
                // ParallelPackageParser在中断的情况下完成
                mInterruptedInThread = Thread.currentThread().getName();
            }
    });
}

调用 parsePackage 解析文件,获得一个 ParseResult 结果。

3.2 parsePackage

protected ParsedPackage parsePackage(File scanFile, int parseFlags)
		throws PackageParser.PackageParserException {
    return mPackageParser.parsePackage(scanFile, parseFlags, true);
}

最终结果还是通过传入的 PackageParser2 进行解析文件。

四 PackageParser2

4.1 parsePackage

public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
            throws PackageParserException {

    // 先判断是否有缓存
    if (useCaches && mCacher != null) {
        ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags);
        if (parsed != null) {
            return parsed;
        }
    }

    ParseInput input = mSharedResult.get().reset();

    // 调用 parsingUtils 进行解析 apk 文件
    ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);

    ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();

    // 保存解析结果到缓存
    if (mCacher != null) {
        mCacher.cacheResult(packageFile, flags, parsed);
    }

    return parsed;
}

五 ParsingPackageUtils

5.1 parsePackage

[frameworks\base\core\java\android\content\pm\parsing\ParsingPackageUtils.java]

public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
        int flags)
        throws PackageParserException {

    // 如果是文件目录就用 parseClusterPackage 解析
    // 这里我们是 apk 所以用下面的 parseMonolithicPackage 解析
    if (packageFile.isDirectory()) {
        return parseClusterPackage(input, packageFile, flags);
    } else {
        return parseMonolithicPackage(input, packageFile, flags);
    }
}

在 parsePackage 中,对目录文件和非目录文件进行了区分,当然最后其实都会调用到同一个函数,所以这里我们只看 parseMonolithicPackage 就足够了。

5.2 parseMonolithicPackage

private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
		int flags) throws PackageParserException {

    try {
            // 解析 apk 文件
        final ParseResult<ParsingPackage> result = parseBaseApk(input,
                        apkFile,
                        apkFile.getCanonicalPath(),
                        assetLoader, flags);

        return input.success(result.getResult().setUse32BitAbi(lite.isUse32bitAbi()));
    }
    ...
}

这里的 parseMonolithicPackage 最后调用了 parseBaseApk 进行解析,当然,如果是解析文件夹的 parseClusterPackage,其实也会调用到 parseBaseApk。

5.3 parseBaseApk

private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
		String codePath, SplitAssetLoader assetLoader, int flags)
		throws PackageParserException {

    // apk 路径
    final String apkPath = apkFile.getAbsolutePath();

    String volumeUuid = null;
    if (apkPath.startsWith(MNT_EXPAND)) {
            final int end = apkPath.indexOf('/', MNT_EXPAND.length());
            volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
    }


    final AssetManager assets = assetLoader.getBaseAssetManager();
    final int cookie = assets.findCookieForPath(apkPath);


    try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
                    ANDROID_MANIFEST_FILENAME)) {
            final Resources res = new Resources(assets, mDisplayMetrics, null);

    // 又调用了一个同名的函数 parseBaseApk
    ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
                    parser, flags);

    ...
}

5.4 parseBaseApk

private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
		String codePath, Resources res, XmlResourceParser parser, int flags)
		throws XmlPullParserException, IOException {
    ... // 省略代码

    final ParseResult<ParsingPackage> result =
                    parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);

    }
}

调用到了 parseBaseApkTags,看名字也知道是解析 apk 相关属性的。

5.5 parseBaseApkTags

private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
		TypedArray sa, Resources res, XmlResourceParser parser, int flags)
		throws XmlPullParserException, IOException {

    ...

    boolean foundApp = false;
    final int depth = parser.getDepth();
    int type;

    // 开始一个循环进行解析
    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                    && (type != XmlPullParser.END_TAG
                    || parser.getDepth() > depth)) {
        if (type != XmlPullParser.START_TAG) {
                continue;
        }

        String tagName = parser.getName();
        final ParseResult result;

        // 先特殊处理 Application 节点
        if (TAG_APPLICATION.equals(tagName)) {
                if (foundApp) {
                        ...
                } else {
                        foundApp = true;
                        // 解析 Application
                        result = parseBaseApplication(input, pkg, res, parser, flags);
                }
        } else {
                // 解析 apk
                result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
        }

        if (result.isError()) {
                return input.error(result);
        }
    }
    ...

    return input.success(pkg);
}

5.6 parseBaseApplication

parseBaseApplication 就是真正解析应用 apk 文件的地方。

private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
            throws XmlPullParserException, IOException {
        final String pkgName = pkg.getPackageName();
        int targetSdk = pkg.getTargetSdkVersion();

        // 获取应用信息,android.content.pm.ApplicationInfo.java
        final ApplicationInfo ai = owner.applicationInfo;
        // 包名
        final String pkgName = owner.applicationInfo.packageName;

        // 获取 application 节点
        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);

        // 获取 application 节点下的 icon
        ai.iconRes = sa.getResourceId(
            com.android.internal.R.styleable.AndroidManifestApplication_icon, 0);
        // 获取 application 节点下的 roundIcon
        ai.roundIconRes = sa.getResourceId(
            com.android.internal.R.styleable.AndroidManifestApplication_roundIcon, 0);

        // 解析 application 节点下的 name,label,icon,roundIcon,logo,banner
        // (居然还有一个叫 banner 的属性)
        if (!parsePackageItemInfo(owner, ai, outError,
                "<application>", sa, false /*nameRequired*/,
                com.android.internal.R.styleable.AndroidManifestApplication_name,
                com.android.internal.R.styleable.AndroidManifestApplication_label,
                com.android.internal.R.styleable.AndroidManifestApplication_icon,
                com.android.internal.R.styleable.AndroidManifestApplication_roundIcon,
                com.android.internal.R.styleable.AndroidManifestApplication_logo,
                com.android.internal.R.styleable.AndroidManifestApplication_banner)) {
            sa.recycle();
            // 如果属性错误就会返回false
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        // 是否有 application 的类
        if (ai.name != null) {
            ai.className = ai.name;
        }

        ... // 省略代码,解析安装包 AndroidManifest 中的各种属性
	    // 版本,等等……

        boolean hasActivityOrder = false;
        boolean hasReceiverOrder = false;
        boolean hasServiceOrder = false;
        final int depth = parser.getDepth();

        // 接下来开始解析四大组件
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG
                || parser.getDepth() > depth)) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            final ParseResult result;
            String tagName = parser.getName();
            boolean isActivity = false;
            switch (tagName) {
                case "activity":
                	// 解析 activity,可以发现 activity 和 receiver 的解析方式的一样的。
                    isActivity = true;
                    // fall-through
                case "receiver":
                    ParseResult<ParsedActivity> activityResult =
                            ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
                                    res, parser, flags, sUseRoundIcon, input);

                    if (activityResult.isSuccess()) {
                        ParsedActivity activity = activityResult.getResult();
                        if (isActivity) {
                            hasActivityOrder |= (activity.getOrder() != 0);
                            // 添加 Activity
                            pkg.addActivity(activity);
                        } else {
                            hasReceiverOrder |= (activity.getOrder() != 0);
                            // 添加 Receiver
                            pkg.addReceiver(activity);
                        }
                    }

                    result = activityResult;
                    break;
                case "service":
                    ParseResult<ParsedService> serviceResult =
                            ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
                                    flags, sUseRoundIcon, input);
                    if (serviceResult.isSuccess()) {
                        ParsedService service = serviceResult.getResult();
                        hasServiceOrder |= (service.getOrder() != 0);
                        // 添加 Service
                        pkg.addService(service);
                    }

                    result = serviceResult;
                    break;
                case "provider":
                    ParseResult<ParsedProvider> providerResult =
                            ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
                                    flags, sUseRoundIcon, input);
                    if (providerResult.isSuccess()) {
	                    // 添加 Provider
                        pkg.addProvider(providerResult.getResult());
                    }

                    result = providerResult;
                    break;
                case "activity-alias":
                    activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
                            parser, sUseRoundIcon, input);
                    if (activityResult.isSuccess()) {
                        ParsedActivity activity = activityResult.getResult();
                        hasActivityOrder |= (activity.getOrder() != 0);
                        pkg.addActivity(activity);
                    }

                    result = activityResult;
                    break;
                default:
                    result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
                    break;
            }

            if (result.isError()) {
                return input.error(result);
            }
        }

        if (TextUtils.isEmpty(pkg.getStaticSharedLibName())) {
            // 为普通的应用程序添加一个隐藏的应用程序细节活动,
            // 将用户转发到应用程序详情页面。
            ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg);
            pkg.addActivity(a.getResult());
        }

		// 为 Activity 排序
        if (hasActivityOrder) {
            pkg.sortActivities();
        }

		// 为 Receiver 排序
        if (hasReceiverOrder) {
            pkg.sortReceivers();
        }

        // 为 Service 排序
        if (hasServiceOrder) {
            pkg.sortServices();
        }

        // 必须在整个{@link ApplicationInfo}被完全处理之后,并且在
        // 每个活动信息有机会从其属性中设置它之后运行。
        // 每个活动信息都有机会从其属性中设置它。
        setMaxAspectRatio(pkg);
        setMinAspectRatio(pkg);
        setSupportsSizeChanges(pkg);

        pkg.setHasDomainUrls(hasDomainURLs(pkg));

        return input.success(pkg);
    }

parseBaseApplication 这个函数主要做了三件事

  1. 解析 application 节点下的属性(我们熟悉的 name,label,icon,roundIcon,logo 都会被解析)
  2. 解析四大组件节点,并把它们加进 pkg(ParsingPackage)中
  3. 如果需要排序就进行排序

接下来我们就看看四大组件的节点是如何解析并加到 ParsingPackage 中的,并且加到 ParsingPackage 中之后,是如何被使用的。

5.7 parsePackageItemInfo

private static boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo,
		String[] outError, String tag, TypedArray sa, boolean nameRequired,
		int nameRes, int labelRes, int iconRes, int roundIconRes, int logoRes, int bannerRes) {
	// 这种情况只能发生在单元测试中,在单元测试中,我们有时需要创建假的
	// 的各种包解析器数据结构。
	if (sa == null) {
		outError[0] = tag + " does not contain any attributes";
		return false;
	}

	...
	outInfo.packageName = owner.packageName;

	return true;
}

parsePackageItemInfo 主要是异常检测,判断安装包里的属性是否正确,如果不正确就会返回 false, 正确就会返回 true。

5.8 parseActivityOrReceiver

parseActivityOrReceiver 是解析 Activity 节点和 Receiver 节点的各种属性。对于 service 和 provider,则是调用 parseService 和 parseProvider 进行解析。

@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public static ParseResult<ParsedActivity> parseActivityOrReceiver(String[] separateProcesses,
            ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
            boolean useRoundIcon, ParseInput input)
            throws XmlPullParserException, IOException {
        
        // 包名
        final String packageName = pkg.getPackageName();
        // 创建一个 ParsedActivity 对象用来保存解析后的数据
        final ParsedActivity activity = new ParsedActivity();

        boolean receiver = "receiver".equals(parser.getName());

        // 获得 Activity 节点的属性
        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
        try {
            // 解析 Activity 节点的属性到 result
            ParseResult<ParsedActivity> result =
                    ParsedMainComponentUtils.parseMainComponent(
                    activity, tag, separateProcesses,
                    pkg, sa, flags, useRoundIcon, input,
                    R.styleable.AndroidManifestActivity_banner,
                    R.styleable.AndroidManifestActivity_description,
                    R.styleable.AndroidManifestActivity_directBootAware,
                    R.styleable.AndroidManifestActivity_enabled,
                    R.styleable.AndroidManifestActivity_icon,
                    R.styleable.AndroidManifestActivity_label,
                    R.styleable.AndroidManifestActivity_logo,
                    R.styleable.AndroidManifestActivity_name,
                    R.styleable.AndroidManifestActivity_process,
                    R.styleable.AndroidManifestActivity_roundIcon,
                    R.styleable.AndroidManifestActivity_splitName,
                    R.styleable.AndroidManifestActivity_attributionTags);
            if (result.isError()) {
                return result;
            }

            if (receiver && pkg.isCantSaveState()) {
                // A heavy-weight application can not have receivers in its main process
                //  一个重量级的应用程序在其主进程中不能有接收者
                if (Objects.equals(activity.getProcessName(), packageName)) {
                    return input.error("Heavy-weight applications can not have receivers "
                            + "in main process");
                }
            }

            // 解析 Activity theme
            activity.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
            activity.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions, pkg.getUiOptions());

            // 解析 Activity 的 flags,Activity 的各种启动相关的属性都会放在 flags 中
            activity.flags |= flag(ActivityInfo.FLAG_ALLOW_TASK_REPARENTING, R.styleable.AndroidManifestActivity_allowTaskReparenting, pkg.isAllowTaskReparenting(), sa)
                   ...; // 省略代码 flags 解析

            if (!receiver) {
            	// 解析 Activity 的属性
                activity.flags |= flag(ActivityInfo.FLAG_HARDWARE_ACCELERATED, R.styleable.AndroidManifestActivity_hardwareAccelerated, pkg.isBaseHardwareAccelerated(), sa)
                        ...  // 省略代码 flags 解析

                // 颜色模式
                activity.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode, ActivityInfo.COLOR_MODE_DEFAULT);

                // 启动模式
                activity.launchMode = sa.getInt(R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE);
                ...
                // 软键盘
                activity.softInputMode = sa.getInt(R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);

                // 属性改变的通知回调
                activity.configChanges = getActivityConfigChanges(
                        sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
                        sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0));

                // 屏幕方向
                int screenOrientation = sa.getInt(R.styleable.AndroidManifestActivity_screenOrientation, SCREEN_ORIENTATION_UNSPECIFIED);
                int resizeMode = getActivityResizeMode(pkg, sa, screenOrientation);
                activity.screenOrientation = screenOrientation;
                activity.resizeMode = resizeMode;

                // 宽高比
                if (sa.hasValue(R.styleable.AndroidManifestActivity_maxAspectRatio)
                        && sa.getType(R.styleable.AndroidManifestActivity_maxAspectRatio)
                        == TypedValue.TYPE_FLOAT) {
                    activity.setMaxAspectRatio(resizeMode,
                            sa.getFloat(R.styleable.AndroidManifestActivity_maxAspectRatio,
                                    0 /*default*/));
                }

               ...

                }
            } else {
            	// 如果不是 Activity 的解析
                activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
                activity.configChanges = 0;
                activity.flags |= flag(ActivityInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestActivity_singleUser, sa);
            }
            // @formatter:on

            ...

            return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
                    false /*isAlias*/, visibleToEphemeral, input,
                    R.styleable.AndroidManifestActivity_parentActivityName,
                    R.styleable.AndroidManifestActivity_permission,
                    R.styleable.AndroidManifestActivity_exported
            );
        } finally {
            sa.recycle();
        }
    }

parseActivityOrReceiver 看名字也知道是解析 Activity 和 Receiver,这里面的一些属性也是我们非常熟悉的。

5.9 parseActivityOrAlias

 @NonNull
    private static ParseResult<ParsedActivity> parseActivityOrAlias(ParsedActivity activity,
            ParsingPackage pkg, String tag, XmlResourceParser parser, Resources resources,
            TypedArray array, boolean isReceiver, boolean isAlias, boolean visibleToEphemeral,
            ParseInput input, int parentActivityNameAttr, int permissionAttr,
            int exportedAttr) throws IOException, XmlPullParserException {

        ... // 省略代码


        final int depth = parser.getDepth();
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG
                || parser.getDepth() > depth)) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            final ParseResult result;

            if (parser.getName().equals("intent-filter")) {
                //  解析 intent-filter
                ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
                        !isReceiver, visibleToEphemeral, resources, parser, input);
                ...
                result = intentResult;
            } else if (parser.getName().equals("meta-data")) {
            	// 解析 meta-data
                result = ParsedComponentUtils.addMetaData(activity, pkg, resources, parser, input);
            } else if (parser.getName().equals("property")) {
            	// 解析 property
                result = ParsedComponentUtils.addProperty(activity, pkg, resources, parser, input);
            }

            ... // 解析其他属性

            if (result.isError()) {
                return input.error(result);
            }
        }

		...

        return input.success(activity);
    }

在 parseActivityOrAlias 主要用于解析 Activity 属性,当然还有一些其他的属性,不过我们主要关注 Activity 的属性。

  • intent-filter
  • meta-data
  • property

然后再看看 service 和 provider 是如何解析的。

5.10 parseService

[base\core\java\android\content\pm\parsing\component\ParsedServiceUtils.java]

@NonNull
public static ParseResult<ParsedService> parseService(String[] separateProcesses,
		ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
		boolean useRoundIcon, ParseInput input)
		throws XmlPullParserException, IOException {
	boolean visibleToEphemeral;
	boolean setExported;

	final String packageName = pkg.getPackageName();
	final ParsedService service = new ParsedService();
	String tag = parser.getName();

	TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestService);
	try {

		// 解析 通用的属性
		ParseResult<ParsedService> result = ParsedMainComponentUtils.parseMainComponent(
				service, tag, separateProcesses, pkg, sa, flags, useRoundIcon, input,
				R.styleable.AndroidManifestService_banner,
				R.styleable.AndroidManifestService_description,
				R.styleable.AndroidManifestService_directBootAware,
				R.styleable.AndroidManifestService_enabled,
				R.styleable.AndroidManifestService_icon,
				R.styleable.AndroidManifestService_label,
				R.styleable.AndroidManifestService_logo,
				R.styleable.AndroidManifestService_name,
				R.styleable.AndroidManifestService_process,
				R.styleable.AndroidManifestService_roundIcon,
				R.styleable.AndroidManifestService_splitName,
				R.styleable.AndroidManifestService_attributionTags
		);


		// exported 属性,是否可以被隐式调用
		setExported = sa.hasValue(R.styleable.AndroidManifestService_exported);
		if (setExported) {
			service.exported = sa.getBoolean(R.styleable.AndroidManifestService_exported,
					false);
		}

		// service 节点下的权限
		String permission = sa.getNonConfigurationString(
				R.styleable.AndroidManifestService_permission, 0);
		service.setPermission(permission != null ? permission : pkg.getPermission());

		// 前台服务类型
		service.foregroundServiceType = sa.getInt(
				R.styleable.AndroidManifestService_foregroundServiceType,
				ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE);

		service.flags |= flag(ServiceInfo.FLAG_STOP_WITH_TASK,
				R.styleable.AndroidManifestService_stopWithTask, sa)
				| flag(ServiceInfo.FLAG_ISOLATED_PROCESS,
				R.styleable.AndroidManifestService_isolatedProcess, sa)
				| flag(ServiceInfo.FLAG_EXTERNAL_SERVICE,
				R.styleable.AndroidManifestService_externalService, sa)
				| flag(ServiceInfo.FLAG_USE_APP_ZYGOTE,
				R.styleable.AndroidManifestService_useAppZygote, sa)
				| flag(ServiceInfo.FLAG_SINGLE_USER,
				R.styleable.AndroidManifestService_singleUser, sa);

		visibleToEphemeral = sa.getBoolean(
				R.styleable.AndroidManifestService_visibleToInstantApps, false);
		if (visibleToEphemeral) {
			service.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
			pkg.setVisibleToInstantApps(true);
		}
	} finally {
		sa.recycle();
	}

	// 开始解析 service 节点下的子节点
	final int depth = parser.getDepth();
	int type;
	while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
			&& (type != XmlPullParser.END_TAG
			|| parser.getDepth() > depth)) {
		if (type != XmlPullParser.START_TAG) {
			continue;
		}

		final ParseResult parseResult;
		switch (parser.getName()) {
			case "intent-filter":
				ParseResult<ParsedIntentInfo> intentResult = ParsedMainComponentUtils
						.parseIntentFilter(service, pkg, res, parser, visibleToEphemeral,
								true /*allowGlobs*/, false /*allowAutoVerify*/,
								false /*allowImplicitEphemeralVisibility*/,
								false /*failOnNoActions*/, input);
				parseResult = intentResult;
				if (intentResult.isSuccess()) {
					ParsedIntentInfo intent = intentResult.getResult();
					service.order = Math.max(intent.getOrder(), service.order);
					service.addIntent(intent);
				}
				break;
			case "meta-data":
				parseResult = ParsedComponentUtils.addMetaData(service, pkg, res, parser, input);
				break;
			case "property":
				parseResult =
						ParsedComponentUtils.addProperty(service, pkg, res, parser, input);
				break;
			default:
				parseResult = ParsingUtils.unknownTag(tag, pkg, parser, input);
				break;
		}

		if (parseResult.isError()) {
                    return input.error(parseResult);
		}
	}

	if (!setExported) {
            boolean hasIntentFilters = service.getIntents().size() > 0;
            service.exported = hasIntentFilters;
	}

	return input.success(service);
}

parseService 是解析 service 节点的方法,这里面分别对 service 节点,和 service 节点下的子节点进行了解析。

service 节点主要包含了三种属性

  1. 通用的属性,例如 icon,logo,name,label 等等。
  2. exported 属性,表示是否可以被其他进程隐式调用,如果设置为了 false ,其他的进程就无法通过 intent-filter 来找到你这个服务了。
  3. 然后获取了是否有前台服务类型。相比于后台服务,前台服务更加不容易被系统杀死。例如 location 定位服务,phoneCall 来电提醒服务等,是极难被系统杀死的。

最后就是调用 ParsedMainComponentUtils 解析三种通用的属性。

5.11 parseProvider

[base\core\java\android\content\pm\parsing\component\ParsedProviderUtils.java]
@NonNull
public static ParseResult<ParsedProvider> parseProvider(String[] separateProcesses,
		ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
		boolean useRoundIcon, ParseInput input)
		throws IOException, XmlPullParserException {
	String authority;
	boolean visibleToEphemeral;

	final int targetSdkVersion = pkg.getTargetSdkVersion();
	final String packageName = pkg.getPackageName();
	final ParsedProvider provider = new ParsedProvider();
	final String tag = parser.getName();

	TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProvider);
	try {
		ParseResult<ParsedProvider> result =
				ParsedMainComponentUtils.parseMainComponent(provider, tag, separateProcesses,
				pkg, sa, flags, useRoundIcon, input,
				... ; // 解析通用属性


		authority = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_authorities, 0);

		provider.exported = sa.getBoolean(R.styleable.AndroidManifestProvider_exported,
				targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1);

		provider.syncable = sa.getBoolean(R.styleable.AndroidManifestProvider_syncable, false);

		String permission = sa.getNonConfigurationString(
				R.styleable.AndroidManifestProvider_permission, 0);
		String readPermission = sa.getNonConfigurationString(
				R.styleable.AndroidManifestProvider_readPermission, 0);
		if (readPermission == null) {
			readPermission = permission;
		}
		if (readPermission == null) {
			provider.setReadPermission(pkg.getPermission());
		} else {
			provider.setReadPermission(readPermission);
		}
		String writePermission = sa.getNonConfigurationString(
				R.styleable.AndroidManifestProvider_writePermission, 0);
		if (writePermission == null) {
			writePermission = permission;
		}
		if (writePermission == null) {
			provider.setWritePermission(pkg.getPermission());
		} else {
			provider.setWritePermission(writePermission);
		}

		...

	return parseProviderTags(pkg, tag, res, parser, visibleToEphemeral, provider, input);
}

parseProvider 解析内容提供者相比其他的四大组件,里面多了许多权限相关的逻辑。然后调用 parseProviderTags 对我们熟悉的几个子节点的解析。

5.12 parseProviderTags


@NonNull
private static ParseResult<ParsedProvider> parseProviderTags(ParsingPackage pkg, String tag,
		Resources res, XmlResourceParser parser, boolean visibleToEphemeral,
		ParsedProvider provider, ParseInput input)
		throws XmlPullParserException, IOException {
	final int depth = parser.getDepth();
	int type;
	while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
			&& (type != XmlPullParser.END_TAG
			|| parser.getDepth() > depth)) {
		if (type != XmlPullParser.START_TAG) {
			continue;
		}

		String name = parser.getName();
		final ParseResult result;
		switch (name) {
			case "intent-filter":
				ParseResult<ParsedIntentInfo> intentResult = ParsedMainComponentUtils
						.parseIntentFilter(provider, pkg, res, parser, visibleToEphemeral,
								true /*allowGlobs*/, false /*allowAutoVerify*/,
								false /*allowImplicitEphemeralVisibility*/,
								false /*failOnNoActions*/, input);
				result = intentResult;
				if (intentResult.isSuccess()) {
					ParsedIntentInfo intent = intentResult.getResult();
					provider.order = Math.max(intent.getOrder(), provider.order);
					provider.addIntent(intent);
				}
				break;
			case "meta-data":
				result = ParsedComponentUtils.addMetaData(provider, pkg, res, parser, input);
				break;
			case "property":
				result = ParsedComponentUtils.addProperty(provider, pkg, res, parser, input);
				break;
			case "grant-uri-permission": {
				result = parseGrantUriPermission(provider, pkg, res, parser, input);
				break;
			}
			case "path-permission": {
				result = parsePathPermission(provider, pkg, res, parser, input);
				break;
			}
			default:
				result = ParsingUtils.unknownTag(tag, pkg, parser, input);
				break;
		}

		if (result.isError()) {
			return input.error(result);
		}
	}

	return input.success(provider);
}

到这里,我们四大节点的解析基本算是理清了。接下来还有几个问题。

  1. 四大节点的子节点,那些通用属性是怎么解析的(intent-filter,meta-data,property 等)?
  2. 解析完了之后又做了什么?

六 ParsedMainComponentUtils

首先我们看通用熟悉的 intent-filter 的解析逻辑。

6.1 intent-filter 示例

如下,就是一个简单的 Android 浏览器的 xml 配置。

<activity
	android:name="org.chromium.webview_shell.WebViewBrowserActivity"
	android:label="@string/title_activity_browser"
	android:exported="true"
	android:windowSoftInputMode="adjustResize"
	android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|density">
	<intent-filter>
		<action android:name="android.intent.action.MAIN" />
		<category android:name="android.intent.category.LAUNCHER" />
	</intent-filter>
	<!-- Catch intents which do not specify a MIME type -->
	<intent-filter>
		<action android:name="android.intent.action.VIEW" />
		<category android:name="android.intent.category.DEFAULT" />
		<category android:name="android.intent.category.BROWSABLE" />
		<data android:scheme="http" />
		<data android:scheme="https" />
	</intent-filter>
	<!-- Catch intents which do specify a MIME type -->
	<intent-filter>
		<action android:name="android.intent.action.VIEW" />
		<category android:name="android.intent.category.DEFAULT" />
		<category android:name="android.intent.category.BROWSABLE" />
		<data android:scheme="http" />
		<data android:scheme="https" />
		<data android:mimeType="text/html"/>
		<data android:mimeType="text/plain"/>
		<data android:mimeType="application/xhtml+xml"/>
		<data android:mimeType="application/vnd.wap.xhtml+xml"/> <!-- XHTML MP -->
	</intent-filter>
</activity>

我们可以对照着这个 xml,看看系统是如何对它进行解析的。

6.2 parseIntentFilter

[base\core\java\android\content\pm\parsing\component\ParsedActivityUtils.java]
static ParseResult<ParsedIntentInfo> parseIntentFilter(
            ParsedMainComponent mainComponent,
        ParsingPackage pkg, Resources resources, XmlResourceParser parser,
        boolean visibleToEphemeral, boolean allowGlobs, boolean allowAutoVerify,
        boolean allowImplicitEphemeralVisibility, boolean failOnNoActions,
        ParseInput input) throws IOException, XmlPullParserException {

    // 调用 ParsedIntentInfoUtils 进行解析
    ParseResult<ParsedIntentInfo> intentResult = ParsedIntentInfoUtils.parseIntentInfo(
            mainComponent.getName(), pkg, resources, parser, allowGlobs,
            allowAutoVerify, input);
    if (intentResult.isError()) {
        return input.error(intentResult);
    }

    ...

    return input.success(intentResult.getResult());
}

ParseResult 实际上就是一个泛型,它封装的就是一个 ParsedIntentInfo。用于解析 intent-filter。

七 ParsedIntentInfoUtils

7.1 parseIntentInfo

public static ParseResult<ParsedIntentInfo> parseIntentInfo(String className,
            ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs,
            boolean allowAutoVerify, ParseInput input)
            throws XmlPullParserException, IOException {

    // 创建了一个 ParsedIntentInfo,解析的数据都会保存在这里面
    ParsedIntentInfo intentInfo = new ParsedIntentInfo();
    TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestIntentFilter);
    try {
        intentInfo.setPriority(sa.getInt(R.styleable.AndroidManifestIntentFilter_priority, 0));
        intentInfo.setOrder(sa.getInt(R.styleable.AndroidManifestIntentFilter_order, 0));

        // label
        TypedValue v = sa.peekValue(R.styleable.AndroidManifestIntentFilter_label);
        if (v != null) {
            intentInfo.labelRes = v.resourceId;
            if (v.resourceId == 0) {
                intentInfo.nonLocalizedLabel = v.coerceToString();
            }
        }

        // roundIcon
        if (ParsingPackageUtils.sUseRoundIcon) {
            intentInfo.icon = sa.getResourceId(
                    R.styleable.AndroidManifestIntentFilter_roundIcon, 0);
        }

        // icon
        if (intentInfo.icon == 0) {
            intentInfo.icon = sa.getResourceId(R.styleable.AndroidManifestIntentFilter_icon, 0);
        }

        // autoVerify
        if (allowAutoVerify) {
            intentInfo.setAutoVerify(sa.getBoolean(
                    R.styleable.AndroidManifestIntentFilter_autoVerify,
                    false));
        }
    } finally {
        sa.recycle();
    }

    // 开始一个循环进行解析
    final int depth = parser.getDepth();
    int type;
    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
            && (type != XmlPullParser.END_TAG
            || parser.getDepth() > depth)) {
        if (type != XmlPullParser.START_TAG) {
            continue;
        }

        final ParseResult result;
        String nodeName = parser.getName();
        switch (nodeName) {
            case "action": {
                String value = parser.getAttributeValue(PackageParser.ANDROID_RESOURCES,
                        "name");
                    intentInfo.addAction(value);
                    result = input.success(null);
                break;
            }
            case "category": {
                String value = parser.getAttributeValue(PackageParser.ANDROID_RESOURCES,
                        "name");
                    intentInfo.addCategory(value);
                    result = input.success(null);
                break;
            }
            case "data":
                result = parseData(intentInfo, res, parser, allowGlobs, input);
                break;
            default:
                result = ParsingUtils.unknownTag("<intent-filter>", pkg, parser, input);
                break;
        }

    }

    // 是否有 CATEGORY_DEFAULT
    intentInfo.hasDefault = intentInfo.hasCategory(Intent.CATEGORY_DEFAULT);


    return input.success(intentInfo);
}

parseIntentInfo 用于解析 IntentInfo,也是一个循环。不过这里我们可以看到最后它会对 intent-filter 做一个判断,就是是否有 Intent.CATEGORY_DEFAULT 标志。

7.2 parseData

然后就是解析 intent-filter 下的 data 节点。我们一般会配置它的 host 或者 scheme。例如前面浏览器配置的 intent-filter。

private static ParseResult<ParsedIntentInfo> parseData(ParsedIntentInfo intentInfo,
            Resources resources, XmlResourceParser parser, boolean allowGlobs, ParseInput input) {
        TypedArray sa = resources.obtainAttributes(parser, R.styleable.AndroidManifestData);
    try {
        String str = sa.getNonConfigurationString(
                R.styleable.AndroidManifestData_mimeType, 0);
        if (str != null) {
            try {
                intentInfo.addDataType(str);
            } catch (IntentFilter.MalformedMimeTypeException e) {
                return input.error(e.toString());
            }
        }

        str = sa.getNonConfigurationString(
                R.styleable.AndroidManifestData_mimeGroup, 0);
        if (str != null) {
            intentInfo.addMimeGroup(str);
        }

        // 解析 scheme 并添加到 intentInfo
        str = sa.getNonConfigurationString(
                R.styleable.AndroidManifestData_scheme, 0);
        if (str != null) {
            intentInfo.addDataScheme(str);
        }

		...

        // 解析 host 并添加到 intentInfo
        String host = sa.getNonConfigurationString(
                R.styleable.AndroidManifestData_host, 0);
        String port = sa.getNonConfigurationString(
                R.styleable.AndroidManifestData_port, 0);
        if (host != null) {
            intentInfo.addDataAuthority(host, port);
        }

        ...

        return input.success(null);
    } finally {
        sa.recycle();
    }
}

到这里,基本的扫描流程我们已经整理结束了。

八 PMS

接下来我们回到 [2.4] 小节,看看扫描结束之后,是如何处理扫描结果的。

8.1 addForInitLI

addForInitLI 里的逻辑非常长,所以这里我只做一个简单的介绍。

private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
		@ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
		@Nullable UserHandle user)
				throws PackageManagerException {
	final boolean scanSystemPartition =
			(parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
	final String renamedPkgName;
	final PackageSetting disabledPkgSetting;
	final boolean isSystemPkgUpdated;
	final boolean pkgAlreadyExists;
	PackageSetting pkgSetting;

	// 首先判断系统应用是否需要更新
	synchronized (mLock) {
		...
	}


	// 然后校验安装包
	collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify);

	// 如果应用程序的版本发生变化,重置配置文件
	maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);

	/*
	 * 如果出现了新的系统应用,并且还有一个非系统的同名的应用程序,
	 */
	boolean shouldHideSystemApp = false;
	if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
			&& !pkgSetting.isSystem()) {

		if (!parsedPackage.getSigningDetails()
				.checkCapability(pkgSetting.signatures.mSigningDetails,
				PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
						&& !pkgSetting.signatures.mSigningDetails.checkCapability(
								parsedPackage.getSigningDetails(),
								PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
			try (@SuppressWarnings("unused") PackageFreezer freezer = freezePackage(
					parsedPackage.getPackageName(),
					"scanPackageInternalLI")) {
				deletePackageLIF(parsedPackage.getPackageName(), null, true,
						mUserManager.getUserIds(), 0, null, false, null);
			}
			pkgSetting = null;
		} else if (newPkgVersionGreater) {
                    // 如果/system上的应用比/data上的应用要新。
                    // 只需删除/data上的应用[保留应用数据]即可。
                    // 并用/system上的版本替换它
                    InstallArgs args = createInstallArgsForExisting(
                                    pkgSetting.getPathString(), getAppDexInstructionSets(
							pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
                    synchronized (mInstallLock) {
                            args.cleanUpResourcesLI();
                    }
		} else {
                    // 如果/system上的应用比在/data上的应用旧。则隐藏
                    // /system上的应用程序,并且再次扫描 /data上的版本
                    // 并像更新一样重新添加
                    shouldHideSystemApp = true;
		}
	}

	// 扫描 Package
	final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, scanFlags
			| SCAN_UPDATE_SIGNATURE, currentTime, user, null);
	if (scanResult.success) {
            synchronized (mLock) {
                boolean appIdCreated = false;
                try {
                    ...
                    // 提交扫描结果
                    commitReconciledScanResultLocked(
                                    reconcileResult.get(pkgName), mUserManager.getUserIds());
                } catch (PackageManagerException e) {
                        if (appIdCreated) {
                                cleanUpAppIdCreation(scanResult);
                        }
                        throw e;
                }
            }
	}

	// 隐藏同名的系统应用
	if (shouldHideSystemApp) {
            synchronized (mLock) {
                    mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
            }
	}

	...
	return scanResult.pkgSetting.pkg;
}

addForInitLI 这个函数很长,主要处理以下一些事情

  1. 系统更新后,出现了和应用包名一样的应用怎么办
  2. 新应用安装包的校验
  3. 调用 scanPackageNewLI 继续扫描,会传入前面的扫描结果,这个函数在应用安装时也会调用
  4. 提交扫描结果,用于后续四大组件的启动

8.2 scanPackageNewLI

调用 scanPackageNewLI()继续扫描,主要是更新 packageSetting(PackageSetting 主要包含了一个 APP 的基本信息,如安装位置,lib 位置等信息)。

  • 调用 commitReconciledScanResultLocked() 提交包扫描结果、更新系统状态
  • 内部又调用了 commitPackageSettings():
private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
		final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
		@Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
	...

        return scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime);
    }
}

8.3 commitReconciledScanResultLocked

 private AndroidPackage commitReconciledScanResultLocked(
            @NonNull ReconciledPackage reconciledPkg, int[] allUsers) {
        final ScanResult result = reconciledPkg.scanResult;
        final ScanRequest request = result.request;

        ... // 先对扫描的结果进行处理

        final int userId = user == null ? 0 : user.getIdentifier();
        // 然后进行提交
        commitPackageSettings(pkg, oldPkg, pkgSetting, oldPkgSetting, scanFlags,
                (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
        if (pkgSetting.getInstantApp(userId)) {
            mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
        }
        pkgSetting.setStatesOnCommit();

        return pkg;
    }

8.4 commitPackageSettings

向系统添加扫描完成的包。

[base\services\core\java\com\android\server\pm\PackageManagerService.java]

private void commitPackageSettings(@NonNull AndroidPackage pkg, @Nullable AndroidPackage oldPkg,
		@NonNull PackageSetting pkgSetting, @Nullable PackageSetting oldPkgSetting,
		final @ScanFlags int scanFlags, boolean chatty, ReconciledPackage reconciledPkg) {

    final String pkgName = pkg.getPackageName();

    ... // 省略代码

    synchronized (mLock) {
        mSettings.insertPackageSettingLPw(pkgSetting, pkg);

        // 将安装包添加到 mPackages 中
        mPackages.put(pkg.getPackageName(), pkg);

        // 将软件包的 KeySets 添加到全局 KeySetManagerService 中
        KeySetManagerService ksms = mSettings.getKeySetManagerService();
        ksms.addScannedPackageLPw(pkg);

        // 将 pkg 添加到
        mComponentResolver.addAllComponents(pkg, chatty);

        ...

        int i;
        for (i = 0; i < collectionSize; i++) {
                ParsedInstrumentation a = pkg.getInstrumentations().get(i);
                a.setPackageName(pkg.getPackageName());

                // 将安装包添加到 mInstrumentation 中
                mInstrumentation.put(a.getComponentName(), a);
        }

        ...

        // 将安装包添加到权限管理中
        mPermissionManager.onPackageAdded(pkg, (scanFlags & SCAN_AS_INSTANT_APP) != 0, oldPkg);
    }
}

到这里,我们终于把安装包的扫描流程理完了。并且,我们也看到了几个熟悉的身影

  1. 安装包的包名都添加到了 mPackages,这是一个 Map,key 是包名,value 是 AndroidPackage。
  2. 安装包的 AndroidPackage 添加到了 mComponentResolver,这是一个 ComponentResolver,四大组件启动中经常看到它的身影,我们等下可以看一下。
  3. 安装包的全类名添加到了 mInstrumentation,这也是一个 map。
  4. 把扫描安装包的权限结果添加到了 mPermissionManager 中,这是一个 PermissionManagerServiceInternal。

我们接下来看一下 ComponentResolver 是如何添加四大组件的扫描结果的。

九 ComponentResolver

9.1 addAllComponents

void addAllComponents(AndroidPackage pkg, boolean chatty) {
	final ArrayList<Pair<ParsedActivity, ParsedIntentInfo>> newIntents = new ArrayList<>();
    synchronized (mLock) {
            addActivitiesLocked(pkg, newIntents, chatty);
            addReceiversLocked(pkg, chatty);
            addProvidersLocked(pkg, chatty);
            addServicesLocked(pkg, chatty);
            onChanged();
    }
    ...
}

ComponentResolver 的 addAllComponents 添加了四大组件,其实就是调用了四个函数,然后分别把它们添加到了四个集合。

9.2 四大组件的集合

@GuardedBy("mLock")
private final ActivityIntentResolver mActivities;

/** All available providers, for your resolving pleasure. */
@GuardedBy("mLock")
private final ProviderIntentResolver mProviders;

/** All available receivers, for your resolving pleasure. */
@GuardedBy("mLock")
private final ReceiverIntentResolver mReceivers;

/** All available services, for your resolving pleasure. */
@GuardedBy("mLock")
private final ServiceIntentResolver mServices;

9.3 addActivitiesLocked

private void addActivitiesLocked(AndroidPackage pkg,
		List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents, boolean chatty) {
    final int activitiesSize = ArrayUtils.size(pkg.getActivities());
    StringBuilder r = null;
    for (int i = 0; i < activitiesSize; i++) {
            // Activity 的信息,添加进 mActivities
            ParsedActivity a = pkg.getActivities().get(i);
            mActivities.addActivity(a, "activity", newIntents);
    }
}

在 addActivitiesLocked 中,

9.4 addReceiversLocked

private void addReceiversLocked(AndroidPackage pkg, boolean chatty) {
    final int receiversSize = ArrayUtils.size(pkg.getReceivers());
    StringBuilder r = null;
    for (int i = 0; i < receiversSize; i++) {
            ParsedActivity a = pkg.getReceivers().get(i);
            mReceivers.addActivity(a, "receiver", null);
    }
}

9.5 addServicesLocked

private void addServicesLocked(AndroidPackage pkg, boolean chatty) {
    final int servicesSize = ArrayUtils.size(pkg.getServices());
    StringBuilder r = null;
    for (int i = 0; i < servicesSize; i++) {
            ParsedService s = pkg.getServices().get(i);
            mServices.addService(s);
    }
}

9.6 addProvidersLocked

private void addProvidersLocked(AndroidPackage pkg, boolean chatty) {
    final int providersSize = ArrayUtils.size(pkg.getProviders());
    StringBuilder r = null;
    for (int i = 0; i < providersSize; i++) {
        ParsedProvider p = pkg.getProviders().get(i);
        mProviders.addProvider(p);
        if (p.getAuthority() != null) {
            String[] names = p.getAuthority().split(";");

            p.setAuthority(null);
            for (int j = 0; j < names.length; j++) {
                if (j == 1 && p.isSyncable()) {
                    // 我们只希望一个提供者的第一个授权可能是
                    // 可同步的,所以如果我们已经使用不同的授权添加了这个提供者
                    // 权限,请清除可同步标志。我们在复制提供者之前
                    // 因为 mProviders 对象包含了一个我们不希望看到的提供者的引用。
                    // 到一个我们不想改变的提供者的引用。
                    // 只对第二个授权进行这个操作,因为产生的提供者
                    // 对象对这个提供者的所有未来授权都是一样的。
                    p = new ParsedProvider(p);
                    p.setSyncable(false);
                }
                if (!mProvidersByAuthority.containsKey(names[j])) {
                    mProvidersByAuthority.put(names[j], p);
                    if (p.getAuthority() == null) {
                            p.setAuthority(names[j]);
                    } else {
                            p.setAuthority(p.getAuthority() + ";" + names[j]);
                    }
                } else {
                    final ParsedProvider other =
                                    mProvidersByAuthority.get(names[j]);
                    final ComponentName component =
                                    (other != null && other.getComponentName() != null)
                                                    ? other.getComponentName() : null;
                    final String packageName =
                                    component != null ? component.getPackageName() : "?";
                }
            }
        }
    }
}

好了,现在我们已经看到了四大组件,如何从 AndroidManifast 中的标签,到 PMS 中的四个数组。

接下来,我们就通过 Service,来看看这几个集合,是如何使用的。还是从我们之前的 Service 的流程说起。

十 四大组件的获取

10.1 Service 的启动

Service启动源码解析 的 startServiceLocked 中,调用了 retrieveServiceLocked 。

// 没有可以复用的 ServiceRecord,则需要去 Apk 中查找,查找功能 PMS 完成
// 这里是通过 AMS 拿到 PMS
ResolveInfo rInfoForUserId0 = mAm.getPackageManagerInternal().resolveService(service,
    resolvedType, flags, userId, callingUid);

10.2 Service 启动中 Service 的获取

mAm 是 ActivityManagerService,这里是获取 AMS 中的 PackageManagerInternal。然后调用 PackageManagerInternal 的 resolveService。

接下来的调用链有些长,这里就不列举代码了。流程如下

  • PackageManagerInternal.resolveService
  • PackageManagerInternalBase.resolveService
  • ResolveIntentHelper.resolveServiceInternal
  • ComputerEngine.queryIntentServicesInternal
  • ComputerEngine.getServiceInfo
  • ComputerEngine.getServiceInfoBody

注意,ComputerEngine 在 Android 12 中是 PackageManagerService 的一个内部类,但是在之后的版本中又移出去了,单独作为一个类。所以路径发生了变化。

最后在 ComputerEngine 的 getServiceInfoBody 中,调用了 mComponentResolver 的 getService。传入的正是全类名。

10.3 getServiceInfoBody

[frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]
protected ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
										 int callingUid) {
    ParsedService s = mComponentResolver.getService(component);

    if (s == null) {
        return null;
    }

    AndroidPackage pkg = mPackages.get(s.getPackageName());
    if (mSettings.isEnabledAndMatchLPr(pkg, s, flags, userId)) {
        PackageSetting ps = mSettings.getPackageLPr(component.getPackageName());
        if (ps == null) return null;
        if (shouldFilterApplicationLocked(
                        ps, callingUid, component, TYPE_SERVICE, userId)) {
                return null;
        }
        return PackageInfoUtils.generateServiceInfo(pkg,
                        s, flags, ps.readUserState(userId), userId, ps);
    }
    return null;
}

10.4 getService

在 ComponentResolver 的 getService 中,就是我们熟悉的,从 mServices 的集合中,取出对应的 Service 了。

[base\services\core\java\com\android\server\pm\ComponentResolver.java]
@Nullable
ParsedService getService(@NonNull ComponentName component) {
    synchronized (mLock) {
        return mServices.mServices.get(component);
    }
}

到此 Android 中的 PMS 是如何解析安装的应用,到 四大组件启动时,PMS 是如何查找到对应的组件我们就理清了。

十一 总结

最后我们做一个简单的总结。

  • PMS 启动的时候会对系统和安装的应用进行扫描
  • 并对扫描的应用进行解析,把他们的四大组件添加到 ComponentResolver 中对应的集合,后续启动的时候,就会从 ComponentResolver 中取

对于具体的流程,我们用一副整体的图做结尾

PMS扫描安装包~1.png