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