A AAudio é uma API de áudio introduzida na versão 8.0 do Android. A versão do Android 8.1 tem melhorias para reduzir a latência quando usada em conjunto com uma HAL e um driver compatível com MMAP. Este documento descreve a camada de abstração de hardware (HAL, na sigla em inglês) e as mudanças de driver necessárias para oferecer suporte ao recurso MMAP do AAudio no Android.
O suporte ao MMAP do AAudio requer:
- informando os recursos do MMAP do HAL
- Implementar novas funções no HAL
- Implementação opcional de um ioctl() personalizado para o buffer de modo EXCLUSIVO
- fornecendo um caminho de dados de hardware adicional
- definir propriedades do sistema que ativam o recurso MMAP
Arquitetura da AAudio
A AAudio é uma nova API C nativa que oferece uma alternativa ao OpenSL ES. Ele usa um padrão de design do Builder para criar streams de áudio.
A AAudio fornece um caminho de dados de baixa latência. No modo EXCLUSIVE, o recurso permite que o código do aplicativo cliente seja gravado diretamente em um buffer mapeado em memória que é compartilhado com o driver ALSA. No modo COMPARTILHADO, o buffer MMAP é usado por um mixer em execução no AudioServer. No modo EXCLUSIVE, a latência é significativamente menor porque os dados ignoram o mixer.
No modo EXCLUSIVO, o serviço solicita o buffer MMAP do HAL e gerencia os recursos. O buffer MMAP está em execução no modo NOIRQ, portanto, não há contadores de leitura/gravação compartilhados para gerenciar o acesso ao buffer. Em vez disso, o cliente mantém um modelo de temporização do hardware e prevê quando o buffer será lido.
No diagrama abaixo, podemos ver os dados de modulação por pulso (PCM) fluindo pelo FIFO do MMAP para o driver ALSA. Os carimbos de data/hora são solicitados periodicamente pelo serviço AAudio e, em seguida, transmitidos para o modelo de temporização do cliente por uma fila de mensagens atômicas.

