Aumento da proteção de framework de mídia

Para melhorar a segurança do dispositivo, o Android 7.0 divide o processo mediaserver monolítico em vários processos com permissões e recursos restritos apenas aos exigidos por cada processo. Essas mudanças minimizam as vulnerabilidades de segurança do framework de mídia da seguinte forma:

  • Divisão de componentes do pipeline de AV em processos sandbox específicos do app.
  • Ativar componentes de mídia atualizáveis (extratores, codecs etc.).

Essas mudanças também melhoram a segurança para os usuários finais, reduzindo significativamente a gravidade da maioria das vulnerabilidades de segurança relacionadas à mídia, mantendo os dispositivos e os dados do usuário final seguros.

Os OEMs e fornecedores de SoC precisam atualizar as mudanças de HAL e framework para torná-las compatíveis com a nova arquitetura. Especificamente, como o código do Android fornecido pelo fornecedor geralmente assume que tudo é executado no mesmo processo, os fornecedores precisam atualizar o código para transmitir identificadores nativos (native_handle) que tenham significado em todos os processos. Para uma implementação de referência de mudanças relacionadas ao aumento da proteção da mídia, consulte frameworks/av e frameworks/native.

Mudanças na arquitetura

As versões anteriores do Android usavam um único processo mediaserver monolítico com muitas permissões (acesso à câmera, acesso a áudio, acesso a drivers de vídeo, acesso a arquivos, acesso à rede etc.). O Android 7.0 divide o processo mediaserver em vários novos processos que cada um requer um conjunto muito menor de permissões:

aumento da proteção do mediaserver

Figura 1. Mudanças na arquitetura para proteção do Media Server

Essa nova arquitetura garante que, mesmo que um processo seja comprometido, o código malicioso não tenha acesso ao conjunto completo de permissões anteriormente detido por mediaserver. Os processos são restritos pelas políticas do SElinux e do seccomp.

Observação:devido a dependências do fornecedor, alguns codecs ainda são executados no mediaserver e, consequentemente, concedem mediaserver mais permissões do que o necessário. Especificamente, o Widevine Classic continua sendo executado no mediaserver para o Android 7.0.

Mudanças no MediaServer

No Android 7.0, o processo mediaserver existe para direcionar a reprodução e a gravação, por exemplo, transmitindo e sincronizando buffers entre componentes e processos. Os processos se comunicam pelo mecanismo padrão do Binder.

Em uma sessão de reprodução de arquivo local padrão, o app transmite um descritor de arquivo (FD) para mediaserver (geralmente pela API MediaPlayer Java) e o mediaserver:

  1. Envolve o FD em um objeto Binder DataSource que é transmitido para o processo de extração, que o usa para ler o arquivo usando o IPC do Binder. O mediaextractor não recebe o FD, mas faz chamadas de Binder para o mediaserver para receber os dados.
  2. Examina o arquivo, cria o extrator apropriado para o tipo de arquivo (por exemplo, MP3Extractor ou MPEG4Extractor) e retorna uma interface Binder para o extrator para o processo mediaserver.
  3. Faz chamadas IPC do Binder para o extrator para determinar o tipo de dados no arquivo (por exemplo, dados MP3 ou H.264).
  4. Faz uma chamada para o processo mediacodec para criar codecs do tipo necessário e recebe interfaces do Binder para esses codecs.
  5. Faz chamadas IPC do Binder repetidas para o extrator para ler amostras codificadas, usa o IPC do Binder para enviar dados codificados ao processo mediacodec para decodificação e recebe dados decodificados.

Em alguns casos de uso, nenhum codec está envolvido (como uma reprodução desativada em que os dados codificados são enviados diretamente para o dispositivo de saída) ou o codec pode renderizar os dados decodificados diretamente em vez de retornar um buffer de dados decodificados (reprodução de vídeo).

Mudanças no MediaCodecService

O serviço de codec é onde os codificadores e decodificadores ficam. Devido às dependências do fornecedor, nem todos os codecs estão no processo de codec ainda. No Android 7.0:

  • Os decodificadores não seguros e os codificadores de software estão no processo do codec.
  • Os decodificadores seguros e os codificadores de hardware estão no mediaserver (inalterado).

Um app (ou mediaserver) chama o processo de codec para criar um codec do tipo necessário e, em seguida, chama esse codec para transmitir dados codificados e extrair dados decodificados (para decodificar) ou para transmitir dados decodificados e extrair dados codificados (para codificar). A transferência de dados de e para codecs já usa a memória compartilhada, então esse processo não muda.

Mudanças no MediaDrmServer

