目录
  1. 1. 一、PKMS 的体系定位
  2. 2. 二、PKMS 启动流程与扫描管线
    1. 2.1. 2.1 SystemServer 中启动 PKMS
    2. 2.2. 2.2 PKMS 构造函数——扫描管线
    3. 2.3. 2.3 scanDirTracedLI——目录扫描
    4. 2.4. 2.4 PackageParser2——APK 解析器
    5. 2.5. 2.5 scanPackageChildLI——深度扫描与登记
  3. 3. 三、APK 安装流程
    1. 3.1. 3.1 安装入口:PackageInstaller Session
    2. 3.2. 3.2 安装的执行阶段
    3. 3.3. 3.3 dexopt 触发
  4. 4. 四、Settings 与 packages.xml
    1. 4.1. 4.1 Settings 类
    2. 4.2. 4.2 packages.xml 内容示例
  5. 5. 五、权限管理
    1. 5.1. 5.1 权限类型
    2. 5.2. 5.2 权限授予逻辑
  6. 6. 六、组件解析(Component Resolution)
  7. 7. 七、核心面试题
    1. 7.1. 6.5 Split APK 与 App Bundle 的处理
    2. 7.2. 6.6 Instant Apps 的支持
    3. 7.3. 6.7 APK 签名方案演进
    4. 7.4. 6.8 PKMS 的性能优化策略
      1. 7.4.1. 8.1 Settings 的读写优化
      2. 7.4.2. 8.2 ComponentResolver 的查询优化
      3. 7.4.3. 8.3 Storage 空间管理
    5. 7.5. 参考总结
【吃透源码系列】之PKMS

PackageManagerService(PKMS)是 Android 系统中负责应用包管理的核心服务。从 APK 的扫描解析、安装卸载,到权限管理、组件解析、共享 UID 管理,PKMS 是整个应用生态的底座。本文基于 Android 11 AOSP 源码,深入分析 PKMS 的启动流程、APK 扫描管线、安装流程、权限模型和关键数据结构。

一、PKMS 的体系定位

PKMS 运行在 system_server 进程中,是引导服务(Bootstrap Service)之一,在 SystemServer 的 startBootstrapServices 阶段被创建,早于 AMS 和 WMS。

SystemServer.startBootstrapServices()
└── PMS.main() // 静态工厂方法
└── PKMS(Context) // 构造函数(执行扫描等主要逻辑)
├── Settings // 所有已安装包配置(packages.xml)
├── PackageParser // APK 解析器(Android 11 改为 PackageParser2)
├── PermissionManagerService // 权限管理(Android 11+ 拆分)
└── Installer // installd 守护进程代理

AOSP 核心路径:

  • 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/PackageInstallerService.java

二、PKMS 启动流程与扫描管线

2.1 SystemServer 中启动 PKMS

// frameworks/base/services/java/com/android/server/SystemServer.java
private void startBootstrapServices() {
// PKMS 在 AMS 之前启动——AMS 需要查询 Package 信息
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
// PKMS 注册到 ServiceManager
ServiceManager.addService("package", mPackageManagerService);
// 首次启动标记
mFirstBoot = mPackageManagerService.isFirstBoot();
// 获取包管理器的客户端接口
mPackageManager = mSystemContext.getPackageManager();
}

2.2 PKMS 构造函数——扫描管线

PKMS 的构造函数是理解 APK 扫描的核心入口。在 Android 11 中,扫描流程大致分为以下几个阶段:

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {

// 阶段1:初始化基础组件
mContext = context;
mInstaller = installer; // installd 的代理
mPmsBgHandler = new Handler(BackgroundThread.getHandler().getLooper());

// 阶段2:创建 Settings 对象(持久化配置管理)
mSettings = new Settings(mPmsBgHandler.getLooper());
// 读取 packages.xml 等持久化文件
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ...);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ...);

// 阶段3:扫描系统 APK
// 依次扫描以下分区:
// 1) /system/framework (framework-res.apk 等)
// 2) /system/priv-app
// 3) /system/app
// 4) /vendor/app 和 /vendor/overlay
// 5) /product/app
// 6) /data/app (已安装的第三方应用)

// 阶段3.1:解析 framework-res.apk(系统资源包)
File frameworkDir = new File(Environment.getRootDirectory(), "framework");

