AIDL แบ็กเอนด์

แบ็กเอนด์ AIDL เป็นเป้าหมายสำหรับการสร้างรหัสต้นขั้ว เมื่อใช้ไฟล์ AIDL คุณจะใช้ไฟล์เหล่านั้นในภาษาใดภาษาหนึ่งพร้อมรันไทม์เฉพาะ คุณควรใช้แบ็กเอนด์ AIDL ที่แตกต่างกัน ทั้งนี้ขึ้นอยู่กับบริบท

AIDL มีแบ็กเอนด์ดังต่อไปนี้:

แบ็กเอนด์ ภาษา พื้นผิว API สร้างระบบ
Java Java SDK/SystemApi (เสถียร*) ทั้งหมด
NDK C++ libbinder_ndk (เสถียร*) aidl_interface
CPP C++ libbinder (ไม่เสถียร) ทั้งหมด
สนิม สนิม libbinder_rs (ไม่เสถียร) aidl_interface
  • พื้นผิว API เหล่านี้มีความเสถียร แต่ API จำนวนมาก เช่น สำหรับการจัดการบริการ สงวนไว้สำหรับการใช้แพลตฟอร์มภายในและไม่พร้อมใช้งานสำหรับแอป สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีใช้ AIDL ในแอป โปรดดู เอกสารประกอบสำหรับนักพัฒนา
  • แบ็กเอนด์ Rust เปิดตัวใน Android 12; แบ็กเอนด์ NDK มีให้บริการตั้งแต่ Android 10
  • ลัง Rust สร้างขึ้นบน libbinder_ndk APEX ใช้กล่องใส่แฟ้มในลักษณะเดียวกับที่ระบบอื่นใช้ ส่วน Rust ถูกรวมเข้ากับ APEX และจัดส่งเข้าไปข้างใน ขึ้นอยู่กับ libbinder_ndk.so ในพาร์ติชันระบบ

สร้างระบบ

มีสองวิธีในการรวบรวม AIDL เป็นรหัสต้นขั้ว ทั้งนี้ขึ้นอยู่กับแบ็กเอนด์ สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับระบบบิลด์ โปรดดูที่ การ อ้างอิงโมดูล Soong

ระบบสร้างหลัก

ในโมดูล java_ cc_ ใดๆ (หรือเทียบเท่ากับ Android.mk ) ไฟล์ .aidl สามารถระบุเป็นไฟล์ต้นทางได้ ในกรณีนี้ แบ็กเอนด์ Java/CPP ของ AIDL จะถูกใช้ (ไม่ใช่แบ็กเอนด์ NDK) และคลาสที่ใช้ไฟล์ AIDL ที่เกี่ยวข้องจะถูกเพิ่มลงในโมดูลโดยอัตโนมัติ อ็อพชัน เช่น local_include_dirs ซึ่งบอกระบบบิลด์ถึงพาธรูทไปยังไฟล์ AIDL ในโมดูลนั้น สามารถระบุได้ในโมดูลเหล่านี้ภายใต้กลุ่ม aidl: โปรดทราบว่าแบ็กเอนด์ Rust ใช้สำหรับ Rust เท่านั้น โมดูล rust_ ได้รับการจัดการแตกต่างกันโดยที่ไฟล์ AIDL ไม่ได้ระบุเป็นไฟล์ต้นทาง แต่โมดูล aidl_interface จะสร้าง rustlib ชื่อ <aidl_interface name>-rust ซึ่งสามารถเชื่อมโยงได้ สำหรับรายละเอียดเพิ่มเติม โปรดดู ตัวอย่าง Rust AIDL

aidl_interface

ดู AIDL ที่เสถียร ชนิดที่ใช้กับระบบบิลด์นี้ต้องมีโครงสร้าง กล่าวคือแสดงเป็น AIDL โดยตรง ซึ่งหมายความว่าไม่สามารถใช้พัสดุแบบกำหนดเองได้

ประเภท

คุณสามารถพิจารณาคอมไพเลอร์ aidl เป็นการใช้งานอ้างอิงสำหรับประเภท เมื่อคุณสร้างอินเทอร์เฟซ ให้เรียกใช้ aidl --lang=<backend> ... เพื่อดูไฟล์อินเทอร์เฟซที่เป็นผลลัพธ์ เมื่อคุณใช้โมดูล aidl_interface คุณสามารถดูเอาต์พุตได้ใน out/soong/.intermediates/<path to module>/

