New Crypto JAVA class to facilitate decryption via MediaCodec.

Change-Id: Ic4e395faa84f003793c2804f2badabab9e7f1034
related-to-bug: 6275919
diff --git a/media/java/android/media/Crypto.java b/media/java/android/media/Crypto.java
new file mode 100644
index 0000000..43e34fb
--- /dev/null
+++ b/media/java/android/media/Crypto.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 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;
+
+/**
+ * Crypto class can be used in conjunction with MediaCodec to decode
+ * encrypted media data.
+ * @hide
+*/
+public final class Crypto {
+    public static final native boolean isCryptoSchemeSupported(byte[] uuid);
+
+    public Crypto(byte[] uuid, byte[] initData) {
+        native_setup(uuid, initData);
+    }
+
+    public final native boolean requiresSecureDecoderComponent(String mime);
+
+    @Override
+    protected void finalize() {
+        native_finalize();
+    }
+
+    public native final void release();
+    private static native final void native_init();
+    private native final void native_setup(byte[] uuid, byte[] initData);
+    private native final void native_finalize();
+
+    static {
+        System.loadLibrary("media_jni");
+        native_init();
+    }
+
+    private int mNativeContext;
+}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 7629d60..66cea9d4 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.media.Crypto;
 import android.view.Surface;
 import java.nio.ByteBuffer;
 import java.util.Map;
@@ -25,8 +26,7 @@
  * encoder/decoder components.
  * @hide
 */
-public class MediaCodec
-{
+final public class MediaCodec {
     /** Per buffer metadata includes an offset and size specifying
         the range of valid data in the associated codec buffer.
     */
@@ -113,11 +113,14 @@
      *
      *  @param surface Specify a surface on which to render the output of this
      *                 decoder.
+     *  @param crypto  Specify a crypto object to facilitate secure decryption
+     *                 of the media data.
      *  @param flags   Specify {@link #CONFIGURE_FLAG_ENCODE} to configure the
      *                 component as an encoder.
     */
     public void configure(
-            Map<String, Object> format, Surface surface, int flags) {
+            Map<String, Object> format,
+            Surface surface, Crypto crypto, int flags) {
         String[] keys = null;
         Object[] values = null;
 
@@ -133,11 +136,12 @@
             }
         }
 
-        native_configure(keys, values, surface, flags);
+        native_configure(keys, values, surface, crypto, flags);
     }
 
     private native final void native_configure(
-            String[] keys, Object[] values, Surface surface, int flags);
+            String[] keys, Object[] values,
+            Surface surface, Crypto crypto, int flags);
 
     /** After successfully configuring the component, call start. On return
      *  you can query the component for its input/output buffers.
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 9ea3d0e..9c3b6a7 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -23,8 +23,7 @@
  * MediaExtractor
  * @hide
 */
