لغة AIDL ثابتة

نظام Android 10 يتيح استخدام واجهة Android ثابتة لغة التعريف (AIDL)، وهي طريقة جديدة لتتبع برنامج تقديم الطلبات (API) وواجهة التطبيق الثنائية (ABI) التي توفّرها AIDL من الواجهات. تعمل لغة AIDL الثابتة تمامًا مثل AIDL، لكنّ نظام الإصدار يتتبّع التوافق مع واجهة المستخدم، وهناك قيود على ما يمكنك فعله:

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

لغة AIDL مهيكلة مقابل ثابتة

تشير لغة AIDL المركّبة إلى أنواع محدّدة في لغة AIDL فقط. على سبيل المثال، ملف تعريف parcelable (ملف تعريف parcelable مخصّص) ليس ملف تعريف AIDL منظَّمًا. قطع قِطع الأراضي عند تحديد حقولها في AIDL، تُعرف باسم العناصر المنظَّمة.

تتطلّب مجموعة AIDL الثابتة مجموعة AIDL منظَّمة حتى يتمكّن نظام الإنشاء والمجمِّع من معرفة ما إذا كانت التغييرات التي تم إجراؤها على العناصر القابلة للتجميع متوافقة مع الإصدارات القديمة. ومع ذلك، ليست كل الواجهات المنظَّمة مستقرة. لكي تكون الواجهة مستقرة، يجب أن تستخدم أنواعًا منظَّمة فقط، ويجب أيضًا أن تستخدم ميزات الإصدارات التالية. وفي المقابل، لا تكون الواجهة مستقرة إذا كان الإصدار الأساسي لإنشاء العلامة أو في حال ضبط السمة unstable:true.

تحديد واجهة AIDL

يظهر تعريف aidl_interface على النحو التالي:

