应用安装-adb方式进行安装

adb方式进行安装

adb 相关代码在system/core/adb/中,入口在/system/core/adb/client/main.cpp的main方法中:

1
2
3
4
int main(int argc, char** argv) {
adb_trace_init(argv);
return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
}

命令的处理在system/core/adb/commandline.cpp中,与安装相关的代码也在其中,经过参数解析调到下面的函数:

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
static int install_app(int argc, const char** argv) {
// The last argument must be the APK file
const char* file = argv[argc - 1];
if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
return syntax_error("filename doesn't end .apk: %s", file);
}

struct stat sb;
if (stat(file, &sb) == -1) {
fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
return 1;
}

int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
return 1;
}

std::string error;
std::string cmd = "exec:cmd package";

// don't copy the APK name, but, copy the rest of the arguments as-is
while (argc-- > 1) {
cmd += " " + escape_arg(std::string(*argv++));
}

// add size parameter [required for streaming installs]
// do last to override any user specified value
cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));

int remoteFd = adb_connect(cmd, &error);
if (remoteFd < 0) {
fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
adb_close(localFd);
return 1;
}

char buf[BUFSIZ];
copy_to_file(localFd, remoteFd);
read_status_line(remoteFd, buf, sizeof(buf));

adb_close(localFd);
adb_close(remoteFd);

if (!strncmp("Success", buf, 7)) {
fputs(buf, stdout);
return 0;
}
fprintf(stderr, "adb: failed to install %s: %s", file, buf);
return 1;
}

其实是通过adb shell cmd package(包管理命令)进行安装,安装后adb 断开。也就是说在adb shell中通过cmd也可以调用各种adb的命令。但是不能用cmd package install 安装手机上的apk。通过cmd -l可以查看cmd支持的命令:

1
2
3
4
5
20|k400_o:/data/data # cmd -l                                                                                                                 
Currently running services:
AAL
//省略
window

输出结果和adb shell dumpsys -l结果一致。cmd对应的可执行文件为/system/bin/cmd,对应的源码为frameworks/native/cmds/cmd/cmd.cpp,入口方法如下:

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

int main(int argc, char* const argv[])
{ //……
sp<IServiceManager> sm = defaultServiceManager();
fflush(stdout);
//……
if ((argc == 2) && (strcmp(argv[1], "-l") == 0)) {//对应adb shell cmd -l命令
Vector<String16> services = sm->listServices();
services.sort(sort_func);
aout << "Currently running services:" << endl;

for (size_t i=0; i<services.size(); i++) {
sp<IBinder> service = sm->checkService(services[i]);
if (service != NULL) {
aout << " " << services[i] << endl;
}
}
return 0;
}

Vector<String16> args;
for (int i=2; i<argc; i++) {
args.add(String16(argv[i]));
}
String16 cmd = String16(argv[1]);//cmd:package
sp<IBinder> service = sm->checkService(cmd);
//……
sp<MyShellCallback> cb = new MyShellCallback();
sp<MyResultReceiver> result = new MyResultReceiver();

// TODO: block until a result is returned to MyResultReceiver.
status_t err = IBinder::shellCommand(service, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, args, cb, result);
//……
return res;
}

PKMS在启动后会注册到ServiceManager中,所以这里的package服务应该就是PKMS了,看下c++层Binder的shellCommand方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
status_t IBinder::shellCommand(const sp<IBinder>& target, int in, int out, int err,
Vector<String16>& args, const sp<IShellCallback>& callback,
const sp<IResultReceiver>& resultReceiver)
{
Parcel send;
Parcel reply;
send.writeFileDescriptor(in);
send.writeFileDescriptor(out);
send.writeFileDescriptor(err);
const size_t numArgs = args.size();
send.writeInt32(numArgs);
for (size_t i = 0; i < numArgs; i++) {
send.writeString16(args[i]);
}
send.writeStrongBinder(callback != NULL ? IInterface::asBinder(callback) : NULL);
send.writeStrongBinder(resultReceiver != NULL ? IInterface::asBinder(resultReceiver) : NULL);
return target->transact(SHELL_COMMAND_TRANSACTION, send, &reply);
}

