تحديد الإصدارات

يتطلب HIDL إصدار كل واجهة مكتوبة في HIDL. بعد نشر واجهة HAL، يتم تجميدها ويجب إجراء أي تغييرات أخرى على الإصدار الجديد من تلك الواجهة. على الرغم من أنه قد لا يتم تعديل واجهة منشورة معينة، إلا أنه يمكن توسيعها بواسطة واجهة أخرى.

هيكل كود HIDL

يتم تنظيم كود HIDL في أنواع وواجهات وحزم محددة من قبل المستخدم:

  • الأنواع المعرفة من قبل المستخدم (UDTs) . يوفر HIDL إمكانية الوصول إلى مجموعة من أنواع البيانات البدائية التي يمكن استخدامها لإنشاء أنواع أكثر تعقيدًا عبر الهياكل والاتحادات والتعدادات. يتم تمرير UDTs إلى أساليب الواجهات، ويمكن تعريفها على مستوى الحزمة (المشتركة بين جميع الواجهات) أو محليًا على الواجهة.
  • واجهات . باعتبارها لبنة بناء أساسية لـ HIDL، تتكون الواجهة من UDT وإعلانات الطريقة. يمكن أن ترث الواجهات أيضًا من واجهة أخرى.
  • الحزم . ينظم واجهات HIDL ذات الصلة وأنواع البيانات التي تعمل عليها. يتم تعريف الحزمة بالاسم والإصدار وتتضمن ما يلي:
    • ملف تعريف نوع البيانات يسمى types.hal .
    • لا توجد واجهات أو أكثر، كل منها في ملف .hal الخاص بها.

types.hal ملفات تعريف نوع البيانات.hal تحتوي على UDTs فقط (يتم الاحتفاظ بجميع UDTs على مستوى الحزمة في ملف واحد). التمثيلات باللغة الهدف متاحة لجميع الواجهات الموجودة في الحزمة.

فلسفة الإصدار

حزمة HIDL (مثل android.hardware.nfc )، بعد نشرها لإصدار معين (مثل 1.0 )، تكون غير قابلة للتغيير؛ لا يمكن تغييره. لا يمكن إجراء التعديلات على الواجهات الموجودة في الحزمة أو أي تغييرات على UDTs إلا في حزمة أخرى .

في HIDL، يتم تطبيق الإصدار على مستوى الحزمة، وليس على مستوى الواجهة، وتشترك كافة الواجهات وUDTs الموجودة في الحزمة في نفس الإصدار. تتبع إصدارات الحزمة الإصدارات الدلالية دون مستوى التصحيح ومكونات بيانات تعريف البناء. ضمن حزمة معينة، يشير نتوء الإصدار الثانوي إلى أن الإصدار الجديد من الحزمة متوافق مع الإصدارات السابقة مع الحزمة القديمة ويشير نتوء الإصدار الرئيسي إلى أن الإصدار الجديد من الحزمة غير متوافق مع الإصدارات السابقة مع الحزمة القديمة.

