在啟動邏輯流之前,應用程序應該使用與其邏輯流相同的音頻屬性來請求音頻焦點。雖然建議發送這樣的焦點請求,但係統並未強制執行。某些應用程序可能會明確跳過發送請求以實現特定行為(例如,在通話期間故意播放聲音)。
因此,您應該將焦點視為一種間接控制和消除衝突播放的方式,而不是主要的音頻控制機制;車輛不應依賴對焦系統來操作音頻子系統。
焦點互動
為了支持 AAOS 的需求,音頻焦點請求是基於請求的CarAudioContext
和當前焦點持有者之間的預定義交互來處理的。交互分為三種類型:獨占、拒絕和並發。
獨家互動
在獨占交互中,一次只允許一個應用程序保持焦點。因此,傳入的焦點請求被授予焦點,而現有的焦點持有者失去焦點。這方面的一個例子是當用戶啟動一個新的音樂應用程序而音樂已經在現有應用程序中播放時。由於兩者都在播放媒體,因此一次只允許其中一個應用程序保持焦點。結果,新啟動的應用程序的焦點請求將返回AUDIOFOCUS_REQUEST_GRANTED
並且當前播放音樂的應用程序將收到一個焦點更改事件,其丟失狀態與發出的請求類型相對應。這是 Android 中最常見的交互模型。
拒絕互動
使用拒絕交互,傳入的請求總是被拒絕。在通話過程中嘗試播放音樂是被拒絕交互的一個示例。在這種情況下,如果撥號器當前正在為呼叫保持音頻焦點,並且第二個應用程序請求焦點以播放音樂,則音樂應用程序將收到AUDIOFOCUS_REQUEST_FAILED
以響應其請求。由於焦點請求被拒絕,因此不會向當前焦點持有者發送任何類型的焦點丟失。
並發交互
AAOS 最獨特的是並發交互。這使得在汽車中請求音頻焦點的應用程序能夠與其他應用程序同時保持焦點。要發生並發交互,必須滿足以下條件。這:
- 傳入的焦點請求必須要求
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
。 - 當前焦點持有者不
setPauseWhenDucked(true)
。 - 當前焦點持有者不選擇接收鴨子事件。
如果滿足這些條件,則焦點請求將返回AUDIOFOCUS_REQUEST_GRANTED
,而當前焦點持有者的焦點不會發生變化。但是,如果當前焦點持有者選擇接收閃避事件或在閃避時暫停,則當前焦點持有者將失去焦點,就像獨占交互一樣。
處理並發流
雖然並發交互有許多有用的應用,但 OEM 必須在硬件級別跨輸出設備處理混音和閃避。因此,強烈建議CarAudioContext
僅路由到與CarAudioContext
無法同時播放的相同輸出設備。通過為並發流設置單獨的輸出設備,這使 HAL 能夠在將其中一個流混合在一起之前將其閃避,或者將物理流路由到車輛中的不同揚聲器。如果邏輯流在 Android 中混合,它們的收益將保持不變,它們將作為同一物理流的一部分交付。
例如,當導航和媒體同時傳遞時,媒體流的增益可能會暫時降低(迴避),以便更清楚地聽到導航指令。或者,導航流可以路由到駕駛員側揚聲器,同時媒體繼續在機艙的其餘部分播放。
交互矩陣
下表顯示了CarAudioService
定義的交互矩陣。行表示當前焦點持有者的CarAudioContext
,列表示傳入請求的。
看一個示例,其中音樂媒體應用程序當前保持焦點,而導航應用程序請求焦點,矩陣顯示這兩個交互可以同時播放,假設滿足並發交互的其他標準。
由於並發交互,可能存在多個焦點持有者。在這種情況下,傳入的焦點請求將與每個當前焦點持有者進行比較,然後再決定應用哪種交互。在這種情況下,最保守的交互會勝出(拒絕,然後是獨占,最後是並發)。
在下表中,提供了傳入焦點請求(列)的 CarAudioContext 與現有焦點持有者(行)的上下文之間的焦點交互。每個單元格代表兩個上下文的預期交互類型,其中:
- R:拒絕交互
- E:獨家互動
- C:並發交互
圖 1.音頻焦點交互
通話期間的導航
在 Android 11 中,引入了新的用戶設置,允許用戶更改導航和電話之間的交互行為。設置後, android.car.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL
會將傳入的NAVIGATION
焦點請求和當前CALL
焦點持有者之間的交互從concurrent更改為reject 。因此,如果用戶不希望導航指令打斷他們的通話,他們可以啟用此設置。這是為用戶保留的,並且可以動態設置以使後續焦點請求尊重新的設置值。
可延遲的音頻焦點
在 Android 11 中,AAOS 增加了對請求可延遲音頻焦點的支持。當非瞬態焦點請求與當前焦點持有者的交互通常會導致它們被拒絕時,這允許延遲非瞬態焦點請求。一旦焦點的變化導致延遲的請求可以獲得焦點的狀態,該請求將被授予。
延遲音頻焦點請求的規則
- 僅限非瞬態請求- 如前所述,只能對非瞬態源發出延遲請求。這是為了避免在相關後長時間播放瞬態聲音。
- 一次只能延遲一個請求- 如果在已經有延遲請求的情況下發出可延遲請求,則原始延遲請求將收到
AUDIOFOCUS_LOSS
更改事件,新請求將收到AUDIOFOCUS_REQUEST_DELAYED
的同步響應。 - 可延遲的請求必須有一個
OnAudioFocusChangeListener
。一旦請求被延遲,監聽器將用於在請求最終被授予(AUDIOFOCUS_GAIN
)或稍後被拒絕(AUDIOFOCUS_LOSS
)時通知請求者。
請求延遲焦點
要構建可以延遲的請求,請使用AudioFocusRequest.Builder#setAcceptsDelayedFocusGain
:
mMediaWithDelayedFocusListener = new MediaWithDelayedFocusListener(); mDelayedFocusRequest = new AudioFocusRequest .Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes(mMusicAudioAttrib) .setOnAudioFocusChangeListener(mMediaWithDelayedFocusListener) .setForceDucking(false) .setWillPauseWhenDucked(false) .setAcceptsDelayedFocusGain(true) .build();
然後,在發出請求時,處理AUDIOFOCUS_REQUEST_DELAYED
響應:
int delayedFocusRequestResults = mAudioManager.requestAudioFocus(mDelayedFocusRequest); if (delayedFocusRequestResults == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { // start audio playback return; } if (delayedFocusRequestResults == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) { // audio playback delayed to audio focus listener return; }
當請求延遲時,焦點監聽器負責處理焦點的變化:
private final class MediaWithDelayedFocusListener implements OnAudioFocusChangeListener { @Override public void onAudioFocusChange(int focusChange) { synchronized (mLock) { switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: … // Start focus playback case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: … // Pause media transiently case AudioManager.AUDIOFOCUS_LOSS: … // Stop media
多區域焦點管理
對於具有多個音頻區域的車輛,將為每個區域獨立管理音頻焦點。因此,對一個區域的請求不會考慮其他區域中的焦點持有者,也不會導致其他區域中的焦點持有者失去焦點。有了這個,主艙的焦點可以與後座娛樂系統分開管理,從而避免一個區域的音頻播放因另一個區域的焦點變化而中斷。
對於所有應用程序,焦點管理由CarAudioService
自動處理。焦點請求的音頻區域是根據其關聯的UserId
或UID
確定的。有關詳細信息,請參閱音頻路由。
同時從多個區域請求音頻
如果應用想要同時在多個區域播放音頻,它必須通過在包中包含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, zoneId); AudioAttributes attributesWithZone = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .addBundle(bundle) .build(); //Create focus request using built attributesWithZone
此捆綁參數允許請求者覆蓋自動音頻區域映射以使用指定的區域 ID。因此,應用程序可以針對不同的音頻區域發出單獨的請求。
HAL 音頻焦點
從 Android 11 開始,HAL 現在可以代表外部流請求焦點。雖然是可選的,但強烈建議使用這些 API,以使外部聲音成為 Android 生態系統的更好參與者,並提供更流暢的用戶體驗。
請記住,HAL 仍然負責最終決定哪些聲音應該優先。就此而言,無論 HAL 是否被授予音頻焦點,都應播放緊急和安全關鍵聲音,並且即使 HAL 失去音頻焦點,也應繼續播放適當的聲音。法規要求的任何聲音也是如此。
同樣,在播放緊急或安全關鍵聲音時,HAL 仍應主動將 Android 流靜音,以確保清晰地聽到它們。
音頻控制@2.0
AudioControl HAL 2.0 版引入了幾個新的 API:
API | 目的 |
---|---|
IAudioControl#registerFocusListener | 向 AudioControl HAL 註冊IFocusListener 的實例。此偵聽器使 HAL 能夠請求和放棄音頻焦點。 HAl 應提供一個ICloseHandle 實例,供 Android 用於取消註冊偵聽器。 |
IAudioControl#onAudioFocusChange | 通過IFocusListener 通知 HAL 狀態更改以聚焦 HAL 發出的請求。這包括對初始焦點請求的響應。 |
IFocusListener#requestAudioFocus | 代表 HAL 請求針對指定用途、區域 ID 和焦點增益類型的焦點。 |
IFocusListener#abandonAudioFocus | 放棄對指定用法和區域 ID 的現有 HAL 焦點請求。 |
HAL 可以同時有多個焦點請求,但僅限於每次使用和區域 ID 配對一個請求。請注意,Android 假定 HAL 會在發出請求後立即開始播放聲音以供使用,並將繼續這樣做直到它放棄焦點。
除了registerFocusListener
之外,這些請求都是oneway
的,以確保 Android 在處理焦點請求時不會延遲 HAL。 HAL不應該在播放安全關鍵的聲音之前等待獲得焦點。 HAL 可以選擇通過IAudioControl#onAudioFocusChange
監聽和響應音頻焦點的變化,儘管在適當的時候鼓勵這樣做。