HIDL Java

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

HIDL インターフェースは主にネイティブ コードからの使用を想定しており、その結果、HIDL では C++ の効率的なコードの自動生成に重点が置かれています。しかし、一部の Android サブシステム(テレフォニーなど)には Java の HIDL インターフェースが含まれているため、HIDL インターフェースは Java から直接使用できることも求められます。

このセクションでは、HIDL インターフェース用の Java フロントエンドについて説明し、サービスを作成、登録、使用する方法に関する詳細情報を提供します。また、HAL および Java で記述された HAL クライアントが HIDL RPC システムと相互作用する仕組みを紹介します。

クライアントの例

以下では、default というサービス名で登録されているパッケージ android.hardware.foo@1.0 内のインターフェース IFoo と、second_impl というカスタム サービス名を持つ追加サービス用のクライアントの例を示します。

ライブラリの追加

対応する HIDL スタブ ライブラリを使用したい場合、依存関係を追加する必要があります。通常、これは静的ライブラリです。

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

これらのライブラリにすでに依存関係が組み込まれていることがわかっている場合は、次のように共有リンケージも使用できます。

// in Android.bp
libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

Android 10 でライブラリを追加する際の追加の考慮事項

Android 10 以降をターゲットとするシステムアプリまたはベンダーアプリの場合、それらのライブラリは静的に追加できます。また、システムアプリ用の既存の uses-library メカニズムを使用して Java API をデバイス上で安定稼働させ、そのデバイスにインストールしたカスタム JAR の HIDL クラスのみを使用する方法もあります。後者の方法であればデバイスのスペースを節約できます。詳細については、Java SDK ライブラリの実装をご覧ください。Android 10 より前のバージョンをターゲットとするアプリの場合は、従来の動作が維持されます。

Android 10 以降では、これらのライブラリの「浅い」バージョンも利用できます。これは、該当のクラスは含まれているが依存元のクラスは含まれていないバージョンです。たとえば、android.hardware.foo-V1.0-java-shallow には foo パッケージのクラスが含まれていますが、すべての HIDL インターフェースの基本クラスなど、android.hidl.base-V1.0-java 内のクラスは含まれていません。ライブラリを作成する際に、必要なインターフェースの基本クラスを依存関係として利用する場合は、次のように指定します。

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java-shallow", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java-shallow

HIDL のベース ライブラリとマネージャー ライブラリは、アプリのブート クラスパス上で利用できなくなり(これらは以前、Android のデリゲート ファースト クラスローダーが原因で、非表示 API として使用されることがありました)、jarjar を使用する新しい名前空間に移されました。これらを使用するアプリ(必然的に priv-apps)で個別のコピーを用意する必要があります。ブート クラスパス上のモジュールは、HIDL を使用している場合、これらの Java ライブラリの「浅い」バリアントを使用し、jarjar_rules: ":framework-jarjar-rules"Android.bp に追加する必要があります。これは、ブート クラスパスに存在するこれらのライブラリのバージョンを使用するためです。

Java ソースの変更

このサービスのバージョンは 1 つ(@1.0)のみであるため、次のコードはそのバージョンのみを取得します。サービスの複数のバージョンを処理する方法については、インターフェース拡張をご覧ください。

import android.hardware.foo.V1_0.IFoo;
...
// retry to wait until the service starts up if it is in the manifest
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IFoo anotherServer = IFoo.getService("second_impl", true /* retry */);
server.doSomething(…);

サービスの提供

フレームワーク コードを Java で記述する場合、HAL からの非同期コールバックを受信するインターフェースを提供する必要があります。

android.hardware.foo パッケージのバージョン 1.0 の IFooCallback インターフェースを Java で実装する場合は、次の手順で行います。

  1. HIDL でインターフェースを定義します。
  2. 参考用に /tmp/android/hardware/foo/IFooCallback.java を開きます。
  3. Java 実装用の新しいモジュールを作成します。
  4. 抽象クラス android.hardware.foo.V1_0.IFooCallback.Stub を確認します。次に拡張用の新しいクラスを作成して、抽象メソッドを実装します。

自動生成されたファイルの表示

自動生成されたファイルを表示するには、次のコマンドを実行します。

hidl-gen -o /tmp -Ljava \
  -randroid.hardware:hardware/interfaces \
  -randroid.hidl:system/libhidl/transport android.hardware.foo@1.0

このコマンドにより、ディレクトリ /tmp/android/hardware/foo/1.0 が生成されます。ファイル hardware/interfaces/foo/1.0/IFooCallback.hal の場合、ファイル /tmp/android/hardware/foo/1.0/IFooCallback.java が生成されます。このファイルでは、Java インターフェース、プロキシコード、スタブがカプセル化されます(プロキシとスタブは両方ともインターフェースに適合したものになります)。

-Lmakefile により、ビルド時にこのコマンドを実行するルールが生成され、android.hardware.foo-V1.0-java をインクルードして適切なファイルにリンクできるようになります。多数のインターフェースを含むプロジェクトでこの処理を自動的に行うためのスクリプトが hardware/interfaces/update-makefiles.sh にあります。上記の例のパスは相対パスです。hardware/interfaces は、公開前の HAL の開発に使用するコードツリーの下の一時ディレクトリに置き換えることができます。

サービスの実行

HAL が提供する IFoo インターフェースは、IFooCallback インターフェースを介してフレームワークに非同期コールバックを行う必要があります。IFooCallback インターフェースは、検出可能なサービスとして名前で登録されていません。代わりに、IFoo には setFooCallback(IFooCallback x) などのメソッドを含める必要があります。

バージョン 1.0 の android.hardware.foo パッケージから IFooCallback を設定するには、Android.mkandroid.hardware.foo-V1.0-java を追加します。サービスを実行するコードは次のとおりです。

import android.hardware.foo.V1_0.IFoo;
import android.hardware.foo.V1_0.IFooCallback.Stub;
....
class FooCallback extends IFooCallback.Stub {
    // implement methods
}
....
// Get the service from which you will be receiving callbacks.
// This also starts the threadpool for your callback service.
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
....
// This must be a persistent instance variable, not local,
//   to avoid premature garbage collection.
FooCallback mFooCallback = new FooCallback();
....
// Do this once to create the callback service and tell the "foo-bar" service
server.setFooCallback(mFooCallback);

インターフェース拡張

あるサービスがすべてのデバイスで IFoo インターフェースを実装しているとします。この場合、特定のデバイスで、インターフェース拡張 IBetterFoo に実装されている追加機能をサービスで提供するには、次のようにします。

interface IFoo {
   ...
};

interface IBetterFoo extends IFoo {
   ...
};

拡張されたインターフェースに対応できるコードを呼び出す際に、castFrom() Java メソッドを使用して、拡張されたインターフェースに基本インターフェースを安全にキャストできます。

IFoo baseService = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IBetterFoo extendedService = IBetterFoo.castFrom(baseService);
if (extendedService != null) {
  // The service implements the extended interface.
} else {
  // The service implements only the base interface.
}