HIDL は、オブジェクト指向言語で動作を定義するために使用される、抽象型のインターフェースを基に構築されています。各インターフェースはパッケージの一部です。
パッケージ
パッケージ名には、package.subpackage
のようにサブレベルを含めることができます。公開されている HIDL パッケージのルート ディレクトリは、hardware/interfaces
または vendor/vendorName
です(Google Pixel デバイスの場合は vendor/google
)。このパッケージ名により、ルート ディレクトリの下に 1 つ以上のサブ ディレクトリが作成されます。パッケージを定義するすべてのファイルが同じディレクトリにあります。たとえば、package android.hardware.example.extension.light@2.0
は hardware/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 を使用したハッシュ化で詳しく説明しています)。
インターフェースをデバイスに依存しない場所に配置する
実際にはブランチ間でインターフェースを共有することをおすすめします。これにより、異なるデバイスやユースケースで、最大限のコードの再利用とコードのテストが可能になります。