一、前言
在 APK安裝概述 中曾提及apk有四種安裝場景,但無論是哪一種方式,最終會提交給 PackageManagerService 處理,只是前置的處理鏈路 不同,所以本篇先對 PMS 這一主要過程進行分析。frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java 【基于Android 9.0】
二、PMS 做了哪些工作
1、對特定的一些系統(tǒng)進程信息進行設置處理,并保存到 Settings 中
2、解析 /etc/permissions 下相關xml文件取得系統(tǒng)相關權限、系統(tǒng)具備的相關功能等信息
3、解析 /data/system/package.xml 文件獲取已安裝應用的相關信息
4、對相關的 apk 和 jar 進行 dex 優(yōu)化處理,主要是 /system/framework 目錄下的相關jar和apk
5、依據(jù) sharedUserId 這個配置來確定 apk 運行在哪個進程,然后把運行的相關進程信息加入到 Settings 中,使得系統(tǒng)可以知道每個 apk 運行在哪個進程中
6、解析 AndroidManifest.xml 文件,提煉文件中的節(jié)點信息
7、掃描本地文件,主要針對系統(tǒng)應用、本地安裝應用等等
8、管理本地 apk ,包括安裝、刪除等
三、安裝過程
3.1 準備工作
前面說到 APK 的信息會提交給 PMS 進行安裝的一系列工作,具體是通過 PackageHandler 發(fā)送消息來驅(qū)動 APK 的復制和安裝,其時序圖如下:

上相過程中有幾點需要說明:
1、在 installStage 方法中創(chuàng)建了 InstallParams 對象,它對應于包的安裝數(shù)據(jù),并創(chuàng)建 INIT_COPY 消息并發(fā)送給 PackageHandler 進行處理;
2、PackageHandler 在處理 INIT_COPY 消息時,會先判斷是否綁定了 DefaultContainerService ,這是用于檢查和賦值可移動文件的服務,比較耗時,所以和 PMS 并沒有運行在同一個進程中,它們之間通過 IMediaContainerService 進行 IPC 通信,沒有綁定則會進行綁定,之后

