New JAVA API to support submitting encrypted buffers of input data.

Change-Id: Ib0df9a9427b4580946179860495b26f743558597
related-to-bug: 6275919
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 66cea9d4..410383d 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -45,10 +45,16 @@
         public int mFlags;
     };
 
+    // The follow flag constants MUST stay in sync with their equivalents
+    // in MediaCodec.h !
     public static int FLAG_SYNCFRAME   = 1;
     public static int FLAG_CODECCONFIG = 2;
     public static int FLAG_EOS         = 4;
-    public static int FLAG_ENCRYPTED   = 8;
+
+    // The following mode constants MUST stay in sync with their equivalents
+    // in media/hardware/CryptoAPI.h !
+    public static int MODE_UNENCRYPTED = 0;
+    public static int MODE_AES_CTR     = 1;
 
     /** Instantiate a codec component by mime type. For decoder components
         this is the mime type of media that this decoder should be able to
@@ -176,6 +182,36 @@
             int index,
             int offset, int size, long presentationTimeUs, int flags);
 
+    /** Similar to {@link queueInputBuffer} but submits a buffer that is
+     *  potentially encrypted. The buffer's data is considered to be
+     *  partitioned into "subSamples", each subSample starts with a
+     *  (potentially empty) run of plain, unencrypted bytes followed
+     *  by a (also potentially empty) run of encrypted bytes.
+     *  @param numBytesOfClearData The number of leading unencrypted bytes in
+     *                             each subSample.
+     *  @param numBytesOfEncryptedData The number of trailing encrypted bytes
+     *                             in each subSample.
+     *  @param numSubSamples    The number of subSamples that make up the
+     *                          buffer's contents.
+     *  @param key              A 16-byte opaque key
+     *  @param iv               A 16-byte initialization vector
+     *  @param mode             The type of encryption that has been applied
+     *
+     *  Either numBytesOfClearData or numBytesOfEncryptedData (but not both)
+     *  can be null to indicate that all respective sizes are 0.
+     */
+    public native final void queueSecureInputBuffer(
+            int index,
+            int offset,
+            int[] numBytesOfClearData,
+            int[] numBytesOfEncryptedData,
+            int numSubSamples,
+            byte[] key,
+            byte[] iv,
+            int mode,
+            long presentationTimeUs,
+            int flags);
+
     // Returns the index of an input buffer to be filled with valid data
     // or -1 if no such buffer is currently available.
     // This method will return immediately if timeoutUs == 0, wait indefinitely
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 217216a..01d3833 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -126,6 +126,21 @@
     return mCodec->queueInputBuffer(index, offset, size, timeUs, flags);
 }
 
+status_t JMediaCodec::queueSecureInputBuffer(
+        size_t index,
+        size_t offset,
+        const CryptoPlugin::SubSample *subSamples,
+        size_t numSubSamples,
+        const uint8_t key[16],
+        const uint8_t iv[16],
+        CryptoPlugin::Mode mode,
+        int64_t presentationTimeUs,
+        uint32_t flags) {
+    return mCodec->queueSecureInputBuffer(
+            index, offset, subSamples, numSubSamples, key, iv, mode,
+            presentationTimeUs, flags);
+}
+
 status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
     return mCodec->dequeueInputBuffer(index, timeoutUs);
 }
@@ -367,6 +382,125 @@
     throwExceptionAsNecessary(env, err);
 }
 
