为何要使用 vndk?
vndk的全称是vendor native development kit,是android 8.0引入的一种新技术。它表现一系列库的合集,用于让供应商开发自己的hals。vndk 包含在 system.img 中,并在运行时与供应商代码动态关联。
官方文档的解释给我的感觉其目的就是收敛android的碎片化。不仅仅vndk,android 8.0引入许多的技术都是为了将system与vendor分割开来。这样可以保持android核心系统的纯净性,而将碎片化扔给供应商维护。渐渐的芯片供应商和设备厂商可以无需关心android系统的实现,仅仅使用vndk就可以完成产品开发。
vndk相关概念
供应商模块
供应商模块是特定于供应商的可执行文件或共享库,这些模块将安装到供应商分区中。
- 在
android.bp文件中,供应商模块必须将vendor或proprietary属性设置为true。 - 在
android.mk文件中,供应商模块必须将local_vendor_module或local_proprietary_module设置为true。
framework共享库
在理想的android 8.0及更高版本环境中,framework进程不加载供应商共享库,而供应商进程仅加载供应商共享库(和一部分framework共享库)。framework进程与供应商进程之间的通信由hidl和hardware binder控制。
供应商进程需要使用framework共享库可能随系统的更新而发生变化。为了保证供应商模块在多个android版本上皆可正常工作,根据framework共享库的特性不同,将其三个子类别:
- ll-ndk库:已知稳定的framework共享库,它们的开发者致力于保持其 api/abi 稳定性。ll-ndk 包含以下库:
libegl.so、libglesv1_cm.so、libglesv2.so、libglesv3.so、libandroid_net.so、libc.so、libdl.so、liblog.so、libm.so、libnativewindow.so、libneuralnetworks.so、libsync.so、libvndksupport.so和libvulkan.so。 合格的 vndk 库 (vndk):可以安全复制两次的framework共享库。framework模块和供应商模块可以与其各自的库副本相关联。framework共享库只有满足以下条件才能成为合格的 vndk 库:
- 不向framework发送或从framework接收 ipc。
- 与 art 虚拟机无关。
- 不读写文件格式不稳定的文件/分区。
- 没有需要法律审查的特殊软件许可。
- 其代码所有者不反对供应商使用该库。
框架专用库 (fwk-only) :不属于上述类别的framework共享库。此类库具有以下特点:
- 被视为framework内部实现细节。
- 不得由供应商模块访问。
- 具有不稳定的 abi/api,无 api/abi 兼容性保证。
- 不会被复制。
ll-ndk
ll-ndk 共享库是具有稳定 abi 的共享库。框架模块和供应商模块均具有相同的最新实现。对于每个 ll-ndk 共享库,android.bp 都包含一个 llndk_library 模块定义:
llndk_library {
name: "libvndksupport",
symbol_file: "libvndksupport.map.txt",
}该模块定义指定了模块名称和符号文件,后者描述了对供应商模块可见的符号。例如:
libvndksupport {
global:
android_load_sphal_library; # vndk
android_unload_sphal_library; # vndk
local:
*;
};same-process hal (sp-hal)
same-process hal (sp-hal) 是预定义的的一组hal,作为供应商共享库进行实现,并被加载到framework进程中。sp-hal 必须仅依赖于 ll-ndk 和 vndk-sp。vndk-sp 是一部分预定义的符合条件的 vndk 库。vndk-sp 库会被仔细审查,以确保将 vndk-sp 库双重加载到framework进程中不会导致问题。sp-hal 和 vndk-sp 均由 google 定义,并通过链接器命名空间进行隔离。
以下库是经过批准的 sp-hal:
libglesv1_cm_${driver}.solibglesv2_${driver}.solibglesv3_${driver}.solibegl_${driver}.sovulkan.${driver}.soandroid.hardware.renderscript@1.0-impl.soandroid.hardware.graphics.mapper@2.0-impl.so
以下库是 sp-hal 可以访问的 vndk-sp 库:
android.hardware.graphics.common@1.0.soandroid.hardware.graphics.mapper@2.0.soandroid.hardware.renderscript@1.0.so(renderscript)librs_internal.so(renderscript)libbase.solibc++.solibcutils.solibhardware.solibhidlbase.solibhidltransport.solibhwbinder.solibion.solibutils.solibz.so
以下 vndk-sp 依赖项 (vndk-sp-private) 对 sp-hal 来说是不可见的:
librscpuref.so(renderscript)librsdriver.so(renderscript)libbacktrace.solibblas.so(renderscript)libbcinfo.so(renderscript)liblzma.solibunwind.so
以下是具有 rs 例外的框架专用库 (fwk-only-rs):
libft2.so(renderscript)libmediandk.so(renderscript)
vndk 库简介
vndk 定义了可与供应商代码相关联的库集:vndk-core、vndk-sp 和 ll-ndk,并阻止供应商使用不在 vndk 集内的库。
vndk-core 库安装在 /system/lib[64]/vndk-${ver} 中,仅适用于 api 级别为 ${ver} 的供应商进程。${ver} 可以通过/vendor/default.prop中的系统属性ro.vndk.version获取。系统进程不得使用这些库,而必须使用安装在 /system/lib[64] 中的库。由于每个进程都具有严格的命名空间限制,因此不会造成重复加载 vndk-core 库。要在 vndk-core 中添加库,请将以下内容添加到 android.bp 中:
vendor_available: true,
vndk: {
enabled: true,
},vndk-sp 库安装在 /system/lib[64]/vndk-sp-${ver} 中,可以被供应商进程和系统进程(通过安装在供应商分区中的 sp-hal 库)使用。vndk-sp 库可以重复加载。要在 vndk-sp 中添加库,请将以下内容添加到 android.bp 中:
vendor_available: true,
vndk: {
enabled: true,
support_system_process: true,
},ll-ndk 库安装在 /system/lib[64] 中。供应商模块可以使用 ll-ndk stub访问 ll-ndk 库的预选符号。ll-ndk 库必须支持向后兼容,且具有 abi 稳定性,以便旧版供应商模块使用新版 ll-ndk 库。由于 ll-ndk 具有 abi 稳定特性,vndk 快照无需包含旧版供应商映像的 ll-ndk 库。
目录
vndk库可以大致划分为以下目录:
/system/lib[64]包含所有framework共享库,具体包括 ll-ndk、vndk 和framework专用库(包括 ll-ndk-private 和一些与 vndk-sp 中的库同名的库)。/system/lib[64]/vndk-sp包含适用于 same-process hal 的 vndk-sp 库。/vendor/lib[64]包含供应商扩展的 vndk 库(dxua 库或 dxux vndk 库)、same-process hal 实现,以及其他供应商共享库。/vendor/lib[64]/vndk-sp可能包含供应商扩展的 vndk-sp 库。
供应商模块从 /system/lib[64] 中加载 vndk 库。
vndk 电子表格
google 会提供一个符合条件的 vndk 电子表格(例如 eligible-list.csv),该电子表格会标记可由供应商模块使用的框架共享库:
| 标记 | 说明 |
|---|---|
| ll-ndk | 可由框架模块和供应商模块使用的共享库(具有稳定的 abi/api)。 |
| ll-ndk-private | ll-ndk 库的私有依赖项。供应商模块不得直接访问此类库。 |
| vndk-sp | sp-hal 框架共享库依赖项。 |
| vndk-sp-private | 所有供应商模块都无法直接访问的 vndk-sp 依赖项。 |
| vndk | 面向供应商模块(sp-hal 和 sp-hal-dep 除外)提供的框架共享库。 |
| vndk-private | 所有供应商模块都无法直接访问的 vndk 依赖项。 |
| fwk-only | 供应商模块不得(直接或间接)访问、仅限框架使用的共享库。 |
| fwk-only-rs | 供应商模块不得访问(rs 用途除外)、仅限框架使用的共享库。 |
下表描述了适用于供应商共享库的标记:
| 标记 | 说明 |
|---|---|
| sp-hal | same-process hal 实现共享库。 |
| sp-hal-dep | sp-hal 供应商共享库依赖项(也称为 sp-hal 依赖项,不包括 ll-ndk 和 vndk-sp)。 |
| vnd-only | 框架模块不可见且不得访问的共享库。所复制的扩展后 vndk 库也将被标记为 vnd-only。 |
标记之间的关系:

