HIDL C++

ב-Android 8 בוצעה ארכיטקטורה מחדש של מערכת ההפעלה Android כדי להגדיר ממשקים ברורים בין פלטפורמת Android שאינה תלויה במכשיר לבין קוד ספציפי למכשיר ולספק. כבר מוגדרים ב-Android ממשקים רבים כאלה בצורת ממשקי HAL, שמוגדרים ככותרות C ב-hardware/libhardware. ‏HIDL מחליף את ממשקי ה-HAL האלה בממשקים יציבים עם גרסאות, שיכולים להיות ממשקי HIDL בצד הלקוח ובצד השרת ב-C++‎ (כפי שמתואר בהמשך) או ב-Java.

בדפים שבקטע הזה מתוארות הטמעות של ממשקי HIDL ב-C++, כולל פרטים על הקבצים שנוצרים באופן אוטומטי מקובצי ה-.hal של HIDL על ידי המהדר hidl-gen, על האופן שבו הקבצים האלה נארזים ועל האופן שבו משלבים את הקבצים האלה עם קוד ה-C++ שמשתמש בהם.

הטמעות בצד הלקוח ובצד השרת

לממשקי HIDL יש הטמעות של לקוח ושל שרת:

  • לקוח של ממשק HIDL הוא הקוד שמשתמש בממשק באמצעות קריאה לשיטות שלו.
  • שרת הוא הטמעה של ממשק HIDL שמקבל קריאות מלקוחות ומחזיר תוצאות (אם צריך).

במעבר מ-HAL של libhardware ל-HAL של HIDL, הטמעת ה-HAL הופכת לשרת והתהליך שמפעיל את ה-HAL הופך ללקוח. הטמעות ברירת המחדל יכולות לשרת גם HALs של העברה וגם HALs מצורפים, והן יכולות להשתנות עם הזמן:

איור 1. התקדמות הפיתוח של ממשקי HAL מדור קודם.

יצירת לקוח HAL

מתחילים בהכללת ספריות ה-HAL בקובץ ה-makefile:

  • יצרן: LOCAL_SHARED_LIBRARIES += android.hardware.nfc@1.0
  • Soong: ‏ shared_libs: [ …, android.hardware.nfc@1.0 ]

בשלב הבא, צריך לכלול את קובצי הכותרות של HAL:

#include <android/hardware/nfc/1.0/IFoo.h>

// in code:
sp<IFoo> client = IFoo::getService();
client->doThing();

יצירת שרת HAL

כדי ליצור את הטמעת ה-HAL, צריך את קובצי ה-.hal שמייצגים את ה-HAL, וכבר יצרתם קובצי make ל-HAL באמצעות -Lmakefile או -Landroidbp ב-hidl-gen (./hardware/interfaces/update-makefiles.sh עושה זאת לקובצי HAL פנימיים והוא מקור טוב למידע). כשמעבירים HALs מ-libhardware, אפשר לבצע הרבה מהעבודה הזו בקלות באמצעות c2hal.

כדי ליצור את הקבצים הנדרשים להטמעת ה-HAL:

PACKAGE=android.hardware.nfc@1.0
LOC=hardware/interfaces/nfc/1.0/default/
m -j hidl-gen
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE

כדי שה-HAL יפעל במצב העברה, הפונקציה HIDL_FETCH_IModuleName צריכה להיות ב-/(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so, כאשר OPTIONAL_IDENTIFIER היא מחרוזת שמזהה את הטמעת העברה. הפקודות שלמעלה עומדות בדרישות של מצב העברה אוטומטית, ויוצרות גם את היעד android.hardware.nfc@1.0-impl, אבל אפשר להשתמש בכל תוסף. לדוגמה, android.hardware.nfc@1.0-impl-foo משתמש ב--foo כדי להבדיל את עצמו.

אם HAL הוא גרסה משנית או תוסף של HAL אחר, צריך להשתמש ב-HAL הבסיסי כדי לתת שם לקובץ הבינארי הזה. לדוגמה, הטמעות של android.hardware.graphics.mapper@2.1 עדיין צריכות להיות בקובץ בינארי שנקרא android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER). בדרך כלל, הערך של OPTIONAL_IDENTIFIER יכלול את גרסת ה-HAL בפועל. כך, לקוחות 2.0 יכולים לאחזר את הקובץ הבינארי ישירות, ולקוחות 2.1 יכולים לבצע שדרוג של ההטמעה.

בשלב הבא, ממלאים את ה-stubs בפונקציונליות ומגדירים דימון (daemon). קוד לדוגמה של דימון (עם תמיכה בהעברה (passthrough)):

#include <hidl/LegacySupport.h>

int main(int /* argc */, char* /* argv */ []) {
    return defaultPassthroughServiceImplementation<INfc>("nfc");
}

defaultPassthroughServiceImplementation קורא ל-dlopen() עבור ספריית -impl שסופקה ומספק אותה כשירות ב-binder. קוד דימון לדוגמה (לשירות ב-binder בלבד):

int main(int /* argc */, char* /* argv */ []) {
    // This function must be called before you join to ensure the proper
    // number of threads are created. The threadpool never exceeds
    // size one because of this call.
    ::android::hardware::configureRpcThreadpool(1 /*threads*/, true /*willJoin*/);

    sp<INfc> nfc = new Nfc();
    const status_t status = nfc->registerAsService();
    if (status != ::android::OK) {
        return 1; // or handle error
    }

    // Adds this thread to the threadpool, resulting in one total
    // thread in the threadpool. We could also do other things, but
    // would have to specify 'false' to willJoin in configureRpcThreadpool.
    ::android::hardware::joinRpcThreadpool();
    return 1; // joinRpcThreadpool should never return
}

הדימון הזה נמצא בדרך כלל ב-$PACKAGE + "-service-suffix" (לדוגמה, android.hardware.nfc@1.0-service), אבל הוא יכול להיות בכל מקום. sepolicy של סוג ספציפי של HAL הוא המאפיין hal_<module> (לדוגמה, hal_nfc). צריך להחיל את המאפיין הזה על הדימון שמפעיל HAL מסוים (אם אותו תהליך משרת כמה HALs, אפשר להחיל עליו כמה מאפיינים).