Android16 应用安装流程源码分析

基于 AN16(Android 16)代码


一、整体流程总览

复制代码
用户点击 APK
       │
       ▼
  InstallStart          入口 Activity,权限检查 + 路由分发
       │
       ├── content:// URI
       │       ▼
       │  InstallStaging   异步拷贝 APK 到 Session 临时目录
       │       │
       └── package:// URI
               ▼
  PackageInstallerActivity   解析 APK,显示安装确认对话框
               │  用户点击"安装"
               ▼
      InstallInstalling       创建/打开 Session,commit 到 PMS
               │  session.commit()
               ▼
  PackageInstallerSession     验证 → seal → verify → install
               │  installNonStaged()
               ▼
      InstallingSession       准备安装参数,检查存储空间
               │  installPackagesTraced()
               ▼
  InstallPackageHelper        PMS 核心五步安装
               │
       ┌───────┴───────┐
       ▼               ▼
  InstallSuccess   InstallFailed
  显示打开/完成     显示错误信息

二、各阶段详解

阶段一:InstallStart(入口路由)

文件packages/PackageInstaller/src/.../InstallStart.java

职责:权限校验 + 路由分发

步骤 内容
获取调用者信息 getLaunchedFromPackage() / getLaunchedFromUid()
权限检查 是否持有 INSTALL_PACKAGES,是否声明 REQUEST_INSTALL_PACKAGES
设备策略检查 DISALLOW_INSTALL_APPSDISALLOW_INSTALL_UNKNOWN_SOURCES
SKG 定制 检查 persist.sys.skg.is3rdNotAllowInstall,为 true 则拒绝并 Toast
路由分发 content://InstallStagingpackage://PackageInstallerActivity

阶段二:InstallStaging(文件暂存)

文件packages/PackageInstaller/src/.../InstallStaging.java

触发条件 :APK 来自 content:// URI(如文件管理器分享)

目的:将文件拷贝到 PackageInstaller 的 Session 目录,防止安装过程中源文件被篡改。

复制代码
onCreate()
  └── 显示拷贝进度对话框

onResume()
  └── 创建 PackageInstaller.Session,设置 SessionParams

StagingAsyncTask.doInBackground()
  ├── installer.openSession(stagedSessionId)
  ├── getContentResolver().openInputStream(packageUri)
  ├── session.openWrite() 获取输出流
  ├── 循环读写(1MB buffer),更新进度
  └── session.fsync() 确保落盘

onPostExecute()
  └── 跳转 DeleteStagedFileOnResult → PackageInstallerActivity

阶段三:PackageInstallerActivity(安装确认)

文件packages/PackageInstaller/src/.../PackageInstallerActivity.java

职责:解析 APK 信息,显示"是否安装"确认界面。

复制代码
onCreate()
  ├── 解析 APK(包名、版本、图标、权限列表)
  └── processAppSnippet()

onResume() → checkIfAllowedAndInitiateInstall()
  ├── [SKG] getFileManagerPermission() 对 com.skg.filemanager 特殊处理
  ├── 受信任来源 → 直接 initiateInstall()
  └── 未知来源 → 检查 OP_REQUEST_INSTALL_PACKAGES app op
        ├── MODE_ALLOWED → initiateInstall()
        └── MODE_ERRORED → 弹出"开启未知来源"对话框

用户点击"安装"
  ├── Session 安装:mInstaller.setPermissionsResult(sessionId, true)
  └── 非 Session 安装:startInstall() → 跳转 InstallInstalling

阶段四:InstallInstalling(提交安装)

文件packages/PackageInstaller/src/.../InstallInstalling.java

职责:将 Session 提交给 PMS,显示"正在安装..."。

复制代码
onCreate()
  ├── 注册 InstallEventReceiver 监听安装结果广播
  └── 获取 stagedSessionId,验证 Session 有效性

onResume() → InstallingAsyncTask
  └── doInBackground()
        └── openSession(mSessionId)

  └── onPostExecute()
        ├── 构建 PendingIntent(接收结果广播)
        └── session.commit(pendingIntent.getIntentSender())
                                ↑
                         进入 Framework 层

阶段五:PackageInstallerSession(Framework 调度)

文件services/core/.../pm/PackageInstallerSession.java

职责:验证 APK 完整性、协调安装时序。

复制代码
commit(statusReceiver)
  ├── markAsSealed()                     标记 Session 不再接受写入
  │
  ├── dispatchStreamValidateAndCommit()
  │     └── handleStreamValidateAndCommit()
  │           └── validateApkInstallLocked()   校验签名、包名、版本、split
  │
  └── handleInstall()
        ├── prepareInheritedFiles()            继承已安装包文件
        ├── parseApk()                         解析 APK
        └── verify()
              ├── runExtractNativeLibraries()  提取 native 库
              └── onVerificationComplete()
                    └── install()
                          └── installNonStaged()
                                └── 创建 InstallingSession

阶段六:InstallingSession(参数准备)

文件services/core/.../pm/InstallingSession.java

职责:准备安装参数,检查存储空间,进入 PMS 安装。

复制代码
installStage()
  └── mPm.mHandler.post(this::start)     投递到 PMS Handler 线程

start()
  ├── handleStartCopy(installRequest)
  │     ├── [SKG] 检查 is3rdNotAllowInstall,为 true 则拒绝
  │     ├── [SKG] 拦截 com.android.vending 升级安装
  │     ├── getMinimalPackageInfo()       检查存储空间,确定安装位置
  │     └── freeCacheForInstallation()   空间不足时尝试清理缓存
  │
  └── handleReturnCode(installRequest)
        └── processApkInstallRequests()
              └── mPm.installPackagesTraced()   进入 PMS 核心

