PKMS 钻研(6) - APK 安装(上)

Updated on 2020.04.02

核心源码(Android 10)

关键类 路径
PackageInstaller.java /frameworks/base/core/java/android/content/pm/PackageInstaller.java
PackageManagerService.java /frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
PackageInstallerSession.java /frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
InstallInstalling.java /packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
PackageInstallerActivity.java /frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java

一、PackageInstallerActivity

在之前的文章中,我们知道 PackageInstallerActivity 调用 startInstallConfirm() 方法 初始化安装确认界面 后,这个安装确认界面就会呈现给用户,用户如果想要安装这个应用程序就会点击确定按钮,就会调用 PackageInstallerActivitybindUi() 方法的 mAlert 点击事件。

1.1 PackageInstallerActivity.bindUi()

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
// frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java

public class PackageInstallerActivity extends AlertActivity {

private void bindUi() {
mAlert.setIcon(mAppSnippet.icon);
mAlert.setTitle(mAppSnippet.label);
mAlert.setView(R.layout.install_content_view);
mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
(ignored, ignored2) -> {
if (mOk.isEnabled()) {
if (mSessionId != -1) {
// 如果原本是确认权限请求则赋予安装权限后退出
mInstaller.setPermissionsResult(mSessionId, true);
finish();
} else {
startInstall(); // 进行安装
}
}
}, null);
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
(ignored, ignored2) -> {
// Cancel and finish
setResult(RESULT_CANCELED); // 取消安装
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
finish();
}, null);
setupAlert();

mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
mOk.setEnabled(false);
}

}

1.2 PackageInstallerActivity.startInstall()

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
// frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java

public class PackageInstallerActivity extends AlertActivity {

private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent();

// 带上安装包的 applicationInfo
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo);
// 带上安装包的 URI
newIntent.setData(mPackageURI);
// 设置目标类
newIntent.setClass(this, InstallInstalling.class);
String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);

// 带上安装包的 mOriginatingURI
if (mOriginatingURI != null) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
}
// 带上安装包的 mReferrerURI
if (mReferrerURI != null) {
newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
}
// 带上安装包的 mOriginatingUid 这个 uid 不是安装应用的 uid
if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
}
if (installerPackageName != null) {
newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName);
}
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
}

newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);

// startInstall 方法用于跳转到 InstallInstalling 这个 Activity
startActivity(newIntent);
finish(); // 关闭当前的 PackageInstallerActivity
}

}

可以看到在 startInstall 方法中,主要是构造了一个 intent,并且将 安装包信息 封装到 intent 中,然后跳转到 InstallInstalling 类。

二、InstallInstalling

InstallInstalling 的主要工作:用于向包管理器发送包的信息并处理包管理的回调。

2.1 InstallInstalling.onCreate()

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
// frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java

public class InstallInstalling extends Activity {

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

ApplicationInfo appInfo = getIntent().getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mPackageURI = getIntent().getData();

// 分别对 package 和 content 协议的 Uri 进行处理
if ("package".equals(mPackageURI.getScheme())) {
try {
getPackageManager().installExistingPackage(appInfo.packageName);
launchSuccess();
} catch (PackageManager.NameNotFoundException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
} else {
// 根据 mPackageURI 创建一个对应的 File
final File sourceFile = new File(mPackageURI.getPath());
PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);

mAlert.setIcon(as.icon);
mAlert.setTitle(as.label);
mAlert.setView(R.layout.install_content_view);
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
(ignored, ignored2) -> {
if (mInstallingTask != null) {
mInstallingTask.cancel(true);
}

if (mSessionId > 0) {
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
mSessionId = 0;
}

setResult(RESULT_CANCELED);
finish();
}, null);
setupAlert();
requireViewById(R.id.installing).setVisibility(View.VISIBLE);

// 如果 savedInstanceState 不为 null,获取此前保存的 mSessionId 和 mInstallId
if (savedInstanceState != null) {
mSessionId = savedInstanceState.getInt(SESSION_ID); // mSessionId 是安装包的会话 id
mInstallId = savedInstanceState.getInt(INSTALL_ID); // mInstallId 是等待的安装事件 id

/**
* 根据 mInstallId 向 InstallEventReceiver 注册一个观察者
* launchFinishBasedOnResult 会接收到安装事件的回调,
* 无论安装成功或者失败都会关闭当前的 Activity(InstallInstalling)
*/
try {
InstallEventReceiver.addObserver(this, mInstallId, this::launchFinishBasedOnResult);
} catch (EventResultPersister.OutOfIdsException e) {
// Does not happen
}
} else {
// savedInstanceState 为 null
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
params.setInstallAsInstantApp(false);
params.setReferrerUri(getIntent().getParcelableExtra(Intent.EXTRA_REFERRER));
params.setOriginatingUri(getIntent().getParcelableExtra(Intent.EXTRA_ORIGINATING_URI));
params.setOriginatingUid(getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID, UID_UNKNOWN));
params.setInstallerPackageName(getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME));
params.setInstallReason(PackageManager.INSTALL_REASON_USER);

