AIDL ליעדי HAL

ב-Android 11 נוספה האפשרות להשתמש ב-AIDL ל-HALs ב-Android, כך שאפשר להטמיע חלקים מ-Android בלי HIDL. כשהדבר אפשרי, כדאי להעביר את ה-HALs לשימוש ב-AIDL בלבד (כש-HALs במקור משתמשים ב-HIDL, צריך להשתמש ב-HIDL).

ב-HALs שמשתמשים ב-AIDL כדי לתקשר בין רכיבי המסגרת, כמו אלה שב-system.img, לבין רכיבי החומרה, כמו אלה שב-vendor.img, צריך להשתמש ב-AIDL יציב. עם זאת, כדי לתקשר בתוך מחיצה, למשל מ-HAL אחד ל-HAL אחר, אין הגבלה על מנגנון ה-IPC שבו משתמשים.

מוטיבציה

ה-AIDL קיים כבר הרבה יותר זמן מה-HIDL, והוא משמש במקומות רבים אחרים, למשל בין רכיבי מסגרת Android או באפליקציות. עכשיו, כשיש ל-AIDL תמיכה יציבה, אפשר להטמיע סטאק שלם עם סביבת זמן ריצה יחידה של IPC. ב-AIDL יש גם מערכת ניהול גרסאות טובה יותר מאשר ב-HIDL. אלה כמה מהיתרונות של AIDL:

  • שימוש בשפה אחת של IPC מאפשר ללמוד, לנפות באגים, לבצע אופטימיזציה ולאבטח רק דבר אחד.
  • ב-AIDL יש תמיכה בניהול גרסאות במקום לבעלי ממשק:
    • הבעלים יכולים להוסיף שיטות לסוף ממשקים, או שדות ל-Parcelables. המשמעות היא שקל יותר ליצור גרסאות של קוד לאורך השנים, וגם שהעלות השנתית נמוכה יותר (אפשר לשנות את הסוגים במקום ולא צריך ספריות נוספות לכל גרסה של הממשק).
    • אפשר לצרף ממשקי תוספים בזמן הריצה במקום במערכת הסוגים, כך שאין צורך לבסס מחדש תוספים במורד הזרם לגרסאות חדשות יותר של ממשקים.
  • אפשר להשתמש בממשק AIDL קיים ישירות כשהבעלים שלו בוחר לבצע לו יציבות. בעבר היה צריך ליצור עותק שלם של הממשק ב-HIDL.

פיתוח בזמן הריצה של AIDL

ל-AIDL יש שלושה קצוות עורפיים שונים: Java,‏ NDK ו-CPP. כדי להשתמש ב-AIDL יציב, תמיד צריך להשתמש בעותק המערכת של libbinder ב-system/lib*/libbinder.so ולדבר ב-/dev/binder. לגבי קוד בתמונה vendor, המשמעות היא שאי אפשר להשתמש ב-libbinder (מ-VNDK): לספרייה הזו יש ממשק API לא יציב של C++ ורכיבים פנימיים לא יציבים. במקום זאת, קוד מקומי של ספק צריך להשתמש בקצה העורפי של NDK ב-AIDL, לקשר ל-libbinder_ndk (שנתמך על ידי libbinder.so במערכת) ולקשר לספריות NDK שנוצרו על ידי רשומות aidl_interface. שמות המודולים המדויקים מפורטים בקטע כללים למתן שמות למודולים.

כתיבת ממשק HAL של AIDL

כדי שאפשר יהיה להשתמש בממשק AIDL בין המערכת לבין הספק, צריך לבצע בממשק שני שינויים:

  • כל הגדרת סוג צריכה לכלול את ההערה @VintfStability.
  • ההצהרה aidl_interface צריכה לכלול את stability: "vintf",.

רק הבעלים של הממשק יכול לבצע את השינויים האלה.

אחרי ביצוע השינויים האלה, הממשק צריך להופיע במניפסט VINTF כדי לפעול. אפשר לבדוק את הדרישה הזו (ודרישות קשורות, כמו אימות שממשקי API שפורסמו מושבתים) באמצעות בדיקת VTS vts_treble_vintf_vendor_test. אפשר להשתמש בממשק @VintfStability בלי הדרישות האלה על ידי קריאה ל-AIBinder_forceDowngradeToLocalStability בקצה העורפי של NDK, ל-android::Stability::forceDowngradeToLocalStability בקצה העורפי של C++ או ל-android.os.Binder#forceDowngradeToSystemStability בקצה העורפי של Java באובייקט של Binder לפני שהוא נשלח לתהליך אחר.

