PackageManagerService(PKMS)是 Android 系统中的核心服务之一,负责应用程序的安装、卸载、信息查询等工作,本文简要分析 PKMS 的启动和安装应用过程,源码基于 android-12.1.0_r4;
1 概述
Android 系统启动时,会启动 PKMS,此服务负责扫描系统中特定的目录,寻找里面的 APK 格式文件,并对这些文件进行解析,然后得到应用程序相关信息,会全面解析应用程序的 AndroidManifest.xml,得到 Activity / Service / BroadcastReceiver / ContentProvider 等信息,最后完成应用程序的安装。
2 PKMS 启动
PKMS 属于 system_server 进程,在 Zygote 启动 system_server 的时候在 SystemServer.main() 中启动了近百个系统服务;
2.1 SystemServer.main()
1 | // SystemServer.java |
上述代码主要做了四件事:
- 启动 installer:阻塞等待 installer 启动完成,以便有机会创建具有适当权限的关键目录,比如 /data/user;
- 检查设备是否加密:如果加密了,则只解析 core 应用,并配置
mOnlyCore = true
,后续会多次使用该变量进行条件判断; - 初始化 PKMS:调用 PKMS.main() 初始化 PKMS;
- 启动 OtaDexOptService 服务:如果设备没有加密则启动 dex 服务;
1 | // SystemServer.java |
在 startOtherServices()
中继续有 PKMS 的相关操作:
- 执行 dex 优化;
- 完成磁盘维护;
- PKMS 准备就绪;
2.2 PKMS.main()
1 | public static PackageManagerService main(Context context, Installer installer, |
创建了一个 Injector 对象,其中使用 Singleton 的模式初始化了很多变量,传递到 PKMS 的构造函数中;
2.3 PKMS 构造函数
1 | // PackageManagerService.java |
PKMS 构造函数分为了 5 个阶段:
- BOOT_PROGRESS_PMS_START:构造 DisplayMetrics 保存分辨率信息,创建 mPermissionManager 进行管理权限,创建 mSettings 来保存安装包信息;
- BOOT_PROGRESS_PMS_SYSTEM_SCAN_START:扫描系统 apk(system/vendor/product 等目录),清除安装时的临时文件和其他不需要的信息;
- BOOT_PROGRESS_PMS_DATA_SCAN_START:扫描 data 目录 apk,
- BOOT_PROGRESS_PMS_SCAN_END:OTA 升级后首次启动要清除不必要的缓存数据,
- BOOT_PROGRESS_PMS_READY
2.3.1 第一阶段 BOOT_PROGRESS_PMS_START
1 | // PackageManagerService.java |
利用 Injector 初始化一些实例,构造 DisplayMetrics,用于保存分辨率;创建 Installer 用于与 installd 交互;创建 mPermissionManager 进行权限管理;构造 Settings 类,保存安装包信息,清除路径不存在的孤立应用,给 mSettings 添加 system/phone/log/nfc/bluetooth/shell/se/networkstack/uwb 9 种 shareUserId 到 mSettings(sharedUserId 属性相同的 package 可以运行在同一个进程中,或者相互读取资源),Settings 可以看做是一个数据动态管理类,它主要会管理 packages.xml 文件中的信息;构造 PackageDexOptimizer 及 DexManager 类,处理 dex 优化;重点看一下 Settings 类:
1 | // frameworks/base/services/core/java/com/android/server/pm/Settings.java |
初始化一些变量指向的路径:
- mSettingsFilename:指向 /data/system/packages.xml,记录了系统中所有安装应用的基本信息;
- mBackupSettingsFilename:指向 /data/system/packages-backup.xml,是 packages.xml 的备份;
- mPackageListFilename:指向 /data/system/packages.list,保存了应用的数据目录和 UID 等信息;
- mStoppedPackagesFilename:指向 /data/system/packages-stopped.xml,记录系统中所有被强制停止运行的应用的信息;
- mBackupStoppedPackagesFilename:指向 /data/system/packages-stopped-backup.xml,是 packages-stopped.xml 的备份;
随后在 PKMS 中调用 Settings.readLPw() 对 packages.xml 进行解析
1 | // PackageManagerService.java |
2.3.2 第二阶段 BOOT_PROGRESS_PMS_SYSTEM_SCAN_START
1 | // PackageManagerService.java |
调用 scanDirTracedLI() 扫描 system/vendor/product 等等目录下的 overlay/priv-app/app 目录;
2.3.3 第三阶段 BOOT_PROGRESS_PMS_DATA_SCAN_START
1 |
|
第三阶段主要工作就是处理 data 目录的应用信息,及时更新,去除不必要的数据;
2.3.4 第四阶段 BOOT_PROGRESS_PMS_SCAN_END
1 | // PKMS.java |
OTA 升级后首次启动要清除不必要的缓存数据、权限等默认项,更新后要清理相关数据,更新 packages.xml;
2.3.5 第五阶段 BOOT_PROGRESS_PMS_READY
GC 回收内存;
2.3.6 总结
第一阶段:创建 Settings 对象,读取 /data/system/packages.xml 和 /data/system/packages-backup.xml,并把解析结果存储到 Settings 对象;
第二阶段:扫描 system/vendor/product 等目录下的 overlay/priv-app/app 目录;
第三阶段:扫描 /data/ 目录;
第四阶段:把第二阶段、第三阶段扫描的结果写入 packages.xml,更新 packages.xml;
第五阶段:GC 回收内存;
2.4 APK 扫描
扫描 apk 总结下来分为两步,
- 扫描 APK,解析 AndroidManifest.xml 文件,得到清单文件各个标签内容;
- 解析清单文件的信息由 Package 保存,从该类的成员变量可看出,和 Android 四大组件相关的信息分别由 activites、receivers、providers、services 保存,由于一个 APK 可声明多个组件,因此 activites 和 receivers 等均声明为 ArrayList;
2.5 APK 安装
调用流程(从点击 apk 文件开始):
- PackageInstallerActivity.bindUi():弹出一个 Alert,点击安装调用到 startInstall();
- startInstall():使用 startActivity() 启动 InstallInstalling,随后执行 onCreate(),onResume(),
- InstallInstalling.onResume():调用 InstallingAsyncTask.execute(),执行到 InstallingAsyncTask.onPostExecute(),又调用了 PackageInstaller.Session.commit();
- PackageInstaller.Session.commit():在其中又通过 IPC 跨进程调用到 system_server 进程的 PackageInstallerSession 服务的 commit();
- PackageInstallerSession.dispatchSessionSealed():发送 handle 消息 MSG_ON_SESSION_SEALED;
- PackageInstallerSession.handleMessage():收到消息调用 handleSessionSealed() 发送 MSG_STREAM_VALIDATE_AND_COMMIT 消息,再调用 handleStreamValidateAndCommit() 发送 MSG_INSTALL 消息,然后调用 handleInstall();
- PackageInstallerSession.verify() -> verifyNonStaged() -> prepareForVerification() -> makeVerificationParamsLocked() -> install() -> installNonStaged() -> PKMS.installStage();
- PackageManagerService.installStage():发送 Handle 消息 INIT_COPY;
- PKMS.PackageHandler.handlerMessage() -> doHandleMessage();
- PKMS.HandlerParams.startCopy() -> handleStartCopy(),handleReturnCode();
- PKMS.InstallParams.handleReturnCode();
- processPendingInstall();
- PKMS.FileInstallArgs.copyApk() -> doCopyApk();
- PackageManagerServiceUtils.copyPackage():先 copy apk 到 /data/app/base.apk 下,再 copy so 文件,然后回到 processPendingInstall() 继续往下执行;
- PackageManagerService.processInstallRequestsAsync():调用 installPackagesTracedLI();
- installPackagesTracedLI():调用 installPackagesLI() 安装解析 apk;
- installPackagesLI()
- preparePackageLI():准备,分析当前安装状态,解析包并初始验证;
- scanPackageTracedLI():根据准备阶段解析的包信息上下文,进一步解析;
- scanPackageLI -> PackageParser2.parsePackage():到这里又到了扫描 apk 的环节,后续步骤参考扫描总结;
- reconcilePackagesLocked():验证扫描后的包信息和系统状态,确保安装成功;
- CommitRequest():提交扫描的包、更新系统状态;
- PackageManagerService.executePostCommitSteps():安装完成后,准备 app 数据、执行 dex 优化;
- prepareAppDataAfterInstallLIF() -> prepareAppData() -> prepareAppDataLeaf() -> Installer.createAppData() -> Installd.createAppData():最终通过 IPC 操作跨进程调用到 init 进程启动的守护进程 Installd 中,后续不再分析;
- performDexOpt():执行 dex 优化;
- 回到 PackageManagerService.processInstallRequestsAsync(),继续往下调用 restoreAndPostInstall();
- restoreAndPostInstall():使用 handler 发送 POST_INSTALL 消息;
- handlePackagePostInstall():处理 POST_INSTALL 消息,发送 ACTION_PACKAGE_ADDED 等广播,调用 notifyInstallObserver() -> PackageInstallObserver2.onPackageInstalled(),发送安装成功的通知;
apk 的安装原理其实就是把 apk 文件 copy 到对应的目录:
把 apk 拷贝到 */data/app/packagename/*,可以直接把 apk 拷贝出来点击安装,比如查看微信的 apk 文件:
1
2adb shell pm list packages -f com.tencent.mm
package:/data/app/~~upY4pffUA2R84QfD_Ce7UA==/com.tencent.mm-RrcnDRXvQ_Luk8HcYou__g==/base.apk=com.tencent.mm开辟存放应用程序文件数据的目录 */data/data/packagename/(db, cache)*,包括应用的 so 库,缓存文件等待;
将 apk 中的 dex 文件安装到 /data/dalvik-cache 目录下;
待补充时序图
2.6 权限扫描
PKMS 的构造函数中会获取 SystemConfig 对象,在 SystemConfig 的构造函数中会调用 readAllPermissions() 从 /system/etc/permissions/、/system/etc/sysconfig 中的各种 xml 文件进行扫描,把 xml 中的标签转换成对应的数据结构,供之后权限管理使用;
3 总结
3.1 构造函数总结
第一阶段:创建 mSettings 对象,读取 /data/system/packages.xml 和 /data/system/packages-backup.xml,并把解析结果存储到 mSettings 对象,代表上次启动时的应用包信息;
第二阶段:扫描 system/vendor/product 等目录下的 overlay/priv-app/app 目录的 apk(系统 app);
第三阶段:扫描 /data/ 目录 apk(用户安装的 app);
第四阶段:根据第二阶段、第三阶段扫描的结果更新 packages.xml;
第五阶段:GC 回收内存;
开机时间有很大一部分是耗费在这五个阶段,还有一大部分是耗费在随后的 dex 优化上;
3.2 APK 扫描总结
- 调用路径:
- PKMS.scanDirTracedLI() -> :被 PKMS 构造函数调用,开启扫描 apk起点;
- scanDirLI() -> :收集 apk,提交文件并行解析;
- ParallelPackageParser.submit() -> :提交文件并行解析
- parsePackage() -> PackageParser2.parsePackage() -> ParsingPackageUtils.parsePackage() -> :进行 apk 解析,区分传入的是目录还是 apk,最终都是调用到 ParsingPackageUtils.parseMonolithicPackage();
- ParsingPackageUtils.parseMonolithicPackage() -> :解析给定的 apk 文件,具体调用 parseBaseApk() 去解析;
- parseBaseApk():
- parseBaseApkTags():解析 Manifest.xml;
- parseBaseApkTag():解析非 application 标签,比如 permission / uses-feature / attribution 等;
- parseBaseApplication():针对 application 标签进行全面解析,例如 activity / receiver / service / provider;
- 返回最终解析结果给 ParsedPackage 对象,然后又回到 PKMS.scanDirLI(),调用 addForInitLI();
- commitReconciledScanResultLocked():内部调用 commitPackageSettings()
- commitPackageSettings():包信息记录在了 PKMS 的属性中;
- 小结:对所有存在 apk 的目录进行扫描,解析所有 apk 的 AndroidManifest.xml,最后把包扫描结果提交到 PKMS 的各个属性中;
3.3 apk 安装总结
- PackageInstallerActivity 点击安装
- APK 用写入 Session 把包信息和 APK 安装操作都提交到了 PKMS;
- IPC 跨进程让 PKMS 执行 copy、扫描、解析;
- system_server 进程的 Installer IPC 跨进程到 Installd 进程(具有 root 权限,8.0 以前好像用的 socket 通信)准备用户目录 /data/user/ ,执行 dex 优化;
- 最后发送安装结果通知 UI 层;