Este artigo descreve algumas dicas e truques para depurar o áudio do Android.
Lavatório
O "tee sink" é um recurso de depuração do AudioFlinger, disponível apenas em compilações personalizadas, para reter um pequeno fragmento de áudio recente para análise posterior. Isso permite a comparação entre o que foi realmente tocado ou gravado versus o que era esperado.
Para privacidade, o tee sink é desabilitado por padrão, tanto em tempo de compilação quanto em tempo de execução. Para usar o tee sink, você precisará habilitá-lo recompilando e também definindo uma propriedade. Certifique-se de desabilitar esse recurso depois de terminar a depuração; o tee sink não deve ser deixado habilitado em builds de produção.
As instruções nesta seção são para Android 7.xe superior. Para Android 5.xe 6.x, substitua /data/misc/audioserver
por /data/misc/media
. Além disso, você deve usar um userdebug ou eng build. Se você usar uma compilação userdebug, desative a veracidade com:
adb root && adb disable-verity && adb reboot
Configuração em tempo de compilação
-
cd frameworks/av/services/audioflinger
- Editar
Configuration.h
. - Remova o comentário
#define TEE_SINK
. -
libaudioflinger.so
. -
adb root
-
adb remount
- Envie ou sincronize o novo
libaudioflinger.so
com o/system/lib
do dispositivo.
Configuração em tempo de execução
-
adb shell getprop | grep ro.debuggable
Confirme se a saída é:[ro.debuggable]: [1]
-
adb shell
-
ls -ld /data/misc/audioserver
Confirme se a saída é:
drwx------ media media ... media
Se o diretório não existir, crie-o da seguinte forma:
mkdir /data/misc/audioserver
chown media:media /data/misc/audioserver
-
echo af.tee=# > /data/local.prop
Onde o valoraf.tee
é um número descrito abaixo. -
chmod 644 /data/local.prop
-
reboot
Valores para a propriedade af.tee
O valor de af.tee
é um número entre 0 e 7, expressando a soma de vários bits, um por recurso. Veja o código em AudioFlinger::AudioFlinger()
em AudioFlinger.cpp
para uma explicação de cada bit, mas brevemente:
- 1 = entrada
- 2 = saída FastMixer
- 4 = AudioRecord e AudioTrack por faixa
Ainda não há um pouco para buffer profundo ou mixer normal, mas você pode obter resultados semelhantes usando "4".
Testar e adquirir dados
- Execute seu teste de áudio.
-
adb shell dumpsys media.audio_flinger
- Procure uma linha na saída
dumpsys
como esta:
tee copied to /data/misc/audioserver/20131010101147_2.wav
Este é um arquivo .wav PCM. - Então
adb pull
quaisquer arquivos/data/misc/audioserver/*.wav
de interesse; observe que os nomes de arquivos dump específicos da faixa não aparecem na saídadumpsys
, mas ainda são salvos em/data/misc/audioserver
após o fechamento da faixa. - Revise os arquivos de despejo quanto a questões de privacidade antes de compartilhar com outras pessoas.
Sugestões
Experimente estas ideias para obter resultados mais úteis:
- Desative os sons de toque e cliques de teclas para reduzir as interrupções na saída de teste.
- Maximize todos os volumes.
- Desative os aplicativos que emitem som ou gravam do microfone, caso não sejam do interesse do seu teste.
- Os dumps específicos da trilha são salvos apenas quando a trilha é fechada; pode ser necessário forçar o fechamento de um aplicativo para despejar seus dados específicos da faixa
- Faça o
dumpsys
imediatamente após o teste; há uma quantidade limitada de espaço de gravação disponível. - Para garantir que você não perca seus arquivos de despejo, carregue-os em seu host periodicamente. Apenas um número limitado de arquivos de despejo é preservado; dumps mais antigos são removidos depois que esse limite é atingido.
Restaurar
Conforme observado acima, o recurso tee sink não deve ser deixado ativado. Restaure sua compilação e dispositivo da seguinte maneira:
- Reverta as alterações do código-fonte para
Configuration.h
. -
libaudioflinger.so
. - Envie ou sincronize o
libaudioflinger.so
restaurado para o/system/lib
do dispositivo. -
adb shell
-
rm /data/local.prop
-
rm /data/misc/audioserver/*.wav
-
reboot
media.log
Macros ALOGx
A API de registro de linguagem Java padrão no Android SDK é android.util.Log .
A API de linguagem C correspondente no Android NDK é __android_log_print
declarada em <android/log.h>
.
Dentro da parte nativa da estrutura do Android, preferimos macros chamadas ALOGE
, ALOGW
, ALOGI
, ALOGV
, etc. Elas são declaradas em <utils/Log.h>
e, para os propósitos deste artigo, vamos nos referir a elas coletivamente como ALOGx
.
Todas essas APIs são fáceis de usar e bem compreendidas, por isso são difundidas em toda a plataforma Android. Em particular, o processo mediaserver
, que inclui o servidor de som AudioFlinger, usa extensivamente o ALOGx
.
No entanto, existem algumas limitações para ALOGx
e amigos:
- Eles são suscetíveis a "log spam": o buffer de log é um recurso compartilhado para que possa transbordar facilmente devido a entradas de log não relacionadas, resultando em informações perdidas. A variante
ALOGV
é desabilitada em tempo de compilação por padrão. Mas é claro que mesmo isso pode resultar em spam de log se estiver ativado. - As chamadas de sistema do kernel subjacentes podem ser bloqueadas, possivelmente resultando em inversão de prioridade e, consequentemente, em distúrbios e imprecisões de medição. Isso é uma preocupação especial para threads de tempo crítico, como
FastMixer
eFastCapture
. - Se um log específico for desabilitado para reduzir o spam de log, todas as informações que seriam capturadas por esse log serão perdidas. Não é possível habilitar um log específico retroativamente, depois que fica claro que o log seria interessante.
NBLOG, media.log e MediaLogService
As APIs NBLOG
e o processo media.log
associado e o serviço MediaLogService
juntos formam um sistema de log mais recente para mídia e são projetados especificamente para resolver os problemas acima. Usaremos vagamente o termo "media.log" para nos referirmos a todos os três, mas estritamente falando NBLOG
é a API de log C++, media.log
é um nome de processo Linux e MediaLogService
é um serviço de ligação do Android para examinar os logs.
Uma "linha do tempo" do media.log
é uma série de entradas de log cuja ordem relativa é preservada. Por convenção, cada thread deve usar sua própria linha do tempo.
Benefícios
Os benefícios do sistema media.log
são:
- Não envia spam para o log principal, a menos e até que seja necessário.
- Pode ser examinado mesmo quando
mediaserver
trava ou trava. - Não é bloqueante por linha do tempo.
- Oferece menos perturbação ao desempenho. (É claro que nenhuma forma de registro é completamente não intrusiva.)
Arquitetura
O diagrama abaixo mostra o relacionamento do processo mediaserver
e o processo init
, antes media.log
seja introduzido:
Pontos notáveis:
-
init
forks e execsmediaserver
. -
init
detecta a morte domediaserver
e re-forks conforme necessário. - O registro
ALOGx
não é mostrado.
O diagrama abaixo mostra o novo relacionamento dos componentes, após o media.log
ser adicionado à arquitetura:
Mudanças importantes:
- Os clientes usam a API
NBLOG
para construir entradas de log e anexá-las a um buffer circular na memória compartilhada. -
MediaLogService
pode despejar o conteúdo do buffer circular a qualquer momento. - O buffer circular é projetado de tal forma que qualquer corrupção da memória compartilhada não
MediaLogService
e ainda poderá despejar o máximo do buffer que não for afetado pela corrupção. - O buffer circular é sem bloqueio e sem bloqueio tanto para escrever novas entradas quanto para ler entradas existentes.
- Nenhuma chamada de sistema do kernel é necessária para gravar ou ler do buffer circular (além de timestamps opcionais).
Onde usar
A partir do Android 4.4, existem apenas alguns pontos de log no AudioFlinger que usam o sistema media.log
. Embora as novas APIs não sejam tão fáceis de usar quanto ALOGx
, elas também não são extremamente difíceis. Nós encorajamos você a aprender o novo sistema de registro para aquelas ocasiões em que é indispensável. Em particular, é recomendado para encadeamentos AudioFlinger que devem ser executados com frequência, periodicamente e sem bloqueio, como os FastMixer
e FastCapture
.
Como usar
Adicionar registros
Primeiro, você precisa adicionar logs ao seu código.
Nos threads FastMixer
e FastCapture
, use um código como este:
logWriter->log("string"); logWriter->logf("format", parameters); logWriter->logTimestamp();
Como essa linha do tempo do NBLog
é usada apenas pelos threads FastMixer
e FastCapture
, não há necessidade de exclusão mútua.
Em outros threads AudioFlinger, use mNBLogWriter
:
mNBLogWriter->log("string"); mNBLogWriter->logf("format", parameters); mNBLogWriter->logTimestamp();
Para encadeamentos diferentes de FastMixer
e FastCapture
, a linha do tempo do NBLog
do encadeamento pode ser usada pelo próprio encadeamento e pelas operações do binder. NBLog::Writer
não fornece nenhuma exclusão mútua implícita por linha do tempo, portanto, certifique-se de que todos os logs ocorram em um contexto em que o mutex mLock
do thread seja mantido.
Depois de adicionar os logs, reconstrua o AudioFlinger.
Cuidado: Uma linha de tempo NBLog::Writer
separada é necessária por encadeamento, para garantir a segurança do encadeamento, pois as linhas de tempo omitem mutexes por design. Se você quiser que mais de um thread use a mesma linha do tempo, você pode proteger com um mutex existente (conforme descrito acima para mLock
). Ou você pode usar o wrapper NBLog::LockedWriter
em vez de NBLog::Writer
. No entanto, isso nega um benefício principal dessa API: seu comportamento sem bloqueio.
A API completa NBLog
está em frameworks/av/include/media/nbaio/NBLog.h
.
Ativar media.log
media.log
está desabilitado por padrão. Está ativo somente quando a propriedade ro.test_harness
é 1
. Você pode habilitá-lo por:
adb root
adb shell
echo ro.test_harness=1 > /data/local.prop
chmod 644 /data/local.prop
reboot
A conexão é perdida durante a reinicialização, então:
adb shellO comando
ps media
agora mostrará dois processos:- media.log
- servidor de mídia
Anote o ID do processo do mediaserver
para mais tarde.
Exibindo as linhas do tempo
Você pode solicitar manualmente um dump de log a qualquer momento. Este comando mostra logs de todas as linhas do tempo ativas e recentes e as limpa:
dumpsys media.log
Observe que, por design, as linhas de tempo são independentes e não há recurso para mesclar linhas de tempo.
Recuperando logs após a morte do mediaserver
Agora tente matar o processo do mediaserver
: kill -9 #
, onde # é o ID do processo que você anotou anteriormente. Você deve ver um dump de media.log
no logcat
principal, mostrando todos os logs que levaram ao travamento.
dumpsys media.log