aidl_interface {
    name: "my-aidl",
    srcs: ["srcs/aidl/**/*.aidl"],
    local_include_dir: "srcs/aidl",
    imports: ["other-aidl"],
    versions_with_info: [
        {
            version: "1",
            imports: ["other-aidl-V1"],
        },
        {
            version: "2",
            imports: ["other-aidl-V3"],
        }
    ],
    stability: "vintf",
    backend: {
        java: {
            enabled: true,
            platform_apis: true,
        },
        cpp: {
            enabled: true,
        },
        ndk: {
            enabled: true,
        },
        rust: {
            enabled: true,
        },
    },

}
  • name: اسم وحدة واجهة AIDL التي تُحدِّد بشكلٍ فريد واجهة AIDL.
  • srcs: قائمة بملفات مصدر AIDL التي تنشئ الواجهة يجب أن يكون المسار لنوع AIDL‏ Foo المحدّد في حزمة com.acme على العنوان <base_path>/com/acme/Foo.aidl، حيث يمكن أن يكون <base_path> أي دليل مرتبط بالدليل الذي يتضمّن Android.bp. في المثال السابق، <base_path> هو srcs/aidl.
  • local_include_dir: المسار الذي يبدأ منه اسم الحزمة وهو يتوافق مع <base_path> الموضّح أعلاه.
  • imports: قائمة بالوحدات aidl_interface التي يستخدمها هذا الإجراء إذا كانت إحدى واجهتَي AIDL تستخدم واجهة أو عنصرًا قابلاً للتقسيم من aidl_interface آخر، ضَع اسمه هنا. ويمكن أن يكون هذا الاسم بحد ذاته للإشارة إلى أحدث إصدار، أو الاسم مع اللاحقة الخاصة بالإصدار (مثل -V1) للإشارة إلى إصدار معيّن. أصبح تحديد الإصدار متاحًا بدءًا من Android 12.
  • versions: الإصدارات السابقة من الواجهة تم تجميدها بمعدل أقل من api_dir، بدءًا من نظام التشغيل Android 11 يتم تجميد versions تحت aidl_api/name. وإذا لم تكن هناك إصدارات ثابتة من الواجهة، يجب عدم تحديد هذا الحقل، ولن يتم إجراء عمليات تحقق من التوافق. تم استبدال هذا الحقل بـ versions_with_info لنظام التشغيل Android. 13 وأعلى.
  • versions_with_info: قائمة الصفوف، يحتوي كلٌ منها على اسم نسخة مجمدة وقائمة مع استيرادات إصدار لـ aidl_interface التي قام هذا الإصدار من aidl_interface باستيرادها. التعريف من الإصدار V من واجهة AIDL، يقع الموقع الجغرافي على aidl_api/IFACE/V تم تقديم هذا الحقل في Android 13، وليس من المفترض أن يتم تعديله في Android.bp مباشرةً. الحقل هو تمت إضافتها أو تعديلها من خلال استدعاء *-update-api أو *-freeze-api. بالإضافة إلى ذلك، يتم نقل حقول versions تلقائيًا إلى versions_with_info عندما يستدعي المستخدم *-update-api أو *-freeze-api.
  • stability: العلامة الاختيارية التي توفر وعدًا بالثبات لهذه الواجهة. يمكن استخدام "vintf" فقط. في حال ترك stability بدون ضبط، سيتم يتحقق من توافق الواجهة مع الإصدارات القديمة ما لم تم تحديد unstable. يرتبط عدم ضبط القيمة بواجهة تتسم بالثبات في سياق الترجمة هذا (أي جميع عناصر النظام، مثلاً، العناصر في system.img والأقسام ذات الصلة، أو جميع عناصر المورّد، مثلاً، العناصر في vendor.img والأقسام ذات الصلة). في حال حذف تم ضبط stability على "vintf"، ويتوافق هذا مع وعد استقرار: يجب الحفاظ على استقرار الواجهة ما دام قيد الاستخدام.
  • gen_trace: العلامة الاختيارية لتفعيل التتبُّع أو إيقافه. بدءًا من الإصدار Android 14، يكون الإعداد التلقائي هو true لخلفيتَي cpp و java.
  • host_supported: العلامة الاختيارية التي عند ضبطها على true تجعل المكتبات التي تم إنشاؤها متاحة للبيئة المستضافة
  • unstable: العلامة الاختيارية المستخدمة لوضع علامة على أن هذه الواجهة لا يجب أن تكون مستقرة. عند ضبط هذا الخيار على true، لا ينشئ نظام الإنشاء ملفًا لبيانات واجهة برمجة التطبيقات للواجهة ولا يطلب تحديثه.
  • frozen: العلامة الاختيارية التي عند ضبطها على true، تعني أنّ الواجهة لم تطرأ أي تغييرات منذ الإصدار السابق من الواجهة. وهذا يمكّن المزيد من عمليات التحقق من وقت الإصدار. عند ضبط القيمة على false، يعني ذلك أنّ الواجهة في مرحلة تطوير وتحتوي على تغييرات جديدة، لذا يؤدي تشغيل foo-freeze-api إلى إنشاء إصدار جديد وتغيير القيمة تلقائيًا إلى true. تم إدخال القدرة لأول مرة في الإصدار 14 من نظام التشغيل Android
  • backend.<type>.enabled: تعمل هذه العلامات على تبديل كل من الخلفيات التي يقوم المحول البرمجي لـ AIDL بإنشاء التعليمات البرمجية. تتوفّر أربعة أنظمة أساسية: Java وC++ وNDK وRust. يتم تفعيل الخلفيات Java وC++ وNDK تلقائيًا. إذا لم تكن هناك حاجة إلى أي من هذه الخلفيات الثلاث، فيجب بشكل صريح. يتم إيقاف Rust تلقائيًا إلى أن يعمل نظام التشغيل Android. 15-
  • backend.<type>.apex_available: قائمة بأسماء ملفات APEX التي تم إنشاؤها تتوفر بها مكتبة بدائل.
  • backend.[cpp|java].gen_log: العلامة الاختيارية التي تتحكم في ما إذا كان سيتم إنشاء رمز إضافي لجمع المعلومات عن المعاملة.
  • backend.[cpp|java].vndk.enabled: العلامة الاختيارية لإنشاء هذه الواجهة جزءًا من مجموعة VNDK. القيمة التلقائية هي false.
  • backend.[cpp|ndk].additional_shared_libraries: تمّت إضافة هذه العلامة في الإصدار 14 من نظام التشغيل Android، وهي تضيف تبعيات إلى مكتبات الأصلية. تكون هذه العلامة مفيدة مع ndk_header وcpp_header.
  • backend.java.sdk_version: العلامة الاختيارية لتحديد الإصدار لحزمة SDK التي بنيت مكتبة كعب Java عليها. القيمة التلقائية هي "system_current". لا يجب ضبط هذا الإعداد في حال backend.java.platform_apis. true.
  • backend.java.platform_apis: العلامة الاختيارية التي يجب ضبطها على true عندما تحتاج المكتبات التي تم إنشاؤها إلى إنشاء استنادًا إلى واجهة برمجة تطبيقات النظام الأساسي بدلاً من حزمة SDK.

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

