音頻路由

在 Android 10 中, car_audio_configuration.xml替換了car_volumes_groups.xmlIAudioControl.getBusForContext 。在新的配置文件中,定義了一個區域列表。每個區域都有一個或多個卷組及其關聯的設備,並且每個設備都有應該在該區域內路由的上下文。要求在每個區域內表示所有上下文。

配置音頻路由

音頻策略文件通常位於供應商分區中,代表主板的音頻硬件配置。 audio_policy_configuration.xml中引用的所有設備都必須在car_audio_configuration.xml中定義。

啟用 AAOS 路由

要使用基於 AAOS 的路由,您必須將audioUseDynamicRouting標誌設置為true

<resources>
    <bool name="audioUseDynamicRouting">true</bool>
</resources>

當為false時,路由和CarAudioService的大部分內容將被禁用,操作系統將回退到AudioService的默認行為。

主要區域

默認情況下,所有音頻都將路由到主要區域。只能有一個主要區域,在配置中由屬性isPrimary="true"指示。

示例配置

例如,一輛車可能有兩個區域——一個主要區域和一個後座娛樂系統。這樣,可能的car_audio_configuration.xml將定義如下:

<audioZoneConfiguration version="2.0">
       <zone name="primary zone" isPrimary="true">
           <volumeGroups>
               <group>
                   <device address="bus0_media_out">
                       <context context="music"/>
                       <context context="announcement"/>
                   </device>
                   <device address="bus3_call_ring_out">
                       <context context="call_ring"/>
                   </device>
                   <device address="bus6_notification_out">
                       <context context="notification"/>
                   </device>
                   <device address="bus7_system_sound_out">
                       <context context="system_sound"/>
                       <context context="emergency"/>
                       <context context="safety"/>
                       <context context="vehicle_status"/>
                   </device>
               </group>
               <group>
                   <device address="bus1_navigation_out">
                       <context context="navigation"/>
                   </device>
                   <device address="bus2_voice_command_out">
                       <context context="voice_command"/>
                   </device>
               </group>
               <group>
                   <device address="bus4_call_out">
                       <context context="call"/>
                   </device>
               </group>
               <group>
                   <device address="bus5_alarm_out">
                       <context context="alarm"/>
                   </device>
               </group>
           </volumeGroups>
       </zone>
        <zone name="rear seat zone" audioZoneId="1">
           <volumeGroups>
               <group>
                   <device address="bus100_rear_seat">
                       <context context="music"/>
                       <context context="navigation"/>
                       <context context="voice_command"/>
                       <context context="call_ring"/>
                       <context context="call"/>
                       <context context="alarm"/>
                       <context context="notification"/>
                       <context context="system_sound"/>
                       <context context="emergency"/>
                       <context context="safety"/>
                       <context context="vehicle_status"/>
                       <context context="announcement"/>
                   </device>
               </group>
           </volumeGroups>
    </zones>
</audioZoneConfiguration>

這裡主要區域已將上下文分離到不同的設備。這使 HAL 能夠使用車輛硬件在每個設備輸出上應用不同的後處理效果和混合。這些設備分為四個卷組:媒體、導航、呼叫和警報。如果系統配置為useFixedVolume ,則每個組的音量級別將傳遞到 HAL 以應用於這些設備的輸出。

對於次要區域,預期輸出是通過單個輸出設備。在此示例中,所有使用都被路由到單個設備和卷組以保持簡單。

佔用區音頻配置

在 Android 11 中, car_audio_configuraton.xml進一步擴展,引入了兩個新字段audioZoneIdoccupantZoneId 。首先, audioZoneId可用於更好地控制區域管理。另一方面, occupantZoneId可用於配置基於用戶 ID 的路由。

要使用這些新字段,需要car_audio_configuration.xml的 V2。重新訪問上面的音頻配置,但利用新字段進行佔用區域 id 和音頻區域 id 映射,沒有捲組定義的新配置可以設置為:

<audioZoneConfiguration version="2.0">
       <zone name="primary zone" isPrimary="true" occupantZoneId="0">
         ...
       </zone>
       <zone name="rear seat zone" audioZoneId="1" occupantZoneId="1">
         ...
       </zone>
    </zones>
