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 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_liband 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:

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