Skip to content

四大组件解析

追踪 Android 17 Manifest 中 Activity、Receiver、Service、Provider 和 activity-alias 的对象构造、字段继承与错误路径。

基于android-17.0.0_r1
AndroidPackageManagerServiceAndroidManifestActivityServiceProviderReceiver源码阅读

四大组件解析 ​

本文面向已经读过 Manifest 解析入口 和 PackageParser2 架构 的读者。前文解释了 base/cluster 入口和 ParsingPackage 的创建;本文继续追踪 application 子树中的 activity、receiver、service、provider、activity-alias 标签,说明它们如何形成 ParsedActivity、ParsedService、ParsedProvider 对象并加入同一个 PackageImpl。

Android 17 没有把四大组件解析写在一个巨大函数中。ParsingPackageUtils 只发现主组件标签并把成功对象加入 package;ParsedComponentUtils 处理名称、图标、label 等基础字段;ParsedMainComponentUtils 处理 process、enabled、exported、directBootAware、split 和 intent matching;各组件 Utils 再处理专有属性和 child tags。这个层次决定了默认值、错误归属和对象所有权。

读完后,读者应能从 parseBaseApplication 或 parseSplitApplication 进入 parseMainComponent;解释 receiver 为什么复用 ParsedActivity、activity-alias 为什么也进入 activity 列表;判断 Service exported 默认值、Provider authorities 的必需条件、heavy-weight 主进程限制、split 默认归属和 AConfig skip 条件;还能从 ParseResult 错误回到具体组件的 Manifest 路径。

1. 组件分层 ​

1.1 公共接口 ​

源码文件:

  • frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedComponent.java
  • frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedMainComponent.java
  • frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedActivity.java
  • frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedService.java
  • frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedProvider.java
层对象典型字段
基础组件ParsedComponentname、packageName、icon、label、meta-data、intent
主组件ParsedMainComponentprocess、enabled、exported、directBootAware、order、split
Activity/ReceiverParsedActivitytheme、launchMode、permission、taskAffinity、window layout
ServiceParsedServicepermission、foregroundServiceType、isolated/native flags
ProviderParsedProviderauthority、read/write permission、URI/path permission、initOrder

Receiver 没有独立 ParsedReceiver 接口,使用 ParsedActivity 类型;activity-alias 也生成 ParsedActivity。Provider 和 Service 才有各自的实现类。

1.2 解析与查询 ​

组件对象在 parser 中是可变构建对象;加入 PackageImpl 后仍可能被 scan policy 设置 system、split 或其他派生属性;调用 hideAsFinal 后才冻结供 PMS 查询。组件解析成功不等于 ComponentResolver 已注册,注册发生在扫描 commit 阶段。

2. 公共字段 ​

2.1 基础组件 ​

源码文件:frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedComponentUtils.java

java
static <Component extends ParsedComponentImpl> ParseResult<Component> parseComponent(
        Component component, String tag, ParsingPackage pkg, TypedArray array,
        boolean useRoundIcon, ParseInput input, int bannerAttr, int descriptionAttr,
        int iconAttr, int labelAttr, int logoAttr, int nameAttr, int roundIconAttr) {
    String name = array.getNonConfigurationString(nameAttr, 0);
    if (TextUtils.isEmpty(name)) {
        return input.error(tag + " does not specify android:name");
    }
    String packageName = pkg.getPackageName();
    String className = ParsingUtils.buildClassName(packageName, name);
    if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
        return input.error(tag + " invalid android:name");
    }
    component.setName(className).setPackageName(packageName);
    int roundIconVal = useRoundIcon ? array.getResourceId(roundIconAttr, 0) : 0;
    if (roundIconVal != 0) {
        component.setIcon(roundIconVal).setNonLocalizedLabel(null);
    } else {
        int iconVal = array.getResourceId(iconAttr, 0);
        if (iconVal != 0) {
            component.setIcon(iconVal);
            component.setNonLocalizedLabel(null);
        }
    }
    component.setLogo(array.getResourceId(logoAttr, 0));
    component.setBanner(array.getResourceId(bannerAttr, 0));
    if (descriptionAttr != NOT_SET) {
        component.setDescriptionRes(array.getResourceId(descriptionAttr, 0));
    }
    TypedValue v = array.peekValue(labelAttr);
    if (v != null) {
        component.setLabelRes(v.resourceId);
        if (v.resourceId == 0) {
            component.setNonLocalizedLabel(v.coerceToString());
        }
    }
    return input.success(component);
}

