CAS 프레임워크

미디어 CAS(Media Conditional Access System) 프레임워크는 디지털 케이블, 위성, 지상파 시스템 및 IPTV 시스템을 비롯하여 다양한 디지털 TV 하드웨어에서 조건부 액세스(CA) 서비스를 사용할 수 있도록 하는 표준 API를 제공합니다. 이 프레임워크는 Android TV 입력 프레임워크Android TV 튜너 프레임워크와 함께 작동하며 TV 입력 서비스(TIS) 앱에서 호출되는 자바 API를 제공합니다.

미디어 CAS의 주요 목표는 다음과 같습니다.

  • 타사 개발자 및 OEM이 Android에서 방송 TV용 CAS를 지원하는 데 사용할 수 있는 공개 Java API 및 네이티브 플러그인 프레임워크를 제공합니다.
  • ATV OEM이 다양한 CAS 공급업체와 일관된 방식으로 상호 운용할 수 있도록 Android 내에서 CAS 프레임워크를 제공합니다.
  • 네이티브 플러그인을 사용하여 여러 서드 파티 CAS 공급업체를 지원합니다. CAS 플러그인은 공급업체별 네트워크 프로토콜, EMM(Entitlement Management Message)/ECM(Entitlement Control Message) 형식 및 디스크램블러를 사용할 수 있습니다.
  • 키 래더와 같은 하드웨어 보안을 지원합니다.
  • TrustZone과 같은 신뢰할 수 있는 실행 환경(TEE)을 지원합니다.

지원되는 구성

하드웨어 튜너 구성

하드웨어가 MPEG 전송 스트림 역다중화 및 디스크램블링을 담당하는 경우 튜너 프레임워크는 하드웨어 기반 TV 튜너와 인터페이스로 접속하기 위해 TIS 앱에 조건부 액세스 PSI(Program-Specific Information) 데이터를 제공합니다.

조건부 액세스 PSI 데이터에는 CA 설명어, ECM 및 EMM이 포함됩니다. 이러한 구조를 통해 CAS 플러그인은 콘텐츠 스트림을 복호화하는 데 필요한 키를 얻을 수 있습니다.

하드웨어 튜너 구성 다이어그램

그림 1. 하드웨어 튜너 구성

하드웨어 구성에는 그림 1에 나와 있는 TrustZone과 같은 TEE 레이어가 있을 수 있습니다. TEE 레이어가 없다면 CAS 클라이언트 플러그인이 플랫폼에서 제공하는 하드웨어 키 래더 서비스와 통신할 수 있습니다. 이러한 인터페이스의 공급업체별 변형으로 인해 미디어 CAS는 이를 표준화하지 않습니다.

소프트웨어 구성

Android 11 이전에는 미디어 CAS 프레임워크를 사용하여 IP 멀티캐스트/유니캐스트의 IPTV와 같은 소프트웨어 기반 콘텐츠를 처리할 수 있었습니다. TIS 앱은 미디어 CAS 자바 객체를 인스턴스화하고 적절하게 프로비저닝해야 합니다.

앱은 MediaExtractor 또는 다른 MPEG2-TS 파서를 사용하여 CA 설명어, ECM 및 EMM과 같은 CA 관련 PSI 데이터를 추출할 수 있습니다. 앱이 프레임워크 MediaExtractor를 사용한다면 세션 열기 및 EMM/ECM 처리와 같은 CAS 세션 관리를 프레임워크 MediaExtractor에 위임할 수 있습니다. 그러면 MediaExtractor가 네이티브 API를 직접 사용하여 CAS 세션을 구성합니다.

그러지 않으면 앱이 CA 관련 PSI 데이터를 추출하고 미디어 CAS 자바 API를 사용하여 CAS 세션을 구성해야 합니다(예: 앱이 자체 MPEG2-TS 파서를 사용하는 경우).

튜너 구성 다이어그램

그림 2. MediaExtractor 프레임워크를 사용한 IPTV 입력, CAS 및 디스크램블러 구성

소프트웨어 추출기 시나리오에서 추출기에는 스크램블링된 각 트랙에 대해 소프트웨어 또는 하드웨어 기반 디스크램블러 객체가 필요합니다(트랙에 보안 디코더가 필요한지 여부와 관계없이). 이는 다음과 같은 이유 때문입니다.

  • 트랙에 보안 디코딩이 필요하지 않으면 추출기는 액세스 유닛을 클리어 버퍼로 디스크램블링하여 클리어 스트림에서처럼 샘플을 추출합니다. 이렇게 하면 MediaCodec이 디스크램블링에 관여할 필요가 없습니다.
  • 트랙에 보안 디코딩이 필요하면 추출기에 여전히 디스크램블러가 필요할 수 있습니다. 이러한 상황은 전송 스트림이 전송 패킷 수준에서 스크램블링될 때 발생하며, 여기서 PES(Packetized Elementary Stream) 헤더가 스크램블링됩니다. 추출기는 특정 정보(예: 프레젠테이션 타임스탬프)를 다운스트림하기 위해 PES 헤더에 액세스해야 합니다.

    전송 스트림이 PES 패킷 수준에서 스크램블링되는 경우 추출기는 디스크램블러를 사용하지 않으며, 여기서 PES 헤더는 그대로 유지됩니다. 그러나 실제 스크램블링된 패킷이 도착할 때까지 스크램블링이 발생하는 시점을 확인할 수 없습니다. 편의를 위해 트랙이 프로그램 매핑 테이블(PMT)을 기반으로 스크램블링되는 것으로 확인되면 디스크램블러가 사용된다고 가정합니다.