בנוסף, כדי לשפר את יכולת ההעברה של הקוד ולמנוע בעיות פוטנציאליות כמו ספריות נוספות מיותרות, משביתים את הקצה העורפי של CPP.

שימו לב שהשימוש ב-backends בדוגמת הקוד הבאה תקין, כי יש שלושה קצוות עורפיים (Java,‏ NDK ו-CPP). הקוד מראה איך להשבית קצה עורפי של CPP:

    aidl_interface: {
        ...
        backends: {
            cpp: {
                enabled: false,
            },
        },
    }

חיפוש ממשקי HAL של AIDL

ממשקי AIDL יציבים של AOSP ל-HALs נמצאים בתיקיות aidl באותן ספריות בסיס שבהן נמצאים ממשקי HIDL:

  • hardware/interfaces מיועד לממשקים שספק החומרה בדרך כלל מספק.
  • frameworks/hardware/interfaces מיועד לממשקים ברמה גבוהה שסופקו לחומרה.
  • system/hardware/interfaces מיועד לממשקים ברמה נמוכה שסופקו לחומרה.

ממשקי התוספים צריכים להיות בספריות משנה אחרות של hardware/interfaces ב-vendor או ב-hardware.

ממשקי תוספים

לכל גרסה של Android יש קבוצה של ממשקי AOSP רשמיים. כששותפי Android רוצים להוסיף יכולות לממשקים האלה, הם לא צריכים לשנות אותם ישירות, כי הפעולה הזו גורמת לסביבת זמן הריצה של Android שלהם להיות לא תואמת לסביבת זמן הריצה של Android ב-AOSP. מומלץ לא לשנות את הממשקים האלה כדי שתמונת ה-GSI תמשיך לפעול.

תוכלו לרשום תוספים בשתי דרכים שונות:

לא משנה איך התוסף רשום, כשרכיבים ספציפיים לספק (כלומר לא חלק מ-AOSP למעלה בזרם) משתמשים בממשק, לא יכולים להתרחש התנגשויות במיזוג. עם זאת, כשמבצעים שינויים במורד הזרם ברכיבים של AOSP למעלה בזרם, יכולות להתרחש התנגשויות במיזוג, ולכן מומלץ להשתמש באסטרטגיות הבאות:

  • להעביר את התוספות לממשק ל-AOSP במהדורה הבאה.
  • תוספות לממשק ב-upstream שמאפשרות גמישות נוספת (ללא התנגשויות במיזוג) במהדורה הבאה.

רכיבי Parcelable של התוסף: ParcelableHolder

ParcelableHolder הוא מופע של הממשק Parcelable שיכול להכיל מופע נוסף של Parcelable.

התרחיש לדוגמה העיקרי של ParcelableHolder הוא הרחבת Parcelable. לדוגמה, קובץ אימג' שמטמיעים של מכשירים מצפים שיוכלו להרחיב את Parcelable, ‏ AospDefinedParcelable שהוגדרו ב-AOSP, כך שיכלול את התכונות הנוספות שלהם.

אתם יכולים להשתמש בממשק ParcelableHolder כדי להרחיב את Parcelable בתכונות שמוסיפות ערך. הממשק ParcelableHolder מכיל מופע של Parcelable. אם תנסו להוסיף שדות ישירות ל-Parcelable, תקבלו הודעת שגיאה:

parcelable AospDefinedParcelable {
  int a;
  String b;
  String x; // ERROR: added by a device implementer
  int[] y; // added by a device implementer
}

כפי שרואים בקוד הקודם, השיטה הזו לא תקינה כי יכול להיות שיהיו התנגשויות בין השדות שהוספתם למכשיר כשהשדה Parcelable ישתנה במהדורות הבאות של Android.

באמצעות ParcelableHolder, הבעלים של אובייקט שאפשר לחלק למקטעים יכול להגדיר נקודת הרחבה במכונה של Parcelable:

parcelable AospDefinedParcelable {
  int a;
  String b;
  ParcelableHolder extension;
}

לאחר מכן, מי שמטמיע את המכשיר יכול להגדיר מכונה משלו של Parcelable עבור התוסף:

parcelable OemDefinedParcelable {
  String x;
  int[] y;
}

אפשר לצרף את המופע החדש של Parcelable למופע המקורי של Parcelable באמצעות השדה ParcelableHolder:


// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;

ap.extension.setParcelable(op);

