音频焦点

在开始逻辑音频流之前,应用会请求音频焦点,使用的音频属性与逻辑音频流所用的属性相同。应用必须遵守焦点丢失的情况,才能在汽车用例中按预期运行。

虽然建议发送焦点请求,但系统不会强制执行。因此,请将焦点视为间接控制和避免播放期间冲突的一种手段,而不是主要的音频控制机制。车辆不应依赖焦点系统来操作音频子系统。

焦点互动

为了支持 AAOS,音频焦点请求根据请求的 CarAudioContext 与当前焦点持有者的 CarAudioContext 之间预定义的互动进行处理。有三种互动类型:

  • 独占
  • 拒绝
  • 并发

独占互动

这是 Android 最常用的互动模型。

独占互动中,一次只允许一个应用持有焦点。因此,传入的焦点请求会被授予焦点,而现有的焦点持有者会丢失焦点。由于这两个应用都播放媒体,因此只允许一个应用持有焦点。因此,新启动的应用的焦点请求会返回 AUDIOFOCUS_REQUEST_GRANTED,而当前正在播放音乐的应用会收到焦点更改事件,其中包含与发出的请求类型相对应的丢失状态。

拒绝互动

对于拒绝互动,传入的请求始终会被拒绝。例如,在通话进行中尝试播放音乐时。在这种情况下,如果“拨号器”应用为通话持有音频焦点,而第二个应用请求焦点来播放音乐,则音乐应用会收到响应请求的 AUDIOFOCUS_REQUEST_FAILED。由于焦点请求被拒绝,因此不会向当前焦点持有者调度焦点丢失事件。

并发互动

并发互动是 AAOS 特有的。它使在汽车中请求音频焦点的应用能够与其他应用并发持有焦点。要发生并发互动,必须满足以下条件。以下条件是:

如果满足这些条件,则焦点请求会返回 AUDIOFOCUS_REQUEST_GRANTED,而当前焦点持有者的焦点不会发生变化。但是,如果当前焦点持有者选择接收音频闪避事件或在音频闪避时暂停,则当前焦点持有者会丢失焦点,就像发生独占互动一样。

处理并发音频流

虽然并发互动有许多用途,但在硬件级别跨输出设备进行混音和音频闪避时要小心。我们强烈建议,允许并发播放的 CarAudioContext 应路由到不同的输出设备。

通过为并发音频流提供单独的输出设备,HAL 可以在混合音频流之前闪避其中一个音频流,或者将物理音频流路由到车辆中的不同扬声器。如果在 Android 中混合逻辑音频流,则增益不会改变,并且会作为同一物理音频流的一部分进行传输。

例如,当同时传输导航和媒体时,可以暂时降低(或闪避)媒体音频流的增益,以便更清楚地听到导航指令。或者,可以将导航音频流路由到驾驶员侧扬声器,而媒体继续在车舱的其他位置播放。

互动矩阵

下表显示了 CarAudioService 定义的互动矩阵。每一行代表当前焦点持有者的 CarAudioContext,每一列代表传入请求的 CarAudioContext

例如,当音乐媒体应用持有焦点,而导航应用请求焦点时,矩阵表明,假设满足 并发互动 的其他条件,则这两个互动可以并发播放。

由于存在并发互动,因此可能会有多个焦点持有者。在这种情况下,传入的焦点请求会与每个当前的焦点持有者进行比较,然后再确定要应用哪种互动。在这种情况下,最保守的互动获胜。拒绝,然后是独占,最后是并发。

图 1. 音频焦点互动矩阵。

在 Android 11 中,引入了一项新的用户设置,允许用户更改导航和通话之间的互动行为。设置后,android.car.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL 会将传入的 NAVIGATION 焦点请求与当前 CALL 焦点持有者之间的互动从并发更改为拒绝。如果用户希望导航指令不中断通话,则可以启用此设置。此设置会为用户保留,并且可以动态设置,以便后续的焦点请求遵循新设置。

可延迟的音频焦点

在 Android 11 中,AAOS 添加了对请求可延迟音频焦点的支持。这样一来,当非瞬时焦点请求与当前焦点持有者的互动通常会导致它们被拒绝时,就可以延迟这些请求。一旦焦点更改导致延迟的请求可以获得焦点的状态,就会授予该请求焦点。

延迟的音频焦点请求的规则

  • 仅限非瞬时请求。 延迟的请求只能针对非瞬时来源发出,以避免瞬时声音在与其相关的很久之后才播放。

  • 一次只能延迟一个请求。 如果在已存在延迟请求的情况下发出可延迟的请求,则原始延迟的请求会收到 AUDIOFOCUS_LOSS 更改事件,并且新请求会收到 AUDIOFOCUS_REQUEST_DELAYED 的同步响应。

  • 可延迟的请求必须具有 OnAudioFocusChangeListener 一旦请求被延迟,侦听器将用于在最终授予请求 (AUDIOFOCUS_GAIN) 或稍后拒绝请求 (AUDIOFOCUS_LOSS) 时通知请求者。

请求可延迟的焦点