ประเภท Java/AIDL ประเภท C++ ประเภท NDK สนิมประเภท
บูลีน bool bool bool
ไบต์ int8_t int8_t i8
char char16_t char16_t u16
int int32_t int32_t i32
ยาว int64_t int64_t i64
ลอย ลอย ลอย f32
สองเท่า สองเท่า สองเท่า f64
สตริง หุ่นยนต์::String16 std::string สตริง
android.os.parcelable android :: Parcelable ไม่มี ไม่มี
IBinder android::IBinder ndk::SpAIBinder สารยึดเกาะ::SpIBinder
ที[] std::vector<T> std::vector<T> ใน: &T
ออก: Vec<T>
ไบต์[] std::vector<uint8_t> std::vector<int8_t> 1 ใน: &[u8]
ออก: Vec<u8>
รายการ<T> std::vector<T> 2 std::vector<T> 3 ใน: &[T] 4
ออก: Vec<T>
ตัวอธิบายไฟล์ android::base::unique_fd ไม่มี เครื่องผูก::พัสดุ::ParcelFileDescriptor
ParcelFileDescriptor android::os::ParcelFileDescriptor ndk::ScopedFileDescriptor เครื่องผูก::พัสดุ::ParcelFileDescriptor
ประเภทอินเทอร์เฟซ (T) android::sp<T> std::shared_ptr<T> สารยึดเกาะ::แข็งแกร่ง
ประเภทพัสดุ (T) ตู่ ตู่ ตู่
ประเภทสหภาพแรงงาน (T) 5 ตู่ ตู่ ตู่
ที[N] 6 std::array<T, N> std::array<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 หรือพัสดุได้ ใน Android T (รุ่นทดลอง AOSP) หรือสูงกว่า T สามารถเป็นประเภทที่ไม่ใช่แบบพื้นฐานได้ ยกเว้นอาร์เรย์

4. ประเภทจะถูกส่งผ่านแตกต่างกันไปสำหรับรหัส Rust ขึ้นอยู่กับว่าเป็นอินพุต (อาร์กิวเมนต์) หรือเอาต์พุต (ค่าที่ส่งคืน)

5. รองรับประเภท Union ใน Android 12 ขึ้นไป

6. ใน Android T (รุ่นทดลอง AOSP) หรือสูงกว่า รองรับอาร์เรย์ขนาดคงที่ อาร์เรย์ขนาดคงที่สามารถมีหลายมิติได้ (เช่น int[3][4] ) ในแบ็กเอนด์ Java อาร์เรย์ขนาดคงที่จะแสดงเป็นประเภทอาร์เรย์

ทิศทาง (เข้า/ออก/เข้า)

เมื่อระบุประเภทของอาร์กิวเมนต์ให้กับฟังก์ชัน คุณสามารถระบุเป็น in , out หรือ inout สิ่งนี้ควบคุมว่าข้อมูลทิศทางใดจะถูกส่งผ่านสำหรับการโทร IPC in คือทิศทางเริ่มต้น และบ่งชี้ว่าข้อมูลถูกส่งผ่านจากผู้โทรไปยังผู้รับสาย out หมายความว่า ข้อมูลถูกส่งผ่านจากผู้รับสายไปยังผู้โทร inout คือการรวมกันของทั้งสองสิ่งนี้ อย่างไรก็ตาม ทีม Android แนะนำให้คุณหลีกเลี่ยงการใช้ตัวระบุอาร์กิวเมนต์ inout หากคุณใช้ inout กับอินเทอร์เฟซที่มีเวอร์ชันและผู้รับสายที่เก่ากว่า ฟิลด์เพิ่มเติมที่มีอยู่ในผู้โทรเท่านั้นจะถูกรีเซ็ตเป็นค่าเริ่มต้น สำหรับ Rust ประเภท 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

ความเป็นโมฆะ

คุณสามารถใส่คำอธิบายประกอบประเภทที่สามารถเป็นค่าว่างในแบ็กเอนด์ Java ด้วย @nullable เพื่อแสดงค่า null ให้กับแบ็กเอนด์ CPP และ NDK ในแบ็กเอนด์ Rust ประเภท @nullable เหล่านี้ถูกเปิดเผยเป็น Option<T> เซิร์ฟเวอร์เนทีฟปฏิเสธค่า Null โดยค่าเริ่มต้น ข้อยกเว้นเพียงอย่างเดียวคือประเภท interface และ IBinder ซึ่งสามารถเป็นค่าว่างได้เสมอสำหรับการอ่าน NDK และการเขียน CPP/NDK สำหรับข้อมูลเพิ่มเติมเกี่ยวกับหมายเหตุประกอบที่เป็น null ได้ โปรดดูที่ คำอธิบายประกอบใน nullable

