4. APK安装流程源码分析

理解 APK 安装流程,是 PackageManager 系统定制的起点。无论是预置应用、静默安装、增量安装,还是定制安装校验策略,最终都要落到 PackageInstaller 与 PackageManagerService(PMS)这条主链路上。本节从源码调用顺序出发,把安装拆成五个阶段:安装请求、会话创建、文件拷贝、解析校验、提交落库,并说明每个阶段可定制的切入点。

一、核心原理:安装是一次跨进程的会话式提交

应用层调用 PackageInstaller 发起安装时,真正执行安装的是 system_server 中的 PMS。二者通过 Binder 通信,并以 PackageInstallerSession 作为中间载体。安装不是一次函数调用就完成,而是“先建会话、再写数据、最后 commit”的三段式过程,这样设计是为了支持分片传输、断点续传、多 APK 拆分包以及安装前的用户确认。

关键角色分工如下:

  • PackageInstaller:应用可访问的入口,负责创建会话、写入 APK 数据、发起 commit。
  • PackageInstallerService:运行在 system_server,管理所有会话的生命周期与持久化。
  • PackageInstallerSession:单个安装会话的状态机,负责暂存文件、校验参数、触发 PMS 安装。
  • PackageManagerService:真正的安装执行者,负责解析、校验、优化、落库与广播。
  • Installd:native 守护进程,负责创建应用数据目录、dexopt、seinfo 上下文等底层操作。
安装的本质:PackageInstaller 负责“把 APK 安全地送到系统”,PMS 负责“把 APK 合法地变成已安装应用”。定制点通常分布在会话参数、校验策略、安装参数与落库结果四个位置。

二、方法流程:五个阶段的源码链路

下面按调用顺序梳理主链路,括号内为常见类与方法名,便于在 AOSP 中定位。

  1. 安装请求:应用调用 PackageInstaller.createSession(),传入 SessionParams(安装位置、安装原因、是否豁免校验等)。该请求经 Binder 进入 PackageInstallerService.createSessionInternal(),生成 sessionId 并持久化到 /data/system/install_sessions.xml。
  2. 会话创建:PackageInstallerSession 被创建并进入 SESSION_CREATED 状态,同时准备好暂存目录 /data/app/vmdl<sessionId>.tmp。
  3. 文件拷贝:应用通过 openWrite() 拿到输出流,把 APK 写入暂存目录;若是拆分包,则按 split 名称分别写入。此阶段只做落盘,不做解析。
  4. 解析与校验:commit 后进入 PackageInstallerSession.commit(),最终调用 PackageManagerService.installStage(),在 PackageInstaller 的 handler 线程中执行 installPackagesTracedLI(),完成 PackageParser 解析、签名校验、SDK 版本与权限校验、ABI 兼容性检查。
  5. 提交落库:校验通过后,PMS 调用 Installd 创建数据目录并执行 dexopt,随后把 PackageSetting 写入 packages.xml,更新 PackageManagerService.mPackages 内存映射,最后发送 ACTION_PACKAGE_ADDED 广播。
APK 安装主链路:请求 → 会话 → 拷贝 → 解析校验 → 提交 应用进程 system_server Installd / 存储 ① 安装请求 createSession(SessionParams) Binder ② 会话创建 PackageInstallerSession ③ 文件拷贝 openWrite() 写入 APK 流式写入 暂存目录 /data/app/vmdl<id>.tmp 落盘 APK 暂存文件 base.apk + split_*.apk ④ 解析与校验 PackageParser.parsePackage 签名 / SDK / 权限 / ABI commit() ⑤ 提交落库 PackageSetting → packages.xml 发送 ACTION_PACKAGE_ADDED installd 数据目录与 dexopt /data/data/<pkg> /data/app/<pkg>-* 常见定制切入点 • SessionParams 默认参数 • 安装来源与豁免校验 • 签名校验策略 • 安装位置与 dexopt 参数 • 落库字段与广播行为

三、案例场景:一次静默安装的完整调用

以系统应用调用静默安装为例,典型代码如下:

PackageInstaller installer = context.getPackageManager().getPackageInstaller();
SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
params.setInstallLocation(PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
params.setInstallReason(PackageManager.INSTALL_REASON_POLICY);
int sessionId = installer.createSession(params);
try (OutputStream out = installer.openSession(sessionId).openWrite("base.apk", 0, apkLength)) {
    copy(apkFile, out);
}
installer.getSession(sessionId).commit(IntentSender);

对应日志中可观察到 PackageInstallerSession 状态迁移:SESSION_CREATED → SESSION_OPEN → SESSION_SEALED → SESSION_COMMITTED。若在 commit 后失败,日志通常出现在 PackageManagerService 的 installPackagesTracedLI 阶段,例如签名不匹配、INSTALL_FAILED_VERSION_DOWNGRADE 或 INSTALL_FAILED_INVALID_APK。

四、关键阶段与定制点对照

阶段 核心类/方法 关键产物 可定制方向
安装请求 PackageInstaller.createSession SessionParams、sessionId 默认安装位置、安装原因、豁免校验
会话创建 PackageInstallerService.createSessionInternal install_sessions.xml 会话持久化策略、并发会话数
文件拷贝 PackageInstallerSession.openWrite vmdl<id>.tmp 下的 APK 暂存路径、分片写入校验
解析校验 PackageParser / PackageManagerService.installStage PackageParser.Package 签名校验、SDK 与权限白名单
提交落库 Installd、packages.xml 写入 PackageSetting、数据目录 dexopt 参数、广播与默认权限

五、误区与复盘

误区一:把 createSession 当成安装完成。createSession 只创建会话,APK 尚未进入 PMS 解析流程,此时取消不会产生任何安装副作用。

误区二:在 openWrite 阶段做签名校验。该阶段只有文件流,没有解析结果,签名校验必须放到 PMS 的解析阶段,否则会误判拆分包与 v2/v3 签名。

误区三:忽略 sessionId 与暂存目录的对应关系。定制清理逻辑时若直接删除 /data/app/vmdl*.tmp,可能破坏正在进行的会话,导致 commit 失败。

复盘建议:抓取 logcat -s PackageInstaller PackageManager,按 sessionId 串联五个阶段;再对照 /data/system/packages.xml 与 /data/system/install_sessions.xml,即可确认安装是卡在拷贝、解析还是落库阶段。掌握这条链路后,后续的静默安装、预置应用、增量安装与安装白名单定制,都可以在对应阶段精准切入。