音声フォーカス

論理ストリームを開始する前に、アプリは論理ストリームに使用するものと同じオーディオ属性を使用して、音声フォーカスをリクエストする必要があります。こうしたフォーカスのリクエストの送信は推奨されますが、システム側で実行されることはありません。アプリによっては、特定の動作を行うために(たとえば、通話中にサウンドを意図的に再生するために)リクエストの送信を明示的にスキップする場合があります。

このため、フォーカスはメインのオーディオ コントロール メカニズムではなく、再生を間接的に制御し、競合を解消するための方法と考えてください。車両におけるオーディオ サブシステムのオペレーションをフォーカス システムに依存するべきではありません。

フォーカス インタラクション

AAOS のニーズに対応するために、音声フォーカスのリクエストは、リクエストの CarAudioContext と現在のフォーカス ホルダーの間で、事前定義されたインタラクションに基づいて処理されます。インタラクションには、排他的、拒否、同時という 3 種類があります。

排他的インタラクション

排他的インタラクションでは、一度に 1 つのアプリのみがフォーカスを保持できます。したがって、既存のフォーカス ホルダーがフォーカスを失っている間、受信フォーカス リクエストにフォーカスが付与されます。たとえば、既存のアプリで音楽が再生されているときに、ユーザーが新しい音楽アプリを起動した場合が挙げられます。両方ともメディアを再生するため、フォーカスを保持できるアプリは一度に 1 つのみです。その結果、新たに開始されたアプリのフォーカス リクエストは AUDIOFOCUS_REQUEST_GRANTED で返され、現在音楽を再生しているアプリは、そのリクエストの種類に応じた喪失ステータスのフォーカス変更イベントを受け取ります。これは、Android でよく見られるインタラクション モデルです。

拒否インタラクション

拒否インタラクションでは、受信リクエストが常に拒否されます。拒否インタラクションの例としては、通話中に音楽を再生しようとした場合が挙げられます。この場合、電話アプリが通話の音声フォーカスを保持している状態で、2 つ目のアプリが音楽を再生するためにフォーカスをリクエストすると、音楽アプリはそのリクエストに応じて AUDIOFOCUS_REQUEST_FAILED を受け取ります。フォーカス リクエストが拒否されるため、現在のフォーカス ホルダーにはいかなるタイプのフォーカス喪失もディスパッチされません。

同時インタラクション

AAOS に最も固有のものは、同時インタラクションです。これにより、車内で音声フォーカスをリクエストするアプリは、他のアプリと同時にフォーカスを保持できます。同時インタラクションを行うには、次の条件が満たされている必要があります。 

これらの基準が満たされると、フォーカス リクエストは AUDIOFOCUS_REQUEST_GRANTED で返され、現在のフォーカス ホルダーのフォーカスは変更されません。ただし、現在のフォーカス ホルダーでダッキング イベントの受け取りか、ダッキング時の一時停止を選択した場合、現在のフォーカス ホルダーは、排他的インタラクションの場合と同様にフォーカスを失います。

同時ストリームの処理

同時インタラクションには多くの便利なアプリがありますが、OEM は、出力デバイス全体のハードウェア レベルでミキシングとダッキングを処理する必要があります。このため CarAudioContext は、同時に再生できない CarAudioContext と同じ出力デバイスにのみルーティングすることを強くおすすめします。同時ストリーム用に別個の出力デバイスがあることで、ミキシングする前に HAL でストリームのいずれかをダッキングしたり、車内の異なるスピーカーに物理ストリームをルーティングしたりできます。論理ストリームが Android 内で混在している場合、ゲインは変わらず、同じ物理ストリームの一部として提供されます。

たとえば、ナビゲーションとメディアが同時に配信される場合、ナビゲーションの案内が明瞭に聞こえるように、メディア ストリームのゲインを一時的に低減(ダッキング)できます。または、ナビゲーション ストリームを運転席側スピーカーにルーティングしながら、キャビンの残りの部分全体でメディアの再生を継続できます。

インタラクション マトリックス

下の表は、CarAudioService で定義されるインタラクション マトリックスを示しています。行は現在のフォーカス ホルダーの CarAudioContext を表し、列は受信リクエストを表します。

