การกำหนดเวอร์ชัน

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

โครงสร้างรหัส HIDL

รหัส HIDL ถูกจัดระเบียบ ตามประเภท อินเทอร์เฟซ และแพ็คเกจที่ผู้ใช้กำหนด:

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

ไฟล์คำจำกัดความประเภทข้อมูล types.hal มีเฉพาะ UDT เท่านั้น (UDT ระดับแพ็คเกจทั้งหมดจะถูกเก็บไว้ในไฟล์เดียว) การแสดงแทนในภาษาเป้าหมายมีให้สำหรับอินเทอร์เฟซทั้งหมดในแพ็คเกจ

ปรัชญาการกำหนดเวอร์ชัน

แพ็คเกจ HIDL (เช่น android.hardware.nfc ) หลังจากที่เผยแพร่สำหรับเวอร์ชันที่กำหนด (เช่น 1.0 ) แล้วจะไม่เปลี่ยนรูป มันไม่สามารถเปลี่ยนแปลงได้ การปรับเปลี่ยนอินเทอร์เฟซในแพ็คเกจหรือการเปลี่ยนแปลง UDT สามารถเกิดขึ้นได้ในแพ็คเกจ อื่น เท่านั้น

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

ตามแนวคิดแล้ว แพ็คเกจสามารถเกี่ยวข้องกับแพ็คเกจอื่นได้หลายวิธี:

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

อินเทอร์เฟซการจัดโครงสร้าง

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

Treble รองรับผู้จำหน่ายและส่วนประกอบของระบบที่คอมไพล์แยกกัน โดยที่ vendor.img บนอุปกรณ์และ system.img สามารถคอมไพล์แยกกันได้ การโต้ตอบทั้งหมดระหว่าง vendor.img และ system.img จะต้องได้รับการกำหนดไว้อย่างชัดเจนและละเอียดถี่ถ้วน เพื่อให้สามารถทำงานได้ต่อไปเป็นเวลาหลายปี ซึ่งรวมถึงพื้นผิว API จำนวนมาก แต่พื้นผิวหลักคือกลไก IPC ที่ HIDL ใช้สำหรับการสื่อสารระหว่างกระบวนการบนขอบเขต system.img / vendor.img

ความต้องการ

ข้อมูลทั้งหมดที่ส่งผ่าน HIDL จะต้องถูกกำหนดไว้อย่างชัดเจน เพื่อให้มั่นใจว่าการใช้งานและลูกค้าสามารถทำงานร่วมกันต่อไปได้แม้ว่าจะรวบรวมแยกกันหรือพัฒนาอย่างอิสระ ข้อมูลจะต้องเป็นไปตามข้อกำหนดต่อไปนี้:

  • สามารถอธิบายใน HIDL ได้โดยตรง (โดยใช้ structs enums ฯลฯ) พร้อมชื่อและความหมายเชิงความหมาย
  • สามารถอธิบายได้ด้วยมาตรฐานสาธารณะ เช่น ISO/IEC 7816
  • สามารถอธิบายได้ด้วยมาตรฐานฮาร์ดแวร์หรือเค้าโครงทางกายภาพของฮาร์ดแวร์
  • อาจเป็นข้อมูลที่ทึบแสง (เช่น คีย์สาธารณะ รหัส ฯลฯ) หากจำเป็น

หากใช้ข้อมูลทึบแสง จะต้องอ่านเพียงด้านเดียวของอินเทอร์เฟซ HIDL ตัวอย่างเช่น หากโค้ด vendor.img ให้ข้อความสตริงหรือข้อมูล vec<uint8_t> บน system.img ข้อมูลดังกล่าวจะไม่สามารถแยกวิเคราะห์โดย system.img เองได้ สามารถส่งผ่านกลับไปยัง vendor.img เพื่อตีความได้เท่านั้น เมื่อส่งค่าจาก vendor.img ไปยังโค้ดของผู้ขายบน system.img หรือไปยังอุปกรณ์อื่น รูปแบบของข้อมูลและวิธีการตีความจะต้องมีคำอธิบายที่แน่ชัดและยังคงเป็นส่วนหนึ่งของอินเทอร์เฟซ

