HIDL C++

Android O では Android OS が再構築され、デバイスに依存しない Android プラットフォームとデバイスおよびベンダーに固有のコードとの間に明確なインターフェースが定義されています。このようなインターフェースは、HAL インターフェースの形式ですでに数多く定義されており、C ヘッダーとして hardware/libhardware で定義されていますが、HIDL により、これらの HAL インターフェースがバージョニングされた安定したインターフェースに置き換えられました。その結果、C++(後述)または Java のクライアント側 / サーバー側 HIDL インターフェースとして使用できます。

このセクションでは、hidl-gen コンパイラによって HIDL .hal ファイルから自動生成されたファイルの詳細、これらのファイルのパッケージ方法、およびこれらのファイルを使用する C++ コードとの統合方法など、HIDL インターフェースの C++ 実装について説明します。

クライアントとサーバーの実装

HIDL インターフェースには、次のようなクライアントとサーバーの実装があります。

  • HIDL インターフェースのクライアントは、メソッドを呼び出すことによりインターフェースを使用するコードです。
  • サーバーは、クライアントからの呼び出しを受信し、(必要な場合に)結果を返す HIDL インターフェースの実装です。

libhardware HAL から HIDL HAL に移行すると、HAL 実装がサーバーになり、HAL を呼び出すプロセスがクライアントになります。デフォルトの実装では、パススルーとバインドされた HAL の両方を提供し、次のように経時的に変化します。

図 1. レガシー HAL の開発の進行

HAL クライアントの作成

最初に、次のように makefile に HAL ライブラリを含めます。

  • Make: LOCAL_SHARED_LIBRARIES += android.hardware.nfc@1.0
  • Soong: shared_libs: [ …, android.hardware.nfc@1.0 ]

次に、このように HAL ヘッダー ファイルを含めます。

    #include <android/hardware/nfc/1.0/IFoo.h>
    …
    // in code:
    sp<IFoo> client = IFoo::getService();
    client->doThing();
    

HAL サーバーの作成

HAL 実装を作成するには、HAL を表す .hal ファイルが存在し、hidl-gen-Lmakefile または -Landroidbp を使用して HAL の makefile をすでに生成している必要があります(./hardware/interfaces/update-makefiles.sh は内部 HAL ファイルに対してこれを行う適切なリファレンスです)。libhardware から HAL を移行する場合は、c2hal を使用すると大部分の作業を簡単に行えます。

HAL の実装に必要なファイルを作成するには、次のように設定します。

    PACKAGE=android.hardware.nfc@1.0
    LOC=hardware/interfaces/nfc/1.0/default/
    m -j hidl-gen
    hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces \
        -randroid.hidl:system/libhidl/transport $PACKAGE
    hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces \
        -randroid.hidl:system/libhidl/transport $PACKAGE
    

HAL がパススルー モードで動作するためには、/(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so 内に関数 HIDL_FETCH_IModuleName が必要です(OPTIONAL_IDENTIFIER はパススルーの実装を識別する文字列)。パススルー モードの要件は、上記のコマンドによって自動的に満たされます。このコマンドで android.hardware.nfc@1.0-impl ターゲットも作成されますが、任意の拡張を使用できます。たとえば、android.hardware.nfc@1.0-impl-foo-foo を使用してそれ自体を区別します。

マイナー バージョンまたは別の HAL の拡張である HAL については、ベースとなる HAL を使用してこのバイナリに名前を付ける必要があります。たとえば、android.hardware.graphics.mapper@2.1 の実装は引き続き android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER) というバイナリに含める必要があります。通常、この OPTIONAL_IDENTIFIER には実際の HAL バージョンが含まれます。バイナリにこのような命名をすることで、2.0 クライアントが直接取得できるようになり、2.1 クライアントは実装をアップキャストできます。

次に、スタブに機能を入力し、デーモンを設定します。デーモンコードの例(パススルーをサポート)を次に示します。

    #include <hidl/LegacySupport.h>

    int main(int /* argc */, char* /* argv */ []) {
        return defaultPassthroughServiceImplementation<INfc>("nfc");
    }
    

defaultPassthroughServiceImplementation は提供された -impl ライブラリを dlopen() し、バインドされたサービスとして提供します。デーモンコードの例(バインドされた純粋なサービスの場合)を次に示します。

    int main(int /* argc */, char* /* argv */ []) {
        // This function must be called before you join to ensure the proper
        // number of threads are created. The threadpool will never exceed
        // size one because of this call.
        ::android::hardware::configureRpcThreadpool(1 /*threads*/, true /*willJoin*/);

        sp nfc = new Nfc();
        const status_t status = nfc->registerAsService();
        if (status != ::android::OK) {
            return 1; // or handle error
        }

        // Adds this thread to the threadpool, resulting in one total
        // thread in the threadpool. We could also do other things, but
        // would have to specify 'false' to willJoin in configureRpcThreadpool.
        ::android::hardware::joinRpcThreadpool();
        return 1; // joinRpcThreadpool should never return
    }
    

このデーモンは通常 $PACKAGE + "-service-suffix"(たとえば android.hardware.nfc@1.0-service)に置かれますが、どこにでも配置できます。HAL の特定のクラスに対する sepolicy は、属性 hal_<module>hal_nfc) など)です。この属性は、特定の HAL を実行するデーモンに適用する必要があります(同じプロセスが複数の HAL に対応する場合、複数の属性を適用します)。