所有主组件都先验证 name、构造完整类名并写入 packageName。名称为空或保留类名会在公共层失败,组件 Utils 不会继续解析自身字段。

2.2 主组件 ​

源码文件:frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedMainComponentUtils.java

java
static <Component extends ParsedMainComponentImpl> ParseResult<Component> parseMainComponent(
        Component component, String tag, String[] separateProcesses, ParsingPackage pkg,
        TypedArray array, int flags, boolean useRoundIcon, String defaultSplitName,
        ParseInput input, int bannerAttr, int descriptionAttr, int directBootAwareAttr,
        int enabledAttr, int iconAttr, int labelAttr, int logoAttr, int nameAttr,
        int processAttr, int roundIconAttr, int splitNameAttr, int attributionTagsAttr,
        int intentMatchingFlagsAttr) {
    ParseResult<Component> result = ParsedComponentUtils.parseComponent(component, tag, pkg,
            array, useRoundIcon, input, bannerAttr, descriptionAttr, iconAttr, labelAttr,
            logoAttr, nameAttr, roundIconAttr);
    if (result.isError()) {
        return result;
    }
    if (directBootAwareAttr != NOT_SET) {
        component.setDirectBootAware(array.getBoolean(directBootAwareAttr, false));
        if (component.isDirectBootAware()) {
            pkg.setPartiallyDirectBootAware(true);
        }
    }
    component.setEnabled(array.getBoolean(enabledAttr, true));
    component.setProcessName(ParsingUtils.buildProcessName(pkg.getPackageName(),
            pkg.getDefaultProcessName(), array.getString(processAttr)));
    component.setSplitName(array.getNonConfigurationString(splitNameAttr, 0));
    if (component.getSplitName() == null) {
        component.setSplitName(defaultSplitName);
    }
    component.setIntentMatchingFlags(array.getInt(intentMatchingFlagsAttr, 0));
    return input.success(component);
}

公共层处理 direct boot、enabled、process、split 和 intent matching。split Manifest 传入当前 split name;base Manifest 传 null。directBootAware 还会提升 package 级 partiallyDirectBootAware。

3. 入口分发 ​

3.1 标签判断 ​

源码文件:frameworks/base/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java

java
private boolean isMainComponentTag(String tag) {
    return tag.equals("activity") || tag.equals("service") || tag.equals("receiver")
            || tag.equals("provider") || tag.equals("activity-alias");
}

base application 和 split application 都使用这个判断。只有五种主组件标签进入 parseMainComponent;meta-data、property、uses-library 等标签走各自 child-tag 路由。

3.2 分发实现 ​

源码文件:frameworks/base/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java

