You can use the APEX file format to package and install lower-level Android OS modules. It allows independent building and installation of components like native services and libraries, HAL implementations, firmware, config files, etc.
Vendor APEXes are installed by the build system automatically in the /vendor
partition and activated at runtime by apexd
just like APEXes in other
partitions.
Use cases
Modularization of vendor images
APEXes facilitate a natural bundling and modularization of feature implementations on vendor images.
When vendor images are built as a combination of independently-built vendor APEXes, device manufacturers are able to easily pick and choose the specific vendor implementations wanted on their device. Manufacturers can even create a new vendor APEX if none of the provided APEXes fit their need, or they have a brand new custom hardware.
For example, an OEM may choose to compose their device with the AOSP wifi implementation APEX, the SoC bluetooth implementation APEX, and a custom OEM telephony implementation APEX.
Without vendor APEXes, an implementation with so many dependencies between vendor components requires careful coordination and tracking. By wrapping all components (including configuration files and extra libraries) in APEXes with clearly defined interfaces at any point of cross-feature communication, the different components become interchangeable.
Developer iteration
Vendor APEXes help developers iterate faster while developing vendor modules by bundling an entire feature implementation, like the wifi HAL, inside a vendor APEX. Developers can then build and individually push the vendor APEX to test changes, instead of rebuilding the whole vendor image.
This simplifies and speeds up the developer iteration cycle for developers who primarily work in one feature area and want to iterate on just that feature area.
The natural bundling of a feature area into an APEX also simplifies the process of building, pushing, and testing changes for that feature area. For example, reinstalling an APEX automatically updates any bundled library or config files the APEX includes.
Bundling a feature area into an APEX also simplifies debugging or reverting when bad device behavior is observed. For example, if telephony is working poorly in a new build, then developers could try installing an older telephony implementation APEX on a device (without needing to flash a full build) and seeing if good behavior is restored.
Example workflow:
# Build the entire device and flash. OR, obtain an already-flashed device.
source build/envsetup.sh && lunch oem_device-userdebug
m
fastboot flashall -w
# Test the device.
... testing ...
# Check previous behavior using a vendor APEX from one week ago, downloaded from
# your continuous integration build.
... download command ...
adb install <path to downloaded APEX>
adb reboot
... testing ...
# Edit and rebuild just the APEX to change and test behavior.
... edit APEX source contents ...
m <apex module name>
adb install out/<path to built APEX>
adb reboot
... testing ...
Examples
Basics
See the main APEX File Format page for generic APEX information, including device requirements, file format details, and installation steps.
In Android.bp
, setting the vendor: true
property makes an APEX module a
vendor APEX.
apex {
..
vendor: true,
..
}
Binaries and shared libraries
An APEX includes transitive dependencies inside the APEX payload unless they have stable interfaces.
Stable native interfaces for vendor APEX dependencies include cc_library
with
stubs
, ndk_library
, or llndk_library
. These dependencies are excluded from
packaging, and dependencies are recorded in the APEX manifest. The manifest is
processed by linkerconfig
so that the external native dependencies are
available at runtime.
Unlike APEXes in the /system
partition, vendor APEXes are typically tied to a
specific VNDK version. VNDK libraries guarantee ABI stability within the
release, so we can treat VNDK libraries as stable and reduce the size of vendor
APEXes by excluding them from the APEXes using the use_vndk_as_stable
property.
In the snippet below, the APEX will contain both the binary (my_service
) and
its non-stable dependencies (*.so
files). It will not contain VNDK libraries,
even when my_service
is built with VNDK libraries like libbase
. Instead, at
runtime my_service
will use libbase
from VNDK libraries provided by the
system.
apex {
..
vendor: true,
use_vndk_as_stable: true,
binaries: ["my_service"],
..
}
In the snippet below, the APEX will contain the shared library
my_standalone_lib
and any of its non-stable dependencies (as described above).
apex {
..
vendor: true,
use_vndk_as_stable: true,
native_shared_libs: ["my_standalone_lib"],
..
}
HAL implementations
To define a HAL implementation, provide the corresponding binaries and libraries inside a vendor APEX similar to the following examples:
To fully encapsulate the HAL implementation, the APEX should also specify any relevant VINTF fragments and init scripts.
VINTF fragments
VINTF fragments are installed in /vendor/etc/vintf
, rather than contained
inside the APEX.
Use the standard vintf_fragments
property:
apex {
..
vendor: true,
vintf_fragments: ["fragment.xml"],
..
}
Init scripts
APEXes can include init scripts in two ways: (A) a prebuilt text file within the
APEX payload, or (B) a regular init script in /vendor/etc
. You can set both
for the same APEX.
Init script in APEX:
prebuilt_etc {
name: "myinit.rc",
src: "myinit.rc"
}
apex {
..
vendor: true,
prebuilts: ["myinit.rc"],
..
}
Init scripts within APEXes can only have service
definitions, not on
<property>
directives. If your service needs to depend on some conditions, then
you need to install the init script in /vendor
.
Init script in /vendor
:
apex {
..
vendor: true,
init_rc: ["myinit.in.vendor.rc"],
..
}
Any references to the binary path within an init script need to reflect the path inside the APEX. You can either hardcode the path in the init script source file, or replace it from an existing source file using a genrule.
Firmware
Example:
Embed firmware in a vendor APEX with the prebuilt_firmware
module type, as
follows.
prebuilt_firmware {
name: "my.bin",
src: "path_to_prebuilt_firmware",
vendor: true,
}
apex {
..
vendor: true,
prebuilts: ["my.bin"], // installed inside APEX as /etc/firmware/my.bin
..
}
prebuilt_firmware
modules are installed in the <apex name>/etc/firmware
directory of the APEX. ueventd
scans /apex/*/etc/firmware
directories to
find firmware modules.
The file_contexts
of the APEX should label any firmware payload entries
properly to ensure that these files are accessible by ueventd
at runtime;
typically, the vendor_file
label is sufficient. For example:
(/.*)? u:object_r:vendor_file:s0
Kernel modules
Embed kernel modules in a vendor APEX as prebuilt modules, as follows.
prebuilt_etc {
name: "my.ko",
src: "my.ko",
vendor: true,
sub_dir: "modules"
}
apex {
..
vendor: true,
prebuilts: ["my.ko"], // installed inside APEX as /etc/modules/my.ko
..
}
The file_contexts
of the APEX should label any kernel module payload entries
properly. For example:
/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0
Kernel modules must be installed explicitly. The following example init script
in the vendor partition shows installation via insmod
:
my_init.rc
:
on early-boot
insmod /apex/myapex/etc/modules/my.ko
..
Runtime resource overlays
Example:
Embed runtime resource overlays in a vendor APEX
using the rros
property.
runtime_resource_overlay {
name: "my_rro",
soc_specific: true,
}
apex {
..
vendor: true,
rros: ["my_rro"], // installed inside APEX as /overlay/my_rro.apk
..
}
Other config files
Vendor APEXes support various other config files typically found on the vendor partition as prebuilts inside vendor APEXes, and more are being added.
Examples:
- Feature declaration XMLs
- Sensors feature XMLs as prebuilts in a sensor HAL vendor APEX
- Input config files
- Touchscreen configs as prebuilts in a config-only vendor APEX
Extra development features
APEX selection at bootup
Example:
Developers can also install multiple versions of vendor APEXes that share the
same APEX name and key, and then choose which version is activated during each
bootup using persistent sysprops. For certain developer use cases, this might be
simpler than installing a new copy of the APEX using adb install
.
Example use cases:
- Install 3 versions of the wifi HAL vendor APEX: QA teams can run manual or automated testing using one version, then reboot into another version and rerun the tests, then compare the final results.
- Install 2 versions of the camera HAL vendor APEX, current and experimental: Dogfooders can use the experimental version without downloading and installing an additional file, so they can easily swap back.
During bootup, apexd
looks for sysprops following a specific format to
activate the right APEX version.
The expected formats for the property key are:
- Bootconfig
- Used to set the default value, in
BoardConfig.mk
. androidboot.vendor.apex.<apex name>
- Used to set the default value, in
- Persistent sysprop
- Used to change the default value, set on an already-booted device.
- Overrides the bootconfig value if present.
persist.vendor.apex.<apex name>
The value of the property should be the filename of the APEX which should be activated.
The only change to the APEX Android.bp
definitions is the
multi_install_skip_symbol_files
field, which should be true
for all
non-default multi-installed APEX definitions:
// Default version.
apex {
name: "com.oem.camera.hal.my_apex_default",
apex_name: "com.oem.camera.hal",
vendor: true,
..
}
// Non-default version.
apex {
name: "com.oem.camera.hal.my_apex_experimental",
apex_name: "com.oem.camera.hal",
vendor: true,
multi_install_skip_symbol_files: true,
..
}
The default version should also be configured using bootconfig in
BoardConfig.mk
:
# Example for APEX "com.oem.camera.hal" with the default above:
BOARD_BOOTCONFIG += \
androidboot.vendor.apex.com.oem.camera.hal=com.oem.camera.hal.my_apex_default
After the device is booted, change the activated version by setting the persistent sysprop:
$ adb root;
$ adb shell setprop \
persist.vendor.apex.com.oem.camera.hal \
com.oem.camera.hal.my_apex_experimental;
$ adb reboot;
If the device supports updating bootconfig after flashing (such as via fastboot
oem
commands), then changing the bootconfig property for the multi-installed
APEX also changes the version activated on bootup.
For virtual reference devices based on Cuttlefish,
you can use the --extra_bootconfig_args
command to set the bootconfig property
directly while launching. For example:
launch_cvd --noresume \
--extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";