AIDL สำหรับ HALs

Android 11 แนะนำความสามารถในการใช้ AIDL สำหรับ HAL ใน Android ทำให้สามารถนำส่วนของ Android ไปใช้โดยไม่ต้องใช้ HIDL ได้ การเปลี่ยน HAL ไปใช้ AIDL โดยเฉพาะเมื่อเป็นไปได้ (เมื่อ HAL ต้นน้ำใช้ HIDL จะต้องใช้ HIDL)

HAL ที่ใช้ AIDL เพื่อสื่อสารระหว่างส่วนประกอบของเฟรมเวิร์ก เช่น ส่วนประกอบใน system.img และส่วนประกอบฮาร์ดแวร์ เช่น ส่วนประกอบใน vendor.img ต้องใช้ AIDL ที่เสถียร อย่างไรก็ตาม ในการสื่อสารภายในพาร์ติชัน เช่น จาก HAL หนึ่งไปยังอีก HAL ไม่มีข้อจำกัดในการใช้กลไก IPC

แรงจูงใจ

AIDL มีอายุการใช้งานยาวนานกว่า HIDL และมีการใช้ในสถานที่อื่นๆ หลายแห่ง เช่น ระหว่างส่วนประกอบของเฟรมเวิร์ก Android หรือในแอป ขณะนี้ AIDL มีการรองรับความเสถียรแล้ว คุณจึงนำสแต็กทั้งหมดไปใช้โดยใช้รันไทม์ IPC เดียวได้ AIDL ยังมีระบบการกำหนดเวอร์ชันที่ดีกว่า HIDL

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

การเขียนอินเทอร์เฟซ AIDL HAL

สำหรับอินเทอร์เฟซ AIDL ที่จะใช้ระหว่างระบบและผู้ขาย อินเทอร์เฟซจำเป็นต้องมีการเปลี่ยนแปลงสองประการ:

  • คำจำกัดความทุกประเภทจะต้องมีคำอธิบายประกอบด้วย @VintfStability
  • การประกาศ aidl_interface จำเป็นต้องมี stability: "vintf", .

มีเพียงเจ้าของอินเทอร์เฟซเท่านั้นที่สามารถทำการเปลี่ยนแปลงเหล่านี้ได้

เมื่อคุณทำการเปลี่ยนแปลงเหล่านี้ อินเทอร์เฟซจะต้องอยู่ใน รายการ VINTF เพื่อให้สามารถทำงานได้ ทดสอบสิ่งนี้ (และข้อกำหนดที่เกี่ยวข้อง เช่น การตรวจสอบว่าอินเทอร์เฟซที่เผยแพร่ถูกแช่แข็ง) โดยใช้การทดสอบ VTS vts_treble_vintf_vendor_test คุณสามารถใช้อินเทอร์เฟ @VintfStability ได้โดยไม่ต้องมีข้อกำหนดเหล่านี้โดยการเรียก AIBinder_forceDowngradeToLocalStability ในแบ็กเอนด์ NDK, android::Stability::forceDowngradeToLocalStability ในแบ็กเอนด์ C++ หรือ android.os.Binder#forceDowngradeToSystemStability ในแบ็กเอนด์ Java บนออบเจ็กต์ Binder ก่อนที่จะส่ง ไปสู่กระบวนการอื่น Java ไม่รองรับการลดระดับบริการให้เป็นความเสถียรของผู้ขาย เนื่องจากแอปทั้งหมดทำงานในบริบทของระบบ

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

โปรดทราบว่าการใช้ backends ในตัวอย่างโค้ดด้านล่างนั้นถูกต้อง เนื่องจากมีแบ็กเอนด์สามรายการ (Java, NDK และ CPP) โค้ดด้านล่างบอกวิธีเลือกแบ็กเอนด์ CPP โดยเฉพาะเพื่อปิดใช้งาน

    aidl_interface: {
        ...
        backends: {
            cpp: {
                enabled: false,
            },
        },
    }

ค้นหาอินเทอร์เฟซ AIDL HAL

