为何要使用 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}.so
libglesv2_${driver}.so
libglesv3_${driver}.so
libegl_${driver}.so
vulkan.${driver}.so
android.hardware.renderscript@1.0-impl.so
android.hardware.graphics.mapper@2.0-impl.so
以下库是 sp-hal 可以访问的 vndk-sp 库:
android.hardware.graphics.common@1.0.so
android.hardware.graphics.mapper@2.0.so
android.hardware.renderscript@1.0.so
(renderscript)librs_internal.so
(renderscript)libbase.so
libc++.so
libcutils.so
libhardware.so
libhidlbase.so
libhidltransport.so
libhwbinder.so
libion.so
libutils.so
libz.so
以下 vndk-sp 依赖项 (vndk-sp-private) 对 sp-hal 来说是不可见的:
librscpuref.so
(renderscript)librsdriver.so
(renderscript)libbacktrace.so
libblas.so
(renderscript)libbcinfo.so
(renderscript)liblzma.so
libunwind.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/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
供应商变体库
在 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使用原理的资料请关注代码网其它相关文章!
发表评论