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.hal
, BsFoo.h
包裝 HIDL 生成的方法以提供附加功能(例如使oneway
事務在另一個線程中運行)。該文件類似於BpFoo.h
,但不是使用 binder 傳遞調用 IPC,而是直接調用所需的函數。 HAL 的未來實現可能會提供多種實現,例如 FooFast HAL 和 FooAccurate HAL。在這種情況下,將為每個附加實現創建一個文件(例如, PTFooFast.cpp
和PTFooAccurate.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
是一個令牌族,例如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
術語
本部分使用以下 HIDL 相關術語:
粘合的 | 指示 HIDL 用於進程之間的遠程過程調用,通過類似 Binder 的機制實現。另請參閱直通。 |
---|---|
回調,異步 | 由 HAL 用戶提供的接口,傳遞給 HAL(通過 HIDL 方法),並由 HAL 調用以隨時返回數據。 |
回調,同步 | 將數據從服務器的 HIDL 方法實現返回到客戶端。不用於返回 void 或單個原始值的方法。 |
客戶 | 調用特定接口的方法的進程。 HAL 或框架進程可能是一個接口的客戶端和另一個接口的服務器。另請參閱直通。 |
延伸 | 表示將方法和/或類型添加到另一個接口的接口。一個接口只能擴展另一個接口。可用於相同包名稱中的次要版本增量或用於新包(例如供應商擴展)以在舊包上構建。 |
生成 | 指示向客戶端返回值的接口方法。要返回一個非原始值或多個值,會生成一個同步回調函數。 |
界面 | 方法和類型的集合。翻譯成 C++ 或 Java 中的類。接口中的所有方法都以相同的方向調用:客戶端進程調用由服務器進程實現的方法。 |
單程 | 應用於 HIDL 方法時,指示該方法不返回任何值且不阻塞。 |
包裹 | 共享一個版本的接口和數據類型的集合。 |
直通 | HIDL 模式,其中服務器是共享庫,由客戶端dlopen 編輯。在直通模式下,客戶端和服務器是相同的進程,但代碼庫不同。僅用於將舊代碼庫引入 HIDL 模型。另請參閱綁定。 |
服務器 | 實現接口方法的進程。另請參閱直通。 |
運輸 | 在服務器和客戶端之間移動數據的 HIDL 基礎架構。 |
版本 | 包的版本。由兩個整數組成,主要的和次要的。次要版本增量可能會添加(但不會更改)類型和方法。 |