อินเทอร์เฟซ AOSP Stable AIDL สำหรับ HAL อยู่ในไดเร็กทอรีฐานเดียวกันกับอินเทอร์เฟซ HIDL ในโฟลเดอร์ aidl

  • ฮาร์ดแวร์/อินเทอร์เฟซ
  • กรอบงาน/ฮาร์ดแวร์/อินเทอร์เฟซ
  • ระบบ/ฮาร์ดแวร์/อินเทอร์เฟซ

คุณควรใส่ส่วนขยายอินเทอร์เฟซลงในไดเร็กทอรีย่อย hardware/interfaces อื่นๆ ใน vendor หรือ hardware

อินเทอร์เฟซส่วนขยาย

Android มีชุดอินเทอร์เฟซ AOSP อย่างเป็นทางการในทุกรุ่น เมื่อพันธมิตร Android ต้องการเพิ่มฟังก์ชันการทำงานให้กับอินเทอร์เฟซเหล่านี้ พวกเขาไม่ควรเปลี่ยนแปลงโดยตรง เนื่องจากอาจหมายความว่ารันไทม์ Android ของพวกเขาเข้ากันไม่ได้กับรันไทม์ AOSP Android สำหรับอุปกรณ์ GMS การหลีกเลี่ยงการเปลี่ยนแปลงอินเทอร์เฟซเหล่านี้ยังเป็นสิ่งที่ทำให้อิมเมจ GSI สามารถทำงานได้ต่อไป

ส่วนขยายสามารถลงทะเบียนได้สองวิธี:

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

  • การเพิ่มอินเทอร์เฟซสามารถอัปสตรีมไปยัง AOSP ในรุ่นถัดไปได้
  • การเพิ่มอินเทอร์เฟซที่ให้ความยืดหยุ่นมากขึ้น โดยไม่มีข้อขัดแย้งในการผสาน สามารถอัปสตรีมได้ในรุ่นถัดไป

Parcelables ส่วนขยาย: ParcelableHolder

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

ก่อนหน้านี้หากไม่มี ParcelableHolder ผู้ใช้อุปกรณ์จะไม่สามารถแก้ไขอินเทอร์เฟซ AIDL ที่เสถียรที่กำหนดโดย AOSP ได้ เนื่องจากจะเป็นข้อผิดพลาดในการเพิ่มฟิลด์เพิ่มเติม:

parcelable AospDefinedParcelable {
  int a;
  String b;
  String x; // ERROR: added by a device implementer
  int[] y; // added by a device implementer
}

ดังที่เห็นในโค้ดก่อนหน้า แนวทางปฏิบัตินี้ใช้งานไม่ได้เนื่องจากฟิลด์ที่เพิ่มโดยตัวดำเนินการอุปกรณ์อาจมีข้อขัดแย้งเมื่อ Parcelable ได้รับการแก้ไขใน Android รุ่นถัดไป

การใช้ ParcelableHolder เจ้าของ Parcelable สามารถกำหนดจุดขยายใน Parcelable ได้

parcelable AospDefinedParcelable {
  int a;
  String b;
  ParcelableHolder extension;
}

จากนั้นผู้ใช้อุปกรณ์สามารถกำหนด Parcelable ของตนเองสำหรับส่วนขยายได้

parcelable OemDefinedParcelable {
  String x;
  int[] y;
}

ในที่สุด Parcelable ใหม่สามารถแนบกับ Parcelable ดั้งเดิมผ่านฟิลด์ ParcelableHolder


// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;

ap.extension.setParcelable(op);

...

OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);

// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();

ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);

...

std::shared_ptr<OemDefinedParcelable> op_ptr;

ap.extension.getParcelable(&op_ptr);

// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);

...

std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);

// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });

ap.extension.set_parcelable(Rc::clone(&op));

...

let op = ap.extension.get_parcelable::<OemDefinedParcelable>();

สร้างเทียบกับรันไทม์ AIDL

