HAL インターフェース定義言語(HIDL)は、HAL とそのユーザー間のインターフェースを規定するインターフェース記述言語(IDL)です。HIDL を使って、インターフェースとパッケージに収集される型とメソッド呼び出しを指定できます。広義では、独立してコンパイルできるコードベース間の通信システムを指します。
HIDL は、プロセス間通信(IPC)に使用することを目的としています。HDL で作成された HAL は、バインダ化された HAL と呼ばれます。バインダのプロセス間通信(IPC)呼び出しを使用して他のアーキテクチャ レイヤと通信できるからです。バインダ化された HAL は、それを使用するクライアントとは別のプロセスで実行されます。プロセスにリンクする必要があるライブラリには、パススルー モードも使用できます(Java ではサポートされていません)。
HIDL では、パッケージに収集される(クラスに似た)インターフェースで構成されるデータ構造とメソッド シグネチャを指定します。HIDL の構文は、C++ や Java のプログラマーにとってなじみやすいものですが、キーワードのセットが異なります。HIDL では、Java スタイルのアノテーションも使用します。
用語
このセクションでは、次の HIDL 関連の用語を使用します。
バインダ化 | プロセス間のリモート プロシージャ コールで HIDL が使用され、バインダに似たメカニズムで実装されていることを示します。パススルーも参照してください。 |
---|---|
コールバック、非同期 | HAL ユーザーが提供し、(HIDL メソッドを使用して)HAL に渡され、データを返すために HAL によって随時呼び出されるインターフェース。 |
コールバック、同期 | サーバーの HIDL メソッド実装からクライアントにデータを返します。 void または単一のプリミティブ値を返すメソッドでは使用されません。 |
クライアント | 特定のインターフェースのメソッドを呼び出すプロセス。HAL または Android フレームワークのプロセスは、あるインターフェースのクライアントと別のインターフェースのサーバー間のプロセスであってもかまいません。パススルーも参照してください。 |
拡張 | メソッドおよび / または型を別のインターフェースに追加するインターフェースを示します。1 つのインターフェースは、別の 1 つのインターフェースのみを拡張できます。同じパッケージ名でのマイナー バージョン インクリメントや、古いパッケージ上でビルドする新しいパッケージ(ベンダー拡張など)に使用できます。 |
生成 | クライアントに値を返すインターフェース メソッドを示します。1 つの非プリミティブ値または複数の値を返すために、同期コールバック関数が生成されます。 |
インターフェース | メソッドと型のコレクション。C++ または Java のクラスに変換されます。1 つのインターフェース内のメソッドはすべて同じ方向に呼び出されます。クライアント プロセスは、サーバー プロセスによって実装されたメソッドを呼び出します。 |
一方向 | HIDL メソッドについて使用される場合は、メソッドが値を返さず、ブロックしないことを意味します。 |
パッケージ | バージョンを共有するインターフェースとデータ型のコレクション。 |
パススルー | サーバーが共有ライブラリである HIDL のモード。クライアントによって dlopen されます。パススルー モードでは、クライアントとサーバーは同じプロセスですが、コードベースは別です。レガシー コードベースを HIDL モデルに移行するためにのみ使用します。
バインダ化も参照してください。 |
サーバー | インターフェースのメソッドを実装するプロセス。パススルーも参照してください。 |
トランスポート | サーバーとクライアント間でデータを移動する HIDL インフラストラクチャ。 |
バージョン | パッケージのバージョン。メジャーとマイナーを表す 2 つの整数で構成されます。マイナー バージョン インクリメントでは、型とメソッドを追加できますが、変更はできません。 |
HIDL の設計
HIDL の目的は、HAL を再構築せずに Android フレームワークを交換できるようにすることです。HAL はベンダーまたは SOC メーカーによって作成され、デバイスの /vendor
パーティションに配置されます。これにより、Android フレームワークをそのパーティション内で、HAL を再コンパイルすることなく OTA で交換できます。
HIDL の設計では、次の点のバランスが考慮されています。
- 相互運用性。さまざまなアーキテクチャ、ツールチェーン、ビルド構成でコンパイルできる、信頼性の高い相互運用可能なプロセス間のインターフェースを作成します。HIDL インターフェースはバージョニングされており、公開後は変更できません。
- 効率性。HIDL では、コピー オペレーションの回数を最小限に抑えようとしています。HIDL 定義のデータは、解凍しないで使用できる C++ 標準レイアウト データ構造の C++ コードに渡されます。HIDL は共有メモリ インターフェースも提供します。RPC は本質的にやや遅いため、HIDL は RPC 呼び出しを使用せずにデータを転送する方法として、共有メモリと高速メッセージキュー(FMQ)の 2 つをサポートしています。
- 直感的。HIDL は、RPC に
in
パラメータのみを使用することで、メモリのオーナー権限に関する厄介な問題を回避します(Android インターフェース定義言語(AIDL)をご覧ください)。メソッドから効率的に返されない値は、コールバック関数を介して返されます。転送のために HIDL にデータを渡しても、HIDL からデータを受け取っても、データのオーナー権限は変更されません。つまり、オーナー権限は常に呼び出し元の関数が保持します。データは、呼び出された関数が存続する間のみ保持すればよく、呼び出された関数が結果を返したらすぐに破棄できます。
パススルー モードの使用
以前のバージョンの Android を搭載したデバイスを Android O にアップデートするには、バインダ化モードと same-process(パススルー)モードで HAL を提供する新しい HIDL インターフェースで、両方の伝統型 HAL(およびレガシー HAL)をラップします。このラップは、HAL と Android フレームワークの両方に対して透過的です。
パススルー モードは、C++ のクライアントと実装でのみ使用できます。以前のバージョンの Android を搭載したデバイスには Java で記述された HAL がないため、Java HAL は本質的にバインダ化されています。
パススルー ヘッダー ファイル
.hal
ファイルがコンパイルされると、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 実装をバインダ化できます。a.b.c.d@M.N::IFoo
という HAL インターフェースの場合、次の 2 つのパッケージが作成されます。
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
はバインダ化されたサービスを見つけようとします。失敗すると、パススルー サービスの検索を試みます。getStub
パラメータは、defaultPassthroughServiceImplementation
以外では使用しないでください(Android O を搭載したデバイスは完全にバインダ化されているため、パススルー モードでサービスを開くことはできません)。
HIDL の文法
設計の点では、HIDL 言語は C に似ています(ただし、C プリプロセッサは使用しません)。以下で説明されていないすべての区切り記号は、(=
と |
の明白な使用を除いて)文法の一部です。
注: HIDL コードスタイルの詳細については、コードスタイル ガイドをご覧ください。
/** */
は、ドキュメント用のコメントを示します。型、メソッド、フィールド、列挙値の宣言にのみ適用できます。/* */
は、複数行のコメントを示します。//
は、行末までのコメントを示します。//
を除いて、改行は他の空白文字と同じです。- 下記の文法の例では、
//
から行末までのテキストは、文法の一部ではなく、文法についてのコメントです。 [empty]
は、用語が空であることを意味します。- リテラルまたは用語の後に続く
?
は、それが省略可能であることを意味します。 ...
は、0 個以上の項目を含むシーケンスを、区切り記号を用いて示します。HIDL には可変個引数がありません。- カンマはシーケンス要素を区切ります。
- セミコロンは、最後の要素を含め、各要素を終了します。
- UPPERCASE は非終端記号です。
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