1. 程式人生 > >Android程式包管理(2)--使用adb install執行安裝過程

Android程式包管理(2)--使用adb install執行安裝過程

二、程式安裝過程

1.使用adb install執行安裝過程

使用adb install執行安裝過程整體流程圖如下:

一、使用adb install命令安裝apk

1.adb指令對應原始檔system/core/adb/commandline.c

2.指令引數解析:根據傳入引數install判斷是安裝指令,直接呼叫到install_app();

3.執行檔案拷貝:在install_app()中:

  • 首先定義兩個拷貝位置:分別對應拷貝到內部或者外部儲存。這裡需要根據傳入引數確定拷貝位置。預設是拷貝到內部儲存中。

  • 執行拷貝:將制定的apk檔案從電腦端拷貝到制定的目錄處。

  • 執行安裝:呼叫pm_command()函式開始去安裝。

  • 刪除檔案:當安裝結束時刪除該apk檔案。

4.開始去安裝:在pm_command()中:

  • 構造shell指令:將引數憑藉成為shell指令。

  • 去執行shell:使用send_shellcommand()去執行pm指令碼,使用app_process執行pm.jar。

二、跨程序去執行安裝

1.執行前準備:

  • 獲取Binder:首先獲取PKMS對應的Binde物件。

  • 解析傳入引數:判斷是install指令確定是安裝指令。呼叫runinstall()。

2.準備跨程序安裝:在runinstall()中:

  • 解析引數:while迴圈依次解析引數確定installFlags,接著獲取apk檔案路徑構造apkURI通過它讀取檔案。

  • 跨程序呼叫:首先新建PackageInstallObserver回撥接收PKMS安裝結果,然後使用mPm.installPackageWithVerificationAndEncryption()跨程序呼叫到PKMS該函式執行。

  • 等待安裝結果:迴圈判斷PackageInstallObserver.finished狀態,等待安裝結果根據結果提示使用者資訊。

三、PKMS執行具體安裝

1.執行安裝準備工作:在installPackageWithVerificationAndEncryption()中:

  • 檢查許可權:首先檢查呼叫者程序是否存在安裝應用的許可權。

  • 獲取uid,設定filteredFlags:獲取呼叫者uid根據uid做後續判斷。如果uid是SHELL_UID為filteredFlags設定INSTALL_FROM_ADB表示否則取消該表示,這裡是需要設定該標識。

  • 發生非同步訊息:獲取INIT_COPY標識的Message物件,設定obj為新建物件InstallParams,該物件分裝了安裝需要的引數及資訊。然後傳送該Message。

  • InstallParams類圖關係如下:

2.執行非同步訊息:在PackageHandler中:

  • 處理訊息:非同步訊息都會呼叫doHandleMessage()處理具體邏輯。

  • 判斷在INIT_COPY標識下:

    • 首先獲取安裝請求物件:obj即HandlerParams的子類InstallParams。

    • 判斷是否啟動服務:mBound表示是否啟動DefaultContainerService服務,該服務由DefaultContainerService.apk提供,主要作用是拷貝檔案到應用程式目錄。如果已啟動則將該安裝請求物件新增到佇列mPendingInstalls中該佇列如果為空則發生訊息MCS_BOUND執行安裝。如果未啟動呼叫connectToService()啟動該服務。

  • 判斷在MCS_BOUND標識下:該標識在DefaultContainerService啟動成功後也會回撥。

    • 獲取mContainerService物件:如果是DefaultContainerService啟動成功後的回撥,直接獲取obj即為該物件。判斷mContainerService是否為空,如果為空代表DefaultContainerService服務未啟動不能執行安裝操作,不為空判斷如果mPendingInstalls佇列存在請求則開始執行。

    • 獲取安裝請求:使用mPendingInstalls.get(0)獲取一個待安裝請求HandlerParams。

    • 執行安裝請求:呼叫該HandlerParams.startCopy()函式,判斷函式返回結果,返回為true代表處理成功。

    • 請求處理成功:請求處理成功時,首先從mPendingInstalls佇列中移除該請求,如果mPendingInstalls佇列此時大小為0代表所有請求已完成則回撥MCS_UNBIND斷開服務。如果還存在請求則繼續回撥MCS_BOUND訊息執行安裝請求。

3.執行安裝請求邏輯:HandlerParams.startCopy()中:

  • 嘗試次數判斷:安裝請求執行失敗後有重試機制。判斷mRetries是否大於最大嘗試次數,超過最大次數發生非同步訊息MCS_GIVE_UP放棄執行返回false。未超過則執行安裝行為。

  • 執行拷貝操作:呼叫handleStartCopy()執行拷貝請求,具體是實現在InstallParams中。

  • 執行安裝操作:呼叫handleReturnCode()執行安裝請求,具體是實現在InstallParams中。

