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:
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
:
- 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. - 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
. - Faz chamadas IPC do Binder para o extrator para determinar o tipo de dados no arquivo (por exemplo, dados MP3 ou H.264).
- Faz uma chamada para o processo
mediacodec
para criar codecs do tipo necessário e recebe interfaces do Binder para esses codecs. - 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.