لغة تعريف واجهة نظام Android ‏(AIDL) لواجهة HAL

يتيح نظام التشغيل Android 11 استخدام لغة تعريف واجهة Android (AIDL) في طبقات تجريد الأجهزة (HAL) في Android، ما يتيح تنفيذ أجزاء من Android بدون HIDL. نقل واجهات HAL إلى استخدام AIDL حصريًا حيثما أمكن ذلك (عندما تستخدم واجهات HAL الأصلية HIDL، يجب استخدام HIDL).

يجب أن تستخدم طبقات HAL التي تستخدم AIDL للتواصل بين مكوّنات إطار العمل، مثل تلك الموجودة في system.img، ومكوّنات الأجهزة، مثل تلك الموجودة في vendor.img، واجهة AIDL ثابتة. ومع ذلك، للتواصل داخل قسم، مثلاً من إحدى طبقات HAL إلى أخرى، لا يوجد أي قيود على آلية IPC التي يمكن استخدامها.

الحافز

تتوفّر لغة AIDL منذ فترة أطول من HIDL، ويتم استخدامها في العديد من الأماكن الأخرى، مثل بين مكونات إطار عمل Android أو في التطبيقات. بعد أن أصبحت AIDL متوافقة مع الإصدارات السابقة، أصبح من الممكن تنفيذ حزمة كاملة باستخدام وقت تشغيل واحد لعملية الاتصال بين العمليات. يحتوي AIDL أيضًا على نظام أفضل للتحكّم في الإصدارات مقارنةً بـ HIDL. في ما يلي بعض مزايا AIDL:

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

إنشاء التطبيق باستخدام وقت تشغيل AIDL

يحتوي AIDL على ثلاث أنظمة خلفية مختلفة: Java وNDK وCPP. لاستخدام AIDL الثابت، استخدِم دائمًا نسخة النظام من libbinder في system/lib*/libbinder.so وتحدّث على /dev/binder. بالنسبة إلى الرمز البرمجي في صورة vendor، يعني ذلك أنّه لا يمكن استخدام libbinder (من VNDK)، لأنّ هذه المكتبة تتضمّن واجهة برمجة تطبيقات غير ثابتة بلغة C++‎ وأجزاء داخلية غير ثابتة. بدلاً من ذلك، يجب أن يستخدم رمز المورّد الأصلي الخلفية NDK الخاصة بلغة AIDL، وأن يتم الربط بـ libbinder_ndk (الذي يستند إلى libbinder.so في النظام)، وأن يتم الربط بمكتبات NDK التي تم إنشاؤها بواسطة إدخالات aidl_interface. لمعرفة أسماء الوحدات النمطية بالتحديد، يُرجى الاطّلاع على قواعد تسمية الوحدات النمطية.

كتابة واجهة AIDL HAL

لكي يتم استخدام واجهة AIDL بين النظام والمورّد، يجب إجراء تغييرَين على الواجهة:

  • يجب إضافة تعليق توضيحي إلى كل تعريف نوع باستخدام @VintfStability.
  • يجب أن يتضمّن نموذج بيان aidl_interface stability: "vintf",.

يمكن لمالك الواجهة فقط إجراء هذه التغييرات.

عند إجراء هذه التغييرات، يجب أن تكون الواجهة في بيان VINTF لكي تعمل. اختبِر ذلك (والمتطلبات ذات الصلة، مثل التحقّق من تجميد الواجهات التي تم إصدارها) باستخدام اختبار "مجموعة اختبارات المورّد" (VTS) vts_treble_vintf_vendor_test. يمكنك استخدام واجهة @VintfStability بدون هذه المتطلبات من خلال استدعاء AIBinder_forceDowngradeToLocalStability في الخلفية الخاصة بحزمة تطوير البرامج الأصلية (NDK)، أو android::Stability::forceDowngradeToLocalStability في الخلفية الخاصة بلغة C++، أو android.os.Binder#forceDowngradeToSystemStability في الخلفية الخاصة بلغة Java على عنصر رابط قبل إرساله إلى عملية أخرى.

بالإضافة إلى ذلك، ولضمان إمكانية نقل الرمز البرمجي إلى أقصى حد وتجنُّب المشاكل المحتملة، مثل المكتبات الإضافية غير الضرورية، عليك إيقاف الخلفية البرمجية CPP.

يوضّح الرمز البرمجي كيفية إيقاف الخلفية البرمجية لمنصة CPP:

    aidl_interface: {
        ...
        backend: {
            cpp: {
                enabled: false,
            },
        },
    }

