From 43cab92abed3b6791328988c77266d9a37aa4a53 Mon Sep 17 00:00:00 2001 From: Henry Fang Date: Fri, 27 Dec 2019 16:50:20 -0800 Subject: [PATCH] Framework support cas@1.2 Test: Manual bug: 141783130 Change-Id: I62d42ad51444b8d58282f5d6992ba6f2169dd6d3 --- Android.bp | 3 +- api/current.txt | 46 +++ media/java/android/media/MediaCas.java | 324 ++++++++++++++++-- .../java/android/media/MediaCasException.java | 13 +- .../android/media/MediaCasStateException.java | 90 +++-- .../java/android/media/tv/TvInputManager.java | 95 ++++- .../java/android/media/tv/TvInputService.java | 50 +++ 7 files changed, 560 insertions(+), 61 deletions(-) diff --git a/Android.bp b/Android.bp index 5fcefa64a92f5..c03c9f1cb69b4 100644 --- a/Android.bp +++ b/Android.bp @@ -326,8 +326,9 @@ java_library { "framework-protos", "game-driver-protos", "android.hidl.base-V1.0-java", - "android.hardware.cas-V1.1-java", "android.hardware.cas-V1.0-java", + "android.hardware.cas-V1.1-java", + "android.hardware.cas-V1.2-java", "android.hardware.contexthub-V1.0-java", "android.hardware.gnss-V1.0-java", "android.hardware.health-V1.0-java-constants", diff --git a/api/current.txt b/api/current.txt index 8d44c7d8e89be..2a6fc14e26d57 100644 --- a/api/current.txt +++ b/api/current.txt @@ -24793,11 +24793,13 @@ package android.media { public final class MediaCas implements java.lang.AutoCloseable { ctor public MediaCas(int) throws android.media.MediaCasException.UnsupportedCasException; + ctor public MediaCas(int, @Nullable String, int) throws android.media.MediaCasException.UnsupportedCasException; method public void close(); method public static android.media.MediaCas.PluginDescriptor[] enumeratePlugins(); method protected void finalize(); method public static boolean isSystemIdSupported(int); method public android.media.MediaCas.Session openSession() throws android.media.MediaCasException; + method @Nullable public android.media.MediaCas.Session openSession(int, int) throws android.media.MediaCasException; method public void processEmm(@NonNull byte[], int, int) throws android.media.MediaCasException; method public void processEmm(@NonNull byte[]) throws android.media.MediaCasException; method public void provision(@NonNull String) throws android.media.MediaCasException; @@ -24805,10 +24807,32 @@ package android.media { method public void sendEvent(int, int, @Nullable byte[]) throws android.media.MediaCasException; method public void setEventListener(@Nullable android.media.MediaCas.EventListener, @Nullable android.os.Handler); method public void setPrivateData(@NonNull byte[]) throws android.media.MediaCasException; + field public static final int PLUGIN_STATUS_PHYSICAL_MODULE_CHANGED = 0; // 0x0 + field public static final int PLUGIN_STATUS_SESSION_NUMBER_CHANGED = 1; // 0x1 + field public static final int SCRAMBLING_MODE_AES128 = 9; // 0x9 + field public static final int SCRAMBLING_MODE_AES_ECB = 10; // 0xa + field public static final int SCRAMBLING_MODE_AES_SCTE52 = 11; // 0xb + field public static final int SCRAMBLING_MODE_DVB_CISSA_V1 = 6; // 0x6 + field public static final int SCRAMBLING_MODE_DVB_CSA1 = 1; // 0x1 + field public static final int SCRAMBLING_MODE_DVB_CSA2 = 2; // 0x2 + field public static final int SCRAMBLING_MODE_DVB_CSA3_ENHANCE = 5; // 0x5 + field public static final int SCRAMBLING_MODE_DVB_CSA3_MINIMAL = 4; // 0x4 + field public static final int SCRAMBLING_MODE_DVB_CSA3_STANDARD = 3; // 0x3 + field public static final int SCRAMBLING_MODE_DVB_IDSA = 7; // 0x7 + field public static final int SCRAMBLING_MODE_MULTI2 = 8; // 0x8 + field public static final int SCRAMBLING_MODE_RESERVED = 0; // 0x0 + field public static final int SCRAMBLING_MODE_TDES_ECB = 12; // 0xc + field public static final int SCRAMBLING_MODE_TDES_SCTE52 = 13; // 0xd + field public static final int SESSION_USAGE_LIVE = 0; // 0x0 + field public static final int SESSION_USAGE_PLAYBACK = 1; // 0x1 + field public static final int SESSION_USAGE_RECORD = 2; // 0x2 + field public static final int SESSION_USAGE_TIMESHIFT = 3; // 0x3 } public static interface MediaCas.EventListener { method public void onEvent(@NonNull android.media.MediaCas, int, int, @Nullable byte[]); + method public default void onPluginStatusUpdate(@NonNull android.media.MediaCas, int, int); + method public default void onResourceLost(@NonNull android.media.MediaCas); method public default void onSessionEvent(@NonNull android.media.MediaCas, @NonNull android.media.MediaCas.Session, int, int, @Nullable byte[]); } @@ -24819,6 +24843,7 @@ package android.media { public final class MediaCas.Session implements java.lang.AutoCloseable { method public void close(); + method @NonNull public byte[] getSessionId(); method public void processEcm(@NonNull byte[], int, int) throws android.media.MediaCasException; method public void processEcm(@NonNull byte[]) throws android.media.MediaCasException; method public void sendSessionEvent(int, int, @Nullable byte[]) throws android.media.MediaCasException; @@ -24831,6 +24856,9 @@ package android.media { public static final class MediaCasException.DeniedByServerException extends android.media.MediaCasException { } + public static final class MediaCasException.InsufficientResourceException extends android.media.MediaCasException { + } + public static final class MediaCasException.NotProvisionedException extends android.media.MediaCasException { } @@ -28817,6 +28845,19 @@ package android.media.tv { field public static final int TIME_SHIFT_STATUS_UNSUPPORTED = 1; // 0x1 field public static final int VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 4; // 0x4 field public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; // 0x3 + field public static final int VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT = 16; // 0x10 + field public static final int VIDEO_UNAVAILABLE_REASON_CAS_CARD_INVALID = 15; // 0xf + field public static final int VIDEO_UNAVAILABLE_REASON_CAS_CARD_MUTE = 14; // 0xe + field public static final int VIDEO_UNAVAILABLE_REASON_CAS_INSUFFICIENT_OUTPUT_PROTECTION = 7; // 0x7 + field public static final int VIDEO_UNAVAILABLE_REASON_CAS_LICENSE_EXPIRED = 10; // 0xa + field public static final int VIDEO_UNAVAILABLE_REASON_CAS_NEED_ACTIVATION = 11; // 0xb + field public static final int VIDEO_UNAVAILABLE_REASON_CAS_NEED_PAIRING = 12; // 0xc + field public static final int VIDEO_UNAVAILABLE_REASON_CAS_NO_CARD = 13; // 0xd + field public static final int VIDEO_UNAVAILABLE_REASON_CAS_PVR_RECORDING_NOT_ALLOWED = 8; // 0x8 + field public static final int VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING = 17; // 0x11 + field public static final int VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN = 18; // 0x12 + field public static final int VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE = 6; // 0x6 + field public static final int VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 5; // 0x5 field public static final int VIDEO_UNAVAILABLE_REASON_TUNING = 1; // 0x1 field public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0; // 0x0 field public static final int VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL = 2; // 0x2 @@ -28838,6 +28879,11 @@ package android.media.tv { method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String, @NonNull String); method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(String); method @Nullable public android.media.tv.TvInputService.Session onCreateSession(@NonNull String, @NonNull String); + field public static final int PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND = 100; // 0x64 + field public static final int PRIORITY_HINT_USE_CASE_TYPE_LIVE = 400; // 0x190 + field public static final int PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK = 300; // 0x12c + field public static final int PRIORITY_HINT_USE_CASE_TYPE_RECORD = 500; // 0x1f4 + field public static final int PRIORITY_HINT_USE_CASE_TYPE_SCAN = 200; // 0xc8 field public static final String SERVICE_INTERFACE = "android.media.tv.TvInputService"; field public static final String SERVICE_META_DATA = "android.media.tv.input"; } diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java index a9e33fd8e472f..cefc9db779243 100644 --- a/media/java/android/media/MediaCas.java +++ b/media/java/android/media/MediaCas.java @@ -16,13 +16,15 @@ package android.media; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.cas.V1_0.HidlCasPluginDescriptor; import android.hardware.cas.V1_0.ICas; import android.hardware.cas.V1_0.IMediaCasService; -import android.hardware.cas.V1_1.ICasListener; +import android.hardware.cas.V1_2.ICasListener; import android.media.MediaCasException.*; +import android.media.tv.TvInputService.PriorityHintUseCaseType; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; @@ -34,6 +36,8 @@ import android.os.RemoteException; import android.util.Log; import android.util.Singleton; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; /** @@ -100,28 +104,185 @@ public final class MediaCas implements AutoCloseable { private static final String TAG = "MediaCas"; private ICas mICas; private android.hardware.cas.V1_1.ICas mICasV11; + private android.hardware.cas.V1_2.ICas mICasV12; private EventListener mListener; private HandlerThread mHandlerThread; private EventHandler mEventHandler; + private @PriorityHintUseCaseType int mPriorityHint; + private String mTvInputServiceSessionId; + + /** + * Scrambling modes used to open cas sessions. + * + * @hide + */ + @IntDef(prefix = "SCRAMBLING_MODE_", + value = {SCRAMBLING_MODE_RESERVED, SCRAMBLING_MODE_DVB_CSA1, SCRAMBLING_MODE_DVB_CSA2, + SCRAMBLING_MODE_DVB_CSA3_STANDARD, + SCRAMBLING_MODE_DVB_CSA3_MINIMAL, SCRAMBLING_MODE_DVB_CSA3_ENHANCE, + SCRAMBLING_MODE_DVB_CISSA_V1, SCRAMBLING_MODE_DVB_IDSA, + SCRAMBLING_MODE_MULTI2, SCRAMBLING_MODE_AES128, SCRAMBLING_MODE_AES_ECB, + SCRAMBLING_MODE_AES_SCTE52, SCRAMBLING_MODE_TDES_ECB, SCRAMBLING_MODE_TDES_SCTE52}) + @Retention(RetentionPolicy.SOURCE) + public @interface ScramblingMode {} + + /** + * DVB (Digital Video Broadcasting) reserved mode. + */ + public static final int SCRAMBLING_MODE_RESERVED = + android.hardware.cas.V1_2.ScramblingMode.RESERVED; + /** + * DVB (Digital Video Broadcasting) Common Scrambling Algorithm (CSA) 1. + */ + public static final int SCRAMBLING_MODE_DVB_CSA1 = + android.hardware.cas.V1_2.ScramblingMode.DVB_CSA1; + /** + * DVB CSA 2. + */ + public static final int SCRAMBLING_MODE_DVB_CSA2 = + android.hardware.cas.V1_2.ScramblingMode.DVB_CSA2; + /** + * DVB CSA 3 in standard mode. + */ + public static final int SCRAMBLING_MODE_DVB_CSA3_STANDARD = + android.hardware.cas.V1_2.ScramblingMode.DVB_CSA3_STANDARD; + /** + * DVB CSA 3 in minimally enhanced mode. + */ + public static final int SCRAMBLING_MODE_DVB_CSA3_MINIMAL = + android.hardware.cas.V1_2.ScramblingMode.DVB_CSA3_MINIMAL; + /** + * DVB CSA 3 in fully enhanced mode. + */ + public static final int SCRAMBLING_MODE_DVB_CSA3_ENHANCE = + android.hardware.cas.V1_2.ScramblingMode.DVB_CSA3_ENHANCE; + /** + * DVB Common IPTV Software-oriented Scrambling Algorithm (CISSA) Version 1. + */ + public static final int SCRAMBLING_MODE_DVB_CISSA_V1 = + android.hardware.cas.V1_2.ScramblingMode.DVB_CISSA_V1; + /** + * ATIS-0800006 IIF Default Scrambling Algorithm (IDSA). + */ + public static final int SCRAMBLING_MODE_DVB_IDSA = + android.hardware.cas.V1_2.ScramblingMode.DVB_IDSA; + /** + * A symmetric key algorithm. + */ + public static final int SCRAMBLING_MODE_MULTI2 = + android.hardware.cas.V1_2.ScramblingMode.MULTI2; + /** + * Advanced Encryption System (AES) 128-bit Encryption mode. + */ + public static final int SCRAMBLING_MODE_AES128 = + android.hardware.cas.V1_2.ScramblingMode.AES128; + /** + * Advanced Encryption System (AES) Electronic Code Book (ECB) mode. + */ + public static final int SCRAMBLING_MODE_AES_ECB = + android.hardware.cas.V1_2.ScramblingMode.AES_ECB; + /** + * Advanced Encryption System (AES) Society of Cable Telecommunications Engineers (SCTE) 52 + * mode. + */ + public static final int SCRAMBLING_MODE_AES_SCTE52 = + android.hardware.cas.V1_2.ScramblingMode.AES_SCTE52; + /** + * Triple Data Encryption Algorithm (TDES) Electronic Code Book (ECB) mode. + */ + public static final int SCRAMBLING_MODE_TDES_ECB = + android.hardware.cas.V1_2.ScramblingMode.TDES_ECB; + /** + * Triple Data Encryption Algorithm (TDES) Society of Cable Telecommunications Engineers (SCTE) + * 52 mode. + */ + public static final int SCRAMBLING_MODE_TDES_SCTE52 = + android.hardware.cas.V1_2.ScramblingMode.TDES_SCTE52; + + /** + * Usages used to open cas sessions. + * + * @hide + */ + @IntDef(prefix = "SESSION_USAGE_", + value = {SESSION_USAGE_LIVE, SESSION_USAGE_PLAYBACK, SESSION_USAGE_RECORD, + SESSION_USAGE_TIMESHIFT}) + @Retention(RetentionPolicy.SOURCE) + public @interface SessionUsage {} + /** + * Cas session is used to descramble live streams. + */ + public static final int SESSION_USAGE_LIVE = android.hardware.cas.V1_2.SessionIntent.LIVE; + /** + * Cas session is used to descramble recoreded streams. + */ + public static final int SESSION_USAGE_PLAYBACK = + android.hardware.cas.V1_2.SessionIntent.PLAYBACK; + /** + * Cas session is used to descramble live streams and encrypt local recorded content + */ + public static final int SESSION_USAGE_RECORD = android.hardware.cas.V1_2.SessionIntent.RECORD; + /** + * Cas session is used to descramble live streams , encrypt local recorded content and playback + * local encrypted content. + */ + public static final int SESSION_USAGE_TIMESHIFT = + android.hardware.cas.V1_2.SessionIntent.TIMESHIFT; + + /** + * Plugin status events sent from cas system. + * + * @hide + */ + @IntDef(prefix = "PLUGIN_STATUS_", + value = {PLUGIN_STATUS_PHYSICAL_MODULE_CHANGED, PLUGIN_STATUS_SESSION_NUMBER_CHANGED}) + @Retention(RetentionPolicy.SOURCE) + public @interface PluginStatus {} + + /** + * The event to indicate that the status of CAS system is changed by the removal or insertion of + * physical CAS modules. + */ + public static final int PLUGIN_STATUS_PHYSICAL_MODULE_CHANGED = + android.hardware.cas.V1_2.StatusEvent.PLUGIN_PHYSICAL_MODULE_CHANGED; + /** + * The event to indicate that the number of CAS system's session is changed. + */ + public static final int PLUGIN_STATUS_SESSION_NUMBER_CHANGED = + android.hardware.cas.V1_2.StatusEvent.PLUGIN_SESSION_NUMBER_CHANGED; private static final Singleton sService = new Singleton() { @Override protected IMediaCasService create() { try { - Log.d(TAG, "Tried to get cas@1.1 service"); - android.hardware.cas.V1_1.IMediaCasService serviceV11 = - android.hardware.cas.V1_1.IMediaCasService.getService(true /*wait*/); - if (serviceV11 != null) { - return serviceV11; - } - } catch (Exception eV1_1) { - try { - Log.d(TAG, "Tried to get cas@1.0 service"); - return IMediaCasService.getService(true /*wait*/); - } catch (Exception eV1_0) { - Log.d(TAG, "Failed to get cas@1.0 service"); + Log.d(TAG, "Trying to get cas@1.2 service"); + android.hardware.cas.V1_2.IMediaCasService serviceV12 = + android.hardware.cas.V1_2.IMediaCasService.getService(true /*wait*/); + if (serviceV12 != null) { + return serviceV12; } + } catch (Exception eV1_2) { + Log.d(TAG, "Failed to get cas@1.2 service"); } + + try { + Log.d(TAG, "Trying to get cas@1.1 service"); + android.hardware.cas.V1_1.IMediaCasService serviceV11 = + android.hardware.cas.V1_1.IMediaCasService.getService(true /*wait*/); + if (serviceV11 != null) { + return serviceV11; + } + } catch (Exception eV1_1) { + Log.d(TAG, "Failed to get cas@1.1 service"); + } + + try { + Log.d(TAG, "Trying to get cas@1.0 service"); + return IMediaCasService.getService(true /*wait*/); + } catch (Exception eV1_0) { + Log.d(TAG, "Failed to get cas@1.0 service"); + } + return null; } }; @@ -139,6 +300,7 @@ public final class MediaCas implements AutoCloseable { private void cleanupAndRethrowIllegalState() { mICas = null; mICasV11 = null; + mICasV12 = null; throw new IllegalStateException(); } @@ -146,6 +308,8 @@ public final class MediaCas implements AutoCloseable { private static final int MSG_CAS_EVENT = 0; private static final int MSG_CAS_SESSION_EVENT = 1; + private static final int MSG_CAS_STATUS_EVENT = 2; + private static final int MSG_CAS_RESOURCE_LOST = 3; private static final String SESSION_KEY = "sessionId"; private static final String DATA_KEY = "data"; @@ -164,6 +328,10 @@ public final class MediaCas implements AutoCloseable { mListener.onSessionEvent(MediaCas.this, createFromSessionId(sessionId), msg.arg1, msg.arg2, bundle.getByteArray(DATA_KEY)); + } else if (msg.what == MSG_CAS_STATUS_EVENT) { + mListener.onPluginStatusUpdate(MediaCas.this, msg.arg1, msg.arg2); + } else if (msg.what == MSG_CAS_RESOURCE_LOST) { + mListener.onResourceLost(MediaCas.this); } } } @@ -189,6 +357,12 @@ public final class MediaCas implements AutoCloseable { msg.setData(bundle); mEventHandler.sendMessage(msg); } + @Override + public void onStatusUpdate(byte status, int arg) + throws RemoteException { + mEventHandler.sendMessage(mEventHandler.obtainMessage( + EventHandler.MSG_CAS_STATUS_EVENT, status, arg)); + } }; /** * Describe a CAS plugin with its CA_system_ID and string name. @@ -257,7 +431,7 @@ public final class MediaCas implements AutoCloseable { final ArrayList mSessionId; Session(@NonNull ArrayList sessionId) { - mSessionId = sessionId; + mSessionId = new ArrayList(sessionId); } /** @@ -363,6 +537,19 @@ public final class MediaCas implements AutoCloseable { } } + /** + * Get Session Id. + * + * @return session Id of the session. + * + * @throws IllegalStateException if the MediaCas instance is not valid. + */ + @NonNull + public byte[] getSessionId() { + validateInternalStates(); + return toBytes(mSessionId); + } + /** * Close the session. * @@ -445,14 +632,23 @@ public final class MediaCas implements AutoCloseable { public MediaCas(int CA_system_id) throws UnsupportedCasException { try { IMediaCasService service = getService(); - android.hardware.cas.V1_1.IMediaCasService serviceV11 = + android.hardware.cas.V1_2.IMediaCasService serviceV12 = + android.hardware.cas.V1_2.IMediaCasService.castFrom(service); + if (serviceV12 == null) { + android.hardware.cas.V1_1.IMediaCasService serviceV11 = android.hardware.cas.V1_1.IMediaCasService.castFrom(service); - if (serviceV11 == null) { - Log.d(TAG, "Used cas@1_0 interface to create plugin"); - mICas = service.createPlugin(CA_system_id, mBinder); + if (serviceV11 == null) { + Log.d(TAG, "Used cas@1_0 interface to create plugin"); + mICas = service.createPlugin(CA_system_id, mBinder); + } else { + Log.d(TAG, "Used cas@1.1 interface to create plugin"); + mICas = mICasV11 = serviceV11.createPluginExt(CA_system_id, mBinder); + } } else { - Log.d(TAG, "Used cas@1.1 interface to create plugin"); - mICas = mICasV11 = serviceV11.createPluginExt(CA_system_id, mBinder); + Log.d(TAG, "Used cas@1.2 interface to create plugin"); + mICas = mICasV11 = mICasV12 = + android.hardware.cas.V1_2.ICas + .castFrom(serviceV12.createPluginExt(CA_system_id, mBinder)); } } catch(Exception e) { Log.e(TAG, "Failed to create plugin: " + e); @@ -465,6 +661,24 @@ public final class MediaCas implements AutoCloseable { } } + /** + * Instantiate a CA system of the specified system id. + * + * @param casSystemId The system id of the CA system. + * @param tvInputServiceSessionId The Id of the session opened in TV Input Service (TIS) + * {@link android.media.tv.TvInputService#onCreateSession(String, String)} + * @param priorityHint priority hint from the use case type for new created CAS system. + * + * @throws UnsupportedCasException if the device does not support the + * specified CA system. + */ + public MediaCas(int casSystemId, @Nullable String tvInputServiceSessionId, + @PriorityHintUseCaseType int priorityHint) throws UnsupportedCasException { + this(casSystemId); + mPriorityHint = priorityHint; + mTvInputServiceSessionId = tvInputServiceSessionId; + } + IHwBinder getBinder() { validateInternalStates(); @@ -476,6 +690,7 @@ public final class MediaCas implements AutoCloseable { * to receives scheme-specific notifications from a MediaCas instance. */ public interface EventListener { + /** * Notify the listener of a scheme-specific event from the CA system. * @@ -501,6 +716,27 @@ public final class MediaCas implements AutoCloseable { int event, int arg, @Nullable byte[] data) { Log.d(TAG, "Received MediaCas Session event"); } + + /** + * Notify the listener that the cas plugin status is updated. + * + * @param mediaCas the MediaCas object to receive this event. + * @param status the plugin status which is updated. + * @param arg an integer whose meaning is specific to the status to be updated. + */ + default void onPluginStatusUpdate(@NonNull MediaCas mediaCas, @PluginStatus int status, + int arg) { + Log.d(TAG, "Received MediaCas Plugin Status event"); + } + + /** + * Notify the listener that the session resources was lost. + * + * @param mediaCas the MediaCas object to receive this event. + */ + default void onResourceLost(@NonNull MediaCas mediaCas) { + Log.d(TAG, "Received MediaCas Resource Reclaim event"); + } } /** @@ -563,6 +799,20 @@ public final class MediaCas implements AutoCloseable { mSession = createFromSessionId(sessionId); } } + + private class OpenSession_1_2_Callback implements + android.hardware.cas.V1_2.ICas.openSession_1_2Callback { + + public Session mSession; + public int mStatus; + + @Override + public void onValues(int status, ArrayList sessionId) { + mStatus = status; + mSession = createFromSessionId(sessionId); + } + } + /** * Open a session to descramble one or more streams scrambled by the * conditional access system. @@ -587,6 +837,40 @@ public final class MediaCas implements AutoCloseable { return null; } + /** + * Open a session with usage and scrambling information, so that descrambler can be configured + * to descramble one or more streams scrambled by the conditional access system. + * + * @param sessionUsage used for the created session. + * @param scramblingMode used for the created session. + * + * @return session the newly opened session. + * + * @throws IllegalStateException if the MediaCas instance is not valid. + * @throws MediaCasException for CAS-specific errors. + * @throws MediaCasStateException for CAS-specific state exceptions. + */ + @Nullable + public Session openSession(@SessionUsage int sessionUsage, @ScramblingMode int scramblingMode) + throws MediaCasException { + validateInternalStates(); + + if (mICasV12 == null) { + Log.d(TAG, "Open Session with scrambling mode is only supported by cas@1.2+ interface"); + throw new UnsupportedCasException("Open Session with scrambling mode is not supported"); + } + + try { + OpenSession_1_2_Callback cb = new OpenSession_1_2_Callback(); + mICasV12.openSession_1_2(sessionUsage, scramblingMode, cb); + MediaCasException.throwExceptionIfNeeded(cb.mStatus); + return cb.mSession; + } catch (RemoteException e) { + cleanupAndRethrowIllegalState(); + } + return null; + } + /** * Send a received EMM packet to the CA system. * diff --git a/media/java/android/media/MediaCasException.java b/media/java/android/media/MediaCasException.java index 35fb104b7f0b1..349e9b3ef186b 100644 --- a/media/java/android/media/MediaCasException.java +++ b/media/java/android/media/MediaCasException.java @@ -16,7 +16,7 @@ package android.media; -import android.hardware.cas.V1_0.Status; +import android.hardware.cas.V1_2.Status; /** * Base class for MediaCas exceptions @@ -85,4 +85,15 @@ public class MediaCasException extends Exception { super(detailMessage); } } + + /** + * Exception thrown when an operation on a MediaCas object is attempted + * and hardware resources are not sufficient to allocate, due to client's lower priority. + */ + public static final class InsufficientResourceException extends MediaCasException { + /** @hide */ + public InsufficientResourceException(String detailMessage) { + super(detailMessage); + } + } } diff --git a/media/java/android/media/MediaCasStateException.java b/media/java/android/media/MediaCasStateException.java index 26c57923912d1..8dbc9f4c7d036 100644 --- a/media/java/android/media/MediaCasStateException.java +++ b/media/java/android/media/MediaCasStateException.java @@ -18,8 +18,7 @@ package android.media; import android.annotation.NonNull; import android.annotation.Nullable; - -import android.hardware.cas.V1_0.Status; +import android.hardware.cas.V1_2.Status; /** * Base class for MediaCas runtime exceptions @@ -48,39 +47,60 @@ public class MediaCasStateException extends IllegalStateException { String diagnosticInfo = ""; switch (err) { - case Status.ERROR_CAS_UNKNOWN: - diagnosticInfo = "General CAS error"; - break; - case Status.ERROR_CAS_NO_LICENSE: - diagnosticInfo = "No license"; - break; - case Status.ERROR_CAS_LICENSE_EXPIRED: - diagnosticInfo = "License expired"; - break; - case Status.ERROR_CAS_SESSION_NOT_OPENED: - diagnosticInfo = "Session not opened"; - break; - case Status.ERROR_CAS_CANNOT_HANDLE: - diagnosticInfo = "Unsupported scheme or data format"; - break; - case Status.ERROR_CAS_INVALID_STATE: - diagnosticInfo = "Invalid CAS state"; - break; - case Status.ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION: - diagnosticInfo = "Insufficient output protection"; - break; - case Status.ERROR_CAS_TAMPER_DETECTED: - diagnosticInfo = "Tamper detected"; - break; - case Status.ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED: - diagnosticInfo = "Not initialized"; - break; - case Status.ERROR_CAS_DECRYPT: - diagnosticInfo = "Decrypt error"; - break; - default: - diagnosticInfo = "Unknown CAS state exception"; - break; + case Status.ERROR_CAS_UNKNOWN: + diagnosticInfo = "General CAS error"; + break; + case Status.ERROR_CAS_NO_LICENSE: + diagnosticInfo = "No license"; + break; + case Status.ERROR_CAS_LICENSE_EXPIRED: + diagnosticInfo = "License expired"; + break; + case Status.ERROR_CAS_SESSION_NOT_OPENED: + diagnosticInfo = "Session not opened"; + break; + case Status.ERROR_CAS_CANNOT_HANDLE: + diagnosticInfo = "Unsupported scheme or data format"; + break; + case Status.ERROR_CAS_INVALID_STATE: + diagnosticInfo = "Invalid CAS state"; + break; + case Status.ERROR_CAS_INSUFFICIENT_OUTPUT_PROTECTION: + diagnosticInfo = "Insufficient output protection"; + break; + case Status.ERROR_CAS_TAMPER_DETECTED: + diagnosticInfo = "Tamper detected"; + break; + case Status.ERROR_CAS_DECRYPT_UNIT_NOT_INITIALIZED: + diagnosticInfo = "Not initialized"; + break; + case Status.ERROR_CAS_DECRYPT: + diagnosticInfo = "Decrypt error"; + break; + case Status.ERROR_CAS_NEED_ACTIVATION: + diagnosticInfo = "Need Activation"; + break; + case Status.ERROR_CAS_NEED_PAIRING: + diagnosticInfo = "Need Pairing"; + break; + case Status.ERROR_CAS_NO_CARD: + diagnosticInfo = "No Card"; + break; + case Status.ERROR_CAS_CARD_MUTE: + diagnosticInfo = "Card Muted"; + break; + case Status.ERROR_CAS_CARD_INVALID: + diagnosticInfo = "Card Invalid"; + break; + case Status.ERROR_CAS_BLACKOUT: + diagnosticInfo = "Blackout"; + break; + case Status.ERROR_CAS_REBOOTING: + diagnosticInfo = "Rebooting"; + break; + default: + diagnosticInfo = "Unknown CAS state exception"; + break; } throw new MediaCasStateException(err, msg, String.format("%s (err=%d)", diagnosticInfo, err)); diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 630d8191eff0a..fc5d67d50ee57 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -110,12 +110,20 @@ public final class TvInputManager { /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({VIDEO_UNAVAILABLE_REASON_UNKNOWN, VIDEO_UNAVAILABLE_REASON_TUNING, - VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL, VIDEO_UNAVAILABLE_REASON_BUFFERING, - VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY}) + VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL, VIDEO_UNAVAILABLE_REASON_BUFFERING, + VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY, VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE, + VIDEO_UNAVAILABLE_REASON_CAS_INSUFFICIENT_OUTPUT_PROTECTION, + VIDEO_UNAVAILABLE_REASON_CAS_PVR_RECORDING_NOT_ALLOWED, + VIDEO_UNAVAILABLE_REASON_CAS_PVR_RECORDING_NOT_ALLOWED, + VIDEO_UNAVAILABLE_REASON_CAS_NO_LICENSE, VIDEO_UNAVAILABLE_REASON_CAS_LICENSE_EXPIRED, + VIDEO_UNAVAILABLE_REASON_CAS_NEED_ACTIVATION, VIDEO_UNAVAILABLE_REASON_CAS_NEED_PAIRING, + VIDEO_UNAVAILABLE_REASON_CAS_NO_CARD, VIDEO_UNAVAILABLE_REASON_CAS_CARD_MUTE, + VIDEO_UNAVAILABLE_REASON_CAS_CARD_INVALID, VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT, + VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING, VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN}) public @interface VideoUnavailableReason {} static final int VIDEO_UNAVAILABLE_REASON_START = 0; - static final int VIDEO_UNAVAILABLE_REASON_END = 5; + static final int VIDEO_UNAVAILABLE_REASON_END = 18; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and @@ -151,9 +159,88 @@ public final class TvInputManager { * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * the source is not physically connected, for example the HDMI cable is not connected. + */ + public static final int VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = 5; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * the resource is not enough to meet requirement. + */ + public static final int VIDEO_UNAVAILABLE_REASON_INSUFFICIENT_RESOURCE = 6; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * the output protection level enabled on the device is not sufficient to meet the requirements + * in the license policy. + */ + public static final int VIDEO_UNAVAILABLE_REASON_CAS_INSUFFICIENT_OUTPUT_PROTECTION = 7; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * the PVR record is not allowed by the license policy. + */ + public static final int VIDEO_UNAVAILABLE_REASON_CAS_PVR_RECORDING_NOT_ALLOWED = 8; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * no license keys have been provided. * @hide */ - public static final int VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = VIDEO_UNAVAILABLE_REASON_END; + public static final int VIDEO_UNAVAILABLE_REASON_CAS_NO_LICENSE = 9; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * Using a license in whhich the keys have expired. + */ + public static final int VIDEO_UNAVAILABLE_REASON_CAS_LICENSE_EXPIRED = 10; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * the device need be activated. + */ + public static final int VIDEO_UNAVAILABLE_REASON_CAS_NEED_ACTIVATION = 11; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * the device need be paired. + */ + public static final int VIDEO_UNAVAILABLE_REASON_CAS_NEED_PAIRING = 12; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * smart card is missed. + */ + public static final int VIDEO_UNAVAILABLE_REASON_CAS_NO_CARD = 13; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * smart card is muted. + */ + public static final int VIDEO_UNAVAILABLE_REASON_CAS_CARD_MUTE = 14; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * smart card is invalid. + */ + public static final int VIDEO_UNAVAILABLE_REASON_CAS_CARD_INVALID = 15; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * of a geographical blackout. + */ + public static final int VIDEO_UNAVAILABLE_REASON_CAS_BLACKOUT = 16; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * CAS system is rebooting. + */ + public static final int VIDEO_UNAVAILABLE_REASON_CAS_REBOOTING = 17; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * of unknown CAS error. + */ + public static final int VIDEO_UNAVAILABLE_REASON_CAS_UNKNOWN = VIDEO_UNAVAILABLE_REASON_END; /** @hide */ @Retention(RetentionPolicy.SOURCE) diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index 629dc7ce78191..7e1f44cb08990 100755 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -17,6 +17,7 @@ package android.media.tv; import android.annotation.FloatRange; +import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; @@ -58,6 +59,8 @@ import android.widget.FrameLayout; import com.android.internal.os.SomeArgs; import com.android.internal.util.Preconditions; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; @@ -95,6 +98,53 @@ public abstract class TvInputService extends Service { */ public static final String SERVICE_META_DATA = "android.media.tv.input"; + /** + * Prioirity hint from use case types. + * + * @hide + */ + @IntDef(prefix = "PRIORITY_HINT_USE_CASE_TYPE_", + value = {PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND, PRIORITY_HINT_USE_CASE_TYPE_SCAN, + PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, PRIORITY_HINT_USE_CASE_TYPE_LIVE, + PRIORITY_HINT_USE_CASE_TYPE_RECORD}) + @Retention(RetentionPolicy.SOURCE) + public @interface PriorityHintUseCaseType {} + + /** + * Use case of priority hint for {@link android.media.MediaCas#MediaCas(int, String , int)}: + * Background. + * TODO Link: Tuner#Tuner(Context, string, int). + */ + public static final int PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND = 100; + + /** + * Use case of priority hint for {@link android.media.MediaCas#MediaCas(int, String , int)}: + * Scan. + * TODO Link: Tuner#Tuner(Context, string, int). + */ + public static final int PRIORITY_HINT_USE_CASE_TYPE_SCAN = 200; + + /** + * Use case of priority hint for {@link android.media.MediaCas#MediaCas(int, String , int)}: + * Playback. + * TODO Link: Tuner#Tuner(Context, string, int). + */ + public static final int PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK = 300; + + /** + * Use case of priority hint for {@link android.media.MediaCas#MediaCas(int, String , int)}: + * Live. + * TODO Link: Tuner#Tuner(Context, string, int). + */ + public static final int PRIORITY_HINT_USE_CASE_TYPE_LIVE = 400; + + /** + * Use case of priority hint for {@link android.media.MediaCas#MediaCas(int, String , int)}: + * Record. + * TODO Link: Tuner#Tuner(Context, string, int). + */ + public static final int PRIORITY_HINT_USE_CASE_TYPE_RECORD = 500; + /** * Handler instance to handle request from TV Input Manager Service. Should be run in the main * looper to be synchronously run with {@code Session.mHandler}.