واجهات

تتضمّن كل واجهة محدّدة في حزمة 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. اجتياز تنفيذ الواجهة كوسيطة طريقة واجهة (للبيانات، راجع غير متزامن عمليات معاودة الاتصال).

عند تسجيل تنفيذ الواجهة، تتتبّع عملية "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);