| # How ADB incremental-install works |
| |
| The regular way an app is installed on an Android devices is for ADB to open a |
| connection to the package manager (`pm`) and write all the bytes. Once received |
| by `pm`, the app is verified via v2 signature checking, adb gets an |
| installation reply (SUCCESS or FAILURE [..]), and the operation is considered |
| over. |
| |
| Incremental-install is a departure from the idea that all bytes needs to be |
| pushed for the installation to be considered over. It even allows an app to |
| start before `pm` has received all the bytes. |
| |
| ## The big picture |
| |
| The big picture of incremental-install revolves around four concepts. |
| |
| - Blocks |
| - Block requests |
| - Incremental Server (`IS`) |
| - V4 signature |
| |
| Each file of an app (apk, splits, obb) are viewed as a series of blocks. |
| |
| In incremental-install mode, `pm` only need to receive a few blocks to validate |
| the app and declare installation over (with SUCCESS/FAILURE) which increase |
| installation speed tremendously. |
| |
| In the background, ADB will keep on steaming blocks linearly, even after `pm` |
| reported being "done". The background streaming is done in ADB's embedded |
| `IS`. |
| |
| The `IS` sends blocks to the device in order it assumes will be accessed by `pm`. |
| And then it sends the remaining block from start to end of file. |
| |
| `pm` will inevitably need blocks it has not received yet. For example, when the |
| app's Central Directory (located at the end of a zip file) must be read to know |
| what files are in the apk. This is where block requests enter the picture. The |
| Android device can issue requests which will make the `IS` bump the priority of |
| a block so it is sent to the device as soon as possible. |
| |
| ### Incremental-install filesystem |
| |
| The block requests are not issued by Android Frameworks. Framework is completely |
| oblivious of the background streaming. Everything is done at the Android kernel |
| level where file access is detected. If a read lands on a block that has not been |
| received yet, the kernel issues a block request to get it from the streaming |
| server immediately. |
| |
| ### App verification |
| |
| In incremental-install mode, `pm` does minimal verification of app integrity. |
| - Checks that there is a v4 signature |
| - Check there is a v2 or v3 signature |
| - Check that v4 is linked to either v2 or v3 |
| - Check the v4 header is signed with same certificate as v2/v3 |
| |
| The rest of the app verification is done by the Android kernel for each block level |
| when they are received. |
| |
| With v2 signing, an apps is signed by building a merkle tree, keeping only the |
| top node hash, signing it, and embedding it in the apk. On `pm` side, to verify |
| the app, the merkle tree is rebuilt, and the top hash is compared against the |
| signed hash. V2 can only work if `pm` has all the bytes of an app which is not |
| the case here. |
| |
| #### v4 signing |
| This problem is solved with V4 signing which does not discard the merkle tree |
| but embed it in the signed file and also outputs the top merkle node hash in |
| a .idsig file. |
| |
| Upon installation the whole merkel tree from V4 is given to `pm` which forwards |
| it to the Android kernel. The kernel is in charge of verifying the integrity |
| of each block when they are received from the `IS` via the merkle tree. |
| |
| For more details about v4 signing, refer to [APK signature scheme v4](https://source.android.com/docs/security/features/apksigning/v4) page. |
| ## How ADB performs incremental-install |
| |
| To perform incremental-install, ADB needs to do two things. |
| |
| - Define the block database to `pm`. |
| - Start a `IS`. |
| |
| ``` |
| ┌───┐ ┌────┐ |
| │adb│ │ppm │ |
| └─┬─┘ └─┬──┘ |
| │ pm install-incremental │ |
| ├─────────────────────────────────►│ |
| │ ┌────┐ │ |
| ├───►│ IS │ │ |
| │ └─┬──┘ │ |
| X │ │ |
| ├──────────────────────────►│ |
| ├──────────────────────────►│ |
| │◄──────────────────────────┤ |
| ├──────────────────────────►│ |
| │ │ |
| ``` |
| |
| ### Local database |
| |
| The call to `pm incremental-install` has arguments describing the `IS` database. |
| It allows the kernel to issue block requests. The arg format to describe the `IS` |
| database is as follows. |
| |
| ``` |
| filename:file_size:file_id:signature[:protocol_version] |
| ``` |
| |
| where |
| |
| - `file_id` is the identified that will be used by the kernel for block |
| requests. There is one arg for each file to be streamed. |
| - `signature` is the top merkle hash. |
| - `[:protocol_version]` is optional. |
| |
| ### Unsigned files |
| |
| There could be unsigned files to be installed. In this case, `pm` has to be made |
| aware of them via a special arg format. |
| |
| ``` |
| filename::file_size:file_id |
| ``` |
| |
| These files are not sent via the `IS` but instead sent on stdin, before |
| the `IS` is started. |
| |
| ``` |
| ┌───┐ ┌────┐ |
| │adb│ │ppm │ |
| └─┬─┘ └─┬──┘ |
| │ pm install-incremental │ |
| ├─────────────────────────────────►│ |
| │ │ |
| │ (stdin) write(unsigned) │ |
| ├─────────────────────────────────►│ |
| │ ┌────┐ │ |
| ├───►│ IS │ │ |
| │ └─┬──┘ │ |
| X │ │ |
| ├──────────────────────────►│ |
| ├──────────────────────────►│ |
| │◄──────────────────────────┤ |
| ├──────────────────────────►│ |
| │ │ |
| ``` |
| |
| ## Learn more |
| |
| There is more documentation about this topic which is unfortunately internal only. |
| |
| - [go/incremental-adb](go/incremental-adb) |
| - [go/apk-v4-signature-format](go/apk-v4-signature-format) |
| - [go/instamatic-design-signature](go/instamatic-design-signature) |