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

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

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

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

libexample vendor_available:true ו-vndk.enabled:true

איור 1. התמיכה מופעלת.

קובץ ההפעלה של המסגרת /system/bin/foo וקובץ ההפעלה של הספק /vendor/bin/bar תלויים ב-libexample, ו-libexample מופיע במאפייני shared_libs שלהם.

אם משתמשים ב-libexample גם במודולים של framework וגם במודולים של ספקים, המערכת יוצרת שתי וריאנטים של 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.

הגדרת המודול

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

מודולים של ספקים

מודולים של ספקים הם קובצי הפעלה או ספריות משותפות ספציפיות לספק, שצריך להתקין במחיצה של הספק. בקובצי Android.bp, מודולים של ספקים חייבים להגדיר את המאפיין של הספק או את המאפיין הקנייני כ-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 יוצרת ספרייה משותפת סטטית למודול של הספק, שמקושרת לספריות האלה כשהאפשרות 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). יש להתייחס לגרסה הבסיסית כאל מודול של מסגרת, ולגרסה של הספק כאל מודול של ספק. אם חלק מהמודולים של המסגרת תלויים במודול הזה, וריאנט הליבה נוצר. אם חלק מהמודולים של הספקים תלויים במודול הזה, גרסת הספק תיבנה. מערכת ה-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 מטפלת בווריאציות של הספק:

ספק_זמין vndk
enabled
vndk
support_same_process
תיאורים של וריאנטים של ספקים
true false false הווריאציות של הספק הן VND-ONLY. ספריות משותפות מותקנות ב-/vendor/lib[64].
true לא תקין (שגיאת build)
true false הווריאציות של הספק הן VNDK. ספריות משותפות מותקנות ב-VNDK APEX.
true הווריאנטים של הספק הם VNDK-SP. ספריות משותפות מותקנות ב-VNDK APEX.

false

false

false

אין וריאציות של הספק. המודול הזה מיועד ל-FWK בלבד.

true לא תקין (שגיאת build)
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 לקובצי עזר של ABI ב-prebuilts/abi-dumps/vndk.

  • סמלים שמיוצאים על ידי וריאציות של ספק VNDK (למשל /apex/com.android.vndk.v${VER}/lib[64]/libexample.so) חייבים להיות זהים לסמלים שמוגדרים ב-Dumps של ABI (ולא לקבוצות העל).
  • סמלים שיוצאו על ידי תוספים של VNDK (למשל /vendor/lib[64]/vndk/libexample.so) חייבים להיות קבוצות רחבות יותר של הסמלים שמוגדרים ב-dumps של ABI.

אם וריאציות של ספק VNDK או תוספי VNDK לא עומדים בדרישות שצוינו למעלה, בודק ה-ABI של VNDK פולט שגיאות 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 עבור הסיומת libexample_ext של VNDK:

  • 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. כך תוכלו לוודא שהמשתמשים ב-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 guards. עם זאת, חשוב לוודא שכל המשתמשים בתוסף VNDK מוסיפים את דגלים ההגדרה. אפשר להגדיר את cc_defaults כדי להוסיף דגלים של הגדרה ל-cflags ולקשר ספריות משותפות באמצעות shared_libs.

לדוגמה, כדי להוסיף פונקציית חבר חדשה Example2::get_b() להרחבה libexample2_ext של VNDK, צריך לשנות את קובץ הכותרת הקיים ולהוסיף שומר #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,
}

חבילות מוצרים

במערכת ה-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