แนวทาง

คุณควรจะสามารถเขียนการใช้งานหรือไคลเอนต์ของ HAL ได้โดยใช้เฉพาะไฟล์ .hal (กล่าวคือ คุณไม่จำเป็นต้องดูแหล่งที่มาของ Android หรือมาตรฐานสาธารณะ) เราขอแนะนำให้ระบุลักษณะการทำงานที่จำเป็นอย่างแน่นอน ข้อความเช่น "การใช้งานอาจทำ A หรือ B" สนับสนุนให้การใช้งานมีความเกี่ยวพันกับลูกค้าที่พวกเขาได้รับการพัฒนาด้วย

เค้าโครงโค้ด HIDL

HIDL มีแพ็คเกจหลักและผู้ขาย

อินเทอร์เฟซ Core HIDL เป็นอินเทอร์เฟซที่ Google ระบุ แพ็คเกจที่เป็นของเริ่มต้นด้วย android.hardware. และตั้งชื่อตามระบบย่อย อาจมีระดับการตั้งชื่อที่ซ้อนกัน ตัวอย่างเช่น แพ็คเกจ NFC ชื่อ android.hardware.nfc และแพ็คเกจกล้องคือ android.hardware.camera โดยทั่วไป แพ็คเกจหลักจะมีชื่อ android.hardware. [ name1 ].[ name2 ]…. แพ็คเกจ HIDL มีเวอร์ชันนอกเหนือจากชื่อ ตัวอย่างเช่น แพ็คเกจ android.hardware.camera อาจเป็นเวอร์ชัน 3.4 ; นี่เป็นสิ่งสำคัญ เนื่องจากเวอร์ชันของแพ็คเกจส่งผลต่อตำแหน่งในแผนผังต้นทาง

แพ็คเกจหลักทั้งหมดอยู่ภายใต้ hardware/interfaces/ ในระบบบิลด์ แพ็คเกจ android.hardware. [ name1 ].[ name2 ]… ที่เวอร์ชัน $m.$n อยู่ภายใต้ hardware/interfaces/name1/name2//$m.$n/ ; แพ็คเกจ android.hardware.camera เวอร์ชัน 3.4 อยู่ในไดเร็กทอรี hardware/interfaces/camera/3.4/. มีการแมปแบบฮาร์ดโค้ดอยู่ระหว่างคำนำหน้าแพ็คเกจ android.hardware. และพาธ hardware/interfaces/

แพ็คเกจที่ไม่ใช่คอร์ (ผู้จำหน่าย) คือแพ็คเกจที่ผลิตโดยผู้จำหน่าย SoC หรือ ODM คำนำหน้าสำหรับแพ็คเกจที่ไม่ใช่คอร์คือ vendor.$(VENDOR).hardware. โดยที่ $(VENDOR) หมายถึงผู้จำหน่าย SoC หรือ OEM/ODM ซึ่งจะแม็พไปยังพาธ vendor/$(VENDOR)/interfaces ในแผนผัง (การแม็ปนี้ยังฮาร์ดโค้ดด้วย)

ชื่อประเภทที่ผู้ใช้กำหนดที่มีคุณสมบัติครบถ้วน

ใน HIDL ทุก UDT มีชื่อที่มีคุณสมบัติครบถ้วนซึ่งประกอบด้วยชื่อ UDT ชื่อแพ็คเกจที่กำหนด UDT และเวอร์ชันแพ็คเกจ ชื่อที่มีคุณสมบัติครบถ้วนจะใช้เมื่อมีการประกาศอินสแตนซ์ประเภทนั้นเท่านั้น และไม่ใช่ตำแหน่งที่มีการกำหนดประเภทเอง ตัวอย่างเช่น สมมติแพ็คเกจ android.hardware.nfc, เวอร์ชัน 1.0 กำหนดโครงสร้างชื่อ NfcData ที่ไซต์ของการประกาศ (ไม่ว่าจะเป็นใน types.hal หรือภายในการประกาศของอินเทอร์เฟซ) การประกาศจะระบุเพียง:

