O framework de sistemas de acesso condicional de mídia (Media CAS) fornece APIs padrão para ativar serviços de acesso condicional (CA) em uma variedade de hardware de TV digital, incluindo sistemas de cabo, satélite, terrestre e IPTV. O framework funciona com o framework de entrada do Android TV e o framework de sintonizador do Android TV, fornecendo APIs Java invocadas pelo app do serviço de entrada de TV (TIS).
Os principais objetivos do Media CAS são os seguintes.
- Fornecer uma API Java pública e um framework de plug-ins nativo que podem ser usados por desenvolvedores terceirizados e OEMs para oferecer suporte ao CAS para TV aberta no Android.
- Fornecer um framework de CAS no Android que permita que os OEMs de ATV interajam com vários fornecedores de CAS de maneira consistente.
- Oferecer suporte a vários fornecedores de CAS terceirizados usando plug-ins nativos. Os plug-ins de CAS podem usar protocolos de rede específicos do fornecedor, formatos de mensagem de gerenciamento de direitos (EMM)/mensagem de controle de direitos (ECM) e decodificadores.
- Oferecer suporte à segurança de hardware, como escadas de chaves.
- Oferecer suporte a ambientes de execução confiáveis (TEEs), como o TrustZone.
Configurações aceitas
Configuração do sintonizador de hardware
Se o hardware for responsável pela demultiplexação do fluxo de transporte MPEG e decodificação, o framework do sintonizador vai fornecer dados de informações específicas do programa (PSI) de acesso condicional ao app TIS para fazer a interface com sintonizadores de TV baseados em hardware.
Os dados de PSI de acesso condicional incluem descritores de CA, ECMs e EMMs. Essas estruturas permitem que o plug-in de CAS receba as chaves necessárias para descriptografar os streams de conteúdo.
Figura 1. Configuração do sintonizador de hardware
A configuração de hardware pode ter uma camada TEE, como o TrustZone, ilustrado na Figura 1. Se não houver uma camada TEE, um plug-in de cliente CAS poderá se comunicar com os serviços de escada de chaves de hardware fornecidos pela plataforma. Devido a variações específicas do fornecedor dessas interfaces, o Media CAS não as padroniza.
Configuração de software
Antes do Android 11, o framework do Media CAS ainda podia ser usado para processar conteúdo baseado em software, como IPTV de multicast/unicast IP. O app TIS é responsável por instanciar e provisionar corretamente o objeto Java do Media CAS.
O app pode usar o MediaExtractor ou outros analisadores MPEG2-TS para extrair dados de PSI relacionados à CA, como descritores de CA, ECMs e EMMs. Se o app usar o framework MediaExtractor, ele poderá delegar o gerenciamento da sessão CAS, como abrir uma sessão e processar EMM/ECM, para o framework MediaExtractor. O MediaExtractor configura a sessão CAS usando a API nativa diretamente.
Caso contrário, o app é responsável por extrair os dados de PSI relacionados à CA e configurar a sessão CAS usando as APIs Java do Media CAS (por exemplo, quando o app usa o próprio analisador MPEG2-TS).
Figura 2. Configuração de entrada de IPTV, CAS e decodificador usando o framework MediaExtractor
No cenário do extrator de software, o extrator exige um objeto de decodificador de software ou hardware para cada faixa codificada, independentemente de a faixa exigir decodificadores seguros. Isso ocorre devido ao seguinte:
- Se a faixa não exigir decodificação segura, o extrator vai decodificar a unidade de acesso para limpar buffers e extrair amostras como se fosse de um stream limpo. Dessa forma, o
MediaCodecnão precisa estar envolvido na decodificação. Se a faixa exigir decodificação segura, o extrator ainda poderá precisar de um decodificador. Isso acontece quando o stream de transporte é codificado no nível do pacote de transporte, em que o cabeçalho do stream elementar em pacotes (PES) é codificado. O extrator precisa acessar o cabeçalho PES para transmitir determinadas informações (por exemplo, o carimbo de data/hora de apresentação).
O decodificador não é usado pelo extrator se o stream de transporte for codificado no nível do pacote PES, em que o cabeçalho PES é deixado limpo. No entanto, não é possível confirmar quando a codificação acontece até que o pacote codificado real chegue. Para simplificar, suponha que um decodificador seja usado se a faixa for determinada como codificada com base na tabela de mapeamento de programas (PMT).
Limitações da configuração de software
Quando a faixa exige decodificação segura, o decodificador precisa ser cauteloso ao permitir uma operação de decodificação em buffers limpos. Como a decodificação de áudio não segura é necessária, se a decodificação de vídeo exigir decodificadores seguros, ela deverá ser codificada em uma sessão diferente do áudio. O ECM da sessão precisa sinalizar ao plug-in que um decodificador seguro é necessário.
Como alternativa, o plug-in precisa ser capaz de vincular uma chave à política de segurança de maneira confiável. Caso contrário, o app poderá receber frames de vídeo com o decodificador de áudio.
Mesmo quando a sessão exige um decodificador seguro, ela pode ser solicitada a gerar uma pequena quantidade de dados para limpar buffers pelo extrator para processar o cabeçalho PES. Para impedir que um app malicioso faça com que o plug-in retorne toda a unidade de acesso, o plug-in precisa analisar o payload de transporte para garantir que ele comece com um cabeçalho PES do tipo de stream apropriado. Caso contrário, o plug-in deverá negar a solicitação.
Sequência de ajuste de CA
Ao sintonizar um novo canal, o módulo TIS se registra para receber descritores de CA, ECMs e EMMs do framework do sintonizador PSI. Um descritor de CA contém o ID do sistema de CA, que identifica exclusivamente um fornecedor de CA específico e outros dados específicos do fornecedor. O TIS consulta o Media CAS para determinar se existe um plug-in de CAS que possa processar o descritor de CA.
Figura 3. Ajuste do conteúdo do CAS
Se o ID do sistema de CA for compatível, uma instância do Media CAS será criada e os dados particulares do fornecedor do descritor de CA serão fornecidos ao plug-in. Em seguida, novas sessões são abertas no Media CAS para processar os streams de áudio e vídeo. As sessões recém-abertas recebem ECMs e EMMs para o plug-in.
Fluxo de plug-in de CAS de amostra
O TIS entrega ECMs ao plug-in de CAS usando APIs do Media CAS. Um ECM contém a palavra de controle criptografada, que precisa ser descriptografada usando informações de um EMM. O plug-in de CAS determina como adquirir um EMM para o recurso com base em informações específicas do fornecedor no descritor de CA, que é fornecido pelo método setPrivateData().
Os EMMs podem ser entregues na banda no stream de conteúdo ou fora da banda usando uma solicitação de rede iniciada pelo plug-in de CA. O TIS usa o método processEMM() para entregar qualquer EMM na banda ao plug-in de CA.
Se uma solicitação de rede for necessária para receber um EMM, o plug-in de CA será responsável por realizar a transação de rede com um servidor de licenças.
Figura 4. Exemplo de plug-in de CAS para processamento de EMM e ECM
Quando o EMM é recebido, o plug-in de CA o analisa para receber a chave criptografada para descriptografar a palavra de controle. A chave EMM criptografada e a palavra de controle criptografada podem ser carregadas em uma escada de chaves ou ambiente confiável para realizar a descriptografia da palavra de controle e a decodificação subsequente do stream de conteúdo.
API Java do Media CAS
A API Java do Media CAS contém os seguintes métodos.
Listar todos os plug-ins de CA disponíveis no dispositivo.
class MediaCas.PluginDescriptor { public String getName(); public int getSystemId(); } static PluginDescriptor[] enumeratePlugins();Construir uma instância do Media CAS para o sistema de CA especificado. Isso significa que o framework do Media CAS pode processar vários sistemas de CAS simultaneamente.
MediaCas(int CA_system_id); MediaCas(@NonNull Context context, int casSystemId, @Nullable String tvInputServiceSessionId, @PriorityHintUseCaseType int priorityHint);Registrar um listener de eventos e permitir que o app especifique um handler cujo looper seja usado.
interface MediaCas.EventListener { void onEvent(MediaCas, int event, int arg, byte[] data); void onSessionEvent(@NonNull MediaCas mediaCas, @NonNull Session session, int event, int arg, @Nullable byte[] data); void onPluginStatusUpdate(@NonNull MediaCas mediaCas, @PluginStatus int status, int arg); void onResourceLost(@NonNull MediaCas mediaCas); } void setEventListener(MediaCas.EventListener listener, Handler handler);Enviar os dados particulares do sistema de CA. Os dados particulares podem vir do descritor de CA, da tabela de acesso condicional ou de fontes fora da banda. Isso não está associado a uma sessão específica.
void setPrivateData(@NonNull byte[] data);Processar um pacote EMM.
void processEmm(@NonNull byte[] data, int offset, int length);Enviar um evento para um sistema de CA. O formato do evento é específico do esquema e opaco para o framework.
void sendEvent(int event, int arg, @Nullable byte[] data);Iniciar uma operação de provisionamento do tipo especificado para um sistema de CA. Quando um dispositivo se inscreve em um serviço de TV paga pela primeira vez, ele precisa ser provisionado no servidor CAS. Fornecer um conjunto de parâmetros relacionados ao dispositivo para provisionamento.
void provision(String provisionString);Acionar uma atualização de direitos. Quando um usuário se inscreve em um novo canal (por exemplo, respondendo a um anúncio ou adicionando um canal no guia da programação (EPG)), o app precisa informar aos clientes de CA para atualizar as chaves de direito de acesso.
void refreshEntitlements(int refreshType);Fechar o objeto do Media CAS.
void close();Abrir uma sessão.
Session openSession(); Session openSession(@SessionUsage int sessionUsage, @ScramblingMode int scramblingMode);Fechar uma sessão aberta anteriormente.
void Session#close();Fornecer os dados particulares da CA de um descritor de CA no PMT, que pode ser da seção de informações do programa ou de informações ES, para uma sessão CAS.
void Session#setPrivateData(@NonNull byte[] sessionId, @NonNull byte[] data);Processar um pacote ECM para uma sessão.
void Session#processEcm(@NonNull byte[] data, int offset, int length);Receber o ID da sessão.
byte[] Session#getSessionId();Enviar um evento de sessão para um sistema de CA. O formato do evento é específico do esquema e opaco para o framework.
void Session#sendSessionEvent(int event, int arg, @Nullable byte[] data);