نسخه سازی رابط

HIDL نیاز دارد که هر رابط نوشته شده در HIDL نسخه شود. پس از انتشار یک رابط HAL، ثابت می شود و هر گونه تغییر بیشتر باید در نسخه جدیدی از آن رابط انجام شود. در حالی که یک رابط منتشر شده را نمی توان تغییر داد، می توان آن را توسط یک رابط دیگر گسترش داد.

ساختار کد HIDL

کد HIDL در انواع، رابط ها و بسته های تعریف شده توسط کاربر سازماندهی شده است :

  • انواع تعریف شده توسط کاربر (UDTs) . 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 داشته باشند.
    • رابط های جدید نیز می توانند به بسته جدید اضافه شوند.
    • تمام انواع داده‌های بسته والد در بسته جدید وجود دارند و می‌توان آنها را با روش‌های (احتمالاً پیاده‌سازی‌شده) از بسته قدیمی مدیریت کرد.
    • انواع داده‌های جدید را می‌توان برای استفاده با روش‌های جدید رابط‌های موجود ارتقا یافته یا با رابط‌های جدید اضافه کرد.
  • توسعه پذیری سازگار با عقب در سطح رابط . بسته جدید همچنین می‌تواند بسته اصلی را با متشکل از رابط‌های منطقی مجزا گسترش دهد که به سادگی عملکردهای اضافی را ارائه می‌کنند و نه اصلی. برای این منظور، موارد زیر ممکن است مطلوب باشد:
    • رابط های موجود در بسته جدید نیاز به توسل به انواع داده های بسته قدیمی دارند.
    • رابط های موجود در بسته جدید می توانند رابط های یک یا چند بسته قدیمی را گسترش دهند.
  • ناسازگاری اصلی به عقب را گسترش دهید . این یک نسخه اصلی ارتقاء بسته است و نیازی به هیچ ارتباطی بین این دو نیست. تا جایی که وجود دارد، می توان آن را با ترکیبی از انواع نسخه قدیمی بسته و ارث بردن زیرمجموعه ای از واسط های بسته قدیمی بیان کرد.

ساختار رابط

برای یک رابط با ساختار خوب، افزودن انواع جدیدی از عملکردها که بخشی از طراحی اصلی نیستند، باید نیاز به تغییر در رابط HIDL داشته باشد. برعکس، اگر می‌توانید یا انتظار داشته باشید که در هر دو طرف رابط تغییری ایجاد کنید که عملکرد جدیدی را بدون تغییر خود اینترفیس معرفی می‌کند، آنگاه رابط ساختاری ندارد.

Treble از فروشنده و اجزای سیستم که به طور جداگانه کامپایل شده اند پشتیبانی می کند که در آن vendor.img در یک دستگاه و system.img می توانند به طور جداگانه کامپایل شوند. تمام تعاملات بین vendor.img و system.img باید به طور صریح و کامل تعریف شوند تا بتوانند سال ها به کار خود ادامه دهند. این شامل بسیاری از سطوح API است، اما یک سطح اصلی مکانیسم IPC است که HIDL برای ارتباطات بین فرآیندی در مرز system.img / vendor.img استفاده می کند.

الزامات

