汽車音響插件服務

Android 14 中的新車 OEM 插件服務允許配置一些汽車組件。特別是針對音頻,引入了三個新的插件服務,使 OEM 能夠在 AAOS 設備上靈活配置音頻管理:

  • 音訊焦點控制
  • 音訊音量和靜音控制
  • 音訊閃避控制

汽車插件服務架構

下圖概述了汽車服務及其與 OEM 汽車服務的關係。與應用程式進程和汽車服務進程類似,OEM汽車服務進程也佔用自己的進程空間。

影像

汽車服務透過尋找config_oemCarService中定義的元件來啟動 OEM 汽車服務。如果配置為空,則OEM服務不存在,不會啟動任何服務。該元件必須擴充OemCarService 。汽車音響服務必須涵蓋取得汽車音響OEM服務的API:

public final class OemCarServiceImp extends OemCarService {
    @Override
    public OemCarAudioFocusService getOemAudioFocusService();

    @Override
    public OemCarAudioDuckingService getOemAudioDuckingService();

    @Override
    public OemCarAudioVolumeService getOemAudioVolumeService();
}

例如,請參閱packages/services/Car/tests/OemCarServiceTestApp中定義的參考測試應用程式。

即使該服務是由汽車服務啟動的,它也不會自動繼承汽車音訊服務可用的權限。因此,OEM 服務所需的任何許可都應透過適當的機制取得。例如,請參閱packages/services/Car/data/etc/com.android.car.oemcarservice.testapp.xml

OEM服務架構的汽車音響服務

在 AAOS 中,汽車音訊服務管理以下操作:

  • 音訊路由
  • 音訊焦點
  • 音訊閃避
  • 音量和靜音

在 Android 14 之前,此行為很大程度上是靜態的,只能透過設定進行修改,儘管情況非常有限。 Android 14 引入了一種汽車音訊服務機制,用於與 OEM 定義的元件進行通信,該元件管理:

  • 音訊焦點
  • 音訊閃避
  • 音量和靜音

下圖顯示了汽車音訊服務和汽車 OEM 服務的簡化架構。汽車音訊服務定義了不同的掛鉤,可以呼叫汽車 OEM 音訊服務來管理音訊行為。只有當定義了相應的 OEM 汽車音訊服務組件時,才會發生後者。否則,汽車音訊服務將使用預設行為。

影像

為了確保汽車音訊服務和汽車 OEM 音訊服務始終同步,對於每次調用,汽車音訊服務都會將音訊堆疊當前狀態的所需部分傳遞給汽車 OEM 音訊服務。例如,當汽車音訊服務攔截評估音訊焦點的請求時,它將堆疊的當前狀態傳遞給汽車 OEM 音訊服務。目前狀態包括當前焦點保持者和當前焦點失去者。焦點遺失者是仍屬於堆疊一部分但暫時失去焦點的焦點請求。

汽車音訊服務必須管理汽車中的所有音訊活動。如果汽車音訊服務不管理音訊行為的某些部分,則暴露給汽車 OEM 音訊服務的資訊是不完整的。例如,如果 OEM 透過註冊自己的音訊焦點策略來覆蓋汽車服務中的音訊焦點處理,則汽車音訊服務無法向汽車 OEM 音訊服務提供完整的資訊。這可能會影響汽車 OEM 音訊服務做出決策的能力,因為它可能缺少汽車音訊服務不可見的資訊。

為了採取行動,汽車音訊服務會呼叫 OEM 汽車服務。這些呼叫是跨進程進行的,這需要進程間通訊(IPC)。 IPC 會增加每次呼叫的延遲。最大限度地減少 OEM 服務中的延遲非常重要。

由於汽車音訊服務對 OEM 服務的呼叫是阻塞的,因此 OEM 服務不應在直接 API 評估上呼叫汽車音訊服務。相反,汽車音訊服務提供必要的訊息,以便兩個進程之間的呼叫只需朝一個方向傳播。

OEM 汽車音響服務定義

