ב-Android 11 נוספה האפשרות להשתמש ב-AIDL עבור HAL ב-Android, וכך אפשר להטמיע חלקים מ-Android בלי HIDL. מעבר לשימוש ב-AIDL באופן בלעדי ב-HALs, כשזה אפשרי (אם ה-HALs במעלה הזרם משתמשים ב-HIDL, צריך להשתמש ב-HIDL).
ממשקי HAL שמשתמשים ב-AIDL כדי לתקשר בין רכיבי framework, כמו אלה שב-system.img, לבין רכיבי חומרה, כמו אלה שב-vendor.img, חייבים להשתמש ב-AIDL יציב. עם זאת, כדי לתקשר בתוך מחיצה, למשל מ-HAL אחד ל-HAL אחר, אין הגבלה על מנגנון ה-IPC שבו משתמשים.
מוטיבציה
AIDL קיימת כבר הרבה זמן, יותר מ-HIDL, והיא נמצאת בשימוש במקומות רבים אחרים, כמו בין רכיבי Android framework או באפליקציות. עכשיו, כש-AIDL תומך ביציבות, אפשר להטמיע מחסנית שלמה עם זמן ריצה יחיד של IPC. בנוסף, מערכת ניהול הגרסאות של AIDL טובה יותר מזו של HIDL. אלה כמה מהיתרונות של AIDL:
- שימוש בשפה אחת של IPC מאפשר ללמוד, לנפות באגים, לבצע אופטימיזציה ולאבטח רק דבר אחד.
- AIDL תומכת בניהול גרסאות במקום לבעלים של ממשק:
- בעלים יכולים להוסיף שיטות לסוף של ממשקים, או שדות לאובייקטים מסוג Parcelable. המשמעות היא שקל יותר ליצור גרסאות של קוד לאורך השנים, וגם העלות השנתית קטנה יותר (אפשר לשנות את הסוגים במקום, ואין צורך בספריות נוספות לכל גרסת ממשק).
- אפשר לצרף ממשקי תוספים בזמן הריצה ולא במערכת הסוגים, כך שאין צורך לבצע rebase לתוספים במורד הזרם לגרסאות חדשות יותר של ממשקים.
- אפשר להשתמש ישירות בממשק 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. שמות המודולים המדויקים מופיעים במאמר בנושא כללים למתן שמות למודולים.
כתיבת ממשק AIDL HAL
כדי להשתמש בממשק AIDL בין המערכת לספק, צריך לבצע שני שינויים בממשק:
- כל הגדרת סוג צריכה להיות עם ההערה
@VintfStability. - ההצהרה
aidl_interfaceצריכה לכלול אתstability: "vintf",.
רק הבעלים של הממשק יכול לבצע את השינויים האלה.
כדי שהשינויים האלה יפעלו, הם צריכים להיות בקובץ המניפסט של VINTF בממשק. כדי לבדוק את זה (ואת הדרישות הקשורות, כמו אימות שהממשקים שפורסמו קפואים), משתמשים בבדיקה של Vendor Test Suite (VTS) vts_treble_vintf_vendor_test. אפשר להשתמש בממשק @VintfStability בלי הדרישות האלה על ידי קריאה ל-AIBinder_forceDowngradeToLocalStability בעורף של NDK, ל-android::Stability::forceDowngradeToLocalStability בעורף של C++ או ל-android.os.Binder#forceDowngradeToSystemStability בעורף של Java באובייקט binder לפני שהוא נשלח לתהליך אחר.
בנוסף, כדי להשיג ניידות מקסימלית של הקוד ולמנוע בעיות פוטנציאליות כמו ספריות מיותרות, צריך להשבית את קצה העורף של CPP.
בדוגמה הבאה אפשר לראות איך להשבית את העורף של CPP:
aidl_interface: {
...
backend: {
cpp: {
enabled: false,
},
},
}
חיפוש ממשקי AIDL HAL
ממשקי AIDL יציבים של AOSP ל-HAL נמצאים בתיקיות aidl באותן תיקיות בסיס כמו ממשקי HIDL:
-
hardware/interfacesהוא ממשקים שמסופקים בדרך כלל על ידי חומרה. -
frameworks/hardware/interfacesמיועד לממשקים ברמה גבוהה שמועברים לחומרה. -
system/hardware/interfacesמיועד לממשקים ברמה נמוכה שמועברים לחומרה.
ממשקי תוספים צריכים להיות בספריות משנה אחרות של hardware/interfaces ב-vendor או ב-hardware.
ממשקי תוספים
בכל גרסה של Android יש קבוצה של ממשקי AOSP רשמיים. כששותפים של Android רוצים להוסיף יכולות לממשקים האלה, הם לא צריכים לשנות אותם ישירות כי זה גורם לזמן הריצה של Android שלהם להיות לא תואם לזמן הריצה של Android ב-AOSP. כדי שתמונת ה-GSI תמשיך לפעול, אל תשנו את הממשקים האלה.
יש שתי דרכים שבהן תוספים יכולים להירשם:
- בזמן הריצה. אפשר לעיין בממשקי התוספים המצורפים
- כאפליקציה עצמאית, רשומה בכל העולם וב-VINTF
לא משנה איך התוסף רשום, אם רכיבים ספציפיים לספק (כלומר, לא חלק מ-AOSP במעלה הזרם) משתמשים בממשק, לא יכולים להתרחש התנגשויות במיזוג. עם זאת, אם מתבצעים שינויים במורד הזרם ברכיבי AOSP במעלה הזרם, יכולות להתרחש התנגשויות במיזוג, ולכן מומלץ להשתמש באסטרטגיות הבאות:
- העברת התוספות לממשק ל-AOSP במהדורה הבאה.
- תוספות לממשק שמאפשרות גמישות רבה יותר (ללא התנגשויות מיזוג) בגרסה הבאה.
אובייקטים מסוג 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:
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.
כתיבת שרת AIDL HAL
@VintfStability צריך להצהיר על שרתי AIDL במניפסט VINTF, לדוגמה:
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>1</version>
<fqname>IVibrator/default</fqname>
</hal>
אחרת, הם צריכים לרשום שירות AIDL כרגיל. כשמריצים בדיקות VTS, כל ממשקי AIDL HAL שהוגדרו אמורים להיות זמינים.
כתיבת לקוח 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) עבור החבילה שצוינה. - יוצרים כללי בנייה לחבילת ה-AIDL החדשה עם כל קצוות העורף המופעלים.
- יוצרים שיטות תרגום בחלק האחורי של Java, CPP ו-NDK כדי לתרגם מסוגי HIDL לסוגי AIDL.
- יוצרים כללי בנייה לספריות תרגום עם יחסי תלות נדרשים.
- יוצרים הצהרות סטטיות כדי לוודא שלמספרי HIDL ו-AIDL יש את אותם ערכים ב-CPP וב-NDK backends.
כדי להמיר חבילה של קובצי HAL לקובצי AIDL:
בונים את הכלי שנמצא ב-
system/tools/hidl/hidl2aidl.כדי ליהנות מהחוויה הכי מלאה, מומלץ להשתמש בכלי הזה שנוצר מהמקור העדכני ביותר. אפשר להשתמש בגרסה העדכנית כדי להמיר ממשקים בענפים ישנים יותר מגרסאות קודמות:
m hidl2aidlמריצים את הכלי עם ספריית פלט ואחריה החבילה שרוצים להמיר.
אפשר להשתמש בארגומנט
-lכדי להוסיף את התוכן של קובץ רישיון חדש לראש כל הקבצים שנוצרו. חשוב להקפיד להשתמש ברישיון הנכון ובתאריך הנכון:hidl2aidl -o <output directory> -l <file with license> <package>לדוגמה:
hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2קוראים את הקבצים שנוצרו ופותרים בעיות בהמרה:
conversion.logמכיל בעיות לא מטופלות שצריך לפתור קודם.- יכול להיות שבקובצי ה-AIDL שנוצרו יהיו אזהרות והצעות שצריך לפעול לפיהן. התגובות האלה מתחילות ב-
//. - מנקים את החבילה ומשפרים אותה.
- בודקים את ההערה
@JavaDeriveלגבי תכונות שאולי נדרשות, כמוtoStringאוequals.
יוצרים רק את יעדי הטירגוט שצריכים:
- משביתים את ה-backends שלא ישמשו אתכם. עדיף להשתמש ב-NDK backend במקום ב-CPP backend. מידע נוסף זמין במאמר בנושא Build מול זמן הריצה של AIDL.
- מסירים ספריות תרגום או קוד שנוצר מהן שלא ישמשו אתכם.
ההבדלים העיקריים בין AIDL ל-HIDL
- בדרך כלל, שימוש ב-
Statusובחריגים המובנים של AIDL משפר את הממשק ומבטל את הצורך בסוג סטטוס נוסף שספציפי לממשק. - כברירת מחדל, הארגומנטים של ממשק AIDL בשיטות הם לא
@nullableכמו שהם היו ב-HIDL.
- בדרך כלל, שימוש ב-
SEPolicy for AIDL HALs
לסוג שירות AIDL שגלוי לקוד הספק צריך להיות מאפיין hal_service_type. אחרת, ההגדרה של sepolicy זהה לכל שירות AIDL אחר (אבל יש מאפיינים מיוחדים ל-HAL). דוגמה להגדרה של הקשר של שירות 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_client_domain ו-hal_server_domain משייכות דומיין למאפיין HAL נתון. לדוגמה, שרת מערכת שהוא לקוח של HAL הזה תואם למדיניות hal_client_domain(system_server, hal_foo). באופן דומה, שרת HAL כולל את hal_server_domain(my_hal_domain, hal_foo).
בדרך כלל, עבור מאפיין HAL נתון, יוצרים גם דומיין כמו hal_foo_default לצורך הפניה או דוגמאות של HAL. עם זאת, חלק מהמכשירים משתמשים בדומיינים האלה לשרתים שלהם. ההבחנה בין דומיינים עבור כמה שרתים חשובה רק אם יש כמה שרתים שמציגים את אותו ממשק וצריכים קבוצת הרשאות שונה בהטמעות שלהם.
בכל פקודות המאקרו האלה, hal_foo הוא לא אובייקט sepolicy. במקום זאת, פקודות המאקרו האלה משתמשות בטוקן הזה כדי להתייחס לקבוצת המאפיינים שמשויכים לזוג שרת-לקוח.
עם זאת, עד עכשיו, hal_foo_service ו-hal_foo (צמד המאפיינים מ-hal_attribute(foo)) לא משויכים. מאפיין HAL משויך לשירותי AIDL HAL באמצעות מאקרו hal_attribute_service (ב-HIDL HAL נעשה שימוש במאקרו 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 בכל ההקשרים של השירות. כששכבות HAL מגדירות כמה מופעים של 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)
ממשקי תוספים מצורפים
אפשר לצרף תוסף לכל ממשק של קובץ מאגד, בין אם זה ממשק ברמה העליונה שרשום ישירות במנהל השירותים או ממשק משנה. כשמקבלים תוסף, צריך לוודא שהסוג שלו הוא מה שציפיתם. אפשר להגדיר תוספים רק מהתהליך שמציג את הכרטיסייה.
כדאי להשתמש בתוספים המצורפים בכל פעם שתוסף משנה את הפונקציונליות של HAL קיים. כשצריך יכולת חדשה לגמרי, אין צורך במנגנון הזה, ואפשר לרשום ממשק תוסף ישירות במנהל השירותים. הכי הגיוני לצרף ממשקי הרחבות כשהם מצורפים לממשקי משנה, כי ההיררכיות האלה יכולות להיות עמוקות או מרובות מופעים. שימוש בתוסף גלובלי כדי לשקף את היררכיית ממשק ה-binder של שירות אחר דורש ניהול חשבונות נרחב כדי לספק יכולות שוות ערך לתוספים שמצורפים ישירות.
כדי להגדיר תוסף ב-binder, משתמשים בממשקי ה-API הבאים:
- קצה עורפי של NDK:
AIBinder_setExtension - קצה עורפי של Java:
android.os.Binder.setExtension - קצה עורפי של CPP:
android::Binder::setExtension - קצה עורפי של Rust:
binder::Binder::set_extension
כדי לקבל הארכה של תקופת ההתחייבות, משתמשים בממשקי ה-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
כשמשתמשים ב-AIDL HALs או בממשקי AIDL HAL, חשוב לשים לב להבדלים ביניהם לבין כתיבת HIDL HALs.
- התחביר של שפת AIDL דומה יותר ל-Java. התחביר של HIDL דומה לזה של C++.
- לכל ממשקי AIDL יש סטטוסים מובנים של שגיאות. במקום ליצור סוגים מותאמים אישית של סטטוסים, יוצרים מספרים של סטטוסים קבועים בקובצי ממשק ומשתמשים ב-
EX_SERVICE_SPECIFICב-CPP וב-NDK backends וב-ServiceSpecificExceptionב-Java backend. מידע נוסף על טיפול בשגיאות - AIDL לא מפעיל באופן אוטומטי מאגרי שרשורים כששולחים אובייקטים של Binder. צריך להפעיל אותם ידנית (ראו ניהול שרשורים).
- AIDL לא מבטלת את הפעולה במקרה של שגיאות לא בדוקות בהעברה (HIDL
Returnמבטלת את הפעולה במקרה של שגיאות לא בדוקות). - ב-AIDL אפשר להצהיר על סוג אחד בלבד לכל קובץ.
- אפשר לציין את הארגומנטים של AIDL כ-
in,outאוinoutבנוסף לפרמטר הפלט (אין קריאות חוזרות סינכרוניות). - ב-AIDL נעשה שימוש ב-
fdכסוג פרימיטיבי במקום ב-handle. - ב-HIDL נעשה שימוש בגרסאות ראשיות לשינויים לא תואמים ובגרסאות משניות לשינויים תואמים. ב-AIDL, שינויים שתואמים לאחור מתבצעים במקום.
ב-AIDL אין מושג מפורש של גרסאות ראשיות, אלא הוא משולב בשמות החבילות. לדוגמה, יכול להיות ש-AIDL ישתמש בשם החבילה
bluetooth2. - כברירת מחדל, AIDL לא מקבלת בירושה עדיפות בזמן אמת. כדי להפעיל את העברת העדיפות בזמן אמת, צריך להשתמש בפונקציה
setInheritRtלכל קובץ.
בדיקות של HAL
בקטע הזה מתוארות שיטות מומלצות לבדיקת HAL. השיטות האלה תקפות גם אם בדיקת השילוב של ה-HAL שלכם לא נמצאת ב-VTS.
מערכת Android מסתמכת על VTS כדי לאמת הטמעות צפויות של HAL. VTS עוזר לוודא ש-Android יכול להיות תואם לאחור להטמעות ישנות של ספקים. להטמעות שלא עוברות את VTS יש בעיות תאימות ידועות שעלולות למנוע את הפעולה שלהן בגרסאות עתידיות של מערכת ההפעלה.
יש שני חלקים עיקריים ב-VTS עבור HAL.
1. אימות של HAL במכשיר מוכר וצפוי על ידי Android
מערכת Android מסתמכת על רשימה סטטית ומדויקת של כל ממשקי HAL המותקנים. הרשימה הזו מופיעה במניפסט VINTF. בדיקות מיוחדות בכל הפלטפורמה מאמתות את התקינות של שכבות ה-HAL בכל המערכת. לפני שכותבים בדיקות ספציפיות ל-HAL, כדאי להריץ גם את הבדיקות האלה, כי הן יכולות להראות אם יש ל-HAL הגדרות VINTF לא עקביות.
אפשר למצוא את קבוצת הבדיקות הזו בכתובת test/vts-testcase/hal/treble/vintf. אם אתם עובדים על הטמעה של HAL של ספק, אתם יכולים להשתמש ב-vts_treble_vintf_vendor_test כדי לאמת אותה. אפשר להריץ את הבדיקה הזו באמצעות הפקודה atest vts_treble_vintf_vendor_test.
הבדיקות האלה אחראיות לאימות של:
- כל ממשק
@VintfStabilityשמוצהר במניפסט VINTF קפוא בגרסה ידועה שפורסמה. כך מוודאים ששני הצדדים של הממשק מסכימים על ההגדרה המדויקת של הגרסה הזו של הממשק. השלב הזה נחוץ לפעולה בסיסית. - כל ממשקי ה-HAL שמוצהרים במניפסט VINTF זמינים במכשיר. כל לקוח עם הרשאות מספיקות לשימוש בשירות HAL מוצהר חייב להיות מסוגל לקבל את השירותים האלה ולהשתמש בהם בכל שלב.
- כל ממשקי HAL שמוצהרים במניפסט VINTF מציגים את הגרסה של הממשק שהם מצהירים עליה במניפסט.
- לא מוגדרים במכשיר ממשקי HAL שיצאו משימוש. מערכת Android מפסיקה את התמיכה בגרסאות ישנות יותר של ממשקי HAL, כפי שמתואר במאמר בנושא מחזור החיים של FCM.
- ה-HAL הנדרשים קיימים במכשיר. חלק מ-HALs נדרשים כדי ש-Android יפעל כראוי.
2. אימות ההתנהגות הצפויה של כל HAL
לכל ממשק HAL יש בדיקות VTS משלו כדי לאמת את ההתנהגות הצפויה מהלקוחות שלו. מקרים הבדיקה מופעלים מול כל מופע של ממשק HAL מוצהר, ומכתיבים התנהגות ספציפית על סמך הגרסה של הממשק שהוטמעה.
ב-C++, אפשר לקבל רשימה של כל שכבות ה-HAL שמותקנות במערכת באמצעות הפונקציה android::getAidlHalInstanceNames ב-libaidlvintf_gtest_helper. ב-Rust, משתמשים ב-binder::get_declared_instances.
הבדיקות האלה מנסות לכסות כל היבט של הטמעת HAL שהמסגרת של Android מסתמכת עליו, או עשויה להסתמך עליו בעתיד.
הבדיקות האלה כוללות אימות של תמיכה בתכונות, טיפול בשגיאות וכל התנהגות אחרת שלקוח עשוי לצפות מהשירות.
אבני דרך של VTS לפיתוח HAL
כשיוצרים או משנים ממשקי HAL של Android, צריך לעדכן את בדיקות VTS (או כל בדיקה אחרת).
צריך לסיים את הבדיקות של VTS ולהכין אותן לאימות ההטמעות של הספקים לפני שהן קפאות לגרסאות של Android Vendor API. הם צריכים להיות מוכנים לפני שהממשקים קפואים, כדי שהמפתחים יוכלו ליצור את ההטמעות שלהם, לאמת אותן ולספק משוב למפתחי ממשקי ה-HAL.
בדיקה ב-Cuttlefish
כשאין חומרה זמינה, מערכת Android משתמשת ב-Cuttlefish ככלי פיתוח לממשקי HAL. כך אפשר לבצע בדיקות שילוב של Android בהיקף רחב.
hal_implementation_test בדיקות שמוודאות של-Cuttlefish יש הטמעות של הגרסאות העדכניות של ממשק HAL, כדי לוודא שמערכת Android מוכנה לטפל בממשקים החדשים, ושבדיקות VTS מוכנות לבדוק את ההטמעות החדשות של הספקים ברגע שחומרה ומכשירים חדשים יהיו זמינים.