HAL 介面定義語言或 HIDL 是一種介面描述語言 (IDL),用於指定 HAL 與其使用者之間的介面。 HIDL 允許指定類型和方法調用,收集到介面和套件中。更廣泛地說,HIDL 是一個用於在可以獨立編譯的程式碼庫之間進行通訊的系統。從 Android 10 開始,HIDL 已被棄用,Android 正在遷移為在所有地方使用AIDL 。
HIDL 旨在用於進程間通訊 (IPC)。使用 HDL 建立的 HAL 稱為綁定化 HAL,因為它們可以使用綁定器進程間通訊 (IPC) 呼叫與其他體系結構層進行通訊。 Binderized HAL 在與使用它們的客戶端不同的進程中運行。對於必須連結到進程的函式庫,也可以使用直通模式(Java 中不支援)。
HIDL 指定資料結構和方法簽名,這些資料結構和方法簽名被組織在收集到套件中的介面(類似於類別)中。 HIDL 的語法對於 C++ 和 Java 程式設計師來說看起來很熟悉,但有不同的關鍵字集。 HIDL 也使用 Java 風格的註解。
術語
本部分使用以下 HIDL 相關術語:
黏合劑化的 | 指示 HIDL 用於進程之間的遠端過程調用,透過類似 Binder 的機制實現。另請參見直通。 |
---|---|
回調,異步 | 由 HAL 使用者提供服務的接口,傳遞給 HAL(使用 HIDL 方法),並由 HAL 呼叫以隨時返回資料。 |
回調,同步 | 將資料從伺服器的 HIDL 方法實作傳回到客戶端。不用於傳回 void 或單一原始值的方法。 |
客戶 | 呼叫特定介面的方法的進程。 HAL 或 Android 框架進程可能是一個介面的客戶端和另一個介面的伺服器。另請參見直通。 |
延伸 | 指示向另一個介面新增方法和/或類型的介面。一個介面只能擴充另一個介面。可用於相同套件名稱中的次要版本增量,或用於在舊套件上建置的新套件(例如供應商擴充)。 |
產生 | 表示向客戶端傳回值的介面方法。為了傳回一個非原始值或多個值,會產生一個同步回呼函數。 |
介面 | 方法和類型的集合。翻譯成 C++ 或 Java 中的類別。介面中的所有方法都以同一方向呼叫:客戶端進程呼叫伺服器進程實作的方法。 |
單程 | 當應用於 HIDL 方法時,表示該方法不傳回任何值且不會阻塞。 |
包裹 | 共享版本的介面和資料類型的集合。 |
直通 | HIDL 模式,其中伺服器是共享庫,由客戶端dlopen 編輯。在直通模式下,客戶端和伺服器是相同的進程,但程式碼庫不同。僅用於將遺留程式碼庫引入 HIDL 模型。另請參見Binderized 。 |
伺服器 | 實作介面方法的進程。另請參見直通。 |
運輸 | 在伺服器和用戶端之間移動資料的 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.hal
, BsFoo.h
包裝 HIDL 產生的方法以提供其他功能(例如使oneway
事務在另一個執行緒中運行)。該檔案類似於BpFoo.h
,但不是使用綁定器傳遞呼叫 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
編輯,並使用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 中沒有可變參數。 - 逗號分隔序列元素。
- 分號終止每個元素,包括最後一個元素。
- 大寫是非終結符。
-
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