Skip to content

Product configuration

Configure car audio zones and buses

Declare output devices in the audio HAL, group them into zones and volume groups, and prove every context routes somewhere.

Advanced5 min readAudio · Zones · Configuration

What you will build

A two-zone audio topology — cabin and rear seat — with four buses in the primary zone, verified so no context is left unrouted.

Estimated time
2–3 hours
Steps
6 steps

Two files describe the same topology in two syntaxes, and they are edited by different people. When they disagree, audio fails silently — no exception, no log at default verbosity, just no sound.

Step 1 — Declare the output devices#

The audio HAL's policy file is where bus addresses come into existence.

device/oem/vega/audio_policy_configuration.xml
<audioPolicyConfiguration version="7.0">
  <modules>
    <module name="primary" halVersion="3.0">
 
      <mixPorts>
        <mixPort name="mixport_bus0_media" role="source"
                 flags="AUDIO_OUTPUT_FLAG_PRIMARY">
          <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                   samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </mixPort>
        <mixPort name="mixport_bus1_nav" role="source">
          <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                   samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </mixPort>
        <mixPort name="mixport_bus2_voice" role="source">
          <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                   samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </mixPort>
        <mixPort name="mixport_bus3_call" role="source">
          <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                   samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </mixPort>
        <mixPort name="mixport_bus100_rear" role="source">
          <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                   samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </mixPort>
      </mixPorts>
 
      <devicePorts>
        <!-- The address attribute is the contract with car_audio_configuration -->
        <devicePort tagName="bus0_media_out" type="AUDIO_DEVICE_OUT_BUS"
                    role="sink" address="bus0_media_out">
          <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                   samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </devicePort>
        <devicePort tagName="bus1_nav_out" type="AUDIO_DEVICE_OUT_BUS"
                    role="sink" address="bus1_nav_out">
          <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                   samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </devicePort>
        <devicePort tagName="bus2_voice_out" type="AUDIO_DEVICE_OUT_BUS"
                    role="sink" address="bus2_voice_out">
          <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                   samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </devicePort>
        <devicePort tagName="bus3_call_out" type="AUDIO_DEVICE_OUT_BUS"
                    role="sink" address="bus3_call_out">
          <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                   samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </devicePort>
        <devicePort tagName="bus100_rear_out" type="AUDIO_DEVICE_OUT_BUS"
                    role="sink" address="bus100_rear_out">
          <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                   samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </devicePort>
      </devicePorts>
 
      <routes>
        <route type="mix" sink="bus0_media_out"   sources="mixport_bus0_media"/>
        <route type="mix" sink="bus1_nav_out"     sources="mixport_bus1_nav"/>
        <route type="mix" sink="bus2_voice_out"   sources="mixport_bus2_voice"/>
        <route type="mix" sink="bus3_call_out"    sources="mixport_bus3_call"/>
        <route type="mix" sink="bus100_rear_out"  sources="mixport_bus100_rear"/>
      </routes>
 
    </module>
  </modules>
</audioPolicyConfiguration>

Step 2 — Group them into zones#

device/oem/vega/car_audio_configuration.xml
<?xml version="1.0" encoding="utf-8"?>
<carAudioConfiguration version="3">
  <zones>
 
    <zone name="primary" isPrimary="true" occupantZoneId="0">
      <volumeGroups>
 
        <group>
          <device address="bus0_media_out">
            <context context="music"/>
            <context context="announcement"/>
          </device>
        </group>
 
        <group>
          <device address="bus1_nav_out">
            <context context="navigation"/>
          </device>
          <device address="bus2_voice_out">
            <context context="voice_command"/>
          </device>
        </group>
 
        <group>
          <device address="bus3_call_out">
            <context context="call"/>
            <context context="call_ring"/>
          </device>
        </group>
 
        <!-- Safety sounds get their own group so they can never be ducked
             along with media. -->
        <group>
          <device address="bus0_media_out">
            <context context="alarm"/>
            <context context="notification"/>
            <context context="system_sound"/>
            <context context="emergency"/>
            <context context="safety"/>
            <context context="vehicle_status"/>
          </device>
        </group>
 
      </volumeGroups>
    </zone>
 
    <zone name="rear_seat" occupantZoneId="1">
      <volumeGroups>
        <group>
          <!-- Every context, on one device. A zone that omits a context has
               undefined behaviour for that sound — usually silence. -->
          <device address="bus100_rear_out">
            <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>
    </zone>
 
  </zones>