-public class MediaExtractor
-{
+final public class MediaExtractor {
     public MediaExtractor(String path) {
         native_setup(path);
     }
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index dd1e505..a3361d4 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -2,6 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
+    android_media_Crypto.cpp \
     android_media_MediaCodec.cpp \
     android_media_MediaCodecList.cpp \
     android_media_MediaExtractor.cpp \
diff --git a/media/jni/android_media_Crypto.cpp b/media/jni/android_media_Crypto.cpp
new file mode 100644
index 0000000..e1a60a1
--- /dev/null
+++ b/media/jni/android_media_Crypto.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2012, 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Crypto-JNI"
+#include <utils/Log.h>
+
+#include "android_media_Crypto.h"
+
+#include "android_runtime/AndroidRuntime.h"
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include <binder/IServiceManager.h>
+#include <media/ICrypto.h>
+#include <media/IMediaPlayerService.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+struct fields_t {
+    jfieldID context;
+};
+
+static fields_t gFields;
+
+static sp<JCrypto> getCrypto(JNIEnv *env, jobject thiz) {
+    return (JCrypto *)env->GetIntField(thiz, gFields.context);
+}
+
+JCrypto::JCrypto(
+        JNIEnv *env, jobject thiz,
+        const uint8_t uuid[16], const void *initData, size_t initSize) {
+    mObject = env->NewWeakGlobalRef(thiz);
+
+    mCrypto = MakeCrypto(uuid, initData, initSize);
+}
+
+JCrypto::~JCrypto() {
+    mCrypto.clear();
+
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+    env->DeleteWeakGlobalRef(mObject);
+    mObject = NULL;
+}
+
+// static
+sp<ICrypto> JCrypto::MakeCrypto() {
+    sp<IServiceManager> sm = defaultServiceManager();
+
+    sp<IBinder> binder =
+        sm->getService(String16("media.player"));
+
+    sp<IMediaPlayerService> service =
+        interface_cast<IMediaPlayerService>(binder);
+
+    if (service == NULL) {
+        return NULL;
+    }
+
+    sp<ICrypto> crypto = service->makeCrypto();
+
+    if (crypto == NULL || crypto->initCheck() != OK) {
+        return NULL;
+    }
+
+    return crypto;
+}
+
+// static
+sp<ICrypto> JCrypto::MakeCrypto(
+        const uint8_t uuid[16], const void *initData, size_t initSize) {
+    sp<ICrypto> crypto = MakeCrypto();
+
+    if (crypto == NULL) {
+        return NULL;
+    }
+
+    status_t err = crypto->createPlugin(uuid, initData, initSize);
+
+    if (err != OK) {
+        return NULL;
+    }
+
+    return crypto;
+}
+
+bool JCrypto::requiresSecureDecoderComponent(const char *mime) const {
+    if (mCrypto == NULL) {
+        return false;
+    }
+
+    return mCrypto->requiresSecureDecoderComponent(mime);
+}
+
+// static
+bool JCrypto::IsCryptoSchemeSupported(const uint8_t uuid[16]) {
+    sp<ICrypto> crypto = MakeCrypto();
+
+    if (crypto == NULL) {
+        return false;
+    }
+
+    return crypto->isCryptoSchemeSupported(uuid);
+}
+
+status_t JCrypto::initCheck() const {
+    return mCrypto == NULL ? NO_INIT : OK;
+}
+
+// static
+sp<ICrypto> JCrypto::GetCrypto(JNIEnv *env, jobject obj) {
+    jclass clazz = env->FindClass("android/media/Crypto");
+    CHECK(clazz != NULL);
+
+    if (!env->IsInstanceOf(obj, clazz)) {
+        return NULL;
+    }
+
+    sp<JCrypto> jcrypto = getCrypto(env, obj);
+
+    if (jcrypto == NULL) {
+        return NULL;
+    }
+
+    return jcrypto->mCrypto;
+}
+
+}  // namespace android
+
+using namespace android;
+
+static sp<JCrypto> setCrypto(
+        JNIEnv *env, jobject thiz, const sp<JCrypto> &crypto) {
+    sp<JCrypto> old = (JCrypto *)env->GetIntField(thiz, gFields.context);
+    if (crypto != NULL) {
+        crypto->incStrong(thiz);
+    }
+    if (old != NULL) {
+        old->decStrong(thiz);
+    }
+    env->SetIntField(thiz, gFields.context, (int)crypto.get());
+
+    return old;
+}
+
+static void android_media_Crypto_release(JNIEnv *env, jobject thiz) {
+    setCrypto(env, thiz, NULL);
+}
+
+static void android_media_Crypto_native_init(JNIEnv *env) {
+    jclass clazz = env->FindClass("android/media/Crypto");
+    CHECK(clazz != NULL);
+
+    gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
+    CHECK(gFields.context != NULL);
+}
+
+static void android_media_Crypto_native_setup(
+        JNIEnv *env, jobject thiz,
+        jbyteArray uuidObj, jbyteArray initDataObj) {
+    jsize uuidLength = env->GetArrayLength(uuidObj);
+
+    if (uuidLength != 16) {
+        jniThrowException(
+                env,
+                "java/lang/IllegalArgumentException",
+                NULL);
+        return;
+    }
+
+    jboolean isCopy;
+    jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy);
+
+    jsize initDataLength = env->GetArrayLength(initDataObj);
+    jbyte *initData = env->GetByteArrayElements(initDataObj, &isCopy);
+
+    sp<JCrypto> crypto = new JCrypto(
+            env, thiz, (const uint8_t *)uuid, initData, initDataLength);
+
+    status_t err = crypto->initCheck();
+
+    env->ReleaseByteArrayElements(initDataObj, initData, 0);
+    initData = NULL;
+
+    env->ReleaseByteArrayElements(uuidObj, uuid, 0);
+    uuid = NULL;
+
+    if (err != OK) {
+        jniThrowException(
+                env,
+                "java/io/IOException",
+                "Failed to instantiate crypto object.");
+        return;
+    }
+
+    setCrypto(env,thiz, crypto);
+}
+
+static void android_media_Crypto_native_finalize(
+        JNIEnv *env, jobject thiz) {
+    android_media_Crypto_release(env, thiz);
+}
+
+static jboolean android_media_Crypto_isCryptoSchemeSupported(
+        JNIEnv *env, jobject thiz, jbyteArray uuidObj) {
+    jsize uuidLength = env->GetArrayLength(uuidObj);
+
+    if (uuidLength != 16) {
+        jniThrowException(
+                env,
+                "java/lang/IllegalArgumentException",
+                NULL);
+        return false;
+    }
+
+    jboolean isCopy;
+    jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy);
+
+    bool result = JCrypto::IsCryptoSchemeSupported((const uint8_t *)uuid);
+
+    env->ReleaseByteArrayElements(uuidObj, uuid, 0);
+    uuid = NULL;
+
+    return result;
+}
+
+static jboolean android_media_Crypto_requiresSecureDecoderComponent(
+        JNIEnv *env, jobject thiz, jstring mimeObj) {
+    if (mimeObj == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return false;
+    }
+
+    sp<JCrypto> crypto = getCrypto(env, thiz);
+
+    if (crypto == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return false;
+    }
+
+    const char *mime = env->GetStringUTFChars(mimeObj, NULL);
+
+    if (mime == NULL) {
+        return false;
+    }
+
+    bool result = crypto->requiresSecureDecoderComponent(mime);
+
+    env->ReleaseStringUTFChars(mimeObj, mime);
+    mime = NULL;
+
+    return result;
+}
+
+static JNINativeMethod gMethods[] = {
+    { "release", "()V", (void *)android_media_Crypto_release },
+    { "native_init", "()V", (void *)android_media_Crypto_native_init },
+
+    { "native_setup", "([B[B)V",
+      (void *)android_media_Crypto_native_setup },
+
+    { "native_finalize", "()V",
+      (void *)android_media_Crypto_native_finalize },
+
+    { "isCryptoSchemeSupported", "([B)Z",
+      (void *)android_media_Crypto_isCryptoSchemeSupported },
+
+    { "requiresSecureDecoderComponent", "(Ljava/lang/String;)Z",
+      (void *)android_media_Crypto_requiresSecureDecoderComponent },
+};
+
+int register_android_media_Crypto(JNIEnv *env) {
+    return AndroidRuntime::registerNativeMethods(env,
+                "android/media/Crypto", gMethods, NELEM(gMethods));
+}
+
diff --git a/media/jni/android_media_Crypto.h b/media/jni/android_media_Crypto.h
new file mode 100644
index 0000000..505725e
--- /dev/null
+++ b/media/jni/android_media_Crypto.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012, 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.
+ */
+
+#ifndef _ANDROID_MEDIA_CRYPTO_H_
+#define _ANDROID_MEDIA_CRYPTO_H_
+
+#include "jni.h"
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ICrypto;
+
+struct JCrypto : public RefBase {
+    static bool IsCryptoSchemeSupported(const uint8_t uuid[16]);
+
+    JCrypto(JNIEnv *env, jobject thiz,
+            const uint8_t uuid[16], const void *initData, size_t initSize);
+
+    status_t initCheck() const;
+
+    bool requiresSecureDecoderComponent(const char *mime) const;
+
+    static sp<ICrypto> GetCrypto(JNIEnv *env, jobject obj);
+
+protected:
+    virtual ~JCrypto();
+
+private:
+    jweak mObject;
+    sp<ICrypto> mCrypto;
+
+    static sp<ICrypto> MakeCrypto();
+
+    static sp<ICrypto> MakeCrypto(
+            const uint8_t uuid[16], const void *initData, size_t initSize);
+
+    DISALLOW_EVIL_CONSTRUCTORS(JCrypto);
+};
+
+}  // namespace android
+
+#endif  // _ANDROID_MEDIA_CRYPTO_H_
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 4b7a811..217216a 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -20,6 +20,7 @@
 
 #include "android_media_MediaCodec.h"
 