struct NfcData {
    vec<uint8_t> data;
};

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

android.hardware.nfc@1.0::NfcData

ไวยากรณ์ทั่วไปคือ PACKAGE @ VERSION :: UDT โดยที่:

  • PACKAGE คือชื่อที่คั่นด้วยจุดของแพ็คเกจ HIDL (เช่น android.hardware.nfc )
  • VERSION เป็นรูปแบบ major.minor-version ที่คั่นด้วยจุดของแพ็กเกจ (เช่น 1.0 )
  • UDT เป็นชื่อที่คั่นด้วยจุดของ HIDL UDT เนื่องจาก HIDL รองรับ UDT ที่ซ้อนกัน และอินเทอร์เฟซ HIDL สามารถมี UDT ได้ (ประเภทของการประกาศแบบซ้อน) จุดจึงถูกใช้เพื่อเข้าถึงชื่อ

ตัวอย่างเช่น หากมีการกำหนดการประกาศแบบซ้อนต่อไปนี้ในไฟล์ประเภททั่วไปในแพ็คเกจ android.hardware.example เวอร์ชัน 1.0 :

// types.hal
package android.hardware.example@1.0;
struct Foo {
    struct Bar {
        // …
    };
    Bar cheers;
};

ชื่อที่มีคุณสมบัติครบถ้วนสำหรับ Bar คือ android.hardware.example@1.0::Foo.Bar หากนอกเหนือจากการอยู่ในแพ็คเกจข้างต้นแล้ว การประกาศแบบซ้อนยังอยู่ในอินเทอร์เฟซที่เรียกว่า IQuux :

// IQuux.hal
package android.hardware.example@1.0;
interface IQuux {
    struct Foo {
        struct Bar {
            // …
        };
        Bar cheers;
    };
    doSomething(Foo f) generates (Foo.Bar fb);
};

ชื่อที่มีคุณสมบัติครบถ้วนสำหรับ Bar คือ android.hardware.example@1.0::IQuux.Foo.Bar

ในทั้งสองกรณี Bar สามารถเรียกได้ว่าเป็น Bar ภายในขอบเขตของการประกาศของ Foo เท่านั้น ที่ระดับแพ็คเกจหรืออินเทอร์เฟซ คุณต้องอ้างถึง Bar ผ่าน Foo : Foo.Bar เช่นเดียวกับในการประกาศวิธีการ doSomething ข้างต้น หรือคุณสามารถประกาศวิธีการอย่างละเอียดมากขึ้นได้ดังนี้:

// IQuux.hal
doSomething(android.hardware.example@1.0::IQuux.Foo f) generates (android.hardware.example@1.0::IQuux.Foo.Bar fb);

ค่าแจงนับที่มีคุณสมบัติครบถ้วน

หาก UDT เป็นประเภท enum ดังนั้นแต่ละค่าของประเภท enum จะมีชื่อที่มีคุณสมบัติครบถ้วนซึ่งขึ้นต้นด้วยชื่อที่มีคุณสมบัติครบถ้วนของประเภท enum ตามด้วยเครื่องหมายทวิภาค จากนั้นตามด้วยชื่อของค่า enum ตัวอย่างเช่น สมมติแพ็คเกจ android.hardware.nfc, เวอร์ชัน 1.0 กำหนดประเภท enum NfcStatus :

enum NfcStatus {
    STATUS_OK,
    STATUS_FAILED
};

เมื่ออ้างถึง STATUS_OK ชื่อแบบเต็มคือ:

