相關文章 Android包管理機制系列html
在上一篇文章Android包管理機制(二)PackageInstaller安裝APK中,咱們學習了PackageInstaller是如何安裝APK的,最後會將APK的信息交由PMS處理。那麼PMS是如何處理的呢?這篇文章會給你答案。前端
APK的信息交由PMS後,PMS經過向PackageHandler發送消息來驅動APK的複製和安裝工做。 先來查看PackageHandler處理安裝消息的調用時序圖。java
接着上一篇文章的代碼邏輯來查看PMS的installStage方法。 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.javaandroid
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);//1
final int installReason = fixUpInstallReason(installerPackageName, installerUid,
sessionParams.installReason);
final InstallParams params = new InstallParams(origin, null, observer,
sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
verificationInfo, user, sessionParams.abiOverride,
sessionParams.grantedRuntimePermissions, certificates, installReason);//2
params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
...
mHandler.sendMessage(msg);//3
}
複製代碼
註釋2處建立InstallParams,它對應於包的安裝數據。註釋1處建立了類型爲INIT_COPY的消息,在註釋3處將InstallParams經過消息發送出去。git
處理INIT_COPY類型的消息的代碼以下所示。 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#PackageHandlergithub
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
int idx = mPendingInstalls.size();
if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
//mBound用於標識是否綁定了服務,默認值爲false
if (!mBound) {//1
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
System.identityHashCode(mHandler));
//若是沒有綁定服務,從新綁定,connectToService方法內部若是綁定成功會將mBound置爲true
if (!connectToService()) {//2
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
return;
} else {
//綁定服務成功,將請求添加到ArrayList類型的mPendingInstalls中,等待處理
mPendingInstalls.add(idx, params);
}
} else {
//已經綁定服務
mPendingInstalls.add(idx, params);
if (idx == 0) {
mHandler.sendEmptyMessage(MCS_BOUND);//3
}
}
break;
}
....
}
}
}
複製代碼
PackageHandler繼承自Handler,它被定義在PMS中,doHandleMessage方法用於處理各個類型的消息,來查看對INIT_COPY類型消息的處理。註釋1處的mBound用於標識是否綁定了DefaultContainerService,默認值爲false。DefaultContainerService是用於檢查和複製可移動文件的服務,這是一個比較耗時的操做,所以DefaultContainerService沒有和PMS運行在同一進程中,它運行在com.android.defcontainer進程,經過IMediaContainerService和PMS進行IPC通訊,以下圖所示。 安全
註釋2處的connectToService方法用來綁定DefaultContainerService,註釋3處發送MCS_BOUND類型的消息,觸發處理第一個安裝請求。 查看註釋2處的connectToService方法: **frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#PackageHandler **session
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)) {//1
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mBound = true;//2
return true;
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return false;
}
複製代碼
註釋2處若是綁定DefaultContainerService成功,mBound會置爲ture 。註釋1處的bindServiceAsUser方法會傳入mDefContainerConn,bindServiceAsUser方法的處理邏輯和咱們調用bindService是相似的,服務創建鏈接後,會調用onServiceConnected方法: **frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java **app
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, Object));//1
}
public void onServiceDisconnected(ComponentName name) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
}
}
複製代碼
註釋1處發送了MCS_BOUND類型的消息,與PackageHandler.doHandleMessage
方法的註釋3處不一樣的是,這裏發送消息帶了Object類型的參數,這裏會對這兩種狀況來進行講解,一種是消息不帶Object類型的參數,一種是消息帶Object類型的參數。async
消息不帶Object類型的參數 查看對MCS_BOUND類型消息的處理:
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
if (msg.obj != null) {//1
mContainerService = (IMediaContainerService) msg.obj;//2
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
System.identityHashCode(mHandler));
}
if (mContainerService == null) {//3
if (!mBound) {//4
Slog.e(TAG, "Cannot bind to media container service");
for (HandlerParams params : mPendingInstalls) {
params.serviceError();//5
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(params));
if (params.traceMethod != null) {
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER,
params.traceMethod, params.traceCookie);
}
return;
}
//綁定失敗,清空安裝請求隊列
mPendingInstalls.clear();
} else {
//繼續等待綁定服務
Slog.w(TAG, "Waiting to connect to media container service");
}
} else if (mPendingInstalls.size() > 0) {
...
else {
Slog.w(TAG, "Empty queue");
}
break;
}
複製代碼
若是消息不帶Object類型的參數,就沒法知足註釋1處的條件,註釋2處的IMediaContainerService類型的mContainerService也沒法被賦值,這樣就知足了註釋3處的條件。 若是知足註釋4處的條件,說明尚未綁定服務,而此前已經在PackageHandler.doHandleMessage
方法的註釋2處調用綁定服務的方法了,這顯然是不正常的,所以在註釋5處負責處理服務發生錯誤的狀況。若是不知足註釋4處的條件,說明已經綁定服務了,就會打印出系統log,告知用戶等待系統綁定服務。
消息帶Object類型的參數 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
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) {
//若是沒有安裝請求了,發送解綁服務的請求
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");
//若是還有其餘的安裝請求,接着發送MCS_BOUND消息繼續處理剩餘的安裝請求
mHandler.sendEmptyMessage(MCS_BOUND);//5
}
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}else {
Slog.w(TAG, "Empty queue");//6
}
break;
}
複製代碼
若是MCS_BOUND類型消息帶Object類型的參數就不會知足註釋1處的條件,就會調用註釋2處的判斷,若是安裝請求數不大於0就會打印出註釋6處的log,說明安裝請求隊列是空的。安裝完一個APK後,就會在註釋5處發出MSC_BOUND消息,繼續處理剩下的安裝請求直到安裝請求隊列爲空。 註釋3處獲得安裝請求隊列第一個請求HandlerParams ,若是HandlerParams 不爲null就會調用註釋4處的HandlerParams的startCopy方法,用於開始複製APK的流程。
先來查看複製APK的時序圖。
HandlerParams是PMS中的抽象類,它的實現類爲PMS的內部類InstallParams。HandlerParams的startCopy方法以下所示。 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#HandlerParams
final boolean startCopy() {
boolean res;
try {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
//startCopy方法嘗試的次數,超過了4次,就放棄這個安裝請求
if (++mRetries > MAX_RETRIES) {//1
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mHandler.sendEmptyMessage(MCS_GIVE_UP);//2
handleServiceError();
return false;
} else {
handleStartCopy();//3
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
handleReturnCode();//4
return res;
}
複製代碼
註釋1處的mRetries用於記錄startCopy方法調用的次數,調用startCopy方法時會先自動加1,若是次數大於4次就放棄這個安裝請求:在註釋2處發送MCS_GIVE_UP類型消息,將第一個安裝請求(本次安裝請求)從安裝請求隊列mPendingInstalls中移除掉。註釋4處用於處理複製APK後的安裝APK邏輯,第3小節中會再次提到它。註釋3處調用了抽象方法handleStartCopy,它的實如今InstallParams中,以下所示。 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#InstallParams
public void handleStartCopy() throws RemoteException {
...
//肯定APK的安裝位置。onSd:安裝到SD卡, onInt:內部存儲即Data分區,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分區
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);//1
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);//2
...
}
}
//根據InstallParams建立InstallArgs對象
final InstallArgs args = createInstallArgs(this);//3
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
...
if (!origin.existing && requiredUid != -1
&& isVerificationEnabled(
verifierUser.getIdentifier(), installFlags, installerUid)) {
...
} else{
ret = args.copyApk(mContainerService, true);//4
}
}
mRet = ret;
}
複製代碼
handleStartCopy方法的代碼不少,這裏截取關鍵的部分。 註釋1處經過IMediaContainerService跨進程調用DefaultContainerService的getMinimalPackageInfo方法,該方法輕量解析APK並獲得APK的少許信息,輕量解析的緣由是這裏不須要獲得APK的所有信息,APK的少許信息會封裝到PackageInfoLite中。接着在註釋2處肯定APK的安裝位置。註釋3處建立了InstallArgs,InstallArgs 是一個抽象類,定義了APK的安裝邏輯,好比複製和重命名APK等,它有3個子類,都被定義在PMS中,以下圖所示。
其中FileInstallArgs用於處理安裝到非ASEC的存儲空間的APK,也就是內部存儲空間(Data分區),AsecInstallArgs用於處理安裝到ASEC中(mnt/asec)即SD卡中的APK。MoveInstallArgs用於處理已安裝APK的移動的邏輯。 對APK進行檢查後就會在註釋4處調用InstallArgs的copyApk方法進行安裝。 不一樣的InstallArgs子類會有着不一樣的處理,這裏以FileInstallArgs爲例。FileInstallArgs的copyApk方法中會直接return FileInstallArgs的doCopyApk方法: frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#FileInstallArgsprivate int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
...
try {
final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
//建立臨時文件存儲目錄
final File tempDir =
mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);//1
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);//2
...
return ret;
}
複製代碼
註釋1處用於建立臨時存儲目錄,好比/data/app/vmdl18300388.tmp,其中18300388是安裝的sessionId。註釋2處經過IMediaContainerService跨進程調用DefaultContainerService的copyPackage方法,這個方法會在DefaultContainerService所在的進程中將APK複製到臨時存儲目錄,好比/data/app/vmdl18300388.tmp/base.apk。目前爲止APK的複製工做就完成了,接着就是APK的安裝過程了。
照例先來查看安裝APK的時序圖。
咱們回到APK的複製調用鏈的頭部方法:HandlerParams的startCopy方法,在註釋4處會調用handleReturnCode方法,它的實如今InstallParams中,以下所示。 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.javavoid 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);//1
synchronized (mInstallLock) {
installPackageTracedLI(args, res);//2
}
//安裝後收尾
args.doPostInstall(res.returnCode, res.uid);//3
}
...
}
});
}
複製代碼
handleReturnCode方法中只調用了processPendingInstall方法,註釋1處用於檢查APK的狀態的,在安裝前確保安裝環境的可靠,若是不可靠會清除複製的APK文件,註釋3處用於處理安裝後的收尾操做,若是安裝不成功,刪除掉安裝相關的目錄與文件。主要來看註釋2處的installPackageTracedLI方法,其內部會調用PMS的installPackageLI方法。 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
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);//1
} 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);//2
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) {//3
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 (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) {
if (!checkUpgradeKeySetLP(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) {
//系統APP不能在SD卡上替換安裝
res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Cannot install updates to system apps on sdcard");
return;
} else if (instantApp) {
//系統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)) {//4
res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
return;
}
startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
"installPackageLI")) {
if (replace) {//5
//替換安裝
...
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*/);
}
...
}
}
複製代碼
installPackageLI方法的代碼有將近500行,這裏截取主要的部分,主要作了幾件事:
這裏咱們以新安裝APK爲例,會調用PMS的installNewPackageLIF方法。 frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
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) {
//安裝成功後,爲新安裝的應用程序準備數據
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);
}
複製代碼
installNewPackageLIF主要作了如下3件事:
安裝APK的過程就講到這裏,就再也不往下分析下去,有興趣的同窗能夠接着深挖。
本文主要講解了PMS是如何處理APK安裝的,主要有幾個步驟:
參考資料 Android包管理機制
Android app安裝過程分析(基於Nougat)
應用程序安裝流程
這裏不只分享Android、Java和移動前端相關技術,還有行業動態、技術資訊、面經和我的感悟。每月都會爭取送書福利給你們。