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