PKMS 钻研(7) - APK 安装(中)

Updated on 2020.04.02

核心源码(Android 10)

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

APK 安装(上) 中,我们了解了 PackageInstaller 安装 APK 的流程,最后会将 APK 的信息交由 PMS 处理。那么 PMS 是如何处理的?这就是我们本篇文章需要分析的。

二、PackageManagerService

2.1 commitLocked

在前一篇文章末尾,我们研究过 commitLocked 方法,回顾下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class PackageInstallerSession extends IPackageInstallerSession.Stub {
... ...

private final PackageManagerService mPm;

private void commitLocked() throws PackageManagerException {
... ...

mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
mInstallerPackageName, mInstallerUid, user, mCertificates);
}
... ...
}

2.2 installStage

接下来正式进入 PMS 源码的分析流程,看看 installStage 方法:

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
public class PackageManagerService extends IPackageManager.Stub implements PackageSender {

... ...

final PackageHandler mHandler;

void installStage(String packageName, File stagedDir, String stagedCid,
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user,
Certificate[][] certificates) {

... ...

// 创建类型为 INIT_COPY 的消息
final Message msg = mHandler.obtainMessage(INIT_COPY);
final int installReason = fixUpInstallReason(installerPackageName, installerUid,
sessionParams.installReason);
// 创建 InstallParams,它对应于包的安装数据
final InstallParams params = new InstallParams(origin, null, observer,
sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
verificationInfo, user, sessionParams.abiOverride,
sessionParams.grantedRuntimePermissions, certificates, installReason);
params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
... ...

// 将 InstallParams 通过消息发送出去
mHandler.sendMessage(msg);
}
... ...
}

2.3 PackageHandler