android.hardware.nfc@1.0::NfcStatus:STATUS_OK

ไวยากรณ์ทั่วไปคือ PACKAGE @ VERSION :: UDT : VALUE โดยที่:

  • PACKAGE @ VERSION :: UDT เป็นชื่อที่มีคุณสมบัติครบถ้วนเหมือนกันทุกประการสำหรับประเภท enum
  • VALUE คือชื่อของค่า

กฎการอนุมานอัตโนมัติ

ไม่จำเป็นต้องระบุชื่อ UDT ที่มีคุณสมบัติครบถ้วน ชื่อ UDT สามารถละเว้นสิ่งต่อไปนี้ได้อย่างปลอดภัย:

  • แพ็คเกจ เช่น @1.0::IFoo.Type
  • ทั้งแพ็คเกจและเวอร์ชัน เช่น IFoo.Type

HIDL พยายามกรอกชื่อให้สมบูรณ์โดยใช้กฎการรบกวนอัตโนมัติ (หมายเลขกฎที่ต่ำกว่าหมายถึงลำดับความสำคัญที่สูงกว่า)

กฎข้อที่ 1

หากไม่มีการระบุแพ็คเกจและเวอร์ชัน ระบบจะพยายามค้นหาชื่อในเครื่อง ตัวอย่าง:

interface Nfc {
    typedef string NfcErrorMessage;
    send(NfcData d) generates (@1.0::NfcStatus s, NfcErrorMessage m);
};

NfcErrorMessage ถูกค้นหาในเครื่อง และพบ typedef ด้านบน NfcData จะถูกค้นหาในเครื่องด้วย แต่เนื่องจากไม่ได้กำหนดไว้ในเครื่อง จึงมีการใช้กฎ 2 และ 3 @1.0::NfcStatus มีเวอร์ชัน ดังนั้นกฎข้อ 1 จึงใช้ไม่ได้

กฎข้อที่ 2

หากกฎข้อ 1 ล้มเหลวและส่วนประกอบของชื่อที่มีคุณสมบัติครบถ้วนหายไป (แพ็คเกจ เวอร์ชัน หรือแพ็คเกจและเวอร์ชัน) ส่วนประกอบจะถูกกรอกอัตโนมัติด้วยข้อมูลจากแพ็คเกจปัจจุบัน จากนั้นคอมไพเลอร์ HIDL จะค้นหาไฟล์ปัจจุบัน (และการนำเข้าทั้งหมด) เพื่อค้นหาชื่อที่มีคุณสมบัติครบถ้วนที่ป้อนอัตโนมัติ จากตัวอย่างด้านบน สมมติว่าการประกาศ ExtendedNfcData ถูกสร้างขึ้นในแพ็คเกจเดียวกัน ( android.hardware.nfc ) ในเวอร์ชันเดียวกัน ( 1.0 ) เป็น NfcData ดังต่อไปนี้:

struct ExtendedNfcData {
    NfcData base;
    // … additional members
};

คอมไพเลอร์ HIDL กรอกชื่อแพ็กเกจและชื่อเวอร์ชันจากแพ็กเกจปัจจุบันเพื่อสร้างชื่อ UDT ที่มีคุณสมบัติครบถ้วน android.hardware.nfc@1.0::NfcData เนื่องจากชื่อนี้มีอยู่ในแพ็คเกจปัจจุบัน (สมมติว่ามีการนำเข้าอย่างถูกต้อง) จึงใช้สำหรับการประกาศ

ชื่อในแพ็คเกจปัจจุบันจะถูกนำเข้าเฉพาะในกรณีที่ข้อใดข้อหนึ่งต่อไปนี้เป็นจริง:

  • มันถูกนำเข้าอย่างชัดเจนด้วยคำสั่ง import
  • มันถูกกำหนดไว้ใน types.hal ในแพ็คเกจปัจจุบัน

