CAS 框架

媒体条件访问系统 (Media CAS) 框架提供了一些标准 API,以在一系列数字电视硬件(包括数字有线电视、卫星电视、地面系统和 IPTV 系统)上实现条件访问 (CA) 服务。该框架可与 Android TV 输入框架Android TV 调谐器框架搭配使用,但前提是必须从 TV 输入服务 (TIS) 应用调用 Java API。

Media CAS 的主要目标如下。

  • 提供一个公共 Java API 和原生插件框架,供第三方开发者和原始设备制造商 (OEM) 用来为 Android 中的广播电视提供 CAS 支持。
  • 在 Android 中提供 CAS 框架,让 ATV OEM 能以一致的方式与不同 CAS 供应商进行互操作。
  • 使用原生插件为多个第三方 CAS 供应商提供支持。CAS 插件可能会使用特定于供应商的网络协议、授权管理信息 (EMM)/授权控制信息 (ECM) 格式和解扰器。
  • 支持硬件安全服务,例如密钥梯。
  • 支持可信执行环境 (TEE),例如 TrustZone。

支持的配置

硬件调谐器配置

如果硬件负责 MPEG 传输流的多路分解和解扰,调谐器框架会向 TIS 应用提供条件访问节目特定信息 (PSI) 数据,以便与基于硬件的电视调谐器连接。

条件访问 PSI 数据包括 CA 描述符、ECM 和 EMM。利用此类结构,CAS 插件可以获得解密内容流所需的密钥。

硬件调谐器配置的示意图。

图 1. 硬件调谐器配置

硬件配置可能具有 TEE 层(例如,TrustZone),如图 1 所示。如果没有 TEE 层,CAS 客户端插件可以与平台提供的硬件密钥梯服务通信。由于这些接口的变体因供应商而异,因此 Media CAS 并未对其进行标准化处理。

软件配置

在 Android 11 之前的版本中,仍可使用 Media CAS 框架来处理基于软件的内容,例如来自 IP 多播/单播的 IPTV 内容。TIS 应用负责实例化 Media CAS Java 对象并对其进行正确配置。

该应用可能会使用 MediaExtractor 或其他 MPEG2-TS 解析器来提取与 CA 相关的 PSI 数据,例如 CA 描述符、ECM 和 EMM。如果应用使用框架 MediaExtractor,它可以将 CAS 会话管理工作(例如,打开会话和处理 EMM/ECM)分配给框架 MediaExtractor。然后,MediaExtractor 会直接使用原生 API 配置 CAS 会话。

否则,应用会负责提取与 CA 相关的 PSI 数据并使用 Media CAS Java API 配置 CAS 会话(例如,当应用使用自己的 MPEG2-TS 解析器时)。

调谐器配置的示意图。

图 2. 使用框架 MediaExtractor 时的 IPTV 输入、CAS 和解扰器配置

在软件提取器方案中,提取器需要为每个加扰音轨使用基于软件或硬件的解扰器对象(无论该音轨是否需要安全解码器)。原因如下。

  • 如果音轨不需要安全解码,提取器会将访问单元解扰到干净缓冲区中,然后提取样本(就像从未加扰内容流中提取一样)。这样,MediaCodec 就不需要参与解扰。
  • 如果音轨需要安全解码,提取器可能仍然需要解扰器。如果传输流在传输数据包级别进行加扰,且对打包的基本流 (PES) 标头进行加扰,就会发生这种情况。提取器需要访问 PES 标头,以向下游提供某些信息(如呈现时间戳)。

    如果传输流在 PES 数据包级别进行了加扰,而 PES 标头未加扰,提取器就不会使用解扰器。但是,在实际的加扰数据包到达之前,无法确认加扰的时间。简单起见,如果根据节目映射表 (PMT) 确定要对音轨进行加扰,则假设使用了解扰器。

软件配置的限制

当音轨需要安全解码时,解扰器在将解扰操作放入干净缓冲区时需要保持谨慎。因为音频需要不安全的解码,所以如果视频解码需要使用安全解码器,就应在与音频不同的会话中对视频进行加扰。会话的 ECM 必须向插件发出信号,要求使用安全解码器。

或者,插件必须能够可靠地将密钥与其安全政策绑定。否则,应用就可以通过音频解扰器轻松获取视频帧。

