開篇
核心源碼
關鍵類 | 路徑 |
---|---|
PackageInstallerSession.java | frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java |
PackageManagerService.java | frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java |
前言
在本系列上一篇文章 【深入研究 PackageManagerService 系列(5)之 PackageInstaller - APK 安裝流程】 中,我們瞭解了 PackageInstaller 安裝 APK 的流程,最後會將 APK 的信息交由 PMS 處理。那麼 PMS 是如何處理的?這就是我們這篇文章需要分析的。
PackageHandler
commitLocked
在前一篇文章末尾,我們講過 commitLocked 方法,我們回顧下:
private final PackageManagerService mPm;
private void commitLocked() throws PackageManagerException {
... ...
/**
* commitLocked 方法很長,我們主要關注這一行代碼
* 調用 PackageManagerService 的 installStage 方法
* 這樣安裝 APK 的代碼邏輯就進入了 PackageManagerService 中
*/
mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
mInstallerPackageName, mInstallerUid, user, mCertificates);
}
installStage
正式進入 PMS 源碼分析流程,我們看看 installStage 方法:
void installStage(String packageName, File stagedDir, String stagedCid,
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user,
Certificate[][] certificates) {
... ...
// 創建類型爲 INIT_COPY 的消息
final Message msg = mHandler.obtainMessage(INIT_COPY);
final int installReason = fixUpInstallReason(installerPackageName, installerUid,
sessionParams.installReason);
// 創建 InstallParams,它對應於包的安裝數據
final InstallParams params = new InstallParams(origin, null, observer,
sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
verificationInfo, user, sessionParams.abiOverride,
sessionParams.grantedRuntimePermissions, certificates, installReason);
params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
... ...
// 將 InstallParams 通過消息發送出去
mHandler.sendMessage(msg);
}
handleMessage
因爲 PackageHandler 繼承 Handler ,所以我們來看下 PackageHandler 的 HandlerMessage 方法:
public void handleMessage(Message msg) {
try {
doHandleMessage(msg);
} finally {
// 設置了線程的優先級爲後臺線程
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
}
INIT_COPY
接下來看下 INIT_COPY 消息的處理流程:
class PackageHandler extends Handler {
private boolean mBound = false;
final ArrayList<HandlerParams> mPendingInstalls = new ArrayList<HandlerParams>();
... ...
// 用於處理各個類型的消息
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
// 取出 InstallParams
HandlerParams params = (HandlerParams) msg.obj;
// idx 爲當前需要安裝的 APK 個數,mPendingInstalls 裏面保存所有需要安裝的 APK 解析出來的 HandlerParams 參數
int idx = mPendingInstalls.size();
if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
// mBound 用於標識是否綁定了服務(DefaultContainerService),
// 如果已經綁定了,則 mBound 爲true,如果是第一次調用 mBound 爲 false,默認值爲 false。
if (!mBound) {
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
System.identityHashCode(mHandler));
// 如果沒有綁定服務,重新綁定,connectToService 方法內部如果綁定成功會將 mBound 置爲 true
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
return;
} else {
// 綁定服務成功,將請求添加到 ArrayList 類型的 mPendingInstalls 中,等待處理
mPendingInstalls.add(idx, params);
}
} else {
// 已經綁定服務
mPendingInstalls.add(idx, params);
if (idx == 0) {
// 如果是第一個安裝請求,則直接發送事件 MCS_BOUND 觸發處理流程
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
break;
}
... ...
}
connectToService
假設我們是第一次走流程,還沒有綁定服務,則會調用 connectToService() 方法,我們看下流程:
class PackageHandler extends Handler {
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);
/**
* bindServiceAsUser 方法會傳入 mDefContainerConn,
* bindServiceAsUser 方法的處理邏輯和我們調用 bindService 是類似的,
* 服務建立連接後,會調用 onServiceConnected
*/
if (mContext.bindServiceAsUser(service, mDefContainerConn,
Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 如果綁定 DefaultContainerService 成功,mBound 會置爲 ture
mBound = true;
return true;
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return false;
}
這裏可以看到 bind 到了一個 service ,這個 service 的 ComponentName 是 "DEFAULT_CONTAINER_COMPONENT" 這個常量,那我們就來看下這個 ComponentName。
static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
DEFAULT_CONTAINER_PACKAGE,
"com.android.defcontainer.DefaultContainerService");
所以我們知道 bind 的 service 是 DefaultContainerService 。綁定 DefaultContainerService 之後,設定進程的優先級爲 THREAD_PRIORITY_DEFAULT。
然後等 bindServiceAsUser 這個方法執行完則又把線程的優先級設爲 THREAD_PRIORITY_BACKGROUND。
我們這邊要重點提到一個 mDefContainerConn 變量,研究一下:
final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
這下我們知道了 mDefContainerConn 的類型是 DefaultContainerConnection ,那我們來看下 DefaultContainerConnection 這個類。
DefaultContainerConnection
// DefaultContainerConnection 實現了 ServiceConnection,所以在連接成功的時候會調用 onServiceConnected 方法
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));
// 發送了 MCS_BOUND 類型的消息
mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
}
public void onServiceDisconnected(ComponentName name) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
}
}
上文我們提及到 mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM) 方法,
其實就是"綁定" DefaultContainerService。我們知道 bind 一個 Service ,其中負責通信的 ServiceConnection,
而本方法中負責通信的就是 mDefContainerConn。所以一旦綁定成功會執行 mDefContainerConn 的 onServiceConnected 方法。
而現實是當綁定成功後在 onServiceConnected 中將一個 IBinder 轉換成了一個 IMediaContainerService。
這個就是 onServiceConnected 回調函數中根據參數傳進來的 IMediaContainerService.Stub 的對象引用創建的一個遠程代理對象,
後面 PacakgeManagerServic 通過該代理對象訪問 DefaultContainerService 服務。
我們簡單梳理一下以上代碼所做的工作:
✨ mBound 用於標識是否綁定了 DefaultContainerService,默認值爲 false。
✨ DefaultContainerService 是用於檢查和複製可移動文件的服務,這是一個比較耗時的操作,因此 DefaultContainerService 沒有和 PMS 運行在同一進程中,它運行在 com.android.defcontainer 進程,通過 IMediaContainerService 和 PMS 進行 IPC 通信。
彼此之間的 IPC 通信如下圖所示:
✨ connectToService 方法用來綁定 DefaultContainerService。
✨ mHandler.sendEmptyMessage(MCS_BOUND):發送 MCS_BOUND 類型的消息,觸發處理第一個安裝請求。
不知道你是否發現,有兩個發送 MCS_BOUND 類型消息的方法:
// PackageHandler.doHandleMessage(已綁定服務)
mHandler.sendEmptyMessage(MCS_BOUND); // 不帶參數
// DefaultContainerConnection(未綁定服務 - 綁定服務)
mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs)); // 帶參數
MCS_BOUND 分析
不帶參數
case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
// 不帶參數,則此條件不滿足
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
System.identityHashCode(mHandler));
}
// 走這邊的邏輯
if (mContainerService == null) {
// 服務沒有綁定,則走這邊,但是之前我們講解過,發送 MCS_BOUND 時,已經綁定了服務,這顯然是不正常的
if (!mBound) {
// Something seriously wrong since we are not bound and we are not
// waiting for connection. Bail out.
Slog.e(TAG, "Cannot bind to media container service");
for (HandlerParams params : mPendingInstalls) {
// 負責處理服務發生錯誤的情況
params.serviceError();
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 {
// Should never happen ideally.
Slog.w(TAG, "Empty queue");
}
break;
}
帶參數
case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
System.identityHashCode(mHandler));
}
// 帶參數,此條件不滿足
if (mContainerService == null) {
... ...
// 走這邊的邏輯,安裝請求隊列不爲空
} else if (mPendingInstalls.size() > 0) {
// 得到安裝請求隊列第一個請求 HandlerParams
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");
// 如果 HandlerParams 不爲 null 就會調用 HandlerParams 的 startCopy 方法,用於開始複製 APK 的流程
if (params.startCopy()) {
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);
}
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
// 如果安裝請求數不大於 0 就會打印 “Empty queue”
} else {
// Should never happen ideally.
Slog.w(TAG, "Empty queue");
}
break;
}
上面的流程其實很簡單,我們根據是否傳入了 ims 這個參數,走兩條流程,核心的方法就是最終的 startCopy()。
複製 APK
上面我們提過,Copy APK 的操作是調用 HandlerParams 的 startCopy 方法。HandlerParams 是 PMS 中的抽象類,它的實現類爲 PMS 的內部類 InstallParams。
startCopy
private abstract class HandlerParams {
private static final int MAX_RETRIES = 4;
/**
* Number of times startCopy() has been attempted and had a non-fatal
* error.
*/
private int mRetries = 0;
... ...
final boolean startCopy() {
boolean res;
try {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
/**
* mRetries 用於記錄 startCopy 方法調用的次數,調用 startCopy 方法時會先自動加 1
* startCopy 方法嘗試的次數超過了 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 抽象方法,這個方法會在 handleStartCopy 執行完拷貝相關行爲之後,根據 handleStartCopy 做進一步的處理,主要返回狀態碼
handleReturnCode();
return res;
}
... ...
abstract void handleStartCopy() throws RemoteException;
abstract void handleServiceError();
abstract void handleReturnCode();
}
這邊我們還是簡單的看一下 MCS_GIVE_UP 和 MCS_RECONNECT 兩種 message 的處理流程,邏輯相當簡單:
MCS_RECONNECT
case MCS_RECONNECT: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_reconnect");
if (mPendingInstalls.size() > 0) {
if (mBound) {
disconnectService();
}
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
for (HandlerParams params : mPendingInstalls) {
// Indicate service bind error
params.serviceError();
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(params));
}
mPendingInstalls.clear();
}
}
break;
}
判斷安裝請求隊列 mPendingInstalls 是否還有元素,如果有元素先斷開綁定,則再次重新調用 connectToService 方法,我們知道 connectToService() 內部會再次執行綁定 DefaultContainerService,而在綁定成功後會再次發送一個 what 值爲 MCS_BOUND 的 Message,從而又回到了 startCopy 裏面。
MCS_GIVE_UP
case MCS_GIVE_UP: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_giveup too many retries");
HandlerParams params = mPendingInstalls.remove(0);
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(params));
break;
}
直接刪除了安裝請求隊列 mPendingInstalls 裏面下標爲 0 的元素,即取消本次安裝請求。
handleStartCopy
我們發現 handleStartCopy 也是一個抽象的方法,那麼它在哪實現?前面我們說過:HandlerParams 是 PMS 中的抽象類,它的實現類爲 PMS 的內部類 InstallParams。
class InstallParams extends HandlerParams {
/*
* 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.
*/
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
... ...
/**
* 確定 APK 的安裝位置
* onSd: 安裝到 SD 卡
* onInt: 內部存儲即 Data 分區
* ephemeral:安裝到臨時存儲
*/
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;
// APK 不能同時安裝在 SD 卡和 Data 分區
if (onInt && onSd) {
// Check if both bits are set.
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 的少量的信息
* 通過 IMediaContainerService 跨進程調用 DefaultContainerService 的 getMinimalPackageInfo 方法,
* 該方法輕量解析 APK 並得到 APK 的少量信息,
* 輕量解析的原因是這裏不需要得到 APK 的全部信息,APK 的少量信息會封裝到 PackageInfoLite 中。
*/
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) {
... ...
} else {
installFlags = sPmsExt.customizeInstallPkgFlags(installFlags, pkgLite,
mSettings.mPackages, getUser());
loc = installLocationPolicy(pkgLite);
... ...
}
}
/**
* 根據 InstallParams 創建 InstallArgs 對象
* InstallArgs 是一個抽象類,定義了 APK 的安裝邏輯,比如"複製"和"重命名" APK 等
*
* abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
*
* InstallArgs 有 3 個子類,都被定義在 PMS 中:
* FileInstallArgs:用於處理安裝到非 ASEC 的存儲空間的 APK ,也就是內部存儲空間(Data分區)
* AsecInstallArgs:用於處理安裝到 ASEC 中(mnt/asec)即 SD 卡中的 APK
* MoveInstallArgs:用於處理已安裝 APK 的移動的邏輯
*/
final InstallArgs args = createInstallArgs(this);
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
... ...
if (!origin.existing && requiredUid != -1
&& isVerificationEnabled(
verifierUser.getIdentifier(), installFlags, installerUid)) {
... ...
} else {
// 對 APK 進行檢查後就會調用 InstallArgs 的 copyApk 方法進行安裝
ret = args.copyApk(mContainerService, true);
}
}
mRet = ret;
}
... ...
}
FileInstallArgs
OK,我們知道 InstallParams 有三個子類,不同的 InstallArgs 子類會有着不同的處理,那我們現在以 FileInstallArgs 爲例跟蹤學習一下具體的流程:
copyApk
/**
* Logic to handle installation of non-ASEC applications, including copying
* and renaming logic.
*/
class FileInstallArgs extends InstallArgs {
private File codeFile;
... ...
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
try {
return doCopyApk(imcs, temp);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
doCopyApk
調用了 doCopyApk 方法:
private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
... ...
try {
final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
// 用於創建臨時存儲目錄,比如 /data/app/vmdl18300388.tmp ,其中 18300388 是安裝的 sessionId
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;
/**
* 通過 IMediaContainerService 跨進程調用 DefaultContainerService 的 copyPackage 方法,
* 這個方法會在 DefaultContainerService 所在的進程中將 APK 複製到臨時存儲目錄,
* 比如 /data/app/vmdl18300388.tmp/base.apk 。
*/
ret = imcs.copyPackage(origin.file.getAbsolutePath(), target); // 真正的文件拷貝
... ...
return ret;
}
安裝 APK
handleReturnCode
我們回到 APK 的複製調用鏈的頭部方法:HandlerParams 的 startCopy 方法,在最後 調用了 handleReturnCode 方法,進行 APK 的安裝。
private abstract class HandlerParams {
private static final int MAX_RETRIES = 4;
private int mRetries = 0;
... ...
final boolean startCopy() {
boolean res;
try {
if (++mRetries > MAX_RETRIES) {
... ...
} else {
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
... ...
}
// 處理複製 APK 後的安裝 APK 邏輯
handleReturnCode(); // 💥 💥 💥 💥 💥 💥
return res;
}
... ...
abstract void handleReturnCode();
}
handleReturnCode 也是一個抽象方法,那麼在哪裏實現?同樣,它的實現在 InstallParams 中。
@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(InstallArgs,int) 方法
processPendingInstall(mArgs, mRet);
}
}
我們發現調用了 processPendingInstall 方法,繼續跟!
processPendingInstall
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) {
/**
* 安裝前處理
* 用於檢查 APK 的狀態的,在安裝前確保安裝環境的可靠,如果不可靠會清除複製的 APK 文件
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageTracedLI(args, res); // 💥 💥 💥 💥 💥 💥
}
/**
* 安裝後收尾
* 用於處理安裝後的收尾操作,如果安裝不成功,刪除掉安裝相關的目錄與文件
args.doPostInstall(res.returnCode, res.uid);
}
... ...
}
});
}
installPackageTracedLI
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
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);
}
... ...
// Get rid of all references to package scan path via parser.
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 (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 || sPmsExt.isOperatorApp(mPackages, mSettings.mPackages, pkgName)) {
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)) {
res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
return;
}
if (!instantApp) {
startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
} else {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);
}
}
try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
"installPackageLI")) {
if (replace) {
// 替換安裝
if (pkg.applicationInfo.isStaticSharedLibrary()) {
PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
if (existingPkg != null && existingPkg.mVersionCode != pkg.mVersionCode) {
res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
+ "static-shared libs cannot be updated");
return;
}
}
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 方法的代碼很長,這裏截取主要的部分,主要做了幾件事:
✨ 1、創建 PackageParser 解析 APK 。
✨ 2、檢查 APK 是否存在,如果存在就獲取此前沒被改名前的包名,賦值給 PackageParser.Package 類型的 pkg ,將標誌位 replace 置爲 true 表示是替換安裝。
✨ 3、如果 Settings 中保存有要安裝的 APK 的信息,說明此前安裝過該 APK ,則需要校驗 APK 的簽名信息,確保安全的進行替換。
✨ 4、將臨時文件重新命名,比如前面提到的 /data/app/vmdl18300388.tmp/base.apk ,重命名爲 /data/app/包名-1/base.apk 。這個新命名的包名會帶上一個數字後綴 1,每次升級一個已有的 App ,這個數字會不斷的累加。
✨ 5、系統 APP 的更新安裝會有兩個限制,一個是系統 APP 不能在 SD 卡上替換安裝,另一個是系統 APP 不能被 Instant App 替換。
✨ 6、根據 replace 來做區分,如果是替換安裝就會調用 replacePackageLIF 方法,其方法內部還會對系統 APP 和非系統 APP 進行區分處理,如果是新安裝 APK 會調用 installNewPackageLIF 方法。
installNewPackageLIF
我們以安裝新 APK 爲例,查看 installNewPackageLIF 的源碼:
/*
* Install a non-existing package.
*/
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");
... ...
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 件事:
✨ 1、掃描 APK,將 APK 的信息存儲在 PackageParser.Package 類型的 newPackage 中,一個 Package 的信息包含了 1 個 base APK 以及 0 個或者多個 split APK 。
✨ 2、更新該 APK 對應的 Settings 信息,Settings 用於保存所有包的動態設置。
✨ 3、如果安裝成功就爲新安裝的應用程序準備數據,安裝失敗就刪除APK。
scanPackageTracedLI
調用 scanPackageTracedLI() 進行安裝 :
public PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
try {
return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
scanPackageLI - 01
scanPackageTracedLI() 調用了 scanPackageLI() 方法:
/**
* Scans a package and returns the newly parsed package.
* Returns {@code null} in case of errors and the error code is stored in mLastScanError
*/
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCallback(mPackageParserCallback);
... ...
return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
}
scanPackageLI - 02
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, final int policyFlags,
int scanFlags, long currentTime, @Nullable UserHandle user)
throws PackageManagerException {
boolean success = false;
try {
// scanPackageDirtyLI 實際安裝 package 的方法
final PackageParser.Package res = scanPackageDirtyLI(pkg, policyFlags, scanFlags,
currentTime, user);
success = true;
return res;
} finally {
if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
// DELETE_DATA_ON_FAILURES is only used by frozen paths
destroyAppDataLIF(pkg, UserHandle.USER_ALL,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
}
}
}
總結
本文主要講解了 PMS 是如何處理 APK 安裝的流程,主要有幾個步驟:
✨ PackageInstaller 安裝 APK 時會將 APK 的信息交由 PMS 處理,PMS 通過向 PackageHandler 發送消息來驅動 APK 的複製和安裝工作。
✨ PMS 發送 INIT_COPY 和 MCS_BOUND 類型的消息,控制 PackageHandler 來綁定 DefaultContainerService ,完成複製 APK 等工作。
✨ 複製 APK 完成後,會開始進行安裝 APK 的流程,包括安裝前的檢查、安裝 APK 和安裝後的收尾工作。
參考
01. http://liuwangshu.cn/framewor...
02. https://www.jianshu.com/p/c43...