العثور على واجهات AIDL HAL

تتوفّر واجهات AIDL الثابتة في AOSP الخاصة بطبقات HAL ضمن مجلدات aidl في أدلة الأساس نفسها التي تتوفّر فيها واجهات HIDL:

  • يُستخدم الرمز hardware/interfaces مع الواجهات التي توفّرها الأجهزة عادةً.
  • frameworks/hardware/interfaces مخصّص للواجهات العالية المستوى التي يتم توفيرها للأجهزة.
  • يُستخدم system/hardware/interfaces للواجهات المنخفضة المستوى التي يتم توفيرها للأجهزة.

ضَع واجهات الإضافات في أدلة فرعية أخرى ضمن hardware/interfaces في vendor أو hardware.

واجهات الإضافات

يحتوي Android على مجموعة من واجهات AOSP الرسمية مع كل إصدار. عندما يريد شركاء Android إضافة إمكانات إلى هذه الواجهات، عليهم عدم تغييرها مباشرةً لأنّ ذلك سيؤدي إلى عدم توافق وقت تشغيل Android مع وقت تشغيل AOSP Android. تجنَّب تغيير هذه الواجهات حتى تتمكّن صورة GSI من مواصلة العمل.

يمكن تسجيل الإضافات بطريقتَين مختلفتَين:

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

  • إضافة واجهات برمجة التطبيقات إلى AOSP في الإصدار التالي
  • إضافات إلى واجهة المصدر تتيح المزيد من المرونة (بدون حدوث تعارضات عند الدمج) في الإصدار التالي

حزم قابلة للتسلسل خاصة بالإضافات: ParcelableHolder

ParcelableHolder هو كائن من نوع الواجهة Parcelable يمكن أن يحتوي على كائن آخر من النوع Parcelable.

حالة الاستخدام الرئيسية لـ ParcelableHolder هي جعل Parcelable قابلاً للتوسيع. على سبيل المثال، لنفترض أنّ مطوّري الأجهزة يتوقّعون أن يتمكّنوا من توسيع نطاق Parcelable وAospDefinedParcelable المحدّدين في AOSP ليشملوا الميزات التي تزيد من قيمة الأجهزة.

استخدِم واجهة ParcelableHolder لتوسيع Parcelable باستخدام الميزات التي تقدّم قيمة مضافة. تحتوي الواجهة ParcelableHolder على مثال Parcelable. إذا حاولت إضافة حقول إلى Parcelable مباشرةً، سيحدث خطأ:

parcelable AospDefinedParcelable {
  int a;
  String b;
  String x; // ERROR: added by a device implementer
  int[] y; // added by a device implementer
}

كما هو موضّح في الرمز البرمجي السابق، هذه الممارسة غير صحيحة لأنّ الحقول التي أضافها مطوّر الجهاز قد تتعارض مع Parcelable عند تعديلها في الإصدارات اللاحقة من Android.

باستخدام ParcelableHolder، يمكن لمالك عنصر قابل للتجزئة تحديد نقطة توسيع في مثيل من Parcelable:

parcelable AospDefinedParcelable {
  int a;
  String b;
  ParcelableHolder extension;
}

بعد ذلك، يمكن لمطوّري الأجهزة تحديد مثيل Parcelable خاص بهم للامتداد:

parcelable OemDefinedParcelable {
  String x;
  int[] y;
}

يمكن إرفاق مثيل Parcelable الجديد بالملف الأصلي Parcelable باستخدام الحقل ParcelableHolder:


// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;

ap.extension.setParcelable(op);

...

OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);

// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();

ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);

...

std::shared_ptr<OemDefinedParcelable> op_ptr;

ap.extension.getParcelable(&op_ptr);

// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);

...

std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);

// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });

ap.extension.set_parcelable(Rc::clone(&op));

...

let op = ap.extension.get_parcelable::<OemDefinedParcelable>();

أسماء مثيلات خادم AIDL HAL

وفقًا للاتفاقية، تتضمّن خدمات AIDL HAL اسم مثيل بالتنسيق $package.$type/$instance. على سبيل المثال، يتم تسجيل مثيل HAL الخاص بجهاز الاهتزاز على النحو التالي: android.hardware.vibrator.IVibrator/default.

كتابة خادم AIDL HAL

@VintfStability يجب الإفصاح عن خوادم AIDL في بيان VINTF، على سبيل المثال:

    <hal format="aidl">
        <name>android.hardware.vibrator</name>
        <version>1</version>
        <fqname>IVibrator/default</fqname>
    </hal>

