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<INfc> 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 に対応する場合、複数の属性を適用できます)。