共享音频输入

音频输入通常来自内置麦克风、外部麦克风或连接到设备的音频接口。音频输入也可以来自电话通话。

有时,两个或多个应用都可能想要“捕获”相同的音频输入。它们可能正在执行不同的任务。例如,一些接收音频的应用可能是“录制”,例如简单的录音机,而其他应用可能是“监听”,例如 Google 助理或响应语音命令的辅助功能服务。

在这两种情况下,这些应用都希望接收音频输入。在本页中,无论应用是录制还是仅监听,我们都使用术语“捕获”。

如果两个或多个应用想要同时捕获音频,则可能存在将音频信号从同一来源传递到所有应用的问题。本页介绍 Android 系统如何在捕获音频的多个应用之间共享音频输入。

Android 10 之前的行为

在 Android 10 之前,音频输入流一次只能被一个应用捕获。如果某个应用已经在录制或监听音频,您的应用可以创建一个 AudioRecord 对象,但在调用 AudioRecord.startRecording() 时会返回错误,并且录制不会开始。

此规则的一个例外是,当具有特权的应用(如 Google 助理或辅助功能服务)拥有 android.permission.CAPTURE_AUDIO_HOTWORD 权限并使用类型为 HOTWORD 的音频源时。在这种情况下,另一个应用可以开始录制。当这种情况发生时,特权应用会终止,新应用会捕获输入。

Android 9 中添加了另一个更改:只有在前台运行的应用(或前台服务)才能捕获音频输入。当没有前台服务或前台 UI 组件的应用开始捕获时,应用会继续运行,但即使它是当时唯一捕获音频的应用,也会收到静音。

Android 10 行为

Android 10 之前,行为是“先到先得”。一旦某个应用开始捕获音频,在该应用停止捕获之前,其他任何应用都无法访问音频输入。

Android 10 采用了优先级方案,可以在应用运行时在应用之间切换音频输入流。在大多数情况下,如果新的应用获取了音频输入,之前正在捕获的应用会继续运行,但会收到静音。在某些情况下,系统可以继续向这两个应用传递音频。下面将解释各种 共享场景

此方案类似于音频焦点处理多个应用争夺音频输出使用的方式。但是,音频焦点通过程序请求来获取和释放焦点进行管理,而此处描述的输入切换方案则基于优先级策略,该策略在新的应用开始捕获音频时自动应用。

为了捕获音频,Android 区分两种类型的应用

  • “普通”应用由用户安装。
  • “特权”应用预装在设备上。其中包括 Google 助理和所有辅助功能服务。

此外,如果应用使用“隐私敏感”音频源,则应用的处理方式会有所不同:CAMCORDERVOICE_COMMUNICATION

使用和共享音频输入的优先级规则如下

  • 特权应用的优先级高于普通应用。
  • 具有可见前台 UI 的应用的优先级高于后台应用。
  • 从隐私敏感源捕获音频的应用的优先级高于不从隐私敏感源捕获音频的应用。
  • 两个普通应用永远不能同时捕获音频。
  • 在某些情况下,特权应用可以与其他应用共享音频输入。
  • 如果两个相同优先级的后台应用正在捕获音频,则最后启动的应用具有更高的优先级。

共享场景

当两个应用尝试捕获音频时,它们都可以接收输入信号,或者其中一个可能会收到静音。

主要有四种场景

  • 助理 + 普通应用
  • 辅助功能服务 + 普通应用
  • 两个普通应用
  • 语音通话 + 普通应用

助理 + 普通应用

助理是特权应用,因为它预装并且拥有 RoleManager.ROLE_ASSISTANT 角色。任何其他具有此角色的预装应用都将以类似方式处理。

Android 会根据以下规则共享输入音频

  • 助理可以接收音频(无论它是在前台还是后台),除非另一个使用隐私敏感音频源的应用已经在捕获。

  • 应用接收音频,除非助理在屏幕上具有可见的 UI 组件。

请注意,只有当助理在后台且其他应用未从隐私敏感音频源捕获时,这两个应用才会接收音频。

辅助功能服务 + 普通应用

AccessibilityService 需要严格的 声明

Android 会根据以下规则共享输入音频

  • 如果服务的 UI 位于顶部,则服务和应用都会接收音频输入。此行为提供了诸如使用语音命令控制语音通话或视频捕获的功能。

  • 如果服务不在顶部,则此情况将被视为下面的普通双应用情况。

两个普通应用

当两个应用同时捕获时,只有一个应用会接收音频,另一个应用会收到静音。

Android 会根据以下规则共享输入音频

  • 如果这两个应用都不是隐私敏感的,则具有 UI 位于顶部的应用会接收音频。如果两个应用都没有 UI,则最近开始捕获的应用会接收音频。
  • 如果其中一个应用是隐私敏感的,即使它具有 UI 位于顶部或最近开始捕获,它也会接收音频,而另一个应用会收到静音。
  • 如果两个应用都是隐私敏感的,则最近开始捕获的应用会接收音频,而另一个应用会收到静音。

语音通话 + 普通应用

如果 AudioManager.getMode() 返回的音频模式为 MODE_IN_CALLMODE_IN_COMMUNICATION,则表示语音通话处于活动状态。

Android 会根据以下规则共享输入音频

Android 11 行为

Android 11(API 级别 30)遵循上述 Android 10 优先级方案。它还在 AudioRecordMediaRecorderAAudioStream 中提供了新方法,这些方法可以启用和禁用同时捕获音频的功能,而不管选择的用例是什么。

新方法是

setPrivacySensitive()true 时,捕获用例为私有,即使是特权助理也不能同时捕获。此设置会覆盖依赖于音频源的默认行为。例如,VOICE_COMMUNICATION 默认情况下是私有的,但 UNPROCESSED 不是。

配置更改

当多个应用同时捕获音频时,只有一个或两个应用处于“活动”状态(接收音频);其他应用处于静音状态(接收静音)。当活动应用发生变化时,音频框架可能会根据以下规则重新配置音频路径

  • 每个活动应用的音频输入设备可能会发生变化(例如,从内置麦克风更改为连接的蓝牙耳机)。
  • 启用与最高优先级活动应用关联的预处理。所有其他预处理都将被忽略。

由于当更高优先级的应用变为活动状态时,活动应用可能会被静音,因此您可以注册 AudioManager.AudioRecordingCallbackAudioRecordMediaRecorder 对象,以便在配置更改时收到通知。可能的更改可能是

  • 捕获静音或取消静音
  • 设备已更改
  • 预处理已更改
  • 流属性已更改(采样率、通道掩码、样本格式)

您必须在捕获开始之前调用 AudioRecord.registerAudioRecordingCallback()。仅当应用正在接收音频并且发生更改时,才会执行回调。

onRecordingConfigChanged() 方法返回一个包含当前音频捕获状态的 AudioRecordingConfiguration。使用以下方法了解更改

isClientSilenced()
如果由于捕获策略导致当前返回给客户端的音频被静音,则返回 true。
getAudioDevice()
返回活动音频设备。
getEffects()
返回活动的预处理效果。请注意,如果客户端不是最高优先级的活动应用,则活动效果可能与 getClientEffects() 返回的效果不同。
getFormat()
返回流属性。请注意,客户端接收的实际音频数据始终尊重 getClientFormat() 返回的所需格式。框架会自动执行从硬件接口使用的格式到客户端指定的格式的必要重采样、通道和格式转换。
AudioRecord.getActiveRecordingConfiguration().
返回活动录制配置。

您可以通过调用 AudioManager.getActiveRecordingConfigurations() 获取设备上所有活动录制的概览。