Volume management is contained in CarAudioService
, which uses fixed volumes
with the expectation that volumes are applied below the HAL by a hardware
amplifier instead of in the software. CarAudioService
organizes output devices
into volume groups to apply the same gains to all devices associated with a
volume group.
Fixed volumes
AAOS implementations use a hardware amplifier to control volume 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 is set to false
),
apps can call AudioManager.setStreamVolume()
to change the volume by stream
type in the software mixer. This may not always be desirable due to potential
effects on other apps and the fact that volume attenuation in the software mixer
can result in fewer significant bits available in the signal when received by
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. 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.
Define 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>
Each volume group should contain one or more output devices with associated
addresses. Addresses should correspond to the output devices defined in
audio_policy_configuration.xml
.
Configure volume group gains
Each volume group has minimum, maximum, and default gain values as well as a
step size based on 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 checks the gain values of the associated devices and configures 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.
Given 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 is 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 in the order defined 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
defaults 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
. To learn more, see the example above
in Define volume groups.
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, volume levels loaded and persisted for the first zone differ from those for the secondary zone.
Handle volume key events
Android defines several keycodes for volume control, including:
KEYCODE_VOLUME_UP
KEYCODE_VOLUME_DOWN
KEYCODE_VOLUME_MUTE
By default, Android routes the volume key events to apps. Automotive
implementations should force these key events to be processed by
CarAudioService
, which then calls 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 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. Corresponding system APIs for CarAudioManager pass values
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
- A value outside the range of -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 to the back
- A value outside the range of -1 to 1 is an error
You decide how these values should be applied and how to display the values 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 through 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 simultaneously can be heard more clearly. In AAOS, audio ducking is implemented by the HAL. Android has no control over sounds beyond the OS. In Android 11, the main information available to the HAL to make ducking decisions is whether or not two output devices both have active streams.
When to duck
While it’s up to the individual OEM to determine how ducking is handled by the HAL, we recommend the following guidelines.
Multiple streams playing in Android commonly occur when two apps or services concurrently hold audio focus. To learn when Android may grant concurrent focus, see the interaction matrix in Restriction types. With the introduction of the car audio plugin, this also depends on your AudioFocus management.
Any streams mixed together by Android are 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.
Recommended ducking behavior
The following are potential concurrent interactions ducking is recommended.
Interaction | Action |
---|---|
EMERGENCY
| Ducks or mutes everything except SAFETY
|
SAFETY |
Ducks everything except EMERGENCY |
NAVIGATION |
Ducks everything except SAFETY and EMERGENCY |
CALL |
Ducks everything except SAFETY , EMERGENCY ,
and NAVIGATION |
VOICE |
Ducks CALL_RING |
VEHICLE_SOUNDS |
You determine the importance of the active sound and whether or not it ducks other sounds. |
MUSIC and ANNOUNCEMENT |
Ducked by everything. Exceptions are touch interaction tones played as
SYSTEM_SOUND .
|
Considerations when ducking
Some apps and services, such as navigation or an assistant, might use multiple players to perform actions. Avoid aggressive unducking when a stream of data stops flowing through output devices to ensure the media doesn't return to full volume before being ducked before3 the next playback from the navigation or an assistant app starts.
For vehicles with multiple sound stages with good enough isolation, you can route audio to different areas of the car instead of ducking. For example, navigation instructions can be routed to the driver’s headrest speakers while continuing to play music throughout the cabin at a normal volume.
Safety critical sounds
Android 11 introduced
HAL audio focus APIs. The HAL ensures
safety-critical sounds are prioritized over other sounds. If the HAL holds audio
focus for USAGE_EMERGENCY
, it's not guaranteed that apps and services from
Android won't play sounds. The HAL determines which streams from Android should
be mixed or muted to play safety-critical sounds.
Configure the volume settings UI
AAOS decouples the volume settings UI from the volume group configuration. These can be overlaid as described in Configure volume group gains. This separation ensures that no changes are required should the configuration of volume groups change.
In the car settings UI, packages/apps/Car/Settings/res/xml/car_volume_items.xml
contains the 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
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 matc
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 learn how many controls should be drawn.getGroupMinVolume()
andgetGroupMaxVolume()
to get lower and upper bounds.getGroupVolume()
to get the current volume.registerVolumeChangeObserver()
to be notified of volume changes.
Car volume group event
Automotive use cases of the volume update and mute toggle have contextual underpinnings that may define the actions of certain apps, such as the volume settings. The current volume and mute callback from the car audio stack provides limited contextual information. To better serve automotive use cases and future scalability, CarVolumeGroupEvent is added to Android 14. Each event carries three critical types of information:
- List of
CarVolumeGroupInfo
EventTypes
(bit-mapped)- List of
ExtraInfos
CarVolumeGroupInfo
The receiver of the event callback has ready access to the list of impacted car
volume group information. This means that the app does not need to make any
additional calls to the Car audio framework to get the latest state. It can
simply use the received CarVolumeGroupInfos
to update the UI or internal
states. To make it easier for apps, the aspects that changed in a car volume
group are also provided as part of EventTypes
, as explained below.
EventTypes
Defines which aspect of CarVolumeGroupInfo
has changed. Apps can use this to
identify changes and take the required actions. For example,
EVENT_TYPE_VOLUME_MAX_INDEX_CHANGED
indicates that the respective
CarVolumeGroups
maximum volume gain index has changed and can be queried by
CarVolumeGroupInfo.getMaxVolumeGainIndex()
.
The following table shows the relationship between EventType
and
CarVolumeGroupInfo
.
EventType | CarVolumeGroupInfo |
---|---|
EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED | CarVolumeGroupInfo.getVolumeGainIndex() |
EVENT_TYPE_VOLUME_MIN_INDEX_CHANGED | CarVolumeGroupInfo.getMinVolumeGainIndex() |
EVENT_TYPE_VOLUME_MAX_INDEX_CHANGED | CarVolumeGroupInfo.getMaxVolumeGainIndex() |
EVENT_TYPE_MUTE_CHANGED | CarVolumeGroupInfo.isMuted() |
EVENT_TYPE_VOLUME_BLOCKED_CHANGED | CarVolumeGroupInfo.isBlocked() |
EVENT_TYPE_ATTENUATION_CHANGED | CarVolumeGroupInfo.isAttenuated() |
EVENT_TYPE_ZONE_CONFIGURATION_CHANGED | CarVolumeGroupInfo.getAudioAttributes() |
ExtraInfos
Provides additional information on why the CarVolumeGroup
has changed. Apps
can use this information to provide additional context to either alert the user
to act or to notify. For example, EXTRA_INFO_TRANSIENT_ATTENUATION_THERMAL
indicates an active transient attenuation due to a thermal overload. The app
can inform the user if they try to increase the volume.
We do not enforce any process for ExtraInfos
. It is left to your discretion
to determine the process based on ExtraInfos
. For example, if the attenuation
is active due to EXTRA_INFO_TRANSIENT_ATTENUATION_DUCKED
, you can also opt to
fade the volume bar UI initially to prevent the user from changing the volume.
Others may opt to show a toast that ducking is active and allow the user
to change the volume.
The car audio framework depends on the AudioControl HAL IAudioGainCallback
to
provide the suggested ExtraInfos
. To learn more, see
Audio Gain Callback.
CarVolumeGroupEvent
scales to meet future needs of the car audio framework. We
intend to support new features through CarVolumeGroupEvent
only. We
strongly recommend that app developers use CarVolumeGroupEvent
to handle
group volume and mute changes.
Car volume group event callback
Android 14, provides a new callback for privileged and
platform apps to register and be notified of CarVolumeGroupEvents
.
To register for callback, use
CarAudioManager#registerCarVolumeGroupEventCallback()
To unregister the callback, use
CarAudioManager#unregisterCarVolumeGroupEventCallback()
If an app registers with the new CarVolumeGroupEventCallback
and the legacy
CarVolumeCallback
, the event CarVolumeGroupEventCallbacks
are prioritized.
The car audio stack no longer triggers CarVolumeCallback
. This prevents
duplicate triggers to the same app for the same event.
We strongly recommend that you use CarVolumeGroupEventCallback
to manage
group volume and mute changes.
Audio gain callback
Since Android 13, AudioControl HAL can trigger an asynchronous callback to manage volume level updates due to changes to the car audio system.
HAL API
AudioControl @2.0 AIDL
Version 2.0 of AudioControl AIDL HAL adds the following API:
API | Purpose |
---|---|
IAudioControl#registerGainCallback |
Registers an instance of IAudioGainCallback with the AudioControl HAL. |
IAudioGainCallback#onAudioDeviceGainsChanged |
Asynchronous callback to notify changes to audio gain config. |
The AudioControl HAL callback includes lists of reasons and the respective
AudioGainConfigInfo
, which consists of:
- Zone Id
- Device port address
- Volume index > index can be either a restricted index or an update index.
Reasons can be broadly categorized as:
- Restriction reasons. Transient change to volume and mute behavior.
- Update reasons. Permanent change to volume behavior.
Restriction types
As of AudioControl
HAL AIDL
V3
, the following are types of supported
restrictions:
- Mute
- Blocking
- Limitation
- Attenuation
Active restriction | User-triggered volume change | User-triggered mute toggle |
---|---|---|
Mute | ❌ | ❌ (unmute) ✔ (mute) |
Blocking | ❌ | ✔ |
Limitation | ❌ (over limit) ✔ (under limit) |
✔ |
Attenuation | ✔ | ✔ |
The priority between restrictions is Mute > Blocking > Limitation > Attenuation.
Mute restrictions
Mute restrictions are:
Reasons.TCU_MUTE
Reasons.REMOTE_MUTE
Car audio framework internally maintains these two mute states:
User mute. Toggled based on request from the user, either through
CarAudioManager
or key events.HAL mute. Toggled based on mute restrictions received through
AudioGain
callback.
To listeners like the Settings app, the volume-group overall mute
(CarVolumeGroupInfo.isMuted()
) state will be based on if either of the
above mutes are enabled.
When the HAL mute is enabled, all incoming volume change and group unmute requests are ignored for the duration of the restriction.
Interaction case: HAL mute is active and User requests for Mute toggle
When HAL mute is enabled and User mute is disabled:
- Volume group overall mute state is changed to
true
. - Requests from User to enable mute will be processed.
- Reason: User mute requests should be honored at all times to preserve users privacy.
When HAL mute is enabled and User mute is enabled:
Volume group overall mute state is changed to
true
.Requests from User to disable mute will
NOT
be processed. Cached User mute state remains enabled.Reason: User unmute requests will only be honored if no active restrictions.
Reason: Un-muting cached User mute may cause unintended sound explosion and endanger user safety. This is especially true if the mute state is enabled across ignition cycles which lowers users' awareness of sound level perception.
Interaction case: HAL Mute enabled and disabled while User mute has no changes
Toggling HAL mute will change volume-group overall mute state. However, it does not directly update the user mute state. When User mute is disabled and HAL mute callback to enable is received:
- Volume group overall mute state is changed to
true
. Requests from User to change volume will
NOT
be processed while HAL mute is enabled.Reason: User cannot perceive sound while the mute is enabled. Allowing volume change can result in a sound explosion and endanger user safety.
Reason: Volume apps can register for callbacks and trigger an unmute (CarAudioManager.setVolumeGroupMute(...,/* mute=*/ true,..)) automatically without user intervention, if this is the expected behavior by the OEM.
When HAL mute is disabled while User mute is disabled:
Volume group mute state is changed to
false
.Reason: Making mute state sticky and requesting User to un-mute may unnecessarily interrupt the User when the mute states toggle frequently.
Requests from users to change volume will be processed normally.
Blocking
Blocking restrictions are:
Reasons.FORCED_MASTER_MUTE
Reasons.REMOTE_MUTE
Reasons.TCU_MUTE
.
When Blocking restrictions are active, requests from users to:
- Change volume are not be processed.
- Toggle mute are processed.
Limitation
Limitation restrictions are:
Reasons.THERMAL_LIMITATION
Reasons.SUSPEND_EXIT_VOL_LIMITATION
When Limitation restrictions are active, requests from users to:
Change volume:
- Within Limitation are processed
- Above Limitation are not processed
Toggle mute are processed.
Attenuation
Attenuation restrictions are:
Reasons.ADAS_DUCKING
Reasons.NAV_DUCKING
Reasons.PROJECTION_DUCKING
When Attenuation restrictions are active, requests from users to:
Change volume are processed. The new current volume level is set to the attenuated volume (instead of to the erstwhile volume). Future volume changes are made from this level.
Toggle mute is processed.
Update to index
The following is considered as the asynchronous volume index update:
Reasons.EXTERNAL_AMP_VOL_FEEDBACK
.
With this reason, AudioControl HAL can update the volume group current index
to the specified index. This is primarily used as feedback from the audio system
for the volume change request from the Car audio framework. The index update is
also communicated with Apps as a CarVolumeGroupEvent
callback to synchronize
the index.
Examples
Use Case: User updates the volume index to 30
User uses the Volume app to change the volume index to 30.
This index is converted to volume gain and sent to Audio HAL.
Vendor implementations of
Audio HAL
receive the new volume gain and update the audio system (like external amp).Audio system responds that the volume level is only updated to index 15 (for reasons unknown to Android).
Vendor implementations of
AudioControl HAL
triggers:IAudioGainCallback.onAudioDeviceGainsChanged(EXTERNAL_AMP_VOL_FEEDBACK, {..., 15 /* New index */})
Car audio service consumes the new index from callback that is used for persistence and callbacks to volume app. The user-requested index is 30. However, the audio system asynchronous feedback updates the index to 15.
Use case: First audio playback after exiting suspend
Volume index before suspend is set to a high level of 95 (range: [0-99]).
Android enters suspend.
Once Android exists suspend (for example, resume):
Vendor
Audio HAL/AudioControl HAL
applies a safe index of 30 to the audio system locally.Vendor
AudioControl HAL
also triggers the callback for the safe index:
IAudioGainCallback.onAudioDeviceGainsChanged(SUSPEND_EXIT_VOL_LIMITATION, {..., 30 /* safe index */})
Car audio service consumes the new index from callback that is used for persistence and its own callbacks to the volume app synchronizing the index. The volume index before suspend is 95. However, after resume, this index is set to a safe volume level of 30 by the
AudioControl HAL
implementor.
Dynamic volume configuration
For this feature we consider the following primary use cases:
Vehicle end-of-line (EOL) configuration.
Automakers prefer to update volume configurations at EOL based on vehicle audio system setup. Typically, this is a sideload without updating the Android SW image.
Automakers may need to update the volume configuration during a service schedule.
Runtime configuration. Automotive audio systems support external amplifier configurations and these ECUs may host the volume range configurations that are queried during boot time.
On-demand configuration. Offered to support the growing need for demand-based audio features in which users subscribe to enhanced signal processing for a period of time. The new volume range configurations are valid for the duration of a subscription.
Design
Dynamic volume configuration is achieved in three stages:
Discovery. The vendor AudioControl HAL implementation discovers new volume range updates through a custom IPC mechanism owned by the vendor.
Once discovered, a callback is generated through
AudioControl::IModuleChangeCallback
.Update. The car audio stack updates volume group states with the new volume ranges.
Efforts are made to maintain the same volume level post volume range update. However, if the index falls out of bounds, the current volume index is set to a safe value. For example, the default level provided by the vendor during the callback.
Callback.
Post volume group range updates, the car audio stack triggers a callback to apps registered through
CarVolumeGroupEventCallback
.CarVolumeGroupEvent
carries the updatedCarVolumeGroupInfo
, Event-type (what changed) and Extra-info (why it changed).
Figure 1. Dynamic volume configuration.
HAL API
AudioControl @ 3.0 AIDL
Version 3.0 of AudioControl AIDL HAL introduces the following APIs:
API | |
---|---|
IAudioControl#setModuleChangeCallback | Sets an instance of IModuleChangeCallback with AudioControl HAL. |
IAudioControl#clearModuleChangeCallback | Clears the instance of IModuleChangeCallback previously set with the AudioControl HAL. |
IModuleChangeCallback#onAudioPortsChanged | Callback to notify changes to AudioPorts |
Sequence
The sequence diagram of dynamic volume configuration is displayed below.
Figure 2. Sequence diagram for dynamic volume configuration.
Key aspects
To optimize this feature, consider the following.
AudioPorts supplied as part of the callback must match the Automotive BUS definition:
- Device port.
IN_DEVICE
,OUT_DEVICE
- Connection.
BUS
- Address. Defined in the Audio HAL definition
- Gain mode.
JOINT
- Device port.
Vendors must define a superset of volume range definitions in the Audio HAL policy and use the callback to customize it for vehicle variants. See the
IModuleChangeCallbac
AIDL definition for more information.When more than one audio BUS belongs to the same volume group, each must have identical volume range definitions. Failure to do so results in the car audio framework rejecting the new volume range definition.