vndk 快照
vndk 快照就是一组预编译的库文件和配置文件的集合。因为vndk的实质就是要规范供应商的开发,如果保证vndk接口稳定的标准化,供应商就无需修改vndk库。系统只要提供所有vndk版本的二进制文件,就可以满足供应商的开发。这些需求的文件就是vndk快照需要提供的内容。
vndk 快照包含以下文件:
vndk-core 和 vndk-sp 共享库的供应商变体。
- 无需 ll-ndk 共享库,因为这类库是向后兼容的。
- 对于 64 位目标,target_arch 和 target_2nd_arch 库都将被编译并包含在内。
- vndk-core、vndk-sp、ll-ndk 和 vndk-private 库的列表,文件为
[vndkcore|vndksp|llndk|vndkprivate].libraries.txt。 - 链接器配置文件为
ld.config.txt。 - 许可文件。
module_paths.txt。记录所有 vndk 库的模块路径;检查 gpl 项目是否已在指定 android 源代码树中发布源代码时,需要用到这种文件。
以下示例展示了 arm64 (target_arch=arm64) vndk 快照 zip 文件 (android-vndk-arm64.zip) 的目录结构。

供应商镜像会依赖于特定版本的vndk,所以系统镜像中应该提供供应商需求的vndk版本的镜像。即使系统镜像与供应商镜像使用不同版本的android进行编译,但是只要保证系统镜像能够提供正确的vndk就保证正常运行。下图展示了android p系统镜像使用android o供应商镜像的场景。