只需要关心其中一句return target->transact(SHELL_COMMAND_TRANSACTION, send, &reply);即可。这里的target不难推测应该就是PKMS,但是PKMS中并没用处理SHELL_COMMAND_TRANSACTION,transact的第一个参数从C++ 层到java层一般是不会变的,不停地往父类查找,发现该消息是在Binder.java中处理的。PKMS是Binder的间接子类,不难推测PKMS肯定覆写了Binder的某个方法,而在SHELL_COMMAND_TRANSACTION的后续处理中会调用到该方法,然后流程进入到PKMS中。来看下java层的Binder,看看收到SHELL_COMMAND_TRANSACTION消息会做些什么:

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
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
int flags) throws RemoteException {
if (code == INTERFACE_TRANSACTION) {
reply.writeString(getInterfaceDescriptor());
return true;
} else if (code == DUMP_TRANSACTION) {对应adb shell dumpsys????
ParcelFileDescriptor fd = data.readFileDescriptor();
String[] args = data.readStringArray();
if (fd != null) {
try {
dump(fd.getFileDescriptor(), args);
} finally {
IoUtils.closeQuietly(fd);
}
}
// Write the StrictMode header.
if (reply != null) {
reply.writeNoException();
} else {
StrictMode.clearGatheredViolations();
}
return true;
} else if (code == SHELL_COMMAND_TRANSACTION) {
//……
try {
if (out != null) {
shellCommand(in != null ? in.getFileDescriptor() : null,
out.getFileDescriptor(),
err != null ? err.getFileDescriptor() : out.getFileDescriptor(),
args, shellCallback, resultReceiver);
}
} finally {
//……
}
return true;
}
return false;
}

shellCommand方法:

1
2
3
4
5
6
7
public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@Nullable FileDescriptor err,
@NonNull String[] args, @Nullable ShellCallback callback,
@NonNull ResultReceiver resultReceiver) throws RemoteException {
onShellCommand(in, out, err, args, callback, resultReceiver);
}

onShellCommander方法:

1
2
3
4
5
6
7
8
9
10
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@Nullable FileDescriptor err,
@NonNull String[] args, @Nullable ShellCallback callback,
@NonNull ResultReceiver resultReceiver) throws RemoteException {
FileOutputStream fout = new FileOutputStream(err != null ? err : out);
PrintWriter pw = new FastPrintWriter(fout);
pw.println("No shell command implementation.");
pw.flush();
resultReceiver.send(0, null);
}

上面是Binder.java实现的onShellCommand方法,而PKMS覆写了该方法,所以这里调用的应该是PKMS的onShellCommand方法:

1
2
3
4
5
6
7
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
(new PackageManagerShellCommand(this)).exec(
this, in, out, err, args, callback, resultReceiver);
}

onShellCommand调用了PackageManagerShellCommand的exec方法来执行命令行获取的命令,PackageManagerShellCommand继承自抽象类ShellCommand,而该类是用来辅助实现Binder.onShellCommand方法的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
String cmd;
//……
mCmd = cmd;
mResultReceiver = resultReceiver;
int res = -1;
try {
res = onCommand(mCmd);
} catch (SecurityException e) {//……
} catch (Throwable e) {//……
} finally {
//……
}
if (DEBUG) Slog.d(TAG, "Finished command " + mCmd + " on " + mTarget);
return res;
}

onCommand方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
public int onCommand(String cmd) {
if (cmd == null) {
return handleDefaultCommands(cmd);
}

final PrintWriter pw = getOutPrintWriter();
try {
switch(cmd) {
case "install":
return runInstall();
//省略代码
default:
return handleDefaultCommands(cmd);
}
} catch (RemoteException e) {
pw.println("Remote exception: " + e);
}
return -1;
}

