AIDL مستقر

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

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

AIDL منظم مقابل مستقر

يشير AIDL المنظم إلى الأنواع المحددة تمامًا في AIDL. على سبيل المثال، إعلان قابل للتوزيع (مخصص قابل للتوزيع) ليس 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 للإصدار 13 وما فوق.
  • versions_with_info : قائمة المجموعات، كل منها يحتوي على اسم الإصدار المجمد وقائمة تحتوي على إصدارات مستوردة من وحداتaidl_interface الأخرى التي استوردها هذا الإصدار منaidl_interface. يوجد تعريف الإصدار V لواجهة AIDL IFACE على موقع aidl_api/ IFACE / V . تم تقديم هذا الحقل في Android 13، وليس من المفترض أن يتم تعديله في Android.bp مباشرة. تتم إضافة الحقل أو تحديثه عن طريق استدعاء *-update-api أو *-freeze-api . أيضًا، يتم ترحيل حقول versions تلقائيًا إلى versions_with_info عندما يستدعي المستخدم *-update-api أو *-freeze-api .
  • stability : العلامة الاختيارية لوعد الاستقرار لهذه الواجهة. حاليًا يدعم فقط "vintf" . إذا لم يتم تعيين هذا، فهذا يتوافق مع واجهة ذات استقرار في سياق التجميع هذا (لذلك لا يمكن استخدام الواجهة المحملة هنا إلا مع الأشياء المجمعة معًا، على سبيل المثال في system.img). إذا تم ضبط هذا على "vintf" ، فهذا يتوافق مع وعد الاستقرار: يجب أن تظل الواجهة مستقرة طوال فترة استخدامها.
  • gen_trace : العلامة الاختيارية لتشغيل التتبع أو إيقافه. بدءًا من Android 14، يكون الوضع الافتراضي true بالنسبة للواجهات الخلفية لـ cpp و java .
  • host_supported : العلامة الاختيارية التي عند تعيينها على true تجعل المكتبات التي تم إنشاؤها متاحة للبيئة المضيفة.
  • unstable : العلامة الاختيارية المستخدمة للإشارة إلى أن هذه الواجهة لا تحتاج إلى أن تكون مستقرة. عندما يتم تعيين هذا على true ، فإن نظام الإنشاء لا يقوم بإنشاء تفريغ API للواجهة ولا يتطلب تحديثه.
  • frozen : العلامة الاختيارية التي عند تعيينها على true تعني أن الواجهة لم تشهد أي تغييرات منذ الإصدار السابق للواجهة. وهذا يتيح المزيد من عمليات التحقق من وقت البناء. عند التعيين على false فهذا يعني أن الواجهة قيد التطوير وبها تغييرات جديدة، لذا فإن تشغيل foo-freeze-api سيؤدي إلى إنشاء إصدار جديد وتغيير القيمة تلقائيًا إلى true . تم تقديمه في Android 14.
  • backend.<type>.enabled : تقوم هذه العلامات بتبديل كل من الواجهات الخلفية التي يقوم مترجم AIDL بإنشاء تعليمات برمجية لها. حاليًا، يتم دعم أربع واجهات خلفية: Java وC++ وNDK وRust. يتم تمكين واجهات Java وC++ وNDK الخلفية بشكل افتراضي. إذا لم تكن هناك حاجة إلى أي من هذه الواجهات الخلفية الثلاثة، فيجب تعطيلها بشكل صريح. يتم تعطيل الصدأ بشكل افتراضي.
  • backend.<type>.apex_available : قائمة بأسماء APEX التي تتوفر لها مكتبة كعب الروتين التي تم إنشاؤها.
  • backend.[cpp|java].gen_log : العلامة الاختيارية التي تتحكم في ما إذا كان سيتم إنشاء تعليمات برمجية إضافية لجمع معلومات حول المعاملة.
  • backend.[cpp|java].vndk.enabled : العلامة الاختيارية لجعل هذه الواجهة جزءًا من VNDK. الافتراضي false .
  • backend.[cpp|ndk].additional_shared_libraries : تم تقديم هذه العلامة في Android 14، وتضيف تبعيات إلى المكتبات الأصلية. هذه العلامة مفيدة مع ndk_header و cpp_header .
  • backend.java.sdk_version : العلامة الاختيارية لتحديد إصدار SDK الذي تم إنشاء مكتبة Java Stub عليه. الافتراضي هو "system_current" . لا ينبغي ضبط هذا عندما تكون قيمة backend.java.platform_apis صحيحة.
  • 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 desire 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

مثال في جافا:

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

