pm install-create
會改變裝置開一個安裝工作階段,讓 base 與它的 split 作為一個不可分割的整體一起安裝。
- 指令
- adb shell pm install-create
- 指令
- 套件(pm)
- 風險
- 會改變裝置
語法
pm install-create [-r] [-g] [-d] [-t] [-S <total bytes>]
pm install-write [-S <bytes>] <session> <name> <path or ->
pm install-commit <session>
pm install-abandon <session>旗標與參數
| token | 意義 |
|---|---|
| -S <bytes> | 在 `install-create` 上是整組的總大小,讓安裝器先保留空間、空間不足就早點失敗。在 `install-write` 上是這個檔案的確切大小,而當位元組來自標準輸入時它是必填的,因為讀取端沒有別的方法知道到哪裡結束。 |
| - | 作為 `install-write` 的路徑參數,代表從標準輸入讀取 APK 的位元組。 |
| -r -g -d -t | 安裝選項,它們屬於 `install-create`。`install-commit` 完全不接受安裝參數,所以掛在 commit 上的旗標會被默默丟掉。 |
範例
pm install-create -r -S 82345678為一組 82 MB 的檔案開工作階段,回覆裡帶著工作階段 id。
pm install-write -S 41000000 1234567 base.apk /data/local/tmp/base.apk把一個已推上去的檔案加入工作階段。
pm install-commit 1234567把工作階段裡的東西當成一個操作安裝完成。
pm install-abandon 1234567丟掉工作階段,釋放暫存的位元組。
輸出怎麼讀
輸出範例
Success: created install session [1234567]`install-create` 會用方括號回覆工作階段 id,那個數字就是後續每一步的參數。每個 `install-write` 回 `Success` 或 `Failure [INSTALL_FAILED_…]`,而最要緊的是 `install-commit` 的失敗——簽章、分割組合是否完整、驗證問題都是在那一刻才浮出來的,因為在那之前根本還沒和已安裝的 app 對照過。
輸出欄位
| 欄位 | 意義 |
|---|---|
| Success: created install session [<id>] | 給 install-write 與 install-commit 用的工作階段 id。 |
| Success | 那次寫入,或那次 commit,被接受了。 |
| Error: must specify a APK size | 從標準輸入讀取的 `install-write` 少了 -S。 |
| INSTALL_FAILED_INVALID_APK | 整組對不起來:重複的 split、不匹配的 base。 |
| INSTALL_FAILED_ABORTED | 工作階段被 abandon,或裝置上的提示沒有被回應。 |
需要什麼權限
和 `pm install` 一樣的授權 `adb` 連線,也一樣要求手機讀得到你暫存的檔案。工作階段多出來的,是一項你現在擁有、而且必須交還的資源。已建立的工作階段會把它的部分位元組留在 `/data/app/vmdl<id>.tmp`,直到 commit、abandon,或被 `PackageInstallerService` 判定過期,所以每一次沒有配上 `install-abandon` 的嘗試都多留一份下來——一種沒有權限錯誤會宣告、也沒有對話框會提到的慢性空間流失。用 `install-write ... -` 串流也不需要額外存取權,只需要那個必填的 `-S <bytes>`,因為傳輸層沒辦法在不同時拆掉那條要帶回結果的回覆的情況下表示檔案結束。`pm install` 會跳的裝置端提示在這裡也會跳,而且是在 commit 的時候跳而不是 create 的時候,所以無人看管的執行通常死在最後一步而不是第一步。
失敗訊息對照
| 訊息 | 意義 |
|---|---|
| Error: must specify a APK size | 從標準輸入讀取的 `install-write` 少了 `-S`。這句話連同那個錯字都是平台原本的用字。要注意的是這個錯誤送到時工作階段還開著,所以它是一種你得自己用 `install-abandon` 收拾的失敗,不是會自己回捲的那種。 |
| Failure [INSTALL_FAILED_INVALID_APK: Split lib_a was defined multiple times] | 整組對不起來——這裡是兩個檔案宣稱了同一個 split 名稱。這是 commit 當下才給的判決,而那就是工作階段模型本身的形狀:在每一份都寫完之前,沒有任何東西會拿去和已安裝的 app 對照,所以在寫入階段犯的錯,要到最後才浮出來。 |
| Failure [INSTALL_FAILED_ABORTED: Failed to commit install session 731045162 with command cmd package install-commit 731045162. Error: INSTALL_FAILED_ABORTED: Session was abandoned] | 巢狀的那種形狀。這一行裡兩個 token 表格都解釋得了,所以辨識器回報前面那個——框架自己選的那個代碼。但還是要把括號讀到底:`Session was abandoned` 才是在告訴你那個 id 已經被 abandon 過了,再 commit 一次永遠不會成功。 |
會改變什麼,以及怎麼回頭
安裝本身和 `pm install` 一樣會取代既有的 app。工作階段特有的風險是「開著沒收」:已建立的工作階段會在裝置上占著暫存的位元組,直到 commit 或 abandon,所以 `pm install-abandon <session>` 是必要的收尾步驟而不是可選項。
版本與廠商注意事項
- `install-write` 的 split 名稱是工作階段內的標籤而不是路徑,桌面版 adb 也只是把檔名本身傳進去。
- Android 7 之後 `cmd package install-create` 是同一個指令的較短路徑,`pm` 是包在外面的 script。
- 從標準輸入串流之所以需要 `-S`,是因為傳輸層沒辦法在不同時拆掉回覆的情況下表示檔案結束。終止讀取的是大小而不是 EOF。
這個我們有 UI
APK Installer 會透過 USB 執行這個指令並解析回應——只要 Chromium 系瀏覽器,不必安裝 SDK,也不會上傳任何東西。
相關指令
和 pm install-create 會在同一次作業裡出現的指令。
| 指令 | 做什麼 |
|---|---|
| pm install | 安裝一個已經在裝置上的 APK,也就是 `adb install` 底層在做的事。 |
| pm path | 印出組成某個已安裝套件的 APK 檔絕對路徑。 |
| pm uninstall | 移除套件,或只針對一個 Android 使用者移除——這是不用 root 也能處理掉預載 app 的方法。 |
其他套件(pm)指令
| 指令 | 做什麼 |
|---|---|
| pm list packages | 列出已安裝的套件,也能一次帶出 APK 路徑、uid、版本碼與安裝來源。 |
| pm disable-user | 在不移除的前提下,把套件對某個 Android 使用者關掉——比解除安裝安全,而且確定能還原。 |
| pm clear | 清除某個 app 的資料——在 Android 13 之後也可以只清快取。 |
| pm trim-caches | 要求系統一直丟掉快取檔案,直到剩下你指定的空間為止。 |
這些行為是在撰寫 Web ADB 工具背後的解析程式時,對照 AOSP、toybox 與核心原始碼查證的。真的會因 Android 版本或廠牌而異的地方,這一頁會直接說明,而不是給你一個在半數手機上都是錯的數字。