0%

通过adb install 安装apk

1. ADB处理的流程

通过adb install命令安装指定apk时,会先将该apk上传到/data/local/tmp或者/sdcard/tmp目录下,然后调用pm脚本,通过Binder通信,调用PackageManagerService的接口安装apk。
通过PackageInstaller.apk安装指定apk时,最终也会调用PackageManagerService的接口实现apk的安装。
以上两种情况最终都会发送INIT-COPY消息,由PackageManagerService启动过程中初始化的mHandler的handleMessage方法处理该消息,开始apk的安装。

1.1. adb 拷贝流程分析

adb 安装apk支持的命令

1
2
3
4
5
6
7
8
"  adb install [-lrtsdg] <file>\n"
" - push this package file to the device and install it\n"
" (-l: forward lock application)\n" // foward_lock
" (-r: replace existing application)\n" // upgrade apk
" (-t: allow test packages)\n"
" (-s: install application on sdcard)\n" // apptosd
" (-d: allow version code downgrade (debuggable packages only))\n" // downgrade
" (-g: grant all runtime permissions)\n" // 赋予所有的运行时权限
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
static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
static const char *const DATA_DEST = "/data/local/tmp/%s";
static const char *const SD_DEST = "/sdcard/tmp/%s";
const char* where = DATA_DEST;
int i;
struct stat sb;

for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-s")) {
where = SD_DEST;
}
}
...
int result = -1;
std::vector<const char*> apk_file = {argv[last_apk]};
std::string apk_dest = android::base::StringPrintf(
where, adb_basename(argv[last_apk]).c_str());
// adb 将pc上的apk包拷贝到手机上的 DATA_DEST/<filename>.apk 或 SD_DEST/<filename>.apk
if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
// 调用pm cmd 进行处理
result = pm_command(transport, serial, argc, argv);
cleanup_apk:
// 清理拷贝到手机目录上的apk
delete_file(transport, serial, apk_dest);
return result;
}

发送shell消息到终端, 使用pm install 命令安装对应的apk.期间,如果出现adb connect连接问题,则会进行重连.如果能够发送给终端执行对应的shell命令.等待执行的结果,并通过adb打印到电脑的控制台上.

1.2. pm执行安装流程

生成system/bin/pm 可执行文件

传入参数.执行 runInstall 函数

在runInstall函数中将shell传过来的pm命令进行解析,并添加到SessionParams的相应flag中

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
private int runInstall() throws RemoteException {
// 对参数进行转换, 初始化SessionParams
final InstallParams params = makeInstallParams();
final String inPath = nextArg();
// 创建 PackageInstallerSession 对象,并加到mSessions 以 sessionId加到集合中,同时更新/data/system/install_sessions.xml文件
final int sessionId = doCreateSession(params.sessionParams,
params.installerPackageName, params.userId);
try {
if (inPath == null && params.sessionParams.sizeBytes == -1) {
System.err.println("Error: must either specify a package size or an APK file");
return 1;
}
// 从之前拷贝到的地址中 DATA_DEST|SD_DEST 拷贝到 /data/app/vml<sessionid>.tmp下|sd卡相关的mnt/secure/asec?? 下的base.apk中
if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
return 1;
}
// 提交sessionId 对应的 PackageInstaller.Session对象,通过session.commit层层传递,最终通过PKMS的installStage函数对apk进行安装
if (doCommitSession(sessionId, false /*logSuccess*/)
!= PackageInstaller.STATUS_SUCCESS) {
return 1;
}
System.out.println("Success");
return 0;
} finally {
try {
mInstaller.abandonSession(sessionId);
} catch (Exception ignore) {
}
}
}

private InstallParams makeInstallParams() {
//初始化SessionParams和InstallParams,赋值相应参数
final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL);
final InstallParams params = new InstallParams();
params.sessionParams = sessionParams;
String opt;
while ((opt = nextOption()) != null) {
switch (opt) {
case "-l":
sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
break;
case "-r":
sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
break;
case "-s":
sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL;
break;
case "-f":
sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
break;
case "-g":
sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
break;
...
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
}
return params;
}

/data/system/install_sessions.xml

1
2
3
4
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<sessions>
<session sessionId="667330637" userId="0" installerUid="0" createdMillis="1325393932643" sessionStageDir="/data/app/vmdl667330637.tmp" prepared="true" sealed="true" mode="1" installFlags="112" installLocation="1" sizeBytes="-1" originatingUid="-1" />
</sessions>

下一步主要涉及到doCommitSession的处理

在PackageInstallerSession的commitLocked函数中,最终调到了PKMS的installStage方法进行应用的安装

1
2
3
4
5
6
7
8
9
10
11
12
final OriginInfo origin;
if (stagedDir != null) {
origin = OriginInfo.fromStagedFile(stagedDir);
} else {
origin = OriginInfo.fromStagedContainer(stagedCid);
}
final Message msg = mHandler.obtainMessage(INIT_COPY);
final InstallParams params = new InstallParams(origin, null, observer,
sessionParams.installFlags, installerPackageName /*安装器*/, sessionParams.volumeUuid,
verificationInfo, user, sessionParams.abiOverride,
sessionParams.grantedRuntimePermissions, certificates);
mHandler.sendMessage(msg);

2. 安装流程

2.1. 从INIT_COPY开始

首先,获取消息的保存的InstallParams对,然后判断当前是否绑定到DefaultContainerService服务,

没有绑定时,调用mHandler.connectToService方法绑定:绑定失败,直接退出;成功,将安装请求信息添加到mHandler.mPendingInstalls(存储等待安装的apk请求信息)中,稍候会处理第一个安装请求。初次绑定并成功后,会调用PackageManageService.mDefContainerConn的回调方法onServiceConnected,获取服务代理对象,并发送一个MCS-BOUND消息

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
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
int idx = mPendingInstalls.size();
if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
// If a bind was already initiated we dont really
// need to do anything. The pending install
// will be processed later on.
if (!mBound) {
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
System.identityHashCode(mHandler));
// If this is the only one pending we might
// have to bind to the service again.
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
params.serviceError();
return;
} else {
// Once we bind to the service, the first
// pending request will be processed.
mPendingInstalls.add(idx, params);
}
}
else {
mPendingInstalls.add(idx, params);
// Already bound to the service. Just make
// sure we trigger off processing the first request.
if (idx == 0) {
// 如果是第一个绑定的,需要发送MCS_BOUND消息
mHandler.sendEmptyMessage(MCS_BOUND消息);
}
}
}


private boolean connectToService() {
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
// 绑定 DefaultContainerService服务
if (mContext.bindServiceAsUser(service, mDefContainerConn,
Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mBound = true;
return true;
}
return false;
}

final private DefaultContainerConnection mDefContainerConn =
new DefaultContainerConnection();
class DefaultContainerConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
IMediaContainerService imcs =
IMediaContainerService.Stub.asInterface(service);
// 绑定成功,发送MCS_BOUND消息
mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
}
}

只有在没有绑定进行绑定时或已绑定且当前是第一个安装请求时才会发送MCS_BOUND空消息,接下来开始处理该消息。

2.2. 处理MCS_BOUND消息

首先要保存服务代理对象到PackageManagerService.mContainerService中。

如果是空消息,说明已经绑定服务,代理对象已保存,即msg.obj=null的情况

判断当前服务代理对象是否为null:如果为null,说明没有绑定服务,处理出错信息,清空所有安装请求;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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) {
if (!mBound) {
// Something seriously wrong since we are not bound and we are not
// waiting for connection. Bail out.
Slog.e(TAG, "Cannot bind to media container service");
for (HandlerParams params : mPendingInstalls) {
// Indicate service bind error
...
}
// 清空所有安装请求
mPendingInstalls.clear();
} else {
Slog.w(TAG, "Waiting to connect to media container service");
}
}

