ב-Android מגרסה 8.1 ואילך, למערכת build יש תמיכה מובנית ב-VNDK. כשמופעלת תמיכה ב-VNDK, מערכת ה-build בודקת את התלות בין המודולים, יוצרת וריאציה ספציפית לספק עבור מודולים של ספקים ומתקינה את המודולים האלה באופן אוטומטי בספריות ייעודיות.
דוגמה לתמיכה ב-VNDK build
בדוגמה הזו, הגדרת המודול Android.bp מגדירה ספרייה בשם libexample. המאפיין vendor_available
מציין שמודולים של מסגרות ומודולים של ספקים עשויים להיות תלויים ב-libexample:
איור 1. התמיכה הופעלה.
גם קובץ ההפעלה של המסגרת /system/bin/foo וגם קובץ ההפעלה של הספק /vendor/bin/bar תלויים ב-libexample, ו-libexample מופיע במאפיינים שלהם shared_libs.
אם libexample משמש גם מודולים של מסגרת וגם מודולים של ספק, נוצרות שתי וריאציות של libexample. הווריאנט המרכזי (שנקרא על שם libexample) משמש מודולים של מסגרת, והווריאנט של הספק (שנקרא על שם libexample.vendor) משמש מודולים של הספק. שתי הגרסאות מותקנות בספריות שונות:
- הווריאציה המרכזית מותקנת בתיקייה
/system/lib[64]/libexample.so. - הווריאציה של הספק מותקנת ב-VNDK APEX כי
vndk.enabledהואtrue.
מידע נוסף זמין במאמר בנושא הגדרת מודול.
הגדרת תמיכה ב-build
כדי להפעיל תמיכה מלאה במערכת build של מכשיר מוצר, מוסיפים את 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/includeframeworks/native/includeframeworks/native/opengl/includehardware/libhardware/includehardware/libhardware_legacy/includehardware/ril/includelibnativehelper/includelibnativehelper/include_deprecatedsystem/core/includesystem/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.
הגדרת המודול
כדי ליצור Android באמצעות BOARD_VNDK_VERSION, צריך לשנות את הגדרת המודול ב-Android.mk או ב-Android.bp. בקטע הזה מתוארים סוגים שונים של הגדרות מודולים, כמה מאפיינים של מודולים שקשורים ל-VNDK ובדיקות תלות שמוטמעות במערכת build.
מודולים של ספקים
מודולים של ספקים הם קבצים הפעלה או ספריות משותפות שספציפיים לספק מסוים, ושחייבים להתקין אותם במחיצה של הספק. בקובצי Android.bp, מודולים של ספקים צריכים להגדיר את המאפיין vendor או proprietary לערך true.
בקובצי Android.mk, מודולים של ספקים צריכים להגדיר את הערך LOCAL_VENDOR_MODULE או LOCAL_PROPRIETARY_MODULE לערך true.
אם BOARD_VNDK_VERSION מוגדר, מערכת ה-build לא מאפשרת יחסי תלות בין מודולים של ספקים לבין מודולים של framework, ומציגה שגיאות אם:
- מודול ללא
vendor:trueתלוי במודול עםvendor:true, או - מודול עם
vendor:trueתלוי במודול שאינוllndk_libraryושאין לוvendor:trueאוvendor_available:true.
בדיקת התלות חלה על header_libs, static_libs ו-shared_libs ב-Android.bp, ועל LOCAL_HEADER_LIBRARIES, LOCAL_STATIC_LIBRARIES ו-LOCAL_SHARED_LIBRARIES ב-Android.mk.
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: *; };
על סמך קובץ הסמלים, מערכת ה-build יוצרת ספרייה משותפת של stub למודולים של ספקים, שמקושרים לספריות האלה כשמופעלת האפשרות BOARD_VNDK_VERSION. סמל נכלל בספרייה המשותפת של ה-stub רק אם:
- לא מוגדר בקטע שנגמר ב-
_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). צריך להתייחס לווריאנט הליבה כאל מודול מסגרת, ולווריאנט הספק כאל מודול ספק. אם מודולים מסוימים של המסגרת תלויים במודול הזה, נוצרת וריאציה של ליבה. אם מודולים מסוימים של ספקים תלויים במודול הזה, הגרסה של הספק תיבנה. מערכת build אוכפת את בדיקות התלות הבאות:
- גרסת הליבה היא תמיד רק מסגרת, ואין גישה למודולים של הספק.
- מודולים של מסגרת אף פעם לא יכולים לגשת לווריאנט של הספק.
- כל יחסי התלות של וריאציית הספק, שמצוינים ב-
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}).
- אם
בטבלה הבאה מפורט איך מערכת ה-build מטפלת בווריאציות של הספקים:
| 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 הם ספריות משותפות של VNDK עם ממשקי API נוספים. התוספים מותקנים ב-/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
אם מודול של ספק תלוי בממשקי API נוספים שמוגדרים על ידי תוספים של VNDK, המודול צריך לציין את השם של תוסף ה-VNDK במאפיין shared_libs שלו:
// 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)
דגלים של הידור מותנה
מערכת ה-Build של Android מגדירה את __ANDROID_VNDK__ כברירת מחדל עבור וריאציות של ספקים ותוספים של 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. הפונקציה cflags או cppflags שצוינה ב-target.vendor ספציפית לווריאציה של הספק.
לדוגמה, התג 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על סמך שני הקבצים האלה, מערכת ה-build יוצרת ספריות משותפות עם הסמלים המיוצאים הבאים:
| נתיב ההתקנה | סמלים שיוצאו |
|---|---|
/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 |
הדרישות לגבי הסמלים המיוצאים
כלי לבדיקת ABI של VNDK משווה את ה-ABI של ווריאציות של ספק VNDK ושל תוספים של VNDK לבין קובצי ה-dump של ה-ABI של ההפניה בתיקייה prebuilts/abi-dumps/vndk.
- הסמלים שמיוצאים על ידי וריאציות של ספקים ב-VNDK (לדוגמה:
/apex/com.android.vndk.v${VER}/lib[64]/libexample.so) צריכים להיות זהים לסמלים שמוגדרים בקובצי ה-ABI (ולא קבוצות-על שלהם). - סמלים שמיוצאים על ידי תוספי VNDK (למשל,
/vendor/lib[64]/vndk/libexample.so) צריכים להיות קבוצות-על של הסמלים שמוגדרים בקובצי ה-dump של ה-ABI.
אם וריאציות של ספקים ב-VNDK או תוספים ל-VNDK לא עומדים בדרישות שלמעלה, כלי הבדיקה של VNDK ABI מפיק שגיאות build ומפסיק את ה-build.
אי-הכללה של קובצי מקור או ספריות משותפות מגרסאות ספקים
כדי להחריג קובצי מקור מהגרסה של הספק, מוסיפים אותם למאפיין 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. מומלץ לשמור את ההצהרות האלה בכותרות נפרדות ולא לשנות את הכותרות הקיימות.
לדוגמה, קובץ כותרת חדש
include-ext/example/ext/feature_name.h נוצר עבור התוסף VNDK
libexample_ext:
- 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 guard:
#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,
}חבילות מוצרים
במערכת build של Android, המשתנה PRODUCT_PACKAGES
מציין את קובצי ההפעלה, הספריות המשותפות או החבילות שצריך
להתקין במכשיר. התלות הטרנזיטיבית של המודולים שצוינו מותקנת במכשיר באופן מרומז.
אם BOARD_VNDK_VERSION מופעל, מודולים עם vendor_available או vndk.enabled מקבלים טיפול מיוחד. אם מודול של מסגרת תלוי במודול עם vendor_available או vndk.enabled, הגרסה הבסיסית נכללת בקבוצת ההתקנה הטרנזיטיבית. אם מודול של ספק
תלוי במודול עם vendor_available, הווריאציה של הספק
נכללת בקבוצת ההתקנה הטרנזיטיבית. עם זאת, וריאציות של מודולים של ספקים עם vndk.enabled מותקנות בין אם הן בשימוש של מודולים של ספקים ובין אם לא.
כשהתלויות לא גלויות למערכת build (לדוגמה, ספריות משותפות שאפשר לפתוח באמצעות 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