مثال في الصدأ:

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

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

يؤدي الإعلان عن وحدة بالاسم foo أيضًا إلى إنشاء هدف في نظام الإنشاء يمكنك استخدامه لإدارة واجهة برمجة التطبيقات الخاصة بالوحدة. عند الإنشاء، يضيف foo-freeze-api تعريفًا جديدًا لواجهة برمجة التطبيقات (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 ، يقوم نظام البناء بإجراء عمليات التحقق من التوافق بين الإصدارات المجمدة وأيضًا بين Top of Tree (ToT) وأحدث إصدار مجمد.

بالإضافة إلى ذلك، تحتاج إلى إدارة تعريف واجهة برمجة التطبيقات (API) لإصدار 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" أو "اختبار".

استقرار الواردات

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

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

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

طرق الواجهة

في وقت التشغيل، عند محاولة استدعاء أساليب جديدة على خادم قديم، يحصل العملاء الجدد إما على خطأ أو استثناء، اعتمادًا على الواجهة الخلفية.

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

للتعرف على إستراتيجيات التعامل مع هذا، راجع الاستعلام عن الإصدارات واستخدام الإعدادات الافتراضية .

الطرود

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

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

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

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

النقابات

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

التنمية القائمة على العلم

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

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

AIDL بناء العلم

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

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

تحدد كائنات واجهة البائع (كائنات VINTF) الإصدارات المتوقعة، والإصدارات المتوفرة على جانبي واجهة البائع.

تستهدف معظم الأجهزة غير Cuttlefish أحدث مصفوفة التوافق فقط بعد تجميد الواجهات، لذلك لا يوجد فرق في مكتبات 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 (AOSP التجريبي)، تم تقديم تغيير في 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 الجديدة. نظرًا لأن مكتبة V4 تعتمد على إصدار غير مجمّد من الواجهة، فإنها تتصرف مثل الإصدار المجمّد الأخير، الإصدار 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 ويتم إسقاط قيم الحقول الجديدة التي تحاول الخدمة إرسالها عند الخروج من العملية.

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

لا يتلقى التطبيق مطلقًا استدعاء لأساليب جديدة من أي عميل عندما تكون قيمة RELEASE_AIDL_USE_UNFROZEN false .

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

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

واجهات VINTF المستقرة الجديدة

عند إضافة حزمة واجهة AIDL جديدة، لا يكون هناك آخر إصدار مجمد، لذلك لا يوجد أي سلوك يمكن الرجوع إليه عندما تكون RELEASE_AIDL_USE_UNFROZEN false . لا تستخدم هذه الواجهات. عندما تكون قيمة 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() . إذا تم الإعلان عنه وفشل في التسجيل، قم بإلغاء العملية. إذا لم يتم الإعلان عنه، فمن المتوقع أن يفشل في التسجيل.

الحبار كأداة للتنمية

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

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

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

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

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

  • ifacename : اسم وحدة aidl_interface
  • version هو إما
    • V version-number للإصدارات المجمدة
    • V latest-frozen-version-number + 1 لإصدار طرف الشجرة (الذي لم يتم تجميده بعد)
  • backend إما
    • java للواجهة الخلفية جافا،
    • cpp للواجهة الخلفية لـ C++،
    • ndk أو ndk_platform لواجهة NDK الخلفية. الأول مخصص للتطبيقات، والأخير مخصص لاستخدام النظام الأساسي،
    • 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،

  • 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
  • استنادًا إلى إصدار ToT: 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 ، وما إلى ذلك) تتم مشاركتها بين العميل والخادم (على سبيل المثال، يمكن أن تكون الفئات في مسار فئة التمهيد). عند مشاركة الفئات، يتم ربط الخادم أيضًا بالإصدار الأحدث من الفئات على الرغم من أنه ربما تم إنشاؤه باستخدام إصدار أقدم من الواجهة. إذا تم تنفيذ واجهة التعريف هذه في الفصل المشترك، فستُرجع دائمًا الإصدار الأحدث. ومع ذلك، من خلال تنفيذ الطريقة كما هو مذكور أعلاه، يتم تضمين رقم إصدار الواجهة في كود الخادم (لأن 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

مثال في جافا:

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. لا يلزم تجاوز الطرق التي يتم ضمان تنفيذها في الجانب البعيد (لأنك متأكد من أن جهاز التحكم عن بعد تم إنشاؤه عندما كانت الطرق في وصف واجهة AIDL) في فئة impl الافتراضية.

تحويل AIDL الحالي إلى AIDL منظم/مستقر

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

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

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

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