在 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
的模組依附於沒有vendor:true
或vendor_available:true
的非llndk_library
模組。
依附元件檢查適用於 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
,則可能會建構兩個變體 (core 和 vendor)。核心變體應視為架構模組,供應商變體則應視為供應商模組。如果某些架構模組依附於這個模組,系統就會建構核心變體。如果部分供應商模組依附於這個模組,系統就會建構供應商變體。建構系統會強制執行下列依附元件檢查:
- 核心變體一律僅限架構,且供應商模組無法存取。
- 架構模組一律無法存取供應商變體。
- 供應商變體的所有依附元件 (在
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_system_process |
供應商變體說明 |
---|---|---|---|
true |
false |
false |
供應商變數為 VND-ONLY。共用程式庫會安裝到 /vendor/lib[64] 。 |
true |
無效 (建構錯誤) | ||
true |
false |
供應商變數為 VNDK。共用程式庫會安裝至 VNDK APEX。 | |
true |
供應商變數為 VNDK-SP。共用程式庫會安裝至 VNDK APEX。 | ||
|
|
|
沒有供應商變體。這個模組是 FWK-ONLY。 |
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
屬性排除。同樣地,由於 exclude_shared_libs
屬性排除了 libfwk_only
,因此只依附於共用程式庫 libboth
。
從 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