Skip to content

Mainline 模块列表

解释 Android 17 Mainline 模块清单的真实来源、APEX/模块元数据映射和设备级查询边界。

基于android-17.0.0_r1
AndroidPMSAPEXMainlineModuleInfo

Mainline 模块列表 ​

本文是 APEX 小节的导航篇,但不提供一份凭记忆写死的“所有 Mainline 模块名单”。Android 17 的设备实际模块集合取决于构建裁剪、预装分区、active APEX 更新和 module-metadata XML。本文追踪系统如何生成、读取和查询这份集合,并说明 ModuleInfo、APEX package name、module name、APK-in-APEX 之间的关系。

1. 清单来源 ​

Mainline 模块至少有三种可见身份:

身份来源用途
APEX module nameapex_manifest / ApexInfo.moduleNameapexd 激活和 module 依赖
APEX package name外层 AndroidManifest.xmlPMS package 查询和安装会话
ModuleInfo packagemodule-metadata XML面向 API 的模块展示和查询

同一模块可能还有 APK-in-APEX package name。不能用 com.android.* 前缀遍历目录来代替这些映射。

2. ModuleInfo ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/ModuleInfoProvider.java,符号:字段与构造函数

java
private final Context mContext;
private IPackageManager mPackageManager;
private final ApexManager mApexManager;
private final Map<String, ModuleInfo> mModuleInfo;
private volatile boolean mMetadataLoaded;
private volatile String mPackageName;

ModuleInfoProvider(Context context) {
    mContext = context;
    mApexManager = ApexManager.getInstance();
    mModuleInfo = new ArrayMap<>();
}

ModuleInfoProvider 不扫描 APEX 文件,也不从 module name 反推功能描述;它读取一个可配置的 module-metadata package,并把 XML 元数据与 ApexManager 的 module 映射拼起来。

3. XML 元数据来源 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/ModuleInfoProvider.java,符号:systemReady、loadModuleMetadata

java
public void systemReady() {
    mPackageName = mContext.getResources()
            .getString(
                    R.string.config_defaultModuleMetadataProvider);
    if (TextUtils.isEmpty(mPackageName)) {
        Slog.w(TAG,
                "No configured module metadata provider.");
        return;
    }

    PackageInfo pi = getPackageManager().getPackageInfo(
            mPackageName,
            PackageManager.GET_META_DATA,
            UserHandle.USER_SYSTEM);
    Context packageContext = mContext
            .createPackageContext(mPackageName, 0);
    Resources resources = packageContext.getResources();
    XmlResourceParser parser = resources.getXml(
            pi.applicationInfo.metaData
                    .getInt(MODULE_METADATA_KEY));
    loadModuleMetadata(parser, resources);
}

模块展示名称、隐藏状态和 package name 来自 configured metadata provider。provider 缺失、包不存在或 XML 读取失败时,ModuleInfo 查询不会凭空生成完整模块清单。

源码文件:frameworks/base/services/core/java/com/android/server/pm/ModuleInfoProvider.java,符号:XML module 解析

java
String modulePackageName =
        XmlUtils.readStringAttribute(parser,
                "packageName");
boolean isHidden = XmlUtils.readBooleanAttribute(
        parser, "isHidden");

ModuleInfo mi = new ModuleInfo();
mi.setHidden(isHidden);
mi.setPackageName(modulePackageName);
mi.setName(moduleName);
mi.setApexModuleName(
        mApexManager.getApexModuleNameForPackageName(
                modulePackageName));

if (Flags.provideInfoOfApkInApex()) {
    mi.setApkInApexPackageNames(
            mApexManager.getApksInApex(
                    modulePackageName));
}
mModuleInfo.put(modulePackageName, mi);

这里是 Mainline 清单的关键拼接点:XML 给出展示 metadata,ApexManager 给出底层 module name 和 APK-in-APEX 列表。若 package name 映射不到 APEX,ModuleInfo.apexModuleName 可以为 null;这允许同一 API 描述 APK 形式的模块。

4. 已安装模块 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/ModuleInfoProvider.java,符号:getInstalledModules

