خلفيات AIDL

تعد الواجهة الخلفية لـ AIDL هدفًا لإنشاء كود كعب الروتين. عند استخدام ملفات AIDL ، فأنت تستخدمها دائمًا بلغة معينة مع وقت تشغيل محدد. اعتمادًا على السياق ، يجب عليك استخدام خلفيات AIDL مختلفة.

AIDL لديه الخلفيات التالية:

الخلفية لغة سطح API بناء الأنظمة
جافا جافا SDK / SystemApi (مستقر *) الكل
NDK سي ++ libbinder_ndk (مستقر *) واجهة المساعد
CPP سي ++ libbinder (غير مستقر) الكل
الصدأ الصدأ libbinder_rs (غير مستقر) واجهة المساعد
  • أسطح API هذه مستقرة ، ولكن العديد من واجهات برمجة التطبيقات ، مثل تلك الخاصة بإدارة الخدمة ، محجوزة لاستخدام النظام الأساسي الداخلي وغير متاحة للتطبيقات. لمزيد من المعلومات حول كيفية استخدام AIDL في التطبيقات ، راجع وثائق المطور .
  • تم تقديم Rust backend في Android 12 ؛ كانت الواجهة الخلفية NDK متاحة اعتبارًا من Android 10.
  • تم بناء صندوق الصدأ فوق libbinder_ndk . تستخدم APEXes صندوق الموثق بنفس الطريقة التي يستخدمها أي شخص آخر على جانب النظام. يتم تجميع جزء الصدأ في APEX ويتم شحنه بداخله. يعتمد ذلك على libbinder_ndk.so على قسم النظام.

بناء الأنظمة

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

نظام البناء الأساسي

في أي cc_ أو java_ Android.bp (أو في معادلاتها Android.mk ) ، يمكن تحديد ملفات .aidl كملفات مصدر. في هذه الحالة ، يتم استخدام الواجهات الخلفية Java / CPP لـ AIDL (وليس الخلفية NDK) ، ويتم إضافة الفئات التي تستخدم ملفات AIDL المقابلة إلى الوحدة تلقائيًا. يمكن تحديد خيارات مثل local_include_dirs ، التي تخبر نظام الإنشاء مسار الجذر لملفات AIDL في تلك الوحدة النمطية في هذه الوحدات النمطية ضمن aidl: group. لاحظ أن الواجهة الخلفية Rust مخصصة للاستخدام مع Rust فقط. يتم التعامل مع وحدات rust_ بشكل مختلف حيث لم يتم تحديد ملفات AIDL كملفات مصدر. بدلاً من ذلك ، تنتج الوحدة النمطية aidl_interface عنصرًا rustlib يسمى <aidl_interface name>-rust والذي يمكن ربطه مقابله. لمزيد من التفاصيل ، راجع مثال Rust AIDL .

واجهة المساعد

انظر AIDL المستقر . يجب هيكلة الأنواع المستخدمة مع نظام البناء هذا ؛ وهذا معبر عنه في AIDL مباشرة. هذا يعني أنه لا يمكن استخدام الطرود المخصصة.

أنواع

يمكنك اعتبار مترجم aidl كتطبيق مرجعي للأنواع. عند إنشاء واجهة ، قم باستدعاء aidl --lang=<backend> ... لمشاهدة ملف الواجهة الناتج. عند استخدام وحدة aidl_interface ، يمكنك عرض الإخراج في out/soong/.intermediates/<path to module>/ .

جافا / نوع AIDL نوع C ++ نوع NDK نوع الصدأ
قيمة منطقية منطقي منطقي منطقي
بايت int8_t int8_t i8
شار char16_t char16_t u16
int int32_t int32_t i32
طويل int64_t int64_t i64
يطفو يطفو يطفو f32
مزدوج مزدوج مزدوج f64
سلسلة android :: String16 الأمراض المنقولة جنسيا :: سلسلة سلسلة
android.os.Parcelable android :: Parcelable غير متاح غير متاح
آي بيندر android :: IBinder ndk :: SpAIBinder الموثق :: SpIBinder
تي [] الأمراض المنقولة جنسيا :: متجه <T> الأمراض المنقولة جنسيا :: متجه <T> في: & T.
خارج: Vec <T>
بايت [] الأمراض المنقولة جنسيا :: متجه <uint8_t> الأمراض المنقولة جنسياً :: المتجه <int8_t> 1 في: & [u8]
خارج: Vec <u8>
قائمة <T> الأمراض المنقولة جنسياً :: متجه <T> 2 الأمراض المنقولة جنسيا :: متجه <T> 3 في: & [T] 4
خارج: Vec <T>
واصف الملف android :: Base :: unique_fd غير متاح الموثق :: parcel :: ParcelFileDescriptor
ParcelFileDescriptor android :: os :: ParcelFileDescriptor ndk :: ScopedFileDescriptor الموثق :: parcel :: ParcelFileDescriptor
نوع الواجهة (T) android :: sp <T> الأمراض المنقولة جنسيا :: shared_ptr <T> الموثق :: قوي
نوع لا يتجزأ (T) تي تي تي
نوع الاتحاد (T) 5 تي تي تي
تي [ن] 6 الأمراض المنقولة جنسيا :: مجموعة <T ، N> الأمراض المنقولة جنسيا :: مجموعة <T ، N> [تي ؛ ن]

