ב-Android 11 נוספה האפשרות להשתמש ב-AIDL ל-HALs ב-Android. כך אפשר להטמיע חלקים מ-Android בלי HIDL. כשהדבר אפשרי, כדאי להעביר את ה-HALs לשימוש ב-AIDL בלבד (כש-HALs במקור משתמשים ב-HIDL, צריך להשתמש ב-HIDL).
ב-HALs שמשתמשים ב-AIDL כדי לתקשר בין רכיבי המסגרת, כמו אלה שמפורטים ב-system.img
, לבין רכיבי החומרה, כמו אלה שמפורטים ב-vendor.img
, צריך להשתמש ב-Stable AIDL. עם זאת, כדי לתקשר בתוך מחיצה, למשל מ-HAL אחד ל-HAL אחר, אין הגבלה על מנגנון ה-IPC שבו משתמשים.
מוטיבציה
ה-AIDL קיים כבר הרבה יותר זמן מה-HIDL, והוא משמש במקומות רבים אחרים, למשל בין רכיבי מסגרת Android או באפליקציות. עכשיו, כשיש ל-AIDL תמיכה יציבה, אפשר להטמיע סטאק שלם עם סביבת זמן ריצה יחידה של IPC. ב-AIDL יש גם מערכת ניהול גרסאות טובה יותר מאשר ב-HIDL.
- שימוש בשפה אחת של IPC מאפשר ללמוד, לנפות באגים, לבצע אופטימיזציה ולאבטח רק דבר אחד.
- ב-AIDL יש תמיכה בניהול גרסאות במקום לבעלי ממשק:
- הבעלים יכולים להוסיף שיטות לסוף ממשקים, או שדות ל-Parcelables. המשמעות היא שקל יותר ליצור גרסאות של קוד לאורך השנים, וגם שהעלות השנתית נמוכה יותר (אפשר לשנות את הסוגים במקום ולא צריך ספריות נוספות לכל גרסה של הממשק).
- אפשר לצרף ממשקי תוספים במהלך זמן הריצה במקום במערכת הסוגים, כך שאין צורך לבסס מחדש תוספים במורד הזרם לגרסאות חדשות יותר של ממשקים.
- אפשר להשתמש בממשק AIDL קיים ישירות כשהבעלים שלו בוחרים לבצע לו יציבות. בעבר, היה צריך ליצור עותק שלם של הממשק ב-HIDL.
פיתוח גרסה מבוססת-זמן ריצה של AIDL
ל-AIDL יש שלושה קצוות עורפי שונים: Java, NDK ו-CPP. כדי להשתמש ב-Stable AIDL, תמיד צריך להשתמש בעותק המערכת של libbinder ב-system/lib*/libbinder.so
ולדבר ב-/dev/binder
. בקוד בתמונת הספק, המשמעות היא שאי אפשר להשתמש ב-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 לפני שהוא נשלח לתהליך אחר. לא ניתן לשדרג לאחור שירות ליציבות של ספק ב-Java, כי כל האפליקציות פועלות בהקשר של מערכת.
בנוסף, כדי לשפר את יכולת ההעברה של הקוד ולמנוע בעיות פוטנציאליות כמו ספריות נוספות מיותרות, צריך להשבית את הקצה העורפי של CPP.
שימו לב שהשימוש ב-backends
בדוגמת הקוד שבהמשך נכון, כי יש שלושה קצוות עורפיים (Java, NDK ו-CPP). הקוד הבא מראה איך לבחור את הקצה העורפי של CPP באופן ספציפי כדי להשבית אותו.
aidl_interface: {
...
backends: {
cpp: {
enabled: false,
},
},
}
חיפוש ממשקי HAL של AIDL
ממשקי AIDL יציבים של AOSP ל-HAL נמצאים באותן ספריות בסיס כמו ממשקי HIDL, בתיקיות aidl
.
- חומרה/ממשקים: לממשקים שספק החומרה בדרך כלל מספק
- frameworks/hardware/interfaces: לממשקים ברמה גבוהה שסופקו לחומרה
- system/hardware/interfaces: לממשקים ברמה נמוכה שסופקו לחומרה
צריך להציב ממשקי תוספים בספריות משנה אחרות של hardware/interfaces
ב-vendor
או ב-hardware
.
ממשקי תוספים
לכל גרסה של Android יש קבוצה של ממשקי AOSP רשמיים. כששותפי Android רוצים להוסיף פונקציונליות לממשקים האלה, הם לא צריכים לשנות אותם ישירות, כי המשמעות היא שסביבת זמן הריצה של Android שלהם לא תהיה תואמת לסביבת זמן הריצה של Android ב-AOSP. במכשירי GMS, הימנעות משינוי הממשקים האלה היא גם הדרך להבטיח שתמונת ה-GSI תמשיך לפעול.
תוכלו לרשום תוספים בשתי דרכים שונות:
- בזמן הריצה, ראו תוספים מצורפים.
- ניהול עצמאי, רשום ברחבי העולם וב-VINTF.
לא משנה איך התוסף רשום, כשרכיבים ספציפיים לספק (כלומר לא חלק מ-AOSP במקור) משתמשים בממשק, אין אפשרות לסכסוך מיזוג. עם זאת, כשמבצעים שינויים במורד הזרם לרכיבי AOSP במקור, יכולים להתרחש קונפליקטים במיזוג. מומלץ להשתמש באסטרטגיות הבאות:
- אפשר להעביר את התוספות לממשק ל-AOSP בגרסה הבאה
- תוספות לממשק שמאפשרות גמישות נוספת, ללא התנגשויות במיזוג, תוכלו להעביר ל-upstream במהדורה הבאה
רכיבי Parcelable של התוסף: ParcelableHolder
ParcelableHolder
הוא Parcelable
שיכול להכיל Parcelable
אחר.
התרחיש לדוגמה העיקרי של ParcelableHolder
הוא להפוך Parcelable
למתרחיב.
לדוגמה, קובץ אימג' שמטמיעים של מכשירים מצפים שיוכלו להרחיב את Parcelable
, AospDefinedParcelable
שהוגדרו ב-AOSP, כך שיכלול את התכונות הנוספות שלהם.
בעבר, בלי ParcelableHolder
, מפתחי המכשירים לא יכלו לשנות ממשק AIDL יציב שהוגדר ב-AOSP, כי הוספת שדות נוספים הייתה נחשבת לשגיאה:
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
על סמך קובצי.hal
של החבילה הנתונה - יוצרים כללי build לחבילת ה-AIDL החדשה עם כל הקצוות העורפי מופעלים
- יצירת שיטות תרגום בקצוות העורפי של Java, CPP ו-NDK כדי לתרגם בין סוגי HIDL לסוגי AIDL
- יצירת כללי build לספריות תרגום עם יחסי התלות הנדרשים
- יצירת טענות נכוֹנוּת (asserts) סטטיות כדי לוודא שלמְמַפְרֵרים של HIDL ו-AIDL יש את אותם ערכים בקצוות העורפי של CPP ו-NDK
כדי להמיר חבילה של קובצי .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
.
יוצרים רק את היעדים הנחוצים.
- משביתים קצוות עורפיים שלא נעשה בהם שימוש. עדיפות לקצה העורפי של NDK על פני הקצה העורפי של CPP. מידע נוסף זמין בקטע בחירת סביבת זמן ריצה.
- מסירים ספריות תרגום או כל קוד שנוצר מהן ולא יימצא בשימוש.
הבדלים עיקריים בין AIDL ל-HIDL
- בדרך כלל, שימוש ב-
Status
ובחריגים המובנים של AIDL משפרים את הממשק ומבטלים את הצורך בסוג סטטוס נוסף שספציפי לממשק. - ארגומנטים של ממשק AIDL בשיטות הם לא
@nullable
כברירת מחדל, כמו שהם היו ב-HIDL.
- בדרך כלל, שימוש ב-
SEPolicy ל-HAL של 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
(ב-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
לכל הקשרי השירות שלנו. רוב ה-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)
ממשקי תוספים מצורפים
אפשר לצרף תוסף לכל ממשק של מקשר, בין אם מדובר בממשק ברמה העליונה שמירשם ישירות בשירות הניהול ובין אם מדובר בממשק משנה. כשמקבלים תוסף, צריך לוודא שהסוג שלו תואם לציפיות. אפשר להגדיר תוספים רק מהתהליך שמגיש את ה-binder.
צריך להשתמש בתוספים מצורפים בכל פעם שתוסף משנה את הפונקציונליות של HAL קיים. כשצריך פונקציונליות חדשה לגמרי, אין צורך להשתמש במנגנון הזה, וניתן לרשום ממשק תוסף ישירות במנהל השירות. ממשקי התוספים המצורפים הכי שימושיים כשהם מצורפים לממשקי משנה, כי ההיררכיות האלה יכולות להיות עמוקות או עם כמה מופעים. שימוש בתוסף גלובלי כדי לשקף את היררכיית הממשק של ה-binder בשירות אחר ידרוש ניהול חשבונות נרחב כדי לספק פונקציונליות זהה לתוספים שמצורפים ישירות.
כדי להגדיר תוסף ב-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 בנוסף לפרמטר הפלט (אין "קריאות חזרה אסינכרניות").
- ב-AIDL נעשה שימוש ב-fd כסוג פרימיטיבי במקום ב-handle.
- ב-HIDL נעשה שימוש בגרסאות ראשיות לשינויים לא תואמים ובגרסאות משניות לשינויים תואמים. ב-AIDL, שינויים שתואמים לאחור מתבצעים במקום.
ב-AIDL אין מושג מפורש של גרסאות ראשיות, אלא הוא משולב בשמות החבילות. לדוגמה, AIDL עשוי להשתמש בשם החבילה
bluetooth2
. - כברירת מחדל, AIDL לא יורש עדיפות בזמן אמת. צריך להשתמש בפונקציה
setInheritRt
לכל קישור כדי לאפשר ירושה של תעדוף בזמן אמת.
בדיקות של חבילה לבדיקת ספקים (VTS) עבור ממשקי HAL
מערכת Android מסתמכת על חבילת בדיקות של ספקים (VTS) כדי לאמת הטמעות HAL צפויות. בעזרת VTS אפשר לוודא שמערכת Android תהיה תואמת לאחור להטמעות ישנות של ספקים. להטמעות שלא עוברות את בדיקת VTS יש בעיות תאימות ידועות שעלולות למנוע מהן לפעול בגרסאות עתידיות של מערכת ההפעלה.
ל-VTS ל-HAL יש שני חלקים עיקריים.
1. מוודאים שה-HALs במכשיר מוכרים ל-Android ומתאימים לו.
קבוצת הבדיקות הזו נמצאת ב-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 מוכנות לבדוק את ההטמעות החדשות של הספקים ברגע שחומרה ומכשירים חדשים יהיו זמינים.