このセクションでは、HIDL のデータ型について説明します。実装の詳細については、HIDL C++(C++ 実装の場合)または HIDL Java(Java 実装の場合)をご覧ください。
C++ との類似点は次のとおりです。
structs
は C++ 構文を使用し、unions
はデフォルトで C++ 構文をサポートしています。両方に名前を付ける必要があります。匿名の構造体と共用体はサポートされていません。- typedef は(C++ であるため)HIDL で使用できます。
- C++ スタイルのコメントを使用できます。それらは生成されたヘッダー ファイルにコピーされます。
Java との類似点は次のとおりです。
- HIDL では、ファイルごとに Java スタイルの名前空間(
android.hardware.
で始まる必要がある)が定義されます。生成される C++ 名前空間は::android::hardware::…
です。 - ファイルのすべての定義は、Java スタイルの
interface
ラッパーに含まれます。 - HIDL 配列宣言は、C++ スタイルではなく Java スタイルに従います。次に例を示します。
struct Point { int32_t x; int32_t y; }; Point[3] triangle; // sized array
- コメントは javadoc 形式と同様です。
データ表現
標準レイアウト(plain-old-data 型の要件のサブセット)で構成された struct
または union
は、生成された C++ コードで一貫したメモリ レイアウトを維持し、struct
および union
メンバーに明示的な配置属性を適用します。
プリミティブ HIDL 型と、(常にプリミティブ型から派生する)enum
および bitfield
型は、標準の C++ 型(cstdint の std::uint32_t
など)にマッピングされます。
Java は符号なし型をサポートしていないため、符号なし HIDL 型は 対応する署名付き Java 型です。STRUCT は Java クラスにマッピングされます。 arrays は Java 配列にマッピングされます。union は現在サポートされていません 説明します。文字列は内部的には UTF8 として保存されます。Java は UTF16 文字列のみ。Java 実装との間で送受信される文字列値 文字セットは変換されないため、再翻訳時に同一にならない場合があります。 常にスムーズにマッピングされます。
C++ で IPC を介して受信したデータは const
としてマークされ、関数呼び出しの存続期間のみ保持される読み取り専用メモリ内に配置されます。データ
Java の IPC 経由で受信したデータは、すでに Java オブジェクトにコピーされているため、
追加のコピーなしで保持される(および変更可能)。
アノテーション
Java スタイルのアノテーションは型宣言に追加できます。アノテーションは、HIDL コンパイラのベンダー テストスイート(VTS)バックエンドによって解析されますが、解析されたアノテーションは実際には HIDL コンパイラにより認識されるわけではありません。解析された VTS アノテーションは、VTS コンパイラ(VTSC)によって処理されます。
アノテーションでは Java 構文を使用します。@annotation
または
@annotation(value)
または @annotation(id=value, id=value…)
ここで、value は定数式、文字列、または値のリストのいずれかです。
Java と同様に {}
内で使用します。同じ名前の複数のアノテーションを同じ項目に付加することができます。
前方宣言
HIDL では、構造体が前方宣言されていない場合があり、ユーザー定義の 自己参照データ型は不可能(たとえば、リンクされたリストを記述できない) または HIDL のツリーなど)。前方宣言は、既存(Android 8.x より前)の HAL のほとんどで使用が制限されており、データ構造の宣言の並べ替えにより取り除くことができます。
この制限により、簡単な簡単な方法で、値によってデータ構造をコピーできます。
複数存在する可能性があるポインタ値を追跡するのではなく、
自己参照データ構造になっています。同じデータを指す 2 つのメソッド パラメータや vec<T>
など、同じデータが 2 回渡される場合は、2 つのコピーが別々に作成されて送信されます。
ネストされた宣言
HIDL は、必要な数のレベルにネストされた宣言をサポートしています(後述の例外が 1 つあります)。次に例を示します。
interface IFoo { uint32_t[3][4][5][6] multidimArray; vec<vec<vec<int8_t>>> multidimVector; vec<bool[4]> arrayVec; struct foo { struct bar { uint32_t val; }; bar b; } struct baz { foo f; foo.bar fb; // HIDL uses dots to access nested type names } …
例外として、インターフェース型は、vec<T>
にのみ、1 レベルだけ埋め込むことができます(vec<vec<IFoo>>
は不可)。
未加工ポインタの構文
HIDL 言語では * を使用せず、 C/C++ の未加工ポインタの完全な柔軟性です。HIDL でのポインタと配列 / ベクトルのカプセル化方法については、vec<T> テンプレートをご覧ください。
インターフェース
interface
キーワードには 2 つの用途があります。
- .hal ファイルのインターフェースの定義を開きます。
- 構造体と共用体のフィールド、メソッド パラメータ、戻り値で特殊な型として使用できます。一般的なインターフェースとして
android.hidl.base@1.0::IBase
の同義語であると見なされます。
たとえば、IServiceManager
には次のメソッドがあります。
get(string fqName, string name) generates (interface service);
このメソッドでは、一部のインターフェースを名前で検索できます。これは、インターフェースを android.hidl.base@1.0::IBase
に置き換えるのと同じです。
インターフェースを渡す方法は、トップレベル パラメータとして渡すか、vec<IMyInterface>
のメンバーとして渡すかの 2 つのみです。ネストされたベクトル、構造体、配列、共用体のメンバーにすることはできません。
MQDescriptorSync と MQDescriptorUnsync
MQDescriptorSync
型と MQDescriptorUnsync
型は、HIDL インターフェース全体で同期または非同期の高速メッセージ キュー(FMQ)記述子を渡します。詳しくは、
HIDL C++(FMQ は
など)が含まれます。
memory 型
memory
型は、HIDL でマッピングされていない共有メモリを表すために使用されます。C++ でのみサポートされます。この型の値を受信側で使用して IMemory
オブジェクトを初期化し、メモリをマッピングして使用可能にできます。詳細については、HIDL C++ をご覧ください。
警告: 構造化データが [共有中] に配置されています。
memory は、そのメモリの存続期間中は形式が変化しない型でなければなりません。
memory
を渡すインターフェース バージョン。そうでない場合、HAL は
致命的な互換性の問題が起きることがあります。
pointer 型
pointer
型は HIDL の内部使用専用です。
bitfield<T> 型テンプレート
bitfield<T>
(T
はユーザー定義列挙型)は、値が T
で定義されている列挙値のビット和であることを示しています。生成されたコードでは、bitfield<T>
は T の基になる型として表現されます。次に例を示します。
enum Flag : uint8_t { HAS_FOO = 1 << 0, HAS_BAR = 1 << 1, HAS_BAZ = 1 << 2 }; typedef bitfield<Flag> Flags; setFlags(Flags flags) generates (bool success);
コンパイラは、Flags 型を uint8_t
と同様に処理します。
(u)int8_t
/ (u)int16_t
/ (u)int32_t
/ (u)int64_t
を使用しない理由は、bitfield
を使用すると追加の HAL 情報がリーダーに提供されるためです。これにより、リーダーは setFlags
が Flag のビット和値を取る(つまり、16 を指定した setFlags
の呼び出しが無効である)ことを認識します。bitfield
を使用しなければ、この情報はドキュメントでのみ伝えられることになります。さらに、VTS は flags の値が Flag のビット和であるかどうかを実際に確認できます。
プリミティブ型のハンドル
警告: どのような種類のアドレスも(物理デバイスのアドレスであっても)、決してネイティブ ハンドルに含めないでください。プロセス間でこの情報を渡すことは危険性が高く、攻撃を受けやすくなります。プロセス間で渡される値は、プロセス内の割り当て済みメモリの検索に使用する前に、検証が必要です。正しくないハンドルを使用すると、 メモリ破損のリスクを軽減できます。
HIDL セマンティクスは値ごとにコピーされます。これは、パラメータがコピーされることを意味します。
大量のデータまたはプロセス間で共有する必要があるデータ(同期フェンスなど)は、永続オブジェクトを指すファイル記述子(共有メモリや実際のファイルなど、ファイル記述子の背後に隠れている可能性があるものについては ashmem
)を渡すことで処理されます。バインダ ドライバは、ファイル記述子を他のプロセスに複製します。
native_handle_t
Android は、libcutils
で定義された一般的なハンドルのコンセプトである native_handle_t
をサポートしています。
typedef struct native_handle { int version; /* sizeof(native_handle_t) */ int numFds; /* number of file-descriptors at &data[0] */ int numInts; /* number of ints at &data[numFds] */ int data[0]; /* numFds + numInts ints */ } native_handle_t;
ネイティブ ハンドルは、値によって渡される int とファイル記述子のコレクションです。1 つのファイル記述子を、int がなくファイル記述子が 1 つあるネイティブ ハンドルに格納できます。handle
プリミティブ型でカプセル化されたネイティブ ハンドルを使用してハンドルを渡すと、ネイティブ ハンドルが HIDL に直接含まれるようになります。
native_handle_t
はサイズが変わるため、構造体に直接含めることはできません。ハンドル フィールドでは、個別に割り当てられた native_handle_t
を指すポインタが生成されます。
以前のバージョンの Android では、libcutils にある同じ関数を使用してネイティブ ハンドルが作成されていました。Android 8.0 以上では、これらの関数は android::hardware::hidl
名前空間にコピーされるか、NDK に移されました。これらの関数は、HIDL 自動生成コードにより、ユーザー作成コードの関与なしで、自動的にシリアル化および逆シリアル化されます。
ハンドルとファイル記述子のオーナー権限
hidl_handle
オブジェクト(トップレベルまたは複合型の一部)を渡す(または返す)HIDL インターフェース メソッドを呼び出す場合、これに含まれるファイル記述子のオーナー権限は次のようになります。
- 引数として
hidl_handle
オブジェクトを渡す呼び出し元は、ラップしたnative_handle_t
に含まれているファイル記述子のオーナー権限を保持します。呼び出し元はファイル記述子の処理を終えたときにそれらを閉じる必要があります。 hidl_handle
オブジェクトを(_cb
関数に渡すことで)返すプロセスは、オブジェクトでラップされたnative_handle_t
に含まれているファイル記述子のオーナー権限を保持します。プロセスはファイル記述子の処理を終えたときにそれらを閉じる必要があります。hidl_handle
を受け取るトランスポートnative_handle_t
内のファイル記述子の所有権 オブジェクトでラップされます。受信側のファイル記述子は、受信中もそのまま使用し、 トランザクション コールバックを呼び出すことはできますが、このファイルを使用するには、ネイティブ ハンドルのクローンを作成する必要があります。 記述できます。トランスポートは自動的に トランザクション完了時のファイル記述子のclose()
。
HIDL は Java でのハンドルをサポートしていません(Java は あります)。
サイズ指定される配列
HIDL 構造体のサイズ指定される配列の要素には、構造体が格納できる任意の型を使用できます。
struct foo { uint32_t[3] x; // array is contained in foo };
文字列
C++ と Java では文字列の表現が異なりますが、基になるトランスポート ストレージ タイプは C++ 構造です。詳細については、HIDL C++ データ型または HIDL Java データ型をご覧ください。
注: HIDL インターフェース(Java 間を含む)によって文字セット変換が発生する 元のエンコードが保持されない場合があります。
vec<T> 型テンプレート
vec<T>
テンプレートは、T
のインスタンスを含む可変サイズのバッファを表します。
T
は次のいずれかです。
- プリミティブ型(例: uint32_t)
- 文字列
- ユーザー定義列挙型
- ユーザー定義構造体
- インターフェース、または
interface
キーワード(vec<IFoo>
、vec<interface>
はトップレベル パラメータとしてのみサポートされます) - ハンドル
- bitfield<U>
- vec<U>。U はこのリストのインターフェイス(
vec<vec<IFoo>>
はサポートされていません) - U[](U のサイズ指定される配列。U にはこのリストのインターフェース以外のものを使用できます)
ユーザー定義型
このセクションでは、ユーザー定義型について説明します。
列挙型
HIDL は匿名の列挙型をサポートしていません。それ以外の点では、HIDL の列挙型は C++11 に似ています。
enum name : type { enumerator , enumerator = constexpr , … }
基本列挙型は、HIDL の整数型の 1 つで定義されます。整数型に基づく列挙型では、最初の列挙子に値が指定されていない場合、デフォルトの値は 0 です。それより後の列挙子に値が指定されていない場合、デフォルトの値は前の値に 1 を足した値です。次に例を示します。
// RED == 0 // BLUE == 4 (GREEN + 1) enum Color : uint32_t { RED, GREEN = 3, BLUE }
列挙型は、以前に定義した列挙型を継承することもできます。子列挙型(この例では FullSpectrumColor
)の最初の列挙子に値が指定されていない場合、デフォルトの値は親列挙型の最後の列挙子の値に 1 を足した値です。次に例を示します。
// ULTRAVIOLET == 5 (Color:BLUE + 1) enum FullSpectrumColor : Color { ULTRAVIOLET }
警告: 列挙型の継承は、他のほとんどの種類の継承とは逆方向に機能します。子列挙値を親列挙値として使用することはできません。これは、子列挙型には親より多くの値が含まれているためです。一方、親列挙値は子列挙値として安全に使用できます。これは、定義上、子列挙値は親列挙値のスーパーセットであるためです。つまり、親列挙型を参照する型は、インターフェースのその後の反復で子列挙型を参照できません。インターフェースの設計時には、この点に留意してください。
列挙型の値は、ネストされた型のようなドット構文ではなく、コロン構文で参照されます。構文は Type:VALUE_NAME
です。値が同じ列挙型または子列挙型の中で参照される場合、型を指定する必要はありません。例:
enum Grayscale : uint32_t { BLACK = 0, WHITE = BLACK + 1 }; enum Color : Grayscale { RED = WHITE + 1 }; enum Unrelated : uint32_t { FOO = Color:RED + 1 };
Android 10 以降、列挙型には定数式で使用できる len
属性が含まれています。MyEnum::len
は、その列挙型のエントリの合計数です。この値は、値の総数とは異なります。値の総数は、
値が重複しています。
構造体
HIDL は匿名の構造体をサポートしていません。それ以外の点では、HIDL の構造体は C と非常によく似ています。
HIDL は、可変長データ構造をサポートしていません。
構造体ですこれには、C/C++ で構造体の最後のフィールドとして使用されることがある不定長の配列が含まれます(サイズが [0]
の場合もあります)。HIDL の vec<T>
は、別個のバッファに格納されたデータで動的にサイズ変更される配列を表現します。このようなインスタンスは、struct
内の vec<T>
のインスタンスで表現されます。
同様に、string
を struct
に含めることができます(関連バッファは独立しています)。生成された C++ では、HIDL ハンドル型のインスタンスは、基になるデータ型のインスタンスが可変長であるため、実際のネイティブ ハンドルへのポインタとして表現されます。
共用体
HIDL は匿名ユニオンをサポートしていません。それ以外の点では、共用体は C と似ています。
共用体には修正型(ポインタ、ファイル記述子、バインダーなど)を含めることはできません
あります。特別なフィールドや関連付けられた型は必要なく、
memcpy()
または同等のものを使用してコピーします。UNION は直接
設定を必要とするすべてのものを含む(または他のデータ構造を使用する)
バインダー オフセット(ハンドルまたはバインダー インターフェースの参照)。例:
union UnionType { uint32_t a; // vec<uint32_t> r; // Error: can't contain a vec<T> uint8_t b;1 }; fun8(UnionType info); // Legal
共用体は、構造体内で宣言することもできます。次に例を示します。
struct MyStruct { union MyUnion { uint32_t a; uint8_t b; }; // declares type but not member union MyUnion2 { uint32_t a; uint8_t b; } data; // declares type but not member }