VNDK 构建系统支持

在 Android 8.1 及更高版本中,构建系统内置 VNDK 支持。启用 VNDK 支持后,构建系统会检查模块之间的依赖关系,为供应商模块构建特定于供应商的变体,并将这些模块自动安装到指定目录中。

VNDK 构建支持示例

在此示例中,Android.bp 模块定义定义了一个名为 libexample 的库。vendor_available 属性表示框架模块和供应商模块可以依赖于 libexample

libexample vendor_available:true and vndk.enabled:true

图 1. 支持已启用。

框架可执行文件 /system/bin/foo 和供应商可执行文件 /vendor/bin/bar 都依赖于 libexample,并且在其 shared_libs 属性中包含 libexample

如果框架模块和供应商模块都使用了 libexample,则会构建 libexample 的两个变体。核心变体(以 libexample 命名)供框架模块使用,而供应商变体(以 libexample.vendor 命名)供供应商模块使用。这两个变体安装在不同的目录中

  • 核心变体安装到 /system/lib[64]/libexample.so
  • 由于 vndk.enabledtrue,供应商变体版本会安装到 VNDK APEX 中。

如需了解详情,请参阅模块定义

配置 build 支持

要为产品设备启用完整的 build 系统支持,请将 BOARD_VNDK_VERSION 添加到 BoardConfig.mk

BOARD_VNDK_VERSION := current

此设置具有全局影响:在 BoardConfig.mk 中定义后,系统会检查所有模块。由于没有机制可以将有问题的模块列入黑名单或白名单,因此您应先清除所有不必要的依赖项,然后再添加 BOARD_VNDK_VERSION。您可以通过在环境变量中设置 BOARD_VNDK_VERSION 来测试和编译模块

$ BOARD_VNDK_VERSION=current m module_name.vendor

启用 BOARD_VNDK_VERSION 后,系统会移除多个默认全局标头搜索路径。其中包括

  • 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

如果模块依赖于这些目录中的标头,您必须(显式)使用 header_libsstatic_libs 和/或 shared_libs 指定依赖项。

VNDK APEX

在 Android 10 及更低版本中,具有 vndk.enabled 的模块安装在 /system/lib[64]/vndk[-sp]-${VER} 中。在 Android 11 及更高版本中,VNDK 库以 APEX 格式打包,VNDK APEX 的名称为 com.android.vndk.v${VER}。根据设备配置,VNDK APEX 会展平不展平,并且可以从规范路径 /apex/com.android.vndk.v${VER} 中获取。

VNDK APEX

图 2. VNDK APEX。

模块定义

要使用 BOARD_VNDK_VERSION 构建 Android,您必须在 Android.mkAndroid.bp 中修订模块定义。本部分介绍了不同类型的模块定义、几个与 VNDK 相关的模块属性以及在 build 系统中实现的依赖项检查。

供应商模块

供应商模块是供应商特定的可执行文件或共享库,必须安装到供应商分区中。在 Android.bp 文件中,供应商模块必须将 vendor 或 proprietary 属性设置为 true。在 Android.mk 文件中,供应商模块必须将 LOCAL_VENDOR_MODULELOCAL_PROPRIETARY_MODULE 设置为 true

如果定义了 BOARD_VNDK_VERSION,则 build 系统不允许供应商模块和框架模块之间存在依赖项,并且在以下情况下会发出错误:

  • 没有 vendor:true 的模块依赖于具有 vendor:true 的模块,或者
  • 具有 vendor:true 的模块依赖于非 llndk_library 模块,该模块既没有 vendor:true 也没有 vendor_available:true

依赖项检查适用于 Android.bp 中的 header_libsstatic_libsshared_libs,以及 Android.mk 中的 LOCAL_HEADER_LIBRARIESLOCAL_STATIC_LIBRARIESLOCAL_SHARED_LIBRARIES

LL-NDK

LL-NDK 共享库是具有稳定 ABI 的共享库。框架模块和供应商模块共享相同且最新的实现。对于每个 LL-NDK 共享库,cc_library 都包含一个带有符号文件的 llndk 属性

cc_library {
    name: "libvndksupport",
    llndk: {
        symbol_file: "libvndksupport.map.txt",
    },
}

符号文件描述了对供应商模块可见的符号。例如

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

基于符号文件,build 系统为供应商模块生成一个桩共享库,当启用 BOARD_VNDK_VERSION 时,这些模块会与这些库链接。只有当符号满足以下条件时,才会包含在桩共享库中:

  • 未在以 _PRIVATE_PLATFORM 结尾的部分中定义,
  • 没有 #platform-only 标记,并且
  • 没有 #introduce* 标记或标记与目标匹配。

