HIDL

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

HIDL 旨在用於進程間通信 (IPC)。使用 HDL 創建的 HAL 稱為綁定化 HAL,因為它們可以使用綁定進程間通信 (IPC) 調用與其他架構層通信。綁定 HAL 在與使用它們的客戶端不同的進程中運行。對於必須鏈接到進程的庫,也可以使用直通模式(Java 不支持)。

HIDL 指定數據結構和方法簽名,組織在收集到包中的接口(類似於類)中。 HIDL 的語法看起來對 C++ 和 Java 程序員來說很熟悉,但使用了一組不同的關鍵字。 HIDL 還使用 Java 風格的註解。

術語

本節使用以下與 HIDL 相關的術語:

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

HIDL設計

HIDL 的目標是無需重建 HAL 即可替換 Android 框架。 HAL 將由供應商或 SOC 製造商構建,並放在設備上的/vendor分區中,從而使 Android 框架在其自己的分區中可以被 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文件時,除了用於 binder 通信的標頭之外, hidl-gen生成一個額外的直通標頭文件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為真,則getService僅嘗試以直通模式打開 HAL。如果getStub為假,則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