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阶段流程分析
引言
通过前面不懈的努力,终于Android包信息体和包解析器要到完结篇了,此处的我们值得掌声(鲜花就算了,我们coder还是很实在的)!在前面的博客PKMS启动详解(五)之Android包信息体和包解析器(中)我们从Android包管理机制的设计者角度出发,着重分析了:
- Android包管理机制中的Android包解析器的PackageParser成员信息(主要是用于解析Android包的各种配件信息,不是配件信息不是配牛)
如果说前面我们分析的PackageParser成员变量等相关的信息,是包管理器为了烹饪Android包准备的各种顶级食材的话,那么PackageParser中定义的各种方法就是为了烹饪Android包准备的各种烹饪技术了。是不是很好奇,Android包管理器是怎么将前面的各种食材进行烹饪得到各种美味呢?通过这篇博客的源码实战分析,我想读者朋友一定会解开上述谜题成为一个烹饪大师的!
在正式开始本篇的博客相关分析前,还是非常有必要放出包解析器PackageParser的核心结构图,这样在后续分析时有一个整体上的参考(有点像我们小孩拼积木的配件图,实在撸不动的时候回来看看)。
注意:本篇的介绍是基于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
一.包解析器PackageParser解析Android包流程概述
包解析器PackageParser解析Android包的工作,难度不大(属于伤害性不大,侮辱性极强的范畴),但其流程的非常的繁琐,跟着流程走一边过来估计读者骂娘的心思都有了。但是没有办法阅读分析源码就是这么枯燥且乏味的事情,但是谁叫我们是coder呢?木有办法!
为了能将相关解析Android包的流程分析清楚,所以这个章节我们先对整个的流程简单概述一下!
前面的博客中我们已经将PackageParser解析Android包需要的各种数据结构(配件)已经备齐了,要得到以上的数据结构,包解析器PackageParser中的相关方法功不可没,其中最最核心的就是parsePackages方法,它从接收一个静态的文件(File类型)开始,会经过一个漫长的包解析过程,直到生成最终可口香甜的的Package:
parsePackages(File file...)
└── parseClusterPackage(File packageDir...)
└── parseClusterPackageLite(File packageDir...)
| └── parseApkLite(File apkFile...)
| └── parseApkLite(String codePath...)
└── parseBaseApk()
└── parseBaseApplication()
| └── parseActivity()
| └── parseService()
| └── ...
└── parseInstrumentation()
└── ...
└── parseMonolithicPackage(File apkFile...)
└── parseMonolithicPackageLite(File packageFile...)
| └── parseApkLite(File apkFile...)
| └── parseApkLite(String codePath...)
└── parseBaseApk()
└── parseBaseApplication()
| └── parseActivity()
| └── parseService()
| └── ...
└── parseInstrumentation()
└── ...
这些方法的具体逻辑在后续章节我们会详细分析,这里先把核心的关键的流程捋出来:
- PackageParser.parsePackages()是包解析器的入口方法,它首先会判定给定的输入是否为一个目录,如果是目录,则以为着目录下可能存在多个拆分后的APK,这就需要以Cluster的方式进行解析;如果仅仅是一个APK文件,就以Monolithic的方式解析;
- 解析APK,需要先得到一个中间数据结构PacakgeLite,包名、版本、拆分包等信息都会保存在这个数据结构中;由于一个包可能有多个拆分的APK,所以PackageLite可能关联到多个APK,每一个APK都对应到ApkLite这个数据结构,也是一些基本信息的封装。之所以以Lite为后缀命名,是因为这两个数据结构都比较轻量,只保存APK中很少信息;
- 一个APK真正的信息都写在AndroidManifest.xml这个文件中,PackageParser.parseBaseApk()这个方法就是用来解析该文件。其解析过程与AndroidManifest.xml的文件结构一一对应,譬如先解析<application>标签的内容,然后解析其下的<activity>,<service>等标签。由于AndroidManifest.xml文件的结构非常复杂,所以该方法逻辑也非常庞大,读者们可以自行分析源码。
至此,包解析器PackageParser就将一个静态的文件,转换成了内存中的数据结构Package,它包含了一个包的所有信息,如包名、包路径、权限、四大组件等,其数据来源主要就是AndroidManifest.xml文件。
二.PackageParser的核心方法
前面我们介绍了包解析器的PackageParser设计思想,它的各种成员信息,而前面我们所做的一切的最终目的都是为了它的最最核心功能服务的就是快速的理解PackageParser是如何解析Android安装包的。而关于理解PackageParser工具类解析Android包我们可以从它的parsePackage方法开始,也从它结束!
为啥说理解PackageParser对于Android包的解析可以从parsePackage方法开始,也从它结束。因为这个方法类似于一个总的入口,通过这个入口我们能理解到它其中的各种方法和小的细节的实现!如果说它是露出冰山的一角,那么我们可以顺着这个冰山看到冰山的全貌!
并且在后续的章节中,为了体现PackageParser一系列的核心方法,我们的排版不会采用层次标题的方式,而是采用并行标题的方式。这个提前和读者朋友们提前亮出来!
2.1 PackageParser.parsePackage(…)方法
前面唠叨了这么久,是时候开始我们的表演了!我们来上源码!
// 【 PackageParser.java 】
/*
解析指定位置的安装包。它自动会检测安装包的模式的是单一APK或者集群APK模式。
这样就可以对"集群APK"的安装包进行理性的检查,比如会检查"base APK"和"拆分APK"是否具有相同的包名和版本号。
请注意,这里面是不执行签名验证的,所以必须要单独执行collectCertificates(Package,int)这个方法
*/
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
if (packageFile.isDirectory()) {//如果解析的是目录,形如/system/app/xxx/,这个是Android 5.x之后加入的
return parseClusterPackage(packageFile, flags);// 详见章节 【 2.2 】
} else {//解析的是/system/app/xxx.apk,这种在5.0之前常见
return parseMonolithicPackage(packageFile, flags); // 详见章节 【 2.12 】,跨度咋这么大呢
}
}
我们先来看下parsePackage的入参,它的参数有两个:
- 第一个为待解析的安装包的路径,
- 另外一个是解析附加的flag值,不同的目录取值是不同的
接着根据掺入解析的安装路径,采取两种不同的解析方式()虽然是不同的两种解析方式,但是最后都是殊途同归:
- 如果packageFil 是一个目录的话,比如 /system/app/xxx/,那就采用第一种解析方式!
- 如果packageFile是一个非目录文件的话,比如 /system/app/xxx.apk,那就采用第二种解析方式!
Android 5.0 以前,APK都是直接位于安装/xxx/app目录下的,比如:xxx/app/MyService.apk。
但是从5.x以后,谷歌引入了apk拆分机制,就是支持将一个apk拆分成很多个具有相同签名的apk,为了支持 apk拆分,谷歌增加了一级目录:/system/app/xxx/,这个目录里会存放一到多个apk,比如 base.apk,split.apk等;
所以PKMS在解析package时,会把多个apk的数据封装成一个 Package,加载到内存中!最终我们就得到了下面的解析分支图:
这里我们先以/system/app/xxx/xxx.apk并且没有差分包的的情况来对分支parseClusterPackage方法进行分析!可能会由读者说了为啥不先从下一种情况入手呢,我想说的是先干硬的的再来软的,复杂的都被我们解决了那么后续分支parseMonolithicPackage方法的情况我们也就手到擒来了。
2.2 PackageParser.parseClusterPackage(…)方法
没有啥好说的,直接接着往下撸!
// 【 PackageParser.java 】
/*
解析给定目录中包含的所有APKs,将它们视为单包。这还会执行完整性检查,
比如require相同的包名和版本代码,一个基本APK,并且是唯一的分裂的名字。
并且这个方法不执行签名验证,如果要执行必须调用collectCertificates方法
上述的翻译挺奇怪的,一句总结就是解析拆分目录下的所有apk信息,并且将信息最终封装成一个Package数据类结构
*/
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
// ***************** 第一步 *****************
// 解析目录,并获取对应的PackageLite
final PackageLite lite = parseClusterPackageLite(packageDir, 0);// 详见章节 【 2.3 】
// 正常流程中中,mOnlyPowerOffAlarmApps和mOnlyCoreApps的取值都为false
if (mOnlyPowerOffAlarmApps) {
...
}
if (!mOnlyPowerOffAlarmApps && mOnlyCoreApps && !lite.coreApp) {
...
}
// 构建AssetManager对象实例
final AssetManager assets = new AssetManager();
try {
// ***************** 第二步 *****************
// 装载应用程序的资源到AssetManager!
loadApkIntoAssetManager(assets, lite.baseCodePath, flags);
if (!ArrayUtils.isEmpty(lite.splitCodePaths)) {// 如果apk被拆分了,加载子apk的资源!
for (String path : lite.splitCodePaths) {
loadApkIntoAssetManager(assets, path, flags);
}
}
final File baseApk = new File(lite.baseCodePath);
// ***************** 第三步 *****************
// 对核心base apk解析,返回封装核心apk信息的Package对象pkg!
final Package pkg = parseBaseApk(baseApk, assets, flags);// 详见章节 【 2.8 】
if (pkg == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse base APK: " + baseApk);
}
// ***************** 第四步 *****************
// 如果apk被拆分了,那就对非核心apk也进行处理
if (!ArrayUtils.isEmpty(lite.splitNames)) {
final int num = lite.splitNames.length;
pkg.splitNames = lite.splitNames;
pkg.splitCodePaths = lite.splitCodePaths;
pkg.splitRevisionCodes = lite.splitRevisionCodes;
pkg.splitFlags = new int[num];
pkg.splitPrivateFlags = new int[num];
for (int i = 0; i < num; i++) {
// 对拆分后的非核心apk也进行解析,这个不在我们当前讨论的范围之内,所以先行忽略
parseSplitApk(pkg, i, assets, flags);
}
}
pkg.setCodePath(packageDir.getAbsolutePath());// 设置package的codePath
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
通过源码逻辑分析,我们知道parseClusterPackage方法的处理逻辑主要分为如下的几部分:
-
首先调用方法parseClusterPackageLite先得到一个中间数据结构PacakgeLite,最终Android的包名、版本、拆分包等信息都会保存在这个数据结构中(并且由于一个包可能有多个拆分的APK,所以PackageLite可能关联到多个APK,每一个APK都对应到ApkLite这个数据结构,也是一些基本信息的封装)
如果对于上面的流程还有不清晰的读者的话,我们可以理解parseClusterPackageLite就是依次对下面数据结构类填充的过程:
1.先ApkLite
2.然后根据前面得到的ApkLite信息填充PackageLite
此时的读者也许在想前面来个获取PacakgeLite,后面又来个获取Package这有必要吗!答案是肯定的,我初步分析有如下两个:
1.首先是对如果存在差分包的,拆分情况合法性的确定
2.另外一个就是获得安装包下的base apk核心apk信息,注意不是说base.apk而是存在拆分情况下的核心apk,或者是不存在拆分但是是类似/system/app/xxx/下apk的情况的apk信息
这个就好像有点我们实际生活中的处对象,如果前期验证都不能通过,那么后面的深入交流和进一步发展就无从谈起了! -
接着创建AssetManager对象,并调用loadApkIntoAssetManager方法载入"base APK"
此处逻辑,不在我们分析讨论的范围之内,读者自行分析
-
然后接着调用PackageParser.parseBaseApk进行下一步的Android包处理逻辑,这个方法主要用来详细解析AndroidManifest.xml文件。其解析过程与AndroidManifest.xml的文件结构一一对应,譬如先解析<application>标签的内容,然后解析其下的<activity>,<service>等标签。最终将上述得到的解析信息封装成Package、此时I的Package中就基本上涵盖了AndroidManifest.xml中涉及的所有信息。
-
如果存在拆分的情况,则调用parseSplitApk遍历所有"拆分APK",此时会载入第二步创建的AssetManager对象作为参数,从而实现对拆分APK资源的解析
根据我们前面的预制条件是不会进入此分支的,所以关于存在拆分包情况的解析,这里就不放出来了,其逻辑并不是很复杂。读者将主线分支理解清楚之后,可以自行存在差分包情况的分析。
2.3 PackageParser.parseClusterPackageLite(…)方法
按照排版的逻辑,此处应该属于前面一个章节的小标题,但是为了展现PackageParser解析器中各种方法,所以采用了平行章节的方式。
好了,我们接着继续分析源码!
// 【 PackageParser.java 】
private static PackageLite parseClusterPackageLite(File packageDir, int flags)
throws PackageParserException {
// 获取目录下的所有文件
final File[] files = packageDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"No packages found in split");
}
String packageName = null;
int versionCode = 0;
// ***************** 第一步 *****************
// 开始验证包名和版本号是否一致
final ArrayMap<String, ApkLite> apks = new ArrayMap<>();
// 遍历所有文件
for (File file : files) {
if (isApkFile(file)) {
// 解析单个APK文件成ApkLite对象
final ApkLite lite = parseApkLite(file, flags);// 详见章节 【 2.4 】
// 遍历的时候只有第一个文件的时候packageName为null
if (packageName == null) {
packageName = lite.packageName;
versionCode = lite.versionCode;
} else {
// 对比当前的lite对象和上一个lite对象的包名是否一致
if (!packageName.equals(lite.packageName)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Inconsistent package " + lite.packageName + " in " + file
+ "; expected " + packageName);
}
// 对比当前的lite对象和上一个lite对象的版本号是否一致
if (versionCode != lite.versionCode) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Inconsistent version " + lite.versionCode + " in " + file
+ "; expected " + versionCode);
}
}
/*
保证不重复添加
将解析得到的ApkLite对象添加到一个ArrayMap集合中
此处对于核心apk,splitName为null
*/
if (apks.put(lite.splitName, lite) != null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Split name " + lite.splitName
+ " defined more than once; most recent was " + file);
}
}
}
// ***************** 第二步 *****************
/*
从列表中移除核心apk,保存到baseApk中,之所以remove null,是因为base apk的splitName为null!
如果没有base apk,那就报错!!
*/
final ApkLite baseApk = apks.remove(null);
if (baseApk == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Missing base APK in " + packageDir);
}
// ***************** 第三步 *****************
// 处理非核心的apk
final int size = apks.size();
String[] splitNames = null;
String[] splitCodePaths = null;
int[] splitRevisionCodes = null;
if (size > 0) {
//获得非核心apk的splitName,codePath和splitRevisionCodes!
splitNames = new String[size];
splitCodePaths = new String[size];
splitRevisionCodes = new int[size];
splitNames = apks.keySet().toArray(splitNames);
// splitNames排序
Arrays.sort(splitNames, sSplitNameComparator);
// 初始化splitCodePaths和splitRevisionCodes
for (int i = 0; i < size; i++) {
splitCodePaths[i] = apks.get(splitNames[i]).codePath;
splitRevisionCodes[i] = apks.get(splitNames[i]).revisionCode;
}
}
// ***************** 第四步 *****************
final String codePath = packageDir.getAbsolutePath();
return new PackageLite(codePath, baseApk, splitNames, splitCodePaths,
splitRevisionCodes);
}
通过源码逻辑分析,我们可以得出parseClusterPackageLite方法的处理逻辑主要分为如下的几部分:
-
遍历安装包目录, 然后调用parseApkLite方法获取安装包目录下各个APK文件的轻量级的数据结构ApkLite,并且解析得到数据加入到ArrayMap列表中。并且如果存在差分包的情况则进行相关的校验,主要是校验包名和版本号是否一致
关于安装包存在拆分的情况,存在许多的限制,并且需要做一些常规的检验等。并且此种情况也不在我们重点主干的分析情况下,所以对于存在拆分的情况只是一笔带过!
-
接着从前面的ArrayMap列表中取出核心的"base APK“赋给baseApk.
-
如果存在差分包的情况则继续对splitNames、splitCodePaths、splitRevisionCodes完成初始化
-
最后填充codePath,并且将前面获取的一系列变量信息填充构造一个PackageLite实例对象返回回去
总体来说,上述源码的逻辑并不是十分复杂,只是比较繁琐而已,而已!
2.4 PackageParser.parseApkLite(File …)方法
此处是针对单个APK文件进行轻量级的解析!
撸起柚子,我们接着继续分析源码!
// 【 PackageParser.java 】
/**
* Utility method that retrieves lightweight details about a single APK
* file, including package name, split name, and install location.
*
* @param apkFile path to a single APK
* @param flags optional parse flags, such as
* {@link #PARSE_COLLECT_CERTIFICATES}
*/
/*
这是一个轻量级的用于检索单个APK的轻量级细节的实用程序方法文件,包括包名称、
分割名称和安装位置的方法
并且要注意此时的第一个入参类型为File,因为parseApkLite有被重载了,所以不要搞混淆了
*/
public static ApkLite parseApkLite(File apkFile, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
AssetManager assets = null;
XmlResourceParser parser = null;
try {
// 创建资源管理器
assets = new AssetManager();
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
// 资源管理器加载待解析apk的资源,主要用于解析AndroidManifest.xml文件
int cookie = assets.addAssetPath(apkPath);
if (cookie == 0) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse " + apkPath);
}
final DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
final Resources res = new Resources(assets, metrics, null);
// 创建解析器,用来解析apk中的AndroidMenifest.xml配置文件
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final Signature[] signatures;
final Certificate[][] certificates;
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {// 如果需要验证签名信息
// TODO: factor signature related items out of Package object
final Package tempPkg = new Package(null);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
// 进行签名验证,并将签名保存在signatures中
collectCertificates(tempPkg, apkFile, 0 /*parseFlags*/);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
signatures = tempPkg.mSignatures;
certificates = tempPkg.mCertificates;
} else {
signatures = null;
certificates = null;
}
// 将<manifest>标签的属性赋给attrs!
final AttributeSet attrs = parser;
// 接着调用重载的parseApkLite继续解析流程
return parseApkLite(apkPath, res, parser, attrs, flags, signatures, certificates);
} catch (XmlPullParserException | IOException | RuntimeException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to parse " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
IoUtils.closeQuietly(assets);
}
}
上述源码逻辑并不复杂,只是比较繁琐,核心的诉求点只有一个就是解析单个APK文件获取轻量级的ApkLite对象实例。我们通过分析可知上述的核心处理步骤为:
- 首先创建AssetManager对象assets
- 接着assets调用addAssetPath加载待解析apk的资源
- 然后用assets作为入参来创建Resources对象res
- 接着调用assets的openXmlResourceParser()来获取XmlResourceParser对象parser对象
- 然后如果传入的flag携带的标志,标明需要获取签名信息,则调用collectCertificates方法获取Android包签名信息
- 最后调用重载的parseApkLite(String,Resource,XmlPullParser,AttributeSet,int,Signature[])方法来返回一个ApkLite对象
在该方法里面有初始化了一个AssertMananger对象,一个DisplayMetrics对象,一个Resources对象,但是细心的读者也许发现了这些对象在当前的方法中都没有被使用到,这些对象都是后续解析中需要用的,因此将这些参数传递给它的重载parseApkLite方法,在它的重载方法中解析完成后关闭资源管理器与解析器。
我们接着继续分析parseApkLite另外一个重载方法,看看它是怎么继续进一步处理的。
2.5 PackageParser.parseApkLite(String …)方法
注意此处,不是博主跑错了片场又重新将前面的标题复制了一篇,这里的parseApkLite是另外一个重载的方法,是一个重载的方法。
在PKMS的分析中,我们可以发现存在非常非常多的重载方法!所以如果下次面试或者其它的环境下有人询问你关于重载的使用,你可以将PKMS中各种重载直接甩给他!
提起精神,我们接着继续分析源码!
// 【 PackageParser.java 】
private static ApkLite parseApkLite(String codePath, Resources res, XmlPullParser parser,
AttributeSet attrs, int flags, Signature[] signatures, Certificate[][] certificates)
throws IOException, XmlPullParserException, PackageParserException {
// ***************** 第一步 *****************
// 解析得到是否有拆分相关信息
final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);// 详见章节 【 2.6 】
int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
int versionCode = 0;
int revisionCode = 0;
boolean coreApp = false;
boolean multiArch = false;
boolean use32bitAbi = false;
boolean extractNativeLibs = true;
// ***************** 第二步 *****************
// 解析<manifest>标签的属性,并填充相关变量
for (int i = 0; i < attrs.getAttributeCount(); i++) {
final String attr = attrs.getAttributeName(i);
// 解析android:installLocation属性,表示安装位置,取值为auto|internalOnly|preferExternal
if (attr.equals("installLocation")) {
installLocation = attrs.getAttributeIntValue(i,
PARSE_DEFAULT_INSTALL_LOCATION);
} else if (attr.equals("versionCode")) {
versionCode = attrs.getAttributeIntValue(i, 0);
} else if (attr.equals("revisionCode")) {
revisionCode = attrs.getAttributeIntValue(i, 0);
} else if (attr.equals("coreApp")) {// 是否是核心app
coreApp = attrs.getAttributeBooleanValue(i, false);
}
}
// ***************** 第三步 *****************
// 接着,解析manifest标签的直接子标签
int type;
final int searchDepth = parser.getDepth() + 1;
final List<VerifierInfo> verifiers = new ArrayList<VerifierInfo>();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
// 解析<package-verifier>标签的属性
if (parser.getDepth() == searchDepth && "package-verifier".equals(parser.getName())) {
final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags);
if (verifier != null) {
verifiers.add(verifier);
}
}
// 解析<application>标签的属性!
if (parser.getDepth() == searchDepth && "application".equals(parser.getName())) {
for (int i = 0; i < attrs.getAttributeCount(); ++i) {
final String attr = attrs.getAttributeName(i);
if ("multiArch".equals(attr)) {
multiArch = attrs.getAttributeBooleanValue(i, false);
}
if ("use32bitAbi".equals(attr)) {
use32bitAbi = attrs.getAttributeBooleanValue(i, false);
}
if ("extractNativeLibs".equals(attr)) {
extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
}
}
}
}
// ***************** 第四步 *****************
// 根据解析得到的数据,构建apk对应的ApkLite对象
return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
revisionCode, installLocation, verifiers, signatures, certificates, coreApp,
multiArch, use32bitAbi, extractNativeLibs);
}
上述源码逻辑依然并不复杂,核心的诉求点只有一个就是继续解析单个APK文件获取轻量级的ApkLite对象实例。我们通过分析可知上述的核心处理步骤为:
- 首先调用parsePackageSplitNames方法解析解析apk的 <manifest>标签的package属性和split属性,然后以返回packageName和splitName的Pair对象
- 接着再次遍历<manifest>标签属性,并获取相应的值,这里面主要是循环解析出installLocation,versionCode,revisionCode,coreApp
- 接着再次解析<manifest>标签的直接子标签<package-verifier>和<application>,并得到相关的标签属性值保存起来
- 最后利用前面解析出来的数据去构造一个ApkLite实例对象返回回去,关于ApkLite对象的介绍可以参见前面博客
此处收功,我们接着继续下面的流程!
2.6 PackageParser.parsePackageSplitNames()方法
路漫漫其修远兮,吾将上下而求索!战斗依然还在继续,我们继续跟进!
// 【 PackageParser.java 】
/*
该方法用于解析apk的AndroidManifest.xml的<manifest>标签的package属性
*/
private static Pair<String, String> parsePackageSplitNames(XmlPullParser parser,
AttributeSet attrs) throws IOException, XmlPullParserException,
PackageParserException {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
}
if (type != XmlPullParser.START_TAG) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"No start tag found");
}
if (!parser.getName().equals(TAG_MANIFEST)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"No <manifest> tag");
}
// 解析manifest标签的package属性
final String packageName = attrs.getAttributeValue(null, "package");
if (!"android".equals(packageName)) {// 如果package不是android,则需要校验格式
final String error = validateName(packageName, true, true);
if (error != null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
"Invalid manifest package: " + error);
}
}
// 解析manifest标签的split属性
String splitName = attrs.getAttributeValue(null, "split");
if (splitName != null) {
if (splitName.length() == 0) {
splitName = null;
} else {
final String error = validateName(splitName, false, false);
if (error != null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
"Invalid manifest split: " + error);
}
}
}
// 返回packageName和splitName的Pair对象!
return Pair.create(packageName.intern(),
(splitName != null) ? splitName.intern() : splitName);
}
此方法的核心逻辑就是无外乎两点:
- 先解析apk的 <manifest>标签的package属性和split属性,在获得package包名之后会调用validateName方法来判断获取的apk包名的有效性,这里简单说下,这个方法主要就是检测是否是数字、字母、下划线和点分隔符,这也是取包名的规则,比如是字母数字下划线加点分隔符,否则都不是合法的应用包名。并且合法的包名至少包含一个点分隔符。
对于包名的合法性判断规则,过滤了一个特别的包名"android",读者知道这个特殊的包名是那个应用吗,是的它就是/system/framework/framework-res.apk!
- 然后将获取到的package和split属性值,以Pair对的形式返回
2.7 PackageParser.parseBaseApk(File …)方法
分析支持,读者估计心里有一个声音了我是谁,我在那里的恍惚感觉了。还是让我们来捋一捋,还记的在章节2.2中parseClusterPackage中我们分了两步走:
1.我们是先通过parseClusterPackageLite方法获取了Android包轻量级的包信息体(我们前面的一大部分工作量都耗在这个里面了)
2.然后接着调用parseBaseApk来真刀真枪的获取全的Android包信息(这就是我们来到此处的原因)
并且我们需要注意,此时解析的是"base apk"文件,不是拆包的apk或者其它
既然解决了我是谁,我来自那里的问题了!我们责无旁贷的,必须向前冲,继续干就好了!
// 【 PackageParser.java 】
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
String volumeUuid = null;
if (apkPath.startsWith(MNT_EXPAND)) {// 解析 /mnt/expand/
final int end = apkPath.indexOf('/', MNT_EXPAND.length());
volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
}
mParseError = PackageManager.INSTALL_SUCCEEDED;
mArchiveSourcePath = apkFile.getAbsolutePath();
if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
// 用于解析AndroidManifest.xml文件
final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
Resources res = null;
XmlResourceParser parser = null;
try {
res = new Resources(assets, mMetrics, null);
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final String[] outError = new String[1];
// 调用重载的parseBaseApk进行下一步的解析
final Package pkg = parseBaseApk(res, parser, flags, outError);// 详见章节 【 2.8 】
if (pkg == null) {
throw new PackageParserException(mParseError,
apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
}
pkg.setVolumeUuid(volumeUuid);
pkg.setApplicationVolumeUuid(volumeUuid);
pkg.setBaseCodePath(apkPath);
pkg.setSignatures(null);
return pkg;
} catch (PackageParserException e) {
throw e;
} catch (Exception e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to read manifest from " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
}
}
此处方法逻辑不复杂,核心步骤如下:
-
判断当前安装包路劲是不是/mnt/expand/,如果是对volumeUuid值做特殊处理
不要问我volumeUuid是啥,我也木有搞懂是啥,直接忽略。
-
接着构建以安装包路径为参数的Resources和XmlResourceParser等实例对象
-
以上述构建的实例对象为参数,接着调用另外一个重载的parseBaseApk进行下一步的处理
接着继续往下撸!
2.8 PackageParser.parseBaseApk(Resources …)方法
一直撸,一直爽!让我们继续撸!
/**
此类用于详细解析base apk的清单文件
*/
private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
final String splitName;
final String pkgName;
try {
// ***************** 第一步 *****************
/*
解析<manifest>标签,获得packageName和splitName的值
并且结果以Pair<packageName, splitName>的形式返回
*/
Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);// 章节 【 2.5 】已经详细分析了
pkgName = packageSplit.first;
splitName = packageSplit.second;
// 异常处理
if (!TextUtils.isEmpty(splitName)) {
outError[0] = "Expected base APK, but found split " + splitName;
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
return null;
}
} catch (PackageParserException e) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
return null;
}
// 构建一个Package实例对象,用于保存最终的解析信息
final Package pkg = new Package(pkgName);
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
// ***************** 第二步 *****************
// 通过解析标签<manifest>标签初始化pkg的属性mVersionCode、baseRevisionCode和mVersionName
pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
pkg.baseRevisionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
pkg.mVersionName = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_versionName, 0);
if (pkg.mVersionName != null) {
pkg.mVersionName = pkg.mVersionName.intern();
}
// 是不是核心App
pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
sa.recycle();
// ***************** 第三步 *****************
// 接着调用parseBaseApkCommon方法,继续解析
return parseBaseApkCommon(pkg, null, res, parser, flags, outError);// 详见章节 【 2.9 】
}
上述逻辑依然不是很复杂,其核心逻辑分为如下几个部分:
-
首先调用parsePackageSplitNames方法解析“base apk”的<manifest>标签,获得packageName和splitName的值并且结果以Pair<packageName, splitName>的形式返回,得到上述的返回值以后再判断合法性
-
接着通过解析标签<manifest>属性初始化pkg的属性mVersionCode、baseRevisionCode和mVersionName
-
接着继续调用parseBaseApkCommon开始更进一步的详细解析,从而完全填充Package实例对象的信息
parseBaseApkCommon的解析逻辑非常繁琐且冗长,主要是完全解析"base apk" 的配置清单文件,然后将解析得到的数据填充Package实例!
读者可以先行想想一下工作量,就是你自己完全解析复杂的Androidmanifest.xml文件!
2.9 PackageParser.parseBaseApkCommon()方法
前方高能预警,此处的parseBaseApkCommon源码逻辑一大堆,难度不大但是及其繁琐!一口气给整完,估计是有点难度,实在不行就整两口!
// 【 PackageParser.java 】
private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
IOException {
mParseInstrumentationArgs = null;
mParseActivityArgs = null;
mParseServiceArgs = null;
mParseProviderArgs = null;
int type;
boolean foundApp = false;
// 继续获取<manifest>标签属性
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
// 获取sharedUserId属性值
String str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
if (str != null && str.length() > 0) {
// 异常处理
String nameError = validateName(str, true, false);
if (nameError != null && !"android".equals(pkg.packageName)) {
outError[0] = "<manifest> specifies bad sharedUserId name \""
+ str + "\": " + nameError;
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
return null;
}
pkg.mSharedUserId = str.intern();
pkg.mSharedUserLabel = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
}
// 获取installLocation属性值
pkg.installLocation = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_installLocation,
PARSE_DEFAULT_INSTALL_LOCATION);
pkg.applicationInfo.installLocation = pkg.installLocation;
// 根据传入的flag设置对应ApplicationInfo的flag值
/* Set the global "forward lock" flag */
if ((flags & PARSE_FORWARD_LOCK) != 0) {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
}
/* Set the global "on SD card" flag */
if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
}
if ((flags & PARSE_IS_EPHEMERAL) != 0) {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_EPHEMERAL;
}
// Resource boolean are -1, so 1 means we don't know the value.
int supportsSmallScreens = 1;
int supportsNormalScreens = 1;
int supportsLargeScreens = 1;
int supportsXLargeScreens = 1;
int resizeable = 1;
int anyDensity = 1;
int outerDepth = parser.getDepth();
// 开始解析Androidmanifest.xml的<manifest>的子标签
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (acceptedTags != null && !acceptedTags.contains(tagName)) {
Slog.w(TAG, "Skipping unsupported element under <manifest>: "
+ tagName + " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
//开始解析<application>标签,这个是重头戏
if (tagName.equals(TAG_APPLICATION)) {
if (foundApp) {
if (RIGID_PARSER) {
outError[0] = "<manifest> has more than one <application>";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
} else {
Slog.w(TAG, "<manifest> has more than one <application>");
XmlUtils.skipCurrentTag(parser);
continue;
}
}
foundApp = true;
/*
调用parseBaseApplication方法进行下一步的解析,
并将解析的数据填充Package实例对象
*/
if (!parseBaseApplication(pkg, res, parser, flags, outError)) {// 详见章节 【 2.10 】
return null;
}
} else if (tagName.equals(TAG_OVERLAY)) {//解析<overlay>标签
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestResourceOverlay);
pkg.mOverlayTarget = sa.getString(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
pkg.mOverlayPriority = sa.getInt(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,
-1);
sa.recycle();
if (pkg.mOverlayTarget == null) {
outError[0] = "<overlay> does not specify a target package";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) {
outError[0] = "<overlay> priority must be between 0 and 9999";
mParseError =
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_KEY_SETS)) {// 解析 <key-sets>标签
if (!parseKeySets(pkg, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_PERMISSION_GROUP)) {// 解析<permission-group>标签
if (parsePermissionGroup(pkg, flags, res, parser, outError) == null) {
return null;
}
} else if (tagName.equals(TAG_PERMISSION)) {// 解析<permission>标签
if (parsePermission(pkg, res, parser, outError) == null) {
return null;
}
} else if (tagName.equals(TAG_PERMISSION_TREE)) {// 解析<permission-tree>
if (parsePermissionTree(pkg, res, parser, outError) == null) {
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION)) {// 解析<uses-permission>
if (!parseUsesPermission(pkg, res, parser)) {
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
|| tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
// 解析<uses-permission-sdk-m>或<uses-permission-sdk-23>标签
if (!parseUsesPermission(pkg, res, parser)) {
return null;
}
} else if (tagName.equals(TAG_USES_CONFIGURATION)) {// 解析<uses-configuration>标签
ConfigurationInfo cPref = new ConfigurationInfo();
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUsesConfiguration);
cPref.reqTouchScreen = sa.getInt(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
Configuration.TOUCHSCREEN_UNDEFINED);
cPref.reqKeyboardType = sa.getInt(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
Configuration.KEYBOARD_UNDEFINED);
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
false)) {
cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
}
cPref.reqNavigation = sa.getInt(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
Configuration.NAVIGATION_UNDEFINED);
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
false)) {
cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
}
sa.recycle();
pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_USES_FEATURE)) { // 解析<uses-feature>标签
FeatureInfo fi = parseUsesFeature(res, parser);
pkg.reqFeatures = ArrayUtils.add(pkg.reqFeatures, fi);
if (fi.name == null) {
ConfigurationInfo cPref = new ConfigurationInfo();
cPref.reqGlEsVersion = fi.reqGlEsVersion;
pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_FEATURE_GROUP)) {// 解析<feature-group>标签
FeatureGroupInfo group = new FeatureGroupInfo();
ArrayList<FeatureInfo> features = null;
final int innerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
final String innerTagName = parser.getName();
if (innerTagName.equals("uses-feature")) {
FeatureInfo featureInfo = parseUsesFeature(res, parser);
// FeatureGroups are stricter and mandate that
// any <uses-feature> declared are mandatory.
featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
features = ArrayUtils.add(features, featureInfo);
} else {
Slog.w(TAG, "Unknown element under <feature-group>: " + innerTagName +
" at " + mArchiveSourcePath + " " +
parser.getPositionDescription());
}
XmlUtils.skipCurrentTag(parser);
}
if (features != null) {
group.features = new FeatureInfo[features.size()];
group.features = features.toArray(group.features);
}
pkg.featureGroups = ArrayUtils.add(pkg.featureGroups, group);
} else if (tagName.equals(TAG_USES_SDK)) {// 解析<uses-sdk>标签
if (SDK_VERSION > 0) {
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUsesSdk);
int minVers = 1;
String minCode = null;
int targetVers = 0;
String targetCode = null;
TypedValue val = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
if (val != null) {
if (val.type == TypedValue.TYPE_STRING && val.string != null) {
targetCode = minCode = val.string.toString();
} else {
// If it's not a string, it's an integer.
targetVers = minVers = val.data;
}
}
val = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
if (val != null) {
if (val.type == TypedValue.TYPE_STRING && val.string != null) {
targetCode = val.string.toString();
if (minCode == null) {
minCode = targetCode;
}
} else {
targetVers = val.data;
}
}
sa.recycle();
if (minCode != null) {
boolean allowedCodename = false;
for (String codename : SDK_CODENAMES) {
if (minCode.equals(codename)) {
allowedCodename = true;
break;
}
}
if (!allowedCodename) {
if (SDK_CODENAMES.length > 0) {
outError[0] = "Requires development platform " + minCode
+ " (current platform is any of "
+ Arrays.toString(SDK_CODENAMES) + ")";
} else {
outError[0] = "Requires development platform " + minCode
+ " but this is a release platform.";
}
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
}
pkg.applicationInfo.minSdkVersion =
android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
} else if (minVers > SDK_VERSION) {
outError[0] = "Requires newer sdk version #" + minVers
+ " (current version is #" + SDK_VERSION + ")";
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
} else {
pkg.applicationInfo.minSdkVersion = minVers;
}
if (targetCode != null) {
boolean allowedCodename = false;
for (String codename : SDK_CODENAMES) {
if (targetCode.equals(codename)) {
allowedCodename = true;
break;
}
}
if (!allowedCodename) {
if (SDK_CODENAMES.length > 0) {
outError[0] = "Requires development platform " + targetCode
+ " (current platform is any of "
+ Arrays.toString(SDK_CODENAMES) + ")";
} else {
outError[0] = "Requires development platform " + targetCode
+ " but this is a release platform.";
}
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
}
pkg.applicationInfo.targetSdkVersion
= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
} else {
pkg.applicationInfo.targetSdkVersion = targetVers;
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_SUPPORT_SCREENS)) { // 解析<supports-screens>标签
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestSupportsScreens);
pkg.applicationInfo.requiresSmallestWidthDp = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp,
0);
pkg.applicationInfo.compatibleWidthLimitDp = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp,
0);
pkg.applicationInfo.largestWidthLimitDp = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp,
0);
// 和屏幕相关的参数解析,屏幕大小等等!
supportsSmallScreens = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens,
supportsSmallScreens);
supportsNormalScreens = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens,
supportsNormalScreens);
supportsLargeScreens = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens,
supportsLargeScreens);
supportsXLargeScreens = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_xlargeScreens,
supportsXLargeScreens);
resizeable = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_resizeable,
resizeable);
anyDensity = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_anyDensity,
anyDensity);
sa.recycle();
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_PROTECTED_BROADCAST)) {// 解析<protected-broadcast>标签
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);
String name = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);
sa.recycle();
if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
if (pkg.protectedBroadcasts == null) {
pkg.protectedBroadcasts = new ArrayList<String>();
}
if (!pkg.protectedBroadcasts.contains(name)) {
pkg.protectedBroadcasts.add(name.intern());
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_INSTRUMENTATION)) {// 解析<instrumentation>标签
if (parseInstrumentation(pkg, res, parser, outError) == null) {
return null;
}
} else if (tagName.equals(TAG_ORIGINAL_PACKAGE)) {// 解析<original-package>标签
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestOriginalPackage);
String orig =sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
if (!pkg.packageName.equals(orig)) {
if (pkg.mOriginalPackages == null) {
pkg.mOriginalPackages = new ArrayList<String>();
pkg.mRealPackage = pkg.packageName;
}
pkg.mOriginalPackages.add(orig);
}
sa.recycle();
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_ADOPT_PERMISSIONS)) { // 解析<adopt-permissions>标签
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestOriginalPackage);
String name = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
sa.recycle();
if (name != null) {
if (pkg.mAdoptPermissions == null) {
pkg.mAdoptPermissions = new ArrayList<String>();
}
pkg.mAdoptPermissions.add(name);
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_USES_GL_TEXTURE)) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
continue;
} else if (tagName.equals(TAG_COMPATIBLE_SCREENS)) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
continue;
} else if (tagName.equals(TAG_SUPPORTS_INPUT)) {//
XmlUtils.skipCurrentTag(parser);
continue;
} else if (tagName.equals(TAG_EAT_COMMENT)) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
continue;
} else if (tagName.equals(TAG_PACKAGE)) {// 解析<package>标签,该标签表示应用的子包!
if (!MULTI_PACKAGE_APK_ENABLED) {
XmlUtils.skipCurrentTag(parser);
continue;
}
// 所有的子包都会解析保存到ArrayList<Package>childPackages集合中!
if (!parseBaseApkChild(pkg, res, parser, flags, outError)) {
// If parsing a child failed the error is already set
return null;
}
} else if (tagName.equals(TAG_RESTRICT_UPDATE)) { // 解析<restrict-update>标签
if ((flags & PARSE_IS_SYSTEM_DIR) != 0) {
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestRestrictUpdate);
final String hash = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestRestrictUpdate_hash, 0);
sa.recycle();
pkg.restrictUpdateHash = null;
if (hash != null) {
final int hashLength = hash.length();
final byte[] hashBytes = new byte[hashLength / 2];
for (int i = 0; i < hashLength; i += 2){
hashBytes[i/2] = (byte) ((Character.digit(hash.charAt(i), 16) << 4)
+ Character.digit(hash.charAt(i + 1), 16));
}
pkg.restrictUpdateHash = hashBytes;
}
}
XmlUtils.skipCurrentTag(parser);
} else if (RIGID_PARSER) {
outError[0] = "Bad element under <manifest>: "
+ parser.getName();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
} else {
Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
}
if (!foundApp && pkg.instrumentation.size() == 0) {
outError[0] = "<manifest> does not contain an <application> or <instrumentation>";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
}
final int NP = PackageParser.NEW_PERMISSIONS.length;
StringBuilder implicitPerms = null;
for (int ip=0; ip<NP; ip++) {
final PackageParser.NewPermissionInfo npi
= PackageParser.NEW_PERMISSIONS[ip];
if (pkg.applicationInfo.targetSdkVersion >= npi.sdkVersion) {
break;
}
if (!pkg.requestedPermissions.contains(npi.name)) {
if (implicitPerms == null) {
implicitPerms = new StringBuilder(128);
implicitPerms.append(pkg.packageName);
implicitPerms.append(": compat added ");
} else {
implicitPerms.append(' ');
}
implicitPerms.append(npi.name);
pkg.requestedPermissions.add(npi.name);
}
}
if (implicitPerms != null) {
Slog.i(TAG, implicitPerms.toString());
}
final int NS = PackageParser.SPLIT_PERMISSIONS.length;
for (int is=0; is<NS; is++) {
final PackageParser.SplitPermissionInfo spi
= PackageParser.SPLIT_PERMISSIONS[is];
if (pkg.applicationInfo.targetSdkVersion >= spi.targetSdk
|| !pkg.requestedPermissions.contains(spi.rootPerm)) {
continue;
}
for (int in=0; in<spi.newPerms.length; in++) {
final String perm = spi.newPerms[in];
if (!pkg.requestedPermissions.contains(perm)) {
pkg.requestedPermissions.add(perm);
}
}
}
// 解析屏幕大小、像素密度相关的属性!
if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.DONUT)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
}
if (supportsNormalScreens != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
}
if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.DONUT)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
}
if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.GINGERBREAD)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
}
if (resizeable < 0 || (resizeable > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.DONUT)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
}
if (anyDensity < 0 || (anyDensity > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.DONUT)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
}
return pkg;
}
如果说前面的章节的方法对于清单文件的解析是点到为止的话,那么parseBaseApkCommon方法就会对apk的清单文件AndroidManifest.xml做一个三百六十度全方位无死角的解析,它将会遍历下面的每一个节点,最最核心的就是对于application、overlay、key-sets、permission-group,permisstion,permission-tree,uses-permission等等节点相关的解析。然后在每个节点解析的具体方法里面将解析得到的数据继续填充Package对象实例,直至解析完成所有节点!
上述源码中关于各个子节点的详细解析,这里就不一一展开分析了!
其分析套路基本都是一样,打通了一个节点的解析其它的基本也就一通百通了。在下面的章节中我会分析下解析<application>节点的源码,因为我们最最熟悉的Android四大组件 Activity、Service等都在它之下!
2.10 PackageParser.parseBaseApplication()方法
任务是艰巨的,前途是光明的!战役至此,胜利就在前方,我们继续往前冲,冲,冲!
// 【 PackageParser.java 】
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
// 开始解析<application>标签属性
// 获取ApplicationInfo对象ai
final ApplicationInfo ai = owner.applicationInfo;
// 获取包名
final String pkgName = owner.applicationInfo.packageName;
// // 从资源里面获取AndroidManifestApplication的数组
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestApplication);
// 解析 android:name android:label android:icon android:roundIcon android:logo android:banner 属性!
if (!parsePackageItemInfo(owner, ai, outError,
"<application>", sa, false /*nameRequired*/,
com.android.internal.R.styleable.AndroidManifestApplication_name,
com.android.internal.R.styleable.AndroidManifestApplication_label,
com.android.internal.R.styleable.AndroidManifestApplication_icon,
com.android.internal.R.styleable.AndroidManifestApplication_roundIcon,
com.android.internal.R.styleable.AndroidManifestApplication_logo,
com.android.internal.R.styleable.AndroidManifestApplication_banner)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
if (ai.name != null) { // 如果有设置过Application的名称,则设置为ApplicationInfo的类名
ai.className = ai.name;
}
// 在AndroidManifest里面是否设置了android:manageSpaceActivity属性,
// 如果设置了则manageSpaceActivity不为空,没有设置manageSpaceActivity为空
String manageSpaceActivity = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_manageSpaceActivity,
Configuration.NATIVE_CONFIG_VERSION);
if (manageSpaceActivity != null) {// 如果设置了,则添加类名
ai.manageSpaceActivityName = buildClassName(pkgName, manageSpaceActivity,
outError);
}
// 解析android:allowBackup属性,为true表示应用允许备份
boolean allowBackup = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_allowBackup, true);
if (allowBackup) { // 如果为true,设置FLAG_ALLOW_BACKUP标志位
ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
// backupAgent, killAfterRestore, fullBackupContent, backupInForeground,
// and restoreAnyVersion are only relevant if backup is possible for the
// given application.
// 解析android:backupAgent属性!
String backupAgent = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_backupAgent,
Configuration.NATIVE_CONFIG_VERSION);
if (backupAgent != null) {
ai.backupAgentName = buildClassName(pkgName, backupAgent, outError);
if (DEBUG_BACKUP) {
Slog.v(TAG, "android:backupAgent = " + ai.backupAgentName
+ " from " + pkgName + "+" + backupAgent);
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_killAfterRestore,
true)) {
ai.flags |= ApplicationInfo.FLAG_KILL_AFTER_RESTORE;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_restoreAnyVersion,
false)) {
ai.flags |= ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_fullBackupOnly,
false)) {
ai.flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_backupInForeground,
false)) {
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
}
}
TypedValue v = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestApplication_fullBackupContent);
if (v != null && (ai.fullBackupContent = v.resourceId) == 0) {
if (DEBUG_BACKUP) {
Slog.v(TAG, "fullBackupContent specified as boolean=" +
(v.data == 0 ? "false" : "true"));
}
// "false" => -1, "true" => 0
ai.fullBackupContent = (v.data == 0 ? -1 : 0);
}
if (DEBUG_BACKUP) {
Slog.v(TAG, "fullBackupContent=" + ai.fullBackupContent + " for " + pkgName);
}
}
ai.theme = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_theme, 0);
ai.descriptionRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_description, 0);
if ((flags&PARSE_IS_SYSTEM) != 0) {
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_persistent,
false)) {
ai.flags |= ApplicationInfo.FLAG_PERSISTENT;
}
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_requiredForAllUsers,
false)) {
owner.mRequiredForAllUsers = true;
}
String restrictedAccountType = sa.getString(com.android.internal.R.styleable
.AndroidManifestApplication_restrictedAccountType);
if (restrictedAccountType != null && restrictedAccountType.length() > 0) {
owner.mRestrictedAccountType = restrictedAccountType;
}
String requiredAccountType = sa.getString(com.android.internal.R.styleable
.AndroidManifestApplication_requiredAccountType);
if (requiredAccountType != null && requiredAccountType.length() > 0) {
owner.mRequiredAccountType = requiredAccountType;
}
// 解析android:debuggable android:vmSafeMode属性!
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_debuggable,
false)) {
ai.flags |= ApplicationInfo.FLAG_DEBUGGABLE;//设置可调试
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_vmSafeMode,
false)) {
ai.flags |= ApplicationInfo.FLAG_VM_SAFE_MODE;
}
// 解析android:hardwareAccelerated属性!
owner.baseHardwareAccelerated = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_hardwareAccelerated,
owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH);
if (owner.baseHardwareAccelerated) {
ai.flags |= ApplicationInfo.FLAG_HARDWARE_ACCELERATED;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_hasCode,
true)) {
ai.flags |= ApplicationInfo.FLAG_HAS_CODE;
}
// 解析 android:allowTaskReparenting属性,这个属性很重要,决定了acivity和task的关系
// 在application标签上设置该属性对内部的所有activity生效!
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_allowTaskReparenting,
false)) {
ai.flags |= ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING;
}
// 解析android:allowClearUserData属性!
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_allowClearUserData,
true)) {
ai.flags |= ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA;
}
// The parent package controls installation, hence specify test only installs.
if (owner.parentPackage == null) {
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_testOnly,
false)) {
ai.flags |= ApplicationInfo.FLAG_TEST_ONLY;
}
}
// 解析android:largeHeap属性!
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_largeHeap,
false)) {
ai.flags |= ApplicationInfo.FLAG_LARGE_HEAP;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_usesCleartextTraffic,
true)) {
ai.flags |= ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_supportsRtl,
false /* default is no RTL support*/)) {
ai.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_multiArch,
false)) {
ai.flags |= ApplicationInfo.FLAG_MULTIARCH;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_extractNativeLibs,
true)) {
ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS;
}
if (sa.getBoolean(
R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage,
false)) {
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE;
}
if (sa.getBoolean(
R.styleable.AndroidManifestApplication_directBootAware,
false)) {
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
}
if (sa.getBoolean(R.styleable.AndroidManifestApplication_resizeableActivity,
owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N)) {
ai.privateFlags |= PRIVATE_FLAG_RESIZEABLE_ACTIVITIES;
}
ai.networkSecurityConfigRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_networkSecurityConfig,
0);
String str;
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_permission, 0);
ai.permission = (str != null && str.length() > 0) ? str.intern() : null;
if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) {
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity,
Configuration.NATIVE_CONFIG_VERSION);
} else {
// Some older apps have been seen to use a resource reference
// here that on older builds was ignored (with a warning). We
// need to continue to do this for them so they don't break.
str = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity);
}
ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName,
str, outError);
if (outError[0] == null) {
CharSequence pname;
if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) {
pname = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_process,
Configuration.NATIVE_CONFIG_VERSION);
} else {
// Some older apps have been seen to use a resource reference
// here that on older builds was ignored (with a warning). We
// need to continue to do this for them so they don't break.
pname = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestApplication_process);
}
ai.processName = buildProcessName(ai.packageName, null, pname,
flags, mSeparateProcesses, outError);
ai.enabled = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_enabled, true);
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_isGame, false)) {
ai.flags |= ApplicationInfo.FLAG_IS_GAME;
}
if (false) {
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState,
false)) {
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
// A heavy-weight application can not be in a custom process.
// We can do direct compare because we intern all strings.
if (ai.processName != null && ai.processName != ai.packageName) {
outError[0] = "cantSaveState applications can not use custom processes";
}
}
}
}
ai.uiOptions = sa.getInt(
com.android.internal.R.styleable.AndroidManifestApplication_uiOptions, 0);
sa.recycle();
if (outError[0] != null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
// 开始解析<application>标签的子标签对应的四大组件
final int innerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("activity")) {// 解析<activity>组件
Activity a = parseActivity(owner, res, parser, flags, outError, false,
owner.baseHardwareAccelerated);// 详见章节 【 2.11 】
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
} else if (tagName.equals("receiver")) {// 解析<receiver>组件
Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.receivers.add(a);
} else if (tagName.equals("service")) {// 解析<service>组件
Service s = parseService(owner, res, parser, flags, outError);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.services.add(s);
} else if (tagName.equals("provider")) {// 解析<provider>组件
Provider p = parseProvider(owner, res, parser, flags, outError);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.providers.add(p);
} else if (tagName.equals("activity-alias")) {
Activity a = parseActivityAlias(owner, res, parser, flags, outError);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
} else if (parser.getName().equals("meta-data")) {
// note: application meta-data is stored off to the side, so it can
// remain null in the primary copy (we like to avoid extra copies because
// it can be large)
if ((owner.mAppMetaData = parseMetaData(res, parser, owner.mAppMetaData,
outError)) == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
} else if (tagName.equals("library")) {
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestLibrary);
// Note: don't allow this value to be a reference to a resource
// that may change.
String lname = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestLibrary_name);
sa.recycle();
if (lname != null) {
lname = lname.intern();
if (!ArrayUtils.contains(owner.libraryNames, lname)) {
owner.libraryNames = ArrayUtils.add(owner.libraryNames, lname);
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-library")) {
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUsesLibrary);
// Note: don't allow this value to be a reference to a resource
// that may change.
String lname = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
boolean req = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestUsesLibrary_required,
true);
sa.recycle();
if (lname != null) {
lname = lname.intern();
if (req) {
owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname);
} else {
owner.usesOptionalLibraries = ArrayUtils.add(
owner.usesOptionalLibraries, lname);
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-package")) {
// Dependencies for app installers; we don't currently try to
// enforce this.
XmlUtils.skipCurrentTag(parser);
} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Unknown element under <application>: " + tagName
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
} else {
outError[0] = "Bad element under <application>: " + tagName;
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
}
}
modifySharedLibrariesForBackwardCompatibility(owner);
if (hasDomainURLs(owner)) {
owner.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
} else {
owner.applicationInfo.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
}
return true;
}
上述方法的目的明确诉求简单,只有一个就是各种开撕application节点属性,以及子节点下的所有信息,比如activity、service、receiver、provider、library、users-librayry等信息,同时将解析后的每一个属性生成相应的对象,添加到传入的Package对象实例里面,最后返回给调用者。整体上属于难度不大,但是逻辑繁琐代码冗长。
其中各处代码的细节注释,我就偷懒了!读者可以在阅读的时候自行理解,难度不大,属于体力活的范畴!
2.11 PackageParser.parseActivity()方法
真的干不动了,肝不动了,这是最后一杯了。
// 【 PackageParser.java 】
private Activity parseActivity(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError,
boolean receiver, boolean hardwareAccelerated)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
// 解析 android:name android:label android:icon android:roundIcon android:logo android:banner 属性!
if (mParseActivityArgs == null) {
mParseActivityArgs = new ParseComponentArgs(owner, outError,
R.styleable.AndroidManifestActivity_name,
R.styleable.AndroidManifestActivity_label,
R.styleable.AndroidManifestActivity_icon,
R.styleable.AndroidManifestActivity_roundIcon,
R.styleable.AndroidManifestActivity_logo,
R.styleable.AndroidManifestActivity_banner,
mSeparateProcesses,
R.styleable.AndroidManifestActivity_process,
R.styleable.AndroidManifestActivity_description,
R.styleable.AndroidManifestActivity_enabled);
}
// 由于 activty 和 receiver 使用的同一个方法,所以需要区分解析的是哪个,此时 receiver 为 false!
mParseActivityArgs.tag = receiver ? "<receiver>" : "<activity>";
mParseActivityArgs.sa = sa;
mParseActivityArgs.flags = flags;
//【2】创建了一个 Activity 对象!
Activity a = new Activity(mParseActivityArgs, new ActivityInfo());
if (outError[0] != null) {
sa.recycle();
return null;
}
// 解析 android:exported 属性!
boolean setExported = sa.hasValue(R.styleable.AndroidManifestActivity_exported);
if (setExported) {
a.info.exported = sa.getBoolean(R.styleable.AndroidManifestActivity_exported, false);
}
// 解析 android:theme 属性!
a.info.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
// 解析 android:uiOptions 属性!
a.info.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions,
a.info.applicationInfo.uiOptions);
// 解析 android:parentActivityName 属性!
String parentName = sa.getNonConfigurationString(
R.styleable.AndroidManifestActivity_parentActivityName,
Configuration.NATIVE_CONFIG_VERSION);
if (parentName != null) {
String parentClassName = buildClassName(a.info.packageName, parentName, outError);
if (outError[0] == null) {
a.info.parentActivityName = parentClassName;
} else {
Log.e(TAG, "Activity " + a.info.name + " specified invalid parentActivityName " +
parentName);
outError[0] = null;
}
}
// 解析 android:permission 属性!
String str;
str = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_permission, 0);
if (str == null) {
a.info.permission = owner.applicationInfo.permission;
} else {
a.info.permission = str.length() > 0 ? str.toString().intern() : null;
}
// 解析 android:taskAffinity 属性,这个属性真重要,能决定该 acitivty 和 task 的关系!!
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestActivity_taskAffinity,
Configuration.NATIVE_CONFIG_VERSION);
a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName,
owner.applicationInfo.taskAffinity, str, outError);
a.info.flags = 0;
// 解析 android:multiprocess 属性
if (sa.getBoolean(
R.styleable.AndroidManifestActivity_multiprocess, false)) {
a.info.flags |= ActivityInfo.FLAG_MULTIPROCESS;
}
// 解析 android:finishOnTaskLaunch 属性,这个属性真重要,能决定该 acitivty 和 task 的关系!!
if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnTaskLaunch, false)) {
a.info.flags |= ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH;
}
// 解析 android:clearTaskOnLaunch 属性,这个属性真重要,能决定该 acitivty 和 task 的关系!
if (sa.getBoolean(R.styleable.AndroidManifestActivity_clearTaskOnLaunch, false)) {
a.info.flags |= ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH;
}
// 解析 android:noHistory 属性,这个属性真重要,能决定该 acitivty 和 task 的关系!
if (sa.getBoolean(R.styleable.AndroidManifestActivity_noHistory, false)) {
a.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
}
// 解析 android:alwaysRetainTaskState 属性,这个属性真重要,能决定该 acitivty 和 task 的关系!
if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysRetainTaskState, false)) {
a.info.flags |= ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE;
}
// 解析 android:stateNotNeeded 属性
if (sa.getBoolean(R.styleable.AndroidManifestActivity_stateNotNeeded, false)) {
a.info.flags |= ActivityInfo.FLAG_STATE_NOT_NEEDED;
}
// 解析 android:excludeFromRecents 属性,不再最近任务显示
if (sa.getBoolean(R.styleable.AndroidManifestActivity_excludeFromRecents, false)) {
a.info.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
}
// 解析 android:allowTaskReparenting 属性,这个属性真重要,能决定该 acitivty 和 task 的关系!
if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowTaskReparenting,
(owner.applicationInfo.flags&ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING) != 0)) {
a.info.flags |= ActivityInfo.FLAG_ALLOW_TASK_REPARENTING;
}
// 解析 android:finishOnCloseSystemDialogs 属性
if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, false)) {
a.info.flags |= ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
}
// 解析 android:showOnLockScreen 属性
if (sa.getBoolean(R.styleable.AndroidManifestActivity_showOnLockScreen, false)
|| sa.getBoolean(R.styleable.AndroidManifestActivity_showForAllUsers, false)) {
a.info.flags |= ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
}
// 解析 android:immersive 属性
if (sa.getBoolean(R.styleable.AndroidManifestActivity_immersive, false)) {
a.info.flags |= ActivityInfo.FLAG_IMMERSIVE;
}
// 解析 android:systemUserOnly 属性
if (sa.getBoolean(R.styleable.AndroidManifestActivity_systemUserOnly, false)) {
a.info.flags |= ActivityInfo.FLAG_SYSTEM_USER_ONLY;
}
// 接下来,对于 activity 和 receiver 解析不同的属性!
if (!receiver) {
// 对于 activity 进入这个!
// 解析 android:hardwareAccelerated 属性
if (sa.getBoolean(R.styleable.AndroidManifestActivity_hardwareAccelerated,
hardwareAccelerated)) {
a.info.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED;
}
// 解析 android:launchMode 属性
a.info.launchMode = sa.getInt(
R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE);
// 解析 android:documentLaunchMode 属性
a.info.documentLaunchMode = sa.getInt(
R.styleable.AndroidManifestActivity_documentLaunchMode,
ActivityInfo.DOCUMENT_LAUNCH_NONE);
// 解析 android:maxRecents 属性
a.info.maxRecents = sa.getInt(
R.styleable.AndroidManifestActivity_maxRecents,
ActivityManager.getDefaultAppRecentsLimitStatic());
// 解析 android:configChanges 属性
a.info.configChanges = sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0);
// 解析 android:windowSoftInputMode 属性
a.info.softInputMode = sa.getInt(
R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);
// 解析 android:persistableMode 属性
a.info.persistableMode = sa.getInteger(
R.styleable.AndroidManifestActivity_persistableMode,
ActivityInfo.PERSIST_ROOT_ONLY);
// 解析 android:allowEmbedded 属性
if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowEmbedded, false)) {
a.info.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED;
}
// 解析 android:autoRemoveFromRecents 属性
if (sa.getBoolean(R.styleable.AndroidManifestActivity_autoRemoveFromRecents, false)) {
a.info.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS;
}
// 解析 android:relinquishTaskIdentity 属性
if (sa.getBoolean(R.styleable.AndroidManifestActivity_relinquishTaskIdentity, false)) {
a.info.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
}
// 解析 android:resumeWhilePausing 属性
if (sa.getBoolean(R.styleable.AndroidManifestActivity_resumeWhilePausing, false)) {
a.info.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
}
// 解析 android:screenOrientation 属性
a.info.screenOrientation = sa.getInt(
R.styleable.AndroidManifestActivity_screenOrientation,
SCREEN_ORIENTATION_UNSPECIFIED);
// 解析 android:resizeableActivity android:supportsPictureInPicture 属性
a.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
final boolean appDefault = (owner.applicationInfo.privateFlags
& PRIVATE_FLAG_RESIZEABLE_ACTIVITIES) != 0;
final boolean resizeableSetExplicitly
= sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity);
final boolean resizeable = sa.getBoolean(
R.styleable.AndroidManifestActivity_resizeableActivity, appDefault);
if (resizeable) {
if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture,
false)) {
a.info.resizeMode = RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
} else {
a.info.resizeMode = RESIZE_MODE_RESIZEABLE;
}
} else if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N
|| resizeableSetExplicitly) {
a.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
} else if (!a.info.isFixedOrientation() && (a.info.flags & FLAG_IMMERSIVE) == 0) {
a.info.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
}
// 解析 android:alwaysFocusable 属性
if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) {
a.info.flags |= FLAG_ALWAYS_FOCUSABLE;
}
// 解析 android:lockTaskMode 属性
a.info.lockTaskLaunchMode =
sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
// 解析 android:directBootAware 属性
a.info.encryptionAware = a.info.directBootAware = sa.getBoolean(
R.styleable.AndroidManifestActivity_directBootAware,
false);
// 解析 android:enableVrMode 属性
a.info.requestedVrComponent =
sa.getString(R.styleable.AndroidManifestActivity_enableVrMode);
} else {
// 对于 receiver,会进入这里!
a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
a.info.configChanges = 0;
// 解析 android:singleUser 属性
if (sa.getBoolean(R.styleable.AndroidManifestActivity_singleUser, false)) {
a.info.flags |= ActivityInfo.FLAG_SINGLE_USER;
if (a.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) {
Slog.w(TAG, "Activity exported request ignored due to singleUser: "
+ a.className + " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
a.info.exported = false;
setExported = true;
}
}
// 解析 android:directBootAware 属性
a.info.encryptionAware = a.info.directBootAware = sa.getBoolean(
R.styleable.AndroidManifestActivity_directBootAware,
false);
}
if (a.info.directBootAware) {
owner.applicationInfo.privateFlags |=
ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
}
sa.recycle();
// 如果解析的是 receiver,针对 height-weight 类型的进程做处理
if (receiver && (owner.applicationInfo.privateFlags
&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
if (a.info.processName == owner.packageName) {
outError[0] = "Heavy-weight applications can not have receivers in main process";
}
}
if (outError[0] != null) {
return null;
}
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
if (parser.getName().equals("intent-filter")) { // 解析 "intent-filter"
ActivityIntentInfo intent = new ActivityIntentInfo(a);
if (!parseIntent(res, parser, true, true, intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in intent filter at "
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
a.intents.add(intent);
}
} else if (!receiver && parser.getName().equals("preferred")) { // 解析 "preferred"
ActivityIntentInfo intent = new ActivityIntentInfo(a);
if (!parseIntent(res, parser, false, false, intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in preferred at "
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
if (owner.preferredActivityFilters == null) {
owner.preferredActivityFilters = new ArrayList<ActivityIntentInfo>();
}
owner.preferredActivityFilters.add(intent);
}
} else if (parser.getName().equals("meta-data")) { // 解析 "meta-data"
if ((a.metaData = parseMetaData(res, parser, a.metaData,
outError)) == null) {
return null;
}
} else if (!receiver && parser.getName().equals("layout")) { // 解析 "layout"
parseLayout(res, parser, a);
} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
if (receiver) {
Slog.w(TAG, "Unknown element under <receiver>: " + parser.getName()
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
Slog.w(TAG, "Unknown element under <activity>: " + parser.getName()
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
}
XmlUtils.skipCurrentTag(parser);
continue;
} else {
if (receiver) {
outError[0] = "Bad element under <receiver>: " + parser.getName();
} else {
outError[0] = "Bad element under <activity>: " + parser.getName();
}
return null;
}
}
}
if (!setExported) {
a.info.exported = a.intents.size() > 0;
}
return a;
}
上述源码无它,唯有解析其标签属性和子标签而已!
2.12 PackageParser.parseMonolithicPackage()方法
分析支持,读者估计心里有一个声音了我是谁,我在那里的恍惚感觉了。还是让我们来捋一捋,还记的在章节2.1中parsePackage中我们分了两步走:
1.对于支持拆分的安装包走parseClusterPackage的分支逻辑
2.对于不支持拆分的安装包走parseMonolithicPackage的逻辑分支
前面的一系列章节重点分析了支持拆分的安装包的解析处理逻辑,而对于对于不支持apk拆分的安装包,PKMS采取了向下兼容的怀柔政策,通过使用PackageParser.parseMonolithicPackage进行解析,而Android系统中最最典型的不支持拆分的apk就是是 /system/framework/framework-res.apk,下面我们来看看这个方法:
// 【 PackageParser.java ]
@Deprecated
/*
接卸单个且不支持拆分的APK安装文件
*、
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
// 解析指定单个APK文件,获取PackageLite实例对象
final PackageLite lite = parseMonolithicPackageLite(apkFile, flags); // 详见章节 【 2.13 】
if (mOnlyPowerOffAlarmApps) {
...
}
if (!mOnlyPowerOffAlarmApps && mOnlyCoreApps) {
...
}
final AssetManager assets = new AssetManager();
try {
// 解析Android包得到完整的Android包信息,注意是完整的包信息
final Package pkg = parseBaseApk(apkFile, assets, flags);// 详见章节 【 2.7 】
pkg.setCodePath(apkFile.getAbsolutePath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
此处的处理逻辑和前面的支持拆分安装包的解析逻辑基本相同:
-
首先调用方法parseMonolithicPackageLite先得到一个中间数据结构PacakgeLite,最终Android的包名、版本等信息都会保存在这个数据结构中(此处PacakgeLite就不存在有多个ApkLite的可能,因为当前解析的是单个不支持拆分的安装包)
-
然后接着调用PackageParser.parseBaseApk进行下一步的Android包处理逻辑,这个方法主要用来详细解析AndroidManifest.xml文件。其解析过程与AndroidManifest.xml的文件结构一一对应,譬如先解析<application>标签的内容,然后解析其下的<activity>,<service>等标签。最终将上述得到的解析信息封装成Package、此时I的Package中就基本上涵盖了AndroidManifest.xml中涉及的所有信息。
2.13 PackageParser.parseMonolithicPackageLite()方法
果然没有玩出啥新花样,都是对前面已经分析过的源码的重新组装。
private static PackageLite parseMonolithicPackageLite(File packageFile, int flags)
throws PackageParserException {
// 解析指定APK文件,获取Apk轻量级精简数据结构类实例
final ApkLite baseApk = parseApkLite(packageFile, flags);// 详见章节 【 2. 4 】
final String packagePath = packageFile.getAbsolutePath();
// 根据得到的ApkLite信息填充返回PackageLite
return new PackageLite(packagePath, baseApk, null, null, null);
}
收工,打卡下班
三.PackageParser解析Android包总结
分析至此我们的PackageParser核心方法剖析就到此结束了!而我们也通过上述的方法将Android包的各种配件烹饪成了美味可口的Android包了,然后我们的PKMS服务就可以对上述的Android包进行下一步的加工了。
虽然整个流程分析结束了,但是我们还是有必要再来回过头来重新看下,还记得我们最最开始梳理的简单解析伪代码图吗:
parsePackages(File file...)
└── parseClusterPackage(File packageDir...)
└── parseClusterPackageLite(File packageDir...)
| └── parseApkLite(File apkFile...)
| └── parseApkLite(String codePath...)
└── parseBaseApk()
└── parseBaseApplication()
| └── parseActivity()
| └── parseService()
| └── ...
└── parseInstrumentation()
└── ...
└── parseMonolithicPackage(File apkFile...)
└── parseMonolithicPackageLite(File packageFile...)
| └── parseApkLite(File apkFile...)
| └── parseApkLite(String codePath...)
└── parseBaseApk()
└── parseBaseApplication()
| └── parseActivity()
| └── parseService()
| └── ...
└── parseInstrumentation()
└── ...
对于上述逻辑,无论是解析支持拆分安装包还是不支持拆分类型的安装包,其核心的处理过程都不变那就是:
- PackageParser首先解析出了ApkLite,得到每个Apk文件的简化信息(对于具有多个Apk文件的Package来说,将得到多个ApkLite);
- 然后利用所有的ApkLite及XML中的其它信息,解析出PackageLite;
- 最后利用PackageLite中的信息(主要是通过它验证安装包的合法性)及XML中的其它信息,最终解析得出Package信息,从而得到一个全的Android包的信息数据结构类为后续的PKMS进一步处理打下基础
一个Android包的信息解析完了,那么我们有没有什么直观的手段可以查看呢!既然问了,那就肯定是有了,这里给大家安利一个好用的dumpsys命令,可以提供给大家查阅安装包的解析后的具体信息,这个命令的终极格式如下:
dumpsys package packagename
我们来测试一下(由于篇幅,我精简了许多,输入的内容有上千行,非常详细):
dumpsys package com.android.settings
Activity Resolver Table:
Full MIME Types:
...
Base MIME Types:
...
Schemes:
...
Non-Data Actions:
...
Receiver Resolver Table:
Schemes:
...
Non-Data Actions:
...
Service Resolver Table:
Non-Data Actions:
...
Provider Resolver Table:
Non-Data Actions:
...
Registered ContentProviders:
...
ContentProvider Authorities:
...
Key Set Manager:
[com.android.settings]
Signing KeySets: 1
Packages:
Package [com.android.settings] (642f3ee):
userId=1000
sharedUser=SharedUserSetting{bfb509d android.uid.system/1000}
pkg=Package{2802702 com.android.settings}
codePath=/system/priv-app/XXXSettings
resourcePath=/system/priv-app/XXXSettings
legacyNativeLibraryDir=/system/priv-app/XXXSettings/lib
primaryCpuAbi=armeabi-v7a
secondaryCpuAbi=null
versionCode=25 minSdk=25 targetSdk=25
versionName=7.1.2
splits=[base]
apkSigningVersion=2
applicationInfo=ApplicationInfo{947e04d com.android.settings}
flags=[ SYSTEM HAS_CODE PERSISTENT ALLOW_CLEAR_USER_DATA ]
privateFlags=[ PRIVILEGED DEFAULT_TO_DEVICE_PROTECTED_STORAGE DIRECT_BOOT_AWARE RESIZEABLE_ACTIVITIES ]
dataDir=/data/user_de/0/com.android.settings
supportsScreens=[small, medium, large, xlarge, resizeable, anyDensity]
timeStamp=2009-01-01 00:00:00
firstInstallTime=2009-01-01 00:00:00
lastUpdateTime=2009-01-01 00:00:00
signatures=PackageSignatures{9ff6813 [b4addb29]}
installPermissionsFixed=true installStatus=1
pkgFlags=[ SYSTEM HAS_CODE PERSISTENT ALLOW_CLEAR_USER_DATA ]
requested permissions:
android.permission.WRITE_MEDIA_STORAGE
...
Shared users:
SharedUser [android.uid.system] (bfb509d):
userId=1000
install permissions:
android.permission.BIND_INCALL_SERVICE: granted=true
...
Dexopt state:
[com.android.settings]
Instruction Set: arm
path: /system/priv-app/XXXSettings/XXXSettings.apk
status: /data/dalvik-cache/arm/system@priv-app@XXXSettings@XXXSettings.apk@classes.dex [compilation_filter=speed,
status=kOatUpToDate]
Compiler stats:
[com.android.settings]
(No recorded stats)
四.写在最后
说不出再见,到这里Android包信息体和包解析器(上/中/下)系列博客终于要落下帷幕了,这三篇博客我们重点分析了:
-
包管理机制设计者如何从架构层出发设计Android包信息体
-
然后从实际源码角度出发,分析了包信息体的具体设计实现
-
接着从包管理机制设计者意图了解PackageParser包解析器,即它的概述和定义是什么
-
再接着PackageParser包解析器作为一个工具类,它的核心成员变量是什么(当然是和包信息体的封装有关)
-
最后PackageParser包解析器作为一个工具类,它的核心方法有那些(核心方法就是解析Android包)
最终我们关于Android包解析的大法就修炼完毕,此时我们再回过头来看下面的类图也许真的就是手到擒来了!
并且这里我大费周章的在PKMS启动系列博客中,单独的将Android包解析独立出来三篇都是为l了后续分析PKMS扫描安装包而做的准备的,只有理解了Android包数据结构存储,解析才能对后续的PKMS分析更加得心应手!
好了,各位青山不改绿水长流,各位江湖见!当然各位读者的点赞和关注是我写作路上前进的最大动力了,如果有啥不对或者不爽的也可以踩一踩也无妨!在后续博客中我们将会继续分析PKMS启动扫描流程阶段,如果读者喜欢欢迎继续关注!如果读者对于此系列博客PKMS有兴趣,欢迎继续下一篇章PKMS启动详解(七)之BOOT_PROGRESS_PMS_SYSTEM_SCAN_START阶段流程分析之旅,让我带领读者一起扫描系统应用安装目录之旅!