HIDL

ภาษาคำจำกัดความอินเทอร์เฟซ HAL หรือ HIDL เป็นภาษาคำอธิบายอินเทอร์เฟซ (IDL) เพื่อระบุอินเทอร์เฟซระหว่าง HAL และผู้ใช้ HIDL อนุญาตให้ระบุประเภทและการเรียกใช้เมธอด โดยรวบรวมไว้ในอินเทอร์เฟซและแพ็คเกจ พูดกว้างๆ ก็คือ HIDL คือระบบสำหรับการสื่อสารระหว่างโค้ดเบสที่อาจคอมไพล์ได้อย่างอิสระ ตั้งแต่ Android 10 เป็นต้นไป HIDL เลิกใช้งานแล้ว และ Android กำลังย้ายข้อมูลไปใช้ AIDL ทุกที่

HIDL มีวัตถุประสงค์เพื่อใช้สำหรับการสื่อสารระหว่างกระบวนการ (IPC) HAL ที่สร้างด้วย HDL เรียกว่า Binderized HALs โดยที่พวกมันสามารถสื่อสารกับเลเยอร์สถาปัตยกรรมอื่นๆ โดยใช้การเรียก Binder Inter-Process Communication (IPC) HAL ที่ถูกผูกมัดทำงานในกระบวนการแยกต่างหากจากไคลเอนต์ที่ใช้งาน สำหรับไลบรารีที่ต้องลิงก์กับกระบวนการ โหมดส่งผ่าน ก็พร้อมใช้งานเช่นกัน (ไม่รองรับใน Java)

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

คำศัพท์เฉพาะทาง

ส่วนนี้ใช้คำศัพท์ที่เกี่ยวข้องกับ HIDL ต่อไปนี้:

ผูกมัด บ่งชี้ว่ามีการใช้ HIDL สำหรับการเรียกขั้นตอนระยะไกลระหว่างกระบวนการต่างๆ ที่ใช้งานผ่านกลไกที่คล้ายกับ Binder ดู ทางผ่าน ด้วย
โทรกลับแบบอะซิงโครนัส อินเทอร์เฟซที่ให้บริการโดยผู้ใช้ HAL ส่งต่อไปยัง HAL (โดยใช้วิธี HIDL) และเรียกโดย HAL เพื่อส่งคืนข้อมูลได้ตลอดเวลา
โทรกลับแบบซิงโครนัส ส่งคืนข้อมูลจากการนำเมธอด HIDL ของเซิร์ฟเวอร์ไปใช้งานไปยังไคลเอนต์ ไม่ได้ใช้สำหรับวิธีการที่ส่งกลับค่าโมฆะหรือค่าดั้งเดิมเดียว
ลูกค้า กระบวนการที่เรียกเมธอดของอินเทอร์เฟซเฉพาะ กระบวนการกรอบงาน HAL หรือ Android อาจเป็นไคลเอนต์ของอินเทอร์เฟซหนึ่งและเซิร์ฟเวอร์ของอีกอินเทอร์เฟซหนึ่ง ดู ทางผ่าน ด้วย
ขยาย ระบุอินเทอร์เฟซที่เพิ่มวิธีการและ/หรือประเภทให้กับอินเทอร์เฟซอื่น อินเทอร์เฟซสามารถขยายอินเทอร์เฟซอื่นได้เพียงอินเทอร์เฟซเดียวเท่านั้น สามารถใช้สำหรับการเพิ่มเวอร์ชันรองในชื่อแพ็คเกจเดียวกัน หรือสำหรับแพ็คเกจใหม่ (เช่น ส่วนขยายของผู้จำหน่าย) เพื่อสร้างบนแพ็คเกจเก่า
สร้างขึ้น ระบุวิธีการอินเทอร์เฟซที่ส่งกลับค่าไปยังไคลเอนต์ หากต้องการส่งคืนค่าที่ไม่ใช่ค่าดั้งเดิมหรือมากกว่าหนึ่งค่า ฟังก์ชันการเรียกกลับแบบซิงโครนัสจะถูกสร้างขึ้น
อินเตอร์เฟซ การรวบรวมวิธีการและประเภท แปลเป็นคลาสใน C ++ หรือ Java วิธีการทั้งหมดในอินเทอร์เฟซถูกเรียกไปในทิศทางเดียวกัน: กระบวนการไคลเอนต์เรียกใช้วิธีการที่ดำเนินการโดยกระบวนการเซิร์ฟเวอร์
ทางเดียว เมื่อใช้กับวิธี HIDL แสดงว่าวิธีการไม่ส่งคืนค่าและไม่ถูกบล็อก
บรรจุุภัณฑ์ การรวบรวมอินเทอร์เฟซและประเภทข้อมูลที่แชร์เวอร์ชัน
ทะลุผ่าน โหมดของ HIDL ซึ่งเซิร์ฟเวอร์เป็นไลบรารีที่ใช้ร่วมกัน dlopen ed โดยไคลเอนต์ ในโหมดส่งผ่าน ไคลเอนต์และเซิร์ฟเวอร์เป็นกระบวนการเดียวกัน แต่มีโค้ดเบสแยกกัน ใช้เพื่อนำโค้ดเบสแบบเดิมมาสู่โมเดล HIDL เท่านั้น ดูเพิ่มเติม ที่ Binderized
เซิร์ฟเวอร์ กระบวนการที่ใช้วิธีการของอินเทอร์เฟซ ดู ทางผ่าน ด้วย
ขนส่ง โครงสร้างพื้นฐาน HIDL ที่ย้ายข้อมูลระหว่างเซิร์ฟเวอร์และไคลเอนต์
รุ่น เวอร์ชันของแพ็คเกจ ประกอบด้วยจำนวนเต็มสองตัว คือ หลักและรอง การเพิ่มเวอร์ชันเล็กน้อยอาจเพิ่มประเภทและวิธีการ (แต่ไม่เปลี่ยนแปลง)