支持的命令很多,这里只关心install,该命令对应runInstall()方法,runInstall()方法可以分为以下几个部分:

  1. 参数解析
1
2
3
final InstallParams params = makeInstallParams();
final String inPath = getNextArg();
setParamsSize(params, inPath);

这部分较简单,就是对前面安装命令中参数的解析并根据参数设置installFlags的标志位。

  1. 创建应用安装会话
1
2
final int sessionId = doCreateSession(params.sessionParams,
params.installerPackageName, params.userId);//adb安装,userid为0
1
2
3
4
5
6
7
8
9
10
11
12
private int doCreateSession(SessionParams params, String installerPackageName, int userId)
throws RemoteException {
userId = translateUserId(userId, "runInstallCreate");
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_SYSTEM;
params.installFlags |= PackageManager.INSTALL_ALL_USERS;
}

final int sessionId = mInterface.getPackageInstaller()
.createSession(params, installerPackageName, userId);
return sessionId;
}

然后调用到PackageInstallerServicecreateSession()方法:

1
2
3
4
5
6
7
8
@Override
public int createSession(SessionParams params, String installerPackageName, int userId) {
try {
return createSessionInternal(params, installerPackageName, userId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
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
 private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
throws IOException {//userid为0
final int callingUid = Binder.getCallingUid();//adb的linux uid为0
mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");

if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
throw new SecurityException("User restriction prevents installing");
}
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID/*0*/)) {
params.installFlags |= PackageManager.INSTALL_FROM_ADB;

} else {
mAppOps.checkPackage(callingUid, installerPackageName);

params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
if ((params.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0
&& !mPm.isCallerVerifier(callingUid)) {
params.installFlags &= ~PackageManager.INSTALL_VIRTUAL_PRELOAD;
}
}

// Only system components can circumvent runtime permissions when installing.
if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
&& mContext.checkCallingOrSelfPermission(Manifest.permission
.INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
throw new SecurityException("You need the "
+ "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
+ "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
}

if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
|| (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
throw new IllegalArgumentException(
"New installs into ASEC containers no longer supported");
}
// Defensively resize giant app icons
if (params.appIcon != null) {
final ActivityManager am = (ActivityManager) mContext.getSystemService(
Context.ACTIVITY_SERVICE);
final int iconSize = am.getLauncherLargeIconSize();
if ((params.appIcon.getWidth() > iconSize * 2)
|| (params.appIcon.getHeight() > iconSize * 2)) {
params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
true);
}
}

switch (params.mode) {
case SessionParams.MODE_FULL_INSTALL:
case SessionParams.MODE_INHERIT_EXISTING:
break;
default:
throw new IllegalArgumentException("Invalid install mode: " + params.mode);
}

// If caller requested explicit location, sanity check it, otherwise
// resolve the best internal or adopted location.
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
//……
} else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
//……
} else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
// For now, installs to adopted media are treated as internal from
// an install flag point-of-view.
params.setInstallFlagsInternal();
} else { //adb 安装走这里
// For now, installs to adopted media are treated as internal from
// an install flag point-of-view.
params.setInstallFlagsInternal();

// Resolve best location for install, based on combination of
// requested install flags, delta size, and manifest settings.
final long ident = Binder.clearCallingIdentity();
try {
params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params);
} finally {
Binder.restoreCallingIdentity(ident);
}
}

final int sessionId;
final PackageInstallerSession session;
synchronized (mSessions) {
// Sanity check that installer isn't going crazy
//……
sessionId = allocateSessionIdLocked();
}

final long createdMillis = System.currentTimeMillis();
// We're staging to exactly one location
File stageDir = null;
String stageCid = null;
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
final boolean isInstant =
(params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
stageDir = buildStageDir(params.volumeUuid, sessionId, isInstant);
} else {
stageCid = buildExternalStageCid(sessionId);
}