已绑定,处理第一个安装请求

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
else if (mPendingInstalls.size() > 0) {
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
// 调用InstallParams.startCopy方法进行复制安装,复制安装成功后,继续以下两步;
if (params.startCopy()) {
// We are done... look for more work or to
// go idle.
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Checking for more work or unbind...");
// Delete pending install
// 从mHandler.mPendingInstalls中移除该安装请求信息;
if (mPendingInstalls.size() > 0) {
mPendingInstalls.remove(0);
}
// 判断是否还有安装请求:没有了,如果当前绑定服务,移除前一个安装请求的MCS_UNBOUND消息,然后重新发送一个10s的延时消息MCS_UNBOUND,
// 用于解除服务绑定;如果还有安装请求,发送一个空的MCS_BOUND消息,用于处理下一个安装请求。
if (mPendingInstalls.size() == 0) {
if (mBound) {
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Posting delayed MCS_UNBIND");
removeMessages(MCS_UNBIND);
Message ubmsg = obtainMessage(MCS_UNBIND);
// Unbind after a little delay, to avoid
// continual thrashing.
sendMessageDelayed(ubmsg, 10000);
}
} else {
// There are more pending requests in queue.
// Just post MCS_BOUND message to trigger processing
// of next pending install.
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Posting MCS_BOUND for next work");
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
}

1 只有当安装请求队列处理到最后,后面没有安装请求时,才会发送MCS_UNBOUND消息;

2.当来了新的安装请求,而消息队列中还存在前一个安装请求的MCS_UNBOUND消息时,可能存在一种情况,就是在初始化安装时是存在绑定服务的,但是在安装过程中,由于前一个请求的MCS_UNBOUND消息,导致服务解绑,出现安装错误

判断每个apk尝试安装的次数,每个apk最多尝试安装4次,均失败,发送MCS_GIVE_UP消息,返回false;否则调用InstallParams.handleStartCopy方法进行复制安装,复制安装成功返回true,若失败,抛出并处理RemoteException,发送MCS_RECONNECTION消息,重新尝试安装。最后调用InstallParams.handleReturnCode方法处理返回结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
final boolean startCopy() {
boolean res;
try {

if (++mRetries > MAX_RETRIES) {
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {
// 进行复制安装
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
handleReturnCode();
return res;
}

2.3. 调用InstallParams.handleStartCopy方法复制apk

该方法主要根据安装位置(默认installFlags既没指定onSd,也没指定onInt),复制资源文件到内部/外部存储中,或者先验证包然后在CHECK_PENDING_VERIFICATION消息处理分支实现复制和安装。

1
2
3
4
5
6
7
8
9
10
11
12
// 已经缓存了文件, 根据stage的类型重新确定安装的标志
if (origin.staged) {
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
installFlags &= ~PackageManager.INSTALL_EXTERNAL;
} else if (origin.cid != null) {
installFlags |= PackageManager.INSTALL_EXTERNAL;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else {
throw new IllegalStateException("Invalid stage location");
}
}

2.3.1. 计算推荐安装位置

如果安装位置标志既设置了内部标志又设置了外部标志,记录返回结果INSTALL_FAILED_INVALID_INSTALL_LOCATION到InstallParams.mRet中;

否则,根据指定安装位置(内部存储/外部存储),调用mContainerService.getMinimalPackageInfo方法,解析AndroidManifest.xml文按,获取包名、installLocation、package-verifier等信息,并计算存储区域的推荐安装位置,将这些信息存储到PackageInfoLite结构中:

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
else {
// 计算推荐安装位置
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
packageAbiOverride);
// 没有缓存文件的情况,需要计算缓存文件释放后空间是否足够
if (!origin.staged && pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
// TODO: focus freeing disk space on the target device
final StorageManager storage = StorageManager.from(mContext);
final long lowThreshold = storage.getStorageLowBytes(
Environment.getDataDirectory());

final long sizeBytes = mContainerService.calculateInstalledSize(
origin.resolvedPath, isForwardLocked(), packageAbiOverride);

try {
// 清空/data/user/<user_id>/
// /data/user_de/<user_id>
// /data/media/<user_id>/Android/data/
// cache code_cache 下的文件,直到删除到满足要求为止
mInstaller.freeCache(null, sizeBytes + lowThreshold);
// 重新计算空间是否足够
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
installFlags, packageAbiOverride);
} catch (InstallerException e) {
Slog.w(TAG, "Failed to free cache", e);
}

/*
* The cache free must have deleted the file we
* downloaded to install.
*
* TODO: fix the "freeCache" call to not delete
* the file we care about.
*/
if (pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
pkgLite.recommendedInstallLocation
= PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
}
}

没有缓存文件时,因为要拷贝缓存文件到/data/app/***下,所以需要计算所需的空间大小,算出来后,通过installd的freecache函数清除缓存文件来释放空间.释放完之后重新计算安装位置是否满足空间要求.

此处涉及到判断空间是否满足要求的判断标准,再此重新说明一下:

2.3.2. 空间不足的判断标准

对于内部存储,是通过fitsOnInternal函数进行判定的, 判定标准时 可用空间大小 >= sizeBytes + 默认1M

对于外部存储,是通过fitsOnExternal函数进行判定, 首先判断外部存储是否挂载,再进行判断 可用空间大小 >= sizeBytes + 默认1M

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// sizeBytes是统计的apk的大小,如果forwardLock为true(code和resource分离?)需要计算解压后的大小.同时需要加上其关联的native库的大小
sizeBytes = PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
public static boolean fitsOnInternal(Context context, long sizeBytes) {
final StorageManager storage = context.getSystemService(StorageManager.class);
final File target = Environment.getDataDirectory();
// 即为 path.getUsableSpace() >= getStorageFullBytes(path) + sizeBytes
return (sizeBytes <= storage.getStorageBytesUntilLow(target));
}
public long getStorageBytesUntilLow(File path) {
return path.getUsableSpace() - getStorageFullBytes(path);
}
public long getStorageFullBytes(File path) {
// 此处默认定为1M大小
return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
DEFAULT_FULL_THRESHOLD_BYTES);
}

可用大小可以通过df命令查看

文件系统可以通过指定uid\gid保护对空间进行保护,特权进程访问到的可使用空间比较多,而普通进程看到的可用空间比较少,用以对存储进行保护

2.3.2.1. 文件系统层对存储空间做的保护

board 打开 BOARD_RESERVED_SPACE_ON 开关时

​ data分区 在init checkfs时会预留空间 10M大小(手机上block大小一般为512, 2560*512B=10M),预留给特定的uid gid的进程使用

​ tune2fs -r 2560 /dev/block/platform/../userdata

​ 使用tune2fs -r 指定 固定值count (指定预留的 block count)

也可以通过 tune2fs -m 指定预留block 占总 block 的百分比

查看文件系统的超级块信息:
dumpe2fs -h userdata.img 或 /dev/block/../userdata

  • Reserved block group size:(resv_blocks) 文件系统 free_block 该项必须配置,该项为系统预留值。一般通过make_ext4fs 制作img时配置。
  • Reserved block count: ( ext4_r_blocks_count ) 该项为预留给 特殊进程使用的预留空间。

特权进程调用stat 看到的及可使用的可用空间为 avail = total - resv_blocks
普通进程调用stat看到的及可使用的可用空间为 avail = total - resv_blocks - ext4_r_blocks_count

resv_blocks 是 kernel 中进行指定:

1
resv_clusters = min_t(ext4_fsblk_t, resv_clusters, 4096);

取的分区的 2% 或 16M的最小值

2.3.3. 根据推荐安装位置处理返回结果

当安装位置不冲突时,根据根据推荐安装位置返回值指定相应的返回结果。

存在一种情况,会调用installLocationPolicy进一步处理推荐安装位置,主要是为了防止系统级应用安装到sd卡上等操作,根据推荐安装位置更新安装位置标志

正常情况下,对于安装在内部/外部存储上的应用,一般返回

PackageHelper.RECOMMEND_INSTALL_INTERNAL | PackageHelper.RECOMMEND_INSTALL_EXTERNAL

installLocationPolicy函数对应用的升级\降级情况进行了判断,如果是升级的情况下,升级的应用是system app,是不允许升级后安装到sd卡上的.

如果没有携带-r参数,且应用已经存在,即在PKMS.mPackages中能够查到,则返回应用已存在,跳出安装流程

2.3.4. 根据安装位置创建安装参数InstallArgs

根据InstallParams.mInstallFlags安装位置标志,调用createInstallArgs方法创建InstallArgs:

  • 对于安装在sd卡(内和外sd卡)和FORWARD-LOCKED的apk,创建AsecInstallArgs类型
  • 对于安装在手机内部存储区域的,创建FileInstallArgs类型。并存储到InstallParams.mArgs中。
  • 移动应用的情况下(从内部存储移动到外部存储或反过来的情况), 创建MoveInstallArgs.
1
2
3
4
5
6
7
8
9
10
11
final InstallArgs args = createInstallArgs(this);
mArgs = args;
private InstallArgs createInstallArgs(InstallParams params) {
if (params.move != null) {
return new MoveInstallArgs(params);
} else if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) {
return new AsecInstallArgs(params);
} else {
return new FileInstallArgs(params);
}
}

2.3.5. 验证包(GMS情况下)

在PMS启动阶段最后,如果指定了验证包,可以用它来验证后续需要验证的apk。该包被授予android.Manifest.permission.PACKAGE_VERIFICATION_AGENT权限,它包含能处理Action为ACTION_PACKAGE_NEEDS_VERIFICATION的Intent的组件。将该包名存储到PackageManagerService.mRequiredVerifierPackage中,非GMS版本中一般不会指定该验证包。

跳转

当所需要安装的apk需要验证时,将验证编号和验证状态存入PackageManagerService.mPendingVerification中。

构造验证包的Intent对象

传入验证包的地址, origin对象来自于installStage

1
2
3
4
5
6
7
8
9
10
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
// 传入验证包的地址, origin对象来自于installStage
verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
PACKAGE_MIME_TYPE);
verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Query all live verifiers based on current user state
final List<ResolveInfo> receivers = queryIntentReceiversInternal(verification,
PACKAGE_MIME_TYPE, 0, verifierUser.getIdentifier());

根据该安装包解析到的验证包和receivers,查找匹配该安装包的用来验证的组件信息列表,并向这些组件发送广播;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (sufficientVerifiers != null) {
final int N = sufficientVerifiers.size();
if (N == 0) {
Slog.i(TAG, "Additional verifiers required, but none installed.");
ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
} else {
for (int i = 0; i < N; i++) {
final ComponentName verifierComponent = sufficientVerifiers.get(i);
final Intent sufficientIntent = new Intent(verification);
sufficientIntent.setComponent(verifierComponent);
mContext.sendBroadcastAsUser(sufficientIntent, verifierUser);
}
}
}

然后根据系统指定验证包和receivers查找验证包中匹配的组件。在返回结果为成功安装的前提下,向该组件发送有序广播,该广播也包含接收目标组件广播的广播接收器,继而发送一个CHECK_PENDING_VERIFICATION的延时handler消息(默认为10s的延时消息)。

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
final ComponentName requiredVerifierComponent = matchComponentForVerifier(
mRequiredVerifierPackage, receivers);
if (ret == PackageManager.INSTALL_SUCCEEDED
&& mRequiredVerifierPackage != null) {
Trace.asyncTraceBegin(
TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
/*
* Send the intent to the required verification agent,
* but only start the verification timeout after the
* target BroadcastReceivers have run.
*/
verification.setComponent(requiredVerifierComponent);
mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final Message msg = mHandler
.obtainMessage(CHECK_PENDING_VERIFICATION);
msg.arg1 = verificationId;
mHandler.sendMessageDelayed(msg, getVerificationTimeout());
}
}, null, 0, null, null);

/*
* We don't want the copy to proceed until verification
* succeeds, so null out this field.
*/
// 验证成功后,才进行拷贝安装的流程
mArgs = null;
}