AIDL มีแบ็กเอนด์ที่แตกต่างกันสามแบบ: Java, NDK, CPP หากต้องการใช้ Stable AIDL คุณต้องใช้สำเนาระบบของ libbinder ที่ system/lib*/libbinder.so และพูดคุยบน /dev/binder เสมอ สำหรับโค้ดบนอิมเมจผู้ขาย หมายความว่าไม่สามารถใช้ libbinder (จาก VNDK) ได้: ไลบรารีนี้มี C++ API ที่ไม่เสถียรและภายในไม่เสถียร รหัสผู้ขายดั้งเดิมต้องใช้แบ็กเอนด์ NDK ของ AIDL เชื่อมโยงกับ libbinder_ndk (ซึ่งได้รับการสนับสนุนโดยระบบ libbinder.so ) และเชื่อมโยงกับไลบรารี -ndk_platform ที่สร้างโดยรายการ aidl_interface

ชื่ออินสแตนซ์ของเซิร์ฟเวอร์ AIDL HAL

ตามแบบแผน บริการ AIDL HAL จะมีชื่ออินสแตนซ์ในรูป $package.$type/$instance ตัวอย่างเช่น อินสแตนซ์ของเครื่องสั่น HAL ได้รับการลงทะเบียนเป็น android.hardware.vibrator.IVibrator/default

กำลังเขียนเซิร์ฟเวอร์ AIDL HAL

เซิร์ฟเวอร์ @VintfStability AIDL จะต้องได้รับการประกาศในรายการ VINTF เช่น:

    <hal format="aidl">
        <name>android.hardware.vibrator</name>
        <version>1</version>
        <fqname>IVibrator/default</fqname>
    </hal>

มิฉะนั้นควรลงทะเบียนบริการ AIDL ตามปกติ เมื่อทำการทดสอบ VTS คาดว่าจะมี AIDL HAL ที่ประกาศไว้ทั้งหมดพร้อมใช้งาน

การเขียนไคลเอ็นต์ AIDL

ไคลเอนต์ AIDL ต้องประกาศตัวเองในเมทริกซ์ความเข้ากันได้ ตัวอย่างเช่น:

    <hal format="aidl" optional="true">
        <name>android.hardware.vibrator</name>
        <version>1-2</version>
        <interface>
            <name>IVibrator</name>
            <instance>default</instance>
        </interface>
    </hal>

การแปลง HAL ที่มีอยู่จาก HIDL เป็น AIDL

ใช้เครื่องมือ hidl2aidl เพื่อแปลงอินเทอร์เฟซ HIDL เป็น AIDL

คุณสมบัติ hidl2aidl :

  • สร้างไฟล์ .aidl ตามไฟล์ .hal สำหรับแพ็คเกจที่กำหนด
  • สร้างกฎการสร้างสำหรับแพ็คเกจ AIDL ที่สร้างขึ้นใหม่โดยเปิดใช้งานแบ็กเอนด์ทั้งหมด
  • สร้างวิธีการแปลในแบ็กเอนด์ Java, CPP และ NDK สำหรับการแปลจากประเภท HIDL เป็นประเภท AIDL
  • สร้างกฎการสร้างสำหรับไลบรารีการแปลที่มีการขึ้นต่อกันที่จำเป็น
  • สร้างการยืนยันแบบคงที่เพื่อให้แน่ใจว่าตัวแจงนับ HIDL และ AIDL มีค่าเหมือนกันในแบ็กเอนด์ CPP และ NDK

