לכל ממשק המוגדר בחבילת HIDL יש מחלקה C++ שנוצרה אוטומטית בתוך מרחב השמות של החבילה שלו. לקוחות ושרתים מתמודדים עם ממשקים בדרכים שונות:
- שרתים מיישמים ממשקים.
- לקוחות קוראים לשיטות על ממשקים.
ניתן לרשום ממשקים לפי שם על ידי השרת או להעביר כפרמטרים לשיטות מוגדרות HIDL. לדוגמה, קוד מסגרת עשוי לשרת ממשק לקבלת הודעות אסינכרוניות מה-HAL ולהעביר ממשק זה ישירות ל-HAL מבלי לרשום אותו.
הטמעת שרת
שרת המיישם את ממשק IFoo
חייב לכלול את קובץ הכותרת IFoo
שנוצר אוטומטית:
#include <android/hardware/samples/1.0/IFoo.h>
הכותרת מיוצאת אוטומטית על ידי הספרייה המשותפת של ממשק IFoo
לקישור. דוגמה IFoo.hal
:
// IFoo.hal interface IFoo { someMethod() generates (vec<uint32_t>); ... }
דוגמה שלד עבור הטמעת שרת של ממשק IFoo:
// From the IFoo.h header using android::hardware::samples::V1_0::IFoo; class FooImpl : public IFoo { Return<void> someMethod(foo my_foo, someMethod_cb _cb) { vec<uint32_t> return_data; // Compute return_data _cb(return_data); return Void(); } ... };
כדי להפוך את היישום של ממשק שרת לזמין ללקוח, אתה יכול:
- רשום את יישום הממשק עם
hwservicemanager
(ראה פרטים למטה),
אוֹ - העבר את יישום הממשק כארגומנט של שיטת ממשק (לפרטים, ראה התקשרות א-סינכרונית ).
בעת רישום יישום הממשק, תהליך hwservicemanager
עוקב אחר ממשקי HIDL רשומים הפועלים במכשיר לפי שם וגרסה. שרתים יכולים לרשום מימוש ממשק HIDL לפי שם ולקוחות יכולים לבקש הטמעות שירות לפי שם וגרסה. תהליך זה משרת את ממשק HIDL android.hidl.manager@1.0::IServiceManager
.
לכל קובץ כותרת ממשק HIDL שנוצר באופן אוטומטי (כגון IFoo.h
) יש שיטה registerAsService()
שניתן להשתמש בה כדי לרשום את מימוש הממשק עם hwservicemanager
. הארגומנט היחיד הנדרש הוא השם של יישומי הממשק שכן לקוחות ישתמשו בשם זה כדי לאחזר את הממשק מה- hwservicemanager
מאוחר יותר:
::android::sp<IFoo> myFoo = new FooImpl(); ::android::sp<IFoo> mySecondFoo = new FooAnotherImpl(); status_t status = myFoo->registerAsService(); status_t anotherStatus = mySecondFoo->registerAsService("another_foo");
ה- hwservicemanager
מתייחס לשילוב של [package@version::interface, instance_name]
כייחודי כדי לאפשר ממשקים שונים (או גרסאות שונות של אותו ממשק) להירשם עם שמות מופעים זהים ללא התנגשויות. אם אתה קורא ל- registerAsService()
עם אותה גרסת חבילה, ממשק ושם מופע בדיוק, ה- hwservicemanager
מסיר את ההפניה שלו לשירות שנרשם קודם לכן ומשתמש בחדש.
יישום לקוח
בדיוק כפי שעושה השרת, לקוח חייב #include
כל ממשק שאליו הוא מתייחס:
#include <android/hardware/samples/1.0/IFoo.h>
לקוח יכול להשיג ממשק בשתי דרכים:
- דרך
I<InterfaceName>::getService
(דרךhwservicemanager
) - באמצעות שיטת ממשק
לכל קובץ כותרת ממשק שנוצר אוטומטית יש שיטת getService
סטטית שניתן להשתמש בה כדי לאחזר מופע שירות מה- hwservicemanager
:
// getService will return nullptr if the service can't be found sp<IFoo> myFoo = IFoo::getService(); sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");
כעת ללקוח יש ממשק IFoo
, והוא יכול לקרוא לו שיטות כאילו היה מימוש מחלקה מקומית. במציאות, ההטמעה עשויה לפעול באותו תהליך, בתהליך אחר, או אפילו במכשיר אחר (עם שלט של HAL). מכיוון שהלקוח קרא getService
על אובייקט IFoo
שנכלל מגרסה 1.0
של החבילה, ה- hwservicemanager
מחזיר מימוש שרת רק אם המימוש הזה תואם ללקוחות 1.0
. בפועל, זה אומר שרק יישומי שרת עם גרסה 1.n
(גרסה x.(y+1)
של ממשק חייבת להאריך (לירשת) xy
).
בנוסף השיטה castFrom
מסופקת כדי להטיל בין ממשקים שונים. שיטה זו פועלת על ידי ביצוע קריאת IPC לממשק המרוחק כדי לוודא שהסוג הבסיסי זהה לסוג המבוקש. אם הסוג המבוקש אינו זמין, אזי nullptr
מוחזר.
sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService(); sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0);
התקשרות א-סינכרונית
יישומי HAL קיימים רבים מדברים עם חומרה אסינכרונית, מה שאומר שהם צריכים דרך אסינכרונית להודיע ללקוחות על אירועים חדשים שהתרחשו. ניתן להשתמש בממשק HIDL כהתקשרות חוזרת אסינכרונית מכיוון שפונקציות ממשק HIDL יכולות לקחת אובייקטי ממשק HIDL כפרמטרים.
קובץ ממשק לדוגמה IFooCallback.hal
:
package android.hardware.samples@1.0; interface IFooCallback { sendEvent(uint32_t event_id); sendData(vec<uint8_t> data); }
דוגמה לשיטה חדשה ב- IFoo
שלוקחת פרמטר IFooCallback
:
package android.hardware.samples@1.0; interface IFoo { struct Foo { int64_t someValue; handle myHandle; }; someMethod(Foo foo) generates (int32_t ret); anotherMethod() generates (vec<uint32_t>); registerCallback(IFooCallback callback); };
הלקוח המשתמש בממשק IFoo
הוא השרת של ממשק IFooCallback
; הוא מספק יישום של IFooCallback
:
class FooCallback : public IFooCallback { Return<void> sendEvent(uint32_t event_id) { // process the event from the HAL } Return<void> sendData(const hidl_vec<uint8_t>& data) { // process data from the HAL } };
זה גם יכול פשוט להעביר את זה על פני מופע קיים של ממשק IFoo
:
sp<IFooCallback> myFooCallback = new FooCallback(); myFoo.registerCallback(myFooCallback);
השרת המיישם את IFoo
מקבל זאת כאובייקט sp<IFooCallback>
. זה יכול לאחסן את ההתקשרות חזרה, ולהתקשר בחזרה ללקוח בכל פעם שהוא רוצה להשתמש בממשק זה.
מקבלי מוות
מכיוון שהטמעות שירות יכולות לרוץ בתהליך אחר, יכול לקרות שהתהליך המיישם ממשק מת בזמן שהלקוח נשאר בחיים. כל קריאה לאובייקט ממשק המתארח בתהליך שמת, תיכשל עם שגיאת העברה ( isOK()
יחזיר false). הדרך היחידה להתאושש מכשל כזה היא לבקש מופע חדש של השירות על ידי קריאה ל- I<InterfaceName>::getService()
. זה עובד רק אם התהליך שקרס הופעל מחדש ורשום מחדש את השירותים שלו אצל servicemanager
(מה שנכון בדרך כלל עבור יישומי HAL).
במקום להתמודד עם זה באופן ריאקטיבי, לקוחות של ממשק יכולים לרשום גם נמען מוות כדי לקבל הודעה כאשר שירות מת. כדי להירשם להודעות כאלה בממשק IFoo
שאוחזר, לקוח יכול לעשות את הפעולות הבאות:
foo->linkToDeath(recipient, 1481 /* cookie */);
פרמטר recipient
חייב להיות מימוש של ממשק android::hardware::hidl_death_recipient
המסופק על ידי HIDL, המכיל שיטה אחת serviceDied()
שתיקרא משרשור במאגר ה-RPC כאשר התהליך המארח את הממשק ימות:
class MyDeathRecipient : public android::hardware::hidl_death_recipient { virtual void serviceDied(uint64_t cookie, const android::wp<::android::hidl::base::V1_0::IBase>& who) { // Deal with the fact that the service died } }
פרמטר ה- cookie
מכיל את ה-cookie שהועבר עם linkToDeath()
, בעוד שהפרמטר who
מכיל מצביע חלש לאובייקט המייצג את השירות בלקוח. עם השיחה לדוגמה שניתנה למעלה, cookie
שווה 1481, who
שווה foo
.
אפשר גם לבטל רישום של מקבל מוות לאחר רישום זה:
foo->unlinkToDeath(recipient);