كتابة ملفات AIDL

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

// in a file like 'some/package/Thing.aidl'
package some.package;

parcelable SubThing {
    String a = "foo";
    int b;
}

يتوفّر خيار تلقائي (ولكن ليس مطلوبًا) لكل من boolean وchar float وdouble وbyte وint وlong وString في Android 12، تكون القيم الافتراضية للتعدادات التي يحددها المستخدم أيضًا في حال عدم تحديد قيمة تلقائية، يتم استخدام قيمة فارغة أو قيمة مشابهة للقيمة 0. يتمّ إعداد التعدادات التي لا تتضمّن قيمة تلقائية على القيمة 0 حتى إذا لم يكن هناك تعداد صفر.

استخدام مكتبات التنويهات الموجزة

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

cc_... {
    name: ...,
    shared_libs: ["my-module-name-cpp"],
    ...
}
# or
java_... {
    name: ...,
    // can also be shared_libs if your preference is to load a library and share
    // it among multiple users or if you only need access to constants
    static_libs: ["my-module-name-java"],
    ...
}
# or
rust_... {
    name: ...,
    rustlibs: ["my-module-name-rust"],
    ...
}

مثال في لغة C++:

#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
    // use just like traditional AIDL

مثال في Java:

import some.package.IFoo;
import some.package.Thing;
...
    // use just like traditional AIDL

مثال في Rust:

use aidl_interface_name::aidl::some::package::{IFoo, Thing};
...
    // use just like traditional AIDL

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

يؤدي أيضًا الإعلان عن وحدة باسم foo إلى إنشاء هدف في نظام الإنشاء يمكنك استخدامه لإدارة واجهة برمجة التطبيقات للوحدة. عند إنشاء foo-freeze-api، تتم إضافة تعريف جديد لواجهة برمجة التطبيقات تحت api_dir أو aidl_api/name، استنادًا إلى إصدار Android، ويُضاف ملف .hash، وكلاهما يمثّل الإصدار المجمّد حديثًا من الواجهة. يعدّل foo-freeze-api أيضًا السمة versions_with_info لتعكس الإصدار الإضافي وimports للإصدار. في الأساس، يتم نسخ imports في versions_with_info من الحقل imports. لكن تم تحديد أحدث إصدار ثابت في imports في versions_with_info الذي لا يحتوي على إصدار صريح. بعد تحديد السمة versions_with_info، يُجري نظام الإنشاء عمليات فحص التوافق بين الإصدارات المجمّدة وبين أحدث إصدار مجمّد و"أعلى الشجرة" (ToT).

بالإضافة إلى ذلك، عليك إدارة تعريف واجهة برمجة التطبيقات لإصدار ToT. عند تعديل واجهة برمجة تطبيقات، يجب تشغيل foo-update-api لتعديل aidl_api/name/current الذي يحتوي على تعريف واجهة برمجة التطبيقات لإصدار ToT.

وللحفاظ على استقرار الواجهة، يمكن للمالكين إضافة عناصر جديدة:

  • المناهج حتى نهاية الواجهة (أو الطرق ذات المعرفة الجديدة مسلسلات)
  • العناصر في نهاية قطعة قطع (تتطلّب إضافة عنصر تلقائي لكل عنصر )
  • القيم الثابتة
  • في Android 11، أدوات العد
  • في Android 12، الحقول التي تنتهي عند نهاية الاتحاد

لا يُسمح بإجراء أي إجراءات أخرى، ولا يمكن لأي شخص آخر تعديل الواجهة (وإلا أنه يخاطر بالاصطدام مع التغييرات التي يجريها المالك).