因为 PackageHandler 继承 Handler ,所以我们来看下 PackageHandlerHandlerMessage 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class PackageManagerService extends IPackageManager.Stub implements PackageSender {

... ...

class PackageHandler extends Handler {
... ...

public void handleMessage(Message msg) {
try {
doHandleMessage(msg);
} finally {
// 设置了线程的优先级为后台线程
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
}

... ...
}
... ...
}

2.3.1 INIT_COPY

接下来看下 INIT_COPY 消息的处理流程:

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
public class PackageManagerService extends IPackageManager.Stub implements PackageSender {
... ...

class PackageHandler extends Handler {
private boolean mBound = false;
final ArrayList<HandlerParams> mPendingInstalls = new ArrayList<HandlerParams>();
... ...

// 用于处理各个类型的消息
void doHandleMessage(Message msg) {
switch (msg.what) {
... ...

case INIT_COPY: {
// 取出 InstallParams,继承自 HandlerParams,
HandlerParams params = (HandlerParams) msg.obj;
// idx 为当前需要安装的 APK 个数,mPendingInstalls 里面保存所有需要
// 安装的 APK 解析出来的 HandlerParams 参数
int idx = mPendingInstalls.size();

// mBound 用于标识是否绑定了服务(DefaultContainerService),如果已经绑定了,
// 则 mBound 为true,如果是第一次调用 mBound 为 false,默认值为 false
if (!mBound) {
// 如果没有绑定服务,则进行绑定工作,connectToService 方法内部进行绑定,
// 如果绑定成功会将 mBound 置为 true
if (!connectToService()) {
... ... // 错误输出
// 绑定服务失败则 return
return;
} else {
// 绑定服务成功,将请求添加到 ArrayList 类型的 mPendingInstalls 中,
// 等待处理
mPendingInstalls.add(idx, params);
}
} else {
// 已经绑定服务
mPendingInstalls.add(idx, params);
if (idx == 0) {
// 如果是第一个安装请求,则直接发送事件 MCS_BOUND 触发处理流程
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
break;
}
... ...
}
... ...
}
... ...
}

2.3.2 connectToService

假设我们是第一次走流程,还没有绑定服务,则会调用 connectToService() 方法,看下流程:

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
public class PackageManagerService extends IPackageManager.Stub implements PackageSender {

... ...

class PackageHandler extends Handler {
private boolean connectToService() {
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
/**
* bindServiceAsUser 方法会传入 mDefContainerConn,
* bindServiceAsUser 方法的处理逻辑和我们调用 bindService 是类似的,
* 服务建立连接后,会调用 onServiceConnected
*/
if (mContext.bindServiceAsUser(service, mDefContainerConn,
Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 如果绑定 DefaultContainerService 成功,mBound 会置为 ture
mBound = true;
return true;
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return false;
}
... ...
}
... ...
}

这里可以看到 bind 了一个 service ,这个 serviceComponentName"DEFAULT_CONTAINER_COMPONENT" 这个常量,那我们就来看下这个 ComponentName 是什么?

1
2
3
static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
DEFAULT_CONTAINER_PACKAGE,
"com.android.defcontainer.DefaultContainerService");

现在我们知道 bind 的 service 是 DefaultContainerService 。绑定 DefaultContainerService 之后,设定进程的优先级为 THREAD_PRIORITY_DEFAULT

然后等 bindServiceAsUser 这个方法执行完则又把线程的优先级设为 THREAD_PRIORITY_BACKGROUND

这边要重点提到一个 mDefContainerConn 变量,研究一下:

1
final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();

mDefContainerConn 的类型是 DefaultContainerConnection ,那我们来看下 DefaultContainerConnection 这个类。

2.3.3 DefaultContainerConnection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class PackageManagerService extends IPackageManager.Stub implements PackageSender {

... ...

// DefaultContainerConnection 实现了 ServiceConnection,
// 所以在连接成功的时候会调用 onServiceConnected 方法
class DefaultContainerConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
final IMediaContainerService imcs = IMediaContainerService.Stub
.asInterface(Binder.allowBlocking(service));
// 发送了 MCS_BOUND 类型的消息
mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
}

public void onServiceDisconnected(ComponentName name) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
}
}
... ...
}

上文提及到 mContext.bindServiceAsUser() 方法,其实就是"绑定" DefaultContainerService。我们知道 bind 一个 Service ,其中负责通信的是 ServiceConnection,所以,本方法中负责通信的就是 mDefContainerConn

一旦绑定成功会执行 mDefContainerConnonServiceConnected 方法。

从源码中可以看到:当绑定成功后在 onServiceConnected 中将一个 IBinder 转换成了一个 IMediaContainerService

这个就是 onServiceConnected 回调函数中根据参数传进来的 IMediaContainerService.Stub 的对象引用创建的一个远程代理对象,后面 PacakgeManagerService 将通过该代理对象访问 DefaultContainerService 服务。

2.3.4 小结

我们简单梳理一下以上代码所做的工作:

        ✨ (1)mBound 用于标识是否绑定了 DefaultContainerService,默认值为 false

        ✨ (2)DefaultContainerService 是用于检查和复制可移动文件的服务,这是一个比较耗时的操作,因此 DefaultContainerService 没有和 PackageManagerService 运行在同一进程中,它运行在 com.android.defcontainer 进程,通过 IMediaContainerServicePackageManagerService 进行 IPC 通信。

彼此之间的 IPC 通信如下图所示:

![IPC 通信原理图.png](https://upload-images.jianshu.io/upload_images/3517194-fc2af2fd9ed961c5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/680)

        ✨ (3)connectToService 方法用来绑定 DefaultContainerService

        ✨ (4)mHandler.sendEmptyMessage(MCS_BOUND):发送 MCS_BOUND 类型的消息,触发处理第一个安装请求

不知道你是否发现,在我们上面研究的源码中,有两处发送了 MCS_BOUND 类型消息的方法:

1
2
3
4
5
// PackageHandler.doHandleMessage(已绑定服务)
mHandler.sendEmptyMessage(MCS_BOUND); // 不带参数

// DefaultContainerConnection(未绑定服务 - 绑定服务)
mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs)); // 带参数

2.4 MCS_BOUND 分析

接下来,我们重点讨论这两个方法!

2.4.1 不带参数

首先研究不带参数的源码:

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
class PackageHandler extends Handler {
... ...

void doHandleMessage(Message msg) {
switch (msg.what) {
... ...

case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
// 不带参数,则此条件不满足
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
System.identityHashCode(mHandler));
}

// 走这边的逻辑
if (mContainerService == null) {
// 服务没有绑定,则走这边,但是之前我们讲解过,发送不带参数的 MCS_BOUND 时,
// 已经绑定了服务,这显然是不正常的
if (!mBound) {
... ...

for (HandlerParams params : mPendingInstalls) {
... ... // 负责处理服务发生错误的情况
}
// 绑定失败,清空安装请求队列
mPendingInstalls.clear();
} else {
// 继续等待绑定服务
Slog.w(TAG, "Waiting to connect to media container service");
}
} else if (mPendingInstalls.size() > 0) {
... ...
} else {
// Should never happen ideally.
Slog.w(TAG, "Empty queue");
}
break;
}
... ...
}