DefaultContainerConnection 同樣是定義在 PMS 中,執(zhí)行鏈路如下:
PackageHandler -> doHandleMessage(INIT_COPY)
-> connectToService()
{
連接成功
mBound = true;
}
-> bindServiceAsUser()
-> DefaultContainerConnection
-> onServiceConnected()
-> sendMessage(MCS_BOUND)
3、發(fā)送 MCS_BOUND 消息時,根據(jù)發(fā)送的 Message 是否帶 Object 分為兩種,如下所示:
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
......
//mBound用于標識是否綁定了服務,默認值為false
if (!mBound) {
......
if (!connectToService()) {
// 綁定 DefaultContainerConnection 成功之后會發(fā)送
// mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, Object));
......
//綁定服務失敗則return
return;
} else {
//綁定服務成功,將請求添加到ArrayList類型的mPendingInstalls中,等待處理
mPendingInstalls.add(idx, params);
}
} else {
//已經(jīng)綁定服務
mHandler.sendEmptyMessage(MCS_BOUND);
}
break;
}
......
}
}
}
4、 MCS_BOUND 消息的處理:
case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
if (msg.obj != null) {
...
}
if (mContainerService == null) {//1
...
} else if (mPendingInstalls.size() > 0) {//2
HandlerParams params = mPendingInstalls.get(0);//3
if (params != null) {
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(params));
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
if (params.startCopy()) {//4
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Checking for more work or unbind...");
//如果APK安裝成功,刪除本次安裝請求
if (mPendingInstalls.size() > 0) {
mPendingInstalls.remove(0);
}
if (mPendingInstalls.size() == 0) {
if (mBound) {
//如果沒有安裝請求了,發(fā)送解綁服務的請求
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Posting delayed MCS_UNBIND");
removeMessages(MCS_UNBIND);
Message ubmsg = obtainMessage(MCS_UNBIND);
sendMessageDelayed(ubmsg, 10000);
}
} else {
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Posting MCS_BOUND for next work");
//如果還有其他的安裝請求,接著發(fā)送MCS_BOUND消息繼續(xù)處理剩余的安裝請求
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}else {
Slog.w(TAG, "Empty queue");
}
break;
}
3.2 復制APK
3.2.1 復制過程時序圖
HandlerParams 是 PMS 中的抽象類,它的實現(xiàn)類為 PMS 的內(nèi)部類 InstallParams。HandlerParams 的 startCopy 方法如下所示:

3.2.2 復制過程的源碼分析
PackageManagerService.java#HandlerParams
final boolean startCopy() {
boolean res;
try {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
//startCopy方法嘗試的次數(shù),超過了4次,就放棄這個安裝請求
if (++mRetries > MAX_RETRIES) {
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
//MCS_GIVE_UP類型消息,將本次安裝請求從安裝請求隊列mPendingInstalls中移除掉
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;
}
在 注釋① 處調(diào)用抽象方法 handleStartCopy ,具體實現(xiàn)在 InstallParams 中,如下所示:
PackageManagerService.java#InstallParams
public void handleStartCopy() throws RemoteException {
...
//確定APK的安裝位置。onSd:安裝到SD卡, onInt:內(nèi)部存儲即Data分區(qū),ephemeral:安裝到臨時存儲(Instant Apps安裝)
final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
PackageInfoLite pkgLite = null;
if (onInt && onSd) {
// APK不能同時安裝在SD卡和Data分區(qū)
Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
//安裝標志沖突,Instant Apps不能安裝到SD卡中
} else if (onSd && ephemeral) {
Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
//獲取APK的少量的信息
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
packageAbiOverride);
if (DEBUG_EPHEMERAL && ephemeral) {
Slog.v(TAG, "pkgLite for install: " + pkgLite);
}
...
if (ret == PackageManager.INSTALL_SUCCEEDED) {
//判斷安裝的位置
int loc = pkgLite.recommendedInstallLocation;
if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
}
...
}else{
loc = installLocationPolicy(pkgLite); // 注釋①
...
}
}
//根據(jù)InstallParams創(chuàng)建InstallArgs對象
final InstallArgs args = createInstallArgs(this); // 注釋②
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
...
if (!origin.existing && requiredUid != -1
&& isVerificationEnabled(
verifierUser.getIdentifier(), installFlags, installerUid)) {
...
} else{
ret = args.copyApk(mContainerService, true); // 注釋③
}
}
mRet = ret;
}
1、注釋① 處確定了 APK 的安裝位置。
2、注釋②處創(chuàng)建 InstallArgs 對象,此對象是一個抽象類,定義了 APK 的復制和重命名APK等安裝邏輯,在 Android 8.x 及之前的版本中有三個子類:FileInstallArgs、AsecInstallArgs、MoveInstallArgs。其中 FileInstallArgs 用于處理安裝到非ASEC的存儲空間的APK,即內(nèi)部存儲空間(Data分區(qū));AsecInstallArgs 用于處理安裝到ASEC(mnt/asec)即SD卡中的APK;MoveInstallArgs 用于處理已安裝APK的移動的邏輯;但在 Android 9.x 之后已經(jīng)去掉了 AsecInstallArgs ,
3、注釋③ 處調(diào)用 InstallArgs 的 copyApk 方法,這里以 FileInstallArgs 的實現(xiàn)為例,內(nèi)部會調(diào)用 FileInstallArgs 的 doCopyApk 方法:
private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
...
try {
final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
//創(chuàng)建臨時文件存儲目錄
final File tempDir =
mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral); // 注釋①
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
Slog.w(TAG, "Failed to create copy file: " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
...
int ret = PackageManager.INSTALL_SUCCEEDED;
ret = imcs.copyPackage(origin.file.getAbsolutePath(), target); // 注釋②
...
return ret;
}
1、注釋① 處用于創(chuàng)建臨時存儲目錄,比如 /data/app/vmdl18300388.tmp,其中 18300388 是安裝的 sessionId;
2、注釋②處通過 IMediaContainerService 跨進程調(diào)用 DefaultContainerService 的 copyPackage 方法,這個方法會在 DefaultContainerService 所在的進程中將 APK 復制到臨時存儲目錄,比如 /data/app/vmdl18300388.tmp/base.apk ,至此 APK 的復制工作結束。
3.3 APK的安裝
3.3.1 APK安裝時序圖
在上述 APK 的賦值調(diào)用鏈的過程中,在 HandlerParams 的 startCopy 方法中,會調(diào)用 handleReturnCode 方法,時序圖如下:

