データ型

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 ソース コードに出現する順序ですべての列挙子が含まれます。たとえば、次のコードは、 WRITEREADNONECOMPAREをこの順序で繰り返します。上記の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 の定数配列は、 libhidlbasehidl_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 型 ( handlestringvec<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タイプは、マップされていない共有メモリを表すlibhidlbasehidl_memoryクラスにマップされます。これは、HIDL でメモリを共有するためにプロセス間で渡す必要があるオブジェクトです。共有メモリを使用するには:

  1. IAllocatorのインスタンス (現在はインスタンス "ashmem" のみが利用可能) を取得し、それを使用して共有メモリを割り当てます。
  2. IAllocator::allocate() HIDL RPC 経由で渡され、 libhidlmemorymapMemory関数を使用してプロセスにマップできるhidl_memoryオブジェクトを返します。
  3. mapMemoryメモリへのアクセスに使用できるsp<IMemory>オブジェクトへの参照を返します。 ( IMemoryIAllocator 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<>として保存してください。