+#include "android_media_Crypto.h"
 #include "android_media_Utils.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/android_view_Surface.h"
@@ -98,12 +99,13 @@
 status_t JMediaCodec::configure(
         const sp<AMessage> &format,
         const sp<ISurfaceTexture> &surfaceTexture,
+        const sp<ICrypto> &crypto,
         int flags) {
     sp<SurfaceTextureClient> client;
     if (surfaceTexture != NULL) {
         client = new SurfaceTextureClient(surfaceTexture);
     }
-    return mCodec->configure(format, client, NULL /* crypto */, flags);
+    return mCodec->configure(format, client, crypto, flags);
 }
 
 status_t JMediaCodec::start() {
@@ -256,6 +258,7 @@
         jobject thiz,
         jobjectArray keys, jobjectArray values,
         jobject jsurface,
+        jobject jcrypto,
         jint flags) {
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
 
@@ -286,7 +289,12 @@
         }
     }
 
-    err = codec->configure(format, surfaceTexture, flags);
+    sp<ICrypto> crypto;
+    if (jcrypto != NULL) {
+        crypto = JCrypto::GetCrypto(env, jcrypto);
+    }
+
+    err = codec->configure(format, surfaceTexture, crypto, flags);
 
     throwExceptionAsNecessary(env, err);
 }
