Roteamento de áudio

No Android 10, car_audio_configuration.xml substitui car_volumes_groups.xml e IAudioControl.getBusForContext . No novo arquivo de configuração, é definida uma lista de zonas. Cada zona tem um ou mais grupos de volumes com seus dispositivos associados e cada dispositivo tem os contextos que devem ser roteados dentro dessa zona. É necessário que todos os contextos sejam representados dentro de cada zona.

Configurando o roteamento de áudio

Os arquivos de política de áudio, que normalmente residem na partição do fornecedor, representam a configuração de hardware de áudio da placa. Todos os dispositivos referenciados em car_audio_configuration.xml devem ser definidos em audio_policy_configuration.xml .

Habilitando o roteamento AAOS

Para usar o roteamento baseado em AAOS, você deve definir o sinalizador audioUseDynamicRouting como true :

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

Quando false , o roteamento e grande parte do CarAudioService serão desabilitados e o SO retornará ao comportamento padrão do AudioService .

Zona primária

Por padrão, todo o áudio será roteado para a zona primária. Só pode haver uma zona primária, que é indicada na configuração pelo atributo isPrimary="true" .

Configuração de amostra

Por exemplo, um veículo pode ter duas zonas - uma zona primária e um sistema de entretenimento no banco traseiro. Com isso, um possível car_audio_configuration.xml seria definido da seguinte forma:

<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>

Aqui, a zona primária separou contextos para diferentes dispositivos. Isso permite que o HAL aplique diferentes efeitos de pós-processamento e mixagem na saída de cada dispositivo usando o hardware do veículo. Os dispositivos foram organizados em quatro grupos de volume: mídia, navegação, chamadas e alarmes. Se o sistema estiver configurado para useFixedVolume , os níveis de volume de cada grupo serão passados ​​para o HAL para serem aplicados à saída desses dispositivos.

Para a zona secundária, a saída esperada é através de um único dispositivo de saída. Neste exemplo, todos os usos são roteados para o único dispositivo e grupo de volumes para manter as coisas simples.

Configuração de áudio da zona de ocupantes

No Android 11, car_audio_configuraton.xml foi expandido ainda mais para apresentar dois novos campos, audioZoneId e occupantZoneId . O primeiro, audioZoneId , pode ser usado para controlar melhor o gerenciamento da zona. Por outro lado, occupantZoneId pode ser usado para configurar o roteamento baseado em ID do usuário.

Para usar esses novos campos, é necessária a V2 do car_audio_configuration.xml . Revisitando a configuração de áudio acima, mas utilizando o novo campo para identificação de zona de ocupante e mapeamento de identificação de zona de áudio, a nova configuração sem as definições de grupo de volume pode ser configurada como:

<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>

A configuração acima define um mapeamento da zona primária para a zona de ocupante 0 e audioZoneId 1 para occupantZoneId 1. Em geral, qualquer mapeamento entre a zona de ocupante e a zona de áudio pode ser configurado, mas o mapeamento deve ser um para um. Aqui estão as regras que definiram os dois novos campos:

  • O audioZoneId da zona primária é sempre zero
  • Os números audioZoneId e occupantZoneId não podem ser repetidos
  • audioZoneId e occupantZoneId só podem ter um mapeamento de um para um

Roteamento por meio de um UID de aplicativo

Uma série de APIs ocultas foi introduzida no CarAudioManager em 10 para permitir que os aplicativos consultem e definam zonas de áudio e foco.

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

As APIs acima permitiam que um aplicativo primário gerenciasse o roteamento de áudio com base no UID de um aplicativo. Como tal, o ID da zona de áudio e o UID do aplicativo também são necessários. Com essas informações em mãos, o roteamento de áudio pode ser definido usando a API CarAudioManager#setZoneIdForUid .

Alterando zonas para um aplicativo

Por padrão, todas as rotas de áudio para a zona primária. Para atualizar um aplicativo para ser roteado para uma zona diferente, use 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 Nota: Os fluxos e o foco não podem alternar zonas dinamicamente. Portanto, a reprodução deve ser interrompida e o foco solicitado novamente para alterar as zonas.

Roteamento com ID de usuário

Embora o roteamento baseado em UID de um aplicativo permita o controle preciso do roteamento de áudio de cada aplicativo, ele também exige que o roteamento de áudio para cada aplicativo seja definido antes que o aplicativo realmente solicite o foco de áudio e reproduza o áudio. Para mitigar esse problema e facilitar ainda mais a reprodução de áudio de aplicativos de terceiros sem modificação, o CarAudioService usa a zona de ocupantes do carro e o mapeamento da zona de áudio para definir o roteamento baseado no ID do usuário. Dessa forma, quando um usuário faz login na zona de ocupantes, o serviço de áudio do carro é notificado. Com este sinal, o gerenciamento e roteamento de foco de áudio são configurados automaticamente para todas as zonas de áudio.

Aplicativos O roteamento baseado em UID ainda pode ser usado, mas deve ser feito independentemente do roteamento de ID do usuário. Isso significa que, se o mapeamento de zona de ocupante para zona de áudio do carro for definido, o roteamento baseado em UID será desabilitado e a tentativa de chamar CarAudioManager#setZoneidForUid gerará um erro.

Embora o roteamento de áudio e o gerenciamento de foco tenham sido simplificados com o gerenciamento de zona de ocupante, o usuário ainda deve ser atribuído a uma zona de ocupante. Isso pode ser feito usando o CarOccupantZoneManager#assignProfileUserToOccupantZone . Esta API requer permissão para gerenciar usuários. A expectativa atual é que os OEMs gerenciem a atribuição de zona de usuário para ocupante por meio de algum tipo de interface do usuário do sistema. Feito isso, o lançamento do aplicativo, o roteamento de áudio e o gerenciamento de foco serão configurados automaticamente para o usuário.

Roteamento com setPreferredDevice

Juntamente com as alterações acima, o Android 11 também possui uma nova API para consultar dispositivos de saída associados a cada zona, CarAudioManager#getOutputDeviceForUsage(int zoneId, int usage).

A API pode ser usada para consultar um dispositivo de saída para uma zona específica e um uso de atributo de áudio. Dessa maneira, os aplicativos primários podem rotear o áudio para diferentes zonas utilizando a API setPreferredDevice do player. A API getOutputDeviceForUsage requer PERMISSION_CAR_CONTROL_AUDIO_SETTINGS e é uma API do sistema. Abaixo está um exemplo de localização do dispositivo de mídia para uma zona específica e roteamento para esse dispositivo usando a API setPreferredDevice .

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