KunSpace
返回博客

Android 音量设置流程:AudioPolicy 与 AudioFlinger 如何配合

2026年7月10日
1 次阅读

读完了?别空手走啊 😏

这行字出现时,你的大拇指应该已经 itch 了

写评论

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

这篇写什么?

按一下音量键,屏幕上的格子变了,声音也变了——中间其实是 AudioPolicy(策略)AudioFlinger(混音执行) 一起干活。厂商适配音量时,几乎都会碰到两件事:

  1. 软音量:在软件侧(Track / Mixer)把 PCM 乘上增益
  2. 硬件音量:写到芯片 / 功放寄存器(或经 HAL 设 port gain)

最终听到的声音 = 两者(再加 EQ、MS12 DAP 等)叠在一起。

一句话:

按键 → AudioService 记 index、刷 UI
     → AudioSystem → AudioPolicy 算 volumeDb / 下发
     → AudioFlinger 软音量(master × stream × track)
     → HAL 软处理 / 硬件增益 → 喇叭

Android 音量设置流程总览

相关:TV 切到 HDMI-IN 的 Patch 流程


一、先分清两层:谁负责「记」、谁负责「算」、谁负责「乘」

层级 主要职责
Java(AudioService) 响应按键、决定调哪路 stream、记 UI 刻度(index)、更新音量条
Native Policy(AudioPolicyManager) 按设备 / 音量曲线把 index 换成 dB / 策略结果,通知该动的 output
Native Flinger(AudioFlinger) 混音时对 Track 做 软音量master × type × …
HAL 把策略落到 port gain / 寄存器 / MS12 postgain,并在写 PCM 时再乘一遍 sink gain(视方案)

所以调试时别只盯一个进程:

  • UI 变了、声音没变 → 多半卡在 Policy→Flinger/HAL
  • 声音变了、UI 不对 → 多半卡在 Java index / alias
  • 媒体小声、HDMI-ARC 却满幅 → 常见是 硬音量 / 固定音量设备 策略

二、Java 层:响应按键 → 记下 index

2.1 入口链路(概念)

音量键 / 触控调节
  → AudioManager
  → AudioService
       handleVolumeKey
         → adjustSuggestedStreamVolume   // 判断调哪路 stream
           → adjustStreamVolume          // alias、device、发消息
             → Handler: MSG_SET_DEVICE_VOLUME
               → setDeviceVolume
                 → applyDeviceVolume_syncVSS
                   → AudioSystem.setStreamVolumeIndex(...)  // 进入 Native

2.2 几个容易晕的点

1)Suggested stream ≠ 最终 stream
adjustSuggestedStreamVolume 会结合「用户是否锁了音量控制流」「当前真正活跃的流(音乐 / 铃音 / 通知…)」选出最终 streamType。电视上多数流常 alias 到 MUSIC,所以你会看到「调铃音也在动媒体曲线」一类现象——往往是 alias 设计,不是乱调。

2)Alias 组别
不同 streamType 可能共享同一套音量策略(mStreamVolumeAlias)。adjustStreamVolume 先解析 alias,再 getDeviceForStream 找当前输出设备。

3)消息机制
真正「把 index 写进设备状态」常走 Handler(如 MSG_SET_DEVICE_VOLUMEsetDeviceVolume),避免在按键线程里塞太多同步工作。setDeviceVolume 还会把同一 alias 下其它流一并 apply,所以 log 里会刷很多 stream——跟当前关心的那一类即可

4)index 是刻度,不是 dB
applyDeviceVolume_syncVSS 算出的 index 是 UI 进度(例如音乐 0~15)。静音、A2DP 绝对音量、full volume 设备等会改 index 算法。最后调用:

AudioSystem.setStreamVolumeIndex(streamType, index, device)

Java 层到此基本结束;真正响度计算在 Native。


三、Native:Policy 算策略,Flinger 做软音量

3.1 AudioSystem → AudioPolicyService

AudioSystem::setStreamVolumeIndex 通过 Binder 找到 media.audio_policy,转到:

AudioPolicyManager::setStreamVolumeIndex
  → 按 stream 取 attributes / volume group
  → setVolumeIndexForAttributes
       → setVolumeCurveIndex(记下曲线上的 index)
       → 遍历活跃 output
       → checkAndSetVolume(算 volumeDb 并 setVolume)