O servidor DRM é usado ao reproduzir conteúdo protegido por DRM, como filmes no Google Play Filmes. Ele processa a descriptografia dos dados criptografados de maneira segura e, portanto, tem acesso ao armazenamento de certificados e chaves e a outros componentes sensíveis. Devido a dependências do fornecedor, o processo de DRM ainda não é usado em todos os casos.

Mudanças no AudioServer

O processo AudioServer hospeda componentes relacionados ao áudio, como entrada e saída de áudio, o serviço policymanager que determina o roteamento de áudio e o serviço de rádio FM. Para saber mais sobre as mudanças e as orientações de implementação de áudio, consulte Implementar áudio.

Mudanças no CameraServer

O CameraServer controla a câmera e é usado ao gravar vídeos para extrair frames da câmera e transmiti-los para mediaserver para processamento posterior. Para detalhes sobre mudanças e orientações de implementação para mudanças no CameraServer, consulte Reforço do framework da câmera.

Mudanças no ExtractorService

O serviço de extração hospeda os extratores, componentes que analisam os vários formatos de arquivo aceitos pelo framework de mídia. O serviço de extração é o menos privilegiado de todos os serviços. Ele não pode ler FDs, então faz chamadas para uma interface Binder (fornecida pelo mediaserver for de cada sessão de reprodução) para acessar arquivos.

Um app (ou mediaserver) faz uma chamada para o processo de extração para receber um IMediaExtractor, chama esse IMediaExtractor para receber IMediaSources da faixa contida no arquivo e, em seguida, chama IMediaSources para ler os dados dela.

Para transferir os dados entre processos, o app (ou mediaserver) inclui os dados na resposta-parcel como parte da transação do Binder ou usa a memória compartilhada:

  • O uso da memória compartilhada exige uma chamada extra de Binder para liberar a memória compartilhada, mas é mais rápida e usa menos energia para buffers grandes.
  • O uso de in-Parcel exige cópias extras, mas é mais rápido e usa menos energia para buffers menores que 64 KB.

Implementação

Para oferecer suporte à transferência de componentes MediaDrm e MediaCrypto para o novo processo mediadrmserver, os fornecedores precisam mudar o método de alocação de buffers seguros para permitir que os buffers sejam compartilhados entre processos.

Em versões anteriores do Android, os buffers seguros eram alocados em mediaserver por OMX::allocateBuffer e usados durante a descriptografia no mesmo processo, conforme mostrado abaixo:

Figura 2. Alocação de buffer do Android 6.0 e versões anteriores no mediaserver.

No Android 7.0, o processo de alocação de buffer foi alterado para um novo mecanismo que oferece flexibilidade e minimiza o impacto nas implementações existentes. Com as pilhas MediaDrm e MediaCrypto no novo processo mediadrmserver, os buffers são alocados de maneira diferente, e os fornecedores precisam atualizar os identificadores de buffer seguros para que eles possam ser transportados pelo binder quando MediaCodec invoca uma operação de descriptografia em MediaCrypto.

Figura 3. Alocação de buffer do Android 7.0 e versões mais recentes no mediaserver.

Usar identificadores nativos

O OMX::allocateBuffer precisa retornar um ponteiro para uma espécie native_handle, que contém descritores de arquivo (FDs) e outros dados inteiros. Uma native_handle tem todas as vantagens do uso de FDs, incluindo o suporte de vinculação para serialização/desserialização, além de permitir mais flexibilidade para fornecedores que não usam FDs.

Use native_handle_create() para alocar o identificador nativo. O código do framework assume a propriedade da estrutura native_handle alocada e é responsável por liberar recursos no processo em que o native_handle é alocado originalmente e no processo em que ele é desserializado. O framework libera identificadores nativos com native_handle_close() seguido por native_handle_delete() e serializa/deserializa o native_handle usando Parcel::writeNativeHandle()/readNativeHandle().

Os fornecedores de SoC que usam FDs para representar buffers seguros podem preencher o FD no native_handle com o FD deles. Os fornecedores que não usam FDs podem representar buffers seguros usando campos adicionais no native_buffer.

Definir local de descriptografia

Os fornecedores precisam atualizar o método de descriptografia OEMCrypto que opera no native_handle para realizar as operações específicas do fornecedor necessárias para tornar o native_handle utilizável no novo espaço de processo. As mudanças normalmente incluem atualizações nas bibliotecas OEMCrypto.

Como allocateBuffer é uma operação OMX padrão, o Android 7.0 inclui uma nova extensão OMX (OMX.google.android.index.allocateNativeHandle) para consultar esse suporte e uma chamada OMX_SetParameter que notifica a implementação OMX de que ela precisa usar identificadores nativos.