PackageManagerService 系列文章以下(基於 Android 9.0 源碼)
🍁 Framework 核心服務之 PackageManagerService 鑽研(1)- 啓動流程
🍁 Framework 核心服務之 PackageManagerService 鑽研(2)- 構造函數
🍁 Framework 核心服務之 PackageManagerService 鑽研(3)- PackageManager
🍁 Framework 核心服務之 PackageManagerService 鑽研(4)- PackageInstaller
🍁 Framework 核心服務之 PackageManagerService 鑽研(5)- APK 安裝流程(PackageInstaller)
🍁 Framework 核心服務之 PackageManagerService 鑽研(6)- APK 安裝流程(PMS)
🍁 Framework 核心服務之 PackageManagerService 鑽研(7)- PackageParserhtml
關鍵類 | 路徑 |
---|---|
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 是如何處理的?這就是咱們這篇文章須要分析的。java
在前一篇文章末尾,咱們講過 commitLocked 方法,咱們回顧下:android
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); }
正式進入 PMS 源碼分析流程,咱們看看 installStage 方法:segmentfault
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); }
由於 PackageHandler 繼承 Handler ,因此咱們來看下 PackageHandler 的 HandlerMessage 方法:安全
public void handleMessage(Message msg) { try { doHandleMessage(msg); } finally { // 設置了線程的優先級爲後臺線程 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } }
接下來看下 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() 方法,咱們看下流程:session
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。app
static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName( DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService");
因此咱們知道 bind 的 service 是 DefaultContainerService 。綁定 DefaultContainerService 以後,設定進程的優先級爲 THREAD_PRIORITY_DEFAULT。async
而後等 bindServiceAsUser 這個方法執行完則又把線程的優先級設爲 THREAD_PRIORITY_BACKGROUND。ide
咱們這邊要重點提到一個 mDefContainerConn 變量,研究一下:
final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
這下咱們知道了 mDefContainerConn 的類型是 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)); // 帶參數
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()。
上面咱們提過,Copy APK 的操做是調用 HandlerParams 的 startCopy 方法。HandlerParams 是 PMS 中的抽象類,它的實現類爲 PMS 的內部類 InstallParams。
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 的處理流程,邏輯至關簡單:
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 裏面。
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 也是一個抽象的方法,那麼它在哪實現?前面咱們說過: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; } ... ... }
OK,咱們知道 InstallParams 有三個子類,不一樣的 InstallArgs 子類會有着不一樣的處理,那咱們如今以 FileInstallArgs 爲例跟蹤學習一下具體的流程:
/** * 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 方法:
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 的複製調用鏈的頭部方法: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 方法,繼續跟!
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); } ... ... } }); }
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); } }
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 方法的代碼很長,這裏截取主要的部分,主要作了幾件事:
✨ 一、建立 PackageParser 解析 APK 。
✨ 二、檢查 APK 是否存在,若是存在就獲取此前沒被更名前的包名,賦值給 PackageParser.Package 類型的 pkg ,將標誌位 replace 置爲 true 表示是替換安裝。
✨ 三、若是 Settings 中保存有要安裝的 APK 的信息,說明此前安裝過該 APK ,則須要校驗 APK 的簽名信息,確保安全的進行替換。
✨ 四、將臨時文件從新命名,好比前面提到的 /data/app/vmdl18300388.tmp/base.apk ,重命名爲 /data/app/包名-1/base.apk 。這個新命名的包名會帶上一個數字後綴 1,每次升級一個已有的 App ,這個數字會不斷的累加。
✨ 五、系統 APP 的更新安裝會有兩個限制,一個是系統 APP 不能在 SD 卡上替換安裝,另外一個是系統 APP 不能被 Instant App 替換。
✨ 六、根據 replace 來作區分,若是是替換安裝就會調用 replacePackageLIF 方法,其方法內部還會對系統 APP 和非系統 APP 進行區分處理,若是是新安裝 APK 會調用 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 件事:
✨ 一、掃描 APK,將 APK 的信息存儲在 PackageParser.Package 類型的 newPackage 中,一個 Package 的信息包含了 1 個 base APK 以及 0 個或者多個 split APK 。
✨ 二、更新該 APK 對應的 Settings 信息,Settings 用於保存全部包的動態設置。
✨ 三、若是安裝成功就爲新安裝的應用程序準備數據,安裝失敗就刪除APK。
調用 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); } }
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); }
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...