HIDL

HAL 接口定義語言或 HIDL(發音為“hide-l”)是一種接口描述語言 (IDL),用於指定 HAL 與其用戶之間的接口。它允許指定類型和方法調用,收集到接口和包中。更廣泛地說,HIDL 是一種用於在可以獨立編譯的代碼庫之間進行通信的系統。從 Android 10 開始,HIDL 已被棄用,Android 正在遷移以在任何地方使用 AIDL。

HIDL 旨在用於進程間通信 (IPC)。進程之間的通信稱為Binderized 。對於必須鏈接到進程的庫,也可以使用直通模式(Java 中不支持)。

HIDL 指定數據結構和方法簽名,這些數據結構和方法簽名以接口(類似於類)進行組織,這些接口被收集到包中。 HIDL 的語法對於 C++ 和 Java 程序員來說看起來很熟悉,儘管有一組不同的關鍵字。 HIDL 還使用 Java 樣式的註釋。

HIDL 設計

HIDL 的目標是無需重新構建 HAL 即可替換框架。 HAL 將由供應商或 SOC 製造商構建,並放在設備上的/vendor分區中,從而使框架在其自己的分區中可以被 OTA 替換,而無需重新編譯 HAL。

HIDL 設計平衡了以下問題:

  • 互操作性。在可以使用各種架構、工具鍊和構建配置編譯的進程之間創建可靠的可互操作接口。 HIDL 接口是版本化的,發布後無法更改。
  • 效率。 HIDL 會盡量減少複製操作的數量。 HIDL 定義的數據以 C++ 標準佈局數據結構交付給 C++ 代碼,無需解包即可使用。 HIDL 還提供共享內存接口,並且由於 RPC 本身有些慢,HIDL 支持兩種無需使用 RPC 調用即可傳輸數據的方法:共享內存和快速消息隊列 (FMQ)。
  • 直觀。 HIDL 通過僅in參數中使用 RPC 來避免內存所有權的棘手問題(請參閱Android 接口定義語言 (AIDL) );無法從方法有效返回的值通過回調函數返回。將數據傳遞到 HIDL 進行傳輸或從 HIDL 接收數據都不會更改數據的所有權 - 所有權始終由調用函數保留。數據只需要在被調用函數的持續時間內持續存在,並且可以在被調用函數返回後立即銷毀。

使用直通模式

要將運行早期版本 Android 的設備更新到 Android O,您可以將傳統(和舊版)HAL 包裝在一個新的 HIDL 接口中,該接口以綁定和相同進程(直通)模式為 HAL 提供服務。這種包裝對 HAL 和 Android 框架都是透明的。

直通模式僅適用於 C++ 客戶端和實現。運行早期 Android 版本的設備沒有用 Java 編寫的 HAL,因此 Java HAL 本質上是綁定的。

編譯.hal文件時, hidl-gen除了用於 binder 通信的頭文件外,還會產生一個額外的 passthrough 頭文件BsFoo.h ;此標頭定義要dlopen編輯的函數。由於直通 HAL 在調用它們的同一進程中運行,因此在大多數情況下,直通方法由直接函數調用(同一線程)調用。 oneway方法在它們自己的線程中運行,因為它們不打算等待 HAL 處理它們(這意味著在直通模式下使用oneway方法的任何 HAL 都必須是線程安全的)。

給定IFoo.halBsFoo.h包裝 HIDL 生成的方法以提供附加功能(例如使oneway事務在另一個線程中運行)。該文件類似於BpFoo.h ,但不是使用 binder 傳遞調用 IPC,而是直接調用所需的函數。 HAL 的未來實現可能會提供多種實現,例如 FooFast HAL 和 FooAccurate HAL。在這種情況下,將為每個附加實現創建一個文件(例如, PTFooFast.cppPTFooAccurate.cpp )。

綁定直通 HAL

您可以綁定支持直通模式的 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 。打開直通 HAL 並將其自身註冊為綁定服務,使相同的 HAL 實現既可用作直通,也可用作綁定服務。

給定類型IFoo ,您可以調用sp<IFoo> IFoo::getService(string name, bool getStub)來訪問IFoo的實例。如果getStub為 true,則getService僅嘗試在直通模式下打開 HAL。如果getStub為 false, getService會嘗試查找綁定服務;如果失敗,它會嘗試查找直通服務。除非在defaultPassthroughServiceImplementation中,否則永遠不應使用getStub參數。 (使用 Android O 啟動的設備是完全綁定的設備,因此不允許以直通模式打開服務。)

HIDL 語法

按照設計,HIDL 語言類似於 C(但不使用 C 預處理器)。下面未描述的所有標點符號(除了明顯使用=| )都是語法的一部分。

注意:有關 HIDL 代碼樣式的詳細信息,請參閱代碼樣式指南

  • /** */表示文檔註釋。這些只能應用於類型、方法、字段和枚舉值聲明。
  • /* */表示多行註釋。
  • //表示對行尾的註釋。除了// ,換行符與任何其他空格相同。
  • 在下面的示例語法中,從//到行尾的文本不是語法的一部分,而是對語法的註釋。
  • [empty]表示該術語可能為空。
  • ?跟隨文字或術語意味著它是可選的。
  • ...表示包含零個或多個項目的序列,其中標點符號如所示。 HIDL 中沒有可變參數。
  • 逗號分隔序列元素。
  • 分號終止每個元素,包括最後一個元素。
  • UPPERCASE 是非終結符。
  • italics是一個令牌族,例如integeridentifier (標準 C 解析規則)。
  • constexpr是 C 風格的常量表達式(例如1 + 11L << 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

術語

本部分使用以下 HIDL 相關術語:

粘合的指示 HIDL 用於進程之間的遠程過程調用,通過類似 Binder 的機制實現。另請參閱直通
回調,異步由 HAL 用戶提供的接口,傳遞給 HAL(通過 HIDL 方法),並由 HAL 調用以隨時返回數據。
回調,同步將數據從服務器的 HIDL 方法實現返回到客戶端。不用於返回 void 或單個原始值的方法。
客戶調用特定接口的方法的進程。 HAL 或框架進程可能是一個接口的客戶端和另一個接口的服務器。另請參閱直通
延伸表示將方法和/或類型添加到另一個接口的接口。一個接口只能擴展另一個接口。可用於相同包名稱中的次要版本增量或用於新包(例如供應商擴展)以在舊包上構建。
生成指示向客戶端返回值的接口方法。要返回一個非原始值或多個值,會生成一個同步回調函數。
界面方法和類型的集合。翻譯成 C++ 或 Java 中的類。接口中的所有方法都以相同的方向調用:客戶端進程調用由服務器進程實現的方法。
單程應用於 HIDL 方法時,指示該方法不返回任何值且不阻塞。
包裹共享一個版本的接口和數據類型的集合。
直通HIDL 模式,其中服務器是共享庫,由客戶端dlopen編輯。在直通模式下,客戶端和服務器是相同的進程,但代碼庫不同。僅用於將舊代碼庫引入 HIDL 模型。另請參閱綁定
服務器實現接口方法的進程。另請參閱直通
運輸在服務器和客戶端之間移動數據的 HIDL 基礎架構。
版本包的版本。由兩個整數組成,主要的和次要的。次要版本增量可能會添加(但不會更改)類型和方法。