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