// 阶段3.2:使用 ParallelPackageParser 多线程解析
scanDirTracedLI(systemAppDir, ...);

// 阶段4:处理所有已解析的包
// 阶段5:更新组件解析表
// 阶段6:更新权限信息
updateAllPermissions(null, true);
}

2.3 scanDirTracedLI——目录扫描

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void scanDirTracedLI(File scanDir, int parseFlags, int scanFlags,
long currentTime, PackageParser2 packageParser) {
// 1. 列出目录下所有 APK 文件
final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) return;

// 2. 使用 ParallelPackageParser 并行解析
try (ParallelPackageParser parallelPackageParser =
new ParallelPackageParser(packageParser, mExecutorService)) {
// 提交所有 APK 文件进行解析
for (File file : files) {
parallelPackageParser.submit(file, parseFlags);
}

// 3. 依次处理每个解析结果(保持顺序,因为依赖关系)
for (ParseResult result; (result = parallelPackageParser.take()) != null; ) {
scanPackageChildLI(result.pkg, parseFlags, scanFlags,
currentTime, null /* user */);
}
}
}

ParallelPackageParser 的设计:解析阶段并行(多个 APK 同时解析 AndroidManifest),但注册阶段串行(按顺序调用 scanPackageChildLI)。这是因为注册阶段涉及 Settings 的写操作(插入/更新 PackageSetting),必须保证线程安全。

2.4 PackageParser2——APK 解析器

Android 11 用 PackageParser2 替代了 PackageParser,核心改进是使用了 TypedKeyValue 和增量解析特性:

// frameworks/base/services/core/java/com/android/server/pm/parsing/PackageParser2.java
public class PackageParser2 {
public ParsedPackage parsePackage(File packageFile, int flags)
throws PackageManagerException {
// 1. 获取 ApkLite(轻量级 APK 信息:包名、版本、签名摘要)
ApkLite baseApk = getApkLite(packageFile, flags);

// 2. 解析 AndroidManifest.xml 为二进制 XML(BinaryXml 格式)
ParseInput input = mParsingThreadLocal.get().reset();
// parseMonolithicPackage 解析整个包信息:
// - package name, versionCode, versionName
// - uses-permission, uses-feature
// - application (四大组件声明)
// - activity, service, receiver, provider 的 intent-filter
ParsedPackage pkg = parseMonolithicPackage(input, baseApk, flags);

return pkg;
}
}

2.5 scanPackageChildLI——深度扫描与登记

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private PackageSetting scanPackageChildLI(ParsedPackage pkg, ...) {
// 1. 创建或更新 PackageSetting(持久化配置)
PackageSetting pkgSetting = mSettings.getPackageLPr(pkg.getPackageName());
if (pkgSetting == null) {
pkgSetting = mSettings.insertPackageSettingLPw(pkg, ...);
}

// 2. 处理 SharedUserId(如果有——多 APK 共享 UID)
if (pkg.getSharedUserId() != null) {
SharedUserSetting sharedUser = mSettings.getSharedUserLPw(
pkg.getSharedUserId(), ...);
pkgSetting.setSharedUserSetting(sharedUser);
}

// 3. 验证签名(与已有安装的签名比较)
if (!mSettings.isSignatureMatch(pkgSetting, pkg.getSigningDetails())) {
// 签名不匹配处理:拒绝更新或回滚
}

// 4. 确定 ABI(CPU 架构)—— arm64-v8a / armeabi-v7a 优先级
String primaryCpuAbi = PackageManagerService.computePrimaryCpuAbi(
pkg.getCpuAbiOverride(), scanFlags);

// 5. 将组件信息注册到 ComponentResolver 的解析表中
commitReconciledScanResultLocked(reconciledPkg, mUserManager.getUserIds());

return pkgSetting;
}

三、APK 安装流程

3.1 安装入口:PackageInstaller Session

APK 安装可以通过多种方式触发:PackageInstaller(系统安装器 UI)、adb install、Google Play 等。所有方式最终都通过 PackageInstallerService 的 Session 机制实现。

// frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java
public class PackageInstallerService extends IPackageInstaller.Stub {
// 创建安装会话
public int createSession(SessionParams params, String installerPackageName, int userId) {
// 生成唯一的 sessionId
final int sessionId = allocateSessionIdLocked();
// 创建临时目录 /data/app/vmdl<sessionId>.tmp
File stageDir = buildStageDir(params, sessionId);
// 创建 PackageInstallerSession
mSessions.put(sessionId, session);
return sessionId;
}
}

3.2 安装的执行阶段

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
void installStage(ActiveInstallSession activeInstallSession) {
// 阶段1:复制 APK 到 /data/app 目录
// 阶段2:通过 Handler 在后台线程执行
mHandler.post(() -> {
processInstallRequests(installRequests, ...);
});
}

void processInstallRequests(List<InstallRequest> installRequests, ...) {
// 1. 验证包名、签名、权限是否合法
// 2. 复制 native libs 到 /data/app/<pkg>/lib/
// 3. 执行 dexopt(dex2oat)——生成 OAT + VDEX 文件
// 4. 创建应用数据目录 /data/data/<pkg>/
// 5. 更新 packages.xml(Settings.writeLPr)
// 6. 发送 ACTION_PACKAGE_ADDED 广播
// 7. 通知 AMS 更新组件信息
}

3.3 dexopt 触发

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void performDexopt(List<PackageSetting> pkgSettings, ...) {
for (PackageSetting pkgSetting : pkgSettings) {
mInstaller.dexopt(pkgSetting.getPath(),
pkgSetting.getAppId(),
pkgSetting.getPkgName(),
instructionSet,
dexoptNeeded,
null /*outputPath*/,
dexFlags,
compilerFilter, // speed / speed-profile / verify 等
...);
}
}

四、Settings 与 packages.xml

4.1 Settings 类

Settings 是 PKMS 的持久化配置管理类,管理所有已安装包的状态信息:

// frameworks/base/services/core/java/com/android/server/pm/Settings.java
final class Settings {
// 所有已安装包的配置
final ArrayMap<String, PackageSetting> mPackages = new ArrayMap<>();

// SharedUserId → SharedUserSetting 的映射
final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<>();

// 读取 packages.xml
boolean readLPw(@NonNull List<UserInfo> users) {
FileInputStream str = new FileInputStream(mSettingsFilename);
XmlPullParser parser = Xml.newPullParser();
// 逐条恢复 PackageSetting 信息
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG) {
String tagName = parser.getName();
if (tagName.equals("package")) {
readPackageLPw(parser);
} else if (tagName.equals("shared-user")) {
readSharedUserLPw(parser);
}
}
}
}
}

4.2 packages.xml 内容示例

packages.xml 位于 /data/system/packages.xml

<package name="com.example.app" codePath="/data/app/com.example.app-xxx"
nativeLibraryPath="/data/app/com.example.app-xxx/lib"
primaryCpuAbi="arm64-v8a" userId="10123" ...>
<sigs count="1">
<cert index="0" key="..."/>
</sigs>
<perms>
<item name="android.permission.INTERNET" granted="true" flags="0"/>
</perms>
</package>

五、权限管理

5.1 权限类型

权限级别 说明 授予方式
NORMAL 低风险权限 安装时自动授予
DANGEROUS 涉及隐私/安全 运行时请求用户授权
SIGNATURE 系统级权限 签名匹配自动授予
SIGNATURE_OR_SYSTEM 系统或同签名应用 签名匹配自动授予

5.2 权限授予逻辑

// frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
private int grantPermissions(PackageState packageState, ...) {
for (String permName : allPermissions) {
Permission permission = mPermissionRegistry.getPermission(permName);
if (permission == null) continue;

if (permission.getProtection() == PermissionInfo.PROTECTION_NORMAL) {
grantPermission(packageState, permName, userId); // 自动授予
} else if (permission.getProtection() == PermissionInfo.PROTECTION_DANGEROUS) {
if (hasUserApproval || restoredFromBackup) {
grantPermission(packageState, permName, userId); // 用户同意
}
} else if (permission.getProtection() == PermissionInfo.PROTECTION_SIGNATURE) {
if (packageState.isSignedWithPlatformKey()) {
grantPermission(packageState, permName, userId); // 签名匹配
}
}
}
}

六、组件解析(Component Resolution)

