高強度間歇訓練

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.halBsFoo.h包裝 HIDL 產生的方法以提供其他功能(例如使oneway事務在另一個執行緒中運行)。該檔案類似於BpFoo.h ,但不是使用綁定器傳遞呼叫 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編輯,並使用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是一個標記族,例如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