Information required to decrypt buffers is now packaged into MediaCodec.CryptoInfo

New API on MediaExtractor to retrieve a CryptoInfo structure where applicable.

Change-Id: I18edfc9ac56a4544c8f17cba24401b96dacbff7d
related-to-bug: 6275919
diff --git a/api/current.txt b/api/current.txt
index 8d12a56..fef1c83 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10960,7 +10960,7 @@
     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, int[], int[], int, byte[], byte[], int, long, int);
+    method public final void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int);
     method public final void release();
     method public final void releaseOutputBuffer(int, boolean);
     method public final void start();
@@ -10985,6 +10985,17 @@
     field public int size;
   }
 
+  public static final class MediaCodec.CryptoInfo {
+    ctor public MediaCodec.CryptoInfo();
+    method public void set(int, int[], int[], byte[], byte[], int);
+    field public byte[] iv;
+    field public byte[] key;
+    field public int mode;
+    field public int[] numBytesOfClearData;
+    field public int[] numBytesOfEncryptedData;
+    field public int numSubSamples;
+  }
+
   public final class MediaCodecList {
     method public static final int countCodecs();
     method public static final android.media.MediaCodecList.CodecCapabilities getCodecCapabilities(int, java.lang.String);
@@ -11016,6 +11027,7 @@
     ctor public MediaExtractor();
     method public boolean advance();
     method public int countTracks();
+    method public boolean getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo);
     method public int getSampleFlags();
     method public long getSampleTime();
     method public int getSampleTrackIndex();
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index a65c2aa..2efacd8 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -309,36 +309,57 @@
             int index,
             int offset, int size, long presentationTimeUs, int flags);
 
+    /** Metadata describing the structure of a (at least partially) encrypted
+     *  input sample.
+     *  A 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.
+     *  numBytesOfClearData can be null to indicate that all data is encrypted.
+    */
+    public final static class CryptoInfo {
+        public void set(
+                int newNumSubSamples,
+                int[] newNumBytesOfClearData,
+                int[] newNumBytesOfEncryptedData,
+                byte[] newKey,
+                byte[] newIV,
+                int newMode) {
+            numSubSamples = newNumSubSamples;
+            numBytesOfClearData = newNumBytesOfClearData;
+            numBytesOfEncryptedData = newNumBytesOfEncryptedData;
+            key = newKey;
+            iv = newIV;
+            mode = newMode;
+        }
+
+        /** The number of subSamples that make up the buffer's contents. */
+        public int numSubSamples;
+        /** The number of leading unencrypted bytes in each subSample. */
+        public int[] numBytesOfClearData;
+        /** The number of trailing encrypted bytes in each subSample. */
+        public int[] numBytesOfEncryptedData;
+        /** A 16-byte opaque key */
+        public byte[] key;
+        /** A 16-byte initialization vector */
+        public byte[] iv;
+        /** The type of encryption that has been applied */
+        public int mode;
+    };
+
     /** 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.
+     *  potentially encrypted.
      *  @param index The index of a client-owned input buffer previously returned
      *               in a call to {@link #dequeueInputBuffer}.
      *  @param offset The byte offset into the input buffer at which the data starts.
-     *  @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.
+     *  @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}.
      */
     public native final void queueSecureInputBuffer(
             int index,
             int offset,
-            int[] numBytesOfClearData,
-            int[] numBytesOfEncryptedData,
-            int numSubSamples,
-            byte[] key,
-            byte[] iv,
-            int mode,
+            CryptoInfo info,
             long presentationTimeUs,
             int flags);
 
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 3f8b2ca..3b17a7d 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -19,6 +19,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
+import android.media.MediaCodec;
 import android.net.Uri;
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -235,6 +236,15 @@
     /** Returns the current sample's flags. */
     public native int getSampleFlags();
 
+    /** If the sample flags indicate that the current sample is at least
+     *  partially encrypted, this call returns relevant information about
+     *  the structure of the sample data required for decryption.
+     *  @param info The android.media.MediaCodec.CryptoInfo structure
+     *              to be filled in.
+     *  @return true iff the sample flags contain {@link #SAMPLE_FLAG_ENCRYPTED}
+    */
+    public native boolean getSampleCryptoInfo(MediaCodec.CryptoInfo info);
+
     private static native final void native_init();
     private native final void native_setup();
     private native final void native_finalize();
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 979ffb0..a120a2f 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -49,6 +49,13 @@
 
 struct fields_t {
     jfieldID context;
+
+    jfieldID cryptoInfoNumSubSamplesID;
+    jfieldID cryptoInfoNumBytesOfClearDataID;
+    jfieldID cryptoInfoNumBytesOfEncryptedDataID;
+    jfieldID cryptoInfoKeyID;
+    jfieldID cryptoInfoIVID;
+    jfieldID cryptoInfoModeID;
 };
 
 static fields_t gFields;
