مؤسسة HIDL

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

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

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

المصطلحات

يستخدم هذا القسم العبارات التالية المتعلّقة بشهادة HIDL:

دمج يشير إلى استخدام HIDL لاستدعاءات الإجراءات عن بُعد بين العمليات، وتنفيذها من خلال آلية تشبه الصنف Binder. اطّلِع أيضًا على المرور.
رد الاتصال، غير متزامن واجهة يعرضها مستخدم HAL، ويتم تمريرها إلى HAL (باستخدام طريقة HIDL)، وتطلبها HAL لعرض البيانات في أي وقت.
معاودة الاتصال، متزامن تعرض البيانات من تنفيذ طريقة HIDL للخادم إلى العميل. ولا يُستخدم للطرق التي تعرض قيمة فارغة أو قيمة أساسية واحدة.
عميل العملية التي تستدعي طرقًا لواجهة معينة. قد تكون عملية إطار عمل HAL أو Android عميلاً لواجهة واحدة وخادمًا لواجهة أخرى. اطّلِع أيضًا على العبور.
يمتد يشير إلى الواجهة التي تضيف طرقًا و/أو أنواعًا إلى واجهة أخرى. يمكن للواجهة تمديد واجهة واحدة أخرى فقط. ويمكن استخدامها مع جزء بسيط من الإصدار في اسم الحزمة نفسه أو لحزمة جديدة (مثل إضافة مورّد) للإنشاء على حزمة قديمة.
يُنشئ يشير إلى طريقة واجهة تعرض قيمًا للعميل. لإرجاع قيمة واحدة غير أولية، أو أكثر من قيمة، يتم إنشاء دالة استدعاء متزامنة.
واحدة مجموعة الطرق والأنواع. إذا تمت ترجمتها إلى صف باستخدام لغة C++ أو Java. يتم استدعاء جميع الطرق في الواجهة في نفس الاتجاه: تستدعي عملية العميل الطرق التي تنفذها عملية الخادم.
ذهاب فقط عند تطبيق الإجراء على طريقة HIDL، يشير ذلك إلى أنّ الطريقة لا تُرجع أي قيم ولا تمنعها.
طرد مجموعة من الواجهات وأنواع البيانات تتشارك إصدارًا.
تقنية مرور الإشارة وضع HIDL الذي يكون فيه الخادم مكتبة مشتركة، dlopenمن خلال العميل. في وضع العبور، يكون العميل والخادم نفس العملية ولكن قواعد تعليمات برمجية منفصلة. يُستخدم فقط لإدخال قواعد الرموز القديمة في نموذج HIDL. راجِع أيضًا المقالة مدمجة.
خادم يشير ذلك المصطلح إلى عملية يتم فيها تنفيذ طرق الواجهة. اطّلِع أيضًا على العبور.
وسيلة مواصلات بنية HIDL الأساسية التي تنقل البيانات بين الخادم والعميل.
إصدار إصدار الحزمة. تتكوّن من عددين صحيحين، كبير وثانوي. قد تضيف زيادات الإصدار الثانوية أنواعًا وطرقًا (ولكن لا تتغير).

تصميم HIDL

إن الهدف من HIDL هو إمكانية استبدال إطار عمل Android دون الحاجة إلى إعادة إنشاء HALs. سينشئ المورّدون أو الشركات المصنّعة لشركة SOC أنظمة HALs ويتم وضعها في قسم /vendor على الجهاز، ما يتيح استبدال إطار عمل Android في قسمه الخاص بإطار عمل OTA بدون إعادة تجميع HALs.

يوازن تصميم HIDL بين المخاوف التالية:

  • إمكانية التشغيل التفاعلي: إنشاء واجهات قابلة للتشغيل التفاعلي بشكل موثوق بين العمليات والتي يمكن تجميعها باستخدام العديد من البُنى الأساسية وسلاسل الأدوات وإنشاء تهيئات التصميم. تم تحديد إصدارات من واجهات HIDL ولا يمكن تغييرها بعد نشرها.
  • الكفاءة: يحاول HIDL تقليل عدد عمليات النسخ. يتم تسليم البيانات المحددة بواسطة HIDL إلى رمز C++ في هياكل بيانات التخطيط القياسية في C++ التي يمكن استخدامها بدون فك ضغط. يوفر HIDL أيضًا واجهات ذاكرة مشتركة، وبما أنّ استدعاء إجراء عن بُعد (RPC) بطيئة إلى حدّ ما، فإنّ HIDL يدعم طريقتَين لنقل البيانات بدون استخدام استدعاء إجراء عن بُعد (RPC): الذاكرة المشتركة وقائمة انتظار الرسائل السريعة (FMQ).
  • سهلة الاستخدام: يتجنّب HIDL المشاكل الشائكة المتعلقة بملكية الذاكرة من خلال استخدام مَعلمات in فقط لاستدعاء إجراء عن بُعد (RPC) (راجِع Android لغة تعريف الواجهة (AIDL))، حيث يتم إرجاع القيم التي لا يمكن إرجاعها بكفاءة من الطرق من خلال دوال معاودة الاتصال. لا يؤدي تمرير البيانات إلى HIDL لنقلها أو استلامها منها إلى تغيير ملكية البيانات، فالملكية تظل دائمًا مع وظيفة الاستدعاء. ويجب أن تظل البيانات للاستمرار طوال مدة الدالة المطلوبة فقط وقد يتم تلفها فورًا بعد إرجاع الدالة المطلوبة.

