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

อินเทอร์เฟซ AIDL ของ AOSP เวอร์ชันเสถียรสำหรับ 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 คาดว่า HAL ของ AIDL ที่ประกาศไว้ทั้งหมดจะพร้อมใช้งาน

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

การทดสอบชุดทดสอบของผู้ให้บริการ (VTS) สําหรับ HAL

Android อาศัยชุดทดสอบของผู้ให้บริการ (VTS) เพื่อยืนยันการติดตั้งใช้งาน HAL ที่คาดไว้ VTS ช่วยให้มั่นใจได้ว่า Android จะใช้งานร่วมกับการใช้งานเดิมของผู้ให้บริการได้ การติดตั้งใช้งานที่ไม่ผ่าน VTS มีปัญหาความเข้ากันได้ที่ทราบกันดี ซึ่งอาจทำให้ใช้งานกับระบบปฏิบัติการเวอร์ชันในอนาคตไม่ได้

VTS สำหรับ HAL ประกอบด้วย 2 ส่วนหลักๆ ดังนี้

1. ยืนยันว่า Android รู้จักและคาดหวัง HAL ในอุปกรณ์

ชุดการทดสอบนี้จะอยู่ใน test/vts-testcase/hal/treble/vintf โดยมีหน้าที่รับผิดชอบในการยืนยันข้อมูลต่อไปนี้

  • อินเทอร์เฟซ @VintfStability ทั้งหมดที่ประกาศในไฟล์ Manifest ของ VINTF จะหยุดอยู่ที่เวอร์ชันที่เผยแพร่แล้วซึ่งทราบ วิธีนี้ช่วยให้มั่นใจว่าทั้ง 2 ฝ่ายของอินเทอร์เฟซจะยอมรับคำจำกัดความที่แน่นอนของอินเทอร์เฟซเวอร์ชันนั้น ซึ่งจำเป็นต่อการดำเนินการขั้นพื้นฐาน
  • HAL ทั้งหมดที่ประกาศในไฟล์ Manifest ของ VINTF จะพร้อมใช้งานในอุปกรณ์นั้น ไคลเอ็นต์ที่มีสิทธิ์เพียงพอในการใช้บริการ HAL ที่ประกาศไว้ต้องสามารถรับและใช้บริการเหล่านั้นได้ทุกเมื่อ
  • HAL ทั้งหมดที่ประกาศในไฟล์ Manifest ของ VINTF จะแสดงอินเทอร์เฟซเวอร์ชันที่ประกาศไว้ในไฟล์ Manifest
  • ไม่มี HAL ที่เลิกใช้งานแสดงในอุปกรณ์ Android จะหยุดรองรับอินเทอร์เฟซ HAL เวอร์ชันที่ต่ำกว่าตามที่อธิบายไว้ในวงจรชีวิตของ FCM
  • มี HAL ที่จำเป็นในอุปกรณ์ HAL บางรายการจําเป็นต่อการทํางานของ Android

2. ยืนยันลักษณะการทำงานที่คาดไว้ของ HAL แต่ละรายการ

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

การทดสอบเหล่านี้พยายามครอบคลุมทุกแง่มุมของการใช้งาน HAL ที่เฟรมเวิร์ก Android ต้องใช้หรืออาจต้องใช้ในอนาคต

การทดสอบเหล่านี้รวมถึงการยืนยันการรองรับฟีเจอร์ การจัดการข้อผิดพลาด และลักษณะการทำงานอื่นๆ ที่ลูกค้าอาจคาดหวังจากบริการ

เหตุการณ์สำคัญ VTS สําหรับการพัฒนา HAL

คุณควรอัปเดตการทดสอบ VTS เมื่อสร้างหรือแก้ไขอินเทอร์เฟซ HAL ของ Android

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

VTS ใน Cuttlefish

เมื่อไม่มีฮาร์ดแวร์ Android จะใช้ Cuttlefish เป็นแพลตฟอร์มการพัฒนาสำหรับอินเทอร์เฟซ HAL ซึ่งช่วยให้ VTS และทดสอบการผสานรวมของ Android ปรับขนาดได้ hal_implementation_test จะทดสอบว่า Cuttlefish มีการใช้งานอินเทอร์เฟซ HAL เวอร์ชันล่าสุดเพื่อให้แน่ใจว่า Android พร้อมจัดการอินเทอร์เฟซใหม่ และ VTS พร้อมทดสอบการใช้งานของผู้ให้บริการรายใหม่ทันทีที่มีฮาร์ดแวร์และอุปกรณ์ใหม่