1. في Android 12 أو إصدار أحدث ، تستخدم مصفوفات البايت uint8_t بدلاً من int8_t لأسباب تتعلق بالتوافق.

2. تدعم الواجهة الخلفية C ++ List<T> حيث تكون T واحدة من String أو IBinder أو ParcelFileDescriptor أو لا يتجزأ. في Android T (AOSP تجريبي) أو أعلى ، يمكن أن يكون T أي نوع غير بدائي (بما في ذلك أنواع الواجهة) باستثناء المصفوفات. توصي AOSP باستخدام أنواع مصفوفة مثل T[] ، لأنها تعمل في جميع الخلفيات.

3. تدعم الواجهة الخلفية NDK List<T> حيث تكون T واحدة من String أو ParcelFileDescriptor أو parcelable. في Android T (AOSP تجريبي) أو أعلى ، يمكن أن يكون T أي نوع غير بدائي باستثناء المصفوفات.

4. يتم تمرير الأنواع بشكل مختلف لرمز Rust اعتمادًا على ما إذا كانت مدخلات (وسيطة) ، أو مخرجات (قيمة تم إرجاعها).

5. أنواع الاتحاد مدعومة في Android 12 والإصدارات الأحدث.

6. في Android T (تجريبي AOSP) أو أعلى ، يتم دعم المصفوفات ذات الحجم الثابت. يمكن أن تحتوي المصفوفات ذات الحجم الثابت على أبعاد متعددة (على سبيل المثال int[3][4] ). في الواجهة الخلفية لـ Java ، يتم تمثيل المصفوفات ذات الحجم الثابت كأنواع مصفوفة.

الاتجاهية (الداخل / الخارج / الداخل)

عند تحديد أنواع الوسيطات الخاصة بالدوال ، يمكنك تحديدها على أنها in ، أو out ، أو inout . يتحكم هذا في تحديد معلومات الاتجاه التي يتم تمريرها لمكالمة IPC. in هو الاتجاه الافتراضي ، ويشير إلى أن البيانات يتم تمريرها من المتصل إلى المستدعي. out يعني أن البيانات يتم تمريرها من المستدعى إلى المتصل. inout هو مزيج من كلاهما. ومع ذلك ، يوصي فريق Android بتجنب استخدام محدد الوسيطة inout . إذا كنت تستخدم inout مع واجهة ذات إصدار ومستدعي أقدم ، فسيتم إعادة تعيين الحقول الإضافية الموجودة فقط في المتصل إلى قيمها الافتراضية. فيما يتعلق بالصدأ ، يتلقى نوع inout العادي &mut Vec<T> ، ويستقبل نوع القائمة inout &mut Vec<T> .

UTF8 / UTF16

باستخدام الواجهة الخلفية لـ CPP ، يمكنك اختيار ما إذا كانت السلاسل هي utf-8 أو utf-16. قم بتعريف السلاسل على أنها @utf8InCpp String في AIDL لتحويلها تلقائيًا إلى utf-8. تستخدم الخلفيات NDK و Rust دائمًا سلاسل utf-8. لمزيد من المعلومات حول التعليق التوضيحي utf8InCpp ، راجع التعليقات التوضيحية في AIDL .

بطلان

