Android 14 中的全新车载 OEM 插件服务支持配置某些汽车组件。具体而言,针对音频,我们推出了三项全新插件服务,让 OEM 能够灵活地在 AAOS 设备上配置音频管理
- 音频焦点控制
- 音量和静音控制
- 音频闪避控制
车载插件服务架构
下图概述了车载服务及其与 OEM 车载服务的关系。与应用进程和车载服务进程类似,OEM 车载服务进程也占用自己的进程空间。
车载服务通过查找 config_oemCarService
中定义的组件来启动 OEM 车载服务。如果配置为空,则 OEM 服务不存在,并且不会启动任何服务。该组件必须扩展 OemCarService。车载音频服务必须覆盖用于获取车载音频 OEM 服务的 API
public final class OemCarServiceImp extends OemCarService {
@Override
public OemCarAudioFocusService getOemAudioFocusService();
@Override
public OemCarAudioDuckingService getOemAudioDuckingService();
@Override
public OemCarAudioVolumeService getOemAudioVolumeService();
}
如需查看示例,请参阅 packages/services/Car/tests/OemCarServiceTestApp
中定义的参考测试应用。
即使服务由车载服务启动,它也不会自动继承车载音频服务可用的权限。因此,OEM 服务所需的任何权限都应通过适当的机制获取。例如,请参阅 packages/services/Car/data/etc/com.android.car.oemcarservice.testapp.xml
。
采用 OEM 服务架构的车载音频服务
在 AAOS 中,车载音频服务管理以下操作
- 音频路由
- 音频焦点
- 音频闪避
- 音量和静音
在 Android 14 之前,此行为在很大程度上是静态的,只能通过设置进行修改,尽管仅适用于极少数情况。Android 14 引入了一种机制,供车载音频服务与 OEM 定义的组件进行通信,以便管理
- 音频焦点
- 音频闪避
- 音量和静音
下图显示了车载音频服务和车载 OEM 服务的简化架构。车载音频服务定义了不同的挂钩,这些挂钩可以调用车载 OEM 音频服务来管理音频行为。只有在定义了相应的 OEM 车载音频服务组件时,才会发生后者。否则,车载音频服务将使用默认行为。
为了确保车载音频服务和车载 OEM 音频服务始终同步,对于每次调用,车载音频服务都会将音频堆栈的当前状态的必需部分传递给车载 OEM 音频服务。例如,当车载音频服务拦截评估音频焦点的请求时,它会将堆栈的当前状态传递给车载 OEM 音频服务。当前状态包括当前焦点持有者和当前焦点丢失者。焦点丢失者是仍然是堆栈一部分但暂时失去焦点的焦点请求。
车载音频服务必须管理车载设备中的所有音频活动。如果车载音频服务不管理音频行为的某些部分,则向车载 OEM 音频服务公开的信息将不完整。例如,如果 OEM 通过注册自己的音频焦点政策来覆盖车载服务中的音频焦点处理,则车载音频服务无法向车载 OEM 音频服务提供完整的信息。这可能会影响车载 OEM 音频服务做出决定的能力,因为它可能缺少车载音频服务不可见的信息。
为了执行操作,车载音频服务会调用 OEM 车载服务。这些调用跨进程进行,这需要进程间通信 (IPC)。IPC 会增加每次调用的延迟。务必最大限度地减少 OEM 服务中的延迟。
由于车载音频服务对 OEM 服务的调用是阻塞的,因此 OEM 服务不应直接在 API 评估中调用车载音频服务。相反,车载音频服务会提供必要的信息,以便两个进程之间的调用只需单向传输。
OEM 车载音频服务定义
OEM 车载音频焦点服务
车载音频服务通过注册音频政策焦点监听器来管理来自应用的音频焦点请求。车载音频服务具有一种机制,可以根据静态的互动矩阵管理焦点行为。该矩阵定义了三种不同的互动类型
并发互动。焦点持有者可以同时保持焦点。
独占互动。传入的焦点请求从当前焦点持有者处获取焦点。
拒绝互动。传入的焦点请求因当前焦点持有者而被拒绝。
虽然这对于某些汽车用例来说已足够,但它并不能满足因 OEM 要求而可能不同的所有互动需求。为此,我们引入了 OemCarAudioFocusService
public interface OEmCarAudioFocusService {
OemCarAuddioFocusResults evaluateAudioFocusRequest(
OemCarAudioFocusEvaluationRequest request);
void notifyAudioFocusChange(
List<AudioFocusEntry> holder,
List<AudioFocusEntry> losers, int zoneId);
}
API evaluateAudioFocusRequest
从车载音频服务调用,每当需要评估音频焦点请求时都会调用它,这是一个双向 API,它会阻塞以等待结果返回。该请求包含有关音频堆栈当前状态的信息
此信息可用于将 newFocusRequest
与 focusHolders
中的当前焦点持有者和 focusLosers
中的当前焦点丢失者进行比较。API 应返回结果
class OemCarAudioFocusResult {
int audioZoneId;
int audioFocusEvaluationResults;
AudioFocusEntry focusResult;
List<AudioFocusEntry> newLosers;
List<AudioFocusEntry> newlyBlocked;
}
其中包含有关 audioFocusEvaluationResults
中的实际评估结果的信息,这表明当前请求已被授予、延迟还是失败。newLosers
和 newlyBlocked
条目中应设置对当前焦点堆栈的任何更改,具体取决于堆栈更改的性质。
其中,newLosers
包含先前持有焦点但现在应失去焦点的条目,无论是永久性还是暂时性。永久性焦点丢失者将从音频焦点堆栈中进一步移除,暂时性焦点丢失者将移至当前焦点丢失者堆栈,直到他们重新获得焦点或被原始焦点请求者放弃。无论如何,请求的焦点监听器都将收到相应的焦点丢失。
newlyBlocked
列表包含先前在焦点丢失者列表中的条目,但现在被新条目阻止。阻止可以是永久性的,也可以是暂时性的;对于永久性焦点阻止,该条目将从堆栈中移除,并且焦点丢失将发送到焦点监听器。对于暂时性焦点丢失,该条目将保留在焦点丢失者堆栈中,但一个新的焦点阻止程序将添加到其阻止程序列表中,不会发送焦点丢失,因为在首次阻止时已发送了一个。当所有当前阻止程序都被移除时,请求最终将被解除阻止,或者如果焦点被放弃,则请求将从堆栈中移除。
第二个 API notifyAudioFocusChange
是单向的,它在每次音频焦点请求或放弃时调用。该 API 主要用于告知 OEM 服务焦点更改,这可能会影响 OEM 车载音频服务的行为。
焦点评估指南
在 AAOS 中,音频焦点用于管理音频播放并确定哪个应用应遵守,以便为用户提供最佳体验。因此,OEM 插件服务在管理音频焦点请求时应考虑以下事项
在没有任何常设高优先级音频焦点(例如电话呼叫、紧急情况或安全)的情况下,应用应能够暂时或永久地获得音频焦点。
当媒体焦点处于活动状态时,请求
呼叫用途焦点的应用应能够并发或独占地接收焦点。
导航用途焦点的应用应能够并发或独占地接收焦点。
助理用途焦点的应用应能够并发或独占地接收焦点。
当常设高优先级音频焦点(例如电话呼叫、紧急警报或安全警报)应用处于活动状态时,任何传入的延迟音频焦点请求都应根据需要授予或延迟。
虽然以上建议并非详尽无遗,但它们可以帮助保证请求焦点的应用在没有活动高优先级声音时应能够获得焦点。即使在高优先级声音处于活动状态时,也应尊重延迟的焦点请求,并且在高优先级声音停止后应能够获得焦点。
OEM 车载音量服务
车载音频服务通过监听来自音频系统的音量调节或直接监听来自车载输入服务的音量按键事件来管理音量按键事件。在每种情况下,车载音频服务的默认行为是根据活动的音频播放器和音频上下文优先级列表确定要更改的音量组。
我们提供两个音量优先级列表。第一个列表按此顺序考虑所有音频上下文。该列表按降序排列,最高优先级位于顶部,最低优先级位于底部。例如,如果导航音频和音乐音频同时处于活动状态,则在发生音量按键事件时,导航音量会发生变化。
- 导航
- 通话
- 音乐
- 公告
- 语音指令
- 来电铃声
- 系统声音
- 安全
- 闹钟
- 通知
- 车辆状态
- 紧急情况
为了使音量按键事件管理不那么复杂,车载音频服务具有第二个音频上下文优先级列表
- 通话
- 媒体
- 公告
- 语音指令
此列表也按降序排列。此第二个列表的目的是允许通过按键事件更改更常见的声音。不常见的声音(或许是持续时间较短的声音)只能通过音频设置界面进行管理。
音量的实际版本可以使用 audioVolumeAdjustmentContextsVersion
配置进行设置。该配置可以设置为 1
或 2
(2
是默认值)。
为了提供更大的音量管理灵活性,Android 14 中引入了 OemCarAudioVolumeService
public interface OemCarAudioVolumeService {
OemCarvolumeChangeInfo getSuggestedGroupForVolumeChange(
OemCarAudioVolumeRequest request, int volumeAdjustment);
}
OEM 车载音频音量服务具有单个方法,该方法接受 volumeAdjustment
和 OemCarAudioVolumeRequest
class OemCarAudioVolumeRequest {
int audioZoneId;
int callState;
List<AudioAttributes> activePlaybackAttributes;
List<AudioAttributes> duckedAttributes;
List<CarVolumeGroupInfo> volumeGroupState;
}
请求的 activePlaybackAttributes
具有活动的音频属性。duckedAttributes
是当前所有被闪避的音频属性。volumeGroupState
具有音量组的当前状态。该请求表示音频堆栈的当前状态,可用于确定应更改哪个音量组。结果应在 OemCarVolumeChangeInfo
中返回
class OemCarVolumeChangeInfo {
boolean change;
CarVolumeGroupInfo volumeGroupChanged;
}
change
布尔值指示是否更改了任何音量,true
指示存在更改,应更新音量组。volumeGroupChanged
是应更改的实际音量组。应根据传递给 API 的原始 volumeAdjustment
参数更改此组。例如,如果结果指示应将导航音量组静音,则布尔值将为 true
,并且返回的音量组应为导航音量组。
OEM 车载闪避服务
车载音频服务通过监控音频焦点更改并向 AudioControl
HAL 发送有关要闪避的音频设备的信号来管理音频闪避。当焦点更改时,将评估所有活动的焦点持有者,以确定应根据这组静态闪避规则闪避哪些持有者
- 紧急声音会闪避除通话声音之外的所有声音
- 安全声音会闪避除紧急声音之外的所有声音
- 导航声音会闪避除安全声音和紧急声音之外的所有声音
- 通话声音会闪避除安全声音、紧急声音和导航声音之外的所有声音
- 语音声音会闪避来电铃声
- 音乐和公告应被所有声音闪避
这些规则并非详尽无遗,OEM 仍有责任根据这些指南确定声音应如何闪避。OEM 可以根据可用要求更主动地控制这些建议。Android 14 中引入了 OemCarDuckingService
class OemCarAudioDuckingService {
List<AudioAttributes> evaluateAttributesToDuck(
OemCarAudioVolumeRequest request);
}
此 API 从车载音频服务在音频焦点更改时调用。它重复使用了 OEM 车载音量服务中引入的 OemCarAudioVolumeRequest
,并包含用于决定要闪避哪些属性的相关信息。要从 API 闪避的音频属性列表与当前音频状态进行比较
当前闪避的音频属性
- 在列表中,继续闪避
- 不在列表中,闪避已关闭
当前未闪避的音频属性
- 在列表中,已闪避
- 不在列表中,闪避已关闭
然后,车载音频服务确定音频属性所属的音频输出设备,并将它们分别添加到闪避音频输出设备列表或取消闪避音频设备列表。这最终会发送到 AudioControl HAL,以在硬件级别执行所需的闪避。
下图显示了使用 OEM 闪避服务时焦点请求的音频闪避控制的简化时序图
当应用通过公共音频管理器 API 请求管理音频焦点时,序列开始。该请求将转发到车载音频服务以确定结果。当确定音频焦点时,车载音频服务会调用 OemCarAudioDuckingService
来评估音频闪避,以评估应闪避哪些音频属性。从 evaluateAttributesToDuck
API 返回结果后,计算要闪避的音频设备,最后将信息发送到 AudioControl
以将闪避应用于音频硬件。
OEM 车载音频服务参考实现
AAOS 在 packages/services/Car/tests/OemCarServiceTestApp
中提供了 OEM 车载服务的参考实现,该实现实现了 OemCarService
以及 OemCarAudioFocusService
、OemCarAudioDuckingService
和 OemCarAudioVolumeService
。对于后者,每项服务都使用 XML 文件来加载静态行为。例如,OemCarAudioFocusServiceImp
加载 oem_focus_config.xml
,其中包含互动矩阵。当调用 evaluateAudioFocusRequest
时,该矩阵用于评估焦点请求。
参考测试应用调试
OEM 车载服务测试应用是 AOSP 源代码的一部分。OEM 可以根据自己的需求进行更改。为了进行调试,请使用 config_oemCarService
配置来启用测试应用。
<!-- This is the component name for the OEM customization service. OEM can choose to implement
this service to customize car service behavior for different policies. If OEMs choose to
implement it, they have to implement a service extending OemCarService exposed by car-lib,
and implement the required component services.
If the component name is invalid, CarService would not connect to any OEM service.
Component name can not be a third party package. It should be pre-installed -->
<string name="config_oemCarService" translatable="false">
com.android.car.oemcarservice.testapp/.OemCarServiceImpl
</string>
要验证 OEM 车载服务是否使用了车载服务 dump
命令来获取 OEM 服务
adb shell dumpsys car_service --oem-service
结果可能类似于以下输出
***CarOemProxyService dump***
mIsFeatureEnabled: true
mIsOemServiceBound: true
mIsOemServiceReady: true
mIsOemServiceConnected: true
mInitComplete: true
OEM_CAR_SERVICE_CONNECTED_TIMEOUT_MS: 5000
OEM_CAR_SERVICE_READY_TIMEOUT_MS: 5000
mComponentName: com.android.car.oemcarservice.testapp/.OemCarServiceImpl
每批 dump
信息中的每个布尔值都确定了功能和服务的状态。例如,dump 信息 mIsOemServiceReady
指定了服务是否已准备好使用,其中 true
表示已准备好,false
表示未准备好。