車載カメラ HAL

Android には、Android の起動プロセスの非常に早い段階で画像のキャプチャと表示を提供し、システムの存続期間中機能し続ける車載用 HIDL ハードウェア抽象化レイヤー (HAL) が含まれています。 HAL にはエクステリア ビュー システム (EVS) スタックが含まれており、通常、Android ベースの車載インフォテインメント (IVI) システムを搭載した車両でリアビュー カメラとサラウンド ビュー ディスプレイをサポートするために使用されます。 EVS を使用すると、ユーザー アプリに高度な機能を実装することもできます。

Android には、EVS 固有のキャプチャおよびディスプレイ ドライバー インターフェイス ( /hardware/interfaces/automotive/evs/1.0内) も含まれています。既存の Android カメラおよび表示サービスの上にリアビュー カメラ アプリを構築することは可能ですが、そのようなアプリは Android の起動プロセスで実行が遅すぎる可能性があります。専用の HAL を使用すると、インターフェースが合理化され、EVS スタックをサポートするために OEM が何を実装する必要があるかが明確になります。

システムコンポーネント

EVS には次のシステム コンポーネントが含まれています。

EVS システムのコンポーネント図

図 1. EVS システム コンポーネントの概要。

EVSアプリ

サンプル C++ EVS アプリ ( /packages/services/Car/evs/app ) は、リファレンス実装として機能します。このアプリは、EVS Manager からビデオ フレームをリクエストし、表示用に完成したフレームを EVS Manager に送信する役割を果たします。 EVS と Car Service が利用可能になり次第、電源投入後 2 秒以内に init によって開始されることが期待されます。 OEM は、必要に応じて EVS アプリを変更または置き換えることができます。

EVSマネージャー

EVS マネージャー ( /packages/services/Car/evs/manager ) は、単純なリアビュー カメラ ディスプレイから 6DOF マルチカメラ レンダリングまで、あらゆるものを実装するために EVS アプリに必要なビルディング ブロックを提供します。そのインターフェイスは HIDL を通じて提供され、複数の同時クライアントを受け入れるように構築されています。他のアプリやサービス (特に Car Service) は、EVS Manager の状態をクエリして、EVS システムがいつアクティブになっているかを確認できます。

EVS HIDL インターフェイス

EVS システム (カメラとディスプレイ要素の両方) は、 android.hardware.automotive.evsパッケージで定義されます。インターフェイスを実行する (合成テスト イメージを生成し、イメージが往復することを検証する) サンプル実装は、 /hardware/interfaces/automotive/evs/1.0/defaultに提供されています。

OEM は/hardware/interfaces/automotive/evsの .hal ファイルによって表現される API を実装する責任があります。このような実装は、物理カメラからデータを構成および収集し、Gralloc が認識できる共有メモリ バッファを介してデータを配信する責任を負います。実装のディスプレイ側は、アプリ (通常は EGL レンダリング経由) で埋めることができる共有メモリ バッファーを提供し、物理ディスプレイに表示する可能性のある他のものよりも優先して完成したフレームを表示する責任があります。 EVS インターフェースのベンダー実装は/vendor/… /device/…またはhardware/… (例: /hardware/[vendor]/[platform]/evs ) に保存できます。

カーネルドライバー

EVS スタックをサポートするデバイスにはカーネル ドライバーが必要です。 OEM には、新しいドライバーを作成する代わりに、既存のカメラやディスプレイのハードウェア ドライバーを介して EVS に必要な機能をサポートするオプションがあります。ドライバーの再利用は、特に画像の表示に他のアクティブなスレッドとの調整が必要なディスプレイ ドライバーの場合に有利になる可能性があります。 Android 8.0 には、v4l2 ベースのサンプル ドライバー ( packages/services/Car/evs/sampleDriver内) が含まれています。これは、v4l2 サポートについてはカーネルに依存し、出力イメージの表示については SurfaceFlinger に依存します。

EVS ハードウェア インターフェイスの説明

このセクションでは HAL について説明します。ベンダーは、自社のハードウェアに適合したこの API の実装を提供することが期待されます。

IEvs列挙子

このオブジェクトは、システム内で利用可能な EVS ハードウェア (1 つ以上のカメラと 1 つのディスプレイ デバイス) を列挙する役割を果たします。

getCameraList() generates (vec<CameraDesc> cameras);

システム内のすべてのカメラの説明を含むベクトルを返します。カメラのセットは固定されており、起動時に認識できると想定されています。カメラの説明の詳細については、 CameraDescを参照してください。

openCamera(string camera_id) generates (IEvsCamera camera);

