일반적으로 오디오 입력은 내장 마이크, 외부 마이크 또는 기기에 연결된 오디오 인터페이스에서 들어옵니다. 또한 전화 통화에서도 오디오가 입력될 수 있습니다.
두 개 이상의 앱이 동일한 오디오 입력을 '캡처'하려는 경우가 있습니다. 각 앱은 서로 다른 작업을 수행할 수 있습니다. 예를 들어 오디오를 수신하는 어떤 앱은 오디오를 '녹음'할 수도 있고(예: 단순한 음성 레코더), 어떤 앱은 오디오를 '들을' 수도 있습니다(예: 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 어시스턴트 및 모든 접근성 서비스가 포함됩니다.
또한 앱이 '개인 정보 보호에 민감한' 오디오 소스(CAMCORDER
또는 VOICE_COMMUNICATION
)를 사용하는 경우 다르게 처리됩니다.
오디오 입력을 사용하고 공유할 때 우선순위를 지정하는 규칙은 다음과 같습니다.
- 권한이 있는 앱은 일반 앱보다 우선순위가 높습니다.
- 가시적인 포그라운드 UI가 있는 앱은 백그라운드 앱보다 우선순위가 높습니다.
- 개인정보 보호에 민감한 소스에서 오디오를 캡처하는 앱은 그렇지 않은 앱보다 우선순위가 높습니다.
- 두 개의 일반 앱이 동시에 오디오를 캡처할 수 없습니다.
- 어떤 경우에는 권한이 있는 앱이 다른 앱과 오디오 입력을 공유할 수 있습니다.
- 우선순위가 동일한 두 개의 백그라운드 앱이 오디오를 캡처할 경우, 마지막에 시작된 앱의 우선순위가 높습니다.
공유 시나리오
두 앱이 오디오를 캡처하려고 할 경우, 두 개의 앱이 모두 입력 신호를 수신하거나 그중 하나가 무음을 수신할 수 있습니다.
4가지의 주요 시나리오가 있습니다.
- 어시스턴트 + 일반 앱
- 접근성 서비스 + 일반 앱
- 일반 앱 2개
- 음성 통화 + 일반 앱
어시스턴트 + 일반 앱
어시스턴트는 사전에 설치되어 있고 RoleManager.ROLE_ASSISTANT
역할을 맡기 때문에 권한이 있는 앱입니다.
이 역할을 가진 다른 사전 설치 앱은 모두 비슷하게 취급됩니다.
Android는 다음과 같은 규칙에 따라 입력 오디오를 공유합니다.
개인 정보 보호에 민감한 오디오 소스를 사용하는 다른 앱이 이미 오디오를 캡처 중인 것이 아니라면 어시스턴트가 오디오를 수신할 수 있습니다 (포그라운드에 있거나 백그라운드에 있는 경우에 모두 해당).
어시스턴트가 화면에 가시적인 UI 구성요소를 표시하고 있지 않으면 앱이 오디오를 수신합니다.
어시스턴트가 백그라운드에 있고 다른 앱이 개인 정보 보호에 민감한 오디오 소스에서 오디오를 캡처하고 있지 않을 경우에만 두 앱이 모두 오디오를 수신합니다.
접근성 서비스 + 일반 앱
AccessibilityService
에는 엄격한 선언이 필요합니다.
Android는 다음과 같은 규칙에 따라 입력 오디오를 공유합니다.
서비스의 UI가 위에 있을 경우, 서비스와 앱이 모두 오디오 입력을 수신합니다. 이 동작은 음성 통화 제어, 음성 명령을 사용한 동영상 캡처 등의 기능을 제공합니다.
접근성 서비스가 위에 있지 않을 경우에는 아래의 일반 앱 2개와 같이 처리합니다.
일반 앱 2개
두 앱이 동시에 오디오를 캡처할 경우, 한 앱만 오디오를 수신하고 나머지 앱은 무음을 수신합니다.
Android는 다음과 같은 규칙에 따라 입력 오디오를 공유합니다.
- 두 앱이 모두 개인 정보 보호에 민감하지 않다면 UI가 위에 있는 앱이 오디오를 수신합니다. 두 앱에 모두 UI가 없을 경우, 가장 최근에 캡처를 시작한 앱이 오디오를 수신합니다.
- 두 앱 중 하나가 개인 정보 보호에 민감할 경우, 그 앱이 오디오를 수신하고 나머지 앱은 무음을 수신합니다. 이는 다른 앱의 UI가 상위에 있거나 더욱 최근에 오디오 캡처를 시작했다 하더라도 마찬가지입니다.
- 두 앱이 모두 개인 정보 보호에 민감할 경우 가장 최근에 캡처를 시작한 앱이 오디오를 수신하고 나머지 앱은 무음을 수신합니다.
음성 통화 + 일반 앱
AudioManager.getMode()
에서 반환한 오디오 모드가 MODE_IN_CALL
또는 MODE_IN_COMMUNICATION
이면 음성 통화가 활성 상태입니다.
Android는 다음과 같은 규칙에 따라 입력 오디오를 공유합니다.
- 전화 통화는 항상 오디오를 수신합니다.
- 접근성 서비스인 앱은 오디오를 캡처할 수 있습니다.
CAPTURE_AUDIO_OUTPUT
권한을 가진 권한이 있는(사전 설치된) 앱일 경우 음성 통화를 캡처할 수 있습니다.음성 통화의 업링크 (TX), 다운링크 (RX), 또는 그 두 가지를 모두 캡처하려면 앱에서 오디오 소스
MediaRecorder.AudioSource.VOICE_UPLINK
또는MediaRecorder.AudioSource.VOICE_DOWNLINK
또는 기기AudioDeviceInfo.TYPE_TELEPHONY
를 지정해야 합니다.
Android 11 동작
Android 11 (API 수준 30)은 위에 설명된 Android 10 우선순위 스키마를 준수합니다. 또한 선택한 사용 사례와 관계없이 오디오를 동시에 캡처하는 기능을 사용 또는 사용 중지하는 새로운 메서드를 AudioRecord
, MediaRecorder
, AAudioStream
에 제공합니다.
새로운 메서드는 다음과 같습니다.
AudioRecord.Builder.setPrivacySensitive()
AudioRecord.isPrivacySensitive()
MediaRecorder.setPrivacySensitive()
MediaRecorder.isPrivacySensitive()
AAudioStreamBuilder_setPrivacySensitive()
AAudioStream_isPrivacySensitive()
setPrivacySensitive()
가 true
이면 캡처 사용 사례가 비공개이며 권한이 있는 어시스턴트도 동시에 캡처할 수 없습니다. 이 설정은 오디오 소스에 따라 달라지는 기본 동작보다 우선 적용됩니다. 예를 들어 VOICE_COMMUNICATION
은 기본적으로 비공개이지만 UNPROCESSED
는 비공개가 아닙니다.
구성 변경
여러 앱이 동시에 오디오를 캡처할 경우, 그중 한두 개의 앱만 '활성화' (오디오 수신)되고 나머지 앱은 음소거 (무음 수신)됩니다. 활성화된 앱이 변경되면 오디오 프레임워크가 다음 규칙에 따라 오디오 경로를 다시 구성할 수 있습니다.
- 각 활성 앱의 오디오 입력 기기는 변경될 수 있습니다 (예: 내장 마이크에서 연결된 블루투스 헤드셋으로 변경).
- 우선순위가 가장 높은 앱과 관련된 사전 처리가 활성화됩니다. 그 외에 다른 모든 사전 처리는 무시됩니다.
활성화된 앱은 그보다 우선순위가 높은 앱이 활성화되면 음소거될 수 있으므로 AudioRecord
또는 MediaRecorder
객체에 AudioManager.AudioRecordingCallback을 등록하여 구성 변경 시 알림을 받을 수 있습니다.
가능한 구성 변경은 다음과 같습니다.
- 캡처 음소거 또는 해제
- 기기 변경
- 사전 처리 변경
- 스트림 속성 변경(샘플링 레이트, 채널 마스크, 샘플 형식)
캡처가 시작되기 전에 AudioRecord.registerAudioRecordingCallback()
를 호출해야 합니다.
이 콜백은 앱이 오디오를 수신하고 변경이 있을 경우에만 실행됩니다.
onRecordingConfigChanged()
메서드는 현재 오디오 캡처 상태가 포함된 AudioRecordingConfiguration
를 반환합니다. 변경사항에 대해 알아보려면 다음 메서드를 사용하세요.
isClientSilenced()
- 현재 클라이언트에 반환된 오디오가 캡처 정책으로 인해 음소거되면 true를 반환합니다.
getAudioDevice()
- 활성 오디오 기기를 반환합니다.
getEffects()
- 활성화된 사전 처리 효과를 반환합니다. 클라이언트가 최우선 활성화 앱이 아니라면
getClientEffects()
에서 반환한 것과 활성화된 효과가 다를 수 있습니다. getFormat()
- 스트림 속성을 반환합니다. 클라이언트가 수신한 실제 오디오 데이터는 항상
getClientFormat()
에서 반환한 필수 형식을 준수합니다. 프레임워크가 하드웨어 인터페이스에서 사용한 형식에서 클라이언트가 지정한 형식으로 전환하는 데 필요한 리샘플링, 채널, 형식 변환을 자동으로 수행합니다. AudioRecord.getActiveRecordingConfiguration()
.- 활성 녹음 구성을 반환합니다.
AudioManager.getActiveRecordingConfigurations()
를 호출하면 기기에 활성화된 모든 녹음된 오디오를 전체적으로 확인할 수 있습니다.