จะปฏิบัติตามกระบวนการเดียวกันนี้หาก NfcData ได้รับการรับรองด้วยหมายเลขเวอร์ชันเท่านั้น:

struct ExtendedNfcData {
    // autofill the current package name (android.hardware.nfc)
    @1.0::NfcData base;
    // … additional members
};

กฎข้อที่ 3

หากกฎข้อ 2 ไม่สามารถสร้างการจับคู่ได้ (ไม่ได้กำหนด UDT ในแพ็คเกจปัจจุบัน) คอมไพเลอร์ HIDL จะสแกนหาการจับคู่ภายในแพ็คเกจที่นำเข้าทั้งหมด จากตัวอย่างข้างต้น สมมติว่า ExtendedNfcData ได้รับการประกาศในเวอร์ชัน 1.1 ของแพ็คเกจ android.hardware.nfc โดยที่ 1.1 นำเข้า 1.0 ตามที่ควร (ดูที่ Package-Level Extensions ) และคำจำกัดความระบุเฉพาะชื่อ UDT:

struct ExtendedNfcData {
    NfcData base;
    // … additional members
};

คอมไพเลอร์ค้นหา UDT ใดๆ ที่ชื่อ NfcData และพบหนึ่งใน android.hardware.nfc ในเวอร์ชัน 1.0 ส่งผลให้ได้ UDT ที่มีคุณสมบัติครบถ้วนของ android.hardware.nfc@1.0::NfcData หากพบมากกว่าหนึ่งรายการที่ตรงกันสำหรับ UDT ที่ผ่านการรับรองบางส่วนที่กำหนด คอมไพลเลอร์ HIDL จะส่งข้อผิดพลาด

ตัวอย่าง

การใช้กฎข้อที่ 2 ประเภทที่นำเข้าที่กำหนดไว้ในแพ็คเกจปัจจุบันจะได้รับความนิยมมากกว่าประเภทที่นำเข้าจากแพ็คเกจอื่น:

// hardware/interfaces/foo/1.0/types.hal
package android.hardware.foo@1.0;
struct S {};

// hardware/interfaces/foo/1.0/IFooCallback.hal
package android.hardware.foo@1.0;
interface IFooCallback {};

// hardware/interfaces/bar/1.0/types.hal
package android.hardware.bar@1.0;
typedef string S;

// hardware/interfaces/bar/1.0/IFooCallback.hal
package android.hardware.bar@1.0;
interface IFooCallback {};

// hardware/interfaces/bar/1.0/IBar.hal
package android.hardware.bar@1.0;
import android.hardware.foo@1.0;
interface IBar {
    baz1(S s); // android.hardware.bar@1.0::S
    baz2(IFooCallback s); // android.hardware.foo@1.0::IFooCallback
};
  • S ถูกสอดแทรกเป็น android.hardware.bar@1.0::S และพบได้ใน bar/1.0/types.hal (เนื่องจาก types.hal จะถูกนำเข้าโดยอัตโนมัติ)
  • IFooCallback ถูกสอดแทรกเป็น android.hardware.bar@1.0::IFooCallback โดยใช้กฎ 2 แต่ไม่พบเนื่องจาก bar/1.0/IFooCallback.hal ไม่ได้นำเข้าโดยอัตโนมัติ (เช่นเดียวกับ types.hal ) ดังนั้น กฎข้อ 3 จะแก้ไขเป็น android.hardware.foo@1.0::IFooCallback แทน ซึ่งนำเข้าผ่าน import android.hardware.foo@1.0; ).

ประเภท.hal

