AudioManager中setStreamVolume与adjustStreamVolume

东门修能
2023-12-01

今日遇到一个bug, 酷狗音乐扬声器播放歌曲,此时假定音量值为10。
插入耳机,按手机音量键将音量值调为0,拔出耳机,播放音乐,发现扬声器音量也被置为0。

一、亲自验证?

  • 只有当音量值调为0时,耳机与扬声器音量会将另一方置0,其他音量时互不影响
  • 目前QQ音乐、网易云均无此现象

二、查看系统volume日志,惊奇发现:

AudioService: setStreamVolume 3, index = 0, flags = 8 from pid = 23805 callingPackage = com.kugou.android
AudioService: update volume cache, stream:3, device:headphone, index:0, caller:com.kugou.android
AudioService: update volume cache, stream:9, device:headphone, index:0, caller:com.kugou.android
ActivityManager: Broadcast: Intent { act=android.media.VOLUME_CHANGED_ACTION flg=0x14000010 (has extras) } ordered=false userid=-1 callerApp=ProcessRecord{3f82fa1 994:system/1000}
AudioService: update volume cache, stream:9, device:speaker, index:0, caller:com.kugou.android
ActivityManager: Broadcast: Intent { act=android.media.VOLUME_CHANGED_ACTION flg=0x14000010 (has extras) } ordered=false userid=-1 callerApp=ProcessRecord{3f82fa1 994:system/1000}
ActivityManager: Broadcast: Intent { act=android.media.VOLUME_CHANGED_ACTION flg=0x14000010 (has extras) } ordered=false userid=-1 callerApp=ProcessRecord{3f82fa1 994:system/1000}
AudioService: applyDeviceVolume_syncVSS stream: 3, device: 8, index: 0
AudioService: applyDeviceVolume_syncVSS stream: 9, device: 2, index: 0
AudioService: sendVolumeUpdate: StreamType = 3, oldIndex = 1, newIndex = 0
com.kugou.android D/setVolume: set:0
AudioService: applyAllVolumes stream: 3, device: 2, index: 0
AudioService: applyAllVolumes stream: 3, device: 4, index: 0
AudioService: applyAllVolumes stream: 3, device: 8, index: 0
AudioService: applyAllVolumes stream: 3, device: 128, index: 0
AudioService: applyAllVolumes stream: 9, device: 2, index: 0
AudioService: applyAllVolumes stream: 9, device: 4, index: 7
AudioService: applyAllVolumes stream: 9, device: 8, index: 0
AudioService: applyAllVolumes stream: 9, device: 128, index: 8
AudioService: adjustSuggestedStreamVolume() stream=3, flags=4116, caller=MediaSessionService
AudioService: adjustStreamVolume() stream =3, dir =0, flags =4112, callingPackage =android, Pid = 994
AudioService: sendVolumeUpdate: StreamType = 3, oldIndex = 0, newIndex = 0
om.kugou.android D/MediaSessionHelper: dispatched volume adjustment
  • stream: 3 表示音频流音量
  • stream: 9 表示文本识别音(暂无使用)
  • device: 2 表示 DEVICE_OUT_SPEAKER 扬声器
  • device: 4 表示 DEVICE_OUT_WIRED_HEADSET 线控耳机
  • device: 8 表示 DEVICE_OUT_WIRED_HEADPHONE 普通耳机
  • device: 128 表示 DEVICE_OUT_BLUETOOTH_A2DP 蓝牙A2DP输出
    当音量设置为0时,竟然将这几个设备音量均设置为0。

三、对比网易云音乐

1. 日志区别
AudioService: adjustSuggestedStreamVolume() stream=3, flags=4113, caller=MediaSessionService
AudioService: adjustStreamVolume() stream =3, dir =-1, flags =4113, callingPackage =android, Pid = 980
AudioService: update volume cache, stream:3, device:headphone, index:70, caller:MediaSessionService
2. 初步结论

网易云使用的是adjustStreamVolume,酷狗使用的是setStreamVolume.
将代码改为adjustStreamVolume发现解决了问题。
setStreamVolume:直接设置音量大小
adjustStreamVolume :设置direction,以步长调节音量大小

四、根本原因:源码

1. 日志区别
AudioManager的setStreamVolume进入AudioService之后,有这么一段
if (!checkSafeMediaVolume(streamTypeAlias, index, device)) { //音量安全检测
   mVolumeController.postDisplaySafeVolumeWarning(flags);
    mPendingVolumeCommand = new StreamVolumeCommand(
                                        streamType, index, flags, device);
} else {
    onSetStreamVolume(streamType, index, flags, device); //重点
    index = mStreamStates[streamType].getIndex(device);
}

private void onSetStreamVolume(int streamType, int index, int flags, int device) {
    setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false);
    // setting volume on master stream type also controls silent mode
    if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
            (mStreamVolumeAlias[streamType] == getMasterStreamType())) {
        int newRingerMode;
        if (index == 0) { //如果设置音量为0,设置新的铃声模式:震动、静音、有声
            newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
                    : VOLUME_SETS_RINGER_MODE_SILENT ? AudioManager.RINGER_MODE_SILENT
                    : AudioManager.RINGER_MODE_NORMAL;
        } else {
            newRingerMode = AudioManager.RINGER_MODE_NORMAL;
        }
        setRingerMode(newRingerMode, TAG + ".onSetStreamVolume", false /*external*/);
    }
}

setRingerMode这个里面会调用setRingerModeInt,这个里面会将0-9的StreamType中符合静音的type
设置mStreamStates[streamType].mute(null, false),接着handler.mute_syncVSS(state);
然后发送消息
if (updateVolume) {
  sendMsg(mAudioHandler,
    MSG_SET_ALL_VOLUMES,
    SENDMSG_QUEUE,
    0,
    0,
    VolumeStreamState.this, 0);
}
接着将一些applyAllVolumes执行jni方法音量置0。

还请以后注意这个问题,使用合理的设置音量方式。

正确使用方式为:
如果想把音量设置为0,先用setStreamVolume设置1,再用adjustStreamVolume设置0。

 类似资料: