音訊路由

在 Android 10 中,car_audio_configuration.xml 會取代 car_volumes_groups.xmlIAudioControl.getBusForContext。 新的設定檔中已定義區域清單。每個可用區都有一或多個 音量群組及其相關聯的裝置,且每部裝置具有 網路應位於該可用區內這裡規定所有背景資訊都必須 每個可用區內資源

設定音訊路由

音訊政策檔案 (通常位於供應商分區中) 代表 主機的音訊硬體設定以下清單中提及的所有裝置: car_audio_configuration.xml必須在 audio_policy_configuration.xml

啟用 AAOS 轉送

如要使用 AAOS 轉送,您必須設定 audioUseDynamicRouting 標記給 true

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

如果設為 false,系統會停用轉送功能和 CarAudioService 中的許多功能, OS 將改回採用 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 來套用不同的後續處理效果,並混合 利用車輛硬體完成工作裝置已排成 4 個音量群組: 媒體、導航、通話和鬧鐘如果系統設定為 useFixedVolume,系統就會傳遞各群組的音量 即可在 HAL 上執行,以套用至這些裝置的輸出內容。

次要可用區的預期輸出內容是經由單一輸出裝置。 在這個範例中,所有使用情形都會轉送至單一裝置和磁碟區群組 化繁為簡

乘客區域音訊設定

在 Android 11 中,car_audio_configuration.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>

上述設定定義了主要可用區與佔用可用區 0 的對應關係,以及 audioZoneId 1 至 occupantZoneId 1.一般而言,任何對應方法 可在乘客區域和音訊區域之間設定,但對應必須設為一對一。 以下規則定義了這兩個新欄位:

  • 主要可用區的 audioZoneId 一律為 0
  • audioZoneIdoccupantZoneId 號碼不得重複
  • audioZoneIdoccupantZoneId 只能有一個對應關係

透過應用程式 UID 轉送

我們在 10 間為 CarAudioManager 導入了一系列隱藏的 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");
}

注意:串流和焦點無法動態切換可用區。因此 必須停止播放並再次要求焦點來變更區域。

使用使用者 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);