AIDL สำหรับ HAL

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

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

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

หากต้องการให้ระบบและผู้ให้บริการใช้อินเทอร์เฟซ AIDL ได้ อินเทอร์เฟซดังกล่าวต้องเปลี่ยนแปลง 2 อย่าง ดังนี้

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

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

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

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

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

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

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

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

  • hardware/interfaces: สำหรับอินเทอร์เฟซที่ฮาร์ดแวร์มักให้ไว้
  • frameworks/hardware/interfaces: สำหรับอินเทอร์เฟซระดับสูงที่ให้บริการแก่ฮาร์ดแวร์
  • system/hardware/interfaces: สำหรับอินเทอร์เฟซระดับต่ำที่ระบุให้กับฮาร์ดแวร์

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

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

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

ส่วนขยายจะลงทะเบียนได้ 2 วิธีดังนี้

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

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

รายการที่แยกส่วนได้ของส่วนขยาย: ParcelableHolder

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

ก่อนหน้านี้ หากไม่มี 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 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 HAL

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

เขียนเซิร์ฟเวอร์ AIDL HAL

@VintfStability เซิร์ฟเวอร์ AIDL ต้องได้รับการประกาศในไฟล์ Manifest 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
  • สร้างกฎการสร้างสำหรับไลบรารีการแปลที่มีทรัพยากร Dependency ที่จำเป็น
  • สร้างการยืนยันแบบคงที่เพื่อให้แน่ใจว่าเครื่องมือแจกแจง 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

SEPolicy สำหรับ AIDL HAL

ประเภทบริการ 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_foo_default สำหรับการอ้างอิงหรือ HAL ตัวอย่างสำหรับแอตทริบิวต์ 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 ได้ เครื่องมือจัดการบริบท (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 ใดก็ได้ ไม่ว่าจะเป็นอินเทอร์เฟซระดับบนสุดที่ลงทะเบียนกับ Service Manager โดยตรงหรืออินเทอร์เฟซย่อย เมื่อได้รับส่วนขยาย คุณต้องยืนยันว่าประเภทของส่วนขยายเป็นไปตามที่คาดไว้ คุณจะตั้งค่าส่วนขยายได้จากกระบวนการที่ให้บริการ Binder เท่านั้น

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

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

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

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

  • ในแบ็กเอนด์ NDK: AIBinder_getExtension
  • ในแบ็กเอนด์ Java: android.os.IBinder.getExtension
  • ในแบ็กเอนด์ CPP: android::IBinder::getExtension
  • ในแบ็กเอนด์ Rust: 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 จะไม่เริ่มต้น Thread Pool โดยอัตโนมัติเมื่อส่งออบเจ็กต์ Binder โดยจะต้องเริ่มต้นด้วยตนเอง (ดูการจัดการชุดข้อความ)
  • AIDL จะไม่หยุดทำงานเมื่อมีข้อผิดพลาดในการขนส่งที่ไม่ได้ตรวจสอบ (HIDL Return จะหยุดทำงานเมื่อมีข้อผิดพลาดที่ไม่ได้ตรวจสอบ)
  • AIDL ประกาศได้เพียง 1 ประเภทต่อไฟล์
  • อาร์กิวเมนต์ AIDL สามารถระบุเป็น in/out/inout นอกเหนือจากพารามิเตอร์เอาต์พุต (ไม่มี "การเรียกกลับแบบซิงค์")
  • AIDL ใช้ fd เป็นประเภทพื้นฐานแทนตัวแฮนเดิล
  • HIDL ใช้เวอร์ชันหลักสำหรับการเปลี่ยนแปลงที่เข้ากันไม่ได้ และเวอร์ชันย่อยสำหรับการเปลี่ยนแปลงที่เข้ากันได้ ใน AIDL จะมีการเปลี่ยนแปลงที่เข้ากันได้แบบย้อนหลัง AIDL ไม่มีแนวคิดที่ชัดเจนเกี่ยวกับเวอร์ชันหลัก แต่รวมไว้ในชื่อแพ็กเกจแทน ตัวอย่างเช่น AIDL อาจใช้ชื่อแพ็กเกจ bluetooth2
  • AIDL จะไม่รับค่าลำดับความสำคัญแบบเรียลไทม์โดยค่าเริ่มต้น ต้องใช้ฟังก์ชัน setInheritRt ต่อ Binder เพื่อเปิดใช้การสืบทอดลําดับความสําคัญแบบเรียลไทม์