Skip to content

PMS 全模块总结与知识图谱

以 Android 17 真实入口和消费者串起 PackageManagerService 的状态、安装、扫描、ART、APEX 与恢复链路。

基于android-17.0.0_r1
AndroidPMSPackageManagerSourceReading

PMS 全模块总结与知识图谱 ​

这篇文章不是把前面的文章标题重新排列,而是给出一张可以实际走源码的地图。读者需要先了解 Binder、SystemServer 和基本的 APK 安装概念;前置可读 PMS 启动时序、ComputerEngine 查询引擎、安装全景 和 dexopt 流程总览。

本文不再解释每个专题的全部字段。它回答的是四个跨模块问题:一个包从哪里进入 PMS,状态由谁拥有,状态变化由谁消费,以及失败时应该沿哪条反向路径定位。所有源码路径均对应 Android 17 android-17.0.0_r1。

1. 先找入口 ​

读 PMS 时先按事件选择入口,不要从 PackageManagerService 的成员变量列表开始。真实入口可以分成四类:

事件第一处源码首要消费者
system_server 启动PackageManagerService 构造函数、InitAppsHelper.initSystemAppsSettings、扫描器、ApexManager
安装/更新PackageInstallerService、PackageInstallerSessionInstallingSession、InstallPackageHelper
查询/状态修改PackageManagerService.IPackageManagerImpl、PackageManagerInternalComputer、PackageStateMutator
运行后反馈notifyDexLoad、UsageStats、CrashRecovery observerDexUseManagerLocal、AppHibernation、PackageWatchdog

入口选择决定你应该先追“事务”还是“快照”。安装和卸载是写路径;查询、dex load 和健康监控通常先取得 snapshot,再把结果交给专门 owner。

2. 状态的唯一 owner ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java、frameworks/base/services/core/java/com/android/server/pm/Settings.java、frameworks/base/services/core/java/com/android/server/pm/ComputerEngine.java。

PMS 的长期包状态最终落在 Settings 管理的 PackageSetting/PackageState 体系;查询不会直接遍历可变对象,而是通过 Computer 读取一个一致视图。写路径使用 commitPackageStateMutation:

java
public PackageStateMutator.Result commitPackageStateMutation(
        PackageStateMutator.InitialState initialState,
        String packageName, Consumer<PackageStateWrite> consumer) {
    synchronized (mLock) {
        PackageStateMutator mutator = new PackageStateMutator(initialState);
        consumer.accept(mutator.forPackage(packageName));
        PackageStateMutator.Result result = mutator.commit(
                mSettings.getPackagesLocked());
        if (result.isCommitted()) {
            scheduleWritePackageRestrictions(/* userId */ UserHandle.USER_ALL);
        }
        return result;
    }
}

阅读时要区分三层对象:

  • AndroidPackage:解析后的不可变 APK 描述,偏代码和 manifest。
  • PackageState/PackageSetting:安装身份、版本、用户状态、路径和运行时包状态。
  • Computer:对外查询使用的快照视图,避免调用方在锁外读到半次 mutation。

packages.xml 等持久化文件不是状态 owner;它们只是 Settings 的落盘形式。看到 XML 改变时,应反向搜索哪个 mutation 产生了字段变化,再追写盘调度。

3. 启动汇合点 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java、frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java。

启动阶段先恢复 Settings 和系统配置,之后 InitAppsHelper 扫描 APEX 与 APK。Android 17 的 APEX 路径不是普通 data-app 扫描的旁路:

java
List<ApexManager.ScanResult> apexScanResults =
        scanApexPackagesTraced(packageParser);
mApexManager.notifyScanResult(apexScanResults);

for (ApexManager.ActiveApexInfo apexInfo
        : mApexManager.getActiveApexInfos()) {
    String apexPackageName = apexInfo.moduleName;
    for (String packageName : mApexManager.getApksInApex(apexPackageName)) {
        // 将 APK-in-APEX 纳入包状态和可见性计算
    }
}

之后普通分区的扫描结果进入 InstallPackageHelper/ScanPackageUtils,并与已恢复的 package state 合并。启动的关键屏障不是“所有文件都读完”,而是包代码路径、用户状态和 APEX 激活结果都能供后续 Computer 查询。

