In Android 10, car_audio_configuration.xml
replaces
car_volumes_groups.xml
and IAudioControl.getBusForContext
.
In the new configuration file, a list of zones is defined. Each zone has one or more
volume groups with their associated devices, and each device has the contexts that
should be routed within that zone. It's required that all contexts are represented
within each zone.
Configuring audio routing
The audio policy files, which typically live in the vendor partition, represent
the audio hardware configuration of the board. All devices referenced in
car_audio_configuration.xml
must be defined within the
audio_policy_configuration.xml
.
Enabling AAOS routing
To use AAOS-based routing, you must set the
audioUseDynamicRouting
flag to true
:
<resources> <bool name="audioUseDynamicRouting">true</bool> </resources>
When false
, routing and much of CarAudioService
will be disabled and
the OS will fall back to the default behavior of the AudioService
.
Primary zone
By default, all audio will be routed to the primary zone. There can only
be one primary zone, which is indicated in the configuration by the attribute
isPrimary="true"
.
Sample configuration
As an example, a vehicle could have two zones — a primary zone and a rear seat
entertainment system. With that, a possible car_audio_configuration.xml
would be defined as follows:
<audioZoneConfiguration version="2.0"> <zone name="primary zone" isPrimary="true"> <volumeGroups> <group> <device address="bus0_media_out"> <context context="music"/> <context context="announcement"/> </device> <device address="bus3_call_ring_out"> <context context="call_ring"/> </device> <device address="bus6_notification_out"> <context context="notification"/> </device> <device address="bus7_system_sound_out"> <context context="system_sound"/> <context context="emergency"/> <context context="safety"/> <context context="vehicle_status"/> </device> </group> <group> <device address="bus1_navigation_out"> <context context="navigation"/> </device> <device address="bus2_voice_command_out"> <context context="voice_command"/> </device> </group> <group> <device address="bus4_call_out"> <context context="call"/> </device> </group> <group> <device address="bus5_alarm_out"> <context context="alarm"/> </device> </group> </volumeGroups> </zone> <zone name="rear seat zone" audioZoneId="1"> <volumeGroups> <group> <device address="bus100_rear_seat"> <context context="music"/> <context context="navigation"/> <context context="voice_command"/> <context context="call_ring"/> <context context="call"/> <context context="alarm"/> <context context="notification"/> <context context="system_sound"/> <context context="emergency"/> <context context="safety"/> <context context="vehicle_status"/> <context context="announcement"/> </device> </group> </volumeGroups> </zones> </audioZoneConfiguration>
Here the primary zone has separated out contexts to different devices. This enables
the HAL to apply different post-processing effects and mixing on each device output
using the vehicle's hardware. The devices have been arranged into four volume groups:
media, navigation, calls, and alarms. If the system is configured to
useFixedVolume
, then the volume levels for each group will be passed
onto the HAL to apply to the output of these devices.
For the secondary zone, the expected output is through a single output device. In this example, all usages are routed to the single device and volume group to keep things simple.
Occupant zone audio configuration
In Android 11 car_audio_configuration.xml
was further expanded to
introduce two new fields, audioZoneId
and occupantZoneId
.
The first, audioZoneId
can be used to better control zone management.
On the other hand, occupantZoneId
can be used to configure user ID-based
routing.
To use these new fields, V2 of the car_audio_configuration.xml
is
required. Revisiting the audio configuration above but utilizing the new field for
occupant zone id and audio zone id mapping, the new configuration without the volume
group definitions can be setup as:
<audioZoneConfiguration version="2.0"> <zone name="primary zone" isPrimary="true" occupantZoneId="0"> ... </zone> <zone name="rear seat zone" audioZoneId="1" occupantZoneId="1"> ... </zone> </zones> </audioZoneConfiguration>
The configuration above defines a mapping for primary zone to occupant zone 0, and
audioZoneId
1 to occupantZoneId
1. In general any mapping
between occupant zone and audio zone can be configured but the mapping must be one-to-one.
Here are the rules that defined the two new fields:
- The
audioZoneId
for the primary zone is always zero audioZoneId
andoccupantZoneId
numbers can not repeataudioZoneId
andoccupantZoneId
can only have a one to one mapping
Routing through an application UID
A series of hidden APIs were introduced to CarAudioManager
in 10 to allow apps
to query and set audio zones and focus.
int[] getAudioZoneIds(); int getZoneIdForUid(int uid); boolean setZoneIdForUid(int zoneId, int uid); boolean clearZoneIdForUid(int uid);
The APIs above allowed for a first party application to manage audio routing based
on an application's UID. As such, both the audio zone ID and the application's UID is
also needed. With that information in hand, audio routing can be set by using the
CarAudioManager#setZoneIdForUid
API.
Changing zones for an app
By default, all audio routes to the primary zone. To update an application to be
routed to a different zone, use CarAudioManager#setZoneIdForUid
:
// Find zone to play int zoneId = ... // Find application's uid Int uid = mContext.getPackageManager() .getApplicationInfo(mContext.getPackageName(), 0) .uid; if (mCarAudioManager.setZoneIdForUid(zoneId, info.uid)) { Log.d(TAG, "Zone successfully updated"); } else { Log.d(TAG, "Failed to change zone"); }
N Note: Streams and focus cannot dynamically switch zones. Therefore, playback must be stopped and focus re-requested to change zones.
Routing with User ID
While an application's UID-based routing allows for the fine control of each application's
audio routing, it also requires that audio routing for each application be defined prior
to the application actually requesting audio focus and playing audio. To mitigate this
issue and further facilitate third party applications to play audio without modification,
CarAudioService
uses the car occupant zone and audio zone mapping to define
the user id based routing. This way, when a user logs into the occupant zone, the car audio
service is notified. With this signal, audio focus management and routing are automatically
configured for all audio zones.
Applications UID-based routing can still be used but must be done independently of
user id routing. This means that if occupant zone to car audio zone mapping is
defined, then UID-based routing is disabled and attempting to call
CarAudioManager#setZoneidForUid
will throw an error.
While audio routing and focus management has been simplified with occupant zone
management, the user must still be assigned to an occupant zone. This can be done by
using the CarOccupantZoneManager#assignProfileUserToOccupantZone
. This
API requires permission to manage users. The current expectation is for OEMs to manage
user to occupant zone assignment through some kind of system UI. Once that is done application
launching, audio routing, focus management will all be automatically configured for the user.
Routing with setPreferredDevice
Along with the changes above, Android 11 also has a new API to query output devices associated with each zone, CarAudioManager#getOutputDeviceForUsage(int zoneId, int usage).
The API can be used to query an output device for a particular zone and an audio attribute
usage. In this manner first party applications can route audio to different zones by
utilizing the player's setPreferredDevice
API. The
getOutputDeviceForUsage
API requires
PERMISSION_CAR_CONTROL_AUDIO_SETTINGS
and is a system API. Below is an
example of finding the media device for a particular zone and routing to that device
using the setPreferredDevice
API.
audioZoneId = ... ; mediaDeviceInfo = mCarAudioManager .getOutputDeviceForUsage(audioZoneId, AudioAttributes.USAGE_MEDIA); … mPlayer.setPreferredDevice(mediaDeviceInfo);