一意のカメラ ID文字列によって識別される特定のカメラと対話するために使用されるインターフェイス オブジェクトを取得します。失敗した場合は NULL を返します。すでに開いているカメラを再度開いても失敗することはありません。アプリの起動とシャットダウンに関連する競合状態を回避するには、カメラを再度開くと、新しいリクエストを実行できるように前のインスタンスをシャットダウンする必要があります。この方法でプリエンプトされたカメラ インスタンスは、最終的な破棄を待って非アクティブ状態にし、カメラの状態に影響を与えるリクエストにOWNERSHIP_LOSTのリターン コードで応答する必要があります。

closeCamera(IEvsCamera camera);

IEvsCamera インターフェイスを解放します ( openCamera()呼び出しの逆です)。カメラのビデオ ストリームは、 closeCameraを呼び出す前にstopVideoStream()を呼び出して停止する必要があります。

openDisplay() generates (IEvsDisplay display);

システムの EVS ディスプレイと排他的に対話するために使用されるインターフェイス オブジェクトを取得します。一度に IEvsDisplay の機能インスタンスを保持できるクライアントは 1 つだけです。 openCameraで説明されている積極的なオープン動作と同様に、新しい IEvsDisplay オブジェクトがいつでも作成される可能性があり、以前のインスタンスはすべて無効になります。無効化されたインスタンスは引き続き存在し、その所有者からの関数呼び出しに応答しますが、終了したときに変更操作を実行する必要はありません。最終的に、クライアント アプリはOWNERSHIP_LOSTエラー戻りコードに気づき、非アクティブなインターフェイスを閉じて解放することが期待されます。

closeDisplay(IEvsDisplay display);

IEvsDisplay インターフェイスを解放します ( openDisplay()呼び出しの逆です)。 getTargetBuffer()呼び出しを介して受信した未処理のバッファは、ディスプレイを閉じる前にディスプレイに返す必要があります。

getDisplayState() generates (DisplayState state);

現在の表示状態を取得します。 HAL 実装は実際の現在の状態を報告する必要がありますが、これは最後に要求された状態とは異なる場合があります。表示状態の変更を担当するロジックはデバイス層の上に存在する必要があるため、HAL 実装が表示状態を自発的に変更することは望ましくありません。 (openDisplay の呼び出しによって) ディスプレイが現在どのクライアントにも保持されていない場合、この関数はNOT_OPENを返します。それ以外の場合は、EVS ディスプレイの現在の状態を報告します ( IEvsDisplay APIを参照)。

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id 。特定のカメラを一意に識別する文字列。デバイスのカーネル デバイス名、またはデバイスの名前 ( Rearviewなど) を指定できます。この文字列の値は HAL 実装によって選択され、上記のスタックによって不透明に使用されます。
  • vendor_flags 。特殊なカメラ情報をドライバーからカスタム EVS アプリに不透明に渡す方法。これはドライバーから解釈されずに EVS アプリに渡されますが、無視しても構いません。

IEvsカメラ

このオブジェクトは単一のカメラを表し、画像をキャプチャするための主要なインターフェイスです。

getCameraInfo() generates (CameraDesc info);

このカメラのCameraDescを返します。

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

カメラがサポートするよう要求されるバッファ チェーンの深さを指定します。 IEvsCamera のクライアントは、この数までのフレームを同時に保持できます。この多数のフレームが、 doneWithFrameによって返されずに受信者に配信された場合、ストリームは、再利用のためにバッファが返されるまでフレームをスキップします。ストリームが既に実行中であっても、いつでもこの呼び出しを行うことは合法であり、その場合は、バッファーをチェーンに必要に応じて追加またはチェーンから削除する必要があります。このエントリ ポイントへの呼び出しが行われない場合、IEvsCamera はデフォルトで少なくとも 1 つのフレームをサポートします。より受け入れられます。

要求されたbufferCountに対応できない場合、関数はBUFFER_NOT_AVAILABLEまたはその他の関連エラーコードを返します。この場合、システムは以前に設定された値で動作し続けます。

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

このカメラから EVS カメラ フレームの配信を要求します。 IEvsCameraStream はstopVideoStream()が呼び出されるまで、新しい画像フレームによる定期的な呼び出しの受信を開始します。フレームはstartVideoStream呼び出しから 500 ミリ秒以内に配信を開始する必要があり、開始後は最低 10 FPS で生成する必要があります。ビデオ ストリームの開始に必要な時間は、バックミラー カメラの起動時間要件に対して実質的にカウントされます。ストリームが開始されていない場合は、エラー コードを返す必要があります。それ以外の場合は OK が返されます。

