インターフェース

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();
    }
    ...
};

サーバー インターフェースの実装をクライアントが使用できるようにするには、次のように設定します。

  1. hwservicemanager でインターフェースの実装を登録します(以下の詳細を参照してください)。

    または

  2. インターフェース メソッドの引数としてインターフェースの実装を渡します(詳細については、非同期コールバックを参照してください)。

インターフェースの実装を登録する際、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 を使用する
  • インターフェース メソッドを使用する

自動生成された各インターフェースのヘッダー ファイルには、hwservicemanager からサービス インスタンスを取得するために使用される次の getService 静的メソッドがあります。

// 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、whofoo となります。

登録後に、次のようにして終了通知受信者の登録を解除することもできます。

foo->unlinkToDeath(recipient);