2.4.2 带参数

接下来研究不带参数的源码:

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
class PackageHandler extends Handler {
... ...

void doHandleMessage(Message msg) {
switch (msg.what) {
... ...

case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
System.identityHashCode(mHandler));
}
// 带参数,此条件不满足
if (mContainerService == null) {
... ...
// 走这边的逻辑,安装请求队列不为空
} else if (mPendingInstalls.size() > 0) {
// 得到安装请求队列第一个请求 HandlerParams
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
// 如果 HandlerParams 不为 null 就会调用 HandlerParams 的
// startCopy 方法,用于开始复制 APK 的流程
if (params.startCopy()) {
// 如果 APK 安装成功,删除本次安装请求
if (mPendingInstalls.size() > 0) {
mPendingInstalls.remove(0);
}
if (mPendingInstalls.size() == 0) {
if (mBound) {
// 如果没有安装请求了,发送解绑服务的请求
removeMessages(MCS_UNBIND);
Message ubmsg = obtainMessage(MCS_UNBIND);
sendMessageDelayed(ubmsg, 10000);
}
} else {
// 如果还有其他的安装请求,接着发送 MCS_BOUND 消息
// 继续处理剩余的安装请求
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
// 如果安装请求数不大于 0 就会打印 “Empty queue”
} else {
// Should never happen ideally.
Slog.w(TAG, "Empty queue");
}
break;
}
... ...
}

上面的流程其实很简单,我们根据是否传入了 ims 这个参数,走两条流程,核心的方法就是最终的 startCopy()

三、复制 APK

上面我们提过,复制 APK 的操作是调用 HandlerParamsstartCopy 方法。HandlerParams 是 PMS 中的抽象类,它的实现类为 PMS 的内部类 InstallParams

3.1 startCopy

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
public class PackageManagerService extends IPackageManager.Stub implements PackageSender {
... ...

private abstract class HandlerParams {
private static final int MAX_RETRIES = 4;

/**
* Number of times startCopy() has been attempted and had a non-fatal
* error.
*/
private int mRetries = 0;
... ...

final boolean startCopy() {
boolean res;
try {
/**
* mRetries 用于记录 startCopy 方法调用的次数,调用 startCopy 方法时会先自动加 1
* startCopy 方法尝试的次数超过了 4 次,就放弃这个安装请求
*/
if (++mRetries > MAX_RETRIES) {
// 发送 MCS_GIVE_UP 类型消息,将第一个安装请求(本次安装请求)从安装请求队列
// mPendingInstalls 中移除掉
mHandler.sendEmptyMessage(MCS_GIVE_UP); // Message 1
handleServiceError();
return false;
} else {
handleStartCopy(); // 💥 💥 💥 重点方法 💥 💥 💥
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT); // Message 2
res = false;
}
// 调用 handleReturnCode 抽象方法,这个方法会在 handleStartCopy 执行完拷贝相关行为之后,
// 根据 handleStartCopy 做进一步的处理,主要返回状态码
handleReturnCode();
return res;
}
... ...

abstract void handleStartCopy() throws RemoteException;
abstract void handleServiceError();
abstract void handleReturnCode();
}
... ...
}

我们在分析 handleStartCopy() 之前,简单的看一下 MCS_GIVE_UPMCS_RECONNECT 两种 message 的处理流程,逻辑相当简单!

MCS_RECONNECT

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
class PackageHandler extends Handler {
... ...

void doHandleMessage(Message msg) {
switch (msg.what) {
... ...

case MCS_RECONNECT: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_reconnect");
if (mPendingInstalls.size() > 0) {
if (mBound) {
disconnectService();
}
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
for (HandlerParams params : mPendingInstalls) {
// Indicate service bind error
params.serviceError();
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(params));
}
mPendingInstalls.clear();
}
}
break;
}
... ...
}
}
}

