HIDL نیاز دارد که هر میانای نوشتهشده در HIDL نسخهبندی شود. پساز انتشار میانای HAL، این میانا ثابت میشود و هرگونه تغییر بعدی باید در نسخه جدیدی از آن میانا اعمال شود. اگرچه نمیتوان واسط منتشرشدهای را تغییر داد، اما میتوان آن را با واسط دیگری گسترش داد.
ساختار کد HIDL
کد HIDL در انواع، میانها، و بستههای تعریفشده توسط کاربر سازماندهی میشود:
- انواع تعریفشده توسط کاربر (UDT). HIDL دسترسی به مجموعهای از انواع دادههای ابتدایی را فراهم میکند که میتوان از آنها برای ترکیب انواع پیچیدهتر ازطریق ساختارها، اتحادیهها، و شمارشها استفاده کرد. UDTها به روشهای میانها منتقل میشوند و میتوانند در سطح بسته (مشترک برای همه میانها) یا بهصورت محلی برای یک میان تعریف شوند.
- میانها. بهعنوان واحد سازنده پایه HIDL، یک میانای شامل UDT و بیانیههای روش است. میاناهای کاربری میتوانند از میانای کاربری دیگری نیز ارثبری کنند.
- بستهها. میاناهای HIDL مرتبط و انواع دادههایی را که روی آنها عمل میکنند سازماندهی میکند. بسته با نام و نسخه شناسایی میشود و
شامل موارد زیر است:
- فایل تعریف نوع داده بهنام
types.hal. - صفر یا چند واسط، هرکدام در فایل
.halخود.
- فایل تعریف نوع داده بهنام
فایل تعریف نوع داده types.hal فقط حاوی UDT است (همه UDTهای سطح بسته در یک فایل نگهداری میشوند). بازنماییها به زبان مقصد برای همه میاناهای بسته دردسترس است.
فلسفه نسخهبندی
بسته HIDL (مانند android.hardware.nfc)، پساز
انتشار برای نسخهای خاص (مانند 1.0)، تغییرناپذیر است؛
نمیتوان آن را تغییر داد. اصلاحات در رابطهای موجود در بسته یا هرگونه
تغییر در UDTهای آن فقط در بسته دیگری میتواند انجام شود.
در HIDL، نسخهبندی در سطح بسته اعمال میشود، نه در سطح میانای کاربری، و همه میاناهای کاربری و UDTها در یک بسته نسخه یکسانی دارند. نسخههای بسته از نسخهبندی معنایی بدون سطح وصله و عناصر فراداده ساخت پیروی میکنند. در یک بسته مشخص، ارتقای نسخه جزئی به این معنی است که نسخه جدید بسته با نسخه قدیمی سازگار با نسخه قدیمی است و ارتقای نسخه عمده به این معنی است که نسخه جدید بسته با نسخه قدیمی سازگار با نسخه قدیمی نیست.
ازنظر مفهومی، یک بسته میتواند به یکی از چند روش زیر با بسته دیگری مرتبط باشد:
- اصلاً.
- گسترشپذیری سازگار با نسخه قدیمی در سطح بسته. این
برای نسخههای جزئی جدید بسته (بازبینی افزایشی بعدی) رخ میدهد؛
بسته جدید همان نام و نسخه عمده بسته قدیمی را دارد، اما نسخه جزئی آن
بالاتر است. ازنظر عملکرد، بسته جدید ابرمجموعهای از بسته قدیمی است، یعنی:
- میاناهای سطح بالای بسته والد در بسته جدید وجود دارد،
هرچند ممکن است میانهها روشهای جدید، UDTهای محلی میانای جدید (افزونه سطح میانای شرحدادهشده در زیر)، و UDTهای جدید در
types.halداشته باشند. - میاناهای جدید را نیز میتوان به بسته جدید اضافه کرد.
- همه انواع داده بسته والد در بسته جدید وجود دارد و میتواند با روشهای (احتمالاً دوباره پیادهسازیشده) بسته قدیمی مدیریت شود.
- انواع داده جدید را نیز میتوان برای استفاده در روشهای جدید رابطهای موجود ارتقایافته یا در رابطهای جدید اضافه کرد.
- میاناهای سطح بالای بسته والد در بسته جدید وجود دارد،
هرچند ممکن است میانهها روشهای جدید، UDTهای محلی میانای جدید (افزونه سطح میانای شرحدادهشده در زیر)، و UDTهای جدید در
- گسترشپذیری سازگار با نسخه قدیمی در سطح میانای. بسته جدید همچنین میتواند بسته اصلی را با داشتن میاناهای منطقاً جداگانه که صرفاً عملکرد اضافی ارائه میدهند، و نه عملکرد اصلی، گسترش دهد.
برای این منظور، موارد زیر ممکن است مطلوب باشد:
- میاناهای بسته جدید باید به انواع داده بسته قدیمی دسترسی داشته باشند.
- میاناهای بسته جدید میتوانند میاناهای یک یا چند بسته قدیمی را گسترش دهند.
- عدم پسسازگاری اصلی را گسترش دهید. این یک نسخه عمده ارتقایافته از بسته است و نیازی به وجود هیچ ارتباطی بین این دو نیست. تا جایی که وجود دارد، میتوان آن را با ترکیبی از انواع نسخه قدیمیتر بسته و وراثت زیرمجموعهای از میاناهای بسته قدیمی بیان کرد.
ساختاردهی میانا
برای داشتن میانایی با ساختار مناسب، افزودن انواع جدیدی از عملکرد که بخشی از طراحی اصلی نیستند باید مستلزم اصلاح میانای HIDL باشد. برعکس، اگر بتوانید یا انتظار داشته باشید که در هر دو طرف میانایی که عملکرد جدیدی را بدون تغییر خود میانجی معرفی میکند، تغییر ایجاد کنید، آنگاه میانجی ساختاریافته نیست.
Treble از عناصر سیستم و فروشنده که بهصورت جداگانه تدوین شدهاند پشتیبانی میکند و در آن vendor.img در دستگاه و system.img میتواند بهصورت جداگانه تدوین شود. همه تعاملات بین vendor.img و system.img باید بهطور صریح و کامل تعریف شود تا بتواند برای سالهای طولانی به کار خود ادامه دهد. این شامل سطوح API زیادی میشود، اما سطح اصلی
سازوکار IPC است که HIDL برای ارتباط بینپردازشی در مرز
system.img/vendor.img استفاده میکند.
الزامات
همه دادههایی که ازطریق HIDL منتقل میشوند باید بهطور صریح تعریف شوند. برای اطمینان از اینکه پیادهسازی و مشتری میتوانند حتی درصورت جداگانه کامپایل شدن یا توسعه مستقل، با هم کار کنند، دادهها باید از الزامات زیر پیروی کنند:
- میتواند مستقیماً در HIDL (بااستفاده از ساختارها، شمارشها، و غیره) با نامهای معنایی و مفهوم توصیف شود.
- میتواند با استاندارد عمومی مانند ISO/IEC 7816 توصیف شود.
- میتواند با استاندارد سختافزار یا چیدمان فیزیکی سختافزار توصیف شود.
- درصورت لزوم، میتواند دادههای مبهم (مثل کلیدهای عمومی، شناسهها، و غیره) باشد.
اگر از دادههای مبهم استفاده میشود، فقط یک طرف رابط HIDL باید آن را بخواند. برای مثال، اگر کد vendor.img مؤلفهای را در
system.img با پیام رشتهای یا vec<uint8_t>
داده ارائه دهد، آن داده نمیتواند توسط خود system.img تجزیه شود؛ فقط میتواند
به vendor.img برگردانده شود تا تفسیر شود. وقتی
مقدار از vendor.img به کد فروشنده در
system.img یا به دستگاه دیگری منتقل میشود، قالب دادهها و نحوه
تفسیر آن باید دقیقاً شرح داده شود و همچنان بخشی از
میانای است.
رهنمودها
شما باید بتوانید پیادهسازی یا کلاینت HAL را فقط بااستفاده از فایلهای hal بنویسید (یعنی نباید به منبع Android یا استانداردهای عمومی نگاه کنید). توصیه میکنیم رفتار دقیق موردنیاز را مشخص کنید. بیانیههایی مانند «پیادهسازی ممکن است A یا B را انجام دهد» پیادهسازیها را تشویق میکند تا با کارخواهانی که با آنها توسعه یافتهاند درهم تنیده شوند.
طرحبندی کد HIDL
HIDL شامل بستههای اصلی و فروشنده است.
میاناهای اصلی HIDL میاناهایی هستند که Google مشخص کرده است. بستههایی که به آنها تعلق دارند
با android.hardware. شروع میشوند و براساس زیرسیستم نامگذاری میشوند،
احتمالاً با سطوح تودرتوی نامگذاری. برای مثال، نام بسته NFC
android.hardware.nfc و نام بسته دوربین
android.hardware.camera است. بهطورکلی، بسته اصلی نام
android.hardware.[name1].[name2]….
بستههای HIDL علاوه بر نامشان نسخه هم دارند. برای مثال، بسته
android.hardware.camera ممکن است در نسخه 3.4 باشد؛ این موضوع
مهم است، زیرا نسخه بسته بر جایگاه آن در درخت منبع تأثیر میگذارد.
همه بستههای اصلی در سیستم ساخت زیر hardware/interfaces/ قرار میگیرند. بسته
android.hardware.[name1].[name2]…
در نسخه $m.$n تحت
hardware/interfaces/name1/name2/…/$m.$n/ است؛ بسته
android.hardware.camera نسخه 3.4 در دایرکتوری
hardware/interfaces/camera/3.4/. است. نگاشت کدبندیشدهای بین پیشوند بسته android.hardware. و مسیر
hardware/interfaces/ وجود دارد.
بستههای غیرهستهای (فروشنده) بستههایی هستند که توسط فروشنده SoC یا ODM تولید میشوند. پیشوند برای بستههای غیرهستهای vendor.$(VENDOR).hardware. است که در آن $(VENDOR) به فروشنده SoC یا OEM/ODM اشاره دارد. این به مسیر
vendor/$(VENDOR)/interfaces در درخت نگاشت میشود (این نگاشت نیز
سختکدگذاری شده است).
نامهای نوع تعریفشده توسط کاربر کاملاً واجدشرایط
در HIDL، هر UDT دارای یک نام کاملاً واجد شرایط است که شامل نام UDT،
نام بستهای که UDT در آن تعریف شده است، و نسخه بسته است. نام کاملاً واجدشرایط فقط زمانی استفاده میشود که نمونههای نوعی اعلام شده باشند و نه زمانی که خود نوع تعریف شده باشد. برای مثال، فرض کنید نسخه 1.0 بسته android.hardware.nfc, ساختاری بهنام NfcData را تعریف میکند. در سایت اظهارنامه (چه در
types.hal یا در اظهارنامه رابط)، اظهارنامه
بهسادگی بیان میکند:
struct NfcData {
vec<uint8_t> data;
};هنگام تعریف نمونهای از این نوع (چه در ساختار داده و چه بهعنوان پارامتر روش)، از نام نوع کاملاً واجدشرایط استفاده کنید:
android.hardware.nfc@1.0::NfcData
نحو کلی عبارت است از
PACKAGE@VERSION::UDT، که در آن:
-
PACKAGEنام بسته HIDL است که با نقطه از هم جدا شده است (برای نمونه،android.hardware.nfc). VERSIONقالب نسخه اصلی.فرعی بسته است که با نقطه از هم جدا شدهاند (مثلاً1.0).-
UDTنام جداشده با نقطه یک HIDL UDT است. ازآنجاییکه HIDL از UDTهای تودرتو پشتیبانی میکند و میاناهای HIDL میتوانند حاوی UDT (نوعی اعلام تودرتو) باشند، از نقطه برای دسترسی به نامها استفاده میشود.
برای مثال، اگر اعلان تودرتوی زیر در فایل انواع رایج در بسته android.hardware.example نسخه 1.0 تعریف شده باشد:
// types.hal package android.hardware.example@1.0; struct Foo { struct Bar { // … }; Bar cheers; };
نام کاملاً واجدشرایط برای Bar
android.hardware.example@1.0::Foo.Bar است. اگر علاوهبر اینکه در بسته بالا باشد، اظهارنامه تودرتو در واسطی بهنام
IQuux باشد:
// IQuux.hal package android.hardware.example@1.0; interface IQuux { struct Foo { struct Bar { // … }; Bar cheers; }; doSomething(Foo f) generates (Foo.Bar fb); };
نام کاملاً واجدشرایط برای Bar این است:
android.hardware.example@1.0::IQuux.Foo.Bar.
در هر دو مورد، Bar فقط در محدوده اظهارنامه Foo میتواند بهعنوان Bar نامیده شود. در سطح بسته یا میانای، باید ازطریق Foo به Bar ارجاع دهید:
Foo.Bar، مانند اعلام روش doSomething در بالا. یا میتوانید روش را بهصورت مفصلتر بهصورت زیر تعریف کنید:
// IQuux.hal doSomething(android.hardware.example@1.0::IQuux.Foo f) generates (android.hardware.example@1.0::IQuux.Foo.Bar fb);
مقادیر فهرست کاملاً واجدشرایط
اگر UDT نوع شمارشی باشد، هر مقدار نوع شمارشی نام کاملی دارد که با نام کامل نوع شمارشی شروع میشود، سپس دونقطه و بعداز آن نام مقدار شمارشی میآید. برای مثال،
فرض کنید بسته android.hardware.nfc, نسخه 1.0
نوع enum NfcStatus را تعریف میکند:
enum NfcStatus { STATUS_OK, STATUS_FAILED };
هنگام ارجاع به STATUS_OK، نام کاملاً واجدشرایط عبارت است از:
android.hardware.nfc@1.0::NfcStatus:STATUS_OK
ساختار کلی بهصورت زیر است:
PACKAGE@VERSION::UDT:VALUE،
که در آن:
-
PACKAGE@VERSION::UDTنام کاملاً واجدشرایط دقیقاً یکسانی برای نوع شمارشی است. -
VALUEنام مقدار است.
قوانین استنباط خودکار
نام UDT کاملاً واجدشرایط نیازی به مشخص شدن ندارد. نام UDT میتواند بهطور ایمن موارد زیر را حذف کند:
- بسته، مثلاً
@1.0::IFoo.Type - هم بسته و هم نسخه، برای مثال
IFoo.Type
HIDL تلاش میکند نام را بااستفاده از قوانین تداخل خودکار تکمیل کند (شماره قانون پایینتر بهمعنای اولویت بالاتر است).
قانون ۱
اگر بسته و نسخهای ارائه نشود، جستجوی نام محلی انجام میشود. مثال:
interface Nfc { typedef string NfcErrorMessage; send(NfcData d) generates (@1.0::NfcStatus s, NfcErrorMessage m); };
NfcErrorMessage بهصورت محلی جستجو میشود و typedef
بالای آن پیدا میشود. NfcData بهصورت محلی نیز جستجو میشود، اما چون بهصورت محلی تعریف نشده است، از قانون ۲ و ۳ استفاده میشود. @1.0::NfcStatus
نسخهای ارائه میدهد، بنابراین قانون ۱ اعمال نمیشود.
قانون ۲
اگر قانون ۱ اجرا نشود و یکی از عناصر نام کاملاً واجدشرایط وجود نداشته باشد
(بسته، نسخه، یا بسته و نسخه)، عنصر با
اطلاعات بسته فعلی بهطور خودکار تکمیل میشود. سپس کامپایلر HIDL در فایل فعلی (و همه واردشدهها) جستجو میکند تا نام کاملاً واجدشرایط تکمیلشده خودکار را پیدا کند.
بااستفاده از مثال بالا، فرض کنید اعلامیه ExtendedNfcData
در همان بسته (android.hardware.nfc) در همان
نسخه (1.0) NfcData بهصورت زیر انجام شده است:
struct ExtendedNfcData {
NfcData base;
// … additional members
};گردآورنده HIDL نام بسته و نام نسخه را از بسته فعلی تکمیل میکند تا نام UDT کاملاً واجدشرایط را تولید کند
android.hardware.nfc@1.0::NfcData. ازآنجاییکه این نام در بسته فعلی وجود دارد (با فرض اینکه بهدرستی وارد شده باشد)، برای اعلان استفاده میشود.
نامی در بسته فعلی فقط درصورت درست بودن یکی از موارد زیر وارد میشود:
- بهطور صریح با عبارت
importوارد شده است. - در
types.halدر بسته کنونی تعریف شده است
اگر NfcData فقط براساس شماره نسخه واجدشرایط شده باشد،
همین فرایند دنبال میشود:
struct ExtendedNfcData { // autofill the current package name (android.hardware.nfc) @1.0::NfcData base; // … additional members };
قانون ۳
اگر قانون ۲ نتواند مطابقت پیدا کند (UDT در بسته فعلی تعریف نشده است)،
گردآورنده HIDL در همه بستههای واردشده بهدنبال مطابقت میگردد.
بااستفاده از مثال بالا، فرض کنید ExtendedNfcData در
نسخه 1.1 بسته android.hardware.nfc تعریف شده است،
1.1 1.0 را همانطور که باید وارد میکند (به
افزونههای سطح بسته مراجعه کنید)، و تعریف
فقط نام UDT را مشخص میکند:
struct ExtendedNfcData {
NfcData base;
// … additional members
};گردآورنده بهدنبال هر UDT بهنام NfcData میگردد و یکی را در
android.hardware.nfc در نسخه 1.0 پیدا میکند که منجر به
یک UDT کاملاً واجدشرایط android.hardware.nfc@1.0::NfcData میشود. اگر بیشاز یک مورد منطبق برای UDT نیمهواجدشرایط دادهشده پیدا شود، کامپایلر HIDL خطا میدهد.
مثال
بااستفاده از قانون ۲، نوع واردشدهای که در بسته فعلی تعریف شده است بر نوع واردشده از بسته دیگر ترجیح داده میشود:
// hardware/interfaces/foo/1.0/types.hal package android.hardware.foo@1.0; struct S {}; // hardware/interfaces/foo/1.0/IFooCallback.hal package android.hardware.foo@1.0; interface IFooCallback {}; // hardware/interfaces/bar/1.0/types.hal package android.hardware.bar@1.0; typedef string S; // hardware/interfaces/bar/1.0/IFooCallback.hal package android.hardware.bar@1.0; interface IFooCallback {}; // hardware/interfaces/bar/1.0/IBar.hal package android.hardware.bar@1.0; import android.hardware.foo@1.0; interface IBar { baz1(S s); // android.hardware.bar@1.0::S baz2(IFooCallback s); // android.hardware.foo@1.0::IFooCallback };
Sبهصورتandroid.hardware.bar@1.0::Sدرونیابی شده است و درbar/1.0/types.halپیدا شده است (زیراtypes.halبهطور خودکار وارد شده است).IFooCallbackبااستفاده از قانون ۲ بهعنوانandroid.hardware.bar@1.0::IFooCallbackمیانیابی میشود، اما نمیتوان آن را پیدا کرد زیراbar/1.0/IFooCallback.halبهطور خودکار درونبرد نمیشود (مثلtypes.hal). بنابراین، قانون ۳ آن را بهandroid.hardware.foo@1.0::IFooCallbackبهجای آن حل میکند، که ازطریقimport android.hardware.foo@1.0;وارد شده است).
types.hal
هر بسته HIDL حاوی یک فایل types.hal است که شامل UDTهایی است که بین همه میاناهای شرکتکننده در آن بسته همرسانی میشود. انواع HIDL
همیشه عمومی هستند؛ صرفنظر از اینکه UDT در
types.hal یا در یک بیانیه رابط اعلام شده باشد، این انواع
در خارج از محدوده تعریفشدهشان قابلدسترس هستند. types.hal
برای توصیف کردن API عمومی بسته درنظر گرفته نشده است، بلکه برای میزبانی UDTهایی
است که همه میاناهای درون بسته از آنها استفاده میکنند. بهدلیل ماهیت HIDL، همه UDTها
بخشی از میانای هستند.
types.hal از UDT و import بیانیه تشکیل شده است.
چون types.hal برای همه واسطهای
بسته دردسترس قرار میگیرد (وارد کردن ضمنی است)، این عبارتهای import
طبق تعریف در سطح بسته هستند. UDTها در types.hal همچنین میتوانند
UDTها و میاناهای واردشده را ترکیب کنند.
برای مثال، برای IFoo.hal:
package android.hardware.foo@1.0; // whole package import import android.hardware.bar@1.0; // types only import import android.hardware.baz@1.0::types; // partial imports import android.hardware.qux@1.0::IQux.Quux; // partial imports import android.hardware.quuz@1.0::Quuz;
موارد زیر وارد میشوند:
-
android.hidl.base@1.0::IBase(بهطور ضمنی) -
android.hardware.foo@1.0::types(بهطور ضمنی) - همه چیز در
android.hardware.bar@1.0(شامل همه میانها وtypes.halآن) -
types.halازandroid.hardware.baz@1.0::types(میاناهای موجود درandroid.hardware.baz@1.0وارد نمیشوند) -
IQux.halوtypes.halازandroid.hardware.qux@1.0 Quuzازandroid.hardware.quuz@1.0(با فرض اینکهQuuzدرtypes.halتعریف شده باشد، کل فایلtypes.halتجزیه میشود، اما انواع دیگر بهجزQuuzوارد نمیشوند).
نسخهبندی در سطح میانا
هر میانای درون بسته در فایل خودش قرار دارد. بستهای که
میانای آن متعلق به آن است در بالای میانای بااستفاده از
package عبارت اعلام میشود. پساز اظهارنامه بسته، صفر یا چند واردات سطح رابط (بسته جزئی یا کامل) ممکن است فهرست شود. برای مثال:
package android.hardware.nfc@1.0;
در HIDL، میانها میتوانند بااستفاده از کلیدواژه
extends از میانهای دیگر ارث ببرند. برای اینکه میانایی میانای دیگری را گسترش دهد، باید ازطریق عبارت import به آن دسترسی داشته باشد. نام میانای
درحال گسترش (میانای پایه) از قوانین تعیین صلاحیت نام نوع
توضیحدادهشده در بالا پیروی میکند. میانگیری فقط میتواند از یک میانگیری ارث ببرد؛
HIDL از ارثبری چندگانه پشتیبانی نمیکند.
مثالهای نسخهبندی ارتقای زیر از بسته زیر استفاده میکنند:
// types.hal package android.hardware.example@1.0 struct Foo { struct Bar { vec<uint32_t> val; }; }; // IQuux.hal package android.hardware.example@1.0 interface IQuux { fromFooToBar(Foo f) generates (Foo.Bar b); }
قوانین ارتقا
برای تعریف بسته package@major.minor، یا A یا همه B
باید درست باشد:
| قانون الف | «نسخه فرعی شروع است»: همه نسخههای فرعی قبلی،
package@major.0، package@major.1، …،
package@major.(minor-1) نباید تعریف شده باشند.
|
|---|
| قانون B | همه موارد زیر درست باشد:
|
|---|
بهدلیل قانون A:
- بسته میتواند با هر شماره نسخه فرعی شروع شود (برای مثال،
android.hardware.biometrics.fingerprintاز@2.1شروع میشود.) - مورد نیاز «
android.hardware.foo@1.0تعریف نشده است» یعنی دایرکتوریhardware/interfaces/foo/1.0نباید اصلاً وجود داشته باشد.
بااینحال، قانون A بر بستهای با همان نام بسته اما با نسخه عمده متفاوت تأثیر نمیگذارد (برای مثال،
android.hardware.camera.device هم @1.0 و هم
@3.2 را تعریف کرده است؛ @3.2 نیازی به تعامل با
@1.0 ندارد.) بنابراین، @3.2::IExtFoo میتواند
@1.0::IFoo را گسترش دهد.
بهشرطی که نام بسته متفاوت باشد،
package@major.minor::IBar میتواند از واسطی با نام
متفاوت گسترش یابد (برای مثال، android.hardware.bar@1.0::IBar میتواند
android.hardware.baz@2.2::IBaz را گسترش دهد). اگر واسطی
نوع فرادستی را با کلیدواژه extend بهطور صریح اعلام نکند،
android.hidl.base@1.0::IBase را گسترش میدهد (بهجز IBase
خودش).
بندهای ب.۲ و ب.۳ باید بهطور همزمان رعایت شوند. برای مثال، حتی اگر
android.hardware.foo@1.1::IFoo گسترش یابد
android.hardware.foo@1.0::IFoo تا قانون B.2 را بگذراند، اگر
android.hardware.foo@1.1::IExtBar گسترش یابد
android.hardware.foo@1.0::IBar، این همچنان ارتقا معتبری نیست.
میاناهای ارتقا
برای ارتقا دادن android.hardware.example@1.0 (تعریفشده در بالا) به
@1.1:
// types.hal package android.hardware.example@1.1; import android.hardware.example@1.0; // IQuux.hal package android.hardware.example@1.1 interface IQuux extends @1.0::IQuux { fromBarToFoo(Foo.Bar b) generates (Foo f); }
این import سطح بسته نسخه 1.0 از
android.hardware.example در types.hal است. اگرچه در نسخه 1.1 بسته
UDT جدیدی اضافه نشده است، ارجاع به UDT در
نسخه 1.0 همچنان لازم است، بنابراین وارد کردن سطح بسته
در types.hal لازم است. (همین اثر را میتوانستید با وارد کردن سطح رابط در IQuux.hal بهدست آورید.)
در extends @1.0::IQuux در بیانیه
IQuux، نسخه IQuux را که درحال
بهارث برده شدن است مشخص کردیم (رفع ابهام لازم است زیرا IQuux برای
اعلان کردن میانای و بهارث بردن از میانای استفاده میشود). ازآنجاییکه بیانیهها
صرفاً نامهایی هستند که همه مشخصههای بسته و نسخه را در سایت
بیانیه به ارث میبرند، ابهامزدایی باید در نام میانای پایه باشد؛ ما
میتوانستیم از UDT کاملاً واجدشرایط نیز استفاده کنیم، اما این کار
اضافی بود.
میانای جدید IQuux روشی را که از @1.0::IQuux بهارث میبرد دوباره اعلام نمیکند
fromFooToBar()؛ این میانای جدید فقط
روشی را که اضافه میکند فهرست میکند fromBarToFoo(). در HIDL، روشهای
بهارثرسیده نمیتوانند در واسطهای فرزند دوباره تعریف شوند، بنابراین
واسط IQuux نمیتواند روش fromFooToBar()
را بهطور صریح تعریف کند.
قراردادهای Uprev
گاهی اوقات نامهای میانای باید میانای توسعهدهنده را تغییر دهند. توصیه میکنیم که افزونههای enum، ساختارها، و اتحادها نامی مشابه با آنچه گسترش میدهند داشته باشند مگراینکه بهاندازه کافی متفاوت باشند که نام جدیدی را توجیه کند. نمونهها:
// in parent hal file enum Brightness : uint32_t { NONE, WHITE }; // in child hal file extending the existing set with additional similar values enum Brightness : @1.0::Brightness { AUTOMATIC }; // extending the existing set with values that require a new, more descriptive name: enum Color : @1.0::Brightness { HW_GREEN, RAINBOW };
اگر روشی میتواند نام معنایی جدیدی داشته باشد (برای مثال
fooWithLocation)، آن نام ترجیح داده میشود. درغیراینصورت، باید نام آن مشابه چیزی باشد که درحال گسترش است. برای مثال، روش
foo_1_1 در @1.1::IFoo میتواند عملکرد
روش foo در @1.0::IFoo را جایگزین کند، اگر نام جایگزین بهتری وجود نداشته باشد.
نسخهبندی در سطح بسته
نسخهبندی HIDL در سطح بسته رخ میدهد؛ پساز انتشار بسته، بسته تغییرناپذیر است (مجموعه رابطها و UDTهای آن قابل تغییر نیست). بستهها میتوانند به روشهای مختلفی با یکدیگر مرتبط باشند، که همه آنها ازطریق ترکیبی از وراثت سطح رابط و ساختن UDTها با ترکیب قابل بیان هستند.
بااینحال، یک نوع رابطه بهطور دقیق تعریف شده است و باید اعمال شود: وراثت سازگار با عقب در سطح بسته. در این سناریو، بسته والد بستهای است که از آن ارثبری میشود و بسته فرزند بستهای است که والد را گسترش میدهد. قوانین وراثت سازگار با نسخه قدیمی در سطح بسته به شرح زیر است:
- همه میاناهای سطح بالای بسته والد از میاناهای بسته فرزند به ارث میرسند.
- واسطهای جدید را نیز میتوان به بسته جدید اضافه کرد (هیچ محدودیتی درباره روابط با واسطهای دیگر در بستههای دیگر وجود ندارد).
- انواع دادههای جدید را نیز میتوان برای استفاده توسط روشهای جدید واسطهای موجود ارتقایافته یا توسط واسطهای جدید اضافه کرد.
این قوانین را میتوان بااستفاده از وراثت سطح میانای HIDL و ترکیببندی UDT پیادهسازی کرد، اما برای دانستن اینکه این روابط تشکیلدهنده افزونه بسته سازگار با نسخه قدیمی هستند، به دانش سطح متا نیاز است. این دانش بهصورت زیر استنباط میشود:
اگر بستهای این شرط را برآورده کند، hidl-gen قوانین سازگاری با نسخههای قدیمی را اعمال میکند.