程序包管理
程序包管理主要有三个内容:
-
提供一个能够根据intent匹配到具体的Activity、Provider、Service。即可以把intent转化为可以被java类加载器识别的Component
-
进行权限检查
-
提供安装、删除应用程序的接口
以上三个功能主要在PackageManagerService类中。以后简称PMS。
包管理概述
框架可以分成三层,是应用程序层、PMS服务层、数据文件层
应用程序层
- 通过contextImpl.getPackageManager()获取,饭后PackageManager对象。
PMS服务层
- 和AMS、WMS一样运行在SystemServer进程。PMS运行时,使用了两个目录下的XML文件保存相关包管理信息
- 第一个目录用于permission的管理,定义了系统中的所有权限。位于
/system/etc/permissions
- 第二个目录用于保存所有按照程序的基本信息,程序使用了那些系统权限等,类似于系统的注册表
- PMS启动时候,会从这两个目录中解析相关的XML文件信息,建立信息树。应用程序可以从这个信息树中查询所有需要的程序包信息。
数据文件层
一般分为三个部分
- 程序文件,所有系统程序保存在
/system/app
目录下,所有的第三方程序保存在/data/app
目录下。安装时候会把apk包移动到data/app
目录下,并且按照包名命名,如com.android.hello.android-1.apk
当在此安装时候,数字1会变为2,再安装有变回1,有点像乒乓机制。 - framework库文件。文件存放于
、system/framework
目录下。库文件类型是apk或者jar。系统开机后,虚拟机会加载这些库文件。在PMS启动时候,这写还没被转化为dex文件,则PMS会把它们转为dex文件,并存到/data/dalvik-cache
目录下 - 应用程序使用的数据文件。应用程序一般用三种存储方式:参数存储、数据库存储、文件存储。这些文件一般保存在
/data/data/xxx
目录下,xxx代表包名。
packages.xml文件格式
last-platform-version标签
记录了系统最后一次的版八号,一般和相应的SDK版八号相同。
permissions标签
保存了系统中所有权限列表,包括framework定义的权限以及应用程序自定义的权限
- name:权限名称,系统权限一般用andorid.permission开头,应用程序一般包名开头。权限名称必须全局唯一
- package:权限所在的包名,Framework对应的包名为android
- protection:保护等级,分别为普通、危险、签名、签名或系统。
cert标签
cert代表certifacation,即“证书”,一个应用程序一般只包含一个证书。cert包含两个属性,分别是index和key
- index:在PMS中保存了所用证书,每个证书对应一个索引,index表示的就是索引。
- key:该属性不是必须的,如果index已存在,key可以没有。而如果index是首次出现,则key的值必须指定,值为2390个16进制值,来源于对apk进行签名的证书文件。
sigs标签
即signature,签名。一个应用程序只能有一个签名,一个签名可以包含多个证书。签名标签有一个属性count,即该签名中包含多少个证书,sigs标签中必须包含count个cert子标签。用来表示具体的证书。
perms标签
即permission,权限。一个应用程序可以申请多个权限,perms标签中包含了所有的权限列表。和permissions不同,这个表示的是一个应用程序需要的权限。那个表示的是整个系统中所有的权限。
package标签
该标签包含了一个应用程序包的相关信息,package的标签属性包括如下几种:
- name:程序包名
- codePath:程序包所在路径,比如
/system/app/Mms.apk
、/data/app/com.hallo.android.Test-a.apk
- nativeLibraryPath:该程序使用的native库文件路径。一般情况下native库文件会被安装到程序数据文件路径下的lib子目录中,比如
/data/data/com.hello.android.Test/lib
- flags:用于标识应用程序类型,类型在Application.java中定义,包括FLAG_SYSTEM、FLAG_DEBUGGABLE、FLAG_PERSISTENT等
- it、ut:分别代表"首次安装时间 install time"和"最后升级时间update time"
- ft、ts:含义相同,代表最后一次修改该记录的时间,一般情况下,这个时间等于ut标签值
- installStatus属性:仅在packages-backup.xml文件中存在,值一般为false,当值为true是,该属性一般不会再写到该文件中
- userId、sharedUserId:应用程序对应的linux用户id值、应用程序共享的Linux用户id。这两个属性互斥
- installer:安装器名称。一般指应用程序调用PackageManager的installPackage()函数时,参数installPackageName的值。
shared-user标签
系统为每一个应用程序分配了一个Linux用户ID,一些native进程的用户id从1000开始,比如shell进程、log进程、Phone进程等。java应用程序对应的用户id从10000开始。从用户角度讲,一个应用程序实际上就是一个用户id。
share-user
标签定义了共享用户id对应的签名和权限,他和package标签的含义本质是相同的。share-user
的属性包括name
和userId
,其意义和package
标签的属性相同,包含的子标签也和package
相同,包括sigs
和perms
标签。
包管理服务启动过程
PMS本身是一个Service
,他与WMS、AMS等重要的系统服务运行在同一个进程——系统进程中。当SystemService
启动时候,它也会启动,启动方法如以下代码所示:
public static final IPackageManager main(Context context,boolean factoryTest){
PackageManagerService m = new packageManagerService(Context,factoryTest);
ServiceManager.addService("package",m);
return m;
}
PMS的启动过程实际上就是该类的构造函数所包含的各种初始化过程。在介绍构造函数内部执行流程前,首先需要了解一下各主要功能之间的关系,因为启动过程实际上是读取相关XML文件中的信息,并把这些信息存放到相关的类成员变量之中。
各主要功能类的关系
包管理服务相关的主要类,作为PMS的内部类被定义。PMS的内部类Settings
基本上包含了所有包管理所需的全部信息,主要包含几类变量:
- 包属性信息
- File mSettingsFilename:配置文件名称,指的就是packages.xml文件
- File mBackupSettingFilename:配置文件可以有一个
backup
文件,该backup
文件用于系统意外关机后原始配置文件进行对比,以检查系统的完整性。 - File mPackageListFilename:指的就是packages.list文件,保存了所有应用程序列表,该文件中每一行对应一个应用程序
Hashmap<String,PackageSetting> mPackages
:在PSM启动后,改变量被填充为包管理信息,这些信息来源于packages.xml
。
- 用户ID相关信息
HashMap<String,SharedUserSetting> mSharedUsers
:改变量保存了所有共享id的信息,来源于packages.xml
中的shared-user
标签ArrayList<Object> mUserIds
:所用用户ID
- 权限管理相关信息
ArrayList<Signature> mPastSignatures
:保存了所有的签名HashMap<String,BasePermission> mPermissions:
保存了所有的权限,该Map是从permission
名称到permission
信息的一个映射。HashMap<String,BasePermission> mPermissionTrees
:对应packages.xml
文件中的permission-tree
标签,目前一直为空,没有被使用。
- 删除信息
ArrayList<String> mPackagesToBeCleaned
:保存的是packages.xml中的cleaning-package标签中包含的package
列表,该列表来源胡那些已经被卸载的应用程序。但其所办函的数据目录处于外部存储器而没有被删除。
PMS主体启动过程
- 创建一个
PMS.Settings
,在该对象的构造函数中,将给裁员变量静态赋值 - 调用
mSettings.addSharedUserLP()
添加四个共享用户ID - 创建一个
Installer
对象,该对象将辅助程序的安装。 - 给以下几个数据文件路径静态赋值:
mAppDataDir
,代表程序的数据目录,其值为/data/data
mSecureAppDataDir
,所谓的解密数据区,目前无用,路径为/data/secure/data
mDrmAppPrivateInstallDir
,所谓的DRM数据区,目前无用,路径为/data/app-private
- 调用
readPermission()
函数,从/system/etc/permissions
目录下读取全部的xml文件 - 调用
mSettings
对象的readLP()
函数从/data/system/packages.xml
文件中读取所有应用程序中和包管理相关的信息,将其保存到mSettings.mPackages
变量中。 - 对java系统中的库文件进行dex提取(转换)
- 创建
FileObserver
,用来检测目录中的添加、删除文件的时间,并当时间发生时执行相应的操作 - 调用
scanDirLI()
扫描AndroidManifest
文件,并将扫描结果保存到PMS中。 - 删除已经不存在程序对应的数据记录。
- 清除没有安装成功的数据记录
- 调用
deleteTempPackageFiles()
删除/data/app
目录下,一vmdl开头的.tmp结尾的文件 - 如果系统升级,调用
updatePermissionsLP()
重新为应用程序赋予权限。 - 调用
mSettings.writeLP()
将mSettings.mPackages
中的数据值重新写入packages.xml
文件中
readPermission()内部过程
- 确保
/system/etc/permissions
目录的存在 - 使用循环,读取该目录下每一个
XML
文件,并调用readPermissionFromXml()
函数逐个解析。 - 最后解析
platform.xml
文件
mSettings.readLP()
该函数会读取所有的安装包信息,主要读取的是packages.xml
文件,读取过程可以分为以下几个步骤:
- 判断
backup
文件是否存在,如果存在说明出现异常,那么进行错误记录,并删除pacages.xml
文件,然后从backup
文件中读取相关的包管理信息。 - 解析xml文件中包含的各种标签。
- 处理
mPendingPackages
列表中对应的包信息
scanPackageLI()内部过程
对程序包进行扫描,并知心程序的安装过程
- 把原始的APK文件复制到相应的程序目录下,
- 为程序创建相应的数据目录及提取dex文件,并修改程序包管理信息等。
mSettings.writeLP()
该函数作用失败mSettings.writeLP()
的数据分别写入packages.xml
和packages.list
文件中
应用程序的安装和卸载
安装及卸载程序的操作都是由PMS完成,安装程序的过程包括在程序目录下创建以包名称命名的程序文件、创建程序数据目录,以及把程序信息保存到相关的配置文件packages.xml
中,卸载过程则是相反的一个操作。
应用程序安装过程
- 调用
PackageManager
类的installPackage()
函数开始,该函数间接调用PMS
的installPackage()
函数。 - 发送异步消息,启动文件复制流程,安装过程首先就是文件复制的过程
- 文件复制是调用
handleStartCopy()
函数完成的,下面开一下具体的实现- 检查
flags
标识,确保参数没有冲突,比如不能同时安装到SD卡和内部存储中 - 以
pacageURI
和flags
为参数,调用MCS服务的getMinimalPackageInfo()
,询问MDS服务是否能够安装packageURI
指定的程序。 - 如果MCS认为可以安装
packageURI
指定的程序,则回调installLocationPolicy()
。 - 调用
InstallArgs
类的coypApk()
函数进行文件复制
- 检查
应用程序卸载过程
- 卸载过程基本上就是安装过程的逆向操作。
- 首先检查该程序是否有
Admin
管理。意义是告诉系统,哪些是被强制安装的,这类程序不可卸载 - 调用
deletPackageLI()
开始删除程序,包括程序文件以及PMS的mSettings.mPackages
变量及mPackages
变量。以及其他相关变量中关于该程序的包管理信息 - 发送广播消息报告删除成功