如果问题发生在早期启动,先看 InitAppsHelper 的扫描结果;如果包已经可查询但用户状态错误,再转向 PackageSetting 和 PackageUserState;如果只有 APK-in-APEX 不可见,继续看 ApexManager.notifyScanResult 后的关联逻辑。

4. 安装的写路径 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java、frameworks/base/services/core/java/com/android/server/pm/InstallingSession.java、frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java。

安装不是一个函数调用,而是会话、校验、文件移动、包状态提交和后续优化的链:

其中 PackageInstallerSession 拥有会话文件和提交状态;InstallPackageHelper 拥有 PMS 侧的包安装事务;PackageState 拥有提交后的身份。不要把 session 的 isCommitted() 当作包已经可查询,必须继续追到 mutation 和包扫描结果。

安装后 dexopt 是后续消费者,不是状态提交的一部分。InstallPackageHelper 在适当条件下调用 performDexoptIfNeededAsync,这解释了安装成功但 oat/vdex 尚未生成的现象。

5. 查询与快照 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java、frameworks/base/services/core/java/com/android/server/pm/ComputerEngine.java。

Binder 查询通常取得 Computer/snapshotComputer(),再由查询对象完成可见性、用户和组件过滤。写路径则在 mLock 内提交 mutation,再让后续 snapshot 看到新 revision。

这套设计带来一个调试规则:

  1. 先确认调用使用的是哪个 snapshot,以及它创建于 mutation 之前还是之后;
  2. 再检查 shouldFilterApplication、canQueryPackage 和 userId;
  3. 最后才判断包状态本身是否错误。

很多“安装成功但查询不到”其实是 visibility 过滤,不是安装事务丢失。反向证据应使用同一 user、同一 calling UID 的查询,而不是只看 system UID 的 dump。

6. ART 与运行时反馈 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/DexOptHelper.java、art/libartservice/service/java/com/android/server/art/DexUseManagerLocal.java。

PMS 的 dexopt owner 是 DexOptHelper/ART Service;运行时 dex 使用由 DexUseManagerLocal 记录。两条链的连接点是包状态和 dex 路径,而不是旧式的单一 DCL 类。

java
DexUseManagerLocal dexUseManager = DexOptHelper.getDexUseManagerLocal();
try (PackageManagerLocal.FilteredSnapshot filteredSnapshot =
        LocalManagerRegistry.getManager(PackageManagerLocal.class)
                .withUnownedFilteredSnapshot(snapshot)) {
    dexUseManager.notifyDexContainersLoaded(
            filteredSnapshot, loadingPackageName,
            classLoaderContextByDexContainerFile);
}

DexUseManagerLocal 通过 owner 查找把路径归到 primary 或 secondary dex,保存 loader、user、ABI 和 class-loader context,再由后台 dexopt 消费。其 protobuf 持久化和后台调度与 DynamicCodeLogger 的 SafetyNet DCL 文本记录相互独立。

阅读 ART 问题时应使用两条证据:getCheckedSecondaryDexInfo/dexopt 日志证明 ART 是否认识该 dex;DynamicCodeLogger 事件证明安全日志是否记录过动态加载。一个有记录不代表另一个也有记录。

7. 用户与休眠 ​

源码文件:frameworks/base/services/core/java/com/android/server/apphibernation/AppHibernationService.java、frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java。

用户级状态由 AppHibernationService 按 user 保存,进入休眠后 force-stop 并清理缓存;全局状态可通过 PackageManagerInternal.deleteOatArtifactsOfPackage 让 ART 删除编译产物。PMS 通过 LocalServices 联动 unstop:

java
AppHibernationManagerInternal ah =
        mInjector.getLocalService(AppHibernationManagerInternal.class);
if (ah != null && ah.isHibernatingForUser(packageName, userId)) {
    ah.setHibernatingForUser(packageName, userId, false);
    ah.setHibernatingGlobally(packageName, false);
}

这段代码说明休眠状态不是 PackageUserState 的简单布尔字段。它由独立服务拥有,但 PMS 的 stopped 状态变化会触发解除。定位休眠问题时要同时看 user CE 的 hibernation proto、PMS stopped state 和 ActivityManager force-stop 记录。

8. APEX 与包身份 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/ApexManager.java、frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java。

