供应商原生开发工具包 (VNDK) 概述

供应商原生开发工具包 (VNDK) 是一组库,供供应商或产品分区中的其他库或二进制文件在运行时使用 dlopen。

弃用

供应商 NDK 在 Android 8.0 中引入,旨在提供框架和供应商代码之间的 API。虽然 VNDK 已成功使用多年,但它存在一些缺点
  • 存储
    • 单个 VNDK APEX 打包了所有 VNDK 库,无论它们是否从设备中使用。
    • GSI 包含多个版本的 VNDK APEX,以支持多个版本的供应商镜像。
  • 可更新性
    • 很难将 VNDK APEX 与平台更新分开更新。
    • 供应商镜像经常通过无线 (OTA) 方式更新,从而降低了将 VNDK 打包在系统镜像中的好处。
基于这些问题,我们决定从 Android 15 开始弃用 VNDK。

关于 VNDK 弃用的详细信息

所有 VNDK 库都打包到 VNDK APEX 中,并安装在 system (-ext) 镜像中。随着 VNDK 的弃用,以前的 VNDK 库将安装在供应商(或产品)镜像中,与其他供应商可用的库相同。这些功能将随着 VNDK 的弃用而移除
  • Android 15 的 VNDK APEX
  • 如果供应商或产品分区是为 Android 15 构建的,则指示目标 VNDK 版本的系统属性将被移除
    • ro.vndk.version
    • ro.product.vndk.version
  • 由于没有 VNDK,VNDK 优化将不可用
    • TARGET_VNDK_USING_CORE_VARIANT for Android Go devices
    • use_vndk_as_stable for Vendor APEXes
  • 供应商快照,高度依赖于 VNDK

弃用的例外情况

这些功能不会随着 VNDK 的弃用而改变
  • VNDK 版本为 14 或更低的 VNDK APEX,这是支持现有供应商镜像所必需的。
  • LL-NDK 不是 VNDK 的一部分。

为何使用 VNDK?

AOSP 允许仅框架更新,其中系统分区可以升级到最新的框架版本,而供应商分区保持不变。尽管在不同时间构建,但每个分区中的二进制文件必须能够彼此协作。

仅框架更新包括以下挑战

  • 框架模块和供应商模块之间的依赖关系。在 Android 8.0 之前,供应商和系统分区中的模块可以彼此链接。然而,来自供应商模块的依赖关系对框架模块的开发施加了不必要的限制。
  • AOSP 库的扩展。Android 要求所有 Android 设备在系统分区替换为标准通用系统镜像 (GSI) 时通过 CTS。然而,由于供应商扩展 AOSP 库以提高性能或为其 HIDL 实现添加额外功能,因此使用标准 GSI 刷写系统分区可能会破坏供应商的 HIDL 实现。有关防止此类破坏的指南,请参阅VNDK 扩展

为了应对这些挑战,Android 包含多项功能,例如 VNDK(本节中描述)、HIDL、hwbinder、设备树覆盖和 sepolicy 覆盖。

VNDK 特定术语

VNDK 相关文档使用以下术语
  • 模块 指的是共享库或可执行文件。模块构成构建时依赖项。
  • 进程 是从可执行文件衍生的操作系统任务。进程构成运行时依赖项。
  • 框架 限定的术语与 system 分区相关
    • 框架可执行文件 指的是 /system/bin/system/xbin 中的可执行文件。
    • 框架共享库 指的是 /system/lib[64] 下的共享库。
    • 框架模块 指的是框架共享库和框架可执行文件。
    • 框架进程 是从框架可执行文件衍生的进程,例如 /system/bin/app_process
  • 供应商 限定的术语与 vendor 分区相关
    • 供应商可执行文件 指的是 /vendor/bin 中的可执行文件
    • 供应商共享库 指的是 /vendor/lib[64] 下的共享库。
    • 供应商模块 指的是供应商可执行文件和供应商共享库。
    • 供应商进程 是从供应商可执行文件衍生的进程,例如 /vendor/bin/android.hardware.camera.provider@2.4-service

VNDK 概念

在理想的 Android 8.0 及更高版本中,框架进程不加载供应商共享库,所有供应商进程仅加载供应商共享库(以及一部分框架共享库),并且框架进程和供应商进程之间的通信受 HIDL 和硬件 Binder 监管。

这样的世界包括以下可能性:来自框架共享库的稳定公共 API 可能不足以满足供应商模块开发者的需求(尽管 API 在 Android 版本之间可能会发生变化),因此需要让供应商进程访问一部分框架共享库。此外,由于性能要求可能会导致妥协,因此必须区别对待一些响应时间至关重要的 HAL。