...

OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);

// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();

ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);

...

std::shared_ptr<OemDefinedParcelable> op_ptr;

ap.extension.getParcelable(&op_ptr);

// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);

...

std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);

// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });

ap.extension.set_parcelable(Rc::clone(&op));

...

let op = ap.extension.get_parcelable::<OemDefinedParcelable>();

שמות של מכונות של שרת AIDL HAL

לפי המוסכמה, לשירותי AIDL HAL יש שם מכונה בפורמט $package.$type/$instance. לדוגמה, מכונה של HAL של ויברטור רשומה בתור android.hardware.vibrator.IVibrator/default.

כתיבת שרת HAL של AIDL

@VintfStability צריך להצהיר על שרתי AIDL במניפסט של VINTF, לדוגמה:

    <hal format="aidl">
        <name>android.hardware.vibrator</name>
        <version>1</version>
        <fqname>IVibrator/default</fqname>
    </hal>

אחרת, צריך לרשום שירות AIDL באופן רגיל. כשמריצים בדיקות VTS, כל ממשקי ה-HAL של AIDL שצוינו אמורים להיות זמינים.

כתיבת לקוח AIDL

לקוחות AIDL חייבים להצהיר על עצמם במטריצה התאימות, לדוגמה:

    <hal format="aidl" optional="true">
        <name>android.hardware.vibrator</name>
        <version>1-2</version>
        <interface>
            <name>IVibrator</name>
            <instance>default</instance>
        </interface>
    </hal>

המרת HAL קיים מ-HIDL ל-AIDL

משתמשים בכלי hidl2aidl כדי להמיר ממשק HIDL ל-AIDL.

התכונות של hidl2aidl:

  • יצירת קובצי AIDL‏ (.aidl) על סמך קובצי HAL‏ (.hal) לחבילה הנתונה.
  • יוצרים כללי build לחבילת ה-AIDL החדשה עם כל הקצוות העורפי מופעלים.
  • יוצרים שיטות תרגום בקצוות העורפי של Java,‏ CPP ו-NDK כדי לתרגם מהטיפוסים של HIDL לטיפים של AIDL.
  • יצירת כללי build לספריות תרגום עם יחסי התלות הנדרשים.
  • יוצרים טענות נכוֹנוּת (asserts) סטטיות כדי לוודא שלמְמַפְרֵרים של HIDL ו-AIDL יש את אותם ערכים בקצוות העורפי של CPP ו-NDK.

כדי להמיר חבילת קבצים של HAL לקובצי AIDL:

  1. בונים את הכלי שנמצא ב-system/tools/hidl/hidl2aidl.

    פיתוח הכלי הזה מהמקור העדכני ביותר מספק את החוויה המלאה ביותר. אפשר להשתמש בגרסה האחרונה כדי להמיר ממשקים בהסתעפויות ישנות יותר מהגרסאות הקודמות:

    m hidl2aidl
  2. מריצים את הכלי עם ספריית פלט ואחריה החבילה שרוצים להמיר.

    אפשר גם להשתמש בארגומנט -l כדי להוסיף את התוכן של קובץ רישיון חדש לחלק העליון של כל הקבצים שנוצרו. חשוב להשתמש ברישיון ובתאריך הנכונים:

    hidl2aidl -o <output directory> -l <file with license> <package>

    לדוגמה:

    hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
  3. קוראים את הקבצים שנוצרו ומתקנים בעיות בהמרה:

    • conversion.log מכיל בעיות שלא טופלו שצריך לפתור קודם.
    • יכול להיות שבקבצי ה-AIDL שנוצרו יהיו אזהרות והצעות שצריך לבצע בהן פעולה. התגובות האלה מתחילות ב-//.
    • לנקות את החבילה ולבצע בה שיפורים.
    • בודקים את ההערה @JavaDerive כדי למצוא תכונות שעשויות להידרש, כמו toString או equals.
  4. יוצרים רק את היעדים הנחוצים:

    • משביתים קצוות עורפיים שלא יהיו בשימוש. עדיף להשתמש בקצה העורפי של NDK במקום בקצה העורפי של CPP. אפשר לעיין במאמר פיתוח ב-build מול סביבת זמן הריצה של AIDL.
    • הסרה של ספריות תרגום או של קוד שנוצר מהן ולא ישמש אתכם.
  5. הבדלים עיקריים בין AIDL ל-HIDL:

    • בדרך כלל, שימוש ב-Status ובחריגים המובנים של AIDL משפרים את הממשק ומבטלים את הצורך בסוג סטטוס נוסף שספציפי לממשק.
    • ארגומנטים של ממשק AIDL בשיטות הם לא @nullable כברירת מחדל, כמו שהם היו ב-HIDL.

