APEX 架构
本文是 APEX 模块的入口,承接 Package Manager 的扫描和安装文章,回答一个架构问题:.apex 文件如何从 apexd 的激活状态进入 PMS 的包模型,并与 APK、APEX 内嵌 APK 区分开。本文不展开 staged 安装回滚、APEX 中 Java 库或 native 库的具体链接细节,这些由后续专题负责。
APEX 不是“另一种 APK 后缀”。它由 apexd 负责激活/挂载,由 PMS 负责扫描其 Android package 元数据并注册到包模型;APEX 内部还可以包含 APK,这些 APK 由 PMS 以 SCAN_AS_APK_IN_APEX 路径单独处理。
1. 三层边界
| 层 | owner | 事实 |
|---|---|---|
| 容器/激活 | apexd / IApexService | 选择 active APEX、校验会话、挂载 payload |
| 包扫描 | PMS ApexManager / InstallPackageHelper | 把 active/factory APEX 转成 AndroidPackage |
| 组件消费 | PMS PackageSetting、classpath、系统服务 | 查询 APEX、处理 APK-in-APEX 和系统模块 |
2. 容器与 payload
APEX 外层文件是可签名的容器,关键元数据包括 APEX manifest、Android manifest、payload 镜像和公钥/签名材料。本文后续只依赖 PMS/apexd 的接口结果,不把 ZIP 内部每个文件名都当成 Java 层稳定 API。
源码文件:system/apex/apexd/apexd.cpp,符号:APEX 激活和 staged session 相关服务入口
// apexd owns staged-session state and activation.
// PMS calls through IApexService rather than mounting payload itself.
Result<void> UnstagePackages(const std::vector<std::string>& paths) {
LOG(DEBUG) << "UnstagePackages() for " << Join(paths, ',');
return ...;
}apexd 的 owner 是容器生命周期和挂载,不是 PMS 的 package resolver。APEX 是否 active、staged session 是否 ready/successful,都必须通过 IApexService/ApexManager 交互读取。
3. ApexManager 门面
源码文件:frameworks/base/services/core/java/com/android/server/pm/ApexManager.java,符号:类声明、getInstance、ActiveApexInfo
public abstract class ApexManager {
public static final int MATCH_ACTIVE_PACKAGE = 1 << 0;
static final int MATCH_FACTORY_PACKAGE = 1 << 1;
private static final Singleton<ApexManager>
sApexManagerSingleton = new Singleton<>() {
@Override
protected ApexManager create() {
return new ApexManagerImpl();
}
};
public static ApexManager getInstance() {
return sApexManagerSingleton.get();
}
public static class ActiveApexInfo {
@Nullable public final String apexModuleName;
public final File apexDirectory;
public final File preInstalledApexPath;
public final boolean isFactory;
public final File apexFile;
public final boolean activeApexChanged;
}
}PMS 通过 ApexManager 访问 active/factory APEX 信息。ActiveApexInfo 同时保留 module name、挂载目录、预装路径、当前 apex file 和 active 是否变化;这些字段用于后续扫描、更新和系统分区映射。
4. PMS 启动扫描
源码文件:frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java,符号:scanApexPackagesTraced、initNonSystemApex
private List<ApexManager.ScanResult>
scanApexPackagesTraced(
PackageParser2 packageParser) {
Trace.traceBegin(
TRACE_TAG_PACKAGE_MANAGER,
"scanApexPackages");
try {
return mInstallPackageHelper.scanApexPackages(
mApexManager.getAllApexInfos(),
packageParser);
} finally {
Trace.traceEnd(
TRACE_TAG_PACKAGE_MANAGER);
}
}
List<ApexManager.ScanResult> results =
scanApexPackagesTraced(packageParser);
mApexManager.notifyScanResult(results);扫描先从 ApexManager 获取 apexd 已知 APEX,再由 InstallPackageHelper 解析包信息,最后把扫描结果回写给 ApexManager。PMS 不从 /apex 目录盲扫来决定 active 集合,目录信息必须与 apexd 的状态对齐。
源码文件:frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java,符号:active APEX 与 APK-in-APEX 映射
for (ApexManager.ActiveApexInfo apexInfo
: mApexManager.getActiveApexInfos()) {
String apexPackageName =
mApexManager.getActivePackageNameForApexModuleName(
apexInfo.apexModuleName);
for (String packageName
: mApexManager.getApksInApex(
apexPackageName)) {
apkInApexPreInstalledPaths.put(
packageName,
apexInfo.preInstalledApexPath);
}
}APEX 内嵌 APK 需要建立“APK package → preinstalled APEX path”的映射,之后 PMS 才能把 APK-in-APEX 作为特殊扫描输入。它们的 package name 不等于外层 APEX module name,不能在查询时混用。
5. 扫描差异
源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,符号:扫描 flags
static final int SCAN_AS_APK_IN_APEX = 1 << 23;
static final int SCAN_AS_APEX = 1 << 26;SCAN_AS_APEX 表示外层 APEX package;SCAN_AS_APK_IN_APEX 表示 APEX payload 内的 APK。两个 flag 会改变解析来源、安装位置、签名/版本关联和后续系统包处理。
源码文件:frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java,符号:ScanParams.forApkInApexScan
static ScanParams forApkInApexScan(
File scanDir, int parseFlags, int scanFlags,
ApexManager.ActiveApexInfo apexInfo) {
return new ScanParams(scanDir,
parseFlags | PARSE_APK_IN_APEX,
scanFlags, apexInfo);
}APK-in-APEX 使用专门 parse flag,同时携带外层 ActiveApexInfo。这让 PMS 能区分 APK 自己的 code path 与外层 APEX 的激活来源。
6. Staged session
源码文件:frameworks/base/services/core/java/com/android/server/pm/ApexManager.java,符号:staged session 抽象方法
abstract ApexInfoList submitStagedSession(
ApexSessionParams params)
throws PackageManagerException;
abstract void markStagedSessionReady(int sessionId)
throws PackageManagerException;
abstract void markStagedSessionSuccessful(int sessionId);
abstract boolean abortStagedSession(int sessionId);提交 staged session、标记 ready、标记 successful 和 abort 是不同状态转换。成功提交只表示 apexd 接受并完成初始验证,不表示下次启动一定激活;调用方还要显式 markStagedSessionReady。
7. Active 查询
源码文件:frameworks/base/services/core/java/com/android/server/pm/ApexManager.java,符号:查询 API
public abstract List<ActiveApexInfo>
getActiveApexInfos();
@Nullable
public abstract String
getActiveApexPackageNameContainingPackage(
@NonNull String containedPackageName);
@Nullable
abstract ApexSessionInfo getStagedSessionInfo(
int sessionId);
@NonNull
abstract SparseArray<ApexSessionInfo> getSessions();查询 active APEX、查找某个 APK 所属 APEX 和读取 staged session 是不同 API。PMS 的包查询如果需要判断某 APK 是否来自 APEX,应该使用 getActiveApexPackageNameContainingPackage,而不是通过路径字符串猜测。
8. 包模型影响
APEX 扫描结果最终进入 PMS 的 AndroidPackage/PackageSetting 集合,但其 active/factory 来源、模块名和系统分区路径仍由 APEX 专有字段维护。外层 APEX 通常不是面向普通用户的 per-user 安装包;PMS 的 user state 不能替代 apexd 的 active 状态。
源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,符号:APEX 查询
List<ApexManager.ActiveApexInfo> apexes =
mApexManager.getActiveApexInfos();
for (ApexManager.ActiveApexInfo apex : apexes) {
// Use active module path and factory metadata
// when constructing package/system mappings.
}包查询看到的是 PMS 注册后的 package;激活状态变化则来自 apexd session 和重启/更新流程。两种状态不同步时,应先检查 apexd session,再检查 PMS scan result。
9. 失败路径
| 阶段 | 失败 | owner/结果 |
|---|---|---|
| staged submit | apexd 初始验证失败 | submitStagedSession 抛 PackageManagerException |
| ready 标记 | session 不能进入下次启动 | markStagedSessionReady 失败 |
| 启动扫描 | APEX info 与解析结果不一致 | PMS 扫描/注册失败 |
| APK-in-APEX | 外层映射缺失 | 不应按普通 APK 路径注册 |
| active 查询 | module/package 关系不存在 | 返回 null 或空集合 |
| 回滚 | active session 放弃失败 | abort/revert 返回 false,设备状态需进一步检查 |
APEX 的“安装成功”至少包含多个屏障:容器被 apexd 接受、session ready、重启后激活、PMS 扫描注册。任何一个屏障失败,都不能只根据 APK 文件是否存在判断模块可用。
10. 阅读检查
沿源码复述:ApexManager.getAllApexInfos → InstallPackageHelper.scanApexPackages → ApexManager.notifyScanResult → PMS 注册 APEX 与 APK-in-APEX;更新路径则是 submitStagedSession → markStagedSessionReady → 重启激活 → PMS 再次扫描。
然后回答:APEX payload 是由 PMS 挂载吗?submitStagedSession 成功是否立即激活?APK-in-APEX 与外层 APEX 是否使用同一个 scan flag?正确答案分别是“不是,由 apexd 负责”“不是,还需要 ready/激活屏障”“不是,分别使用 SCAN_AS_APEX 和 SCAN_AS_APK_IN_APEX 语义”。
