Android 6.0时候,pm install apk 还是直接调用 PackageManager来执行:
private int runInstall() {
.......
LocalPackageInstallObserver obs = new LocalPackageInstallObserver();
try {
VerificationParams verificationParams = new VerificationParams(verificationURI,
originatingURI, referrerURI, VerificationParams.NO_UID, null);
mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
installerPackageName, verificationParams, abi, userId);
synchronized (obs) {
while (!obs.finished) {
try {
obs.wait();
} catch (InterruptedException e) {
}
}
if (obs.result == PackageManager.INSTALL_SUCCEEDED) {
System.out.println("Success");
return 0;
} else {
System.err.println("Failure ["
+ installFailureToString(obs)
+ "]");
return 1;
}
}
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(PM_NOT_RUNNING_ERR);
return 1;
}
}
Android 7.0的时候就改为了session 方式:
/*
* Keep this around to support existing users of the "pm install" command that may not be
* able to be updated [or, at least informed the API has changed] such as ddmlib.
*
* Moving the implementation of "pm install" to "cmd package install" changes the executing
* context. Instead of being a stand alone process, "cmd package install" runs in the
* system_server process. Due to SELinux rules, system_server cannot access many directories;
* one of which being the package install staging directory [/data/local/tmp].
*
* The use of "adb install" or "cmd package install" over "pm install" is highly encouraged.
*/
private int runInstall() throws RemoteException {
final InstallParams params = makeInstallParams();
final int sessionId = doCreateSession(params.sessionParams,
params.installerPackageName, params.userId);
try {
final String inPath = nextArg();
if (inPath == null && params.sessionParams.sizeBytes == 0) {
System.err.println("Error: must either specify a package size or an APK file");
return 1;
}
if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
return 1;
}
if (doCommitSession(sessionId, false /*logSuccess*/)
!= PackageInstaller.STATUS_SUCCESS) {
return 1;
}
System.out.println("Success");
return 0;
} finally {
try {
mInstaller.abandonSession(sessionId);
} catch (Exception ignore) {
}
}
}
Session 是 PackageInstaller 的新机制,主要调用下面接口:
public int createSession(android.content.pm.PackageInstaller.SessionParams params, java.lang.String installerPackageName, int userId) throws android.os.RemoteException;
public android.content.pm.IPackageInstallerSession openSession(int sessionId) throws android.os.RemoteException;
我们知道,Android的跨进程调用都是通过 binder 实现的,SystemServer中的 PackageInstallerService 负责创建session,然后这个session负责把用户提供的apk写到/data/local/tmp目录下,如何做的呢,就是通过 FileBridge,其实就是文件socket, SystemServer中创建一个文件描述符socket,用户进程通过session向这个socket发送apk数据,发送完了调用 session.commit 进行提交。PackagetInstallerSession.java 处理提交过程:
private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
throws PackageManagerException {
...........
// Verify that stage looks sane with respect to existing application.
// This currently only ensures packageName, versionCode, and certificate
// consistency.
validateInstallLocked(pkgInfo, appInfo);
..............
mRelinquished = true;
mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
installerPackageName, installerUid, user, mCertificates);
}
实际的安装过程都在 SystemServer 的 PMS 执行,PackageManagerService.java:
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);
…………..
mHandler.sendMessage(msg);
}
然后查看 PackageHandler 类的 doHandleMessage 函数,首先处理 INIT_COPY 消息,然后处理 MCS_BOUND 消息,
在 MCS_BOUND 消息中执行代码:
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, “queueInstall”,
System.identityHashCode(params));
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, “startCopy”);
if (params.startCopy()) {
这个 startCopy 实现在 HandlerParams 类中:
private abstract class HandlerParams {
..........
final boolean startCopy() {
boolean res;
try {
/// M: 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();
/// M: Slog.i(TAG, "Apk copy done");
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
handleReturnCode();
return res;
}
这个 handleStartCopy 函数也在 InstallParams 类中实现了,通过 FileInstallArgs 类的 copyApk 函数完成复制动作。
最后在 InstallParams 类中 handleReturnCode 函数中调用 processPendingInstall 函数, 这里发出了 POST_INSTALL 消息。
PMS 在 POST_INSTALL 消息处理中调用了 handlePackagePostInstall 函数,这个函数发出了 app 安装或者升级的广播:
// Send added for users that see the package for the first time
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
extras, 0 /*flags*/, null /*targetPackage*/,
null /*finishedReceiver*/, firstUsers);