الخدمات & نقل البيانات

يصف هذا القسم كيفية تسجيل الخدمات واكتشافها وكيفية إرسال البيانات إلى الخدمة عن طريق استدعاء الأساليب المحددة في الواجهات في ملفات .hal .

تسجيل الخدمات

يمكن تسجيل خوادم واجهة HIDL (الكائنات التي تنفذ الواجهة) كخدمات مسماة. لا يلزم أن يكون الاسم المسجل مرتبطًا بالواجهة أو اسم الحزمة. إذا لم يتم تحديد اسم، فسيتم استخدام الاسم "الافتراضي"؛ يجب استخدام هذا لـ HALs التي لا تحتاج إلى تسجيل تطبيقين لنفس الواجهة. على سبيل المثال، استدعاء C++ لتسجيل الخدمة المحدد في كل واجهة هو:

status_t status = myFoo->registerAsService();
status_t anotherStatus = anotherFoo->registerAsService("another_foo_service");  // if needed

يتم تضمين إصدار واجهة HIDL في الواجهة نفسها. يرتبط تلقائيًا بتسجيل الخدمة ويمكن استرجاعه عبر استدعاء الأسلوب ( android::hardware::IInterface::getInterfaceVersion() ) على كل واجهة HIDL. لا يلزم تسجيل كائنات الخادم ويمكن تمريرها عبر معلمات أسلوب HIDL إلى عملية أخرى ستجري استدعاءات أسلوب HIDL إلى الخادم.

اكتشاف الخدمات

يتم إجراء الطلبات بواسطة رمز العميل لواجهة معينة حسب الاسم والإصدار، واستدعاء getService على فئة HAL المطلوبة:

// C++
sp<V1_1::IFooService> service = V1_1::IFooService::getService();
sp<V1_1::IFooService> alternateService = V1_1::IFooService::getService("another_foo_service");
// Java
V1_1.IFooService service = V1_1.IFooService.getService(true /* retry */);
V1_1.IFooService alternateService = V1_1.IFooService.getService("another", true /* retry */);

يتم التعامل مع كل إصدار من واجهة HIDL كواجهة منفصلة. وبالتالي، يمكن تسجيل الإصدار 1.1 IFooService والإصدار 2.2 IFooService كـ "foo_service" ويحصل getService("foo_service") على أي من الواجهتين على الخدمة المسجلة لتلك الواجهة. ولهذا السبب، في معظم الحالات، لا يلزم توفير أي معلمة اسم للتسجيل أو الاكتشاف (بمعنى الاسم "الافتراضي").

يلعب كائن واجهة البائع أيضًا دورًا في طريقة النقل للواجهة التي تم إرجاعها. بالنسبة لواجهة IFoo في الحزمة android.hardware.foo@1.0 ، فإن الواجهة التي يتم إرجاعها بواسطة IFoo::getService تستخدم دائمًا طريقة النقل المعلنة لـ android.hardware.foo في بيان الجهاز إذا كان الإدخال موجودًا؛ وإذا لم يكن أسلوب النقل متاحًا، فسيتم إرجاع nullptr.

في بعض الحالات، قد يكون من الضروري الاستمرار فورًا حتى دون الحصول على الخدمة. يمكن أن يحدث هذا (على سبيل المثال) عندما يريد العميل إدارة إشعارات الخدمة بنفسه أو في برنامج تشخيصي (مثل atrace ) والذي يحتاج إلى الحصول على جميع خدمات hw واستردادها. في هذه الحالة، يتم توفير واجهات برمجة تطبيقات إضافية مثل tryGetService في C++ أو getService("instance-name", false) في Java. يجب أيضًا استخدام واجهة برمجة التطبيقات getService القديمة المتوفرة في Java مع إشعارات الخدمة. لا يؤدي استخدام واجهة برمجة التطبيقات هذه إلى تجنب حالة السباق حيث يقوم الخادم بتسجيل نفسه بعد أن يطلبه العميل باستخدام إحدى واجهات برمجة التطبيقات التي لا تسمح بإعادة المحاولة.

خدمة إخطارات الوفاة

يمكن للعملاء الذين يريدون أن يتم إعلامهم عند وفاة إحدى الخدمات تلقي إشعارات الوفاة التي يقدمها إطار العمل. لتلقي الإخطارات، يجب على العميل:

  1. فئة فرعية فئة/واجهة HIDL hidl_death_recipient (في كود C++، وليس في HIDL).
  2. تجاوز أسلوب serviceDied() الخاص بها.
  3. إنشاء مثيل لكائن من الفئة الفرعية hidl_death_recipient .
  4. قم باستدعاء الأسلوب linkToDeath() الموجود على الخدمة للمراقبة، وتمرير كائن واجهة IDeathRecipient . لاحظ أن هذه الطريقة لا تأخذ ملكية مستلم الوفاة أو الوكيل الذي يتم الاتصال به.

