HIDL フレームワークの下位互換性の確認

HIDL HAL は、Android コアシステム(system.img またはフレームワークとも呼ばれます)が下位互換性を持つことを保証します。ベンダー テストスイート(VTS)のテストでは、HAL が期待どおりに動作することを確認します(たとえば、1.1 HAL のテストはすべての 1.2 実装で実施されます)。一方、フレームワークのテストでは、サポートされている HAL(1.0、1.1、1.2)が提供されている場合に、フレームワークがその HAL で適切に動作することを確認する必要があります。

HAL インターフェース定義言語(HIDL)の詳細については、HIDLHIDL のバージョニング、および HIDL HAL のサポート終了をご覧ください。

HAL のアップグレードについて

HAL のアップグレードには、メジャーとマイナーの 2 種類があります。 ほとんどのシステムには HAL 実装が 1 つしかありませんが、複数の実装がサポートされています。次に例を示します。

android.hardware.teleport@1.0 # initial interface
android.hardware.teleport@1.1 # minor version upgrade
android.hardware.teleport@1.2 # another minor version upgrade
...
android.hardware.teleport@2.0 # major version upgrade
...

システム パーティションには通常、HAL 実装の特定のグループとの通信を管理するフレームワーク デーモン(teleportd など)が含まれます。あるいは、便利なクライアントの動作を実装するシステム ライブラリ(android.hardware.configstore-utils など)をシステムに含めることもできます。上記の例では、デバイスにインストールされている HAL のバージョンに関係なく teleportd が機能する必要があります。

Google が管理するバージョン

メジャー バージョン アップグレード(1.0、2.0、3.0 など)が存在する場合は、そのバージョンのサポートが終了するまで、少なくとも 1 つの Google 管理デバイスで各メジャー バージョンの実装を管理する必要があります。特定のメジャー バージョンを装備した Google 管理デバイスが存在しない場合、Google はそのメジャー バージョンの古い実装を引き続き管理します。

この管理方法では、古い実装(1.2 など)は保持できますが、新しい実装(2.0 など)の作成時にデフォルトでは使用されないため、追加のオーバーヘッドがわずかに発生します。

マイナー バージョン アップグレードのテスト

フレームワークでマイナー バージョンの下位互換性をテストするには、マイナー バージョンの実装を自動的に生成する手段が必要です。Google が管理するバージョンに関する制限により、hidl-gen は 1.(x+n) の実装を使用して 1.x の実装を提供するアダプターのみを生成します(それ以外は生成できません)。2.0 の実装から 1.0 の実装を生成することは(メジャー バージョンの定義上)できません。

たとえば、1.2 の実装で 1.1 をテストするには、1.1 の実装をシミュレートする手段が必要です。1.2 のインターフェースは、多少動作は異なりますが、そのまま 1.1 の実装として使用できます(フレームワークが手動でバージョンを確認したり、castFrom を呼び出したりする点が異なります)。

基本的なアイデアは次のとおりです。

  1. Android モバイル デバイスに x.(y+n) のインターフェースをインストールします。
  2. x.y のターゲット アダプターをインストールして有効にします。
  3. 古いマイナー バージョンを実行しているときにデバイスが期待どおりに動作するかどうかをテストします。

これらのアダプターは、実装が実際には 1.2 のインターフェースに支えられており、1.1 のインターフェースを提示しているだけであることを完全に隠蔽します(アダプターは 1.2 のインターフェースを使用していますが、1.1 のインターフェースであるかのように見せています)。

ワークフローの例

この例では、Android デバイスは android.hardware.foo@1.1::IFoo/default を実行しています。クライアントを android.hardware.foo@1.0::IFoo/default で適切に動作させるには、次の手順を実施します。

  1. ターミナルで次のコマンドを実行します。
    $ PACKAGE=android.hidl.allocator@1.0-adapter
    $ INTERFACE=IAllocator
    $ INSTANCE=ashmem
    $ THREAD_COUNT=1 # can see current thread use on `lshal -i -e`
    $ m -j $PACKAGE
    $ /data/nativetest64/$PACKAGE/$PACKAGE $INTERFACE $INSTANCE $THREAD_COUNT
    Trying to adapt down android.hidl.allocator@1.0-adapter/default
    Press any key to disassociate adapter.
  2. adb shell stop(または start)を使用してクライアントを再起動するか、単にプロセスを終了します。
  3. テストが完了したら、アダプターの関連付けを解除します。
  4. デバイスまたはクライアントを再起動して、システム状態を復元します。

追加のターゲット

hidl-gen は、ビルドシステム内で hidl_interface により指定されたすべてのインターフェースのアダプターのビルド ターゲットを自動的に追加します。a.b.c@x.y というパッケージには、追加の C++ ターゲット a.b.c@x.y-adapter があります。

a.b.c@x.y のアダプターは、a.b.c@x.(y+n)::ISomething/instance-name のような実装を入力として受け取り、a.b.c@x.y::ISomething/instance-name を登録する必要があります。この実装では y+n の実装の登録解除も行う必要があります。

次のようなインターフェースがあるとします。

// IFoo.hal
package a.b.c@1.0;
interface IFoo {
    doFoo(int32_t a) generates (int64_t b);
    doSubInterface() generates (IFoo a);
};

a.b.c@1.0-adapter が提供するコードは以下のようになります。

// autogenerated code
// in namespace a::b::c::V1_0::IFoo
struct MockFoo {
    // takes some subclass of V1_0. May be V1_1, V1_2, etc...
    MockFoo(V1_0::IFoo impl) mImpl(impl) {}

    Return<int64_t> doFoo(int32_t a) {
        return this->mImpl->doFoo(a);
    }

    Return<V1_0::ICallback> doSubInterface() {
        // getMockForBinder returns MockCallback instance
        // that corresponds to a particular binder object
        // It can't return a new object every time or
        // clients using interfacesSame will have
        // divergent behavior when using the mock.
        auto _hidl_out = this->mImpl->doSubInterface();
        return getMockForBinder(_hidl_out);
    }
};

データ値は、自動生成されたモッククラスに(またはそれらのモッククラスから)正確に転送されます。ただし、サブ インターフェースは返されるため例外となります。それらのインターフェースは、対応する最新のコールバック オブジェクトでラップする必要があります。