判断安装请求队列 mPendingInstalls 是否还有元素,如果有元素先断开绑定,则再次重新调用 connectToService 方法,我们知道 connectToService() 内部会再次执行绑定 DefaultContainerService,而在绑定成功后会再次发送一个 what 值为 MCS_BOUNDMessage,从而又回到了 startCopy 里面。

3.1.2 MCS_GIVE_UP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class PackageHandler extends Handler {
... ...

void doHandleMessage(Message msg) {
switch (msg.what) {
... ...

case MCS_GIVE_UP: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_giveup too many retries");
HandlerParams params = mPendingInstalls.remove(0);
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(params));
break;
}
}
}
}

直接删除了安装请求队列 mPendingInstalls 里面下标为 0 的元素,即取消本次安装请求。

3.2 handleStartCopy

我们发现 handleStartCopy 也是一个抽象方法,那么它在哪实现?前面我们说过:HandlerParamsPMS 中的抽象类,它的实现类为 PMS 的内部类 InstallParams

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
79
80
81
82
83
84
85
86
87
88
89
90
91
public class PackageManagerService extends IPackageManager.Stub implements PackageSender {
... ...

class InstallParams extends HandlerParams {
... ...

public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
... ...

/**
* 确定 APK 的安装位置
* onSd: 安装到 SD 卡
* onInt: 内部存储即 Data 分区
* ephemeral:安装到临时存储
*/
final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
PackageInfoLite pkgLite = null;

// APK 不能同时安装在 SD 卡和 Data 分区
if (onInt && onSd) {
// Check if both bits are set.
Slog.w(TAG, "Conflicting flags specified for installing ...");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
// 安装标志冲突,Instant Apps 不能安装到 SD 卡中
} else if (onSd && ephemeral) {
Slog.w(TAG, "Conflicting flags specified for installing ...");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
/**
* 获取 APK 的少量的信息
* 通过 IMediaContainerService 跨进程调用 DefaultContainerService 的
* getMinimalPackageInfo 方法,该方法轻量解析 APK 并得到 APK 的少量信息,
* 轻量解析的原因是这里不需要得到 APK 的全部信息,APK 的少量信息会封装到
* PackageInfoLite 中。
*/
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
installFlags, packageAbiOverride);

if (DEBUG_EPHEMERAL && ephemeral) {
Slog.v(TAG, "pkgLite for install: " + pkgLite);
}
... ..
}
if (ret == PackageManager.INSTALL_SUCCEEDED) {
// 判断安装的位置
int loc = pkgLite.recommendedInstallLocation;
if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
... ...
} else {
installFlags = sPmsExt.customizeInstallPkgFlags(installFlags, pkgLite,
mSettings.mPackages, getUser());
loc = installLocationPolicy(pkgLite);
... ...
}
}

