在 Android 8.1 及更高版本中,構建系統具有內置的 VNDK 支持。啟用 VNDK 支持後,構建系統會檢查模塊之間的依賴關係,為供應商模塊構建特定於供應商的變體,並自動將這些模塊安裝到指定目錄中。
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中。 - 供應商變體安裝到 VNDK APEX 中,因為
vndk.enabled為true。
有關更多詳細信息,請參閱模塊定義。
配置構建支持
要為產品設備啟用完整的構建系統支持,請將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 頂點
在 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} 。

模塊定義
要使用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.mk中的header_libs 、 static_libs和shared_libs ,以及Android.bp中的LOCAL_HEADER_LIBRARIES 、 LOCAL_STATIC_LIBRARIES和LOCAL_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; # llndk
android_unload_sphal_library; # llndk
local:
*;
};
基於符號文件,構建系統為供應商模塊生成一個存根共享庫,當BOARD_VNDK_VERSION啟用時,這些庫與這些庫鏈接。僅當滿足以下條件時,符號才會包含在存根共享庫中:
- 未在以
_PRIVATE或_PLATFORM結尾的部分中定義, - 沒有
#platform-only標籤,並且 - 沒有
#introduce*標籤或標籤與目標匹配。
越南盾
在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 變體(即,帶有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 啟用 | vndk support_same_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屬性排除。同樣,它僅依賴於共享庫libboth ,因為libfwk_only被exclude_shared_libs屬性排除。
從 VNDK 擴展導出標頭
VNDK 擴展可以向 VNDK 共享庫添加新類或新函數。建議將這些聲明保留在獨立的標頭中,並避免更改現有標頭。
例如,為 VNDK 擴展libexample_ext創建了一個新的頭文件include-ext/example/ext/feature_name.h :
- 安卓.bp
- 包含-ext/example/ext/feature_name.h
- 包括/example/example.h
- src/example.c
- src/ext/feature_name.c
在以下Android.bp中, libexample僅導出include ,而libexample_ext導出include和include-ext 。這確保了libexample的用戶不會錯誤地包含feature_name.h :
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_
為cc_defaults的用戶定義了一個名為libexample2_ext_defaults的libexample2_ext :
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的用戶可以簡單地將libexample2_ext_defaults包含在他們的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