java
private ParseResult<ParseMainComponentResult> parseMainComponent(ParseInput input,
        ParsingPackage pkg, Resources res, XmlResourceParser parser, String tagName,
        String defaultSplitName, int flags)
        throws XmlPullParserException, IOException {
    final ParseMainComponentResult resultToReturn = new ParseMainComponentResult();
    final ParseResult parseResult;
    boolean isActivity = false;
    switch (tagName) {
        case "activity": {
            isActivity = true;
            // fall-through
        }
        case "receiver":
            ParseResult<ParsedActivity> activityResult =
                    ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
                            res, parser, flags, sUseRoundIcon, defaultSplitName, input);
            if (activityResult.isSuccess()) {
                ParsedActivity activity = activityResult.getResult();
                if (isActivity) {
                    resultToReturn.mHasActivityOrder |= (activity.getOrder() != 0);
                    pkg.addActivity(activity);
                } else {
                    resultToReturn.mHasReceiverOrder |= (activity.getOrder() != 0);
                    pkg.addReceiver(activity);
                }
            }
            parseResult = activityResult;
            break;
        case "service":
            ParseResult<ParsedService> serviceResult =
                    ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
                            flags, sUseRoundIcon, defaultSplitName, input);
            if (serviceResult.isSuccess()) {
                ParsedService service = serviceResult.getResult();
                resultToReturn.mHasServiceOrder |= (service.getOrder() != 0);
                pkg.addService(service);
            }
            parseResult = serviceResult;
            break;
        case "provider":
            ParseResult<ParsedProvider> providerResult =
                    ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
                            flags, sUseRoundIcon, defaultSplitName, input);
            if (providerResult.isSuccess()) {
                pkg.addProvider(providerResult.getResult());
            }
            parseResult = providerResult;
            break;
        case "activity-alias":
            ParseResult<ParsedActivity> aliasResult = ParsedActivityUtils.parseActivityAlias(
                    pkg, res, parser, sUseRoundIcon, defaultSplitName, input);
            if (aliasResult.isSuccess()) {
                ParsedActivity activity = aliasResult.getResult();
                resultToReturn.mHasActivityOrder |= (activity.getOrder() != 0);
                pkg.addActivity(activity);
            }
            parseResult = aliasResult;
            break;
        default:
            throw new IllegalArgumentException("Unknown tag passed to parseMainComponent: "
                    + tagName);
    }
    if (parseResult.isError()) {
        return input.error(parseResult);
    }
    return input.success(resultToReturn);
}

分发器只在子解析成功后加入 package 列表。activity/receiver 共用 parser,alias 进入 activity 列表;非零 intent order 通过 ParseMainComponentResult 汇总。

4. Activity组 ​

4.1 共用入口 ​

源码文件:frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java

java
public static ParseResult<ParsedActivity> parseActivityOrReceiver(
        String[] separateProcesses, ParsingPackage pkg, Resources res,
        XmlResourceParser parser, int flags, boolean useRoundIcon,
        String defaultSplitName, ParseInput input)
        throws XmlPullParserException, IOException {
    final ParsedActivityImpl activity = new ParsedActivityImpl();
    final boolean receiver = "receiver".equals(parser.getName());
    final String tag = "<" + parser.getName() + ">";
    TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
    try {
        ParseResult<ParsedActivityImpl> result =
                ParsedMainComponentUtils.parseMainComponent(activity, tag,
                        separateProcesses, pkg, sa, flags, useRoundIcon, defaultSplitName,
                        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,
                        R.styleable.AndroidManifestActivity_intentMatchingFlags);
        if (result.isError()) {
            return input.error(result);
        }
        return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
                false /*isAlias*/, false /*visibleToEphemeral*/, input,
                R.styleable.AndroidManifestActivity_parentActivityName,
                R.styleable.AndroidManifestActivity_permission,
                R.styleable.AndroidManifestActivity_exported);
    } finally {
        sa.recycle();
    }
}

Receiver 复用 Activity styleable 和 ParsedActivityImpl,通过 receiver 标志跳过 Activity 专有字段;Activity 还会解析 launch mode、theme、task affinity、window layout 等。

4.2 Activity 字段 ​

ParsedActivityUtils 在公共字段之后解析 theme、uiOptions、launchMode、documentLaunchMode、configChanges、screenOrientation、resizeMode、aspect ratio、window layout、parentActivityName、permission 和 exported。Activity 的 exported 还受 intent-filter、target SDK 和显式属性影响。

4.3 activity-alias ​

源码文件:frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedActivityUtils.java

java
public static ParseResult<ParsedActivity> parseActivityAlias(ParsingPackage pkg,
        Resources res, XmlResourceParser parser, boolean useRoundIcon,
        String defaultSplitName, ParseInput input)
        throws XmlPullParserException, IOException {
    TypedArray sa = res.obtainAttributes(parser,
            R.styleable.AndroidManifestActivityAlias);
    try {
        String targetActivity = sa.getNonConfigurationString(
                R.styleable.AndroidManifestActivityAlias_targetActivity,
                Configuration.NATIVE_CONFIG_VERSION);
        if (targetActivity == null) {
            return input.error("<activity-alias> does not specify android:targetActivity");
        }
        targetActivity = ParsingUtils.buildClassName(pkg.getPackageName(), targetActivity);
        if (targetActivity == null) {
            return input.error("Empty class name in package " + pkg.getPackageName());
        }
        // The real implementation resolves target activity and builds alias fields.
        return input.success(ParsedActivityImpl.makeAlias(targetActivity, null));
    } finally {
        sa.recycle();
    }
}