/**
* 根据 InstallParams 创建 InstallArgs 对象
* InstallArgs 是一个抽象类,定义了 APK 的安装逻辑,比如"复制"和"重命名" APK 等
* InstallArgs 有 3 个子类,都被定义在 PMS 中:
* FileInstallArgs:用于处理安装到非 ASEC 的存储空间的 APK ,也就是内部存储空间
* AsecInstallArgs:用于处理安装到 ASEC 中(mnt/asec)即 SD 卡中的 APK
* MoveInstallArgs:用于处理已安装 APK 移动的逻辑
*/
final InstallArgs args = createInstallArgs(this);
mArgs = args;

if (ret == PackageManager.INSTALL_SUCCEEDED) {
... ...

if (!origin.existing && requiredUid != -1
&& isVerificationEnabled(
verifierUser.getIdentifier(), installFlags, installerUid)) {
... ...
} else {
// 对 APK 进行检查后就会调用 InstallArgs 的 copyApk 方法进行安装
ret = args.copyApk(mContainerService, true);
}
}

mRet = ret;
}
... ...
}
... ...
}

3.3 FileInstallArgs

现在,我们知道 InstallParams 有三个子类,不同的 InstallArgs 子类会有着不同的处理,那我们现在以 FileInstallArgs 为例跟踪研究具体的流程:

