แบบ HIDL

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

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

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

คำศัพท์

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

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

รูปแบบ HIDL

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

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

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

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

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

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

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

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

การเชื่อมโยง HAL ของ Passthrough

คุณสามารถรวมการติดตั้งใช้งาน 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จะได้รับการdlopenและมีการยืนยันการใช้งานโดยใช้ HIDL_FETCH_IFoo คุณสร้างโค้ดพื้นฐานได้โดยใช้ hidl-gen และ -Lc++-impl และ -Landroidbp-impl
  • a.b.c.d@M.N::IFoo-service เปิด Passthrough HAL และลงทะเบียนตัวเองเป็นบริการรวมตัวที่เชื่อมโยง ทำให้สามารถใช้งาน HAL เดียวกันเพื่อใช้เป็นทั้ง Passthrough และ Binderized

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

ไวยากรณ์ HIDL

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

หมายเหตุ: ดูรายละเอียดเกี่ยวกับรูปแบบโค้ด HIDL ได้ที่ Code Style Guide

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