การออกแบบ HIDL

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

การออกแบบ HIDL สร้างสมดุลระหว่างข้อกังวลต่อไปนี้:

  • การทำงานร่วมกัน สร้างอินเทอร์เฟซที่ทำงานร่วมกันได้อย่างน่าเชื่อถือระหว่างกระบวนการต่างๆ ซึ่งอาจคอมไพล์ด้วยสถาปัตยกรรม ห่วงโซ่เครื่องมือ และการกำหนดค่าต่างๆ ของบิลด์ อินเทอร์เฟซ HIDL เป็นเวอร์ชันและไม่สามารถเปลี่ยนแปลงได้หลังจากเผยแพร่แล้ว
  • ประสิทธิภาพ . HIDL พยายามลดจำนวนการดำเนินการคัดลอกให้เหลือน้อยที่สุด ข้อมูลที่กำหนดโดย HIDL จะถูกส่งไปยังโค้ด C++ ในโครงสร้างข้อมูลโครงร่างมาตรฐานของ C++ ที่สามารถใช้งานได้โดยไม่ต้องแกะกล่อง HIDL ยังมีอินเทอร์เฟซหน่วยความจำที่ใช้ร่วมกัน และเนื่องจาก RPC ค่อนข้างช้าโดยธรรมชาติ HIDL จึงสนับสนุนการถ่ายโอนข้อมูลสองวิธีโดยไม่ต้องใช้การเรียก RPC: หน่วยความจำที่ใช้ร่วมกัน และ Fast Message Queue (FMQ)
  • ใช้งานง่าย HIDL หลีกเลี่ยงปัญหายุ่งยากในการเป็นเจ้าของหน่วยความจำโดยใช้เฉพาะ in พารามิเตอร์สำหรับ RPC (ดู Android Interface Definition Language (AIDL) ) ค่าที่ไม่สามารถส่งคืนได้อย่างมีประสิทธิภาพจากวิธีการจะถูกส่งคืนผ่านฟังก์ชันการโทรกลับ การส่งข้อมูลไปยัง HIDL เพื่อถ่ายโอนหรือรับข้อมูลจาก HIDL จะไม่เปลี่ยนความเป็นเจ้าของข้อมูล ความเป็นเจ้าของจะยังคงอยู่กับฟังก์ชันการโทรเสมอ ข้อมูลจำเป็นต้องคงอยู่ในช่วงเวลาของฟังก์ชันที่ถูกเรียกเท่านั้น และอาจถูกทำลายทันทีหลังจากที่ฟังก์ชันที่ถูกเรียกกลับมา

การใช้โหมดส่งผ่าน

หากต้องการอัปเดตอุปกรณ์ที่ใช้ Android เวอร์ชันก่อนหน้าเป็น Android O คุณสามารถรวม HAL ทั้งแบบธรรมดา (และแบบเดิม) ไว้ในอินเทอร์เฟซ HIDL ใหม่ที่ให้บริการ HAL ในโหมด Binderized และโหมดกระบวนการเดียวกัน (ส่งผ่าน) การห่อนี้มีความโปร่งใสสำหรับทั้ง HAL และกรอบงาน Android

โหมดส่งผ่านใช้ได้เฉพาะกับไคลเอนต์ C++ และการใช้งานเท่านั้น อุปกรณ์ที่ใช้ Android เวอร์ชันก่อนหน้าไม่มี HAL ที่เขียนด้วยภาษา Java ดังนั้น Java HAL จึงถูกผูกไว้โดยธรรมชาติ

เมื่อคอมไพล์ไฟล์ .hal แล้ว hidl-gen จะสร้างไฟล์ส่วนหัว passthrough พิเศษ BsFoo.h นอกเหนือจากส่วนหัวที่ใช้สำหรับการสื่อสารของ Binder; ส่วนหัวนี้กำหนดฟังก์ชันที่จะ dlopen ed เนื่องจาก HAL แบบพาสทรูทำงานในกระบวนการเดียวกับที่ถูกเรียกใช้ ในกรณีส่วนใหญ่ วิธีการส่งผ่านจะถูกเรียกใช้โดยการเรียกใช้ฟังก์ชันโดยตรง (เธรดเดียวกัน) วิธี oneway ทำงานในเธรดของตัวเองเนื่องจากไม่ได้ตั้งใจให้รอให้ HAL ประมวลผล (ซึ่งหมายความว่า HAL ใด ๆ ที่ใช้วิธีการ oneway ในโหมดส่งผ่านจะต้องปลอดภัยสำหรับเธรด)

เมื่อระบุ IFoo.hal แล้ว BsFoo.h จะล้อมเมธอดที่สร้างโดย HIDL เพื่อให้มีคุณสมบัติเพิ่มเติม (เช่น การทำธุรกรรม oneway ที่รันในเธรดอื่น) ไฟล์นี้คล้ายกับ BpFoo.h แต่แทนที่จะส่งผ่านการโทร IPC โดยใช้เครื่องผูก ฟังก์ชันที่ต้องการจะถูกเรียกใช้โดยตรง การใช้งาน HAL ในอนาคต อาจมี การใช้งานหลายอย่าง เช่น FooFast HAL และ FooAccurate HAL ในกรณีเช่นนี้ ไฟล์สำหรับการใช้งานเพิ่มเติมแต่ละรายการจะถูกสร้างขึ้น (เช่น PTFooFast.cpp และ PTFooAccurate.cpp )

Binderizing passthrough HALs

คุณสามารถรวมการใช้งาน HAL ที่รองรับโหมดส่งผ่านได้ ด้วยอินเทอร์เฟซ HAL abcd@MN::IFoo สองแพ็คเกจจะถูกสร้างขึ้น:

  • abcd@MN::IFoo-impl ประกอบด้วยการใช้งาน HAL และแสดงฟังก์ชัน IFoo* HIDL_FETCH_IFoo(const char* name) บนอุปกรณ์รุ่นเก่า แพ็คเกจนี้จะ dlopen ed และการใช้งานจะถูกสร้างอินสแตนซ์โดยใช้ HIDL_FETCH_IFoo คุณสามารถสร้างโค้ดฐานได้โดยใช้ hidl-gen และ -Lc++-impl และ -Landroidbp-impl
  • abcd@MN::IFoo-service เปิด passthrough HAL และลงทะเบียนตัวเองเป็นบริการแบบผูกมัด ซึ่งช่วยให้สามารถนำ HAL เดียวกันไปใช้ทั้งแบบส่งผ่านและแบบผูกได้

