6
此文章参考官方文档,以自己的理解做了整理和精简,未免存在不正确的地方。

VNDK的全称是Vendor Native Development Kit,是Android 8.0引入的一种新技术。它表现一系列库的合集,用于让供应商开发自己的HALs。VNDK 包含在 system.img 中,并在运行时与供应商代码动态关联。

为何要使用 VNDK?

官方文档的解释给我的感觉其目的就是收敛Android的碎片化。不仅仅VNDK,Android 8.0引入许多的技术都是为了将System与Vendor分割开来。这样可以保持Android核心系统的纯净性,而将碎片化扔给供应商维护。渐渐的芯片供应商和设备厂商可以无需关心Android系统的实现,仅仅使用VNDK就可以完成产品开发。

VNDK相关概念

供应商模块

供应商模块是特定于供应商的可执行文件或共享库,这些模块将安装到供应商分区中。

  • Android.bp 文件中,供应商模块必须将vendor或proprietary属性设置为 true
  • Android.mk 文件中,供应商模块必须将 LOCAL_VENDOR_MODULELOCAL_PROPRIETARY_MODULE 设置为 true

Framework共享库

在理想的Android 8.0及更高版本环境中,Framework进程不加载供应商共享库,而供应商进程仅加载供应商共享库(和一部分Framework共享库)。Framework进程与供应商进程之间的通信由HIDL和hardware binder控制。

供应商进程需要使用Framework共享库可能随系统的更新而发生变化。为了保证供应商模块在多个Android版本上皆可正常工作,根据Framework共享库的特性不同,将其三个子类别:

  • LL-NDK库:已知稳定的Framework共享库,它们的开发者致力于保持其 API/ABI 稳定性。LL-NDK 包含以下库:libEGL.solibGLESv1_CM.solibGLESv2.solibGLESv3.solibandroid_net.solibc.solibdl.soliblog.solibm.solibnativewindow.solibneuralnetworks.solibsync.solibvndksupport.solibvulkan.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。

标记之间的关系:
treble_vndk_design.png

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_snapshot_directory.png

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

vndk_snapshot_system_only.png

启用 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_libsstatic_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_librarycc_library_staticcc_library_sharedcc_library_headers 模块定义支持三个与 VNDK 相关的属性:vendor_availablevndk.enabledvndk.support_system_process

如果 一个库标记为vendor_availablevndk.enabledtrue,则可能被编译两次,生成两种变体:核心变体和供应商变体。

  • 核心变体被视为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.enabledvndk.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 中指定不同的 cflagscppflags。在 target.vendor 中指定的 cflagscppflags 是专门针对供应商变体的。

例如,以下 Android.bp 定义了 libexamplelibexample_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: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 。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 Source: VNDK


戈壁老王
143 声望64 粉丝

做为一个不称职的老年码农,一直疏忽整理笔记,开博记录一下,用来丰富老年生活,