音频焦点

通常情况下,任何时候都只能由一个应用在全局范围内持有音频焦点。其他应用请求获得焦点时,前一个应用会收到焦点丢失事件,新的应用则会获得焦点。但也有一些例外情况,例如,当通话应用持有音频焦点时,新的焦点请求会被拒。

新的上下文交互定义

汽车音频焦点的管理方式从 Android 9 开始有所变化。CarAudioFocus 引入了一个交互矩阵,该矩阵可在收到焦点请求时根据新的请求来源及当前焦点持有者的用途捕获所需行为。交互类型分以下三种:

独占交互

在独占应用上下文交互中,一次只允许一个应用持有焦点。如果两个媒体播放器应用都在播放媒体,则只有其中一个可以持有焦点。这是大多数焦点持有者和焦点请求者都请求 AudioManager.AUDIOFOCUS_GAIN 时的默认交互方式。在这种情况下,当焦点请求获批时,系统会将 AudioManager.AUDIOFOCUS_LOSS 分派给当前的焦点持有者。除了 AudioManager.AUDIOFOCUS_GAIN 以外,独占交互可能还会请求其他音频焦点。交互仍是独占模式,且系统会分派相应类型的焦点丢失事件。

拒绝交互

在拒绝上下文交互中,焦点请求者发出的请求会一直遭拒。尝试从通知转换为闹铃就是拒绝用途交互的一个示例。在本例中,如果播放通知铃声的应用正持有音频焦点,而另一个应用要请求焦点以播放闹铃,则闹钟应用发出的焦点请求会被拒。由于焦点请求遭拒,因此系统不会向当前焦点持有者分派任何类型的焦点丢失事件。

并发交互

车载音频用户最感兴趣的一个方面就是并发上下文交互。在这种交互模式下,请求音频焦点的车载应用可与其他应用同时播放音频。除了能够进行车载音频路由之外,原始设备制造商 (OEM) 还可以将音频路由到汽车的其他部分。要实现并发交互,焦点请求者必须请求 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCKsetPauseWhenDucked(true) 在与 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 结合使用时会正常运行。系统将为并发焦点请求发送焦点丢失事件,使应用暂停播放。后者通常用于有声图书应用,可防止用户错过音频。

虽然并发交互适用于许多实用应用,但原始设备制造商 (OEM) 必须在硬件级别跨输出设备实现混音和降低音量。例如,同时提供导航和媒体播放服务时,原始设备制造商 (OEM) 可以暂时将驾驶员侧播放媒体的音响静音(降低媒体音量),改为播放导航提示。如果汽车的配置是将媒体和导航音频传送至不同的设备,则可通过在将数据传送至导航设备时降低媒体的音量来实现这一点。在这种情况下,原始设备制造商 (OEM) 可在驾驶员侧播放导航提示,在车厢的其余位置继续播放媒体。但是,如果将两个来源传送至同一输出设备,则不会降低音量。因此,建议您将同时播放的所有内容路由到不同的设备,以便系统适当地降低音量。

交互矩阵

下方的表 1 展示了 CarAudioFocus 中定义的交互矩阵。在该表中,行内容表示当前正持有焦点的应用,列内容表示传入的焦点请求者。

如果某个音乐媒体应用正持有音频焦点,某导航应用请求获得焦点,则该矩阵表示这两个交互可以同时进行。如果传入的导航应用请求的焦点为 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,则该应用的焦点请求会获批,且系统不会向媒体应用发送焦点丢失事件。该矩阵允许同时存在多个焦点持有者。在这种情况下,系统会将传入的焦点请求者与当前的各个焦点持有者进行比较,然后决定进行哪种转换。

表 1. 汽车音频焦点用途交互矩阵。

传入焦点请求者的上下文:

上下文 无效 音乐 导航 语音 铃声 通话 闹钟 通知 系统
无效 REJ REJ REJ REJ REJ REJ REJ REJ REJ
音乐 REJ EXC CON EXC EXC EXC EXC CON CON
导航 REJ CON CON EXC CON EXC CON CON CON
语音 REJ CON REJ CON EXC EXC REJ REJ REJ
铃声 REJ REJ CON CON CON CON REJ REJ CON
通话 REJ REJ CON REJ CON CON CON CON REJ
闹钟 REJ CON CON EXC EXC EXC CON CON CON
通知 REJ CON CON EXC EXC EXC CON CON CON
系统 REJ CON CON EXC EXC EXC CON CON CON

说明:

  • REJ:拒绝
  • EXC:独占
  • CON:并发

如需了解详情,请参阅 packages/services/Car/service/src/com/android/car/audio/CarAudioFocus.java

多音频区焦点管理

Android 10 保留了 Android 9 中的应用用途交互规则,但对每个音频区进行了进一步划分。因此,主车厢与后座娱乐系统的焦点可分开管理,以免某个音频区的焦点发生变化时另一个音频区的播放中断。

所有应用的焦点均由 CarAudioService 自动管理且通过 CarAudioManager.setZoneIdForUid API 进行设置。将 UID 映射到特定音频区后,相应音频区会自动请求焦点。以下是焦点请求示例:

//Create focus
ret = mAudioManager
.requestAudioFocus(mFocusListener,
mMusicAudioAttrib,AudioManager.AUDIOFOCUS_GAIN, 0);

if (ret == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { Log.i(TAG, "Got focus for usage " + mMusicAudioAttrib.getUsage()); start(); } else { Log.i(TAG, "MediaPlayer denied focus for usage " + mMusicAudioAttrib.getUsage()); }

该示例与第三方应用请求焦点的方式没有任何区别,因此第三方应用仍可以使用音频区功能,但请注意,音频区必须由原始设备制造商 (OEM) 的应用启动器或其他管理应用进行管理。

同时流式传输到多个音频区

如果将应用流式传输到单个音频区,从应用的角度来看,音频焦点运作方式没有发生变化,且可通过使用 CarAudioManager.setZoneIdForUid 对音频区的管理进行设置。但是,如果应用需要同时在多个音频区播放音频,则必须在软件包中包含 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,
               zoneIdForDisplayId);

mMusicAudioAttribForDisplay = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .addBundle(bundle) .build();
//Create focus