تحتوي كل واجهة محدّدة في حزمة 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) من الواجهة (يُكتسَب من)
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 1481، وwho
يساوي foo.
من الممكن أيضًا إلغاء تسجيل مستلم الوفاة بعد تسجيله:
foo->unlinkToDeath(recipient);