PMS之apk安装流程

本文是基于api 25(Android 7.1.1),主要是安装保存在sd 卡的应用apk 的流程。

基础知识

两种不同的应用

/system/app/目录下代表的是系统应用。
/data/app/目录下代表的是安装的应用。

安装的方式

  1. 保存在sd 卡以后安装
  2. 通过adb脚本安装
  3. 通过应用市场安装

本文的流程是保存在sd 卡以后安装的流程。

PMS 代码注意点

PackageManagerService(后续用PMS 表示)的最上方注释需要仔细阅读(不然看源码可能有点懵)。
这里稍微的解释下注释的内容。PMS 中有很多方法的后缀是LI、LIF、LPr、LPw。需要理解是什么意思就需要知道PMS 中有两个很重要的锁mPackagesmInstallLock以及一个mFrozenPackages变量。还要了解一个注解的使用com.android.internal.annotations.GuardedBy(这个注解可以被MethodField使用,意思是使用相应的MethodField的时候一定需要拥有相应的锁)。

方法 意义
fooLI 需要mInstallLock
fooLIF 需要mInstallLock锁,并且需要包是frozen
fooLPr 需要mPackages锁以后读
fooLPw 需要mPackages锁以后写

mPackages使用的非常频繁(例如:四大组建的调用)但是占用的时间比较短。mInstallLock则只在‘安装’(与sd 卡交互等等)的时候才会使用,并且执行内容比较耗时。在synchronized(mPackages)的代码块中不允许出现synchronized(mInstallLock)

客户端调用安装的代码

1
2
3
4
5
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.fromFile(new File(fileName)), "application/vnd.android.package-archive");
intent.putExtra("android.intent.extra.INSTALLER_PACKAGE_NAME", context.getPackageName());
context.startActivity(intent);

客户端可能需要手动调用安装的apk 的逻辑。

其他

  1. adb shell dumpsys package p查看安装的包信息。从PackageManagerService#dump代码可以看到相应的打印信息
  2. com.android.packageinstaller进程开始,这个进程代表的就是安装Apk 的功能。
  3. aosp/frameworks/base/core/res/res/values/attrs_manifest.xml对应的是AndroidManifest 中声明的信息

安装流程

安装的总体时序图PMS安装apk流程之安装仅安装

第1步,已经将apk 拷贝到了/data/app/vmdlxxxxx.tmp/PackageInstaller(这里没有详细看vmdlxxxxx.tmp 的命名规则)

第2不,会命名出base.apk。

第3步,简要的解析了manifest 和application 标签中的信息,然后生成了ApkLite。

第4步,提取so 文件。这个过程其实有一点细节是怎么去判断是当前手机的架构的。

第5步,app 已经拷贝到了data/apk/base.apk,此时知道了包名、文件的路径等。这个时候才开始安装的流程。

第7-10步,可以放在一起。调用Service去解析apk 信息以及getMinimalPackageInfo这里会返回一些数据,包含很多安装出错的数据。这里的流程是执行DefaultContainerService的一个内部类的getMinimalPackageInfo方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
//解析出安装的路径。DefaultContainerService 中的getMinimalPackageInfo,最后的PackageHelper#resolveInstallLocation
public static int resolveInstallLocation(Context context, String packageName,
int installLocation, long sizeBytes, int installFlags) {
ApplicationInfo existingInfo = null;
//省略代码...
return PackageHelper.RECOMMEND_INSTALL_EPHEMERAL;
//省略代码...
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
//省略代码...
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
//省略代码...
return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; // 空间不足会返回错误
}

定义的一些安装的错误信息,可以在PackageHelper中找到。

1
2
3
4
5
6
7
8
9
10
public static final int RECOMMEND_INSTALL_INTERNAL = 1;
public static final int RECOMMEND_INSTALL_EXTERNAL = 2;
public static final int RECOMMEND_INSTALL_EPHEMERAL = 3;
public static final int RECOMMEND_FAILED_INSUFFICIENT_STORAGE = -1;//空间不足
public static final int RECOMMEND_FAILED_INVALID_APK = -2;//不合法的apk
public static final int RECOMMEND_FAILED_INVALID_LOCATION = -3;//不合法的安装路径
public static final int RECOMMEND_FAILED_ALREADY_EXISTS = -4;//包已经安装了
public static final int RECOMMEND_MEDIA_UNAVAILABLE = -5;
public static final int RECOMMEND_FAILED_INVALID_URI = -6;
public static final int RECOMMEND_FAILED_VERSION_DOWNGRADE = -7;//不能降低包的版本

