AIDL مستقر

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

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

تحديد واجهة AIDL

تعريف لل aidl_interface يبدو مثل هذا:

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

}
  • name : اسم وحدة واجهة AIDL أن identies فريد واجهة 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 الخاص بك يستخدم واجهة أو parcelable من آخر aidl_interface ، وضعت اسمها هنا. يمكن أن يكون هذا الاسم في حد ذاته، للإشارة إلى الإصدار الأحدث، أو الاسم مع لاحقة إصدار (مثل -V1 ) للإشارة إلى نسخة محددة. تحديد الإصدار مدعوم منذ Android 12
  • versions : إن الإصدارات السابقة من واجهة التي يتم تجميدها بموجب api_dir ، ابتداء من الروبوت 11، versions هي المجمدة تحت aidl_api/ name . إذا لم تكن هناك إصدارات مجمدة للواجهة ، فلا ينبغي تحديد ذلك ، ولن تكون هناك فحوصات توافق.
  • stability : العلم اختياري لوعد استقرار هذه الواجهة. حاليا يعتمد فقط "vintf" . إذا لم يتم ضبط هذا ، فهذا يتوافق مع واجهة ذات ثبات ضمن سياق التجميع هذا (لذلك لا يمكن استخدام الواجهة المحملة هنا إلا مع الأشياء المجمعة معًا ، على سبيل المثال على system.img). إذا تم تعيين هذا إلى "vintf" ، وهذا يتوافق مع وعد الاستقرار: يجب أن تبقى واجهة مستقرة طالما يتم استخدامه.
  • gen_trace : العلم اختياري لتشغيل تتبع أو إيقاف تشغيله. الافتراضي هو false .
  • host_supported : العلم اختياري عند تعيينها إلى true يجعل المكتبات ولدت المتاحة للبيئة المضيف.
  • unstable : العلم اختياري يستخدم في تحديد أن هذه الواجهة لا يحتاج إلى أن تكون مستقرة. عندما يتم تعيين هذا true ، وبناء نظام لا يخلق تفريغ API للواجهة ولا يتطلب ذلك إلى تحديث.
  • backend.<type>.enabled : هذه العلامات تبديل على كل من الخلفيات التي المترجم AIDL سوف تولد رمز ل. حاليا، يتم اعتماد ثلاثة الخلفيات: java ، cpp ، و ndk . يتم تمكين جميع الخلفيات بشكل افتراضي. عندما لا تكون هناك حاجة إلى خلفية محددة ، يجب تعطيلها بشكل صريح.
  • backend.<type>.apex_available : قائمة أسماء APEX أن المكتبة كعب ولدت متاحة لل.
  • backend.[cpp|java].gen_log : العلم اختياري ضوابط سواء لتوليد رمز إضافية لجمع المعلومات حول هذه الصفقة.
  • backend.[cpp|java].vndk.enabled : العلم اختياري لجعل هذه الواجهة جزءا من VNDK. الافتراضي هو false .
  • backend.java.platform_apis : العلم اختياري ضوابط سواء بنيت مكتبة جافا كعب ضد واجهات برمجة التطبيقات الخاصة من المنصة. يجب تعيين هذا إلى "true" عندما stability ومن المقرر أن "vintf" .
  • backend.java.sdk_version : العلم اختياري لتحديد إصدار SDK أن مكتبة جافا كعب بنيت ضده. الافتراضي هو "system_current" . هذا لا ينبغي أن يكون مجموعة عندما backend.java.platform_apis غير صحيح.
  • backend.java.platform_apis : العلم الاختيارية التي يجب تعيينها إلى true عندما تحتاج المكتبات ولدت لبناء ضد API منصة بدلا من SDK.

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

كتابة ملفات AIDL

تشبه الواجهات في AIDL المستقر الواجهات التقليدية ، باستثناء أنه لا يُسمح لها باستخدام عناصر غير منظمة (لأنها غير مستقرة!). الاختلاف الأساسي في AIDL المستقر هو كيفية تعريف الطرود. سابقا، تم إعلان parcelables إلى الأمام. في 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"],
    ...
}

المثال في 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

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

تعلن وحدة نمطية مع اسم فو يخلق أيضا هدفا في بناء نظام التي يمكنك استخدامها لإدارة API من الوحدة النمطية. عندما بنيت، فو تجميد المعهد يضيف تعريف API جديد تحت api_dir أو aidl_api/ name ، اعتمادا على الإصدار الروبوت، ويضيف .hash ملف، سواء تمثل النسخة المجمدة حديثا من واجهة. بناء هذه التحديثات أيضا versions الملكية لتعكس نسخة إضافية. وبمجرد أن versions يتم تحديد الملكية، يعمل نظام بناء الشيكات التوافق بين الإصدارات المجمدة وأيضا بين أعلى شجرة (تدريب المدربين) وأحدث نسخة المجمدة.

بالإضافة إلى ذلك ، تحتاج إلى إدارة تعريف API الخاص بإصدار ToT. كلما وAPI المحدثة، تشغيل فو التحديث المعهد إلى تحديث aidl_api/ name /current الذي يحتوي على تعريف API تدريب المدربين نسخة ل.

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

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

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

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

طرق الواجهة

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

الطرود

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

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

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

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

النقابات

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

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

في 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. الأول مخصص للتطبيقات ، والأخير لاستخدام النظام الأساسي.

نفترض أن هناك وحدة نمطية مع اسم فو والإصدار الأخير هو وتدعم كل من NDK وC ++. في هذه الحالة ، يُنشئ AIDL الوحدات النمطية التالية:

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

مقارنة بـ Android 11 ،

  • foo- backend ، التي تشير إلى أحدث إصدار مستقر تصبح foo- V2 - backend
  • foo-unstable- backend ، التي تشير إلى إصدار تدريب المدربين يصبح foo- V3 - backend

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

  • وبناء على النسخة 1: foo-V1-(cpp|ndk|ndk_platform).so
  • واستنادا إلى الإصدار 2: foo-V2-(cpp|ndk|ndk_platform).so
  • وبناء على نسخة تدريب المدربين: foo-V3-(cpp|ndk|ndk_platform).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();

للغة جافا، والجانب البعيد يجب تنفيذ getInterfaceVersion() و getInterfaceHash() على النحو التالي:

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

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

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

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

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

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

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

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

// once per an interface in a process
IFoo::setDefaultImpl(std::unique_ptr<IFoo>(MyDefault));

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

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

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

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

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

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