音楽メディアアプリが現在フォーカスを保持していて、ナビゲーション アプリがフォーカスをリクエストしている例を見ると、マトリックスは 2 つのインタラクションが同時に再生できることを示しています(同時インタラクションの他の基準が満たされると仮定)。

同時インタラクションにより、複数のフォーカス ホルダーが存在することも可能です。この場合、適用するインタラクションの種類を決定する前に、受信フォーカス リクエストが現在の各フォーカス ホルダーと比較されます。その際、最も保守的なインタラクションが優先されます(拒否、次に排他的、最後に同時)。

下の表は、受信フォーカス リクエストの CarAudioContext(列)と既存のフォーカス ホルダーのコンテキスト(行)の間のフォーカス インタラクションを示しています。各セルは、2 つのコンテキストで想定されるインタラクション タイプを表します。

  • R: 拒否インタラクション
  • E: 排他的インタラクション
  • C: 同時インタラクション

音声フォーカスのインタラクション

図 1. 音声フォーカスのインタラクション

Android 11 では、ナビゲーションと通話の間のインタラクション動作をユーザーが変更できるように、新しいユーザー設定が導入されました。設定すると、android.car.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL は、受信した NAVIGATION フォーカス リクエストと現在の CALL フォーカス ホルダーの間のインタラクションを同時から拒否に変更します。そのため、ユーザーが通話の妨げになるナビゲーション案内を無効にする場合に、この設定を有効にできます。これはユーザーに対して保持され、後続のフォーカス リクエストが新しい設定値を尊重するように、動的に設定できます。

遅延可能オーディオ フォーカス

Android 11 では、遅延可能オーディオ フォーカスのリクエストのサポートが AAOS に追加されました。これにより、通常であれば現在のフォーカス ホルダーとのインタラクションが拒否される場合に、一時的でないフォーカス リクエストを遅延させることができます。フォーカスの変更により、遅延リクエストがフォーカスを得られる状態になると、リクエストが許可されます。

遅延オーディオ フォーカス リクエストのルール

  • 一時的でないリクエストのみ - 前述のとおり、遅延リクエストは一時的でないソースに対してのみ行うことができます。これは、一時的なサウンドが関連した後も長時間再生されることを防ぐためです。
  • 遅延できるリクエストは一度に 1 つのみ - 遅延リクエストがすでにあるときに遅延可能リクエストがあった場合、元の遅延リクエストは、AUDIOFOCUS_LOSS 変更イベントを受信し、新しいリクエストは AUDIOFOCUS_REQUEST_DELAYED の同期レスポンスを受信します。
  • 遅延可能リクエストには OnAudioFocusChangeListener が必要 - リクエストが遅延すると、最終的にリクエストが許可されたとき(AUDIOFOCUS_GAIN)、または後で拒否されたとき(AUDIOFOCUS_LOSS)に、リスナーを使用してリクエスト元に通知されます。

遅延可能フォーカスのリクエスト

遅延可能なリクエストを作成するには、AudioFocusRequest.Builder#setAcceptsDelayedFocusGain を使用します。

mMediaWithDelayedFocusListener = new MediaWithDelayedFocusListener();

mDelayedFocusRequest = new AudioFocusRequest
     .Builder(AudioManager.AUDIOFOCUS_GAIN)
     .setAudioAttributes(mMusicAudioAttrib)
     .setOnAudioFocusChangeListener(mMediaWithDelayedFocusListener)
     .setForceDucking(false)
     .setWillPauseWhenDucked(false)
     .setAcceptsDelayedFocusGain(true)
     .build();

次に、リクエストを行う際、AUDIOFOCUS_REQUEST_DELAYED レスポンスを処理します。

int delayedFocusRequestResults = mAudioManager.requestAudioFocus(mDelayedFocusRequest);
if (delayedFocusRequestResults == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// start audio playback
return;
}
if (delayedFocusRequestResults == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
     // audio playback delayed to audio focus listener
     return;
}

リクエストが遅延すると、フォーカス リスナーがフォーカスの変化を処理します。

