HIDL C++

يعيد نظام التشغيل Android 8 تصميم نظام التشغيل لتحديد واجهات واضحة بين منصّة Android المستقلة عن الجهاز والرمز البرمجي الخاص بالجهاز والمورّد. يحدّد نظام التشغيل Android العديد من هذه الواجهات في شكل واجهات HAL، ويحدّدها على أنّها رؤوس C في hardware/libhardware. تستبدل HIDL واجهات HAL هذه بواجهات ثابتة ومُحدَّدة الإصدار، والتي يمكن أن تكون واجهات HIDL من جانب العميل والخادم في C++ (الموضَّحة أدناه) أو Java.

توضّح الصفحات في هذا القسم عمليات تنفيذ واجهات HIDL باستخدام C++، بما في ذلك تفاصيل عن الملفات التي يتم إنشاؤها تلقائيًا من ملفات HIDL .hal من خلال المُجمِّع hidl-gen، وكيفية تجميع هذه الملفات، وكيفية دمج هذه الملفات مع رمز C++ الذي يستخدمها.

عمليات تنفيذ العميل والخادم

تحتوي واجهات HIDL على تطبيقات للعملاء والخوادم:

  • العميل لواجهة HIDL هو الرمز الذي يستخدم الواجهة من خلال استدعاء طرقها.
  • الخادم هو عملية تنفيذ لواجهة HIDL التي تتلقّى طلبات من العملاء وتُعرِض النتائج (إذا لزم الأمر).

عند الانتقال من واجهات برمجة التطبيقات libhardware HAL إلى واجهات برمجة التطبيقات HIDL HAL، يصبح تنفيذ HAL هو الخادم وتصبح العملية التي تستدعي HAL هي العميل. يمكن أن توفّر عمليات التنفيذ التلقائية كلّ من نماذج HAL المُدارة وHAL المُدارة باستخدام الربط، ويمكن أن تتغيّر بمرور الوقت:

الشكل 1: مستوى التقدّم في تطوير واجهات برمجة التطبيقات القديمة لأجهزة الاستشعار

إنشاء عميل HAL

ابدأ بتضمين مكتبات HAL في ملف makefile:

  • العلامة التجارية: LOCAL_SHARED_LIBRARIES += android.hardware.nfc@1.0
  • سونغ: shared_libs: [ …, android.hardware.nfc@1.0 ]

بعد ذلك، أدرِج ملفات HAL الخاصة بالعناوين:

#include <android/hardware/nfc/1.0/IFoo.h>

// in code:
sp<IFoo> client = IFoo::getService();
client->doThing();

إنشاء خادم HAL

لإنشاء عملية تنفيذ HAL، يجب أن يكون لديك ملفات .hal التي تمثّل HAL وأن تكون قد أنشأت ملفات makefiles لـ HAL باستخدام -Lmakefile أو -Landroidbp على hidl-gen (يُجري ./hardware/interfaces/update-makefiles.sh ذلك لملفات HAL الداخلية وهو مرجع جيد). عند نقل ملفات HAL من libhardware، يمكنك تنفيذ الكثير من هذا العمل بسهولة باستخدام c2hal.

لإنشاء الملفات اللازمة لتنفيذ HAL:

PACKAGE=android.hardware.nfc@1.0
LOC=hardware/interfaces/nfc/1.0/default/
m -j hidl-gen
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE

لكي يعمل HAL في وضع "النقل المباشر"، يجب أن تكون الدالة HIDL_FETCH_IModuleName موجودة في /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so حيث يكون OPTIONAL_IDENTIFIER سلسلة تحدِّد تنفيذ النقل المباشر. يتم استيفاء متطلبات وضع "النقل المباشر" تلقائيًا من خلال الطلبات المذكورة أعلاه، والتي تنشئ أيضًا الهدف android.hardware.nfc@1.0-impl ، ولكن يمكن استخدام أي إضافة. على سبيل المثال، تستخدم android.hardware.nfc@1.0-impl-foo -foo لتمييز نفسها.

إذا كان HAL هو إصدار ثانوي أو إضافةً إلى HAL آخر، يجب استخدام HAL الأساسي لتسمية هذا الملف الثنائي. على سبيل المثال، يجب أن تظلّ عمليات تنفيذ android.hardware.graphics.mapper@2.1 في ملف ثنائي يُسمى android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER). عادةً ما يتضمّن OPTIONAL_IDENTIFIER هنا الإصدار الفعلي من HAL. من خلال تسمية الملف الثنائي على هذا النحو، يمكن لعملاء الإصدار 2.0 استرجاعه مباشرةً، ويمكن لعملاء الإصدار 2.1 ترقية التنفيذ.

بعد ذلك، املأ العناصر النائبة بالوظائف وإعداد الخادم الدائم. مثال على رمز الخادم (يتوافق مع وضع "المرور المباشر"):

#include <hidl/LegacySupport.h>

int main(int /* argc */, char* /* argv */ []) {
    return defaultPassthroughServiceImplementation<INfc>("nfc");
}

يطلب defaultPassthroughServiceImplementation dlopen() مكتبة -impl المقدَّمة ويقدّمها كهيأة خدمة مرتبطة. مثال على رمز الخادم الدائم (للخدمة التي تستخدم Binder فقط):

int main(int /* argc */, char* /* argv */ []) {
    // This function must be called before you join to ensure the proper
    // number of threads are created. The threadpool never exceeds
    // size one because of this call.
    ::android::hardware::configureRpcThreadpool(1 /*threads*/, true /*willJoin*/);

    sp<INfc> nfc = new Nfc();
    const status_t status = nfc->registerAsService();
    if (status != ::android::OK) {
        return 1; // or handle error
    }

    // Adds this thread to the threadpool, resulting in one total
    // thread in the threadpool. We could also do other things, but
    // would have to specify 'false' to willJoin in configureRpcThreadpool.
    ::android::hardware::joinRpcThreadpool();
    return 1; // joinRpcThreadpool should never return
}

عادةً ما يكون هذا الخادم الدائم في $PACKAGE + "-service-suffix" (مثل android.hardware.nfc@1.0-service)، ولكن يمكن أن يكون في أي مكان. سمة sepolicy لفئة معيّنة من HAL هي السمة hal_<module> (على سبيل المثال، hal_nfc). يجب تطبيق هذه السمة على الخادم الدائم الذي يشغِّل واجهة HAL معيّنة (إذا كانت العملية نفسها توفّر واجهات HAL متعددة، يمكن تطبيق سمات متعددة عليها).