这篇写什么?
上一篇 音量设置流程 讲的是 index → Policy 算 dB → Flinger 软音量 → HAL。
本文补全 按键按下之后、进 Native 之前:应用 / 遥控器如何进到 AudioService,以及途中的静音、蓝牙绝对音量、CEC、透传、无障碍、全音量设备等分支。
音量「听感不对」类问题见 音量问题排查。
一句话:
音量键 → MediaSession → AudioManager
→ AudioService.adjustStreamVolume / setStreamVolume
→ 静音 or adjustIndex
→ MSG_SET_DEVICE_VOLUME → AudioSystem.setStreamVolumeIndexAS
→(旁路)AVRCP / HearingAid / HDMI-CEC / passthrough 提示
→ sendVolumeUpdate → 音量条 UI
〇、HAL 侧落点(盒子 vs Soundbar)
Framework 最终会把增益策略落到 HAL,产品形态不同,入口也不同:
| 产品 | 常见 HAL 接口 |
|---|---|
| 盒子(STB) | out_set_volume |
| Soundbar | adev_set_audio_port_config(port gain) |
本文主线停在 AudioService;HAL 之后如何乘增益,仍看 音量设置流程 · HAL 章节。
一、应用层入口
应用通过 AudioManager 调节:
// 相对调节:加 / 减 / 静音方向
AudioManager.adjustStreamVolume(int streamType, int direction, int flags);
// 绝对设置:直接指定 index
AudioManager.setStreamVolume(int streamType, int index, int flags);
adjustStreamVolume 会经 Binder 调到 IAudioService:
public void adjustStreamVolume(int streamType, int direction, int flags) {
final IAudioService service = getService();
service.adjustStreamVolume(streamType, direction, flags,
getContext().getOpPackageName());
}
| 参数 | 含义 |
|---|---|
streamType |
音频流类型(MUSIC / RING / ACCESSIBILITY…) |
direction |
ADJUST_RAISE / LOWER / MUTE / UNMUTE / TOGGLE_MUTE / SAME 等 |
flags |
是否显示 UI、是否震动等标志位 |
真正干活在 AudioService.adjustStreamVolume(...)。
二、音量键:从 MediaSession 进到 AudioService
遥控器 / 系统音量键往往不直接调 App 的 AudioManager,而是走 MediaSession 再落到 AudioService:

简化链路:
MediaSessionManager
dispatchVolumeKeyEvent… / …ToSessionAsSystemService
→ MediaSessionService
handleVolumeKeyEventLocked → dispatchAdjustVolumeLocked
→ AudioManager.adjustSuggestedStreamVolumeForUid
→ AudioService.adjustSuggestedStreamVolume
→ adjustStreamVolume
要点:
- Suggested stream:先猜该调哪路流(活跃会话 / 用户锁定的 volControlStream)
- TV 上多数流常 alias 到 MUSIC,所以 log 里常见
stream=3 - 真正改 index / 静音后,再走 Handler 与 UI
三、AudioService 内部:静音 / 调 index / 刷 UI

