共享音频输入

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

有时,两个或多个应用可能都想要“捕获”同一音频输入。这些应用可能在执行不同的任务。例如,接收音频的某些应用可能是“录音”,比如简单的录音器,而其他应用可能在“监听”,比如 Google 助理或响应语音指令的无障碍服务。

无论是哪种情况,这些应用都希望接收音频输入。 在本页面中,无论应用是录制还是侦听,我们都使用“捕获”一词。

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

Android 10 之前的行为

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

该规则的一个例外情况是,特权应用(例如 Google 助理或无障碍服务)拥有 android.permission.CAPTURE_AUDIO_HOTWORD 权限并使用 HOTWORD 类型的音频源。在这种情况下,另一个应用可以开始录音。出现这种情况时,特权应用终止,新应用捕获输入。

Android 9 中还添加了一项更改:只有在前台(或前台服务)中运行的应用才能捕获音频输入。当没有前台服务或前台界面组件的应用开始捕获时,应用将继续运行,但会受到静默处理,即便它是当时唯一捕获音频的应用也是如此。

Android 10 行为

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

Android 10 采用优先级方案,可以在运行的应用之间切换输入音频流。在大多数情况下,如果新应用获取音频输入,则之前捕获音频的应用会继续运行,但会受到静默处理。在某些情况下,系统可以继续向这两个应用传送音频。下面将介绍各种共享方案

此架构类似于音频焦点处理多个应用争夺音频输出使用的方式。不过,音频焦点获取和焦点释放由编程请求管理;此处描述的输入切换方案基于优先级策略,该策略会在新应用开始捕获音频时自动应用。

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

  • “普通”应用由用户安装。
  • 设备上预安装了“特权”应用。其中包括 Google 助理以及所有无障碍服务。

此外,如果应用使用“对隐私敏感”的音频源(CAMCORDERVOICE_COMMUNICATION),则其会受到区别对待。

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

  • 特权应用的优先级高于普通应用。
  • 具有可见前台界面的应用比后台应用具有更高的优先级。
  • 相较于从非隐私敏感源捕获音频的应用,从隐私敏感源捕获音频的应用有着更高的优先级。
  • 两个普通应用永远无法同时捕获音频。
  • 在某些情况下,特权应用可以与其他应用共享音频输入。
  • 如果两个优先级相同的后台应用都在捕获音频,则后开始的那个优先级更高。

共享方案

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

四种主要方案如下:

  • Google 助理 + 普通应用
  • 无障碍服务 + 普通应用
  • 两个普通应用
  • 语音通话 + 普通应用

Google 助理 + 普通应用

Google 助理是特权应用,因为它是预安装的,并且拥有 RoleManager.ROLE_ASSISTANT 角色。拥有此角色的任何其他预安装应用都会受到类似处理。

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

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

  • 除非 Google 助理在屏幕顶部具有可见的界面组件,否则应用会接收音频。

请注意,这两个应用仅在 Google 助理位于后台且其他应用未从隐私敏感音频源进行捕获时才接收音频。

无障碍服务 + 普通应用

AccessibilityService 需要严格的声明

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

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

  • 如果该服务不在顶部,则此情况的处理方式与下述两个普通应用的情况一样。

两个普通应用

当两个应用同时进行捕获时,只有一个应用接收音频,另一个应用会受到静默处理。

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

  • 如果两个应用都不具备隐私敏感性,则由界面位于顶部的应用接收音频。如果两个应用都没有界面,则较晚开始者接收音频。
  • 如果其中一个应用具备隐私敏感性,则由其接收音频,另一个应用则会受到静默处理,即使后者由界面位于顶部或较晚开始捕获也是如此。
  • 如果这两个应用都对隐私敏感,那么最近开始捕获的应用将接收音频,另一个应用会保持静默。

语音通话 + 普通应用

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

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

Android 11 行为

Android 11(API 级别 30)遵循上述 Android 10 优先级方案。它还在 AudioRecordMediaRecorderAAudioStream 中提供了新方法,无论所选用例如何,这些方法都可启用和停用同时捕获音频的功能。

这些新方法包括:

如果将 setPrivacySensitive() 设为 true,则捕获用例是私有的,即使是特权助理也不能并发捕获。此设置会替换依赖于音频源的默认行为。例如,VOICE_COMMUNICATION 默认为私有状态,但 UNPROCESSED 则不是私有状态。

配置变更

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

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

当优先级较高的应用处于活动状态时,活动应用可能会受到静默处理,因此您可以在 AudioRecordMediaRecorder 对象上注册一个 AudioManager.AudioRecordingCallback,以便在配置发生更改时收到通知。可能的更改如下:

  • 捕获受到静默处理或解除静默处理
  • 设备更改
  • 预处理更改
  • 音频流属性更改(采样率、通道掩码、采样格式)

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

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

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

您可以通过调用 AudioManager.getActiveRecordingConfigurations() 来大致了解设备上所有正在进行的录音。