يمكنك إضافة تعليق توضيحي على الأنواع التي يمكن أن تكون خالية في الواجهة الخلفية لجافا باستخدام @nullable لفضح القيم الفارغة للخلفيات الخلفية CPP و NDK. في الواجهة الخلفية Rust ، يتم عرض هذه الأنواع @nullable كخيار Option<T> . ترفض الخوادم الأصلية القيم الخالية افتراضيًا. الاستثناءات الوحيدة لذلك هي أنواع interface وأنواع IBinder ، والتي يمكن أن تكون دائمًا خالية لقراءات NDK وكتب CPP / NDK. لمزيد من المعلومات حول التعليق التوضيحي nullable ، راجع التعليقات التوضيحية في AIDL .

الطرود المخصصة

في خلفيات C ++ و Java الخلفية في نظام البناء الأساسي ، يمكنك الإعلان عن عنصر لا يتجزأ يتم تنفيذه يدويًا في الخلفية المستهدفة (في C ++ أو في Java).

    package my.package;
    parcelable Foo;

أو مع إعلان رأس C ++:

    package my.package;
    parcelable Foo cpp_header "my/package/Foo.h";

ثم يمكنك استخدام هذا النوع من الطرود كنوع في ملفات AIDL ، لكن لن يتم إنشاؤه بواسطة AIDL.

الصدأ لا يدعم الطرود المخصصة.

قيم افتراضية

يمكن أن تعلن العناصر المركبة الهيكلية عن القيم الافتراضية لكل حقل للأولويات والسلسلة String من هذه الأنواع.

    parcelable Foo {
      int numField = 42;
      String stringField = "string value";
      char charValue = 'a';
      ...
    }

في الواجهة الخلفية لـ Java عندما تكون القيم الافتراضية مفقودة ، تتم تهيئة الحقول كقيم صفرية للأنواع الأولية null للأنواع غير الأولية.

في الخلفيات الأخرى ، تتم تهيئة الحقول بقيم افتراضية مهيأة عندما لا يتم تحديد القيم الافتراضية. على سبيل المثال ، في الخلفية C ++ ، تتم تهيئة حقول String كسلسلة فارغة ويتم تهيئة حقول List<T> vector<T> . يتم تهيئة الحقول @nullable كحقول ذات قيمة خالية.

معالجة الأخطاء

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

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

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

في Java ، يتم تعيين الأخطاء للاستثناءات ، مثل android.os.RemoteException . بالنسبة للاستثناءات الخاصة بالخدمة ، تستخدم Java android.os.ServiceSpecificException جنبًا إلى جنب مع الخطأ المحدد من قبل المستخدم.

لا يستخدم الكود الأصلي في Android استثناءات. تستخدم الواجهة الخلفية CPP android::binder::Status . تستخدم الواجهة الخلفية NDK ndk::ScopedAStatus . كل طريقة تم إنشاؤها بواسطة AIDL ترجع واحدة من هذه ، والتي تمثل حالة الطريقة. تستخدم الواجهة الخلفية Rust نفس قيم رمز الاستثناء مثل NDK ، ولكنها تحولها إلى أخطاء Rust أصلية ( StatusCode ، ExceptionCode ) قبل تسليمها إلى المستخدم. بالنسبة للأخطاء الخاصة بالخدمة ، تستخدم الحالة التي تم إرجاعها أو Status ScopedAStatus EX_SERVICE_SPECIFIC جنبًا إلى جنب مع الخطأ المحدد من قبل المستخدم.

يمكن العثور على أنواع الأخطاء المضمنة في الملفات التالية:

الخلفية تعريف
جافا android/os/Parcel.java
CPP binder/Status.h
NDK android/binder_status.h
الصدأ android/binder_status.h

استخدام خلفيات مختلفة

هذه التعليمات خاصة برمز نظام Android الأساسي. تستخدم هذه الأمثلة نوعًا محددًا ، my.package.IFoo . للحصول على إرشادات حول كيفية استخدام الواجهة الخلفية لـ Rust ، راجع مثال Rust AIDL في صفحة Android Rust Patterns .

استيراد الأنواع

سواء كان النوع المحدد واجهة أم لا يتجزأ أم اتحادًا ، يمكنك استيراده في Java:

    import my.package.IFoo;

أو في الخلفية CPP:

    #include <my/package/IFoo.h>

أو في NDK الخلفية (لاحظ مساحة الاسم الإضافية aidl ):

    #include <aidl/my/package/IFoo.h>

أو في الواجهة الخلفية Rust:

    use my_package::aidl::my::package::IFoo;