两条常用入口最后都会汇到 AudioHandler + VolumeStreamState:
| 入口 | 路径 |
|---|---|
| 绝对设置 | setStreamVolume → onSetStreamVolume → setStreamVolumeInt |
| 相对调节 | adjustSuggestedStreamVolume → adjustStreamVolume |
3.1 静音分支
当 isMuteAdjust 为真,且设备不在 mFullVolumeDevices(全音量设备往往不允许本地静音)时:
ADJUST_TOGGLE_MUTE:翻转streamState.mIsMutedADJUST_MUTE/UNMUTE:按方向设静音状态- MUSIC 时常同步
setSystemAudioMute(CEC System Audio 等场景) - 对同一 alias 下的流调用
VolumeStreamState.mute(state) - 强制相机音效流(
STREAM_SYSTEM_ENFORCED+ camera sound forced)可跳过
随后常发 MSG_SET_ALL_VOLUMES → applyAllVolumes()。
3.2 正常加减(非静音)
条件大致是:不是 full volume 设备,且 adjustIndex(...) 成功(或当前处于静音态需要配合处理):
ADJUST_RAISE:若原先静音,立即mute(false)(Amlogic 等平台会对 alias 组批量 unmute)ADJUST_LOWER:在 single volume 等场景可能 延迟 unmute(MSG_UNMUTE_STREAM)
然后:
sendMsg(MSG_SET_DEVICE_VOLUME) → setDeviceVolume
→ VolumeStreamState.setStreamVolumeIndex
→ AudioSystem.setStreamVolumeIndexAS // 进入 Native Policy
handleMessage 里典型分支:
| msg | 动作 |
|---|---|
MSG_SET_DEVICE_VOLUME |
setDeviceVolume |
MSG_SET_ALL_VOLUMES |
setAllVolumes |
MSG_UNMUTE_STREAM |
延迟取消静音 |
3.3 刷音量条 UI
sendVolumeUpdate → VolumeController.postVolumeChanged → SystemUI VolumeDialogControllerImpl → VolumeDialogImpl 画条。
改 HAL 增益 与 刷 UI 是两条线:可以只改数不显示条,也可以只显示条(视 flags)。
四、旁路同步:蓝牙、助听器、CEC、透传
4.1 蓝牙 AVRCP 绝对音量
adjustStreamVolume 时若当前设备在 A2DP 集合,且流 alias 为 MUSIC:
静音 → newIndex = 0
否则 → 用当前 index
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex / 10) // 刻度换算视平台
遥控器调音量 → 同步到蓝牙音箱(示意 log):
AudioService: adjustStreamVolume: postSetAvrcpAbsoluteVolumeIndex index=50 stream=3
AS.BtHelper: setAvrcpAbsoluteVolumeIndex index=5
BluetoothA2dp: setAvrcpAbsoluteVolume
AvrcpVolumeManager: sendVolumeChanged ... deviceVolume=5
蓝牙音箱端调音量 → 回写系统(示意 log):
AvrcpTargetJni: setVolume
AvrcpVolumeManager: setVolume ... avrcpVolume=55 deviceVolume=6
AudioService: setStreamVolume(stream=3, index=6, calling=com.android.bluetooth)
两边是「系统调 → AVRCP 下发」与「音箱调 → setStreamVolume 回写」对称关系。
4.2 助听器
device == DEVICE_OUT_HEARING_AID
&& streamType == getHearingAidStreamType()
→ mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType)
4.3 HDMI-CEC 音量
当同时满足大致条件:
mHdmiPlaybackClient != nullmHdmiCecVolumeControlEnabled- stream alias 为 MUSIC
- 设备在
mFullVolumeDevices
则本地不算「可变软音量」,而是把方向映射成按键发给对端:
| direction | keyCode |
|---|---|
| RAISE | KEYCODE_VOLUME_UP |
| LOWER | KEYCODE_VOLUME_DOWN |
| MUTE / UNMUTE / TOGGLE | KEYCODE_VOLUME_MUTE |
调用 mHdmiPlaybackClient.sendVolumeKeyEvent(...),由电视 / 功放真正改音量。
4.4 Passthrough:本地调不动
透传时本地往往无法有效调节 PCM 增益,应对用户提示,由后端设备控制:
STREAM_MUSIC && isVolumePassthrough() → showPassthroughWarning()
与「全音量设备隐藏音量条」常一起出现(见下一节)。
五、无障碍(TalkBack)音量
5.1 独立音量 vs 跟 MUSIC
updateStreamVolumeAlias 里:
a11yStreamAlias = sIndependentA11yVolume
? STREAM_ACCESSIBILITY
: STREAM_MUSIC
若 sIndependentA11yVolume == true:
STREAM_ACCESSIBILITY用独立 index,并从 SettingsreadSettings()恢复- 遥控器按音量键常调的是 MUSIC,调不动 TalkBack——这是预期行为
TalkBack 语音大小请到:
Settings → Device Preferences → Accessibility → TalkBack
→ Configuration → Speech volume
若产品要求「遥控器也能调无障碍音量」,常见改法是把对应 alias 改回 STREAM_MUSIC(需评估与独立响度策略的冲突)。
5.2 静音键调不了无障碍流
if (isMuteAdjust && !isStreamAffectedByMute(streamType)) {
return; // 直接返回,不静音
}
isStreamAffectedByMute 看位掩码 mMuteAffectedStreams:
return (mMuteAffectedStreams & (1 << streamType)) != 0;
公版默认受静音影响的流常见包括 MUSIC / RING / NOTIFICATION / SYSTEM / VOICE_CALL / BLUETOOTH_SCO 等,往往不含 STREAM_ACCESSIBILITY。
例:streamType = 10(ACCESSIBILITY)时,1 << 10 与默认掩码相与为 0 → 返回 false → 静音键无效。
若产品要求静音键也静掉 TalkBack,需把 ACCESSIBILITY 位加入 DEFAULT_MUTE_STREAMS_AFFECTED / mMuteAffectedStreams(注意无障碍可达性合规)。
六、全音量设备(Full Volume)与隐藏音量条
「全音量」意味着:本地不做可变 index 衰减,音量交给对端(TV / 功放)或透传链路。
6.1 两种常见把 HDMI OUT 设成全音量的情况
(1)打开 CEC,且打开 CEC Volume Control
hdmiCecSink && mHdmiCecVolumeControlEnabled
→ addAudioSystemDeviceOutToFullVolumeDevices(DEVICE_OUT_HDMI)
(2)编码 / 透传输出(非 PCM)
例如非 MS12,或 MS12 但 digital_audio_format == BYPASS 时,看 SPDIF/HDMI 格式是否为 AML_STEREO_PCM:
hal_control_vol_en = (format == AML_STEREO_PCM) ? true : false;
非 PCM(DTS / Dolby 透传等)时本地 HAL 音量控制关闭,行为上接近「调不动 / 交给后级」。
6.2 UI:全音量时隐藏音量条
sendVolumeUpdate 中对 MUSIC:
if (isFullVolumeDevice(device)) {
flags &= ~FLAG_SHOW_UI; // 隐藏音量条
// log: Hide volume bar for TV's available or in audio passthrough mode.
}
典型 log:
AudioService: adjustStreamVolume() stream=3, dir=1, ...
AudioService: Hide volume bar for TV's available or in audio passthrough mode.
6.3 setDeviceVolumeBehaviorInternal
| behavior | 效果 |
|---|---|
VARIABLE |
移出 full / fixed,本地可调 |
FIXED |
固定音量设备 |
FULL |
加入 full volume(HDMI 时往往还要求 CEC sink + CEC volume 可用,否则可能「完全没法调」) |
ABSOLUTE… |
当前路径常直接抛不支持 |
七、排查时怎么对号入座
| 现象 | 先看 |
|---|---|
| 按键有 UI、Native 无增益变化 | MSG_SET_DEVICE_VOLUME 之后 Policy/HAL |
| 有按键 log、无音量条 | FLAG_SHOW_UI 被清掉?full volume / passthrough? |
| 蓝牙不同步 | AVRCP absolute volume 路径 / setAvrcpAbsoluteVolumeIndex |
| CEC 下本地调不动 | 是否 full volume + sendVolumeKeyEvent |
| 透传调不动 | isVolumePassthrough / 非 PCM 格式 |
| TalkBack 遥控器调不动 | sIndependentA11yVolume,去 Speeech volume 设置 |
| TalkBack 静音键无效 | isStreamAffectedByMute / mMuteAffectedStreams |
| DVB 后再播 OTT 变小 | 见 音量问题排查 · AD/continuous |
按键进 MediaSession 了吗?
→ adjustStreamVolume 走到静音还是 adjustIndex?
→ 是否 full volume / passthrough / a11y 提前 return?
→ MSG_SET_DEVICE_VOLUME → setStreamVolumeIndexAS?
→ 旁路:AVRCP / CEC / HearingAid 是否该同步却没同步?
→ UI flags 是否被去掉 SHOW_UI?
八、小结
- 入口:App 用
adjustStreamVolume/setStreamVolume;按键多经 MediaSession → adjustSuggestedStreamVolume。 - 主干:
AudioService里分 静音 与 adjustIndex,再经MSG_SET_DEVICE_VOLUME下到AudioSystem。 - 旁路:A2DP 绝对音量、助听器、HDMI-CEC 按键、passthrough 提示。
- 无障碍:独立 volume 时遥控器调 MUSIC 正常调不动 TalkBack;静音掩码常不含 ACCESSIBILITY。
- 全音量:CEC volume control 或 非 PCM 透传 会让 HDMI 变为 full volume,并 隐藏音量条。
- HAL:盒子常见
out_set_volume,Soundbar 常见adev_set_audio_port_config。
把「Java 分支」和「Native 曲线 / HAL」分开看,音量键类问题会清晰很多。
相关阅读
评论
还没有评论,来做第一个吧
读完了不评论,作者会以为你在沉思