HIDL
HAL 接口定義語言(簡稱 HIDL,發音為“hide-l”)是用於指定 HAL 和其用戶之間的接口的一種接口描述語言 (IDL)。HIDL 允許指定類型和方法調用(會匯集到接口和軟件包中)。從更廣泛的意義上來說,HIDL 是用於在可以獨立編譯的代碼庫之間進行通信的系統。
HIDL 旨在用於進程間通信 (IPC)。進程之間的通信經過 Binder 化。對於必須與進程相關聯的代碼庫,還可以使用直通模式(在 Java 中不受支持)。
HIDL 可指定數據結構和方法簽名,這些內容會整理歸類到接口(與類相似)中,而接口會匯集到軟件包中。盡管 HIDL 具有一系列不同的關鍵字,但 C++ 和 Java 程序員對 HIDL 的語法並不陌生。此外,HIDL 還使用 Java 樣式的注釋。
HIDL 設計
HIDL 的目標是,框架可以在無需重新構建 HAL 的情況下進行替換。HAL 將由供應商或 SOC 制造商構建,放置在設備的 /vendor
分區中,這樣一來,框架就可以在其自己的分區中通過 OTA 進行替換,而無需重新編譯 HAL。
HIDL 設計在以下方面之間保持了平衡:
- 互操作性。在可以使用各種架構、工具鏈和編譯配置來編譯的進程之間創建可互操作的可靠接口。HIDL 接口是分版本的,發布后不得再進行更改。
- 效率。HIDL 會嘗試盡可能減少復制操作的次數。HIDL 定義的數據以 C++ 標准布局數據結構傳遞至 C++ 代碼,無需解壓,可直接使用。此外,HIDL 還提供共享內存接口;由於 RPC 本身有點慢,因此 HIDL 支持兩種無需使用 RPC 調用的數據傳輸方法:共享內存和快速消息隊列 (FMQ)。
- 直觀。通過僅針對 RPC 使用
in
參數,HIDL 避開了內存所有權這一棘手問題(請參閱 Android 接口定義語言 (AIDL));無法從方法高效返回的值將通過回調函數返回。無論是將數據傳遞到 HIDL 中以進行傳輸,還是從 HIDL 接收數據,都不會改變數據的所有權,也就是說,數據所有權始終屬於調用函數。數據僅需要在函數被調用期間保留,可在被調用的函數返回數據后立即清除。
使用直通模式
要將運行早期版本的 Android 的設備更新為使用 Android O,您可以將慣用的(和舊版)HAL 封裝在一個新 HIDL 接口中,該接口將在綁定式模式和同進程(直通)模式提供 HAL。這種封裝對於 HAL 和 Android 框架來說都是透明的。
直通模式僅適用於 C++ 客戶端和實現。運行早期版本的 Android 的設備沒有用 Java 編寫的 HAL,因此 Java HAL 自然而然經過 Binder 化。
直通式標頭文件
編譯 .hal
文件時,除了用於 Binder 通信的標頭之外,hidl-gen
還會生成一個額外的直通標頭文件 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
)。
Binder 化直通式 HAL
您可以將支持直通模式的 HAL 實現 Binder 化。如果有一個 HAL 接口 a.b.c.d@M.N::IFoo
,系統會創建兩個軟件包:
a.b.c.d@M.N::IFoo-impl
。包含 HAL 的實現,並暴露函數IFoo* HIDL_FETCH_IFoo(const char* name)
。在舊版設備上,此軟件包經過dlopen
處理,且實現使用HIDL_FETCH_IFoo
進行了實例化。您可以使用hidl-gen
和-Lc++-impl
以及-Landroidbp-impl
來生成基礎代碼。a.b.c.d@M.N::IFoo-service
。打開直通式 HAL,並將其自身注冊為 Binder 化服務,從而使同一 HAL 實現能夠同時以直通模式和 Binder 化模式使用。
如果有一個 IFoo
,您可以調用 sp<IFoo> IFoo::getService(string name, bool getStub)
,以獲取對 IFoo
實例的訪問權限。如果 getStub
為 True,則 getService
會嘗試僅在直通模式下打開 HAL。如果 getStub
為 False,則 getService
會嘗試找到 Binder 化服務;如果未找到,則它會嘗試找到直通式服務。除了在 defaultPassthroughServiceImplementation
中,其余情況一律不得使用 getStub
參數。(搭載 Android O 的設備是完全 Binder 化的設備,因此不得在直通模式下打開服務。)
HIDL 語法
根據設計,HIDL 語言與 C 語言類似(但前者不使用 C 預處理器)。下面未描述的所有標點符號(用途明顯的 =
和 |
除外)都是語法的一部分。
注意:有關 HIDL 代碼樣式的詳細信息,請參閱代碼樣式指南。
/** */
表示文檔注釋。此樣式只能應用於類型、方法、字段和枚舉值聲明。/* */
表示多行注釋。//
表示注釋一直持續到行結束。除了//
,換行符與任何其他空白一樣。- 在以下示例語法中,從
//
到行結束的文本不是語法的一部分,而是對語法的注釋。 [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;