Understanding Android Audio Volume Architecture and Configuration (Android 6.0)
The article explains Android 6.0’s audio volume architecture, detailing how streams, devices, and per‑stream volume indices are defined, cached, persisted, and modified via AudioManager or AudioTrack/MediaPlayer, and how these changes travel through AudioService, AudioPolicy, and AudioFlinger to the hardware.
While troubleshooting a Bluetooth device that produced no sound, the author discovered that the issue was related to volume settings. This led to a study of Android's audio volume architecture, using Android 6.0 as the reference platform.
1. Relationship Between Audio Streams, Devices, and Volume
Android defines several audio streams in AudioSystem.java :
int STREAM_VOICE_CALL = 0; // 电话
int STREAM_SYSTEM = 1; // 系统
int STREAM_RING = 2; // 响铃和消息
int STREAM_MUSIC = 3; // 音乐
int STREAM_ALARM = 4; // 闹钟
int STREAM_NOTIFICATION = 5; // 通知
int STREAM_BLUETOOTH_SCO = 6; // 蓝牙
int STREAM_SYSTEM_ENFORCED = 7; // 强制系统声音
int STREAM_DTMF = 8; // 双音多频
int STREAM_TTS = 9; // 语音Each stream is associated with one or more volume controls. The mapping is defined in AudioService.java :
private final int[] STREAM_VOLUME_ALIAS_VOICE = new int[] {
// STREAM_VOICE_CALL
AudioSystem.STREAM_VOICE_CALL,
// STREAM_SYSTEM
AudioSystem.STREAM_RING,
// STREAM_RING
AudioSystem.STREAM_RING,
// STREAM_MUSIC
AudioSystem.STREAM_MUSIC,
// STREAM_ALARM
AudioSystem.STREAM_ALARM,
// STREAM_NOTIFICATION
AudioSystem.STREAM_RING,
// STREAM_BLUETOOTH_SCO
AudioSystem.STREAM_BLUETOOTH_SCO,
// STREAM_SYSTEM_ENFORCED
AudioSystem.STREAM_RING,
// STREAM_DTMF
AudioSystem.STREAM_RING,
// STREAM_TTS
AudioSystem.STREAM_MUSIC
};Similar alias arrays exist for television/ set‑top‑box platforms ( STREAM_VOLUME_ALIAS_TELEVISION ) and the default platform ( STREAM_VOLUME_ALIAS_DEFAULT ). The system stores the current platform alias in mStreamVolumeAlias .
Audio input and output devices are also defined in audio.h . Example output devices:
AUDIO_DEVICE_OUT_EARPIECE = 0x1, // 听筒
AUDIO_DEVICE_OUT_SPEAKER = 0x2, // 扬声器
AUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4, // 线控耳机
AUDIO_DEVICE_OUT_WIRED_HEADPHONE = 0x8, // 普通耳机
AUDIO_DEVICE_OUT_BLUETOOTH_SCO = 0x10, // 单声道蓝牙耳机
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20, // 蓝牙电话
AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40, // 车载免提蓝牙设备
AUDIO_DEVICE_OUT_BLUETOOTH_A2DP = 0x80; // 立体声蓝牙耳机Corresponding input devices include built‑in mic and call mic. Each stream‑device pair has an independent volume index, so the total number of volume entries equals number of streams × number of devices .
2. Volume Caching and Persistence
Volume values are cached in AudioService via the inner class VolumeStreamState . The mapping device → index is stored in a SparseIntArray :
private final SparseIntArray mIndexMap = new SparseIntArray(8);All stream states are kept in an array:
private VolumeStreamState[] mStreamStates;When the device is powered off and on again, the last volume setting is restored, indicating persistence. Prior to Android 6.0, persistence was handled by the settings.db System table; from Android 6.0 onward, an XML file is used for faster access.
Default volume levels are defined in AudioSystem.java :
public static int[] DEFAULT_STREAM_VOLUME = new int[] {
4, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
5, // STREAM_RING
11, // STREAM_MUSIC
6, // STREAM_ALARM
5, // STREAM_NOTIFICATION
7, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
11, // STREAM_DTMF
11 // STREAM_TTS
};Maximum and minimum volume indices are defined in AudioService.java :
private static int[] MAX_STREAM_VOLUME = new int[] {5,7,7,15,7,7,15,7,15,15};
private static int[] MIN_STREAM_VOLUME = new int[] {1,0,0,0,0,0,1,0,0,0};3. Volume Setting Process
There are two primary ways to change volume:
Through AudioManager
Through AudioTrack or MediaPlayer
3.1 Using AudioManager
The AudioManager class is a lightweight wrapper created in the app process. It communicates with the system‑level AudioService via Binder IPC. The key method is:
public void setStreamVolume(int streamType, int index, int flags) {
IAudioService service = getService();
try {
service.setStreamVolume(streamType, index, flags, getContext().getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setStreamVolume", e);
}
}This call eventually reaches AudioSystem.setStreamVolumeIndex , which forwards the request to AudioPolicyService , then to AudioPolicyManager , and finally to the native AudioFlinger where the volume is applied to the appropriate playback thread.
status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device) {
const sp
& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->setStreamVolumeIndex(stream, index, device);
}
status_t AudioSystem::setStreamVolume(audio_stream_type_t stream, float value,
audio_io_handle_t output) {
if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
const sp
& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
af->setStreamVolume(stream, value, output);
return NO_ERROR;
}
status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,
audio_io_handle_t output) {
......
if (thread == NULL) {
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value);
}
} else {
thread->setStreamVolume(stream, value);
}
return NO_ERROR;
}The PlaybackThread simply stores the new volume and uses it during audio mixing.
3.2 Using AudioTrack / MediaPlayer
Each audio stream is represented by an AudioTrack instance that registers with AudioFlinger . AudioFlinger , assisted by AudioPolicy , creates a corresponding Track in a playback thread and mixes up to 32 concurrent streams.
During mixing, the final per‑track volume is calculated as:
finalVolume = streamVolume * masterVolume * trackVolumewhere trackVolume is set via AudioTrack.setVolume(float) (range 0.0‑1.0). The volume value is written to shared memory and read by AudioFlinger across process boundaries.
4. Summary
The Android audio volume system is a multi‑layered architecture involving persistent storage (System table or XML), in‑memory caches ( VolumeStreamState ), and several service components ( AudioService , AudioPolicyService , AudioFlinger ). Adjustments made via AudioManager or directly through AudioTrack / MediaPlayer propagate through Binder IPC to native audio policy and hardware layers, ensuring that each stream‑device pair uses the correct volume index.
Tencent Music Tech Team
Public account of Tencent Music's development team, focusing on technology sharing and communication.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.