メディア フレームワークの強化

Android 7.0 は、デバイスのセキュリティを改善するためにモノリシックな mediaserver プロセスを複数のプロセスに分割し、各プロセスで必要な権限と機能のみを使用するようにしています。以下の変更により、メディア フレームワークのセキュリティの脆弱性が改善されます。

  • AV パイプライン コンポーネントをアプリ固有のサンドボックス プロセスに分割する。
  • 更新可能なメディア コンポーネント(extractor、codec など)を有効にする。

これらの変更により、メディア関連のセキュリティの脆弱性が大幅に改善され、エンドユーザーのデバイスとデータの安全性も向上します。

OEM と SoC ベンダーは、HAL とフレームワークの変更を新しいアーキテクチャに対応させるために更新する必要があります。具体的には、ベンダーが提供する Android コードはすべて同じプロセスで実行されることが多いため、ベンダーはコードを更新してネイティブ ハンドル(native_handle)を渡す必要があります。メディア強化に関する変更のリファレンス実装については、frameworks/avframeworks/native をご覧ください。

アーキテクチャの変更

Android の以前のバージョンでは、1 つのモノリシック mediaserver プロセスを多くの権限(カメラアクセス、オーディオ アクセス、ビデオドライバ アクセス、ファイル アクセス、ネットワーク アクセスなど)で使用していました。Android 7.0 では、mediaserver プロセスをより少ない権限を必要とするいくつかの新しいプロセスに分割します。

mediaserver の強化

図 1. mediaserver の強化のためのアーキテクチャの変更

この新しいアーキテクチャにより、プロセスが不正使用されても、以前 mediaserver が保持していた一連の権限に悪質なコードがアクセスできないようにします。プロセスは SElinux と seccomp ポリシーによって制限されます。

注: ベンダーの依存関係により、一部のコーデックはまだ mediaserver 上で動作するため、mediaserver に必要以上の権限が必要となります。特に、Widevine Classic は引き続き Android 7.0 の mediaserver で動作します。

MediaServer の変更

Android 7.0 では、コンポーネントやプロセス間でのバッファの受け渡しと同期など、再生と記録の処理に mediaserver プロセスが使用されます。プロセスは、標準 Binder メカニズムを介して通信します。

標準のローカル ファイル再生セッションでは、アプリはファイル記述子(FD)を mediaserver(通常は MediaPlayer Java API を介して)に渡し、mediaserver は次の処理を行います。

  1. FD を Binder DataSource オブジェクトにラップする。Binder DataSource オブジェクトは extractor プロセスに渡され、Binder IPC を使用しているファイルの読み取りに使用されます(mediaextractor は FD を取得しませんが、データ取得のために Binder に mediaserver を呼び出させます)。
  2. ファイルを検証してファイル形式に適切な extractor(MP3Extractor や MPEG4Extractor など)を作成し、mediaserver プロセスに extractor の Binder のインターフェースを返す。
  3. Binder IPC で extractor を呼び出し、ファイル(MP3 や H.264 データなど)内のデータの種類を判別する。
  4. mediacodec プロセスを呼び出して必要なタイプのコーデックを作成し、これらのコーデックの Binder インターフェースを受け取る。
  5. extractor への Binder IPC 呼び出しを繰り返してエンコードされたサンプルを読み取り、Binder IPC を使用してエンコードされたデータをデコード処理のために mediacodec プロセスに送信し、デコードしたデータを受信する。

コーデックが関係しない(エンコードされたデータが直接出力デバイスに送信されるオフロード状態の再生など)、コーデックがデコードされたデータのバッファを返す代わりにデコードされたデータを直接レンダリングする(動画の再生)、といったユースケースもあります。

MediaCodecService の変更

コーデック サービスには、エンコーダとデコーダがあります。ベンダーの依存関係により、一部のコーデックはまだコーデック プロセスに含まれていません。Android 7.0 では次のようになります。

  • セキュアではないデコーダとソフトウェア エンコーダは、コーデック プロセスで使用される。
  • セキュアなデコーダとハードウェア エンコーダは、mediaserver で使用される(変更なし)。

アプリ(または mediaserver)はコーデック プロセスを呼び出して必要なタイプのコーデックを作成した後に、そのコーデックを呼び出し、エンコードされたデータを渡してデコードされたデータを受け取ったり(デコードの場合)、デコードされたデータを渡してエンコードされたデータを受け取ったり(エンコードの場合)します。コーデックとの間のデータ転送にはすでに共有メモリが使用されているため、プロセスは変更されません。

MediaDrmServer の変更

