HIDL

زبان تعریف میانای HAL یا HIDL یک زبان توصیف میانجی (IDL) برای مشخص کردن میانجی بین HAL و کاربران آن است. ‫HIDL امکان تعیین انواع و فراخوانی‌های روش را فراهم می‌کند که در میانه‌ها و بسته‌ها جمع‌آوری می‌شوند. به‌طور کلی‌تر، HIDL سیستمی برای برقراری ارتباط بین پایگاه‌های کدی است که ممکن است به‌طور مستقل کامپایل شوند.

‫HIDL برای استفاده در ارتباط بین فرایندی (IPC) درنظر گرفته شده است. «لایه انتزاع سخت‌افزار» ساخته‌شده با HDL را «لایه انتزاع سخت‌افزار» پیوندشده می‌نامند زیرا می‌توانند بااستفاده از فراخوانی‌های ارتباط بین‌فرایندی (IPC) پیوند با لایه‌های معماری دیگر ارتباط برقرار کنند. ‫HALهای پیوندشده در فرایندی جداگانه از کاربری که از آن‌ها استفاده می‌کند اجرا می‌شوند. برای کتابخانه‌هایی که باید به فرایندی پیوند داده شوند، حالت گذردهی نیز دردسترس است (در Java پشتیبانی نمی‌شود).

‫HIDL ساختارهای داده و امضاهای روش را که در میانه‌ها سازمان‌دهی شده‌اند (مشابه کلاس) و در بسته‌ها جمع‌آوری شده‌اند مشخص می‌کند. نحو HIDL برای برنامه‌نویسان C++‎ و Java آشنا به‌نظر می‌رسد، اما مجموعه متفاوتی از کلیدواژه‌ها را دارد. ‫HIDL همچنین از حاشیه‌نویسی‌های سبک Java استفاده می‌کند.

اصطلاحات

این بخش از اصطلاحات مربوط به HIDL زیر استفاده می‌کند:

binderized نشان می‌دهد که از HIDL برای فراخوانی‌های رویه‌ای از دور بین فرایندها استفاده می‌شود، که ازطریق سازوکاری شبیه Binder پیاده‌سازی شده است. همچنین گذر را ببینید.
بازخوانی، ناهمگام رابطی که توسط کاربر HAL ارائه می‌شود، به HAL منتقل می‌شود (بااستفاده از روش HIDL)، و توسط HAL فراخوانی می‌شود تا داده‌ها را در هر زمان برگرداند.
بازخوانی، هم‌زمان داده‌ها را از پیاده‌سازی روش HIDL سرور به کارخواه برمی‌گرداند. برای روش‌هایی که مقدار تهی یا یک مقدار اولیه واحد را برمی‌گردانند استفاده نمی‌شود.
مشتری فرایندی که روش‌های میانای خاصی را فراخوانی می‌کند. فرایند HAL یا چارچوب Android ممکن است کارخواه یک میانای و سرور میانای دیگر باشد. انتقال مستقیم را نیز ببینید.
گسترش می‌یابد نشان‌دهنده میانایی است که روش‌ها و/یا انواع را به میانای دیگری اضافه می‌کند. یک میانای کاربر فقط می‌تواند یک میانای کاربر دیگر را گسترش دهد. می‌تواند برای افزایش نسخه جزئی در همان نام بسته یا برای بسته جدید (مثلاً افزونه فروشنده) برای ساختن روی بسته قدیمی‌تر استفاده شود.
تولید می‌کند نشان‌دهنده روش واسطی است که مقادیر را به مشتری برمی‌گرداند. برای برگرداندن یک مقدار غیرابتدایی یا بیش‌از یک مقدار، تابع بازخوانی همگام تولید می‌شود.
میانای کاربر مجموعه‌ای از روش‌ها و انواع. به کلاسی در C++‎ یا Java ترجمه می‌شود. همه روش‌های موجود در میان‌گیری در یک جهت فراخوانده می‌شوند: فرایند کارخواه روش‌های پیاده‌سازی‌شده توسط فرایند سرور را فرا می‌خواند.
یک‌طرفه وقتی روی روش HIDL اعمال می‌شود، نشان می‌دهد که روش هیچ مقداری برنمی‌گرداند و مسدود نمی‌شود.
بسته مجموعه‌ای از میاناهای کاربری و انواع داده که نسخه مشترکی دارند.
گذرگاه حالت HIDL که در آن سرور کتابخانه مشترکی است که کارخواه آن را dlopen می‌کند. در حالت عبور، کارخواه و کارساز فرایند یکسانی دارند اما پایه‌های کد جداگانه‌ای دارند. فقط برای آوردن پایه‌های کد قدیمی به مدل HIDL استفاده می‌شود. همچنین Binderized را ببینید.
سرور فرایندی که روش‌های یک میان‌گیری را پیاده‌سازی می‌کند. انتقال مستقیم را نیز ببینید.
حمل‌ونقل زیرساخت HIDL که داده‌ها را بین سرور و کارخواه منتقل می‌کند.
نسخه نسخه بسته. شامل دو عدد صحیح، اصلی و فرعی است. افزایش‌های نسخه جزئی ممکن است انواع و روش‌ها را اضافه کند (اما تغییر ندهد).

طراحی HIDL

هدف HIDL این است که چارچوب Android بتواند بدون نیاز به بازسازی HALها جایگزین شود. فروشندگان یا سازندگان SOC لایه‌های انتزاعی سخت‌افزار را می‌سازند و آن‌ها را در بخش /vendor در دستگاه قرار می‌دهند، که این کار باعث می‌شود چارچوب Android در بخش خودش با OTA جایگزین شود بدون اینکه لایه‌های انتزاعی سخت‌افزار بازهمگردانی شوند.

