音訊焦點

在開始邏輯串流之前,應用程式應使用與邏輯串流相同的音訊屬性,要求取得音訊焦點。雖然建議您傳送這類焦點要求,但系統不會強制執行。有些應用程式可能會明確略過傳送要求,以便執行特定行為 (例如在通話期間刻意播放音訊)。

因此,您應將焦點視為間接控制及消除播放衝突的方式,而非主要的音訊控制機制;車輛不應依賴焦點系統來運作音訊子系統。

焦點互動

為滿足 AAOS 的需求,系統會根據要求的 CarAudioContext 與目前焦點持有者之間預先定義的互動,處理音訊焦點要求。互動有三種類型:排他、拒絕和並行。

獨家互動

在專屬互動中,一次只能有一個應用程式保留焦點。因此,當現有焦點持有者失去焦點時,傳入的焦點要求會獲得焦點。舉例來說,如果使用者在現有應用程式播放音樂時啟動新的音樂應用程式,就會發生這種情況。由於兩者都正在播放媒體,因此一次只能允許一個應用程式保留焦點。因此,新啟動的應用程式焦點要求會傳回 AUDIOFOCUS_REQUEST_GRANTED,而目前正在播放音樂的應用程式會收到焦點變更事件,並顯示與所提出要求類型相對應的遺失狀態。這是 Android 最常見的互動模式。

拒絕互動

在拒絕互動中,系統一律會拒絕傳入要求。嘗試在通話進行中播放音樂,就是互動遭拒的例子。在這種情況下,如果撥號程式目前正在保留通話的音訊焦點,而第二個應用程式要求播放音樂,音樂應用程式會收到 AUDIOFOCUS_REQUEST_FAILED 做為回應。由於焦點要求遭到拒絕,因此不會將任何類型的焦點遺失事件調度至目前的焦點持有者。

並行互動

AAOS 最特別的功能就是並行互動。這樣一來,要求車內音訊焦點的應用程式就能與其他應用程式同時保留焦點。如要同時進行互動,必須符合下列條件:以下是:

如果符合這些條件,焦點要求會傳回 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 自動處理。系統會根據焦點要求的相關 UserIdUID,決定音訊區域。詳情請參閱「音訊路由」。

同時要求多個區域的音訊

如果應用程式想在多個區域中同時播放音訊,就必須在套件中加入 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 監聽及回應音訊焦點的變更,但建議在適當情況下才這麼做。