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 ด้วย ข้อดีบางประการของ AIDL มีดังนี้

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

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

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

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

หากต้องการใช้อินเทอร์เฟซ 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 ก่อนที่จะส่งไปยังกระบวนการอื่น

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

โค้ดแสดงวิธีปิดใช้แบ็กเอนด์ CPP

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

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

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

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

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

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

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

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

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

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

ส่วนขยายที่ส่งผ่านได้: ParcelableHolder

ParcelableHolder คืออินสแตนซ์ของอินเทอร์เฟซ Parcelable ที่สามารถ มีอินสแตนซ์อื่นของ Parcelable

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

ใช้ParcelableHolderอินเทอร์เฟซเพื่อขยาย Parcelable ด้วยฟีเจอร์ที่เพิ่มมูลค่า อินเทอร์เฟซ ParcelableHolder มีอินสแตนซ์ของ Parcelable หากพยายามเพิ่มฟิลด์ลงใน Parcelable โดยตรง ระบบจะแสดงข้อผิดพลาด ดังนี้

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 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 (.aidl) ตามไฟล์ HAL (.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 ดูสร้างเทียบกับรันไทม์ AIDL
    • นำไลบรารีการแปลหรือโค้ดที่สร้างขึ้นซึ่งจะไม่ใช้ ออก
  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 ที่กำหนด ให้สร้างโดเมน เช่น hal_foo_default สำหรับ HAL อ้างอิงหรือตัวอย่าง อย่างไรก็ตาม อุปกรณ์บางเครื่องใช้ โดเมนเหล่านี้สำหรับเซิร์ฟเวอร์ของตัวเอง การแยกความแตกต่างระหว่างโดเมนสำหรับเซิร์ฟเวอร์หลายเครื่องจะมีความสำคัญก็ต่อเมื่อมีเซิร์ฟเวอร์หลายเครื่องที่ให้บริการอินเทอร์เฟซเดียวกันและต้องมีกลุ่มสิทธิ์ที่แตกต่างกันในการติดตั้งใช้งาน ในมาโครทั้งหมดนี้ hal_foo ไม่ใช่ออบเจ็กต์ sepolicy แต่มาโครเหล่านี้จะใช้โทเค็นนี้เพื่ออ้างอิงกลุ่มแอตทริบิวต์ที่เชื่อมโยงกับคู่เซิร์ฟเวอร์ไคลเอ็นต์แทน

อย่างไรก็ตาม ขณะนี้ hal_foo_service และ hal_foo (คู่แอตทริบิวต์จาก hal_attribute(foo)) ยังไม่ได้เชื่อมโยงกัน แอตทริบิวต์ HAL จะเชื่อมโยง กับบริการ HAL ของ AIDL โดยใช้มาโคร hal_attribute_service (HAL ของ HIDL ใช้มาโคร 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 ที่มีอยู่ เมื่อต้องการความสามารถใหม่โดยสิ้นเชิง คุณไม่จำเป็นต้องใช้กลไกนี้ และสามารถลงทะเบียนอินเทอร์เฟซส่วนขยาย กับ Service Manager ได้โดยตรง อินเทอร์เฟซส่วนขยายที่แนบมา จะสมเหตุสมผลที่สุดเมื่อแนบกับอินเทอร์เฟซย่อย เนื่องจากลำดับชั้นเหล่านี้ อาจมีหลายระดับหรือหลายอินสแตนซ์ การใช้ส่วนขยายส่วนกลางเพื่อจำลอง ลำดับชั้นของอินเทอร์เฟซของ Binder ของบริการอื่นต้องมีการ ทำบัญชีอย่างละเอียดเพื่อให้ความสามารถเทียบเท่ากับส่วนขยายที่แนบมาโดยตรง

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

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

หากต้องการขยายเวลาในโฟลเดอร์ ให้ใช้ 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 ทั้งหมดมีสถานะข้อผิดพลาดในตัว แทนที่จะสร้างประเภทสถานะที่กำหนดเอง ให้สร้างจำนวนเต็มสถานะคงที่ในไฟล์อินเทอร์เฟซ แล้วใช้ EX_SERVICE_SPECIFIC ในแบ็กเอนด์ CPP และ NDK และใช้ ServiceSpecificException ในแบ็กเอนด์ Java ดูการจัดการข้อผิดพลาด
  • AIDL จะไม่เริ่มพูลเธรดโดยอัตโนมัติเมื่อมีการส่งออบเจ็กต์ Binder คุณต้องเริ่มการดำเนินการด้วยตนเอง (ดูการจัดการเธรด)
  • AIDL จะไม่หยุดทำงานเมื่อเกิดข้อผิดพลาดในการรับส่งที่ไม่ได้ตรวจสอบ (HIDL Return จะหยุดทำงานเมื่อเกิดข้อผิดพลาดที่ไม่ได้ตรวจสอบ)
  • AIDL ประกาศได้เพียงประเภทเดียวต่อไฟล์
  • คุณระบุอาร์กิวเมนต์ AIDL เป็น in, out หรือ inout ได้นอกเหนือจาก พารามิเตอร์เอาต์พุต (ไม่มีการเรียกกลับแบบซิงโครนัส)
  • AIDL ใช้ fd เป็นประเภทดั้งเดิมแทน handle
  • HIDL ใช้เวอร์ชันหลักสำหรับการเปลี่ยนแปลงที่ใช้ร่วมกันไม่ได้ และเวอร์ชันย่อยสำหรับการเปลี่ยนแปลงที่ใช้ร่วมกันได้ ใน AIDL การเปลี่ยนแปลงที่เข้ากันได้แบบย้อนหลังจะดำเนินการในที่เดิม AIDL ไม่มีแนวคิดที่ชัดเจนเกี่ยวกับเวอร์ชันหลัก แต่จะรวมไว้ในชื่อแพ็กเกจแทน เช่น AIDL อาจใช้ชื่อแพ็กเกจ bluetooth2
  • โดยค่าเริ่มต้น AIDL จะไม่รับช่วงลำดับความสำคัญแบบเรียลไทม์ ต้องใช้ฟังก์ชัน setInheritRt ต่อ Binder เพื่อเปิดใช้การรับค่าลำดับความสำคัญแบบเรียลไทม์

การทดสอบสำหรับ HAL

ส่วนนี้อธิบายแนวทางปฏิบัติแนะนำสำหรับการทดสอบ HAL แนวทางปฏิบัติเหล่านี้ใช้ได้แม้ว่าการทดสอบการผสานรวมสำหรับ HAL จะไม่ได้อยู่ใน VTS ก็ตาม

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

VTS สำหรับ HAL มี 2 ส่วนหลักๆ

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

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

คุณดูชุดการทดสอบนี้ได้ใน test/vts-testcase/hal/treble/vintf หากคุณกำลังทำงานกับการติดตั้งใช้งาน HAL ของผู้ให้บริการ ให้ใช้ vts_treble_vintf_vendor_test เพื่อยืนยัน คุณเรียกใช้การทดสอบนี้ได้ด้วยคำสั่ง atest vts_treble_vintf_vendor_test

การทดสอบเหล่านี้มีหน้าที่ตรวจสอบสิ่งต่อไปนี้

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

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

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

ใน C++ คุณสามารถดูรายการ HAL ทั้งหมดที่ติดตั้งในระบบได้ด้วยฟังก์ชัน android::getAidlHalInstanceNames ใน libaidlvintf_gtest_helper ใน Rust ให้ใช้ binder::get_declared_instances

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

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

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

คาดว่าการทดสอบ VTS (หรือการทดสอบอื่นๆ) จะได้รับการอัปเดตอยู่เสมอเมื่อสร้างหรือ แก้ไขอินเทอร์เฟซ HAL ของ Android

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

ทดสอบบน Cuttlefish

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

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