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