前言
本章主要围绕以上几个知识点进行讲解,最后会手写一个 权限申请框架;
PKMS
PackageManagerService 的作用:
- PackageManagerService(简称 PKMS),是 Android 系统中核心服务之一,负责应用程序的安装,卸载,信息查询,等工作;
- 解析AndroidNanifest.xml清单文件,解析清单文件中的所有节点信息;
- 扫描.apk文件,安装系统应用,安装本地应用等;
- 管理本地应用,主要有, 安装,卸载,应用信息查询 等;
PKMS 概述来说:Android 系统启动时,会启动(应用程序管理服务器 PKMS ),此服务负责扫描系统中特定的目录,寻找里面的APK格式的文件,并对这些文件进行解析,然后得到应用程序相关信息,最后完成应用程序的安装PKMS在安装应用过程中, 会全面解析应用程序的 AndroidManifest.xml 文件, 来得到 Activity, Service, BroadcastReceiver, ContextProvider 等信息, 在结合 PKMS 服务就可以在 Android OS 中正常的使用应用程序了,在 Android 系统中, 系统启动时由 SystemServer 启动 PKMS 服务, 启动该服务后会执行应用程序的安装过程;
PKMS 的角色位置,我们来看一张图
客户端可通过 Context.getPackageManager() 获得 ApplicationPackageManager 对象, 而 mPM 指向的是 Proxy 代理,当调用到 mPM. 方法后,将会调用到 IPackageManager 的 Proxy 代理方法,然后通过Binder 机制中的 mRemote 与服务端 PackageManagerService 通信 并调用到 PackageManagerService 的方法;
PKMS 启动过程分析
启动过程,依然从 SystemServer 的 run 看起,我们进入这个 run 方法的 startBootstrapServices() 方法,这个方法主要用来启动引导服务;
1 | scss复制代码private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) { |
重点一在第五步,手机开机慢的主要原因:
手机开机,BootRoom 拉起BootLoader程序,BootLoader 会拉起 Linux 内核驱动,驱动会初始化init进程,init进程会创建zygote进程,zygote进程会fork出它的第大儿子 系统服务进程「system server」系统服务进程会初始化 PKMS,PKMS 在构造方法中 就会扫描手机上的所有应用,手机上应用越多,时间越长,扫描完之后 还会进行 dex 优化;
重点二在第三步,我们进入第三步的 main 方法看下:
1 | java复制代码public static PackageManagerService main(Context context, Installer installer, |
这里主要是调用 PackageManagerService 的构造方法,进行实例化,并初始化相关信息,这里主要分为 5 个阶段;
1 | ini复制代码public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest, |
- BOOT_PROGRESS_PMS_START,PKMS 的构造方法中进行;
- mMetrics = new DisplayMetrics();初始化屏幕相关类;
- mInstaller = installer;赋值安装器;
- mPermissionManager = injector.getPermissionManagerServiceInternal();创建PermissionManager 进行权限管理;
- 创建 settings 保存安装包信息;
这个 Settings 比较关键,我们进入这个 Settings 的构造方法看下;
1 | less复制代码Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence, |
packages.xml中 存放着所有的系统权限 以及 安装在手机上的每一个应用程序信息。每次开机都会执行这个 PKMS 的构造方法,进行应用的安装处理,如果手机上安装的应用很多很多,开机时间也会相应的变长;
- EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START;扫描系统的 apk 应用;
这个阶段就是开始扫描系统的 apk 应用,
1 | arduino复制代码public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest, |
扫描系统的应用,主要扫描下面几个目录的 apk:
1 | arduino复制代码private static int scanFlagForPartition(PackagePartitions.SystemPartition partition) { |
- BOOT_PROGRESS_PMS_DATA_SCAN_START,扫描data目录下的apk,这里主要是安装的第三方应用;
1 | scss复制代码mAppInstallDir = new File(Environment.getDataDirectory(), "app"); |
处理 data 目录的应用信息,及时更新,祛除不必要的数据;
- BOOT_PROGRESS_PMS_SCAN_END OTA 升级后首次启动要清除不必要的缓存数据,权限等默认项更新后要清理相关数据,将阶段2 阶段3扫描后的数据更新到 packages.xml 文件中;
1 | ini复制代码EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, |
- BOOT_PROGRESS_PMS_READY GC 内存回收
1 | scss复制代码EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis()); |
APK 的扫描
PKMS的构造函数中调用了 scanDirTracedLI 方法 来扫描某个目录的 apk 文件;
1 | arduino复制代码private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, |
从这里执行 APK 的扫描操作,我们进入这个 scanDirLI 看下;
1 | arduino复制代码private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime, |
这里主要是 收集 apk 文件 交给 submit 方法处理;
1 | ini复制代码public void submit(File scanFile, int parseFlags) { |
submit 把 PackageParser 封装好的信息交给 parsePackage 方法;
1 | ini复制代码public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches) |
解析 apk 文件
1 | arduino复制代码public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, |
我们进入 parseMonolithicPackage 这个方法看下:
1 | java复制代码private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile, |
通过 parseBaseApk 解析 apk 中的清单文件;
1 | ini复制代码private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile, |
通过 XmlResourceParser parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME) 解析 apk 中的清单文件;
通过 parseBaseApk 获取应用的相关信息,并存储起来;
1 | ini复制代码private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath, |
startParsingPackage 获取清单文件中的标签 例如「application」「permission」「package」「manifest」
parseBaseApkTags 获取application标签中的中的「activity」「service」「receiver」「provider」;
所以,静态广播是什么时候注册的?Activity的启动模式是在什么时候获取的呢? 答案就显而易见了;
开机的时候,在 PKMS 的构造方法中 会扫描所有的 apk,包括系统 apk 和应用 apk,并解析它们的清单文件,获取到 receiver 的时候就会注册,获取到 activity 的时候 一并获取了 launchMode;
扫描外部apk直接通过接口,获取 Package 对象,PackageParserCompat.parsePackage(context, apk, PackageParser.PARSE_MUST_BE_APK);
安装 APK
用户点击 xxx.apk 文件进行安装,从「开始安装」到「完成安装」到过程;这里分为两个步骤,一个是复制流程,一个是安装流程;
复制流程
点击xxx.apk执行onClick事件,通过Intent 发起startActivity 跳转到 PackageInstallerActivity 这个界面,这个界面是以 Dialog 形式展示,存在「安装」和「取消」事件,进入「安装」事件看下;
从 PackageInstallerActivity 的 bindUI 发起流程,会调用 startInstall 方法进行整个的安装流程;
1 | scss复制代码private void bindUi() { |
调用 startInstall 发起安装
1 | scss复制代码private void startInstall() { |
跳转到 安装中 InstallInstalling.class 界面,在 onResume 执行安装逻辑
1 | scss复制代码protected void onResume() { |
mInstallingTask.execute(); 执行 execute 方法进行安装;这里是一个 AsyncTask,要处理 doInBackground 和 onPostExecute
1 | ini复制代码private final class InstallingAsyncTask extends AsyncTask<Void, Void, |
doInBackground方法中:通过IO流 将apk文件写入session中;
onPostExecute方法中:通过 session.commit 方法,跨进程操作,将 session 交给 PKMS,最终走到 PKMS 的 installStage 方法;
1 | ini复制代码void installStage(InstallParams params) { |
msg.what == INIT_COPY; handleMessage 的这个 case 我们进入看下:
1 | scala复制代码class PackageHandler extends Handler { |
INIT_COPY 中最终执行的是 HandlerParams 的 startCopy 方法,将 apk 复制到 /data/app 目录下;
最终走到 PKMS 的 handleStartCopy() 方法 和 handleReturnCode 方法;
1 | scss复制代码public void handleStartCopy() { |
主要是为了创建 InstallArgs,在 handleReturnCode 中执行 copyApk 方法;
1 | scss复制代码void handleReturnCode() { |
handleReturnCode 执行到 args.copyApk() 的时候,才真正的将 apk 文件复制到了 /data/app 目录下;
1 | ini复制代码int copyApk() { |
通过 IO 流 执行 copy 操作;这也就能解释为什么 apk 安装包可以删除的原因了;
扫描流程大致可以理解为:
- 在 PackageParser 扫描完一个 APK 后,此时系统已经根据该 APK 中 AndroidManifest.xml,创建了一个完整的 Package 对象;
- 扫描APK,解析AndroidManifest.xml文件,得到清单文件各个标签内容;
- 解析清单文件的信息由 Package 保存,从该类的成员变量可看出,和 Android 四大组件相关
的信息分别由 activites、receivers、providers、services 保存,由于一个 APK 可声明多个组件,因此 activites 和 receivers 等均声明为 ArrayList;
安装流程
安装流程也是有 PKMS 发起的,它发起的起点就是 handleReturnCode 的回调中调用了processPendingInstall 方法
1 | scss复制代码void handleReturnCode() { |
通过调用 processInstallRequestsAsync 执行安装逻辑
1 | scss复制代码private void processInstallRequestsAsync(boolean success, |
这里直接调用 installPackagesTracedLI 进行安装;
1 | typescript复制代码private void installPackagesTracedLI(List<InstallRequest> requests) { |
这里直接调用 installPackagesLI 方法进行 apk 扫描以及安装
1 | ini复制代码private void installPackagesLI(List<InstallRequest> requests) { |
构建安装请求,然后调用 executePostCommitSteps 进行安装;
1 | ini复制代码private void executePostCommitSteps(CommitRequest commitRequest) { |
这里调用 prepareAppDataAfterInstallLIF 最终调用到 prepareAppDataLeaf 中的 batch.createAppData
1 | less复制代码private @NonNull CompletableFuture<?> prepareAppDataLeaf(@NonNull Installer.Batch batch, |
Batch 的 createAppData 最终调用到的是其 execute 方法;
1 | ini复制代码public synchronized void execute(@NonNull Installer installer) throws InstallerException { |
通过 installer.createAppDataBatched(args) 方法进行 app 的创建;
1 | less复制代码public @NonNull CreateAppDataResult[] createAppDataBatched(@NonNull CreateAppDataArgs[] args) |
最终 mInstalld.createAppDataBatched(args); 进入 linux 底层的安装逻辑,底层的安装逻辑我也不太清楚,只能到这里了;
权限扫描
PKMS 的构造方法中会调用到 SystemConfig systemConfig = SystemConfig.getInstance(); 方法,此方法会执行权限的扫描/system/etc/permissions
SytestemConfig 的构造方法中 会执行权限的读取操作;
readPermissions 和扫描到的AndroidManifest文件中声明的权限会和这里进行一个匹配,匹配上了给予权限;
好了,本章的讲解就到这里吧,静默安装和权限申请框架我们放在了下一章;
下一章预告
PMS 权限管理
欢迎三连
来都来了,点个关注点个赞吧,你的支持是我最大的动力~~
本文转载自: 掘金