Google is committed to advancing racial equity for Black communities. See how.

CAS Framework

The Media conditional access systems (Media CAS) framework provides standard APIs to enable conditional access (CA) services on a range of digital TV hardware, including digital cable, satellite, terrestrial systems, and IPTV systems. The framework works with the Android TV Input framework and Android TV Tuner framework, providing Java APIs invoked from the TV Input Service (TIS) app.

The main objectives of Media CAS are as follows.

  • Provide a public Java API and native plugin framework that can be used by third-party developers and OEMs to support CAS for broadcast TV in Android.
  • Provide a CAS framework within Android that lets ATV OEMs interoperate with a variety of CAS vendors in a consistent manner.
  • Support multiple third-party CAS vendors using native plugins. CAS plugins might use vendor-specific network protocols, entitlement management message (EMM)/entitlement control message (ECM) formats, and descramblers.
  • Support hardware security such as key ladders.
  • Support trusted execution environments (TEEs) such as TrustZone.

Supported configurations

Hardware Tuner configuration

If the hardware is responsible for MPEG transport stream demultiplexing and descrambling, Tuner framework provides conditional access program-specific information (PSI) data to the TIS app in order to interface with hardware-based TV tuners.

Conditional access PSI data includes CA descriptors, ECMs, and EMMs. These structures enable the CAS plugin to obtain keys needed to decrypt the content streams.

Diagram of the hardware Tuner configuration.

Figure 1. Hardware Tuner configuration

The hardware configuration might have a TEE layer, such as TrustZone, which is illustrated in Figure 1. If there isn't a TEE layer, a CAS client plugin can communicate with hardware key ladder services provided by the platform. Due to vendor-specific variations of these interfaces, Media CAS doesn't standardize them.

Software configuration

Before Android 11, the Media CAS framework could still be used to process software-based content, such as IPTV from IP multicast/unicast. The TIS app is responsible for instantiating and properly provisioning the Media CAS Java object.

The app might use MediaExtractor or other MPEG2-TS parsers to extract CA-related PSI data, such as CA descriptors, ECMs, and EMMs. If the app uses the framework MediaExtractor, it can delegate the CAS session management, such as opening a session and processing EMM/ECM, to the framework MediaExtractor. MediaExtractor then configures the CAS session using the native API directly.

Otherwise, the app is responsible for extracting the CA-related PSI data and configuring the CAS session using the Media CAS Java APIs (for example, when the app uses its own MPEG2-TS parser).

Diagram of the Tuner configuration.

Figure 2. IPTV input, CAS, and descrambler configuration using the framework MediaExtractor

In the software extractor scenario, the extractor requires a software or hardware-based descrambler object for each scrambled track, regardless of whether the track calls for secure decoders. This is due to the following.

  • If the track doesn’t require secure decoding, the extractor descrambles the access unit to clear buffers and extracts samples as if from a clear stream. This way MediaCodec doesn’t need to be involved in the descrambling.
  • If the track requires secure decoding, the extractor might still need a descrambler. This happens when the transport stream is scrambled at the transport packet level, where the packetized elementary stream (PES) header is scrambled. The extractor needs to access the PES header to downstream certain information (for example, the presentation timestamp).

    The descrambler isn't used by the extractor if the transport stream is scrambled at the PES packet level, where the PES header is left clear. However, it's not possible to confirm when the scrambling happens until the actual scrambled packet arrives. For simplicity, assume a descrambler is used if the track is determined to be scrambled based on the program mapping table (PMT).

Limitations of software configuration

When the track requires secure decoding, the descrambler needs to be cautious when letting a descramble operation into clear buffers. Because insecure audio decoding is required, if video decoding requires secure decoders, it should be scrambled on a different session from audio. The ECM for the session must signal to the plugin that a secure decoder is required.

Alternatively, the plugin must be able to reliably tie a key to its security policy. Otherwise, the app can easily get video frames with the audio descrambler.

Even when the session requires a secure decoder, it might be asked to output a small amount of data to clear buffers by the extractor to process the PES header. To prevent a malicious app from making the plugin return the entire access unit, the plugin needs to parse the transport payload to ensure that the payload starts with a PES header of the appropriate stream type. Otherwise, the plugin should deny the request.

