Audiofocus - access control to the audio subsystem

This is a translation of the article  Respecting Audio Focus by Kristan Uccello, Google Developer Relations

It is considered rude to interrupt during the presentation, it shows disrespect for the speaker and annoys the audience. If your application does not take into account the rules for working with audio focus, it means that it does not respect other applications and annoys the user. If you've never heard of audio focus, check out the Android developer training material documentation  .
When multiple applications can play audio, it is important to think about how they will interact. To avoid the situation when all players play simultaneously, Android uses the concept of audio focus to control the playback of sounds: your application should play audio only when it received audio focus. This article describes some tips on how to properly and best-fit the audio state changes for the user.

Audio Focus Request


No need to be greedy and request audio focus right at the start of the application; it’s better to wait until the application starts to do something with the audio stream. Upon receipt of the service audiofokusa through  the AudioManager , you can use the constants  AUDIOFOCUS_GAIN * to indicate the desired mode focus.

Focus request example
AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
int result = am.requestAudioFocus(mOnAudioFocusChangeListener,
    // Hint: the music stream.
    AudioManager.STREAM_MUSIC,
    // Request permanent focus.
    AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
  mState.audioFocusGranted = true;
} else if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
  mState.audioFocusGranted = false;
}

In the example, we request a constant audio focus. Instead, we could request a temporary ( AUDIOFOCUS_GAIN_TRANSIENT ) focus, which is suitable for playing sounds lasting up to 45 seconds.
Another application can use the “quacking” mode ( AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK ) for situations where the audio subsystem can be shared with other applications (for example, for the phrase “burn more” in the fitness application, expecting that background music will not be interrupted). An application requesting focus in “quack” mode should not use the audio subsystem for longer than 15 seconds in a row.

We process changes in the status of the audio focus


To handle audio focus state change events, the application must create an OnAudioFocusChangeListener instance . In this handler, you need to handle the AUDIOFOCUS_GAIN *  and  AUDIOFOCUS_LOSS * events . It is worth noting that the AUDIOFOCUS_GAIN event has several features described in the second example.

Event Handling Example
mOnAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {  
@Override
public void onAudioFocusChange(int focusChange) {
  switch (focusChange) {
  case AudioManager.AUDIOFOCUS_GAIN:
    mState.audioFocusGranted = true;
    if(mState.released) {
      initializeMediaPlayer();
    }
    switch(mState.lastKnownAudioFocusState) {
    case UNKNOWN:
      if(mState.state == PlayState.PLAY && !mPlayer.isPlaying()) {
        mPlayer.start();
      }
      break;
    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
      if(mState.wasPlayingWhenTransientLoss) {
        mPlayer.start();
      }
      break;
    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
      restoreVolume();
      break;
    }
    break;
  case AudioManager.AUDIOFOCUS_LOSS:
    mState.userInitiatedState = false;
    mState.audioFocusGranted = false;
    teardown();
    break;
  case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
    mState.userInitiatedState = false;
    mState.audioFocusGranted = false;
    mState.wasPlayingWhenTransientLoss = mPlayer.isPlaying();
    mPlayer.pause();
    break;
  case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
    mState.userInitiatedState = false;
    mState.audioFocusGranted = false;
    lowerVolume();
    break;
  }
  mState.lastKnownAudioFocusState = focusChange;
  }
};