java
List<ModuleInfo> getInstalledModules(
        @PackageManager.InstalledModulesFlags int flags) {
    if (!mMetadataLoaded) {
        throw new IllegalStateException(
                "Call to getInstalledModules before metadata loaded");
    }
    if ((flags & PackageManager.MATCH_ALL) != 0) {
        return new ArrayList<>(mModuleInfo.values());
    }

    List<PackageInfo> packages = getPackageManager()
            .getInstalledPackages(
                    flags | PackageManager.MATCH_APEX,
                    UserHandle.getCallingUserId())
            .getList();
    ArrayList<ModuleInfo> result = new ArrayList<>();
    for (PackageInfo p : packages) {
        ModuleInfo module = mModuleInfo.get(p.packageName);
        if (module != null) {
            result.add(module);
        }
    }
    return result;
}

默认查询先通过 PMS 取当前调用 user 可见的 installed packages,并强制加入 MATCH_APEX;MATCH_ALL 则直接返回 metadata XML 中的全部模块。由此可见“设备上定义的模块”和“当前用户查询到的 installed module”不是同一个集合。

5. 按 module 查询 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/ModuleInfoProvider.java,符号:getModuleInfo

java
ModuleInfo getModuleInfo(String name,
        @ModuleInfoFlags int flags) {
    if (!mMetadataLoaded) {
        throw new IllegalStateException(
                "Call to getModuleInfo before metadata loaded");
    }
    if ((flags & PackageManager.MODULE_APEX_NAME) != 0) {
        for (ModuleInfo module : mModuleInfo.values()) {
            if (name.equals(module.getApexModuleName())) {
                return module;
            }
        }
        return null;
    }
    return mModuleInfo.get(name);
}

不带 MODULE_APEX_NAME 时,参数按 package name 解释;带 flag 时,参数按 APEX module name 解释。两个名字恰好相同的设备会掩盖这个差异,调试时应故意使用映射不同的模块或 APK-in-APEX 来验证。

6. PMS 查询 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,符号:getModuleInfo

java
public ModuleInfo getModuleInfo(
        String packageName,
        @PackageManager.ModuleInfoFlags int flags) {
    return mModuleInfoProvider.getModuleInfo(
            packageName, flags);
}

PMS 只是 Binder/LocalService 门面,真正的 metadata 生命周期由 ModuleInfoProvider.systemReady() 控制。调用过早会抛 IllegalStateException,而不是返回一个尚未填充的空对象。

7. 设备实际集合 ​

实际 Mainline 集合由以下变量共同决定:

  1. 产品分区中预装的 APEX/APK。
  2. apexd 当前 active 选择和 /data/apex 更新。
  3. PMS 启动时的 APEX 扫描结果。
  4. module-metadata provider XML 是否声明该模块。
  5. 调用用户是否能在 getInstalledPackages(... MATCH_APEX) 中看到对应 package。

因此可以把源码导航中的模块按能力分组阅读,而不要把一个产品设备上的模块表当成 Android 17 全局常量:

方向首先查看
Runtime / Java coreApexManager active info、classpath 环境、PMS system scan
Native / linkerApexInfo module path、apexd/linkerconfig、SCAN_AS_APEX
Media / network / securityactive module name → package name 映射、module metadata
APK-in-APEXInitAppsHelper、registerApkInApex、ModuleInfo flag
更新/回滚StagingManager、ApexSessionInfo、ApexManager session APIs

8. 设备级排查 ​

text
ModuleInfoProvider.systemReady
  -> module-metadata XML loaded
  -> ApexManager.getApexModuleNameForPackageName
  -> ModuleInfo map
  -> getInstalledPackages(MATCH_APEX, callingUser)
  -> installed module result

当 getModuleInfo 查不到模块时,按顺序检查:metadata provider 是否配置、XML 是否加载、参数是否是 package name 还是 module name、ApexManager 是否完成 scan、调用 user 是否能看到 APEX package。不要先假设“模块不存在”。

9. 阅读检查 ​

读者可以沿源码复述:PMS systemReady → ModuleInfoProvider 找到 metadata provider → 解析 XML → 通过 ApexManager 补 module/APK-in-APEX 映射 → 查询时按 MATCH_APEX 和 calling user 过滤。

然后回答:ModuleInfo XML 是否等于 active APEX 清单?MATCH_ALL 是否只返回当前 user 已安装模块?同一模块的 package name 与 module name 是否总是相同?答案分别是“不是”“不是,它返回 metadata map 全集”“不保证”。