No modo COMPARTILHADO, um modelo de temporização também é usado, mas ele fica no AAudioService.
Para a captura de áudio, um modelo semelhante é usado, mas os dados PCM fluem na direção oposta.
Mudanças na HAL
Para tinyALSA, consulte:
external/tinyalsa/include/tinyalsa/asoundlib.h external/tinyalsa/include/tinyalsa/pcm.c
int pcm_start(struct pcm *pcm); int pcm_stop(struct pcm *pcm); int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset, unsigned int *frames); int pcm_get_poll_fd(struct pcm *pcm); int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames); int pcm_mmap_get_hw_ptr(struct pcm* pcm, unsigned int *hw_ptr, struct timespec *tstamp);
Para o HAL legado, consulte:
hardware/libhardware/include/hardware/audio.h hardware/qcom/audio/hal/audio_hw.c
int start(const struct audio_stream_out* stream); int stop(const struct audio_stream_out* stream); int create_mmap_buffer(const struct audio_stream_out *stream, int32_t min_size_frames, struct audio_mmap_buffer_info *info); int get_mmap_position(const struct audio_stream_out *stream, struct audio_mmap_position *position);
Para a HAL de áudio HIDL:
hardware/interfaces/audio/2.0/IStream.hal hardware/interfaces/audio/2.0/types.hal hardware/interfaces/audio/2.0/default/Stream.h
start() generates (Result retval); stop() generates (Result retval) ; createMmapBuffer(int32_t minSizeFrames) generates (Result retval, MmapBufferInfo info); getMmapPosition() generates (Result retval, MmapPosition position);
Informar suporte ao MMAP
A propriedade do sistema "aaudio.mmap_policy" precisa ser definida como 2 (AAUDIO_POLICY_AUTO) para que a estrutura de áudio saiba que o modo MMAP é compatível com a HAL de áudio. Consulte "Como ativar o caminho de dados do AAudio MMAP" abaixo.
O arquivo audio_policy_configuration.xml também precisa conter um perfil de saída e entrada específico para o modo MMAP/NO IRQ para que o gerenciador de políticas de áudio saiba qual stream abrir quando os clientes MMAP forem criados:
<mixPort name="mmap_no_irq_out" role="source" flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/> </mixPort> <mixPort name="mmap_no_irq_in" role="sink" flags="AUDIO_INPUT_FLAG_MMAP_NOIRQ"> <profile name="" format="AUDIO_FORMAT_PCM_16_BIT" samplingRates="48000" channelMasks="AUDIO_CHANNEL_IN_STEREO"/> </mixPort>
Abrir e fechar um fluxo MMAP
createMmapBuffer(int32_t minSizeFrames) generates (Result retval, MmapBufferInfo info);
O fluxo MMAP pode ser aberto e fechado chamando as funções do Tinyalsa.
Consultar a posição do MMAP
O carimbo de data/hora transmitido de volta para o modelo de temporização contém uma posição de frame e um tempo MONOTONIC em nanossegundos:
getMmapPosition() generates (Result retval, MmapPosition position);
O HAL pode receber essas informações do driver ALSA chamando uma nova função do Tinyalsa:
int pcm_mmap_get_hw_ptr(struct pcm* pcm, unsigned int *hw_ptr, struct timespec *tstamp);
Descritores de arquivo para memória compartilhada
O caminho de dados MMAP da AAudio usa uma região de memória compartilhada entre o hardware e o serviço de áudio. A memória compartilhada é referenciada usando um descritor de arquivo gerado pelo driver ALSA.
Mudanças no kernel
Se o descritor de arquivo estiver diretamente associado a um
arquivo de driver /dev/snd/
, ele poderá ser usado pelo serviço AAudio no
modo COMPARTILHADO. No entanto, o descritor não pode ser transmitido ao código do cliente para
o modo EXCLUSIVO. O descritor de arquivo /dev/snd/
forneceria um acesso muito
amplo ao cliente, por isso é bloqueado pelo SELinux.
Para oferecer suporte ao modo EXCLUSIVO, é necessário converter o
descritor /dev/snd/
em um descritor de arquivo
anon_inode:dmabuf
. O SELinux permite que esse descritor de arquivo seja transmitido ao cliente. Ela
também pode ser usada pelo AAudioService.
Um descritor de arquivo anon_inode:dmabuf
pode ser gerado usando a
biblioteca de memória do Android Ion.
Para mais informações, consulte estes recursos externos:
- "The Android ION memory allocator" (O alocador de memória do ION do Android) https://lwn.net/Articles/480055/
- "Visão geral do ION do Android" https://wiki.linaro.org/BenjaminGaignard/ion
- "Integrating the ION memory allocator" https://lwn.net/Articles/565469/
Mudanças na HAL
O serviço AAudio precisa saber se esse anon_inode:dmabuf
é
compatível. Antes do Android 10.0, a única maneira de fazer isso era transmitir o tamanho do buffer
MMAP como um número negativo, por exemplo. -2048 em vez de 2048, se houver suporte. No Android 10.0 e versões mais recentes,
é possível definir a flag AUDIO_MMAP_APPLICATION_SHAREABLE
.
mmapBufferInfo |= AUDIO_MMAP_APPLICATION_SHAREABLE;
Mudanças no subsistema de áudio
A AAudio exige um caminho de dados adicional no front-end de áudio do subsistema de áudio para operar em paralelo com o caminho original do AudioFlinger. Esse caminho legado é usado para todos os outros sons do sistema e do aplicativo. Essa funcionalidade pode ser fornecida por um mixer de software em um DSP ou um mixer de hardware no SOC.
Ativar o caminho de dados MMAP do AAudio
A AAudio vai usar o caminho de dados legado do AudioFlinger se o MMAP não tiver suporte ou não conseguir abrir um stream. Portanto, o AAudio vai funcionar com um dispositivo de áudio que não oferece suporte ao caminho MMAP/NOIRQ.
Ao testar o suporte ao MMAP para AAudio, é importante saber se você está testando o caminho de dados do MMAP ou apenas o caminho de dados legado. A seguir, descobrimos como ativar ou forçar caminhos de dados específicos e como consultar o caminho usado por um stream.
Propriedades do sistema
É possível definir a política MMAP usando as propriedades do sistema:
- 1 = AAUDIO_POLICY_NEVER: use apenas o caminho legado. Não tente usar o MMAP.
- 2 = AAUDIO_POLICY_AUTO: tenta usar o MMAP. Se isso falhar ou não estiver disponível, use o caminho legado.
- 3 = AAUDIO_POLICY_ALWAYS: use apenas o caminho MMAP. Não usar o caminho legado.
Eles podem ser definidos no Makefile do dispositivo, como este:
# Enable AAudio MMAP/NOIRQ data path. # 2 is AAUDIO_POLICY_AUTO so it will try MMAP then fallback to Legacy path. PRODUCT_PROPERTY_OVERRIDES += aaudio.mmap_policy=2 # Allow EXCLUSIVE then fall back to SHARED. PRODUCT_PROPERTY_OVERRIDES += aaudio.mmap_exclusive_policy=2
Também é possível substituir esses valores depois que o dispositivo for inicializado. Reinicie o audioserver para que a mudança entre em vigor. Por exemplo, para ativar o modo AUTO para MMAP:
adb root
adb shell setprop aaudio.mmap_policy 2
adb shell killall audioserver
Há funções fornecidas em
ndk/sysroot/usr/include/aaudio/AAudioTesting.h
que permitem
substituir a política para usar o caminho MMAP:
aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);
Para descobrir se um stream está usando o caminho MMAP, chame:
bool AAudioStream_isMMapUsed(AAudioStream* stream);