واجهات

تحتوي كل واجهة محددة في حزمة 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 will return 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() خطأ). الطريقة الوحيدة للتعافي من هذا الفشل هي طلب مثيل جديد للخدمة عن طريق استدعاء 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);