HIDL データ宣言は、C++ 標準レイアウト データ構造を生成します。これらの構造は、自然な場所 (スタック上、ファイルまたはグローバル スコープ、またはヒープ上) に配置でき、同じ方法で構成できます。クライアント コードは、const 参照とプリミティブ型を渡して HIDL プロキシ コードを呼び出しますが、スタブ コードとプロキシ コードはシリアル化の詳細を隠します。
注:データ構造を明示的にシリアル化または逆シリアル化するために、開発者が作成したコードはいかなる時点でも必要ありません。
以下の表は、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 |
以下のセクションでは、データ型について詳しく説明します。
列挙型
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
を使用して enum を反復できるようになりました。この範囲には、親の列挙型から最後の子まで、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
コンテキストで使用できます。値が列挙内に複数回出現する場合、その値は範囲内に複数回出現します。
ビットフィールド<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
体のメモリ レイアウトが一貫していることが保証されます。構造体には、個別の可変長バッファーを指す HIDL 型 ( handle
、 string
、 vec<T>
など) を含めることができます。
ハンドル
警告:いかなる種類のアドレス (物理デバイス アドレスも含む) もネイティブ ハンドルの一部であってはなりません。この情報をプロセス間で受け渡すことは危険であり、攻撃を受けやすくなります。プロセス間で渡される値は、プロセス内で割り当てられたメモリを検索するために使用する前に検証する必要があります。そうしないと、不正なハンドルにより不正なメモリ アクセスやメモリ破損が発生する可能性があります。
handle
型は、C++ ではhidl_handle
構造体によって表されます。これは、 const native_handle_t
オブジェクトへのポインターの単純なラッパーです (これは Android に長い間存在していました)。
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
を持たず、単一のfd
を備えたnative_handle_t
によって表されます。クライアントとサーバーが異なるプロセスに存在する場合、RPC 実装はファイル記述子を自動的に処理して、両方のプロセスが同じファイル上で動作できるようにします。
プロセスによってhidl_handle
で受信されたファイル記述子は、そのプロセス内では有効ですが、受信関数を超えて保持されることはありません (関数が返されると閉じられます)。ファイル記述子への永続的なアクセスを維持したいプロセスは、囲まれたファイル記述子をdup()
するか、 hidl_handle
オブジェクト全体をコピーする必要があります。
メモリ
HIDL memory
タイプは、マップされていない共有メモリを表すlibhidlbase
のhidl_memory
クラスにマップされます。これは、HIDL でメモリを共有するためにプロセス間で渡す必要があるオブジェクトです。共有メモリを使用するには:
-
IAllocator
のインスタンス (現在はインスタンス "ashmem" のみが利用可能) を取得し、それを使用して共有メモリを割り当てます。 -
IAllocator::allocate()
HIDL RPC 経由で渡され、libhidlmemory
のmapMemory
関数を使用してプロセスにマップできるhidl_memory
オブジェクトを返します。 -
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();
インターフェース
インターフェイスはオブジェクトとして渡すことができます。 「インターフェイス」という単語は、 android.hidl.base@1.0::IBase
型の糖衣構文として使用できます。さらに、現在のインターフェイスとインポートされたインターフェイスはタイプとして定義されます。
インターフェイスを保持する変数は、強力なポインターである必要があります: sp<IName>
。インターフェイス パラメーターを受け取る HIDL 関数は、生のポインターを強力なポインターに変換し、直感的ではない動作を引き起こします (ポインターが予期せずクリアされる可能性があります)。問題を回避するには、HIDL インターフェイスを常にsp<>
として保存してください。