VNDK 编译系统支持

编译系统在 Android 8.1 中具有内置的 VNDK 支持。如果启用了 VNDK 支持,编译系统就会检查各模块之间的依赖关系,为供应商模块编译特定于供应商的变体,并自动将这些模块安装到指定目录中。

以下示例演示了基本概念:

具有 vendor_available:true 和 vndk.enabled:true 的 libexample

图 1. 启用 VNDK 支持。

Android.bp 模块定义定义了一个名为 libexample 的库。vendor_available 属性表示框架模块和供应商模块均可能依赖于 libexample。在本示例中,框架可执行文件 /system/bin/foo 和供应商可执行文件 /vendor/bin/bar 均依赖于 libexample,并且在其 shared_libs 属性中具有 libexample

如果框架模块和供应商模块均使用 libexample,则编译 libexample 的两个变体。核心变体(以 libexample 命名)由框架模块使用,供应商变体(以 libexample.vendor 命名)由供应商模块使用。

这两个变体将安装到不同的目录中。核心变体将安装到 /system/lib[64]/libexample.so 中。供应商变体将安装到 /system/lib[64]/vndk/libexample.so 中,因为 vndk.enabledtrue

有关更多详情,请参阅模块定义

配置

要为产品设备启用完整编译系统支持,请将 BOARD_VNDK_VERSION 添加到 BoardConfig.mk

BOARD_VNDK_VERSION := current

迁移备注

BOARD_VNDK_VERSION 添加到 BoardConfig.mk 会产生全局效应。如果是在 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_libsstatic_libs 和/或 shared_libs 的依赖关系。

模块定义

要使用 BOARD_VNDK_VERSION 编译 Android,开发者必须在 Android.mkAndroid.bp 中修改其模块定义。本小节介绍了不同种类的模块定义,一些与 VNDK 相关的模块属性,以及在编译系统中实现的依赖性检查。

供应商模块

供应商模块是特定于供应商的可执行文件或共享库(必须将这些模块安装到供应商分区中)。在 Android.bp 文件中,供应商模块必须将供应商或专有属性设置为 true。在 Android.mk 文件中,供应商模块必须将 LOCAL_VENDOR_MODULELOCAL_PROPRIETARY_MODULE 设置为 true

如果定义了 BOARD_VNDK_VERSION,则编译系统不允许在供应商模块和框架模块之间建立依赖关系。在以下情况下,编译系统会发出错误:

  • 不具有 vendor:true 的模块依赖于具有 vendor:true 的模块,或
  • 具有 vendor:true 的模块依赖于既不具有 vendor:true 也不具有 vendor_available:true 的非 llndk_library 模块。

前面提到的依赖性检查适用于 Android.bp 中的 header_libsstatic_libsshared_libs,也适用于 Android.mk 中的 LOCAL_HEADER_LIBRARIESLOCAL_STATIC_LIBRARIESLOCAL_SHARED_LIBRARIES

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:
    *;
};

编译系统会根据符号文件为供应商模块生成存根共享库。如果启用了 BOARD_VNDK_VERSION,供应商模块将与这些存根共享库建立关联。

只有在满足以下条件时,存根共享库中才会包含符号:

  • 它未在以 _PRIVATE_PLATFORM 结尾的部分中定义。
  • 它不含 #platform-only 标记,并且
  • 它不含 #introduce* 标记或者该标记与目标匹配。

VNDK

Android.bp 文件中,cc_librarycc_library_staticcc_library_sharedcc_library_headers 模块定义支持三个与 VNDK 相关的属性:vendor_availablevndk.enabledvndk.support_system_process

如果 vendor_availablevndk.enabledtrue,则可以编译两种变体(核心变体和供应商变体)。核心变体应被视为框架模块,而供应商变体应被视为供应商模块。如果某些框架模块依赖于此模块,则会编译核心变体。如果某些供应商模块依赖于此模块,则会编译供应商变体。

编译系统会强制执行以下依赖性检查:

  • 核心变体始终供框架专用,无法供供应商模块访问。
  • 供应商变体始终无法供框架模块访问。
  • 供应商变体的所有依赖项(在 header_libsstatic_libs 和/或 shared_libs 中指定)必须是 llndk_library 或具有 vendor_availablevndk.enabled 的模块。
  • 如果 vendor_availabletrue,则供应商变体可供所有供应商模块访问。
  • 如果 vendor_availablefalse,则供应商变体仅可供其他 VNDK 或 VNDK-SP 模块访问(即,具有 vendor:true 的模块无法与 vendor_available:false 模块相关联)。

系统将通过以下规则确定 cc_librarycc_library_shared 的默认安装路径:

  • 将核心变体安装到 /system/lib[64] 中。
  • 供应商变体安装路径可能会有所不同:
    • 如果 vndk.enabledfalse,则将供应商变体安装到 /vendor/lib[64] 中。
    • 如果 vndk.enabledtrue,则 vndk.support_system_process 可以是 truefalse
      • 如果 vndk.support_system_processfalse,则供应商变体将安装到 /system/lib[64]/vndk-${VER} 中。
      • 否则,供应商变体将安装到 /system/lib[64]/vndk-sp-${VER} 中。

下表总结了编译系统如何处理供应商变体:

vendor_available

vndk

Vendor variant descriptions

enabled

support_same_process

true