على الرغم من أنه يمكنك استيراد نوع متداخل في Java ، إلا أنه في الخلفيات الخلفية CPP / NDK ، يجب عليك تضمين الرأس لنوع الجذر الخاص به. على سبيل المثال ، عند استيراد Bar من النوع المتداخل معرّف في my/package/IFoo.aidl ( IFoo هو نوع جذر الملف) ، يجب عليك تضمين <my/package/IFoo.h> للواجهة الخلفية CPP (أو <aidl/my/package/IFoo.h> NDK).

تنفيذ الخدمات

لتنفيذ خدمة ، يجب أن ترث من فئة كعب الروتين الأصلي. يقرأ هذا الفصل الأوامر من برنامج تشغيل الموثق وينفذ الأساليب التي تقوم بتنفيذها. تخيل أن لديك ملف AIDL مثل هذا:

    package my.package;
    interface IFoo {
        int doFoo();
    }

في Java ، يجب أن تمتد من هذه الفئة:

    import my.package.IFoo;
    public class MyFoo extends IFoo.Stub {
        @Override
        int doFoo() { ... }
    }

في الخلفية CPP:

    #include <my/package/BnFoo.h>
    class MyFoo : public my::package::BnFoo {
        android::binder::Status doFoo(int32_t* out) override;
    }

في NDK الخلفية (لاحظ مساحة الاسم الإضافية aidl ):

    #include <aidl/my/package/BnFoo.h>
    class MyFoo : public aidl::my::package::BnFoo {
        ndk::ScopedAStatus doFoo(int32_t* out) override;
    }

في الخلفية Rust:

    use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFoo};
    use binder;

    /// This struct is defined to implement IRemoteService AIDL interface.
    pub struct MyFoo;

    impl Interface for MyFoo {}

    impl IFoo for MyFoo {
        fn doFoo(&self) -> binder::Result<()> {
           ...
           Ok(())
        }
    }

التسجيل والحصول على الخدمات

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

في جافا:

    import android.os.ServiceManager;
    // registering
    ServiceManager.addService("service-name", myService);
    // getting
    myService = IFoo.Stub.asInterface(ServiceManager.getService("service-name"));
    // waiting until service comes up (new in Android 11)
    myService = IFoo.Stub.asInterface(ServiceManager.waitForService("service-name"));
    // waiting for declared (VINTF) service to come up (new in Android 11)
    myService = IFoo.Stub.asInterface(ServiceManager.waitForDeclaredService("service-name"));

في الخلفية CPP:

    #include <binder/IServiceManager.h>
    // registering
    defaultServiceManager()->addService(String16("service-name"), myService);
    // getting
    status_t err = getService<IFoo>(String16("service-name"), &myService);
    // waiting until service comes up (new in Android 11)
    myService = waitForService<IFoo>(String16("service-name"));
    // waiting for declared (VINTF) service to come up (new in Android 11)
    myService = waitForDeclaredService<IFoo>(String16("service-name"));

في NDK الخلفية (لاحظ مساحة الاسم الإضافية aidl ):

    #include <android/binder_manager.h>
    // registering
    status_t err = AServiceManager_addService(myService->asBinder().get(), "service-name");
    // getting
    myService = IFoo::fromBinder(SpAIBinder(AServiceManager_getService("service-name")));
    // is a service declared in the VINTF manifest
    // VINTF services have the type in the interface instance name.
    bool isDeclared = AServiceManager_isDeclared("android.hardware.light.ILights/default");
    // wait until a service is available (if isDeclared or you know it's available)
    myService = IFoo::fromBinder(SpAIBinder(AServiceManager_waitForService("service-name")));

في الخلفية Rust:

use myfoo::MyFoo;
use binder;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;

fn main() {
    binder::ProcessState::start_thread_pool();
    // [...]
    let my_service = MyFoo;
    let my_service_binder = BnFoo::new_binder(
        my_service,
        BinderFeatures::default(),
    );
    binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
    // Does not return - spawn or perform any work you mean to do before this call.
    binder::ProcessState::join_thread_pool()
}

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

  • في Java ، استخدم android.os.IBinder::linkToDeath .
  • في الواجهة الخلفية CPP ، استخدم android::IBinder::linkToDeath .
  • في NDK الخلفية ، استخدم AIBinder_linkToDeath .
  • في الواجهة الخلفية Rust ، أنشئ كائن DeathRecipient ، ثم اتصل my_binder.link_to_death(&mut my_death_recipient) . لاحظ أنه نظرًا لأن DeathRecipient يمتلك رد الاتصال ، يجب أن تبقي هذا الكائن حيًا طالما أنك تريد تلقي الإشعارات.