这里体现 Policy 的价值:不是简单 index → 音量,还要看:

  • 当前是否静音、通话 / BT SCO 互斥
  • 固定音量设备(有的通路强制 0 dB 软件衰减,改交给耳机或功放)
  • 多 strategy 抢同一输出时的优先级(硬件增益场景尤其明显)
  • TV 等平台上,是否用 MUSIC 曲线统一驱动、开机动画期间特殊衰减等

算完后 outputDesc->setVolume(...),把结果交给执行侧;必要时再 setVoiceVolume

3.2 AudioFlinger:软音量在混音时落地

软音量经典形态(示意):

最终增益 ≈ masterVolume × streamTypeVolume × trackVolume × VolumeShaper …

在 Direct / Mixer 线程的 prepareTracks_l 里,会对活跃 track 调用类似 processVolume_l

  • 主静音 / 流静音 / 播放限制 → 增益置 0
  • 否则按上面公式算左右声道
  • 有 effect chain 时,音量常交给 effect;否则 setVolumeForOutput_l 直接设输出

可以记: Policy 决定「这一路该多少」;Flinger 在 每条 track / 每次混音准备 时把数乘到 PCM(或交给下游)。


四、HAL:硬件增益与写数时再乘一遍

到芯片侧,常见两条腿:

4.1 端口配置:adev_set_audio_port_config(配置阶段)

Framework / Policy 通过 port config 下发 GAIN 时,HAL 会:

  1. 校验 AUDIO_PORT_CONFIG_GAIN
  2. 在 patch 列表里找到对应补丁
  3. sink / source 角色写入 sink_gain[] / src_gain[]
  4. 扬声器增益变化时做 easing(淡入淡出),避免「咔」一下跳变
  5. 若走 Dolby MS12,可能同步 DAP postgain / main volume

这是「把策略增益记进 HAL 设备状态」。

4.2 数据处理:audio_hal_data_processing(写流阶段)

真正出 PCM 时,HAL 常再:

  • 按输出格式分支(IEC61937 / AC3 / DTS / PCM32 / TV 8ch 映射…)
  • 对 SPK/HP 乘上 sink_gain、EQ、线路增益等
  • MS12 管音量时,软件侧可能 强制 volume=1.0,避免双重衰减
  • easing 进行中时,由 ease 进程接管,而不是再暴力 apply_volume

所以:同一套 index,可能一部分在 Flinger 软乘,一部分在 HAL/MS12/功放——查「为什么音量不线性」时要两边对照。


五、软音量 vs 硬件音量(适配时怎么选)

软音量 硬件音量
改什么 PCM 样本幅度 功放 / Codec / MS12 增益
优点 实现快、流级别灵活 底噪、动态范围、功耗往往更好
缺点 减太狠伤信噪比 要适配每路输出设备
常见组合 App/媒体流精细调节 总音量、TV 喇叭主音量

TV / Soundbar 上常见策略:

  • 喇叭:偏硬件增益 +(可选)软 easing
  • HDMI-ARC / 部分数字口:软件常固定 0 dB,音量交给回传设备(CEC/功放)
  • A2DP:看是否支持 AVRCP 绝对音量,决定 HAL 是否还要再衰减

六、排查清单(实用)

  1. UI index 变了吗?(Java sendVolumeUpdate
  2. Policy 是否算出合理 volumeDb?checkAndSetVolume / 曲线)
  3. Flinger track 软音量是否更新?processVolume_l / mute)
  4. HAL sink_gain 变了吗?adev_set_audio_port_config
  5. 写 PCM 时是否又乘了一次?是否与 MS12 双重控制冲突?
  6. 当前设备是 SPEAKER 还是 ARC/耳机? 策略可能完全不同

Log 建议同时抓:audioserveraudio_policy、HAL 音量相关 tag;只抓一层很容易误判。


总结

Android 音量不是「一个 API 改寄存器」这么简单,而是:

  1. Java 负责交互与 index
  2. AudioPolicy 负责曲线、设备、优先级 → dB
  3. AudioFlinger 负责混音路径上的软音量
  4. HAL 负责硬件增益与出流时再应用

软 / 硬分工当前输出设备 想清楚,音量适配和「调了没反应」类问题会好查很多。


相关阅读

评论

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

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