Vendor APEX

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_liband 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 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",
}

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 have only service definitions. Init scripts in Vendor APEXes can have on <property> directives as well.

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.

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:

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>
  • 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";