OEM汽車音響聚焦服務

汽車音訊服務透過註冊音訊策略焦點偵聽器來管理來自應用程式的音訊焦點請求。汽車音訊服務有一種基於靜態互動矩陣來管理焦點行為的機制。此矩陣定義了三種不同類型的交互作用:

  • 並發互動。焦點持有者可以同時保持焦點。

  • 獨家互動。傳入的焦點請求將從目前焦點持有者取得焦點。

  • 拒絕互動。根據目前焦點持有者拒絕傳入的焦點請求。

雖然這足以滿足某些汽車用例,但它並不能滿足由於 OEM 要求而可能有所不同的所有互動需求。為此,我們引入OemCarAudioFocusService

public interface OEmCarAudioFocusService {
    OemCarAuddioFocusResults evaluateAudioFocusRequest(
        OemCarAudioFocusEvaluationRequest request);
    
    void notifyAudioFocusChange(
        List<AudioFocusEntry> holder,
        List<AudioFocusEntry> losers, int zoneId);
}

每當有需要評估的音訊焦點請求時,都會從汽車音訊服務呼叫evaluateAudioFocusRequest ,它是一個雙向 API,會阻止結果返回。該請求包含有關音訊堆疊當前狀態的資訊:

此資訊可用於與focusHolders中的當前焦點持有者和focusLosers中的當前焦點遺失者進行比較來評估newFocusRequest 。 API 應傳回結果:

class OemCarAudioFocusResult {
    int audioZoneId;
    int audioFocusEvaluationResults;
    AudioFocusEntry focusResult;
    List<AudioFocusEntry> newLosers;
    List<AudioFocusEntry> newlyBlocked;
}

其中包含有關audioFocusEvaluationResults中實際評估結果的信息,該信息指示當前請求是否已被授予、延遲或失敗。對當前焦點堆疊的任何更改都應在newLosersnewlyBlocked條目中設置,具體取決於堆疊更改的性質。

其中newLosers包含以前持有焦點但現在應該永久或暫時失去焦點的條目。永久焦點遺失者將從音訊焦點堆疊中進一步移除,而瞬態焦點遺失者將被移至目前焦點遺失者堆疊,直到它們重新獲得焦點或被原始焦點請求者放棄。無論如何,請求的焦點偵聽器將收到相應的焦點遺失。

newlyBlocked清單包含先前在焦點遺失者清單中但現在被新條目封鎖的條目。此封鎖可以是永久的或暫時的,對於永久焦點被封鎖的條目將從堆疊中刪除,並且焦點遺失將被傳送到焦點偵聽器。對於短暫的焦點丟失,該條目將保留在焦點丟失者堆疊中,但新的焦點阻止程序將添加到其阻止程序列表中,不會發送焦點丟失,因為之前在第一次阻止時發送了焦點丟失。當所有目前的阻塞器被移除時,該請求最終將被解除阻塞,或者如果焦點被放棄,它將被從堆疊中移除。

第二個 API, notifyAudioFocusChange ,是一種在每次音訊焦點請求或放棄時呼叫的單向方法。該 API 主要用於通知 OEM 服務有關焦點更改的信息,這可能會影響 OEM 汽車音訊服務的行為。

焦點評估指南

在 AAOS 中,音訊焦點用於管理音訊播放並確定應遵循哪個應用程式才能為使用者提供最佳體驗。因此,OEM 插件服務在管理音訊焦點請求時應考慮以下因素:

  • 如果沒有任何固定的高優先音訊焦點(例如電話、緊急情況或安全性),應用程式應該能夠暫時或永久獲得音訊焦點。

  • 當媒體焦點處於活動狀態時,應用程式請求:

    • 呼叫使用焦點,應該能夠同時或獨佔地接收焦點。

    • 導航使用焦點,應該能夠同時或單獨接收焦點。

    • 助理使用焦點,應該能夠同時或單獨接收焦點。

  • 當高優先級音訊焦點(例如電話、緊急警報或安全警報)應用程式處於活動狀態時,任何傳入的延遲音訊焦點請求都應根據需要授予或延遲。