استخدام وضع العبور

لتحديث الأجهزة التي تعمل بإصدارات سابقة من نظام التشغيل Android إلى Android O، يمكنك تضمين كل من HALs التقليدية (والقديمة) في واجهة HIDL الجديدة التي تعرض HAL في وضعَي الربط والمعالجة نفسها (المرور). ويكون هذا التغليف شفافًا لكل من HAL وإطار عمل Android.

وضع العبور متاح فقط لعملاء C++ وعمليات التنفيذ. ولا تحتوي الأجهزة التي تعمل بإصدارات سابقة من Android على HALs مكتوبة بلغة Java، وبالتالي تكون HALs في جافا مرتبطة بطبيعتها.

عند تجميع ملف .hal، ينتج hidl-gen ملف رأس عبور إضافي BsFoo.h بالإضافة إلى العناوين المستخدمة في اتصال الحافظ. يحدد هذا العنوان الدوال التي يجب dlopen معالجتها. ونظرًا لتشغيل HALs العبور في العملية نفسها التي يتم استدعاؤها، يتم في معظم الحالات استدعاء طرق العبور من خلال استدعاء الدالة المباشر (سلسلة التعليمات نفسها). يتم تشغيل oneway طريقة في سلسلة المحادثات الخاصة بها لأنّها لا تهدف إلى انتظار معالجة HAL لها (أي أنّ أي طريقة HAL تستخدم طرق oneway في وضع العبور يجب أن تكون آمنة في سلاسل المحادثات).

استنادًا إلى IFoo.hal، يلتفّ BsFoo.h الطرق التي يتم إنشاؤها من خلال HIDL لتوفير ميزات إضافية (مثل إجراء معاملة oneway في سلسلة محادثات أخرى). يشبه هذا الملف ملف BpFoo.h، ولكن بدلاً من تمرير طلبات IPC باستخدام أداة الربط، يتم استدعاء الدوال المطلوبة مباشرةً. قد يؤدي التنفيذ المستقبلي لHALs إلى عمليات تنفيذ متعددة، مثل FooFast HAL وFooMetric HAL. في هذه الحالات، سيتم إنشاء ملف لكل عملية تنفيذ إضافية (على سبيل المثال، PTFooFast.cpp و PTFooAccurate.cpp).

ربط HALs للمرور

يمكنك دمج عمليات تنفيذ HAL التي تدعم وضع العبور. بناءً على واجهة HAL a.b.c.d@M.N::IFoo، يتم إنشاء حزمتَين:

  • a.b.c.d@M.N::IFoo-impl. يحتوي على تنفيذ HAL ويعرض الدالة IFoo* HIDL_FETCH_IFoo(const char* name). على الأجهزة القديمة، تم dlopenإعداد هذه الحزمة ويتم تنفيذ تنفيذها باستخدام HIDL_FETCH_IFoo. يمكنك إنشاء الرمز الأساسي باستخدام hidl-gen و-Lc++-impl و-Landroidbp-impl.
  • a.b.c.d@M.N::IFoo-service. يؤدي إلى فتح بروتوكول HAL العبور وتسجيله كخدمة مدمجة، ما يتيح استخدام تنفيذ HAL نفسه كعملية مرور ودمج.

وفقًا للنوع IFoo، يمكنك الاتصال بـ sp<IFoo> IFoo::getService(string name, bool getStub) للوصول إلى أحد المثيلات الظاهرة لـ IFoo. إذا كانت القيمة getStub صحيحة، سيحاول getService فتح HAL فقط في وضع العبور. إذا لم يتم ضبط getStub على "خطأ"، سيحاول getService العثور على خدمة ثنائية، وفي حال فشل ذلك، يحاول العثور على خدمة العبور. يجب عدم استخدام المعلَمة getStub مطلقًا إلا في defaultPassthroughServiceImplementation. (الأجهزة التي يتم إطلاقها بنظام Android O هي أجهزة مجمّعة بالكامل، لذا لا يُسمح بفتح الخدمة في وضع العبور).