File file = new File(mPackageURI.getPath());
try {
PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);
params.setAppPackageName(pkg.packageName);
params.setInstallLocation(pkg.installLocation);
params.setSize(PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
} catch (PackageParser.PackageParserException e) {
Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");
Log.e(LOG_TAG, "Cannot calculate installed size " + file + ". Try only apk size.");
params.setSize(file.length());
} catch (IOException e) {
Log.e(LOG_TAG, "Cannot calculate installed size " + file + ". Try only apk size.");
params.setSize(file.length());
}

try {
mInstallId = InstallEventReceiver
.addObserver(this, EventResultPersister.GENERATE_NEW_ID, this::launchFinishBasedOnResult);
} catch (EventResultPersister.OutOfIdsException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}

try {
mSessionId = getPackageManager().getPackageInstaller().createSession(params);
} catch (IOException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
}

mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);

mSessionCallback = new InstallSessionCallback();
}
}

}

2.2 InstallInstalling.onResume()

我们查看 InstallInstallingonResume 方法:

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
// frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java

public class InstallInstalling extends Activity {

@Override
protected void onResume() {
super.onResume();

// This is the first onResume in a single life of the activity
if (mInstallingTask == null) {
PackageInstaller installer = getPackageManager().getPackageInstaller();
// 根据 mSessionId 得到 SessionInfo,SessionInfo 代表安装会话的详细信息
PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);

// 如果 sessionInfo 不为 Null 并且不是活动的,就创建并执行 InstallingAsyncTask
if (sessionInfo != null && !sessionInfo.isActive()) {
mInstallingTask = new InstallingAsyncTask();
mInstallingTask.execute();
} else {
// we will receive a broadcast when the install is finished
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
}
}
}

}

2.3 InstallInstalling.InstallingAsyncTask

既然启动了 InstallingAsyncTask,那我们就来看看相关的 doInBackground()onPostExecute() 方法:

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
// frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java

public class InstallInstalling extends Activity {

private final class InstallingAsyncTask extends AsyncTask<Void, Void, PackageInstaller.Session> {
volatile boolean isDone;

// 根据包(APK)的 Uri,将 APK 的信息通过 IO 流的形式写入到 PackageInstaller.Session 中
@Override
protected PackageInstaller.Session doInBackground(Void... params) {
PackageInstaller.Session session;
try {
session = getPackageManager().getPackageInstaller().openSession(mSessionId);
} catch (IOException e) {
return null;
}

session.setStagingProgress(0);

try {
File file = new File(mPackageURI.getPath());

try (InputStream in = new FileInputStream(file)) {
long sizeBytes = file.length();
try (OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes)) {
byte[] buffer = new byte[1024 * 1024];
while (true) {
int numRead = in.read(buffer);

if (numRead == -1) {
session.fsync(out);
break;
}

if (isCancelled()) {
session.close();
break;
}

out.write(buffer, 0, numRead);
if (sizeBytes > 0) {
float fraction = ((float) numRead / (float) sizeBytes);
session.addProgress(fraction);
}
}
}
}

return session;
} catch (IOException | SecurityException e) {
Log.e(LOG_TAG, "Could not write package", e);
session.close();
return null;
} finally {
synchronized (this) {
isDone = true;
notifyAll();
}
}
}

@Override
protected void onPostExecute(PackageInstaller.Session session) {
if (session != null) {
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntent.setPackage(getPackageName());
broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);

// 创建了一个 PendingIntent
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallInstalling.this,
mInstallId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
/**
* 将该 PendingIntent 的 IntentSender 通过 PackageInstaller.Session 的 commit 方法发送出去
*/
session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
getPackageManager().getPackageInstaller().abandonSession(mSessionId);

if (!isCancelled()) {
launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
}
}
}
}

}