具体看一个比较容易出现的错误RECOMMEND_FAILED_VERSION_DOWNGRADE,代码比较容易看懂。

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
//handleStartCopy->installLocationPolicy->checkDowngrade
private static void checkDowngrade(PackageParser.Package before, PackageInfoLite after)
throws PackageManagerException {
if (after.versionCode < before.mVersionCode) {
throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
"Update version code " + after.versionCode + " is older than current "
+ before.mVersionCode);
} else if (after.versionCode == before.mVersionCode) {
if (after.baseRevisionCode < before.baseRevisionCode) {
throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
"Update base revision code " + after.baseRevisionCode
+ " is older than current " + before.baseRevisionCode);
}
if (!ArrayUtils.isEmpty(after.splitNames)) {
for (int i = 0; i < after.splitNames.length; i++) {
final String splitName = after.splitNames[i];
final int j = ArrayUtils.indexOf(before.splitNames, splitName);
if (j != -1) {
if (after.splitRevisionCodes[i] < before.splitRevisionCodes[j]) {
throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
"Update split " + splitName + " revision code "
+ after.splitRevisionCodes[i] + " is older than current "
+ before.splitRevisionCodes[j]);
}
}
}
}
}
}

第13步,installPackageLI的时候已经过滤错误,这里是开始安装了。有一个小细节是rename 的时候会出现名称是包名-1或者包名-2看下面的代码其实就是在packageName-1packageName-2之间一直切换。

1
2
3
4
5
6
7
8
9
10
//PackageManagerService
private File getNextCodePath(File targetDir, String packageName) {
int suffix = 1;
File result;
do {
result = new File(targetDir, packageName + "-" + suffix);
suffix++;
} while (result.exists());
return result;
}

第14步,解析出包信息。这里面就是详细的解析出数据的。这里给出解析activityreceiverserviceprovider的逻辑(可以看出activityreceiver其实保存的是同一个对象)。直到这一个步骤完成才真正的解析出了AndroidManifest.xml中的所有信息,并且这里并没有将信息保存在PMS 中。

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
//PackageParser
//parsePackage->parseMonolithicPackage->parseBaseApk->parseBaseApkCommon->parseBaseApplication
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError){
//省略代码...
String tagName = parser.getName();
if (tagName.equals("activity")) { //解析activity
Activity a = parseActivity(owner, res, parser, flags, outError, false,
owner.baseHardwareAccelerated);
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 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 p = parseProvider(owner, res, parser, flags, outError);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.providers.add(p);
}
//省略代码...
}

第17步,会在data/appvmdlxxxxx.tmp/生成oat 文件夹。

ART 推出了预先 (AOT) 编译,可提高应用的性能。在安装时,ART 使用设备自带的 dex2oat 工具来编译应用。

第19步,freezePackageForInstall会将当前安装的包加入到mFrozenPackages。这里前面的逻辑已经说明过,LIF。

第22步,scanPackageLI会从AndroidManifest.xml中扫描出信息,保存在PMS#mPackagesPMS#mActivitiesPMS#mReceiversPMS#mServicesPMS#mProviders等里面。下面给出的是解析ServiceActivity

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
//PackageManagerService
//installNewPackageLIF->scanPackageTracedLI->scanPackageLI->scanPackageDirtyLI
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
final int policyFlags, final int scanFlags, long currentTime, UserHandle user)
throws PackageManagerException {
//省略代码...
N = pkg.services.size();
r = null;
for (i=0; i<N; i++) {
PackageParser.Service s = pkg.services.get(i);
s.info.processName = fixProcessName(pkg.applicationInfo.processName,
s.info.processName, pkg.applicationInfo.uid);
mServices.addService(s);
if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(s.info.name);
}
}
if (r != null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Services: " + r);
}
//省略代码...
N = pkg.activities.size();
r = null;
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.activities.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
mActivities.addActivity(a, "activity");
if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(a.info.name);
}
}
if (r != null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Activities: " + r);
}
//省略代码...
}

第23-28步,可以放在一起,主要是生成/data/data/目录下的数据。第26步的createAppData会在data/data目录下生成相应的用户数据(似乎仅仅生成了文件夹cachecode_cache)。第28步的linkNativeLibraryDirectory处理so 文件。到这里完成了data/data中相关信息创建。

到此就结束了应用apk 的安装过程(执行完installPackageLI后),此时可以通过adb打开相应的Activity了,但是没有创建桌面图标。

所以安装的整个逻辑可以总结为:

  1. 将应用apk 拷贝到data/apk目录,以便能够找到代码。
  2. dex2oat 优化操作。
  3. 解析AndroidManifest.xml 中的信息,然后保存在PMS 中供以后使用。
  4. 生成相应的data/data/包名信息。

安装后的处理逻辑

上面安装流程里面的第12步,后续还会处理安装以后的逻辑,最后会调用到PMS#finishPackageInstall,然后会处理到PMS#handlePackagePostInstall。(这里的代码没有跟踪下去了😳)

问题

  1. 应用安装的入口在哪?应用卸载的入口在哪?卸载的入口可以查看PMS中的deleteXX 或者removeXX 方法。
  2. apk 的信息安装以后放在哪了。
  3. 安装出错是在哪里提示的。
  4. 应用图标生成的过程在哪?跟android.intent.action.PACKAGE_ADDED这个广播有关系。

PMS 代码理解
参考1
参考2
参考3