以下部分详细介绍了 VNDK 如何处理供应商的框架共享库和同进程 HAL (SP-HAL)。

供应商的框架共享库

本节介绍了对供应商进程可访问的共享库进行分类的标准。支持跨多个 Android 版本的供应商模块有两种方法:

  1. 稳定框架共享库的 ABI/API。新的框架模块和旧的供应商模块可以使用相同的共享库,以减少内存占用和存储空间。唯一的共享库还可以避免一些双重加载问题。但是,维护稳定的 ABI/API 的开发成本很高,并且稳定每个框架共享库导出的所有 ABI/API 是不现实的。
  2. 复制旧的框架共享库。随之而来的是对侧信道的严格限制,侧信道定义为框架模块和供应商模块之间进行通信的所有机制,包括(但不限于)Binder、套接字、管道、共享内存、共享文件和系统属性。除非通信协议已冻结且稳定(例如,通过 hwbinder 的 HIDL),否则不得进行通信。双重加载共享库也可能会导致问题;例如,如果由新库创建的对象被传递到旧库的函数中,则可能会发生错误,因为这些库可能会以不同的方式解释该对象。

根据共享库的特性,使用不同的方法。因此,框架共享库分为三个子类别:

  • LL-NDK 库 是已知稳定的框架共享库。它们的开发者致力于维护其 API/ABI 的稳定性。
    • LL-NDK 包括以下库: libEGL.solibGLESv1_CM.solibGLESv2.solibGLESv3.solibandroid_net.solibc.solibdl.soliblog.solibm.solibnativewindow.solibneuralnetworks.solibsync.solibvndksupport.solibvulkan.so
  • 符合条件的 VNDK 库 (VNDK) 是可以安全复制两次的框架共享库框架模块供应商模块 可以与其自己的副本链接。框架共享库只有在满足以下条件时才能成为符合条件的 VNDK 库:
    • 它不向框架发送/接收 IPC。
    • 它与 ART 虚拟机无关。
    • 它不读取/写入具有不稳定文件格式的文件/分区。
    • 它没有需要法律审核的特殊软件许可。
    • 其代码所有者不反对供应商使用。
  • 仅限框架库 (FWK-ONLY) 是不属于上述类别的框架共享库。这些库:
    • 被视为框架内部实现细节。
    • 供应商模块不得访问。
    • 具有不稳定的 ABI/API,且没有 API/ABI 兼容性保证。
    • 不被复制。

同进程 HAL (SP-HAL)

同进程 HAL (SP-HAL) 是一组预定的 HAL,它们被实现为供应商共享库 并加载到框架进程 中。SP-HAL 通过链接器命名空间(控制对共享库可见的库和符号)进行隔离。SP-HAL 必须仅依赖于 LL-NDKVNDK-SP

VNDK-SP 是符合条件的 VNDK 库的预定义子集。VNDK-SP 库经过仔细审查,以确保将 VNDK-SP 库双重加载到框架进程中不会引起问题。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

VNDK-SP 库在其 Android.bp 文件中指定 vndk: { support_system_process: true }。如果还指定了 vndk: {private:true},则这些库称为 VNDK-SP-Private,并且 SP-HAL 看不到它们。

以下是 具有 RS 例外的仅限框架库 (FWK-ONLY-RS)

  • libft2.so (RenderScript)
  • libmediandk.so (RenderScript)

VNDK 版本控制

VNDK 共享库已进行版本控制

  • ro.vndk.version 系统属性会自动添加到 /vendor/default.prop
  • VNDK 和 VNDK-SP 共享库作为 VNDK apex com.android.vndk.v${ro.vndk.version} 安装,并挂载到 /apex/com.android.vndk.v${ro.vndk.version}

ro.vndk.version 的值由以下算法选择:

  • 如果 BOARD_VNDK_VERSION 不等于 current,则使用 BOARD_VNDK_VERSION
  • 如果 BOARD_VNDK_VERSION 等于 current
    • 如果 PLATFORM_VERSION_CODENAMEREL,则使用 PLATFORM_SDK_VERSION(例如 28)。
    • 否则,使用 PLATFORM_VERSION_CODENAME(例如 P)。

供应商测试套件 (VTS)

Android 供应商测试套件 (VTS) 强制要求使用非空的 ro.vndk.version 属性。新推出的设备和升级设备都必须定义 ro.vndk.version。一些 VNDK 测试用例(例如 VtsVndkFilesTestVtsVndkDependencyTest)依赖于 ro.vndk.version 属性来加载匹配的符合条件的 VNDK 库数据集。