</carAudioConfiguration>

Every context must appear in every zone

A zone missing a context produces silence for that sound — discovered when somebody finally takes a call from the rear seat. The rear zone above lists all twelve on one device precisely for this reason. Validate exhaustively, not by ear.

Step 3 — Install both files#

device/oem/vega/vega.mk
PRODUCT_COPY_FILES += \
    device/oem/vega/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml \
    device/oem/vega/car_audio_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/car_audio_configuration.xml
 
# Multi-zone support must be enabled for the rear zone to exist
PRODUCT_PROPERTY_OVERRIDES += \
    audio.deep_buffer.media=true

Step 4 — Check the addresses match before you build#

This is the highest-value thirty seconds in the whole tutorial.

Diff the address lists mechanically
cd device/oem/vega
 
grep -o 'address="[^"]*"' audio_policy_configuration.xml \
  | sed 's/address="//;s/"//' | sort -u > /tmp/hal_addresses.txt
 
grep -o 'address="[^"]*"' car_audio_configuration.xml \
  | sed 's/address="//;s/"//' | sort -u > /tmp/car_addresses.txt
 
diff /tmp/hal_addresses.txt /tmp/car_addresses.txt && echo "addresses match"

bus0_media_out and bus0_media are different devices

The address is a string, matched exactly. This single class of typo accounts for a remarkable share of "no audio on the new board" bring-up time, and nothing in the build catches it. Run the diff above every time either file changes — it is worth putting in CI.

Step 5 — Build and verify#

m -j && emulator -wipe-data -no-snapshot & adb wait-for-device && sleep 45

Verify the three layers agree

# 1. The HAL declared the devices
adb shell dumpsys audio_policy | grep -i 'bus.*_out'
 
# 2. CarAudioService found them and built zones
adb shell dumpsys car_service --services CarAudioService | grep -A20 -i zone
 
# 3. Volume groups exist in each zone
adb shell dumpsys car_service --services CarAudioService | grep -i -A5 'volume group'
 
# 4. Playback actually reaches a bus
adb shell am start -a android.intent.action.VIEW -d file:///system/media/audio/ringtones/Ring_Synth_04.ogg
adb shell dumpsys media.audio_flinger | grep -i -A5 'Output thread'

Whichever step fails first tells you which file to fix: 1 fails means the audio policy file, 2 fails means the car audio file, 3 fails means grouping.

Step 6 — Test each context#

Prove every context routes
# Use audio attributes to force a specific context
adb shell cmd media_session dispatch play
 
# Watch which device each stream lands on
adb shell dumpsys audio | grep -i -A10 'stream volumes'
adb shell dumpsys car_service --services CarAudioService | grep -i -A3 'focus'

The test worth automating: play something with each USAGE_* value and assert it appears on the expected bus. Twelve contexts, two zones, twenty-four cases — and the failure mode without it is a customer discovering that rear-seat phone calls are silent.

Troubleshooting#

SymptomCause
No audio anywhereBus addresses mismatched between the two files
One app silentIts context is not routed in that zone
Rear zone silentZone missing that context
Volume knob affects the wrong thingContexts in the wrong volume group
Chimes duck with mediaSafety contexts share a group with music
Crackling under loadBuffer sizes too small for the SoC
Works on emulator, silent on targetEmulator HAL uses different addresses

Next#

The first thing the driver ever sees.

References & further reading

Code links target the main branch on cs.android.com. AOSP moves — if a path 404s, search the symbol instead.