HIDL

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

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

HIDL 旨在用於進程間通信 (IPC)。使用 HDL 創建的 HALS 稱為綁定 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 模型。另請參閱綁定
服務器實現接口方法的進程。另請參閱直通
運輸在服務器和客戶端之間移動數據的 HIDL 基礎架構。
版本包的版本。由兩個整數組成,主要的和次要的。次要版本增量可能會添加(但不會更改)類型和方法。

HIDL 設計

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

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