وفي ما عدا ذلك، يجب تسجيل خدمة AIDL بشكل عادي. عند إجراء اختبارات VTS، من المتوقّع أن تكون جميع حِزم AIDL HAL المعلَن عنها متاحة.

كتابة برنامج AIDL

يجب أن تعرّف عملاء AIDL عن أنفسهم في مصفوفة التوافق، على سبيل المثال:

    <hal format="aidl" optional="true">
        <name>android.hardware.vibrator</name>
        <version>1-2</version>
        <interface>
            <name>IVibrator</name>
            <instance>default</instance>
        </interface>
    </hal>

تحويل واجهة HAL الحالية من HIDL إلى AIDL

استخدِم الأداة hidl2aidl لتحويل واجهة HIDL إلى AIDL.

hidl2aidl الميزات:

  • أنشئ ملفات AIDL (.aidl) استنادًا إلى ملفات HAL (.hal) للحزمة المحدّدة.
  • أنشئ قواعد إنشاء لحزمة AIDL التي تم إنشاؤها حديثًا مع تفعيل جميع الخلفيات.
  • أنشِئ طرق ترجمة في الخلفيات Java وCPP وNDK لترجمة من أنواع HIDL إلى أنواع AIDL.
  • أنشئ قواعد إنشاء لمكتبات الترجمة مع التبعيات المطلوبة.
  • أنشئ عمليات تأكيد ثابتة لضمان أنّ القيم في تعدادات HIDL وAIDL هي نفسها في الخلفيتين CPP وNDK.

اتّبِع الخطوات التالية لتحويل حزمة من ملفات HAL إلى ملفات AIDL:

  1. أنشئ الأداة المتوفّرة في system/tools/hidl/hidl2aidl.

    ويوفّر إنشاء هذه الأداة من أحدث مصدر تجربة أكثر شمولاً. يمكنك استخدام الإصدار الأحدث لتحويل الواجهات على الفروع القديمة من الإصدارات السابقة:

    m hidl2aidl
  2. نفِّذ الأداة مع دليل الإخراج متبوعًا بالحزمة التي سيتم تحويلها.

    يمكنك اختياريًا استخدام الوسيطة -l لإضافة محتوى ملف ترخيص جديد إلى أعلى جميع الملفات التي تم إنشاؤها. احرص على استخدام الترخيص والتاريخ الصحيحَين:

    hidl2aidl -o <output directory> -l <file with license> <package>

    مثلاً:

    hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
  3. اقرأ الملفات التي تم إنشاؤها وأصلِح أي مشاكل في عملية التحويل:

    • يحتوي conversion.log على أي مشاكل لم يتم التعامل معها ويجب حلّها أولاً.
    • قد تتضمّن ملفات AIDL التي تم إنشاؤها تحذيرات واقتراحات تتطلّب اتّخاذ إجراء. تبدأ هذه التعليقات بالرمز //.
    • تنظيف الحزمة وإجراء تحسينات عليها
    • راجِع @JavaDerive التعليق التوضيحي للميزات التي قد تحتاج إليها، مثل toString أو equals.
  4. إنشاء الاستهدافات التي تحتاج إليها فقط:

    • إيقاف الخدمات الخلفية التي لن يتم استخدامها يُفضَّل استخدام الخلفية البرمجية لـ NDK على الخلفية البرمجية لـ CPP، ويُرجى الاطّلاع على إنشاء التطبيق باستخدام وقت تشغيل AIDL.
    • إزالة مكتبات الترجمة أو أي من الرموز التي تم إنشاؤها ولن يتم استخدامها
  5. اطّلِع على الاختلافات الرئيسية بين AIDL وHIDL:

    • يؤدي استخدام Status والاستثناءات المضمّنة في AIDL عادةً إلى تحسين الواجهة وإزالة الحاجة إلى نوع حالة آخر خاص بالواجهة.
    • لا تكون وسيطات واجهة AIDL في الطرق @nullable تلقائيًا كما كانت في HIDL.

SEPolicy لواجهات HAL المحدّدة بلغة AIDL

يجب أن يتضمّن نوع خدمة AIDL المرئي لرمز المورّد السمة hal_service_type. في ما عدا ذلك، يكون إعداد sepolicy هو نفسه مثل أي خدمة AIDL أخرى (على الرغم من وجود سمات خاصة لطبقات HAL). في ما يلي مثال على تعريف سياق خدمة HAL:

    type hal_foo_service, service_manager_type, hal_service_type;