然后将InstallParams.mArgs置为null,保证在验证完成之前不进行apk的复制。在CHECK_PENDING_VERIFICATION消息处理过程中,会发送相关广播,被PMS中定义的验证包接收,然后进行apk的复制和安装,最后会发送一个MCS_UNBOUND消息。

2.3.6. 复制apk和lib文件

1
ret = args.copyApk(mContainerService, true);

根据安装参数args的具体类型,决定执行FIleInstallArgs还是AsecInstallArgs的copyApk方法实现apk复制。

2.3.7. FileInstallArgs.copyApk方法

  • 文件已经存在即base.apk已经拷贝完成的情况下,直接跳过拷贝流程
1
2
3
4
5
6
if (origin.staged) {
if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
codeFile = origin.file;
resourceFile = origin.file;
return PackageManager.INSTALL_SUCCEEDED;
}
  • origin.staged为空时,创建临时目录,执行文件拷贝
  1. 复制apk文件:调用DefaultContainerService的copyPacakge方法,先对apk文件进行解析,然后将要安装的apk文件拷贝到/data/app/vmdl<随机整数>.tmp/base.apk中;如果包含分段的apk,将各个分段apk拷贝到相应的/data/app/vmdl<随机整数>.tmp/split_<splitName[i]>.apk中;
  2. 复制本地库文件:调用NativeLibrariesHelper.copyNativeBinariesWithOverride方法,将apk中的lib文件拷贝到/data/app/ vmdl<随机整数>.tmp/lib/,,,中;

最终将结果返回,保存到InstallParams.mRet中,用于handleReturnCode方法对返回结果进行处理。

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
// 创建临时目录   "vmdl" + sessionId + ".tmp"
final File tempDir =
mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
codeFile = tempDir;
resourceFile = tempDir;

final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
@Override
public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
try {
final File file = new File(codeFile, name);
final FileDescriptor fd = Os.open(file.getAbsolutePath(),
O_RDWR | O_CREAT, 0644);
Os.chmod(file.getAbsolutePath(), 0644);
return new ParcelFileDescriptor(fd);
} catch (ErrnoException e) {
throw new RemoteException("Failed to open: " + e.getMessage());
}
}
};
// 拷贝到base.apk 如果包含split app ,同时需要拷贝split app
ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
...
final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(codeFile);
// 根据架构集,拷贝对应的native lib
// 如拷贝到 **/lib/arm目录下
/*
ABI_TO_INSTRUCTION_SET_MAP.put("armeabi", "arm");
ABI_TO_INSTRUCTION_SET_MAP.put("armeabi-v7a", "arm");
ABI_TO_INSTRUCTION_SET_MAP.put("mips", "mips");
ABI_TO_INSTRUCTION_SET_MAP.put("mips64", "mips64");
ABI_TO_INSTRUCTION_SET_MAP.put("x86", "x86");
ABI_TO_INSTRUCTION_SET_MAP.put("x86_64", "x86_64");
ABI_TO_INSTRUCTION_SET_MAP.put("arm64-v8a", "arm64");
*/
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
abiOverride);
}

private void copyFile(String sourcePath, IParcelFileDescriptorFactory target, String targetName)
throws IOException, RemoteException {
Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(sourcePath);
// 前面创建了一个IParcelFileDescriptorFactory类型
// 通过open函数返回一个ParcelFileDescriptor类型,用于跨进程的访问临时目录下的文件,实现apk文件复制;
out = new ParcelFileDescriptor.AutoCloseOutputStream(
target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));
Streams.copy(in, out);
} finally {
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(in);
}
}

2.3.8. AsecInstallArgs.copyApk方法

当apk安装到sd卡(注意是安装在sd卡,并不是apk文件所在的位置),或者apk为FOWARD_LOCKED类型时调用该类的方法进行apk复制。

首先,判断是否已经复制过,如果已复制,借助MountService获取复制目录信息,更新代码/资源/库路径,跳过后续复制流程:

1
2
3
4
5
6
if (origin.staged && origin.cid != null) {
if (DEBUG_INSTALL) Slog.d(TAG, origin.cid + " already staged; skipping copy");
cid = origin.cid;
setMountPath(PackageHelper.getSdDir(cid));
return PackageManager.INSTALL_SUCCEEDED;
}

如果没复制,开始复制流程,复制流程与安装在内部流程一样,主要区别在于临时目录的路径不同(首次会先在手机中创建/mnt目录,/mnt/secure/ smdl<随机整数>.tmp存放复制的apk和lib,/mnt/secure/asec存放包名-num.asec),复制apk和库文件成功后,更新代码/资源/库路径信息(将smdl<随机整数>.tmp重命名为包名-num)。
到此,不管安装在外部还是内部存储区域的apk,复制操作完成,接下来根据复制过程的返回结果InstallParams.mRet以及InstallParams.mArgs调用InstallParams.handleReturnCode方法进行处理,实现apk的安装。

2.3.9. InstallParams.handleReturnCode安装apk

在startCopy函数中,当handleStartCopy成功执行完毕后,执行handleReturnCode函数进行apk的安装.

首先,判断InstallParams.mArgs:若为null,说明MCS不可用/进行的是包验证,不进行安装操作;否则,调用InstallParams.handleReturnCode方法进行安装:

1
2
3
4
5
void handleReturnCode() {
if (mArgs != null) {
processPendingInstall(mArgs, mRet);
}
}

由于apk的安装是一个耗时操作,因此在processPendingInstall方法中,将安装放到消息队列中,然后在分发消息时,作为消息的回调方法,执行该Runnable,在新的线程中进行安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
// Result object to be returned
PackageInstalledInfo res = new PackageInstalledInfo();
res.setReturnCode(currentStatus);
res.uid = -1;
res.pkg = null;
res.removedInfo = null;
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageTracedLI(args, res);
}
args.doPostInstall(res.returnCode, res.uid);
}
...
}
}

首先,创建一个PackageInstalledInfo res结构,存储安装过程中的结果信息,当成功完成复制,进行安装,其中args.doPreInstall和args.doPostInstall方法只是判定当返回码不是PackageManagerINSTALL_SUCCEEDED时删除代码/资源/本地库文件,在正常安装时二者基本不做操作。真正的安装操作是在PackageManagerService.installPackageLI中完成的.

2.3.10. installPackageLI函数执行应用的安装

以安装在内部存储为例,跟一下实际安装的流程

