系统服务之PMS

PMS(PackageManagerService)是 Android 提供的包管理系统服务,它用来管理所有的包信息,包括应用安装、卸载、更新以及解析 AndroidManifest.xml。


打开app大致流程

  1. 遍历/data/app的文件夹
  2. 解压apk文件
  3. dom解析AndroidManifest.xml文件
  4. 定位到入口函数(想象成MainActivity)
  5. 反射创建MainActivity
  6. MainActivity对象展示,接下来就是App看到的界面了。

其中,1,2,3步由PMS来处理,4,5,6由AMS处理。

PMS主要功能

  1. 管理设备上安装的所有应用程序,并在系统启动时加载应用程序;
  2. 根据请求的Intent匹配到对应的Activity、Provider、Service,提供包含包名和组件的信息对象;
  3. 调用需要权限的系统函数时,检查程序是否具备相应权限从而保证系统安全;
  4. 提供应用程序的安装、卸载的接口;

PMS包管理

  1. 应用程序层:使用getPackageManager()获取包的管理对象PackageManager,PMS使用的也是Binder通信,PackageManager是从ServiceManager中获取注册的Binder对象,具体的实现为 PackageManagerService,PMS实现对所有程序的安装和加载; (所有系统程序的文件处于/system/app/目录下,第三方程序文件处于/data/app/ 目录下,在程序安装过程中PMS会将要安装的apk文件复制到/data/app/目录下)
  2. PMS服务层:PMS运行在SystemServer进程中,主要使用/system/etc/permissions.xml 和/data/system/packages.xml管理包信息;
  3. 数据文件管理:PMS负责对系统的配置文件、apk安装文件、apk的数据文件执行管理、读写、创 建和删除等功能;

PMS的启动过程

在Android系统启动过程中,程序会执行到SystemServer中,然后调用startBootstrapServices()方法启,动核心服务,在startBootstrapServices()方法中完成PMS的启动:

1
2
3
4
startBootstrapServices
PackageManagerService.main
PackageManagerService m = new PackageManagerService
ServiceManager.addService("package", m)

PMS的工作过程

ArrayMap<String, PackageParser.Package> mPackages
在扫描程序文件目录时会将信息保存 在Package对象中,然后将所有程序包名极其的package保存在此集合中;

Settings mSettings
保存整个系统信息的Setting对象;

  1. 创建Settings对象,将PMS中的mPackage集合传入,此集合保存所有apk的解析数据
  2. 调用readLPw()方法解析系统配置文件package.xml(mSettings.readLPw() )主要是操作mPackages,保存手机中所有app的应用信息
  3. 调用scanDirTracedLI()扫描系统app(/system/app/)
  4. 调用scanDirTracedLI()扫描第三方app(/data/app/)
  5. 执行mSettings.writeLPr()将扫描后的结果,重新写入配置文件,将mPackages中的数据分别写入package.xml和package.list文件中

简单来说系统在启动会会创建PMS对象,使用PMS对象读取配置文件, 然后扫描手机上所有的app程序,并将所有的程序的内容信息都封装在Package对象中,然后将Package 集合中信息转换为PMS的属性供系统使用,最后并更新配置文件;

PackageInstaller 提供了 应用安装、更新、移除的能力,具体实现是PackageInstallerService


APK的包组成

apk文件本质还是一个zip文件

classes.dex: Dex是DalvikVM executes的缩写,即Android Dalvik执行文件
Androidmanifest.xml:Project中androidManifest.xml编译后得到的二进制xml文件
META-INF:主要保存各个资源文件的SHA1 hash值,用于校验资源文件是否被修改,防止二次打包时资源文件被替换。该目录下主要包括三个文件:

  • MANIFEST.MF:保存了 apk 所有文件的摘要信息(SHA-1+Base64)
  • CERT.SF:保存了对 MANIFEST.MF 文件再进行一次 SHA-1 并 Base64 加密的信息,并同时保存了 MANIFEST.MF 文件的摘要信息
  • CERT.RSA:使用私钥对CERT.SF签名,保存了签名结果、公钥和所采用的加密算法等信息

res: Project中res目录下资源文件编译后得到的二进制xml文件
resources.arsc:包含了所有资源文件的映射,可以理解为资源索引,通过该文件能找到对应的资源文件
lib:ndk编译出来的so库