session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
params, createdMillis, stageDir, stageCid, false, false);

synchronized (mSessions) {
mSessions.put(sessionId, session);
}

mCallbacks.notifySessionCreated(session.sessionId, session.userId);
writeSessionsAsync();
return sessionId;
}

上面方法的主要作用是创建PackageInstallerSession并添加到mSessions中,并将sessionId返回,该Session在安装结束或失败后都会被销毁。里面涉及到Binder的两种用法,因为和这里的主题关系不大,以后另作了解。

1
final int callingUid = Binder.getCallingUid();//
1
2
final long ident = Binder.clearCallingIdentity();
Binder.restoreCallingIdentity(ident);

上段代码里有个对安装流程比较关键的地方:

1
stageDir = buildStageDir(params.volumeUuid, sessionId, isInstant);

该目录为/data/app/vmdl×××××.tmp(之前了解开机扫描app的过程中好像看到过,这种目录在扫描中会被跳过),从adb读取的apk文件会暂时存放到该目录下,然后apk中的so文件也会提取到该目录。

session的具体创建过程就不了解了,只看下session创建时的参数:

1
2
3
session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
params, createdMillis, stageDir, stageCid, false, false);
  • mInternalCallback封装了Session生命周期中的回调;
  • mContext:Session的上下文
  • mPm:PackageManagerService的实例;
  • mInstallThread.getLooper()安装线程的looper,mInstallThread为HandlerThread实例,从这里可以推断应用安装是在子线程中进行的,该子线程是在PackageInstallerService初始化方法中创建的,而PackageInstallerService的初始化方法是在PKMS的初始化方法中调用的。
  • sessionId作为session的标示;
  • userId这里为0;
  • installerPackageName对应packageInstaller的包名,比如com.android.packageinstaller;
  • callingUid为0
  • params:安装参数;
  • stageDir:apk暂存目录;
  1. 把apk复制到临时目录
1
2
3
4
if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {//把base.apk拷贝到/data/app/vmdl1025879988.tmp目录下
return 1;
}

前面已经构建了应用安装的session信息,接下来这一步会根据sessionId获取session信息(SessionInfo类),然后从标准输入流读入apk文件并写入到/data/app/vmdl×××××.tmp/base.apk.

通过打断点的方式可以看到临时目录的信息:

stageDir

nativeLib的复制会在下一步进行。

  1. 提交会话
