HIDL

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.cppPTFooAccurate.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 は、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