میانه‌ها

هر واسط تعریف‌شده در بسته 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. پیاده‌سازی میانای را به‌عنوان آرگومان روش میانای عبور دهید (برای جزئیات، بازخوان‌های ناهم‌زمان را ببینید).

هنگام ثبت پیاده‌سازی میانای HIDL، 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 remoting) اجرا شود. چون مشتری getService را در شیء IFoo که از نسخه 1.0 بسته اضافه شده است فراخوانده است، hwservicemanager پیاده‌سازی سرور را فقط درصورتی برمی‌گرداند که آن پیاده‌سازی با مشتریان 1.0 سازگار باشد. در عمل، این یعنی فقط پیاده‌سازی‌های سرور با نسخه 1.n (نسخه x.(y+1) از یک میانای باید گسترش یابد (از x.y به ارث برسد)).

علاوه‌براین، روش 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 حاوی کوکی‌ای است که با linkToDeath() ارسال شده است، درحالی‌که پارامتر who حاوی اشاره‌گر ضعیفی به شیء نشان‌دهنده سرویس در کارخواه است. با تماس نمونه‌ای که در بالا ارائه شده است، cookie برابر با ۱۴۸۱ و who برابر با foo است.

همچنین می‌توانید گیرنده فوت‌شده را پس‌از ثبت کردن لغو ثبت کنید:

foo->unlinkToDeath(recipient);