oneway doneWithFrame(BufferDesc buffer);

IEvsCameraStream に配信されたフレームを返します。 IEvsCameraStream インターフェイスに配信されたフレームの消費が完了したら、再利用のためにフレームを IEvsCamera に返す必要があります。利用可能なバッファーの数は限られており (1 つ程度の場合もあります)、バッファーが使い果たされると、バッファーが返されるまでそれ以上のフレームは配信されず、フレームがスキップされる可能性があります (ヌル ハンドルを持つバッファーは終了を示します)。ストリームのデータであり、この関数を通じて返す必要はありません)。成功すると OK を返すか、 INVALID_ARGまたはBUFFER_NOT_AVAILABLEを含む可能性のある適切なエラー コードを返します。

stopVideoStream();

EVS カメラ フレームの配信を停止します。配信は非同期であるため、この呼び出しが戻った後もしばらくフレームが到着し続ける可能性があります。各フレームは、ストリームの終了が IEvsCameraStream に通知されるまで返される必要があります。すでに停止されているストリーム、または開始されていないストリームに対してstopVideoStream呼び出すことは合法ですが、その場合は無視されます。

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

HAL 実装からドライバー固有の情報を要求します。 opaqueIdentifierに許可される値はドライバー固有ですが、値を渡さないとドライバーがクラッシュする可能性があります。ドライバーは、認識されないopaqueIdentifierに対して 0 を返す必要があります。

setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);

ドライバー固有の値を HAL 実装に送信します。この拡張は、車両固有の拡張を容易にするためにのみ提供されており、デフォルト状態で機能するためにこの呼び出しを必要とする HAL 実装はありません。ドライバーが値を認識して受け入れる場合は、OK が返される必要があります。それ以外の場合は、 INVALID_ARGまたはその他の代表的なエラー コードが返されるはずです。

struct BufferDesc {
    uint32  width;      // Units of pixels
    uint32  height;     // Units of pixels
    uint32  stride;     // Units of pixels
    uint32  pixelSize;  // Size of single pixel in bytes
    uint32  format;     // May contain values from android_pixel_format_t
    uint32  usage;      // May contain values from Gralloc.h
    uint32  bufferId;   // Opaque value
    handle  memHandle;  // gralloc memory buffer handle
}

APIを介して渡される画像を記述します。 HAL ドライブは、画像バッファを記述するためにこの構造体を埋める役割を果たし、HAL クライアントはこの構造体を読み取り専用として扱う必要があります。これらのフィールドには、クライアントがeglCreateImageKHR()拡張機能を介して EGL でイメージを使用するために必要となるANativeWindowBufferオブジェクトを再構築できるようにするのに十分な情報が含まれています。

  • width 。表示される画像のピクセル単位の幅。
  • height 。表示される画像のピクセル単位の高さ。
  • stride 。各行がメモリ内で実際に占めるピクセル数。行の配置のためのパディングが考慮されます。 gramloc がバッファ記述に採用している規則に一致するようにピクセル単位で表現されます。
  • pixelSize 。個々のピクセルが占めるバイト数。画像内の行間を移動するのに必要なバイト単位のサイズを計算できます (バイト単位のstride = ピクセル単位のstride * pixelSize )。
  • format 。画像で使用されるピクセル形式。提供される形式は、プラットフォームの OpenGL 実装と互換性がある必要があります。互換性テストに合格するには、カメラの使用にはHAL_PIXEL_FORMAT_YCRCB_420_SPを、ディスプレイにはRGBAまたはBGRA優先する必要があります。
  • usage 。 HAL 実装によって設定される使用フラグ。 HAL クライアントは、これらを変更せずに渡すことが期待されます (詳細については、 Gralloc.h関連フラグを参照してください)。
  • bufferId 。 HAL API のラウンドトリップ後にバッファを認識​​できるようにするために、HAL 実装によって指定される一意の値。このフィールドに格納される値は、HAL 実装によって任意に選択できます。
  • memHandle 。画像データを含む基礎となるメモリ バッファのハンドル。 HAL 実装は、ここに Gralloc バッファ ハンドルを保存することを選択する場合があります。

IEvsカメラストリーム

クライアントは、非同期ビデオ フレーム配信を受信するためにこのインターフェイスを実装します。

deliverFrame(BufferDesc buffer);

