/ Android

requestAudioFocusが失敗するケースとは

Androidでムービー・オーディオ周りを扱うことがあり,フォアグラウンドで再生時にバックグラウンドで再生されている音楽等を自動的に停止させたくてrequestAudioFocus周りを調べていた.
だが,APIドキュメント等を調べても,このリクエストに失敗するのがどのようなケースなのかについて具体的に書かれたものがなかった.そのためAndroidのソースコードを読んで分かったことを記す.


  • requestAudioFocusでAudioFocusを取得することで,フォアグラウンドで音を再生する際にバックグラウンドで再生中の音をどう扱うかを制御できる
  • ただし,あくまでrequestのため,失敗するケースがある
  • Android NougatのAPIソースコードより,着信中・通話中のケースでAudioFocusのリクエストが失敗する

requestAudioFocusとは

Androidでは,音の再生や音量の調整などの制御をどのアプリに握らせるのかという部分をandroid.media.AudioManagerクラスで管理している.これのrequestAudioFocusを用いることでAudioFocusをリクエストでき,リクエストに成功した(AUDIOFOCUS_REQUEST_GRANTEDが返ってきた)時点でバックグラウンド再生されていた音楽等が停止(durationHintAUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCKを用いた場合は,バックグラウンド再生されていた音楽等の音量が一時的に下がる)される.

しかし,AudioFocus取得のメソッド名がrequestAudioFocusであること,またこの返り値としてAUDIOFOCUS_REQUEST_GRANTEDが用意されていることからわかるように,AUDIOFOCUS_REQUEST_FAILEDが返ってくる,つまりAudioFocus取得に失敗するケースが存在する.具体的にどのようなケースにおいてリクエストに失敗するのかについて,requestAudioFocusのAPI DocsAudio Handling周りの公式ドキュメント等を調べてみたが,どこにもそれらしきことをはっきりと明記したものは見当たらなかった.

リクエストが失敗するケースとは

Android Nougat 7.0.0 のソースコードを辿る[1]と,以下のような流れになっていた.

  1. android.media.AudioManager#requestAudioFocus
  2. com.android.server.audio.AudioService#requestAudioFocus
  3. com.android.server.audio.MediaFocusControl#requestAudioFocus

そして,com.android.server.audio.MediaFocusControl#requestAudioFocus内で呼ばれているcom.android.server.audio.MediaFocusControl#canReassignAudioFocusの結果返されるboolean値を判断材料として用いていることが分かった.

com.android.server.audio.MediaFocusControl#canReassignAudioFocusは以下のように実装されている.

/**
 * Helper function:
 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
 * The implementation guarantees that a state where focus cannot be immediately reassigned
 * implies that an "locked" focus owner is at the top of the focus stack.
 * Modifications to the implementation that break this assumption will cause focus requests to
 * misbehave when honoring the AudioManager.AUDIOFOCUS_FLAG_DELAY_OK flag.
 */
private boolean canReassignAudioFocus() {
    // focus requests are rejected during a phone call or when the phone is ringing
    // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
    if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) {
        return false;
    }
    return true;
}

また,com.android.server.audio.MediaFocusControl#isLockedFocusOwnerは以下のように実装されている.

private boolean isLockedFocusOwner(FocusRequester fr) {
    return (fr.hasSameClient(AudioSystem.IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner());
}

android.media.AudioSystem#IN_VOICE_COMM_FOCUS_IDは以下のように定義されている.

public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";

com.android.server.audio.MediaFocusControl#canReassignAudioFocusのコメント等から,電話の着信中もしくは通話中にAudioFocusをリクエストすると,AUDIOFOCUS_REQUEST_FAILEDが返されて失敗するようだ.

ついつい忘れそうになるが,Androidスマートフォンも携帯電話の一つなので,電話機能を優先するのは然もありなんといったところだろう.


参考・関連



  1. Androidのソースコードに潜る際は,Androidソースコード検索サービス - Developer Collaboration Projectを利用した. ↩︎