HIDL

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

تم تصميم HIDL ليتم استخدامه للاتصال بين العمليات (IPC). تسمى HALs التي تم إنشاؤها باستخدام HDL بـ HALs الموثقة حيث يمكنها التواصل مع طبقات البنية الأخرى باستخدام مكالمات الاتصال بين العمليات (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 هو إمكانية استبدال إطار عمل Android دون الحاجة إلى إعادة بناء HALs. سيتم إنشاء HALs بواسطة البائعين أو صانعي SOC ووضعها في قسم /vendor على الجهاز، مما يتيح استبدال إطار عمل Android، في القسم الخاص به، بـ OTA دون إعادة ترجمة HALs.

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

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

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

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

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

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

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

ربط العبور HALs

يمكنك ربط تطبيقات HAL التي تدعم وضع العبور. بالنظر إلى واجهة HAL abcd@MN::IFoo ، يتم إنشاء حزمتين:

  • abcd@MN::IFoo-impl . يحتوي على تطبيق HAL ويكشف عن الوظيفة IFoo* HIDL_FETCH_IFoo(const char* name) . على الأجهزة القديمة، يتم dlopen هذه الحزمة ويتم إنشاء مثيل لها باستخدام HIDL_FETCH_IFoo . يمكنك إنشاء الكود الأساسي باستخدام hidl-gen و -Lc++-impl و -Landroidbp-impl .
  • abcd@MN::IFoo-service . يفتح طبقة HAL للعبور ويسجل نفسه كخدمة مجلدة، مما يتيح استخدام نفس تطبيق طبقة HAL كخدمة عبور ومربوطة.

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

قواعد هيدل

حسب التصميم، لغة 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