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

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

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

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

libexample provider_available:true ו-vndk.enabled:true

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

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

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

  • הווריאנט העיקרי תמיד זמין ל-framework בלבד ואין לו גישה לספק מודולים.
  • תמיד אי אפשר לגשת לווריאנט של הספק למודולים של framework.
  • כל יחסי התלות של הווריאנט של הספק, שמפורטים 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
מופעל
vndk
support_same_process
תיאורי הווריאציות של הספק
true false false הווריאציות של הספק הן רק VND. הספריות המשותפות הן הותקנה אל /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__, ערכים שונים של cflags או אפשר לציין את cppflags בAndroid.bp. 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 נוצר עבור 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,
}

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

במערכת ה-build של Android, המשתנה PRODUCT_PACKAGES מציין את קובצי ההפעלה, הספריות המשותפות או החבילות במכשיר. יחסי התלות המעבריים של וגם מותקנים במרומז את המכשיר.

אם המדיניות BOARD_VNDK_VERSION מופעלת, מודולים עם vendor_available או vndk.enabled יש לך הזדמנות מיוחדת לטיפול. אם מודול framework תלוי במודול עם 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