من الناحية النظرية، يمكن أن ترتبط الحزمة بحزمة أخرى بإحدى الطرق العديدة:

  • مُطْلَقاً .
  • قابلية التوسعة المتوافقة مع الإصدارات السابقة على مستوى الحزمة . يحدث هذا مع عمليات تحديث الإصدار الثانوي الجديدة (المراجعة المتزايدة التالية) للحزمة؛ الحزمة الجديدة لها نفس الاسم والإصدار الرئيسي مثل الحزمة القديمة، ولكن إصدار ثانوي أعلى. من الناحية الوظيفية، الحزمة الجديدة هي مجموعة شاملة من الحزمة القديمة، وهذا يعني:
    • توجد واجهات المستوى الأعلى للحزمة الأصلية في الحزمة الجديدة، على الرغم من أن الواجهات قد تحتوي على أساليب جديدة، وUDTs جديدة للواجهة المحلية (امتداد مستوى الواجهة الموضح أدناه)، وUDTs جديدة في types.hal .
    • يمكن أيضًا إضافة واجهات جديدة إلى الحزمة الجديدة.
    • جميع أنواع بيانات الحزمة الأصلية موجودة في الحزمة الجديدة ويمكن التعامل معها من خلال الأساليب (التي ربما أعيد تنفيذها) من الحزمة القديمة.
    • يمكن أيضًا إضافة أنواع بيانات جديدة للاستخدام إما عن طريق الطرق الجديدة للواجهات الموجودة التي تم تحديثها، أو عن طريق الواجهات الجديدة.
  • قابلية التوسيع المتوافقة مع الإصدارات السابقة على مستوى الواجهة . يمكن للحزمة الجديدة أيضًا توسيع الحزمة الأصلية من خلال تكوين واجهات منفصلة منطقيًا توفر ببساطة وظائف إضافية، وليس الوظيفة الأساسية. ولهذا الغرض، قد يكون من المرغوب فيه ما يلي:
    • تحتاج الواجهات الموجودة في الحزمة الجديدة إلى اللجوء إلى أنواع بيانات الحزمة القديمة.
    • قد تعمل الواجهات الموجودة في الحزمة الجديدة على توسيع واجهات حزمة قديمة واحدة أو أكثر.
  • تمديد عدم التوافق الأصلي مع الإصدارات السابقة . يعد هذا إصدارًا رئيسيًا من الحزمة ولا يلزم أن يكون هناك أي ارتباط بين الاثنين. وبقدر ما هو موجود، يمكن التعبير عنه بمزيج من الأنواع من الإصدار الأقدم من الحزمة، ووراثة مجموعة فرعية من واجهات الحزمة القديمة.

واجهات الهيكلة

للحصول على واجهة جيدة التنظيم، يجب أن تتطلب إضافة أنواع جديدة من الوظائف التي لا تشكل جزءًا من التصميم الأصلي تعديلاً على واجهة 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 وإصدار الحزمة. يتم استخدام الاسم المؤهل بالكامل فقط عندما يتم الإعلان عن مثيلات النوع وليس حيث يتم تعريف النوع نفسه. على سبيل المثال، لنفترض أن الحزمة 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 يدعم UDTs المتداخلة ويمكن أن تحتوي واجهات HIDL على UDTs (نوع من التصريحات المتداخلة)، يتم استخدام النقاط للوصول إلى الأسماء.

على سبيل المثال، إذا تم تعريف التصريح المتداخل التالي في ملف الأنواع الشائعة في الحزمة 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 باسم Bar فقط ضمن نطاق إعلان Foo . على مستوى الحزمة أو الواجهة، يجب عليك الرجوع إلى Bar عبر 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 عبارة عن نوع تعداد، فإن كل قيمة من نوع التعداد لها اسم مؤهل بالكامل يبدأ بالاسم المؤهل بالكامل لنوع التعداد، متبوعًا بنقطتين، ثم متبوعًا باسم قيمة التعداد. على سبيل المثال، لنفترض أن الحزمة android.hardware.nfc, الإصدار 1.0 يحدد نوع التعداد 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 إكمال الاسم باستخدام قواعد التداخل التلقائي (رقم القاعدة الأقل يعني أولوية أعلى).

المادة 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; ).

أنواع.hal

تحتوي كل حزمة HIDL على ملف types.hal الذي يحتوي على UDTs المشتركة بين كافة الواجهات المشاركة في تلك الحزمة. أنواع HIDL تكون عامة دائمًا؛ بغض النظر عما إذا تم تعريف UDT في types.hal أو ضمن تعريف الواجهة، فإن هذه الأنواع يمكن الوصول إليها خارج النطاق الذي تم تعريفها فيه. لا يُقصد types.hal وصف واجهة برمجة التطبيقات العامة لحزمة ما، بل استضافة UDTs المستخدمة بواسطة جميع الواجهات داخل الحزمة. نظرًا لطبيعة HIDL، تعد كافة UDTs جزءًا من الواجهة.

يتكون types.hal من UDTs وبيانات import . نظرًا لأن types.hal أصبح متاحًا لكل واجهة من واجهات الحزمة (وهو استيراد ضمني)، فإن عبارات import هذه تكون على مستوى الحزمة حسب التعريف. قد تتضمن UDTs في types.hal أيضًا UDTs والواجهات المستوردة.

على سبيل المثال، بالنسبة إلى 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 الوراثة المتعددة.