CA tuning sequence

When tuning to a new channel, the TIS module registers to receive CA descriptors, ECMs, and EMMs from the PSI Tuner framework. A CA descriptor contains the CA system ID, which uniquely identifies a specific CA vendor and other vendor-specific data. TIS queries the Media CAS to determine if a CAS plugin exists that can handle the CA descriptor.

Diagram of tuning CAS content.

Figure 3. Tuning CAS content

If the CA system ID is supported, an instance of the Media CAS is created and the vendor private data from the CA descriptor is provided to the plugin. Then, new sessions are opened in Media CAS to handle the audio and video streams. The newly opened sessions receive ECMs and EMMs for the plugin.

Sample CAS plugin flow

TIS delivers ECMs to the CAS plugin using Media CAS APIs. An ECM contains the encrypted control word, which needs to be decrypted using information from an EMM. The CAS plugin determines how to acquire an EMM for the asset based on vendor-specific information in the CA descriptor, which is provided by the setPrivateData() method.

EMMs might be delivered in band in the content stream or out of band using a network request initiated by the CA plugin. TIS uses the processEMM() method to deliver any in band EMMs to the CA plugin.

If a network request is required to obtain an EMM, the the CA plugin is responsible for performing the network transaction with a license server.

Diagram of an example CAS.

Figure 4. Example CAS plugin for EMM and ECM processing

When the EMM is received, the CA plugin parses it to obtain the encrypted key to decrypt the control word. The encrypted EMM key and encrypted control word might be loaded into a key ladder or trusted environment to perform the control word decryption and subsequent descrambling of the content stream.

Media CAS Java API

The Media CAS Java API contains the following methods.

  • List all available CA plugins on the device.

    class MediaCas.PluginDescriptor {
      public String getName();
      public int getSystemId();
    }
    static PluginDescriptor[] enumeratePlugins();
    
  • Construct a Media CAS instance for the specified CA system. This means that the Media CAS framework can handle multiple CAS systems simultaneously.

    MediaCas(int CA_system_id);
    MediaCas(@NonNull Context context, int casSystemId,
             @Nullable String tvInputServiceSessionId,
             @PriorityHintUseCaseType int priorityHint);
    
  • Register an event listener and allow the app to specify a handler whose looper is used.

    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);
    
  • Send the private data for the CA system. The private data can come from the CA descriptor, the conditional access table, or out of band sources. This isn't associated with a particular session.

    void setPrivateData(@NonNull byte[] data);
    
  • Process an EMM packet.

    void processEmm(@NonNull byte[] data, int offset, int length);
    
  • Send an event to a CA system. The format of the event is specific to the scheme and opaque to the framework.

    void sendEvent(int event, int arg, @Nullable byte[] data);
    
  • Initiate a provisioning operation of the specified type for a CA system. When a device signs up for a pay TV service for the first time, it needs to get provisioned to the CAS server first. Provide a set of related parameters to the device for provisioning.

    void provision(String provisionString);
    
  • Trigger a refresh of entitlements. When a user subscribes to a new channel (for example, by responding to an advertisement or by adding a channel on the electronic program guide (EPG)), the app should be able to tell the CA clients to refresh entitlement keys.

    void refreshEntitlements(int refreshType);
    
  • Close the Media CAS object.

    void close();
    
  • Open a session.

    Session openSession();
    Session openSession(@SessionUsage int sessionUsage, @ScramblingMode int scramblingMode);
    
  • Close a previously opened session.

    void Session#close();
    
  • Provide the CA private data from a CA descriptor in the PMT, which can be from the program info or ES info section, to a CAS session.

    void Session#setPrivateData(@NonNull byte[] sessionId, @NonNull byte[] data);
    
  • Process an ECM packet for a session.

    void Session#processEcm(@NonNull byte[] data, int offset, int length);
    
  • Get the session ID.

    byte[] Session#getSessionId();
    
  • Send a session event to a CA system. The format of the event is specific to the scheme and is opaque to the framework.

    void Session#sendSessionEvent(int event, int arg, @Nullable byte[] data);