ทำตามขั้นตอนเหล่านี้เพื่อแปลงแพ็คเกจของไฟล์ .hal เป็นไฟล์ .aidl:

  1. สร้างเครื่องมือที่อยู่ใน system/tools/hidl/hidl2aidl

    การสร้างเครื่องมือนี้จากแหล่งที่มาล่าสุดจะมอบประสบการณ์ที่สมบูรณ์ที่สุด คุณสามารถใช้เวอร์ชันล่าสุดเพื่อแปลงอินเทอร์เฟซในสาขาเก่าจากรีลีสก่อนหน้าได้

    m hidl2aidl
    
  2. รันเครื่องมือด้วยไดเร็กทอรีเอาต์พุตตามด้วยแพ็คเกจที่ต้องการแปลง

    ทางเลือก ให้ใช้อาร์กิวเมนต์ -l เพื่อเพิ่มเนื้อหาของไฟล์ลิขสิทธิ์ใหม่ที่ด้านบนของไฟล์ที่สร้างขึ้นทั้งหมด ตรวจสอบให้แน่ใจว่าใช้ใบอนุญาตและวันที่ที่ถูกต้อง

    hidl2aidl -o <output directory> -l <file with license> <package>
    

    ตัวอย่างเช่น:

    hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
    
  3. อ่านไฟล์ที่สร้างขึ้นและแก้ไขปัญหาใดๆ เกี่ยวกับการแปลง

    • conversion.log มีปัญหาที่ไม่สามารถจัดการได้ซึ่งต้องแก้ไขก่อน
    • ไฟล์ .aidl ที่สร้างขึ้นอาจมีคำเตือนและคำแนะนำที่อาจจำเป็นต้องดำเนินการ ความคิดเห็นเหล่านี้ขึ้นต้นด้วย //
    • ใช้โอกาสในการทำความสะอาดและปรับปรุงแพ็คเกจ
    • ตรวจสอบคำอธิบาย @JavaDerive เพื่อดูคุณสมบัติที่อาจจำเป็น เช่น toString หรือ equals
  4. สร้างเฉพาะเป้าหมายที่คุณต้องการ

    • ปิดใช้งานแบ็กเอนด์ที่จะไม่ใช้ ต้องการแบ็กเอนด์ NDK มากกว่าแบ็กเอนด์ CPP โปรดดู การเลือกรันไทม์
    • ลบไลบรารีการแปลหรือโค้ดที่สร้างขึ้นซึ่งจะไม่ถูกนำมาใช้
  5. ดู ความแตกต่างที่สำคัญของ AIDL/HIDL

    • โดยทั่วไปการใช้ Status และข้อยกเว้นในตัวของ AIDL จะปรับปรุงอินเทอร์เฟซและขจัดความจำเป็นในการใช้สถานะเฉพาะอินเทอร์เฟซอื่น
    • อาร์กิวเมนต์อินเทอร์เฟซ AIDL ในวิธีการไม่ใช่ @nullable ตามค่าเริ่มต้นเหมือนกับที่อยู่ใน HIDL

การแยกตัวสำหรับ AIDL HALs

ประเภทบริการ AIDL ซึ่งมองเห็นได้จากรหัสผู้ขายจะต้องมีแอตทริบิวต์ hal_service_type มิฉะนั้น การกำหนดค่า sepolicy จะเหมือนกับบริการ AIDL อื่นๆ (แม้ว่าจะมีคุณลักษณะพิเศษสำหรับ HAL ก็ตาม) นี่คือคำจำกัดความตัวอย่างของบริบทบริการ HAL:

    type hal_foo_service, service_manager_type, hal_service_type;

สำหรับบริการส่วนใหญ่ที่กำหนดโดยแพลตฟอร์ม บริบทบริการที่มีประเภทที่ถูกต้องจะถูกเพิ่มไว้แล้ว (เช่น android.hardware.foo.IFoo/default จะถูกทำเครื่องหมายเป็น hal_foo_service อยู่แล้ว) อย่างไรก็ตาม หากไคลเอนต์เฟรมเวิร์กรองรับชื่ออินสแตนซ์หลายชื่อ จะต้องเพิ่มชื่ออินสแตนซ์เพิ่มเติมในไฟล์ service_contexts เฉพาะอุปกรณ์

    android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0

