高強度間歇訓練

HAL 介面定義語言或 HIDL 是一種介面說明語言 (IDL),用於指定 HAL 和使用者之間的介面。HIDL 可讓您指定在介面和套件中收集的類型和方法呼叫。更廣泛來說,HIDL 是一種系統,用於在可獨立編譯的程式碼集之間進行通訊。

HIDL 的用途是供處理序間通訊 (IPC) 使用,使用 HDL 建立的 HAL 稱為繫結化 HAL,可以透過繫結器處理序間通訊 (IPC) 呼叫與其他架構層通訊。繫結化 HAL 的執行程序與使用繫結的 HAL 不同,如果是必須連結至程序的程式庫,您也可以使用直通模式 (Java 不支援)。

HIDL 會指定資料結構和方法簽章,這些簽章的分類方式是以介面 (類似於類別) 的形式歸類,並收集至套件中。C++ 和 Java 程式設計師雖然對 HIDL 的語法很熟悉,但同時使用一組不同的關鍵字。HIDL 也會使用 Java 樣式的註解。

術語

本節使用下列 HIDL 相關字詞:

繫結 表示將 HIDL 用於程序之間的遠端程序呼叫,並透過類似 Binder 的機制實作。另請參閱快速導入
回呼、非同步回呼 由 HAL 使用者提供的介面、傳遞至 HAL (使用 HIDL 方法),並由 HAL 呼叫以便隨時傳回資料。
回呼、同步 將伺服器的 HIDL 方法實作所提供的資料傳回用戶端。 不適用於傳回空白或單一原始值的方法。
用戶端 呼叫特定介面方法的程序。HAL 或 Android 架構程序可能是不同介面的用戶端和另一個介面的伺服器。另請參閱快速導入一節。
延伸 表示可將方法和/或類型新增至其他介面的介面。一個介面只能擴充另一個介面。可用於在相同的套件名稱中增加次要版本,或是用於在舊版套件上建構的新套件 (例如廠商擴充功能)。
產生 指出可將值傳回用戶端的介面方法。如要傳回一個非原始值或多個值,系統會產生同步回呼函式。
介面 方法和類型的集合。在 C++ 或 Java 中轉譯為類別。系統會按照相同方向呼叫介面中的所有方法:用戶端程序叫用由伺服器程序實作的方法。
單程 套用至 HIDL 方法時,表示該方法未傳回任何值,且不會封鎖。
包裹 共用版本的介面和資料類型集合。
直通 伺服器是一個共用程式庫的 HIDL 模式,由用戶端dlopen使用。在直通模式下,用戶端和伺服器的程序相同,但程式碼集分開。僅適用於將舊版程式碼集帶入 HIDL 模型。另請參閱繫結機制
伺服器 實作介面方法的程序。另請參閱快速導入一節。
交通 在伺服器和用戶端之間移動資料的 HIDL 基礎架構。
version 套件的版本。由兩個整數組成,即主要和次要。次要版本增量可能會新增類型和方法,但無法變更類型和方法。

HIDL 設計

HIDL 的目標在於 Android 架構可以取代,不必重建 HAL。HAL 將由廠商或 SOC 製造商建構,並在裝置上納入 /vendor 分區,啟用 Android 架構的專屬分區,以改用 OTA 而無須重新編譯 HAL。

HIDL 設計可平衡下列顧慮:

  • 互通性。在程序之間建立穩定可互通的介面,這些介面可以使用各種架構、工具鍊和建構設定進行編譯。HIDL 介面已建立版本,發布後無法變更。
  • 效率。HIDL 會試著減少複製作業的數量。HIDL 定義的資料會以 C++ 標準版面配置資料結構的形式傳遞到 C++ 程式碼,且無須解壓縮即可使用。HIDL 也提供共用記憶體介面,而且由於 RPC 本身較為緩慢,因此 HIDL 支援在不使用 RPC 呼叫的情況下轉移資料的方法:共用記憶體和快速訊息佇列 (FMQ)。
  • 符合直覺。HIDL 透過只針對 RPC 使用 in 參數,避免記憶體擁有權嚴重的問題 (請參閱 Android 介面定義語言 (AIDL));無法透過回呼函式傳回無法有效傳回的值。無論是將資料傳遞到 HIDL 進行轉移,還是從 HIDL 接收資料,都不會改變資料的擁有權,因此擁有權一律會保留在呼叫函式中。資料只需保留呼叫函式的持續時間,且可能在呼叫函式傳回後立即刪除。

使用直通模式

如要將搭載舊版 Android 的裝置更新至 Android O,可以在新的 HIDL 介面中納入傳統 (和舊版) HAL,以在繫結和相同程序 (直通) 模式下提供 HAL。HAL 和 Android 架構都能公開這個包裝。

直通模式僅適用於 C++ 用戶端和實作。搭載舊版 Android 的裝置不會以 Java 編寫 HAL,因此 Java HAL 本質上會形成繫結。

在編譯 .hal 檔案時,除了用於繫結器通訊的標頭以外,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 介面 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,並將自身註冊為繫結器化服務,使相同的 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