هر رابط تعریف شده در بسته 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(); } ... };
برای در دسترس قرار دادن یک رابط سرور برای مشتری، می توانید:
- پیاده سازی رابط را با
hwservicemanagerثبت کنید (به جزئیات زیر مراجعه کنید)،
یا - پیاده سازی رابط را به عنوان آرگومان یک روش واسط ارسال کنید (برای جزئیات، به تماس های ناهمزمان مراجعه کنید).
هنگام ثبت پیادهسازی رابط، فرآیند 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) یک رابط باید گسترش یابد (از) 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() است که از یک رشته در Threadpool 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 برابر با 1481 و who برابر با foo است.
همچنین امکان لغو ثبت نام گیرنده فوت پس از ثبت نام وجود دارد:
foo->unlinkToDeath(recipient);