Parcelables กำหนดเอง

ในแบ็กเอนด์ C++ และ Java ในระบบบิลด์หลัก คุณสามารถประกาศแพ็กเก็จที่ปรับใช้ด้วยตนเองในแบ็กเอนด์เป้าหมาย (ใน C++ หรือใน Java)

    package my.package;
    parcelable Foo;

หรือด้วยการประกาศส่วนหัว C ++:

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

จากนั้นคุณสามารถใช้พัสดุนี้เป็นประเภทในไฟล์ AIDL ได้ แต่ AIDL จะไม่สร้างไฟล์ดังกล่าว

สนิมไม่รองรับพัสดุแบบกำหนดเอง

ค่าเริ่มต้น

พัสดุที่มีโครงสร้างสามารถประกาศค่าเริ่มต้นสำหรับแต่ละฟิลด์สำหรับ primitives, String s และอาร์เรย์ของประเภทนี้

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

ในแบ็กเอนด์ Java เมื่อไม่มีค่าเริ่มต้น ฟิลด์จะถูกเริ่มต้นเป็นค่าศูนย์สำหรับประเภทพื้นฐานและ null สำหรับประเภทที่ไม่ใช่พื้นฐาน

ในแบ็กเอนด์อื่นๆ ฟิลด์จะเริ่มต้นด้วยค่าเริ่มต้นที่เริ่มต้นเมื่อไม่ได้กำหนดค่าเริ่มต้นไว้ ตัวอย่างเช่น ในแบ็กเอนด์ C++ ฟิลด์ String จะถูกเตรียมข้อมูลเบื้องต้นเป็นสตริงว่าง และฟิลด์ List<T> จะถูกเตรียมข้อมูลเบื้องต้นเป็น vector<T> ว่าง ฟิลด์ @nullable จะเริ่มต้นเป็นฟิลด์ค่า null

การจัดการข้อผิดพลาด

ระบบปฏิบัติการ Android มีประเภทข้อผิดพลาดในตัวสำหรับบริการที่จะใช้เมื่อรายงานข้อผิดพลาด สิ่งเหล่านี้ถูกใช้โดย Binder และสามารถใช้ได้โดยบริการใดๆ ที่ใช้อินเทอร์เฟซ Binder การใช้งานของพวกเขาได้รับการบันทึกไว้อย่างดีในคำจำกัดความของ AIDL และไม่ต้องการสถานะที่ผู้ใช้กำหนดหรือประเภทการส่งคืน

เมื่อฟังก์ชัน AIDL รายงานข้อผิดพลาด ฟังก์ชันอาจไม่เริ่มต้นหรือแก้ไขพารามิเตอร์เอาต์พุต โดยเฉพาะอย่างยิ่ง พารามิเตอร์เอาต์พุตอาจแก้ไขได้หากข้อผิดพลาดเกิดขึ้นระหว่างการแยกส่วน ตรงข้ามกับที่เกิดขึ้นระหว่างการประมวลผลธุรกรรมเอง โดยทั่วไป เมื่อได้รับข้อผิดพลาดจากฟังก์ชัน inout พารามิเตอร์ขาเข้าและขา out ทั้งหมดรวมถึงค่าส่งคืน (ซึ่งทำหน้าที่เหมือนพารามิเตอร์ out ในบางแบ็กเอนด์) ควรได้รับการพิจารณาให้อยู่ในสถานะไม่แน่นอน

หากอินเทอร์เฟซ AIDL ต้องการค่าความผิดพลาดเพิ่มเติมที่ไม่ครอบคลุมโดยประเภทข้อผิดพลาดในตัว พวกเขาอาจใช้ข้อผิดพลาดในตัวเฉพาะบริการพิเศษที่อนุญาตให้รวมค่าข้อผิดพลาดเฉพาะบริการที่กำหนดโดยผู้ใช้ . ข้อผิดพลาดเฉพาะบริการเหล่านี้มักถูกกำหนดไว้ในอินเทอร์เฟซ AIDL เป็น const int หรือ int -backed enum และจะไม่แยกวิเคราะห์โดย Binder