بالنسبة إلى معظم الخدمات التي تحدّدها المنصة، تتم إضافة سياق خدمة يتضمّن النوع الصحيح تلقائيًا (على سبيل المثال، يتم وضع علامة hal_foo_service على android.hardware.foo.IFoo/default تلقائيًا). ومع ذلك، إذا كان أحد برامج إطار العمل يتيح استخدام أسماء مثيلات متعددة، يجب إضافة أسماء مثيلات إضافية في ملفات service_contexts خاصة بالجهاز:

    android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0

عند إنشاء نوع جديد من طبقة تجريد الأجهزة (HAL)، يجب إضافة سمات طبقة تجريد الأجهزة. قد تكون سمة HAL معيّنة مرتبطة بأنواع خدمات متعدّدة (يمكن أن يكون لكل منها مثيلات متعدّدة كما ذكرنا للتو). بالنسبة إلى طبقة تجريد الأجهزة (HAL)، foo، هناك hal_attribute(foo). يحدّد هذا الماكرو السمتَين hal_foo_client وhal_foo_server. بالنسبة إلى نطاق معيّن، يربط الماكروان hal_client_domain وhal_server_domain نطاقًا بسمة HAL معيّنة. على سبيل المثال، يتوافق خادم النظام الذي يعمل كعميل لواجهة HAL هذه مع السياسة hal_client_domain(system_server, hal_foo). يتضمّن خادم HAL أيضًا hal_server_domain(my_hal_domain, hal_foo).

عادةً، بالنسبة إلى سمة HAL معيّنة، يتم أيضًا إنشاء نطاق مثل hal_foo_default للرجوع إلى HAL أو أمثلة عليه. ومع ذلك، تستخدم بعض الأجهزة هذه النطاقات لخوادمها الخاصة. لا يهم التمييز بين النطاقات لعدة خوادم إلا إذا كانت هناك عدة خوادم تعرض الواجهة نفسها وتحتاج إلى مجموعة أذونات مختلفة في عمليات التنفيذ. في كل وحدات الماكرو هذه، hal_foo ليس كائن sepolicy. بدلاً من ذلك، تستخدم وحدات الماكرو هذه الرمز المميز للإشارة إلى مجموعة السمات المرتبطة بزوج خادم عميل.

ومع ذلك، لم يتم حتى الآن ربط hal_foo_service وhal_foo (زوج السمات من hal_attribute(foo)). يتم ربط سمة HAL بخدمات AIDL HAL باستخدام ماكرو hal_attribute_service (تستخدم واجهات HIDL HAL ماكرو hal_attribute_hwservice)، على سبيل المثال، hal_attribute_service(hal_foo, hal_foo_service). وهذا يعني أنّ العمليات hal_foo_client يمكنها الحصول على HAL، وأنّ العمليات hal_foo_server يمكنها تسجيل HAL. ويتم تنفيذ قواعد التسجيل هذه من خلال مدير السياق (servicemanager).

قد لا تتطابق أسماء الخدمات دائمًا مع سمات HAL، على سبيل المثال: hal_attribute_service(hal_foo, hal_foo2_service). بشكل عام، بما أنّ ذلك يعني أنّ الخدمات تُستخدَم معًا دائمًا، يمكنك إزالة hal_foo2_service واستخدام hal_foo_service لجميع سياقات الخدمة. عندما تضبط طبقات تجريد الأجهزة (HAL) عدة مثيلات من hal_attribute_service، يكون ذلك لأنّ اسم سمة طبقة تجريد الأجهزة الأصلية ليس عامًا بما يكفي ولا يمكن تغييره.

بوضع كل ذلك معًا، يبدو مثال HAL على النحو التالي:

    public/attributes:
    // define hal_foo, hal_foo_client, hal_foo_server
    hal_attribute(foo)

    public/service.te
    // define hal_foo_service
    type hal_foo_service, hal_service_type, protected_service, service_manager_type

    public/hal_foo.te:
    // allow binder connection from client to server
    binder_call(hal_foo_client, hal_foo_server)
    // allow client to find the service, allow server to register the service
    hal_attribute_service(hal_foo, hal_foo_service)
    // allow binder communication from server to service_manager
    binder_use(hal_foo_server)

    private/service_contexts:
    // bind an AIDL service name to the selinux type
    android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0

    private/<some_domain>.te:
    // let this domain use the hal service
    binder_use(some_domain)
    hal_client_domain(some_domain, hal_foo)

    vendor/<some_hal_server_domain>.te
    // let this domain serve the hal service
    hal_server_domain(some_hal_server_domain, hal_foo)