تقارير الأخطاء وتصحيح أخطاء API للخدمات

عند تشغيل تقارير الأخطاء (على سبيل المثال ، مع adb bugreport ) ، فإنها تجمع المعلومات من جميع أنحاء النظام للمساعدة في تصحيح المشكلات المختلفة. بالنسبة لخدمات dumpsys ، تستخدم تقارير الأخطاء عمليات التفريغ الثنائية في جميع الخدمات المسجلة لدى مدير الخدمة لتفريغ معلوماتها في تقرير الأخطاء. يمكنك أيضًا استخدام dumpsys في سطر الأوامر للحصول على معلومات من خدمة مع خدمة dumpsys SERVICE [ARGS] . في واجهات C ++ و Java الخلفية ، يمكنك التحكم في الترتيب الذي يتم به إغراق الخدمات باستخدام وسيطات إضافية addService . يمكنك أيضًا استخدام dumpsys --pid SERVICE للحصول على معرف المنتج للخدمة أثناء التصحيح.

لإضافة مخرجات مخصصة إلى خدمتك ، يمكنك تجاوز طريقة dump في كائن الخادم كما لو كنت تقوم بتنفيذ أي طريقة IPC أخرى محددة في ملف AIDL. عند القيام بذلك ، يجب عليك تقييد الإغراق على إذن التطبيق android.permission.DUMP أو قصر الإغراق على معرّفات UID محددة.

في الواجهة الخلفية لـ Java:

    @Override
    protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
        @Nullable String[] args) {...}

في الخلفية CPP:

    status_t dump(int, const android::android::Vector<android::String16>&) override;

في الخلفية NDK:

    binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;

في Rust backend ، عند تنفيذ الواجهة ، حدد ما يلي (بدلاً من السماح لها بالتعيين الافتراضي):

    fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>

ديناميكي الحصول على واصف الواجهة

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

في Java ، يمكنك الحصول على واصف الواجهة برمز مثل:

    service = /* get ahold of service object */
    ... = service.asBinder().getInterfaceDescriptor();

في الخلفية CPP:

    service = /* get ahold of service object */
    ... = IInterface::asBinder(service)->getInterfaceDescriptor();

لا تدعم الخلفيات NDK و Rust هذه الوظيفة.

الحصول على واصف الواجهة بشكل ثابت

في بعض الأحيان (مثل عند تسجيل خدمات @VintfStability ) ، تحتاج إلى معرفة واصف الواجهة بشكل ثابت. في Java ، يمكنك الحصول على الواصف عن طريق إضافة كود مثل:

    import my.package.IFoo;
    ... IFoo.DESCRIPTOR

في الخلفية CPP:

    #include <my/package/BnFoo.h>
    ... my::package::BnFoo::descriptor

في NDK الخلفية (لاحظ مساحة الاسم الإضافية aidl ):

    #include <aidl/my/package/BnFoo.h>
    ... aidl::my::package::BnFoo::descriptor

في الخلفية Rust:

    aidl::my::package::BnFoo::get_descriptor()

نطاق التعداد

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

بالنسبة MyEnum المحدد في AIDL ، يتم توفير التكرار على النحو التالي.

في الخلفية CPP:

    ::android::enum_range<MyEnum>()

في الخلفية NDK:

   ::ndk::enum_range<MyEnum>()

في الخلفية Rust:

    MyEnum::enum_range()

إدارة الموضوع

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

بالنسبة للواجهة الخلفية لجافا ، يمكن أن يزيد حجم مجموعة سلاسل العمليات (حيث أنها بدأت بالفعل):

    BinderInternal.setMaxThreads(<new larger value>);

بالنسبة للواجهة الخلفية CPP ، تتوفر العمليات التالية:

    // set max threadpool count (default is 15)
    status_t err = ProcessState::self()->setThreadPoolMaxThreadCount(numThreads);
    // create threadpool
    ProcessState::self()->startThreadPool();
    // add current thread to threadpool (adds thread to max thread count)
    IPCThreadState::self()->joinThreadPool();

وبالمثل ، في الخلفية NDK:

    bool success = ABinderProcess_setThreadPoolMaxThreadCount(numThreads);
    ABinderProcess_startThreadPool();
    ABinderProcess_joinThreadPool();

في الخلفية Rust:

    binder::ProcessState::start_thread_pool();
    binder::add_service(“myservice”, my_service_binder).expect(“Failed to register service?”);
    binder::ProcessState::join_thread_pool();