لاختبار تجميد جميع الواجهات للإصدار، يمكنك إنشاء الإصدار مع ضبط المتغيّرات البيئية التالية:

  • AIDL_FROZEN_REL=true m ... - يتطلب الإنشاء تجميد كل واجهات AIDL الثابتة التي لم يتم تحديد حقل owner: لها.
  • AIDL_FROZEN_OWNERS="aosp test" - يتطلب الإصدار تجميد جميع واجهات AIDL الثابتة مع تحديد الحقل owner: على أنّه "aosp" أو "test".

ثبات عمليات الاستيراد

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

في رمز نظام Android الأساسي، يمثّل android.hardware.graphics.common أكبر مثال على هذا النوع من ترقية الإصدار.

استخدام واجهات ذات إصدارات

طرق الواجهة

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

  • الخلفية cpp تحصل على ::android::UNKNOWN_TRANSACTION.
  • تتلقّى ndk الخلفية STATUS_UNKNOWN_TRANSACTION.
  • تحصل الواجهة الخلفية في java على android.os.RemoteException مع رسالة مفادها لم يتم تنفيذ واجهة برمجة التطبيقات.

وبالنسبة إلى استراتيجيات التعامل مع هذا، انظر إصدارات طلب البحث استخدام الإعدادات التلقائية

قطع قِطع الأراضي

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

ينبغي ألا يتوقع العملاء أن تستخدم الخوادم الحقول الجديدة ما لم يعرفوا يقوم الخادم بتنفيذ الإصدار الذي يحتوي على الحقل المحدد (انظر إصدارات طلب البحث).

عمليات التعداد والثوابت

وبالمثل، يجب أن يرفض العملاء والخوادم القيم الثابتة والمُجمِّعات غير المعروفة أو يتجاهلونها حسب الاقتضاء، لأنّه قد تتم إضافة المزيد في المستقبل. على سبيل المثال، يجب ألا يوقف الخادم عملية التنفيذ عند تلقّي ملف ناتج عن ملف تعريف مجهول. يجب أن يتجاهل الخادم العداد أو يعرض رسالة تُعلم العميل بأنّه غير متوافق مع هذا التنفيذ.

الاتحادات

فشلت محاولة إرسال اتحاد مع حقل جديد إذا كان الاستقبال قديمًا لا تعرفه عن هذا المجال. ولن يلاحظ التنفيذ عملية الدمج مع الحقل الجديد. يتم تجاهل الخطأ إذا كانت المعاملة اتجاه واحد، وإلا يكون الخطأ BAD_VALUE(لنظام التشغيل C++ أو NDK الخلفي) أو IllegalArgumentException(لنظام التشغيل Java الخلفي). الخطأ هو إذا كان العميل يرسل اتحادًا تم تعيينه إلى الحقل الجديد إلى حقل قديم أو عندما يكون برنامجًا قديمًا يتلقى الاتحاد من خادم جديد.

إدارة نُسخ متعددة

يمكن أن تحتوي مساحة اسم الرابط في Android على إصدار واحد فقط من aidl محدّد. لتجنُّب الحالات التي تحتوي فيها أنواع aidl التي تم إنشاؤها على عدة التعريفات. تتضمن لغة C++ قاعدة التعريف الواحدة التي تتطلب تعريفًا واحدًا فقط. لكل رمز.

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

إذا تم استخدام مكتبة الواجهات بواسطة العديد من الوحدات المختلفة، قد يكون من المفيد لإنشاء cc_defaults وjava_defaults وrust_defaults لأي مجموعة من والمكتبات والعمليات التي تحتاج إلى استخدام الإصدار ذاته. عند تقديم جديد من الواجهة يمكن تحديث هذه الإعدادات الافتراضية وجميع الوحدات يستخدمونها معًا، لضمان عدم استخدامهم لإصدارات مختلفة من الواجهة.

cc_defaults {
  name: "my.aidl.my-process-group-ndk-shared",
  shared_libs: ["my.aidl-V3-ndk"],
  ...
}