واجهات الإضافات المرتبطة

يمكن ربط إضافة بأي واجهة رابطة، سواء كانت واجهة مستوى أعلى مسجّلة مباشرةً في &quot;مدير الخدمات&quot; أو كانت واجهة فرعية. عند الحصول على إضافة، يجب التأكّد من أنّ نوع الإضافة هو كما هو متوقّع. يمكنك ضبط الإضافات فقط من العملية التي تعرض مستندًا.

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

لضبط إضافة على حزمة، استخدِم واجهات برمجة التطبيقات التالية:

  • الخلفية في NDK: AIBinder_setExtension
  • الخادم الخلفي المستند إلى Java: android.os.Binder.setExtension
  • الخلفية الخاصة بالتكلفة لكل دقيقة: android::Binder::setExtension
  • الخادم الخلفي المستند إلى Rust: binder::Binder::set_extension

للحصول على إضافة في حزمة، استخدِم واجهات برمجة التطبيقات التالية:

  • الخلفية في NDK: AIBinder_getExtension
  • الخادم الخلفي المستند إلى Java: android.os.IBinder.getExtension
  • الخلفية الخاصة بالتكلفة لكل دقيقة: android::IBinder::getExtension
  • الخادم الخلفي المستند إلى Rust: binder::Binder::get_extension

يمكنك العثور على مزيد من المعلومات حول واجهات برمجة التطبيقات هذه في مستندات الدالة getExtension في الخلفية المعنية. يمكنك الاطّلاع على مثال حول كيفية استخدام الإضافات في hardware/interfaces/tests/extension/vibrator.

الاختلافات الرئيسية بين AIDL وHIDL

عند استخدام AIDL HAL أو واجهات AIDL HAL، يجب الانتباه إلى الاختلافات مقارنةً بكتابة HIDL HAL.

  • بنية لغة AIDL أقرب إلى Java. تتشابه صيغة HIDL مع C++.
  • تحتوي جميع واجهات AIDL على حالات خطأ مدمَجة. بدلاً من إنشاء أنواع حالات مخصّصة، أنشئ أعدادًا صحيحة ثابتة للحالات في ملفات الواجهة واستخدِم EX_SERVICE_SPECIFIC في الخلفيتَين CPP وNDK وServiceSpecificException في الخلفية Java. اطّلِع على التعامل مع الأخطاء.
  • لا تبدأ AIDL مجموعات سلاسل المحادثات تلقائيًا عند إرسال عناصر الرابط. يجب بدء هذه العمليات يدويًا (راجِع إدارة سلاسل المحادثات).
  • لا يتم إيقاف AIDL عند حدوث أخطاء غير معالَجة في النقل (يتم إيقاف HIDL Return عند حدوث أخطاء غير معالَجة).
  • يمكن أن تعرّف لغة AIDL نوعًا واحدًا فقط لكل ملف.
  • يمكن تحديد وسيطات AIDL على أنّها in أو out أو inout بالإضافة إلى مَعلمة الإخراج (لا توجد عمليات ردّ اتصال متزامنة).
  • يستخدم AIDL fd كنوع أساسي بدلاً من handle.
  • يستخدم HIDL إصدارات رئيسية للتغييرات غير المتوافقة وإصدارات ثانوية للتغييرات المتوافقة. في AIDL، يتم إجراء تغييرات متوافقة مع الإصدارات القديمة في مكانها. لا يتضمّن AIDL مفهومًا صريحًا للإصدارات الرئيسية، بل يتم تضمينها في أسماء الحِزم. على سبيل المثال، قد تستخدم AIDL اسم الحزمة bluetooth2.
  • لا ترث AIDL أولوية الوقت الفعلي تلقائيًا. يجب استخدام الدالة setInheritRt لكل أداة ربط لتفعيل ميزة "توريث الأولوية في الوقت الفعلي".

اختبارات طبقة تجريد الأجهزة (HAL)

يوضّح هذا القسم أفضل الممارسات لاختبار طبقات تجريد الأجهزة (HAL). تكون هذه الممارسات صالحة حتى إذا لم يكن اختبار الدمج الخاص بطبقة HAL في VTS.

يعتمد نظام التشغيل Android على VTS للتحقّق من عمليات تنفيذ HAL المتوقّعة. تساعد مجموعة اختبارات التوافق مع المورّدين (VTS) في ضمان توافق Android مع عمليات التنفيذ القديمة للمورّدين. تتضمّن عمليات التنفيذ التي لا تجتاز اختبار VTS مشاكل توافق معروفة قد تمنعها من العمل مع الإصدارات المستقبلية من نظام التشغيل.