ทุกแพ็คเกจ HIDL มีไฟล์ types.hal ที่มี UDT ที่ใช้ร่วมกันระหว่างอินเทอร์เฟซทั้งหมดที่เข้าร่วมในแพ็คเกจนั้น ประเภท HIDL จะเป็นแบบสาธารณะเสมอ ไม่ว่า UDT จะถูกประกาศใน types.hal หรือภายในการประกาศอินเทอร์เฟซก็ตาม ประเภทเหล่านี้สามารถเข้าถึงได้นอกขอบเขตที่กำหนดไว้ types.hal ไม่ได้มีวัตถุประสงค์เพื่ออธิบาย API สาธารณะของแพ็คเกจ แต่เป็นโฮสต์ UDT ที่ใช้โดยอินเทอร์เฟซทั้งหมดภายในแพ็คเกจ เนื่องจากลักษณะของ HIDL UDT ทั้งหมดจึงเป็นส่วนหนึ่งของอินเทอร์เฟซ

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

ตัวอย่างเช่น สำหรับ IFoo.hal :

package android.hardware.foo@1.0;
// whole package import
import android.hardware.bar@1.0;
// types only import
import android.hardware.baz@1.0::types;
// partial imports
import android.hardware.qux@1.0::IQux.Quux;
// partial imports
import android.hardware.quuz@1.0::Quuz;

ต่อไปนี้จะถูกนำเข้า:

  • android.hidl.base@1.0::IBase (โดยปริยาย)
  • android.hardware.foo@1.0::types (โดยปริยาย)
  • ทุกอย่างใน android.hardware.bar@1.0 (รวมถึงอินเทอร์เฟซทั้งหมดและ types.hal )
  • types.hal จาก android.hardware.baz@1.0::types (ไม่ได้นำเข้าอินเทอร์เฟซใน android.hardware.baz@1.0 )
  • IQux.hal และ types.hal จาก android.hardware.qux@1.0
  • Quuz จาก android.hardware.quuz@1.0 (สมมติว่า Quuz ถูกกำหนดไว้ใน types.hal ไฟล์ types.hal ทั้งหมดจะถูกแยกวิเคราะห์ แต่ประเภทอื่นที่ไม่ใช่ Quuz จะไม่ถูกนำเข้า)

การกำหนดเวอร์ชันระดับอินเทอร์เฟซ

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

package android.hardware.nfc@1.0;

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

ตัวอย่างการกำหนดเวอร์ชัน uprev ด้านล่างใช้แพ็คเกจต่อไปนี้:

// types.hal
package android.hardware.example@1.0
struct Foo {
    struct Bar {
        vec<uint32_t> val;
    };
};

// IQuux.hal
package android.hardware.example@1.0
interface IQuux {
    fromFooToBar(Foo f) generates (Foo.Bar b);
}

กฎของอัพเพรฟ

หากต้องการกำหนดแพ็คเกจ package@major.minor A หรือ B ทั้งหมดจะต้องเป็นจริง:

กฎ ก "เป็นเวอร์ชันรองเริ่มต้น": เวอร์ชันรองก่อนหน้าทั้งหมด package@major.0 , package@major.1 , …, package@major.(minor-1) จะต้องไม่ได้ถูกกำหนดไว้
หรือ
กฎข

ทั้งหมดต่อไปนี้เป็นจริง:

  1. "เวอร์ชันรองก่อนหน้าถูกต้อง": ต้องกำหนด package@major.(minor-1) และปฏิบัติตามกฎ A เดียวกัน (ไม่มีการ package@major.0 ถึง package@major.(minor-2) ) หรือกฎ B (ถ้าเป็น uprev จาก @major.(minor-2) );

    และ

  2. "สืบทอดอย่างน้อยหนึ่งอินเทอร์เฟซที่มีชื่อเดียวกัน": มีอินเทอร์เฟซอยู่ที่ package@major.minor::IFoo ที่ขยาย package@major.(minor-1)::IFoo (หากแพ็คเกจก่อนหน้ามีอินเทอร์เฟซ);

    และ

  3. "No inherited interface with a different name": ต้องไม่มี package@major.minor::IBar ที่ขยาย package@major.(minor-1)::IBaz โดยที่ IBar และ IBaz เป็นชื่อที่แตกต่างกันสองชื่อ หากมีอินเทอร์เฟซชื่อเดียวกัน package@major.minor::IBar จะต้องขยาย package@major.(minor-k)::IBar โดยที่ไม่มี IBar อยู่ด้วย k ที่เล็กกว่า

