VNDK Build System Support

The build system comes with built-in VNDK support in Android 8.1. If the VNDK support is enabled, the build system checks the dependencies between modules, builds a vendor-specific variant for vendor modules, and automatically installs those modules into designated directories.

The following example illustrates the basic concepts:

libexample with vendor_available:true and vndk.enabled:true

Figure 1. VNDK support enable.

The Android.bp module definition defines a library named libexample. The vendor_available property means that both framework modules and vendor modules may depend on libexample. In this example, both the framework executable /system/bin/foo and the vendor executable /vendor/bin/bar depend on libexample and have libexample in their shared_libs properties.

If libexample is used by both framework modules and vendor modules, two variants of libexample are built. The core variant (named after libexample) is used by framework modules and the vendor variant (named after libexample.vendor) is used by vendor modules.

Two variants are installed into different directories. The core variant is installed into /system/lib[64]/libexample.so. The vendor variant is installed into /system/lib[64]/vndk/libexample.so because vndk.enabled is true.

For more details, see Module definition.

Configuration

To enable full build system support for a product device, add BOARD_VNDK_VERSION to BoardConfig.mk:

BOARD_VNDK_VERSION := current

Migration notes

Adding BOARD_VNDK_VERSION to BoardConfig.mk has a global effect. When defined in BoardConfig.mk, all modules are checked. There is no mechanism to blacklist or whitelist an offending module. The practice is to add BOARD_VNDK_VERSION after cleaning all unnecessary dependencies.

During a migration process, you can test and compile a module by setting BOARD_VNDK_VERSION in your environment variables:

$ BOARD_VNDK_VERSION=current m module_name.vendor

Yet another side effect is the removal of default global header search paths. If BOARD_VNDK_VERSION is enabled, the following default header search paths are not added by default:

  • frameworks/av/include
  • frameworks/native/include
  • frameworks/native/opengl/include
  • hardware/libhardware/include
  • hardware/libhardware_legacy/include
  • hardware/ril/include
  • libnativehelper/include
  • libnativehelper/include_deprecated
  • system/core/include
  • system/media/audio/include

If a module depends on the headers from these directories, its author must explicitly specify the dependencies with header_libs, static_libs, and/or shared_libs.

Module definition

To build Android with BOARD_VNDK_VERSION, developers must revise their module definition in either Android.mk or Android.bp. This subsection describes different kinds of module definitions, several VNDK-related module properties, and the dependency checks implemented in the build system.

Vendor modules

Vendor modules are vendor-specific executables or shared libraries that must be installed into a vendor partition. In Android.bp files, vendor modules must set vendor or proprietary property to true. In Android.mk files, vendor modules must set LOCAL_VENDOR_MODULE or LOCAL_PROPRIETARY_MODULE to true.

If BOARD_VNDK_VERSION is defined, the build system disallows the dependencies between vendor modules and framework modules. The build system emits errors if:

  • a module without vendor:true depends on a module with vendor:true, or
  • a module with vendor:true depends on a non-llndk_library module that has neither vendor:true nor vendor_available:true.

The aforementioned dependency check applies to header_libs, static_libs, and shared_libs in Android.bp. It also applies to LOCAL_HEADER_LIBRARIES, LOCAL_STATIC_LIBRARIES and LOCAL_SHARED_LIBRARIES in Android.mk.

LL-NDK

LL-NDK shared libraries are shared libraries with stable ABIs. Both framework and vendor modules share the same and the latest implementation. For each LL-NDK shared library, there is an llndk_library module definition in an Android.bp file:

llndk_library {
    name: "libvndksupport",
    symbol_file: "libvndksupport.map.txt",
}

This module definition specifies a module name and a symbol file, which describes the symbols that should be visible to vendor modules. For example:

LIBVNDKSUPPORT {
  global:
    android_load_sphal_library; # vndk
    android_unload_sphal_library; # vndk
  local:
    *;
};

Based on the symbol file, the build system generates a stub shared library for vendor modules. Vendor modules link with these stub shared libraries if BOARD_VNDK_VERSION is enabled.

A symbol is included in the stub shared library only if:

  • it is not defined in the section end with _PRIVATE or _PLATFORM,
  • it does not have #platform-only tag, and
  • it does not have #introduce* tags or the tag matches with the target.

VNDK

In Android.bp files, cc_library, cc_library_static, cc_library_shared, and cc_library_headers module definitions support three VNDK-related properties: vendor_available, vndk.enabled, and vndk.support_system_process.