cc_library {
  name: "foo",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

cc_binary {
  name: "bar",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

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

يمكن استخدام aidl_interfaces_defaults للحفاظ على تعريف واحد ل أحدث إصدارات من الملحقات لواجهة aidl_interface التي يمكن تعديلها في مكان واحد، واستخدامها من قِبل جميع وحدات aidl_interface التي تريد استيراد هذه الواجهة المشتركة.

aidl_interface_defaults {
  name: "android.popular.common-latest-defaults",
  imports: ["android.popular.common-V3"],
  ...
}

aidl_interface {
  name: "android.foo",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

aidl_interface {
  name: "android.bar",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

التطوير المستنِد إلى الإبلاغ عن المشاكل

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

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

علامة إصدار AIDL

العلامة التي تتحكّم في هذا السلوك هي RELEASE_AIDL_USE_UNFROZEN وتكون محدّدة في build/release/build_flags.bzl. true تعني النسخة غير المجمدة من تُستخدم الواجهة في وقت التشغيل وتعني false أن مكتبات فإن جميع النسخ غير المجمدة تتصرف مثل آخر نسخة مجمدة. يمكنك إلغاء العلامة إلى true إذا كان التطوير المحلي، ولكن يجب إعادته إلى الإصدار false قبل الإصدار. يتم عادةً تطوير الإصدارات باستخدام إعدادات تم ضبط العلامة فيها على true.

مصفوفة التوافق وبيان التوافق

تحدِّد عناصر واجهة المورّد (عناصر VINTF) الإصدارات المتوقّعة والإصدارات المقدَّمة على أيّ من جانبَي واجهة المورّد.

تستهدف معظم الأجهزة بخلاف الحبار أحدث مصفوفة توافق. بعد تجميد الواجهات فقط، لن يحدث أي فارق في واجهة AIDL المكتبات المستندة إلى RELEASE_AIDL_USE_UNFROZEN.

المصفوفات

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

على سبيل المثال، عند إضافة الإصدار 4 غير المتوقّف، استخدِم <version>3-4</version>.

عند تجميد الإصدار 4، يمكنك إزالة الإصدار 3 من مصفوفة التوافق، لأنّه يتم استخدام الإصدار 4 المجمّد عندما يكون RELEASE_AIDL_USE_UNFROZEN false.

ملفات البيانات

في Android 15، تم إجراء تغيير في libvintf لتعديل ملفات البيان في وقت الإنشاء استنادًا إلى قيمة RELEASE_AIDL_USE_UNFROZEN.

تشير البيانات وأجزاء البيان إلى إصدار الواجهة. تنفذها الخدمة. عند استخدام أحدث إصدار غير مجمّد من واجهة، يجب تعديل البيان ليعكس هذا الإصدار الجديد. عند RELEASE_AIDL_USE_UNFROZEN=false، يتم تعديل إدخالات البيان من قِبل libvintf لتعكس التغيير في مكتبة AIDL التي تم إنشاؤها. تم تعديل الإصدار من الإصدار غير المُجمّد N إلى الإصدار المجمّد الأخير N - 1. وبالتالي، لا يحتاج المستخدمون إلى إدارة عدة بيانات بيان أو أجزاء بيانات بيان لكل خدمة من خدماتهم.

التغييرات في برنامج HAL

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

التغييرات في تنفيذ HAL

يكمن أكبر فرق في تطوير HAL مقارنةً بالتطوير المستنِد إلى العلامة في اشتراط أن تكون عمليات تنفيذ HAL متوافقة مع الإصدارات القديمة والمجمّدة الأخيرة لكي تعمل عندما يكون RELEASE_AIDL_USE_UNFROZEN هو false. إنّ مراعاة التوافق مع الإصدارات القديمة في عمليات التنفيذ ورمز الجهاز هو عملية جديدة . راجع استخدام الإصدارات الواجهات.

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

مثال: تحتوي واجهة على ثلاثة إصدارات مجمّدة. تم تعديل الواجهة باستخدام طريقة جديدة. يتم تحديث كل من العميل والخدمة لاستخدام الإصدار 4 الجديد المكتبة. وبما أنّ مكتبة الإصدار 4 تستند إلى إصدار غير مجمّد من الواجهة، فإنّها تتصرف مثل آخر إصدار مجمّد، وهو الإصدار 3، عندما يكون RELEASE_AIDL_USE_UNFROZEN هو false، وتمنع استخدام الطريقة الجديدة.

عند تجميد الواجهة، تستخدم جميع قيم RELEASE_AIDL_USE_UNFROZEN هذا الإصدار المجمّد، ويمكن إزالة الرمز البرمجي الذي يعالج التوافق مع الإصدارات القديمة.

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

// Get the callback along with the version of the callback
ScopedAStatus RegisterMyCallback(const std::shared_ptr<IMyCallback>& cb) override {
    mMyCallback = cb;
    // Get the version of the callback for later when we call methods on it
    auto status = mMyCallback->getInterfaceVersion(&mMyCallbackVersion);
    return status;
}

// Example of using the callback later
void NotifyCallbackLater() {
  // From the latest frozen version (V2)
  mMyCallback->foo();
  // Call this method from the unfrozen V3 only if the callback is at least V3
  if (mMyCallbackVersion >= 3) {
    mMyCallback->bar();
  }
}

قد لا تكون الحقول الجديدة في الأنواع الحالية (parcelable وenum وunion) متوفّرة أو تحتوي على قيمها التلقائية عندما يكون RELEASE_AIDL_USE_UNFROZEN false ويتم تجاهل قيم الحقول الجديدة التي تحاول الخدمة إرسالها عند الخروج من العملية.

لا يمكن إرسال الأنواع الجديدة التي تمت إضافتها في هذا الإصدار غير المجمّد. أو استلامها من خلال الواجهة.

لا تطلب عملية التنفيذ أبدًا استخدام طرق جديدة من أي عميل عندما تم false ميزة RELEASE_AIDL_USE_UNFROZEN.

يجب استخدام المعرّفات الجديدة مع الإصدار الذي تم تقديمها فيه فقط، وليس مع الإصدار السابق.

يتم عادةً استخدام foo->getInterfaceVersion() لمعرفة إصدار جهاز التحكّم عن بُعد. التي تستخدمها واجهة المستخدم. ومع ذلك، مع دعم الإصدارات المستندة إلى العلامات، يمكنك وتنفذ نسختين مختلفتين، لذلك قد ترغب في الحصول على إصدار الواجهة الحالية. يمكنك إجراء ذلك من خلال الحصول على إصدار الواجهة للموضوع الحالي، على سبيل المثال، this->getInterfaceVersion() أو الطرق الأخرى لـ my_ver. اطّلِع على طلب إصدار الواجهة للموضوع بعيد لمزيد من المعلومات.

واجهات VINTF الثابتة الجديدة

عند إضافة حزمة واجهة AIDL جديدة، لا يتوفّر آخر إصدار مجمّد، وبالتالي، لا يتوفّر سلوك يمكن الرجوع إليه عند RELEASE_AIDL_USE_UNFROZENfalse. لا تستخدم هذه الواجهات. عندما يكون RELEASE_AIDL_USE_UNFROZEN false، لن يسمح مدير الخدمة للخدمة بتسجيل الواجهة. ولن يتمكن العملاء من العثور عليه.

يمكنك إضافة الخدمات بشكل مشروط استنادًا إلى قيمة علامة RELEASE_AIDL_USE_UNFROZEN في ملف إنشاء الجهاز:

ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
    android.hardware.health.storage-service
endif

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

استخدام Cuttlefish كأداة تطوير

بعد تجميد VINTF كل عام، يتم تعديل توافق إطار العمل. مصفوفة (FCM) target-level وPRODUCT_SHIPPING_API_LEVEL للحبار لذا فهي تعكس الأجهزة التي يتم إطلاقها مع إصدار العام المقبل. نحن نعدّل target-level وPRODUCT_SHIPPING_API_LEVEL للتأكّد من توفّر جهاز إطلاق تم اختباره ويستوفي المتطلبات الجديدة لإصدار العام القادم.

عندما يكون RELEASE_AIDL_USE_UNFROZEN هو true، يتم استخدام Cuttlefish لتطوير إصدارات Android المستقبلية. يستهدف مستوى FCM وPRODUCT_SHIPPING_API_LEVEL لإصدار Android القادم من العام المقبل، ما يتطلّب منه استيفاء متطلبات برامج المورّدين (VSR) للإصدار القادم.

عندما تكون قيمة RELEASE_AIDL_USE_UNFROZEN هي false، يكون لدى الحبّار السابق target-level وPRODUCT_SHIPPING_API_LEVEL لإظهار جهاز إصدار. في Android 14 والإصدارات الأقدم، سيكون هذا التفرقة من خلال فروع Git المختلفة التي لا تعتمد التغيير في خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" target-level أو مستوى واجهة برمجة التطبيقات للشحن أو أي رمز آخر يستهدف العنصر التالي .

قواعد تسمية الوحدات

في Android 11، يتم تلقائيًا إنشاء وحدة مكتبة نموذجية لكل مجموعة من الإصدارات والخلفيات المفعَّلة. للإحالة إلى وحدة مكتبة بدائل معينة للربط، فلا تستخدم اسم aidl_interface، ولكن اسم وحدة مكتبة بدائل، وهو ifacename-version-backend، حيث

  • ifacename: اسم وحدة aidl_interface
  • version إما
    • Vversion-number للإصدارات التي لا يمكن تحديثها
    • Vlatest-frozen-version-number + 1 للإصدار الأكثر حداثة (الذي لم يتم تجميده بعد)
  • backend هو أحد
    • java لنظام Java الأساسي
    • cpp لنظام التشغيل C++ الخلفي
    • ndk أو ndk_platform للواجهة الخلفية NDK. يُستخدَم الأول للتطبيقات، ويُستخدَم الأخير لاستخدام النظام الأساسي حتى Android 13. ضِمن الإصدار 13 من نظام التشغيل Android والإصدارات الأحدث، يجب استخدام ndk فقط.
    • rust لنظام Rust الأساسي

لنفترض أنّ هناك وحدة باسم foo وأحدث إصدار لها هو 2، وهي متوافقة مع كل من NDK وC++. في هذه الحالة، تُنشئ أداة AIDL الوحدات التالية:

  • استنادًا إلى الإصدار 1
    • foo-V1-(java|cpp|ndk|ndk_platform|rust)
  • استنادًا إلى الإصدار 2 (أحدث إصدار ثابت)
    • foo-V2-(java|cpp|ndk|ndk_platform|rust)
  • استنادًا إلى إصدار ToT
    • foo-V3-(java|cpp|ndk|ndk_platform|rust)

مقارنةً بالإصدار 11 من نظام التشغيل Android:

  • foo-backend، الذي كان يشير إلى أحدث إصدار دوارٍ ، أصبح foo-V2-backend
  • foo-unstable-backend، الذي يشير إلى إصدار ToT يصبح foo-V3-backend

أسماء ملفات الإخراج دائمًا هي نفسها أسماء الوحدات.

  • استنادًا إلى الإصدار 1: foo-V1-(cpp|ndk|ndk_platform|rust).so
  • استنادًا إلى الإصدار 2: foo-V2-(cpp|ndk|ndk_platform|rust).so
  • استنادًا إلى إصدار بنود الخدمة: foo-V3-(cpp|ndk|ndk_platform|rust).so

يُرجى العِلم أنّ مُجمِّع AIDL لا يُنشئ وحدة إصدار unstable أو وحدة غير مرتبطة بإصدار لواجهة AIDL ثابتة. اعتبارًا من Android 12، يتضمّن اسم الوحدة الذي يتم إنشاؤه من واجهة AIDL مستقرة دائمًا إصدارها.

طرق الواجهات الوصفية الجديدة

يضيف نظام التشغيل Android 10 عدة طرق لواجهة وصفية لملف AIDL الثابت.

إرسال طلب بحث عن إصدار الواجهة للعنصر البعيد

يمكن للعملاء الاستعلام عن إصدار الواجهة والقيمة المحوَّلة لها التي ينفّذها العنصر البعيد مقارنة القيم المعروضة بقيم الواجهة التي يستخدمها العميل.

مثال مع الخلفية cpp:

sp<IFoo> foo = ... // the remote object
int32_t my_ver = IFoo::VERSION;
int32_t remote_ver = foo->getInterfaceVersion();
if (remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::HASH;
std::string remote_hash = foo->getInterfaceHash();

مثال على استخدام الخلفية ndkndk_platform):

IFoo* foo = ... // the remote object
int32_t my_ver = IFoo::version;
int32_t remote_ver = 0;
if (foo->getInterfaceVersion(&remote_ver).isOk() && remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::hash;
std::string remote_hash;
foo->getInterfaceHash(&remote_hash);

مثال مع الخلفية java:

IFoo foo = ... // the remote object
int myVer = IFoo.VERSION;
int remoteVer = foo.getInterfaceVersion();
if (remoteVer < myVer) {
  // the remote side is using an older interface
}

String myHash = IFoo.HASH;
String remoteHash = foo.getInterfaceHash();

بالنسبة إلى لغة Java، يجب على الجانب البعيد تنفيذ getInterfaceVersion() getInterfaceHash() على النحو التالي (يتم استخدام super بدلاً من IFoo لتجنب أخطاء النسخ واللصق. قد تحتاج إلى التعليق التوضيحي @SuppressWarnings("static") لإيقاف التحذيرات، وذلك استنادًا إلى إعدادات javac):

class MyFoo extends IFoo.Stub {
    @Override
    public final int getInterfaceVersion() { return super.VERSION; }

    @Override
    public final String getInterfaceHash() { return super.HASH; }
}

ويعود السبب في ذلك إلى أنّ الفئات التي تم إنشاؤها (IFoo وIFoo.Stub وما إلى ذلك) تتم مشاركتها بين العميل والخادم (على سبيل المثال، يمكن أن تكون الفئات في ملف booted classpath). وعند مشاركة الصفوف، يرتبط الخادم أيضًا بـ إصدار أحدث من الفصول الدراسية على الرغم من أنه ربما تم بناؤه باستخدام إصدار من إصدار الواجهة. وإذا تم تنفيذ هذه الواجهة الوصفية في واجهة برمجة التطبيقات فإنها تعرض دائمًا الإصدار الأحدث. ومع ذلك، من خلال تنفيذ الطريقة كما هو موضح أعلاه، رقم إصدار الواجهة مضمن في رمز الخادم (لأنّ IFoo.VERSION هي static final int مضمّنة عند الإشارة إليه) وبالتالي يمكن أن تعرض الطريقة الإصدار المحدد الذي تم إنشاء الخادم باستخدامه.

التعامل مع الواجهات القديمة

من الممكن تحديث العميل إلى الإصدار الأحدث من AIDL ولكن الخادم يستخدم واجهة AIDL القديمة. في هذه الحالات، يؤدي استدعاء طريقة على واجهة قديمة إلى عرض القيمة UNKNOWN_TRANSACTION.

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

مثال في لغة C++ في نظام التشغيل Android 13 والإصدارات الأحدث:

class MyDefault : public IFooDefault {
  Status anAddedMethod(...) {
   // do something default
  }
};

// once per an interface in a process
IFoo::setDefaultImpl(::android::sp<MyDefault>::make());

foo->anAddedMethod(...); // MyDefault::anAddedMethod() will be called if the
                         // remote side is not implementing it

مثال في Java:

IFoo.Stub.setDefaultImpl(new IFoo.Default() {
    @Override
    public xxx anAddedMethod(...)  throws RemoteException {
        // do something default
    }
}); // once per an interface in a process

foo.anAddedMethod(...);

لست بحاجة إلى تقديم التنفيذ التلقائي لجميع الطرق في AIDL من واجهة pyplot. الطرق التي يمكن ضمان تنفيذها في الجانب البعيد (لأنك متأكد من أن جهاز التحكم عن بُعد قد تم إنشاؤه عندما كانت الطرق في لا يلزم إلغاء وصف واجهة AIDL) في impl التلقائي. الصف.

تحويل لغة AIDL الحالية إلى تنسيق AIDL منظَّم أو ثابت

إذا كانت لديك واجهة AIDL حالية ورمز يستخدمها، استخدِم الخطوات التالية لتحويل الواجهة إلى واجهة AIDL ثابتة.

  1. حدِّد جميع التبعيات لواجهة المستخدم. بالنسبة إلى كل حزمة تعتمد عليها الواجهة، حدِّد ما إذا كانت الحزمة محدّدة في حزمة AIDL الثابتة. إذا كانت هذه السمة غير محدّدة، يجب تحويل الحزمة.

  2. حوِّل جميع العناصر القابلة للتجميع في واجهتك إلى عناصر قابلة للتجميع ثابتة (يمكن أن تظل ملفات الواجهة نفسها بدون تغيير). إجراء ذلك من خلال وتعبر عن هيكلها مباشرة في ملفات AIDL. يجب أن تتضمن صفوف الإدارة ستتم إعادة كتابته لاستخدام هذه الأنواع الجديدة. ويمكن إجراء ذلك قبل إنشاء حزمة aidl_interface (أدناه).

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