HIDL C++‎

‫Android 8 معماری سیستم‌عامل Android را بازسازی می‌کند تا میاناهای واضحی بین پلاتفرم Android مستقل از دستگاه و کد مختص دستگاه و فروشنده تعریف کند. ‫Android ازقبل بسیاری از این رابط‌ها را در قالب رابط‌های HAL تعریف می‌کند، که به‌عنوان سرایندهای C در hardware/libhardware تعریف شده‌اند. ‫HIDL این میاناهای HAL را با میاناهای پایدار و نسخه‌بندی‌شده جایگزین می‌کند که می‌توانند میاناهای HIDL سمت کارخواه و سمت سرور در C++ (توضیحات در زیر) یا Java باشند.

صفحه‌های این بخش پیاده‌سازی‌های C++‎ از میاناهای HIDL را شرح می‌دهند، ازجمله جزئیات مربوط به فایل‌های تولیدشده خودکار از فایل‌های HIDL .hal توسط کامپایلر hidl-gen، نحوه بسته‌بندی این فایل‌ها، و نحوه ادغام این فایل‌ها با کد C++‎ که از آن‌ها استفاده می‌کند.

پیاده‌سازی‌های کارخواه و سرور

واسط‌های HIDL پیاده‌سازی‌های کارخواه و سرور دارند:

  • کاربر میانای HIDL کدی است که با فراخوانی روش‌های آن از میانای استفاده می‌کند.
  • سرور پیاده‌سازی یک میانای HIDL است که تماس‌ها را از مشتریان دریافت می‌کند و نتایج را (درصورت نیاز) برمی‌گرداند.

در انتقال از libhardware HAL به HIDL HAL، پیاده‌سازی HAL به سرور تبدیل می‌شود و فرایند فراخوانی به HAL به کلاینت تبدیل می‌شود. پیاده‌سازی‌های پیش‌فرض می‌توانند هم HALهای عبوری و هم HALهای متصل‌شده را ارائه دهند و ممکن است با گذشت زمان تغییر کنند:

شکل ۱. پیشرفت توسعه برای 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 شما باشد و قبلاً بااستفاده از -Lmakefile یا -Landroidbp در hidl-gen برای HAL خود فایل‌های ساخت تولید کرده باشید (./hardware/interfaces/update-makefiles.sh این کار را برای فایل‌های داخلی HAL انجام می‌دهد و مرجع خوبی است). هنگام انتقال از HALهای 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 می‌شود. با نام‌گذاری باینری به این شکل، مشتریان نسخه ۲.۰ می‌توانند آن را مستقیماً بازیابی کنند، و مشتریان نسخه ۲.۱ می‌توانند پیاده‌سازی را ارتقا دهند.

سپس، جای خالی‌ها را با عملکرد پر کنید و یک دیمون راه‌اندازی کنید. نمونه کد کارگزار (پشتیبانی از انتقال):

#include <hidl/LegacySupport.h>

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

‫defaultPassthroughServiceImplementation برای کتابخانه -impl ارائه‌شده تماس می‌گیرد dlopen() و آن را به‌عنوان سرویس دسته‌بندی‌شده ارائه می‌دهد. کد دیمون نمونه (برای سرویس خالص بایندرشده):

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 خاصی را اجرا می‌کند اعمال شود (اگر فرایند یکسانی به چندین HAL سرویس می‌دهد، چندین مشخصه را می‌توان بر آن اعمال کرد).