Mobile Development 12 min read

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.

Tencent Music Tech Team
Tencent Music Tech Team
Tencent Music Tech Team
Understanding Android Audio Volume Architecture and Configuration (Android 6.0)

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 * trackVolume

where 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.

mobile developmentAndroidAudioAudioManagerAudioServicevolume
Tencent Music Tech Team
Written by

Tencent Music Tech Team

Public account of Tencent Music's development team, focusing on technology sharing and communication.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.