【Android Audio】A2DP 与 HFP 音量同步逻辑架构

📅 2026/7/8 3:43:16
【Android Audio】A2DP 与 HFP 音量同步逻辑架构
蓝牙 A2DP 与 HFP 音量同步逻辑架构分析本文档总结了 Android 蓝牙模块中关于 A2DP Output通过 AVRCP和 HFP 协议的音量同步逻辑。音量同步主要分为“平台控制音量同步到设备”和“设备控制音量同步到平台”两个方向。1. A2DP Output (AVRCP) 音量同步逻辑A2DP 的音量同步通常依赖于 AVRCP (Audio/Video Remote Control Profile) 协议中的Absolute Volume (绝对音量)功能。相关逻辑主要在AvrcpVolumeManager.java中处理。AVRCP 的音量范围是 0 - 127而 Android 系统的媒体音量范围通常是 0 - 15或其它最大值因此中间涉及音量的映射转换。1.1 设备控制音量同步到平台 (Device - Platform)当用户在蓝牙耳机端调节音量时耳机通过 AVRCP 发送音量改变指令。关键代码路径蓝牙协议栈接收到 AVRCP 音量变化。调用AvrcpVolumeManager.setVolume(BluetoothDevice, int)。将 AVRCP 音量 (0-127) 转换为系统音量 (如 0-15)。调用AudioManager.setStreamVolume()更新系统音量并带上FLAG_BLUETOOTH_ABS_VOLUME标志。代码片段引用 (AvrcpVolumeManager.java)/** * Informs {link AudioManager} of a remote device volume change and stores it. * param avrcpVolume in range (0 - 127) received from remote device. */voidsetVolume(NonNullBluetoothDevicedevice,intavrcpVolume){intdeviceVolumeavrcpToSystemVolume(avrcpVolume);// 转换音量// ... logging ...mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,deviceVolume,(deviceVolume!getVolume(device,-1)?AudioManager.FLAG_SHOW_UI:0)|AudioManager.FLAG_BLUETOOTH_ABS_VOLUME);// 同步给系统 Audio 框架storeVolumeForDevice(device,deviceVolume);}关键日志分析logcat不支持avrcp会打印05-25 16:51:13.315 2563 2563 D AvrcpTargetService: stream volume change to 9 XX:XX:XX:XX:63:DCdumpsys bluetooth_manager和logcat中会有setVolume打印storeVolume打印index为avrcp的0-127的音量AvrcpVolumeManager: mCurrentDevice: XX:XX:XX:XX:63:DC Current System Volume: 3 Device Volume Memory Map: Device Address : Device Name : Vol : AbsVol 48:73:CB:FC:63:DC : Redmi Buds ... : 3 : true BTAudio Volume Events: 07-06 08:44:52.499 setVolume: deviceXX:XX:XX:XX:43:20 avrcpVolume71 deviceVolume56 mDeviceMaxVolume100 07-06 08:44:52.538 storeVolume: Storing stream volume level for device XX:XX:XX:XX:43:20 : 561.2 平台控制音量同步到设备 (Platform - Device)当用户在手机端系统界面或按键调节媒体音量时平台需要将新的音量同步给蓝牙设备。关键代码路径系统音量发生改变。触发AvrcpVolumeManager.sendVolumeChanged(BluetoothDevice, int)。将系统音量转换为 AVRCP 音量 (0-127)。调用 native 接口mNativeInterface.sendVolumeChanged()将音量发给耳机。(另外在AudioService.java中也会通过postSetAvrcpAbsoluteVolumeIndex和BtHelper.java通知 A2DP 改变绝对音量。)代码片段引用 (AvrcpVolumeManager.java)/** * Informs remote device of a system volume change and stores it. * param deviceVolume in range (0 - {code mDeviceMaxVolume}) received from system. */voidsendVolumeChanged(NonNullBluetoothDevicedevice,intdeviceVolume){if(deviceVolumegetVolume(device,-1)){return;}intavrcpVolumesystemToAvrcpVolume(deviceVolume);// 转换为 AVRCP 0-127 的范围// ... logging ...mNativeInterface.sendVolumeChanged(device,avrcpVolume);// 调用 Native 接口发送给设备storeVolumeForDevice(device,deviceVolume);}关键日志分析logcat中有打印AudioService设置音量setAvrcpAbsoluteVolumeIndex的打印index是Audio侧的范围index下面打印index为Audio侧音量index的10倍值05-25 16:51:15.006 743 1107 D AS.AudioService: setStreamVolume postSetAvrcpAbsoluteVolumeIndex index100stream0dumpsys bluetooth_manager和logcat中有sendVolumeChanged的打印audio同步给avrcp的音量和audio音量AvrcpVolumeManager: mCurrentDevice: XX:XX:XX:XX:63:DC Current System Volume: 3 Device Volume Memory Map: Device Address : Device Name : Vol : AbsVol 48:73:CB:FC:63:DC : Redmi Buds ... : 3 : true BTAudio Volume Events: 05-25 16:50:33.658 sendVolumeChanged: deviceXX:XX:XX:XX:63:DC avrcpVolume5 deviceVolume1 mDeviceMaxVolume25 05-25 16:50:33.659 storeVolume: Storing stream volume level for device XX:XX:XX:XX:63:DC : 105-25 16:50:33.658 2563 2577 D AvrcpVolumeManager: sendVolumeChanged: deviceXX:XX:XX:XX:63:DC avrcpVolume5 deviceVolume1 mDeviceMaxVolume25 05-25 16:50:33.659 2563 2577 D AvrcpVolumeManager: storeVolume: Storing stream volume level for device XX:XX:XX:XX:63:DC : 1dumpsys audio中Events log: volume changes字段也会记录音量调节历史记录mBtHelper.setAvrcpAbsoluteVolumeIndex的调节值(Audio侧的index转换在AvrcpVolumeManager):05-25 16:51:30:039 setAvrcpVolume: index:32. HFP 音量同步逻辑HFP (Hands-Free Profile) 用于通话场景。通话时的音量同步依赖于 AT 指令ATVGS(扬声器音量) 和ATVGM(麦克风音量)。音量范围通常是 0 - 15。2.1 平台控制音量同步到设备 (Platform - Device)当用户在手机端调整通话音量 (SCO Volume) 时系统需要通过 AT 指令通知耳机。关键代码路径AudioManager发出音量改变广播AudioManager.ACTION_VOLUME_CHANGED。HeadsetService.java中的广播接收器mHeadsetReceiver拦截到该广播判断如果是STREAM_BLUETOOTH_SCO(或STREAM_VOICE_CALL) 发生了变化就向对应的状态机发送INTENT_SCO_VOLUME_CHANGED消息。HeadsetStateMachine.java处理该消息调用processIntentScoVolume()。比较当前记录的mSpeakerVolume如果不一致则通过mNativeInterface.setVolume()发送ATVGS给设备。代码片段引用 (HeadsetService.java)privatefinalBroadcastReceivermHeadsetReceivernewBroadcastReceiver(){OverridepublicvoidonReceive(Contextcontext,Intentintent){// ...caseAudioManager.ACTION_VOLUME_CHANGED:{intstreamTypeintent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,-1);intvolStreamAudioManager.STREAM_BLUETOOTH_SCO;if(deprecateStreamBtSco()){volStreamAudioManager.STREAM_VOICE_CALL;}if(streamTypevolStream){doForEachConnectedStateMachine(stateMachine-stateMachine.sendMessage(HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED,intent));}break;}// ...代码片段引用 (HeadsetStateMachine.java)voidprocessIntentScoVolume(Intentintent,BluetoothDevicedevice){intvolumeValueintent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE,0);if(mSpeakerVolume!volumeValue){mSpeakerVolumevolumeValue;mNativeInterface.setVolume(device,HeadsetHalConstants.VOLUME_TYPE_SPK,mSpeakerVolume);// 发送 VGS 指令}}2.2 设备控制音量同步到平台 (Device - Platform)当用户在蓝牙耳机端调节通话音量时耳机通过 AT 指令 (ATVGS等) 通知手机端。关键代码路径Native 层接收到耳机的 AT 音量控制指令向状态机发送STACK_EVENT且类型为EVENT_TYPE_VOLUME_CHANGED。HeadsetStateMachine.java调用processVolumeEvent(int volumeType, int volume)。若为扬声器音量 (VOLUME_TYPE_SPK)更新内部mSpeakerVolume变量。调用AudioManager.setStreamVolume()来同步更新手机系统的通话音量。代码片段引用 (HeadsetStateMachine.java)voidprocessVolumeEvent(intvolumeType,intvolume){// ...if(volumeTypeHeadsetHalConstants.VOLUME_TYPE_SPK){mSpeakerVolumevolume;booleanshowVolume!Flags.hfpVolumeControlProperty()||SystemProperties.getBoolean(HFP_VOLUME_CONTROL_ENABLED,true);intflagshowVolume(mCurrentStatemAudioOn)?AudioManager.FLAG_SHOW_UI:0;intvolStreamAudioManager.STREAM_BLUETOOTH_SCO;if(deprecateStreamBtSco()){volStreamAudioManager.STREAM_VOICE_CALL;}intcurrentVolmSystemInterface.getAudioManager().getStreamVolume(volStream);if(volume!currentVol){mSystemInterface.getAudioManager().setStreamVolume(volStream,volume,flag);// 同步给系统}}elseif(volumeTypeHeadsetHalConstants.VOLUME_TYPE_MIC){mMicVolumevolume;// 麦克风音量同步}// ...}3. 核心架构总结解耦设计无论是 A2DP 还是 HFP蓝牙协议层都尽量与系统的AudioManager进行双向绑定。底层蓝牙堆栈负责与远程设备的实际通信(AVRCP 或 AT 指令)然后回调到 Java 层的Service和StateMachine。循环避免 (Loop Prevention)在实现双向同步时代码中均有if (volume ! currentVol)这类判断避免平台和设备之间的音量同步陷入死循环。音量数值映射 (Value Mapping)A2DP(AVRCP) 的音量标准是绝对音量0-127这需要和 Android 系统的离散音量阶数例如0-15或0-25做数学映射而 HFP 的ATVGS本身范围就较小通常可以直接使用系统原生的 SCO 音量范围进行对应。