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