תמיכה במערכת build של VNDK

ב-Android מגרסה 8.1 ואילך, למערכת build יש תמיכה מובנית ב-VNDK. כשמופעלת תמיכה ב-VNDK, מערכת ה-build בודקת את התלות בין המודולים, יוצרת וריאציה ספציפית לספק עבור מודולים של ספקים ומתקינה את המודולים האלה באופן אוטומטי בספריות ייעודיות.

דוגמה לתמיכה ב-VNDK build

בדוגמה הזו, הגדרת המודול Android.bp מגדירה ספרייה בשם libexample. המאפיין vendor_available מציין שמודולים של מסגרות ומודולים של ספקים עשויים להיות תלויים ב-libexample:

‪libexample vendor_available:true and vndk.enabled:true

איור 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/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}.

VNDK APEX

איור 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.

false

false

false

אין וריאציות של ספקים. המודול הזה הוא 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