تستخدم أمثلة إصدار 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 (إذا كان uprev من @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 أصغر.

بسبب القاعدة أ:

  • يمكن أن تبدأ الحزمة بأي رقم إصدار ثانوي (على سبيل المثال، android.hardware.biometrics.fingerprint يبدأ عند @2.1 .)
  • الشرط " android.hardware.foo@1.0 غير محدد" يعني أن دليل hardware/interfaces/foo/1.0 لا ينبغي أن يكون موجودًا.

ومع ذلك، لا تؤثر القاعدة "أ" على الحزمة التي لها نفس اسم الحزمة ولكن إصدار رئيسي مختلف (على سبيل المثال، 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 نفسه).

يجب اتباع 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 ، لا يزال هذا غير صحيح.

رفع الواجهات

لترقية 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 . على الرغم من عدم إضافة UDTs جديدة في الإصدار 1.1 من الحزمة، إلا أن المراجع إلى UDTs في الإصدار 1.0 لا تزال مطلوبة، وبالتالي يتم الاستيراد على مستوى الحزمة في types.hal . (كان من الممكن تحقيق نفس التأثير من خلال الاستيراد على مستوى الواجهة في IQuux.hal .)

في extends @1.0::IQuux في إعلان IQuux ، حددنا إصدار IQuux الذي تم توريثه (مطلوب توضيح لأن IQuux يستخدم للإعلان عن واجهة وللوراثة من واجهة). نظرًا لأن الإعلانات هي مجرد أسماء ترث جميع سمات الحزمة والإصدار في موقع الإعلان، فيجب أن يكون التوضيح باسم الواجهة الأساسية؛ كان بإمكاننا استخدام UDT المؤهل بالكامل أيضًا، لكن ذلك كان سيكون زائدًا عن الحاجة.

الواجهة الجديدة IQuux لا تعيد تعريف الطريقة fromFooToBar() التي ترثها من @1.0::IQuux ؛ فهو يسرد ببساطة الطريقة الجديدة التي يضيفها fromBarToFoo() . في HIDL، قد لا يتم الإعلان عن الأساليب الموروثة مرة أخرى في الواجهات الفرعية، لذلك لا يمكن لواجهة IQuux الإعلان عن أسلوب fromFooToBar() بشكل صريح.

اتفاقيات Uprev

في بعض الأحيان، يجب أن تقوم أسماء الواجهات بإعادة تسمية الواجهة الموسعة. نوصي بأن يكون لامتدادات التعداد والبنيات والاتحادات نفس اسم ما تمتد إليه ما لم تكن مختلفة بما يكفي لتبرير اسم جديد. أمثلة:

// 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 الخاصة بها). يمكن أن ترتبط الحزم ببعضها البعض بعدة طرق، وكلها يمكن التعبير عنها من خلال مزيج من الوراثة على مستوى الواجهة وبناء UDTs من خلال التكوين.

ومع ذلك، هناك نوع واحد من العلاقات محدد بدقة ويجب تنفيذه: الميراث المتوافق مع الإصدارات السابقة على مستوى الحزمة . في هذا السيناريو، الحزمة الأصلية هي الحزمة الموروثة منها والحزمة الفرعية هي الحزمة الأصلية. قواعد الوراثة المتوافقة مع الإصدارات السابقة على مستوى الحزمة هي كما يلي:

  1. يتم توريث جميع واجهات المستوى الأعلى للحزمة الأصلية من خلال الواجهات الموجودة في الحزمة الفرعية.
  2. يمكن أيضًا إضافة واجهات جديدة إلى الحزمة الجديدة (لا توجد قيود على العلاقات مع الواجهات الأخرى في الحزم الأخرى).
  3. يمكن أيضًا إضافة أنواع بيانات جديدة للاستخدام إما عن طريق الطرق الجديدة للواجهات الموجودة التي تم تحديثها، أو عن طريق الواجهات الجديدة.

يمكن تنفيذ هذه القواعد باستخدام وراثة مستوى واجهة HIDL وتكوين UDT، ولكنها تتطلب معرفة على مستوى التعريف لمعرفة أن هذه العلاقات تشكل امتداد حزمة متوافق مع الإصدارات السابقة. ويتم استنتاج هذه المعرفة على النحو التالي:

إذا كانت الحزمة تلبي هذا المتطلب، hidl-gen يفرض قواعد التوافق مع الإصدارات السابقة.