قواعد HIDL

من حيث التصميم، تشبه لغة HIDL لغة C (لكنها لا تستخدم المعالج الأولي C). جميع علامات الترقيم غير المشار إليها أدناه (باستثناء الاستخدام الواضح لـ = و|) هي جزء من القواعد النحوية.

ملاحظة: للحصول على تفاصيل حول نمط رموز HIDL، يمكنك الاطّلاع على دليل نمط الرموز.

  • تشير السمة /** */ إلى تعليق في المستند. لا يمكن تطبيقها إلا على تعريفات النوع والطريقة والحقل والتعداد.
  • تشير السمة /* */ إلى تعليق متعدد الأسطر.
  • تشير السمة // إلى تعليق في نهاية السطر. بخلاف //، تكون الأسطر الجديدة مماثلة لأي مسافة بيضاء أخرى.
  • في مثال القواعد النحوية أدناه، إنّ النص الممتد من // إلى نهاية السطر لا يشكّل جزءًا من القواعد النحوية، بل يشكّل تعليقًا على القواعد النحوية.
  • تعني [empty] أنّ العبارة قد تكون فارغة.
  • بعد أن تكون قيمة العلامة ? حرفية أو عبارة، هي سمة اختيارية.
  • تشير ... إلى التسلسل الذي يحتوي على صفر أو أكثر من العناصر مع فصل علامات الترقيم كما هو موضح. ما مِن وسيطات متباينة في HIDL.
  • تفصل فاصلات عناصر التسلسل.
  • تُنهي الفواصل المنقوطة كل عنصر، بما في ذلك العنصر الأخير.
  • الأحرف الكبيرة هي حالة غير طرفية.
  • italics هي مجموعة رموز مميزة مثل integer أو identifier (قواعد تحليل C العادية).
  • constexpr هو تعبير ثابت بنمط C (مثل 1 + 1 و1L << 3).
  • import_name هو اسم حزمة أو واجهة، مؤهل كما هو موضّح في إصدار HIDL.
  • تُعد الأحرف words الصغيرة رموزًا مميزة حرفية.

مثال:

ROOT =
    PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... }  // not for types.hal
  | PACKAGE IMPORTS ITEM ITEM...  // only for types.hal; no method definitions

ITEM =
    ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?;
  |  safe_union identifier { UFIELD; UFIELD; ...};
  |  struct identifier { SFIELD; SFIELD; ...};  // Note - no forward declarations
  |  union identifier { UFIELD; UFIELD; ...};
  |  enum identifier: TYPE { ENUM_ENTRY, ENUM_ENTRY ... }; // TYPE = enum or scalar
  |  typedef TYPE identifier;

VERSION = integer.integer;

PACKAGE = package android.hardware.identifier[.identifier[...]]@VERSION;

PREAMBLE = interface identifier EXTENDS

EXTENDS = <empty> | extends import_name  // must be interface, not package

GENERATES = generates (FIELD, FIELD ...)

// allows the Binder interface to be used as a type
// (similar to typedef'ing the final identifier)
IMPORTS =
   [empty]
  |  IMPORTS import import_name;

TYPE =
  uint8_t | int8_t | uint16_t | int16_t | uint32_t | int32_t | uint64_t | int64_t |
 float | double | bool | string
|  identifier  // must be defined as a typedef, struct, union, enum or import
               // including those defined later in the file
|  memory
|  pointer
|  vec<TYPE>
|  bitfield<TYPE>  // TYPE is user-defined enum
|  fmq_sync<TYPE>
|  fmq_unsync<TYPE>
|  TYPE[SIZE]

FIELD =
   TYPE identifier

UFIELD =
   TYPE identifier
  |  safe_union identifier { FIELD; FIELD; ...} identifier;
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SFIELD =
   TYPE identifier
  |  safe_union identifier { FIELD; FIELD; ...};
  |  struct identifier { FIELD; FIELD; ...};
  |  union identifier { FIELD; FIELD; ...};
  |  safe_union identifier { FIELD; FIELD; ...} identifier;
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SIZE =  // Must be greater than zero
     constexpr

ANNOTATIONS =
     [empty]
  |  ANNOTATIONS ANNOTATION

ANNOTATION =
  |  @identifier
  |  @identifier(VALUE)
  |  @identifier(ANNO_ENTRY, ANNO_ENTRY  ...)

ANNO_ENTRY =
     identifier=VALUE

VALUE =
     "any text including \" and other escapes"
  |  constexpr
  |  {VALUE, VALUE ...}  // only in annotations

ENUM_ENTRY =
     identifier
  |  identifier = constexpr