SEPolicy ל-HALs של AIDL

סוג שירות AIDL שגלוי לקוד של הספק חייב לכלול את המאפיין hal_service_type. אחרת, הגדרת המדיניות של sepolicy זהה לכל שירות AIDL אחר (אבל יש מאפיינים מיוחדים ל-HALs). זוהי דוגמה להגדרה של הקשר שירות ב-HAL:

    type hal_foo_service, service_manager_type, hal_service_type;

ברוב השירותים שמוגדרים על ידי הפלטפורמה, המערכת כבר מוסיפה הקשר של שירות עם הסוג הנכון (לדוגמה, android.hardware.foo.IFoo/default כבר מסומן בתור hal_foo_service). עם זאת, אם לקוח של מסגרת תומך בכמה שמות של מכונות, צריך להוסיף שמות של מכונות נוספות בקובצי service_contexts ספציפיים למכשיר:

    android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0

כשיוצרים סוג חדש של HAL, צריך להוסיף מאפייני HAL. מאפיין HAL ספציפי יכול להיות משויך למספר סוגים של שירותים (לכל אחד מהם יכולות להיות כמה מכונות, כפי שצוין למעלה). ל-HAL, foo, יש את hal_attribute(foo). המאקרו הזה מגדיר את המאפיינים hal_foo_client ו-hal_foo_server. למאפיין נתון של HAL, המאקרו hal_client_domain והמאקרו hal_server_domain משייכים דומיין לדומיין נתון. לדוגמה, שרת מערכת שהוא לקוח של ה-HAL הזה תואם למדיניות hal_client_domain(system_server, hal_foo). באופן דומה, שרת HAL כולל את hal_server_domain(my_hal_domain, hal_foo).

בדרך כלל, לכל מאפיין HAL נתון, יוצרים גם דומיין כמו hal_foo_default לצורך הפניה או לדוגמה של מאפייני HAL. עם זאת, במכשירים מסוימים נעשה שימוש בדומיינים האלה בשביל השרתים שלהם. יש משמעות להבדל בין דומיינים במספר שרתים רק אם יש כמה שרתים שמספקים את אותו ממשק וצריכים קבוצת הרשאות שונה בהטמעות שלהם. בכל המאקרוים האלה, hal_foo הוא לא אובייקט של מדיניות אבטחה. במקום זאת, המאקרו משתמש באסימון הזה כדי להפנות לקבוצת המאפיינים שמשויכים לזוג של שרת לקוח.

עם זאת, עד כה, hal_foo_service ו-hal_foo (צמד המאפיינים מ-hal_attribute(foo)) לא משויכים. מאפיין HAL משויך לשירותי HAL של AIDL באמצעות המאקרו hal_attribute_service (שירותי HAL של HIDL משתמשים במאקרו hal_attribute_hwservice), לדוגמה, hal_attribute_service(hal_foo, hal_foo_service). המשמעות היא שתהליכים מסוג hal_foo_client יכולים לקבל גישה ל-HAL, ותהליכים מסוג hal_foo_server יכולים לרשום את ה-HAL. אכיפת כללי הרישום האלה מתבצעת על ידי מנהל ההקשר (servicemanager).

שמות השירותים לא תמיד תואמים למאפייני HAL, לדוגמה, hal_attribute_service(hal_foo, hal_foo2_service). באופן כללי, מכיוון שהמשמעות של האפשרות הזו היא שהשירותים תמיד נמצאים בשימוש יחד, אפשר להסיר את hal_foo2_service ולהשתמש ב-hal_foo_service לכל הקשרי השירות. כש-HALs מגדירים כמה מכונות hal_attribute_service, הסיבה לכך היא ששם המאפיין המקורי של HAL לא כללי מספיק ואי אפשר לשנות אותו.

