HIDL パッケージで定義されたすべてのインターフェースは、パッケージの名前空間内に独自の自動生成 C++ クラスを持ちます。クライアントとサーバーは、次のようにそれぞれ異なる方法でインターフェースを処理します。
- サーバーはインターフェースを実装します。
- クライアントはインターフェースのメソッドを呼び出します。
インターフェースは、サーバーにより名前で登録されるか、HIDL 定義のメソッドにパラメータとして渡されます。たとえば、フレームワーク コードは、HAL から非同期メッセージを取得するインターフェースを提供し、そのインターフェースを登録せずに直接 HAL に渡します。
サーバーの実装
IFoo
インターフェースを実装するサーバーには、次のように自動生成された IFoo
ヘッダー ファイルが含まれます。
#include <android/hardware/samples/1.0/IFoo.h>
ヘッダーは、リンクする IFoo
インターフェースの共有ライブラリによって自動的にエクスポートされます。IFoo.hal
の例を次に示します。
// IFoo.hal interface IFoo { someMethod() generates (vec<uint32_t>); ... }
IFoo インターフェースのサーバー実装用のスケルトンの例を次に示します。
// From the IFoo.h header using android::hardware::samples::V1_0::IFoo; class FooImpl : public IFoo { Return<void> someMethod(foo my_foo, someMethod_cb _cb) { vec<uint32_t> return_data; // Compute return_data _cb(return_data); return Void(); } ... };
サーバー インターフェースの実装をクライアントが使用できるようにするには、次のように設定します。
hwservicemanager
でインターフェースの実装を登録します(以下の詳細を参照してください)。
または
- インターフェース メソッドの引数としてインターフェースの実装を渡します(詳細については、非同期コールバックを参照してください)。
インターフェースの実装を登録する際、hwservicemanager
プロセスは、デバイス上で実行される登録済みの HIDL インターフェースを名前とバージョンで追跡します。サーバーは名前で HIDL インターフェースの実装を登録し、クライアントは名前とバージョンでサービスの実装をリクエストします。このプロセスは、HIDL インターフェース android.hidl.manager@1.0::IServiceManager
に対応します。
自動生成された各 HIDL インターフェースのヘッダー ファイル(IFoo.h
など)は、インターフェースの実装を hwservicemanager
に登録するために使用可能な registerAsService()
メソッドを持ちます。必要な引数は、インターフェースの実装の名前のみです。クライアントはこの名前を使用して後で hwservicemanager
からインターフェースを取得します。
::android::sp<IFoo> myFoo = new FooImpl(); ::android::sp<IFoo> mySecondFoo = new FooAnotherImpl(); status_t status = myFoo->registerAsService(); status_t anotherStatus = mySecondFoo->registerAsService("another_foo");
hwservicemanager
は、異なるインターフェース(または同じインターフェースの異なるバージョン)が競合せず同一のインスタンス名で登録できるように、[package@version::interface, instance_name]
の組み合わせを一意のものとして扱います。同一のパッケージ バージョン、インターフェース、インスタンス名で registerAsService()
を呼び出した場合、hwservicemanager
は以前に登録されたサービスへのリファレンスを削除し、新しいサービスを使用します。
クライアントの実装
サーバーと同様に、クライアントは参照するすべてのインターフェースを次のように #include
する必要があります。
#include <android/hardware/samples/1.0/IFoo.h>
クライアントは次の 2 つの方法でインターフェースを取得します。
- (
hwservicemanager
を介して)I<InterfaceName>::getService
を使用する - インターフェース メソッドを使用する
自動生成された各インターフェースのヘッダー ファイルにある getService
静的メソッドを使用して、hwservicemanager
からサービス インスタンスを取得できます。
// getService will return nullptr if the service can't be found sp<IFoo> myFoo = IFoo::getService(); sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");
これにより、クライアントは IFoo
インターフェースを持ち、ローカルクラスの実装のようにメソッドを呼び出せるようになります。実際には、同一のプロセスや異なるプロセス、または別のデバイス(HAL リモーティングを使用)で実装が実行される場合があります。クライアントが、パッケージのバージョン 1.0
に含まれる IFoo
オブジェクトで getService
を呼び出しているため、hwservicemanager
は、実装が 1.0
クライアントと互換性がある場合にのみサーバーの実装を返します。実際には、これはバージョン 1.n
のサーバー実装のみを意味します(インターフェースのバージョン x.(y+1)
は x.y
を拡張(継承)するものとなります)。
また、異なるインターフェース間でキャストするためのメソッド castFrom
も用意されています。このメソッドは、リモート インターフェースに対して IPC 呼び出しを行うことで機能し、ベースとなる型がリクエストされた型と同一であることを確認します。リクエストされた型が使用できない場合、nullptr
を返します。
sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService(); sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0);
非同期コールバック
既存の多くの HAL 実装は非同期ハードウェアと通信するため、新たに発生したイベントをクライアントに通知するための非同期的な方法が必要となります。HIDL インターフェース関数は HIDL インターフェース オブジェクトをパラメータとして受け取ることができるため、HIDL インターフェースを非同期コールバックとして使用できます。
インターフェース ファイル IFooCallback.hal
の例:
package android.hardware.samples@1.0; interface IFooCallback { sendEvent(uint32_t event_id); sendData(vec<uint8_t> data); }
IFooCallback
パラメータを受け取る IFoo
の新しいメソッドの例:
package android.hardware.samples@1.0; interface IFoo { struct Foo { int64_t someValue; handle myHandle; }; someMethod(Foo foo) generates (int32_t ret); anotherMethod() generates (vec<uint32_t>); registerCallback(IFooCallback callback); };
IFoo
インターフェースを使用するクライアントは IFooCallback
インターフェースのサーバーで、次のように IFooCallback
の実装を提供します。
class FooCallback : public IFooCallback { Return<void> sendEvent(uint32_t event_id) { // process the event from the HAL } Return<void> sendData(const hidl_vec<uint8_t>& data) { // process data from the HAL } };
また、次のように IFoo
インターフェースの既存のインスタンスにそのまま渡すこともできます。
sp<IFooCallback> myFooCallback = new FooCallback(); myFoo.registerCallback(myFooCallback);
IFoo
を実装するサーバーは、これを sp<IFooCallback>
オブジェクトとして受け取ります。また、コールバックを保存し、このインターフェースを使用するたびにクライアントにコールバックします。
終了通知受信者
サービスの実装は別のプロセスで実行されるため、クライアントが稼働している間にインターフェースを実装するプロセスが停止する可能性があります。停止したプロセスでホストされるインターフェース オブジェクトの呼び出しは、トランスポート エラーにより失敗します(isOK()
は false を返します)。このような障害から回復するためには、I<InterfaceName>::getService()
を呼び出してサービスの新しいインスタンスをリクエストする必要があります。この方法が機能するのは、失敗したプロセスを再起動してサービスを servicemanager
(通常 HAL 実装では有効です)に再登録した場合のみです。
事後対応としてこれに対処する代わりに、インターフェースのクライアントでは、サービスの終了時に通知を受け取る終了通知受信者を登録することもできます。取得した IFoo
インターフェースでこのような通知を登録するには、クライアントで次を実行します。
foo->linkToDeath(recipient, 1481 /* cookie */);
recipient
パラメータは、HIDL が提供する android::hardware::hidl_death_recipient
インターフェースの実装である必要があります。これには、インターフェースをホストするプロセスが停止した場合に RPC スレッドプール内のスレッドから呼び出される次のような単一のメソッド serviceDied()
が含まれます。
class MyDeathRecipient : public android::hardware::hidl_death_recipient { virtual void serviceDied(uint64_t cookie, const android::wp<::android::hidl::base::V1_0::IBase>& who) { // Deal with the fact that the service died } }
cookie
パラメータには、linkToDeath()
で渡された Cookie が含まれますが、who
パラメータには、クライアントのサービスを表すオブジェクトへの弱いポインタが含まれます。上記のサンプル呼び出しでは、cookie
は 1481、who
は foo
となります。
登録後に、次のようにして終了通知受信者の登録を解除することもできます。
foo->unlinkToDeath(recipient);