# 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.
![APEX file format](apex-format.png)
**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,
/apex/name@ver
).
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 <= 8
## 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`.
- ``: The APEX is signed with the `` certificate in the same
directory as `PRODUCT_DEFAULT_DEV_CERTIFICATE`.
- `:`: The APEX is signed with the certificate that is defined by the
Soong module named ``. 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/@`
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/`.
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//` 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
Set the following system property to `true` to support APEX file updates.
```
:
PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true
BoardConfig.mk:
TARGET_FLATTEN_APEX := false
```
or just
```
:
$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)
```
## Flattened APEX
For legacy devices, it is sometimes impossible or infeasible to update the old
kernel to fully support APEX. For example, the kernel might have been built
without `CONFIG_BLK_DEV_LOOP=Y`, which is crucial for mounting the file system
image inside an APEX.
Flattened APEX is a specially built APEX that can be activated on devices with a
legacy kernel. Files in a flattened APEX are directly installed to a directory
under the built-in partition. For example, `lib/libFoo.so` in a flattend APEX
`my.apex` is installed to `/system/apex/my.apex/lib/libFoo.so`.
Activating a flattened APEX doesn't involve the loop device. The entire
directory `/system/apex/my.apex` is directly bind-mounted to `/apex/name@ver`.
Flattened APEXs can't be updated by downloading updated versions of the APEXs
from network because the downloaded APEXs can't be flattened. Flattened APEXs
can be updated only via a regular OTA.
Note that flattened APEX is the default configuration for now. This means all
APEXes are by default flattened unless you explicitly configure your device to
support updatable APEX (explained above).
Also note that, mixing flattened and non-flattened APEXes in a device is NOT
supported. It should be either all non-flattened or all flattened. This is
especially important when shipping pre-signed APEX prebuilts for the projects
like Mainline. APEXes that are not pre-signed (i.e. built from the source)
should also be non-flattened and signed with proper keys in that case. The
device should inherit from `updatable_apex.mk` as explained above.
## 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.
![Compressed APEX file format](compressed-apex-format.png)
**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//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.