Para melhorar a segurança do dispositivo, o Android 7.0 divide o processo monolítico do mediaserver
em vários processos com permissões e recursos restritos apenas aos exigidos por cada processo. Essas mudanças atenuam as vulnerabilidades de segurança da estrutura de mídia:
- Dividir os componentes do pipeline AV em processos em área restrita específicos do aplicativo.
- Habilitando componentes de mídia atualizáveis (extratores, codecs, etc.).
Essas alterações também melhoram a segurança dos usuários finais, reduzindo significativamente a gravidade da maioria das vulnerabilidades de segurança relacionadas à mídia, mantendo os dispositivos e dados do usuário final protegidos.
OEMs e fornecedores de SoC precisam atualizar seu HAL e alterações de estrutura para torná-los compatíveis com a nova arquitetura. Especificamente, como o código Android fornecido pelo fornecedor geralmente assume que tudo é executado no mesmo processo, os fornecedores devem atualizar seu código para passar identificadores nativos ( native_handle
) que tenham significado entre os processos. Para obter uma implementação de referência das mudanças relacionadas à proteção de mídia, consulte frameworks/av
e frameworks/native
.
Mudanças arquitetônicas
As versões anteriores do Android usavam um único processo de mediaserver
monolítico com muitas permissões (acesso à câmera, acesso de áudio, acesso ao driver de vídeo, acesso a arquivo, acesso à rede, etc.). O Android 7.0 divide o processo do mediaserver
em vários novos processos, cada um deles requer um conjunto muito menor de permissões:
Figura 1. Mudanças de arquitetura para endurecimento do servidor de mídia
Esta 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 detidas pelo mediaserver. Os processos são restritos pelas políticas SElinux e seccomp.
Nota: Devido às dependências do fornecedor, alguns codecs ainda são executados no mediaserver
e, conseqüentemente, concedem ao mediaserver
mais permissões do que o necessário. Especificamente, o Widevine Classic continua rodando no mediaserver
para Android 7.0.
Mudanças no MediaServer
No Android 7.0, o processo mediaserver
existe para direcionar a reprodução e gravação, por exemplo, passando e sincronizando buffers entre componentes e processos. Os processos se comunicam por meio do mecanismo Binder padrão.
Em uma sessão de reprodução de arquivo local padrão, o aplicativo passa um descritor de arquivo (FD) para mediaserver
(geralmente por meio da API MediaPlayer Java) e mediaserver
:
- Envolve o FD em um objeto Binder DataSource que é passado para o processo do extrator, que o usa para ler o arquivo usando Binder IPC. (O extrator de mídia não obtém o FD, mas, em vez disso, faz chamadas de Binder ao
mediaserver
demediaserver
para obter os dados.) - Examina o arquivo, cria o extrator apropriado para o tipo de arquivo (por exemplo, MP3Extractor ou MPEG4Extractor) e retorna uma interface Binder do extrator para o processo do
mediaserver
. - Faz chamadas Binder IPC para o extrator para determinar o tipo de dados no arquivo (por exemplo, dados MP3 ou H.264).
- Chamadas para o processo
mediacodec
para criar codecs do tipo necessário; recebe interfaces Binder para esses codecs. - Faz chamadas Binder IPC repetidas para o extrator para ler amostras codificadas, usa o Binder IPC 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 descarregada 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).
Alterações de MediaCodecService
O serviço de codec é onde vivem os codificadores e decodificadores. Devido às dependências do fornecedor, nem todos os codecs estão no processo de codec ainda. No Android 7.0:
- Decodificadores não seguros e codificadores de software vivem no processo do codec.
- Decodificadores seguros e codificadores de hardware residem no
mediaserver
(inalterado).
Um aplicativo (ou servidor de mídia) chama o processo de codec para criar um codec do tipo necessário e, em seguida, chama esse codec para passar dados codificados e recuperar dados decodificados (para decodificação) ou para passar dados decodificados e recuperar dados codificados (para codificação) . A transferência de dados de e para codecs já usa a memória compartilhada, de modo que o processo permanece inalterado.
Alterações de MediaDrmServer
O servidor DRM é usado ao reproduzir conteúdo protegido por DRM, como filmes no Google Play Filmes. Ele lida com a descriptografia dos dados criptografados de maneira segura e, como tal, tem acesso ao certificado e armazenamento de chaves e outros componentes confidenciais. Devido às dependências do fornecedor, o processo DRM ainda não é usado em todos os casos.
Alterações de 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 obter detalhes sobre mudanças de áudio e orientação de implementação, consulte Implementando áudio .
Mudanças no CameraServer
O CameraServer controla a câmera e é usado ao gravar vídeo para obter quadros de vídeo da câmera e depois passá-los ao mediaserver
para manuseio posterior. Para obter detalhes sobre mudanças e orientação de implementação para mudanças no CameraServer, consulte Camera Framework Hardening .
Alterações de ExtractorService
O serviço do extrator hospeda os extratores , componentes que analisam os vários formatos de arquivo suportados pela estrutura de mídia. O serviço extrator é o menos privilegiado de todos os serviços - ele não pode ler FDs, então, em vez disso, faz chamadas para uma interface do Binder (fornecida a ele pelo mediaserver for
cada sessão de reprodução) para acessar os arquivos.
Um aplicativo (ou mediaserver
) faz uma chamada para o processo extrator para obter um IMediaExtractor
, chama esse IMediaExtractor
para obter IMediaSources
para a faixa contida no arquivo e, em seguida, chama IMediaSources
para ler os dados deles.
Para transferir os dados entre processos, o aplicativo (ou mediaserver
) inclui os dados no reply-Parcel como parte da transação Binder ou usa memória compartilhada:
- O uso da memória compartilhada requer uma chamada extra do Binder para liberar a memória compartilhada, mas é mais rápido e usa menos energia para buffers grandes.
- Usar o In-Parcel requer cópia extra, mas é mais rápido e usa menos energia para buffers menores que 64 KB.
Implementação
Para apoiar o movimento de MediaDrm
e MediaCrypto
componentes para o novo mediadrmserver
processo, os fornecedores devem mudar o método de alocação de buffers seguras para permitir buffers para ser compartilhado entre processos.
Em versões anteriores do Android, os buffers seguros são alocados em mediaserver
por OMX::allocateBuffer
e usados durante a descriptografia no mesmo processo, conforme mostrado abaixo:
Figura 2. Android 6.0 e menor alocação de buffer no mediaserver.
No Android 7.0, o processo de alocação de buffer mudou para um novo mecanismo que fornece flexibilidade enquanto minimiza o impacto nas implementações existentes. Com MediaDrm
e MediaCrypto
pilhas no novo mediadrmserver
processo, buffers são alocados de forma diferente e os fornecedores devem atualizar as alças tampão seguras para que possam ser transportados através aglutinante quando MediaCodec
invoca uma operação de descriptografar em MediaCrypto
.
Figura 3. Android 7.0 e alocação de buffer superior no mediaserver.
Usando alças nativas
O OMX::allocateBuffer
deve retornar um ponteiro para uma estrutura native_handle
, que contém descritores de arquivo (FDs) e dados inteiros adicionais. Um native_handle
tem todas as vantagens de usar FDs, incluindo suporte a fichário existente para serialização / desserialização, enquanto permite mais flexibilidade para fornecedores que não usam FDs atualmente.
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
está originalmente alocado e no processo em que é desserializado. A estrutura libera identificadores nativos com native_handle_close()
seguido por native_handle_delete()
e serializa / desserializa 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 seu FD. Os fornecedores que não usam FDs podem representar buffers seguros usando campos adicionais no native_buffer
.
Configurando a localização descriptografia
Os fornecedores devem atualizar o método OEMCrypto decrypt que opera no native_handle
para executar quaisquer operações específicas do fornecedor necessárias para tornar o native_handle
utilizável no novo espaço de processo (as alterações geralmente 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 deve usar identificadores nativos.