インターフェースとパッケージ

HIDL は、オブジェクト指向言語で動作を定義するために使用される、抽象型のインターフェースを基に構築されています。各インターフェースはパッケージの一部です。

パッケージ

パッケージ名には、package.subpackage のようにサブレベルを含めることができます。公開されている HIDL パッケージのルート ディレクトリは、hardware/interfaces または vendor/vendorName です(Google Pixel デバイスの場合は vendor/google)。このパッケージ名により、ルート ディレクトリの下に 1 つ以上のサブ ディレクトリが作成されます。パッケージを定義するすべてのファイルが同じディレクトリにあります。たとえば、package android.hardware.example.extension.light@2.0hardware/interfaces/example/extension/light/2.0 の下にあります。

パッケージのプレフィックスと場所を次の表に示します。

パッケージ プレフィックス 場所 インターフェース型
android.hardware.* hardware/interfaces/* HAL
android.frameworks.* frameworks/hardware/interfaces/* フレームワーク、またはその関連
android.system.* system/hardware/interfaces/* システム、またはその関連
android.hidl.* system/libhidl/transport/* コア

package ディレクトリには、拡張子が .hal であるファイルが含まれます。すべてのファイルには、ファイルが属するパッケージ名とバージョン名を指定する package ステートメントが含まれている必要があります。types.hal ファイルが存在する場合は、インターフェースを定義するのではなく、パッケージ内のすべてのインターフェースがアクセスできるデータ型を定義します。

インターフェースの定義

types.hal を除いて、他のすべての .hal ファイルはインターフェースを定義します。インターフェースは通常、次のように定義されます。

interface IBar extends IFoo { // IFoo is another interface
    // embedded types
    struct MyStruct {/*...*/};

    // interface methods
    create(int32_t id) generates (MyStruct s);
    close();
};

明示的な extends 宣言のないインターフェースは、暗黙的に android.hidl.base@1.0::IBase から拡張されます(Java の java.lang.Object と同様です)。暗黙的にインポートされた IBase インターフェースは、ユーザー定義インターフェースで再宣言できない、または他の方法で使用できない予約済みメソッドを宣言します。これらのメソッドには次のものが含まれます。

  • ping
  • interfaceChain
  • interfaceDescriptor
  • notifySyspropsChanged
  • linkToDeath
  • unlinkToDeath
  • setHALInstrumentation
  • getDebugInfo
  • debug
  • getHashChain

インポート

import ステートメントは HIDL メカニズムで、別のパッケージ内のパッケージのインターフェースと型にアクセスします。import ステートメントは次の 2 つのエンティティに関係します。

  • インポートするエンティティ。パッケージまたはインターフェースのいずれかです。
  • インポートされるエンティティ。これもパッケージまたはインターフェースのいずれかです。

インポートするエンティティは、import ステートメントの場所によって決まります。ステートメントがパッケージの types.hal 内にある場合、インポートされる内容は、パッケージ全体で表示されます。これはパッケージ レベルのインポートです。ステートメントがインターフェース ファイル内にある場合、インポートするエンティティはインターフェース自体です。これはインターフェース レベルのインポートです。

インポートされるエンティティは、import キーワードの後の値によって決まります。この値は、完全修飾名である必要はありません。コンポーネントが省略された場合、現在のパッケージの情報が自動的に入力されます。完全修飾された値の場合、次のインポート ケースがサポートされます。

  • パッケージ全体のインポート。値がパッケージ名とバージョンである場合(構文は以下に記載)、パッケージ全体が、インポートするエンティティにインポートされます。
  • 部分的なインポート。値によって次のように異なります。
    • 値がインターフェースの場合、パッケージの types.hal とそのインターフェースが、インポートするエンティティにインポートされます。
    • 値が types.hal で定義された UDT の場合、その UDT のみが、インポートするエンティティにインポートされます(types.hal の他の型はインポートされません)。
  • 型のみのインポート。値が上記の部分的なインポートの構文を使用し、インターフェース名ではなく types キーワードを使用する場合、指定パッケージの types.hal 内の UDT のみがインポートされます。

インポートするエンティティは、次の組み合わせにアクセスできます。

  • インポートされるパッケージの、types.hal で定義された共通 UDT。
  • インポートされるパッケージのインターフェース(パッケージ全体のインポートの場合)、または指定されたインターフェース(部分的なインポートの場合)。これはインターフェースを呼び出し、ハンドルを渡し、継承するためです。

import ステートメントは、完全修飾型名構文を使用して、インポートされるパッケージまたはインターフェースの名前とバージョンを指定します。

import android.hardware.nfc@1.0;            // import a whole package
import android.hardware.example@1.0::IQuux; // import an interface and types.hal
import android.hardware.example@1.0::types; // import just types.hal

インターフェースの継承

以前に定義されたインターフェースから、インターフェースを拡張することもできます。 拡張は、次の 3 つのタイプのいずれかになります。

  • インターフェースは、API を変更せずに組み込んで、別のインターフェースに機能を追加できます。
  • パッケージは、API を変更せずに組み込んで、別のパッケージに機能を追加できます。
  • インターフェースは、パッケージまたは特定のインターフェースから型をインポートできます。

