يضيف 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
) للإشارة إلى إصدار معيّن. أصبح بإمكانك تحديد إصدار منذ Android 12.versions
: الإصدارات السابقة من الواجهة التي تم تجميدها ضمنapi_dir
، وبدءًا من الإصدار 11 من نظام التشغيل Android، يتم تجميد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: ...,
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
مع رسالة تفيد بأنّه لم يتم تنفيذ واجهة برمجة التطبيقات.
للاطّلاع على استراتيجيات التعامل مع هذا الأمر، راجِع إصدارات طلب البحث واستخدام الإعدادات التلقائية.
قطع قِطع الأراضي
عند إضافة حقول جديدة إلى العناصر القابلة للتقسيم، يتخلّص منها العملاء والخوادم القديمة. عندما تتلقّى الخوادم والعملاء الجدد عناصر قابلة للتقسيم قديمة، تتم تعبئة القيم التلقائية للحقول الجديدة تلقائيًا. وهذا يعني أنّه يجب تحديد الإعدادات التلقائية لجميع الحقول الجديدة في عنصر 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"],
...
}
التطوير المستند إلى العلم
لا يمكن استخدام الواجهات قيد التطوير (غير المجمّدة) على الأجهزة المتوفّرة للإصدار العلني، لأنّه لا يمكن ضمان توافقها مع الإصدارات السابقة.
تتيح AIDL الاحتفاظ بنسخة احتياطية من وقت التشغيل لمكتبات الواجهات غير المجمّدة ليتم كتابة الرمز البرمجي وفقًا لأحدث إصدار غير مجمد مع مواصلة استخدامه على أجهزة الإصدار. يشبه سلوك التوافق مع الإصدارات القديمة للعملاء السلوك الحالي، ويجب أن تتّبع عمليات التنفيذ أيضًا هذه السلوكيات مع الإصدار الاحتياطي. راجِع مقالة استخدام الواجهات التي تتضمّن إصدارات.
علامة إنشاء لغة تعريف واجهة نظام Android
العلامة التي تتحكّم في هذا السلوك هي 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 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()
. إذا تم الإبلاغ عن الخطأ وتعذّر تسجيله،
عليك إلغاء العملية. وفي حال عدم الإفصاح عن ذلك، من المتوقّع أن يتعذّر التسجيل.
الحبار كأداة للتنمية
كل عام بعد تجميد VINTF، نعدّل target-level
مصفوفة توافق الإطار (FCM) وPRODUCT_SHIPPING_API_LEVEL
من Cuttlefish
لكي تعكسان الأجهزة التي يتم طرحها مع إصدار العام المقبل. نحن نعدّل
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
، يكون لدى 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)
مقارنةً بالإصدار 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
- استنادًا إلى إصدار 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
وما إلى ذلك) تتم مشاركتها بين العميل والخادم (على سبيل المثال، يمكن أن تكون الفئات في مسار فئة التمهيد). عند مشاركة الصفوف، يتم أيضًا ربط الخادم بالإصدار
الأحدث من الصفوف على الرغم من أنّه قد تم إنشاؤه باستخدام إصدار
قديم من الواجهة. إذا تم تنفيذ هذه الواجهة الوصفية في الفئة المشتركة،
فسيتم دائمًا عرض أحدث إصدار. ومع ذلك، من خلال تنفيذ الطريقة
كما هو موضّح أعلاه، يتم تضمين رقم إصدار الواجهة في رمز الخادم
(لأنّ IFoo.VERSION
هو static final int
يتم تضمينه عند الإشارة إليه)
وبالتالي يمكن للطريقة عرض الإصدار الدقيق الذي تم إنشاء الخادم به.
التعامل مع الواجهات القديمة
من الممكن أن يكون العميل قد تم تحديثه باستخدام الإصدار الأحدث من واجهة ملف تعريف واجهة نظام Android (AIDL)، ولكن يستخدم الخادم واجهة ملف تعريف واجهة نظام Android القديمة. في هذه الحالات، يؤدي
استدعاء طريقة على واجهة قديمة إلى عرض القيمة 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
(كما هو موضّح أعلاه) تحتوي على اسم وحدتك وعناصرها المُستندة إليها وأي معلومات أخرى تحتاج إليها. ولإضفاء ثبات على البيانات (وليس فقط تنظيمها)، يجب أيضًا تحديد إصدار لها. لمزيد من المعلومات، يُرجى الاطّلاع على واجهات تحديد الإصدار.