Chapter 113: Audio Routing as System Policy
手机上的声音输出位置看似由 App 决定,例如音乐 App 选择扬声器,会议 App 选择蓝牙耳机,录音 App 选择内置麦克风。系统路径中真正承担决策的是 audio routing policy。Audio routing 指系统把一个音频输入流或输出流连接到具体设备的过程;policy 指这个连接需要同时满足设备状态、音频用途、用户选择、权限、通话状态、焦点、功耗和延迟约束。
本章用一个贯穿现象展开:音乐正在通过蓝牙耳机播放,用户拔掉耳机盒中的另一只耳机、接入 USB-C 声卡、随后收到 VoIP 来电,最后闹钟触发。读完本章后,读者应能追踪声音从哪里出、为什么切换、谁有权切换、App 应在何处同步状态,并能把“突然外放、蓝牙延迟、通话声音走听筒、闹钟压过媒体”这类现象归因到系统策略层。
移动 OS 把音频路由收束到系统服务层,是因为音频设备具有用户隐私和体验风险。麦克风路径涉及录音权限和隐私指示器;扬声器路径涉及外放风险;蓝牙和 USB 路径涉及连接稳定性、编码延迟和硬件能力;通话路径涉及基带、电话服务、VoIP session 和紧急提示。App 可以表达意图,也可以请求某个可用设备,但系统保留最终仲裁权。
本章对比 Android 和 Apple 平台时使用同一条责任链:App 音频意图 → framework 表达 → 系统音频服务 → 策略检查 → 设备能力和硬件路径 → App 可见通知 → 用户可见结果。Android 侧会落到 AudioAttributes、AudioManager、Audio Policy、AudioFlinger、HAL 和设备端口;Apple 侧会落到 AVAudioSession 的 category、mode、option、route change notification、Core Audio 和硬件路径。
113.1 Audio Routing 的系统策略属性
Audio routing 的系统策略属性体现在一个事实:App 发出的是音频用途声明,系统决定用途如何落到设备。一个播放流通常带有 media、alarm、notification、voice communication、assistant、game 等用途;一个录音流通常带有 microphone source、communication source 或系统认可的 capture 场景。系统根据这些用途和设备状态计算路由,随后把输出交给 speaker、receiver、wired headset、Bluetooth、USB 或投送路径。
贯穿例子从音乐播放开始。音乐 App 通过媒体播放器或 AudioTrack 输出 PCM 数据,设置的用途是 media。蓝牙耳机已连接时,系统把 media 流路由到 A2DP 或 LE Audio 等媒体输出路径。用户打开手机扬声器按钮时,App 只能把这个选择表达给系统;系统还要检查当前是否存在通话、当前蓝牙 profile 是否可用、设备是否处于固定音量模式、是否有更高优先级的提示音正在播放。路由结果因此是系统策略计算结果,而非单个 API 调用的直接硬件开关。
系统策略层至少接收六类输入。第一类是设备事实,包括内置扬声器、听筒、麦克风、有线耳机、USB audio、蓝牙 profile、AirPlay 或 Cast 目标是否可用。第二类是音频用途,包括媒体、通话、闹钟、通知、导航语音、录音和系统提示。第三类是用户选择,包括控制中心、音量面板、蓝牙设置、投送选择器和 App 内部选择器传给系统的偏好。第四类是权限和身份,包括麦克风权限、电话应用角色、后台播放能力和平台 entitlement。第五类是运行状态,包括前台、后台、锁屏、电话来电、音频焦点和 session active state。第六类是硬件约束,包括采样率、声道、profile、codec、buffer、DSP offload、功耗和温控。
这条路径可以抽象为一个策略闭环。图中的 policy engine 表示系统服务和配置共同形成的决策面,不对应某个单一公开类名。
系统音频服务的职责在图中有两个出口。一个出口通向 HAL、driver 和硬件,让声音真正进入设备;另一个出口通向 App 通知,让应用知道路由发生变化。后一个出口决定了 App 的工程质量:音频图要不要重建、UI 设备图标要不要更新、播放要不要暂停、录音格式要不要重新查询,都应由 route change 事件驱动。
Android 公开文档把设备枚举、通信设备选择和设备变化回调放在 AudioManager 一侧;AOSP 文档把设备拓扑、端口、路由、音量表和 policy engine 放在 audio policy configuration 一侧。Apple 公开 API 把 App 意图集中到 AVAudioSession,category、mode 和 option 决定 session 能参与哪些系统路由。两者的共同点是 App 表达约束,系统做仲裁。
113.2 Route Selection:Speaker、Receiver、Headset、Bluetooth、USB
Route selection 要先区分设备类型和场景用途。Speaker 是外放设备,适合媒体、闹钟、免提通话和游戏反馈。Receiver 是手机听筒,适合贴耳通话。Headset 包含有线耳机和带麦耳机,通常同时提供输出和输入。Bluetooth 需要再分 profile:A2DP 偏向高质量媒体输出,HFP 或 SCO 偏向双向语音通话,LE Audio 又引入新的能力组合。USB audio 提供外部声卡或数字音频设备,能力取决于设备描述、采样率、声道数、格式和电源状态。
同一首音乐在不同设备上路由时,系统处理的约束完全不同。走 speaker 时,系统主要处理媒体音量、外放风险、响度和功耗。走 wired headset 时,系统处理插拔事件和耳机麦克风输入。走 Bluetooth A2DP 时,系统处理蓝牙连接、codec、无线 buffer 和延迟。走 USB audio 时,系统处理 USB 枚举、设备能力、采样率转换和外设供电。走投送路径时,系统还要处理网络连接、远端设备状态和投送协议延迟。
贯穿例子中,音乐先通过蓝牙耳机播放。用户接入 USB-C 声卡后,系统会把“USB 设备可用”加入设备事实集合,但最终路由取决于用户选择和当前用途。媒体 App 可能继续使用蓝牙,因为用户刚才选择了蓝牙;也可能在系统 UI 中切换到 USB 声卡。VoIP 来电出现后,同一台 USB 声卡是否可用于通话还要看它是否提供输入端、是否满足 communication path、是否被系统列为可用通信设备。
设备选择可以按下面的检查顺序判断:先看音频用途,再看可用设备,再看用户选择,再看平台角色和权限,最后看硬件能力。媒体播放优先保持用户选择和连续体验;通话路径优先保证双向语音、回声消除、音量曲线和隐私;闹钟和紧急提示优先保证用户能听到;录音路径优先满足权限、前台状态、麦克风选择和隐私策略。
| 设备 | 典型用途 | 系统检查点 | 失败或降级表现 |
|---|---|---|---|
| Speaker | 媒体、闹钟、免提、游戏 | 外放风险、音量组、焦点、温控 | 突然外放、音量受限、提示音压过媒体 |
| Receiver | 蜂窝通话、私密语音 | 通话状态、贴耳场景、电话角色 | 声音很小、走听筒、免提切换失败 |
| Wired headset | 媒体、录音、监听 | 插拔状态、麦克风存在、线控事件 | 拔出后暂停、录音输入切回内置麦克风 |
| Bluetooth | 媒体、通话、车载 | profile、codec、连接状态、电量 | 延迟升高、音质切换、通话抢占媒体 |
| USB audio | 外接声卡、低延迟、专业录音 | 设备能力、采样率、供电、权限 | 无声、重采样、声道降级、设备断开 |
这张表说明一个工程判断:设备名称本身无法决定路由,设备能力和用途绑定后才形成有效路径。蓝牙耳机可以是媒体输出,也可以是通话输入输出;USB 设备可以是高保真输出,也可能没有麦克风输入;speaker 可以播放媒体,也可能在通话中受免提状态约束。排查时应把“设备存在”推进到“设备在当前用途下可用”。
Android 的 setCommunicationDevice 从 API level 31 开始提供通信场景设备选择,官方文档同时说明请求设备必须来自可用通信设备列表,请求在设备断开或进程结束时失效。旧的 speakerphone 和 Bluetooth SCO 直接控制接口在后续 API 中被标记为 deprecated,表达了平台从直接开关迁移到通信设备选择的方向。这个版本边界说明 App 的路由能力随着 API level 变化,工程设计应把设备选择封装成平台适配层。
Apple 侧的输出选择更集中地受到 audio session category、mode 和 option 约束。playback 主要表达输出播放意图,playAndRecord 表达双向音频意图,voiceChat 或 videoChat mode 会把系统策略推向语音处理路径。defaultToSpeaker、allowBluetooth、allowBluetoothA2DP、allowAirPlay 等 option 会改变可选路由集合。公开的 Audio Session Categories and Modes 文档说明 category 用来表达 App 音频意图,并列出 category 对输入输出、静音开关、锁屏和混音行为的影响。
113.3 Route Change Notification 与 App 状态同步
Route change notification 是系统把路由结果反馈给 App 的机制。设备插拔、蓝牙断开、用户在系统 UI 中切换输出、电话或 VoIP session 抢占、采样率变化、硬件格式变化,都可能让原有音频图失效。App 接到通知后要同步三类状态:音频处理图、用户界面和播放录制状态。
Android 的典型可见事件包括 ACTION_AUDIO_BECOMING_NOISY、音频设备回调、播放器事件和音频焦点变化。Android 开发者文档在 Handling changes in audio output 中说明,耳机拔出或蓝牙断开后,音频可能自动切回内置扬声器,媒体类 App 通常应暂停播放,防止高音量内容突然外放。这个事件本质上是 route change 后的用户体验保护点。
Apple 的典型可见事件包括 AVAudioSession.routeChangeNotification、interruption notification、media services reset 和 session active state 变化。当前公开文档页面需要 JavaScript,但 API 语义保持在 AVAudioSession 体系内;归档的 interruption 文档也说明了 session 被系统停用后,App 应保存状态、更新 UI、在合适时重新激活 session。路由变化处理和 interruption 处理共享同一个工程原则:先承认系统已经改变音频环境,再按新环境重建 App 状态。
贯穿例子中,蓝牙耳机断开后,App 的稳定处理顺序应是:停止依赖旧设备句柄,查询当前 route 或当前设备集合,判断是否会外放,按产品语义暂停或继续,重新配置采样率、声道和 buffer,更新 UI 上的输出设备图标。如果正在录音,还要重新确认输入设备、录音权限和 privacy indicator;如果正在通话,还要重新确认通信设备和回声消除路径。
Route change 之后最容易出现的工程错误是缓存旧状态。App 在启动时读取一次“当前输出是蓝牙”,随后一直假设蓝牙存在;当设备断开时,底层流已经切走,UI 仍显示蓝牙,音频图仍按旧采样率运行,用户听到外放或失声。这类问题的根因在 App 状态机:它把 route 当成静态配置,没有把 route change 当成输入事件。
可以把 App 的响应拆成五个动作。第一步,读取通知原因和当前 route,放弃旧 route 结论。第二步,判断用户可见风险,例如是否突然外放、是否中断录音、是否切入通话音质。第三步,更新音频图,包括 input node、output node、mixer、effect、encoder 或 decoder 的设备相关参数。第四步,同步 UI 和媒体状态,例如设备名称、暂停按钮、通话免提按钮、录音电平。第五步,按平台规则重新激活 session 或重新请求焦点。
这个顺序给排查提供了可复用证据链。用户报告“插上 USB 声卡后没声音”时,先看系统是否识别设备,再看 route change 是否到达 App,再看 App 是否重新配置音频格式,再看输出是否被焦点或 session 状态拦截,再看 HAL 或外设能力。用户报告“蓝牙切换后 UI 错乱”时,先看 App 是否把 route notification 合并进状态机,再看 UI 是否来自当前 route 快照。
113.4 Voice Call、VoIP、Media、Alarm、Notification 的策略冲突
多种声音同时出现时,系统必须决定谁继续、谁暂停、谁降低音量、谁改路由。Voice call、VoIP、media、alarm、notification、navigation prompt 和 system sound 的冲突,通常通过音频焦点、audio session、usage、category、ringer mode、Do Not Disturb、电话状态和系统角色共同解决。路由策略和焦点策略在这里交叉:焦点决定谁有权发声,路由决定声音从哪里发出。
Voice call 是最高约束场景之一。蜂窝电话通常由系统电话服务、基带状态、电话音频路径、听筒或免提输出、蓝牙 HFP 路径共同管理。第三方 App 无权把普通媒体流伪装成系统电话路径。Android 文档中 MODE_IN_CALL 只允许主电话应用在相应权限下选择;普通通信 App 使用 MODE_IN_COMMUNICATION 和 setCommunicationDevice 表达 VoIP 意图。Apple 侧通常通过 AVAudioSessionCategoryPlayAndRecord 加 voice chat 或 video chat mode 表达 VoIP,用系统 session 策略参与路由。
VoIP 和 media 的冲突体现为“媒体连续性”和“语音可懂度”的取舍。音乐播放时收到 VoIP 来电,系统会让 VoIP session 获取更高的音频优先级,媒体 App 收到焦点损失或 interruption。VoIP 通话期间,蓝牙设备可能从 A2DP 媒体 profile 切换到 HFP 语音 profile,声音质量下降、延迟模型变化、麦克风路径启用。用户看到的是音乐暂停、通话接通、蓝牙音质变化;系统内部发生的是用途、profile、输入输出设备和处理链的整体切换。
Alarm 和 notification 的冲突重点是用户可达性。闹钟通常需要在锁屏、后台和媒体播放之上发声,还要接受系统音量、勿扰模式、闹钟类别和厂商策略约束。普通 notification 更常受到勿扰模式、通知渠道、系统提示音策略和当前焦点约束。导航语音常用 ducking 方式,让媒体降低音量继续播放;语音备忘录或语音识别这类录音场景可能请求短暂独占,限制其他提示音干扰采集。
贯穿例子中,音乐走蓝牙播放,VoIP 来电后切入通信路径,闹钟随后触发。一个合理的系统结果可能是:音乐暂停,VoIP 获得通信设备和麦克风,闹钟作为高优先级提示打断或覆盖当前音频,用户在系统 UI 看到通话和闹钟状态。另一个结果可能是:闹钟只在本机扬声器发声,蓝牙仍保持通话。这种差异由平台版本、厂商策略、用户设置和设备能力决定;排查时应先收集具体 route、focus、session、ringer 和 DND 状态。
这类冲突可以用同一组维度比较。
| 场景 | 主要系统目标 | 常见策略动作 | App 应同步的状态 |
|---|---|---|---|
| Voice call | 私密、低延迟、可懂度 | 听筒或 HFP、媒体暂停、电话角色优先 | 通话中、免提状态、蓝牙 profile |
| VoIP | 双向语音、回声控制 | communication device、语音 mode、焦点抢占 | 麦克风状态、输出设备、重连状态 |
| Media | 连续播放、音质、用户选择 | A2DP、speaker、USB、投送、duck 或 pause | 播放状态、输出图标、音量面板 |
| Alarm | 用户可听见 | 高优先级提示、锁屏可达、音量策略 | 闹钟展示、暂停媒体、恢复条件 |
| Notification | 提示且受设置约束 | 通知渠道、DND、短声音 | 提示状态、静音设置、焦点变化 |
表格中的“主要系统目标”能帮助定位责任边界。通话声音走听筒,多数情况下是系统根据私密语音目标做出的路由;音乐拔耳机后暂停,是 App 对系统 route change 的体验响应;通知静音,常见入口是通知渠道、勿扰模式或系统提示策略。把现象放进目标和策略动作中,归因会比直接猜 API 更稳定。
113.5 Android Audio Policy 与设备路由决策
Android 的音频路由可以从 App 层向下追踪:App 使用 MediaPlayer、Media3、AudioTrack、AudioRecord 或通话框架表达音频用途;framework 把用途、格式、session、device preference 传给系统服务;Audio Policy 计算策略和设备;AudioFlinger 执行混音、线程和输出流管理;Audio HAL 把 stream 连接到底层 PCM、DSP、codec 或蓝牙、USB、投送路径。这个链路中,Audio Policy 负责“该去哪里”,AudioFlinger 更偏向“如何混合和交付数据”。
Android 的公开 AOSP 文档说明,Android 7.0 引入 XML audio policy configuration 来描述音频拓扑;Android 10 对 audio policy manager 做了重构,支持 OEM-specific routing strategies、customizable volume groups、由 policy engine 声明的 routing strategies 和更丰富的设备管理。配置文件可以描述 output/input stream profile、device ports、routes、volume curves 和标准 A2DP、USB include。这个配置面让厂商把硬件拓扑和策略规则放入系统镜像,而 App 只看到 framework API。
用贯穿例子看 Android 路径。音乐 App 的 AudioAttributes.USAGE_MEDIA 进入系统后,会映射到产品策略和音量组。蓝牙耳机存在时,policy 可选择蓝牙媒体输出;USB 声卡接入后,policy 会看到新的 device port 和 route;VoIP 来电进入 MODE_IN_COMMUNICATION 后,communication device 选择和语音处理链参与决策;闹钟触发时,alarm usage 进入自己的音量组和焦点规则。App 看到的是设备变化、焦点变化和播放状态变化,底层实际经历了 policy 重新计算。
AudioAttributes 是 Android App 表达音频语义的关键入口。usage 告诉系统“这段声音用于什么”,content type 告诉系统“它是什么内容”,flags 提供少量特殊约束。系统据此选择 volume stream、focus 行为、路由策略和后台限制。媒体声音用 media usage,导航提示用 assistance navigation guidance 或类似语义,闹钟用 alarm usage,通信用 voice communication usage。错误的 usage 会把声音放进错误策略域,例如把闹钟当媒体会影响音量、焦点和用户预期。
AudioManager 的设备 API 是 App 可见的观测面。getDevices 能看到输入输出设备集合;registerAudioDeviceCallback 从 API level 23 开始提供设备连接变化通知;getAvailableCommunicationDevices、setCommunicationDevice 和 getCommunicationDevice 从 API level 31 开始服务通信场景。通信设备选择的生命周期由系统控制,请求进程结束、设备断开或清除请求后失效;多个 App 同时请求时,当前控制音频 mode 的应用有更高优先级。这些规则说明,Android 允许 App 表达偏好,但偏好必须进入系统仲裁。
Android Audio Policy 还承担 vendor boundary。不同手机的扬声器数量、听筒路径、USB 支持、蓝牙 codec、车载集成、空间音频、DSP offload 和通话降噪能力不同。OEM 通过 audio policy configuration、HAL、vendor service 和音频效果链暴露这些能力。应用层不应把 Pixel、Samsung、车机或平板的路由行为看成完全一致;可迁移做法是基于公开 API 查询能力,基于 route change 更新状态,基于 usage 表达语义。
排查 Android 路由问题时,建议按责任链定位。第一步看 App 是否设置了正确的 AudioAttributes、audio focus 和通信 mode。第二步看系统可见设备集合和当前通信设备。第三步看是否收到 device callback、becoming noisy 或焦点事件。第四步看系统策略是否因为电话、蓝牙 profile、DND、固定音量设备或权限拒绝改变行为。第五步再进入厂商实现、HAL、蓝牙栈或 USB 外设能力。这样可以把 App 代码问题、framework 策略问题和硬件能力问题分开。
113.6 Apple Audio Session Category / Mode 与路由约束
Apple 平台把 App 的音频意图集中到 audio session。AVAudioSession 是 App 与系统音频策略之间的公开中介;category 定义基本行为,mode 定义更具体的处理场景,option 调整混音、扬声器、蓝牙、AirPlay 等路由能力,active state 表示 App 何时真正参与系统音频仲裁。App 配置 session 后,系统根据当前设备、其他 session、电话状态、静音开关、锁屏、后台模式和硬件能力决定 route。
Category 的作用是定义能力边界。ambient 适合可与其他音频混合的短声音,soloAmbient 是默认独占倾向,playback 适合媒体播放且可配合后台 audio 能力,record 适合输入采集,playAndRecord 适合 VoIP、录音监听和双向语音,multiRoute 适合少数多路输出输入场景。公开归档文档列出这些 category 对静音开关、锁屏、混音、输入输出能力的影响;这说明 route 选择从一开始就受 category 限定。
Mode 的作用是把同一 category 放入更具体的处理链。voiceChat 让系统按语音通信优化输入输出,通常关联回声消除、自动增益、降噪和通话型路由。videoChat 面向视频通话,gameChat 面向游戏语音,measurement 减少系统信号处理以服务测量场景,moviePlayback 面向影片播放。Mode 改变的是系统对音频内容和设备路径的解释,进而影响 buffer、处理链、可用 route 和 interruption 行为。
Option 的作用是扩大或修正可选路由集合。defaultToSpeaker 会让 playAndRecord 这类双向 session 更倾向扬声器输出,适合免提或视频通话。allowBluetooth 允许使用蓝牙免提类语音设备,常用于 HFP 路径。allowBluetoothA2DP 允许高质量蓝牙媒体输出,适合播放场景。allowAirPlay 让 session 可参与 AirPlay 路由。mixWithOthers、duckOthers、interruptSpokenAudioAndMixWithOthers 影响其他 session 的混音和压低音量行为。
贯穿例子中,音乐 App 使用 playback category,蓝牙耳机作为媒体 route;VoIP App 激活 playAndRecord 加 voice chat mode 后,系统可能把蓝牙从媒体 profile 切到语音 profile,或把输出放到听筒、扬声器。用户选择 AirPlay 后,playback 可以进入远端输出,但双向 VoIP 通常受到更多约束,因为 AirPlay 延迟和输入路径并不适合实时通话。这个判断不依赖 App 喜好,而依赖 category、mode、option 和设备能力共同形成的 route 集合。
Apple 平台的 route change 也要求 App 重建状态。AVAudioEngine 的 input node、output node、mixer node 和 effect node 可能因为硬件采样率、声道数、buffer duration 或输入输出设备变化而需要重新连接或重新启动。录音 App 尤其需要在 route change 后读取新的 input route 和 format;VoIP App 需要同步免提按钮、蓝牙按钮和 session active state;媒体 App 需要判断是否继续播放或暂停。
Apple 私有实现边界需要明确。公开文档能支持的结论是:App 通过 AVAudioSession 表达音频意图,系统通过 category、mode、option、route notification 和 interruption 管理路由与冲突。Core Audio、daemon、driver 和硬件的具体内部策略有公开组件和私有实现混合,工程分析应把“公开 API 行为”和“合理平台推断”分开。写代码和排查时,以公开 API 状态、通知、Instruments 观察和用户可见行为为证据。
113.7 Audio Routing 对权限、功耗、延迟和用户预期的综合影响
Audio routing 的最终影响集中在四个方面:权限、功耗、延迟和用户预期。权限决定 App 能否进入录音或通信路径;功耗决定系统是否允许长时间维持蓝牙、USB、DSP、投送或后台播放;延迟决定设备是否适合游戏、乐器、通话和监听;用户预期决定系统在路由变化时要保护哪种体验。
权限影响最明显的是输入路径。麦克风 route 需要运行时权限、前台状态或平台认可的后台能力,还要触发隐私指示器。Android 侧还会根据录音用途、前台 UI、foreground service、隐私敏感 use case 和特殊角色处理 capture path。Apple 侧通过 TCC、audio session、privacy indicator 和后台能力共同约束录音。用户看到“录不到音”时,可能是权限拒绝,也可能是 route 被通话占用、输入设备断开或系统给了 silent samples。
功耗影响路由持续性。蓝牙 A2DP、HFP、LE Audio、USB 外设供电、AirPlay、Cast、DSP offload、扬声器大音量播放都消耗电量。系统会在后台状态、低电量模式、温控、连接质量和音频用途之间做取舍。媒体播放可以通过后台播放能力长期运行;普通短声音没有同等级长期资源;持续录音或实时监听受到更严格的前台、隐私和电源约束。
延迟影响设备选择的适用性。内置扬声器和有线耳机通常路径更短;蓝牙媒体 profile 需要编码、无线传输和接收端 buffer;蓝牙通话 profile 支持双向语音但音质和处理链不同;USB audio 的延迟取决于外设、驱动、buffer 和采样率;AirPlay 和 Cast 更适合媒体播放,实时互动场景通常要承担更高延迟。系统在通话、游戏、乐器和录音监听场景中会倾向满足低延迟或双向语音目标。
用户预期是系统策略最外层的约束。拔掉耳机后,用户通常预期音乐暂停,减少突然外放。接电话时,用户预期媒体暂停,通话能听清。闹钟响起时,用户预期能被提醒。连接车载蓝牙时,用户预期导航和电话优先于背景音乐。App 设计如果绕开这些预期,即使技术上能发声,也会和系统治理目标冲突。
定位音频路由问题时,可以使用一条稳定判断顺序:先确认 App 的音频用途和 session/category,再确认系统看到的可用设备,再确认用户选择和系统 UI 状态,再确认焦点、interruption、通话、DND 和权限,再确认硬件能力、profile、采样率和 buffer,最后观察 App 是否收到 route change 并同步状态。这条顺序把“声音从哪里出”拆成可检查证据,适用于 Android、Apple 和跨平台音频框架。
回到贯穿例子,音乐、USB 声卡、蓝牙、VoIP 和闹钟同时出现时,系统执行的是多目标仲裁。媒体要连续,通话要低延迟和私密,闹钟要可达,麦克风要受权限保护,蓝牙和 USB 要满足硬件能力,App 要在每次变化后同步状态。理解这一点后,音频路由问题就能从“某个 API 没生效”转化为“哪一层策略输入改变了最终 route”。
最小自检任务
用户报告:音乐 App 正在通过蓝牙耳机播放,随后用户插入 USB-C 声卡并在系统输出面板中选择 USB;一分钟后 VoIP 来电,接通后对方听不到声音;通话过程中闹钟响起,用户听到闹钟从手机扬声器发出,音乐没有恢复。请按 Android 和 Apple 共同责任链写出分析路径,说明每一步应检查的对象、可能的策略边界、App 应同步的状态,以及用户可见结果如何归因。
答案要点
应先确认音乐 App 的音频用途是 media,系统最初把输出 route 放到蓝牙媒体路径。USB-C 声卡插入后,设备集合发生变化,系统输出面板中的用户选择会成为 route selection 的输入;App 应收到设备变化或 route change,并更新 UI 和音频格式。若 USB 声卡支持输出但没有输入,媒体播放可以走 USB,VoIP 通话的麦克风路径仍需另选内置麦克风、蓝牙麦克风或其他可用输入。
VoIP 来电接通后,应检查通信 session 或 communication mode 是否激活。Android 侧看 MODE_IN_COMMUNICATION、setCommunicationDevice 请求、可用通信设备列表、麦克风权限和 audio focus;Apple 侧看 AVAudioSession 是否使用 playAndRecord 及 voice chat 或 video chat mode,是否允许蓝牙或扬声器选项,session 是否 active。对方听不到声音的原因可落在麦克风权限、输入 route、USB 设备无输入、蓝牙 profile 切换失败、App 未重建音频图或系统通话策略抢占。
闹钟响起时,应把它视为高优先级系统提示音。闹钟从扬声器发出说明系统为了用户可达性选择了本机输出或对当前通话路径做了覆盖。音乐没有恢复,需要检查媒体 App 是否收到了焦点恢复或 interruption end,是否根据用户意图自动恢复,是否仍处于暂停状态,是否因为通话 session 仍 active 而继续失去焦点。最终归因应覆盖 App 意图、framework 表达、系统服务策略、设备能力、权限状态和用户可见结果。
本章知识点总结
- 路由本质:Audio routing 是系统把音频用途连接到具体输入输出设备的策略计算结果。
- 意图表达:App 通过 usage、category、mode、option 和 device preference 表达音频意图。
- 系统仲裁:系统根据设备状态、用户选择、权限、通话状态、焦点、功耗和延迟决定最终 route。
- 设备边界:Speaker、receiver、headset、Bluetooth、USB 和投送路径的能力、延迟和隐私边界不同。
- 蓝牙差异:蓝牙媒体 profile 和语音 profile 对音质、输入输出能力和延迟的约束不同。
- 变化通知:Route change notification 是 App 同步音频图、UI 和播放录制状态的关键输入。
- 状态重建:路由变化后应重新读取当前 route、设备能力、格式、焦点和 session 状态。
- 冲突治理:通话、VoIP、媒体、闹钟和通知通过焦点、session、usage、音量组和系统角色共同治理。
- Android 策略:Android Audio Policy 连接
AudioAttributes、设备端口、product strategy、volume group 和 output selection。 - Android 适配:Android 通信设备选择从 API level 31 起通过
setCommunicationDevice表达,并由系统仲裁生命周期。 - Apple Session:Apple 通过
AVAudioSessioncategory、mode、option 和 active state 约束可用路由集合。 - 公开边界:Apple 音频内部策略应区分公开 API 行为、公开组件和合理平台推断。
- 权限影响:麦克风 route 需要权限、隐私指示器、前台或后台能力,以及输入设备可用性共同成立。
- 功耗影响:蓝牙、USB、投送、DSP 和后台播放都会受到电量、温控和连接状态约束。
- 延迟影响:低延迟场景应优先检查 profile、buffer、采样率、处理链和硬件路径。
- 排查顺序:先看音频用途和 session,再看设备集合、用户选择、焦点权限、硬件能力和 App 状态同步。