| # APEX File Format |
| |
| Android Pony EXpress (APEX) is a container format introduced in Android Q that |
| is used in the install flow for lower-level system modules. This format |
| facilitates the updates of system components that don't fit into the standard |
| Android application model. Some example components are native services and |
| libraries, hardware abstraction layers |
| ([HALs](/https://source.android.com/devices/architecture/hal-types)), runtime |
| ([ART](/https://source.android.com/devices/tech/dalvik)), and class libraries. |
| |
| The term "APEX" can also refer to an APEX file. |
| |
| This document describes technical details of the APEX file format. If you are |
| looking at how to build an APEX package, kindly refer to [this how-to](howto.md) |
| document. |
| |
| ## Background |
| |
| Although Android supports updates of modules that fit within the standard app |
| model (for example, services, activities) via package installer apps (such as |
| the Google Play Store app), using a similar model for lower-level OS components |
| has the following drawbacks: |
| |
| - APK-based modules can't be used early in the boot sequence. The package |
| manager is the central repository of information about apps and can only be |
| started from the activity manager, which becomes ready in a later stage of |
| the boot procedure. |
| - The APK format (particularly the manifest) is designed for Android apps and |
| system modules aren't always a good fit. |
| |
| ## Design |
| |
| This section describes the high-level design of the APEX file format and the |
| APEX manager, which is a service that manages APEX files. |
| |
| ### APEX format {#apex-format} |
| |
| This is the format of an APEX file. |
| |
|  |
| |
| **Figure 1.** APEX file format |
| |
| At the top level, an APEX file is a zip file in which files are stored |
| uncompressed and located at 4 KB boundaries. |
| |
| The four files in an APEX file are: |
| |
| - `apex_manifest.json` |
| - `AndroidManifest.xml` |
| - `apex_payload.img` |
| - `apex_pubkey` |
| |
| The `apex_manifest.json` file contains the package name and version, which |
| identify an APEX file. |
| |
| The `AndroidManifest.xml` file allows the APEX file to use APK-related tools and |
| infrastructure such as ADB, PackageManager, and package installer apps (such as |
| Play Store). For example, the APEX file can use an existing tool such as `aapt` |
| to inspect basic metadata from the file. The file contains package name and |
| version information. This information is generally also available in |
| `apex_manifest.json`. `AndroidManifest.xml` might contain additional targeting |
| information that can be used by the existing app publishing tools. |
| |
| `apex_manifest.json` is recommended over `AndroidManifest.xml` for new code and |
| systems that deal with APEX. |
| |
| `apex_payload.img` is an ext4 file system image backed by dm-verity. The image |
| is mounted at runtime via a loop device. Specifically, the hash tree and |
| metadata block are created using libavb. The file system payload isn't parsed |
| (because the image should be mountable in place). Regular files are included |
| inside the `apex_payload.img` file. |
| |
| `apex_pubkey` is the public key used to sign the file system image. At runtime, |
| this key ensures that the downloaded APEX is signed with the same entity that |
| signs the same APEX in the built-in partitions. |
| |
| ### APEX manager |
| |
| The APEX manager (or `apexd`) is a native daemon responsible for verifying, |
| installing, and uninstalling APEX files. This process is launched and is ready |
| early in the boot sequence. APEX files are normally pre-installed on the device |
| under `/system/apex`. The APEX manager defaults to using these packages if no |
| updates are available. |
| |
| The update sequence of an APEX uses the |
| [PackageManager class](https://developer.android.com/reference/android/content/pm/PackageManager) |
| and is as follows. |
| |
| 1. An APEX file is downloaded via a package installer app, ADB, or other |
| source. |
| 1. The package manager starts the installation procedure. Upon recognizing that |
| the file is an APEX, the package manager transfers control to the APEX |
| manager. |
| 1. The APEX manager verifies the APEX file. |
| 1. If the APEX file is verified, the internal database of the APEX manager is |
| updated to reflect that the APEX file will be activated at next boot. |
| 1. The requestor of the install receives a broadcast upon successful |
| verification of the package. |
| 1. To continue the installation, the system automatically reboots the device. |
| 1. At reboot, the APEX manager starts, reads the internal database, and does |
| the following for each APEX file listed: |
| |
| 1. Verifies the APEX file. |
| 1. Creates a loop device from the APEX file. |
| 1. Creates a device mapper block device on top of the loop device. |
| 1. Mounts the device mapper block device onto a unique path (for example, |
| <code>/apex/<var>name</var>@<var>ver</var></code>). |
| |
| When all APEX files listed in the internal database are mounted, the APEX |
| manager provides a binder service for other system components to query |
| information about the installed APEX files. For example, the other system |
| components can query the list of APEX files installed in the device or query the |
| exact path where a specific APEX is mounted, so the files can be accessed. |
| |
| ### APEX files are APK files |
| |
| APEX files are valid APK files because they are signed zip archives (using the |
| APK signature scheme) containing an `AndroidManifest.xml` file. This allows APEX |
| files to use the infrastructure for APK files, such as a package installer app, |
| the signing utility, and the package manager. |
| |
| The `AndroidManifest.xml` file inside an APEX file is minimal, consisting of the |
| package `name`, `versionCode`, and optional `targetSdkVersion`, `minSdkVersion`, |
| and `maxSdkVersion` for fine-grained targeting. This information allows APEX |
| files to be delivered via existing channels such as package installer apps and |
| ADB. |
| |
| ### File types supported |
| |
| The APEX format supports these file types: |
| |
| - Native shared libs |
| - Native executables |
| - JAR files |
| - Data files |
| - Config files |
| |
| The APEX format can only update some of these file types. Whether a file type |
| can be updated depends on the platform and how stable the interfaces for the |
| files types are defined. |
| |
| ### Signing |
| |
| APEX files are signed in two ways. First, the `apex_payload.img` (specifically, |
| the vbmeta descriptor appended to `apex_payload.img`) file is signed with a key. |
| Then, the entire APEX is signed using the |
| [APK signature scheme v3](/https://source.android.com/security/apksigning/v3). |
| Two different keys are used in this process. |
| |
| On the device side, a public key corresponding to the private key used to sign |
| the vbmeta descriptor is installed. The APEX manager uses the public key to |
| verify APEXs that are requested to be installed. Each APEX must be signed with |
| different keys and is enforced both at build time and runtime. |
| |
| ### APEX in built-in partitions |
| |
| APEX files can be located in built-in partitions such as `/system`. The |
| partition is already over dm-verity, so the APEX files are mounted directly over |
| the loop device. |
| |
| If an APEX is present in a built-in partition, the APEX can be updated by |
| providing an APEX package with the same package name and a higher version code. |
| The new APEX is stored in `/data` and, similar to APKs, the newer version |
| shadows the version already present in the built-in partition. But unlike APKs, |
| the newer version of the APEX is only activated after reboot. |
| |
| ## Kernel requirements |
| |
| To support APEX mainline modules on an Android device, the following Linux |
| kernel features are required: the loop driver and dm-verity. The loop driver |
| mounts the file system image in an APEX module and dm-verity verifies the APEX |
| module. |
| |
| The performance of the loop driver and dm-verity is important in achieving good |
| system performance when using APEX modules. |
| |
| ### Supported kernel versions |
| |
| APEX mainline modules are supported on devices using kernel versions 4.4 or |
| higher. New devices launching with Android Q or higher must use kernel version |
| 4.9 or higher to support APEX modules. |
| |
| ### Required kernel patches |
| |
| The required kernel patches for supporting APEX modules are included in the |
| Android common tree. To get the patches to support APEX, use the latest version |
| of the Android common tree. |
| |
| #### Kernel version 4.4 |
| |
| This version is only supported for devices that are upgraded from Android 9 to |
| Android Q and want to support APEX modules. To get the required patches, a |
| down-merge from the `android-4.4` branch is strongly recommended. The following |
| is a list of the required individual patches for kernel version 4.4. |
| |
| - UPSTREAM: loop: add ioctl for changing logical block size |
| ([4.4](https://android-review.googlesource.com/c/kernel/common/+/777013){: .external}) |
| - BACKPORT: block/loop: set hw_sectors |
| ([4.4](https://android-review.googlesource.com/c/kernel/common/+/777014/7){: .external}) |
| - UPSTREAM: loop: Add LOOP_SET_BLOCK_SIZE in compat ioctl |
| ([4.4](https://android-review.googlesource.com/c/kernel/common/+/777015/7){: .external}) |
| - ANDROID: mnt: Fix next_descendent |
| ([4.4](https://android-review.googlesource.com/c/kernel/common/+/405314){: .external}) |
| - ANDROID: mnt: remount should propagate to slaves of slaves |
| ([4.4](https://android-review.googlesource.com/c/kernel/common/+/320406){: .external}) |
| - ANDROID: mnt: Propagate remount correctly |
| ([4.4](https://android-review.googlesource.com/c/kernel/common/+/928253){: .external}) |
| - Revert "ANDROID: dm verity: add minimum prefetch size" |
| ([4.4](https://android-review.googlesource.com/c/kernel/common/+/867875){: .external}) |
| - UPSTREAM: loop: drop caches if offset or block_size are changed |
| ([4.4](https://android-review.googlesource.com/c/kernel/common/+/854265){: .external}) |
| |
| #### Kernel versions 4.9/4.14/4.19 |
| |
| To get the required patches for kernel versions 4.9/4.14/4.19, down-merge from |
| the `android-common` branch. |
| |
| ### Required kernel configuration options |
| |
| The following list shows the base configuration requirements for supporting APEX |
| modules that were introduced in Android Q. The items with an asterisk (\*) are |
| existing requirements from Android 9 and lower. |
| |
| ``` |
| (*) CONFIG_AIO=Y # AIO support (for direct I/O on loop devices) |
| CONFIG_BLK_DEV_LOOP=Y # for loop device support |
| CONFIG_BLK_DEV_LOOP_MIN_COUNT=16 # pre-create 16 loop devices |
| (*) CONFIG_CRYPTO_SHA1=Y # SHA1 hash for DM-verity |
| (*) CONFIG_CRYPTO_SHA256=Y # SHA256 hash for DM-verity |
| CONFIG_DM_VERITY=Y # DM-verity support |
| ``` |
| |
| ### Kernel command line parameter requirements |
| |
| To support APEX, make sure the kernel command line parameters meet the following |
| requirements. |
| |
| - `loop.max_loop` must NOT be set |
| - `loop.max_part` must be <= 7 |
| |
| ## Building an APEX |
| |
| Note: Because the implementation details for APEX are still under development, |
| the content in this section is subject to change. |
| |
| This section describes how to build an APEX using the Android build system. The |
| following is an example of `Android.bp` for an APEX named `apex.test`. |
| |
| ``` |
| apex { |
| name: "apex.test", |
| manifest: "apex_manifest.json", |
| file_contexts: "file_contexts", |
| // libc.so and libcutils.so are included in the apex |
| native_shared_libs: ["libc", "libcutils"], |
| binaries: ["vold"], |
| java_libs: ["core-all"], |
| prebuilts: ["my_prebuilt"], |
| compile_multilib: "both", |
| key: "apex.test.key", |
| certificate: "platform", |
| } |
| ``` |
| |
| `apex_manifest.json` example: |
| |
| ``` |
| { |
| "name": "com.android.example.apex", |
| "version": 1 |
| } |
| ``` |
| |
| `file_contexts` example: |
| |
| ``` |
| (/.*)? u:object_r:system_file:s0 |
| /sub(/.*)? u:object_r:sub_file:s0 |
| /sub/file3 u:object_r:file3_file:s0 |
| ``` |
| |
| #### File types and locations in APEX |
| |
| File type | Location in APEX |
| ---------------- | ---------------------------------------------------------- |
| Shared libraries | `/lib` and `/lib64` (`/lib/arm` for translated arm in x86) |
| Executables | `/bin` |
| Java libraries | `/javalib` |
| Prebuilts | `/etc` |
| |
| ### Transitive dependencies |
| |
| APEX files automatically include transitive dependencies of native shared libs |
| or executables. For example, if `libFoo` depends on `libBar`, the two libs are |
| included when only `libFoo` is listed in the `native_shared_libs` property. |
| |
| ### Handling multiple ABIs |
| |
| Install the `native_shared_libs` property for both primary and secondary |
| application binary interfaces (ABIs) of the device. If an APEX targets devices |
| with a single ABI (that is, 32 bit only or 64 bit only), only libraries with the |
| corresponding ABI are installed. |
| |
| Install the `binaries` property only for the primary ABI of the device as |
| described below: |
| |
| - If the device is 32 bit only, only the 32-bit variant of the binary is |
| installed. |
| - If the device supports both 32/64 ABIs, but with |
| `TARGET_PREFER_32_BIT_EXECUTABLES=true`, then only the 32-bit variant of the |
| binary is installed. |
| - If the device is 64 bit only, then only the 64-bit variant of the binary is |
| installed. |
| - If the device supports both 32/64 ABIs, but without |
| TARGET_PREFER_32_BIT_EXECUTABLES`=true`, then only the 64-bit variant of the |
| binary is installed. |
| |
| To add fine-grained control over the ABIs of the native libraries and binaries, |
| use the |
| `multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries]` |
| properties. |
| |
| - `first`: Matches the primary ABI of the device. This is the default for |
| binaries. |
| - `lib32`: Matches the 32-bit ABI of the device, if supported. |
| - `lib64`: Matches the 64-bit ABI of the device, it supported. |
| - `prefer32`: Matches the 32-bit ABI of the device, if supported. If the |
| 32-bit ABI isn't supported, matches the 64-bit ABI. |
| - `both`: Matches both ABIs. This is the default for |
| `native_shared_libraries`. |
| |
| The `java`, `libraries`, and `prebuilts` properties are ABI-agnostic. |
| |
| This example is for a device that supports 32/64 and doesn't prefer 32: |
| |
| ``` |
| apex { |
| // other properties are omitted |
| native_shared_libs: ["libFoo"], // installed for 32 and 64 |
| binaries: ["exec1"], // installed for 64, but not for 32 |
| multilib: { |
| first: { |
| native_shared_libs: ["libBar"], // installed for 64, but not for 32 |
| binaries: ["exec2"], // same as binaries without multilib.first |
| }, |
| both: { |
| native_shared_libs: ["libBaz"], // same as native_shared_libs without multilib |
| binaries: ["exec3"], // installed for 32 and 64 |
| }, |
| prefer32: { |
| native_shared_libs: ["libX"], // installed for 32, but not for 64 |
| }, |
| lib64: { |
| native_shared_libs: ["libY"], // installed for 64, but not for 32 |
| }, |
| }, |
| } |
| ``` |
| |
| ### vbmeta signing |
| |
| Sign each APEX with different keys. When a new key is required, create a |
| public-private key pair and make an `apex_key` module. Use the `key` property to |
| sign the APEX using the key. The public key is automatically included in the |
| APEX with the name `avb_pubkey`. |
| |
| Create an rsa key pair. |
| |
| ``` |
| $ openssl genrsa -out foo.pem 4096 |
| ``` |
| |
| Extract the public key from the key pair. |
| |
| ``` |
| $ avbtool extract_public_key --key foo.pem --output foo.avbpubkey |
| ``` |
| |
| In Android.bp: |
| |
| ``` |
| apex_key { |
| name: "apex.test.key", |
| public_key: "foo.avbpubkey", |
| private_key: "foo.pem", |
| } |
| ``` |
| |
| In the above example, the name of the public key (`foo`) becomes the ID of the |
| key. The ID of the key used to sign an APEX is written in the APEX. At runtime, |
| `apexd` verifies the APEX using a public key with the same ID in the device. |
| |
| ### ZIP signing |
| |
| Sign APEXs in the same way as APKs. Sign APEXs twice, once for the mini file |
| system (`apex_payload.img` file) and once for the entire file. |
| |
| To sign an APEX at the file-level, set the `certificate` property in one of |
| these three ways: |
| |
| - Not set: If no value is set, the APEX is signed with the certificate located |
| at `PRODUCT_DEFAULT_DEV_CERTIFICATE`. If no flag is set, the path defaults |
| to `build/target/product/security/testkey`. |
| - `<name>`: The APEX is signed with the `<name>` certificate in the same |
| directory as `PRODUCT_DEFAULT_DEV_CERTIFICATE`. |
| - `:<name>`: The APEX is signed with the certificate that is defined by the |
| Soong module named `<name>`. The certificate module can be defined as |
| follows. |
| |
| ``` |
| android_app_certificate { |
| name: "my_key_name", |
| certificate: "dir/cert", |
| // this will use dir/cert.x509.pem (the cert) and dir/cert.pk8 (the private key) |
| } |
| ``` |
| |
| Note: The `key` and `certificate` values do NOT need to be derived from the same |
| public/private key pairs. APK signing (specified by `certificate`) is required |
| because an APEX is an APK. |
| |
| ## Installing an APEX |
| |
| To install an APEX, use ADB. |
| |
| ``` |
| $ adb install apex_file_name |
| $ adb reboot |
| ``` |
| |
| ## Using an APEX |
| |
| After reboot, the APEX is mounted at the `/apex/<apex_name>@<version>` |
| directory. Multiple versions of the same APEX can be mounted at the same time. |
| Among the mount paths, the one that corresponds to the latest version is |
| bind-mounted at `/apex/<apex_name>`. |
| |
| Clients can use the bind-mounted path to read or execute files from APEX. |
| |
| APEXs are typically used as follows: |
| |
| 1. An OEM or ODM preloads an APEX under `/system/apex` when the device is |
| shipped. |
| 1. Files in the APEX are accessed via the `/apex/<apex_name>/` path. |
| 1. When an updated version of the APEX is installed in `/data/apex`, the path |
| points to the new APEX after reboot. |
| |
| ### Updating a service with an APEX |
| |
| To update a service using an APEX: |
| |
| 1. Mark the service in the system partition as updatable. Add the option |
| `updatable` to the service definition. |
| |
| ``` |
| /system/etc/init/myservice.rc: |
| |
| service myservice /system/bin/myservice |
| class core |
| user system |
| ... |
| updatable |
| ``` |
| |
| 1. Create a new `.rc` file for the updated service. Use the `override` option |
| to redefine the existing service. |
| |
| ``` |
| /apex/my.apex@1/etc/init.rc: |
| |
| service myservice /apex/my.apex@1/bin/myservice |
| class core |
| user system |
| ... |
| override |
| ``` |
| |
| Service definitions can only be defined in the `.rc` file of an APEX. Action |
| triggers aren't supported in APEXs. |
| |
| If a service marked as updatable starts before the APEXs are activated, the |
| start is delayed until the activation of the APEXs is complete. |
| |
| ## Configuring system to support APEX updates |
| |
| Inherit `updatable_apex.mk`. |
| |
| ``` |
| <device.mk>: |
| |
| $(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk) |
| ``` |
| |
| ## Compressed apexes {#compressed-apex} |
| |
| APEX compression is a new feature introduced in Android S. Its main purpose is |
| to reduce the storage impact of updatable APEX packages: after an update to an |
| APEX is installed, its pre-installed version is not used anymore, and space that |
| is taken by it effectively becomes a dead weight. |
| |
| APEX compression minimizes the storage impact by using a highly-compressed |
| set of APEX files on read-only partitions (e.g. `/system`). In Android S a |
| DEFLATE zip compression is used. |
| |
| Note: compression doesn't provide any optimization in the following scenarios: |
| |
| * Bootstrap apexes that are required to be mounted very early in the boot |
| sequence. List of bootstrap apexes is configured in `kBootstrapApexes` |
| constant in `system/apex/apexd/apexd.cpp`. |
| * Non-updatable apexes. Compression is only beneficial in case an updated |
| version of an apex is installed on `/data partition`. |
| Full list of updatable apexes is available at |
| https://source.android.com/devices/architecture/modular-system. |
| * Dynamic shared libs apexes. Since `apexd` will always activate both versions |
| of such apexes (pre-installed and upgraded), compressing them doesn't provide |
| any value. |
| |
| ### Compressed APEX file format |
| |
| This is the format of a compressed APEX file. |
| |
|  |
| |
| **Figure 2.** Compressed APEX file format |
| |
| At the top level, a compressed APEX file is a zip file containing the original apex in deflated |
| form with compression level of 9 and other files stored uncompressed. |
| |
| The four files in an APEX file are: |
| |
| * `original_apex`: deflated with compression level of 9 |
| * `apex_manifest.pb`: stored only |
| * `AndroidManifest.xml`: stored only |
| * `apex_pubkey`: stored only |
| |
| |
| `original_apex` is the original uncompressed [APEX file](#apex-format). |
| |
| `apex_manifest.pb` `AndroidManifest.xml` `apex_pubkey` are copies of the |
| corresponding files from `original_apex`. |
| |
| |
| ### Building compressed apex |
| |
| Compressed apex can be built using `apex_compression_tool.py` located at |
| `system/apex/tools`. |
| |
| Note: the outer apk container of the produced compressed apex file won't be |
| automatically signed. You will need to manually sign it with using the correct |
| certificate. See [Signing Builds for Release]( |
| https://source.android.com/devices/tech/ota/sign_builds#apex-signing-key-replacement). |
| |
| There are a few different parameters related to APEX compression available in |
| the build system. |
| |
| In `Android.bp` whether an apex is compressible is controlled by `compressible` |
| property: |
| |
| ``` |
| apex { |
| name: "apex.test", |
| manifest: "apex_manifest.json", |
| file_contexts: "file_contexts", |
| compressible: true, |
| } |
| ``` |
| |
| Note: this only serves as a hint to build system that this apex can be |
| compressed. Such property is required due to the fact that not all apexes are |
| compressible as mentioned in the [section above](#compressed-apex). |
| |
| TODO(b/183208430): add docs on how this works for prebuilts. |
| |
| A `PRODUCT_COMPRESSED_APEX` product flag is used to control whether a system |
| image built from source should contain compressed apexes or not. |
| |
| For local experimentation you can force a build to compress apexes by setting |
| `OVERRIDE_PRODUCT_COMPRESSED_APEX=true`. |
| |
| Compressed APEX files generated by the build system will have `.capex` |
| extension. It makes it easier to distinguish between compressed and uncompressed |
| versions of an APEX. |
| |
| ### Supported compression algorithms |
| |
| Android S only supports deflate zip compression. |
| |
| ### Activating compressed apex during boot |
| |
| Before activating a compressed APEX, `original_apex` inside it will be |
| decompressed into `/data/apex/decompressed` directory. The resulting |
| decompressed APEX will be hard linked to the `/data/apex/active` directory. |
| |
| Note: because of the hard link step above, it's important that files under |
| `/data/apex/decompressed` have the same SELinux label as files under |
| `/data/apex/active`. |
| |
| Consider following example as an illustration of the process described above. |
| |
| Let's assume that `/system/apex/com.android.foo.capex` is a compressed APEX |
| being activated, and it's `versionCode` is `37`. |
| |
| 1. First `original_apex` inside `/system/apex/com.android.foo.capex` is |
| decompressed into `/data/apex/decompressed/com.android.foo@37.apex`. |
| 2. After that `restorecon /data/apex/decompressed/com.android.foo@37.apex` is |
| performed to make sure that it has a correct SELinux label. |
| 3. Verification checks are performed on |
| `/data/apex/decompressed/com.android.foo@37.apex` to ensure it's validity: |
| * `apexd` checks that public key bundled in |
| `/data/apex/decompressed/com.android.foo@37.apex` is equal to the one |
| bundled in `/system/apex/com.android.foo.capex` |
| 4. Next `/data/apex/decompressed/com.android.foo@37.apex` is hard linked to |
| `/data/apex/active/com.android.foo@37.apex`. |
| 5. Finally, regular activation logic for uncompressed APEX files is performed |
| for `/data/apex/active/com.android.foo@37.apex`. |
| |
| For more information see implementation of `OnStart` function in |
| `system/apex/apexd/apexd.cpp`. |
| |
| ### Interaction with OTA |
| |
| Compressed APEX files have some implications on the OTA delivery and |
| application. Since an OTA might contain a compressed APEX file with higher |
| version compared to what is currently active on the device, some free space must |
| be reserved before rebooting a device to apply an OTA. |
| |
| To help OTA system, two new binder APIs are exposed by apexd: |
| |
| * `calculateSizeForCompressedApex` - calculates size required for decompressing |
| APEX files in OTA package. It can be used to check if device has enough space |
| before downloading an OTA. |
| * `reserveSpaceForCompressedApex` - reserves space on the disk that in the |
| future will be used by apexd for decompression of compressed APEX files inside |
| the OTA package. |
| |
| |
| In case of A/B OTA, `apexd` will attempt decompression in the background as part |
| of the postinstall OTA routine. If decompression fails, `apexd` will fallback to |
| decompressing during the boot that applies the OTA. |
| |
| ## Alternatives considered when developing APEX |
| |
| Here are some options that we considered when designing the APEX file format, |
| and why we included or excluded them. |
| |
| ### Regular package management systems |
| |
| Linux distributions have package management systems like `dpkg` and `rpm`, which |
| are powerful, mature and robust. However, they weren't adopted for APEX because |
| they can't protect the packages after installation. Verification is done only |
| when packages are being installed. Attackers can break the integrity of the |
| installed packages unnoticed. This is a regression for Android where all system |
| components were stored in read-only file systems whose integrity is protected by |
| dm-verity for every I/O. Any tampering to system components must be prohibited, |
| or be detectable so that the device can refuse to boot if compromised. |
| |
| ### dm-crypt for integrity |
| |
| The files in an APEX container are from built-in partitions (for example, the |
| `/system` partition) that are protected by dm-verity, where any modification to |
| the files are prohibited even after the partitions are mounted. To provide the |
| same level of security to the files, all files in an APEX are stored in a file |
| system image that is paired with a hash tree and a vbmeta descriptor. Without |
| dm-verity, an APEX in the `/data` partition is vulnerable to unintended |
| modifications made after it's verified and installed. |
| |
| In fact, the `/data` partition is also protected by encryption layers such as |
| dm-crypt. Although this provides some level of protection against tampering, its |
| primary purpose is privacy, not integrity. When an attacker gains access to the |
| `/data` partition, there can be no further protection, and this again is a |
| regression compared to every system component being in the `/system` partition. |
| The hash tree inside an APEX file together with dm-verity provides the same |
| level of content protection. |
| |
| ### Redirecting paths from `/system` to `/apex` |
| |
| System component files packaged in an APEX are accessible via new paths like |
| `/apex/<name>/lib/libfoo.so`. When the files were part of the `/system` |
| partition, they were accessible via paths such as `/system/lib/libfoo.so`. A |
| client of an APEX file (other APEX files or the platform) should use the new |
| paths. This change in paths might require updates to the existing code. |
| |
| One way to avoid the path change is to overlay the file contents in an APEX file |
| over the `/system` partition. However, we decided not to overlay files over the |
| `/system` partition because we believed this would negatively affect performance |
| as the number of files being overlayed (possibly even stacked one after another) |
| increases. |
| |
| Another option was to hijack file access functions such as `open`, `stat`, and |
| `readlink`, so that paths that start with `/system` are redirected to their |
| corresponding paths under `/apex`. We discarded this option because it's |
| practically infeasible to change all functions that accept paths. For example, |
| some apps statically link Bionic, which implements the functions. In that case, |
| the redirection won't happen for the app. |