HIDL データ宣言により、C++ 標準レイアウト データ構造が生成されます。これらの構造は、適切と思われる任意の場所(スタック、ファイルまたはグローバル スコープ、ヒープ)に配置でき、同じ方法で構成できます。クライアント コードは HIDL プロキシ コードを呼び出して const 参照とプリミティブ型を渡しますが、スタブ コードとプロキシ コードはシリアル化の詳細を隠蔽します。
注: データ構造を明示的にシリアル化または逆シリアル化するためのコードをデベロッパーが作成する必要は一切ありません。
次の表は、HIDL プリミティブを C++ データ型にマッピングしたものです。
HIDL 型 | C++ 型 | ヘッダー / ライブラリ |
---|---|---|
enum |
enum class |
|
uint8_t..uint64_t |
uint8_t..uint64_t |
<stdint.h> |
int8_t..int64_t |
int8_t..int64_t |
<stdint.h> |
float |
float |
|
double |
double |
|
vec<T> |
hidl_vec<T> |
libhidlbase |
T[S1][S2]...[SN] |
T[S1][S2]...[SN] |
|
string |
hidl_string |
libhidlbase |
handle |
hidl_handle |
libhidlbase |
safe_union |
(custom) struct |
|
struct |
struct |
|
union |
union |
|
fmq_sync |
MQDescriptorSync |
libhidlbase |
fmq_unsync |
MQDescriptorUnsync |
libhidlbase |
以下のセクションでは、データ型について詳しく説明します。
enum
HIDL の列挙型は C++ の列挙型になります。次に例を示します。
enum Mode : uint8_t { WRITE = 1 << 0, READ = 1 << 1 }; enum SpecialMode : Mode { NONE = 0, COMPARE = 1 << 2 };
これは次のようになります。
enum class Mode : uint8_t { WRITE = 1, READ = 2 }; enum class SpecialMode : uint8_t { WRITE = 1, READ = 2, NONE = 0, COMPARE = 4 };
Android 10 以降では、::android::hardware::hidl_enum_range
を使用して列挙型を反復処理できます。この範囲には、親列挙型から最後の子までのすべての列挙子が、HIDL ソースコードに現れる順序で含まれます。たとえば、次のコードは、WRITE
、READ
、NONE
、COMPARE
をこの順序で反復処理します。上述の SpecialMode
の場合、次のようになります。
template <typename T> using hidl_enum_range = ::android::hardware::hidl_enum_range<T> for (SpecialMode mode : hidl_enum_range<SpecialMode>) {...}
hidl_enum_range
は逆反復子も実装しており、constexpr
コンテキストで使用できます。ある列挙内に値が複数回現れる場合、値はその範囲内に複数回現れます。
bitfield<T>
bitfield<T>
(T
はユーザー定義の列挙型)は C++ でその列挙型の基になる型となります。上記の例では、bitfield<Mode>
は uint8_t
になります。
vec<T>
hidl_vec<T>
クラス テンプレートは libhidlbase
の一部で、任意のサイズの任意の HIDL 型のベクトルを渡すために使用できます。同等の固定サイズのコンテナは hidl_array
です。hidl_vec::setToExternal()
関数を使用して、T
型の外部データバッファを指すように hidl_vec<T>
を初期化することもできます。
生成された C++ ヘッダーに構造体を適切に出力 / 挿入するほか、vec<T>
の使用により、std::vector
とベア T
ポインタを相互変換する便利な関数が生成されます。パラメータに vec<T>
を使用した場合、これを使用する関数がオーバーロードされ(2 つのプロトタイプを生成)、パラメータの HIDL 構造体と std::vector<T>
型の両方の受け渡しが行われます。
配列
HIDL の定数配列は、libhidlbase
の hidl_array
クラスで表されます。hidl_array<T, S1, S2, …,
SN>
は、N 次元の固定サイズ配列 T[S1][S2]…[SN]
を表します。
文字列
hidl_string
クラス(libhidlbase
の一部)は、HIDL インターフェースを介して文字列を渡すために使用可能で、/system/libhidl/base/include/hidl/HidlSupport.h
で定義されています。クラス内の最初の保存場所は、その文字バッファへのポインタです。
hidl_string
は、operator=
、暗黙的なキャスト、.c_str()
関数を使用して、std::string and char*
(C スタイルの文字列)と相互変換する方法を認識します。HIDL 文字列構造体には、以下の処理を行うための適切なコピー コンストラクタと割り当て演算子があります。
std::string
または C 文字列から HIDL 文字列を読み込む。- HIDL 文字列から新しい
std::string
を作成する。
また、HIDL 文字列には変換コンストラクタがあるので、HIDL 文字列を受け取るメソッドで C 文字列(char *
)と C++ 文字列(std::string
)を使用できます。
構造体
HIDL の struct
に含めることができるのは固定サイズのデータ型のみです。関数を含めることはできません。HIDL 構造体の定義は、C++ の標準レイアウトの struct
に直接マッピングされます。これにより、struct
が一貫したメモリ レイアウトを持つことが保証されます。構造体には、個別の可変長バッファを指す handle
、string
、vec<T>
などの HIDL 型を含めることができます。
ハンドル
警告: どのような種類のアドレスも(物理デバイスのアドレスであっても)、決してネイティブ ハンドルに含めないでください。プロセス間でこの情報を渡すことは危険性が高く、攻撃を受けやすくなります。プロセス間で渡される値は、プロセス内の割り当て済みメモリの検索に使用する前に、検証が必要です。検証を行わない場合、不正なハンドルによって不正なメモリアクセスやメモリ破損が発生するおそれがあります。
handle
型は、const native_handle_t
オブジェクト(これは Android にかなり以前から存在していました)へのポインタの単純なラッパーである、C++ の hidl_handle
構造体によって表されます。
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;
デフォルトでは、hidl_handle
はラップする native_handle_t
ポインタのオーナー権限を取得しません。このハンドルは、32 ビットプロセスと 64 ビットプロセスで使用できるように、native_handle_t
へのポインタを安全に格納するためだけに存在します。
hidl_handle
が自身の包含されたファイル記述子を所有するのは、次のような状況です。
shouldOwn
パラメータをtrue
に設定してsetTo(native_handle_t* handle, bool shouldOwn)
メソッドを呼び出した後hidl_handle
オブジェクトが別のhidl_handle
オブジェクトからのコピー コンストラクションによって作成されたときhidl_handle
オブジェクトが別のhidl_handle
オブジェクトからコピー割り当てされたとき
hidl_handle
は、native_handle_t*
オブジェクトとの暗黙的および明示的な相互変換を提供します。HIDL での handle
型の主な役割は、HIDL インターフェースを介してファイル記述子を渡すことです。したがって、単一のファイル記述子は、int
のない native_handle_t
と単一の fd
で表されます。クライアントとサーバーが別のプロセスに存在する場合、RPC の実装は自動的にファイル記述子を処理し、両方のプロセスが同一のファイルで動作できるようにします。
プロセスによって hidl_handle
で取得されたファイル記述子はそのプロセス内で有効になりますが、受信側の関数の存続期間を超える保持は行われません(関数が返るとクローズします)。ファイル記述子への永続的なアクセスを保持しようとするプロセスは、包含されたファイル記述子を dup()
するか、hidl_handle
オブジェクト全体をコピーする必要があります。
メモリ
HIDL の memory
型は、マッピングされていない共有メモリを表す libhidlbase
の hidl_memory
クラスにマッピングされます。これは、HIDL でメモリを共有するためにプロセス間で渡す必要があるオブジェクトです。共有メモリを使用するには、次の手順を実施します。
IAllocator
のインスタンス(現在は「ashmem」インスタンスのみが利用可能です)を取得し、これを使用して共有メモリを割り当てます。IAllocator::allocate()
はhidl_memory
オブジェクトを返します。このオブジェクトは、HIDL RPC を介して渡し、libhidlmemory
のmapMemory
関数を使用してプロセスにマッピングすることができます。mapMemory
は、メモリへのアクセスに使用できるsp<IMemory>
オブジェクトへの参照を返します(IMemory
とIAllocator
はandroid.hidl.memory@1.0
で定義されます)。
IAllocator
のインスタンスを使用して、次のようにメモリを割り当てることができます。
#include <android/hidl/allocator/1.0/IAllocator.h> #include <android/hidl/memory/1.0/IMemory.h> #include <hidlmemory/mapping.h> using ::android::hidl::allocator::V1_0::IAllocator; using ::android::hidl::memory::V1_0::IMemory; using ::android::hardware::hidl_memory; .... sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem"); ashmemAllocator->allocate(2048, [&](bool success, const hidl_memory& mem) { if (!success) { /* error */ } // now you can use the hidl_memory object 'mem' or pass it around }));
メモリに対する実際の変更は、mem
を作成した側または HIDL RPC 経由でそれを受信する側で、IMemory
オブジェクトを介して実行する必要があります。
// Same includes as above sp<IMemory> memory = mapMemory(mem); void* data = memory->getPointer(); memory->update(); // update memory however you wish after calling update and before calling commit data[0] = 42; memory->commit(); // … memory->update(); // the same memory can be updated multiple times // … memory->commit();
インターフェース
インターフェースはオブジェクトとして渡すことができます。interface という言葉を、型 android.hidl.base@1.0::IBase
の糖衣構文として使用できます。さらに、現在のインターフェースおよびインポートされたすべてのインターフェースは、型として定義されます。
インターフェースを保持する変数は、sp<IName>
のような強いポインタでなければなりません。インターフェース パラメータを受け取る HIDL 関数は、未加工ポインタを強いポインタに変換します。これにより、直感的でない動作が引き起こされます(ポインタが予期せず消去されることがあります)。問題を回避するには、HIDL インターフェースを常に sp<>
として格納してください。