PKMS启动详解(五)之Android包信息体和包解析器(中)
Android PackageManagerService系列博客目录:
PKMS启动详解系列博客概要
PKMS启动详解(一)之整体流程分析
PKMS启动详解(二)之怎么通过packages.xml对已安装应用信息进行持久化管理?
PKMS启动详解(三)之BOOT_PROGRESS_PMS_START流程分析
PKMS启动详解(四)之Android包信息体和包解析器(上)
PKMS启动详解(五)之Android包信息体和包解析器(中)
PKMS启动详解(六)之Android包信息体和包解析器(下)
PKMS启动详解(七)之BOOT_PROGRESS_PMS_SYSTEM_SCAN_START阶段流程分析
PKMS启动详解(八)之BOOT_PROGRESS_PMS_DATA_SCAN_START阶段流程分析
引言
在前面的博客PKMS启动详解(四)之Android包信息体和包解析器(上)中我们重点从Android包管理机制的设计者角度出发,着重分析了:
- Android包管理机制中的Android包信息体设计思想和源码逻辑
- Android包管理机制中的Android包解析器PackageParser设计思想
那么在今天的博客中,我们将再接再厉,一鼓作气的继续分析Android包解析器PackageParser源码实现逻辑!
在正式开始本篇的博客相关分析前,还是非常有必要放出包解析器PackageParser的核心结构图(前面是借用其他博主的,这里就自己花费些时间单独画了):
同时本篇博客对于包解析器PackageParser类源码,会遵从如下几个方面来进行入手来分析(也可以人为是本篇博客的中心):
1.从包管理机制设计者意图了解它,即它的概述和定义是什么
2.它作为一个工具类,它的核心成员变量是什么(当然是和包信息体的封装有关)
3.它作为一个工具类,它的核心方法有那些(核心方法就是解析Android包)
注意:本篇的介绍是基于Android 7.xx平台为基础的(并且为了书写简便后续PackageManagerService统一简称为PKMS),其中涉及的代码路径如下:
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
frameworks/base/core/java/android/content/pm/PackageParser.java
一.从源码的角度概述Android包解析器PackageParser
在前面的博客中,我们从理论上知道了包解析器PackageParser是Android包管理机制设计的一个用于解析Android包的工具类,但是对于它的具体实现我们知之甚少,从这个章节开始我们就从源码角度出发对其展开猛烈的攻势,争取将其一举拿下!实干出真知!
通常了解一个人首先是从看脸开始(也不排除其它的位置啊,哈哈!),而看Android源码类通常可以从设计者着对它的概述开始,我们这里对于包解析器PackageParser的深入了解也不能免俗的遵循这个定理,我们看看设计者是如何对它进行定义的,如下:
public class PackageParser {
...
}
对于上述的描述,英文比较好的读者估计连蒙带猜基本能内部消化了,对于不能内部消化的只能且听我来分解了:
上述的注释非常经典没有过多的拖泥带水,我们对其强撸一番!
1.PackageParser是用于磁盘上包文件(APKs)的解析器。它即支持打包成单个“单块”APK的应用程序的解析,同时也支持打包成多个APK在单个目录中的“集群”应用程序的解析
2.一个"集群"APK有一个"基准"APK(base APK)组成和其他一些"分割"APK(“split” APKs)构成,其中这些"分割"APK用一些数字来分割。这些"分割"APK的必须都是有效的安装,同时必须满足下面的几个条件:
- 所有APKs必须具有完全相同的包名、版本代码和签名证书。
- 所有APK必须有唯一的拆分名称。
- 所有安装必须包含一个单一的APK
3.并且该类是一个hide类,说明它只能用于Android内部使用,不能被第三方应用使用
1.1 Android安装包的形态
在前面的注释中,多次提到了single APK和multiple APKS这两个概念,那么它们纠结表示什么呢!我们们分别来说(其实从单复数就说明情况了)。
1.1.1 Single APK
对于Single APK就比较好理解了,它通常所开发的APK,即每一个应用安装包有且只存在一个.apk文件,形如下面的形式:
xxx:/system/system_ext/priv-app/Settings $ ls
Settings.apk
1.1.2 Multiple APKS
在Android L(5.0)以后,支持APK拆分,即一个APK可以分割成很多部分,位于相同的目录下,每一个部分都是一个单独的APK文件,所有的APK文件具备相同的签名,在APK解析过程中,会将拆分的APK重新组合成内存中的一个Package。对于一个完整的APK,Android称其为signle APK;对于拆分后的APK,Android称其为multiple APKS。此时形如下面的形式:
xxx:/system/system_ext/priv-app/Settings $ ls
Settings-base.apk Settings-split.apk
对于有过一定Android开发经验的读者一定知道在Android L(5.0)以前,APK文件都是直接位于app或priv-app目录下,譬如短彩信APK的目录就是/system/priv-app/Mms.apk;到了Android L(5.0)之后,多了一级目录结构,譬如短彩信APK的目录是/system/priv-app/Mms/Mms.apk,这是Android为了支持APK拆分而做的改动,如果要将短彩信APK进行拆分,那所有被拆出来的APK都位于/system/priv-app/Mms/即可,这样在包解析时,就会变成以multiple APKS的方式解析目录。
并且这里有一点需要注意,对于/system/priv-app/Mms/下面的安装包,无论他是否multiple APKS形式的,最后都会调用parseClusterPackage方法来进行解析,这个读者一定要注意,注意!千万不要把Multiple APKS和Cluster解析混淆了!它们之间的前提是Multiple APKS一定是采取Cluster解析方式,但是采取Cluster不一定是Multiple APKS也可能是Single APK。
关于Multiple APKS详细介绍可以参看谷歌官网多APK支持!
1.1.3 不同安装包的解析方式
对于Android安装包的形式我们理解清楚了,从而会导致包解析器PackageParser对于上述二者会采取不同的解析方式,但是最后都会徐途同归(至于怎么具体解析的,后续详细分析)!
并且这里再次强调一点,对于Single APK安装包采取parseMonolithicPackage()还是parseClusterPackage()的解析方式,这个取决于安装包的路径是否是一个文件夹!注意,注意,注意!
1.2 包解析器PackageParser解析Android包的步骤
通过前面的努力我们知道了,PackageParser的核心功能就是解析各种类型的Android包(其实也就两种而已),而解析包的最最主要逻辑就是解析APK文件,在正式开始PackageParser解析Android包之前,这里我们先给出解析一个APK主要是分为两个步骤:
- 将APK解析成Package数据结构:即解析APK文件为Package对象的过程。
此处是我们今天博客的重点!
- 将解析得到的Package填充为PackageInfo数据结构:即由Package对象生成Package对象生成PackageInfo包信息体的过程
关于Android包信息体我们在前面的博客中已经重点的介绍过,重点关注它是怎么被填充的
二.PackageParser重要成员
对于PackageParser我们该了解的不该了解的都了解了,是时候对其展开真刀真枪的实干了。这里我们先从其成员(或者是内部类)入手展开!在PackageParser中定义了非常多的内部类用来解析PackageParser,如下所示:
2.1 IntentInfo内部类
通过前面的博客总结我么可知,对于APK中的androidmanifest.xml的<activity>,<service>标签,都可以配置<intent-filter>,来过滤其可以接收的Intent,这些信息也需要在包解析器中体现出来,为此组件Component依赖于IntentInfo这个数据结构。每一个具体的组件所依赖的IntentInfo不同,所以Component和IntentInfo之间的依赖关系采用了桥接(Bridge)这种设计模式,通过泛型的手段实现。
所以这里我们就来看下IntentInfo的实现。
没有啥好说的,直接来看源码!
public class PackageParser {
...
public static class IntentInfo extends IntentFilter {
public boolean hasDefault;
public int labelRes;
public CharSequence nonLocalizedLabel;
public int icon;
public int logo;
public int banner;
public int preferred;
}
...
}
这里这里可以看到IntentInfo就是对IntentFilter意图过滤器的再次封装,它是用来承载包组件<intent-filter>标签的数据结构。最终我们会得到如下的类图:
最后我们补充一点,对于IntentFilter我想读者肯定很熟悉了,它就是江湖人称的意图过滤器,通常它的过滤规则过滤规则包含以下三个方面:
- Action: 每一个IntentFilter可以定义零个或多个标签,如果Intent想要通过这个IntentFilter,则Intent所辖的Action需要匹配其中至少一个。
- Category: 每一个IntentFilter可以定义零个或多个标签,如果Intent想要通过这个IntentFilter,则Intent所辖的Category必须是IntentFilter所定义的Category的子集,才能通过IntentFilter。譬如Intent设置了两个Category:CATEGORY_LAUNCHER和CATEGORY_MAIN,只有那些至少定义了这两项Category的IntentFilter,才会放行该Intent。
启动Activity时,会为Intent设置默认的Category,即CATEGORY_DEFAULT。目标Activity需要添加一个category为 CATEGORY_DEFAULT的IntentFilter来匹配这一类隐式的Intent。 - Data:每一个IntentFilter可以定义零个或多个,数据可以通过类型(MIMEType)和位置(URI)来描述,如果Intent想要通过这个IntentFilter,则Intent所辖的Data需要匹配其中至少一个。
2.2 Component内部类
通过前面的博客总结我么可知,一个包中有很多组件,为此设计了一个高层的基类Component,所有具体的组件都是Component的子类。在具体捯饬相关组件前,我们先来看下高层的基类Component组件它是怎么设计并且用代码实现的。
无需多言,直接开撸源码!
public class PackageParser {
...
public static class Component<II extends IntentInfo> {
public final Package owner;
public final ArrayList<II> intents;
public final String className;
public Bundle metaData;
ComponentName componentName;
String componentShortName;
}
...
}
Component被设计成为一个基类,是Android包组件的众多组件的基类数据结构类,然后各个组件类继承并扩展它。并且Android包中每一个具体的组件所依赖的IntentInfo不同,所以Component和IntentInfo之间的依赖关系采用了桥接(Bridge)这种设计模式,通过泛型的手段实现。最终通过我们如上一番分析,我们会得到如下的类图:
2.3 XXXIntentInfo内部类
在前面的章节2.1 IntentInfo内部类中我们有说到了包管理设计者从架构层次出发设计了一个数据结构基类IntentInfo来表示组件可以过滤接收的Intent信息。而Android包管理中具体到不同的组件,会继承它扩展它,这里共有如下三个扩展类:
- ActivityIntentInfo
- ServiceIntentInfo
- ProviderIntentInfo
细心的读者可以留意了,Android常见的四大组件不是四个吗,上述定义的XXXIntentInfo为啥只有三个啊!好吗,实际原因是receiver广播组件和Activity复用了同一个类ActivityIntentInfo而已。
接下来我们简单来看下上述的几个类的源码实现,如下:
public class PackageParser {
...
public final static class ActivityIntentInfo extends IntentInfo {
public final Activity activity;
public ActivityIntentInfo(Activity _activity) {
activity = _activity;
}
...
}
public final static class ServiceIntentInfo extends IntentInfo {
public final Service service;
public ServiceIntentInfo(Service _service) {
service = _service;
}
...
}
public static final class ProviderIntentInfo extends IntentInfo {
public final Provider provider;
public ProviderIntentInfo(Provider provider) {
this.provider = provider;
}
...
}
}
上述的源码比较简单,就是Android包的组件根据实际意图情况继承扩展了IntentInfo 最终通过我们如上一番分析,我们会得到如下的类图:
2.4 Component子类组件实现
通过前面的博客总结我么可知,一个包中有很多组件,为此设计了一个高层的基类Component,所有具体的组件都是Component的子类。什么是组件呢?AndroidManifest.xml文件中所定义的的一些标签,就是组件,譬如<activity>,<service>,<provider>,<permission>等,这些标签分别对应到包解析器中的一个数据结构,它们各自有自身的属性。并且这些组件我们可以将其归纳为如下两大类:
接下来我们简单来看下上述的几个类的源码实现,如下:
public class PackageParser {
...
public final static class Activity extends Component<ActivityIntentInfo> {
public final ActivityInfo info;
public Activity(final ParseComponentArgs args, final ActivityInfo _info) {
...
}
}
public final static class Service extends Component<ServiceIntentInfo> {
public final ServiceInfo info;
public Service(final ParseComponentArgs args, final ServiceInfo _info) {
...
}
}
public final static class Provider extends Component<ProviderIntentInfo> {
public final ProviderInfo info;
public boolean syncable;
public Provider(final ParseComponentArgs args, final ProviderInfo _info) {
...
}
}
public final static class Instrumentation extends Component<IntentInfo> {
public final InstrumentationInfo info;
public Instrumentation(final ParsePackageItemArgs args, final InstrumentationInfo _info) {
...
}
}
public final static class Permission extends Component<IntentInfo> {
public final PermissionInfo info;
public boolean tree;
public PermissionGroup group;
public Permission(Package _owner) {
...
}
public Permission(Package _owner, PermissionInfo _info) {
...
}
}
public final static class PermissionGroup extends Component<IntentInfo> {
public final PermissionGroupInfo info;
public PermissionGroup(Package _owner) {
...
}
public PermissionGroup(Package _owner, PermissionGroupInfo _info) { ...
}
}
}
上述的源码比较简单,就是Android包的组件根据实际组件情况继承扩展了Component基类,最终通过我们如上一番分析,我们会得到如下的类图:
2.5 内部类ParsePackageItemArgs和ParseComponentArgs
分析源码就是这么枯燥且乏味的一件事件,蜻蜓点水吗不能深入!太深入了,又限于细节不能自拔了。所以对于一些细节型的类,我们就简单过下!
ParsePackageItemArgs和ParseComponentArgs是一对父子关系类,其设计的用途主要是用来存储Android包组件中额外的一些参数。这个没有啥好说的,简单看下源码:
public class PackageParser {
...
static class ParsePackageItemArgs {
final Package owner;
final String[] outError;
final int nameRes;
final int labelRes;
final int iconRes;
final int roundIconRes;
final int logoRes;
final int bannerRes;
String tag;
TypedArray sa;
ParsePackageItemArgs(Package _owner, String[] _outError,
int _nameRes, int _labelRes, int _iconRes, int _roundIconRes, int _logoRes,
int _bannerRes) {
...
}
}
static class ParseComponentArgs extends ParsePackageItemArgs {
final String[] sepProcesses;
final int processRes;
final int descriptionRes;
final int enabledRes;
int flags;
ParseComponentArgs(Package _owner, String[] _outError,
int _nameRes, int _labelRes, int _iconRes, int _roundIconRes, int _logoRes,
int _bannerRes,
String[] _sepProcesses, int _processRes,
int _descriptionRes, int _enabledRes) {
...
}
}
...
}
关于二者的介绍,就先这样吗,类图也省了,小芝麻的就不捡起来了!
2.6 内部类NewPermissionInfo和SplitPermissionInfo
Android包解析器中的组件的基类以及子类基本介绍完毕,剩下来的就是一些零碎的一些成员类信息,譬如这个章节要说的NewPermissionInfo和SplitPermissionInfo。
NewPermissionInfo和SplitPermissionInfo见名思意,是和Android组件权限有关的,我们还是简单来看下!
public class PackageParser {
public static class NewPermissionInfo {
public final String name;
public final int sdkVersion;
public final int fileVersion;
public NewPermissionInfo(String name, int sdkVersion, int fileVersion) {
...
}
}
public static class SplitPermissionInfo {
public final String rootPerm;
public final String[] newPerms;
public final int targetSdk;
public SplitPermissionInfo(String rootPerm, String[] newPerms, int targetSdk) {
...
}
}
}
关于二者的介绍,就先这样吗,类图也省了,小芝麻的就不捡起来了!
2.7 内部类ApkLite
前面的几个章节中我们分析了Android包组件相关的一些基类和其它的一些零碎类,从这个章节开始我们将要分析的是组织结构更高一个级别关于Android包的一些类的信息。
ApkLite抛开源码字面意思是精简的APK,通过注释看它表示的是轻量级的存储解析独立APK信息的一个数据结构类,我们从源码入手来看下!
public class PackageParser {
...
public static class ApkLite {
public final String codePath;
public final String packageName;
public final String splitName;
public final int versionCode;
public final int revisionCode;
public final int installLocation;
public final VerifierInfo[] verifiers;
public final Signature[] signatures;
public final Certificate[][] certificates;
public final boolean coreApp;
public final boolean multiArch;
public final boolean use32bitAbi;
public final boolean extractNativeLibs;
public ApkLite(String codePath, String packageName, String splitName, int versionCode,
int revisionCode, int installLocation, List<VerifierInfo> verifiers,
Signature[] signatures, Certificate[][] certificates, boolean coreApp,
boolean multiArch, boolean use32bitAbi, boolean extractNativeLibs) {
...
}
}
...
}
最终经过我们的一顿倒腾,可以得到如下的一个类图:
2.8 内部类PackageLite
如果说前面的ApkLite是用来封装APK文件的一个数据结构类,那么这里的PackageLite则是用来封装一个“独立的”应用程序包信息轻量级数据结构类!
这里特别注意二者的维度是不同的,一个安装包下可能存在几个APK文件!
PackageLite抛开源码字面意思是精简的安装包信息,通过注释看它表示的是轻量级的存储解析独立安装包信息的一个数据结构类,我们从源码入手来看下!
public class PackageParser {
...
public static class PackageLite {
public final String packageName;
public final int versionCode;
public final int installLocation;
public final VerifierInfo[] verifiers;
public final String[] splitNames;
public final String codePath;
public final String baseCodePath;
public final String[] splitCodePaths;
public final int baseRevisionCode;
public final int[] splitRevisionCodes;
public final boolean coreApp;
public final boolean multiArch;
public final boolean use32bitAbi;
public final boolean extractNativeLibs;
public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
String[] splitCodePaths, int[] splitRevisionCodes) {
...
}
...
}
}
过于其中的每个变量信息,建议读者最好是带入实际情况进行分析,并且最好不要开始就分析有拆包的情况,先得到主干,拆分的情况可以对整体调理清楚之后再行深入!
总之,最终经过我们的一顿倒腾,可以得到如下的一个类图:
2.8 内部类Package
分析至此,读者有没有一个感觉就是PackageParser包解析器为我们捣鼓了一大堆关于Android包的零部件,都是算乱的遗落在角落各处!有一种十分凌乱拉跨的感觉,难道Android包管理的设计者没有考虑拿一个大一点的容器或者收纳箱将那么一大堆零件归纳起来吗?肯定有了,它就是我们将要介绍的Package类,它才是Android包对应的集大成者的一个数据结构类,几乎囊括了Android包的方方面面。我们来看下,Android包管理者是怎么对它进行设计的!
public class PackageParser {
...
public final static class Package {
public String packageName;
public String[] splitNames;
public String volumeUuid;
public String codePath;
public String baseCodePath;
public String[] splitCodePaths;
public int baseRevisionCode;
public int[] splitRevisionCodes;
public int[] splitFlags;
public int[] splitPrivateFlags;
public boolean baseHardwareAccelerated;
public final ApplicationInfo applicationInfo = new ApplicationInfo();
public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
public final ArrayList<Service> services = new ArrayList<Service>(0);
public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
public final ArrayList<String> requestedPermissions = new ArrayList<String>();
public ArrayList<String> protectedBroadcasts;
public Package parentPackage;
public ArrayList<Package> childPackages;
public ArrayList<String> libraryNames = null;
public ArrayList<String> usesLibraries = null;
public ArrayList<String> usesOptionalLibraries = null;
public String[] usesLibraryFiles = null;
public ArrayList<ActivityIntentInfo> preferredActivityFilters = null;
public ArrayList<String> mOriginalPackages = null;
public String mRealPackage = null;
public ArrayList<String> mAdoptPermissions = null;
public Bundle mAppMetaData = null;
public int mVersionCode;
public String mVersionName;
public String mSharedUserId;
public int mSharedUserLabel;
public Signature[] mSignatures;
public Certificate[][] mCertificates;
public int mPreferredOrder = 0;
public long[] mLastPackageUsageTimeInMills =
new long[PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT];
public Object mExtras;
public ArrayList<ConfigurationInfo> configPreferences = null;
public ArrayList<FeatureInfo> reqFeatures = null;
public ArrayList<FeatureGroupInfo> featureGroups = null;
public int installLocation;
public boolean coreApp;
public boolean mRequiredForAllUsers;
public String mRestrictedAccountType;
public String mRequiredAccountType;
public String mOverlayTarget;
public int mOverlayPriority;
public boolean mTrustedOverlay;
public ArraySet<PublicKey> mSigningKeys;
public ArraySet<String> mUpgradeKeySets;
public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping;
public String cpuAbiOverride;
public boolean use32bitAbi;
public byte[] restrictUpdateHash;
...
}
...
}
上面的有些注释是我直接翻译过来的,所以有些指代不是很明确,这个各位见谅!因为Pakcage的成员变量太多了,关于各个成员的具体指代只能强烈建议读者在学习的时候最好是自己代入实际情况,然后自行进行相关的验证是再好不过的了。
并且还是我开头说的,在这分析的过程中不要代入拆包的这种情况进来,否则分析起来就会很麻烦造成主干,细节纠缠不清!
通过注释和源码我们可以知道Package是对Android包的集大成者的一个数据结构类,它是通过解析APK而对应的一个"包"的大类,这个包代表的就是一个磁盘上的APK安装包所有的信息。总之经过我们一顿猛虎一半的操作,我们最终会得到如下的一个Android包机制的大类图,如下:
三.PackageParser重要方法
如果说前面我们分析的PackageParser成员变量等相关的信息,是包管理器为了烹饪Android包准备的各种顶级食材的话,那么PackageParser中定义的各种方法就是为了烹饪Android包准备的各种烹饪技术了。是不是很好奇,Android包管理器是怎么将前面的各种食材进行烹饪得到各种美味呢?这也是本章节和后续博客重点要分析的内容!
这里我回过头来一看,好家伙关于PackageParser中的成员相关的介绍就捣鼓了蛮多了,如果再将其中的方法也放在一起来分析,估计读者和我自己都要奔溃了!所以这里我先简单介绍下PackageParser中重要的方法(方法详细的实现在后续博客介绍),如下:
- parsePackage()
- parseClusterPackage()
- parseMonolithicPackage()
- parsePackageLite()
- parseClusterPackageLite()
- parseMonolithicPackageLite()
- parseApkLite()
好了,这里只简单的将PackageParser核心方法摆了出来,由于篇幅和排版限制就不在这篇博客中放出来了,在接下来中会专门来一篇博客来分析上述几个核心的方法看看包管理器是怎么烹饪各种Android包组件,得到美味可口的Android包的。
总结
好了,到这里PackageManagerService启动详解(四)之Android包信息体和解析器(上)分析就告一段落了,在本篇博客中我们重点阐述了如下知识点:
- Android包管理机制中的Android包解析器设计思想,以及它的重要成员等相关信息
但是由于篇幅,这里就将Android包解析器怎么通过各种方法烹饪它定义的各种成员,得到美味可口的Android包信息放在后续的博客中来进行分析和讲解了!
好了,各位青山不改绿水长流,各位江湖见!当然各位读者的点赞和关注是我写作路上前进的最大动力了,如果有啥不对或者不爽的也可以踩一踩也无妨!未完待续,欢迎读者继续关注。“关不掉的收音机”下一篇PKMS启动详解(六)之Android包信息体和包解析器(下)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)