启用 vndk
编译选项
在 boardconfig.mk添加board_vndk_version=current可以在编译过程开启vndk。也可以在编译模块时传递该编译选项,例如: m -j board_vndk_version=current my-lib)。
当启用 board_vndk_version=current 后,编译系统会检查库的依赖性和头文件的合法性。
- 确保vendor对象仅依赖于vndk库集:vndk-core、vndk-sp 和 ll-ndk。确保core组件不依赖与vendor组件。
- 移除全局头文件的依赖项,以便编译器可以明确是否使用
-d__android_vndk__来编译头文件。就是说全局头文件无法再使用传递方式包含在头文件内。
启用 board_vndk_version后,系统会移除多个默认的全局头文件搜索路径。模块使用这些路径下的头文件时,需要明确指定与 header_libs、static_libs 和/或 shared_libs 的依赖关系。这些路径中包括:
frameworks/av/includeframeworks/native/includeframeworks/native/opengl/includehardware/libhardware/includehardware/libhardware_legacy/includehardware/ril/includelibnativehelper/includelibnativehelper/include_deprecatedsystem/core/includesystem/media/audio/include
供应商变体库
在 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,则可能被编译两次,生成两种变体:核心变体和供应商变体。
- 核心变体被视为framework模块,将安装到
/system/lib[64]中。 - 而供应商变体应被视为供应商模块。系统根据模块依赖性来决定是否编译变体,并进行依赖性检查。供应商变体安装路径会根据
android.bp中的属性来决定。
下表总结了编译系统如何处理供应商变体,
| vendor_available | vndk .enabled | vndk. support_system_process | 供应商变体说明 |
|---|---|---|---|
| true | false | false | 供应商变体为 vnd-only。共享库将安装到 /vendor/lib[64] 中。 |
| true | false | true | 无效(编译错误) |
| true | true | false | 供应商变体为 vndk。共享库将安装到 /system/lib[64]/vndk-${ver} 中。 |
| true | true | true | 供应商变体为 vndk-sp。共享库将安装到 /system/lib[64]/vndk-sp-${ver} 中。 |
| false | false | false | 没有供应商变体。此模块为 fwk-only。 |
| false | false | true | 无效(编译错误) |
| false | true | false | 供应商变体为 vndk-private。共享库将安装到 /system/lib[64]/vndk-${ver} 中。供应商模块不得直接使用这些变体。 |
| false | true | true | 供应商变体为 vndk-sp-private。共享库将安装到 /system/lib[64]/vndk-sp-${ver} 中。供应商模块不得直接使用这些变体。 |
注意:供应商可以为其模块设置 vendor_available,但不得设置 vndk.enabled 和 vndk.support_system_process,因为供应商模块无法在通用系统映像 (gsi) 中找到它们。
条件编译
默认情况下,android 编译系统会为供应商变体和 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。它为libexample的供应商变体定义了"libexample_enable_vndk=1",为libexample的扩展库定义了 "libexample_enable_vndk=1" 和"libexample_enable_vndk_ext=1"。
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",
],
}vndk 扩展
android还提供了vndk扩展的方法,就是使用自己修改的vndk共享库来替换原始的vndk共享库。因为供应商很可能根据自己的需求来更改aosp库的源码,可能是为了提高性能,或者添加新钩子、新 api 或新功能。vndk 扩展库会安装到 /vendor/lib[64]/vndk[-sp] 中,并在系统运行时会替换原始的 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 。vndk 扩展(或 vndk-sp 扩展)也可以依赖于其他供应商共享库:
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,
shared_libs: [
"libvendor",
],
}
cc_library {
name: "libvendor",
vendor: true,
}如果供应商模块依赖于由 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)
规则和sepolicy
vndk 规则
完整的 vndk 规则列表如下。
- 框架进程不得从供应商分区中加载非 sp-hal 共享库(此规则从 android 8.1 开始严格地强制实施)。
- 供应商进程不得从系统分区中加载非 ll-ndk 库、非 vndk-sp 库和非 vndk 库(android o 中并未严格地强制实施此规则,但未来版本中会这么做)。
- 注意:要想从未来版本(比 android 8.0 更高的版本)仅针对框架的 ota 中受益,就不得在搭载 android 8.0 出厂的设备中违反此规则。
- 已安装的 vndk 库必须是由 google 定义的合格 vndk 库的子集。
sp-hal 和 sp-hal-dep 的外部依赖项必须仅限于 ll-ndk 库或由 google 定义的 vndk-sp 库。
- sp-hal 共享库的依赖项必须仅限于 ll-ndk 库、由 google 定义的 vndk-sp 库、其他 sp-hal 库和/或可标记为 sp-hal-dep 库的其他供应商共享库。
- 只有当供应商共享库不是 aosp 库,且其依赖项仅限于 ll-ndk 库、由 google 定义的 vndk-sp 库、sp-hal 库和/或其他 sp-hal-dep 库时,才可标记为 sp-hal-dep 库。
- vndk-sp 必须保持独立。在 android 8.0 中,系统以一种特殊方式处理
librs_internal.so,但在未来版本中,其处理方式会被重新考虑。 - 不得通过非 hidl 接口(包括但不限于 binder、套接字、共享内存、文件等)进行框架-供应商通信。
- 系统分区必须足够大,以便容纳所有符合条件的 vndk 库的两个副本,以及不符合条件的框架共享库的一个副本。
sepolicy
本部分中介绍的框架进程对应于 sepolicy 中的 coredomain,而供应商进程对应于 non-coredomain。例如,/dev/binder 只能在 coredomain 中被访问,而 /dev/vndbinder 只能在非 coredomain 中被访问。
类似政策会限制对系统分区和供应商分区上的共享库的访问。下表列出了访问不同类别的共享库时所需的权限:
| 类别 | 分区 | 是否可从 coredomain 访问 | 是否可从 非 coredomain 访问 |
|---|---|---|---|
| ll-ndk | 系统 | 是 | 是 |
| ll-ndk-private | 系统 | 是 | 是 |
| vndk-sp/vndk-sp-private | 系统 | 是 | 是 |
| vndk-sp-ext | 供应商 | 是 | 是 |
| vndk | 系统 | 是 | 是 |
| vndk-ext | 供应商 | 否 | 是 |
| fwk-only | 系统 | 是 | 否 |
| fwk-only-rs | 系统 | 是 | 否 |
| sp-hal | 供应商 | 是 | 是 |
| sp-hal-dep | 供应商 | 是 | 是 |
| vnd-only | 供应商 | 否 | 是 |
ll-ndk-private 和 vndk-sp-private 必须从这两个域中都可访问,因为非 coredomain 会间接访问这些库。同样,sp-hal-dep 必须可从 coredomain 访问,因为 sp-hal 依赖该域。
same_process_hal_file 标签
供应商分区中包含下面几个库。确保这些库既可以从 coredomain 访问,又可以从非 coredomain 访问。
- vndk-sp-ext,位于
/vendor/lib[64]/vndk-sp - sp-hal,位于
/vendor/lib[64]或/vendor/lib[64]/hw - sp-hal-dep,位于
/vendor/lib[64]或/vendor/lib[64]/hw
将这些文件明确标记为 same_process_hal_file。因为在默认情况下,从 coredomain 无法访问 vendor 分区中的任何内容。请向供应商特定的 file_contexts 文件中添加与以下命令行类似的命令行:
/vendor/lib(64)?/hw/libmysphal\.so u:object_r:same_process_hal_file:s0 /vendor/lib(64)?/vndk-sp/libbase\.so u:object_r:same_process_hal_file:s0 /vendor/lib(64)?/libbaseinternal\.so u:object_r:same_process_hal_file:s0
参考文档:
以上就是android vndk使用及原理深入探究的详细内容,更多关于android vndk使用原理的资料请关注代码网其它相关文章!
发表评论