ビデオ フレームの検査の準備ができるたびに、HAL からの呼び出しを受信します。このメソッドによって受信されたバッファ ハンドルはIEvsCamera::doneWithFrame()への呼び出しを介して返される必要があります。 IEvsCamera::stopVideoStream()への呼び出しによってビデオ ストリームが停止された場合、パイプラインのドレイン時にこのコールバックが続行される可能性があります。各フレームは依然として返される必要があります。ストリーム内の最後のフレームが配信されると、NULL のbufferHandle が配信され、ストリームの終了を示し、それ以上のフレーム配信は行われません。 NULL のbufferHandle 自体はdoneWithFrame()経由で送り返す必要はありませんが、他のすべてのハンドルは返される必要があります

独自のバッファ形式も技術的には可能ですが、互換性テストでは、バッファがサポートされている 4 つの形式のいずれかである必要があります: NV21 (YCrCb 4:2:0 Semi-Planar)、YV12 (YCrCb 4:2:0 Planar)、YUYV (YCrCb 4: 2:2 インターリーブ)、RGBA (32 ビット R:G:B:x)、BGRA (32 ビット B:G:R:x)。選択した形式は、プラットフォームの GLES 実装上で有効な GL テクスチャ ソースである必要があります。

アプリは、 bufferIdフィールドとBufferDesc構造内のmemHandle間の対応関係に依存しないでください。基本的に、 bufferId値は HAL ドライバー実装にとってプライベートであり、必要に応じて使用 (および再利用) できます。

IEvsDisplay

このオブジェクトは Evs ディスプレイを表し、ディスプレイの状態を制御し、画像の実際のプレゼンテーションを処理します。

getDisplayInfo() generates (DisplayDesc info);

システムによって提供される EVS ディスプレイに関する基本情報を返します ( DisplayDescを参照)。

setDisplayState(DisplayState state) generates (EvsResult result);

表示状態を設定します。クライアントは、目的の状態を表現するために表示状態を設定できます。HAL 実装は、他の状態にあるときは、その要求を無視する応答が返される場合でも、その状態に対する要求を適切に受け入れる必要があります。

初期化時に、ディスプレイはNOT_VISIBLE状態で開始するように定義されており、その後クライアントはVISIBLE_ON_NEXT_FRAME状態を要求してビデオの提供を開始することが期待されます。ディスプレイが不要になった場合、クライアントは最後のビデオ フレームを通過した後にNOT_VISIBLE状態を要求することが期待されます。

いつでもどの状態でもリクエストできます。ディスプレイがすでに表示されている場合は、 VISIBLE_ON_NEXT_FRAMEに設定すると表示されたままになります。要求された状態が認識できない列挙値でない限り、常に OK を返します。その場合は、 INVALID_ARGが返されます。

getDisplayState() generates (DisplayState state);

表示状態を取得します。 HAL 実装は実際の現在の状態を報告する必要がありますが、これは最後に要求された状態とは異なる場合があります。表示状態の変更を担当するロジックはデバイス層の上に存在する必要があるため、HAL 実装が表示状態を自発的に変更することは望ましくありません。

getTargetBuffer() generates (handle bufferHandle);

ディスプレイに関連付けられたフレーム バッファーへのハンドルを返します。このバッファは、ソフトウェアや GL によってロックされ、書き込まれる可能性があります。このバッファは、ディスプレイが表示されなくなった場合でも、 returnTargetBufferForDisplay()の呼び出しを介して返される必要があります。

独自のバッファ形式も技術的には可能ですが、互換性テストでは、バッファがサポートされている 4 つの形式のいずれかである必要があります: NV21 (YCrCb 4:2:0 Semi-Planar)、YV12 (YCrCb 4:2:0 Planar)、YUYV (YCrCb 4: 2:2 インターリーブ)、RGBA (32 ビット R:G:B:x)、BGRA (32 ビット B:G:R:x)。選択した形式は、プラットフォームの GLES 実装上で有効な GL レンダー ターゲットである必要があります。

エラーが発生した場合は、null ハンドルを持つバッファーが返されますが、そのようなバッファーをreturnTargetBufferForDisplayに戻す必要はありません。

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

バッファが表示の準備ができていることをディスプレイに伝えます。 getTargetBuffer()の呼び出しによって取得されたバッファのみがこの呼び出しでの使用に有効であり、 BufferDescの内容はクライアント アプリによって変更できません。この呼び出しの後、クライアントはバッファを使用できなくなります。成功すると OK を返すか、 INVALID_ARGまたはBUFFER_NOT_AVAILABLEを含む可能性のある適切なエラー コードを返します。

struct DisplayDesc {
     string  display_id;
     int32   vendor_flags;  // Opaque value
}