</audioZoneConfiguration>

上面的配置定義了primary zone到occupant zone 0的映射, audioZoneId 1到occupantZoneId 1的映射。一般來說,occupant zone和audio zone之間的任何映射都可以配置,但映射必須是一對一的。以下是定義這兩個新字段的規則:

  • 主要區域的audioZoneId始終為零
  • audioZoneIdoccupantZoneId數字不能重複
  • audioZoneIdoccupantZoneId只能有一對一的映射

通過應用程序 UID 進行路由

CarAudioManager在 10 中引入了一系列隱藏的 API,以允許應用程序查詢和設置音頻區域和焦點。

int[] getAudioZoneIds();
int getZoneIdForUid(int uid);
boolean setZoneIdForUid(int zoneId, int uid);
boolean clearZoneIdForUid(int uid);

上述 API 允許第一方應用程序根據應用程序的 UID 管理音頻路由。因此,還需要音頻區域 ID 和應用程序的 UID。有了這些信息,就可以使用CarAudioManager#setZoneIdForUid API 設置音頻路由。

更改應用的區域

默認情況下,所有音頻路由到主要區域。要更新要路由到不同區域的應用程序,請使用CarAudioManager#setZoneIdForUid

// Find zone to play
int zoneId = ...

// Find application's uid
Int uid = mContext.getPackageManager()
        .getApplicationInfo(mContext.getPackageName(), 0)
        .uid;

if (mCarAudioManager.setZoneIdForUid(zoneId, info.uid)) {
    Log.d(TAG, "Zone successfully updated");
} else {
    Log.d(TAG, "Failed to change zone");
}

N注意:流和焦點不能動態切換區域。因此,必須停止播放並重新請求焦點以更改區域。

使用用戶 ID 路由

雖然應用程序的基於 UID 的路由允許對每個應用程序的音頻路由進行精細控制,但它還要求在應用程序實際請求音頻焦點和播放音頻之前定義每個應用程序的音頻路由。為了緩解此問題並進一步促進第三方應用程序無需修改即可播放音頻, CarAudioService使用汽車乘員區域和音頻區域映射來定義基於用戶 ID 的路由。這樣,當用戶登錄到乘員區時,就會通知汽車音頻服務。有了這個信號,所有音頻區域的音頻焦點管理和路由都會自動配置。

應用程序基於 UID 的路由仍然可以使用,但必須獨立於用戶 ID 路由來完成。這意味著如果定義了乘員區域到汽車音頻區域的映射,則基於 UID 的路由將被禁用,並且嘗試調用CarAudioManager#setZoneidForUid將引發錯誤。

雖然佔用區域管理簡化了音頻路由和焦點管理,但仍必須將用戶分配到佔用區域。這可以通過使用CarOccupantZoneManager#assignProfileUserToOccupantZone來完成。此 API 需要管理用戶的權限。當前的期望是 OEM 通過某種系統 UI 來管理用戶到佔用區域的分配。完成後,應用程序啟動、音頻路由、焦點管理都將為用戶自動配置。

使用 setPreferredDevice 路由

除了上述更改之外,Android 11 還提供了一個新 API 來查詢與每個區域關聯的輸出設備,即 CarAudioManager#getOutputDeviceForUsage(int zoneId, int usage)。

API 可用於查詢輸出設備的特定區域和音頻屬性使用情況。通過這種方式,第一方應用程序可以利用播放器的setPreferredDevice API 將音頻路由到不同的區域。 getOutputDeviceForUsage API 需要PERMISSION_CAR_CONTROL_AUDIO_SETTINGS並且是一個系統 API。下面是使用setPreferredDevice API 查找特定區域的媒體設備並路由到該設備的示例。

audioZoneId = ... ;
mediaDeviceInfo = mCarAudioManager
            .getOutputDeviceForUsage(audioZoneId, AudioAttributes.USAGE_MEDIA);
…
mPlayer.setPreferredDevice(mediaDeviceInfo);