تمام داده های ارسال شده از طریق HIDL باید به صراحت تعریف شوند. برای اطمینان از اینکه یک پیاده سازی و کلاینت می تواند به کار با هم ادامه دهد، حتی زمانی که به طور جداگانه کامپایل شده یا به طور مستقل توسعه یافته است، داده ها باید از شرایط زیر پیروی کنند:

  • می توان مستقیماً در HIDL (با استفاده از ساختارهای enums و غیره) با نام های معنایی و معنی توصیف کرد.
  • می توان با یک استاندارد عمومی مانند 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 آنهایی هستند که توسط گوگل مشخص شده اند. بسته هایی که متعلق به آنها هستند با 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 در آن تعریف شده است و نسخه بسته است. نام کاملاً واجد شرایط فقط زمانی استفاده می‌شود که نمونه‌هایی از نوع تعریف شده باشند و نه در جایی که خود نوع تعریف شده است. برای مثال، فرض کنید بسته android.hardware.nfc, نسخه 1.0 ساختاری به نام 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 شناخته شود. در سطح بسته یا رابط، باید به Bar via Foo مراجعه کنید: 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 یک نوع enum باشد، پس هر مقدار از نوع enum یک نام کاملاً واجد شرایط دارد که با نام کاملاً واجد شرایط نوع enum شروع می‌شود، سپس یک دونقطه و سپس نام مقدار enum قرار می‌گیرد. به عنوان مثال، فرض کنید بسته 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 دقیقاً همان نام کاملاً واجد شرایط برای نوع enum است.
  • VALUE نام مقدار است.

قوانین استنتاج خودکار

یک نام UDT کاملا واجد شرایط نیازی به ذکر نیست. یک نام UDT می تواند با خیال راحت موارد زیر را حذف کند:

  • بسته، به عنوان مثال @1.0::IFoo.Type
  • هم بسته و هم نسخه، به عنوان مثال IFoo.Type

HIDL سعی می کند نام را با استفاده از قوانین تداخل خودکار تکمیل کند (عدد قانون کمتر به معنای اولویت بالاتر است).

قانون 1

اگر بسته و نسخه ای ارائه نشده باشد، جستجوی نام محلی انجام می شود. مثال:

interface Nfc {
    typedef string NfcErrorMessage;
    send(NfcData d) generates (@1.0::NfcStatus s, NfcErrorMessage m);
};

NfcErrorMessage به صورت محلی جستجو می شود و typedef بالای آن پیدا می شود. NfcData نیز به صورت محلی جستجو می شود، اما از آنجایی که به صورت محلی تعریف نشده است، از قانون 2 و 3 استفاده می شود. @1.0::NfcStatus یک نسخه ارائه می کند، بنابراین قانون 1 اعمال نمی شود.

قانون 2

اگر قانون 1 ناموفق باشد و یک مؤلفه از نام کاملاً واجد شرایط (بسته، نسخه یا بسته و نسخه) وجود نداشته باشد، مؤلفه به طور خودکار با اطلاعات بسته فعلی پر می شود. سپس کامپایلر 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
};

قانون 3

اگر قانون 2 نتواند مطابقت ایجاد کند (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 یک خطا ایجاد می کند.

مثال

با استفاده از قانون 2، یک نوع وارداتی تعریف شده در بسته فعلی نسبت به نوع وارداتی از بسته دیگر ترجیح داده می شود:

// 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 با استفاده از قانون 2 درون یابی می شود، اما نمی توان آن را پیدا کرد زیرا bar/1.0/IFooCallback.hal به طور خودکار وارد نمی شود (همانطور که types.hal است). بنابراین، قانون 3 آن را به android.hardware.foo@1.0::IFooCallback حل می کند که از طریق import android.hardware.foo@1.0; ).

انواع.حال

هر بسته 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 آن.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 از وراثت چندگانه پشتیبانی نمی کند.

نمونه های نسخه uprev زیر از بسته زیر استفاده می کنند:

// 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);
}

قوانین Uprev

برای تعریف یک بسته package@major.minor ، A یا تمام B باید درست باشد:

قانون الف "یک نسخه ابتدایی مینور است": همه نسخه های مینور قبلی، package@major.0 ، package@major.1 ، ...، package@major.(minor-1) نباید تعریف شوند.
یا
قانون ب

