לכל ממשק שמוגדר בחבילת HIDL יש מחלקה משלו של C++ שנוצרה אוטומטית במרחב השמות של החבילה. לקוחות ושרתים מטפלים בממשקים בדרכים שונות:
- שרתים מטמיעים ממשקים.
- שיטות קריאה של לקוחות בממשקים.
הממשקים יכולים להירשם לפי שם על ידי השרת או להעביר אותם בתור ל-methods שהוגדרו ל-HIDL. לדוגמה, קוד framework יכול להציג לקבל הודעות אסינכרוניות מ-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
(פרטים נוספים בהמשך)
או
- מעבירים את הטמעת הממשק כארגומנט של שיטת ממשק (לנתונים מפורטים, ראו אסינכרוני). קריאות חוזרות (callbacks)).
במהלך הרישום של הטמעת הממשק,
תהליך 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 returns 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)
מהממשק צריך להימשך (בירושה מ-)
x.y
).
בנוסף, אפשר להשתמש בשיטה castFrom
כדי להפעיל Cast בין
ממשקים שונים. השיטה הזו פועלת על ידי ביצוע קריאת IPC לשלט הרחוק
כדי לוודא שהסוג הבסיסי זהה לסוג
נדרש. אם הסוג המבוקש לא זמין, nullptr
הוא
הוחזרו.
sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService(); sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0);
קריאות חוזרות (callback) אסינכרוניות
הרבה הטמעות 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);