مثال على الكود الكاذب (C++ وJava متشابهان):

class IMyDeathReceiver : hidl_death_recipient {
  virtual void serviceDied(uint64_t cookie,
                           wp<IBase>& service) override {
    log("RIP service %d!", cookie);  // Cookie should be 42
  }
};
....
IMyDeathReceiver deathReceiver = new IMyDeathReceiver();
m_importantService->linkToDeath(deathReceiver, 42);

قد يتم تسجيل نفس متلقي الوفاة في عدة خدمات مختلفة.

نقل البيانات

قد يتم إرسال البيانات إلى الخدمة عن طريق استدعاء الأساليب المحددة في الواجهات في ملفات .hal . هناك نوعان من الأساليب:

  • تنتظر طرق الحظر حتى يصدر الخادم نتيجة.
  • ترسل الطرق أحادية الاتجاه البيانات في اتجاه واحد فقط ولا تحجب. إذا تجاوزت كمية البيانات أثناء الرحلة في مكالمات RPC حدود التنفيذ، فقد تقوم المكالمات إما بحظر أو إرجاع إشارة خطأ (لم يتم تحديد السلوك بعد).

الطريقة التي لا تُرجع قيمة ولكن لم يتم الإعلان عنها على أنها oneway لا تزال محظورة.

يتم استدعاء كافة الأساليب المعلنة في واجهة HIDL في اتجاه واحد، إما من HAL أو إلى HAL. لا تحدد الواجهة الاتجاه الذي سيتم استدعاؤه فيه. يجب أن توفر البنيات التي تحتاج إلى استدعاءات تنشأ من طبقة HAL واجهتين (أو أكثر) في حزمة HAL وتخدم الواجهة المناسبة من كل عملية. يتم استخدام كلمتي العميل والخادم فيما يتعلق باتجاه الاتصال للواجهة (أي يمكن أن يكون HAL خادمًا لواجهة واحدة وعميلًا لواجهة أخرى).

عمليات الاسترجاعات

تشير كلمة رد الاتصال إلى مفهومين مختلفين، يتميزان برد الاتصال المتزامن ورد الاتصال غير المتزامن .

يتم استخدام عمليات الاسترجاعات المتزامنة في بعض أساليب HIDL التي تقوم بإرجاع البيانات. أسلوب HIDL الذي يُرجع أكثر من قيمة واحدة (أو يُرجع قيمة واحدة من النوع غير البدائي) يُرجع نتائجه عبر دالة رد الاتصال. إذا تم إرجاع قيمة واحدة فقط وهي من النوع البدائي، فلن يتم استخدام رد اتصال ويتم إرجاع القيمة من الطريقة. ينفذ الخادم أساليب HIDL ويقوم العميل بتنفيذ عمليات الاسترجاعات.

تسمح عمليات الاسترجاعات غير المتزامنة لخادم واجهة HIDL بإنشاء المكالمات. ويتم ذلك عن طريق تمرير مثيل للواجهة الثانية عبر الواجهة الأولى. يجب أن يعمل عميل الواجهة الأولى كخادم للواجهة الثانية. يمكن لخادم الواجهة الأولى استدعاء الأساليب الموجودة على كائن الواجهة الثانية. على سبيل المثال، قد يرسل تطبيق HAL معلومات بشكل غير متزامن مرة أخرى إلى العملية التي تستخدمها عن طريق استدعاء الأساليب على كائن الواجهة الذي تم إنشاؤه وتقديمه بواسطة تلك العملية. قد تكون الطرق الموجودة في الواجهات المستخدمة لرد الاتصال غير المتزامن محظورة (وقد تُرجع قيمًا إلى المتصل) أو oneway . على سبيل المثال، راجع "عمليات الاسترجاعات غير المتزامنة" في HIDL C++ .

لتبسيط ملكية الذاكرة، تأخذ استدعاءات الأسلوب وعمليات رد الاتصال in فقط ولا تدعم معلمات out أو inout .

حدود لكل معاملة

