Home | History | Annotate | Download | only in jni
      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/stagefright/foundation/ADebug.h>
     32 
     33 namespace android {
     34 
     35 struct fields_t {
     36     jfieldID context;
     37 };
     38 
     39 static fields_t gFields;
     40 
     41 static sp<JCrypto> getCrypto(JNIEnv *env, jobject thiz) {
     42     return (JCrypto *)env->GetLongField(thiz, gFields.context);
     43 }
     44 
     45 JCrypto::JCrypto(
     46         JNIEnv *env, jobject thiz,
     47         const uint8_t uuid[16], const void *initData, size_t initSize) {
     48     mObject = env->NewWeakGlobalRef(thiz);
     49 
     50     mCrypto = MakeCrypto(uuid, initData, initSize);
     51 }
     52 
     53 JCrypto::~JCrypto() {
     54     mCrypto.clear();
     55 
     56     JNIEnv *env = AndroidRuntime::getJNIEnv();
     57 
     58     env->DeleteWeakGlobalRef(mObject);
     59     mObject = NULL;
     60 }
     61 
     62 // static
     63 sp<ICrypto> JCrypto::MakeCrypto() {
     64     sp<IServiceManager> sm = defaultServiceManager();
     65 
     66     sp<IBinder> binder = sm->getService(String16("media.drm"));
     67     sp<IMediaDrmService> service = interface_cast<IMediaDrmService>(binder);
     68     if (service == NULL) {
     69         return NULL;
     70     }
     71 
     72     sp<ICrypto> crypto = service->makeCrypto();
     73     if (crypto == NULL || (crypto->initCheck() != OK && crypto->initCheck() != NO_INIT)) {
     74         return NULL;
     75     }
     76 
     77     return crypto;
     78 }
     79 
     80 // static
     81 sp<ICrypto> JCrypto::MakeCrypto(
     82         const uint8_t uuid[16], const void *initData, size_t initSize) {
     83     sp<ICrypto> crypto = MakeCrypto();
     84 
     85     if (crypto == NULL) {
     86         return NULL;
     87     }
     88 
     89     status_t err = crypto->createPlugin(uuid, initData, initSize);
     90 
     91     if (err != OK) {
     92         return NULL;
     93     }
     94 
     95     return crypto;
     96 }
     97 
     98 bool JCrypto::requiresSecureDecoderComponent(const char *mime) const {
     99     if (mCrypto == NULL) {
    100         return false;
    101     }
    102 
    103     return mCrypto->requiresSecureDecoderComponent(mime);
    104 }
    105 
    106 // static
    107 bool JCrypto::IsCryptoSchemeSupported(const uint8_t uuid[16]) {
    108     sp<ICrypto> crypto = MakeCrypto();
    109 
    110     if (crypto == NULL) {
    111         return false;
    112     }
    113 
    114     return crypto->isCryptoSchemeSupported(uuid);
    115 }
    116 
    117 status_t JCrypto::initCheck() const {
    118     return mCrypto == NULL ? NO_INIT : OK;
    119 }
    120 
    121 // static
    122 sp<ICrypto> JCrypto::GetCrypto(JNIEnv *env, jobject obj) {
    123     jclass clazz = env->FindClass("android/media/MediaCrypto");
    124     CHECK(clazz != NULL);
    125 
    126     if (!env->IsInstanceOf(obj, clazz)) {
    127         return NULL;
    128     }
    129 
    130     sp<JCrypto> jcrypto = getCrypto(env, obj);
    131 
    132     if (jcrypto == NULL) {
    133         return NULL;
    134     }
    135 
    136     return jcrypto->mCrypto;
    137 }
    138 
    139 // JNI conversion utilities
    140 static Vector<uint8_t> JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArray) {
    141     Vector<uint8_t> vector;
    142     size_t length = env->GetArrayLength(byteArray);
    143     vector.insertAt((size_t)0, length);
    144     env->GetByteArrayRegion(byteArray, 0, length, (jbyte *)vector.editArray());
    145     return vector;
    146 }
    147 
    148 }  // namespace android
    149 
    150 using namespace android;
    151 
    152 static sp<JCrypto> setCrypto(
    153         JNIEnv *env, jobject thiz, const sp<JCrypto> &crypto) {
    154     sp<JCrypto> old = (JCrypto *)env->GetLongField(thiz, gFields.context);
    155     if (crypto != NULL) {
    156         crypto->incStrong(thiz);
    157     }
    158     if (old != NULL) {
    159         old->decStrong(thiz);
    160     }
    161     env->SetLongField(thiz, gFields.context, (jlong)crypto.get());
    162 
    163     return old;
    164 }
    165 
    166 static void android_media_MediaCrypto_release(JNIEnv *env, jobject thiz) {
    167     setCrypto(env, thiz, NULL);
    168 }
    169 
    170 static void android_media_MediaCrypto_native_init(JNIEnv *env) {
    171     jclass clazz = env->FindClass("android/media/MediaCrypto");
    172     CHECK(clazz != NULL);
    173 
    174     gFields.context = env->GetFieldID(clazz, "mNativeContext", "J");
    175     CHECK(gFields.context != NULL);
    176 }
    177 
    178 static void android_media_MediaCrypto_native_setup(
    179         JNIEnv *env, jobject thiz,
    180         jbyteArray uuidObj, jbyteArray initDataObj) {
    181     jsize uuidLength = env->GetArrayLength(uuidObj);
    182 
    183     if (uuidLength != 16) {
    184         jniThrowException(
    185                 env,
    186                 "java/lang/IllegalArgumentException",
    187                 NULL);
    188         return;
    189     }
    190 
    191     jboolean isCopy;
    192     jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy);
    193 
    194     jsize initDataLength = 0;
    195     jbyte *initData = NULL;
    196 
    197     if (initDataObj != NULL) {
    198         initDataLength = env->GetArrayLength(initDataObj);
    199         initData = env->GetByteArrayElements(initDataObj, &isCopy);
    200     }
    201 
    202     sp<JCrypto> crypto = new JCrypto(
    203             env, thiz, (const uint8_t *)uuid, initData, initDataLength);
    204 
    205     status_t err = crypto->initCheck();
    206 
    207     if (initDataObj != NULL) {
    208         env->ReleaseByteArrayElements(initDataObj, initData, 0);
    209         initData = NULL;
    210     }
    211 
    212     env->ReleaseByteArrayElements(uuidObj, uuid, 0);
    213     uuid = NULL;
    214 
    215     if (err != OK) {
    216         jniThrowException(
    217                 env,
    218                 "android/media/MediaCryptoException",
    219                 "Failed to instantiate crypto object.");
    220         return;
    221     }
    222 
    223     setCrypto(env,thiz, crypto);
    224 }
    225 
    226 static void android_media_MediaCrypto_native_finalize(
    227         JNIEnv *env, jobject thiz) {
    228     android_media_MediaCrypto_release(env, thiz);
    229 }
    230 
    231 static jboolean android_media_MediaCrypto_isCryptoSchemeSupportedNative(
    232         JNIEnv *env, jobject /* thiz */, jbyteArray uuidObj) {
    233     jsize uuidLength = env->GetArrayLength(uuidObj);
    234 
    235     if (uuidLength != 16) {
    236         jniThrowException(
    237                 env,
    238                 "java/lang/IllegalArgumentException",
    239                 NULL);
    240         return JNI_FALSE;
    241     }
    242 
    243     jboolean isCopy;
    244     jbyte *uuid = env->GetByteArrayElements(uuidObj, &isCopy);
    245 
    246     bool result = JCrypto::IsCryptoSchemeSupported((const uint8_t *)uuid);
    247 
    248     env->ReleaseByteArrayElements(uuidObj, uuid, 0);
    249     uuid = NULL;
    250 
    251     return result ? JNI_TRUE : JNI_FALSE;
    252 }
    253 
    254 static jboolean android_media_MediaCrypto_requiresSecureDecoderComponent(
    255         JNIEnv *env, jobject thiz, jstring mimeObj) {
    256     if (mimeObj == NULL) {
    257         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    258         return JNI_FALSE;
    259     }
    260 
    261     sp<JCrypto> crypto = getCrypto(env, thiz);
    262 
    263     if (crypto == NULL) {
    264         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    265         return JNI_FALSE;
    266     }
    267 
    268     const char *mime = env->GetStringUTFChars(mimeObj, NULL);
    269 
    270     if (mime == NULL) {
    271         return JNI_FALSE;
    272     }
    273 
    274     bool result = crypto->requiresSecureDecoderComponent(mime);
    275 
    276     env->ReleaseStringUTFChars(mimeObj, mime);
    277     mime = NULL;
    278 
    279     return result ? JNI_TRUE : JNI_FALSE;
    280 }
    281 
    282 static void android_media_MediaCrypto_setMediaDrmSession(
    283         JNIEnv *env, jobject thiz, jbyteArray jsessionId) {
    284     if (jsessionId == NULL) {
    285         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    286         return;
    287     }
    288 
    289     sp<ICrypto> crypto = JCrypto::GetCrypto(env, thiz);
    290 
    291     if (crypto == NULL) {
    292         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
    293         return;
    294     }
    295 
    296     Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
    297 
    298     status_t err = crypto->setMediaDrmSession(sessionId);
    299 
    300     if (err != OK) {
    301         String8 msg("setMediaDrmSession failed");
    302         if (err == ERROR_DRM_SESSION_NOT_OPENED) {
    303             msg += ": session not opened";
    304         } else if (err == ERROR_UNSUPPORTED) {
    305             msg += ": not supported by this crypto scheme";
    306         } else if (err == NO_INIT) {
    307             msg += ": crypto plugin not initialized";
    308         } else {
    309             msg.appendFormat(": general failure (%d)", err);
    310         }
    311         jniThrowException(env, "android/media/MediaCryptoException", msg.string());
    312     }
    313 }
    314 
    315 static const JNINativeMethod gMethods[] = {
    316     { "release", "()V", (void *)android_media_MediaCrypto_release },
    317     { "native_init", "()V", (void *)android_media_MediaCrypto_native_init },
    318 
    319     { "native_setup", "([B[B)V",
    320       (void *)android_media_MediaCrypto_native_setup },
    321 
    322     { "native_finalize", "()V",
    323       (void *)android_media_MediaCrypto_native_finalize },
    324 
    325     { "isCryptoSchemeSupportedNative", "([B)Z",
    326       (void *)android_media_MediaCrypto_isCryptoSchemeSupportedNative },
    327 
    328     { "requiresSecureDecoderComponent", "(Ljava/lang/String;)Z",
    329       (void *)android_media_MediaCrypto_requiresSecureDecoderComponent },
    330 
    331     { "setMediaDrmSession", "([B)V",
    332       (void *)android_media_MediaCrypto_setMediaDrmSession },
    333 };
    334 
    335 int register_android_media_Crypto(JNIEnv *env) {
    336     return AndroidRuntime::registerNativeMethods(env,
    337                 "android/media/MediaCrypto", gMethods, NELEM(gMethods));
    338 }
    339 
    340