2.3.10.1. 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
@startuml
PKMS-> PKMS :installPackageTracedLI
PKMS-> PKMS : installPackageLI(args, res)
PKMS -> PackageParser: new PackageParser()
PKMS -> PackageParser: setSeparateProcesses(mSeparateProcesses)
PKMS -> PackageParser: setDisplayMetrics(mMetrics)
PKMS -> PackageParser: PackageParser.Package pkg = parsePackage(tmpPackageFile, parseFlags)
opt args.certificates
PKMS -> PackageParser: populateCertificates(pkg, args.certificates)
else
PKMS -> PackageParser: collectCertificates(pkg, parseFlags)
end
PKMS->PKMS : derivePackageAbi(pkg, new File(pkg.codePath), abiOverride , extract libs);
PKMS-> PKMS : updateSharedLibrariesLPw(pkg, null)
PKMS -> PackageDexOptimizer : performDexOpt(pkg, pkg.usesLibraryFiles, REASON_INSTALL);
PKMS -> FileInstallArgs : doRename(res.returnCode, pkg, oldCodePath)
PKMS->PKMS: startIntentFilterVerifications(userid, replace, pkg);
PKMS->PKMS: freezePackageForInstall(pkgName, installFlags, reason:"installPackageLI"));
opt replace
PKMS->PKMS:replacePackageLIF(pkg, parseFlags, scanFlags| SCAN_REPLACING, ...)
else
PKMS-> PKMS: installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES, ...)
PKMS -> PKMS: scanPackageTracedLI(pkg, parseFlags, scanFlags,...)
PKMS ->PKMS: PackageParser.Package scannedPkg = scanPackageLI(childPkg, policyFlags, scanFlags..)
PKMS ->PKMS: scanPackageDirtyLI(pkg, policyFlags, scanFlags...)
PKMS -> PKMS: return scannedPkg
PKMS -> PKMS: updateSettingsLI(newPackage, installerPackageName)
PKMS -> PKMS: prepareAppDataAfterInstallLIF(newPackage)
end
PKMS->PackageSettingBase: res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds()
@enduml

分解下安装流程,分成10个步骤:

2.3.10.2. 调用parsePackage方法解析AndroidManifest.xml文件

首先调用PackageParser.parsePackage方法解析AndroidManifest.xml文件,将解析结果存入Package pkg中,这部分内容在扫描安装过程中,跳转此处介绍过,此处略去。解析失败,结束安装,在res中存储返回码和返回信息,输出log。

2.3.10.3. 存入abiOverride,TEST_ONLY

存入abiOverride,TEST_ONLY, 验证该APK是否只是用于测试,如果解析时该包只允许测试,而安装标志不允许测试,结束安装,在res中存储返回码和返回信息,输出log。

2.3.10.4. 证书验证

如果安装参数中携带的certificates(install session中已经处理了签名)不为空,则执行populateCertificates方法,直接赋值签名

Populates the correct packages fields with the given certificates.This is useful when we’ve already processed the certificates [such as during package installation through an installer session]

1
2
3
4
5
6
7
8
9
10
pkg.mCertificates = certificates;
try {
pkg.mSignatures = convertToSignatures(certificates);
}
pkg.mSigningKeys = new ArraySet<>(certificates.length);
for (int i = 0; i < certificates.length; i++) {
Certificate[] signerCerts = certificates[i];
Certificate signerCert = signerCerts[0];
pkg.mSigningKeys.add(signerCert.getPublicKey());
}

如果安装参数中没有携带签名,或者签名的过程中在 convertToSignatures 函数中处理出错,则执行collectCertificates方法从Apk中收集签名.
该方法进行证书验证和ManifestDigest验证(验证AndroidManifest.xml,存入pkg.manifestDigest), 验证失败,结束安装,在res中存储返回码和返回信息,输出log. 系统包只校验AndroidManifest.xml文件,而其他包要校验所有文件.

1
2
collectCertificatesInternal(pkg, parseFlags);
collectCertificates(pkg, new File(pkg.baseCodePath), parseFlags);

证书验证部分在Android7.0 上新加了V2签名校验的方案,详细资料可参考这里,以及官方资料

签名校验流程图

2.3.10.4.1. V2签名认证
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
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV2");
// 所以是使用 ApkSignatureSchemeV2Verifier 的verify函数进行V2签名校验的
allSignersCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
signatures = convertToSignatures(allSignersCerts);
// APK verified using APK Signature Scheme v2.
// V2 签名验证通过,此处将verified标志位设置为true,
verified = true;
} catch (ApkSignatureSchemeV2Verifier.SignatureNotFoundException e) {
// No APK Signature Scheme v2 signature found
// 没有使用V2签名时, 忽略后续的步骤
} catch (Exception e) {
// APK Signature Scheme v2 signature was found but did not verify
// V2签名校验失败,则抛出异常
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Failed to collect certificates from " + apkPath
+ " using APK Signature Scheme v2",
e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
if (verified) {
if (pkg.mCertificates == null) {
// 填充pkg的 mCertificates mSignatures mSigningKeys 字段
pkg.mCertificates = allSignersCerts;
pkg.mSignatures = signatures;
pkg.mSigningKeys = new ArraySet<>(allSignersCerts.length);
for (int i = 0; i < allSignersCerts.length; i++) {
Certificate[] signerCerts = allSignersCerts[i];
Certificate signerCert = signerCerts[0];
pkg.mSigningKeys.add(signerCert.getPublicKey());
}
}
// Not yet done, because we need to confirm that AndroidManifest.xml exists and,
// if requested, that classes.dex exists.
}

V2签名校验通过的情况下,不会再进行V1签名的校验; 如果存在V2签名,但校验没通过,会抛出异常,校验未通过.

2.3.10.4.2. V1签名校验

关于V1签名的资料请查看这里, Android APK V1 签名原理

首先创建StrictJarFile的类实例,用来初始化

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
jarFile = new StrictJarFile(
apkPath,
!verified, // whether to verify JAR signature
IsSprdPrebuiltApp ? false : signatureSchemeRollbackProtectionsEnforced);

public StrictJarFile(String fileName,
boolean verify,
boolean signatureSchemeRollbackProtectionsEnforced)
throws IOException, SecurityException {
// 打开 apk文件, 解压apk文件
this.nativeHandle = nativeOpenJarFile(fileName);
this.raf = new RandomAccessFile(fileName, "r");

try {
// V2签名校验失败的情况
if (verify) {
HashMap<String, byte[]> metaEntries = getMetaEntries();
// 指定 MANIFEST.MF 文件,并读取其中记录的文件保存到entries中
this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
// 初始化校验器, 将manifest传入校验器
this.verifier = new StrictJarVerifier(
fileName,
manifest,
metaEntries,
signatureSchemeRollbackProtectionsEnforced);
Set<String> files = manifest.getEntries().keySet();
for (String file : files) {
// MANIFEST.MF 中记录的文件没有在包中,抛出异常
if (findEntry(file) == null) {
throw new SecurityException(fileName + ": File " + file + " in manifest does not exist");
}
}
// 此处将CERT.SF 中的内容再和 MANIFEST.MF 指纹对比,保证 MANIFEST.MF 文件没有被篡改,比对是在其内部的verifyCertificate函数中
isSigned = verifier.readCertificates() && verifier.isSignedJar();
// V2 签名校验通过,此处置空,表示不再进行V1的签名校验
} else {
isSigned = false;
this.manifest = null;
this.verifier = null;
}
}
...
}

├── META-INF
│ ├── ANDROID_.RSA
│ ├── ANDROID_.SF
│ └── MANIFEST.MF

.RSA 是 PKCS#7 标准格式的文件,我们只关心它所保存的以下两种数据:

a. 用私钥对 .SF 文件指纹进行非对称加密后得到的 加密数据
b. 携带公钥以及各种身份信息的 数字证书

MANIFEST.MF、CERT.SF、CERT.RSA 如何各司其职构成了 APK 的签名:

a. 解析出 CERT.RSA 文件中的证书、公钥,解密 CERT.RSA 中的加密数据
b. 解密结果和 CERT.SF 的指纹进行对比,保证 CERT.SF 没有被篡改
c. 而 CERT.SF 中的内容再和 MANIFEST.MF 指纹对比,保证 MANIFEST.MF 文件没有被篡改
d. MANIFEST.MF 中的内容和 APK 所有文件指纹逐一对比,保证 APK 没有被篡改

1
2
3
// 要校验的包放在 toVerify 的列表中
final List<ZipEntry> toVerify = new ArrayList<>();
toVerify.add(manifestEntry);

使用loadCertificates函数进行校验:

1
2
3
4
5
6
7
8
9
10
11
12
13
for (ZipEntry entry : toVerify) {
final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
}
final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
protected static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry) throws PackageParserException {
try {
// 返回验证entry的压缩数据流
is = jarFile.getInputStream(entry);
// 对每个待验证的entry进行校验
readFullyIgnoringContents(is);
return jarFile.getCertificateChains(entry);
}
}

