A partir do Android 17 (nível 37 da API) e atualizado pelas atualizações do sistema do Google Play (módulos APEX principais), o Android apresenta decodificadores de áudio de software no processo para formatos de áudio compactados, incluindo Opus e AAC.
Ao executar decodificadores de áudio diretamente no processo do app em vez de em um processo de sistema isolado separado, os apps podem reduzir a latência de decodificação de áudio em aproximadamente 40%, diminuir a utilização da CPU e aumentar a duração da bateria durante a reprodução contínua de mídia, mensagens de voz e jogos.
Decodificação fora do processo x no processo
Historicamente, o Android executava todos os decodificadores de mídia de software da plataforma em um
daemon de sistema dedicado e isolado (mediaswcodec) para proteger apps e o
SO contra fluxos de bits de mídia malformados. No entanto, o sandbox fora do processo
introduz uma sobrecarga de desempenho mensurável:
- Latência de comunicação entre processos (IPC):cada frame de entrada de áudio enfileirado para o decodificador e cada buffer de áudio PCM decodificado retornado exige serialização de IPC do Binder entre processos e troca de contexto.
- Sobrecarga de memória compartilhada:as transferências de buffer entre processos exigem sincronização de memória compartilhada (
C2Buffer) e gerenciamento de cache. - Contenção de escalonamento:sob carga pesada da CPU, a contenção de escalonamento de linhas de execução entre o processo do app e
mediaswcodecpode causar privação de buffer e renderização lenta de áudio em pipelines de áudio de baixa latência (como DAWs, comunicação VoIP e jogos interativos).
A execução de decodificadores de mídia no processo resolve diretamente esses gargalos de desempenho:
- Elimina a latência da IPC:ignora as transações da IPC do Binder e as trocas de contexto, reduzindo a latência de decodificação de ponta a ponta em aproximadamente 40%.
- Minimiza a sobrecarga do buffer:transmite buffers de áudio diretamente no espaço de memória do app local sem exigir mapeamentos de memória compartilhada entre processos.
- Evita instabilidade de programação:executa a decodificação nas próprias linhas de execução de prioridade do app (como linhas de execução AAudio ou Oboe em tempo real), evitando interrupções de áudio e subfluxos de buffer sob carga pesada do sistema.
Segurança de memória com Rust
Historicamente, o Android isolava decodificadores de software em um processo separado (mediaswcodec) porque eles processam fluxos de bits não confiáveis de arquivos de mídia externos, o que torna exploits de corrupção de memória (como estouros de buffer) uma grande preocupação de segurança.
O Android pode mover com segurança decodificadores de software diretamente para o processo do app de chamada implementando-os em linguagens seguras para a memória, como Rust, ou isolando-os com Lightweight Fault Isolation (LFI).
Para eliminar com segurança a sobrecarga de sandbox fora do processo para AAC
(c2.android.inproc.aac.decoder), o Android oferece um decodificador implementado em
Rust puro ou protegido por estruturas de interface de função externa (FFI) do Rust seguro.
O Rust é considerado seguro para decodificação no processo pelos seguintes motivos:
- Segurança de memória em tempo de compilação:a propriedade, o empréstimo e a verificação de ciclo de vida estritos do Rust garantem que a memória não possa ser acessada depois de liberada ou alterada enquanto é compartilhada.
- Eliminação de vulnerabilidades comuns:o Rust evita completamente estouros de buffer de heap, estouros de pilha, leituras e gravações de matriz fora dos limites, erros de uso após liberação e vulnerabilidades de liberação dupla em tempo de compilação.
- Tributo zero de sandbox de tempo de execução:como a segurança de memória é comprovada matematicamente no tempo de compilação e verificada por verificação de limites, os decodificadores Rust são executados na velocidade nativa dentro do processo do app sem exigir transições de tabela de páginas ou contextos de sandbox de hardware.
Segurança de memória com isolamento de falhas leve (LFI, na sigla em inglês)
Para decodificadores de áudio C/C++ legados complexos que ainda não foram reescritos em
Rust, especificamente o decodificador Opus (c2.android.inproc.opus.decoder
com tecnologia libopus), o Android oferece segurança no processo usando
Isolamento de falhas leve (LFI, na sigla em inglês).
A LFI é considerada segura para decodificadores C/C++ pelos seguintes motivos:
- Sandbox de memória linear protegida por hardware:o LFI compila a biblioteca de codec C/C++ em código de máquina linear derivado do WebAssembly confinado a um slot de memória linear de 4 GB protegido por hardware.
- Chaves de proteção de memória do Linux (
pku): o LFI usa chaves de proteção de memória do Linux (pku/pkey_mprotect) para particionar permissões de espaço de endereço. A biblioteca isolada não pode ler nem gravar memória fora do slot de sandbox de 4 GB atribuído. - Interceptação de chamadas do sistema:nenhuma chamada do sistema operacional host (como E/S de arquivo, acesso à rede ou controle de processos) é permitida no sandbox. Qualquer
chamada de biblioteca sem suporte é encaminhada para stubs mínimos de simulação (
lfi_stubs.c). - Recuperação segura de falhas:se um fluxo de bits Opus malformado ou adversário tentar uma leitura ou gravação fora dos limites no sandbox linear, o tempo de execução do LFI vai capturar a falha com segurança no espaço do usuário e retornar um erro de decodificação
C2_CORRUPTEDlimpo sem falhar no app host.
Arquiteturas e dispositivos compatíveis com LFI
O LFI não exige instruções personalizadas de hardware da CPU e é compatível com as arquiteturas de processador ARM64 (aarch64) e x86_64:
- Dispositivos ARM64 (
aarch64):a maioria dos dispositivos móveis de consumo, incluindo smartphones (como dispositivos Pixel com SoCs Google Tensor, Qualcomm Snapdragon e MediaTek), dobráveis, tablets e unidades de infoentretenimento automotivo, funciona com processadores ARM64 e é compatível com decodificadores LFI no processo. - Dispositivos x86_64:emuladores Android executados em estações de trabalho de desenvolvedores com CPUs Intel ou AMD, bem como Chromebooks ChromeOS executando apps Android usando o ARC (Android Runtime para ChromeOS), são executados em arquiteturas x86_64 e oferecem suporte a sandbox LFI.
Decodificadores de áudio no processo disponíveis
| Formato de áudio | Tipo MIME | Nome do componente em processo | Implementação |
|---|---|---|---|
| Opus | audio/opus |
c2.android.inproc.opus.decoder |
C/C++ libopus com LFI |
| AAC | audio/mp4a-latm |
c2.android.inproc.aac.decoder |
Rust com segurança de memória (C2ApexAacDec) |
- AAC (
c2.android.inproc.aac.decoder): processa fluxos ADTS (com análise dinâmica de cabeçalho ADTS) e unidades de acesso (AUs) brutas com rastreamento preciso de carimbos de data/hora de apresentação (somente processamento de AU de frame único em caso deAAC_PACKAGING_RAW). - Opus (
c2.android.inproc.opus.decoder): decodifica pacotes de contêineres Ogg Opus ou Matroska padrão com reamostragem interna para PCM de 48 kHz.
Ativar decodificadores no processo
No momento, os decodificadores no processo não são o padrão do sistema ao chamar
MediaCodec.createDecoderByType().
A plataforma mantém uma prioridade de seleção do Codec 2.0 mais alta para decodificadores legados
fora do processo durante a verificação do ecossistema.
Para ativar um decodificador no processo hoje para reprodução ou teste de baixa latência,
instancie o codec explicitamente pelo nome do componente usando
MediaCodec.createByCodecName().
Instanciar por nome do componente
O exemplo a seguir demonstra como instanciar explicitamente o decodificador AAC no processo, voltando ao decodificador padrão se o componente no processo não estiver disponível no dispositivo:
Kotlin
val INPROC_AAC_CODEC = "c2.android.inproc.aac.decoder" fun createAudioDecoder(): MediaCodec { return try { // Attempt to opt in to the low-latency in-process AAC decoder MediaCodec.createByCodecName(INPROC_AAC_CODEC) } catch (e: IllegalArgumentException) { // Fall back to the default platform AAC decoder MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_AUDIO_AAC) } }
Java
private static final String INPROC_AAC_CODEC = "c2.android.inproc.aac.decoder"; public MediaCodec createAudioDecoder() throws IOException { try { // Attempt to opt in to the low-latency in-process AAC decoder return MediaCodec.createByCodecName(INPROC_AAC_CODEC); } catch (IllegalArgumentException e) { // Fall back to the default platform AAC decoder return MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_AUDIO_AAC); } }
Configurar o Jetpack Media3 ou o ExoPlayer
Se o app usar o Jetpack Media3 ou o ExoPlayer, crie um MediaCodecSelector personalizado para priorizar decodificadores no processo quando eles estiverem disponíveis no dispositivo:
Kotlin
class InprocPreferredMediaCodecSelector : MediaCodecSelector { override fun getDecoderInfos( mimeType: String, requiresSecureDecoder: Boolean, requiresTunnelingDecoder: Boolean ): List<MediaCodecInfo> { val defaultInfos = MediaCodecUtil.getDecoderInfos( mimeType, requiresSecureDecoder, requiresTunnelingDecoder ) val inprocNames = setOf( "c2.android.inproc.opus.decoder", "c2.android.inproc.aac.decoder" ) // Sort in-process decoders to the top of the selection list return defaultInfos.sortedByDescending { it.name in inprocNames } } }
Java
public class InprocPreferredMediaCodecSelector implements MediaCodecSelector { private static final Set<String> INPROC_NAMES = new HashSet<>(Arrays.asList( "c2.android.inproc.opus.decoder", "c2.android.inproc.aac.decoder" )); @Override public List<MediaCodecInfo> getDecoderInfos( String mimeType, boolean requiresSecureDecoder, boolean requiresTunnelingDecoder) throws MediaCodecUtil.DecoderQueryException { List<MediaCodecInfo> defaultInfos = new ArrayList<>( MediaCodecUtil.getDecoderInfos(mimeType, requiresSecureDecoder, requiresTunnelingDecoder) ); // Sort in-process decoders to the top of the selection list defaultInfos.sort((a, b) -> { boolean aInproc = INPROC_NAMES.contains(a.name); boolean bInproc = INPROC_NAMES.contains(b.name); return Boolean.compare(bInproc, aInproc); }); return defaultInfos; } }
Roteiro para decodificadores padrão do sistema
O Android está se preparando para fazer a transição desses decodificadores no processo com segurança de memória para se tornarem os decodificadores padrão do sistema para os respectivos tipos MIME de áudio em versões futuras da plataforma (começando com Opus LFI e AAC Rust no Android 18 ou em atualizações futuras do módulo Mainline).
Quando um decodificador no processo se torna o componente padrão do Codec 2.0 para o tipo MIME dele, as chamadas padrão para MediaCodec.createDecoderByType(...) são encaminhadas automaticamente pela implementação no processo, oferecendo uma latência de decodificação aproximadamente 40% menor sem exigir mudanças no código do app.