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.
<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#
<?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#
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=trueStep 4 — Check the addresses match before you build#
This is the highest-value thirty seconds in the whole tutorial.
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 45Verify 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#
# 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#
| Symptom | Cause |
|---|---|
| No audio anywhere | Bus addresses mismatched between the two files |
| One app silent | Its context is not routed in that zone |
| Rear zone silent | Zone missing that context |
| Volume knob affects the wrong thing | Contexts in the wrong volume group |
| Chimes duck with media | Safety contexts share a group with music |
| Crackling under load | Buffer sizes too small for the SoC |
| Works on emulator, silent on target | Emulator HAL uses different addresses |
Next#
The first thing the driver ever sees.

