AAOS has its own volume management within CarAudioService
. It uses fixed
volumes with the expectation that volumes should be applied below the HAL by a hardware
amplifier rather than in software. It also organizes output devices into volume groups
to apply the same gains to all devices associated with the volume group.
Using fixed volumes
AAOS implementations should control volume using a hardware amplifier instead of a
software mixer. To avoid side effects, set the config_useFixedVolume
flag
to true (overlay as necessary):
<resources> <!-- Car uses hardware amplifier for volume. --> <bool name="config_useFixedVolume">true</bool> </resources>
When the config_useFixedVolume
flag is not set (or set to false),
applications can call AudioManager.setStreamVolume()
and change the
volume by stream type in the software mixer. This may be undesirable because of
the potential effect on other applications and the fact that volume attenuation in
the software mixer results in fewer significant bits available in the signal when
received at the hardware amplifier.
Volume groups
Volume groups manage the volumes for a collection of devices within an audio zone. For each volume group the volume can be controlled independently, and the resulting gains are configured on the associated devices to be applied by the vehicle's amplifier. Volume settings are persisted for the user, and are loaded when the user signs in.
Defining volume groups
CarAudioService uses volume groups defined in car_audio_configuration.xml
:
<audioZoneConfiguration version="2.0"> <zones> <zone name="primary zone" isPrimary="true"> <volumeGroups> <group> <device address="bus0_media_out"> <context context="music"/> </device> </group> <group> <device address="bus1_navigation_out"> <context context="navigation"/> </device> <device address="bus2_voice_command_out"> <context context="voice_command"/> </device> </group> ... </volumeGroups> </zone> </zones> </audioZoneConfiguration>
Example car_audio_configuration.xml
implementation.
Each volume group should contain one or more output devices with associated addresses.
These addresses should correspond to output devices defined in
audio_policy_configuration.xml
.
Configuring volume group gains
Each volume group has a minimum, maximum, and default gain values as well as
a step size. These are determined based on the values configured in
audio_policy_configuration.xml
for the devices associated
with the volume group.
<devicePort tagName="bus0_media_out" role="sink" type="AUDIO_DEVICE_OUT_BUS" address="bus0_media_out"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/> <gains> <gain name="" mode="AUDIO_GAIN_MODE_JOINT" minValueMB="-3200" maxValueMB="600" defaultValueMB="0" stepValueMB="100"/> </gains> </devicePort>
During initialization, the volume group will check the gain values of the associated devices and configure the group as follows:
- Step size. Must be the same for all devices controlled by the volume group
- Minimum gain. Smallest minimum gain amongst the devices in the group
- Maximum gain. Highest maximum gain amongst the devices in the group
- Default gain. Highest default gain amongst the devices in the group
Because of the way these values are configured, it's possible to set the gain of a volume group outside the range supported for a device associated with the volume group. In this case, for that device the gain will be set to the device's minimum or maximum gain value based on whether the volume group's value is below or above the range.
Volume group identifiers
Volume groups are identified at runtime by their order of definition in the XML file.
IDs range from 0 to N-1 within an audio zone, where N is the number of volume groups in
that zone. In this way, volume group IDs are not unique across zones. These identifiers
are used for CarAudioManager
APIs associated with volume groups. Any API
that takes in a groupId
without a zoneId
will default to the
primary audio zone.
Multi-zone volume management
Each audio zone is expected to have one or more volume groups, and each volume group
is only associated with a single audio zone. This relationship is defined as part of
car_audio_configuration.xml
. See the example provided in
Defining volume groups above.
The current volume levels for each zone are persisted for the user associated with that zone. These settings are zone specific, meaning if a user signs in on a display associated with the primary zone, and then later on signs into a zone associated with a secondary audio zone, the volume levels loaded and persisted for the first zone will be different than those for the secondary zone.
Handling volume key events
Android defines several keycodes for volume control, including
KEYCODE_VOLUME_UP
, KEYCODE_VOLUME_DOWN
, and
KEYCODE_VOLUME_MUTE
. By default, Android routes the volume key
events to applications. Automotive implementations should force these key events to
CarAudioService
, which can then call setGroupVolume
or
setMasterMute
as appropriate.
To force this behavior, set the config_handleVolumeKeysInWindowManager
flag to true
:
<resources> <bool name="config_handleVolumeKeysInWindowManager">true</bool> </resources>
Volume key events currently have no way of distinguishing which zone they're
intended for, and as such are assumed to all be associated with the primary audio zone.
When a volume key event is received, CarAudioService
determines which volume
group to adjust by fetching the audio contexts for the active players, and then adjusting
the volume group that contains the output device associated with the highest priority
audio context. The prioritization is determined based on a fixed ordering defined in
CarVolume.AUDIO_CONTEXT_VOLUME_PRIORITY
.
Fade and balance
Both versions of the AudioControl HAL include APIs for setting fade and balance in
the vehicle. There are corresponding system APIs for CarAudioManager that pass values
down to the AudioControl HAL. These APIs require
android.car.permission.CAR_CONTROL_AUDIO_VOLUME
.
The AudioControl APIs are:
setBalanceTowardRight(float value)
. Shifts the speaker volume toward the right (+) or left (-) side of the car. 0.0 is centered, +1.0 is fully right, -1.0 is fully left, and a value outside the range -1 to 1 is an error.setFadeTowardFront(float value)
- Shifts the speaker volume toward the front (+) or back (-) of the car. 0.0 is centered, +1.0 is fully forward, -1.0 is fully rearward, and a value outside the range -1 to 1 is an error.
It is up to OEMs to decide how these values should be applied and how they will be surfaced to users. They could be applied strictly to media or across the board to all Android sounds.
Android 11 also introduced support for applying audio effects to output devices. With this, it's possible to alternatively manage fade and balance via audio effects on the appropriate output devices rather than through these APIs.
Audio ducking
Audio ducking occurs when the vehicle reduces the gain for one stream so that another stream playing at the same time can be heard more clearly. In AAOS, audio ducking is left up to the HAL to implement as there are potentially many sounds outside of Android that the OS has no control over. In Android 11, the main information available to the HAL to make ducking decisions is whether two output devices both have active streams.
When to duck
While it's up to the individual OEM to determine how ducking will be handled by their HAL, there are some general guidelines we recommend. Multiple streams playing within Android will most commonly occur when two apps/services hold audio focus concurrently. With that in mind, see Interaction matrix to learn when Android may grant concurrent focus and, therefore, when it's possible for two different streams to play concurrently.
Keep in mind that any streams mixed together by Android will be done so prior to any gains being applied. As such, any stream that should be ducked when played concurrently with another should be routed to separate output devices so the HAL can apply ducking before mixing them together.
Recommended ducking behavior
The following are potential concurrent interactions where we recommend ducking to be applied:
EMERGENCY
. Duck or mute everything exceptSAFETY
to ensure the driver hears the soundSAFETY
. Duck everything exceptEMERGENCY
to ensure the driver hears the soundNAVIGATION
. Duck everything exceptSAFETY
andEMERGENCY
CALL
. Duck everything exceptSAFETY
,EMERGENCY
, andNAVIGATION
VOICE
. DuckCALL_RING
- It's up to OEMs to determine the importance of the active
VEHICLE_SOUNDS
and whether or not they should duck other sounds to ensure the driver hears them. MUSIC
andANNOUNCEMENT
should be ducked by everything. The main exception to this are touch interaction tones which currently get played asSYSTEM_SOUND
Other considerations when ducking
Some apps/services such as navigation or assistant might use multiple players to complete their actions. OEMs should avoid unducking too aggressively based on when stream data stops coming through these output devices to ensure the user doesn't momentarily have media return to full volume before being ducked back down as the next playback from the navigation or assistant app begins.
For vehicles with multiple sound stages with good enough isolation, there's also the option to route audio to different areas of the car rather than ducking. For example, navigation instructions could be routed to the driver's headrest speakers while music continues to play throughout the cabin at a normal volume.
Safety critical sounds
While Android 11 introduced
HAL audio focus APIs,
it's still up to the HAL to ensure safety critical sounds are prioritized over
others. Even if the HAL holds audio focus for USAGE_EMERGENCY
, that does not
guarantee apps and services within Android will not play sounds. It is up to the HAL to
determine which streams from Android should be mixed or muted as safety critical sounds are
played.
Configuring the volume settings UI
AAOS decouples the volume settings UI from volume group configuration (which can be overlaid as described in Configuring volume groups). This separation ensures that no changes are required if the volume groups configuration changes in the future.
In the Car Settings UI, the packages/apps/Car/Settings/res/xml/car_volume_items.xml
file contains UI elements (title and icon resources) associated with each defined
AudioAttributes.USAGE
. This file provides for a reasonable rendering of
the defined VolumeGroups
by using resources associated with the first
recognized usage contained in each VolumeGroup.
For example, the following example defines a VolumeGroup as including both
voice_communication
and voice_communication_signalling
. The default
implementation of the car settings UI renders the VolumeGroup using the resources associated
with voice_communication
as that is the first in the file.
<carVolumeItems xmlns:car="http://schemas.android.com/apk/res-auto"> <item car:usage="voice_communication" car:title="@*android:string/volume_call" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="voice_communication_signalling" car:title="@*android:string/volume_call" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="media" car:title="@*android:string/volume_music" car:icon="@*android:drawable/ic_audio_media"/> <item car:usage="game" car:title="@*android:string/volume_music" car:icon="@*android:drawable/ic_audio_media"/> <item car:usage="alarm" car:title="@*android:string/volume_alarm" car:icon="@*android:drawable/ic_audio_alarm"/> <item car:usage="assistance_navigation_guidance" car:title="@string/navi_volume_title" car:icon="@drawable/ic_audio_navi"/> <item car:usage="notification_ringtone" car:title="@*android:string/volume_ringtone" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="assistant" car:title="@*android:string/volume_unknown" car:icon="@*android:drawable/ic_audio_vol"/> <item car:usage="notification" car:title="@*android:string/volume_notification" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="notification_communication_request" car:title="@*android:string/volume_notification" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="notification_communication_instant" car:title="@*android:string/volume_notification" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="notification_communication_delayed" car:title="@*android:string/volume_notification" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="notification_event" car:title="@*android:string/volume_notification" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="assistance_accessibility" car:title="@*android:string/volume_notification" car:icon="@*android:drawable/ic_audio_ring_notif"/> <item car:usage="assistance_sonification" car:title="@*android:string/volume_unknown" car:icon="@*android:drawable/ic_audio_vol"/> <item car:usage="unknown" car:title="@*android:string/volume_unknown" car:icon="@*android:drawable/ic_audio_vol"/> </carVolumeItems>
The attributes and values used in the above configuration are declared in
packages/apps/Car/Settings/res/values/attrs.xml
. The volume settings
UI uses the following VolumeGroup-based CarAudioManager APIs:
getVolumeGroupCount()
to know how many controls should be drawn.getGroupMinVolume()
andgetGroupMaxVolume()
to get lower and upper bounds.getGroupVolume()
to get the current volume.registerVolumeChangeObserver()
to get notified on volume changes.