@@ -513,7 +521,8 @@
     { "release", "()V", (void *)android_media_MediaCodec_release },
 
     { "native_configure",
-      "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;I)V",
+      "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;"
+      "Landroid/media/Crypto;I)V",
       (void *)android_media_MediaCodec_native_configure },
 
     { "start", "()V", (void *)android_media_MediaCodec_start },
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 6b1257d..6bb4071 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -27,6 +27,7 @@
 
 struct ALooper;
 struct AMessage;
+struct ICrypto;
 struct ISurfaceTexture;
 struct MediaCodec;
 
@@ -40,6 +41,7 @@
     status_t configure(
             const sp<AMessage> &format,
             const sp<ISurfaceTexture> &surfaceTexture,
+            const sp<ICrypto> &crypto,
             int flags);
 
     status_t start();
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 3074bb1..2e74ffd 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -879,6 +879,7 @@
                 "android/media/MediaPlayer", gMethods, NELEM(gMethods));
 }
 
+extern int register_android_media_Crypto(JNIEnv *env);
 extern int register_android_media_MediaCodec(JNIEnv *env);
 extern int register_android_media_MediaExtractor(JNIEnv *env);
 extern int register_android_media_MediaCodecList(JNIEnv *env);
@@ -968,6 +969,11 @@
         goto bail;
     }
 
+    if (register_android_media_Crypto(env) < 0) {
+        ALOGE("ERROR: MediaCodec native registration failed");
+        goto bail;
+    }
+
     /* success -- return valid version number */
     result = JNI_VERSION_1_4;