使用getInputStream函数初始化VerifierEntry结构,并返回验证item的zip的压缩数据流

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
public InputStream getInputStream(ZipEntry ze) {
final InputStream is = getZipInputStream(ze);
if (isSigned) {
StrictJarVerifier.VerifierEntry entry = verifier.initEntry(ze.getName());
return new JarFileInputStream(is, ze.getSize(), entry);
}
return is;
}
VerifierEntry initEntry(String name) {
// 之前传入了MANIFEST.MF文件的句柄,即如果该文件中没有该验证item的记录,直接退出
Attributes = manifest.getAttributes(name);
// entry has no digest
if (attributes == null) {
return null;
}

ArrayList<Certificate[]> certChains = new ArrayList<Certificate[]>();
// signatures保存的是 MANIFEST,MF文件中记录项的数据指纹的Base64值
// 如果把 MANIFEST.MF 当做是对 APK 中各个文件的 hash 记录,那么 *.SF 就是 MANIFEST.MF 及其各个条目的 hash 记录。
Iterator<Map.Entry<String, HashMap<String, Attributes>>> it = signatures.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, HashMap<String, Attributes>> entry = it.next();
HashMap<String, Attributes> hm = entry.getValue();
if (hm.get(name) != null) {
// Found an entry for entry name in .SF file
String signatureFile = entry.getKey();
Certificate[] certChain = certificates.get(signatureFile);
if (certChain != null) {
certChains.add(certChain);
}
}
}

// entry is not signed
if (certChains.isEmpty()) {
return null;
}
Certificate[][] certChainsArray = certChains.toArray(new Certificate[certChains.size()][]);

for (int i = 0; i < DIGEST_ALGORITHMS.length; i++) {
// 遍历算法, 目前是这几个 "SHA-512", "SHA-384", "SHA-256", "SHA1"
final String algorithm = DIGEST_ALGORITHMS[i];
// 清单文件中记录了采用的算法计算指纹信息(比较常用的是 SHA1-Digest),可打开MANIFEST.MF文件查看
final String hash = attributes.getValue(algorithm + "-Digest");
try {
return new VerifierEntry(name, MessageDigest.getInstance(algorithm), hashBytes,
certChainsArray, verifiedEntries);
}
}
}

调用readFullyIgnoringContents函数,读取待验证entry文件的数据流,对每个待验证entry进行校验,即和计算该文件的数字指纹和清单中记录的hash值进行比对

1
2
3
4
5
6
7
8
9
10
11
12
13
// 读取完毕后,进行校验
if (count == 0) {
done = true;
entry.verify();
}
void verify() {
byte[] d = digest.digest();
if (!MessageDigest.isEqual(d, Base64.decode(hash))) {
throw invalidDigest(JarFile.MANIFEST_NAME, name, name);
}
// 校验通过后,放进上一步初始化的VerifierEntry中,以entry的name为索引,name是验证文件的名称
verifiedEntries.put(name, certChains);
}

如果校验失败,此时verifiedEntries为空,在loadCertificates后进行检查,为空时抛出异常

1
2
3
4
5
final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
if (ArrayUtils.isEmpty(entryCerts)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Package " + apkPath + " has no certificates at entry "
}

2.3.10.5. 判断是应用升级的情况

即installFlags携带INSTALL_REPLACE_EXISTING(adb 安装时指定了-r参数)

  1. 在mSettings.mRenamedPackages中查找该包名,如果能够查找到返回旧的包名,再检查Package的mOriginalPackages,如果 pkg的 mOriginalPackages 包含该旧包名,且该旧包能在PKMS.mPackages中查到,则说明该包已经安装,此时为应用升级的情况.在这种情况下,需更新当前的包名为旧包的包名,以及更新其所属组件的关联的packageName名称,并将replace标记标为true.
  2. 直接在PKMS.mPackages查找到了该包,说明包已经安装,只需要将replace设置为true

当replace为true时,检查版本号,在AndroidN版本上,如果新安装的包sdk小于22,而已安装的 >=22,跳出安装流程,不能从支持runtimepermission的跳到不支持的版本上

2.3.10.6. 当Packages.xml中存在package的记录时,比对签名

Quick sanity check that we’re signed correctly if updating; we’ll check this again later when scanning, but we want to bail early here before tripping over redefined permissions.

upgradeKeySets 这一项来自于解析AndroidManifest.xml节点的key-sets项,一般情况下,不会配置该项.

当旧包记录中keySetData不为空时,比对当前包中mSigningKeys是否包含这一项,包含则通过检查,没有包含,则设置错误提示退出安装.

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
// 使用 KeySetManagerService 查看是否使用了有效的 upgradeKeySets
if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {
// 比对签名未通过时,提示并退出安装
if (!checkUpgradeKeySetLP(ps, pkg)) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
return;
}
} else {
try {
verifySignaturesLP(ps, pkg);
} catch (PackageManagerException e) {
res.setError(e.error, e.getMessage());
return;
}
}
private boolean shouldCheckUpgradeKeySetLP(PackageSetting oldPs, int scanFlags) {
// Can't rotate keys during boot or if sharedUser.
// olgPs为存在packages.xml中的记录, 如果其有shareUserId, 返回false
if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.sharedUser != null
|| !oldPs.keySetData.isUsingUpgradeKeySets()) {
return false;
}
// app is using upgradeKeySets; make sure all are valid
// 使用 KeySetManagerService 查看是否使用了有效的 upgradeKeySets
KeySetManagerService ksms = mSettings.mKeySetManagerService;
long[] upgradeKeySets = oldPs.keySetData.getUpgradeKeySets();
for (int i = 0; i < upgradeKeySets.length; i++) {
if (!ksms.isIdValidKeySetId(upgradeKeySets[i])) {
return false;
}
}
return true;
}

demo: 通过checkUpgradeKeySetLP函数比对当前包的mSigningKeys 中是否包含 记录中的 KeySet对应的 public-key项,校验失败设置错误提示,退出安装

1
2
3
4
5
6
7
8
9
<key-sets>
<key-set android:name="AB" >
<public-key android:name="keyA"
android:value="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwf5zJblvYSB7Ym7or/7GggAAu7mp7RrykPJsXhod8doFhVT5s7eF3A4MCE55vvANP7HvwMw2b+T6qx7Pq0VJtbbSDtlBHBtIc47Pjq0CsDg590BUcgKp7PdJ9J6UVgtzDnV6cGEpXmSag3sY+lqiW04ytPhCVwzYTWGdYe9+TIl47cBrveRfLOlGrcuFQe+zCTmDFqzBKCRHK9b7l5PDWvXXyg65Uu/MBUA/TZWO0fEqOlxZG/nn6DUKQLhPdmJRXWJ3WqMNMhJGD+nKtkmdX703xRqmg4h+6g0S7M9Y3IQ2NUGyw05AYzCguHB/Mv6uVIiW659wpbyb45TgKG3UhQIDAQAB" />
<public-key android:name="keyB"
android:value="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoeFZqMqTbZiozFTXMkXtSKJRzn2qODZgvVXAAwKTi50xYcbPcHTfKxtif8+q7OCp/50JYDH32bg6wkUunn5+dEaHkxZY8d7uw46tQtl5dNGi+6cc4MezVLCS6nkqNDusAgdvgLU6Fl6SGi02KTp1vkt6CwLO977YJP7kt9ouDRTG7ASJiq3OyRRoOqYHhD9gpsbUq4w+1bXGfuuZujA1dXyovXtvrHUGOdFIEBYOVYGfCcwh3lXPmjNJMlHtKQkurq8/LH7a1B5ocoXCGsyR8YHdlWfrqRAfzgOB1KCnNNmWqskU9LOci3uQn9IDeMEFmAd8FqF8SwV+4Ludk/xWGQIDAQAB" />
</key-set>
<upgrade-key-set android:name="AB"/>
</key-sets>

旧包中keySetData项为空, 可以查看Packages.xml文件,对应的标签为upgrade-keyset, 没有该项,则通过verifySignaturesLP函数进行签名比对

2.3.10.6.1. verifySignaturesLP 进行签名比对(新老安装包)

在mSettings.mPackages中存在该包, 需要比对该包的签名和现在解析的包的签名进行比对.签名不一致,退出安装.

关于databaseVersion项在启动篇介绍过, 对于private volume. databaseVersion的值总是3,而public volume的值往往等于sdkVersion.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 首先记录中包的签名不为空
if (pkgSetting.signatures.mSignatures != null) {
// Already existing package. Make sure signatures match
// Make sure s2 contains all signatures in s1. 这个函数比较简单,只是简单的比对是否 s1 和 s2 完全相同
boolean match = compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH;
if (!match) {
// 这个地方上来检测 pkg安装到的 volume 对应的databaseVersion是否小于2, 不小于2的情况下,直接退出.所以后面的流程都没走.
match = compareSignaturesCompat(pkgSetting.signatures, pkg)
== PackageManager.SIGNATURE_MATCH;
}
if (!match) {
// 这个地方上来检测 pkg安装到的 volume 对应的databaseVersion是否小于3, 不小于3的情况下,直接退出.所以后面的流程都没走.
match = compareSignaturesRecover(pkgSetting.signatures, pkg)
== PackageManager.SIGNATURE_MATCH;
}
if (!match) {
throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " signatures do not match the "
+ "previously installed version; ignoring!");
}
}