4.執行apk的拷貝:在HandlerParams.handleStartCopy()中:

  • 獲取指定的安裝位置:首先從flags中獲取指定的安裝位置,這裡的flags來自於之前解析引數傳入的filteredFlags。判斷如果同時指定既在sd卡安裝又在內部儲存安裝則賦值ret安裝失敗。

  • 獲取手機儲存狀況:通過dsm服務呼叫getMemoryLowThreshold()獲取當前內部儲存最小余量。小於這個值就是空間不足。

  • 授予apk讀取許可權:授予DefContainerService URI讀許可權,使其可以執行apk的拷貝操作。

  • 獲取PackageInfoLite物件並判斷:首先獲取待安裝apk的packageFile物件,根據該物件呼叫mContainerService.getMinimalPackageInfo()獲取其對應的PackageInfoLite物件,該物件描述apk的包名、版本、推薦安裝位置、簽名等資訊。然後判斷該apk的安裝推薦位置是RECOMMEND_FAILED_INSUFFICIENT_STORAGE代表當前儲存空間不足需要釋放空間,計算該apk安裝需要的空間然後使用mInstaller.freeCache()釋放空間,之後在嘗試獲取一次PackageInfoLite物件。

  • 撤銷apk讀取許可權:撤銷對uri的讀取許可權mContext.revokeUriPermission()。

  • 確定最終安裝的位置:根據pkgLite.recommendedInstallLocation確定ret值,並且最終確定安裝位置sd卡或者是內部儲存,並設定flags值。

  • 新建安裝引數物件InstallArgs:呼叫createInstallArgs(this)建立一個安裝引數物件,根據flag對安裝位置區別處理。這裡如果是安裝在sd卡上返回AsecInstallArgs物件,反之是FileInstallArgs物件。它們都繼承自InstallArgs。注意這裡構造的時候傳入的是當前的安裝請求物件HandlerParams,該物件分裝了這次請求需要的所有資訊引數。

  • 判斷一系列驗證檢查:如果設定了包驗證開啟,則收集資訊傳送廣播給包校驗器檢查,檢查通過才能接著進行安裝。如果未設定則直接呼叫安裝引數物件InstallArgs.copyApk()執行拷貝apk。

5.具體開始執行apk的拷貝操作:在InstallArgs.copyApk()中,假設安裝位置在內部儲存則該方法具體實現在FileInstallArgs中:

  • 判斷temp:temp代表拷貝到/data/app下的檔名是否需要隨機,這裡我們傳入的true是要隨機,因為如果不隨機以apk結尾,PKMS會監聽到該檔案之後直接執行安裝操作,我們不需要直接進行該安裝操作,所以設定隨機名。

  • 執行apk拷貝操作:首先獲取建立apk對應File物件codeFile,為DCM服務授權讀取許可權。接著使用imcs.copyResource()拷貝資源到codeFile檔案。同時為該apk建立native目錄並且拷貝native庫到該位置。最後返回安裝結果ret。

至此安裝操作完成HandlerParams.handleStartCopy()執行完畢賦值安裝結果給mRet變數。

6.拷貝完成執行安裝操作:在HandlerParams.handleReturnCode()中:

  • 判斷安裝引數物件InstallArgs:首先判斷InstallArgs物件如果不為null則開始執行安裝流程,呼叫processPendingInstall()。安裝結束之後刪除臨時檔案,如果未開啟包驗證則該檔案不存在不需要刪除。

  • 執行安裝操作:使用Hanlder釋出非同步任務,新建安裝結果物件PackageInstalledInfo,接著呼叫installPackageLI()執行安裝操作,傳入安裝引數物件InstallArgs及PackageInstalledInfo獲取安裝結果。安裝完成之後新建物件PostInstallData分裝InstallArgs及PackageInstalledInfo然後放入mRunningInstalls這個map中,新建一個token為索引。最後發生一個非同步訊息POST_INSTALL並攜帶索引token,通知安裝結果。

7.安裝完成執行通知操作:在PackageHandler中:

  • 判斷在POST_INSTALL標識下:

    • 獲取安裝結果物件:在mRunningInstalls中根據token獲取安裝結果PostInstallData。如果存在則分別取出對應的InstallArgs和PackageInstalledInfo。

    • 處理安裝成功情況:判斷PackageInstalledInfo.returnCode是安裝成功,首先發送安裝成功廣播ACTION_PACKAGE_ADDED,如果是應用更新操作還要傳送ACTION_PACKAGE_REPLACED廣播。然後進行資源清理,接著取出InstallArgs.observer不為空代表存在安裝結果回撥物件,則回撥其對應結果。

至此adb install安裝程式流程完成。

簡單總結流程:

  • 將電腦端apk資源拷貝到制定的臨時資料夾下。

  • 將apk拷貝到/data/app目錄下,但是檔名需要隨機處理。

  • 呼叫PKMS函式執行apk安裝。

  • 回撥安裝結果給監聽器。