PKMS 钻研(5) - PackageParser

一、开篇

1.1 核心源码

关键类 路径
PackageParser.java frameworks/base/core/java/android/content/pm/PackageParser.java
PackageManagerService.java frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

1.2 简介

在分析 PackageManagerService 构造函数的时候,我们知道:Android 安装一个 APK 的时候首先会解析 APK,而解析 APK 则需要用到一个工具类,这个工具类就是我们这篇博文的主角 PackageParser(在我们之前研究的源码中,你已经见过它的身影了)!

二、PackageParser

2.1 官方说明

首先我们看下官方对 PackageParser 的说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* Parser for package files (APKs) on disk. This supports apps packaged either
* as a single "monolithic" APK, or apps packaged as a "cluster" of multiple
* APKs in a single directory.
* <p>
* Apps packaged as multiple APKs always consist of a single "base" APK (with a
* {@code null} split name) and zero or more "split" APKs (with unique split
* names). Any subset of those split APKs are a valid install, as long as the
* following constraints are met:
* <ul>
* <li>All APKs must have the exact same package name, version code, and signing
* certificates.
* <li>All APKs must have unique split names.
* <li>All installations must contain a single base APK.
* </ul>
*
* 解析磁盘上的 APK 安装包文件。它既能解析一个"单一" APK 文件,也能解析一个"集群" APK 文件
 * (即一个 APK 文件里面包含多个 APK 文件)。一个"集群" APK 有一个"基准" APK (base APK) 组成
* 和其他一些"分割" APK ("split" APKs) 构成,其中这些"分割" APK 用一些数字来分割。
* 这些"分割"的 APK 必须都是有效的安装,同时必须满足下面的几个条件:
* 所有的 APK 必须具有完全相同的软件包名称,版本代码和签名证书;
* 所有的 APK 必须具有唯一的拆分名称;
* 所有安装必须包含一个单一的APK;
*
* @hide
*/
public class PackageParser {

2.2 创建解析器

1
2
3
4
5
6
7
// 具体源码及逻辑流程省略...

// 创建解析器
final PackageParser pp = new PackageParser();

// 解析 APK
tmpPkg = pp.parsePackage(codePath, parseFlags);

2.3 解析 APK

在分析 PackageParser 解析 APK 之前,我们有必要了解一下 Split APK 机制!

2.3.1 Split APK机制

Split APK 是 Google 为解决 65536 上限,以及 APK 安装包越来越大等问题,在 Android L 中引入的机制。

Split APK 可以将一个庞大的 APK,按屏幕密度,ABI 等形式拆分成多个独立的 APK,在应用程序更新时,不必下载整个 APK,只需单独下载某个模块即可安装更新。

Split APK 将原来一个 APK 中多个模块共享同一份资源的模型分离成多个 APK 使用各自的资源,并且可以继承 Base APK 中的资源,多个 APK 有相同的 data,cache 目录。

在引入了 Split APK 机制后,APK 有两种分类:
Single APK:安装文件为一个完整的 APK,即 Base APK,源码中定义为 Monolithic
Mutiple APK:安装文件在一个文件目录中,其内部有多个被拆分的 APK,这些 APK 由一个 Base APK 和一个或多个 Split APK 组成,源码中定义为 Cluster

2.3.2 parsePackage

接下来我们开始正式分析 parsePackage 方法:

1
2
3
4
5
6
/**
* Equivalent to {@link #parsePackage(File, int, boolean)} with {@code useCaches == false}.
*/
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
return parsePackage(packageFile, flags, false /* useCaches */);
}

新增一个 useCaches 参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public Package parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
if (parsed != null) {
return parsed;
}

long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;

// 如果解析的 packageFile 是一个目录,则调用 parseClusterPackage 方法
if (packageFile.isDirectory()) {
parsed = parseClusterPackage(packageFile, flags);
} else {
// 如果是单个 APK 文件,则调用 parseMonolithicPackage 方法
parsed = parseMonolithicPackage(packageFile, flags);
}

long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
cacheResult(packageFile, flags, parsed);
if (LOG_PARSE_TIMINGS) {
parseTime = cacheTime - parseTime;
cacheTime = SystemClock.uptimeMillis() - cacheTime;
if (parseTime + cacheTime > LOG_PARSE_TIMINGS_THRESHOLD_MS) {
Slog.i(TAG, "Parse times for '" + packageFile + "': parse=" + parseTime
+ "ms, update_cache=" + cacheTime + " ms");
}
}