接下来检测shareUser的签名,即如果应用指定了shareUserId项,可将指定shareUser的项看作同类的包,需要挨个检测签名是否一致,以出现的第一个包的签名为基准.

1
2
3
4
5
6
// Check for shared user signatures
if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) {
// Already existing package. Make sure signatures match
boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
}

此处可以追溯pkgSetting.sharedUser.signatures.mSignatures第一次被赋值的地方.

scanPackageDirtyLI函数处调用了mSettings.insertPackageSettingLPw(pkgSetting, pkg) 函数,此处检测shareUser的签名如果为空,会更新为当前扫描包的签名

1
2
3
4
5
// If this app defines a shared user id initialize
// the shared user signatures as well.
if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) {
p.sharedUser.signatures.assignSignatures(pkg.mSignatures);
}

2.3.10.7. 验证权限

如果从apk中解析到的权限信息,在之前已经定义过,即mSettings.mPermissions包含该权限名,那么首先验证签名信息是否符合(升级包时,一般包),签名符合继续安装流程;签名不符合时,如果冲突的权限来自framework-res.apk,删除当前apk冲突的权限,继续安装流程,否则终止安装。

如果不是framework-res.apk包,需要其定义的权限是否为PROTECTION_DANGEROUS权限, 如果是dangerous权限,但已经存在的同名权限的类型为非dangerous的权限,则需要变更当前权限的protectionLevel为记录中的protectionLevel

原因是防止特权扩大,如某应用在其他应用组中添加了该权限,如果重新定义该权限名称为dangerous权限,则会导致之前的应用组的该权限被默认授权.??

2.3.10.8. 更新指令集以及拷贝本地库并执行dexopt优化

可以参考处理本地库目录, 以及为非系统应用更新共享库信息.

由于本地库目录的变更, 一些应用可能使用到了此库,所以需要更新这些应用的usesLibraries项,更新路径信息.

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
else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
// Enable SCAN_NO_DEX flag to skip dexopt at a later stage
scanFlags |= SCAN_NO_DEX;

try {
String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?
args.abiOverride : pkg.cpuAbiOverride);
derivePackageAbi(pkg, new File(pkg.codePath), abiOverride,
true /* extract libs */);
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
return;
}

// Shared libraries for the package need to be updated.
synchronized (mPackages) {
try {
// 更新pkg.usesLibraries项,更新路径信息
updateSharedLibrariesLPw(pkg, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "updateSharedLibrariesLPw failed: " + e.getMessage());
}
}
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
// Do not run PackageDexOptimizer through the local performDexOpt
// method because `pkg` may not be in `mPackages` yet.
//
// Also, don't fail application installs if the dexopt step fails. // 失败并不退出
mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
null /* instructionSets */, false /* checkProfiles */,
getCompilerFilterForReason(REASON_INSTALL));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}

2.3.10.9. 目录更名

调用FileInstallArgs.doRename方法,将/data/app/vmdl<随机整数>.tmp目录更名为/data/app/packageName-num,num的值在1和2之间循环取值,同时还要更新FileInstallArgs和Package pkg相关的路径信息:

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
boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {

final File targetDir = codeFile.getParentFile();
final File beforeCodeFile = codeFile;
final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName);

if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
try {
Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
}
// 重新设置Selinux上下文
if (!SELinux.restoreconRecursive(afterCodeFile)) {
Slog.w(TAG, "Failed to restorecon");
return false;
}

// Reflect the rename internally
codeFile = afterCodeFile;
resourceFile = afterCodeFile;

// Reflect the rename in scanned details
pkg.setCodePath(afterCodeFile.getAbsolutePath());
pkg.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
afterCodeFile, pkg.baseCodePath));
pkg.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
afterCodeFile, pkg.splitCodePaths));

// Reflect the rename in app info
pkg.setApplicationVolumeUuid(pkg.volumeUuid);
pkg.setApplicationInfoCodePath(pkg.codePath);
pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
pkg.setApplicationInfoResourcePath(pkg.codePath);
pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath);
pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);

return true;
}

冻结应用,杀死进程.但如果安装参数中携带了INSTALL_DONT_KILL_APP标记时,不会杀掉该进程.

更名完毕后,根据是全新安装还是覆盖安装,执行全新安装installNewPackageLI覆盖安装replacePackageLIF方法

2.3.10.10. 覆盖安装

replacePackageLIF函数进行覆盖安装

首先,对于重复安装时先比对 新包和 已安装包的签名信息是否匹配,对于升级安装时验证新包是否包含旧包的所有签名密钥,失败直接返回,停止覆盖安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// verify signatures are valid
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {
if (!checkUpgradeKeySetLP(ps, pkg)) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"New package not signed by keys specified by upgrade-keysets: "
+ pkgName);
return;
}
} else {
// default to original signature matching
if (compareSignatures(oldPackage.mSignatures, pkg.mSignatures)
!= PackageManager.SIGNATURE_MATCH) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"New package has a different signature: " + pkgName);
return;
}
}

如果旧包的restrictUpdateHash字段不为空,且旧包为system app时,需要对apk进行MD校验, 只有当apk的md值等于该restrictUpdateHash字段的值时,才允许升级.

该字段来自于解析apk的Manifest节点, restrict-update : android:hash.

don’t allow a system upgrade unless the upgrade hash matches

接下来检查新老包的shareUserid有没有发生变化,包括其parent和child包的也要一并检查.如果shareUserId变化了,提示错误,退出安装.

1
2
3
4
5
6
7
8
9
// Check for shared user id changes
String invalidPackageName =
getParentOrChildPackageChangedSharedUser(oldPackage, pkg);
if (invalidPackageName != null) {
res.setError(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
"Package " + invalidPackageName + " tried to change user "
+ oldPackage.mSharedUserId);
return;
}

查询为哪些用户安装了旧包,并保存起来

1
installedUsers = ps.queryInstalledUsers(allUsers, true);

更新PackageInstalledInfo.removedInfo信息

对应于PackageRemovedInfo removedInfo类

最后根据apk的类型进行系统级/非系统级替换安装:

2.3.10.10.1. 非系统级apk覆盖安装

调用replaceNonSystemPackageLIF方法进行非系统级apk的覆盖安装,与全新安装的区别主要是,在安装前会先删除内部数据结构信息,从而可以进行扫描安装:

首先,调用deletePackageLI方法删除已安装apk的数据结构信息,但是保留数据目录。对于ForwardLocked/External(是否忘记porting InternalSd)类型的apk,在杀死该应用前发送一个广播,使用户可以放弃资源:

1
2
3
4
5
6
7
8
9
10
if (deletedPackage.isForwardLocked() || isExternal(deletedPackage)) {
if (DEBUG_INSTALL) {
Slog.i(TAG, "upgrading pkg " + deletedPackage + " is ASEC-hosted -> UNAVAILABLE");
}
final int[] uidArray = new int[] { deletedPackage.applicationInfo.uid };
final ArrayList<String> pkgList = new ArrayList<String>(1);
pkgList.add(deletedPackage.applicationInfo.packageName);
// mediaStatus为false, 发送android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE的广播
sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
}

调用clearAppDataLIF方法清除所有用户下的该应用的DE区和CE区的/code_cache目录.(通过installd完成)

1
2
clearAppDataLIF(pkg, UserHandle.USER_ALL, StorageManager.FLAG_STORAGE_DE
| StorageManager.FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);

调用clearAppProfilesLIF方法清除 /data/misc/profiles/ref//primary.prof 文件和

清除所有用户下的该文件 /data/misc/profiles/cur///primary.prof

接下来执行的步骤和全新安装流程基本相同, 即执行scanPackageTracedLI进入scanPackageLI方法扫描安装apk,调用updateSettingsLI方法(主要调用了updateSettingsInternalLI方法),将新安装的apk信息更新到packages.xml中.跳转到此

但是需要更新新安装包PackageSetting的oldCodePaths字段(携带SCAN_DONT_KILL_APP标志时,即不杀应用时),该字段保存了旧包的安装包baseapk即splitapk的路径, 新安装包的子包的此字段也需要同步更新.

至此,非系统级apk覆盖安装结束,最终回到installPackageLI中。

2.3.10.10.2. 系统级apk覆盖安装

调用replaceSystemPackageLIF函数进行系统级apk的覆盖安装

首先需要调用removePackageLI方法清除旧包的数据结构信息: 从PackageManagerService.mPackages中清除已安装的系统包,接着调用cleanPackageDataStructuresLILPw从PackageManagerService.mProviders/mServices/mActivities等组件中清除旧包所包含的组件信息;

