يضيف نظام التشغيل Android 10 لغة تعريف واجهة Android (AIDL) الثابتة، وهي طريقة جديدة لتتبُّع واجهة برمجة التطبيقات (API) وواجهة التطبيق الثنائية (ABI) المقدَّمة من واجهات AIDL. يعمل تنسيق AIDL الثابت تمامًا مثل تنسيق AIDL، ولكن يتتبّع نظام الإنشاء ملف تعريف التوافق مع الواجهة، وهناك قيود على الإجراءات التي يمكنك اتّخاذها:
- يتم تحديد الواجهات في نظام الإنشاء باستخدام
aidl_interfaces
. - يمكن أن تحتوي الواجهات على بيانات منظَّمة فقط. يتم تلقائيًا إنشاء عناصر Parcelable التي تمثّل الأنواع المفضّلة استنادًا إلى تعريف AIDL ويتم تجميعها وفك تجميعها تلقائيًا.
- يمكن تحديد الواجهات على أنّها مستقرة (متوافقة مع الإصدارات القديمة). وعندما يحدث ذلك، يتم تتبُّع واجهة برمجة التطبيقات وتحديد إصدارها في ملف بجانب واجهة IDE.
لغة تعريف واجهة نظام Android (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 التي تشكّل الواجهة يجب أن يكون المسار لنوع AIDLFoo
المحدّد في حزمة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
) للإشارة إلى إصدار معيّن. أصبح بإمكانك تحديد إصدار منذ الإصدار 12 من Android.versions
: الإصدارات السابقة من الواجهة التي يتم تجميدها ضمنapi_dir
، بدءًا من Android 11، يتم تجميدversions
ضمنaidl_api/name
. إذا لم تكن هناك إصدارات مجمّدة من واجهة، يجب عدم تحديد هذا الخيار ولن يتم إجراء عمليات تحقّق من التوافق. تم استبدال هذا الحقل بـversions_with_info
في الإصدار 13 من نظام التشغيل Android والإصدارات الأحدث.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"
. في حال عدم ضبط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
. تم طرحها في Android 14. backend.<type>.enabled
: تعمل هذه العلامات على تفعيل أو إيقاف كل الخلفيات التي ينشئ عنها مجمع AIDL رمزًا. تتوفّر أربعة أنظمة أساسية: Java وC++ وNDK وRust. يتم تفعيل الخلفيات Java وC++ وNDK تلقائيًا. إذا لم تكن أيّ من الخلفيات الثلاث هذه مطلوبة، يجب إيقافها صراحةً. يكون Rust غير مفعَّل تلقائيًا حتى الإصدار 15 من Android.-
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 stub استنادًا إليها. القيمة التلقائية هي"system_current"
. يجب عدم ضبط هذه القيمة عندما تكونbackend.java.platform_apis
true
. -
backend.java.platform_apis
: العلامة الاختيارية التي يجب ضبطها علىtrue
عندما تحتاج المكتبات التي تم إنشاؤها إلى الإنشاء باستخدام واجهة برمجة تطبيقات النظام الأساسي بدلاً من حزمة SDK
لكلّ تركيبة من الإصدارات وخدمات الخلفية المفعّلة، يتمّ إنشاء مكتبة الرمز المرجعي. لمعرفة كيفية الإشارة إلى إصدار معيّن من مكتبة الرمز المرجعي لنظام خلفية معيّن، يُرجى الاطّلاع على قواعد تسمية الوحدات.
كتابة ملفات AIDL
تتشابه الواجهات في AIDL الثابت مع الواجهات التقليدية، مع اختلاف واحد هو أنّه لا يُسمح لها باستخدام واجهة Parcelable غير المنظَّمة (لأنّه هذه الواجهات غير ثابتة، راجِع واجهة برمجة التطبيقات (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: ...,
// use `shared_libs:` to load your library and its transitive dependencies
// dynamically
shared_libs: ["my-module-name-cpp"],
// use `static_libs:` to include the library in this binary and drop
// transitive dependencies
static_libs: ["my-module-name-cpp"],
...
}
# or
java_... {
name: ...,
// use `static_libs:` to add all jars and classes to this jar
static_libs: ["my-module-name-java"],
// use `libs:` to make these classes available during build time, but
// not add them to the jar, in case the classes are already present on the
// boot classpath (such as if it's in framework.jar) or another jar.
libs: ["my-module-name-java"],
// use `srcs:` with `-java-sources` if you want to add classes in this
// library jar directly, but you get transitive dependencies from
// somewhere else, such as the boot classpath or another jar.
srcs: ["my-module-name-java-source", ...],
...
}
# 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
مع رسالة تفيد بأنّه لم يتم تنفيذ واجهة برمجة التطبيقات.
لمعرفة استراتيجيات التعامل مع هذه المشكلة، يُرجى الاطّلاع على طلب النُسخ واستخدام الإعدادات التلقائية.
العناصر القابلة للتقسيم
عند إضافة حقول جديدة إلى العناصر القابلة للتقسيم، تُسقطها الخوادم والعملاء القدامى. عندما تتلقّى الخوادم والعملاء الجدد عناصر قابلة للتقسيم قديمة، يتم تلقائيًا ملء القيم التلقائية لسمات الجديدة. وهذا يعني أنّه يجب تحديد الإعدادات التلقائية لجميع الحقول الجديدة في عنصر parcelable.
يجب ألا يتوقّع العملاء أن تستخدم الخوادم الحقول الجديدة ما لم يعلموا أنّه يتم تنفيذ الإصدار الذي تم تحديد الحقل فيه على الخادم (راجِع طلب الإصدارات).
عمليات التعداد والثوابت
وبالمثل، يجب أن يرفض العملاء والخوادم القيم الثابتة والمُجمِّعات غير المعروفة أو يتجاهلونها حسب الاقتضاء، لأنّه قد تتم إضافة المزيد في المستقبل. على سبيل المثال، يجب عدم إيقاف الخادم عند استلامه عدّادًا لا يعرف عنه. يجب أن يتجاهل الخادم العداد أو يعرض رسالة تُعلم العميل بأنّه غير متوافق مع هذا التنفيذ.
الاتحادات
لا تنجح محاولة إرسال عملية دمج تتضمّن حقلًا جديدًا إذا كان المستلِم قديمًا ولم يكن لديه معرفة بالحقل. ولن يلاحظ التنفيذ أبدًا عملية الدمج مع
الحقل الجديد. يتم تجاهل الخطأ إذا كانت المعاملة
اتجاه واحد، وإلا يكون الخطأ 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 استخدام الإصدارات الاحتياطية لمكتبات الواجهات غير المجمّدة في وقت التشغيل لكتابة الرمز البرمجي وفقًا لأحدث إصدار غير مجمّد مع مواصلة استخدامه على الأجهزة العلنية. يشبه سلوك التوافق مع الإصدارات القديمة للعملاء السلوك الحالي، ويجب أن تتّبع عمليات التنفيذ أيضًا هذه السلوكيات عند استخدام الإعداد التلقائي. راجِع مقالة استخدام الواجهات التي تتضمّن إصدارات.
علامة إنشاء لغة تعريف واجهة نظام Android
العلامة التي تتحكّم في هذا السلوك هي RELEASE_AIDL_USE_UNFROZEN
وتكون محدّدة في build/release/build_flags.bzl
. يشير الرمز true
إلى أنّه يتم استخدام الإصدار غير المجمّد من
الواجهة في وقت التشغيل، ويشير الرمز false
إلى أنّ مكتبات
الإصدارات غير المجمّدة تتصرف جميعها مثل آخر إصدار مجمّد.
يمكنك إلغاء العلامة ليصبح القيمة true
عند استخدام وضع التطوير على الجهاز، ولكن يجب إعادة ضبطها على false
قبل الإصدار. يتم عادةً تطوير الإصدارات باستخدام إعدادات تم ضبط العلامة فيها على true
.
مصفوفة التوافق وملفات البيان
تحدِّد عناصر واجهة المورّد (عناصر VINTF) الإصدارات المتوقّعة والإصدارات المقدَّمة على أيّ من جانبَي واجهة المورّد.
لا تستهدِف معظم الأجهزة غير المزوّدة بنظام Cuttlefish أحدث مصفوفة توافق
إلا بعد تجميد الواجهات، لذا لا يوجد فرق في مكتبات IDEVELOP
استنادًا إلى 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 client
يجب أن يكون رمز برنامج HAL المتوافق مع العميل متوافقًا مع الإصدارات السابقة المتوافقة والمجمّدة. عندما يكون RELEASE_AIDL_USE_UNFROZEN
هو false
، تبدو الخدمات دائمًا
مثل آخر إصدار مجمّد أو إصدار سابق (على سبيل المثال، يؤدي استدعاء METHODS جديدة غير مجمّدة
إلى عرض UNKNOWN_TRANSACTION
، أو تحتوي حقول parcelable
الجديدة على
قيمها التلقائية). يجب أن يكون عملاء إطار عمل Android متوافقين مع الإصدارات السابقة، ولكن هذه تفاصيل جديدة لعملاء المورّدين وعملاء الواجهات التي يملكها الشركاء.
تغييرات في تنفيذ HAL
يكمن أكبر فرق في تطوير HAL مقارنةً بالتطوير المستنِد إلى العلامة في
اشتراط أن تكون عمليات تنفيذ HAL متوافقة مع الإصدارات القديمة
والأخيرة التي تم تجميدها لكي تعمل عندما يكون RELEASE_AIDL_USE_UNFROZEN
هو false
.
إنّ مراعاة التوافق مع الإصدارات القديمة في عمليات التنفيذ ورمز الجهاز هو عملية جديدة. اطّلِع على استخدام واجهات
محدَّدة الإصدار.
تكون الاعتبارات المتعلقة بالتوافق مع الإصدارات القديمة متماثلة بشكل عام بالنسبة إلى العملاء والخوادم، ورمز إطار العمل ورمز المورّد، ولكن هناك اختلافات طفيفة يجب الانتباه إليها، لأنّك الآن بصدد تنفيذ إصدارَين يستخدمان رمز المصدر نفسه (الإصدار الحالي غير المثبَّت ).
مثال: تحتوي واجهة على ثلاثة إصدارات مجمّدة. تم تعديل الواجهة باستخدام
طريقة جديدة. تم تحديث كلّ من العميل والخدمة لاستخدام الإصدار 4 الجديد من مكتبة Billing Library. وبما أنّ مكتبة الإصدار 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
ويتم تجاهل قيم الحقول الجديدة التي تحاول الخدمة إرسالها عند
الخروج من العملية.
لا يمكن إرسال الأنواع الجديدة المُضافة في هذا الإصدار غير المُجمّد أو استلامها من خلال الواجهة.
لا يتلقّى التنفيذ طلبًا أبدًا من أي عملاء للحصول على طرق جديدة عندما يكون
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()
. إذا تم الإبلاغ عن الخطأ وتعذّر تسجيله، يجب
إيقاف العملية. وفي حال عدم الإفصاح عن ذلك، من المتوقّع أن يتعذّر تسجيله.
استخدام Cuttlefish كأداة تطوير
كل عام بعد تجميد VINTF، نعدّل target-level
مصفوفة توافق الإطار (FCM) وPRODUCT_SHIPPING_API_LEVEL
من Cuttlefish
لكي تعكسان الأجهزة التي يتم طرحها مع إصدار العام المقبل. نحن نعدّل
target-level
وPRODUCT_SHIPPING_API_LEVEL
للتأكّد من توفّر
جهاز إطلاق تم اختباره ويستوفي المتطلبات الجديدة لإصدار volgendjaar's.
عندما يكون RELEASE_AIDL_USE_UNFROZEN
هو true
، يتم استخدام Cuttlefish
لتطوير إصدارات Android المستقبلية. يستهدف مستوى FCM وPRODUCT_SHIPPING_API_LEVEL
لإصدار Android القادم من العام المقبل، ما يتطلّب منه استيفاء متطلبات برامج المورّدين (VSR) للإصدار القادم.
عندما يكون RELEASE_AIDL_USE_UNFROZEN
هو false
، يكون لدى Cuttlefish الإصداران السابقان
target-level
وPRODUCT_SHIPPING_API_LEVEL
للإشارة إلى جهاز الإصدار.
في الإصدار 14 من Android والإصدارات الأقدم، يمكن التمييز بين هذين الإصدارين
باستخدام فروع Git المختلفة التي لا تلتقط التغيير في FCM
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. في الإصدار Android 13 والإصدارات الأحدث، استخدِم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)
مقارنةً بنظام التشغيل Android 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();
مثال على استخدام الخلفية ndk
(وndk_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
يتم تضمينه عند الإشارة إليه)
وبالتالي يمكن للطريقة عرض الإصدار الدقيق الذي تم إنشاء الخادم به.
التعامل مع الواجهات القديمة
من الممكن أن يكون العميل قد تم تحديثه باستخدام الإصدار الأحدث من واجهة لغة تعريف واجهة نظام Android (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. لا يلزم إلغاء تنفيذ الطرق التي يُضمن تنفيذها في الجانب البعيد
(لأنّك متأكد من أنّه تم إنشاء الجانب البعيد عندما كانت الطرق في وصف واجهة IDE) في فئة impl
التلقائية.
تحويل ملف AIDL الحالي إلى ملف AIDL منظَّم أو ثابت
إذا كانت لديك واجهة AIDL حالية ورمز يستخدمها، استخدِم الخطوات التالية لتحويل الواجهة إلى واجهة AIDL ثابتة.
حدِّد جميع التبعيات لواجهة المستخدم. بالنسبة إلى كل حزمة تعتمد عليها الواجهة، حدِّد ما إذا كانت الحزمة محدّدة في حزمة AIDL الثابتة. إذا كانت هذه السمة غير محدّدة، يجب تحويل الحزمة.
حوِّل جميع العناصر القابلة للتجميع في واجهتك إلى عناصر قابلة للتجميع ثابتة (يمكن أن تظل ملفات الواجهة نفسها بدون تغيير). يمكنك إجراء ذلك من خلال التعبير عن بنيتها مباشرةً في ملفات AIDL. يجب إعادة كتابة فئات الإدارة لاستخدام هذه الأنواع الجديدة. ويمكن إجراء ذلك قبل إنشاء حزمة
aidl_interface
(أدناه).أنشئ حزمة
aidl_interface
(كما هو موضّح أعلاه) تحتوي على اسم وحدتك وعناصرها المُستندة إليها وأي معلومات أخرى تحتاج إليها. ولإضفاء ثبات على البيانات (وليس فقط تنظيمها)، يجب أيضًا تحديد إصدار لها. لمزيد من المعلومات، يُرجى الاطّلاع على واجهات تحديد الإصدار.