ApexManager 是 PMS 与 apexd 的边界,负责 active APEX、session、APK-in-APEX 和 module name 查询。PMS 不直接解析 APEX 激活事务的底层状态,而是在扫描阶段取得 ActiveApexInfo/ScanResult,将其中的包纳入自己的包模型。

这解释了两个常见误区:

  • APEX session 已 ready 不等于 APK-in-APEX 已经在 PMS 查询表中;还要经过扫描结果提交。
  • APEX 回滚由 Rollback/ apexd 协作完成,不等于普通 APK PackageInstallerSession 的文件替换。

跨 APEX/APK 问题应沿“session → apexd active state → ApexManager 查询 → PMS scan result → PackageState”顺序取证。

9. 恢复与故障链 ​

源码文件:platform/packages/modules/CrashRecovery/service/java/com/android/server/PackageWatchdog.java、platform/packages/modules/CrashRecovery/service/java/com/android/server/rollback/RollbackPackageHealthObserver.java。

PackageWatchdog 只负责失败窗口、observer 选择和调用时机。Rollback observer 再查询可用 rollback,按用户影响等级选择包或 APEX 回滚;RescueParty 可能返回更低 impact,因此最终动作不是固定的 rollback。

MITIGATION_RESULT_SUCCESS 只表示 observer 接受并排队动作。要验证恢复完成,还需继续看 RollbackManager session、staged reboot、PMS 再扫描和 PackageWatchdog 后续失败计数。

10. 状态持久化地图 ​

跨模块阅读时,文件名本身比“数据库”这个泛称更有用:

状态主要 owner持久化/证据恢复方式
包身份与安装状态Settings / PackageStatepackages settings 与 restrictions启动扫描、mutation reconcile
dex 使用DexUseManagerLocal/data/system/package-dex-usage.pbART Service cleanup
DCL 安全记录PackageDynamicCodeLoadingpackage-dcl.list包/user sync、文件 hash
休眠状态AppHibernationServicesystem/system_ce 下 hibernation protopackage/stopped reconcile
watchdog observerPackageWatchdogAtomicFile XML、boot metadataobserver 重新注册、BootThreshold
APEX 激活ApexManager/apexdstaged session 与 apexd 状态下次启动激活/回滚后重扫

持久化文件是恢复输入,不是最终事实。每个 owner 都有一次启动验证或 cleanup:包状态要和扫描结果一致,dex 记录要和文件可见性一致,休眠要和 stopped 状态一致,watchdog 要和重新注册的 observer 一致。

11. 一条完整阅读路线 ​

面对一个“安装后不可用”的问题,可以按以下源码顺序走:

  1. 从 PackageInstallerSession.commit 确认 session 是否进入提交路径;
  2. 进入 InstallingSession 和 InstallPackageHelper,确认解析、签名、复制和 commitPackageStateMutation;
  3. 用同一 user/UID 创建 Computer 查询,确认包可见性和 PackageUserState;
  4. 若启动失败,查看 DexOptHelper、ART artifact 状态和 DexUseManagerLocal 的 CLC/ABI;
  5. 若是 APEX/APK-in-APEX,回到 ApexManager active/session 与 InitAppsHelper scan result;
  6. 若反复崩溃,查看 PackageWatchdog failure reason、impact 和 Rollback/RescueParty 的执行 session。

这条路线的关键是每一步都问“谁拥有状态,谁消费变化”。不要用 pm list packages 的一次输出替代安装事务、用户过滤和 ART 编译三种不同证据。

12. 结尾练习 ​

  1. 选择一个已安装包,分别画出“包状态查询”和“dex 使用记录”的 owner/consumer;指出为什么两者都能提到同一个 APK 路径,却不共享同一持久化文件。
  2. 给定“APEX session 已提交但包不可见”的现象,按 ApexManager、InitAppsHelper、PackageState 和 Computer 的顺序列出四个检查点。
  3. 给定“应用五次崩溃但没有回滚”,区分 failure window 未达到、impact threshold 阻止、RescueParty 被选中和 rollback session 异步失败四种可能。
  4. 修改某个用户的 stopped/hibernation 状态后,预测哪些服务会收到联动:PMS、ActivityManager、AppHibernationService、ART Service 中哪些会变化,哪些不会。