EVS 表示の基本プロパティと、EVS 実装に必要なプロパティについて説明します。 HAL は、EVS 表示を記述するためにこの構造体を埋める役割を果たします。物理ディスプレイ、または別のプレゼンテーション デバイスとオーバーレイまたは混合された仮想ディスプレイのいずれかになります。

  • display_id 。ディスプレイを一意に識別する文字列。これは、デバイスのカーネル デバイス名、またはデバイスの名前 ( Rearviewなど) である可能性があります。この文字列の値は HAL 実装によって選択され、上記のスタックによって不透明に使用されます。
  • vendor_flags 。特殊なカメラ情報をドライバーからカスタム EVS アプリに不透明に渡す方法。これはドライバーから解釈されずに EVS アプリに渡されますが、無視しても構いません。
enum DisplayState : uint32 {
    NOT_OPEN,               // Display has not been “opened” yet
    NOT_VISIBLE,            // Display is inhibited
    VISIBLE_ON_NEXT_FRAME,  // Will become visible with next frame
    VISIBLE,                // Display is currently active
    DEAD,                   // Display is not available. Interface should be closed
}

EVS ディスプレイの状態について説明します。EVS ディスプレイは無効にする(ドライバーに表示されない) ことも、有効にする(ドライバーに画像を表示する) こともできます。ディスプレイがまだ表示されていないが、 returnTargetBufferForDisplay()呼び出しを介して画像の次のフレームが配信されると表示されるように準備されている一時的な状態が含まれます。

EVSマネージャー

EVS マネージャーは、外部カメラ ビューを収集して表示するためのパブリック インターフェイスを EVS システムに提供します。ハードウェア ドライバーがリソース (カメラまたはディスプレイ) ごとにアクティブなインターフェイスを 1 つだけ許可する場合、EVS マネージャーはカメラへの共有アクセスを容易にします。単一のプライマリ EVS アプリは EVS Manager の最初のクライアントであり、表示データの書き込みが許可される唯一のクライアントです (追加のクライアントにはカメラ画像への読み取り専用アクセスを許可できます)。

EVS マネージャーは、基盤となる HAL ドライバーと同じ API を実装し、複数の同時クライアントをサポートすることで拡張サービスを提供します (複数のクライアントが EVS マネージャーを通じてカメラを開いてビデオ ストリームを受信できます)。

EVS マネージャーと EVS ハードウェア API の図。

図 2. EVS マネージャーは、基礎となる EVS ハードウェア API をミラーリングします。

EVS ハードウェア HAL 実装または EVS マネージャー API を介して動作する場合、EVS マネージャー API が同時カメラ ストリーム アクセスを許可することを除き、アプリには違いはありません。 EVS マネージャーは、それ自体が EVS ハードウェア HAL レイヤーの許可された 1 つのクライアントであり、EVS ハードウェア HAL のプロキシとして機能します。

次のセクションでは、EVS Manager 実装で異なる (拡張された) 動作を持つ呼び出しのみについて説明します。残りの呼び出しは EVS HAL の説明と同じです。

IEvs列挙子

openCamera(string camera_id) generates (IEvsCamera camera);

一意のカメラ ID文字列によって識別される特定のカメラと対話するために使用されるインターフェイス オブジェクトを取得します。失敗した場合は NULL を返します。 EVS マネージャー層では、十分なシステム リソースが利用可能である限り、すでに開いているカメラを別のプロセスによって再度開くことができ、ビデオ ストリームを複数のコンシューマー アプリにティー接続できるようになります。 EVS マネージャー層のcamera_id文字列は、EVS ハードウェア層に報告されるものと同じです。

IEvsカメラ

EVS マネージャーが提供する IEvsCamera 実装は内部で仮想化されているため、あるクライアントによるカメラ上の操作は他のクライアントに影響を与えず、各クライアントはカメラへの独立したアクセスを保持します。

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

ビデオストリームを開始します。クライアントは、同じ基礎となるカメラ上でビデオ ストリームを個別に開始および停止できます。最初のクライアントが起動すると、基礎となるカメラが起動します。

doneWithFrame(uint32 frameId, handle bufferHandle) generates (EvsResult result);

フレームを返します。各クライアントは、終了したらフレームを返さなければなりませんが、望む限りフレームを保持することが許可されています。クライアントが保持するフレーム数が設定された制限に達すると、クライアントはフレームを返すまでそれ以上フレームを受信しません。このフレームスキップは他のクライアントに影響を与えず、期待どおりにすべてのフレームを受信し続けます。

stopVideoStream();

