KunSpace
返回博客

拔掉 ARC 后电视音量飙到最大:isFullVolumeDevice 踩坑

2026年7月6日
尚无阅读

读完了?别空手走啊 😏

写到这了还不点个赞?键盘会痛的

写评论

读完了不评论,作者会以为你在沉思

这篇写什么?

现象很吓人,也很容易复现:

  1. 电视先调到 较低音量
  2. 用 HDMI ARC 接上 Soundbar,播 Spotify 等音乐
  3. 拔掉 Soundbar 的 HDMI
  4. 声音切回电视喇叭时,突然变成 最大音量

排查顺序是:先排除「双屏 / 面板 Gain」误判 → 用 log 证明下层只是在执行 index=100 → 再追到 Java 层谁把 100 塞进去的。

一句话:

不是 HAL 自己拉满,也不是面板 Gain=1
  → AudioPolicy 只是收到 volumeIndex=100
  → 根因:AudioService.isFullVolumeDevice
     误把 mHdmiSystemAudioSupported OR 进来
  → 拔 ARC 后喇叭被当成 full-volume 设备

拔 ARC 音量飙最大:排查路径

相关背景:Android 音量设置流程


一、现象与复现

步骤 操作
1 系统音量调低
2 连接 Soundbar(HDMI ARC),播放音乐
3 拔掉 Soundbar HDMI
4 音频路由回本机喇叭 → 音量跳到最大

关键点:没有去操作什么「大屏小屏切换播放」,只是断开 ARC,上层就会把音量写成最大。


二、先排除:不是「面板 Gain=1 = 最大音量」

一开始容易怀疑:客户大屏 / 小屏面板音量控制是不是把音量写成满了。

日志里能看到类似:

hal_param_app_panel_id_and_volume=0,1.0000
hal_param_app_panel_id_and_volume=1,1.0000

面板 volume 参数实为 Gain×1

和客户对齐后确认:这里的 1.0000 是增益倍数(×1),意思是「按系统当前音量原样输出」,不是把音量 index 设成 100%。

结论:

  • 与大屏 / 小屏面板音量无关
  • 断开 Soundbar 时,是 上层主动把系统音量 index 推到最大

三、日志:谁把 volumeIndex 写成了 100?

继续抓 log,在 Audio Policy 侧能看到:

checkAndSetVolume ... volumeIndex: 100
  volumeMinIndex: 0  volumeMaxIndex: 100
  curDevice: 0x2   // 常见对应 SPEAKER

随后 HAL 走 adev_set_audio_port_config,各 outport gain 多为 1.0,活跃通路在切换瞬间仍可能和 HDMI_ARC / SPEAKER 纠缠在一起——但 「100」这个数字已经在 Policy 层定了

checkAndSetVolume 收到 volumeIndex=100

查看 AudioPolicyManager.cpp 里相关逻辑:厂商侧 未改,是原生路径——根据上层下发的 index,换算 dB,再 updateGain / setVolumeCurveIndex

APM checkAndSetVolume → updateGain

也就是说:Native Policy 是「忠实执行者」,真正要查的是谁调用了 setStreamVolumeIndex / setVolumeCurveIndex,并且传入了 100

setVolumeCurveIndex device=SPEAKER, index=100

setVolumeIndexForAttributes 里最终落到:

status = setVolumeCurveIndex(index, device, curves);

setVolumeCurveIndex 调用点

到这里可以下半个结论:100 来自更上层的 AudioService,不是 APM 自己发明的。


四、复现时建议怎么抓证据

开机后立刻开始存 log,复现后再 dump 音频状态,方便和「拔 ARC 瞬间」对齐:

# 开机后持续抓 log
adb logcat > /data/logcat_arc_volume.log

# 复现「拔 ARC → 音量飙最大」之后
adb shell "dumpsys media.audio_flinger > /data/dump_audio.txt"
adb shell "dumpsys media.audio_policy >> /data/dump_audio.txt"
adb shell "dumpsys audio >> /data/dump_audio.txt"
adb pull /data/logcat_arc_volume.log .
adb pull /data/dump_audio.txt .

在 log 里重点搜:

  • setVolumeCurveIndex + index 100
  • applyAllVolumes / isFullVolumeDevice
  • 拔 ARC 前后的设备切换、MUTE_CHANGED 一类事件

五、根因:isFullVolumeDevice 误判

最终在 AudioService 日志里钉死:

AudioService: applyAllVolumes isFullVolumeDevice device:2, index:100
APM_AudioPolicyManager: setVolumeCurveIndex device 00000002, index 100

applyAllVolumes + isFullVolumeDevice → index 100

对应代码类似(示意):

private boolean isFullVolumeDevice(int deviceType) {
    // ...
    return mFullVolumeDevices.contains(deviceType)
            || mHdmiSystemAudioSupported;  // ← 问题在这里
}

isFullVolumeDevice 中多余的 OR 条件

为什么拔 ARC 会中招?

概念 含义
Full volume device 被策略当成「应由外部设备控音量 / 本机应按满刻度处理」一类设备;applyAllVolumes 时可能直接把 index 写成 max(如 100)
mFullVolumeDevices 真正登记过的 full-volume 设备集合(按 deviceType 判断)
mHdmiSystemAudioSupported HDMI CEC / System Audio 能力相关标志,表示「系统音频经 HDMI 通路协同」一类状态

错误写法把 全局标志 mHdmiSystemAudioSupported|| 并进「某个 deviceType 是不是 full-volume」:

  • 只要该标志仍为 true
  • 任意 传入的 deviceType(包括本机 SPEAKER)都可能被当成 full-volume
  • 拔掉 ARC、路由回到喇叭时,applyAllVolumes 就会给喇叭写下 index=100

这和「音量键怎么发」不是一回事:isFullVolumeDevice 并不是直接绑在音量键发送上,但会在 应用全部音量 / 设备切换刷新 时把设备按满音量处理。

修复

去掉多余条件即可:

private boolean isFullVolumeDevice(int deviceType) {
    // ...
    return mFullVolumeDevices.contains(deviceType);
}

只让 真正登记在 mFullVolumeDevices 里的设备 走 full-volume 逻辑;HDMI System Audio 支持与否,不应「污染」对本机喇叭的判断。


六、排查清单(同类问题可复用)

  • 复现:低音量 → ARC 外放 → 拔线 → 是否瞬间最大
  • 排除面板 / 应用 Gain:确认是「×1」还是「写成 max」
  • log 搜 volumeIndex: 100 / setVolumeCurveIndex ... index 100
  • 确认 APM 是否原样下发(多数情况只是执行者)
  • isFullVolumeDevice / applyAllVolumes
  • 检查是否把 全局 HDMI 标志 错误 OR 进了 按设备类型 的判断

七、小结

层级 角色 本次结论
面板 / 客户 APK Gain 易被误判 1.0 = 乘 1,不是最大音量
AudioPolicy / HAL 执行 index→增益 原样执行 100,非根因
AudioService 决定 index isFullVolumeDevice 误 OR mHdmiSystemAudioSupported

拔 ARC 后音量飙最大,本质是:设备切换刷新音量时,把本机喇叭错当成 full-volume 设备,写下了最大 index。
修法很克制:让 isFullVolumeDevice 只看设备集合,不要用 HDMI 全局标志「一票通过」。


相关阅读

评论

还没有评论,来做第一个吧

读完了不评论,作者会以为你在沉思