لا يتم فرض حدود لكل معاملة على كمية البيانات المرسلة في أساليب HIDL وعمليات الاسترجاعات. ومع ذلك، تعتبر المكالمات التي تتجاوز 4 كيلو بايت لكل معاملة زائدة. إذا تمت رؤية ذلك، فمن المستحسن إعادة تصميم واجهة HIDL المحددة. هناك قيد آخر وهو الموارد المتاحة للبنية التحتية لـ HIDL للتعامل مع المعاملات المتزامنة المتعددة. يمكن أن تكون المعاملات المتعددة قيد التنفيذ في وقت واحد بسبب وجود سلاسل رسائل أو عمليات متعددة ترسل مكالمات إلى عملية ما أو مكالمات oneway متعددة لا يتم التعامل معها بسرعة من خلال عملية الاستلام. الحد الأقصى للمساحة الإجمالية المتاحة لجميع المعاملات المتزامنة هو 1 ميجابايت بشكل افتراضي.

في الواجهة المصممة جيدًا، لا ينبغي أن يحدث تجاوز حدود الموارد هذه؛ إذا حدث ذلك، فإن المكالمة التي تجاوزتها قد يتم حظرها حتى تصبح الموارد متاحة أو تشير إلى خطأ في النقل. يتم تسجيل كل تكرار لتجاوز حدود كل معاملة أو تجاوز موارد تنفيذ HIDL عن طريق إجمالي المعاملات أثناء الرحلة لتسهيل تصحيح الأخطاء.

تطبيقات الطريقة

يقوم HIDL بإنشاء ملفات رأس تعلن عن الأنواع والأساليب وعمليات الاسترجاعات الضرورية في اللغة الهدف (C++ أو Java). النموذج الأولي للطرق وعمليات رد الاتصال المحددة بواسطة HIDL هو نفسه لكل من كود العميل والخادم. يوفر نظام HIDL تطبيقات الوكيل للطرق على جانب المتصل التي تنظم البيانات لنقل IPC، ورمز كعب الروتين على جانب المستدعي الذي يمرر البيانات إلى تطبيقات المطور للطرق.

يمتلك منادي الوظيفة (أسلوب HIDL أو رد الاتصال) ملكية هياكل البيانات التي تم تمريرها إلى الوظيفة، ويحتفظ بالملكية بعد الاستدعاء؛ وفي جميع الحالات، لا يحتاج المستدعى إليه إلى تحرير المخزن أو تحريره.

  • في لغة C++، قد تكون البيانات للقراءة فقط (قد تؤدي محاولات الكتابة إليها إلى حدوث خطأ في التجزئة) وتكون صالحة طوال مدة المكالمة. يمكن للعميل نسخ البيانات بعمق لنشرها خارج نطاق المكالمة.
  • في Java، يتلقى الكود نسخة محلية من البيانات (كائن Java عادي)، والتي قد يحتفظ بها ويعدلها أو يسمح بجمع البيانات المهملة.

نقل البيانات بخلاف RPC

لدى HIDL طريقتان لنقل البيانات دون استخدام استدعاء RPC: الذاكرة المشتركة وقائمة انتظار الرسائل السريعة (FMQ)، وكلاهما مدعومان فقط في C++.

  • ذكريات مشتركه . يتم استخدام memory نوع HIDL المضمنة لتمرير كائن يمثل الذاكرة المشتركة التي تم تخصيصها. يمكن استخدامها في عملية الاستلام لتعيين الذاكرة المشتركة.
  • قائمة انتظار الرسائل السريعة (FMQ) . يوفر HIDL نوع قائمة انتظار رسائل مقولبة يقوم بتنفيذ تمرير الرسائل بدون انتظار. لا يستخدم النواة أو المجدول في وضع العبور أو التجليد (لن يحتوي الاتصال بين الأجهزة على هذه الخصائص). عادةً، يقوم HAL بإعداد نهاية قائمة الانتظار الخاصة به، مما يؤدي إلى إنشاء كائن يمكن تمريره من خلال RPC عبر معلمة من نوع HIDL المضمن MQDescriptorSync أو MQDescriptorUnsync . يمكن استخدام هذا الكائن من خلال عملية الاستلام لإعداد الطرف الآخر من قائمة الانتظار.
    • لا يُسمح بتجاوز قوائم انتظار المزامنة، ويمكن أن تحتوي على قارئ واحد فقط.
    • يُسمح لقوائم الانتظار غير المتزامنة بالتجاوز، ويمكن أن تحتوي على العديد من القراء، يجب على كل منهم قراءة البيانات في الوقت المناسب وإلا فقدها.
    لا يُسمح لأي من النوعين بالتجاوز (ستفشل القراءة من قائمة انتظار فارغة)، ويمكن أن يحتوي كل نوع على كاتب واحد فقط.

لمزيد من التفاصيل حول FMQ، راجع قائمة انتظار الرسائل السريعة (FMQ) .