ビデオストリームを停止します。各クライアントは、他のクライアントに影響を与えることなく、いつでもビデオ ストリームを停止できます。特定のカメラの最後のクライアントがストリームを停止すると、ハードウェア層の基礎となるカメラ ストリームが停止します。

setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);

ドライバー固有の値を送信し、あるクライアントが別のクライアントに影響を与える可能性があります。 EVS マネージャーはベンダー定義の制御ワードの意味を理解できないため、制御ワードは仮想化されず、副作用は特定のカメラのすべてのクライアントに適用されます。たとえば、ベンダーがこの呼び出しを使用してフレーム レートを変更した場合、影響を受けるハードウェア層カメラのすべてのクライアントは新しいレートでフレームを受信します。

IEvsDisplay

EVS Manager レベルであっても、ディスプレイの所有者は 1 人だけ許可されます。 Manager は機能を追加せず、単に IEvsDisplay インターフェイスを基礎となる HAL 実装に直接渡します。

EVSアプリ

Android には、EVS マネージャーおよび車両 HAL と通信して基本的なリアビュー カメラ機能を提供する EVS アプリのネイティブ C++ リファレンス実装が含まれています。このアプリは、システム起動プロセスの非常に早い段階で開始され、利用可能なカメラと車の状態 (ギアと方向指示器の状態) に応じて適切なビデオが表示されることが予想されます。 OEM は、EVS アプリを変更したり、独自の車両固有のロジックやプレゼンテーションに置き換えたりできます。

図 3. EVS アプリのサンプル ロジック、カメラ リストの取得。



図 4. EVS アプリのサンプル ロジック、受信フレーム コールバック。

画像データは標準グラフィックス バッファーでアプリに提供されるため、アプリは画像をソース バッファーから出力バッファーに移動する必要があります。これにより、データ コピーのコストが発生しますが、アプリが希望する方法で画像を表示バッファーにレンダリングする機会も提供されます。

たとえば、アプリは、場合によってはインライン スケールまたは回転操作を使用して、ピクセル データ自体を移動することを選択する場合があります。アプリは、ソース イメージを OpenGL テクスチャとして使用し、アイコン、ガイドライン、アニメーションなどの仮想要素を含む複雑なシーンを出力バッファーにレンダリングすることもできます。より洗練されたアプリでは、複数の同時入力カメラを選択し、単一の出力フレームに結合することもできます (車両周囲のトップダウンの仮想ビューでの使用など)。

EVS ディスプレイ HAL で EGL/SurfaceFlinger を使用する

このセクションでは、EGL を使用して Android 10 で EVS Display HAL 実装をレンダリングする方法について説明します。

EVS HAL リファレンス実装は、 EGL を使用して画面上にカメラ プレビューをレンダリングし、 libguiを使用してターゲット EGL レンダリング サーフェスを作成します。 Android 8 (以降) では、 libguiVNDK-privateとして分類されます。これは、ベンダー プロセスが使用できない VNDK ライブラリで利用できるライブラリのグループを指します。 HAL 実装はベンダー パーティションに存在する必要があるため、ベンダーは HAL 実装で Surface を使用できません。

ベンダープロセス用の libgui の構築

libguiの使用は、EVS Display HAL 実装で EGL/SurfaceFlinger を使用する唯一のオプションとして機能します。 libguiを実装する最も簡単な方法は、ビルド スクリプトで追加のビルド ターゲットを使用して、frameworks/native/libs/gui を直接使用することです。このターゲットは、次の 2 つのフィールドが追加されていることを除いて、 libguiターゲットとまったく同じです。

  • name
  • vendor_available
