ממשקים

לכל ממשק המוגדר בחבילת 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();
    }
    ...
};

כדי להפוך את היישום של ממשק שרת לזמין ללקוח, אתה יכול:

  1. רשום את יישום הממשק עם hwservicemanager (ראה פרטים למטה),

    אוֹ

  2. העבר את יישום הממשק כארגומנט של שיטת ממשק (לפרטים, ראה התקשרות א-סינכרונית ).

בעת רישום יישום הממשק, תהליך 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);