CAS 框架

媒體條件接收系統 (Media CAS) 框架提供標準 API,以在一系列數字電視硬件上啟用條件接收 (CA) 服務,包括數字有線電視、衛星、地面系統和 IPTV 系統。該框架與Android TV Input 框架Android TV Tuner 框架一起使用,提供從 TV Input Service (TIS) 應用調用的 Java API。

Media CAS的主要目標如下。

  • 提供可供第三方開發者和 OEM 使用的公共 Java API 和原生插件框架,以支持 Android 中廣播電視的 CAS。
  • 在 Android 中提供一個 CAS 框架,讓 ATV OEM 以一致的方式與各種 CAS 供應商進行互操作。
  • 使用原生插件支持多個第三方 CAS 供應商。 CAS 插件可能使用供應商特定的網絡協議、授權管理消息 (EMM)/授權控制消息 (ECM) 格式和解擾器。
  • 支持鑰匙梯等硬件安全。
  • 支持 TrustZone 等可信執行環境 (TEE)。

支持的配置

硬件調諧器配置

如果硬件負責 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 數據並使用媒體 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,它唯一標識特定 CA 供應商和其他供應商特定數據。 TIS 查詢媒體 CAS 以確定是否存在可以處理 CA 描述符的 CAS 插件。

調整 CAS 內容的圖表。

圖 3.調整 CAS 內容

如果支持 CA 系統 ID,則創建媒體 CAS 的實例,並將來自 CA 描述符的供應商私有數據提供給插件。然後,在 Media CAS 中打開新會話來處理音頻和視頻流。新打開的會話接收插件的 ECM 和 EMM。

示例 CAS 插件流程

TIS 使用媒體 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 密鑰和加密的控製字加載到密鑰階梯或可信環境中,以執行控製字解密和內容流的後續解擾。

媒體 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);
    
  • 註冊一個事件監聽器並允許應用程序指定一個使用循環器的處理程序。

    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);
    
  • 關閉媒體 CAS 對象。

    void close();
    
  • 打開一個會話。

    Session openSession();
    Session openSession(@SessionUsage int sessionUsage, @ScramblingMode int scramblingMode);
    
  • 關閉以前打開的會話。

    void Session#close();
    
  • 將來自 PMT 中 CA 描述符的 CA 私有數據(可以來自程序信息或 ES 信息部分)提供給 CAS 會話。

    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);