フレームワークを条件付きでコンパイルするために使用するビルドフラグはすべて、HIDL を使用して記述する必要があります。関連するビルドフラグはグループ化し、単一の .hal ファイルに含める必要があります。HIDL を使用して構成アイテムを指定すると、次のようなメリットがあります。
- バージョンの管理(新しい構成アイテムを追加するには、ベンダーと OEM は HAL を明示的に拡張する必要があります)
- ドキュメントの整備
- SELinux を使用したアクセス制御
- Vendor Test Suite による構成アイテムのサニティ チェック(範囲チェック、アイテム間の依存関係チェックなど)
- C++ と Java の両方で自動生成される API
フレームワークで使用されるビルドフラグを特定する
まず、フレームワークを条件付きでコンパイルするために使用するビルド構成を特定してから、古い構成を破棄してセットを小さくします。たとえば、surfaceflinger には次のビルドフラグのセットが特定されます。
TARGET_USES_HWC2TARGET_BOARD_PLATFORMTARGET_DISABLE_TRIPLE_BUFFERINGTARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYSNUM_FRAMEBUFFER_SURFACE_BUFFERSTARGET_RUNNING_WITHOUT_SYNC_FRAMEWORKVSYNC_EVENT_PHASE_OFFSET_NSSF_VSYNC_EVENT_PHASE_OFFSET_NSPRESENT_TIME_OFFSET_FROM_VSYNC_NSMAX_VIRTUAL_DISPLAY_DIMENSION
HAL インターフェースを作成する
サブシステムのビルド構成は HAL インターフェースを介してアクセスされますが、構成値を提供するためのインターフェースは HAL パッケージの android.hardware.configstore(現在はバージョン 1.0)でグループ化されます。たとえば、hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal で、surfaceflinger の HAL インターフェース ファイルを作成するには、次のようにします。
package android.hardware.configstore@1.0;
interface ISurfaceFlingerConfigs {
// TO-BE-FILLED-BELOW
};
.hal ファイルを作成したら、hardware/interfaces/update-makefiles.sh を実行して、新しい .hal ファイルを Android.bp ファイルと Android.mk ファイルに追加します。
ビルドフラグの関数を追加する
ビルドフラグごとに、新しい関数をインターフェースに追加します。たとえば、hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal では次のようになります。
interface ISurfaceFlingerConfigs {
disableTripleBuffering() generates(OptionalBool ret);
forceHwcForVirtualDisplays() generates(OptionalBool ret);
enum NumBuffers: uint8_t {
USE_DEFAULT = 0,
TWO = 2,
THREE = 3,
};
numFramebufferSurfaceBuffers() generates(NumBuffers ret);
runWithoutSyncFramework() generates(OptionalBool ret);
vsyncEventPhaseOffsetNs generates (OptionalUInt64 ret);
presentTimeOffsetFromSyncNs generates (OptionalUInt64 ret);
maxVirtualDisplayDimension() generates(OptionalInt32 ret);
};
関数を追加する場合:
- 名前は簡潔にする。 makefile 変数名を関数名に変換しないでください。接頭辞
TARGET_とBOARD_は不要です。 - コメントを追加する。 構成アイテムの目的、それによるフレームワークの動作の変化、有効な値、その他の関連情報をデベロッパーが理解するのに役立ちます。
関数の戻り値の型は Optional[Bool|String|Int32|UInt32|Int64|UInt64] です。型は、同じディレクトリの types.hal で定義され、値が HAL で指定されているかどうかを示すフィールドでプリミティブ値をラップします。値が HAL で指定されていない場合は、デフォルト値が使用されます。
struct OptionalString {
bool specified;
string value;
};
必要に応じて、構成アイテムのタイプを最もよく表す列挙型を定義し、その列挙型を戻り値の型として使用します。上記の例では、NumBuffers 列挙型が定義され、有効な値の数を制限しています。このようなカスタムデータ型を定義するときは、値が HAL で指定されているかどうかを示すためのフィールドや列挙型の値(たとえば、USE_DEFAULT)を追加します。
1 つのビルドフラグが HIDL の 1 つの関数になることは必須ではありません。またモジュールのオーナーは、密接に関連するビルドフラグを構造体に集約し、その構造体を返す関数を持つこともできます(そうすると関数呼び出しの回数を減らせます)。
たとえば、2 つのビルドフラグを 1 つの構造体に集約するオプションは、hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal では次のようになります。
interface ISurfaceFlingerConfigs {
// other functions here
struct SyncConfigs {
OptionalInt64 vsyncEventPhaseoffsetNs;
OptionalInt64 presentTimeoffsetFromSyncNs;
};
getSyncConfigs() generates (SyncConfigs ret);
// other functions here
};
単一の HAL 関数の代替
すべてのビルドフラグに単一の HAL 関数を使用する代わりに、HAL インターフェースでは getBoolean(string
key) や getInteger(string key) のような単純な関数も用意されています。実際の key=value ペアは別々のファイルに格納され、HAL サービスはこれらのファイルを読み取り解析して値を提供します。
この方法は簡単に定義できますが、HIDL のメリット(バージョン管理の適用、ドキュメント化の容易さ、アクセス制御)が含まれないため、推奨されません。
単一のインターフェースと複数のインターフェース
構成アイテム用の HAL インターフェースの設計には、次の 2 つの選択肢があります。
- すべての構成アイテムに対応する単一のインターフェース
- それぞれが関連する構成アイテムのセットに対応する複数のインターフェース
単一のインターフェースのほうが簡単ですが、1 つのファイルに追加される構成アイテムが増えると、管理が困難になる場合があります。さらに、アクセス制御がきめ細かく行われないため、インターフェースへのアクセス権を付与されたプロセスはすべての構成アイテムを読み取れます(構成アイテムの一部のセットへのアクセスは許可されません)。あるいは、アクセス権が付与されていない場合は、構成アイテムを読み取れません。
このような問題があるため、Android では、関連する構成アイテムのグループには、1 つの HAL インターフェースで複数のインターフェースを使用します。たとえば、surfaceflinger 関連の構成アイテムには ISurfaceflingerConfigs を、Bluetooth 関連の構成アイテムには IBluetoothConfigs を使用します。