HIDL

HAL インターフェース定義言語(HIDL、「ハイド - エル」と発音)は、HAL とそのユーザー間のインターフェースを指定するインターフェース記述言語(IDL)です。これにより、インターフェースとパッケージに収集される型とメソッド呼び出しを指定できます。より広義には、HIDL は独立してコンパイルできるコードベース間の通信システムです。

HIDL は、プロセス間通信(IPC)に使用するためのものです。 プロセス間通信は、バインドと呼ばれます。プロセスにリンクする必要があるライブラリには、パススルー モードも使用できます(Java ではサポートされていません)。

HIDL では、パッケージに収集される(クラスに似た)インターフェースで構成されるデータ構造とメソッド シグネチャを指定します。HIDL の構文は、C++ や Java のプログラマーにとってなじみやすいものですが、一連のキーワードが異なります。HIDL では、Java スタイルのアノテーションも使用します。

HIDL の設計

HIDL の目的は、HAL を再作成せずにフレームワークを交換できるようにすることです。HAL はベンダーや SOC メーカーにより作成され、デバイスの /vendor パーティションに配置されます。これにより、フレームワークをそのパーティション内で、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
    

用語

このセクションでは、次の HIDL 関連の用語を使用します。

バインド プロセス間のリモート プロシージャー コールで HIDL が使用され、バインダーのようなメカニズムで実装されていることを示します。パススルーもご覧ください
コールバック、非同期 HAL ユーザーが提供し、(HIDL メソッドを介して)HAL に渡し、HAL がデータを返すために随時呼び出すインターフェース。
コールバック、同期 サーバーの HIDL メソッド実装からクライアントにデータを返します。 void または単一のプリミティブ値を返すメソッドでは使用されません。
クライアント 特定のインターフェースのメソッドを呼び出すプロセス。HAL またはフレームワーク プロセスは、あるインターフェースのクライアントと、別のインターフェースのサーバー間で行うこともできます。パススルーもご覧ください
拡張 メソッドや型を別のインターフェースに追加するインターフェースを示します。 1 つのインターフェースは、別の 1 つのインターフェースのみを拡張できます。同じパッケージ名でのマイナー バージョン インクリメントや、古いパッケージ上でビルドする新しいパッケージ(ベンダー拡張など)に使用できます。
生成 クライアントに値を返すインターフェース メソッドを示します。1 つの非プリミティブ値または複数の値を返すために、同期コールバック関数が生成されます。
インターフェース メソッドと型のコレクション。C++ または Java のクラスに翻訳されています。インターフェース内のメソッドはすべて、同じ方向に呼び出されます。クライアント プロセスは、サーバー プロセスによって実装されたメソッドを呼び出します。
片道 HIDL メソッドに適用される場合、メソッドは値を返さず、ブロックしないことを示します。
パッケージ バージョンを共有するインターフェースとデータ型のコレクション。
パススルー サーバーが共有ライブラリである HIDL のモード。クライアントによって dlopen されます。パススルー モードでは、クライアントとサーバーは同じプロセスですが、コードベースは別です。レガシー コードベースを HIDL モデルに移行するためにのみ使用します。 バインドもご覧ください
サーバー インターフェースのメソッドを実装するプロセス。パススルーもご覧ください
トランスポート サーバーとクライアント間でデータを移動する HIDL インフラストラクチャ。
バージョン パッケージのバージョン。major と minor の 2 つの整数型で構成されます。マイナー バージョンのインクリメントでは型とメソッドを追加できますが、変更はできません。