VNDK

Android.bp 文件中,cc_librarycc_library_staticcc_library_sharedcc_library_headers 模块定义支持三个与 VNDK 相关的属性:vendor_availablevndk.enabledvndk.support_system_process

如果 vendor_availablevndk.enabledtrue,则可以构建两个变体版本(核心供应商)。核心变体版本应被视为框架模块,供应商变体版本应被视为供应商模块。如果某些框架模块依赖于此模块,则会构建核心变体版本。如果某些供应商模块依赖于此模块,则会构建供应商变体版本。build 系统会强制执行以下依赖项检查:

  • 核心变体版本始终是仅限框架的,并且供应商模块无法访问。
  • 供应商变体版本始终是框架模块无法访问的。
  • 供应商变体版本的所有依赖项(在 header_libsstatic_libs 和/或 shared_libs 中指定)必须是 llndk_library 或具有 vendor_availablevndk.enabled 的模块。
  • 如果 vendor_availabletrue,则所有供应商模块都可以访问供应商变体版本。
  • 如果 vendor_availablefalse,则供应商变体版本仅可由其他 VNDK 或 VNDK-SP 模块访问(即,具有 vendor:true 的模块无法链接 vendor_available:false 模块)。

cc_librarycc_library_shared 的默认安装路径由以下规则确定:

  • 核心变体版本安装到 /system/lib[64]
  • 供应商变体版本的安装路径可能有所不同:
    • 如果 vndk.enabledfalse,则供应商变体版本安装到 /vendor/lib[64]
    • 如果 vndk.enabledtrue,则供应商变体版本安装到 VNDK APEX (com.android.vndk.v${VER})。

下表总结了 build 系统如何处理供应商变体版本:

vendor_available vndk
enabled
vndk
support_system_process
供应商变体版本说明
true false false 供应商变体版本为 VND-ONLY。共享库安装到 /vendor/lib[64]
true 无效(Build 错误)
true false 供应商变体版本为 VNDK。共享库安装到 VNDK APEX。
true 供应商变体版本为 VNDK-SP。共享库安装到 VNDK APEX。

false

false

false

没有供应商变体版本。此模块为 FWK-ONLY

true 无效(Build 错误)
true false 供应商变体版本为 VNDK-Private。共享库安装到 VNDK APEX。供应商模块不得直接使用这些库。
true 供应商变体版本为 VNDK-SP-Private。共享库安装到 VNDK APEX。供应商模块不得直接使用这些库。

VNDK 扩展

VNDK 扩展是具有其他 API 的 VNDK 共享库。扩展安装到 /vendor/lib[64]/vndk[-sp](没有版本后缀),并在运行时覆盖原始 VNDK 共享库。

定义 VNDK 扩展

在 Android 9 及更高版本中,Android.bp 原生支持 VNDK 扩展。要构建 VNDK 扩展,请定义另一个具有 vendor:trueextends 属性的模块

cc_library {
    name: "libvndk",
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libvndk_ext",
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libvndk",
    },
}

具有 vendor:truevndk.enabled:trueextends 属性的模块定义了 VNDK 扩展

  • extends 属性必须指定基本 VNDK 共享库名称(或 VNDK-SP 共享库名称)。
  • VNDK 扩展(或 VNDK-SP 扩展)以它们扩展的基本模块名称命名。例如,libvndk_ext 的输出二进制文件是 libvndk.so,而不是 libvndk_ext.so
  • VNDK 扩展安装到 /vendor/lib[64]/vndk
  • VNDK-SP 扩展安装到 /vendor/lib[64]/vndk-sp
  • 基本共享库必须同时具有 vndk.enabled:truevendor_available:true

VNDK-SP 扩展必须从 VNDK-SP 共享库扩展(vndk.support_system_process 必须相等)

cc_library {
    name: "libvndk_sp",
    vendor_available: true,
    vndk: {
        enabled: true,
        support_system_process: true,
    },
}

cc_library {
    name: "libvndk_sp_ext",
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libvndk_sp",
        support_system_process: true,
    },
}

VNDK 扩展(或 VNDK-SP 扩展)可能依赖于其他供应商共享库

cc_library {
    name: "libvndk",
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libvndk_ext",
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libvndk",
    },
    shared_libs: [
        "libvendor",
    ],
}

cc_library {
    name: "libvendor",
    vendor: true,
}

使用 VNDK 扩展