เนื่องจากกฎ A:

  • แพ็คเกจสามารถเริ่มต้นด้วยหมายเลขเวอร์ชันรอง (เช่น android.hardware.biometrics.fingerprint เริ่มต้นที่ @2.1 )
  • ข้อกำหนด " ไม่ได้กำหนด android.hardware.foo@1.0 " หมายความว่าไดเรกทอรี hardware/interfaces/foo/1.0 ไม่ควรมีอยู่ด้วยซ้ำ

อย่างไรก็ตาม กฎ A จะไม่ส่งผลต่อแพ็คเกจที่มีชื่อแพ็คเกจเดียวกัน แต่เป็นเวอร์ชัน หลัก ที่แตกต่างกัน (เช่น android.hardware.camera.device กำหนดทั้ง @1.0 และ @3.2 ส่วน @3.2 ไม่จำเป็นต้องโต้ตอบกับ @1.0 .) ดังนั้น @3.2::IExtFoo สามารถขยาย @1.0::IFoo ได้

หากชื่อแพ็กเกจแตกต่างกัน package@major.minor::IBar อาจขยายจากอินเทอร์เฟซที่มีชื่ออื่น (เช่น android.hardware.bar@1.0::IBar สามารถขยาย android.hardware.baz@2.2::IBaz ). หากอินเทอร์เฟซไม่ได้ประกาศประเภทซุปเปอร์อย่างชัดเจนด้วยคีย์เวิร์ด extend อินเทอร์เฟซนั้นจะขยาย android.hidl.base@1.0::IBase (ยกเว้น IBase เอง)

ข.2 และ ข.3 ต้องปฏิบัติตามพร้อมกัน ตัวอย่างเช่น แม้ว่า android.hardware.foo@1.1::IFoo จะขยาย android.hardware.foo@1.0::IFoo เพื่อผ่านกฎ B.2 หาก android.hardware.foo@1.1::IExtBar จะขยาย android.hardware.foo@1.0::IBar นี่ยังไม่ใช่การปรับปรุงที่ถูกต้อง

อินเทอร์เฟซที่ปรับปรุงใหม่

หากต้องการ uprev android.hardware.example@1.0 (กำหนดไว้ด้านบน) เป็น @1.1 :

// types.hal
package android.hardware.example@1.1;
import android.hardware.example@1.0;

// IQuux.hal
package android.hardware.example@1.1
interface IQuux extends @1.0::IQuux {
    fromBarToFoo(Foo.Bar b) generates (Foo f);
}

นี่คือ import ระดับแพ็คเกจของเวอร์ชัน 1.0 ของ android.hardware.example ใน types.hal แม้ว่าจะไม่มีการเพิ่ม UDT ใหม่ในเวอร์ชัน 1.1 ของแพ็คเกจ แต่ก็ยังจำเป็นต้องมีการอ้างอิงถึง UDT ในเวอร์ชัน 1.0 ดังนั้นการนำเข้าระดับแพ็คเกจใน types.hal (เอฟเฟกต์เดียวกันนี้สามารถทำได้ด้วยการนำเข้าระดับอินเทอร์เฟซใน IQuux.hal )

ใน extends @1.0::IQuux ในการประกาศของ IQuux เราได้ระบุเวอร์ชันของ IQuux ที่กำลังสืบทอดมา (จำเป็นต้องมีการอธิบายความกำกวมเนื่องจาก IQuux ใช้เพื่อประกาศอินเทอร์เฟซและสืบทอดจากอินเทอร์เฟซ) เนื่องจากการประกาศเป็นเพียงชื่อที่สืบทอดแพ็คเกจและแอ็ตทริบิวต์เวอร์ชันทั้งหมดที่ไซต์ของการประกาศ การแก้ความกำกวมจะต้องอยู่ในชื่อของอินเทอร์เฟซพื้นฐาน เราสามารถใช้ UDT ที่มีคุณสมบัติครบถ้วนได้เช่นกัน แต่นั่นจะซ้ำซ้อน