真实实现还会解析 target activity、name、enabled/exported、icon/label、permission、split 和 intent;targetActivity 缺失或类名非法会失败,alias 最终仍加入 activities 列表。

5. Service ​

5.1 属性入口 ​

源码文件:frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java

java
public static ParseResult<ParsedService> parseService(String[] separateProcesses,
        ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
        boolean useRoundIcon, String defaultSplitName, ParseInput input)
        throws XmlPullParserException, IOException {
    boolean visibleToEphemeral;
    boolean setExported;
    final ParsedServiceImpl service = new ParsedServiceImpl();
    TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestService);
    try {
        ParseResult<ParsedServiceImpl> result = ParsedMainComponentUtils.parseMainComponent(
                service, parser.getName(), separateProcesses, pkg, sa, flags,
                useRoundIcon, defaultSplitName, 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,
                R.styleable.AndroidManifestService_intentMatchingFlags);
        if (result.isError()) {
            return input.error(result);
        }
        setExported = sa.hasValue(R.styleable.AndroidManifestService_exported);
        if (setExported) {
            service.setExported(sa.getBoolean(
                    R.styleable.AndroidManifestService_exported, false));
        }
        String permission = sa.getNonConfigurationString(
                R.styleable.AndroidManifestService_permission, 0);
        service.setPermission(permission != null ? permission : pkg.getPermission());
        service.setForegroundServiceType(sa.getInt(
                R.styleable.AndroidManifestService_foregroundServiceType,
                ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE));
    } finally {
        sa.recycle();
    }
    // Child intent-filter, meta-data and property tags follow.
    return input.success(service);
}

Service permission 默认回退到 application permission,foregroundServiceType 默认 NONE;exported 是否显式设置决定后续默认逻辑。Service flags 还包括 stopWithTask、isolatedProcess、externalService、useAppZygote、singleUser 和 nativeService。

5.2 exported 默认 ​

源码文件:frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedServiceUtils.java

java
if (!setExported) {
    boolean hasIntentFilters = service.getIntents().size() > 0;
    if (hasIntentFilters) {
        final ParseResult exportedCheckResult = input.deferError(
                service.getName() + ": Targeting S+ requires an explicit value for "
                        + "android:exported when intent filters are present",
                DeferredError.MISSING_EXPORTED_FLAG);
        if (exportedCheckResult.isError()) {
            return input.error(exportedCheckResult);
        }
    }
    service.setExported(hasIntentFilters);
}

没有显式 exported 时,有 intent-filter 默认 true,没有 intent-filter 默认 false;target S+ 且有 intent-filter 会产生 deferred error,并在后续校验时阻止安装。

5.3 Service 失败 ​

heavy-weight application 禁止 Service 运行在 main process;PCC feature 开启时 isolatedProcess 与 privateComputeCore 同时设置也会失败。child tag 的未知项由 unknownTag 处理,AConfig skip 则跳过而不是失败。

6. Provider ​

6.1 属性入口 ​

源码文件:frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedProviderUtils.java