1
2
3
4
if (doCommitSession(sessionId, false /*logSuccess*/)
!= PackageInstaller.STATUS_SUCCESS) {//提取lib到/data/app/vmdl1025879988.tmp目录下???
return 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
private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
PackageInstaller.Session session = null;
try {
session = new PackageInstaller.Session(
mInterface.getPackageInstaller().openSession(sessionId));

final LocalIntentReceiver receiver = new LocalIntentReceiver();
session.commit(receiver.getIntentSender());

final Intent result = receiver.getResult();
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
if (status == PackageInstaller.STATUS_SUCCESS) {
if (logSuccess) {
pw.println("Success");
}
} else {
pw.println("Failure ["
+ result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
}
return status;
} finally {
IoUtils.closeQuietly(session);
}
}

调用PackageInstaller.Session#commit()提交installersession,然后把执行的结果写到receiver中,并在标准输出输出结果。再来看下session.commit的具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Attempt to commit everything staged(暂存) in this session. This may require
* user intervention(介入), and so it may not happen immediately. The final
* result of the commit will be reported through the given callback.
* <p>
* Once this method is called, the session is sealed and no additional
* mutations may be performed on the session. If the device reboots
* before the session has been finalized, you may commit the session again.
*
* @throws SecurityException if streams opened through
* {@link #openWrite(String, long, long)} are still open.
*/
public void commit(@NonNull IntentSender statusReceiver) {
try {
mSession.commit(statusReceiver, false);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}

该方法一旦调用该installersession就会封存起来,不再允许进行改变。mSession为IPackageInstallerSession类型,具体类型为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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
Preconditions.checkNotNull(statusReceiver);

final boolean wasSealed;
synchronized (mLock) {
//Check if the caller is the owner of this session. Otherwise throw a
//SecurityException.
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotDestroyedLocked("commit");

final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(
mContext, statusReceiver, sessionId, isInstallerDeviceOwnerLocked(), userId);
mRemoteObserver = adapter.getBinder();

if (forTransfer) {//adb安装forTransfer值为false
mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
if (mInstallerUid == mOriginalInstallerUid) {
throw new IllegalArgumentException("Session has not been transferred");
}
} else {
if (mInstallerUid != mOriginalInstallerUid) {
throw new IllegalArgumentException("Session has been transferred");
}
}

wasSealed = mSealed;
if (!mSealed) {
try {
//Seal the session to prevent further modification and validate the contents of it.
sealAndValidateLocked();
} catch (IOException e) {
throw new IllegalArgumentException(e);
} catch (PackageManagerException e) {
destroyInternal();

// Cannot call dispatchFinal synchronous as this might be called from inside the
// system server on the main thread. Hence the call back scheduled in
// dispachFinal has to be scheduled on a different thread.
mHandler.obtainMessage(MSG_SESSION_FINISHED_WITH_EXCEPTION, e).sendToTarget();

return;
}
}

// Client staging is fully done at this point
//更新callback中的进度,callback存在于Launcher和PackageInstaller中,本地安装
//时会回调到packageInstaller中
mClientProgress = 1f;
computeProgressLocked(true);

// This ongoing commit should keep session active, even though client
// will probably close their end.
mActiveCount.incrementAndGet();

mCommitted = true;//切换到安装线程,提交信息
mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
}

if (!wasSealed) {
// Persist the fact that we've sealed ourselves to prevent
// mutations of any hard links we create. We do this without holding
// the session lock, since otherwise it's a lock inversion.
mCallback.onSessionSealedBlocking(this);
}
}

首先进行了一些检测工作,之后封存session,然后更新进度,该过程会调用一系列的回调方法,最终通知Launcher和PackageInstaller等应用安装进度,再然后从binder线程切换到安装线程,正式进行session的提交,最后将封存的session信息写到/data/system/install_sessions.xml中,该文件的内容大致如下:

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

下面就该了解下MSG_COMMIT这个消息是怎么处理的了,该message是在Handler的callback中处理的,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
case MSG_COMMIT:
synchronized (mLock) {
try {
commitLocked();
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG,
"Commit of session " + sessionId + " failed: " + completeMsg);
destroyInternal();
dispatchSessionFinished(e.error, completeMsg, null);
}
}

break;

首先调用commitLocked()提交会话,如果出现异常则进行一些清理工作,删除安装过程中创建的文件,并将该session标记为destroyed,然后分发session提交失败的消息,该分发过程在安装成功的情况下再介绍,这里先不管。

接着继续看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
46
47
48
49
50
51
  private void commitLocked()
throws PackageManagerException {
//省略一些检测代码
if (needToAskForPermissionsLocked()) {//请求安装权限,adb安装不需要申请权限
// User needs to accept permissions; give installer an intent they
// can use to involve user.
final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);
intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
try {
mRemoteObserver.onUserActionRequired(intent);
} catch (RemoteException ignored) {
}

// Commit was keeping session marked as active until now; release
// that extra refcount so session appears idle.
closeInternal(false);
return;
}
//……
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
//常见的安装模式是MODE_FULL_INSTALL=1,安装时带-p参数时会将该标志位置为1,
//MODE_INHERIT_EXISTING的情况先不管
}

// TODO: surface more granular state from dexopt
mInternalProgress = 0.5f;
computeProgressLocked(true);//更新进度