อินเทอร์เฟซใหม่ IQuux ไม่ได้ประกาศวิธีการอีกครั้ง fromFooToBar() มันสืบทอดมาจาก @1.0::IQuux ; มันเพียงแสดงวิธีการใหม่ที่เพิ่ม fromBarToFoo() ใน HIDL วิธีการที่สืบทอดมาอาจ ไม่ สามารถประกาศอีกครั้งในอินเทอร์เฟซลูก ดังนั้นอินเทอร์เฟ IQuux จึงไม่สามารถประกาศวิธีการ fromFooToBar() ได้อย่างชัดเจน

อนุสัญญา Uprev

บางครั้งชื่ออินเทอร์เฟซต้องเปลี่ยนชื่ออินเทอร์เฟซที่ขยาย เราขอแนะนำว่าส่วนขยาย enum โครงสร้าง และสหภาพแรงงานมีชื่อเดียวกันกับส่วนขยาย เว้นแต่จะแตกต่างกันเพียงพอที่จะรับประกันชื่อใหม่ ตัวอย่าง:

// in parent hal file
enum Brightness : uint32_t { NONE, WHITE };

// in child hal file extending the existing set with additional similar values
enum Brightness : @1.0::Brightness { AUTOMATIC };

// extending the existing set with values that require a new, more descriptive name:
enum Color : @1.0::Brightness { HW_GREEN, RAINBOW };

หากวิธีการสามารถมีชื่อความหมายใหม่ (เช่น fooWithLocation ) แสดงว่าเป็นที่ต้องการ มิฉะนั้น ควรตั้งชื่อให้คล้ายกับสิ่งที่ขยายออกไป ตัวอย่างเช่น วิธีการ foo_1_1 ใน @1.1::IFoo อาจแทนที่ฟังก์ชันการทำงานของวิธี foo ใน @1.0::IFoo หากไม่มีชื่ออื่นที่ดีกว่า

การกำหนดเวอร์ชันระดับแพ็คเกจ

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

อย่างไรก็ตาม ความสัมพันธ์ประเภทหนึ่งได้รับการกำหนดไว้อย่างเคร่งครัดและต้องบังคับใช้: การสืบทอดที่เข้ากันได้แบบย้อนหลังระดับแพ็กเกจ ในสถานการณ์สมมตินี้ แพ็คเกจ หลัก คือแพ็คเกจที่สืบทอดมา และแพ็คเกจ ย่อย คือแพ็คเกจหลักที่ขยาย กฎการสืบทอดที่เข้ากันได้แบบย้อนหลังระดับแพ็คเกจมีดังนี้:

  1. อินเทอร์เฟซระดับบนสุดทั้งหมดของแพ็คเกจหลักสืบทอดมาจากอินเทอร์เฟซในแพ็คเกจย่อย
  2. อินเทอร์เฟซใหม่อาจถูกเพิ่มในแพ็คเกจใหม่ (ไม่มีข้อจำกัดเกี่ยวกับความสัมพันธ์กับอินเทอร์เฟซอื่นในแพ็คเกจอื่น)
  3. ชนิดข้อมูลใหม่อาจถูกเพิ่มเพื่อใช้โดยวิธีการใหม่ของอินเทอร์เฟซที่มีอยู่หรือโดยอินเทอร์เฟซใหม่

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

หากแพ็คเกจตรงตามข้อกำหนดนี้ hidl-gen จะบังคับใช้กฎความเข้ากันได้แบบย้อนหลัง