ต้องเพิ่มแอตทริบิวต์ HAL เมื่อเราสร้าง HAL ประเภทใหม่ คุณลักษณะ HAL เฉพาะอาจเชื่อมโยงกับบริการหลายประเภท (แต่ละประเภทอาจมีหลายอินสแตนซ์ตามที่เราเพิ่งกล่าวถึง) สำหรับ HAL เรามี foo hal_attribute(foo) มาโครนี้กำหนดแอ็ตทริบิวต์ hal_foo_client และ hal_foo_server สำหรับโดเมนที่กำหนด มาโคร hal_client_domain และ hal_server_domain จะเชื่อมโยงโดเมนกับแอตทริบิวต์ HAL ที่กำหนด ตัวอย่างเช่น เซิร์ฟเวอร์ระบบที่เป็นไคลเอ็นต์ของ HAL นี้สอดคล้องกับนโยบาย hal_client_domain(system_server, hal_foo) เซิร์ฟเวอร์ HAL มี hal_server_domain(my_hal_domain, hal_foo) ในทำนองเดียวกัน โดยทั่วไป สำหรับแอตทริบิวต์ HAL ที่กำหนด เรายังสร้างโดเมน เช่น hal_foo_default สำหรับการอ้างอิงหรือ HAL ตัวอย่าง อย่างไรก็ตาม อุปกรณ์บางชนิดใช้โดเมนเหล่านี้สำหรับเซิร์ฟเวอร์ของตนเอง การแยกความแตกต่างระหว่างโดเมนสำหรับเซิร์ฟเวอร์หลายเครื่องจะมีความสำคัญก็ต่อเมื่อเรามีเซิร์ฟเวอร์หลายเครื่องที่ให้บริการอินเทอร์เฟซเดียวกันและต้องการชุดสิทธิ์อนุญาตที่แตกต่างกันในการใช้งาน ในมาโครเหล่านี้ทั้งหมด hal_foo ไม่ใช่วัตถุ sepolicy จริงๆ แต่มาโครเหล่านี้จะใช้โทเค็นนี้เพื่ออ้างอิงถึงกลุ่มของแอตทริบิวต์ที่เกี่ยวข้องกับคู่เซิร์ฟเวอร์ไคลเอ็นต์

อย่างไรก็ตาม จนถึงตอนนี้ เรายังไม่ได้เชื่อมโยง hal_foo_service และ hal_foo (คู่แอตทริบิวต์จาก hal_attribute(foo) ) คุณลักษณะ HAL เชื่อมโยงกับบริการ AIDL HAL โดยใช้มาโคร hal_attribute_service (HIDL HAL ใช้มาโคร hal_attribute_hwservice ) ตัวอย่างเช่น hal_attribute_service(hal_foo, hal_foo_service) ซึ่งหมายความว่ากระบวนการ hal_foo_client สามารถรับ HAL ได้ และกระบวนการ hal_foo_server สามารถลงทะเบียน HAL ได้ การบังคับใช้กฎการลงทะเบียนเหล่านี้กระทำโดย Context Manager ( servicemanager ) โปรดทราบว่าชื่อบริการอาจไม่สอดคล้องกับแอตทริบิวต์ HAL เสมอไป ตัวอย่างเช่น เราอาจเห็น hal_attribute_service(hal_foo, hal_foo2_service) โดยทั่วไปแล้ว เนื่องจากสิ่งนี้บอกเป็นนัยว่าบริการต่างๆ จะถูกใช้งานร่วมกันเสมอ เราจึงสามารถพิจารณาลบ hal_foo2_service และใช้ hal_foo_service สำหรับบริบทบริการทั้งหมดของเรา HAL ส่วนใหญ่ที่ตั้ง hal_attribute_service หลายรายการเป็นเพราะชื่อแอตทริบิวต์ HAL ดั้งเดิมไม่กว้างพอและไม่สามารถเปลี่ยนแปลงได้

เมื่อนำทั้งหมดนี้มารวมกัน ตัวอย่าง HAL จะมีลักษณะดังนี้:

    public/attributes:
    // define hal_foo, hal_foo_client, hal_foo_server
    hal_attribute(foo)

    public/service.te
    // define hal_foo_service
    type hal_foo_service, hal_service_type, protected_service, service_manager_type

    public/hal_foo.te:
    // allow binder connection from client to server
    binder_call(hal_foo_client, hal_foo_server)
    // allow client to find the service, allow server to register the service
    hal_attribute_service(hal_foo, hal_foo_service)
    // allow binder communication from server to service_manager
    binder_use(hal_foo_server)

    private/service_contexts:
    // bind an AIDL service name to the selinux type
    android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0

    private/<some_domain>.te:
    // let this domain use the hal service
    binder_use(some_domain)
    hal_client_domain(some_domain, hal_foo)

    vendor/<some_hal_server_domain>.te
    // let this domain serve the hal service
    hal_server_domain(some_hal_server_domain, hal_foo)

