diff options
| author | 2012-04-04 14:06:32 -0700 | |
|---|---|---|
| committer | 2012-04-04 14:06:32 -0700 | |
| commit | 8240d9239d9aabed75c49f9d4d69fd8a5fe4c899 (patch) | |
| tree | dfd2281e3754fa3df237ff69b88a5a91a0dbb014 | |
| parent | cf15200ddb9148dfd27b039cbc30b86620e7c018 (diff) | |
New Crypto JAVA class to facilitate decryption via MediaCodec.
Change-Id: Ic4e395faa84f003793c2804f2badabab9e7f1034
related-to-bug: 6275919
| -rw-r--r-- | media/java/android/media/Crypto.java | 49 | ||||
| -rw-r--r-- | media/java/android/media/MediaCodec.java | 14 | ||||
| -rw-r--r-- | media/java/android/media/MediaExtractor.java | 3 | ||||
| -rw-r--r-- | media/jni/Android.mk | 1 | ||||
| -rw-r--r-- | media/jni/android_media_Crypto.cpp | 291 | ||||
| -rw-r--r-- | media/jni/android_media_Crypto.h | 59 | ||||
| -rw-r--r-- | media/jni/android_media_MediaCodec.cpp | 15 | ||||
| -rw-r--r-- | media/jni/android_media_MediaCodec.h | 2 | ||||
| -rw-r--r-- | media/jni/android_media_MediaPlayer.cpp | 6 |
9 files changed, 430 insertions, 10 deletions
diff --git a/media/java/android/media/Crypto.java b/media/java/android/media/Crypto.java new file mode 100644 index 000000000000..43e34fb98958 --- /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 7629d60f7b1f..66cea9d48cce 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 @@ import java.util.Map; * 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 @@ public class MediaCodec * * @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 @@ public class MediaCodec } } - 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 9ea3d0e5b36f..9c3b6a70e627 100644 --- a/media/java/android/media/MediaExtractor.java +++ b/media/java/android/media/MediaExtractor.java @@ -23,8 +23,7 @@ import java.util.Map; * 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 dd1e505b1ffb..a3361d458817 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -2,6 +2,7 @@ LOCAL_PATH:= $(call my-dir) 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 000000000000..e1a60a189595 --- /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 000000000000..505725e87419 --- /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 4b7a811b0a5b..217216ae70e7 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 @@ JMediaCodec::~JMediaCodec() { 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 @@ static void android_media_MediaCodec_native_configure( jobject thiz, jobjectArray keys, jobjectArray values, jobject jsurface, + jobject jcrypto, jint flags) { sp<JMediaCodec> codec = getMediaCodec(env, thiz); @@ -286,7 +289,12 @@ static void android_media_MediaCodec_native_configure( } } - 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 @@ static JNINativeMethod gMethods[] = { { "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 6b1257db74b1..6bb40716e993 100644 --- a/media/jni/android_media_MediaCodec.h +++ b/media/jni/android_media_MediaCodec.h @@ -27,6 +27,7 @@ namespace android { struct ALooper; struct AMessage; +struct ICrypto; struct ISurfaceTexture; struct MediaCodec; @@ -40,6 +41,7 @@ struct JMediaCodec : public RefBase { 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 3074bb17be24..2e74ffd8b3d5 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -879,6 +879,7 @@ static int register_android_media_MediaPlayer(JNIEnv *env) "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 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) 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; |