يتضمّن نظام VTS لأجهزة HAL جزأين رئيسيَّين.

‫1. التأكّد من أنّ Android يعرف طبقات تجريد الأجهزة (HAL) على الجهاز ويتوقّعها

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

يمكنك العثور على هذه المجموعة من الاختبارات في test/vts-testcase/hal/treble/vintf. إذا كنت تعمل على تنفيذ HAL خاص بمورّد، استخدِم vts_treble_vintf_vendor_test للتحقّق منه. يمكنك إجراء هذا الاختبار باستخدام الأمر atest vts_treble_vintf_vendor_test.

تكون هذه الاختبارات مسؤولة عن التحقّق مما يلي:

  • يتم تجميد كل واجهة @VintfStability تم تعريفها في بيان VINTF عند إصدار معروف. يؤكّد ذلك أنّ كلا الجانبين من واجهة برمجة التطبيقات يتفقان على التعريف الدقيق لهذا الإصدار من الواجهة. وهذا الإجراء ضروري لتشغيل التطبيق بشكل أساسي.
  • تتوفّر جميع طبقات HAL المُحدَّدة في بيان VINTF على هذا الجهاز. يجب أن يتمكّن أي برنامج عميل لديه أذونات كافية لاستخدام خدمة HAL معرَّفة من الحصول على هذه الخدمات واستخدامها في أي وقت.
  • تعرض جميع طبقات تجريد الأجهزة (HAL) المحدّدة في بيان VINTF إصدار الواجهة المحدّد في البيان.
  • لا يتم عرض أي طبقات HAL قديمة على الجهاز. يتوقف نظام التشغيل Android عن توفير الدعم للإصدارات الأقدم من واجهات HAL كما هو موضّح في دورة حياة FCM.
  • تتوفّر طبقات HAL المطلوبة على الجهاز. بعض طبقات HAL مطلوبة لكي يعمل نظام التشغيل Android بشكل سليم.

‫2- التحقّق من السلوك المتوقّع لكل طبقة تجريد للأجهزة

يحتوي كل واجهة HAL على اختبارات VTS خاصة بها للتحقّق من السلوك المتوقّع من عملائها. يتم تنفيذ حالات الاختبار على كل مثيل لواجهة HAL معرَّفة، ويتم فرض سلوك محدّد استنادًا إلى إصدار الواجهة الذي تم تنفيذه.

في لغة C++، يمكنك الحصول على قائمة بكل طبقات HAL المثبَّتة على النظام باستخدام الدالة android::getAidlHalInstanceNames في libaidlvintf_gtest_helper. في لغة Rust، استخدِم binder::get_declared_instances.

تحاول هذه الاختبارات تغطية كل جانب من جوانب تنفيذ طبقة تجريد الأجهزة (HAL) التي يعتمد عليها إطار عمل Android أو قد يعتمد عليها في المستقبل.

وتشمل هذه الاختبارات التحقّق من توفّر الميزات والتعامل مع الأخطاء وأي سلوك آخر قد يتوقّعه العميل من الخدمة.

المراحل الرئيسية في VTS لتطوير طبقة HAL

من المتوقّع أن يتم تحديث اختبارات VTS (أو أي اختبارات أخرى) عند إنشاء أو تعديل واجهات HAL في Android.

يجب الانتهاء من اختبارات VTS وتجهيزها للتحقّق من عمليات تنفيذ المورّدين قبل تجميدها لإصدارات Android Vendor API. ويجب أن تكون هذه الواجهات جاهزة قبل تجميدها ليتمكّن المطوّرون من إنشاء عمليات التنفيذ والتحقّق منها وتقديم الملاحظات إلى مطوّري واجهات HAL.

الاختبار على Cuttlefish

عندما لا يتوفّر جهاز، يستخدم Android Cuttlefish كأداة تطوير لواجهات HAL. يتيح ذلك إجراء اختبارات تكامل قابلة للتوسيع على Android.

hal_implementation_test التي تتضمّن عمليات تنفيذ لأحدث إصدارات واجهة HAL للتأكّد من أنّ Android جاهز للتعامل مع الواجهات الجديدة وأنّ اختبارات VTS جاهزة لاختبار عمليات التنفيذ الجديدة للمورّدين فور توفّر أجهزة جديدة.