כשמרכזים את כל הנתונים האלה, דוגמה ל-HAL נראית כך:

    public/attributes:
    // define hal_foo, hal_foo_client, hal_foo_server
    hal_attribute(foo)

    public/service.te
    // define hal_foo_service
    type hal_foo_service, hal_service_type, protected_service, service_manager_type

    public/hal_foo.te:
    // allow binder connection from client to server
    binder_call(hal_foo_client, hal_foo_server)
    // allow client to find the service, allow server to register the service
    hal_attribute_service(hal_foo, hal_foo_service)
    // allow binder communication from server to service_manager
    binder_use(hal_foo_server)

    private/service_contexts:
    // bind an AIDL service name to the selinux type
    android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0

    private/<some_domain>.te:
    // let this domain use the hal service
    binder_use(some_domain)
    hal_client_domain(some_domain, hal_foo)

    vendor/<some_hal_server_domain>.te
    // let this domain serve the hal service
    hal_server_domain(some_hal_server_domain, hal_foo)

ממשקי תוספים מצורפים

אפשר לצרף תוסף לכל ממשק של מקשר, בין אם מדובר בממשק ברמה העליונה שמירשם ישירות ב-Service Manager ובין אם מדובר בממשק משנה. כשמקבלים תוסף, צריך לוודא שהסוג שלו תואם לציפיות. אפשר להגדיר תוספים רק מהתהליך שמגיש את ה-binder.

צריך להשתמש בתוספים מצורפים בכל פעם שתוסף משנה את הפונקציונליות של HAL קיים. אם אתם צריכים יכולת חדשה לגמרי, אין צורך במנגנון הזה, ותוכלו לרשום ממשק תוסף ישירות במנהל השירות. ממשקי התוספים המצורפים הכי שימושיים כשהם מצורפים לממשקי משנה, כי ההיררכיות האלה יכולות להיות עמוקות או עם כמה מופעים. כדי להשתמש בתוסף גלובלי כדי לשקף את היררכיית הממשק של השירות המקושר של שירות אחר, צריך לבצע ניהול חשבונות נרחב כדי לספק יכולות מקבילות לתוספים שמצורפים ישירות.

כדי להגדיר תוסף ב-binder, משתמשים בממשקי ה-API הבאים:

  • קצה עורפי של NDK: AIBinder_setExtension
  • קצה עורפי של Java: ‏ android.os.Binder.setExtension
  • קצה עורפי של CPP: android::Binder::setExtension
  • קצה עורפי ב-Rust: binder::Binder::set_extension

כדי לקבל תוסף ל-binder, משתמשים בממשקי ה-API הבאים:

  • קצה עורפי של NDK: AIBinder_getExtension
  • קצה עורפי של Java: ‏ android.os.IBinder.getExtension
  • קצה עורפי של CPP: android::IBinder::getExtension
  • קצה עורפי ב-Rust: binder::Binder::get_extension

מידע נוסף על ממשקי ה-API האלה זמין במסמכי העזרה של הפונקציה getExtension בקצה העורפי המתאים. דוגמה לשימוש בתוספים מופיעה במאמר hardware/interfaces/tests/extension/vibrator.

ההבדלים העיקריים בין AIDL לבין HIDL

כשמשתמשים ב-HAL של AIDL או בממשקי AIDL HAL, חשוב לשים לב להבדלים בהשוואה לכתיבת HAL של HIDL.

  • תחביר השפה של AIDL קרוב יותר ל-Java. התחביר של HIDL דומה ל-C++‎.
  • לכל ממשקי AIDL יש מצבי שגיאה מובנים. במקום ליצור סוגי סטטוס מותאמים אישית, יוצרים מספרים שלמים קבועים של סטטוס בקובצי ממשק ומשתמשים ב-EX_SERVICE_SPECIFIC בקצוות העורפי של CPP ו-NDK וב-ServiceSpecificException בקצה העורפי של Java. טיפול בשגיאות
  • כששולחים אובייקטים של Binder, ה-AIDL לא מפעיל באופן אוטומטי מאגרי חוטים. צריך להתחיל אותם באופן ידני (מידע נוסף זמין במאמר ניהול שרשור).
  • ב-AIDL לא מתבצעת ביטול במקרה של שגיאות העברה לא מאומתות (ב-HIDL Return מתבצעת ביטול במקרה של שגיאות לא מאומתות).
  • ב-AIDL אפשר להצהיר על סוג אחד בלבד לכל קובץ.
  • אפשר לציין את הארגומנטים של AIDL בתור in, ‏ out או inout, בנוסף לפרמטר הפלט (אין קריאות חזרה (callbacks) סינכרוניות).
  • ב-AIDL נעשה שימוש ב-fd כסוג הפרימיטיבי במקום ב-handle.
  • ב-HIDL נעשה שימוש בגרסאות ראשיות לשינויים לא תואמים ובגרסאות משניות לשינויים תואמים. ב-AIDL, שינויים שתואמים לאחור מתבצעים במקום. ב-AIDL אין מושג מפורש של גרסאות ראשיות, אלא הוא משולב בשמות החבילות. לדוגמה, AIDL עשוי להשתמש בשם החבילה bluetooth2.
  • כברירת מחדל, AIDL לא יורש עדיפות בזמן אמת. צריך להשתמש בפונקציה setInheritRt לכל מקשר כדי לאפשר ירושה של תעדוף בזמן אמת.