当应用通过 PackageManager.queryIntentActivities() 查询能处理某个 Intent 的 Activity 时,PKMS 通过 ComponentResolver 高效地进行组件匹配。

// frameworks/base/services/core/java/com/android/server/pm/ComponentResolver.java
public class ComponentResolver {
private final ActivityIntentResolver mActivities;
private final ServiceIntentResolver mServices;
private final ReceiverIntentResolver mReceivers;
private final ProviderIntentResolver mProviders;

List<ResolveInfo> queryActivities(Intent intent, String resolvedType,
int flags, int userId) {
return mActivities.queryIntent(intent, resolvedType, flags, userId);
}
}

根 IntentResolver 的匹配逻辑:

  1. 匹配 Action(如果 Intent 指定了 action)
  2. 匹配 Category(所有 category 都要匹配——“ALL” 语义)
  3. 匹配 Data(URI scheme、host、path、mimeType)
  4. 按优先级排序(priority 值和 preferred activity 记录)

七、核心面试题

Q1:PKMS 如何实现应用间共享 UID?它的安全风险是什么?

PKMS 通过 SharedUserSetting 管理共享 UID。当多个 APK 声明相同的 android:sharedUserId 且使用相同的签名时,它们会被分配到相同的 Linux UID,从而可以相互访问对方的文件。风险:如果一个共享 UID 的应用存在安全漏洞,同 UID 的所有应用的数据都可能被利用。Android 11+ 进一步收紧了 sharedUserId 的使用。

Q2:PKMS 中的 packages.xml 和 packages.list 分别存储什么信息?

packages.xml 存储所有已安装应用的完整元数据:包名、代码路径、native 库路径、签名证书、授予的权限、SharedUserId 信息等。packages.list 以简洁格式存储包名和 UID 映射关系:<pkgName> <uid> <dataDir>,供 installd 和 lmkd 快速查询。

Q3:Android 11 中 PermissionManagerService 为什么从 PKMS 中独立出来?

(1) 权限检查是高频操作,独立成单独服务后可以更好地优化;(2) 分离权限管理代码使得代码边界更清晰,便于安全审计;(3) 为未来的权限模型演进(如 Scoped Storage、One-Time Permissions、App Hibernation)提供更灵活的基础。

Q4:ParallelPackageParser 如何保证解析效率的同时又保证注册的安全?

解析阶段并行(多个线程同时解析 APK 的 AndroidManifest.xml — CPU 密集型),但注册阶段串行(通过 parallelPackageParser.take() 按序获取结果并调用 scanPackageChildLI)。这样解析效率由多核并行加速,而注册的 Settings 写操作保持单线程安全,避免了锁竞争。

Q5:PKMS 如何处理 APK 更新的签名校验?

PKMS 在 scanPackageChildLI 中调用 mSettings.isSignatureMatch() 比较新旧 APK 的签名。更新要求新签名与已安装的签名一致(除非是系统应用升级且系统签名允许覆盖)。对于使用 sharedUserId 的应用,新签名还必须与 sharedUser 中其他应用的签名一致。签名校验使用公钥比较,不是简单的证书指纹比较。

6.5 Split APK 与 App Bundle 的处理

Google Play 的 App Bundle 将 APK 拆分为 base module 和多个 feature module。PKMS 需要处理 split APK 的安装:

// PackageManagerService.java
// Split APK 安装时,多个 APK 分属同一个 packageName
// PKMS 通过 PackageSetting.splitNames 跟踪已安装的 splits
private void scanPackageOnlyLI(PackageSetting pkgSetting, ...) {
// 检查 split APK 的依赖关系
// base.apk 需要先于 split 模块安装
// 所有 split 必须有相同的签名
// split 的 versionCode 必须与 base 一致
}

每个 split APK 有自己的 AndroidManifest.xml,但其 package 属性必须与 base APK 一致。PKMS 在扫描时合并所有 split 的组件声明到同一个 Package 对象中。

6.6 Instant Apps 的支持

Android Instant Apps 允许用户在不安装完整 APK 的情况下试用应用:

// PackageManagerService.java
// Instant App 以特殊的 flag (INSTANT_APP) 安装
// 它们有受限的权限列表——不能使用大多数危险权限
// 它们的安装目录在 /data/app-ephemeral/ 而非 /data/app/