即使会话需要安全解码器,提取器也可能会要求它输出少量数据到干净缓冲区,以处理 PES 标头。为防止恶意应用让插件返回整个访问单元,插件需要解析传输载荷,以确保载荷以相应流类型的 PES 标头开头。否则,插件应拒绝该请求。

CA 调谐序列

调谐到新频道时,TIS 模块会进行注册,以从 PSI 调谐器框架接收 CA 描述符、ECM 和 EMM。CA 描述符包含 CA 系统 ID,该 ID 可独一无二地标识特定的 CA 供应商和其他供应商特定数据。TIS 会查询 Media CAS,以确定是否存在可处理 CA 描述符的 CAS 插件。

调谐 CAS 内容的示意图。

图 3. 调谐 CAS 内容

如果 CA 系统 ID 受支持,系统就会创建 Media CAS 的实例,并向插件提供来自 CA 描述符的供应商专有数据。 然后,系统会在 Media CAS 中打开新会话,以处理音频和视频流。新打开的会话会接收该插件的 ECM 和 EMM。

示例 CAS 插件流程

TIS 使用 Media CAS API 将 ECM 传送到 CAS 插件。ECM 包含加密的控制字,需要使用 EMM 中的信息才能解密。CAS 插件会根据 CA 描述符中的供应商特定信息(由 setPrivateData() 方法提供)确定如何为资源获取 EMM。

EMM 可能会在内容流中进行带内传送,也可能会通过 CA 插件发起的网络请求进行带外传送。TIS 使用 processEMM() 方法将任何带内 EMM 传送到 CA 插件。

如果需要发起网络请求才能获取 EMM,CA 插件将负责对许可服务器执行网络事务。

示例 CAS 的示意图。

图 4. 用于处理 EMM 和 ECM 的示例 CAS 插件

收到 EMM 后,CA 插件会对其进行解析,以获取用于解密控制字的加密密钥。加密的 EMM 密钥和加密的控制字可能会被加载到密钥梯或可信环境中,以执行控制字解密和后续的内容流解扰。

Media CAS Java API

Media CAS Java API 包含以下方法。

  • 列出设备上所有可用的 CA 插件。

    class MediaCas.PluginDescriptor {
      public String getName();
      public int getSystemId();
    }
    static PluginDescriptor[] enumeratePlugins();
    
  • 为指定的 CA 系统构建 Media CAS 实例。这意味着,Media CAS 框架可以同时处理多个 CAS 系统。

    MediaCas(int CA_system_id);
    MediaCas(@NonNull Context context, int casSystemId,
             @Nullable String tvInputServiceSessionId,
             @PriorityHintUseCaseType int priorityHint);
    
  • 注册事件监听器,并允许应用指定使用哪个处理程序的 Looper。

    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);
    
  • 发送 CA 系统的专用数据。这些专用数据可能来自 CA 描述符、条件访问表或带外来源。它们与特定会话无关。

    void setPrivateData(@NonNull byte[] data);
    
  • 处理 EMM 数据包。

    void processEmm(@NonNull byte[] data, int offset, int length);
    
  • 向 CA 系统发送事件。事件的格式因架构而异,对框架而言不透明。

    void sendEvent(int event, int arg, @Nullable byte[] data);
    
  • 为 CA 系统启动指定类型的配置操作。当设备首次注册付费电视服务时,需要先配置到 CAS 服务器。请为设备提供一组相关参数以进行配置。

    void provision(String provisionString);
    
  • 触发授权刷新。当用户订阅新频道(例如,通过响应广告或添加电子收视指南 (EPG) 中的频道)时,应用应该能够告知 CA 客户端刷新授权密钥。

    void refreshEntitlements(int refreshType);
    
  • 关闭 Media CAS 对象。

    void close();
    
  • 打开一个会话。

    Session openSession();
    Session openSession(@SessionUsage int sessionUsage, @ScramblingMode int scramblingMode);
    
  • 关闭之前打开的会话。

    void Session#close();
    
  • 向 CAS 会话提供来自 PMT 中 CA 描述符(可能位于“节目信息”或“ES 信息”部分)的 CA 专用数据。

    void Session#setPrivateData(@NonNull byte[] sessionId, @NonNull byte[] data);
    
  • 处理会话的 ECM 数据包。

    void Session#processEcm(@NonNull byte[] data, int offset, int length);
    
  • 获取会话 ID。

    byte[] Session#getSessionId();
    
  • 向 CA 系统发送会话事件。事件的格式因架构而异,对框架而言不透明。

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