summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jeff Tinker <jtinker@google.com> 2018-12-12 08:31:22 -0800
committer Jeff Tinker <jtinker@google.com> 2019-01-11 15:31:04 -0800
commit20594d8b9d98209e973b735c33ccb31c3e36d3d1 (patch)
tree91fab08b13344730fddf149141fcd98f004e4e5c
parent1bafe67c7855673c163483bedf8e304b9b75ec44 (diff)
Add additional error conditions to MediaDrm
New codes are being added to handle resource contention, lost session state, frame size too large and insufficient security level for decryption. Also cleans up inconsistent use of tamper detected error where invalid state error should have been used. bug:111504510 bug:111505796 test: cts and gts media test cases Change-Id: Ifa652d31ea151fbdc9faa0dd15b19f885d82b916
-rw-r--r--api/current.txt14
-rw-r--r--media/java/android/media/MediaCodec.java29
-rw-r--r--media/java/android/media/MediaDrm.java281
-rw-r--r--media/jni/android_media_MediaCodec.cpp30
-rw-r--r--media/jni/android_media_MediaDrm.cpp54
5 files changed, 344 insertions, 64 deletions
diff --git a/api/current.txt b/api/current.txt
index 72dd139fe39c..a36ffbed7fd2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24173,8 +24173,11 @@ package android.media {
public static final class MediaCodec.CryptoException extends java.lang.RuntimeException {
ctor public MediaCodec.CryptoException(int, java.lang.String);
method public int getErrorCode();
+ field public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8
field public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4
+ field public static final int ERROR_INSUFFICIENT_SECURITY = 7; // 0x7
field public static final int ERROR_KEY_EXPIRED = 2; // 0x2
+ field public static final int ERROR_LOST_STATE = 9; // 0x9
field public static final int ERROR_NO_KEY = 1; // 0x1
field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3
field public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5
@@ -24622,6 +24625,7 @@ package android.media {
method public void setOnEventListener(android.media.MediaDrm.OnEventListener);
method public void setOnExpirationUpdateListener(android.media.MediaDrm.OnExpirationUpdateListener, android.os.Handler);
method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler);
+ method public void setOnSessionLostStateListener(android.media.MediaDrm.OnSessionLostStateListener, android.os.Handler);
method public void setPropertyByteArray(java.lang.String, byte[]);
method public void setPropertyString(java.lang.String, java.lang.String);
field public static final deprecated int EVENT_KEY_EXPIRED = 3; // 0x3
@@ -24740,6 +24744,10 @@ package android.media {
method public abstract void onKeyStatusChange(android.media.MediaDrm, byte[], java.util.List<android.media.MediaDrm.KeyStatus>, boolean);
}
+ public static abstract interface MediaDrm.OnSessionLostStateListener {
+ method public abstract void onSessionLostState(android.media.MediaDrm, byte[]);
+ }
+
public static final class MediaDrm.ProvisionRequest {
method public byte[] getData();
method public java.lang.String getDefaultUrl();
@@ -24748,6 +24756,12 @@ package android.media {
public static abstract class MediaDrm.SecurityLevel implements java.lang.annotation.Annotation {
}
+ public static final class MediaDrm.SessionException extends java.lang.RuntimeException {
+ ctor public MediaDrm.SessionException(int, java.lang.String);
+ method public int getErrorCode();
+ field public static final int ERROR_RESOURCE_CONTENTION = 1; // 0x1
+ }
+
public class MediaDrmException extends java.lang.Exception {
ctor public MediaDrmException(java.lang.String);
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 0375de3cc496..bc9500ddb3b7 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2289,6 +2289,30 @@ final public class MediaCodec {
*/
public static final int ERROR_UNSUPPORTED_OPERATION = 6;
+ /**
+ * This indicates that the security level of the device is not
+ * sufficient to meet the requirements set by the content owner
+ * in the license policy.
+ */
+ public static final int ERROR_INSUFFICIENT_SECURITY = 7;
+
+ /**
+ * This indicates that the video frame being decrypted exceeds
+ * the size of the device's protected output buffers. When
+ * encountering this error the app should try playing content
+ * of a lower resolution.
+ */
+ public static final int ERROR_FRAME_TOO_LARGE = 8;
+
+ /**
+ * This error indicates that session state has been
+ * invalidated. It can occur on devices that are not capable
+ * of retaining crypto session state across device
+ * suspend/resume. The session must be closed and a new
+ * session opened to resume operation.
+ */
+ public static final int ERROR_LOST_STATE = 9;
+
/** @hide */
@IntDef({
ERROR_NO_KEY,
@@ -2296,7 +2320,10 @@ final public class MediaCodec {
ERROR_RESOURCE_BUSY,
ERROR_INSUFFICIENT_OUTPUT_PROTECTION,
ERROR_SESSION_NOT_OPENED,
- ERROR_UNSUPPORTED_OPERATION
+ ERROR_UNSUPPORTED_OPERATION,
+ ERROR_INSUFFICIENT_SECURITY,
+ ERROR_FRAME_TOO_LARGE,
+ ERROR_LOST_STATE
})
@Retention(RetentionPolicy.SOURCE)
public @interface CryptoErrorCode {}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index cdbc7b44f905..75b391534dd4 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -132,11 +132,19 @@ public final class MediaDrm implements AutoCloseable {
private static final String PERMISSION = android.Manifest.permission.ACCESS_DRM_CERTIFICATES;
private EventHandler mEventHandler;
- private EventHandler mOnKeyStatusChangeEventHandler;
- private EventHandler mOnExpirationUpdateEventHandler;
+ private EventHandler mKeyStatusChangeHandler;
+ private EventHandler mExpirationUpdateHandler;
+ private EventHandler mSessionLostStateHandler;
+
private OnEventListener mOnEventListener;
private OnKeyStatusChangeListener mOnKeyStatusChangeListener;
private OnExpirationUpdateListener mOnExpirationUpdateListener;
+ private OnSessionLostStateListener mOnSessionLostStateListener;
+
+ private final Object mEventLock = new Object();
+ private final Object mKeyStatusChangeLock = new Object();
+ private final Object mExpirationUpdateLock = new Object();
+ private final Object mSessionLostStateLock = new Object();
private long mNativeContext;
@@ -200,6 +208,35 @@ public final class MediaDrm implements AutoCloseable {
private static final native boolean isCryptoSchemeSupportedNative(
@NonNull byte[] uuid, @Nullable String mimeType);
+ private EventHandler createHandler() {
+ Looper looper;
+ EventHandler handler;
+ if ((looper = Looper.myLooper()) != null) {
+ handler = new EventHandler(this, looper);
+ } else if ((looper = Looper.getMainLooper()) != null) {
+ handler = new EventHandler(this, looper);
+ } else {
+ handler = null;
+ }
+ return handler;
+ }
+
+ private EventHandler updateHandler(Handler handler) {
+ Looper looper;
+ EventHandler newHandler = null;
+ if (handler != null) {
+ looper = handler.getLooper();
+ } else {
+ looper = Looper.myLooper();
+ }
+ if (looper != null) {
+ if (handler == null || handler.getLooper() != looper) {
+ newHandler = new EventHandler(this, looper);
+ }
+ }
+ return newHandler;
+ }
+
/**
* Instantiate a MediaDrm object
*
@@ -209,14 +246,10 @@ public final class MediaDrm implements AutoCloseable {
* specified scheme UUID
*/
public MediaDrm(@NonNull UUID uuid) throws UnsupportedSchemeException {
- Looper looper;
- if ((looper = Looper.myLooper()) != null) {
- mEventHandler = new EventHandler(this, looper);
- } else if ((looper = Looper.getMainLooper()) != null) {
- mEventHandler = new EventHandler(this, looper);
- } else {
- mEventHandler = null;
- }
+ mEventHandler = createHandler();
+ mKeyStatusChangeHandler = createHandler();
+ mExpirationUpdateHandler = createHandler();
+ mSessionLostStateHandler = createHandler();
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
@@ -272,6 +305,40 @@ public final class MediaDrm implements AutoCloseable {
}
/**
+ * Thrown when an error occurs in any method that has a session context.
+ */
+ public static final class SessionException extends RuntimeException {
+ public SessionException(int errorCode, @Nullable String detailMessage) {
+ super(detailMessage);
+ mErrorCode = errorCode;
+ }
+
+ /**
+ * This indicates that apps using MediaDrm sessions are
+ * temporarily exceeding the capacity of available crypto
+ * resources. The app should retry the operation later.
+ */
+ public static final int ERROR_RESOURCE_CONTENTION = 1;
+
+ /** @hide */
+ @IntDef({
+ ERROR_RESOURCE_CONTENTION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SessionErrorCode {}
+
+ /**
+ * Retrieve the error code associated with the SessionException
+ */
+ @SessionErrorCode
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+
+ private final int mErrorCode;
+ }
+
+ /**
* Register a callback to be invoked when a session expiration update
* occurs. The app's OnExpirationUpdateListener will be notified
* when the expiration time of the keys in the session have changed.
@@ -282,15 +349,12 @@ public final class MediaDrm implements AutoCloseable {
*/
public void setOnExpirationUpdateListener(
@Nullable OnExpirationUpdateListener listener, @Nullable Handler handler) {
- if (listener != null) {
- Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
- if (looper != null) {
- if (mEventHandler == null || mEventHandler.getLooper() != looper) {
- mEventHandler = new EventHandler(this, looper);
- }
+ synchronized(mExpirationUpdateLock) {
+ if (listener != null) {
+ mExpirationUpdateHandler = updateHandler(handler);
}
+ mOnExpirationUpdateListener = listener;
}
- mOnExpirationUpdateListener = listener;
}
/**
@@ -324,15 +388,12 @@ public final class MediaDrm implements AutoCloseable {
*/
public void setOnKeyStatusChangeListener(
@Nullable OnKeyStatusChangeListener listener, @Nullable Handler handler) {
- if (listener != null) {
- Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
- if (looper != null) {
- if (mEventHandler == null || mEventHandler.getLooper() != looper) {
- mEventHandler = new EventHandler(this, looper);
- }
+ synchronized(mKeyStatusChangeLock) {
+ if (listener != null) {
+ mKeyStatusChangeHandler = updateHandler(handler);
}
+ mOnKeyStatusChangeListener = listener;
}
- mOnKeyStatusChangeListener = listener;
}
/**
@@ -360,6 +421,46 @@ public final class MediaDrm implements AutoCloseable {
}
/**
+ * Register a callback to be invoked when session state has been
+ * lost. This event can occur on devices that are not capable of
+ * retaining crypto session state across device suspend/resume
+ * cycles. When this event occurs, the session must be closed and
+ * a new session opened to resume operation.
+ *
+ * @param listener the callback that will be run, or {@code null} to unregister the
+ * previously registered callback.
+ * @param handler the handler on which the listener should be invoked, or
+ * {@code null} if the listener should be invoked on the calling thread's looper.
+ */
+ public void setOnSessionLostStateListener(
+ @Nullable OnSessionLostStateListener listener, @Nullable Handler handler) {
+ synchronized(mSessionLostStateLock) {
+ if (listener != null) {
+ mSessionLostStateHandler = updateHandler(handler);
+ }
+ mOnSessionLostStateListener = listener;
+ }
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the
+ * session state has been lost and is now invalid
+ */
+ public interface OnSessionLostStateListener
+ {
+ /**
+ * Called when session state has lost state, to inform the app
+ * about the condition so it can close the session and open a new
+ * one to resume operation.
+ *
+ * @param md the MediaDrm object on which the event occurred
+ * @param sessionId the DRM session ID on which the event occurred
+ */
+ void onSessionLostState(
+ @NonNull MediaDrm md, @NonNull byte[] sessionId);
+ }
+
+ /**
* Defines the status of a key.
* A KeyStatus for each key in a session is provided to the
* {@link OnKeyStatusChangeListener#onKeyStatusChange}
@@ -437,7 +538,9 @@ public final class MediaDrm implements AutoCloseable {
*/
public void setOnEventListener(@Nullable OnEventListener listener)
{
- mOnEventListener = listener;
+ synchronized(mEventLock) {
+ mOnEventListener = listener;
+ }
}
/**
@@ -513,6 +616,7 @@ public final class MediaDrm implements AutoCloseable {
private static final int DRM_EVENT = 200;
private static final int EXPIRATION_UPDATE = 201;
private static final int KEY_STATUS_CHANGE = 202;
+ private static final int SESSION_LOST_STATE = 203;
private class EventHandler extends Handler
{
@@ -532,52 +636,72 @@ public final class MediaDrm implements AutoCloseable {
switch(msg.what) {
case DRM_EVENT:
- if (mOnEventListener != null) {
- if (msg.obj != null && msg.obj instanceof Parcel) {
- Parcel parcel = (Parcel)msg.obj;
- byte[] sessionId = parcel.createByteArray();
- if (sessionId.length == 0) {
- sessionId = null;
- }
- byte[] data = parcel.createByteArray();
- if (data.length == 0) {
- data = null;
+ synchronized(mEventLock) {
+ if (mOnEventListener != null) {
+ if (msg.obj != null && msg.obj instanceof Parcel) {
+ Parcel parcel = (Parcel)msg.obj;
+ byte[] sessionId = parcel.createByteArray();
+ if (sessionId.length == 0) {
+ sessionId = null;
+ }
+ byte[] data = parcel.createByteArray();
+ if (data.length == 0) {
+ data = null;
+ }
+
+ Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")");
+ mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
}
-
- Log.i(TAG, "Drm event (" + msg.arg1 + "," + msg.arg2 + ")");
- mOnEventListener.onEvent(mMediaDrm, sessionId, msg.arg1, msg.arg2, data);
}
}
return;
case KEY_STATUS_CHANGE:
- if (mOnKeyStatusChangeListener != null) {
- if (msg.obj != null && msg.obj instanceof Parcel) {
- Parcel parcel = (Parcel)msg.obj;
- byte[] sessionId = parcel.createByteArray();
- if (sessionId.length > 0) {
- List<KeyStatus> keyStatusList = keyStatusListFromParcel(parcel);
- boolean hasNewUsableKey = (parcel.readInt() != 0);
-
- Log.i(TAG, "Drm key status changed");
- mOnKeyStatusChangeListener.onKeyStatusChange(mMediaDrm, sessionId,
- keyStatusList, hasNewUsableKey);
+ synchronized(mKeyStatusChangeLock) {
+ if (mOnKeyStatusChangeListener != null) {
+ if (msg.obj != null && msg.obj instanceof Parcel) {
+ Parcel parcel = (Parcel)msg.obj;
+ byte[] sessionId = parcel.createByteArray();
+ if (sessionId.length > 0) {
+ List<KeyStatus> keyStatusList = keyStatusListFromParcel(parcel);
+ boolean hasNewUsableKey = (parcel.readInt() != 0);
+
+ Log.i(TAG, "Drm key status changed");
+ mOnKeyStatusChangeListener.onKeyStatusChange(mMediaDrm, sessionId,
+ keyStatusList, hasNewUsableKey);
+ }
}
}
}
return;
case EXPIRATION_UPDATE:
- if (mOnExpirationUpdateListener != null) {
- if (msg.obj != null && msg.obj instanceof Parcel) {
- Parcel parcel = (Parcel)msg.obj;
- byte[] sessionId = parcel.createByteArray();
- if (sessionId.length > 0) {
- long expirationTime = parcel.readLong();
-
- Log.i(TAG, "Drm key expiration update: " + expirationTime);
- mOnExpirationUpdateListener.onExpirationUpdate(mMediaDrm, sessionId,
- expirationTime);
+ synchronized(mExpirationUpdateLock) {
+ if (mOnExpirationUpdateListener != null) {
+ if (msg.obj != null && msg.obj instanceof Parcel) {
+ Parcel parcel = (Parcel)msg.obj;
+ byte[] sessionId = parcel.createByteArray();
+ if (sessionId.length > 0) {
+ long expirationTime = parcel.readLong();
+
+ Log.i(TAG, "Drm key expiration update: " + expirationTime);
+ mOnExpirationUpdateListener.onExpirationUpdate(mMediaDrm, sessionId,
+ expirationTime);
+ }
+ }
+ }
+ }
+ return;
+
+ case SESSION_LOST_STATE:
+ synchronized(mSessionLostStateLock) {
+ if (mOnSessionLostStateListener != null) {
+ if (msg.obj != null && msg.obj instanceof Parcel) {
+ Parcel parcel = (Parcel)msg.obj;
+ byte[] sessionId = parcel.createByteArray();
+ Log.i(TAG, "Drm session lost state event: ");
+ mOnSessionLostStateListener.onSessionLostState(mMediaDrm,
+ sessionId);
}
}
}
@@ -619,9 +743,42 @@ public final class MediaDrm implements AutoCloseable {
if (md == null) {
return;
}
- if (md.mEventHandler != null) {
- Message m = md.mEventHandler.obtainMessage(what, eventType, extra, obj);
- md.mEventHandler.sendMessage(m);
+ switch (what) {
+ case DRM_EVENT:
+ synchronized(md.mEventLock) {
+ if (md.mEventHandler != null) {
+ Message m = md.mEventHandler.obtainMessage(what, eventType, extra, obj);
+ md.mEventHandler.sendMessage(m);
+ }
+ }
+ break;
+ case EXPIRATION_UPDATE:
+ synchronized(md.mExpirationUpdateLock) {
+ if (md.mExpirationUpdateHandler != null) {
+ Message m = md.mExpirationUpdateHandler.obtainMessage(what, obj);
+ md.mExpirationUpdateHandler.sendMessage(m);
+ }
+ }
+ break;
+ case KEY_STATUS_CHANGE:
+ synchronized(md.mKeyStatusChangeLock) {
+ if (md.mKeyStatusChangeHandler != null) {
+ Message m = md.mKeyStatusChangeHandler.obtainMessage(what, obj);
+ md.mKeyStatusChangeHandler.sendMessage(m);
+ }
+ }
+ break;
+ case SESSION_LOST_STATE:
+ synchronized(md.mSessionLostStateLock) {
+ if (md.mSessionLostStateHandler != null) {
+ Message m = md.mSessionLostStateHandler.obtainMessage(what, obj);
+ md.mSessionLostStateHandler.sendMessage(m);
+ }
+ }
+ break;
+ default:
+ Log.e(TAG, "Unknown message type " + what);
+ break;
}
}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 257860890437..7b07bea3cf1a 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -72,7 +72,10 @@ static struct CryptoErrorCodes {
jint cryptoErrorResourceBusy;
jint cryptoErrorInsufficientOutputProtection;
jint cryptoErrorSessionNotOpened;
+ jint cryptoErrorInsufficientSecurity;
jint cryptoErrorUnsupportedOperation;
+ jint cryptoErrorFrameTooLarge;
+ jint cryptoErrorLostState;
} gCryptoErrorCodes;
static struct CodecActionCodes {
@@ -1005,10 +1008,22 @@ static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
err = gCryptoErrorCodes.cryptoErrorSessionNotOpened;
defaultMsg = "Attempted to use a closed session";
break;
+ case ERROR_DRM_INSUFFICIENT_SECURITY:
+ err = gCryptoErrorCodes.cryptoErrorInsufficientSecurity;
+ defaultMsg = "Required security level is not met";
+ break;
case ERROR_DRM_CANNOT_HANDLE:
err = gCryptoErrorCodes.cryptoErrorUnsupportedOperation;
defaultMsg = "Operation not supported in this configuration";
break;
+ case ERROR_DRM_FRAME_TOO_LARGE:
+ err = gCryptoErrorCodes.cryptoErrorFrameTooLarge;
+ defaultMsg = "Decrytped frame exceeds size of output buffer";
+ break;
+ case ERROR_DRM_SESSION_LOST_STATE:
+ err = gCryptoErrorCodes.cryptoErrorLostState;
+ defaultMsg = "Session state was lost, open a new session and retry";
+ break;
default: /* Other negative DRM error codes go out as is. */
break;
}
@@ -1994,11 +2009,26 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) {
gCryptoErrorCodes.cryptoErrorSessionNotOpened =
env->GetStaticIntField(clazz.get(), field);
+ field = env->GetStaticFieldID(clazz.get(), "ERROR_INSUFFICIENT_SECURITY", "I");
+ CHECK(field != NULL);
+ gCryptoErrorCodes.cryptoErrorInsufficientSecurity =
+ env->GetStaticIntField(clazz.get(), field);
+
field = env->GetStaticFieldID(clazz.get(), "ERROR_UNSUPPORTED_OPERATION", "I");
CHECK(field != NULL);
gCryptoErrorCodes.cryptoErrorUnsupportedOperation =
env->GetStaticIntField(clazz.get(), field);
+ field = env->GetStaticFieldID(clazz.get(), "ERROR_FRAME_TOO_LARGE", "I");
+ CHECK(field != NULL);
+ gCryptoErrorCodes.cryptoErrorFrameTooLarge =
+ env->GetStaticIntField(clazz.get(), field);
+
+ field = env->GetStaticFieldID(clazz.get(), "ERROR_LOST_STATE", "I");
+ CHECK(field != NULL);
+ gCryptoErrorCodes.cryptoErrorLostState =
+ env->GetStaticIntField(clazz.get(), field);
+
clazz.reset(env->FindClass("android/media/MediaCodec$CodecException"));
CHECK(clazz.get() != NULL);
field = env->GetStaticFieldID(clazz.get(), "ACTION_TRANSIENT", "I");
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index be71dad571bb..83364590039d 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -110,6 +110,7 @@ struct EventWhat {
jint kWhatDrmEvent;
jint kWhatExpirationUpdate;
jint kWhatKeyStatusChange;
+ jint kWhatSessionLostState;
} gEventWhat;
struct KeyTypes {
@@ -141,6 +142,16 @@ struct StateExceptionFields {
jclass classId;
};
+struct SessionExceptionFields {
+ jmethodID init;
+ jclass classId;
+ jfieldID errorCode;
+};
+
+struct SessionExceptionErrorCodes {
+ jint kResourceContention;
+} gSessionExceptionErrorCodes;
+
struct HDCPLevels {
jint kHdcpLevelUnknown;
jint kHdcpNone;
@@ -180,6 +191,7 @@ struct fields_t {
EntryFields entry;
CertificateFields certificate;
StateExceptionFields stateException;
+ SessionExceptionFields sessionException;
jclass certificateClassId;
jclass hashmapClassId;
jclass arraylistClassId;
@@ -310,6 +322,9 @@ void JNIDrmListener::notify(DrmPlugin::EventType eventType, int extra,
case DrmPlugin::kDrmPluginEventKeysChange:
jwhat = gEventWhat.kWhatKeyStatusChange;
break;
+ case DrmPlugin::kDrmPluginEventSessionLostState:
+ jwhat = gEventWhat.kWhatSessionLostState;
+ break;
default:
ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType);
return;
@@ -343,6 +358,30 @@ static void throwStateException(JNIEnv *env, const char *msg, status_t err) {
env->Throw(static_cast<jthrowable>(exception));
}
+static void throwSessionException(JNIEnv *env, const char *msg, status_t err) {
+ ALOGE("Session exception: %s (%d)", msg, err);
+
+ jint jErrorCode = 0;
+ switch(err) {
+ case ERROR_DRM_RESOURCE_CONTENTION:
+ jErrorCode = gSessionExceptionErrorCodes.kResourceContention;
+ break;
+ default:
+ break;
+ }
+
+ jobject exception = env->NewObject(gFields.sessionException.classId,
+ gFields.sessionException.init, static_cast<int>(err),
+ env->NewStringUTF(msg));
+
+ env->SetIntField(exception, gFields.sessionException.errorCode, jErrorCode);
+ env->Throw(static_cast<jthrowable>(exception));
+}
+
+static bool isSessionException(status_t err) {
+ return err == ERROR_DRM_RESOURCE_CONTENTION;
+}
+
static bool throwExceptionAsNecessary(
JNIEnv *env, status_t err, const char *msg = NULL) {
@@ -370,7 +409,7 @@ static bool throwExceptionAsNecessary(
case ERROR_DRM_CANNOT_HANDLE:
drmMessage = "Invalid parameter or data format";
break;
- case ERROR_DRM_TAMPER_DETECTED:
+ case ERROR_DRM_INVALID_STATE:
drmMessage = "Invalid state";
break;
default:
@@ -399,6 +438,9 @@ static bool throwExceptionAsNecessary(
jniThrowException(env, "android/media/MediaDrmResetException",
"mediaserver died");
return true;
+ } else if (isSessionException(err)) {
+ throwSessionException(env, msg, err);
+ return true;
} else if (err != OK) {
String8 errbuf;
if (drmMessage != NULL) {
@@ -705,6 +747,8 @@ static void android_media_MediaDrm_native_init(JNIEnv *env) {
gEventWhat.kWhatExpirationUpdate = env->GetStaticIntField(clazz, field);
GET_STATIC_FIELD_ID(field, clazz, "KEY_STATUS_CHANGE", "I");
gEventWhat.kWhatKeyStatusChange = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "SESSION_LOST_STATE", "I");
+ gEventWhat.kWhatSessionLostState = env->GetStaticIntField(clazz, field);
GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_STREAMING", "I");
gKeyTypes.kKeyTypeStreaming = env->GetStaticIntField(clazz, field);
@@ -831,6 +875,14 @@ static void android_media_MediaDrm_native_init(JNIEnv *env) {
FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
GET_METHOD_ID(gFields.stateException.init, clazz, "<init>", "(ILjava/lang/String;)V");
gFields.stateException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
+
+ FIND_CLASS(clazz, "android/media/MediaDrm$SessionException");
+ GET_METHOD_ID(gFields.sessionException.init, clazz, "<init>", "(ILjava/lang/String;)V");
+ gFields.sessionException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
+ GET_FIELD_ID(gFields.sessionException.errorCode, clazz, "mErrorCode", "I");
+
+ GET_STATIC_FIELD_ID(field, clazz, "ERROR_RESOURCE_CONTENTION", "I");
+ gSessionExceptionErrorCodes.kResourceContention = env->GetStaticIntField(clazz, field);
}
static void android_media_MediaDrm_native_setup(