VNDK 建構系統支援

在 Android 8.1 以上版本中,建構系統已內建 VNDK 支援功能。啟用 VNDK 支援後,建構系統會檢查模組之間的依附元件、為供應商模組建構供應商專屬的變化版本,並自動將這些模組安裝至指定的目錄。

VNDK 建構支援範例

在這個範例中,Android.bp 模組定義定義了名為 libexample 的程式庫。vendor_available 屬性表示架構模組和供應商模組可能會依附於 libexample

libexample vendor_available:true 和 vndk.enabled:true

圖 1. 支援已啟用。

框架可執行檔 /system/bin/foo 和供應商可執行檔 /vendor/bin/bar 都會依賴 libexample,且在其 shared_libs 屬性中都有 libexample

如果 libexample 同時由架構模組和供應商模組使用,系統會建構兩個 libexample 變化版本。架構模組使用核心變化版本 (以 libexample 命名),且供應商模組使用供應商變化版本 (以 libexample.vendor 命名)。這兩個變化版本會安裝在不同的目錄中:

  • 核心變化版本會安裝至 /system/lib[64]/libexample.so
  • 由於 vndk.enabledtrue,因此供應商變化版本會安裝至 VNDK APEX。

詳情請參閱「模組定義」。

設定建構支援

如要為產品裝置啟用完整的建構系統支援,請將 BOARD_VNDK_VERSION 新增至 BoardConfig.mk

BOARD_VNDK_VERSION := current

這項設定具有「全域」效果:在 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 明確指定依附元件。

VNDK APEX

在 Android 10 以下版本中,具有 vndk.enabled 的模組會安裝在 /system/lib[64]/vndk[-sp]-${VER} 中。在 Android 11 以上版本中,VNDK 程式庫會以 APEX 格式封裝,而 VNDK APEX 的名稱為 com.android.vndk.v${VER}。視裝置設定而定,VNDK APEX 會扁平化未扁平化,並可從標準路徑 /apex/com.android.vndk.v${VER} 取得。

VNDK APEX

圖 2. VNDK APEX。

模組定義

如要使用 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 的模組依附於非 llndk_library 模組,該模組不含 vendor:truevendor_available:true

依附元件檢查作業適用於 Android.bp 中的 header_libsstatic_libsshared_libs,以及 Android.mk 中的 LOCAL_HEADER_LIBRARIESLOCAL_STATIC_LIBRARIESLOCAL_SHARED_LIBRARIES

LL-NDK

LL-NDK 共用程式庫是採用穩定 ABI 的共用程式庫。架構和供應商模組都會共用相同且最新的實作項目。對於每個 LL-NDK 共用程式庫,cc_library 都包含一個含有符號檔案的 llndk 屬性:

cc_library {
    name: "libvndksupport",
    llndk: {
        symbol_file: "libvndksupport.map.txt",
    },
}

符號檔案會說明供應商模組可見的符號。例如:

LIBVNDKSUPPORT {
  global:
    android_load_sphal_library; # llndk
    android_unload_sphal_library; # llndk
  local:
    *;
};

建構系統會根據符號檔案,為供應商模組產生 Stub 共用程式庫,並在啟用 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 APEX(com.android.vndk.v${VER})。

下表摘要說明建構系統如何處理供應商變化版本:

vendor_available vndk
enabled
vndk
support_same_process
供應商變化版本說明
true false false 供應商變化版本為 VND-ONLY。共用程式庫會安裝至 /vendor/lib[64]
true 無效 (建構錯誤)
true false 供應商子類為 VNDK。共用程式庫會安裝至 VNDK APEX。
true 供應商變體為 VNDK-SP。共用資料庫已安裝至 VNDK APEX。

false

false

false

沒有供應商變化版本。本單元僅適用於

true 無效 (建構錯誤)
true false 供應商子類為 VNDK-Private。共用資料庫已安裝至 VNDK APEX。這些模組不得直接由供應商模組使用。
true 供應商變化版本為 VNDK-SP-Private。共用程式庫會安裝至 VNDK APEX。這些模組不得由供應商模組直接使用。

VNDK 擴充功能

VNDK 擴充功能是具有額外 API 的 VNDK 共用程式庫。擴充功能會安裝至 /vendor/lib[64]/vndk[-sp] (沒有版本後置字串),並在執行階段覆寫原始 VNDK 共用程式庫。

定義 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 必須相等):

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 共用程式庫之間的細微差異 (例如在其中一個變化版本中新增或移除功能):

  • 核心變化版本 (例如 /system/lib[64]/libexample.so)
  • 供應商變化版本 (例如 /apex/com.android.vndk.v${VER}/lib[64]/libexample.so)
  • 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 中也可能會指定不同的 cflagscppflagstarget.vendor 中指定的 cflagscppflags 是供應商變化版本專屬。

例如,下列 Android.bp 定義了 libexamplelibexample_ext

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",
    ],
}

以下是 src/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
/apex/com.android.vndk.v${VER}/lib[64]/libexample.so allvndk
/vendor/lib[64]/vndk/libexample.so allvndkvndk_ext

