Volume management

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.

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() and getGroupMaxVolume() 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 ofAudio 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 ofAudioControl 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:

  1. 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.

  2. Runtime configuration. Automotive audio systems support external amplifier configurations and these ECUs may host the volume range configurations that are queried during boot time.

  3. 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 updated CarVolumeGroupInfo, Event-type (what changed) and Extra-info (why it changed).

image

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.

image

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
  • 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.