The AUDIOFOCUS_GAIN constant is used in the code in two different roles. Firstly, to obtain audio focus as in example 1. At the same time, the OnAudioFocusChangeListener event does not occur , that is, upon successful request (and receipt) of audio focus, the handler will NOT receive the corresponding AUDIOFOCUS_GAIN event.
AUDIOFOCUS_GAIN is also used in the OnAudioFocusChangeListener implementation   as an event option. As indicated earlier, the AUDIOFOCUS_GAIN event is not raised when an audio focus request is made. On the contrary, it can occur only after the corresponding event,  AUDIOFOCUS_LOSS *, occurs . AUDIOFOCUS_GAIN is the only constant that is used in both situations.
There are four situations that need to be considered in the event handler of the audio focus state change event. When an application receives the AUDIOFOCUS_LOSS event, this usually means that it will not receive audio focus back. In this case, the application should free up resources associated with the audio subsystem and stop playback. As an example, imagine a user listens to music through your application, and then launches a game that takes away the audio focus from the audio player. It is impossible to predict how long the user will close the game. Most likely, he will go to the main screen (leaving the game in the background) and launch another application. Or he will return to the audio player, resuming his work, which will require a new request for audio focus in onResume.
However, there is another case worthy of discussion. There is a difference between losing audio focus permanently (as in the example above) or temporarily. When an application receives the AUDIOFOCUS_LOSS_TRANSIENT event , it is expected that the application will pause audio until it receives the AUDIOFOCUS_GAIN event. When the AUDIOFOCUS_LOSS_TRANSIENT event occurs, the application must remember that the loss of focus is temporary, in order to figure out what behavior is correct when the focus returns. (see example 2).
Sometimes an application loses audio focus (i.e., it receives AUDIOFOCUS_LOSS), and an interrupted application terminates, or in some other way loses audio focus. In this situation, the last application that had audio focus may receive the AUDIOFOCUS_GAIN event.
In the subsequent AUDIOFOCUS_GAIN event, the application must understand whether it received audio focus after a temporary loss and should simply resume playback, or restore and configure playback after a complete loss of focus.
If the application uses audio only for a short time (no more than 45 seconds), it should request audio focus in AUDIOFOCUS_GAIN_TRANSIENT mode   and release it immediately after playback or recording is finished. The audio focus in the system is treated like a stack: the focus is given to the application that owned it last.
When audio focus is received, it's time to create MediaPlayer or MediaRecorder and reserve resources. Also, when an application receives AUDIOFOCUS_LOSS, it is good practice to free all reserved resources. There are three options for obtaining audio focus, corresponding to different options for losing focus. It would be nice to explicitly handle all the options for losing focus in the OnAudioFocusChangeListener handler  .

Table 1 . The meaning of the constants of gain and loss of audio focus
GAIN
LOSS
AUDIOFOCUS_GAIN
AUDIOFOCUS_LOSS
AUDIOFOCUS_GAIN_TRANSIENT (*)
AUDIOFOCUS_LOSS_TRANSIENT
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK (*)
AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
Note: the constant is used in two places. When audio focus is requested, it is transmitted as an AudioManager hint; it is also used as an event option in OnAudioFocusChangeListener . Focus gain constants, indicated by (*), are used only when requesting audio focus. Focus loss constants are used only in the OnAudioFocusChangeListener handler.

Table 2 . Types of audio streams.
A type
Description
STREAM_ALARM
Alarm clock
STREAM_DTMF
Tone dial
STREAM_MUSIC
Multimedia playback (music, podcasts, videos)
STREAM_NOTIFICATION
Notifications
STREAM_RING
Phone call
STREAM_SYSTEM
System sounds
The application requests audio focus from  AudioManager  (as in the example by the link at the end of the article). The parameters are an optional handler, a hint with the type of audio channel (table 2) and the type of audio focus from table 1. Any initialization of audio should be done only if the system allowed receiving audio focus (AudioManager. AUDIOFOCUS_REQUEST_GRANTED ).
Note: If a telephone conversation occurs, the system will not allow receiving audio focus ( AUDIOFOCUS_REQUEST_FAILED ) and will not send the application the AUDIOFOCUS_GAIN event after the call ends.
A brief description of the application's response to OnAudioFocusChange () events is described in Table 3.
In case of loss of audio focus, you must be sure that the focus is completely lost. If the application receives the event  AUDIOFOCUS_LOSS_TRANSIENT  or AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK , it can hold reserved resources (do not call release ()), because most likely a new event will change audio focus. It is worth saving information about temporary loss of focus in some flag or by going to a separate vertex of the state graph.
If the application requested constant audio focus in AUDIOFOCUS_GAIN mode and received the AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK event, the appropriate response would be to turn down the volume (not forgetting to keep the old volume value) and then return the volume when receiving the AUDIOFOCUS_GAIN event (see picture).



Table 3 . The reaction of the application when the state of the audio focus changes.
Type of focus change
Reaction
AUDIOFOCUS_GAIN
Receive event after a loss of focus event : Resume media playback if the application state does not contradict this. For example, a user paused before a focus loss event.
AUDIOFOCUS_LOSS
Stop playback, free resources
AUDIOFOCUS_LOSS_TRANSIENT
Pause playback and save the flag that focus loss is temporary, so that when processing AUDIOFOCUS_GAIN it is possible to resume playback if necessary. Do not release resources.
AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
Make the volume quieter or pause playback, not forgetting to monitor the status as is the case with AUDIOFOCUS_LOSS_TRANSIENT. Do not release resources.


Conclusion and what to read


Being an “exemplary citizen” in the camp of audio applications on android devices means respecting the rules for working with audio focus and correctly handling all situations. Try to get your application to act in a reasonable way and not present the user with unpleasant surprises. You can tell a lot more about the android audio subsystem, the links below can read more about it.

The source codes from the article are available at:
https://android.googlesource.com/platform/development/+/master/samples/RandomMusicPlayer

Also popular now: