Audio Routing

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 and occupantZoneId numbers can not repeat
  • audioZoneId and occupantZoneId 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);