private final class MediaWithDelayedFocusListener implements
OnAudioFocusChangeListener {
       @Override
       public void onAudioFocusChange(int focusChange) {
           synchronized (mLock) {
               switch (focusChange) {
                   case AudioManager.AUDIOFOCUS_GAIN:
                       … // Start focus playback
                   case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                       … // Pause media transiently
                   case AudioManager.AUDIOFOCUS_LOSS:
                       … // Stop media

マルチゾーン フォーカス管理

オーディオ ゾーンが複数ある車両の場合、音声フォーカスはゾーンごとに個別に管理されます。そのため、あるゾーンへのリクエストでは、他のゾーンでフォーカスを保持しているものは考慮されず、他のゾーンのフォーカス ホルダーがフォーカスを失うこともありません。これにより、メインキャビンのフォーカスを後部座席のエンターテインメント システムとは別に管理できるため、あるゾーンでのフォーカスの変更によって別のゾーンでのオーディオ再生が中断されることを回避できます。

すべてのアプリで、フォーカス管理は CarAudioService によって自動的に処理されます。フォーカス リクエストのオーディオ ゾーンは、関連付けられた UserId または UID に基づいて決定されます。詳細については、オーディオ ルーティングをご覧ください。

複数のゾーンからのオーディオの同時リクエスト

アプリが複数のゾーンで同時にオーディオを再生する場合は、次のようにバンドルに AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID を含めて、ゾーンごとにフォーカスをリクエストする必要があります。

//Create attribute with bundle and AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID
Bundle bundle = new Bundle();
bundle.putInt(CarAudioManager.AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID,
               zoneId);

AudioAttributes attributesWithZone = new AudioAttributes.Builder()
     .setUsage(AudioAttributes.USAGE_MEDIA)
     .addBundle(bundle)
     .build();

//Create focus request using built attributesWithZone

このバンドル パラメータにより、リクエスト元はオーディオ ゾーンの自動マッピングをオーバーライドして、代わりに指定したゾーン ID を使用できます。したがって、アプリはオーディオ ゾーンごとに個別のリクエストを発行できます。

HAL 音声フォーカス

Android 11 以降、外部ストリームに代わって HAL がフォーカスをリクエストできるようになりました。これらの API では、外部サウンドを有効にして Android エコシステムに参加しやすくし、スムーズなユーザー エクスペリエンスを実現することを強くおすすめします。

どのサウンドを優先させるかの最終的な判断は HAL が行うということに留意してください。この点で、緊急のサウンドと安全上重要なサウンドは、HAL が音声フォーカスを付与されたかどうかにかかわらず再生する必要があり、HAL が音声フォーカスを失っても、必要に応じて再生し続ける必要があります。規則で定められているサウンドについても同じことが言えます。

同様に、HAL は、緊急のサウンドと安全上重要なサウンドを再生する際、Android ストリームを必要に応じて積極的にミュートし、明瞭に聞こえるようにする必要があります。

AudioControl@2.0

AudioControl HAL のバージョン 2.0 では、新しい API がいくつか導入されています。

API 目的
IAudioControl#registerFocusListener AudioControl HAL に IFocusListener のインスタンスを登録します。 このリスナーにより、HAL は音声フォーカスのリクエストと破棄を行えます。HAl は、リスナーを登録解除するために Android で使用する ICloseHandle インスタンスを提供することが想定されます。
IAudioControl#onAudioFocusChange HAL によって IFocusListener を介して行われたフォーカス リクエストに対するステータスの変更について、HAL に通知します。これには、最初のフォーカス リクエストに対するレスポンスも含まれます。
IFocusListener#requestAudioFocus 指定された用途、ゾーン ID、フォーカス ゲイン タイプについて、HAL に代わってフォーカスをリクエストします。
IFocusListener#abandonAudioFocus 指定された用途とゾーン ID に対する既存の HAL フォーカス リクエストを破棄します。

HAL は同時に複数のフォーカス リクエストを行うことができますが、用途とゾーン ID のペアごとに 1 つのリクエストに限られます。なお、Android では、リクエストが行われるとすぐに HAL が用途についてサウンドの再生を開始し、フォーカスが破棄されるまで再生を継続することが想定されます。

registerFocusListener を除き、こうしたリクエストはすべて oneway であり、フォーカス リクエストの処理中に Android が HAL を遅延させないようにします。HAL は、安全上重要なサウンドを再生する前にフォーカスを取得すべきではありません。適切な場合には、HAL が IAudioControl#onAudioFocusChange を通じて音声フォーカスの変化をリッスンし、応答することが推奨されます。