1
2
3
4
5
6
7
8
9
10
removePackageLI(deletedPackage, true);
void removePackageLI(PackageSetting ps, boolean chatty) {
synchronized (mPackages) {
mPackages.remove(ps.name);
final PackageParser.Package pkg = ps.pkg;
if (pkg != null) {
cleanPackageDataStructuresLILPw(pkg, chatty);
}
}
}

接着,调用mSettings.disableSystemPackageLPw方法将已安装的包信息disabled:先根据包名查询mSettings.mDisabledSysPackages是否存在包信息,如果存在,直接返回false;不存在,更改标志为FLAG_UPDATED_SYSTEM_APP,将已安装的包信息添加到mSettings.mDisabledSysPackages中,并根据已安装的包信息创建一个新的备份,用这个备份来更替换mSettings.mPackages的包信息,以及替换共享用户的包信息/mSettings.mUserIds或mSettings.mOtherUserIds(即,要保留原始安装包信息,只对备份修改):

省略了子包的相关操作….

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
// 将已安装的包信息disabled
boolean disabled = mSettings.disableSystemPackageLPw(oldPkg.packageName, true);
boolean disableSystemPackageLPw(String name, boolean replaced) {
final PackageSetting p = mPackages.get(name);
final PackageSetting dp = mDisabledSysPackages.get(name);
// always make sure the system package code and resource paths dont change
// !isUpdatedSystemApp // 没有被标记为 FLAG_UPDATED_SYSTEM_APP 的情况下, 初始情况下,就是没有被标记的
if (dp == null && p.pkg != null && p.pkg.isSystemApp() && !p.pkg.isUpdatedSystemApp()) {
// 进入此条件的前提是当前替换的旧包在不在系统禁用包中, 即在mDisabledSysPackages中
if((p.pkg != null) && (p.pkg.applicationInfo != null)) {
p.pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
// 将旧包加入到系统禁用包中
mDisabledSysPackages.put(name, p);

if (replaced) {
// a little trick... when we install the new package, we don't
// want to modify the existing PackageSetting for the built-in
// version. so at this point we need a new PackageSetting that
// is okay to muck with.
// 创建新的packageSetting, 因为升级的是系统包,不再使用之前的旧的PackageSetting
PackageSetting newp = new PackageSetting(p);
// 将和name绑定的PackageSetting替换为现在新创建的PackageSetting
replacePackageLPw(name, newp);
}
return true;
}
// 存在直接返回false
return false;
}

旧包没在mDisabledSysPackages中时,说明原先的包是安装状态,需要填充PackageInstalledInfo.PackageRemovedInfo信息. 调用createInstallArgsForExisting方法生成这个信息, We need to make sure to delete the older one’s .apk.

执行clearAppDataLIFclearAppProfilesLIF 清除codecache和primary.prof 文件;

设置ApplicationInfo.FLAG_UPDATED_SYSTEM_APP 标志,通过isUpdatedSystemApp查询时返回true

执行scanPackageTracedLI(#scanPackageTracedLI)函数进行安装.

设置应用的安装时间和更新时间firstInstallTime lastUpdateTime, 还有子包的

遍历当前包的所有子包,检查更新包的子包是否和旧包的子包相同. 旧包子包没有在更新包中,需要将该包删除(从mPackages中清除,并包括组件信息等),并清除数据(不止是codecache,没有DELETE_KEEP_DATA标志的前提下)

调用updateSettingsLI方法(主要调用了updateSettingsInternalLI方法),将新安装的apk信息更新到packages.xml中

调用prepareAppDataAfterInstallLIF方法为新安装的包准备数据目录

如果安装失败,调用removeInstalledPackageLI删除新的包,扫描恢复旧的安装包,若是第一次更新从mSettings.mDisabledSysPackages删除旧的包信息,更新安装的包名,并再次更新packages.xml文件

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
if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
// Re installation failed. Restore old information
// Remove new pkg information
if (newPackage != null) {
removeInstalledPackageLI(newPackage, true);
}
// Add back the old system package
try {
// 重新扫描旧包
scanPackageTracedLI(deletedPackage, policyFlags, SCAN_UPDATE_SIGNATURE, 0, user);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to restore original package: " + e.getMessage());
}

synchronized (mPackages) {
// 此标志为true,说明是第一次更新时,上述步骤中将旧包禁用 (使用disableSystemPackageLPw方法禁用的,此时要重新启用回来)
if (disabledSystem) {
enableSystemPackageLPw(deletedPackage);
}

// Ensure the installer package name up to date
// 设置应用的安装时间和更新时间firstInstallTime lastUpdateTime, 还有子包的
setInstallerPackageNameLPw(deletedPackage, installerPackageName);

// Update permissions for restored package
updatePermissionsLPw(deletedPackage, UPDATE_PERMISSIONS_ALL);
// 更新packages.xml文件
mSettings.writeLPr();
}

至此,系统级apk覆盖安装完成,最终回到installPackageLI。

2.3.10.11. 全新安装

调用PackageManagerService. installNewPackageLI方法执行全新安装。

如果当前包已安装过,即使是更名的,也结束installNewPackageLI安装:

1
2
3
4
5
6
7
8
9
10
11
if (mSettings.mRenamedPackages.containsKey(pkgName)) {
res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
+ " without first uninstalling package running as "
+ mSettings.mRenamedPackages.get(pkgName));
return;
}
if (mPackages.containsKey(pkgName)) {
res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
+ " without first uninstalling.");
return;
}

调用PackageManagerService.scanPackageLI方法扫描安装apk,创建数据目录,主要创建/data/data/数据目录,和/data/data//lib库文件目录,并将库目录链接到/data/app//lib/arm库文件目录;

然后调用updateSettingsLI方法(主要调用了updateSettingsInternalLI方法),将新安装的apk信息更新到packages.xml中:

  • 首先,先更新packages.xml文件,此时该apk为安装未完成状态;
  • 调用updatePermissionsLPW,为安装的apk分配权限,并将相应的gid号保存到PackageSettngs或SharedUserSetting的gids数组中;
  • 更新安装状态为安装完成,和PackageInstalledInfo res的属性,重新更新packages.xml文件。
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
private void updateSettingsInternalLI(PackageParser.Package newPackage,
String installerPackageName, int[] allUsers, int[] installedForUsers,
PackageInstalledInfo res, UserHandle user) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
String pkgName = newPackage.packageName;
synchronized (mPackages) {
// 指定安装未完成状态
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
mSettings.writeLPr();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}

if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.codePath);
synchronized (mPackages) {
// 调用updatePermissionsLPW,为安装的apk分配权限,并将相应的gid号保存到PackageSettngs或SharedUserSetting的gids数组中;
updatePermissionsLPw(newPackage.packageName, newPackage,
UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
? UPDATE_PERMISSIONS_ALL : 0));
PackageSetting ps = mSettings.mPackages.get(pkgName);
final int userId = user.getIdentifier();
if (ps != null) {
// 新安装的包为system app,需要重新启用之前可能禁用的组件, 因为该函数可被多个地方调用, 全新安装的情况下,是不会走到这里的
if (isSystemApp(newPackage)) {
if (DEBUG_INSTALL) {
Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
}
// Enable system package for requested users
// origUser不为空时,标明为覆盖安装?
if (res.origUsers != null) {
for (int origUserId : res.origUsers) {
if (userId == UserHandle.USER_ALL || userId == origUserId) {
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
origUserId, installerPackageName);
}
}
}
// Also convey the prior install/uninstall state
if (allUsers != null && installedForUsers != null) {
for (int currentUserId : allUsers) {
final boolean installed = ArrayUtils.contains(
installedForUsers, currentUserId);
if (DEBUG_INSTALL) {
Slog.d(TAG, " user " + currentUserId + " => " + installed);
}
// 重新指定安装状态, 只有installedForUsers的安装状态才为true
ps.setInstalled(installed, currentUserId);
}
}
}
// 指定为某个用户安装, 仅为该用户安装
if (userId != UserHandle.USER_ALL) {
ps.setInstalled(true, userId);
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
}
}
res.name = pkgName;
res.uid = newPackage.applicationInfo.uid;
res.pkg = newPackage;
// 更新为安装完成状态
mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
mSettings.setInstallerPackageName(pkgName, installerPackageName);
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
//to update install status
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
// 更新Packages.xml文件表示安装完成
mSettings.writeLPr();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
2.3.10.11.1. 准备数据目录