طراحی HIDL نگرانی‌های زیر را متعادل می‌کند:

  • هم‌کنش‌پذیری. میان فرایندهایی که ممکن است با معماری‌های مختلف، زنجیره‌های ابزار، و پیکربندی‌های ساخت کامپایل شوند، میاناهای قابلیت عملکرد متقابل قابل‌اعتمادی ایجاد کنید. میاناهای HIDL نسخه‌بندی شده‌اند و پس‌از انتشار نمی‌توان آن‌ها را تغییر داد.
  • کارایی. ‫HIDL تلاش می‌کند تعداد عملیات کپی را به حداقل برساند. داده‌های تعریف‌شده با HIDL به کد C++‎ در ساختارهای داده چیدمان استاندارد C++‎ تحویل داده می‌شود که می‌توان بدون باز کردن بسته‌بندی از آن‌ها استفاده کرد. ‫HIDL همچنین رابط‌های حافظه مشترک ارائه می‌دهد و ازآنجایی‌که RPCها ذاتاً تا حدودی کند هستند، HIDL از دو روش برای انتقال داده بدون استفاده از فراخوانی RPC پشتیبانی می‌کند: حافظه مشترک و صف پیام سریع (FMQ).
  • شهودی. ‫HIDL بااستفاده از تنها in پارامتر برای RPC (به زبان تعریف واسط Android (AIDL) مراجعه کنید) از مشکلات پیچیده مالکیت حافظه جلوگیری می‌کند؛ مقادیری که نمی‌توانند به‌طور کارآمد از روش‌ها برگردانده شوند ازطریق توابع برگشتی برگردانده می‌شوند. نه ارسال داده به HIDL برای انتقال و نه دریافت داده از HIDL مالکیت داده را تغییر نمی‌دهد—مالکیت همیشه با تابع فراخواننده باقی می‌ماند. داده‌ها فقط باید درطول مدت تابع فراخوانده‌شده باقی بمانند و ممکن است بلافاصله پس‌از بازگشت تابع فراخوانده‌شده ازبین بروند.

استفاده از حالت «انتقال مستقیم»

برای به‌روزرسانی دستگاه‌های دارای نسخه‌های قدیمی‌تر Android به Android O، می‌توانید هر دو HAL مرسوم (و قدیمی) را در یک میانای HIDL جدید که در حالت‌های binderized و هم‌فرایندی (عبوری) به HAL سرویس می‌دهد بپیچید. این بسته‌بندی برای هر دو «لایه انتزاعی سخت‌افزار» و چارچوب Android شفاف است.

حالت عبور فقط برای پیاده‌سازی‌ها و مشتریان C++ دردسترس است. دستگاه‌هایی که نسخه‌های قدیمی‌تر Android را اجرا می‌کنند، HALهای نوشته‌شده در Java ندارند، بنابراین ‫HALهای Java ذاتاً binderized هستند.

وقتی فایل .hal تدوین می‌شود، hidl-gen علاوه‌بر سرایندهای مورد استفاده برای ارتباط با binder، یک فایل سرایند عبوری اضافی BsFoo.h تولید می‌کند؛ این سرایند توابعی را تعریف می‌کند که باید dlopen شوند. ازآنجایی‌که HALهای عبوری در همان فرایندی اجرا می‌شوند که در آن فراخوانده می‌شوند، در اکثر موارد روش‌های عبوری با فراخوانی مستقیم تابع (همان رشته) فراخوانده می‌شوند. روش‌های oneway در رشته خودشان اجرا می‌شوند زیرا قرار نیست منتظر بمانند تا HAL آن‌ها را پردازش کند (این یعنی هر HAL که از روش‌های oneway در حالت عبور استفاده می‌کند باید رشته-امن باشد).

با درنظر گرفتن IFoo.hal، BsFoo.h روش‌های تولیدشده با HIDL را می‌پوشاند تا ویژگی‌های بیشتری ارائه دهد (مثلاً اجرای تراکنش‌های oneway در رشته‌ای دیگر). این فایل شبیه به BpFoo.h است، اما به‌جای انتقال تماس‌های IPC بااستفاده از binder، توابع موردنظر مستقیماً فراخوانی می‌شوند. اجراهای آینده HAL ممکن است چندین اجرا ارائه دهند، مانند FooFast HAL و FooAccurate HAL. در چنین مواردی، فایلی برای هر پیاده‌سازی اضافی ایجاد می‌شود (برای نمونه، PTFooFast.cpp و PTFooAccurate.cpp).

Binderizing passthrough 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 گذر مستقیم را باز می‌کند و خود را به‌عنوان سرویس binderized ثبت می‌کند، و امکان استفاده از همان پیاده‌سازی HAL را هم به‌عنوان گذر مستقیم و هم binderized فراهم می‌کند.

با درنظر گرفتن نوع IFoo، می‌توانید sp<IFoo> IFoo::getService(string name, bool getStub) را فراخوانی کنید تا به نمونه‌ای از IFoo دسترسی پیدا کنید. اگر getStub درست باشد، getService سعی می‌کند HAL را فقط در حالت «دیدن محیط واقعی» باز کند. اگر getStub نادرست باشد، getService تلاش می‌کند سرویس پیوندشده‌ای پیدا کند؛ اگر این کار ناموفق بود، تلاش می‌کند سرویس عبوری را پیدا کند. از پارامتر getStub هرگز نباید استفاده شود مگراینکه در defaultPassthroughServiceImplementation باشد. (دستگاه‌هایی که با Android O راه‌اندازی می‌شوند، دستگاه‌های کاملاً binderized هستند، بنابراین باز کردن سرویس در حالت عبور مجاز نیست.)

دستور زبان 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