HIDL กําหนดให้อินเทอร์เฟซทุกรายการที่เขียนด้วย HIDL ต้องมีเวอร์ชัน หลังจากเผยแพร่อินเทอร์เฟซ HAL แล้ว ระบบจะหยุดอินเทอร์เฟซนั้นไว้ และการเปลี่ยนแปลงเพิ่มเติมจะต้องทำในอินเทอร์เฟซเวอร์ชันใหม่ แม้ว่าอินเทอร์เฟซที่เผยแพร่แล้วจะแก้ไขไม่ได้ แต่อินเทอร์เฟซอื่นสามารถขยายอินเทอร์เฟซนั้นได้
โครงสร้างโค้ด HIDL
โค้ด HIDL จะจัดระเบียบเป็นประเภท อินเทอร์เฟซ และแพ็กเกจที่ผู้ใช้กำหนด ดังนี้
- ประเภทที่กําหนดโดยผู้ใช้ (UDT) HIDL ให้สิทธิ์เข้าถึงชุดประเภทข้อมูลพื้นฐานซึ่งสามารถใช้เพื่อเขียนประเภทที่ซับซ้อนมากขึ้นผ่านโครงสร้าง ยูเนียน และการแจกแจง ระบบจะส่ง UDT ไปยังเมธอดของอินเทอร์เฟซ และสามารถกำหนดได้ที่ระดับแพ็กเกจ (ใช้ร่วมกันกับอินเทอร์เฟซทั้งหมด) หรือที่อินเทอร์เฟซ
- อินเทอร์เฟซ อินเทอร์เฟซเป็นองค์ประกอบพื้นฐานของ HIDL ซึ่งประกอบด้วยการประกาศ UDT และเมธอด อินเทอร์เฟซยังรับค่าจากอินเทอร์เฟซอื่นได้ด้วย
- แพ็กเกจ จัดระเบียบอินเทอร์เฟซ HIDL ที่เกี่ยวข้องและประเภทข้อมูลที่อินเทอร์เฟซดังกล่าวทำงานด้วย แพ็กเกจจะระบุด้วยชื่อและเวอร์ชัน รวมถึงข้อมูลต่อไปนี้
- ไฟล์คําจํากัดความประเภทข้อมูลชื่อ
types.hal
- อินเทอร์เฟซอย่างน้อย 1 รายการ โดยแต่ละรายการอยู่ในไฟล์
.hal
ของตนเอง
- ไฟล์คําจํากัดความประเภทข้อมูลชื่อ
ไฟล์คำจำกัดความของประเภทข้อมูล types.hal
มีเฉพาะ UDT (UDT ระดับแพ็กเกจทั้งหมดจะเก็บไว้ในไฟล์เดียว) การแสดงในภาษาเป้าหมายจะใช้ได้กับอินเทอร์เฟซทั้งหมดในแพ็กเกจ
ปรัชญาการกำหนดเวอร์ชัน
แพ็กเกจ HIDL (เช่น android.hardware.nfc
) หลังจากเผยแพร่สำหรับเวอร์ชันหนึ่งๆ (เช่น 1.0
) จะเปลี่ยนแปลงไม่ได้ การแก้ไขอินเทอร์เฟซในแพ็กเกจหรือการเปลี่ยนแปลง UDT ใดๆ จะเกิดขึ้นได้ในแพ็กเกจอื่นเท่านั้น
ใน HIDL เวอร์ชันจะมีผลในระดับแพ็กเกจ ไม่ใช่ระดับอินเทอร์เฟซ และอินเทอร์เฟซและ UDT ทั้งหมดในแพ็กเกจจะใช้เวอร์ชันเดียวกัน เวอร์ชันของแพ็กเกจจะเป็นไปตามการกำหนดเวอร์ชันแบบเชิงอรรถโดยไม่มีองค์ประกอบระดับแพตช์และข้อมูลเมตาของบิลด์ ภายในแพ็กเกจหนึ่งๆ การอัปเกรดเวอร์ชันย่อยหมายความว่าแพ็กเกจเวอร์ชันใหม่เข้ากันได้แบบย้อนหลังกับแพ็กเกจเก่า และการอัปเกรดเวอร์ชันหลักหมายความว่าแพ็กเกจเวอร์ชันใหม่เข้ากันไม่ได้แบบย้อนหลังกับแพ็กเกจเก่า
ในแง่แนวคิด แพ็กเกจหนึ่งอาจเกี่ยวข้องกับแพ็กเกจอื่นได้หลายวิธี ดังนี้
- ไม่เลย
- ความสามารถในการขยายการทำงานแบบเข้ากันได้กับเวอร์ชันเก่าระดับแพ็กเกจ กรณีนี้เกิดขึ้นเมื่ออัปเดตแพ็กเกจเป็นเวอร์ชันย่อยใหม่ (การแก้ไขที่เพิ่มขึ้นครั้งถัดไป) โดยแพ็กเกจใหม่จะมีชื่อและเวอร์ชันหลักเหมือนกับแพ็กเกจเก่า แต่เวอร์ชันย่อยจะสูงกว่า ในแง่ฟังก์ชันการทำงาน แพ็กเกจใหม่จะเป็นเซตที่ใหญ่กว่าแพ็กเกจเก่า หมายความว่า
- อินเทอร์เฟซระดับบนสุดของแพ็กเกจหลักจะอยู่ในแพ็กเกจใหม่ แม้ว่าอินเทอร์เฟซอาจมีเมธอดใหม่ UDT ระดับอินเทอร์เฟซใหม่ (ส่วนขยายระดับอินเทอร์เฟซที่อธิบายไว้ด้านล่าง) และ UDT ใหม่ใน
types.hal
- นอกจากนี้ คุณยังเพิ่มอินเทอร์เฟซใหม่ลงในแพ็กเกจใหม่ได้ด้วย
- ประเภทข้อมูลทั้งหมดของแพ็กเกจหลักจะอยู่ในแพ็กเกจใหม่และสามารถจัดการโดยเมธอด (อาจติดตั้งใช้งานอีกครั้ง) จากแพ็กเกจเก่า
- นอกจากนี้ คุณยังเพิ่มประเภทข้อมูลใหม่เพื่อใช้งานด้วยวิธีการใหม่ของอินเทอร์เฟซที่มีอยู่ซึ่งอัปเกรดแล้ว หรือด้วยอินเทอร์เฟซใหม่ก็ได้
- อินเทอร์เฟซระดับบนสุดของแพ็กเกจหลักจะอยู่ในแพ็กเกจใหม่ แม้ว่าอินเทอร์เฟซอาจมีเมธอดใหม่ UDT ระดับอินเทอร์เฟซใหม่ (ส่วนขยายระดับอินเทอร์เฟซที่อธิบายไว้ด้านล่าง) และ UDT ใหม่ใน
- ความสามารถในการขยายระดับอินเทอร์เฟซที่เข้ากันได้แบบย้อนหลัง นอกจากนี้ แพ็กเกจใหม่ยังขยายแพ็กเกจเดิมได้ด้วยการมีอินเทอร์เฟซที่แยกตามตรรกะซึ่งให้ฟังก์ชันการทำงานเพิ่มเติมเท่านั้น ไม่ใช่ฟังก์ชันหลัก
ในกรณีนี้ คุณอาจต้องดำเนินการต่อไปนี้
- อินเทอร์เฟซในแพ็กเกจใหม่ต้องใช้ประเภทข้อมูลของแพ็กเกจเก่า
- อินเทอร์เฟซในแพ็กเกจใหม่สามารถขยายอินเทอร์เฟซของแพ็กเกจเก่าได้อย่างน้อย 1 แพ็กเกจ
- ขยายความไม่เข้ากันแบบย้อนหลังเดิม นี่เป็นแพ็กเกจเวอร์ชันหลักที่อัปเกรด และไม่จำเป็นต้องมีความสัมพันธ์กัน หากมี ก็สามารถแสดงด้วยชุดค่าผสมของประเภทจากแพ็กเกจเวอร์ชันเก่าและการรับค่าอินเทอร์เฟซย่อยของแพ็กเกจเก่า
การจัดโครงสร้างอินเทอร์เฟซ
สำหรับอินเทอร์เฟซที่มีโครงสร้างดี การเพิ่มฟังก์ชันการทำงานประเภทใหม่ที่ไม่ได้อยู่ในการออกแบบเดิมจะต้องมีการแก้ไขอินเทอร์เฟซ HIDL ในทางกลับกัน หากคุณทำได้หรือคาดว่าจะทำการเปลี่ยนแปลงทั้ง 2 ด้านของอินเทอร์เฟซที่เปิดตัวฟังก์ชันใหม่โดยไม่เปลี่ยนอินเทอร์เฟซนั้น แสดงว่าอินเทอร์เฟซนั้นไม่มีโครงสร้าง
Treble รองรับคอมโพเนนต์ของผู้ให้บริการและระบบที่คอมไพล์แยกต่างหาก ซึ่งสามารถคอมไพล์ vendor.img
ในอุปกรณ์และ system.img
แยกกันได้ การโต้ตอบทั้งหมดระหว่าง vendor.img
กับ system.img
ต้องได้รับการกําหนดอย่างละเอียดและชัดเจนเพื่อให้ทํางานต่อไปได้อีกหลายปี ซึ่งรวมถึงอินเทอร์เฟซ API หลายรายการ แต่อินเทอร์เฟซหลักคือกลไก IPC ที่ HIDL ใช้สำหรับการสื่อสารระหว่างกระบวนการในขอบเขต system.img
/vendor.img
ข้อกำหนด
ข้อมูลทั้งหมดที่ส่งผ่าน HIDL ต้องได้รับการกําหนดไว้อย่างชัดเจน ข้อมูลต้องเป็นไปตามข้อกําหนดต่อไปนี้เพื่อให้การติดตั้งใช้งานและไคลเอ็นต์ทํางานร่วมกันได้ต่อไปแม้ว่าจะคอมไพล์แยกกันหรือพัฒนาแยกต่างหากก็ตาม
- อธิบายได้ใน HIDL โดยตรง (โดยใช้โครงสร้าง อาร์เรย์แบบจำกัด ฯลฯ) พร้อมชื่อและความหมายเชิงความหมาย
- อธิบายได้ด้วยมาตรฐานสาธารณะ เช่น 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 ประกอบด้วยแพ็กเกจหลักและแพ็กเกจของผู้ให้บริการ
อินเทอร์เฟซ 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 ยูทิลิตีทุกรายการจะมีชื่อที่สมบูรณ์ในตัวเอง ซึ่งประกอบด้วยชื่อยูทิลิตี ชื่อแพ็กเกจที่กําหนดยูทิลิตี และเวอร์ชันแพ็กเกจ ระบบจะใช้ชื่อแบบเต็มที่สมบูรณ์เฉพาะเมื่อมีการประกาศอินสแตนซ์ของประเภทนั้นๆ เท่านั้น และจะไม่ใช้ในกรณีที่มีการกําหนดประเภทนั้นๆ ตัวอย่างเช่น สมมติว่าแพ็กเกจ 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
คือชื่อที่แยกด้วยจุดของ UDT HIDL เนื่องจาก 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
ในทั้ง 2 กรณี 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
เป็นชื่อที่มีคุณสมบัติเหมาะสมโดยสมบูรณ์แบบเดียวกันสำหรับประเภท enumVALUE
คือชื่อของค่า
กฎการอนุมานอัตโนมัติ
โดยไม่จำเป็นต้องระบุชื่อ 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
ตามที่ควรจะเป็น (ดูส่วนขยายระดับแพ็กเกจ) และคําจํากัดความระบุเฉพาะชื่อ UDT เท่านั้น
struct ExtendedNfcData { NfcData base; // … additional members };
คอมไพเลอร์จะค้นหา UDT ที่มีชื่อว่า NfcData
และพบใน android.hardware.nfc
เวอร์ชัน 1.0
ส่งผลให้มี UDT ของ android.hardware.nfc@1.0::NfcData
ที่ผ่านการรับรองอย่างสมบูรณ์ หากพบรายการที่ตรงกันมากกว่า 1 รายการสําหรับ 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
เนื่องจากไม่ได้นำเข้า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 ที่อินเทอร์เฟซทั้งหมดในแพ็กเกจใช้ UDT ทั้งหมดเป็นส่วนหนึ่งของอินเทอร์เฟซเนื่องจากลักษณะของ HIDL
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 ทั้งหมดต่อไปนี้ต้องเป็นจริง
กฎ ก | "Is a start minor version": ต้องไม่กำหนดเวอร์ชันย่อยก่อนหน้าทั้งหมด นั่นคือ package@major.0 , package@major.1 , …, package@major.(minor-1)
|
---|
กฎ ข. | เงื่อนไขต่อไปนี้ทั้งหมดเป็นจริง
|
---|
เนื่องจากกฎ ก.
- แพ็กเกจสามารถเริ่มต้นด้วยหมายเลขเวอร์ชันย่อยใดก็ได้ (เช่น
android.hardware.biometrics.fingerprint
เริ่มต้นที่@2.1
) - ข้อกำหนด "
android.hardware.foo@1.0
ไม่ได้กําหนด" หมายความว่าไดเรกทอรีhardware/interfaces/foo/1.0
ไม่ควรมีอยู่เลย
อย่างไรก็ตาม กฎ ก. จะไม่มีผลกับแพ็กเกจที่มีชื่อแพ็กเกจเดียวกันแต่มีเวอร์ชันหลักต่างกัน (เช่น 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
เอง)
คุณต้องปฏิบัติตามข้อ 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
ระดับแพ็กเกจของเวอร์ชัน 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 ประเภทได้รับการกําหนดอย่างเคร่งครัดและต้องบังคับใช้ ซึ่งก็คือการสืบทอดที่เข้ากันได้แบบย้อนหลังระดับแพ็กเกจ ในกรณีนี้ แพ็กเกจหลักคือแพ็กเกจที่รับค่ามา และแพ็กเกจย่อยคือแพ็กเกจที่ขยายแพ็กเกจหลัก กฎการสืบทอดที่เข้ากันได้แบบย้อนหลังระดับแพ็กเกจมีดังนี้
- อินเทอร์เฟซระดับบนสุดทั้งหมดของแพ็กเกจหลักจะรับค่ามาจากอินเทอร์เฟซในแพ็กเกจย่อย
- นอกจากนี้ คุณยังเพิ่มอินเทอร์เฟซใหม่ลงในแพ็กเกจใหม่ได้ด้วย (ไม่มีข้อจำกัดเกี่ยวกับความสัมพันธ์กับอินเทอร์เฟซอื่นๆ ในแพ็กเกจอื่นๆ)
- นอกจากนี้ คุณยังเพิ่มประเภทข้อมูลใหม่เพื่อใช้งานด้วยวิธีการใหม่ของอินเทอร์เฟซที่มีอยู่ซึ่งอัปเกรดแล้ว หรือด้วยอินเทอร์เฟซใหม่ก็ได้
กฎเหล่านี้สามารถใช้งานได้โดยใช้การสืบทอดระดับอินเทอร์เฟซ HIDL และการจัดเรียง UDT แต่ต้องใช้ความรู้ระดับเมตาเพื่อทราบว่าความสัมพันธ์เหล่านี้ถือเป็นส่วนขยายแพ็กเกจที่เข้ากันได้แบบย้อนหลัง ระบบจะอนุมานความรู้นี้ดังนี้
หากแพ็กเกจเป็นไปตามข้อกำหนดนี้ hidl-gen
จะบังคับใช้กฎความเข้ากันได้แบบย้อนหลัง