在開始邏輯串流之前,應用程式應使用與邏輯串流相同的音訊屬性,要求取得音訊焦點。雖然建議您傳送這類焦點要求,但系統不會強制執行。有些應用程式可能會明確略過傳送要求,以便執行特定行為 (例如在通話期間刻意播放音訊)。
因此,您應將焦點視為間接控制及消除播放衝突的方式,而非主要的音訊控制機制;車輛不應依賴焦點系統來運作音訊子系統。
焦點互動
為滿足 AAOS 的需求,系統會根據要求的 CarAudioContext
與目前焦點持有者之間預先定義的互動,處理音訊焦點要求。互動有三種類型:排他、拒絕和並行。
獨家互動
在專屬互動中,一次只能有一個應用程式保留焦點。因此,當現有焦點持有者失去焦點時,傳入的焦點要求會獲得焦點。舉例來說,如果使用者在現有應用程式播放音樂時啟動新的音樂應用程式,就會發生這種情況。由於兩者都正在播放媒體,因此一次只能允許一個應用程式保留焦點。因此,新啟動的應用程式焦點要求會傳回 AUDIOFOCUS_REQUEST_GRANTED
,而目前正在播放音樂的應用程式會收到焦點變更事件,並顯示與所提出要求類型相對應的遺失狀態。這是 Android 最常見的互動模式。
拒絕互動
在拒絕互動中,系統一律會拒絕傳入要求。嘗試在通話進行中播放音樂,就是互動遭拒的例子。在這種情況下,如果撥號程式目前正在保留通話的音訊焦點,而第二個應用程式要求播放音樂,音樂應用程式會收到 AUDIOFOCUS_REQUEST_FAILED
做為回應。由於焦點要求遭到拒絕,因此不會將任何類型的焦點遺失事件調度至目前的焦點持有者。
並行互動
AAOS 最特別的功能就是並行互動。這樣一來,要求車內音訊焦點的應用程式就能與其他應用程式同時保留焦點。如要同時進行互動,必須符合下列條件:以下是:
- 傳入的焦點要求必須要求
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
。 - 目前的焦點持有者並未
setPauseWhenDucked(true)
。 - 目前的焦點持有者未選擇接收 duck 事件。
如果符合這些條件,焦點要求會傳回 AUDIOFOCUS_REQUEST_GRANTED
,而目前的焦點持有者則不會變更焦點。不過,如果目前的焦點持有者選擇接收 duck 事件,或在 duck 時暫停,則目前的焦點持有者會失去焦點,就像使用專屬互動一樣。
處理並行串流
雖然並行互動有許多實用的應用程式,但原始設備製造商必須在硬體層級處理輸出裝置的混音和靜音。因此,強烈建議您只將 CarAudioContext
路由至與 CarAudioContext
無法同時播放的相同輸出裝置。透過為並行串流提供個別輸出裝置,可讓 HAL 在混合串流之前先將其中一個串流靜音,或將實體串流路由至車輛中的不同喇叭。如果邏輯串流在 Android 中混合,則系統不會變更其增益,並且會以相同實體串流的一部分傳送。
舉例來說,當導航和媒體同時傳送時,媒體串流的增益值可能會暫時降低 (降低音量),讓使用者更清楚聽見導航指示。或者,您也可以將導航串流傳送至駕駛員側喇叭,同時在車內其他地方繼續播放媒體。
互動矩陣
下表顯示 CarAudioService
定義的互動矩陣。資料列代表目前焦點容器的 CarAudioContext
,而資料欄則代表傳入請求的 CarAudioContext
。
舉例來說,如果音樂媒體應用程式目前持有焦點,而導覽應用程式要求焦點,矩陣會顯示這兩項互動可同時播放,前提是符合同時互動的其他條件。
由於同時發生互動,因此可能會有多個焦點持有者。在這種情況下,系統會比較傳入的焦點要求與目前的焦點持有者,然後決定要套用哪種互動。在這種情況下,最保守的互動會勝出 (先拒絕,再排他,最後是並行)。
在下表中,我們提供 CarAudioContext 與傳入焦點要求 (資料欄) 之間的焦點互動,以及現有焦點持有者的上下文 (資料列)。每個儲存格代表兩個情境的預期互動類型。
圖 1. 音訊焦點互動
通話期間的導航
在 Android 11 中,我們推出了新的使用者設定,讓使用者變更導覽和通話之間的互動行為。設定後,android.car.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL
會將傳入的 NAVIGATION
焦點要求與目前 CALL
焦點持有者之間的互動,從「同時」變更為「拒絕」。因此,如果使用者不希望導航指示干擾通話,可以啟用這項設定。這項設定會保留給使用者,且可動態設定,讓後續焦點要求遵循新的設定值。
可延遲的音訊焦點
在 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 屬於選用,但我們強烈建議您使用這些 API,讓外部音效在 Android 生態系統中發揮更大作用,並提供更流暢的使用者體驗。
請注意,HAL 仍須負責針對哪些聲音應獲得優先順序做出最終呼叫。因此,無論 HAL 是否已獲得音訊焦點,都應播放緊急和安全性重要音效,且即使 HAL 失去音訊焦點,也應繼續適當播放。任何法規規定的聲音也是如此。
同樣地,當播放緊急或安全攸關的音效時,HAL 仍應主動將 Android 串流靜音,以確保音效播放清楚。
AudioControl@2.0
AudioControl HAL 2.0 版本推出了幾個新的 API:
API | 目的 |
---|---|
IAudioControl#registerFocusListener |
將 IFocusListener 例項註冊至 AudioControl HAL。這個事件監聽器可讓 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
監聽及回應音訊焦點的變更,但建議在適當情況下才這麼做。