インターフェースは他の 1 つのインターフェースのみ拡張できます。複数の継承はできません。 0 以外のマイナー バージョン番号のパッケージ内の各インターフェースは、以前のバージョンのパッケージ内でインターフェースを拡張する必要があります。たとえば、パッケージ derivative のバージョン 4.0 のインターフェース IBar が、パッケージ original のバージョン 1.2 のインターフェース IFoo をベースにしていて(拡張していて)、パッケージ original のバージョン 1.3 が作成される場合、IBar バージョン 4.1 は IFoo バージョン 1.3 を拡張できません。代わりに、IBar バージョン 4.1 は、IFoo バージョン 1.2 と関連付けられている IBar バージョン 4.0 を拡張する必要があります。必要な場合は、IBar バージョン 5.0 は IFoo バージョン 1.3 を拡張できます。

インターフェースの拡張は、生成されたコードにライブラリ依存関係や HAL 間のインクルードがあることを意味しません。データ構造とメソッド定義を HIDL レベルでインポートするだけです。HAL のすべてのメソッドは、その HAL に実装する必要があります。

ベンダー拡張

ベンダー拡張は、拡張するコア インターフェースを表す基本オブジェクトのサブクラスとして実装される場合があります。同じオブジェクトが、ベースの HAL 名およびバージョンと、拡張の(ベンダー)HAL 名およびバージョンで登録されます。

バージョニング

パッケージはバージョン管理され、インターフェースはパッケージのバージョンを持ちます。 バージョンは、major と minor の 2 つの整数型で表されます。

  • メジャー バージョンには下位互換性がありません。メジャー バージョン番号が増えると、マイナー バージョン番号は 0 にリセットされます。
  • マイナー バージョンには下位互換性があります。マイナー番号が増えても、新しいバージョンは以前のバージョンと完全に下位互換性があります。新しいデータ構造とメソッドを追加できますが、既存のデータ構造やメソッド シグネチャは変更できません。

HAL の複数のメジャー バージョンまたはマイナー バージョンがデバイス上に同時に存在していてもかまいません。ただし、以前のマイナー バージョンのインターフェースで動作するクライアント コードは、同じインターフェースのそれ以降のマイナー バージョンでも動作するため、メジャー バージョンよりもマイナー バージョンが優先されます。バージョニングとベンダー拡張の詳細については、HIDL のバージョニングをご覧ください。

インターフェース レイアウトの概要

このセクションでは、HIDL インターフェース パッケージ(hardware/interfaces など)を管理する方法について要約し、HIDL セクション全体で説明した情報をまとめます。このセクションを読む前に、HIDL のバージョニングhidl-gen を使用したハッシュ化におけるハッシュの概念、一般的な HIDL の使用の詳細、および次の定義について理解してください。

用語 定義
アプリケーション バイナリ インターフェース(ABI) アプリケーション プログラミング インターフェースとバイナリ リンケージが必要です。
完全修飾名(fqName) hidl 型を識別するための名前。例: android.hardware.foo@1.0::IFoo
パッケージ HIDL インターフェースと型を含むパッケージ。例: android.hardware.foo@1.0
パッケージ ルート HIDL インターフェースを含むルート パッケージ。例: HIDL インターフェース android.hardware はパッケージ ルートの android.hardware.foo@1.0 にあります。
パッケージ ルートのパス パッケージ ルートがマッピングされる Android ソースツリー内の場所。

詳細な定義については、HIDL の用語を参照してください。

すべてのファイルを、パッケージ ルートのマッピングと完全修飾名から見つけることが可能

パッケージ ルートは -r android.hardware:hardware/interfaces 引数として hidl-gen に指定されています。たとえば、パッケージが vendor.awesome.foo@1.0::IFoo で、hidl-gen-r vendor.awesome:some/device/independent/path/interfaces が送信された場合、インターフェース ファイルは $ANDROID_BUILD_TOP/some/device/independent/path/interfaces/foo/1.0/IFoo.hal に配置されます。

実際には、awesome という名前のベンダーまたは OEM の場合、標準インターフェースを vendor.awesome に置くことをおすすめします。パッケージパスが選択された後は、インターフェースの ABI に書き込まれるため変更できません。

パッケージパスのマッピングは一意にする

たとえば、-rsome.package:$PATH_A-rsome.package:$PATH_B がある場合、一貫したインターフェース ディレクトリにするために、$PATH_A$PATH_B と同一にする必要があります(これにより、インターフェースのバージョニングも容易になります)。

パッケージ ルートにはバージョニング ファイルが必要

たとえば、-r vendor.awesome:vendor/awesome/interfaces というパッケージパスを作成する場合、$ANDROID_BUILD_TOP/vendor/awesome/interfaces/current.txt というファイルを作成し、その中に hidl-gen-Lhash オプションを使用して作成したインターフェースのハッシュを含める必要があります(これについては、hidl-gen を使用したハッシュ化で詳しく説明しています)。

インターフェースをデバイスに依存しない場所に配置する

実際には、ブランチ間でインターフェースを共有することをおすすめします。これにより、異なるデバイスやユースケースで、最大限のコードの再利用とコードのテストが可能になります。