Define error codes for MediaCodec.CryptoException
Define specific failure cases so apps have the information they
need to deal with these conditions.
Also adds a new ResourceBusyException to MediaDrm
Change-Id: Iaecf269d58108f28179974b05671bf29b9fe4b7d
related-to-bug: 10157154
related-to-bug: 9695816
diff --git a/api/current.txt b/api/current.txt
index 6bb40e1..7756bc1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12029,6 +12029,9 @@
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_KEY_EXPIRED = 2; // 0x2
+ field public static final int ERROR_NO_KEY = 1; // 0x1
+ field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3
}
public static final class MediaCodec.CryptoInfo {
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 8cf0b4b..703eb27 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -300,6 +300,24 @@
mErrorCode = errorCode;
}
+ /**
+ * This indicates that no key has been set to perform the requested
+ * decrypt operation.
+ */
+ public static final int ERROR_NO_KEY = 1;
+
+ /**
+ * This indicates that the key used for decryption is no longer
+ * valid due to license term expiration.
+ */
+ public static final int ERROR_KEY_EXPIRED = 2;
+
+ /**
+ * This indicates that a required crypto resource was not able to be
+ * allocated while attempting the requested operation.
+ */
+ public static final int ERROR_RESOURCE_BUSY = 3;
+
public int getErrorCode() {
return mErrorCode;
}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 7677d8a1..cd97ad9 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -273,6 +273,7 @@
* Open a new session with the MediaDrm object. A session ID is returned.
*
* @throws NotProvisionedException if provisioning is needed
+ * @throws ResourceBusyException if required resources are in use
*/
public native byte[] openSession() throws NotProvisionedException;
@@ -379,6 +380,7 @@
* reprovisioning is required
* @throws DeniedByServerException if the response indicates that the
* server rejected the request
+ * @throws ResourceBusyException if required resources are in use
*/
public native byte[] provideKeyResponse(byte[] scope, byte[] response)
throws NotProvisionedException, DeniedByServerException;
diff --git a/media/java/android/media/ResourceBusyException.java b/media/java/android/media/ResourceBusyException.java
new file mode 100644
index 0000000..a5abe21
--- /dev/null
+++ b/media/java/android/media/ResourceBusyException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * Exception thrown when an operation on a MediaDrm object is attempted
+ * and hardware resources are not available, due to being in use.
+ */
+public final class ResourceBusyException extends MediaDrmException {
+ public ResourceBusyException(String detailMessage) {
+ super(detailMessage);
+ }
+}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index cd1d9ce..8689e19 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -49,9 +49,14 @@
DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED = -3,
};
+struct CryptoErrorCodes {
+ jint cryptoErrorNoKey;
+ jint cryptoErrorKeyExpired;
+ jint cryptoErrorResourceBusy;
+} gCryptoErrorCodes;
+
struct fields_t {
jfieldID context;
-
jfieldID cryptoInfoNumSubSamplesID;
jfieldID cryptoInfoNumBytesOfClearDataID;
jfieldID cryptoInfoNumBytesOfEncryptedDataID;
@@ -342,6 +347,21 @@
jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error");
+ /* translate OS errors to Java API CryptoException errorCodes */
+ switch (err) {
+ case ERROR_DRM_NO_LICENSE:
+ err = gCryptoErrorCodes.cryptoErrorNoKey;
+ break;
+ case ERROR_DRM_LICENSE_EXPIRED:
+ err = gCryptoErrorCodes.cryptoErrorKeyExpired;
+ break;
+ case ERROR_DRM_RESOURCE_BUSY:
+ err = gCryptoErrorCodes.cryptoErrorResourceBusy;
+ break;
+ default:
+ break;
+ }
+
jthrowable exception =
(jthrowable)env->NewObject(clazz, constructID, err, msgObj);
@@ -350,9 +370,8 @@
static jint throwExceptionAsNecessary(
JNIEnv *env, status_t err, const char *msg = NULL) {
- if (err >= ERROR_DRM_WV_VENDOR_MIN && err <= ERROR_DRM_WV_VENDOR_MAX) {
+ if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
// We'll throw our custom MediaCodec.CryptoException
-
throwCryptoException(env, err, msg);
return 0;
}
@@ -370,6 +389,12 @@
case INFO_OUTPUT_BUFFERS_CHANGED:
return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
+ case ERROR_DRM_NO_LICENSE:
+ case ERROR_DRM_LICENSE_EXPIRED:
+ case ERROR_DRM_RESOURCE_BUSY:
+ throwCryptoException(env, err, msg);
+ break;
+
default:
{
jniThrowException(env, "java/lang/IllegalStateException", msg);
@@ -852,6 +877,22 @@
gFields.cryptoInfoModeID = env->GetFieldID(clazz, "mode", "I");
CHECK(gFields.cryptoInfoModeID != NULL);
+
+ clazz = env->FindClass("android/media/MediaCodec$CryptoException");
+ CHECK(clazz != NULL);
+
+ jfieldID field;
+ field = env->GetStaticFieldID(clazz, "ERROR_NO_KEY", "I");
+ CHECK(field != NULL);
+ gCryptoErrorCodes.cryptoErrorNoKey = env->GetStaticIntField(clazz, field);
+
+ field = env->GetStaticFieldID(clazz, "ERROR_KEY_EXPIRED", "I");
+ CHECK(field != NULL);
+ gCryptoErrorCodes.cryptoErrorKeyExpired = env->GetStaticIntField(clazz, field);
+
+ field = env->GetStaticFieldID(clazz, "ERROR_RESOURCE_BUSY", "I");
+ CHECK(field != NULL);
+ gCryptoErrorCodes.cryptoErrorResourceBusy = env->GetStaticIntField(clazz, field);
}
static void android_media_MediaCodec_native_setup(
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 7799ca4..16a1e48 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -242,6 +242,9 @@
} else if (err == ERROR_DRM_NOT_PROVISIONED) {
jniThrowException(env, "android/media/NotProvisionedException", msg);
return true;
+ } else if (err == ERROR_DRM_RESOURCE_BUSY) {
+ jniThrowException(env, "android/media/ResourceBusyException", msg);
+ return true;
} else if (err == ERROR_DRM_DEVICE_REVOKED) {
jniThrowException(env, "android/media/DeniedByServerException", msg);
return true;