3.3.2 安裝源碼分析
PackageManagerService#handleReturnCode:
void handleReturnCode() {
if (mArgs != null) {
processPendingInstall(mArgs, mRet);
}
}
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
PackageInstalledInfo res = new PackageInstalledInfo();
res.setReturnCode(currentStatus);
res.uid = -1;
res.pkg = null;
res.removedInfo = null;
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
//安裝前處理
args.doPreInstall(res.returnCode); //注釋①
synchronized (mInstallLock) {
installPackageTracedLI(args, res); //注釋②
}
//安裝后收尾
args.doPostInstall(res.returnCode, res.uid); //注釋③
}
...
// 安裝結束發(fā)送消息
Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0); // 注釋⑤
mHandler.sendMessage(msg);
}
});
}
注釋① 處檢查APK的狀態(tài),在安裝前確保安裝環(huán)境的可靠,如果不可靠會清除復制的APK文件,注釋③ 處會檢測是否安裝成功,失敗則刪除安裝相關的目錄和文件。安裝完成之后在 注釋⑤ 處會發(fā)送 POST_INSALL 消息通知已安裝完成,此處稍后會說明。
注釋② 處的 installPackageTracedLI 會調(diào)用 PMS 的 installPackageLI 方法:
PackageManagerService.java#installPackageLI:
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
...
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final PackageParser.Package pkg;
try {
//解析APK
pkg = pp.parsePackage(tmpPackageFile, parseFlags); //注釋①
} catch (PackageParserException e) {
res.setError("Failed parse during installPackageLI", e);
return;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
...
pp = null;
String oldCodePath = null;
boolean systemApp = false;
synchronized (mPackages) {
// 檢查APK是否存在
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
String oldName = mSettings.getRenamedPackageLPr(pkgName);//獲取沒被改名前的包名
if (pkg.mOriginalPackages != null
&& pkg.mOriginalPackages.contains(oldName)
&& mPackages.containsKey(oldName)) {
pkg.setPackageName(oldName); //注釋②
pkgName = pkg.packageName;
replace = true;//設置標志位表示是替換安裝
if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="
+ oldName + " pkgName=" + pkgName);
}
...
}
PackageSetting ps = mSettings.mPackages.get(pkgName);
//查看Settings中是否存有要安裝的APK的信息,如果有就獲取簽名信息
if (ps != null) { //注釋③
if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
PackageSetting signatureCheckPs = ps;
if (pkg.applicationInfo.isStaticSharedLibrary()) {
SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
if (libraryEntry != null) {
signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
}
}
//檢查簽名的正確性
if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ pkg.packageName + " upgrade keys do not match the "
+ "previously installed version");
return;
}
}
...
}
int N = pkg.permissions.size();
for (int i = N-1; i >= 0; i--) {
//遍歷每個權限,對權限進行處理
PackageParser.Permission perm = pkg.permissions.get(i);
BasePermission bp = mSettings.mPermissions.get(perm.info.name);
}
}
}
if (systemApp) {
if (onExternal) {
//系統(tǒng)APP不能在SD卡上替換安裝
res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Cannot install updates to system apps on sdcard");
return;
} else if (instantApp) {
//系統(tǒng)APP不能被Instant App替換
res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
"Cannot update a system app with an instant app");
return;
}
}
...
//重命名臨時文件
if (!args.doRename(res.returnCode, pkg, oldCodePath)) { //注釋④
res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
return;
}
startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
"installPackageLI")) {
if (replace) { //注釋⑤
//替換安裝
...
replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
installerPackageName, res, args.installReason);
} else {
//安裝新的APK
installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
args.user, installerPackageName, volumeUuid, res, args.installReason);
}
}
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
//更新應用程序所屬的用戶
res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
ps.setUpdateAvailable(false /*updateAvailable*/);
}
...
}
}
這里需要說明幾點:
1、注釋③處,會先檢測 Settings 中保存有要安裝的 APK 信息,則說明安裝該 APK ,因此需要檢驗APK 的簽名信息,確保安全的進行替換。
2、注釋④處,會對臨時文件重新命名,例如 /data/app/vmdl18300388.tmp/base.apk,重命名為 /data/app/包名-oONlnRRPYyleU63AveqbYA==/base.apk。新的包名后面帶上的一串字母和數(shù)字的混合字符串,是使用MD5的方式對隨機生成的16個字符進行加密之后的產(chǎn)物。
3、注釋⑤處,根據(jù) replace 來做區(qū)分,如果是替換安裝就會調(diào)用replacePackageLIF方法,其方法內(nèi)部還會對系統(tǒng)APP和非系統(tǒng)APP進行區(qū)分處理,如果是新安裝APK會調(diào)用installNewPackageLIF方法
PackageManagerService.java#installNewPackageLIF:
private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags,
int scanFlags, UserHandle user, String installerPackageName, String volumeUuid,
PackageInstalledInfo res, int installReason) {
...
try {
//掃描APK
PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags,
System.currentTimeMillis(), user);
//更新Settings信息
updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
//安裝成功后,為新安裝的應用程序準備數(shù)據(jù)
prepareAppDataAfterInstallLIF(newPackage);
} else {
//安裝失敗則刪除APK
deletePackageLIF(pkgName, UserHandle.ALL, false, null,
PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
}
} catch (PackageManagerException e) {
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
3.4 發(fā)送消息
在上面 processPendingInstall 方法的源碼分析中,在 注釋⑤ 處會發(fā)送 POST_INSTALL 消息通知安裝完成,那么接下來就來具體看一看在 PackageHandler 中是怎么處理這個消息的。
class PackageHandler extends Handler {
void doHandleMessage(Message msg) {
switch (msg.what) {
... ...
case POST_INSTALL: {
... ...
handlePackagePostInstall();
... ...
break;
}
}
}
}
private void handlePackagePostInstall(...){
// 如果已經(jīng)成功的安裝了應用,在發(fā)送廣播之前先授予一些必要的權限
grantRequestedRuntimePermissions(res.pkg, args.user.getIdentifier(),
args.installGrantPermissions);
// 安裝完成之后發(fā)送"ACTION_PACKAGE_ADDED"廣播
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
packageName, extras, null, null, firstUsers);
// 如果是升級更新安裝,還會發(fā)送ACTION_PACKAGE_REPLACED和ACTION_MY_PACKAGE_REPLACED廣播
// 這兩個廣播不同之處在于PACKAGE_REPLACE將攜帶一個extra信息
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
packageName, extras, null, null, updateUsers);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null, null, packageName, null, updateUsers);
// 執(zhí)行gc操作
Runtime.getRuntime().gc();
// 調(diào)用FileInstallArgs的doPostDeleteLI進行資源清理
res.removedInfo.args.doPostDeleteLI(true);
// 回調(diào)onPackageInstalled方法
installObserver.onPackageInstalled(res.name, res.returnCode,
res.returnMsg, extras);
}
以上為主要的方法摘要,具體可總結為:
1、第一步:這里主要是先將安裝信息從安裝列列表中移除,這個也是前面在processPendingInstall中添加的
2、第二步:安裝成功后,獲取運行時權限
3、第三步:獲取權限后,發(fā)送ACTION_PACKAGE_ADDED廣播,告訴Laucher之流,更新icon
4、第四步:如果是升級更新則在發(fā)送兩條廣播
- ACTION_PACKAGE_REPLACED:一個新版本的應用安裝到設備上,替換換之前已經(jīng)存在的版本
- ACTION_MY_PACKAGE_REPLACED:應用的新版本替換舊版本被安裝,只發(fā)給被更新的應用自己
5、第五步:如果安裝包中設置了PRIVATE_FLAG_FORWARD_LOCK或者被要求安裝在SD卡上,則調(diào)用sendResourcesChangedBroadcast方法來發(fā)送一個資源更改的廣播
6、第六步:如果該應用是一個瀏覽器,則要清除瀏覽器設置,重新檢查瀏覽器設置
7、第七步:強制調(diào)用gc,出發(fā)JVM進行垃圾回收操作
8、第八步:刪除舊的安裝信息
9、回調(diào)回調(diào) IPackageInstallObserver2 的 packageInstalled 方法。告訴 PackageInstaller 安裝結果。從而實現(xiàn)了安裝回調(diào)到UI層
3.5 安裝總結
上述幾部分大致說明 PMS 處理 APK 的主要步驟,可總結如下:
1、當 PackageInstaller 將 APK 的信息提交給 PMS 處理,PMS 會通過向 PackageHandler 發(fā)送消息來驅(qū)動 APK 的復制和安裝工作
2、PMS 發(fā)送 INIT_COPY 和 MCS_BOUND 類型的消息,控制 PackageHandler 來綁定 DefaultContainerService 來完成 APK 的復制等工作
3、復制 APK 完成之后,則開始進行安裝 APK 的流程,包括安裝前的檢查、安裝 APK 和安裝后的收尾工作。
參考
[ 1 ] https://maoao530.github.io/2017/01/18/package-install/
[ 2 ] https://blog.csdn.net/yiranfeng/article/details/103941371
[ 3 ] http://liuwangshu.cn/framework/pms/3-pms-install.html
[ 4 ] https://www.freesion.com/article/5119749905/
[ 5 ] http://www.itdecent.cn/p/9ddb930153b7