java
public static ParseResult<ParsedProvider> parseProvider(String[] separateProcesses,
        ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
        boolean useRoundIcon, String defaultSplitName, ParseInput input)
        throws IOException, XmlPullParserException {
    final int targetSdkVersion = pkg.getTargetSdkVersion();
    final String packageName = pkg.getPackageName();
    final ParsedProviderImpl provider = new ParsedProviderImpl();
    TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProvider);
    try {
        ParseResult<ParsedProviderImpl> result = ParsedMainComponentUtils.parseMainComponent(
                provider, parser.getName(), separateProcesses, pkg, sa, flags,
                useRoundIcon, defaultSplitName, input,
                R.styleable.AndroidManifestProvider_banner,
                R.styleable.AndroidManifestProvider_description,
                R.styleable.AndroidManifestProvider_directBootAware,
                R.styleable.AndroidManifestProvider_enabled,
                R.styleable.AndroidManifestProvider_icon,
                R.styleable.AndroidManifestProvider_label,
                R.styleable.AndroidManifestProvider_logo,
                R.styleable.AndroidManifestProvider_name,
                R.styleable.AndroidManifestProvider_process,
                R.styleable.AndroidManifestProvider_roundIcon,
                R.styleable.AndroidManifestProvider_splitName,
                R.styleable.AndroidManifestProvider_attributionTags,
                R.styleable.AndroidManifestProvider_intentMatchingFlags);
        if (result.isError()) {
            return input.error(result);
        }
        String authority = sa.getNonConfigurationString(
                R.styleable.AndroidManifestProvider_authorities, 0);
        provider.setSyncable(sa.getBoolean(R.styleable.AndroidManifestProvider_syncable, false))
                .setExported(sa.getBoolean(R.styleable.AndroidManifestProvider_exported,
                        targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1));
        String permission = sa.getNonConfigurationString(
                R.styleable.AndroidManifestProvider_permission, 0);
        String readPermission = sa.getNonConfigurationString(
                R.styleable.AndroidManifestProvider_readPermission, 0);
        provider.setReadPermission(readPermission != null ? readPermission
                : permission != null ? permission : pkg.getPermission());
        String writePermission = sa.getNonConfigurationString(
                R.styleable.AndroidManifestProvider_writePermission, 0);
        provider.setWritePermission(writePermission != null ? writePermission
                : permission != null ? permission : pkg.getPermission());
    } finally {
        sa.recycle();
    }
    if (authority == null) {
        return input.error("<provider> does not include authorities attribute");
    }
    if (authority.length() <= 0) {
        return input.error("<provider> has empty authorities attribute");
    }
    provider.setAuthority(authority);
    return parseProviderTags(pkg, parser.getName(), res, parser,
            false /*visibleToEphemeral*/, provider, input);
}

Provider authorities 是硬性要求;缺失或为空都会失败。exported 默认兼容旧 target SDK;read/write permission 按 provider permission → application permission 回退。

6.2 URI 与 path ​

Provider child tags 包含 intent-filter、meta-data、property、grant-uri-permission 和 path-permission。grant URI pattern 按 advanced、simple、prefix、suffix、literal 顺序选择;找到 pattern 会自动打开 grantUriPermissions。path permission 至少需要一种有效 path 约束。

6.3 Provider 失败 ​

heavy-weight application 禁止 Provider 运行在 main process。authority 错误、URI/path permission 错误和 main process 限制是独立分支,可能在同一个 Manifest 中先后触发。

7. 条件与状态 ​

7.1 split 归属 ​

base parser 传入 null defaultSplitName;split parser 传入当前 split name。组件显式声明 splitName 时使用显式值,否则继承当前 split,最终影响组件启动时选择的 code path。

7.2 AConfig skip ​

base、split、Service 和 Provider child tag 循环都会检查 AconfigFlags.getInstance().skipCurrentElement(pkg, parser)。命中后调用 XmlUtils.skipCurrentTag,不创建组件或 child metadata;这是条件跳过,不是 parse error。

7.3 package 派生 ​

directBootAware 会设置 package partiallyDirectBootAware;PCC sandbox flag 会设置 package hasPccComponents;intent-filter order 会更新组件 order,并由 ParseMainComponentResult 汇总。组件列表按 Manifest 解析顺序添加,order 是后续 resolver 的排序依据。

8. 失败与验证 ​

8.1 错误边界 ​

组件条件结果
所有组件name 缺失/保留类名ParseResult error
Activity/aliastarget 或类名非法ParseResult error
ServiceS+ + intent-filter + exported 未显式deferred error
Serviceheavy-weight 主进程ParseResult error
Providerauthorities 缺失/为空ParseResult error
Providerheavy-weight 主进程ParseResult error
Provider childURI/path permission 非法ParseResult error
任意 childAConfig skip跳过标签
未知 childunknownTag由 parser 模式决定

