زبان تعریف میانای 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 هستند.
فایلهای سرایند Passthrough
وقتی فایل .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