6、音频系统:AAOS音频架构、CarAudioService、音频焦点管理、路由策略配置
音频系统在车机里有多重要?这么说吧,用户对车机最直观的感受,除了导航准不准,就是声音好不好。导航播报、电话通话、媒体音乐、语音助手……这些声音怎么协调、怎么切换、怎么分配到不同的扬声器,全得靠AAOS的音频架构来搞定。
我个人习惯把车机音频系统比作一个“交通指挥中心”。每个音频App都是一辆车,音频焦点是红绿灯,CarAudioService就是那个站在路中央的交警。今天我们就来拆解这个指挥中心是怎么运作的。
6.1 AAOS音频架构概览
先看整体架构。AAOS的音频系统分为三层:应用层、框架层、HAL层。
这张图我画得比较简洁,但核心脉络都在里面了。应用层只管“我要播放”,框架层负责“谁先播、播多大、走哪个通道”,HAL层负责“怎么让喇叭响”。
我在项目中遇到过一个问题:某个第三方音乐App播放时,导航语音突然不出来了。查了半天,发现是App没有正确请求音频焦点,导致导航的音频流被媒体流“淹没了”。这就是典型的框架层协调出了问题。
6.2 CarAudioService——车机音频的“大管家”
CarAudioService是AAOS在Android原生音频框架上做的扩展。它主要干三件事:
- 音量分组管理:把不同的音频流(媒体、导航、电话、语音助手)分成独立的音量组,每组可以独立调节音量。
- 音频路由控制:决定某个音频流走哪个物理设备(主扬声器、头枕扬声器、蓝牙耳机、AUX等)。
- 焦点策略增强:在Android原生焦点机制上,增加了车机特有的“多音频流共存”策略。
核心概念:音量组(Volume Group)
在AAOS中,每个音频用途(如导航、电话、媒体)都对应一个音量组。每个音量组有自己的音量曲线、最大最小值、以及对应的物理设备。
配置音量组是在car_audio_configuration.xml里完成的。我贴一段典型的配置:
<!-- car_audio_configuration.xml -->
<carAudioConfiguration>
<volumeGroups>
<group>
<name>media</name>
<devices>
<device address="bus0_media_out"></device>
</devices>
</group>
<group>
<name>navigation</name>
<devices>
<device address="bus1_nav_out"></device>
</devices>
</group>
<group>
<name>voice_call</name>
<devices>
<device address="bus2_call_out"></device>
</devices>
</group>
</volumeGroups>
</carAudioConfiguration>
你想想看,如果没有这种分组机制,导航和媒体混在一起,用户调音量时就会“一调全调”,体验极差。分组之后,用户调导航音量,媒体音量纹丝不动——这才是车机该有的样子。
6.3 音频焦点管理——谁说了算?
音频焦点是Android原生就有的机制,但AAOS对它做了“车规级”改造。原生Android的焦点机制很简单:一个App获得焦点,其他App就暂停。但在车上,导航和媒体经常需要同时出声——导航播报时,媒体音量应该降低而不是暂停。
AAOS引入了“音频焦点等级”的概念:
| 焦点类型 | 行为描述 | 典型场景 |
|---|---|---|
| FOCUS_GAIN | 完全获得焦点,其他音频暂停 | 电话通话 |
| FOCUS_GAIN_TRANSIENT | 短暂获得焦点,其他音频暂停 | 语音助手唤醒 |
| FOCUS_GAIN_TRANSIENT_MAY_DUCK | 短暂获得焦点,其他音频降低音量(闪避) | 导航播报 |
| FOCUS_GAIN_TRANSIENT_EXCLUSIVE | 独占焦点,其他音频完全停止 | 紧急报警音 |
我的经验:导航App一定要用FOCUS_GAIN_TRANSIENT_MAY_DUCK。我曾经见过一个导航App用了FOCUS_GAIN,结果每次播报都把音乐暂停了,用户投诉说“听歌体验像在打嗝”。
代码层面,请求焦点的写法是这样的:
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK)
.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build())
.setOnAudioFocusChangeListener(focusChange -> {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
// 失去焦点,停止播放
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
// 暂时失去焦点,可以暂停
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// 重新获得焦点,恢复播放
}
})
.build();
int result = audioManager.requestAudioFocus(focusRequest);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// 焦点请求成功,开始播放
}
避坑指南:我曾经在某个项目里,导航App请求焦点时忘了设置setUsage为USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,结果系统把它当成了媒体流,闪避策略根本没生效。嗯,这个坑我踩过,你们别踩了。
6.4 路由策略配置——声音往哪走?
路由策略解决的是“哪个音频流走哪个物理设备”的问题。在车上,场景比手机复杂得多:
- 媒体音乐走全车扬声器
- 导航语音只走驾驶位头枕扬声器
- 电话通话走蓝牙耳机或前排扬声器
- 语音助手走前排麦克风阵列
这些路由规则是在car_audio_routing.xml里配置的。我给你们看一个实际项目的配置片段:
<!-- car_audio_routing.xml -->
<routingConfiguration>
<deviceRoutes>
<!-- 媒体路由到全车扬声器 -->
<route>
<source>media</source>
<sink>speaker_all</sink>
</route>
<!-- 导航路由到驾驶位头枕 -->
<route>
<source>navigation</source>
<sink>speaker_driver_headrest</sink>
</route>
<!-- 电话路由到前排扬声器 -->
<route>
<source>voice_call</source>
<sink>speaker_front</sink>
</route>
</deviceRoutes>
</routingConfiguration>
路由策略的核心逻辑在CarAudioService里实现。它会根据当前的音频焦点状态、用户设置、以及硬件连接情况,动态调整路由。比如:
- 当用户插入蓝牙耳机时,电话音频自动切换到蓝牙
- 当导航播报时,媒体音量自动降低(闪避)
- 当语音助手激活时,所有其他音频暂停
动态路由的典型流程:
- App请求音频焦点
- CarAudioService评估焦点请求,决定是否授予
- 根据焦点类型,触发闪避或暂停策略
- 根据音频用途,查找对应的路由规则
- 通过AudioPolicyService下发路由指令到HAL层
- HAL层切换音频通道,声音从正确的设备输出
说白了,路由策略就是一套“if-else”规则,只不过这套规则是在系统层面、实时执行的。你想想看,如果没有这套机制,导航声音从后排喇叭传过来,驾驶员听着得多别扭?
6.5 调试与验证
音频系统调试起来比较麻烦,因为问题往往不是“能不能响”,而是“响得对不对”。我常用的调试手段有:
- dumpsys audio:查看当前音频焦点持有者、音量状态、路由信息
- dumpsys car_service:查看CarAudioService的内部状态,包括音量组配置
- logcat -s CarAudioService:过滤CarAudioService的日志,看焦点请求和路由切换
举个例子,当导航播报时媒体没有闪避,我会先跑dumpsys audio看焦点持有者是谁,再跑dumpsys car_service看路由配置是否正确。大部分问题都出在配置上——要么是音量组没配对,要么是焦点类型用错了。
我的调试习惯:先在模拟器上用media和navigation两个App做焦点测试,确认闪避行为正常后,再上真车验证路由。这样能快速定位问题是出在框架层还是HAL层。
音频系统是车机体验的“面子工程”。声音出问题,用户第一时间就能感知到。把CarAudioService、焦点管理、路由策略这三块吃透,你就能搞定AAOS音频开发中90%的问题。剩下的10%,嗯,那得靠你在项目里慢慢踩坑积累了。
公众号:蓝海资料掘金营,微信deep3321