تتضمّن كل واجهة محدّدة في حزمة 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);