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
and LLNDK libraries. 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.
In the following snippet, the APEX contains both the binary (my_service
) and its
non-stable dependencies (*.so
files).
apex {
..
vendor: true,
binaries: ["my_service"],
..
}
In the following snippet, the APEX contains the shared library
my_standalone_lib
and any of its non-stable dependencies (as described above).
apex {
..
vendor: true,
native_shared_libs: ["my_standalone_lib"],
..
}
Make APEX smaller
APEX may get bigger because it bundles non-stable dependencies. We recommend
using static linking. Common libraries like libc++.so
and libbase.so
can be
statically linked to HAL binaries. Making a dependency to provide a stable
interface can be another option. The dependency won't be bundled in the APEX.
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 can be served from a vendor APEX when fragments are located in
etc/vintf
of the APEX.
Use the prebuilts
property to embed the VINTF fragments in the APEX.
apex {
..
vendor: true,
prebuilts: ["fragment.xml"],
..
}
prebuilt_etc {
name: "fragment.xml",
src: "fragment.xml",
sub_dir: "vintf",
}
Query APIs
When VINTF fragments are added to APEX, use libbinder_ndk
APIs to get the
mappings of HAL interfaces and APEX names.
AServiceManager_isUpdatableViaApex("com.android.foo.IFoo/default")
:true
if the HAL instance is defined in APEX.AServiceManager_getUpdatableApexName("com.android.foo.IFoo/default", ...)
: gets the APEX name which defines the HAL instance.AServiceManager_openDeclaredPassthroughHal("mapper", "instance", ...)
: use this to open a passthrough HAL.
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 in vendor APEXes can have service
definitions and
on <property or event>
directives.
Make sure that a service
definition points a binary in the same APEX.
For example, com.android.foo
APEX may define a service named foo-service
.
on foo-service /apex/com.android.foo/bin/foo
...
Be careful when using on
directives. Since init scripts in APEXes are
parsed and executed after APEXes are activated, some events or properties
can't be used. Use apex.all.ready=true
to fire actions as early as possible.
Bootstrap APEXes can use on init
, but not
on early-init
.
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
Bootstrap Vendor APEXes
Some HAL services like keymint
should be available before APEXes are
activated. Those HALs usually set early_hal
in their service definition in the
init script. Another example is animation
class which is typically started
earlier than the post-fs-data
event. When such an early HAL service is
packaged in vendor APEX, make the apex "vendorBootstrap": true
in its APEX
Manifest so that it can be activated earlier. Note that bootstrap APEXes can be
activated only from the prebuilt location like /vendor/apex
, not from
/data/apex
.
System properties
These are the system properties that the framework reads to support vendor APEXes:
input_device.config_file.apex=<apex name>
- when set, the input configuration files (*.idc
,*.kl
, and*.kcm
) are searched from/etc/usr
directory of the APEX.ro.vulkan.apex=<apex name>
- when set, the Vulkan driver is loaded from the APEX. Since the Vulkan driver is used by early HALs, make the APEX Bootstrap APEX and configure that linker namespace visible.
Set the system properties in init scripts using setprop
command.
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.
// Default version.
apex {
name: "com.oem.camera.hal.my_apex_default",
vendor: true,
..
}
// Non-default version.
apex {
name: "com.oem.camera.hal.my_apex_experimental",
vendor: 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";