要构建可以延迟的请求:

  1. 使用 AudioFocusRequest.Builder#setAcceptsDelayedFocusGain

    mMediaWithDelayedFocusListener = new MediaWithDelayedFocusListener();
    
    mDelayedFocusRequest = new AudioFocusRequest
         .Builder(AudioManager.AUDIOFOCUS_GAIN)
         .setAudioAttributes(mMusicAudioAttrib)
         .setOnAudioFocusChangeListener(mMediaWithDelayedFocusListener)
         .setForceDucking(false)
         .setWillPauseWhenDucked(false)
         .setAcceptsDelayedFocusGain(true)
         .build();
    
  2. 发出请求时,处理 AUDIOFOCUS_REQUEST_DELAYED 响应

    int delayedFocusRequestResults = mAudioManager.requestAudioFocus(mDelayedFocusRequest);
    if (delayedFocusRequestResults == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        // start audio playback
        return;
    }
    if (delayedFocusRequestResults == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
         // audio playback delayed to audio focus listener
         return;
    }
    
  3. 当请求被延迟时,焦点侦听器处理焦点变化

    private final class MediaWithDelayedFocusListener implements
    OnAudioFocusChangeListener {
           @Override
           public void onAudioFocusChange(int focusChange) {
               synchronized (mLock) {
                   switch (focusChange) {
                       case AudioManager.AUDIOFOCUS_GAIN:
                            // Start focus playback
                       case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                            // Pause media transiently
                       case AudioManager.AUDIOFOCUS_LOSS:
                            // Stop media
    

多区域焦点管理

对于具有多个音频区域的车辆,每个区域的音频焦点都是独立管理的。因此,对一个区域的请求不会考虑其他区域持有的焦点,也不会导致其他区域中的焦点持有者丢失焦点。这样,主车舱的焦点可以与后座娱乐系统的焦点分开管理,从而不会因对另一个区域的焦点所做的更改而中断一个区域中的音频播放。

对于所有应用,CarAudioService 都会自动管理焦点。焦点请求的音频区域由其关联的 UserIdUID 确定(有关详情,请参阅 多区域音频路由)。

同时从多个区域请求音频

如果应用想要同时在多个区域中播放音频,则必须通过在捆绑包中包含 AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID 来请求每个区域的焦点

//Create attribute with bundle and AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID
Bundle bundle = new Bundle();
bundle.putInt(CarAudioManager.AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID,
               zoneId);

AudioAttributes attributesWithZone = new AudioAttributes.Builder()
     .setUsage(AudioAttributes.USAGE_MEDIA)
     .addBundle(bundle)
     .build();

//Create focus request using built attributesWithZone

此捆绑包参数允许请求者替换自动音频区域映射,而是使用指定的区域 ID。因此,应用可以为不同的音频区域发出单独的请求。

HAL 音频焦点

从 Android 11 开始,HAL 能够代表外部音频流请求焦点。虽然是可选的,但强烈建议使用这些 API,以便外部声音能够最佳地参与到 Android 生态系统中,并提供无缝的用户体验。

HAL 最终决定哪些声音应获得优先权。在某种程度上,无论 HAL 是否被授予音频焦点,都应播放紧急和安全关键声音,并且即使 HAL 丢失音频焦点,也应继续酌情播放这些声音。政府法规要求的任何声音也是如此。

HAL 应在播放紧急或安全关键声音时主动将 Android 音频流静音,以确保可以清楚地听到这些声音。

AudioControl@2.0

AudioControl HAL 2.0 版本引入了以下新 API:

API 用途
IAudioControl#registerFocusListener 向 AudioControl HAL 注册 IFocusListener 的实例。此侦听器使 HAL 能够请求和放弃音频焦点。HAL 提供一个 ICloseHandle 实例,供 Android 用于注销侦听器。
IAudioControl#onAudioFocusChange 通知 HAL 由 HAL 通过 IFocusListener 发出的焦点请求的状态变化,包括对初始焦点请求的响应。
IFocusListener#requestAudioFocus 代表 HAL 请求指定用途、区域 ID 和焦点增益类型的焦点。
IFocusListener#abandonAudioFocus 放弃指定用途和区域 ID 的现有 HAL 焦点请求。

HAL 可以同时发出多个焦点请求,但每个用途和区域 ID 配对只能发出一个请求。Android 假设 HAL 在发出请求后立即开始播放某个用途的声音,并持续播放,直到放弃焦点为止。

除了 registerFocusListener 之外,这些请求都是 oneway,以确保 Android 在处理焦点请求时不会延迟 HAL。HAL 不应等待获得焦点后再播放安全关键声音。对于 HAL 来说,通过 IAudioControl#onAudioFocusChange 侦听和响应音频焦点变化是可选的。

OEM 车载音频焦点服务

在 Android 14 中,AAOS 引入了车载 OEM 插件服务,以实现某些车载组件的可配置性。对于车载音频插件服务,插件服务允许 OEM 管理车载音频服务拦截的焦点请求。这使 OEM 在按照规则和法规的要求管理焦点方面具有更大的灵活性。因此,不同制造商之间以及不同区域之间的音频焦点互动可能有所不同。音频焦点的基本前提仍然成立,即应用仍应请求焦点以更好地管理音频,从而增强用户体验。一般来说,应用发出的音频焦点请求仍然适用某些规则:

  • 在没有任何常驻的高优先级音频焦点(包括电话呼叫、紧急警报或安全通知)的情况下,应用应能够以瞬时或永久方式获得音频焦点。

  • 当媒体焦点处于活动状态时:

    • 请求通话用途焦点的应用应能够并发或独占地接收通话。

    • 请求导航用途焦点的应用应能够并发或独占地接收导航焦点。

    • 请求助理用途焦点的应用应能够并发或独占地接收用途焦点。

  • 当常驻的高优先级音频焦点(包括电话呼叫、紧急警报或安全通知)应用处于活动状态时,任何传入的延迟音频焦点请求都应根据需要授予或延迟。

虽然以上建议并非详尽无遗,但它们可以帮助请求焦点的应用在没有活动的高优先级声音时获得焦点。即使在高优先级声音处于活动状态时,也应遵守延迟的焦点请求,并且在优先级高的声音停止时应能够获得焦点。