如果供应商模块依赖于 VNDK 扩展定义的其他 API,则该模块必须在其 shared_libs 属性中指定 VNDK 扩展的名称

// A vendor shared library example
cc_library {
    name: "libvendor",
    vendor: true,
    shared_libs: [
        "libvndk_ext",
    ],
}

// A vendor executable example
cc_binary {
    name: "vendor-example",
    vendor: true,
    shared_libs: [
        "libvndk_ext",
    ],
}

如果供应商模块依赖于 VNDK 扩展,则这些 VNDK 扩展会自动安装到 /vendor/lib[64]/vndk[-sp]。如果模块不再依赖于 VNDK 扩展,请在 CleanSpec.mk 中添加清除步骤以移除共享库。例如

$(call add-clean-step, rm -rf $(TARGET_OUT_VENDOR)/lib/libvndk.so)

条件编译

本部分介绍了如何处理以下三个 VNDK 共享库之间细微的差异(例如,从变体版本之一添加或移除功能):

  • 核心变体版本(例如,/system/lib[64]/libexample.so
  • 供应商变体版本(例如,/apex/com.android.vndk.v${VER}/lib[64]/libexample.so
  • VNDK 扩展(例如,/vendor/lib[64]/vndk[-sp]/libexample.so

条件编译器标志

Android build 系统默认情况下为供应商变体版本和 VNDK 扩展定义 __ANDROID_VNDK__。您可以使用 C 预处理器保护来保护代码

void all() { }

#if !defined(__ANDROID_VNDK__)
void framework_only() { }
#endif

#if defined(__ANDROID_VNDK__)
void vndk_only() { }
#endif

除了 __ANDROID_VNDK__ 之外,还可以在 Android.bp 中指定不同的 cflagscppflagstarget.vendor 中指定的 cflagscppflags 专用于供应商变体版本。

例如,以下 Android.bp 定义了 libexamplelibexample_ext

cc_library {
    name: "libexample",
    srcs: ["src/example.c"],
    vendor_available: true,
    vndk: {
        enabled: true,
    },
    target: {
        vendor: {
            cflags: ["-DLIBEXAMPLE_ENABLE_VNDK=1"],
        },
    },
}

cc_library {
    name: "libexample_ext",
    srcs: ["src/example.c"],
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libexample",
    },
    cflags: [
        "-DLIBEXAMPLE_ENABLE_VNDK=1",
        "-DLIBEXAMPLE_ENABLE_VNDK_EXT=1",
    ],
}

这是 src/example.c 的代码清单

void all() { }

#if !defined(LIBEXAMPLE_ENABLE_VNDK)
void framework_only() { }
#endif

#if defined(LIBEXAMPLE_ENABLE_VNDK)
void vndk() { }
#endif

#if defined(LIBEXAMPLE_ENABLE_VNDK_EXT)
void vndk_ext() { }
#endif

根据这两个文件,build 系统会生成具有以下导出符号的共享库:

安装路径 导出符号
/system/lib[64]/libexample.so allframework_only
/apex/com.android.vndk.v${VER}/lib[64]/libexample.so allvndk
/vendor/lib[64]/vndk/libexample.so allvndkvndk_ext

对导出符号的要求

VNDK ABI 检查器会将VNDK 供应商变体版本VNDK 扩展的 ABI 与 prebuilts/abi-dumps/vndk 下的参考 ABI 转储进行比较。

  • VNDK 供应商变体版本(例如,/apex/com.android.vndk.v${VER}/lib[64]/libexample.so)导出的符号必须与 ABI 转储中定义的符号相同(而不是其超集)。
  • VNDK 扩展(例如,/vendor/lib[64]/vndk/libexample.so)导出的符号必须是 ABI 转储中定义的符号的超集。

如果VNDK 供应商变体版本VNDK 扩展未能遵循上述要求,VNDK ABI 检查器会发出 build 错误并停止 build。

从供应商变体版本中排除源文件或共享库

要从供应商变体版本中排除源文件,请将其添加到 exclude_srcs 属性。同样,要确保共享库未与供应商变体版本链接,请将这些库添加到 exclude_shared_libs 属性。例如

cc_library {
    name: "libexample_cond_exclude",
    srcs: ["fwk.c", "both.c"],
    shared_libs: ["libfwk_only", "libboth"],
    vendor_available: true,
    target: {
        vendor: {
            exclude_srcs: ["fwk.c"],
            exclude_shared_libs: ["libfwk_only"],
        },
    },
}

在此示例中,libexample_cond_exclude 的核心变体版本包含来自 fwk.cboth.c 的代码,并且依赖于共享库 libfwk_onlylibbothlibexample_cond_exclude 的供应商变体版本仅包含来自 both.c 的代码,因为 fwk.c 已被 exclude_srcs 属性排除。同样,它仅依赖于共享库 libboth,因为 libfwk_only 已被 exclude_shared_libs 属性排除。

从 VNDK 扩展导出标头

VNDK 扩展可以向 VNDK 共享库添加新类或新函数。建议将这些声明保留在独立的标头中,并避免更改现有标头。

例如,为 VNDK 扩展 libexample_ext 创建了一个新的标头文件 include-ext/example/ext/feature_name.h

  • Android.bp
  • include-ext/example/ext/feature_name.h
  • include/example/example.h
  • src/example.c
  • src/ext/feature_name.c

在以下 Android.bp 中,libexample 仅导出 include,而 libexample_ext 同时导出 includeinclude-ext。这可确保 feature_name.h 不会被 libexample 的用户错误地包含

cc_library {
    name: "libexample",
    srcs: ["src/example.c"],
    export_include_dirs: ["include"],
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libexample_ext",
    srcs: [
        "src/example.c",
        "src/ext/feature_name.c",
    ],
    export_include_dirs: [
        "include",
        "include-ext",
    ],
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libexample",
    },
}

如果将扩展分离到独立的标头文件不可行,另一种方法是添加 #ifdef 保护。但是,请确保所有 VNDK 扩展用户都添加了 define 标志。您可以定义 cc_defaults 以将 define 标志添加到 cflags,并将共享库与 shared_libs 链接。

例如,要向 VNDK 扩展 libexample2_ext 添加新的成员函数 Example2::get_b(),您必须修改现有标头文件并添加 #ifdef 保护

#ifndef LIBEXAMPLE2_EXAMPLE_H_
#define LIBEXAMPLE2_EXAMPLE_H_

class Example2 {
 public:
  Example2();

  void get_a();

#ifdef LIBEXAMPLE2_ENABLE_VNDK_EXT
  void get_b();
#endif

 private:
  void *impl_;
};

#endif  // LIBEXAMPLE2_EXAMPLE_H_

libexample2_ext 的用户定义了一个名为 libexample2_ext_defaultscc_defaults

cc_library {
    name: "libexample2",
    srcs: ["src/example2.cpp"],
    export_include_dirs: ["include"],
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libexample2_ext",
    srcs: ["src/example2.cpp"],
    export_include_dirs: ["include"],
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libexample2",
    },
    cflags: [
        "-DLIBEXAMPLE2_ENABLE_VNDK_EXT=1",
    ],
}