阶段七:InstallPackageHelper(PMS 核心五步安装)

文件services/core/.../pm/InstallPackageHelper.java

职责:APK 的实际安装,从文件处理到数据库写入。

复制代码
installPackagesTraced()
  │
  ├── 第1步 prepareInstallPackages()
  │         解析 APK 完整信息,校验签名兼容性
  │         检查是否允许降级,处理权限和依赖
  │
  ├── 第2步 scanInstallPackages()
  │         扫描 APK,生成 PackageSetting
  │         分配 UID / appId,注册到 PMS 内部数据结构
  │
  ├── 第3步 reconcileInstallPackages()
  │         处理包替换逻辑,签名一致性校验
  │         shared user 处理,权限继承
  │
  ├── 第4步 renameAndUpdatePaths()
  │         Stage 目录重命名为最终安装目录
  │         /data/app/~~random~~/com.example.app-random/
  │
  └── 第5步 commitInstallPackages()
            freezePackageForInstall()     冻结应用
            commitPackagesLocked()        写入 packages.xml
            executePostCommitStepsLIF()
              ├── prepareAppDataAfterInstallLIF()   创建应用数据目录
              ├── performDexOpt()                   DEX 优化
              └── 发送 PACKAGE_ADDED / PACKAGE_REPLACED 广播

阶段八:安装结果回调

复制代码
InstallPackageHelper 安装完成
  ↓
InstallingSession.IPackageInstallObserver2.onPackageInstalled()
  ↓
PackageInstallerSession.dispatchSessionFinished()
  ↓
sendOnPackageInstalled() → 通过 IntentSender 发送广播
  ↓
InstallEventReceiver 收到广播
  ↓
InstallInstalling.launchFinishBasedOnResult()
  ├── 成功 → InstallSuccess(显示"打开"/"完成")
  └── 失败 → InstallFailed(显示错误原因)

三、SKG 定制点汇总

位置 定制内容
InstallStart.java persist.sys.skg.is3rdNotAllowInstall 为 true 时,Toast 提示并拒绝安装
PackageInstallerActivity.java com.skg.filemanager 特殊处理,视为已知来源,跳过未知来源校验
PackageInstallerActivity.java getFileManagerPermission() 自定义文件管理器安装权限检查
InstallingSession.java handleStartCopy() 中二次检查 is3rdNotAllowInstall(防绕过)
InstallingSession.java 拦截 com.android.vending(Google Play Store)的升级安装

四、关键源码文件索引

文件 路径 作用
InstallStart packages/PackageInstaller/src/.../InstallStart.java 入口,权限检查,路由
InstallStaging packages/PackageInstaller/src/.../InstallStaging.java content:// URI 文件暂存
PackageInstallerActivity packages/PackageInstaller/src/.../PackageInstallerActivity.java APK 解析,安装确认 UI
InstallInstalling packages/PackageInstaller/src/.../InstallInstalling.java Session commit,等待结果
InstallSuccess packages/PackageInstaller/src/.../InstallSuccess.java 安装成功界面
InstallFailed packages/PackageInstaller/src/.../InstallFailed.java 安装失败界面
PackageInstallerSession services/core/.../pm/PackageInstallerSession.java Session 管理,验证,调度
InstallingSession services/core/.../pm/InstallingSession.java 安装参数准备,空间检查
InstallPackageHelper services/core/.../pm/InstallPackageHelper.java PMS 核心五步安装
PackageManagerService services/core/.../pm/PackageManagerService.java PMS 入口,转发到 Helper

五、核心知识点速记

为什么需要 InstallStaging?
content:// URI 属于另一个进程的文件,安装过程中源文件可能被修改,必须先拷贝到 PackageInstaller 控制的 Session 目录确保完整性。

为什么 commit() 后还要等广播?
session.commit() 只是提交任务,PMS 在独立 Handler 线程异步执行,结果通过 PendingIntent 广播异步通知,InstallInstalling 监听广播后再跳转结果页。

packages.xml 是什么?
/data/system/packages.xml,PMS 的包信息数据库,记录所有已安装包的签名、权限、安装路径等,系统启动时从这里恢复包信息。

DEX 优化在哪一步?

commitInstallPackages()executePostCommitStepsLIF() 中执行 performDexOpt(),是安装最后阶段,这也是为什么大 APK 安装时间较长。

相关推荐
帅次1 小时前
LazyColumn 懒加载、items 与 key
android·flutter·kotlin·android studio·webview
zhangphil1 小时前
Android显示系统RenderThread绘制HARDWARE/普通格式Bitmap与GPU与CPU处理机制
android
美狐美颜SDK开放平台2 小时前
什么是美颜SDK?高并发场景下的企业级美颜SDK如何开发?
android·人工智能·ios·美颜sdk·第三方美颜sdk·视频美颜sdk
YF02112 小时前
Protobuf与 gRPC 的关系:从理论到 Android + Go 实战通信全解析
android·后端·grpc
YF02112 小时前
Android 卡顿性能优化专项治理:从 ANR 根源到系统性重构实践
android·app
蒙奇·D·路飞-2 小时前
Kotlin安卓app版本自动升级设计实现
android
博客zhu虎康2 小时前
小程序按钮实现先表单校验再走手机号获取功能
android·javascript·小程序
码途漫谈3 小时前
Easy-Vibe高级开发篇阅读笔记(十三)——多平台开发之Android App 原生开发
android·人工智能·笔记·ai·开源·ai编程
街灯L3 小时前
【ADB】使用ADB工具箱卸载安卓系统软件
android·adb