android_media_MediaCrypto.cpp revision dc614f86a5a61d0b4287796dfa028c637f615d34
1/* 2 * Copyright 2012, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17//#define LOG_NDEBUG 0 18#define LOG_TAG "MediaCrypto-JNI" 19#include <utils/Log.h> 20 21#include "android_media_MediaCrypto.h" 22 23#include "android_runtime/AndroidRuntime.h" 24#include "jni.h" 25#include "JNIHelp.h" 26 27#include <binder/IServiceManager.h> 28#include <cutils/properties.h> 29#include <media/ICrypto.h> 30#include <media/IMediaDrmService.h> 31#include <media/IMediaPlayerService.h> 32#include <media/stagefright/foundation/ADebug.h> 33 34namespace android { 35 36struct fields_t { 37 jfieldID context; 38}; 39 40static fields_t gFields; 41 42static sp<JCrypto> getCrypto(JNIEnv *env, jobject thiz) { 43 return (JCrypto *)env->GetLongField(thiz, gFields.context); 44} 45 46JCrypto::JCrypto( 47 JNIEnv *env, jobject thiz, 48 const uint8_t uuid[16], const void *initData, size_t initSize) { 49 mObject = env->NewWeakGlobalRef(thiz); 50 51 mCrypto = MakeCrypto(uuid, initData, initSize); 52} 53 54JCrypto::~JCrypto() { 55 mCrypto.clear(); 56 57 JNIEnv *env = AndroidRuntime::getJNIEnv(); 58 59 env->DeleteWeakGlobalRef(mObject); 60 mObject = NULL; 61} 62 63// static 64sp<ICrypto> JCrypto::MakeCrypto() { 65 sp<IServiceManager> sm = defaultServiceManager(); 66 sp<ICrypto> crypto; 67 68 char value[PROPERTY_VALUE_MAX]; 69 if (property_get("media.mediadrmservice.enable", value, NULL) 70 && (!strcmp("1", value) || !strcasecmp("true", value))) { 71 sp<IBinder> binder = 72 sm->getService(String16("media.drm")); 73 sp<IMediaDrmService> service = 74 interface_cast<IMediaDrmService>(binder); 75 if (service == NULL) { 76 return NULL; 77 } 78 crypto = service->makeCrypto(); 79 } else { 80 sp<IBinder> binder = 81 sm->getService(String16("media.player")); 82 sp<IMediaPlayerService> service = 83 interface_cast<IMediaPlayerService>(binder); 84 if (service == NULL) { 85 return NULL; 86 } 87 crypto = service->makeCrypto(); 88 } 89 90 if (crypto == NULL || (crypto->initCheck() != OK && crypto->initCheck() != NO_INIT)) { 91 return NULL; 92 } 93 94 return crypto; 95} 96 97// static 98sp<ICrypto> JCrypto::MakeCrypto( 99 const uint8_t uuid[16], const void *initData, size_t initSize) { 100 sp<ICrypto> crypto = MakeCrypto(); 101 102 if (crypto == NULL) { 103 return NULL; 104 } 105 106 status_t err = crypto->createPlugin(uuid, initData, initSize); 107 108 if (err != OK) { 109 return NULL; 110 } 111 112 return crypto; 113} 114 115bool JCrypto::requiresSecureDecoderComponent(const char *mime) const { 116 if (mCrypto == NULL) { 117 return false; 118 } 119 120 return mCrypto->requiresSecureDecoderComponent(mime); 121} 122 123// static 124bool JCrypto::IsCryptoSchemeSupported(const uint8_t uuid[16]) { 125 sp<ICrypto> crypto = MakeCrypto(); 126 127 if (crypto == NULL) { 128 return false; 129 } 130 131 return crypto->isCryptoSchemeSupported(uuid); 132} 133 134status_t JCrypto::initCheck() const { 135 return mCrypto == NULL ? NO_INIT : OK; 136} 137 138// static 139sp<ICrypto> JCrypto::GetCrypto(JNIEnv *env, jobject obj) { 140 jclass clazz = env->FindClass("android/media/MediaCrypto"); 141 CHECK(clazz != NULL); 142 143 if (!env->IsInstanceOf(obj, clazz)) { 144 return NULL; 145 } 146 147 sp<JCrypto> jcrypto = getCrypto(env, obj); 148 149 if (jcrypto == NULL) { 150 return NULL; 151 } 152 153 return jcrypto->mCrypto; 154} 155 156// JNI conversion utilities 157static Vector<uint8_t> JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArray) { 158 Vector<uint8_t> vector; 159 size_t length = env->GetArrayLength(byteArray); 160 vector.insertAt((size_t)0, length); 161 env->GetByteArrayRegion(byteArray, 0, length, (jbyte *)vector.editArray()); 162 return vector; 163} 164 165} // namespace android 166 167using namespace android; 168 169static sp<JCrypto> setCrypto( 170 JNIEnv *env, jobject thiz, const sp<JCrypto> &crypto) { 171 sp<JCrypto> old = (JCrypto *)env->GetLongField(thiz, gFields.context); 172 if (crypto != NULL) { 173 crypto->incStrong(thiz); 174 } 175 if (old != NULL) { 176 old->decStrong(thiz); 177 } 178 env->SetLongField(thiz, gFields.context, (jlong)crypto.get()); 179 180 return old; 181} 182 183static void android_media_MediaCrypto_release(JNIEnv *env, jobject thiz) { 184 setCrypto(env, thiz, NULL); 185} 186 187static void android_media_MediaCrypto_native_init(JNIEnv *env) { 188 jclass clazz = env->FindClass("android/media/MediaCrypto"); 189 CHECK(clazz != NULL); 190 191 gFields.context = env->GetFieldID(clazz, "mNativeContext", "J"); 192 CHECK(gFields.context != NULL); 193} 194 195static void android_media_MediaCrypto_native_setup( 196 JNIEnv *env, jobject thiz, 197 jbyteArray uuidObj, jbyteArray initDataObj) { 198 jsize uuidLength = env->GetArrayLength(uuidObj); 199 200 if (uuidLength != 16) { 201 jniThrowException( 202 env, 203 "java/lang/IllegalArgumentException", 204 NULL); 205 return; 206 } 207 208 jboolean isCopy; 209 jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy); 210 211 jsize initDataLength = 0; 212 jbyte *initData = NULL; 213 214 if (initDataObj != NULL) { 215 initDataLength = env->GetArrayLength(initDataObj); 216 initData = env->GetByteArrayElements(initDataObj, &isCopy); 217 } 218 219 sp<JCrypto> crypto = new JCrypto( 220 env, thiz, (const uint8_t *)uuid, initData, initDataLength); 221 222 status_t err = crypto->initCheck(); 223 224 if (initDataObj != NULL) { 225 env->ReleaseByteArrayElements(initDataObj, initData, 0); 226 initData = NULL; 227 } 228 229 env->ReleaseByteArrayElements(uuidObj, uuid, 0); 230 uuid = NULL; 231 232 if (err != OK) { 233 jniThrowException( 234 env, 235 "android/media/MediaCryptoException", 236 "Failed to instantiate crypto object."); 237 return; 238 } 239 240 setCrypto(env,thiz, crypto); 241} 242 243static void android_media_MediaCrypto_native_finalize( 244 JNIEnv *env, jobject thiz) { 245 android_media_MediaCrypto_release(env, thiz); 246} 247 248static jboolean android_media_MediaCrypto_isCryptoSchemeSupportedNative( 249 JNIEnv *env, jobject /* thiz */, jbyteArray uuidObj) { 250 jsize uuidLength = env->GetArrayLength(uuidObj); 251 252 if (uuidLength != 16) { 253 jniThrowException( 254 env, 255 "java/lang/IllegalArgumentException", 256 NULL); 257 return JNI_FALSE; 258 } 259 260 jboolean isCopy; 261 jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy); 262 263 bool result = JCrypto::IsCryptoSchemeSupported((const uint8_t *)uuid); 264 265 env->ReleaseByteArrayElements(uuidObj, uuid, 0); 266 uuid = NULL; 267 268 return result ? JNI_TRUE : JNI_FALSE; 269} 270 271static jboolean android_media_MediaCrypto_requiresSecureDecoderComponent( 272 JNIEnv *env, jobject thiz, jstring mimeObj) { 273 if (mimeObj == NULL) { 274 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 275 return JNI_FALSE; 276 } 277 278 sp<JCrypto> crypto = getCrypto(env, thiz); 279 280 if (crypto == NULL) { 281 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 282 return JNI_FALSE; 283 } 284 285 const char *mime = env->GetStringUTFChars(mimeObj, NULL); 286 287 if (mime == NULL) { 288 return JNI_FALSE; 289 } 290 291 bool result = crypto->requiresSecureDecoderComponent(mime); 292 293 env->ReleaseStringUTFChars(mimeObj, mime); 294 mime = NULL; 295 296 return result ? JNI_TRUE : JNI_FALSE; 297} 298 299static void android_media_MediaCrypto_setMediaDrmSession( 300 JNIEnv *env, jobject thiz, jbyteArray jsessionId) { 301 if (jsessionId == NULL) { 302 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 303 return; 304 } 305 306 sp<ICrypto> crypto = JCrypto::GetCrypto(env, thiz); 307 308 if (crypto == NULL) { 309 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 310 return; 311 } 312 313 Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); 314 315 status_t err = crypto->setMediaDrmSession(sessionId); 316 317 if (err != OK) { 318 String8 msg("setMediaDrmSession failed"); 319 if (err == ERROR_DRM_SESSION_NOT_OPENED) { 320 msg += ": session not opened"; 321 } else if (err == ERROR_UNSUPPORTED) { 322 msg += ": not supported by this crypto scheme"; 323 } else if (err == NO_INIT) { 324 msg += ": crypto plugin not initialized"; 325 } else { 326 msg.appendFormat(": general failure (%d)", err); 327 } 328 jniThrowException(env, "android/media/MediaCryptoException", msg.string()); 329 } 330} 331 332static const JNINativeMethod gMethods[] = { 333 { "release", "()V", (void *)android_media_MediaCrypto_release }, 334 { "native_init", "()V", (void *)android_media_MediaCrypto_native_init }, 335 336 { "native_setup", "([B[B)V", 337 (void *)android_media_MediaCrypto_native_setup }, 338 339 { "native_finalize", "()V", 340 (void *)android_media_MediaCrypto_native_finalize }, 341 342 { "isCryptoSchemeSupportedNative", "([B)Z", 343 (void *)android_media_MediaCrypto_isCryptoSchemeSupportedNative }, 344 345 { "requiresSecureDecoderComponent", "(Ljava/lang/String;)Z", 346 (void *)android_media_MediaCrypto_requiresSecureDecoderComponent }, 347 348 { "setMediaDrmSession", "([B)V", 349 (void *)android_media_MediaCrypto_setMediaDrmSession }, 350}; 351 352int register_android_media_Crypto(JNIEnv *env) { 353 return AndroidRuntime::registerNativeMethods(env, 354 "android/media/MediaCrypto", gMethods, NELEM(gMethods)); 355} 356 357