雖然上述建議並不詳盡,但它們可以幫助保證請求焦點的應用程式在沒有活動的高優先級聲音時應該能夠獲得焦點。即使高優先級聲音處於活動狀態,仍應尊重延遲的焦點請求,並且一旦高優先級聲音停止,就應該能夠獲得焦點。

OEM汽車批量服務

汽車音訊服務透過監聽來自音訊系統的音量調整或直接監聽來自汽車輸入服務的音量鍵事件來管理音量鍵事件。在每種情況下,汽車音訊服務的預設行為是根據活動音訊播放器和音訊上下文優先順序清單確定要變更哪個音量組。

我們提供兩個卷優先列表。第一個列表按此順序考慮所有音訊上下文。此清單按降序排列,最高優先順序位於頂部,最低優先順序位於底部。例如,如果導航音訊和音樂音訊同時激活,則導航音量在音量鍵事件期間改變。

  1. 導航
  2. 稱呼
  3. 音樂
  4. 公告
  5. 語音控制
  6. 來電鈴聲
  7. 系統聲音
  8. 安全
  9. 警報
  10. 通知
  11. 車輛狀態
  12. 緊急狀況

為了使音量鍵事件管理不那麼複雜,汽車音訊服務有一個音訊上下文的第二優先列表:

  1. 稱呼
  2. 媒體
  3. 公告
  4. 語音控制

該列表也按降序排列。第二個清單的目的是允許透過按鍵事件更改更常見的聲音。不常見的聲音(可能持續時間較短)只能透過音訊設定 UI 進行管理。

音量的實際版本可以使用audioVolumeAdjustmentContextsVersion配置進行設定。配置可以設定為122是預設值)。

為了讓音量管理更大的靈活性,Android 14 中引入了OemCarAudioVolumeService

public interface OemCarAudioVolumeService {
    OemCarvolumeChangeInfo getSuggestedGroupForVolumeChange(
OemCarAudioVolumeRequest request, int volumeAdjustment);
}

OEM 汽車音訊音量服務有一個方法,它接受一個volumeAdjustment和一個OemCarAudioVolumeRequest

class OemCarAudioVolumeRequest {
    int audioZoneId;
    int callState;
    List<AudioAttributes> activePlaybackAttributes;
    List<AudioAttributes> duckedAttributes;
    List<CarVolumeGroupInfo> volumeGroupState;
}

請求的activePlaybackAttributes具有活動音訊屬性。 duckedAttributes是目前迴避的所有音訊屬性。 volumeGroupState具有磁碟區組的目前狀態。此請求表示音訊堆疊的目前狀態,可用於確定應變更哪個磁碟區組。結果應在OemCarVolumeChangeInfo中傳回:

class OemCarVolumeChangeInfo {
    boolean change;
    CarVolumeGroupInfo volumeGroupChanged;
}

change布林值指示是否有任何磁碟區已更改, true指示發生變更並且應更新磁碟區組。 volumeGroupChanged是應更改的實際磁碟區組。此群組應根據傳遞給 API 的原始volumeAdjustment參數進行更改。例如,如果結果表明導航音量組應該靜音,則布林值將為true ,並且傳回的音量組應該是用於導航的音量組。

OEM汽車閃避服務

汽車音訊服務透過監視音訊焦點變化並向AudioControl HAL 發送有關要迴避哪些音訊設備的訊號來管理音訊迴避。當焦點改變時,將評估所有活動焦點持有者,以確定應根據這組靜態迴避規則迴避哪些焦點:

  • 緊急聲音掩蓋了除呼叫聲音之外的所有聲音
  • 除了緊急聲音之外,安全性可以避開一切
  • 導航避開了除安全和緊急聲音之外的所有聲音
  • 通話會避開除安全、緊急和導航聲音之外的所有聲音
  • 聲音鴨子呼叫鈴聲
  • 音樂和公告應該被萬物所掩蓋

