3. PackageManager核心数据结构

PackageManagerService(PMS)在开机扫描、安装、查询、权限校验的整个生命周期中,并不是直接操作 APK 文件,而是围绕一组核心数据结构展开。理解这些类的职责边界与相互引用关系,是定制 PMS 的第一步。本节从“解析—持久化—运行时—权限”四条线索出发,梳理 Package、PackageParser、PackageSetting、SharedUserSetting、Permission 等关键类。

一、核心原理:四层数据模型

PMS 的数据结构可以按“来源”和“生命周期”分为四层:

  • 解析层:PackageParser 及其内部类 Package、Activity、Service、Provider、Permission 等,负责把 AndroidManifest.xml 解析成内存对象。
  • 持久化层:PackageSetting、SharedUserSetting、PermissionSetting 等继承自 SettingBase,对应 /data/system/packages.xml 中的记录。
  • 运行时层:PackageManagerService 内部维护的 mPackages、mSettings、mSharedUsers 等集合,是查询与安装的入口。
  • 权限层:BasePermission、Permission、PermissionState 描述权限定义与授予状态。

这四层并非孤立:解析层产出 PackageParser.Package,PMS 再据此创建或更新 PackageSetting,而 PackageSetting 又通过 sharedUser 字段关联 SharedUserSetting,权限则通过 PermissionState 挂载到 SettingBase 上。

PackageManager 核心数据结构关系图 解析层(内存对象,来自 AndroidManifest.xml) PackageParser parsePackage() PackageParser.Package packageName / activities Activity / Service Provider / Receiver PermissionInfo uses-permission 持久化层(packages.xml 映射) PackageSetting pkgName / codePath sharedUser / pkgFlags SharedUserSetting name / userId packages 集合 PermissionSetting PermissionState granted / flags SettingBase 基类 运行时层(PackageManagerService 内存集合) mPackages Map<String, Package> 查询入口 mSettings Settings 对象 mPackages / mSharedUsers mSharedUsers Map<String, SharedUserSetting> 共享 UID 管理 mPermissions 权限定义 集合 sharedUser

二、方法流程:从 APK 到内存对象

以一次开机扫描为例,核心调用链如下:

PackageManagerService.scanDirTracedLI()
  └─ scanPackageTracedLI()
       └─ PackageParser.parsePackage(File, int flags)
            ├─ parseBaseApk()        // 解析 AndroidManifest.xml
            ├─ parseUsesPermission() // 收集 uses-permission
            └─ 返回 PackageParser.Package
       └─ scanPackageLI()
            ├─ mSettings.getPackageLPr(pkgName)  // 查 PackageSetting
            ├─ mSettings.getSharedUserLPw()      // 查/建 SharedUserSetting
            └─ mPackages.put(pkgName, pkg)       // 写入运行时集合

可以看到,PackageParser.Package 是“临时产物”,而 PackageSetting 才是跨重启保留的“档案”。定制时若只改解析逻辑而不更新 Setting,重启后修改会丢失。

三、关键类职责对照

类名 所在层 核心字段 定制关注点
PackageParser.Package 解析层 packageName、codePath、activities、permissions 新增标签解析、修改 manifest 语义
PackageSetting 持久化层 name、codePath、sharedUser、pkgFlags 安装状态、签名、UID 分配
SharedUserSetting 持久化层 name、userId、packages 共享 UID 的合并与拆分
BasePermission 权限层 name、protectionLevel、permState 权限定义与保护级别
PermissionState 权限层 granted、flags、userId 运行时授权状态

四、案例场景:定制“预置应用不可卸载”

假设要定制一个规则:所有 /system/priv-app 下的应用都不可卸载。实现思路是:

  1. 在 PackageParser.parseBaseApk() 中读取 applicationInfo.flags,识别 FLAG_SYSTEM。
  2. 在 scanPackageLI() 中,当检测到系统应用时,给 PackageSetting.pkgFlags 追加自定义标志位。
  3. 在 deletePackage() 入口处校验该标志位,命中则直接返回失败。
提示:PackageSetting 继承自 SettingBase,而 SettingBase 又持有 PermissionState 集合。因此修改 PackageSetting 时,务必同步考虑权限状态的持久化,否则会出现“应用不可卸载但权限仍可被撤销”的不一致。

五、误区与复盘

常见误区一:把 PackageParser.Package 当作长期缓存对象。实际上每次 scanPackageLI 都会重新解析,真正跨重启的是 PackageSetting。
常见误区二:直接修改 mPackages 而不写回 mSettings。PMS 在 writeSettingsLPr() 时以 Settings 为准,内存改动会在下次重启后丢失。
常见误区三:忽略 SharedUserSetting 的引用计数。多个应用共享 UID 时,删除其中一个应用不能直接移除 SharedUserSetting,否则会破坏其余应用的 UID 映射。

复盘时建议按“解析是否成功 → Setting 是否落盘 → 运行时集合是否一致 → 权限状态是否同步”四步排查。掌握这四层数据结构的边界,后续无论是定制安装流程、修改权限模型,还是实现应用白名单,都能快速定位到正确的修改点。