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

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

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

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

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

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

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

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

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

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

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

ส่วนต่อประสานโครงสร้าง

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

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

แนวปฏิบัติ

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

การจัดวางโค้ด HIDL

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

อินเทอร์เฟซหลัก HIDL คืออินเทอร์เฟซที่ Google กำหนด แพ็คเกจที่เป็นของเริ่มต้นด้วย android.hardware. และตั้งชื่อตามระบบย่อย อาจมีระดับการตั้งชื่อที่ซ้อนกัน ตัวอย่างเช่น แพ็คเกจ NFC ชื่อ android.hardware.nfc และแพ็คเกจกล้องคือ android.hardware.camera โดยทั่วไป แพ็คเกจหลักจะมีชื่อ android.hardware. [ name1 ].[ name2 2 ]…. แพ็คเกจ 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; ).

types.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);
}

กฎ Uprev

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

กฎ A "Is a start minor version": ต้องไม่กำหนดเวอร์ชันรองก่อนหน้านี้ทั้งหมด 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 ). หากอินเทอร์เฟซไม่ได้ประกาศประเภท super อย่างชัดเจนด้วยคีย์เวิร์ด extend มันจะขยาย android.hidl.base@1.0::IBase (ยกเว้น IBase เอง)

ต้องติดตาม B.2 และ B.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 ระดับแพ็กเกจของ android.hardware.example เวอร์ชัน 1.0 ใน 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 จะบังคับใช้กฎความเข้ากันได้แบบย้อนหลัง