return parsed;
}

我们这边选取 parseClusterPackage 作为分析的分支,当你搞懂这个方法的具体逻辑,单个 APK 的解析原理就很简单了。

三、parseClusterPackage

我们来看看 parseClusterPackage() 的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
/*
* 调用 parseClusterPackageLite 方法用于轻量级解析目录文件,
* 之所以要轻量级解析是因为解析 APK 是一个复杂耗时的操作,这里的逻辑并不需要 APK 所有的信息。
*/
final PackageLite lite = parseClusterPackageLite(packageDir, 0);

/*
* mOnlyCoreApps 用来指示 PackageParser 是否只解析“核心”应用,
* “核心”应用指的是 AndroidManifest 中 coreApp 的属性值为 true,
* 只解析“核心”应用是为了创建一个极简的启动环境,
* 我们可以通过 PackageParser 的 setOnlyCoreApps 方法来设置 mOnlyCoreApps 的值。
*
* lite.coreApp 表示当前包是否包含“核心”应用,如果不满足条件就会抛出异常。
*/
if (mOnlyCoreApps && !lite.coreApp) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + packageDir);
}

// Build the split dependency tree.
SparseArray<int[]> splitDependencies = null;
final SplitAssetLoader assetLoader;
if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
try {
splitDependencies =
SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
} catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
e.getMessage());
}
} else {
assetLoader = new DefaultSplitAssetLoader(lite, flags);
}

try {
// 获取 AssetManager 对象
final AssetManager assets = assetLoader.getBaseAssetManager();
final File baseApk = new File(lite.baseCodePath);
// 解析 base APK,获得对应的 Package 对象
final Package pkg = parseBaseApk(baseApk, assets, flags);
if (pkg == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse base APK: " + baseApk);
}

if (!ArrayUtils.isEmpty(lite.splitNames)) {
// 获取 split APK 的数量
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];
pkg.applicationInfo.splitNames = pkg.splitNames;
pkg.applicationInfo.splitDependencies = splitDependencies;
pkg.applicationInfo.splitClassLoaderNames = new String[num];

for (int i = 0; i < num; i++) {
final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
// 解析每个 split APK
parseSplitApk(pkg, i, splitAssets, flags);
}
}

// 设置相应属性
pkg.setCodePath(packageDir.getCanonicalPath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} catch (IOException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to get path: " + lite.baseCodePath, e);
} finally {
IoUtils.closeQuietly(assetLoader);
}
}

3.1 parseClusterPackageLite

我们来看看轻量级解析函数 parseClusterPackageLite() 的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static PackageLite parseClusterPackageLite(File packageDir, int flags) 
throws PackageParserException {
... ...

for (File file : files) {
if (isApkFile(file)) {
// 通过 parseApkLite 方法解析每个 Mutiple APK,
// 得到每个 Mutiple APK 对应的 ApkLite(轻量级 APK 信息)。
final ApkLite lite = parseApkLite(file, flags);
... ...
}
}
... ...

final String codePath = packageDir.getAbsolutePath();
// 将这些 ApkLite 封装为一个 PackageLite(轻量级包信息)并返回
return new PackageLite(codePath, baseApk, splitNames, isFeatureSplits, usesSplitNames,
configForSplits, splitCodePaths, splitRevisionCodes);
}

3.2 parseBaseApk

我们来看看 Base Apk 的解析函数 parseBaseApk() 的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
private static final String MNT_EXPAND = "/mnt/expand/";

private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();

String volumeUuid = null;
if (apkPath.startsWith(MNT_EXPAND)) {
final int end = apkPath.indexOf('/', MNT_EXPAND.length());
// 如果 APK 的路径以 /mnt/expand/ 开头,就截取该路径获取 volumeUuid
volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
}

mParseError = PackageManager.INSTALL_SUCCEEDED;
mArchiveSourcePath = apkFile.getAbsolutePath();

if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);

XmlResourceParser parser = null;
try {
final int cookie = assets.findCookieForPath(apkPath);
if (cookie == 0) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Failed adding asset path: " + apkPath);
}
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final Resources res = new Resources(assets, mMetrics, null);

final String[] outError = new String[1];
// 再次调用 parseBaseApk 方法
final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
if (pkg == null) {
throw new PackageParserException(mParseError,
apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
}

// 用于以后标识这个解析后的 Package
pkg.setVolumeUuid(volumeUuid);
// 用于标识该 App 所在的存储卷 UUID
pkg.setApplicationVolumeUuid(volumeUuid);
pkg.setBaseCodePath(apkPath);
pkg.setSigningDetails(SigningDetails.UNKNOWN);

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);
}
}