+static void android_media_MediaCodec_queueSecureInputBuffer(
+        JNIEnv *env,
+        jobject thiz,
+        jint index,
+        jint offset,
+        jintArray numBytesOfClearDataObj,
+        jintArray numBytesOfEncryptedDataObj,
+        jint numSubSamples,
+        jbyteArray keyObj,
+        jbyteArray ivObj,
+        jint mode,
+        jlong timestampUs,
+        jint flags) {
+    ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
+
+    sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+    if (codec == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    status_t err = OK;
+
+    CryptoPlugin::SubSample *subSamples = NULL;
+    jbyte *key = NULL;
+    jbyte *iv = NULL;
+
+    if (numSubSamples <= 0) {
+        err = -EINVAL;
+    } else if (numBytesOfClearDataObj == NULL
+            && numBytesOfEncryptedDataObj == NULL) {
+        err = -EINVAL;
+    } else if (numBytesOfEncryptedDataObj != NULL
+            && env->GetArrayLength(numBytesOfEncryptedDataObj) < numSubSamples) {
+        err = -ERANGE;
+    } else if (numBytesOfClearDataObj != NULL
+            && env->GetArrayLength(numBytesOfClearDataObj) < numSubSamples) {
+        err = -ERANGE;
+    } else {
+        jboolean isCopy;
+
+        jint *numBytesOfClearData =
+            (numBytesOfClearDataObj == NULL)
+                ? NULL
+                : env->GetIntArrayElements(numBytesOfClearDataObj, &isCopy);
+
+        jint *numBytesOfEncryptedData =
+            (numBytesOfEncryptedDataObj == NULL)
+                ? NULL
+                : env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
+
+        subSamples = new CryptoPlugin::SubSample[numSubSamples];
+
+        for (jint i = 0; i < numSubSamples; ++i) {
+            subSamples[i].mNumBytesOfClearData =
+                (numBytesOfClearData == NULL) ? 0 : numBytesOfClearData[i];
+
+            subSamples[i].mNumBytesOfEncryptedData =
+                (numBytesOfEncryptedData == NULL)
+                    ? 0 : numBytesOfEncryptedData[i];
+        }
+
+        if (numBytesOfEncryptedData != NULL) {
+            env->ReleaseIntArrayElements(
+                    numBytesOfEncryptedDataObj, numBytesOfEncryptedData, 0);
+            numBytesOfEncryptedData = NULL;
+        }
+
+        if (numBytesOfClearData != NULL) {
+            env->ReleaseIntArrayElements(
+                    numBytesOfClearDataObj, numBytesOfClearData, 0);
+            numBytesOfClearData = NULL;
+        }
+    }
+
+    if (err == OK && keyObj != NULL) {
+        if (env->GetArrayLength(keyObj) != 16) {
+            err = -EINVAL;
+        } else {
+            jboolean isCopy;
+            key = env->GetByteArrayElements(keyObj, &isCopy);
+        }
+    }
+
+    if (err == OK && ivObj != NULL) {
+        if (env->GetArrayLength(ivObj) != 16) {
+            err = -EINVAL;
+        } else {
+            jboolean isCopy;
+            iv = env->GetByteArrayElements(ivObj, &isCopy);
+        }
+    }
+
+    if (err == OK) {
+        err = codec->queueSecureInputBuffer(
+                index, offset,
+                subSamples, numSubSamples,
+                (const uint8_t *)key, (const uint8_t *)iv,
+                (CryptoPlugin::Mode)mode,
+                timestampUs, flags);
+    }
+
+    if (iv != NULL) {
+        env->ReleaseByteArrayElements(ivObj, iv, 0);
+        iv = NULL;
+    }
+
+    if (key != NULL) {
+        env->ReleaseByteArrayElements(keyObj, key, 0);
+        key = NULL;
+    }
+
+    delete[] subSamples;
+    subSamples = NULL;
+
+    throwExceptionAsNecessary(env, err);
+}
+
 static jint android_media_MediaCodec_dequeueInputBuffer(
         JNIEnv *env, jobject thiz, jlong timeoutUs) {
     ALOGV("android_media_MediaCodec_dequeueInputBuffer");
@@ -532,6 +666,9 @@
     { "queueInputBuffer", "(IIIJI)V",
       (void *)android_media_MediaCodec_queueInputBuffer },
 
+    { "queueSecureInputBuffer", "(II[I[II[B[BIJI)V",
+      (void *)android_media_MediaCodec_queueSecureInputBuffer },
+
     { "dequeueInputBuffer", "(J)I",
       (void *)android_media_MediaCodec_dequeueInputBuffer },
 
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 6bb4071..570c33b 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -19,6 +19,7 @@
 
 #include "jni.h"
 
+#include <media/hardware/CryptoAPI.h>
 #include <media/stagefright/foundation/ABase.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
@@ -53,6 +54,17 @@
             size_t index,
             size_t offset, size_t size, int64_t timeUs, uint32_t flags);
 
+    status_t queueSecureInputBuffer(
+            size_t index,
+            size_t offset,
+            const CryptoPlugin::SubSample *subSamples,
+            size_t numSubSamples,
+            const uint8_t key[16],
+            const uint8_t iv[16],
+            CryptoPlugin::Mode mode,
+            int64_t presentationTimeUs,
+            uint32_t flags);
+
     status_t dequeueInputBuffer(size_t *index, int64_t timeoutUs);
 
     status_t dequeueOutputBuffer(