在 Android 8.1 及更高版本中,构建系统内置 VNDK 支持。启用 VNDK 支持后,构建系统会检查模块之间的依赖关系,为供应商模块构建特定于供应商的变体,并将这些模块自动安装到指定目录中。
VNDK 构建支持示例
在此示例中,Android.bp
模块定义定义了一个名为 libexample
的库。vendor_available
属性表示框架模块和供应商模块可以依赖于 libexample
图 1. 支持已启用。
框架可执行文件 /system/bin/foo
和供应商可执行文件 /vendor/bin/bar
都依赖于 libexample
,并且在其 shared_libs
属性中包含 libexample
。
如果框架模块和供应商模块都使用了 libexample
,则会构建 libexample
的两个变体。核心变体(以 libexample
命名)供框架模块使用,而供应商变体(以 libexample.vendor
命名)供供应商模块使用。这两个变体安装在不同的目录中
- 核心变体安装到
/system/lib[64]/libexample.so
。 - 由于
vndk.enabled
为true
,供应商变体版本会安装到 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_libs
、static_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}
中获取。
图 2. VNDK APEX。
模块定义
要使用 BOARD_VNDK_VERSION
构建 Android,您必须在 Android.mk
或 Android.bp
中修订模块定义。本部分介绍了不同类型的模块定义、几个与 VNDK 相关的模块属性以及在 build 系统中实现的依赖项检查。
供应商模块
供应商模块是供应商特定的可执行文件或共享库,必须安装到供应商分区中。在 Android.bp
文件中,供应商模块必须将 vendor 或 proprietary 属性设置为 true
。在 Android.mk
文件中,供应商模块必须将 LOCAL_VENDOR_MODULE
或 LOCAL_PROPRIETARY_MODULE
设置为 true
。
如果定义了 BOARD_VNDK_VERSION
,则 build 系统不允许供应商模块和框架模块之间存在依赖项,并且在以下情况下会发出错误:
- 没有
vendor:true
的模块依赖于具有vendor:true
的模块,或者 - 具有
vendor:true
的模块依赖于非llndk_library
模块,该模块既没有vendor:true
也没有vendor_available:true
。
依赖项检查适用于 Android.bp
中的 header_libs
、static_libs
和 shared_libs
,以及 Android.mk
中的 LOCAL_HEADER_LIBRARIES
、LOCAL_STATIC_LIBRARIES
和 LOCAL_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_library
、cc_library_static
、cc_library_shared
和 cc_library_headers
模块定义支持三个与 VNDK 相关的属性:vendor_available
、vndk.enabled
和 vndk.support_system_process
。
如果 vendor_available
或 vndk.enabled
为 true
,则可以构建两个变体版本(核心和供应商)。核心变体版本应被视为框架模块,供应商变体版本应被视为供应商模块。如果某些框架模块依赖于此模块,则会构建核心变体版本。如果某些供应商模块依赖于此模块,则会构建供应商变体版本。build 系统会强制执行以下依赖项检查:
- 核心变体版本始终是仅限框架的,并且供应商模块无法访问。
- 供应商变体版本始终是框架模块无法访问的。
- 供应商变体版本的所有依赖项(在
header_libs
、static_libs
和/或shared_libs
中指定)必须是llndk_library
或具有vendor_available
或vndk.enabled
的模块。 - 如果
vendor_available
为true
,则所有供应商模块都可以访问供应商变体版本。 - 如果
vendor_available
为false
,则供应商变体版本仅可由其他 VNDK 或 VNDK-SP 模块访问(即,具有vendor:true
的模块无法链接vendor_available:false
模块)。
cc_library
或 cc_library_shared
的默认安装路径由以下规则确定:
- 核心变体版本安装到
/system/lib[64]
。 - 供应商变体版本的安装路径可能有所不同:
- 如果
vndk.enabled
为false
,则供应商变体版本安装到/vendor/lib[64]
。 - 如果
vndk.enabled
为true
,则供应商变体版本安装到 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。 | ||
|
|
|
没有供应商变体版本。此模块为 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:true
和 extends
属性的模块
cc_library { name: "libvndk", vendor_available: true, vndk: { enabled: true, }, } cc_library { name: "libvndk_ext", vendor: true, vndk: { enabled: true, extends: "libvndk", }, }
具有 vendor:true
、vndk.enabled:true
和 extends
属性的模块定义了 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:true
和vendor_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
中指定不同的 cflags
或 cppflags
。target.vendor
中指定的 cflags
或 cppflags
专用于供应商变体版本。
例如,以下 Android.bp
定义了 libexample
和 libexample_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 |
all 、framework_only |
/apex/com.android.vndk.v${VER}/lib[64]/libexample.so |
all 、vndk |
/vendor/lib[64]/vndk/libexample.so |
all 、vndk 、vndk_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.c
和 both.c
的代码,并且依赖于共享库 libfwk_only
和 libboth
。libexample_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
同时导出 include
和 include-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_defaults
的 cc_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_available
或 vndk.enabled
的模块会获得特殊处理。如果框架模块依赖于具有 vendor_available
或 vndk.enabled
的模块,则核心变体版本会包含在传递安装集中。如果供应商模块依赖于具有 vendor_available
的模块,则供应商变体版本会包含在传递安装集中。但是,无论是否被供应商模块使用,都将安装具有 vndk.enabled
的模块的供应商变体版本。
当依赖项对 build 系统不可见时(例如,可以使用运行时 dlopen()
打开的共享库),您应在 PRODUCT_PACKAGES
中指定模块名称以显式安装这些模块。
如果模块具有 vendor_available
或 vndk.enabled
,则模块名称代表其核心变体版本。要在 PRODUCT_PACKAGES
中显式指定供应商变体版本,请将 .vendor
后缀附加到模块名称。例如
cc_library { name: "libexample", srcs: ["example.c"], vendor_available: true, }
在此示例中,libexample
代表 /system/lib[64]/libexample.so
,libexample.vendor
代表 /vendor/lib[64]/libexample.so
。要安装 /vendor/lib[64]/libexample.so
,请将 libexample.vendor
添加到 PRODUCT_PACKAGES
PRODUCT_PACKAGES += libexample.vendor