这个方法内部主要就是做了两件事:

1、解析 volumeUuid
2、调用 parseBaseApk(res, parser, flags, outError) 来获取 Package 对象 pkg 并返回

3.3 parseBaseApk 重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser,
int flags, String[] outError) throws XmlPullParserException, IOException {
final String splitName;
final String pkgName;
... ...

// 用包名创建一个 Package 对象
final Package pkg = new Package(pkgName);

// 从资源中提取自定义属性集 com.android.internal.R.styleable.AndroidManifest 得到 TypedArray
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);

// 初始化 pkg 的属性
pkg.mVersionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
pkg.mVersionCodeMajor = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCodeMajor, 0);
pkg.applicationInfo.setVersionCode(pkg.getLongVersionCode());
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();
}

// 读取 APK 的 AndroidManifest 中的 coreApp 的值,判断是否为核心 Apk
pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);

pkg.mCompileSdkVersion = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_compileSdkVersion, 0);
pkg.applicationInfo.compileSdkVersion = pkg.mCompileSdkVersion;
pkg.mCompileSdkVersionCodename = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_compileSdkVersionCodename, 0);
if (pkg.mCompileSdkVersionCodename != null) {
pkg.mCompileSdkVersionCodename = pkg.mCompileSdkVersionCodename.intern();
}
pkg.applicationInfo.compileSdkVersionCodename = pkg.mCompileSdkVersionCodename;

// 获取资源后要回收
sa.recycle();

return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}

3.4 parseBaseApkCommon

最终调用了 parseBaseApkCommon 方法,这个方法主要用来解析 APK 的 AndroidManifest 中的 各个标签,比如 application、permission、uses-sdk、feature-group 等等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
IOException {
... ...

// 是否要安装在 SD 卡上
if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
}

... ...

// 开始解析 xml
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {

... ...

// private static final String TAG_APPLICATION = "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;
// 其中四大组件的标签在 application 标签下,
// 解析 application 标签的方法为 parseBaseApplication。
if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
return null;
}
} else if (tagName.equals(TAG_OVERLAY)) {
... ...
}
... ...
}

3.5 parseBaseApplication

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// parseBaseApplication 方法很长,我们这里只截取了解析四大组件相关的代码。
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
... ...

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")) {
// 调用 parseActivity 方法解析 activity 标签,
// 得到一个 Activity 对象(PackageParser 的静态内部类)
Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}

// 将解析得到的 Activity 对象保存在 Package 的列表 activities 中
owner.activities.add(a);
parsedComponent = a.info;

} else if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}

owner.receivers.add(a);
parsedComponent = a.info;

} else if (tagName.equals("service")) {
Service s = parseService(owner, res, parser, flags, outError, cachedArgs);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}

owner.services.add(s);
parsedComponent = s.info;

} else if (tagName.equals("provider")) {
Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}

owner.providers.add(p);
parsedComponent = p.info;
}
... ...
}
... ...
}

四、Package 数据结构

包被解析后,最终存储在 Package ,它是 PackageParser内部类,它的部分成员变量如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* Representation of a full package parsed from APK files on disk. A package
* consists of a single base APK, and zero or more split APKs.
*/
public final static class Package implements Parcelable {

public String packageName;

// The package name declared in the manifest as the package can be
// renamed, for example static shared libs use synthetic package names.
public String manifestPackageName;

/** Names of any split APKs, ordered by parsed splitName */
public String[] splitNames;

// TODO: work towards making these paths invariant

public String volumeUuid;

/**
* Path where this package was found on disk. For monolithic packages
* this is path to single base APK file; for cluster packages this is
* path to the cluster directory.
*/
public String codePath;

/** Path of base APK */
public String baseCodePath;
/** Paths of any split APKs, ordered by parsed splitName */
public String[] splitCodePaths;

... ...


// For now we only support one application per package.
public ApplicationInfo applicationInfo = new ApplicationInfo();

public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
public final ArrayList<PermissionGroup> permissionGroups =
new ArrayList<PermissionGroup>(0);
/*
* activities 列表中存储了类型为 Activity 的对象,需要注意的是这个 Acticity 并不是
* 我们常用的那个 Activity ,而是 PackageParser 的静态内部类,Package 中的其他列表也都是如此。
*/
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);

... ...
}