تحديد إصدارات الواجهة

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

بنية رمز HIDL

يتم تنظيم رمز HIDL في ملفّات برمجية تحدّدها الأنواع والواجهات والحِزم:

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

لا يحتوي ملف تعريف أنواع البيانات types.hal إلا على أنواع البيانات الوصفية (يتم الاحتفاظ بجميع أنواع البيانات الوصفية على مستوى الحزمة في ملف واحد). تتوفّر العروض باللغة المستهدفة لجميع الواجهات في الحزمة.

فلسفة تحديد الإصدار

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

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

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

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

تنظيم الواجهة

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

يتوافق Treble مع مكونات النظام والمورّد التي تم تجميعها بشكل منفصل، ويمكن فيها compiling vendor.img على جهاز وsystem.img بشكل منفصل. يجب تحديد جميع التفاعلات بين vendor.img و system.img بشكل صريح وشامل حتى يمكنهم مواصلة العمل لسنوات عديدة. ويشمل ذلك العديد من مساحات عرض واجهات برمجة التطبيقات، ولكن أحد مساحات العرض الرئيسية هي آلية IPC التي تستخدمها HIDL للتواصل بين العمليات على حدود system.img/vendor.img.

المتطلبات

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

  • يمكن وصفها في HIDL مباشرةً (باستخدام قوائم القيم المحدَّدة للبنى، وما إلى ذلك) باستخدام الأسماء والمعنى الدلالي.
  • يمكن وصفها باستخدام معيار عام مثل ISO/IEC 7816.
  • يمكن وصفها باستخدام معيار أجهزة أو التصميم المادي للأجهزة.
  • يمكن أن تكون بيانات غير شفافة (مثل المفاتيح العامة والمعرّفات وما إلى ذلك) إذا لزم الأمر.

في حال استخدام بيانات غير شفافة، يجب أن يقرأها جانب واحد فقط من واجهة HIDL. على سبيل المثال، إذا كان رمز vendor.img يمنح عنصرًا في system.img رسالة سلسلة أو بيانات vec<uint8_t> ، لا يمكن أن يفكّر system.img هذه البيانات بنفسه، بل يمكن إرسالها مرة أخرى إلى vendor.img لتفسيرها. عند تمرير قيمة من vendor.img إلى رمز المورّد على system.img أو إلى جهاز آخر، يجب وصف تنسيق البيانات وطريقة تفسيرها بدقة، ويجب أن يظلّ ذلك جزءًا من الواجهة.

الإرشادات

من المفترض أن تتمكّن من كتابة رمز تنفيذ أو رمز عملاء لواجهة HAL باستخدام ملفّات ‎.hal فقط (أي أنّه ليس عليك الاطّلاع على مصدر Android أو المعايير العامة). ننصحك بتحديد السلوك المطلوب تحديدًا. تشجّع العبارات مثل "قد يؤدي التنفيذ إلى إجراء "أ" أو "ب" على ربط عمليات التنفيذ بالعملاء الذين تم تطويرها معهم.

تنسيق رمز 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/.

الحِزم غير الأساسية (الحِزم الخاصة بالمورّد) هي الحِزم التي ينتجها مورّد شريحة المعالجة المركزية أو المصنّع الأصلي للتصميم. البادئة للحِزم غير الأساسية هي vendor.$(VENDOR).hardware. حيث يشير $(VENDOR) إلى مورّد منظومة على الرقاقة أو مصنّع أصلي للأجهزة أو مصنّع للتصميم الأصلي للأجهزة. يتم ربط هذا المسار بمسار vendor/$(VENDOR)/interfaces في الشجرة (يتم أيضًا ربطه بترميز ثابت).

أسماء الأنواع المحدَّدة من المستخدِم والمؤهَّلة بالكامل

في HIDL، يكون لكل بنية بيانات مستخدمة مرة واحدة اسم مؤهَّل بالكامل يتألف من اسم البنية، واسم الحزمة التي تم تحديد البنية فيها، وإصدار الحزمة. لا يتم استخدام الاسم المؤهَّل بالكامل إلا عند تعريف نُسخ من النوع وليس عند تعريف النوع نفسه. على سبيل المثال، لنفترض أنّ حزمة 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 هو تنسيق الحزمة مفصولاً بنقاط major.minor-version (مثلاً، 1.0).
  • UDT هو الاسم المُفصَل بنقاط لوحدة بيانات HIDL غير القابلة للتغيير. بما أنّ HIDL تتيح استخدام وحدات البيانات الوصفية المتداخلة ويمكن أن تحتوي واجهات HIDL على وحدات بيانات وصفية (نوع من التعريفات المتداخلة)، يتم استخدام النقاط للوصول إلى الأسماء.

على سبيل المثال، إذا تم تحديد البيان المُدمَج التالي في ملف common types في الحزمة 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);

قيم التعداد المؤهَّلة بالكامل

إذا كان العنصر التعريفي للمستخدم من النوع "قائمة أرقام مميزة"، تحتوي كل قيمة من نوع القائمة على اسم مؤهَّل بالكامل يبدأ بالاسم المؤهَّل بالكامل لنوع القائمة، يليه علامة نقطتان، ثم اسم قيمة القائمة. على سبيل المثال، لنفترض أنّه يتم تحديد نوع مصنّف NfcStatus في الإصدار 1.0 للحزمة android.hardware.nfc,:

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 العثور على مطابقة (لم يتم تعريف بنية البيانات غير المحددة في الحزمة الحالية)، يبحث مُجمِّع 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، ما يؤدي إلى إنشاء بنية بيانات مستخدمة مؤهَّلة بالكامل من النوع android.hardware.nfc@1.0::NfcData. إذا تم العثور على أكثر من مطابقة واحدة لبنية بيانات مستخدمة جزئيًا، يُرسِل مترجم 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;).

types.hal

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

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

على سبيل المثال، بالنسبة إلى 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);
}

قواعد Uprev

لتحديد حزمة package@major.minor، يجب أن يكون A أو كلّ B صحيحًا:

القاعدة "أ" "هو إصدار ثانوي أولي": يجب عدم تحديد جميع الإصدارات الثانوية السابقة، package@major.0 وpackage@major.1 و… package@major.(minor-1).
أو
القاعدة ب

جميع ما يلي صحيح:

  1. "الإصدار الثانوي السابق صالح": يجب تحديد package@major.(minor-1) وتطبيق القاعدة "أ" نفسها (لم يتم تحديد أي من package@major.0 إلى package@major.(minor-2) ) أو القاعدة "ب" (إذا كان إصدارًا أحدث من @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 متوفّرًا.

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

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

في extends @1.0::IQuux في تعريف IQuux، حدّدنا إصدار IQuux الذي يتم الاستناد إليه (يجب توضيح المعنى لأنّ IQuux تُستخدَم ل تعريف واجهة والاستفادة من واجهة). بما أنّ التعريفات هي ببساطة أسماء تكتسِب جميع سمات الحزمة والإصدار في موقع التعريف، يجب أن يكون توضيح المعنى في اسم الواجهة الأساسية. كان بإمكاننا استخدام بنية البيانات الوصفية المؤهَّلة بالكامل أيضًا، ولكن كان ذلك سيكون مكرّرًا.

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

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

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

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

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