// Unpack native libraries
extractNativeLibraries(mResolvedStageDir, params.abiOverride);
//……
// We've reached point of no return; call into PMS to install the stage.
// Regardless of success or failure we always destroy session.
final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
//省略
};

final UserHandle user;
if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
} else {
user = new UserHandle(userId);
}

mRelinquished = true;//释放控制
mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
mInstallerPackageName, mInstallerUid, user, mCertificates);
}

首先是做一些检测,然后更新当前进度,之后调用extractNativeLibraries提取so文件到前面提过的临时目录,再然后构造观察者并传递给PKMS进行安装。

至此,会话的提交过程结束,PackageInstallerSession的用处也就到此为止了,接下来的过程会在PKMS中进行。

  1. PKMS复制应用

接着上部分,来看下PKMS的installStage方法:

1
2
3
4
5
6
7
8
9
10
11
12
 void installStage(String packageName, File stagedDir, String stagedCid,
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user,
Certificate[][] certificates) {
//……

final Message msg = mHandler.obtainMessage(INIT_COPY);
//……
msg.obj = params;
//……
mHandler.sendMessage(msg);
}

该方法大部分代码是在构造Message的obj对象,然后将message发送给消息循环进行处理。先来看下mHandler关联的looper是哪个,在PKMS的构造方法中找到mHandler的定义:

mHandler = new PackageHandler(mHandlerThread.getLooper());

再来看下mHandlerThread是什么:

1
2
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);

再来看下ServiceThread的声明:

1
2
3
4
5

/**
* Special handler thread that we create for system services that require their own loopers.
*/
public class ServiceThread extends HandlerThread {//……}

从上面的注释可以知道,ServiceThread是专为需要自己的looper的系统服务创建的HandlerThread,比如说PKMS,应用安装是一种耗时操作,需要在子线程中进行,但是在安装的不同阶段有需要从外界获取信息,以提供不同的服务,所以就需要自己的消息循环。

言归正传,来看下上面发送的消息是什么含义,找到mHandler对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
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();
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
System.identityHashCode(mHandler));
if (params.traceMethod != null) {
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod,
params.traceCookie);
}
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) {
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
break;
}

上面的代码大致做了两件事:调用 connectToService方法绑定服务,然后将Message传递过来的params添加到mPendingInstalls中。来看下connectToService方法干了些什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private boolean connectToService() {
if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" +
" DefaultContainerService");
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
if (mContext.bindServiceAsUser(service, mDefContainerConn,
Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mBound = true;
return true;
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return false;
}

主要就是绑定了下面这个component对应的服务,然后设定优先级:

1
2
3
4
5
static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";

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

来看下这是个什么服务,全局搜索com.android.defcontainer找到对应的AndroidManifest文件,进而找到该组件为frameworks/base/packages/DefaultContainerService中的DefaultContainerService.java来看下这个类的注释:

1
2
3
4
5
6
/**
* Service that offers to inspect and copy files that may reside on removable
* storage. This is designed to prevent the system process from holding onto
* open files that cause the kernel to kill it when the underlying device is
* removed.
*/

也就是说这个类是用来检测和copy可移除存储(从这里看应该也包括临时文件)的文件的。设计这个类的目的是防止持有这个文件的系统进程在存储设备被移除时被内核杀掉,好了,看来后面的copy操作这个类完成的,知道这么多就够了。

再来看下绑定服务时的ServiceConnection对象是怎么定义的,也就是说服务绑定后会发生什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
final private DefaultContainerConnection mDefContainerConn =
new DefaultContainerConnection();
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));
mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
}

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

服务绑定后获得DefaultContainerService服务的本地对象,然后发送MCS_BOUND消息,该消息的obj为服务的对象imcs,该消息是在INIT_COPY消息之后放入消息队列的,所以该消息会在INIT_COPY处理完之后再进行处理,也就是说上面代码中的mPendingInstalls.add(idx, params);会在处理MCS_BOUND消息之前执行。

