本文基于Android Q 的源码进行分析和介绍
在介绍之前先思考几个问题:
- PackageManagerService是在哪个进程下的
- 为什么要有PackageManagerService
- PackageManagerService主要功能是什么
由于PackageManagerService名称实在是过长,接下来将使用PkMS代替PackageManagerService作为简介。至于为什么不用PMS,因为PMS通常代表的是PermissionManagerService,而且这两个服务是相关性非常强,基本上PermissionManagerService可以看作是PackageManagerService的一个子服务,为了避免混淆,所以将PackageManagerService称之为PkMS而PermissionManagerService称之为PMS用作区分。
PkMS的启动
前面问题中的的PackageManagerService是在哪个进程下的将在本节进行解答。首先明确一下Android启动流程,这个估计很多人都知道,也不是我们我们的重点,这里就大致介绍一下。大致是uboot->init->Zygote,其中Zygote是由init进程解析init.rc启动的第一个Java进程,之后Zygote就会fork出system_server进程,详细代码见forkSystemServer()
方法,然后从SystemServer.java
的main()
方法进入。接着SystemServer.java
会启动各种服务,里面就包括了PkMS的启动,启动PkMS的函数调用流程如下:
SystemServer.main()
SystemServer.run()
SystemServer.startBootstrapServices()
PackageManagerService.main()
由此可以看出PkMS就是一个由SystemServer启动的一个普通类而已,所在的进程是system_server, 那么应用要怎么与PkMS进行通信呢?答案很显然是Binder。Binder是Android进程间通信的一种机制,一个进程可以注册多个Binder,任何一个类只要实现了对应的Binder接口都可以注册到service_manager当中。而应用只要获取对应Binder的代理类(直接new出来的,里面的核心是一个int类型的变量叫handle,serverice_manager对应是0),就可以调用类的接口。这里刻意将服务用类来代替是避免对于服务有一种高不可攀的错觉。
PkMS的功能
前面问题中的为什么要有PackageManagerService和PackageManagerService主要功能是什么将在本节回答。首先我们要了解apk的文件的结构,分为三个部分:
- Java代码,主要是classes.dex文件,可能会有多个
- 资源文件,包括AndroidManifest.xml(已编译成二进制),res目录下的所有文件,res目录主要是布局,图片等等。assets目录下的文件,这个主要是应用的原生资源。resources.arsc,这个文件中包括了已编译的字符串,值类型(bool, int, dimens等),这些是值直接放在resources.arsc中的。以及res目录下的文件引用。大家可以通过Android Studio->Build->Analyze APK或者是到SDK目录下build-tools/选择一个编译版本用aapt2命令进行查看,AOSP中的aapt2是在out/host/linux-x86/bin/下
- 校验文件,这个文件的主要是看文件有没有被修改或者签名是否正确
了解了apk的结构之后,那么要如何启动应用呢?应用层启动Activity的代码我们都知道:
Intent intent = new Intent("action");
startActivity(intent);
接着会传给AMS启动Activity,假如说没有PkMS的话,那AMS要启动Activity可要废老大劲了,要先得到apk的路径,之后将其解压缩,然后解析AndroidManifest.xml,获取action对应Activity的完整类名,接着启动进程,之后ActivityThread创建Activity的实例,然后再给到AMS进行管理。而且每次启动apk的Activity都要解压缩apk文件,文件操作又是一个非常耗时的操作,那么有没有一个方法将其简化呢?这时就轮到PkMS登场了,也是PkMS最主要的任务之一,将apk信息缓存下来,AMS需要的时候直接从PkMS中取就是了,而且它们是同进程的,所以使用Binder的操作不用跨进程,获取信息的时间几乎为0,而解析apk的时间可以参照应用安装的时间,你能忍受不,这就是为什么要有PackageManagerService的原因。接下来总结一下PkMS的主要功能:
安装/卸载应用
PkMS接受三种方式安装应用:
-
PackageInstaller, 准确的说是具有
android.intent.action.INSTALL_PACKAGE
的应用,并且有且只能有一个这个action的应用,具体的check流程如下://PackageManagerService.java private @NonNull String getRequiredInstallerLPr() { final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setDataAndType(Uri.parse("content://com.example/foo.apk"), PACKAGE_MIME_TYPE); final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, UserHandle.USER_SYSTEM); if (matches.size() == 1) { ResolveInfo resolveInfo = matches.get(0); if (!resolveInfo.activityInfo.applicationInfo.isPrivilegedApp()) { throw new RuntimeException("The installer must be a privileged app"); } return matches.get(0).getComponentInfo().packageName; } else { throw new RuntimeException("There must be exactly one installer; found " + matches); } }
-
Shell安装, 命令为
adb shell pm install [PATH|-]
,其他的参数可以通过pm命令查看,不过这个命令只能安装有shell权限的文件夹下的apk,一般shell会有/data/local/tmp
的读写权限,基本上应用宝等应用安装应用都是通过这个方式进行安装的 -
adb安装,
adb install [PATH]
,Shell安装是安装手机内的apk,而adb则是安装PC上的apk文件,两者是不一样的。
卸载应用也是这三种方式,可以自行了解
存储应用信息
PkMS安装应用的目的就是获取apk的信息,然后进行缓存。PkMS共有两级缓存:
- 一级缓存是在内存里面,是一个ArrayMap,
final ArrayMap<String, PackageParser.Package> mPackages = new ArrayMap<>();
,安装/卸载应用以及开机的时候会更新里面的数据 - 二级缓存是作为文件进行存储在
/data/system/
中,主要信息存储在packages.xml
中
当然如果是第一次开机或者是恢复出厂的时候会删除data分区,所以要删除所有的应用信息然后重新扫描所有的apk