上篇文章我們分析了android M PackageManagerService 啓動過程,今天我們繼續深入瞭解下對於apk的安裝,PackageManager又是怎樣做的呢?
應用程序安裝有很多種方法,開發者最常見的就是使用adb install命令或者pm install腳本命令。
很多的應用市場也都有自己的封裝,android原生的安裝應用爲PackageInsteller。
本篇涉及源碼路徑:
system/core/adb/ commandline.cpp
frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java
frameworks/base/packages/DefaultContainerService/
frameworks/base/core/java/android/content/pm/PackageParser.java
adb install原理
我們先看adb install原理
adb install命令來源於/android/system/core/adb工具,從上面的代碼看到,adb install實際執行的是install_app()。
Install_app會根據存儲路徑將apk文件copy到/data/local/temp或者sd卡中的路徑,後面進行apk複製時會從這個路徑裏面取。
這裏我們可以看到adb命令最終調用的是pm腳本來進行apk的安裝。
PM腳本
pm是android中的一個腳本,由pm.java編譯生成,最終被放到system/bin下的,其代碼路徑爲:
frameworks/base/cmds/pm
pm腳本的main方法中創建一個Pm對象,然後調用其run方法跑起來。
run方法中獲取到package包管理器的binder客戶端本地代理Proxy,就可以和IPackageManager服務端通信了。
這裏還通過binder通信獲取到之前在PKMS構造函數最後完成時創建的PackageInstallerService服務,後面安裝時會用到它。
接着,解析arg參數,匹配到install時,會執行runInstall。
這裏先創建了一個LocalPackageInstallObserver,這個obs將用於接收PKMS返回的安裝消息。
最終,我們通過installPackageAsUser調用到了PKMS中。
怎麼過去的?
this指的是mRemote, mRemote即是package的服務端PKMS。
installPackageAsUser方法
前面講到PM腳本通過binder將安裝命令發送到了PKMS中,接下來看PKMS是怎樣進行apk安裝的。
說明:
- 檢查是否具有安裝權限,或者是否用戶禁止安裝等;
- 封裝一個INIT_COPY的消息,並且創建InstallParams對象,然後把消息通過handler發送出去;
根據PKMS啓動過程,我們知道,之前創建了PackageHandler,且其消息處理線程已經運行起來,很確定,INIT_COPY就是在PackageHandler的handleMessage中處理的。
PackageHandler的handleMessage方法直接調用的是doHandleMessage,根據msg類型匹配到INIT_COPY分支。
Apk的安裝需要依賴一個apk提供的服務來支持,這個服務就是DefaultContainerService,即源碼中的DefaultCotainerService.apk。
第一次安裝的時候,由於服務還未啓動,需要bindService來啓動該服務,如果已經啓動,則將安裝信息通過MCS_BOUND觸發。
關於DefaultContainerService怎麼啓動就不說了哈,我們直接看對MCS_BOUND消息的處理。
在MCS_BOUND中,調用了startCopy方法。
startCopy方法中會緊接着調用handleStartCopy,這個方法是個抽象方法,具體的實現由子類完成,之前我們創建的handleParams爲InstallParams,所以這裏會調用到InstallParams的handleStartCopy方法中,如果安裝完成,則調用handleReturnCode,返回處理結果。
handleStartCopy
說明:
- DCS的getMinimalPackageInfo方法會去解析apk文件,得到一個PackageLite對象,該對象是一個輕量級(相對於pkg)的用於描述APK的結構,這裏面會取到recommendedInstallLocation,表示apk推薦的安裝路徑。
- 計算apk的大小,檢查存儲空間是否足夠;
- installLocationPolicy檢查檢查推薦的安裝路徑,例如系統apk不允許安裝在SD卡上;
- createInstallArgs將根據不同的安裝位置創建不同的InstallArgs,如果是內部存儲則返回FileInstallArgs,外部則是AsecInstallArgs(SD卡),其中還有負責移動的MoveInstallArgs;
- 最後調用args的copyApk開始進行apk的拷貝;
copyApk
通過前面的分析,我們已經得到apk適合的安裝位置,接下來就由copyApk來完成apk的複製工作。
說明:
- 首先會通過installService的方法allocateStageDirLegacy在/data/app下創建臨時文件,臨時文件名爲 臨時文件名爲vmdl-隨機數.tmp;
- 調用DCS的copyFile執行復制操作,最終將/data/local/temp下的apk文件複製到/data/app/下,文件名爲base.apk, 然後修改相關文件的讀寫權限等。
- 調用copyNativeBinariesWithOverride對lib庫進行復制;
DefContainer:Copying /data/app/com.iflytek.inputmethod.gionee-1/base.apk to base.apk
至此,handleStartCopy就完成了,apk拷貝完了,其實apk並沒有完全解析並且歸檔到PKMS的數據結構中,因此,接下來我們還要對apk文件解析歸檔。
handleReturnCode
由於最初創建的handleParams爲InstallParams,所以handleReturnCode也是installParams的子類方法。
該方法中mArgs爲FileInstallArgs,所以會直接調用processPendingInstall進行接下來的處理。
說明:
- 在mHandler中拋一個Runnable對象,如果之前handleStartCopy中沒有出現問題,則返回碼應該是INSTALL_SUCCEEDED,就會調用doPreInstall對安裝路徑進行清理,如果是success,則什麼都不做;
- 調用installPackageLI進行掃描安裝,即我們之前的scanPackageLI ==> parsePackage==> parseBaseApk ==> parseBaseApplication…
- 最後會調用doRename對文件進行命名;
- 調用doPostInstall進行安裝文件的處理。
- 如果允許backup則通過BackupManager對其進行備份處理;
- 向handr拋一個POST_INSTALL消息;
進入POST_INSTALL消息處理環節:
說明:
- 依次發送PACKAGE_ADDED,ACTION_PACKAGE_REPLACED廣播;
- 強制執行依次GC進行資源回收;
- 調用FileInstallArgs的doPostDeleteLI進行資源清理;
- 調用onPackageInstalled向PM通知安裝的結果(obs);
之前的obs在調用完install請求後,進入wait狀態等待PKMS的返回,在onPackageInstalled方法中notifyAll將其喚醒輸出安裝結果。
安裝返回值結果
常見的APK安裝結果(返回值)通常有以下幾種。
返回值 | 說明 | 解決方式 |
INSTALL_SUCCEEDED | 安裝成功 | - |
INSTALL_FAILED_ALREADY_EXISTS | apk已經安裝過 | 加-r參數 |
INSTALL_FAILED_INVALID_APK | apk安裝包無效 | 確認apk文件是否有效 |
INSTALL_FAILED_INVALID_URI | uri無效,路徑不對.. | 確認apk的絕對路徑 |
INSTALL_FAILED_INSUFFICIENT_STORAGE | 空間不足,中間文件vmdl無法創建 | 清除空間 |
INSTALL_FAILED_DUPLICATE_PACKAGE | 主要檢查版本號 |
|
INSTALL_FAILED_NO_SHARED_USER | 請求的SharedUserId不存在 |
|
INSTALL_FAILED_UPDATE_INCOMPATIBLE | 與之前安裝的簽名不對 | 將之前的app刪除後再安裝 |
INSTALL_FAILED_SHARED_USER_INCOMPATIBLE | 與sharedUserId的簽名不匹配 |
|
INSTALL_FAILED_MISSING_SHARED_LIBRARY | 使用的共享庫不存在 | 確認所使用的共享庫系統是否已支持。 |
INSTALL_FAILED_OLDER_SDK INSTALL_FAILED_NEWER_SDK | Sdk版本不匹配<uses-sdk> | 請使用正確的targetsdk |
INSTALL_FAILED_CONFLICTING_PROVIDER | provider衝突 | 確認manifest中使用的provider |
INSTALL_FAILED_TEST_ONLY | android:testOnly="true" | Android studio中請使用build生成apk安裝,或者adb install –t參數 |
INSTALL_FAILED_VERSION_DOWNGRADE | VersionCode低了 |
|
……………… |
|
|
安裝流程小結
至此,apk的安裝流程就基本上完成了,下面的流程圖可以完整展現該過程。
後續將會繼續分析權限管理部分,歡迎關注。