概述:https://mp.weixin.qq.com/s/i6LlSf8kHOBOk5iKoVVoxA
概述包含哪些部分:
权限管理模块负责apk权限相关的事情,比如请求某个权限,apk权限状态存储,收集所有apk声明的权限
共享库模块负责apk使用到的所有共享库
记录存储模块会把apk相关的很多信息记录并且存储到文件中,比如apk安装后关于apk安装的信息会存储下来,这样就可以供其他使用者检索
所有apk信息模块会收集所有已安装apk的AndroidManifest解析出来的信息,以供其他使用者检索
四大组件模块为了加快检索四大组件的速度,会把所有已安装apk的四大组件信息收集起来
apk管理模块主要负责apk的安装/卸载/更新,它是根基模块,因为它的某个功能会对其他模块产生影响。
快照管理模块主要目的为加快访问PackageManagerService中的各种数据。
模块的启动
共享库模块、记录存储模块、权限管理模块三个模块的启动为扫描所有apk做准备,而扫描所有apk又分为扫描所有系统apk和扫描所有普通apk,扫描所有apk最终的结果是所有apk信息最终存放在了所有apk信息模块和四大组件模块,这样使用者就可以从PackageManagerService获取到某个apk的信息了,比如获取某个Activity的信息。
而扫描所有apk也会或多或少的影响共享库模块、记录存储模块、权限管理模块,比如扫描某个系统apk,该系统apk声明了新的权限、声明了新的共享库,则会把声明的共享库信息记录在共享库模块,而声明的权限则会通知权限管理模块增加此权限,同时记录存储模块也会把该权限存储到文件中。
APK的安装
该apk的安装信息 (apk包名、版本信息、apk文件路径、appid、声明和使用的权限等) 以PackageSetting对象添加到记录存储模块,记录存储模块会把所有的PackageSetting对象重新写入到文件中
该apk信息 (apk包名、声明的四大组件、版本信息、解析的权限等) 添加到所有apk信息模块
该apk信息中的声明的四大组件信息添加到四大组件模块
因为该apk使用了某个共享库,则会从共享库模块把该共享库信息查询出来交给PackageSetting对象
因为该apk声明和使用了权限,则声明和使用的权限会被添加到权限管理模块。
https://mp.weixin.qq.com/s/GAgTBX60Zi9umRYzStF4Hw
lib目录 该目录下面包含了使用到的so库
res、assets目录 这两个目录下面包含了各种资源比如图片、layout文件等
META-INF 该目录下面包含了和签名证书有关的文件
dex java/kotlin编译后的字节码文件
resources.arsc 文件可以视为一个资源表,它存储了所有资源的ID和这些资源在APK文件或其他位置中的引用
AndroidManifest.xml
PMS划分:
apk管理模块、权限管理模块、共享库模块、记录存储模块、所有apk信息模块、四大组件模块。
权限管理模块负责apk权限相关的事情,比如请求某个权限,apk权限状态存储,收集所有apk声明的权限
共享库模块负责apk使用到的所有共享库
记录存储模块会把apk相关的很多信息记录并且存储到文件中,比如apk安装后关于apk安装的信息会存储下来,这样就可以供其他使用者检索
所有apk信息模块会收集所有已安装apk的AndroidManifest解析出来的信息,以供其他使用者检索
四大组件模块为了加快检索四大组件的速度,会把所有已安装apk的四大组件信息收集起来
apk管理模块主要负责apk的安装/卸载/更新,它是根基模块,因为它的某个功能会对其他模块产生影响。
记录存储模块内容
https://mp.weixin.qq.com/s/ZeV0NpLU9jKx83lYuWwDIg
记录存储模块,服务于PackageManagerService服务,所有的工作都交给了Settings类,我的作用是记录apk的安装及附加信息并且把这些信息存储到文件中,记录存储的这些信息的主要目的是供使用者来查询,比如可以通过包名查询某个apk是否被安装
是什么:apk的包名、版本号、apk安装路径、安装时间、签名、请求权限状态信息等信息记录下来
apk安装的工作交给了PackageSetting类。不管是系统apk还是普通apk只要被安装在Android设备上,都会对应一个PackageSetting实例
PackageSetting类负责apk安装信息的记录,Settings类负责apk安装信息的存储。特意将PackageSetting的属性划分为apk基础信息、不可序列化信息、apk的用户状态信息、请求权限状态信息四部分
当一个apk被安装成功后,PackageSetting类会把apk安装的信息分为apk基础信息、不可序列化信息、apk的用户状态信息、请求权限状态信息四部分来记录。
- apk基础信息有包名、apk版本信息、appid、apk文件路径等,而Settings类会把基础信息存储在 /data/system/package.xml 文件中。
- 不可序列化信息是不需要存储的,而是每次Android设备重新启动的时候需要重新收集。
- apk的用户状态信息记录了当前用户安装apk的状态信息 (状态比如是否安装),而Settings类会把这些信息存储在 /data/system/users/userid/package-restrictions.xml 文件 (路径中的userid指的是用户id,比如0,10)。
- 请求权限状态信息记录了当前用户下所有apk的所有申请的权限的状态 (权限的状态比如是否允许),而Settings类会把这些信息会存储在 /data/misc_de/userid/apexdata/com.android.permission/runtime-permissions.xml 文件 (路径中的userid指的是用户id,比如0,10)
在Settings类中存在类型为WatchedArrayMap<String, PackageSetting>的属性mPackages,它就代表所有的已安装apk,它的key值为包名,如下代码:
final WatchedArrayMap<String, PackageSetting> mPackages;
当apk安装时,Settings类会为mPackages属性增加一个记录,并且把这些信息更新到相应的文件;当apk删除时,Settings类会从mPackages把相应的记录删除,并且更新相应的文件;当apk升级时,Settings类会从mPackages中找到对应记录,更新相应记录信息,并且更新相应文件。
声明权限信息
先来介绍下声明权限吧,权限分为声明权限和使用权限,每个apk都可以声明权限,声明的权限是可以被其他apk来使用的。声明权限是在AndroidManifest中使用permission标签,记录所有声明权限的工作交给了LegacyPermissionSettings类
记录存储模块的初始化
所有的事情都交给了Settings类,在PackageManagerService类中会存在类型为Settings的属性mSettings (如下代码)
final Settings mSettings;
而我的初始化是从PackageManagerService的构造方法开始的,PackageManagerService会调用Settings对象的readLPw方法开始初始化工作,主要做了以下工作:
- 从 /data/system/package.xml 文件中把所有信息都读取出来,并把相应的数据填充给相应的记录类,比如遇到package标签,则会把数据填充给PackageSetting对象;比如遇到permissions标签会把每个声明的权限添加到LegacyPermissionSettings对象中。
- 从 /data/system/users/userid/package-restrictions.xml (路径中的userid指的是用户id,比如0,10) 文件中,把每个apk的用户状态信息读取出来
- 从/data/misc_de/userid/apexdata/com.android.permission/runtime-permissions.xml (userid同上) 文件中,把每个apk的请求权限状态信息读取出来。
一句话总结我的初始化工作就是从文件中把先前存储的信息读取出来,并且填充给相应的类,初始化工作完成后,我就可以为PackageManagerService提供服务了。
共享库
https://mp.weixin.qq.com/s/fETMe8RezB7tswoasqlrBg
在Android中共享库是指由系统提供的可以被多个程序使用的系统库,而共享库的使用是在AndroidManifest.xml文件中使用uses-library标签 (使用java库)或者uses-native-library标签,系统Apk是可以通过library标签声明共享库的,framework.jar是一个特殊的共享库。
总结下共享库被使用的过程:(以下基于某个Apk使用了某个共享库)
- 共享库模块会根据共享库名字、版本信息等查询到对应的共享库信息,并且把共享库信息交给对应的Apk (其实保存在PackageSetting对象)
- 当App进程启动后,会从ActivityManagerService拿到对应的ApplicationInfo信息,而ApplicationInfo存储了apk路径、包名、apk版本号、共享库信息等等非常关键的信息
- App进程会把ApplicationInfo中的apk路径、native lib路径、共享库文件路径等信息交给PathClassLoader,PathClassLoader的父类BaseDexClassLoader会持有关于共享库相关的ClassLoader
- 在查找某个类时,会先从共享库对应的ClassLoader查找,找到了返回;否则从BaseDexClassLoader中查找。
用一句简单的话总结就是:共享库文件会被加载到App进程的PathClassLoader中,这样App进程就可以找到共享库的类了。从此也可以看出除framework.jar外的共享库是在每个App进程都占用自己的内存空间,没有共享内存。
APK信息
https://mp.weixin.qq.com/s/NiEV72mEMKg6ovcBIsRTbg
标签数据类的命名格式是ParsedXXXXImpl (如ParsedActivityImpl)
每个标签数据类都有自己对应的接口,接口命名格式为ParsedXXXX (如ParsedActivity),即使像ParsedUsesPermissionImpl这么简单的类它都有自己的接口ParsedUsesPermission
ParsedComponentImpl类收集了大部分标签共用的信息,而它的接口是ParsedComponent
ParsedMainComponentImpl类收集了四大组件标签共用的信息,而它的接口是ParsedMainComponent
- 四大组件对应的数据类分别是ParsedActivityImpl、ParsedServiceImpl、ParsedProviderImpl。
- 权限相关的标签permission、uses-permission对应的数据类是ParsedPermissionImpl、ParsedUsesPermissionImpl。
- AndroidManifest会对应一个PackageImpl实例,而它的父类ParsingPackageImpl存放了所有的数据类。
标签数据类有一个规则:每个类的命名规则是ParsedXXXXImpl (XXXX 代表对应的标签),每个类都有对应的接口命名规则是ParsedXXXX 。
apk的安装流程
PackageManagerService、Settings、PackageInstallerSession、PackageInstallerService、InstallPackageHelper、Installer
apk的安装会经过前期准备、安装、后期收尾这三个阶段,前期准备成功后才会进入安装阶段,安装阶段成功后才会进入后期收尾阶段。除了后期收尾外,前两个阶段只要发生错误就会停止apk的安装。
前期准备的工作有拷贝、完整性校验、解析apk、提取native libs、版本号验证;
安装的工作有准备 (Prepare) 、扫描 (Scan) 、调和 (Reconcile) 、提交 (Commit) ;
后期收尾的工作有创建app data根目录、dex优化、移除已有apk、发送安装成功广播。
- 不管apk是通过adb安装的(apk存储于PC的磁盘)还是应用市场安装的(apk存储于设备),首先apk会被拷贝到 /data/app/xxx.tmp目录下面(xxx是一个随机生成的字符串)
- 在经过重重的验证、校验(签名、版本号),/data/app/xxx.tmp 目录会重命名为 /data/app/[randomStrA]/[packageName]-[randomStrB] 目录,也就是被拷贝的apk最终路径是 /data/app/[randomStrA]/[packageName]-[randomStrB]/base.apk 。同时会为apk生成一个唯一的id又称appid
- 解析apk的AndroidManifest中的内容为ParsedPackage,ParsedPackage中的权限等信息经过验证通过后,ParsedPackage传递给PMS,这样其他使用者比如ActivityManagerService就可以从PMS获取刚安装apk的信息了。
- 刚安装的apk的安装信息比如包名、版本、签名证书、安装时间等会存储到PackageSetting,PackageSetting会传递给Settings,Settings会把它持久化到packages.xml文件。
- 创建app data根目录,app data根目录是apk运行期间数据存储的根目录,并且app data根目录只有当前apk程序有读写执行权,其他不用没有任何权限。
- 对apk的dex进行优化,优化即使不成功也不影响apk的安装,dex优化可以保证app运行性能上的提升。
-
发送安装成功广播。apk越大包含的so越多,安装apk的时间越长。主要时长体现在拷贝、提取native libs、dex优化这几项工作。