ב-Android מגרסה 8.1 ואילך, למערכת הבנייה יש תמיכה מובנית ב-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 למכשיר מוצר, מוסיפים את
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 ובדיקות תלות שמוטמעות במערכת הבנייה.
Vendor modules
מודולים של ספקים הם קבצים הפעלה או ספריות משותפות שספציפיים לספק, וצריך להתקין אותם במחיצה של הספק. בקובצי Android.bp, מודולים של ספקים צריכים להגדיר את המאפיין vendor או proprietary לערך true.
בקובצי Android.mk, מודולים של ספקים צריכים להגדיר את
LOCAL_VENDOR_MODULE או LOCAL_PROPRIETARY_MODULE בתור
true.
אם BOARD_VNDK_VERSION מוגדר, מערכת ה-build לא מאפשרת תלויות בין מודולים של ספקים לבין מודולים של מסגרות, ומציגה שגיאות אם:
- מודול ללא
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). צריך להתייחס לגרסת הליבה כאל מודול מסגרת ולגרסת הספק כאל מודול ספק. אם מודולים מסוימים של המסגרת תלויים במודול הזה, נוצרת וריאציה של ליבה. אם מודולים מסוימים של ספקים תלויים במודול הזה, הגרסה של הספק תיבנה. מערכת הבנייה אוכפת את בדיקות התלות הבאות:
- גרסת הליבה היא תמיד framework-only ואין למודולים של ספקים גישה אליה.
- מודולים של מסגרת אף פעם לא יכולים לגשת לגרסת הספק.
- כל יחסי התלות של וריאציית הספק, שמצוינים ב-
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 |
הדרישות לגבי הסמלים המיוצאים
כלי הבדיקה של VNDK ABI משווה את ה-ABI של וריאציות של ספקים ב-VNDK ושל תוספים ל-VNDK לבין קובצי ה-dump של ה-ABI של ההפניה בתיקייה prebuilts/abi-dumps/vndk.
- הסמלים שמיוצאים על ידי וריאציות של ספקים ב-VNDK (לדוגמה:
/apex/com.android.vndk.v${VER}/lib[64]/libexample.so) צריכים להיות זהים לסמלים שמוגדרים ב-ABI dumps (ולא קבוצות-על שלהם). - סמלים שמיוצאים על ידי תוספי VNDK (למשל,
/vendor/lib[64]/vndk/libexample.so) צריכים להיות קבוצות-על של הסמלים שמוגדרים בקובצי ה-dump של ה-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. מומלץ להשאיר את ההצהרות האלה בכותרות נפרדות ולא לשנות את הכותרות הקיימות.
לדוגמה, קובץ כותרת חדש
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:
#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