ใน 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 พร้อมกับข้อผิดพลาดที่ผู้ใช้กำหนด

ประเภทข้อผิดพลาดในตัวสามารถพบได้ในไฟล์ต่อไปนี้:

แบ็กเอนด์ คำนิยาม
Java 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)

ดำเนินการบริการ

หากต้องการใช้บริการ คุณต้องสืบทอดจากคลาสต้นขั้วดั้งเดิม คลาสนี้อ่านคำสั่งจากไดรเวอร์ Binder และดำเนินการตามวิธีการที่คุณใช้ ลองนึกภาพว่าคุณมีไฟล์ 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 บริการ นอกเหนือจาก API ด้านล่างแล้ว API บางตัวจะตรวจสอบบริการ (หมายความว่าจะกลับมาทันทีหากไม่มีบริการ) ตรวจสอบอินเทอร์เฟซ 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()
}

คุณสามารถขอรับการแจ้งเตือนเมื่อบริการที่โฮสต์เครื่องผูกตาย ซึ่งจะช่วยหลีกเลี่ยงการรั่วไหลของพร็อกซีการเรียกกลับหรือช่วยในการกู้คืนข้อผิดพลาด ทำการโทรเหล่านี้บนวัตถุพร็อกซี Binder

  • ใน 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 ) จะมีการรวบรวมข้อมูลจากทั่วทั้งระบบเพื่อช่วยในการดีบักปัญหาต่างๆ สำหรับบริการ AIDL รายงานจุดบกพร่องจะใช้ไบนารี dumpsys สในบริการทั้งหมดที่ลงทะเบียนกับตัวจัดการบริการเพื่อดัมพ์ข้อมูลลงในรายงานจุดบกพร่อง คุณยังสามารถใช้ dumpsys บน commandline เพื่อรับข้อมูลจากบริการที่มี dumpsys SERVICE [ARGS] ในแบ็กเอนด์ C++ และ Java คุณสามารถควบคุมลำดับที่บริการถูกดัมพ์โดยใช้อาร์กิวเมนต์เพิ่มเติมเพื่อ addService คุณยังสามารถใช้ dumpsys --pid SERVICE เพื่อรับ PID ของบริการขณะทำการดีบัก

ในการเพิ่มเอาต์พุตแบบกำหนดเองให้กับบริการของคุณ คุณสามารถแทนที่วิธีการ 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 เมื่อใช้งานอินเทอร์เฟซ ให้ระบุสิ่งต่อไปนี้ (แทนที่จะปล่อยให้เป็นค่าเริ่มต้น):

    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 คุณสามารถรับ descriptor โดยการเพิ่มโค้ดเช่น:

    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()

Enum Range

ในแบ็กเอนด์ดั้งเดิม คุณสามารถวนซ้ำค่าที่เป็นไปได้ที่ enum สามารถรับได้ เนื่องจากการพิจารณาขนาดโค้ด จาวายังไม่รองรับสิ่งนี้ในขณะนี้

สำหรับ enum MyEnum ที่กำหนดใน AIDL การวนซ้ำมีดังต่อไปนี้

ในแบ็กเอนด์ CPP:

    ::android::enum_range<MyEnum>()

ในแบ็กเอนด์ NDK:

   ::ndk::enum_range<MyEnum>()

ในแบ็กเอนด์ Rust:

    MyEnum::enum_range()

การจัดการเธรด

ทุกอินสแตนซ์ของ libbinder ในกระบวนการจะรักษาหนึ่งเธรดพูล สำหรับกรณีการใช้งานส่วนใหญ่ ค่านี้ควรเป็นหนึ่ง threadpool เดียวที่แชร์กับแบ็กเอนด์ทั้งหมด ข้อยกเว้นเพียงอย่างเดียวคือเมื่อรหัสผู้ขายอาจโหลดสำเนา libbinder เพื่อพูดคุยกับ /dev/vndbinder เนื่องจากสิ่งนี้อยู่บนโหนด Binder ที่แยกจากกัน Threadpool จึงไม่ถูกแบ่งใช้

สำหรับแบ็กเอนด์ Java threadpool สามารถเพิ่มขนาดได้เท่านั้น (เนื่องจากเริ่มต้นแล้ว):

    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();