Mainline 模块列表
本文是 APEX 小节的导航篇,但不提供一份凭记忆写死的“所有 Mainline 模块名单”。Android 17 的设备实际模块集合取决于构建裁剪、预装分区、active APEX 更新和 module-metadata XML。本文追踪系统如何生成、读取和查询这份集合,并说明 ModuleInfo、APEX package name、module name、APK-in-APEX 之间的关系。
1. 清单来源
Mainline 模块至少有三种可见身份:
| 身份 | 来源 | 用途 |
|---|---|---|
| APEX module name | apex_manifest / ApexInfo.moduleName | apexd 激活和 module 依赖 |
| APEX package name | 外层 AndroidManifest.xml | PMS package 查询和安装会话 |
| ModuleInfo package | module-metadata XML | 面向 API 的模块展示和查询 |
同一模块可能还有 APK-in-APEX package name。不能用 com.android.* 前缀遍历目录来代替这些映射。
2. ModuleInfo
源码文件:frameworks/base/services/core/java/com/android/server/pm/ModuleInfoProvider.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
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 解析
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
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
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
public ModuleInfo getModuleInfo(
String packageName,
@PackageManager.ModuleInfoFlags int flags) {
return mModuleInfoProvider.getModuleInfo(
packageName, flags);
}PMS 只是 Binder/LocalService 门面,真正的 metadata 生命周期由 ModuleInfoProvider.systemReady() 控制。调用过早会抛 IllegalStateException,而不是返回一个尚未填充的空对象。
7. 设备实际集合
实际 Mainline 集合由以下变量共同决定:
- 产品分区中预装的 APEX/APK。
- apexd 当前 active 选择和
/data/apex更新。 - PMS 启动时的 APEX 扫描结果。
- module-metadata provider XML 是否声明该模块。
- 调用用户是否能在
getInstalledPackages(... MATCH_APEX)中看到对应 package。
因此可以把源码导航中的模块按能力分组阅读,而不要把一个产品设备上的模块表当成 Android 17 全局常量:
| 方向 | 首先查看 |
|---|---|
| Runtime / Java core | ApexManager active info、classpath 环境、PMS system scan |
| Native / linker | ApexInfo module path、apexd/linkerconfig、SCAN_AS_APEX |
| Media / network / security | active module name → package name 映射、module metadata |
| APK-in-APEX | InitAppsHelper、registerApkInApex、ModuleInfo flag |
| 更新/回滚 | StagingManager、ApexSessionInfo、ApexManager session APIs |
8. 设备级排查
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 全集”“不保证”。
