CAS框架

媒體條件接收系統 (Media CAS) 框架提供標準 API,以在一系列數位電視硬體(包括數位有線、衛星、地面系統和 IPTV 系統)上啟用條件接收 (CA) 服務。該框架與Android TV 輸入框架Android TV Tuner 框架配合使用,提供從 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,它唯一地識別特定的CA供應商和其他供應商特定的資料。 TIS 查詢媒體 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金鑰和加密的控製字可以載入到金鑰梯或可信任環境中以執行控製字解密和隨後的內容流解擾。

媒體 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);
    
  • 關閉 Media 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);