DRM サーバーは、Google Play ムービーの映画など、DRM 保護がかかったコンテンツの再生に使用されます。セキュアな方法で暗号化されたデータの復号を処理し、証明書や鍵ストレージなどの機密性の高いコンポーネントにアクセスします。ベンダーの依存関係のため、DRM プロセスはすべてのケースでまだ使用されていません。

AudioServer の変更

AudioServer のプロセスでは、音声入出力などのオーディオ関連コンポーネント、オーディオ ルーティングを決定する PolicyManager サービス、FM ラジオサービスをホストします。オーディオの変更と実装方法について詳しくは、オーディオの実装をご覧ください。

CameraServer の変更

CameraServer はカメラを制御し、録画時にカメラから取得したビデオフレームを処理のため mediaserver に渡す際に使用されます。CameraServer の変更と実装方法について詳しくは、カメラ フレームワークの強化をご覧ください。

ExtractorService の変更

ExtractorService は、メディア フレームワークでサポートされているさまざまなファイル形式を解析する extractor をホストします。ExtractorService はすべてのサービスの中でも最も権限の少ないサービスです。FD を読み取れないため、代わりに(各再生セッションにつき mediaserver for によって提供される)Binder インターフェースに呼び出しを行ってファイルにアクセスします。

IMediaExtractor を取得するために extractor プロセスを呼び出すアプリ(または mediaserver)は、IMediaExtractor を呼び出してファイルに含まれるトラックのために IMediaSources を取得し、IMediaSources を呼び出してそれらからデータを読み取ります。

プロセス間でデータを転送するために、アプリ(または mediaserver)は reply-Parcel 内のデータを Binder トランザクションの一部として含めるか、共有メモリを使用します。

  • 共有メモリを使用するには、共有メモリを解放するための余分な Binder 呼び出しが必要になりますが、大きなバッファでも高速で電力消費も少なくなります。
  • in-Parcel を使用するには、余分な複製が必要になりますが、64 KB 未満のバッファなら高速で消費電力も少なくなります。

実装

MediaDrm コンポーネントと MediaCrypto コンポーネントの新しい mediadrmserver プロセスへの移動をサポートするため、ベンダーはプロセス間でバッファを共有できるようにセキュアなバッファの割り当て方法を変更する必要があります。

以前の Android リリースでは、セキュアなバッファは OMX::allocateBuffer によって mediaserver で割り当てられており、次のように同じプロセス内での復号の際に使用されます。

図 2. MediaServer での Android 6.0 以下のバッファ割り当て。

Android 7.0 では、バッファ割り当てプロセスが新しいメカニズムに変更され、既存の実装への影響を最小限に抑えながら柔軟に変更できるようになりました。新しい mediadrmserver プロセス内の MediaDrm スタックと MediaCrypto スタックにより、バッファは別々に割り当てられ、ベンダーはセキュアなバッファ ハンドルを更新する必要があります。MediaCodecMediaCrypto での復号を呼び出すときに Binder 間で移動ができるようにするためです。

図 3. MediaServer での Android 7.0 以降のバッファ割り当て。

ネイティブ ハンドルの使用

OMX::allocateBuffernative_handle 構造体へのポインタを返す必要があります。この構造体には、ファイル記述子(FD)と追加の整数データが格納されています。native_handle には、FD を使用するメリット(シリアル化および逆シリアル化のための既存の Binder サポートを含む)があり、FD を使用していないベンダーにとってはより柔軟性があります。

native_handle_create() を使用してネイティブ ハンドルを割り当てます。フレームワーク コードは、割り当てられた native_handle 構造体のオーナー権限を持ち、native_handle が最初から割り当てられたプロセスと逆シリアル化されたプロセスの両方でリソースを解放する役目を果たします。フレームワークは、native_handle_close() でネイティブ ハンドルを解放し、次に native_handle_delete()、そして Parcel::writeNativeHandle()/readNativeHandle() を使用して native_handle のシリアル化または逆シリアル化を行います。

セキュアなバッファを表すために FD を使用する SoC ベンダーは、その FD で native_handle 内の FD を生成できます。FD を使用しないベンダーは、native_buffer 内の追加のフィールドでセキュアなバッファを表すことができます。

復号の場所の設定

ベンダーは native_handle で動作する復号メソッド OEMCrypto を更新し、native_handle を新しいプロセス空間で利用可能にするため必要なベンダー固有の操作を実行できるようにする必要があります。通常、OEMCrypto ライブラリの更新が含まれます。

allocateBuffer は標準の OMX 動作であるため、Android 7.0 には、サポートをクエリする新しい OMX 拡張(OMX.google.android.index.allocateNativeHandle)と、ネイティブ ハンドルを使用することを OMX 実装に通知する OMX_SetParameter 呼び出しが含まれています。