DRM errors signaled by the CryptoPlugin are now visible to MediaCodec clients

through a custom exception "MediaCodec.CryptoException".

Change-Id: I30215e9e13bab68abad23e27dcead7c1accd07f1
related-to-bug: 6365261
diff --git a/api/current.txt b/api/current.txt
index afcca224..c7c83e4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10972,8 +10972,8 @@
     method public java.nio.ByteBuffer[] getInputBuffers();
     method public java.nio.ByteBuffer[] getOutputBuffers();
     method public final java.util.Map<java.lang.String, java.lang.Object> getOutputFormat();
-    method public final void queueInputBuffer(int, int, int, long, int);
-    method public final void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int);
+    method public final void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException;
+    method public final void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
     method public final void release();
     method public final void releaseOutputBuffer(int, boolean);
     method public final void start();
@@ -10998,6 +10998,11 @@
     field public int size;
   }
 
+  public static final class MediaCodec.CryptoException extends java.lang.RuntimeException {
+    ctor public MediaCodec.CryptoException(int, java.lang.String);
+    method public int getErrorCode();
+  }
+
   public static final class MediaCodec.CryptoInfo {
     ctor public MediaCodec.CryptoInfo();
     method public void set(int, int[], int[], byte[], byte[], int);
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 2efacd8..258760f 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -280,6 +280,19 @@
     */
     public native final void flush();
 
+    public final static class CryptoException extends RuntimeException {
+        public CryptoException(int errorCode, String detailMessage) {
+            super(detailMessage);
+            mErrorCode = errorCode;
+        }
+
+        public int getErrorCode() {
+            return mErrorCode;
+        }
+
+        private int mErrorCode;
+    }
+
     /** After filling a range of the input buffer at the specified index
      *  submit it to the component.
      *
@@ -304,10 +317,13 @@
      *  @param presentationTimeUs The time at which this buffer should be rendered.
      *  @param flags A bitmask of flags {@link #FLAG_SYNCFRAME},
      *               {@link #FLAG_CODECCONFIG} or {@link #FLAG_EOS}.
+     *  @throws CryptoException if a crypto object has been specified in
+     *          {@link #configure}
     */
     public native final void queueInputBuffer(
             int index,
-            int offset, int size, long presentationTimeUs, int flags);
+            int offset, int size, long presentationTimeUs, int flags)
+        throws CryptoException;
 
     /** Metadata describing the structure of a (at least partially) encrypted
      *  input sample.
@@ -361,7 +377,7 @@
             int offset,
             CryptoInfo info,
             long presentationTimeUs,
-            int flags);
+            int flags) throws CryptoException;
 
     /** Returns the index of an input buffer to be filled with valid data
      *  or -1 if no such buffer is currently available.
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index a120a2f..8009fb5 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -36,6 +36,7 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooper.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AString.h>
 #include <media/stagefright/MediaErrors.h>
 
 namespace android {
@@ -129,8 +130,10 @@
 
 status_t JMediaCodec::queueInputBuffer(
         size_t index,
-        size_t offset, size_t size, int64_t timeUs, uint32_t flags) {
-    return mCodec->queueInputBuffer(index, offset, size, timeUs, flags);
+        size_t offset, size_t size, int64_t timeUs, uint32_t flags,
+        AString *errorDetailMsg) {
+    return mCodec->queueInputBuffer(
+            index, offset, size, timeUs, flags, errorDetailMsg);
 }
 
 status_t JMediaCodec::queueSecureInputBuffer(
@@ -142,10 +145,11 @@
         const uint8_t iv[16],
         CryptoPlugin::Mode mode,
         int64_t presentationTimeUs,
-        uint32_t flags) {
+        uint32_t flags,
+        AString *errorDetailMsg) {
     return mCodec->queueSecureInputBuffer(
             index, offset, subSamples, numSubSamples, key, iv, mode,
-            presentationTimeUs, flags);
+            presentationTimeUs, flags, errorDetailMsg);
 }
 
 status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
@@ -251,7 +255,31 @@
     setMediaCodec(env, thiz, NULL);
 }
 
-static jint throwExceptionAsNecessary(JNIEnv *env, status_t err) {
+static void throwCryptoException(JNIEnv *env, status_t err, const char *msg) {
+    jclass clazz = env->FindClass("android/media/MediaCodec$CryptoException");
+    CHECK(clazz != NULL);
+
+    jmethodID constructID =
+        env->GetMethodID(clazz, "<init>", "(ILjava/lang/String;)V");
+    CHECK(constructID != NULL);
+
+    jstring msgObj = env->NewStringUTF(msg != NULL ? msg : "Unknown Error");
+
+    jthrowable exception =
+        (jthrowable)env->NewObject(clazz, constructID, err, msgObj);
+
+    env->Throw(exception);
+}
+
+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) {
+        // We'll throw our custom MediaCodec.CryptoException
+
+        throwCryptoException(env, err, msg);
+        return 0;
+    }
+
     switch (err) {
         case OK:
             return 0;
@@ -383,10 +411,13 @@
         return;
     }
 
-    status_t err = codec->queueInputBuffer(
-            index, offset, size, timestampUs, flags);
+    AString errorDetailMsg;
 
-    throwExceptionAsNecessary(env, err);
+    status_t err = codec->queueInputBuffer(
+            index, offset, size, timestampUs, flags, &errorDetailMsg);
+
+    throwExceptionAsNecessary(
+            env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
 }
 
 static void android_media_MediaCodec_queueSecureInputBuffer(
@@ -497,13 +528,17 @@
         }
     }
 
+    AString errorDetailMsg;
+
     if (err == OK) {
         err = codec->queueSecureInputBuffer(
                 index, offset,
                 subSamples, numSubSamples,
                 (const uint8_t *)key, (const uint8_t *)iv,
                 (CryptoPlugin::Mode)mode,
-                timestampUs, flags);
+                timestampUs,
+                flags,
+                &errorDetailMsg);
     }
 
     if (iv != NULL) {
@@ -519,7 +554,8 @@
     delete[] subSamples;
     subSamples = NULL;
 
-    throwExceptionAsNecessary(env, err);
+    throwExceptionAsNecessary(
+            env, err, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
 }
 
 static jint android_media_MediaCodec_dequeueInputBuffer(
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 570c33b..e2688be 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -28,6 +28,7 @@
 
 struct ALooper;
 struct AMessage;
+struct AString;
 struct ICrypto;
 struct ISurfaceTexture;
 struct MediaCodec;
@@ -52,7 +53,8 @@
 
     status_t queueInputBuffer(
             size_t index,
-            size_t offset, size_t size, int64_t timeUs, uint32_t flags);
+            size_t offset, size_t size, int64_t timeUs, uint32_t flags,
+            AString *errorDetailMsg);
 
     status_t queueSecureInputBuffer(
             size_t index,
@@ -63,7 +65,8 @@
             const uint8_t iv[16],
             CryptoPlugin::Mode mode,
             int64_t presentationTimeUs,
-            uint32_t flags);
+            uint32_t flags,
+            AString *errorDetailMsg);
 
     status_t dequeueInputBuffer(size_t *index, int64_t timeoutUs);