ภาษาที่ใช้สื่อสารข้อมูลระหว่างคอมโพเนนต์ของ HAL หรือ HIDL คือภาษาคำอธิบายอินเทอร์เฟซ (IDL) เพื่อระบุอินเทอร์เฟซระหว่าง HAL กับผู้ใช้ HIDL อนุญาตให้ระบุประเภทและการเรียกใช้เมธอด ซึ่งรวบรวมไว้ในอินเทอร์เฟซและแพ็กเกจ กล่าวอย่างกว้างๆ HIDL คือระบบการสื่อสารระหว่างโค้ดเบสที่อาจคอมไพล์แยกกัน
HIDL มีไว้เพื่อใช้สำหรับการสื่อสารระหว่างกระบวนการ (IPC) HAL ที่สร้างขึ้นด้วย HDL เรียกว่า HAL แบบ Binderized เนื่องจากสามารถสื่อสารกับเลเยอร์สถาปัตยกรรมอื่นๆ โดยใช้การเรียก Binder การสื่อสารระหว่างกระบวนการ (IPC) HAL แบบ Binderized จะทํางานในกระบวนการแยกต่างหากจากไคลเอ็นต์ที่ใช้ สำหรับห้องสมุดที่ต้องลิงก์กับกระบวนการ คุณจะใช้โหมดการส่งผ่านได้ด้วย (ไม่รองรับใน Java)
HIDL จะระบุโครงสร้างข้อมูลและลายเซ็นเมธอด ซึ่งจัดระเบียบไว้ในอินเทอร์เฟซ (คล้ายกับคลาส) ที่รวบรวมไว้ในแพ็กเกจ ไวยากรณ์ของ HIDL ดูเหมือนจะคุ้นเคยกับโปรแกรมเมอร์ C++ และ Java แต่มีคีย์เวิร์ดชุดอื่น HIDL ยังใช้คำอธิบายประกอบสไตล์ Java ด้วย
คำศัพท์
ส่วนนี้ใช้คําศัพท์ต่อไปนี้ที่เกี่ยวข้องกับ HIDL
ไฟล์ Binder | บ่งบอกว่ามีการใช้ HIDL สําหรับการเรียกใช้กระบวนการระยะไกลระหว่างกระบวนการ ซึ่งติดตั้งใช้งานผ่านกลไกที่คล้ายกับ Binder โปรดดูการส่งต่อด้วย |
---|---|
Callback แบบอะซิงโครนัส | อินเทอร์เฟซที่ให้บริการโดยผู้ใช้ HAL ซึ่งส่งไปยัง HAL (โดยใช้เมธอด HIDL) และ HAL จะเรียกใช้เพื่อแสดงผลข้อมูลได้ทุกเมื่อ |
Callback, แบบซิงโครนัส | ส่งคืนข้อมูลจากการใช้งานเมธอด HIDL ของเซิร์ฟเวอร์ไปยังไคลเอ็นต์ ไม่ได้ใช้สําหรับเมธอดที่แสดงผลเป็นค่าว่างหรือค่าพื้นฐานเดี่ยว |
client | กระบวนการที่เรียกใช้เมธอดของอินเทอร์เฟซหนึ่งๆ HAL หรือกระบวนการเฟรมเวิร์ก Android อาจเป็นไคลเอ็นต์ของอินเทอร์เฟซหนึ่งและเซิร์ฟเวอร์ของอีกอินเทอร์เฟซหนึ่ง โปรดดูการส่งต่อด้วย |
ขยาย | ระบุอินเทอร์เฟซที่เพิ่มเมธอดและ/หรือประเภทลงในอินเทอร์เฟซอื่น อินเทอร์เฟซหนึ่งจะขยายอินเทอร์เฟซอื่นได้เพียงอินเทอร์เฟซเดียวเท่านั้น ใช้กับการอัปเกรดเวอร์ชันย่อยในชื่อแพ็กเกจเดียวกันหรือสำหรับแพ็กเกจใหม่ (เช่น ส่วนขยายของผู้ให้บริการ) เพื่อสร้างจากแพ็กเกจเก่าได้ |
สร้าง | ระบุเมธอดอินเทอร์เฟซที่แสดงผลค่าไปยังไคลเอ็นต์ หากต้องการแสดงผลค่าที่ไม่ใช่ค่าพื้นฐานค่าเดียวหรือมากกว่า 1 ค่า ระบบจะสร้างฟังก์ชัน Callback แบบซิงค์ |
อินเทอร์เฟซ | ชุดวิธีการและประเภท แปลเป็นคลาสใน C++ หรือ Java ระบบจะเรียกใช้เมธอดทั้งหมดในอินเทอร์เฟซในทิศทางเดียวกัน กล่าวคือ กระบวนการของไคลเอ็นต์จะเรียกใช้เมธอดที่กระบวนการของเซิร์ฟเวอร์นำมาใช้ |
เที่ยวเดียว | เมื่อใช้กับเมธอด HIDL จะระบุว่าเมธอดไม่แสดงผลค่าใดๆ และไม่บล็อก |
พัสดุ | ชุดอินเทอร์เฟซและประเภทข้อมูลที่แชร์เวอร์ชันเดียวกัน |
การปล่อยผ่านสัญญาณ | โหมดของ HIDL ที่เซิร์ฟเวอร์เป็นไลบรารีที่ใช้ร่วมกัน โดยไคลเอ็นต์เป็นผู้dlopen แก้ไข ในโหมดการส่งต่อ ไคลเอ็นต์และเซิร์ฟเวอร์เป็นกระบวนการเดียวกัน แต่มีโค้ดฐานแยกกัน ใช้เพื่อนําโค้ดเบสเดิมมาไว้ในรูปแบบ HIDL เท่านั้น
โปรดดูBinderized |
เซิร์ฟเวอร์ | กระบวนการที่ใช้เมธอดของอินเทอร์เฟซ โปรดดูการส่งต่อด้วย |
การขนส่ง | โครงสร้างพื้นฐาน HIDL ที่ย้ายข้อมูลระหว่างเซิร์ฟเวอร์กับไคลเอ็นต์ |
เวอร์ชัน | เวอร์ชันของแพ็กเกจ ประกอบด้วยจำนวนเต็ม 2 รายการ ได้แก่ หลักและรอง การอัปเกรดเวอร์ชันย่อยอาจเพิ่ม (แต่จะไม่เปลี่ยนแปลง) ประเภทและเมธอด |
การออกแบบ HIDL
เป้าหมายของ HIDL คือสามารถแทนที่เฟรมเวิร์ก Android ได้โดยไม่ต้องสร้าง HAL ขึ้นมาใหม่ HAL สร้างขึ้นโดยผู้ให้บริการหรือผู้ผลิต SoC และใส่ไว้ในพาร์ติชัน /vendor
ในอุปกรณ์ ซึ่งช่วยให้เฟรมเวิร์ก Android ในพาร์ติชันของตัวเองแทนที่ด้วย OTA ได้โดยไม่ต้องคอมไพล์ HAL อีกครั้ง
การออกแบบ HIDL จะช่วยรักษาสมดุลระหว่างข้อกังวลต่อไปนี้
- ความสามารถในการทำงานร่วมกัน สร้างอินเทอร์เฟซที่ทำงานร่วมกันได้อย่างน่าเชื่อถือระหว่างกระบวนการต่างๆ ซึ่งอาจคอมไพล์ด้วยสถาปัตยกรรม เครื่องมือและการกำหนดค่าการสร้างที่หลากหลาย อินเทอร์เฟซ HIDL มีเวอร์ชันและเปลี่ยนแปลงไม่ได้หลังจากเผยแพร่แล้ว
- ประสิทธิภาพ HIDL จะพยายามลดจำนวนการดำเนินการคัดลอก ระบบจะส่งข้อมูลที่กําหนดโดย HIDL ไปยังโค้ด C++ ในเลย์เอาต์มาตรฐาน C++ ซึ่งเป็นโครงสร้างข้อมูลที่ใช้ได้โดยไม่ต้องแตกไฟล์ HIDL ยังมีอินเทอร์เฟซหน่วยความจำที่ใช้ร่วมกันด้วย และเนื่องจาก RPC ทำงานค่อนข้างช้า HIDL จึงรองรับ 2 วิธีในการโอนข้อมูลโดยไม่ต้องใช้การเรียก RPC ได้แก่ หน่วยความจำที่ใช้ร่วมกันและ Fast Message Queue (FMQ)
- ใช้งานง่าย HIDL หลีกเลี่ยงปัญหาที่ยากลำบากเกี่ยวกับความเป็นเจ้าของหน่วยความจำโดยใช้พารามิเตอร์
in
เท่านั้นสําหรับ RPC (ดูภาษาการกําหนดอินเทอร์เฟซ Android (AIDL)) ค่าที่แสดงผลจากเมธอดได้อย่างมีประสิทธิภาพจะแสดงผลผ่านฟังก์ชันการเรียกกลับ การส่งข้อมูลไปยัง HIDL เพื่อโอนหรือการรับข้อมูลจาก HIDL จะไม่เปลี่ยนแปลงการเป็นเจ้าของข้อมูล สิทธิ์การเป็นเจ้าของจะยังคงอยู่กับฟังก์ชันการเรียกเสมอ ข้อมูลต้องคงอยู่ตลอดระยะเวลาที่เรียกใช้ฟังก์ชันเท่านั้น และอาจถูกทำลายทันทีหลังจากที่ฟังก์ชันที่เรียกใช้แสดงผล
ใช้โหมดส่งผ่าน
หากต้องการอัปเดตอุปกรณ์ที่ใช้ Android เวอร์ชันเก่าเป็น Android O คุณสามารถรวม HAL แบบดั้งเดิม (และเดิม) ไว้ในอินเทอร์เฟซ HIDL ใหม่ที่ให้บริการ HAL ในโหมด Binderized และโหมดเดียวกัน (Passthrough) การรวมนี้จะทําให้ทั้ง HAL และเฟรมเวิร์ก Android ทำงานได้อย่างราบรื่น
โหมดการส่งผ่านใช้ได้กับไคลเอ็นต์และการใช้งาน C++ เท่านั้น อุปกรณ์ที่ใช้ Android เวอร์ชันเก่าจะไม่มี HAL ที่เขียนด้วย Java ดังนั้น HAL ของ Java จึงมี Binder อยู่แล้วโดยพื้นฐาน
ไฟล์ส่วนหัวการส่งผ่าน
เมื่อคอมไพล์ไฟล์ .hal
แล้ว hidl-gen
จะสร้างไฟล์ส่วนหัวของพาสทรู BsFoo.h
เพิ่มเติมนอกเหนือจากส่วนหัวที่ใช้สำหรับการสื่อสาร Binder ส่วนหัวนี้จะกำหนดฟังก์ชันที่จะdlopen
เนื่องจาก HAL แบบพาสทรูทำงานในกระบวนการเดียวกับที่เรียกใช้ ในกรณีส่วนใหญ่ ระบบจะเรียกใช้เมธอดพาสทรูโดยการเรียกใช้ฟังก์ชันโดยตรง (เธรดเดียวกัน) เมธอด oneway
จะทํางานในเธรดของตนเองเนื่องจากไม่ได้มีไว้เพื่อรอให้ HAL ประมวลผล (ซึ่งหมายความว่า HAL ที่ใช้เมธอด oneway
ในโหมดการส่งผ่านต้องเป็นแบบเธรดเซฟ)
เมื่อได้รับ IFoo.hal
แล้ว BsFoo.h
จะรวมวิธีการที่ HIDL สร้างขึ้นเพื่อมอบฟีเจอร์เพิ่มเติม (เช่น การทำธุรกรรม oneway
ให้ทำงานในเธรดอื่น) ไฟล์นี้คล้ายกับ BpFoo.h
แต่แทนที่จะส่งการเรียก IPC โดยใช้ Binder ระบบจะเรียกใช้ฟังก์ชันที่ต้องการโดยตรง การติดตั้งใช้งาน HAL ในอนาคตอาจให้การติดตั้งใช้งานหลายรายการ เช่น HAL FooFast และ HAL FooAccurate ในกรณีเช่นนี้ ระบบจะสร้างไฟล์สําหรับการติดตั้งใช้งานแต่ละครั้งเพิ่มเติม (เช่น PTFooFast.cpp
และ
PTFooAccurate.cpp
)
การเชื่อมโยง HAL การแสดงภาพ
คุณสามารถทำให้การใช้งาน HAL รองรับโหมดส่งผ่านได้ เมื่อระบุอินเทอร์เฟซ HAL a.b.c.d@M.N::IFoo
ระบบจะสร้างแพ็กเกจ 2 รายการดังนี้
a.b.c.d@M.N::IFoo-impl
มีการใช้งาน HAL และแสดงฟังก์ชันIFoo* HIDL_FETCH_IFoo(const char* name)
ในอุปกรณ์รุ่นเดิม แพ็กเกจนี้จะdlopen
และการสร้างอินสแตนซ์การใช้งานจะใช้HIDL_FETCH_IFoo
คุณสร้างโค้ดพื้นฐานได้โดยใช้hidl-gen
และ-Lc++-impl
และ-Landroidbp-impl
a.b.c.d@M.N::IFoo-service
. เปิด HAL แบบส่งผ่านและลงทะเบียนตัวเองเป็นบริการแบบ Binderized ซึ่งช่วยให้ใช้ HAL เดียวกันได้ทั้งในแบบส่งผ่านและแบบ Binderized
เมื่อทราบประเภท IFoo
คุณสามารถเรียกใช้ sp<IFoo>
IFoo::getService(string name, bool getStub)
เพื่อเข้าถึงอินสแตนซ์ของ IFoo
หาก getStub
เป็นจริง getService
จะพยายามเปิด HAL ในโหมดการแสดงภาพเท่านั้น หาก getStub
เป็นเท็จ getService
จะพยายามค้นหาบริการที่เชื่อมโยง หากไม่สำเร็จ ก็จะพยายามค้นหาบริการการส่งผ่าน ไม่ควรใช้พารามิเตอร์ getStub
ยกเว้นใน defaultPassthroughServiceImplementation
(อุปกรณ์ที่เปิดตัวด้วย Android O เป็นอุปกรณ์ที่ใช้ Binder ทั้งหมด จึงไม่ได้รับอนุญาตให้เปิดบริการในโหมดการส่งผ่าน)
ไวยากรณ์ HIDL
ภาษา HIDL ออกแบบมาให้คล้ายกับ C (แต่ไม่ได้ใช้สเปรดชีต C) เครื่องหมายวรรคตอนทั้งหมดที่ไม่ได้อธิบายไว้ด้านล่าง (นอกเหนือจากการใช้ =
และ |
ที่ชัดเจน) เป็นส่วนหนึ่งของไวยากรณ์
หมายเหตุ: ดูรายละเอียดเกี่ยวกับสไตล์โค้ด HIDL ได้ที่คู่มือสไตล์โค้ด
/** */
หมายถึงความคิดเห็นในเอกสารประกอบ ซึ่งใช้ได้กับประกาศค่าประเภท เมธอด ฟิลด์ และ enum เท่านั้น/* */
หมายถึงความคิดเห็นหลายบรรทัด//
ระบุความคิดเห็นจนถึงบรรทัดสุดท้าย นอกเหนือจาก//
แล้ว การขึ้นบรรทัดใหม่จะเหมือนกับช่องว่างอื่นๆ- ในตัวอย่างไวยากรณ์ด้านล่าง ข้อความจาก
//
จนถึงท้ายบรรทัดไม่ได้เป็นส่วนหนึ่งของไวยากรณ์ แต่เป็นความคิดเห็นเกี่ยวกับไวยากรณ์ [empty]
หมายความว่าคํานั้นอาจว่างเปล่า?
ที่ตามหลังข้อความหรือคําหมายความว่าไม่บังคับ...
หมายถึงลําดับที่มีรายการตั้งแต่ 0 รายการขึ้นไป โดยมีเครื่องหมายวรรคตอนคั่นตามที่ระบุ HIDL ไม่มีอาร์กิวเมนต์แบบผันแปร- คอมมาคั่นองค์ประกอบลำดับ
- เซมิโคลอนเป็นตัวสิ้นสุดขององค์ประกอบแต่ละรายการ รวมถึงองค์ประกอบสุดท้าย
- UPPERCASE ไม่ใช่อักขระสิ้นสุด
italics
คือตระกูลโทเค็น เช่นinteger
หรือidentifier
(กฎการแยกวิเคราะห์ C มาตรฐาน)constexpr
คือนิพจน์ค่าคงที่สไตล์ C (เช่น1 + 1
และ1L << 3
)import_name
คือชื่อแพ็กเกจหรืออินเทอร์เฟซที่ผ่านการรับรองตามที่อธิบายไว้ใน การกำหนดเวอร์ชันของ HIDLwords
ตัวพิมพ์เล็กคือโทเค็นแบบตรงตัว
ตัวอย่าง
ROOT = PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... } // not for types.hal | PACKAGE IMPORTS ITEM ITEM... // only for types.hal; no method definitions ITEM = ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?; | safe_union identifier { UFIELD; UFIELD; ...}; | struct identifier { SFIELD; SFIELD; ...}; // Note - no forward declarations | union identifier { UFIELD; UFIELD; ...}; | enum identifier: TYPE { ENUM_ENTRY, ENUM_ENTRY ... }; // TYPE = enum or scalar | typedef TYPE identifier; VERSION = integer.integer; PACKAGE = package android.hardware.identifier[.identifier[...]]@VERSION; PREAMBLE = interface identifier EXTENDS EXTENDS = <empty> | extends import_name // must be interface, not package GENERATES = generates (FIELD, FIELD ...) // allows the Binder interface to be used as a type // (similar to typedef'ing the final identifier) IMPORTS = [empty] | IMPORTS import import_name; TYPE = uint8_t | int8_t | uint16_t | int16_t | uint32_t | int32_t | uint64_t | int64_t | float | double | bool | string | identifier // must be defined as a typedef, struct, union, enum or import // including those defined later in the file | memory | pointer | vec<TYPE> | bitfield<TYPE> // TYPE is user-defined enum | fmq_sync<TYPE> | fmq_unsync<TYPE> | TYPE[SIZE] FIELD = TYPE identifier UFIELD = TYPE identifier | safe_union identifier { FIELD; FIELD; ...} identifier; | struct identifier { FIELD; FIELD; ...} identifier; | union identifier { FIELD; FIELD; ...} identifier; SFIELD = TYPE identifier | safe_union identifier { FIELD; FIELD; ...}; | struct identifier { FIELD; FIELD; ...}; | union identifier { FIELD; FIELD; ...}; | safe_union identifier { FIELD; FIELD; ...} identifier; | struct identifier { FIELD; FIELD; ...} identifier; | union identifier { FIELD; FIELD; ...} identifier; SIZE = // Must be greater than zero constexpr ANNOTATIONS = [empty] | ANNOTATIONS ANNOTATION ANNOTATION = | @identifier | @identifier(VALUE) | @identifier(ANNO_ENTRY, ANNO_ENTRY ...) ANNO_ENTRY = identifier=VALUE VALUE = "any text including \" and other escapes" | constexpr | {VALUE, VALUE ...} // only in annotations ENUM_ENTRY = identifier | identifier = constexpr