בדיקות של חבילה לבדיקת ספקים (VTS) עבור ממשקי HAL

מערכת Android מסתמכת על חבילת בדיקות של ספקים (VTS) כדי לאמת הטמעות HAL צפויות. VTS עוזר לוודא ש-Android תואם לאחור להטמעות ישנות של ספקים. להטמעות שלא עוברות את בדיקת VTS יש בעיות תאימות ידועות שעלולות למנוע מהן לפעול בגרסאות עתידיות של מערכת ההפעלה.

ל-VTS ל-HAL יש שני חלקים עיקריים.

1. מוודאים ש-Android מכירה את ממשקי ה-HAL במכשיר ומצפה להם

אפשר למצוא את קבוצת הבדיקות הזו ב-test/vts-testcase/hal/treble/vintf. הבדיקות האלה אחראיות לאימות:

  • כל ממשק @VintfStability שמוצהר במניפסט של VINTF קפוא בגרסה ידועה שפורסמה. כך תוכלו לוודא ששני הצדדים של הממשק מסכימים על ההגדרה המדויקת של אותה גרסת ממשק. הדבר הכרחי לפעולה הבסיסית.
  • כל ממשקי ה-HAL שמוצהרים במניפסט של VINTF זמינים במכשיר הזה. כל לקוח עם הרשאות מספיקות לשימוש בשירות HAL מוצהר חייב להיות מסוגל לקבל את השירותים האלה ולהשתמש בהם בכל שלב.
  • כל ממשקי ה-HAL שמוצהרים במניפסט של VINTF מציגים את הגרסה של הממשק שהם מגדירים במניפסט.
  • אין HALs שהוצאו משימוש שמוצגים במכשיר. מערכת Android מפסיקה את התמיכה בגרסאות ישנות יותר של ממשקי HAL, כפי שמתואר בקטע מחזור החיים של FCM.
  • ממשקי ה-HAL הנדרשים נמצאים במכשיר. כדי ש-Android יפעל כראוי, נדרשים לו ממשקי HAL מסוימים.

2. אימות ההתנהגות הצפויה של כל HAL

לכל ממשק HAL יש בדיקות VTS משלו כדי לאמת את ההתנהגות הצפויה מהלקוחות שלו. תרחישי הבדיקה פועלים בכל מופע של ממשק HAL מוצהר, ומאכפים התנהגות ספציפית על סמך גרסת הממשק שהופעלה.

הבדיקה הזו מנסה לכסות כל היבט של הטמעת ה-HAL שעליו מסתמכת מסגרת Android, או שעשוי להסתמך עליו בעתיד.

הבדיקות האלה כוללות אימות של תמיכה בתכונות, טיפול בשגיאות וכל התנהגות אחרת שהלקוח עשוי לצפות לקבל מהשירות.

אבני דרך של VTS לפיתוח HAL

כשאתם יוצרים או משנים ממשקי HAL של Android, אתם אמורים לעדכן את בדיקות VTS.

בדיקות VTS צריכות להסתיים ולהיות מוכנות לאימות הטמעות של ספקים לפני שהן יקפיאו את הגרסאות של Android Vendor API. הם צריכים להיות מוכנים לפני שהממשקים יקפיאו, כדי שמפתחים יוכלו ליצור את ההטמעות שלהם, לאמת אותן ולספק משוב למפתחי ממשק ה-HAL.

VTS ב-Cuttlefish

כשהחומרה לא זמינה, מערכת Android משתמשת ב-Cuttlefish ככלי פיתוח לממשקי HAL. כך אפשר לבצע בדיקות VTS ובדיקות שילוב של Android בקנה מידה נרחב.

hal_implementation_test בודק אם ב-Cuttlefish יש הטמעות של הגרסאות העדכניות ביותר של ממשקי HAL, כדי לוודא ש-Android מוכן לטפל בממשקים החדשים, ובדיקות VTS מוכנות לבדוק את ההטמעות החדשות של הספקים ברגע שהחומרה והמכשירים החדשים יהיו זמינים.