If vendor_available or vndk.enabled is true, two variants (core and vendor) may be built. The core variant should be treated as a framework module and the vendor variant should be treated as a vendor module. If some framework modules depend on this module, the core variant is built. If some vendor modules depend on this module, the vendor variant is built.

The build system enforces these dependency checks:

  • The core variant is always framework-only and inaccessible to vendor modules.
  • The vendor variant is always inaccessible to framework modules.
  • All dependencies of the vendor variant, which are specified in header_libs, static_libs, and/or shared_libs, must be either an llndk_library or a module with vendor_available or vndk.enabled.
  • If vendor_available is true (the only valid value for Android 8.1), the vendor variant is accessible to all vendor modules.
  • In AOSP master, if vendor_available is false, the vendor variant is accessible only to other VNDK or VNDK-SP modules (i.e., modules with vendor:true cannot link vendor_available:false modules).

The default installation path for cc_library or cc_library_shared is determined by the following rules:

  • The core variant is installed to /system/lib[64].
  • The vendor variant installation path may vary:
    • If vndk.enabled is false, the vendor variant is installed into /vendor/lib[64].
    • If vndk.enabled is true, vndk.support_system_process can be either true or false.
      • If vndk.support_system_process is false, the vendor variant is installed into /system/lib[64]/vndk.
      • Conversely, the vendor variant is installed to /system/lib[64]/vndk-sp.

The table below summarizes how the build system handles the vendor variants:

vendor_available

vndk

Vendor variant descriptions

enabled

support_same_process

true

false

false

The vendor variants are VND-ONLY

Shared libraries are installed into /vendor/lib[64].

true

Invalid (Build error)

true

false

The vendor variants are VNDK.

Shared libraries are installed to /system/lib[64]/vndk.

true

The vendor variants are VNDK-SP.

Shared libraries are installed to /system/lib[64]/vndk-sp.

false

false

false

No vendor variants. This module is FWK-ONLY.

true

Invalid (Build error)

true

false

The vendor variants are VNDK-Indirect.

Shared libraries are installed to /system/lib[64]/vndk.

These must not be directly used by vendor modules.

New in AOSP master (not in Android 8.1).

true

The vendor variants are VNDK-SP-Indirect-Private.

Shared libraries are installed to /system/lib[64]/vndk-sp.

These must not be directly used by vendor modules.

New in AOSP master (not in Android 8.1).

Conditional compilation

If there are some subtle differences between the core variant and the vendor variant, you can use target.vendor to specify different options for conditional compilation. For example:

cc_library {
    name: "libconditional_example",
    srcs: ["fwk.c", "both.c"],
    shared_libs: ["libfwk_only", "libboth"],
    target: {
        vendor: {
            exclude_srcs: ["fwk.c"],
            exclude_shared_libs: ["libfwk_only"],
            cflags: ["-DVENDOR_VARIANT=1"],
            cppflags: ["-DVENDOR_VARIANT=1"],
        },
    },
}

In this example, the core variant of libconditional_example includes the code from fwk.c and both.c and depends on the shared libraries libfwk_only and libboth.

On the other hand, the vendor variant of libconditional_example includes only the code from both.c because fwk.c is excluded by the exclude_srcs property. Similarly, libconditional_example depends only on the shared library libboth because libfwk_only is excluded by the exclude_shared_libs property. cflags and cppflags may specified vendor-specific options as well.

Product packages

In the Android build system, the variable PRODUCT_PACKAGES specifies the executables, shared libraries, or packages that should be installed into the device. The transitive dependencies of the specified modules are implicitly installed into the device as well.

If BOARD_VNDK_VERSION is enabled, modules with vendor_available or vndk.enabled get special treatments. If a framework module depends on a module with vendor_available or vndk.enabled, the core variant is included in the transitive installation set. Similarly, if a vendor module depends on a module with vendor_available or vndk.enabled, the vendor variant is included in the transitive installation set.

When the dependencies are invisible to the build system (e.g. shared libraries that may be opened with dlopen() in runtime), you should specify the module names in PRODUCT_PACKAGES to install those modules explicitly.

If a module has vendor_available or vndk.enabled, the module name stands for its core variant. To explicitly specify the vendor variant in PRODUCT_PACKAGES, append a .vendor suffix to the module name. For example:

cc_library {
    name: "libexample",
    srcs: ["example.c"],
    vendor_available: true,
}

In this example, libexample stands for /system/lib[64]/libexample.so and libexample.vendor stands for /vendor/lib[64]/libexample.so. To install /vendor/lib[64]/libexample.so, add libexample.vendor to PRODUCT_PACKAGES:

PRODUCT_PACKAGES += libexample.vendor