APK打包流程

  1. 通过aapt2打包res资源文件,生成R.java resources.arsc和res文件
  2. 通过Javac编译R.java,Java接口文件,java源文件生成.class文件
  3. 通过d8命令将.class文件和第三方库中的.class文件处理生成classes.dex
  4. 通过aapt2工具将aapt生成的resources.arsc 和res文件,未编译的资源assets文件和classes.dex 一起打包生成apk(未签名)
  5. 通过zipalign工具将未签名的apk进行对齐处理
  6. 通过apksigner工具对上面的apk进行签名

aapt2android资源打包工具
Kotlinc把.kt文件编译成.class文件
javac把.java文件编译成.class文件
d8新一代.class转化成.dex的工具
zipalign字节码对齐工具
apksignerapk签名工具

apk的安装方式

  1. 安装系统APK和预置的APK(第一次开机时安装,没有安装界)
    PackageManagerService的构造中会扫描对应目录下的apk,完成安装

  2. 网络下载应用安装――通过market应用完成,没有安装界面
    调用PackageManager的installPackage方法执行安装

  3. ADB工具安装,没有安装界面
    /repo/system/core/adb/commandline.cpp中install_app方法,该方法调用pm_command通过send_shell_command方法将数据发送到手机端的adbd守护进程中,adbd在收到PC中Console发来的数据之后启动一个Shell,然后执行pm脚本(pm位于/system/bin目录下).
    pm脚本通过app_process执行pm.jar包的main函数(\system\framework\pm.jar) 对应源码: /repo/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java

  4. 第三方应用安装,有安装界面

以上几种方式均通过PackageInstallObserver来监听安装是否成功。


安装APK的代码

1
2
3
4
5
6
7
8
9
File file = new File(getExternalFilesDir(null), "xxx.apk");

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Uri apkUri =FileProvider.getUriForFile(this, "com.xxx.xxx.fileprovider", file);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");

startActivity(intent);

APK安装主体流程

  1. 复制APK到/data/app目录下,解压并扫描安装包
  2. 将APP的dex文件拷贝到/data/dalvik-cache目录,再在/data/data/目录下创建应用程序的数据目录(以应用包名命令),用来存放应用的数据库、xml文件、cache、二进制的so动态库等
  3. 解析apk的AndroidManifest.xml文件,注册四大组件,将apk的权限、应用包名、apk的安装位置、版本、userID等重要信息保存在/data/system/packages.xml文件中。这些操作都是在PackageManagerService中完成。
  4. 资源管理器解析APK里的资源文件。
  5. dex2oat操作,对dex文件进行优化,并保存在dalvik-cache目录下。
  6. 更新权限信息。
  7. 安装完成后,发送Intent.ACTION_PACKAGE_ADDED广播。
  • PMS中先把APK拷贝到 /data/app,然后使用PackageParser2解析APK 获取 四大组件、搜集签名、PackageSetting等信息,并进行校验确保安装成功;
  • 接着提交信息包更新系统状态及PMS的内存数据;
  • 然后使用 Installer 准备在用户目录/data/user进行 dexOpt;
  • 最后发送安装结果通知UI层。

复制解析验证更新信息dex优化


PMS和AMS关联

无论是Android系统启动后执行的PMS启动,还是使用PackageInstaller安装APK的过程,最终都会使用PackageParser扫描相应的apk文件,将扫描提取的信息保在Package对象中,扫描完成后会回调PMS中方法将扫描获取的四大组件信息转换保存在PMS属性中,主要使用ActivityIntentResolver(activity和br一样)、ServiceIntentResolver、ProviderIntentResolver保存信息

ActivityIntentResolver mActivities:
遍历所有程序的目录,并解析所有的注册清单文件,将提取 所有的Intent-filter数据保存在对应的集合中;
ActivityIntentResolver mReceivers:同上
ServiceIntentResolver mServices:同上
ProviderIntentResolver mProviders:同上

1
2
3
4
5
6
7
8
//ActivityStarter#startActivityMayWait
//ActivityInfo是ResolveInfo的成员变量

......
ResolveInfo rInfo = mSupervisor.resolveIntent(intent......);
......
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo......);
......

activity跳转时,会调用PMS获取对应activity的信息