那么问题来了,commit 到哪去了?我们不妨来看一下 PackageInstaller.Sessioncommit 方法:

2.4 PackageInstaller.Session

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// frameworks/base/core/java/android/content/pm/PackageInstaller.java

public class PackageInstaller {

public static class Session implements Closeable {
/** {@hide} */
protected final IPackageInstallerSession mSession;
... ...

public void commit(@NonNull IntentSender statusReceiver) {
try {
mSession.commit(statusReceiver, false);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
... ...

}

}

我们发现,mSession 的类型为 IPackageInstallerSession ,这说明要通过 IPackageInstallerSession 来进行进程间的通信,最终会调用 PackageInstallerSessioncommit 方法,这样又回到框架层处理了。

三、PackageInstallerSession

3.1 PackageInstallerSession.commit()

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
// frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java

public class PackageInstallerSession extends IPackageInstallerSession.Stub {

@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
if (hasParentSessionId()) {
throw new IllegalStateException(
"Session " + sessionId + " is a child of multi-package session "
+ mParentSessionId + " and may not be committed directly.");
}
if (!markAsCommitted(statusReceiver, forTransfer)) {
return;
}
if (isMultiPackage()) {
final SparseIntArray remainingSessions = mChildSessionIds.clone();
final IntentSender childIntentSender =
new ChildStatusIntentReceiver(remainingSessions, statusReceiver)
.getIntentSender();
RuntimeException commitException = null;
boolean commitFailed = false;
for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
final int childSessionId = mChildSessionIds.keyAt(i);
try {
// commit all children, regardless if any of them fail; we'll throw/return
// as appropriate once all children have been processed
if (!mSessionProvider.getSession(childSessionId)
.markAsCommitted(childIntentSender, forTransfer)) {
commitFailed = true;
}
} catch (RuntimeException e) {
commitException = e;
}
}
if (commitException != null) {
throw commitException;
}
if (commitFailed) {
return;
}
}
mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
}

}

3.2 Handler.Callback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java

public class PackageInstallerSession extends IPackageInstallerSession.Stub {

private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_COMMIT:
handleCommit(); // 调用 handleCommit() 方法
break;
case MSG_ON_PACKAGE_INSTALLED:
... ...
}

return true;
}
};

}

3.3 PackageInstallerSession.handleCommit()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java

public class PackageInstallerSession extends IPackageInstallerSession.Stub {

private void handleCommit() {
... ...

try {
synchronized (mLock) {
commitNonStagedLocked(childSessions); // 调用 commitNonStagedLocked() 方法
}
} catch (PackageManagerException e) {
... ...
}
}

}

3.4 PackageInstallerSession.commitNonStagedLocked()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java

public class PackageInstallerSession extends IPackageInstallerSession.Stub {

private final PackageManagerService mPm;

@GuardedBy("mLock")
private void commitNonStagedLocked(List<PackageInstallerSession> childSessions) throws PackageManagerException {
final PackageManagerService.ActiveInstallSession committingSession = makeSessionActiveLocked();
if (committingSession == null) {
return;
}
if (isMultiPackage()) {
... ...

mPm.installStage(activeChildSessions);
} else {
// 调用 PackageManagerService 的 installStage 方法,这样安装 APK 的代码逻辑就进入了 PackageManagerService 中
mPm.installStage(committingSession);
}
}

}

四、总结

本篇文章讲解了 PackageInstaller 安装 APK 的过程,简单来说就两步:

        ✒  1. 将 APK 的信息通过 IO 流的形式写入到 PackageInstaller.Session 中。

        ✒  2. 调用 PackageInstaller.Session 的 commit() 方法,将 APK 的信息交由 PackageManagerService 处理。