لغة تعريف واجهة HAL أو HIDL هي لغة وصف واجهة (IDL) لتحديد الواجهة بين HAL و مستخدميها. تتيح HIDL تحديد الأنواع وطلبات الاتصال بالطرق، والتي يتم جمعها في الواجهات والحِزم. على نطاق أوسع، HIDL هو نظام للتواصل بين قواعد البيانات التي يمكن تجميعها بشكل مستقل.
يُقصد استخدام HIDL للتواصل بين العمليات (IPC). تُعرف واجهات HAL التي تم إنشاؤها باستخدام HDL باسم IDEHAL، وذلك لأنّها يمكنها التواصل مع طبقات التصميم الأخرى باستخدام IDE طلبات الاتصالات بين العمليات (IPC). يتم تشغيل واجهات HAL المُجمَّعة في عملية منفصلة عن العميل الذي يستخدمها. بالنسبة إلى المكتبات التي يجب ربطها بعملية، يتوفّر أيضًا وضع المرور (غير متوافق مع Java).
تحدِّد HIDL بنى البيانات وتوقيعات الطرق، وهي منظَّمة في واجهات (تشبه الفئة) يتم جمعها في حِزم. تبدو بنية HIDL مألوفة لمبرمجي C++ و Java، ولكن مع مجموعة مختلفة من الكلمات الرئيسية. يستخدم HIDL أيضًا التعليقات التوضيحية بأسلوب Java.
المصطلحات
يستخدم هذا القسم المصطلحات التالية ذات الصلة بـ HIDL:
مُجمَّع | يشير إلى أنّه يتم استخدام HIDL لاستدعاء الإجراءات عن بُعد بين العمليات، يتم تنفيذها من خلال آلية تشبه Binder. اطّلِع أيضًا على النقل. |
---|---|
ردّ اتصال، غير متزامن | واجهة يعرضها مستخدم HAL، ويتم تمريرها إلى HAL (باستخدام طريقة HIDL)، ويدعوها HAL لعرض البيانات في أي وقت. |
callback, synchronous | تُعيد البيانات من تنفيذ طريقة HIDL في الخادم إلى العميل. لا يتم استخدامها مع الطرق التي تُرجع قيمة فارغة أو قيمة أساسية واحدة. |
client | عملية تستدعي طرق واجهة معيّنة قد تكون عملية HAL أو عملية إطار عمل Android عميلًا لواجهة وخادومًا لواجهة أخرى. اطّلِع أيضًا على النقل. |
تمتد | يشير إلى واجهة تضيف طُرقًا و/أو أنواعًا إلى واجهة أخرى. يمكن أن تمتد واجهة إلى واجهة واحدة أخرى فقط. يمكن استخدامها لزيادة إصدار بسيطة في اسم الحزمة نفسه أو لحزمة جديدة (مثل إضافة مطوِّر) لإنشاء حزمة قديمة. |
إنشاء | يشير إلى طريقة واجهة تُعيد القيم إلى العميل. لعرض قيمة واحدة غير أساسية أو أكثر من قيمة واحدة، يتم إنشاء دالة ردّ اتصال تزامني. |
واجهة | مجموعة من الطرق والأنواع تم تحويلها إلى فئة في C++ أو Java. يتم استدعاء كل ال methods في الواجهة بالاتجاه نفسه: تستدعي عملية العميل methods التي تنفذها عملية الخادم. |
ذهاب فقط | عند تطبيقها على طريقة HIDL، تشير إلى أنّ الطريقة لا تعرض أي قيم ولا تحظر. |
طرد | مجموعة من الواجهات وأنواع البيانات التي تشترك في إصدار |
تقنية مرور الإشارة | وضع HIDL الذي يكون فيه الخادم مكتبة مشتركة، dlopen ed
من قِبل العميل في وضع "النقل المباشر"، يكون العميل والخادم العملية نفسها ولكنهما
يحتويان على قواعد بيانات منفصلة. لا تُستخدَم إلا لإدخال قواعد بيانات برمجية قديمة في نموذج HIDL.
راجِع أيضًا Binderized. |
خادم | عملية تنفِّذ طُرق واجهة اطّلِع أيضًا على النقل. |
وسيلة مواصلات | بنية HIDL الأساسية التي تنقل البيانات بين الخادم والعميل |
إصدار | إصدار حزمة يتألّف من عددين صحيحَين، رئيسي وثانوي. قد تضيف الإصدارات الصغرى أنواعًا وطُرقًا (ولكن لا تغيرها). |
تصميم HIDL
يهدف HIDL إلى استبدال إطار عمل Android بدون الحاجة إلى
إعادة إنشاء واجهات برمجة التطبيقات لمستوى الحِزم الأساسية. ينشئ المورّدون أو صانعو المنظومة على الرقاقة واجهات HAL ويضعونها في ملف تعريف
/vendor
على الجهاز، ما يتيح استبدال إطار عمل Android في ملف التعريف
الخاص به بملف OTA بدون إعادة تجميع واجهات HAL.
يوازن تصميم HIDL بين المخاوف التالية:
- إمكانية التشغيل التفاعلي: إنشاء واجهات قابلة للتشغيل التفاعلي بشكل موثوق بين العمليات التي يمكن تجميعها باستخدام تصاميم وسلسلة أدوات وإعدادات إنشاء مختلفة واجهات HIDL لها إصدارات ولا يمكن تغييرها بعد نشرها.
- الكفاءة: تحاول HIDL تقليل عدد عمليات النسخ. يتم إرسال البيانات المحدّدة باستخدام HIDL إلى رمز C++ في تنسيق C++ العادي هياكل البيانات التي يمكن استخدامها بدون فك الحزمة. توفّر HIDL أيضًا واجهات ذاكرة مشترَكة، وبما أنّ طلبات RPC بطيئة بطبيعتها إلى حدٍ ما، تتيح HIDL طريقتَين لنقل البيانات بدون استخدام طلب RPC: الذاكرة المشترَكة و"قائمة الرسائل السريعة" (FMQ).
- بسيطة: تتجنّب HIDL المشاكل المعقدة المتعلّقة بملكية الذاكرة من خلال
استخدام مَعلمات
in
فقط لواجهة طلب البيانات عن بُعد (RPC) (راجِع لغة تعريف واجهة Android (AIDL)). ويتم عرض القيم التي لا يمكن عرضها بكفاءة من خلال الطرق من خلال دوالّ الاستدعاء. لا يؤدي نقل البيانات إلى HIDL أو استلام البيانات من HIDL إلى تغيير ملكية البيانات، بل تبقى الملكية دائمًا مع الدالة التي تُجري عملية الاتصال. يجب أن تظل البيانات محفوظة فقط طوال مدة الدالة التي تمّ استدعاؤها، ويمكن إزالتها فور انتهاء الدالة التي تمّ استدعاؤها.
استخدام وضع "النقل المباشر"
لتحديث الأجهزة التي تعمل بإصدارات سابقة من Android إلى Android O، يمكنك تغليف كل من واجهات HAL التقليدية (والقديمة) في واجهة HIDL جديدة توفّر HAL في وضعَي الربط والمعالجة نفسها (النقل المباشر). ويكون هذا الحِزم شفّافًا لكل من HAL وإطار عمل Android.
لا يتوفّر وضع "النقل المباشر" إلا لعملاء C++ وعمليات التنفيذ. لا تتضمّن الأجهزة التي تعمل بإصدارات أقدم من Android واجهات HAL مكتوبة بلغة Java، لذا تكون واجهات HAL في Java مرتبطة بشكلٍ أساسي.
ملفات الرأس التي يتم تمريرها
عند تجميع ملف .hal
، ينشئ hidl-gen
ملف عنوان إضافي للمرور BsFoo.h
بالإضافة إلى العناوين
المستخدَمة للتواصل مع أداة الربط، ويحدِّد هذا العنوان الدوال التي يجب
dlopen
ها. وبما أنّ واجهات HAL للمرور تعمل في العملية نفسها التي يتم فيها
استدعاؤها، يتم في معظم الحالات استدعاء طُرق المرور من خلال دعوة دالة مباشرة (الخيط نفسه). يتم تشغيل طرق oneway
في سلسلة المهام الخاصة بها
لأنّه ليس من المقصود الانتظار إلى أن يعالجها HAL (هذا يعني أنّ أي HAL
يستخدم طرق oneway
في وضع "النقل المباشر" يجب أن يكون آمنًا في سلسلة المهام).
عند توفُّر IFoo.hal
، يُغلِف BsFoo.h
methods التي تم إنشاؤها باستخدام HIDL لتوفير ميزات إضافية (مثل تنفيذ معاملات oneway
في سلسلة محادثات أخرى). هذا الملف مشابه لملف
BpFoo.h
، ولكن بدلاً من تمرير طلبات IPC باستخدام أداة الربط، يتم استدعاء الدوال المطلوبة مباشرةً. قد تقدّم عمليات التنفيذ المستقبلية لواجهة HAL حلولاً متعددة، مثل FooFast HAL و
FooAccurate HAL. في هذه الحالات، سيتم
إنشاء ملف لكل عملية تنفيذ إضافية (مثل PTFooFast.cpp
و
PTFooAccurate.cpp
).
ربط واجهات برمجة التطبيقات لنظام التشغيل HAL لميزة "العرض المباشر"
يمكنك ربط عمليات تنفيذ 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. - تفصل الفواصل بين عناصر التسلسل.
- تُستخدم الفواصل المنقوطة لإنهاء كل عنصر، بما في ذلك العنصر الأخير.
- UPPERCASE هو عنصر غير نهائي.
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