PKMS 通过 isInstantApp(packageName, userId) 判断是否为 Instant App,并在权限授权、组件查询时施加限制。

6.7 APK 签名方案演进

Android 支持三种签名方案:

方案 引入版本 签名方式 特点
V1 (JAR) Android 1.0 对 APK 内每个文件签名 最兼容,但验证慢
V2 (APK Signature) Android 7.0 对整个 APK 签名 验证快,保护 APK 完整性
V3 (APK Signature) Android 9.0 支持签名密钥轮换 允许更换签名密钥而不丢失更新能力

PKMS 在安装时验证签名方案:

// frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
static PackageParser.SigningDetails verifySignatures(
PackageSetting pkgSetting, PackageSetting disabledPkgSetting,
PackageParser.SigningDetails parsedSignatures, ...)
throws PackageManagerException {
// 1. 检查签名是否与已安装版本匹配
// 2. 如果是 sharedUserId,检查是否与 sharedUser 中其他应用签名一致
// 3. 对于 V3 签名,检查密钥轮换历史链
}

6.8 PKMS 的性能优化策略

8.1 Settings 的读写优化

packages.xml 的读写是 PKMS 启动中的主要耗时操作之一(尤其是在安装了数百个应用后)。优化策略:

  1. 增量写入Settings.writeLPr() 不是每次都全量覆写。正常情况下使用 writePendingPackagesLPw() 只写入有变更的包。
  2. 异步写入:写入操作在 mPmsBgHandler(后台线程)上执行,不阻塞主线程。
  3. 二进制缓存:Android 11 引入 packages.xml.bin(二进制格式的缓存),在下一次启动时优先从 bin 读取,解析速度比 XML 快 3-5 倍。

8.2 ComponentResolver 的查询优化

ComponentResolver 使用多层索引加速 Intent 匹配:

// ComponentResolver.java 内部数据结构
// Action → Map<ComponentName, ActivityIntentInfo>
// 每个 ActivityIntentInfo 包含一组 IntentFilter
//
// 查询流程:
// 1. 如果 Intent 指定了 packageName → 直接从 mComponents 查找(O(1))
// 2. 如果 Intent 指定了 action → 从 mActionToComponents 查找(O(1))
// 3. 逐一匹配 category(AND 逻辑)
// 4. 如果指定了 data URI → 进一步过滤 mimeType/scheme/host/path

查询结果缓存:PackageManager.queryIntentActivities() 的结果会缓存一段时间(通过 PackageManagerService.snapshot() 机制)。

8.3 Storage 空间管理

PKMS 监控 /data 分区空间:

// PackageManagerService.java
static final long LOW_STORAGE_THRESHOLD_BYTES = 500 * 1024 * 1024; // 500MB

private void checkStorageThreshold() {
StatFs stat = new StatFs(Environment.getDataDirectory().getAbsolutePath());
long availableBytes = stat.getAvailableBytes();
if (availableBytes < LOW_STORAGE_THRESHOLD_BYTES) {
// 发送 ACTION_DEVICE_STORAGE_LOW 广播
// 暂停非关键的后台 dexopt
// 清理安装失败的残留文件
}
}

AOSP 核心路径参考:

  • 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/ComponentResolver.java
  • frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java
  • frameworks/base/services/core/java/com/android/server/pm/parsing/PackageParser2.java
  • frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

参考总结

PKMS 是 Android 系统中代码量最大(约 3 万行+)的系统服务之一。它承载了包管理、权限、组件解析三大职责。理解 PKMS 不仅是面试的需要,更是深入理解 Android 应用生命周期、安全模型和组件交互的基础。

关键设计理念:

  1. 分阶段扫描:先解析(并行)再注册(串行),在速度和正确性间平衡
  2. 持久化优先:所有包信息先写入 packages.xml 再通知其他服务——确保崩溃恢复时数据完整
  3. 权限分离:Android 11 将权限管理从 PKMS 分离,体现了单一职责原则
  4. 兼容性包袱:PKMS 必须支持从 Android 1.0 至今的所有签名方案(V1-V3)和安装模式,这是它代码量大的原因之一
打赏
  • 微信
  • 支付宝

评论