3.3.1 copyApk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class PackageManagerService extends IPackageManager.Stub implements PackageSender {
... ...

class FileInstallArgs extends InstallArgs {
private File codeFile;
private File resourceFile;

... ...

int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
try {
return doCopyApk(imcs, temp); // 调转 doCopyApk 方法
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
... ...
}
... ...
}

3.3.2 doCopyApk

调用了 doCopyApk 方法:

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
class FileInstallArgs extends InstallArgs {
private File codeFile;
private File resourceFile;

... ...

private int doCopyApk(IMediaContainerService imcs, boolean temp)
throws RemoteException {
... ...

try {
final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
// 用于创建临时存储目录,比如 /data/app/vmdl18300388.tmp,
// 其中 18300388 是安装的 sessionId
final File tempDir =
mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
Slog.w(TAG, "Failed to create copy file: " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}

... ...

int ret = PackageManager.INSTALL_SUCCEEDED;
/**
* 通过 IMediaContainerService 跨进程调用 DefaultContainerService 的 copyPackage 方法,
* 这个方法会在 DefaultContainerService 所在的进程中将 APK 复制到临时存储目录,
* 比如 /data/app/vmdl18300388.tmp/base.apk 。
*/
ret = imcs.copyPackage(origin.file.getAbsolutePath(), target); // 真正的文件拷贝
... ...

return ret;
}
... ...
}

四、安装 APK

4.1 handleReturnCode

我们回到 APK 的复制调用链的头部方法:HandlerParams 的 startCopy 方法,在最后 调用了 handleReturnCode 方法,进行 APK 的安装。

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
private abstract class HandlerParams {
private static final int MAX_RETRIES = 4;

private int mRetries = 0;
... ...

final boolean startCopy() {
boolean res;
try {
if (++mRetries > MAX_RETRIES) {
... ...
} else {
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
... ...
}
// 处理复制 APK 后的安装 APK 逻辑
handleReturnCode();
return res;
}
... ...

abstract void handleReturnCode();
}

handleReturnCode 也是一个抽象方法,那么在哪里实现?同样,它的实现在 InstallParams 中。

1
2
3
4
5
6
7
8
9
10
@Override
void handleReturnCode() {
// If mArgs is null, then MCS couldn't be reached. When it
// reconnects, it will try again to install. At that point, this
// will succeed.
if (mArgs != null) {
// "装载代码"的入口是 processPendingInstall(InstallArgs,int) 方法
processPendingInstall(mArgs, mRet);
}
}

我们发现调用了 processPendingInstall 方法,继续跟!

4.2 processPendingInstall

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
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
PackageInstalledInfo res = new PackageInstalledInfo();
res.setReturnCode(currentStatus);
res.uid = -1;
res.pkg = null;
res.removedInfo = null;
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
/**
* 安装前处理
* 用于检查 APK 的状态的,在安装前确保安装环境的可靠,如果不可靠会清除复制的 APK 文件
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageTracedLI(args, res);
}
/**
* 安装后收尾
* 用于处理安装后的收尾操作,如果安装不成功,删除掉安装相关的目录与文件
args.doPostInstall(res.returnCode, res.uid);
}
... ...
}
});
}

4.3 installPackageTracedLI

1
2
3
4
5
6
7
8
private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage");
installPackageLI(args, res);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}

4.4 installPackageLI

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
... ...

PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);

Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final PackageParser.Package pkg;
try {
// 解析 APK
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
} catch (PackageParserException e) {
res.setError("Failed parse during installPackageLI", e);
return;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
... ...

// Get rid of all references to package scan path via parser.
pp = null;
String oldCodePath = null;
boolean systemApp = false;
synchronized (mPackages) {
// 检查 APK 是否存在
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
// 获取没被改名前的包名
String oldName = mSettings.getRenamedPackageLPr(pkgName);
if (pkg.mOriginalPackages != null
&& pkg.mOriginalPackages.contains(oldName)
&& mPackages.containsKey(oldName)) {
pkg.setPackageName(oldName);
pkgName = pkg.packageName;
// 设置标志位表示是替换安装
replace = true;
if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="
+ oldName + " pkgName=" + pkgName);
}
... ...
}

PackageSetting ps = mSettings.mPackages.get(pkgName);
// 查看 Settings 中是否存有要安装的 APK 的信息,如果有就获取签名信息
if (ps != null) {
if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);

PackageSetting signatureCheckPs = ps;
if (pkg.applicationInfo.isStaticSharedLibrary()) {
SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
if (libraryEntry != null) {
signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
}
}

// 检查签名的正确性
if (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) {
if (!checkUpgradeKeySetLP(signatureCheckPs, pkg)) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
return;
}
}
... ...
}

int N = pkg.permissions.size();
for (int i = N-1; i >= 0; i--) {
// 遍历每个权限,对权限进行处理
PackageParser.Permission perm = pkg.permissions.get(i);
BasePermission bp = mSettings.mPermissions.get(perm.info.name);
... ...
}
}

if (systemApp || sPmsExt.isOperatorApp(mPackages, mSettings.mPackages, pkgName)) {
if (onExternal) {
// 系统APP不能在SD卡上替换安装
res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Cannot install updates to system apps on sdcard");
return;
} else if (instantApp) {
// 系统 APP 不能被 Instant App 替换
res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
"Cannot update a system app with an instant app");
return;
}
}

... ...

// 重命名临时文件
if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
return;
}

if (!instantApp) {
startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
} else {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);
}
}

try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
"installPackageLI")) {
if (replace) {
// 替换安装
if (pkg.applicationInfo.isStaticSharedLibrary()) {
PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
if (existingPkg != null && existingPkg.mVersionCode != pkg.mVersionCode) {
res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
+ "static-shared libs cannot be updated");
return;
}
}
replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
installerPackageName, res, args.installReason);
} else {
// 安装新的 APK
installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
args.user, installerPackageName, volumeUuid, res, args.installReason);
}
}

// 更新应用程序所属的用户
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
ps.setUpdateAvailable(false /*updateAvailable*/);
}
... ...
}
}

