一、CarAudioZone介绍
在汽车领域,围绕多个用户同时与平台互动并且每个用户都希望使用单独媒体的需求,出现了一系列新的用例。例如,后座上的乘客在后座显示屏上观看 YouTube 视频时,司机可以在驾驶舱中播放音乐。多可用区音频允许不同的音频源通过车辆的不同可用区同时进行播放,从而实现此功能。
Android 10 中的多可用区音频使原始设备制造商 (OEM) 能够将音频配置为不同的可用区。每个可用区由车辆内的一组设备组成,并且有各自的音量组、用于上下文的路由配置以及焦点管理。这样,主驾驶舱可配置为一个音频可用区,而后座显示屏的耳机插孔可配置为第二个可用区。
可用区定义为 car_audio_configuration.xml 的一部分。然后,CarAudioService 会读取该配置,并帮助 AudioService 根据关联可用区路由音频流。每个可用区仍会根据上下文和应用 UID 定义路由规则。创建播放器时,CarAudioService 会确定播放器与哪个可用区相关联,然后根据使用情况,确定 AudioFlinger 应将音频路由到哪个设备。
系统为每个音频可用区单独维护焦点。这使得不同可用区中的应用可以单独生成音频,而不会彼此干扰,同时让应用保持关注其所在可用区内焦点的变化。CarAudioService 内的 CarZonesAudioFocus 负责管理每个可用区的焦点。
二、CarAudioZone相关类
CarAudioZone相关有如下几个类:
CarAudioZone
A class encapsulates an audio zone in car.
类封装汽车中的音频区域。
CarAudioZone代码位于:
packages/services/Car/service/src/com/android/car/audio/CarAudioZone.java
CarAudioZone的定义:
class CarAudioZone {}
CarAudioZonesHelper
A helper class loads all audio zones from the configuration XML file.
帮助程序类从配置 XML 文件加载所有音频区域。
CarAudioZonesHelper代码位于:
packages/services/Car/service/src/com/android/car/audio/CarAudioZonesHelper.java
CarAudioZonesHelper的定义:
class CarAudioZonesHelper {}
CarAudioZonesValidator
汽车音频区域验证器
CarAudioZonesValidator代码位于:
packages/services/Car/service/src/com/android/car/audio/CarAudioZonesValidator.java
CarAudioZonesValidator的定义:
final class CarAudioZonesValidator {}
三、CarAudioZone配置
配置多个可用区
在 Android 10 中,car_audio_configuration.xml 取代了 car_volumes_groups.xml 和 IAudioControl.getBusForContext。新的配置文件中定义了可用区列表。每个可用区都有一个或多个音量组及其关联设备,而每台设备都有其应在该可用区内进行路由的上下文。所有上下文都必须在各个可用区进行表示。
audio_policy 通常位于 vendor 分区中,表示主板的音频硬件配置。car_audio_configuration.xml 中引用的所有设备都将在 audio_policy_configuration.xml 中进行定义。
启用多可用区支持
如果存在多可用区音频,则必须将 audioUseDynamicRouting 标志设置为 true:
resources> <bool name="audioUseDynamicRouting">true</bool></resources>
如果设置为 false,CarAudioService 将回退为使用 car_volumes_groups.xml 和 IAudioControl.getBusForContext。虽然 HAL 层不需要进行任何更改,但 car_audio_configuration.xml 文件定义了设备与上下文的关系,因此在 Android 10 中已弃用 IAudioControl.getBusForContext。
主可用区
主可用区是系统默认向其路由所有音频的位置。只能有一个主可用区,该可用区在配置中通过属性 isPrimary="true" 进行指示。如果未指定主可用区,则默认情况下,系统会推荐使用列表中的第一个可用区。
示例配置
对于之前定义的示例用例,车辆需要两个可用区,即一个主可用区和一个后座娱乐系统可用区。对于该配置,可能的 car_audio_configuration.xml 的定义如下:
<audioZoneConfiguration version="1.0">
<zone name="primary zone" isPrimary="true">
<volumeGroups>
<group>
<device address="bus0_media_out">
<context context="music"/>
</device>
<device address="bus3_call_ring_out">
<context context="call_ring"/>
</device>
<device address="bus6_notification_out">
<context context="notification"/>
</device>
<device address="bus7_system_sound_out">
<context context="system_sound"/>
</device>
</group>
<group>
<device address="bus1_navigation_out">
<context context="navigation"/>
</device>
<device address="bus2_voice_command_out">
<context context="voice_command"/>
</device>
</group>
<group>
<device address="bus4_call_out">
<context context="call"/>
</device>
</group>
<group>
<device address="bus5_alarm_out">
<context context="alarm"/>
</device>
</group>
</volumeGroups>
<displays>
<display port="0"/>
</displays>
</zone>
<zone name="rear seat zone">
<volumeGroups>
<group>
<device address="bus100_rear_seat">
<context context="music"/>
<context context="navigation"/>
<context context="voice_command"/>
<context context="call_ring"/>
<context context="call"/>
<context context="alarm"/>
<context context="notification"/>
<context context="system_sound"/>
</device>
</group>
</volumeGroups>
<displays>
<display port="1"/>
</displays>
</zones>
</audioZoneConfiguration>
在这里,主可用区将上下文划分至各个设备。这样一来,HAL 便可使用车辆的硬件,在各个设备输出中应用不同的后处理效果和混音。设备已划分为四个音量组:媒体、导航、通话和闹钟。如果系统配置为 useFixedVolume,则每个组的音量级别都将传递到 HAL,以应用于这些设备的输出。
对于次要可用区,应通过单个耳机插孔进行输出。在此示例中,所有使用请求都将路由到单台设备和音量组,以简化操作。
对于每个可用区,都可以添加可选的显示屏列表,以及与每个显示屏关联的实体显示屏端口。这使某些应用能够查询与其想要定位的特定显示屏相关联的可用区(见下文)。
新的隐藏 API(仅限 OEM 和系统应用)
在 Android 10 中,向 CarAudioManager 引入了一系列隐藏 API,使应用可以查询并设置音频可用区和焦点。
int[] getAudioZoneIds();
int getZoneIdForUid(int uid);
boolean setZoneIdForUid(int zoneId, int uid);
boolean clearZoneIdForUid(int uid);
int getZoneIdForDisplay(Display display);
更改应用的可用区
默认情况下,所有音频将路由到主可用区。如需更新应用以路由到其他可用区,请使用 setZoneIdForUid:
int zoneIdForDisplayId =
mCarAudioManager.getZoneIdForDisplay(selectedDisplay);
Int uid = mContext.getPackageManager()
.getApplicationInfo(mContext.getPackageName(), 0)
.uid;
if (mCarAudioManager.setZoneIdForUid(zoneId, info.uid)) {
Log.d(TAG, "Zone successfully updated");
} else {
Log.d(TAG, "Failed to change zone");
}