在 Android 8.1 以上版本中,建構系統已內建 VNDK 支援功能。啟用 VNDK 支援後,建構系統會檢查模組之間的依附元件、為供應商模組建構供應商專屬的變化版本,並自動將這些模組安裝至指定的目錄。
VNDK 建構支援範例
在這個範例中,Android.bp
模組定義定義了名為 libexample
的程式庫。vendor_available
屬性表示架構模組和供應商模組可能會依附於 libexample
:
圖 1. 支援已啟用。
框架可執行檔 /system/bin/foo
和供應商可執行檔 /vendor/bin/bar
都會依賴 libexample
,且在其 shared_libs
屬性中都有 libexample
。
如果 libexample
同時由架構模組和供應商模組使用,系統會建構兩個 libexample
變化版本。架構模組使用核心變化版本 (以 libexample
命名),且供應商模組使用供應商變化版本 (以 libexample.vendor
命名)。這兩個變化版本會安裝在不同的目錄中:
- 核心變化版本會安裝至
/system/lib[64]/libexample.so
。 - 由於
vndk.enabled
是true
,因此供應商變化版本會安裝至 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_libs
、static_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}
取得。
圖 2. VNDK APEX。
模組定義
如要使用 BOARD_VNDK_VERSION
建構 Android,您必須在 Android.mk
或 Android.bp
中修訂模組定義。本節將說明不同類型的模組定義、幾個與 VNDK 相關的模組屬性,以及在建構系統中實作的依附元件檢查。
供應商模組
供應商模組是供應商專屬的執行檔或共用程式庫,必須安裝至供應商分區。在 Android.bp
檔案中,供應商模組必須將供應商或專屬屬性設為 true
。在 Android.mk
檔案中,供應商模組必須將 LOCAL_VENDOR_MODULE
或 LOCAL_PROPRIETARY_MODULE
設為 true
。
如果已定義 BOARD_VNDK_VERSION
,建構系統會禁止供應商模組和架構模組之間的依附元件,並在下列情況下發出錯誤:
- 沒有
vendor:true
的模組依附於含有vendor:true
的模組;或 - 含有
vendor:true
的模組依附於非llndk_library
模組,該模組不含vendor:true
或vendor_available:true
。
依附元件檢查作業適用於 Android.bp
中的 header_libs
、static_libs
和 shared_libs
,以及 Android.mk
中的 LOCAL_HEADER_LIBRARIES
、LOCAL_STATIC_LIBRARIES
和 LOCAL_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_library
、cc_library_static
、cc_library_shared
和 cc_library_headers
模組定義支援三個與 VNDK 相關的屬性:vendor_available
、vndk.enabled
和 vndk.support_system_process
。
如果 vendor_available
或 vndk.enabled
為 true
,則可能會建構兩個變化版本 (核心和供應商)。核心變化版本應視為架構模組,供應商變化版本則應視為供應商模組。如果某些架構模組依附於此模組,則會建構核心變化版本。如果部分供應商模組依附此模組,系統就會建構供應商變化版本。建構系統會強制執行下列依附元件檢查:
- 核心變化版本一律僅限架構,且無法讓供應商模組存取。
- 架構模組一律無法存取供應商變化版本。
- 在
header_libs
、static_libs
和/或shared_libs
中指定的所有供應商變數依附元件,必須是llndk_library
或含有vendor_available
或vndk.enabled
的模組。 - 如果
vendor_available
為true
,則所有供應商模組都能存取供應商變數。 - 如果
vendor_available
為false
,則只有其他 VNDK 或 VNDK-SP 模組可存取供應商變化版本 (也就是含有vendor:true
的模組無法連結vendor_available:false
模組)。
cc_library
或 cc_library_shared
的預設安裝路徑會根據下列規則決定:
- 核心變化版本會安裝至
/system/lib[64]
。 - 供應商變化版本的安裝路徑可能會有所不同:
- 如果
vndk.enabled
為false
,供應商變化版本會安裝至/vendor/lib[64]
。 - 如果
vndk.enabled
為true
,供應商變化版本會安裝至 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。 | ||
|
|
|
沒有供應商變化版本。本單元僅適用於 |
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: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
必須相等):
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
中也可能會指定不同的 cflags
或 cppflags
。target.vendor
中指定的 cflags
或 cppflags
是供應商變化版本專屬。
例如,下列 Android.bp
定義了 libexample
和 libexample_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 |
all 、framework_only |
/apex/com.android.vndk.v${VER}/lib[64]/libexample.so |
all 、vndk |
/vendor/lib[64]/vndk/libexample.so |
all 、vndk 、vndk_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.c
和 both.c
的程式碼,並依附於共用程式庫 libfwk_only
和 libboth
。libexample_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
則會匯出 include
和 include-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_defaults
的 cc_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_available
或 vndk.enabled
的模組。如果架構模組依附於含有 vendor_available
或 vndk.enabled
的模組,核心變化版本會納入轉移安裝集。如果供應商模組依附於含有 vendor_available
的模組,供應商變化版本就會納入轉移安裝組合。不過,無論供應商模組是否由供應商模組使用,都會安裝含有 vndk.enabled
的供應商變化版本。
如果依附元件對建構系統不可見 (例如在執行階段可能會使用 dlopen()
開啟的共用程式庫),您應在 PRODUCT_PACKAGES
中指定模組名稱,以便明確安裝這些模組。
如果模組有 vendor_available
或 vndk.enabled
,模組名稱代表其核心變數。如要在 PRODUCT_PACKAGES
中明確指定供應商變化版本,請在模組名稱後方加上 .vendor
字尾。例如:
cc_library { name: "libexample", srcs: ["example.c"], vendor_available: true, }
在此範例中,libexample
代表 /system/lib[64]/libexample.so
,libexample.vendor
代表 /vendor/lib[64]/libexample.so
。如要安裝 /vendor/lib[64]/libexample.so
,請將 libexample.vendor
新增至 PRODUCT_PACKAGES
:
PRODUCT_PACKAGES += libexample.vendor