cc_library_shared {
    name: "libgui_vendor",
    vendor_available: true,
    vndk: {
        enabled: false,
    },
    double_loadable: true,

defaults: ["libgui_bufferqueue-defaults"],
srcs: [ … // bufferhub is not used when building libgui for vendors target: { vendor: { cflags: [ "-DNO_BUFFERHUB", "-DNO_INPUT", ], …

注: ベンダー ターゲットは、パーセル データから 1 つの 32 ビット ワードを削除するNO_INPUTマクロを使用して構築されます。 SurfaceFlinger はこのフィールドが削除されていると想定しているため、SurfaceFlinger はパーセルの解析に失敗します。これはfcntl障害として観察されます。

W Parcel  : Attempt to read object from Parcel 0x78d9cffad8 at offset 428 that is not in the object list
E Parcel  : fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is 0, fds[i] is 0, fd_count is 20, error: Unknown error 2147483647
W Parcel  : Attempt to read object from Parcel 0x78d9cffad8 at offset 544 that is not in the object list

この状態を解決するには:

diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 6066421fa..25cf5f0ce 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -54,6 +54,9 @@ status_t layer_state_t::write(Parcel& output) const
     output.writeFloat(color.b);
 #ifndef NO_INPUT
     inputInfo.write(output);
+#else
+    // Write a dummy 32-bit word.
+    output.writeInt32(0);
 #endif
     output.write(transparentRegion);
     output.writeUint32(transform);

サンプルのビルド手順を以下に示します。 $(ANDROID_PRODUCT_OUT)/system/lib64/libgui_vendor.soを受け取ることが期待されます。

$ cd <your_android_source_tree_top>
$ . ./build/envsetup.
$ lunch <product_name>-<build_variant>
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=10
TARGET_PRODUCT=<product_name>
TARGET_BUILD_VARIANT=<build_variant>
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm64
TARGET_ARCH_VARIANT=armv8-a
TARGET_CPU_VARIANT=generic
TARGET_2ND_ARCH=arm
TARGET_2ND_ARCH_VARIANT=armv7-a-neon
TARGET_2ND_CPU_VARIANT=cortex-a9
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=<host_linux_version>
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release
BUILD_ID=QT
OUT_DIR=out
============================================

$ m -j libgui_vendor … $ find $ANDROID_PRODUCT_OUT/system -name "libgui_vendor*" .../out/target/product/hawk/system/lib64/libgui_vendor.so .../out/target/product/hawk/system/lib/libgui_vendor.so

EVS HAL 実装でバインダーを使用する

Android 8 (以降) では、 /dev/binderデバイス ノードはフレームワーク プロセス専用になったため、ベンダー プロセスからアクセスできなくなりました。代わりに、ベンダー プロセスは/dev/hwbinderを使用し、AIDL インターフェイスを HIDL に変換する必要があります。ベンダー プロセス間で AIDL インターフェイスを引き続き使用したい場合は、バインダー ドメイン/dev/vndbinderを使用してください。

IPCドメイン説明
/dev/binder AIDL インターフェイスを使用したフレームワーク/アプリ プロセス間の IPC
/dev/hwbinder HIDL インターフェイスを使用したフレームワーク/ベンダー プロセス間の IPC
HIDL インターフェイスを使用したベンダー プロセス間の IPC
/dev/vndbinder AIDL インターフェイスを使用したベンダー/ベンダー プロセス間の IPC

SurfaceFlinger は AIDL インターフェイスを定義しますが、ベンダー プロセスはフレームワーク プロセスとの通信に HIDL インターフェイスのみを使用できます。既存の AIDL インターフェイスを HIDL に変換するには、少なからぬ作業量が必要です。幸いなことに、Android には、ユーザー空間ライブラリ プロセスがリンクされているlibbinderのバインダー ドライバーを選択する方法が用意されています。

diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp
index d8fb3166..5fd02935 100644
--- a/evs/sampleDriver/service.cpp
+++ b/evs/sampleDriver/service.cpp
@@ -21,6 +21,7 @@
 #include <utils/Errors.h>
 #include <utils/StrongPointer.h>
 #include <utils/Log.h>
+#include <binder/ProcessState.h>

 #include "ServiceNames.h"
 #include "EvsEnumerator.h"
@@ -43,6 +44,9 @@ using namespace android;
 int main() {
     ALOGI("EVS Hardware Enumerator service is starting");


+    // Use /dev/binder for SurfaceFlinger
+    ProcessState::initWithDriver("/dev/binder");
+


     // Start a thread to listen to video device addition events.
     std::atomic<bool> running { true };
     std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running));

注: ベンダー プロセスは、 ProcessまたはIPCThreadStateを呼び出す、またはバインダー呼び出しを行う前に、これを呼び出す必要があります。

SELinux ポリシー

デバイス実装がフル トレブルの場合、SELinux はベンダー プロセスが/dev/binderを使用できないようにします。たとえば、EVS HAL サンプル実装はhal_evs_driverドメインに割り当てられており、 binder_deviceドメインに対する読み取り/書き込み権限が必要です。

W ProcessState: Opening '/dev/binder' failed: Permission denied
F ProcessState: Binder driver could not be opened. Terminating.
F libc    : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 9145 (android.hardwar), pid 9145 (android.hardwar)
W android.hardwar: type=1400 audit(0.0:974): avc: denied { read write } for name="binder" dev="tmpfs" ino=2208 scontext=u:r:hal_evs_driver:s0 tcontext=u:object_r:binder_device:s0 tclass=chr_file permissive=0

ただし、これらの権限を追加すると、フルトレブル デバイスのsystem/sepolicy/domain.teで定義されている次の Neverallow ルールに違反するため、ビルドが失敗します。

libsepol.report_failure: neverallow on line 631 of system/sepolicy/public/domain.te (or line 12436 of policy.conf) violated by allow hal_evs_driver binder_device:chr_file { read write };
libsepol.check_assertions: 1 neverallow failures occurred
full_treble_only(`
  neverallow {
    domain
    -coredomain
    -appdomain
    -binder_in_vendor_violators
  } binder_device:chr_file rw_file_perms;
')

binder_in_vendor_violators 、バグを捕捉し、開発をガイドするために提供される属性です。上記の Android 10 違反を解決するためにも使用できます。

diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
index f1f31e9fc..6ee67d88e 100644
--- a/evs/sepolicy/evs_driver.te
+++ b/evs/sepolicy/evs_driver.te
@@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain;
 hal_server_domain(hal_evs_driver, hal_evs)
 hal_client_domain(hal_evs_driver, hal_evs)

+# Allow to use /dev/binder
+typeattribute hal_evs_driver binder_in_vendor_violators;
+
 # allow init to launch processes in this context
 type hal_evs_driver_exec, exec_type, file_type, system_file_type;
 init_daemon_domain(hal_evs_driver)

EVS HAL リファレンス実装をベンダー プロセスとして構築する

参考として、次の変更をpackages/services/Car/evs/Android.mkに適用できます。説明されているすべての変更が実装で機能することを必ず確認してください。

diff --git a/evs/sampleDriver/Android.mk b/evs/sampleDriver/Android.mk
index 734feea7d..0d257214d 100644
--- a/evs/sampleDriver/Android.mk
+++ b/evs/sampleDriver/Android.mk
@@ -16,7 +16,7 @@ LOCAL_SRC_FILES := \
 LOCAL_SHARED_LIBRARIES := \
     android.hardware.automotive.evs@1.0 \
     libui \
-    libgui \
+    libgui_vendor \
     libEGL \
     libGLESv2 \
     libbase \
@@ -33,6 +33,7 @@ LOCAL_SHARED_LIBRARIES := \
 LOCAL_INIT_RC := android.hardware.automotive.evs@1.0-sample.rc

 LOCAL_MODULE := android.hardware.automotive.evs@1.0-sample
+LOCAL_PROPRIETARY_MODULE := true

 LOCAL_MODULE_TAGS := optional
 LOCAL_STRIP_MODULE := keep_symbols
@@ -40,6 +41,7 @@ LOCAL_STRIP_MODULE := keep_symbols
 LOCAL_CFLAGS += -DLOG_TAG=\"EvsSampleDriver\"
 LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
 LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+LOCAL_CFLAGS += -Iframeworks/native/include

 # NOTE:  It can be helpful, while debugging, to disable optimizations
 #LOCAL_CFLAGS += -O0 -g
diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp
index d8fb31669..5fd029358 100644
--- a/evs/sampleDriver/service.cpp
+++ b/evs/sampleDriver/service.cpp
@@ -21,6 +21,7 @@
 #include <utils/Errors.h>
 #include <utils/StrongPointer.h>
 #include <utils/Log.h>
+#include <binder/ProcessState.h>

 #include "ServiceNames.h"
 #include "EvsEnumerator.h"
@@ -43,6 +44,9 @@ using namespace android;
 int main() {
     ALOGI("EVS Hardware Enumerator service is starting");
+    // Use /dev/binder for SurfaceFlinger
+    ProcessState::initWithDriver("/dev/binder");
+
     // Start a thread to listen video device addition events.
     std::atomic<bool> running { true };
     std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running));
diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
index f1f31e9fc..632fc7337 100644
--- a/evs/sepolicy/evs_driver.te
+++ b/evs/sepolicy/evs_driver.te
@@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain;
 hal_server_domain(hal_evs_driver, hal_evs)
 hal_client_domain(hal_evs_driver, hal_evs)

+# allow to use /dev/binder
+typeattribute hal_evs_driver binder_in_vendor_violators;
+
 # allow init to launch processes in this context
 type hal_evs_driver_exec, exec_type, file_type, system_file_type;
 init_daemon_domain(hal_evs_driver)
@@ -22,3 +25,7 @@ allow hal_evs_driver ion_device:chr_file r_file_perms;

 # Allow the driver to access kobject uevents
 allow hal_evs_driver self:netlink_kobject_uevent_socket create_socket_perms_no_ioctl;
+
+# Allow the driver to use the binder device
+allow hal_evs_driver binder_device:chr_file rw_file_perms;