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 上。
二、方法流程:从 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 下的应用都不可卸载。实现思路是:
- 在
PackageParser.parseBaseApk()中读取applicationInfo.flags,识别FLAG_SYSTEM。 - 在
scanPackageLI()中,当检测到系统应用时,给PackageSetting.pkgFlags追加自定义标志位。 - 在
deletePackage()入口处校验该标志位,命中则直接返回失败。
提示:
PackageSetting 继承自 SettingBase,而 SettingBase 又持有 PermissionState 集合。因此修改 PackageSetting 时,务必同步考虑权限状态的持久化,否则会出现“应用不可卸载但权限仍可被撤销”的不一致。
五、误区与复盘
常见误区一:把
常见误区二:直接修改
常见误区三:忽略
PackageParser.Package 当作长期缓存对象。实际上每次 scanPackageLI 都会重新解析,真正跨重启的是 PackageSetting。常见误区二:直接修改
mPackages 而不写回 mSettings。PMS 在 writeSettingsLPr() 时以 Settings 为准,内存改动会在下次重启后丢失。常见误区三:忽略
SharedUserSetting 的引用计数。多个应用共享 UID 时,删除其中一个应用不能直接移除 SharedUserSetting,否则会破坏其余应用的 UID 映射。
复盘时建议按“解析是否成功 → Setting 是否落盘 → 运行时集合是否一致 → 权限状态是否同步”四步排查。掌握这四层数据结构的边界,后续无论是定制安装流程、修改权限模型,还是实现应用白名单,都能快速定位到正确的修改点。