之后就是在MCS_BOUND消息的处理过程中调用HandlerParams的startCopy()方法开始apk以及lib文件的复制:

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 (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);

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

从这些代码还看不出这里的copy到底是干吗的,再来看下handleStartCopy()方法的注释:

1
2
3
4
5
6
/*
* Invoke remote method to get package information and install
* location values. Override install location based on default
* policy if needed and then create install arguments based
* on the install location.
*/

从上面的注释来看这里的copy是用来获取package信息和安装位置的信息的,如果有必要的话改变安装位置并根据安装位置创建安装参数。调试时发现并没有走copy的过程,结合DefaultContainerService的注释推测这部分的功能可能和SD的安装有关,handleStartCopy()会调用到FileInstallArgscopyApk()方法,最终借由DefaultContainerService完成apk的复制和重命名的过程,再借由NativeLibraryHelper完成native lib的复制。这部分代码量比较大,目前来看这部分对安装过程的理解帮助不大,所以暂时先不做了解了,目前只是推测可能和安装到sd卡有关系。复制完成后会发送MCS_UNBIND消息解除服务的绑定。

到此,PKMS对应用的复制操作告一段落,接下来进行真正的安装过程。

  1. 应用安装

上面了解了PKMS对apk的复制过程,在该过程结束后会返回安装成功与否(只是该阶段)的结果。上面还没有说这个结果是怎么处理的,这个结果是否成功关系着安装是否能继续下去,所以对结果的处理一就是下一步正式安装的开始。从上面的代码不难看出,该过程是在handleReturnCode()方法中进行的,该方法的实现在HandlerParams的子类InstallParams中:

1
2
3
4
5
6
7
8
9
@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(mArgs, mRet);
}
}
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
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
// Queue up an async operation since the package installation may take a little while.
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);
}

// 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.
final boolean update = res.removedInfo != null
&& res.removedInfo.removedPackage != null;
final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;
boolean doRestore = !update
&& ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);

// Set up the post-install work request bookkeeping. This will be used
// and cleaned up by the post-install event handling regardless of whether
// there's a restore pass performed. Token values are >= 1.
int token;
if (mNextInstallToken < 0) mNextInstallToken = 1;
token = mNextInstallToken++;

PostInstallData data = new PostInstallData(args, res);
mRunningInstalls.put(token, data);
if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);

if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
// Pass responsibility to the Backup Manager. It will perform a
// restore if appropriate, then pass responsibility back to the
// Package Manager to run the post-install observer callbacks
// and broadcasts.
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
if (bm != null) {
if (DEBUG_INSTALL) Log.v(TAG, "token " + token
+ " to BM for possible restore");
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
try {
// TODO: http://b/22388012
if (bm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
} else {
doRestore = false;
}
} catch (RemoteException e) {
// can't happen; the backup manager is local
} catch (Exception e) {
Slog.e(TAG, "Exception trying to enqueue restore", e);
doRestore = false;
}
} else {
Slog.e(TAG, "Backup Manager not found!");
doRestore = false;
}
}

if (!doRestore) {
// No restore possible, or the Backup Manager was mysteriously not
// available -- just fire the post-install work request directly.
if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);

Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "postInstall", token);

Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
mHandler.sendMessage(msg);
}
}
});
}

关键的地方有两个:

  • installPackageTracedLI(args, res);
  • POST_INSTALL消息

installPackageTracedLI进行应用的安装,而POST_INSTALL消息对应安装后的操作,比如发送广播告知其他角色进行相应的更新,例如launcher。

先来看下安装的过程:

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