8.2 资源清理 ​

各 Utils 在 finally 中 recycle TypedArray;XML parser 和 AssetLoader 由上层 Manifest parser 管理。组件 parse 失败后,已创建对象不会加入 package 列表,当前 package 也不会进入 hideAsParsed/PMS scan。

8.3 测试范围 ​

源码测试应覆盖:name/类名错误、receiver 复用 Activity、alias target 缺失、Service exported 默认和 S+ deferred error、Provider authorities 与 URI/path permission、heavy-weight 主进程、split 默认归属、direct boot package 标记和 AConfig skip。每个测试都应解释 Manifest 输入、关键断言和未覆盖的 PMS 注册阶段。

8.4 诊断命令 ​

text
adb shell dumpsys package <package-name>
adb shell dumpsys package activities
adb shell dumpsys package providers
adb shell logcat -s PackageParser PackageManagerService

对照 component name、process、enabled/exported、permission、splitName、directBootAware、order 和 provider authorities。Manifest 有组件而 dumpsys 没有时,先查 AConfig skip、ParseResult error,再查签名、ABI、reconcile 和 ComponentResolver。

9. 源码路线 ​

定位组件解析问题时,按以下顺序阅读:

  1. ParsingPackageUtils.parseBaseApplication/parseSplitApplication:主组件标签循环和 defaultSplitName。
  2. ParsingPackageUtils.parseMainComponent:标签分发和 package add 方法。
  3. ParsedComponentUtils.parseComponent:name、icon、label、logo、banner。
  4. ParsedMainComponentUtils.parseMainComponent:process、enabled、directBoot、split、attribution、intent matching。
  5. ParsedActivityUtils:activity、receiver、activity-alias 专有分支。
  6. ParsedServiceUtils:permission、foreground service type、flags、exported 和 heavy-weight 限制。
  7. ParsedProviderUtils:authority、权限回退、URI/path permission 和 flags。
  8. PackageImpl component lists:对象加入顺序和 finalization。
  9. PackageInfoUtils/ComponentResolver:解析对象如何成为查询和注册对象。

一个实用练习是:给定一个 split APK,其中 Service 未声明 exported 但含 intent-filter,Provider 缺少 authorities,Receiver 声明 directBootAware,Activity-alias targetActivity 不存在。请判断 parser 会在哪个组件先返回错误、哪些对象已创建但不会加入 package、哪些 package 级标记可能已被设置,以及 PMS 是否会进入签名/ABI 阶段。

10. 设计收束 ​

Android 17 的四大组件解析是一条“公共字段 → 类型专有字段 → package 列表 → PMS 注册”链路:

  • ParsedComponentUtils 负责 name、icon、label、logo、banner 和基础 meta-data;ParsedMainComponentUtils 负责 process、enabled、directBoot、split、attribution、intent matching 和公共 intent-filter。
  • Activity、Receiver、activity-alias 共用 ParsedActivity;Service 和 Provider 使用各自实现。分发器只在子解析成功后把对象加入对应列表。
  • Service exported 默认值依赖 intent-filter,并在 target S+ 时要求显式声明;Provider authorities 必须存在,exported 兼容旧 target SDK。
  • split 组件未显式指定 splitName 时继承当前 split;directBootAware 和 PCC flag 会产生 package 级派生;AConfig skip 会跳过标签而非报告错误。
  • 组件 parse error 会阻止当前 package 进入 hideAsParsed/PMS scan;TypedArray、XML parser、AssetLoader 和 package 对象由不同层负责清理。
  • 解析成功、PackageImpl 列表完成、ComponentResolver 注册和 dumpsys 可见是不同状态,诊断必须沿调用链分别确认。

后续专题会拆解 uses-permission 及 sdk-23/sdk-M 兼容解析,继续说明 Manifest 权限声明如何进入 ParsingPackage,并由 PermissionManager 处理授予和回收。