音訊輸入通常來自內建麥克風、外接麥克風或連接至裝置的音訊介面。音訊輸入內容也可以來自電話對話。
有時,兩個或更多應用程式可能都想「擷取」相同的音訊輸入內容。可能會執行不同的工作。舉例來說,某些接收音訊的應用程式可能會「錄音」,例如簡單的語音錄音器,而其他應用程式可能會「聆聽」,例如 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 的應用程式優先順序高於背景應用程式。
- 從隱私權敏感來源擷取音訊的應用程式,其優先順序高於未從隱私權敏感來源擷取音訊的應用程式。
- 兩個一般應用程式絕對無法同時擷取音訊。
- 在某些情況下,具有特殊權限的應用程式可以與其他應用程式共用音訊輸入內容。
- 如果兩個優先順序相同的背景應用程式都擷取音訊,優先順序較高的會是最後啟動的應用程式。
共用情境
當兩個應用程式嘗試擷取音訊時,兩者都可能會收到輸入信號,或是其中一個應用程式收到靜音。
主要有四種情況:
- Google 助理 + 一般應用程式
- 無障礙服務 + 一般應用程式
- 兩個一般應用程式
- 語音通話 + 一般應用程式
Google 助理 + 一般應用程式
由於 Google 助理是預先安裝的應用程式,且擁有 RoleManager.ROLE_ASSISTANT
角色,因此屬於特殊權限應用程式。任何其他具有此角色的預先安裝應用程式都會受到類似的處理。
Android 會根據下列規則分享輸入音訊:
除非其他應用程式已使用隱私權敏感的音訊來源進行擷取,否則 Google 助理可接收音訊 (無論是在前景或背景)。
除非 Google 助理在畫面頂端顯示可見的 UI 元件,否則應用程式會接收音訊。
請注意,只有在 Google 助理處於背景執行狀態,且其他應用程式未從隱私權敏感的音訊來源擷取音訊時,兩個應用程式才會接收音訊。
無障礙服務 + 一般應用程式
AccessibilityService
需要嚴格的宣告。
Android 會根據下列規則分享輸入音訊:
如果服務的 UI 位於頂端,則服務和應用程式都會接收音訊輸入內容。這項行為可提供語音指令控制語音通話或錄影等功能。
如果服務不在頂層,系統會將此情況視為下方一般兩個應用程式情況。
兩個一般應用程式
當兩個應用程式同時擷取時,只有一個應用程式會收到音訊,另一個應用程式則會收到靜音。
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
時,擷取用途為私人用途,即使是擁有權限的 Google 助理也無法同時擷取。這項設定會覆寫依音訊來源而定的預設行為。舉例來說,VOICE_COMMUNICATION
預設為私人,但 UNPROCESSED
則不是。
設定變更
當多個應用程式同時擷取音訊時,只有一或兩個處於「啟用」狀態 (接收音訊),其他則處於靜音狀態 (接收靜音)。當使用中的應用程式發生變更時,音訊架構可能會根據下列規則重新設定音訊路徑:
- 每個有效應用程式的音訊輸入裝置可能會變更 (例如從內建麥克風變成已連接的藍牙耳機)。
- 系統會啟用與優先順序最高的有效應用程式相關聯的預先處理程序。系統會忽略所有其他預先處理作業。
由於優先順序較高的應用程式啟用時,可能會使目前啟用的應用程式靜音,因此您可以在 AudioRecord
或 MediaRecorder
物件上登錄 AudioManager.AudioRecordingCallback,以便在設定變更時收到通知。可能的變更包括:
- 擷取靜音或取消靜音
- 裝置變更
- 預先處理作業已變更
- 串流屬性已變更 (取樣率、通道遮罩、取樣格式)
您必須先呼叫 AudioRecord.registerAudioRecordingCallback()
,才能開始擷取。只有在應用程式接收音訊並發生變更時,系統才會執行回呼。
方法 onRecordingConfigChanged()
會傳回包含目前音訊擷取狀態的 AudioRecordingConfiguration
。請使用下列方法瞭解變更內容:
isClientSilenced()
- 如果傳回給用戶端的音訊目前因擷取政策而靜音,則傳回 true。
getAudioDevice()
- 傳回有效的音訊裝置。
getEffects()
- 會傳回有效的預先處理效果。請注意,如果用戶端不是最高優先順序的使用中應用程式,則活動效果可能與
getClientEffects()
傳回的效果不同。 getFormat()
- 傳回串流屬性。請注意,用戶端收到的實際音訊資料一律會遵循
getClientFormat()
傳回的必要格式。架構會自動執行必要的重新取樣、管道和格式轉換作業,將硬體介面使用的格式轉換為用戶端指定的格式。 AudioRecord.getActiveRecordingConfiguration()
。- 傳回進行中的錄製設定。
您可以呼叫 AudioManager.getActiveRecordingConfigurations()
,取得裝置上所有有效錄音的概覽。