false

false

供应商变体是 VND-ONLY

共享库将安装到 /vendor/lib[64] 中。

true

无效(编译错误)

true

false

供应商变体是 VNDK

共享库将安装到 /system/lib[64]/vndk-${VER} 中。

true

供应商变体是 VNDK-SP

共享库将安装到 /system/lib[64]/vndk-sp-${VER} 中。

false

false

false

没有供应商变体。此模块为 FWK-ONLY。

true

无效(编译错误)

true

false

供应商变体是 VNDK-Private

共享库将安装到 /system/lib[64]/vndk-${VER} 中。

供应商模块不得直接使用这些变体。

true

供应商变体是 VNDK-SP-Private。

共享库将安装到 /system/lib[64]/vndk-sp-${VER} 中。

供应商模块不得直接使用这些变体。

VNDK 扩展

VNDK 扩展是具有额外 API 的 VNDK 共享库,将安装到 /vendor/lib[64]/vndk[-sp] 中(不含版本后缀),并在系统运行时替换原始的 VNDK 共享库。

定义 VNDK 扩展

在 Android P 中,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 必须等于:

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 共享库之间的细微差异(例如,为其中一个变体添加或移除某项功能):(1) 核心变体(例如 /system/lib[64]/libexample.so)、(2) 供应商变体(例如 /system/lib[64]/vndk[-sp]-${VER}/libexample.so)和 (3) VNDK 扩展(例如 /vendor/lib[64]/vndk[-sp]/libexample.so)。

条件编译器标志

默认情况下,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 是专门针对供应商变体的。例如,以下代码是针对 libexamplelibexample_extAndroid.bp 模块定义:

cc_library {
    name: "libexample",
    srcs: ["example.c"],
    vendor_available: true,
    vndk: {
        enabled: true,
    },
    target: {
        vendor: {
            cflags: ["-DLIBEXAMPLE_ENABLE_VNDK=1"],
        },
    },
}

cc_library {
    name: "libexample_ext",
    srcs: ["example.c"],
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libexample",
    },
    cflags: [
        "-DLIBEXAMPLE_ENABLE_VNDK=1",
        "-DLIBEXAMPLE_ENABLE_VNDK_EXT=1",
    ],
}

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

针对每个变体导出的符号如下:

安装路径 导出的符号
/system/lib[64]/libexample.so allframework_only
/system/lib[64]/vndk-${VER}/libexample.so allvndk
/vendor/lib[64]/vndk/libexample.so allvndkvndk_ext

VNDK ABI 合规性检查工具会将 VNDK 和 VNDK 扩展的 ABI 同 prebuilts/abi-dumps/vndk 下的 ABI 转储进行比较:

  • 通过原始 VNDK 共享库导出的符号必须与 ABI 转储中定义的符号相同(而不是后者的超集)。
  • 通过 VNDK 扩展导出的符号必须是 ABI 转储中定义的符号的超集。

排除源文件或共享库

要从供应商变体中排除源文件,请将相应文件添加到 exclude_srcs 属性中。同样,要确保特定共享库未与供应商变体相关联,请将这些共享库添加到 exclude_shared_libs 属性中。例如:

cc_library {
    name: "libcond_exclude_example",
    srcs: ["fwk.c", "both.c"],
    shared_libs: ["libfwk_only", "libboth"],
    target: {
        vendor: {
            exclude_srcs: ["fwk.c"],
            exclude_shared_libs: ["libfwk_only"],
        },
    },
}

在本示例中,libcond_exclude_example 的核心变体包含 fwk.cboth.c 中的代码,并且依赖于共享库 libfwk_onlylibboth

另一方面,libcond_exclude_example 的供应商变体仅包含 both.c 中的代码,因为 fwk.c 已被 exclude_srcs 属性排除。同样,libcond_exclude_example 仅依赖于共享库 libboth,因为 libfwk_only 已被
exclude_shared_libs 属性排除。

产品包

在 Android 编译系统中,变量 PRODUCT_PACKAGES 指定应安装到设备中的可执行文件、共享库或软件包。指定模块的传递依赖项也会隐式安装到设备中。

如果启用了 BOARD_VNDK_VERSION,具有 vendor_availablevndk.enabled 的模块会得到特殊处理。如果框架模块依赖于具有 vendor_availablevndk.enabled 的模块,则核心变体将纳入传递安装集中。同样,如果供应商模块依赖于具有 vendor_availablevndk.enabled 的模块,则供应商变体将纳入传递安装集中。

当相关依赖项对编译系统不可见时(例如,可以在运行时使用 dlopen() 打开的共享库),您应该在 PRODUCT_PACKAGES 中指定模块名称来明确安装这些模块。

如果某个模块具有 vendor_availablevndk.enabled,则模块名称代表该模块的核心变体。要在 PRODUCT_PACKAGES 中明确指定供应商变体,请将 .vendor 后缀附加到模块名称上。例如:

cc_library {
    name: "libexample",
    srcs: ["example.c"],
    vendor_available: true,
}

在本示例中,libexample 代表 /system/lib[64]/libexample.solibexample.vendor 代表 /vendor/lib[64]/libexample.so。要安装 /vendor/lib[64]/libexample.so,请将 libexample.vendor 添加到 PRODUCT_PACKAGES

PRODUCT_PACKAGES += libexample.vendor