AIDL สำหรับ HALs

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

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

แรงจูงใจ

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

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

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

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

  • คำจำกัดความทุกประเภทต้องมีคำอธิบายประกอบด้วย @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 บนวัตถุตัวประสานก่อนที่จะส่ง สู่อีกกระบวนการหนึ่ง Java ไม่รองรับการดาวน์เกรดบริการเพื่อความเสถียรของผู้ขาย เนื่องจากแอปทั้งหมดทำงานในบริบทของระบบ

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

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

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

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

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

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

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

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

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

  • ในแบ็กเอนด์ NDK: AIBinder_getExtension
  • ในแบ็กเอนด์ Java: android.os.IBinder.getExtension
  • ในแบ็กเอนด์ CPP: android::IBinder::getExtension

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

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

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

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

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

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

แรงจูงใจ

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

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

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

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

  • คำจำกัดความทุกประเภทต้องมีคำอธิบายประกอบด้วย @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 บนวัตถุตัวประสานก่อนที่จะส่ง สู่อีกกระบวนการหนึ่ง Java ไม่รองรับการดาวน์เกรดบริการเพื่อความเสถียรของผู้ขาย เนื่องจากแอปทั้งหมดทำงานในบริบทของระบบ

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

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

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

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

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

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

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

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

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

  • ในแบ็กเอนด์ NDK: AIBinder_getExtension
  • ในแบ็กเอนด์ Java: android.os.IBinder.getExtension
  • ในแบ็กเอนด์ CPP: android::IBinder::getExtension

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

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

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

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