匯出符號相關規定

VNDK ABI 檢查器會將 VNDK 供應商變化版本VNDK 擴充功能的 ABI 與 prebuilts/abi-dumps/vndk 下的參考 ABI 傾印內容進行比較。

  • VNDK 供應商變化版本 (例如 /apex/com.android.vndk.v${VER}/lib[64]/libexample.so) 匯出的符號必須與 ABI 傾印中定義的符號完全相同 (而非超集)。
  • VNDK 擴充功能 (例如 /vendor/lib[64]/vndk/libexample.so) 匯出的符號必須是 ABI 傾印中定義的符號的超集。

如果 VNDK 供應商變體VNDK 擴充功能不符合上述規定,VNDK ABI 檢查工具會發出建構錯誤並停止建構。

從供應商變化版本中排除來源檔案或共用程式庫

如要從供應商變化版本中排除來源檔案,請將這些檔案新增至 exclude_srcs 屬性。同樣地,為確保共用程式庫不會連結至供應商變化版本,請將這些程式庫新增至 exclude_shared_libs 屬性。例如:

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

在這個範例中,libexample_cond_exclude 的核心變化版本包含 fwk.cboth.c 的程式碼,並依附於共用程式庫 libfwk_onlylibbothlibexample_cond_exclude 的供應商變化版本只包含 both.c 的程式碼,因為 fwk.c 已遭 exclude_srcs 屬性排除。同樣地,它僅取決於共用資料庫 libboth,因為 libfwk_only 遭到 exclude_shared_libs 屬性排除。

從 VNDK 擴充功能匯出標頭

VNDK 擴充功能可能會在 VNDK 共用程式庫中新增類別或函式。建議您將這些宣告放在獨立的標頭中,並避免變更現有標頭。

舉例來說,系統會為 VNDK 擴充功能 libexample_ext 建立新的標頭檔案 include-ext/example/ext/feature_name.h

  • Android.bp
  • include-ext/example/ext/feature_name.h
  • include/example/example.h
  • src/example.c
  • src/ext/feature_name.c

在以下 Android.bp 中,libexample 只會匯出 include,而 libexample_ext 則會匯出 includeinclude-ext。這可確保 feature_name.h 不會被 libexample 的使用者錯誤納入:

cc_library {
    name: "libexample",
    srcs: ["src/example.c"],
    export_include_dirs: ["include"],
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libexample_ext",
    srcs: [
        "src/example.c",
        "src/ext/feature_name.c",
    ],
    export_include_dirs: [
        "include",
        "include-ext",
    ],
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libexample",
    },
}

如果無法將擴充功能分離至獨立的標頭檔案,則可改為新增 #ifdef 防護機制。不過,請務必確保所有 VNDK 擴充功能使用者都已加入定義標記。您可以定義 cc_defaults,將定義標記新增至 cflags,並將共用程式庫連結至 shared_libs

舉例來說,如要將新的成員函式 Example2::get_b() 新增至 VNDK 擴充功能 libexample2_ext,您必須修改現有的標頭檔案,並新增 #ifdef 防護機制:

#ifndef LIBEXAMPLE2_EXAMPLE_H_
#define LIBEXAMPLE2_EXAMPLE_H_

class Example2 {
 public:
  Example2();

  void get_a();

#ifdef LIBEXAMPLE2_ENABLE_VNDK_EXT
  void get_b();
#endif

 private:
  void *impl_;
};

#endif  // LIBEXAMPLE2_EXAMPLE_H_

libexample2_ext 的使用者定義名為 libexample2_ext_defaultscc_defaults

cc_library {
    name: "libexample2",
    srcs: ["src/example2.cpp"],
    export_include_dirs: ["include"],
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libexample2_ext",
    srcs: ["src/example2.cpp"],
    export_include_dirs: ["include"],
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libexample2",
    },
    cflags: [
        "-DLIBEXAMPLE2_ENABLE_VNDK_EXT=1",
    ],
}

cc_defaults {
    name: "libexample2_ext_defaults",
    shared_libs: [
        "libexample2_ext",
    ],
    cflags: [
        "-DLIBEXAMPLE2_ENABLE_VNDK_EXT=1",
    ],
}

libexample2_ext 的使用者可以直接在 defaults 屬性中加入 libexample2_ext_defaults

cc_binary {
    name: "example2_user_executable",
    defaults: ["libexample2_ext_defaults"],
    vendor: true,
}

產品套件

在 Android 建構系統中,PRODUCT_PACKAGES 變數會指定要安裝在裝置上的執行檔、共用程式庫或套件。指定模組的轉換依附元件也會隱含安裝至裝置。

如果啟用 BOARD_VNDK_VERSION,系統會特別處理含有 vendor_availablevndk.enabled 的模組。如果架構模組依附於含有 vendor_availablevndk.enabled 的模組,核心變化版本會納入轉移安裝集。如果供應商模組依附於含有 vendor_available 的模組,供應商變化版本就會納入轉移安裝組合。不過,無論供應商模組是否由供應商模組使用,都會安裝含有 vndk.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