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;