其中installPackageLI代码量较多就不全贴出来了,大致记录下这个方法做了些什么事:

  • 确定scanFlags的值

  • 包解析生成Package对象pkg = pp.parsePackage(tmpPackageFile, parseFlags);

  • 更新pkg的信息,包括cpuAbiOverride,签名,证书,packageName,权限等内容,另外如果该应用已经存在,需要根据原有信息判断安装的apk是否与原apk兼容,包括包名以及是否是降级安装等,并将正在安装的应用包名改为和已有应用一致。

  • 获取abi信息,更新sharedLibraries信息

    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
    if (args.move != null) {
    //……
    // We did an in-place move, so dex is ready to roll
    } 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);
    final boolean extractNativeLibs = !pkg.isLibrary();
    derivePackageAbi(pkg, new File(pkg.codePath), abiOverride,
    extractNativeLibs, mAppLib32InstallDir);
    } 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 {
    updateSharedLibrariesLPr(pkg, null);
    } catch (PackageManagerException e) {
    Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
    }
    }
    }
  • 应用文件夹重命名,并更新pkg中的相关信息

    1
    2
    3
    4
    5

    if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
    res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
    return;
    }

    之后应用所在的文件夹更名为类似于“com.u17.comic.phone-u3tLbNr2Lkty3W30ltXxFQ==”的形式,该目录仍位于/data/app/下。

  • 需要的话,进行odex优化

  • 验证IntentFilterstartIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);

  • 杀掉对应应用(如果该应用存在),调用replacePackageLIF替换已有应用或者调用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
      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");

    // Remember this for later, in case we need to rollback this install
    String pkgName = pkg.packageName;
    //检测是否是新应用,若不是则退出并保存错误信息
    try {
    PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags,
    System.currentTimeMillis(), user);

    updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);

    if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
    prepareAppDataAfterInstallLIF(newPackage);

    } else {
    // Remove package from internal structures, but keep around any
    // data that might have already existed
    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);
    }

    关键的代码有三行:

    • PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags, System.currentTimeMillis(), user);用来扫描pkg的信息,这部分在开机安装应用部分已经了解过了,主要就是更新PKMS中的PackageSetting等信息,并将包的信息添加到系统中,之后该包的信息就可以被查询和解析了。

    • updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);更新mSettings中的信息,并写入到packages.xml中,这样再次开机就可以直接从该文件读取应用信息。

    • prepareAppDataAfterInstallLIF(newPackage);准备应用的数据。

到这里installPackageTracedLI(args, res); 的工作就结束了,接下来要做的就是通知系统的其他角色应用已经安装完成,该工作是在对POST_INSTALL消息中进行处理的,为了节省控件就不贴这部分代码了,该消息的处理过程关键是调用handlePackagePostInstall方法。该方法的主要工作如下:

  • 授予应用权限

    1
    2
    3
    4
    5
    6
    7
    8
    // Now that we successfully installed the package, grant runtime
    // permissions if requested before broadcasting the install. Also
    // for legacy apps in permission review mode we clear the permission
    // review flag which is used to emulate runtime permissions for
    // legacy apps.
    if (grantPermissions) {//安装时带-g参数
    grantRequestedRuntimePermissions(res.pkg, res.newUsers, grantedPermissions);
    }
  • 发送广播,其中包括ACTION_PACKAGE_ADDEDACTION_PACKAGE_REPLACED

  • 调用installObserver的onPackageInstalled方法,installObserver是之前从InstallArgs中传递过来的,该回调中会发送ACTION_SESSION_COMMITTED广播,launcher会接受该广播,并更新应用的信息。

广播的处理涉及到Launcher和PackageInstall等应用,这里暂时不做了解了,等以后有机会再说~~~

adb安装应用大致过程:

​ adb命令解析 : cmd.cpp - PKMS - PackageManagerShellCommand

​ -> 参数解析 : InstallParams

​ -> 创建session: PackageInstallerService - PackageInstallSession

​ -> 应用复制到临时目录

​ -> 提交session: 更新进度,提取nativelib, 添加回调

​ -> 应用安装:文件夹重命名,添加/更新应用信息到系统

​ -> 发送广播,调用回调,通知应用安装完成。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2018 - 2022 得一 All Rights Reserved.

访客数 : | 访问量 :