PackageManagerService之简介

1,362 阅读5分钟

本文基于Android Q 的源码进行分析和介绍

在介绍之前先思考几个问题:

  1. PackageManagerService是在哪个进程下的
  2. 为什么要有PackageManagerService
  3. 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.javamain()方法进入。接着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的功能

前面问题中的为什么要有PackageManagerServicePackageManagerService主要功能是什么将在本节回答。首先我们要了解apk的文件的结构,分为三个部分:

  1. Java代码,主要是classes.dex文件,可能会有多个
  2. 资源文件,包括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/下
  3. 校验文件,这个文件的主要是看文件有没有被修改或者签名是否正确

了解了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接受三种方式安装应用:

  1. 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);
        }
    }
    
    
  2. Shell安装, 命令为adb shell pm install [PATH|-],其他的参数可以通过pm命令查看,不过这个命令只能安装有shell权限的文件夹下的apk,一般shell会有/data/local/tmp的读写权限,基本上应用宝等应用安装应用都是通过这个方式进行安装的

  3. adb安装,adb install [PATH],Shell安装是安装手机内的apk,而adb则是安装PC上的apk文件,两者是不一样的。

卸载应用也是这三种方式,可以自行了解

存储应用信息

PkMS安装应用的目的就是获取apk的信息,然后进行缓存。PkMS共有两级缓存:

  1. 一级缓存是在内存里面,是一个ArrayMap, final ArrayMap<String, PackageParser.Package> mPackages = new ArrayMap<>();,安装/卸载应用以及开机的时候会更新里面的数据
  2. 二级缓存是作为文件进行存储在/data/system/中,主要信息存储在packages.xml

当然如果是第一次开机或者是恢复出厂的时候会删除data分区,所以要删除所有的应用信息然后重新扫描所有的apk