ด้วยประเภท IFoo คุณสามารถเรียก sp<IFoo> IFoo::getService(string name, bool getStub) เพื่อเข้าถึงอินสแตนซ์ของ IFoo หาก getStub เป็นจริง getService จะพยายามเปิด HAL ในโหมดส่งผ่านเท่านั้น หาก getStub เป็นเท็จ getService จะพยายามค้นหาบริการที่ถูกผูกไว้ หากล้มเหลว ระบบจะพยายามค้นหาบริการส่งผ่าน ไม่ควรใช้พารามิเตอร์ getStub ยกเว้นใน defaultPassthroughServiceImplementation (อุปกรณ์ที่เปิดตัวด้วย Android O เป็นอุปกรณ์ที่มีการผูกข้อมูลอย่างสมบูรณ์ ดังนั้นจึงไม่อนุญาตให้เปิดบริการในโหมดส่งผ่าน)

ไวยากรณ์ HIDL

ตามการออกแบบ ภาษา HIDL จะคล้ายกับ C (แต่ไม่ได้ใช้ตัวประมวลผลล่วงหน้า C) เครื่องหมายวรรคตอนทั้งหมดที่ไม่ได้อธิบายไว้ด้านล่าง (นอกเหนือจากการใช้ = และ | อย่างชัดเจน) เป็นส่วนหนึ่งของไวยากรณ์

หมายเหตุ: สำหรับรายละเอียดเกี่ยวกับรูปแบบโค้ด HIDL โปรดดู คำแนะนำเกี่ยวกับสไตล์โค้ด

  • /** */ ระบุความคิดเห็นเกี่ยวกับเอกสาร สิ่งเหล่านี้สามารถใช้ได้เฉพาะกับการประกาศประเภท วิธีการ ฟิลด์ และค่าแจงนับเท่านั้น
  • /* */ ระบุความคิดเห็นหลายบรรทัด
  • // ระบุความคิดเห็นที่ท้ายบรรทัด นอกเหนือจาก // การขึ้นบรรทัดใหม่จะเหมือนกับช่องว่างอื่นๆ
  • ในตัวอย่างไวยากรณ์ด้านล่าง ข้อความจาก // ถึงท้ายบรรทัดไม่ได้เป็นส่วนหนึ่งของไวยากรณ์ แต่เป็นความคิดเห็นเกี่ยวกับไวยากรณ์แทน
  • [empty] หมายความว่าคำนั้นอาจว่างเปล่า
  • ? การทำตามตัวอักษรหรือคำศัพท์หมายความว่าเป็นทางเลือก
  • ... ระบุลำดับที่มีรายการเป็นศูนย์หรือมากกว่านั้นโดยมีเครื่องหมายวรรคตอนคั่นตามที่ระบุ ไม่มีข้อโต้แย้งที่แปรผันใน HIDL
  • เครื่องหมายจุลภาคแยกองค์ประกอบลำดับ
  • เครื่องหมายอัฒภาคจะสิ้นสุดแต่ละองค์ประกอบ รวมถึงองค์ประกอบสุดท้ายด้วย
  • UPPERCASE ไม่ใช่เทอร์มินัล
  • italics เป็นตระกูลโทเค็น เช่น integer หรือ identifier (กฎการแยกวิเคราะห์ C มาตรฐาน)
  • constexpr เป็นนิพจน์คงที่สไตล์ C (เช่น 1 + 1 และ 1L << 3 )
  • import_name เป็นชื่อแพ็กเกจหรืออินเทอร์เฟซ ที่มีคุณสมบัติตามที่อธิบายไว้ใน HIDL Versioning
  • words ตัวพิมพ์เล็กเป็นสัญลักษณ์ที่แท้จริง

ตัวอย่าง:

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