调用prepareAppDataAfterInstallLIF方法为新安装的包准备数据目录

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
private void prepareAppDataAfterInstallLIF(PackageParser.Package pkg) {
final PackageSetting ps;
synchronized (mPackages) {
ps = mSettings.mPackages.get(pkg.packageName);
// 创建/config/sdcardfs/<packageSetting.name>/appid文件,里面写入 packageSetting.appid值
mSettings.writeKernelMappingLPr(ps);
}

final UserManager um = mContext.getSystemService(UserManager.class);
UserManagerInternal umInternal = getUserManagerInternal();
for (UserInfo user : um.getUsers()) {
final int flags;
// 查询用户是否解锁, 即Unlocking 或 Unlocked状态,
if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE;
} else {
continue;
}
// 该用户下安装了该apk, 则需要准备数据目录, 通过installd 创建目录
// flag携带DE时, /data/user_de/<userid>/<packageName>/
// 携带CE时, /data/user_ce/<userid>/<packageName>/
if (ps.getInstalled(user.id)) {
// Whenever an app changes, force a restorecon of its data
// TODO: when user data is locked, mark that we're still dirty
prepareAppDataLIF(pkg, user.id, flags, true);
}
}
}

installNewPackageLI方法完成,回到installPackageLI方法;

否则,调用deletePackageLI方法,删除安装的数据信息和数据/缓存目录,除非这些目录在安装前已经存在,然后返回installPackageL方法:

2.3.11. 小结

在安装完成后,更新已安装的用户id,保存到res的newUsers中.

1
2
3
4
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
}

至此,InstallPackageLI方法结束,回到processPendingInstall方法继续处理。

2.3.12. processPendingInstall方法后续处理

installPackageLI安装完成后,首先调用args.doPostInstall方法,确保当安装失败时删除复制的apk、资源和库。

1
2
3
// A restore should be performed at this point if (a) the install
// succeeded, (b) the operation is not an update, and (c) the new
// package has not opted out of backup participation.

根据之前的记录的RemoveInfo的信息, 覆盖安装的情况下,此信息才不为空.

当removeinfo为空(全新安装的情况),且安装包对应的applicationinfo中有FLAG_ALLOW_BACKUP标记时(来自于Manifest), doRestore为true

doRestore为true时,连接BACKUP_SERVICE,执行BackupManagerService.restoreAtInstall方法, 最终执行到 PerformUnifiedRestoreTask 的execute方法, startRestore方法 ??

接着,将安装次序和安装信息保存到PackageManangerService.mRunningInstalls中:(mNextInstallToken>=1)

1
2
3
4
5
6
7
token = mNextInstallToken++;
PostInstallData data = new PostInstallData(args, res);
mRunningInstalls.put(token, data); //以安装次序为key 保存了 PostInstallData对象
static class PostInstallData {
public InstallArgs args;
public PackageInstalledInfo res;
}

doRestore为false的情况下, 即覆盖安装的情况下, 或者前面的restoreAtInstall方法执行错误的情况(可能是没有 BackupManagerService , 或连接不上时)

执行POST_INSTALL阶段

2.3.13. POST_INSTALL阶段(处理POST_INSTALL消息)

这部分主要发送一些安装成功的广播,可以被launcher接收,在桌面添加图表等操作,并回调Observer安装成功的接口,具体处理流程如下:

从mRunningInstalls中取出之前保存的 PostInstallData对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PostInstallData data = mRunningInstalls.get(msg.arg1);
// 此时传来的 arg2 参数为空
final boolean didRestore = (msg.arg2 != 0);
if (data != null) {
InstallArgs args = data.args;
PackageInstalledInfo parentRes = data.res;

final boolean grantPermissions = (args.installFlags
& PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
final boolean killApp = (args.installFlags
& PackageManager.INSTALL_DONT_KILL_APP) == 0;
final String[] grantedPermissions = args.installGrantPermissions;

// Handle the parent package
handlePackagePostInstall(parentRes, grantPermissions, killApp,
grantedPermissions, didRestore, args.installerPackageName,
args.observer);

// Handle the child packages
... 省略子包的
}

2.3.13.1. handlePackagePostInstall函数的处理

主要看handlePackagePostInstall的实现, 该函数中第一个参数为 PackageInstalledInfo , 该参数为之前保存安装结果的result, 里面保存了错误类型,安装进度,removedinfo信息等.

如果其中removedInfo不为空,发送android.intent.action.PACKAGE_REMOVED的广播,提示旧包被删除,广播中携带参数killapp(是否杀掉相应进程)

a. 如果对应的removedinfo中的dataRemoved为true, 且不是删除的system app, 则另外发送android.intent.action.PACKAGE_FULLY_REMOVED的广播

如果对应的removedAppId>=0,发送android.intent.action.UID_REMOVED的广播, 在前面的步骤中,removedAppId赋值是在覆盖系统包时,如果系统包旧包含有子包的情况下,且旧包的某子包未在新安装的包的子包中时,会涉及到该种情况

如果携带的参数中包含grantPermissions, 赋予运行时权限,并更新packages.list文件

如果其parent包不为空,查看parent包是否在mDisabledSysPackages 禁用包中, 且该包为特权包, 给其赋予申请的运行时权限

无论是否携带 grantPermissions对应的安装时参数时

1
2
3
4
5
if (res.pkg.parentPackage != null) {
synchronized (mPackages) {
grantRuntimePermissionsGrantedToDisabledPrivSysPackageParentLPw(res.pkg);
}
}
2.3.13.1.1. 设置用户集firstUsers和updateUsers

设置第一次添加该包的用户集firstUsers和更新该包的用户集updateUsers:当res.origiUsers为空时,只设置发firstUsers为res.newUsers;当存在res.origiUsers时,将res.newUsers中不存在于res.origiUsers的用户存入firstUsers,其余的存入updateUsers中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Determine the set of users who are adding this package for
// the first time vs. those who are seeing an update.
int[] firstUsers = EMPTY_INT_ARRAY;
int[] updateUsers = EMPTY_INT_ARRAY;
if (res.origUsers == null || res.origUsers.length == 0) {
firstUsers = res.newUsers;
} else {
for (int newUser : res.newUsers) {
boolean isNew = true;
for (int origUser : res.origUsers) {
if (origUser == newUser) {
isNew = false;
break;
}
}
if (isNew) {
firstUsers = ArrayUtils.appendInt(firstUsers, newUser);
} else {
// 该用户下已经安装, 需要将当前的安装的用户添加到updateUsers中
updateUsers = ArrayUtils.appendInt(updateUsers, newUser);
}
}
}
2.3.13.1.2. 向用户集发送包广播

然后,向firstUsers和updateUsers中的用户发送ACTION_PACKAGE_ADDED “android.intent.action.PACKAGE_ADDED” 广播:

  • 对于覆盖安装的情况, 并向updateUsers的用户发送包替换 ACTION_PACKAGE_REPLACED (“android.intent.action.PACKAGE_REPLACED” ) 广播
  • 对于全新安装的情况, didRestore 为true,但此时传入的参数为null, 且非系统包的情况下,发送ACTION_PACKAGE_FIRST_LAUNCH的广播, 在这个安装场景下,这条是不符合条件的
  • 并对FORWARD_LOCKED/外置SD卡的包发送ACTION_EXTERNAL_APPLICATIONS_AVAILABLE的广播
2.3.13.1.3. 为firstUsers 新安装用户集处理挂起的运行时权限申请请求

在mRestoredUserGrants中取出挂起的权限申请请求,然后从其中找出当前包申请的权限,对其进行权限授予,更新flag,并更新/data/users//runtime-permissions.xml文件

1
2
3
for (int userId : firstUsers) {
mSettings.applyPendingPermissionGrantsLPw(packageName, userId);
}
2.3.13.1.4. 覆盖安装情况下删除旧包的相关文件

调用res.romovedInfo.args.doPostDeleteLI方法删除旧包的代码/资源/本地库/dex文件等。

1
2
3
4
5
boolean doPostDeleteLI(boolean delete) {
// XXX err, shouldn't we respect the delete flag?
cleanUpResourcesLI();
return true;
}
2.3.13.1.5. 回调监听器的onPackageInstalled接口

最后,当在PackageInstalled.apk安装时指定了包安装监听器,回调该监听器的onPackageInstalled接口,告知监听的应用包已经安装完成. Bundle在成功安装时为null:

1
2
3
Bundle extras = extrasForInstallResult(res);
installObserver.onPackageInstalled(res.name, res.returnCode,
res.returnMsg, extras);

处理完后,安装结束

1
Log.d(TAG, "pms install end");

2.4. 剩余消息处理

  • 处理MCS_RECONNECT消息

当在复制apk时抛出RemoteException,startCopy会发出MCS_RECONNECTION消息,尝试重新复制和安装:如果,当前绑定到MCS上,解除绑定;然后重新绑定该服务,如果绑定失败,处理所有安装请求的错误信息,并移除所有的安装请求

  • 处理MCS_GIVE_UP消息

当尝试4次复制apk,均失败时,在startCopy中发送该消息将此安装请求从队列中移除:

  • 处理MCS_UNBIND消息

当前没有安装请求时,会解除与MCS服务的绑定;如果此时又来了安装请求,那么会再次发送一个MCS_BOUND的空消息,继续进行安装