همه موارد زیر درست است:

  1. "نسخه مینور قبلی معتبر است": package@major.(minor-1) باید تعریف شود و از همان قانون A پیروی کند (هیچ یک از package@major.0 از طریق package@major.(minor-2) تعریف شده است) یا قانون B (اگر یک بالارفتن از @major.(minor-2) باشد);

    و

  2. "حداقل یک رابط با همین نام به ارث برده شود": یک رابط package@major.minor::IFoo وجود دارد که package@major.(minor-1)::IFoo (اگر بسته قبلی دارای یک رابط باشد);

    و

  3. "بدون واسط ارثی با نام متفاوت": package@major.minor::IBar نباید وجود داشته باشد که package@major.(minor-1)::IBaz ، که در آن IBar و IBaz دو نام متفاوت هستند. اگر رابطی با همین نام وجود دارد، package@major.minor::IBar باید package@major.(minor-k)::IBar گسترش دهد تا هیچ IBar با k کوچکتر وجود نداشته باشد.

به دلیل قانون 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 ندارد. @1.0 .) بنابراین، @3.2::IExtFoo می‌تواند @1.0::IFoo را گسترش دهد.

به شرطی که نام بسته متفاوت باشد، package@major.minor::IBar می تواند از یک رابط با نام متفاوت گسترش یابد (به عنوان مثال، android.hardware.bar@1.0::IBar می تواند android.hardware.baz@2.2::IBaz گسترش دهد. ). اگر یک رابط صریحاً یک super type را با کلمه کلیدی extend اعلام نکند، android.hidl.base@1.0::IBase گسترش می دهد (به جز خود IBase ).

B.2 و B.3 باید همزمان رعایت شوند. برای مثال، حتی اگر 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 ، این هنوز یک آپدیت معتبر نیست.

رابط های Uprev

برای بالا بردن 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 است. در حالی که هیچ UDT جدیدی در نسخه 1.1 بسته اضافه نشده است، ارجاع به UDT ها در نسخه 1.0 همچنان مورد نیاز است، بنابراین وارد کردن در سطح بسته در types.hal می شود. (همان اثر را می‌توان با وارد کردن سطح رابط در IQuux.hal به دست آورد.)

در extends @1.0::IQuux در اعلان IQuux ، نسخه IQuux را مشخص کردیم که در حال به ارث بردن است (ابهام‌زدایی لازم است زیرا IQuux برای اعلام یک رابط و برای ارث بردن از یک رابط استفاده می‌شود). از آنجایی که اعلان‌ها به سادگی نام‌هایی هستند که تمام ویژگی‌های بسته و نسخه را در سایت اعلان به ارث می‌برند، ابهام‌زدایی باید در نام رابط پایه باشد. ما می توانستیم از UDT کاملاً واجد شرایط نیز استفاده کنیم، اما این کار اضافی بود.

رابط جدید IQuux متد را fromFooToBar() مجدداً اعلام نمی‌کند و از @1.0::IQuux به ارث می‌برد. به سادگی متد جدیدی را که 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‌ها بر اساس ترکیب قابل بیان هستند.

با این حال، یک نوع رابطه کاملاً تعریف شده است و باید اجرا شود: وراثت سازگار با سطح بسته . در این سناریو، بسته والد بسته ای است که از آن به ارث رسیده است و بسته فرزند همان بسته ای است که والد را گسترش می دهد. قوانین وراثت سازگار با سطح بسته به شرح زیر است:

  1. تمام واسط های سطح بالای بسته والد توسط رابط های بسته فرزند به ارث برده می شوند.
  2. اینترفیس‌های جدید را نیز می‌توان به بسته جدید اضافه کرد (بدون محدودیت در رابطه با رابط‌های دیگر در بسته‌های دیگر).
  3. انواع داده‌های جدید را می‌توان برای استفاده با روش‌های جدید رابط‌های موجود ارتقا یافته یا با رابط‌های جدید اضافه کرد.

این قوانین را می‌توان با استفاده از وراثت در سطح رابط HIDL و ترکیب UDT پیاده‌سازی کرد، اما برای دانستن این که این روابط یک پسوند بسته سازگار با عقب را تشکیل می‌دهند، به دانش فراسطحی نیاز دارد. این دانش به شرح زیر استنباط می شود:

اگر بسته ای این الزام را برآورده کند، hidl-gen قوانین سازگاری با عقب را اعمال می کند.