installPackageLI 方法的代码很长,这里截取主要的部分,主要做了几件事:

        ✒ 1、创建 PackageParser 解析 APK 。
        ✒ 2、检查 APK 是否存在,如果存在就获取此前没被改名前的包名,赋值给 PackageParser.Package 类型的 pkg ,将标志位 replace 置为 true 表示是替换安装。
        ✒ 3、如果 Settings 中保存有要安装的 APK 的信息,说明此前安装过该 APK ,则需要校验 APK 的签名信息,确保安全的进行替换。
        ✒ 4、将临时文件重新命名,比如前面提到的 /data/app/vmdl18300388.tmp/base.apk ,重命名为 /data/app/包名-1/base.apk 。这个新命名的包名会带上一个数字后缀 1,每次升级一个已有的 App ,这个数字会不断的累加。
        ✒ 5、系统 APP 的更新安装会有两个限制,一个是系统 APP 不能在 SD 卡上替换安装,另一个是系统 APP 不能被 Instant App 替换。
        ✒ 6、根据 replace 来做区分,如果是替换安装就会调用 replacePackageLIF 方法,其方法内部还会对系统 APP 和非系统 APP 进行区分处理,如果是新安装 APK 会调用 installNewPackageLIF 方法。

4.5 installNewPackageLIF

我们以安装新 APK 为例,查看 installNewPackageLIF 的源码:

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
/*
* Install a non-existing package.
*/
private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags,
int scanFlags, UserHandle user, String installerPackageName, String volumeUuid,
PackageInstalledInfo res, int installReason) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage");

... ...

try {
// 扫描 APK
PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags,
System.currentTimeMillis(), user);

// 更新 Settings 信息
updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);

if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
// 安装成功后,为新安装的应用程序准备数据
prepareAppDataAfterInstallLIF(newPackage);

} else {
// 安装失败则删除 APK
deletePackageLIF(pkgName, UserHandle.ALL, false, null,
PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
}
} catch (PackageManagerException e) {
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}

Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}

installNewPackageLIF 主要做了以下 3 件事:

        ✒ 1、扫描 APK,将 APK 的信息存储在 PackageParser.Package 类型的 newPackage 中,一个 Package 的信息包含了 1 个 base APK 以及 0 个或者多个 split APK 。
        ✒ 2、更新该 APK 对应的 Settings 信息,Settings 用于保存所有包的动态设置。
        ✒ 3、如果安装成功就为新安装的应用程序准备数据,安装失败就删除APK。

4.6 scanPackageTracedLI

调用 scanPackageTracedLI() 进行安装 :

1
2
3
4
5
6
7
8
9
public PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
try {
return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}

4.7 scanPackageLI - 01

scanPackageTracedLI() 调用了 scanPackageLI() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Scans a package and returns the newly parsed package.
* Returns {@code null} in case of errors and the error code is stored in mLastScanError
*/
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);
... ...

return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
}

4.8 scanPackageLI - 02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, final int policyFlags,
int scanFlags, long currentTime, @Nullable UserHandle user)
throws PackageManagerException {
boolean success = false;
try {
// scanPackageDirtyLI 实际安装 package 的方法
final PackageParser.Package res = scanPackageDirtyLI(pkg, policyFlags, scanFlags,
currentTime, user);
success = true;
return res;
} finally {
if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
// DELETE_DATA_ON_FAILURES is only used by frozen paths
destroyAppDataLIF(pkg, UserHandle.USER_ALL,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
}
}
}

最终调用 scanPackageLI 方法!

五、总结

本文主要讲解了 PMS 是如何处理 APK 安装的流程,主要有几个步骤:

        ✒ PackageInstaller 安装 APK 时会将 APK 的信息交由 PMS 处理,PMS 通过向 PackageHandler 发送消息来驱动 APK 的复制和安装工作。
        ✒ PMS 发送 INIT_COPY 和 MCS_BOUND 类型的消息,控制 PackageHandler 来绑定 DefaultContainerService ,完成复制 APK 等工作。
        ✒ 复制 APK 完成后,会开始进行安装 APK 的流程,包括安装前的检查、安装 APK 和安装后的收尾工作。