@@ -387,12 +394,7 @@
         jobject thiz,
         jint index,
         jint offset,
-        jintArray numBytesOfClearDataObj,
-        jintArray numBytesOfEncryptedDataObj,
-        jint numSubSamples,
-        jbyteArray keyObj,
-        jbyteArray ivObj,
-        jint mode,
+        jobject cryptoInfoObj,
         jlong timestampUs,
         jint flags) {
     ALOGV("android_media_MediaCodec_queueSecureInputBuffer");
@@ -404,6 +406,25 @@
         return;
     }
 
+    jint numSubSamples =
+        env->GetIntField(cryptoInfoObj, gFields.cryptoInfoNumSubSamplesID);
+
+    jintArray numBytesOfClearDataObj =
+        (jintArray)env->GetObjectField(
+                cryptoInfoObj, gFields.cryptoInfoNumBytesOfClearDataID);
+
+    jintArray numBytesOfEncryptedDataObj =
+        (jintArray)env->GetObjectField(
+                cryptoInfoObj, gFields.cryptoInfoNumBytesOfEncryptedDataID);
+
+    jbyteArray keyObj =
+        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoKeyID);
+
+    jbyteArray ivObj =
+        (jbyteArray)env->GetObjectField(cryptoInfoObj, gFields.cryptoInfoIVID);
+
+    jint mode = env->GetIntField(cryptoInfoObj, gFields.cryptoInfoModeID);
+
     status_t err = OK;
 
     CryptoPlugin::SubSample *subSamples = NULL;
@@ -612,6 +633,30 @@
 
     gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
     CHECK(gFields.context != NULL);
+
+    clazz = env->FindClass("android/media/MediaCodec$CryptoInfo");
+    CHECK(clazz != NULL);
+
+    gFields.cryptoInfoNumSubSamplesID =
+        env->GetFieldID(clazz, "numSubSamples", "I");
+    CHECK(gFields.cryptoInfoNumSubSamplesID != NULL);
+
+    gFields.cryptoInfoNumBytesOfClearDataID =
+        env->GetFieldID(clazz, "numBytesOfClearData", "[I");
+    CHECK(gFields.cryptoInfoNumBytesOfClearDataID != NULL);
+
+    gFields.cryptoInfoNumBytesOfEncryptedDataID =
+        env->GetFieldID(clazz, "numBytesOfEncryptedData", "[I");
+    CHECK(gFields.cryptoInfoNumBytesOfEncryptedDataID != NULL);
+
+    gFields.cryptoInfoKeyID = env->GetFieldID(clazz, "key", "[B");
+    CHECK(gFields.cryptoInfoKeyID != NULL);
+
+    gFields.cryptoInfoIVID = env->GetFieldID(clazz, "iv", "[B");
+    CHECK(gFields.cryptoInfoIVID != NULL);
+
+    gFields.cryptoInfoModeID = env->GetFieldID(clazz, "mode", "I");
+    CHECK(gFields.cryptoInfoModeID != NULL);
 }
 
 static void android_media_MediaCodec_native_setup(
@@ -666,7 +711,7 @@
     { "queueInputBuffer", "(IIIJI)V",
       (void *)android_media_MediaCodec_queueInputBuffer },
 
-    { "queueSecureInputBuffer", "(II[I[II[B[BIJI)V",
+    { "queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
       (void *)android_media_MediaCodec_queueSecureInputBuffer },
 
     { "dequeueInputBuffer", "(J)I",
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index e2cdbca..adf0e66 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -30,12 +30,15 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
 #include <media/stagefright/NuMediaExtractor.h>
 
 namespace android {
 
 struct fields_t {
     jfieldID context;
+
+    jmethodID cryptoInfoSetID;
 };
 
 static fields_t gFields;
@@ -166,7 +169,32 @@
 }
 
 status_t JMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
-    return mImpl->getSampleFlags(sampleFlags);
+    *sampleFlags = 0;
+
+    sp<MetaData> meta;
+    status_t err = mImpl->getSampleMeta(&meta);
+
+    if (err != OK) {
+        return err;
+    }
+
+    int32_t val;
+    if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
+        (*sampleFlags) |= NuMediaExtractor::SAMPLE_FLAG_SYNC;
+    }
+
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
+        (*sampleFlags) |= NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED;
+    }
+
+    return OK;
+}
+
+status_t JMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
+    return mImpl->getSampleMeta(sampleMeta);
 }
 
 }  // namespace android
@@ -369,6 +397,110 @@
     return sampleFlags;
 }
 