這些規則並不詳盡,原始設備製造商仍然負責根據這些準則確定如何迴避聲音。 OEM 可以根據可用要求更主動地控制這些建議。 Android 14 中引進了OemCarDuckingService

class OemCarAudioDuckingService {
List<AudioAttributes>   evaluateAttributesToDuck(
        OemCarAudioVolumeRequest request);
}

當音訊焦點變更時,汽車音訊服務會呼叫此 API。它重新使用了OEM 汽車音量服務中引入的OemCarAudioVolumeRequest ,並包含相關資訊來決定要迴避哪些屬性。將來自 API 的音訊屬性清單與目前音訊狀態進行比較:

  • 目前迴避的音訊屬性:

    • 在名單上,繼續被迴避
    • 不在清單中,迴避功能已關閉
  • 音訊屬性目前未閃避:

    • 在名單上,迴避
    • 不在清單中,迴避功能已關閉

然後,汽車音訊服務會確定音訊屬性屬於哪些音訊輸出設備,並將它們分別新增至隱藏的音訊輸出設備清單或未隱藏的音訊設備清單。這最終被發送到AudioControl HAL以在硬體層級執行所需的閃避。

下圖顯示了使用 OEM 閃避服務時針對焦點請求的音訊閃避控制的簡化序列圖:

影像

當應用程式請求透過公共音訊管理器 API管理音訊焦點時,此序列開始。該請求被轉發到汽車音訊服務以確定結果。當決定音訊焦點時,汽車音訊服務會呼叫OemCarAudioDuckingService來評估音訊閃避,以評估應閃避哪些音訊屬性。一旦從evaluateAttributesToDuck API 傳回結果,就會計算要閃避的音訊設備,最後將訊息傳送到AudioControl以將閃避應用於音訊硬體。

OEM汽車音響服務參考實施

AAOS 在packages/services/Car/tests/OemCarServiceTestApp中提供了 OEM 汽車服務的參考實現,它實現了OemCarService以及OemCarAudioFocusServiceOemCarAudioDuckingServiceOemCarAudioVolumeService 。對於後者,每個服務都使用 XML 檔案來載入靜態行為。例如, OemCarAudioFocusServiceImp會載入oem_focus_config.xml ,其中包含交互矩陣。當呼叫evaluateAudioFocusRequest時,此矩陣用於評估焦點請求。

參考測試應用程式調試

OEM 汽車服務測試應用程式是 AOSP 原始碼的一部分。 OEM 可以根據自己的需求進行更改。為了進行偵錯,請使用config_oemCarService組態來啟用測試應用程式。

<!-- This is the component name for the OEM customization service. OEM can choose to implement
this service to customize car service behavior for different policies. If OEMs choose to
implement it, they have to implement a service extending OemCarService exposed by car-lib,
and implement the required component services.
If the component name is invalid, CarService would not connect to any OEM service.
Component name can not be a third party package. It should be pre-installed -->
<string name="config_oemCarService" translatable="false">
com.android.car.oemcarservice.testapp/.OemCarServiceImpl
</string>

若要驗證 OEM 汽車服務,請使用 OEM 服務的 car service dump指令:

adb shell dumpsys car_service --oem-service

結果可能類似以下輸出:

***CarOemProxyService dump***
  mIsFeatureEnabled: true
  mIsOemServiceBound: true
  mIsOemServiceReady: true
  mIsOemServiceConnected: true
  mInitComplete: true
  OEM_CAR_SERVICE_CONNECTED_TIMEOUT_MS: 5000
  OEM_CAR_SERVICE_READY_TIMEOUT_MS: 5000
  mComponentName: com.android.car.oemcarservice.testapp/.OemCarServiceImpl

每批dump資訊中的每個布林值決定功能和服務的狀態。例如,轉儲資訊mIsOemServiceReady指定服務是否準備好使用,其中true表示已準備好, false表示尚未準備好。