소프트웨어 구성 제한사항

트랙에 보안 디코딩이 필요할 경우 디스크램블러가 디스크램블링 작업을 클리어 버퍼로 보낼 때 주의해야 합니다. 비보안 오디오 디코딩이 필요하므로, 동영상 디코딩에 보안 디코더가 필요할 경우 동영상 디코딩은 오디오와는 다른 세션에서 스크램블링되어야 합니다. 세션의 ECM은 보안 디코더가 필요하다는 신호를 플러그인에 보내야 합니다.

또는 플러그인이 키를 보안 정책과 안정적으로 연결할 수 있어야 합니다. 그러지 않으면 앱이 오디오 디스크램블러를 사용하여 동영상 프레임을 쉽게 가져올 수 있습니다.

세션에 보안 디코더가 필요한 경우에도 추출기가 PES 헤더를 처리하기 위해 소량의 데이터를 클리어 버퍼에 출력하도록 요청할 수 있습니다. 악성 앱으로 인해 플러그인이 액세스 유닛 전체를 반환하는 것을 방지하려면 플러그인이 전송 페이로드를 파싱하여 페이로드가 적절한 스트림 유형의 PES 헤더로 시작되도록 해야 합니다. 그러지 않으면 플러그인이 요청을 거부해야 합니다.

CA 미세 조정 시퀀스

새로운 채널로 미세 조정할 때 TIS 모듈은 PSI 튜너 프레임워크의 CA 설명어, ECM 및 EMM을 수신하도록 등록합니다. CA 설명어에는 특정 CA 공급업체 및 기타 공급업체 관련 데이터를 고유하게 식별하는 CA 시스템 ID가 포함되어 있습니다. TIS는 미디어 CAS에 쿼리하여 CA 설명어를 처리할 수 있는 CAS 플러그인이 있는지 확인합니다.

CAS 콘텐츠 미세 조정 다이어그램

그림 3. CAS 콘텐츠 미세 조정

CA 시스템 ID가 지원된다면 미디어 CAS의 인스턴스가 생성되며 CA 설명어의 공급업체 비공개 데이터가 플러그인에 제공됩니다. 그런 다음, 오디오 및 동영상 스트림을 처리하기 위해 미디어 CAS에서 새 세션이 열립니다. 새로 열린 세션은 플러그인의 ECM 및 EMM을 수신합니다.

샘플 CAS 플러그인 흐름

TIS는 미디어 CAS API를 사용하여 ECM을 CAS 플러그인에 전달합니다. ECM에는 EMM의 정보를 사용하여 복호화해야 하는 암호화된 제어 단어가 포함되어 있습니다. CAS 플러그인은 setPrivateData() 메서드에서 제공하는 CA 설명어의 공급업체 관련 정보를 기반으로 애셋의 EMM을 획득하는 방법을 결정합니다.

EMM은 콘텐츠 스트림의 대역 내에서 또는 CA 플러그인에 의해 시작된 네트워크 요청을 사용하여 대역 외로 전송될 수 있습니다. TIS는 processEMM() 메서드를 사용하여 대역 내 EMM을 CA 플러그인에 전달합니다.

EMM을 가져오기 위해 네트워크 요청이 필요하다면 CA 플러그인은 라이선스 서버를 사용하여 네트워크 트랜잭션을 실행해야 합니다.

CAS 예 다이어그램

그림 4. EMM 및 ECM 처리를 위한 CAS 플러그인 예

EMM이 수신되면 CA 플러그인은 암호화된 키를 얻기 위해 EMM을 파싱하여 제어 단어를 복호화합니다. 암호화된 EMM 키 및 암호화된 제어 단어를 키 래더 또는 신뢰할 수 있는 환경에 로드하여 제어 단어 복호화 및 콘텐츠 스트림의 후속 디스크램블링을 실행할 수 있습니다.

미디어 CAS 자바 API

미디어 CAS 자바 API에는 다음과 같은 메서드가 있습니다.

  • 기기에서 사용 가능한 모든 CA 플러그인을 나열합니다.

    class MediaCas.PluginDescriptor {
      public String getName();
      public int getSystemId();
    }
    static PluginDescriptor[] enumeratePlugins();
    
  • 지정된 CA 시스템의 미디어 CAS 인스턴스를 생성합니다. 즉, 미디어 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 시스템에 대해 지정된 유형의 프로비저닝 작업을 시작합니다. 기기가 유료 TV 서비스에 처음 가입할 때 먼저 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(프로그램 정보 또는 ES 정보 섹션에서 비롯될 수 있음)에 있는 CA 설명어의 CA 비공개 데이터를 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);