+static jboolean android_media_MediaExtractor_getSampleCryptoInfo(
+        JNIEnv *env, jobject thiz, jobject cryptoInfoObj) {
+    sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+    if (extractor == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return -1ll;
+    }
+
+    sp<MetaData> meta;
+    status_t err = extractor->getSampleMeta(&meta);
+
+    if (err != OK) {
+        return false;
+    }
+
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
+        return false;
+    }
+
+    size_t numSubSamples = size / sizeof(size_t);
+
+    if (numSubSamples == 0) {
+        return false;
+    }
+
+    jintArray numBytesOfEncryptedDataObj = env->NewIntArray(numSubSamples);
+    jboolean isCopy;
+    jint *dst = env->GetIntArrayElements(numBytesOfEncryptedDataObj, &isCopy);
+    for (size_t i = 0; i < numSubSamples; ++i) {
+        dst[i] = ((const size_t *)data)[i];
+    }
+    env->ReleaseIntArrayElements(numBytesOfEncryptedDataObj, dst, 0);
+    dst = NULL;
+
+    size_t encSize = size;
+    jintArray numBytesOfPlainDataObj = NULL;
+    if (meta->findData(kKeyEncryptedSizes, &type, &data, &size)) {
+        if (size != encSize) {
+            // The two must be of the same length.
+            return false;
+        }
+
+        numBytesOfPlainDataObj = env->NewIntArray(numSubSamples);
+        jboolean isCopy;
+        jint *dst = env->GetIntArrayElements(numBytesOfPlainDataObj, &isCopy);
+        for (size_t i = 0; i < numSubSamples; ++i) {
+            dst[i] = ((const size_t *)data)[i];
+        }
+        env->ReleaseIntArrayElements(numBytesOfPlainDataObj, dst, 0);
+        dst = NULL;
+    }
+
+    jbyteArray keyObj = NULL;
+    if (meta->findData(kKeyCryptoKey, &type, &data, &size)) {
+        if (size != 16) {
+            // Keys must be 16 bytes in length.
+            return false;
+        }
+
+        keyObj = env->NewByteArray(size);
+        jboolean isCopy;
+        jbyte *dst = env->GetByteArrayElements(keyObj, &isCopy);
+        memcpy(dst, data, size);
+        env->ReleaseByteArrayElements(keyObj, dst, 0);
+        dst = NULL;
+    }
+
+    jbyteArray ivObj = NULL;
+    if (meta->findData(kKeyCryptoIV, &type, &data, &size)) {
+        if (size != 16) {
+            // IVs must be 16 bytes in length.
+            return false;
+        }
+
+        ivObj = env->NewByteArray(size);
+        jboolean isCopy;
+        jbyte *dst = env->GetByteArrayElements(ivObj, &isCopy);
+        memcpy(dst, data, size);
+        env->ReleaseByteArrayElements(ivObj, dst, 0);
+        dst = NULL;
+    }
+
+    int32_t mode;
+    if (!meta->findInt32(kKeyCryptoMode, &mode)) {
+        mode = 0;
+    }
+
+    env->CallVoidMethod(
+            cryptoInfoObj,
+            gFields.cryptoInfoSetID,
+            numSubSamples,
+            numBytesOfPlainDataObj,
+            numBytesOfEncryptedDataObj,
+            keyObj,
+            ivObj,
+            mode);
+
+    return true;
+}
+
 static void android_media_MediaExtractor_native_init(JNIEnv *env) {
     jclass clazz = env->FindClass("android/media/MediaExtractor");
     CHECK(clazz != NULL);
@@ -376,6 +508,12 @@
     gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
     CHECK(gFields.context != NULL);
 
+    clazz = env->FindClass("android/media/MediaCodec$CryptoInfo");
+    CHECK(clazz != NULL);
+
+    gFields.cryptoInfoSetID =
+        env->GetMethodID(clazz, "set", "(I[I[I[B[BI)V");
+
     DataSource::RegisterDefaultSniffers();
 }
 
@@ -485,6 +623,9 @@
     { "getSampleFlags", "()I",
         (void *)android_media_MediaExtractor_getSampleFlags },
 
+    { "getSampleCryptoInfo", "(Landroid/media/MediaCodec$CryptoInfo;)Z",
+        (void *)android_media_MediaExtractor_getSampleCryptoInfo },
+
     { "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
 
     { "native_setup", "()V",
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index 1aacea2..f7ce2ff 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -27,6 +27,7 @@
 
 namespace android {
 
+struct MetaData;
 struct NuMediaExtractor;
 
 struct JMediaExtractor : public RefBase {
@@ -50,6 +51,7 @@
     status_t getSampleTrackIndex(size_t *trackIndex);
     status_t getSampleTime(int64_t *sampleTimeUs);
     status_t getSampleFlags(uint32_t *sampleFlags);
+    status_t getSampleMeta(sp<MetaData> *sampleMeta);
 
 protected:
     virtual ~JMediaExtractor();