cc_defaults {
    name: "libexample2_ext_defaults",
    shared_libs: [
        "libexample2_ext",
    ],
    cflags: [
        "-DLIBEXAMPLE2_ENABLE_VNDK_EXT=1",
    ],
}

libexample2_ext 的用户只需在其 defaults 属性中包含 libexample2_ext_defaults 即可

cc_binary {
    name: "example2_user_executable",
    defaults: ["libexample2_ext_defaults"],
    vendor: true,
}

产品软件包

在 Android build 系统中,变量 PRODUCT_PACKAGES 指定应安装到设备中的可执行文件、共享库或软件包。指定模块的传递依赖项也会隐式安装到设备中。

如果启用了 BOARD_VNDK_VERSION,则具有 vendor_availablevndk.enabled 的模块会获得特殊处理。如果框架模块依赖于具有 vendor_availablevndk.enabled 的模块,则核心变体版本会包含在传递安装集中。如果供应商模块依赖于具有 vendor_available 的模块,则供应商变体版本会包含在传递安装集中。但是,无论是否被供应商模块使用,都将安装具有 vndk.enabled 的模块的供应商变体版本。

当依赖项对 build 系统不可见时(例如,可以使用运行时 dlopen() 打开的共享库),您应在 PRODUCT_PACKAGES 中指定模块名称以显式安装这些模块。

如果模块具有 vendor_availablevndk.enabled,则模块名称代表其核心变体版本。要在 PRODUCT_PACKAGES 中显式指定供应商变体版本,请将 .vendor 后缀附加到模块名称。例如

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

在此示例中,libexample 代表 /system/lib[64]/libexample.solibexample.vendor 代表 /vendor/lib[64]/libexample.so。要安装 /vendor/lib[64]/libexample.so,请将 libexample.vendor 添加到 PRODUCT_PACKAGES

PRODUCT_PACKAGES += libexample.vendor