อินเทอร์เฟซส่วนขยายที่แนบมา

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

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

หากต้องการตั้งค่าส่วนขยายบน Binder ให้ใช้ API ต่อไปนี้:

  • ในแบ็กเอนด์ NDK: AIBinder_setExtension
  • ในแบ็กเอนด์ Java: android.os.Binder.setExtension
  • ในแบ็กเอนด์ CPP: android::Binder::setExtension
  • ในแบ็กเอนด์สนิม: binder::Binder::set_extension

หากต้องการรับส่วนขยายบน Binder ให้ใช้ API ต่อไปนี้:

  • ในแบ็กเอนด์ NDK: AIBinder_getExtension
  • ในแบ็กเอนด์ Java: android.os.IBinder.getExtension
  • ในแบ็กเอนด์ CPP: android::IBinder::getExtension
  • ในแบ็กเอนด์สนิม: binder::Binder::get_extension

คุณสามารถค้นหาข้อมูลเพิ่มเติมสำหรับ API เหล่านี้ได้ในเอกสารประกอบของฟังก์ชัน getExtension ในแบ็กเอนด์ที่เกี่ยวข้อง ตัวอย่างของวิธีใช้ส่วนขยายสามารถพบได้ใน hardware/interfaces/tests/extension/vibrator

ความแตกต่างที่สำคัญของ AIDL/HIDL

เมื่อใช้ AIDL HAL หรือใช้อินเทอร์เฟซ AIDL HAL โปรดระวังความแตกต่างเมื่อเปรียบเทียบกับการเขียน HIDL HAL

  • ไวยากรณ์ของภาษา AIDL นั้นใกล้เคียงกับ Java มากขึ้น ไวยากรณ์ HIDL คล้ายกับ C++
  • อินเทอร์เฟซ AIDL ทั้งหมดมีสถานะข้อผิดพลาดในตัว แทนที่จะสร้างประเภทสถานะที่กำหนดเอง ให้สร้าง ints สถานะคงที่ในไฟล์อินเทอร์เฟซ และใช้ EX_SERVICE_SPECIFIC ในแบ็กเอนด์ CPP/NDK และ ServiceSpecificException ในแบ็กเอนด์ Java ดู การจัดการข้อผิดพลาด
  • AIDL จะไม่เริ่มเธรดพูลโดยอัตโนมัติเมื่อมีการส่งออบเจ็กต์ Binder ต้องเริ่มต้นด้วยตนเอง (ดู การจัดการเธรด )
  • AIDL ไม่ยกเลิกข้อผิดพลาดในการขนส่งที่ไม่ได้ตรวจสอบ (HIDL Return จะยกเลิกเมื่อข้อผิดพลาดที่ไม่ได้ตรวจสอบ)
  • AIDL สามารถประกาศได้เพียงประเภทเดียวต่อไฟล์
  • อาร์กิวเมนต์ AIDL สามารถระบุเป็นเข้า/ออก/เข้า นอกเหนือจากพารามิเตอร์เอาต์พุต (ไม่มี "การเรียกกลับแบบซิงโครนัส")
  • AIDL ใช้ fd เป็นชนิดดั้งเดิมแทนตัวจัดการ
  • HIDL ใช้เวอร์ชันหลักสำหรับการเปลี่ยนแปลงที่เข้ากันไม่ได้ และเวอร์ชันรองสำหรับการเปลี่ยนแปลงที่เข้ากันได้ ใน AIDL จะทำการเปลี่ยนแปลงที่เข้ากันได้แบบย้อนหลัง AIDL ไม่มีแนวคิดที่ชัดเจนเกี่ยวกับเวอร์ชันหลัก แต่จะรวมอยู่ในชื่อแพ็คเกจแทน ตัวอย่างเช่น AIDL อาจใช้ชื่อแพ็คเกจ bluetooth2
  • AIDL จะไม่สืบทอดลำดับความสำคัญแบบเรียลไทม์ตามค่าเริ่มต้น ต้องใช้ฟังก์ชัน setInheritRt ต่อ-binder เพื่อเปิดใช้งานการสืบทอดลำดับความสำคัญแบบเรียลไทม์