Before starting a logical stream, an app should request audio focus using the same audio attributes as it is used for its logical stream. While sending such a focus request is recommended, it isn't enforced by the system. Some apps may explicitly skip sending the request to achieve specific behaviors (for example, to intentionally play sound during a phone call).
For this reason, you should consider focus as a way to indirectly control and deconflict playback, and not as a primary audio control mechanism; the vehicle shouldn't depend on the focus system for operation of the audio subsystem.
Focus interactions
To support the needs of AAOS, audio focus requests are handled based on predefined
interactions between the request's CarAudioContext
and that of current
focus holders. There are three types of interactions: exclusive, reject, and concurrent.
Exclusive interaction
In exclusive interactions only one application is allowed to hold focus at a time.
Therefore the incoming focus request is granted focus while the existing focus holder
loses focus. An example of this would be when a user starts a new music application while
music is already playing in an existing application. Since both are playing media, only
one of the applications is allowed to hold focus at a time. As a result, the newly started
application's focus request returns with AUDIOFOCUS_REQUEST_GRANTED
and
the application currently playing music receives a focus change event with a loss
status that corresponds to the type of request that was made. This is the interaction
model most commonly seen with Android.
Reject interaction
With reject interactions, the incoming request is always rejected. Attempting to play
music while a call is in progress is an example of a rejected interaction. In this case,
if the dialer is currently holding audio focus for a call and a second application request
focus to play music, the music application receives AUDIOFOCUS_REQUEST_FAILED
in response to its request. Since the focus request is rejected, no focus loss of any type
is dispatched to the current focus holder.
Concurrent interaction
Most unique to AAOS are the concurrent interactions. This gives applications requesting audio focus in the car the ability to hold focus concurrently with other applications. For a concurrent interaction to take place, the following conditions must be met. The:
- Incoming focus request must ask for
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
. - Current focus holder does not
setPauseWhenDucked(true)
. - Current focus holder does not opt to receive duck events.
If these criteria are met, then the focus request returns with
AUDIOFOCUS_REQUEST_GRANTED
while the current focus holder has no
change in focus. However, if the current focus holder opts to receive duck events or
to pause when ducked, the current focus holder loses focus just as with an exclusive
interaction.
Handling concurrent streams
While the concurrent interaction has many useful applications, OEMs must take care of
the mixing and ducking at the hardware level across output devices. Because of this, it is
strongly recommended that CarAudioContext
s only be routed to the same output
device as CarAudioContext
s that they cannot play concurrently with. By having
separate output devices for concurrent streams, this enables the HAL to duck one of the
streams before mixing them together, or to route the physical streams to different speakers
in the vehicle. If the logical streams are mixed within Android, their gains are not altered
and are delivered as part of the same physical stream.
For example, when navigation and media are delivered simultaneously, the gain for the media stream could temporarily be reduced (ducked) so the navigation instructions can be heard more clearly. Alternatively, the navigation stream could be routed to the driver side speakers while media continues to play throughout the rest of the cabin.
Interaction matrix
The table below shows the interaction matrix as defined by CarAudioService
.
The rows represent the current focus holder's CarAudioContext
and the columns
represent that of the incoming request.
Looking at an example, where a music media app is currently holding focus and a navigation app request focus, the matrix shows that the two interactions can play concurrently, assuming the other criteria for Concurrent interactions are met.
Because of the concurrent interactions, it is possible for more than one focus holder to exist. In this case, an incoming focus request is compared with each of the current focus holders before deciding what sort of interaction to apply. In this case, the most conservative interaction wins out (reject, then exclusive, and finally concurrent).
In the following table, focus interactions between the CarAudioContext for an incoming focus request (columns) and the context of existing focus holders (rows) are provided. Each cell represents the expected interaction type for the two contexts.
Figure 1. Audio focus interactions
Navigation during phone calls
In Android 11, a new user setting has been introduced to allow users to alter the
interaction behavior between navigation and phone calls. When set,
android.car.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL
changes
the interaction between incoming NAVIGATION
focus requests and current
CALL
focus holders from concurrent to reject.
So if a user would prefer not to have navigation instructions interrupting their call,
they can enable this setting. This is persisted for the user, and can be set dynamically
so that subsequent focus requests respect the new setting value.
Delayable audio focus
In Android 11, AAOS has added support for requesting delayable audio focus. This allows non-transient focus requests to be delayed when their interaction with current focus holders would normally result in them being rejected. Once a change in focus results in a state where the delayed request can gain focus, the request is granted.
Rules for delayed audio focus requests
- Non-transient requests only - as previously mentioned, a delayed request can only be made for non-transient sources. This is to avoid having a transient sound play long after it's relevant.
- Only one request can be delayed at a time - If a delayable request
is made while there is already a delayed request, the original delayed request
receives a
AUDIOFOCUS_LOSS
change event, and the new request receives a synchronous response ofAUDIOFOCUS_REQUEST_DELAYED
. - Delayable requests must have an
OnAudioFocusChangeListener
. Once a request is delayed, the listener is used to notify the requester when the request eventually is granted (AUDIOFOCUS_GAIN
), or if it's rejected later on (AUDIOFOCUS_LOSS
).
Request delayable focus
To build a request that can be delayed, use the
AudioFocusRequest.Builder#setAcceptsDelayedFocusGain
:
mMediaWithDelayedFocusListener = new MediaWithDelayedFocusListener(); mDelayedFocusRequest = new AudioFocusRequest .Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes(mMusicAudioAttrib) .setOnAudioFocusChangeListener(mMediaWithDelayedFocusListener) .setForceDucking(false) .setWillPauseWhenDucked(false) .setAcceptsDelayedFocusGain(true) .build();
Then, when making the request, handle the AUDIOFOCUS_REQUEST_DELAYED
response:
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; }
When the request is delayed, the focus listener is responsible for handling changes in focus:
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
Multi-zone focus management
For vehicles with multiple audio zones, audio focus is managed independently for each zone. As such, a request to one zone doesn't take into account what is holding focus in other zones, nor does it cause focus holders in other zones to lose focus. With this, the main cabin's focus can be managed separately from a rear seat entertainment system, thus avoiding interrupting the audio playback in one zone by the changes in focus in another.
For all applications focus management is taken care of by the CarAudioService
automatically. A focus request's audio zone is determined based on its associated
UserId
or UID
. For details, see
Audio Routing.
Requesting audio from multiple zones concurrently
If an app wants to play audio in multiple zones concurrently, it must request focus for
each zone by including AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID
in the bundle:
// 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
This bundle parameter allows the requester to override the automatic audio zone mappings to instead use the specified zone ID. Therefore, with this an app could issue separate requests for different audio zones.
HAL Audio Focus
Starting in Android 11, the HAL is now enabled to request focus on behalf of external streams. While optional, these APIs are highly encouraged to enable external sounds to be better participants in the Android ecosystem and to provide a smoother user experience.
Keep in mind that the HAL is still responsible for making the final call around what sounds should get priority. To this extent, emergency and safety critical sounds should be played regardless of whether or not the HAL is granted audio focus, and should continue to be played as appropriate even if the HAL loses audio focus. The same is true for any sounds required by regulations.
Along the same lines, the HAL should still proactively mute Android streams as appropriate when playing emergency or safety critical sounds to ensure they are heard clearly.
AudioControl@2.0
Version 2.0 of AudioControl HAL introduces several new APIs:
API | Purpose |
---|---|
IAudioControl#registerFocusListener |
Registers an instance of IFocusListener with the AudioControl HAL.
This listener enables the HAL to request and abandon audio focus. The HAl is expected
to provide an ICloseHandle instance to be used by Android to unregister
the listener.
|
IAudioControl#onAudioFocusChange |
Notifies the HAL of changes in status to focus requests made by the HAL through
the IFocusListener . This includes responses to initial focus requests.
|
IFocusListener#requestAudioFocus |
Requests focus on behalf of the HAL for a specified usage, zone Id, and focus gain type. |
IFocusListener#abandonAudioFocus |
Abandons existing HAL focus requests for the specified usage and zone Id. |
The HAL can have multiple focus requests at the same time, but is limited to one request per usage and zone Id pairing. Note that Android assumes the HAL immediately starts to play sounds for a usage once a request has been made, and continues to do so till it abandons focus.
Other than registerFocusListener
, these requests are all oneway
to ensure that Android does not delay the HAL while a focus request is processed. The HAL
should not wait to gain focus before playing safety-critical sounds. It's
optional for the HAL to listen for and respond to changes in audio focus through
IAudioControl#onAudioFocusChange
, although encouraged when appropriate.