android_media_MediaDrm.cpp revision 656fd0402613cec0196d5e2ae0a460d044d2805b
1/*
2 * Copyright 2013, 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 "MediaDrm-JNI"
19#include <utils/Log.h>
20
21#include "android_media_MediaDrm.h"
22
23#include "android_runtime/AndroidRuntime.h"
24#include "android_runtime/Log.h"
25#include "android_os_Parcel.h"
26#include "jni.h"
27#include "JNIHelp.h"
28
29#include <binder/IServiceManager.h>
30#include <binder/Parcel.h>
31#include <media/IDrm.h>
32#include <media/IMediaPlayerService.h>
33#include <media/stagefright/foundation/ADebug.h>
34#include <media/stagefright/MediaErrors.h>
35
36namespace android {
37
38#define FIND_CLASS(var, className) \
39    var = env->FindClass(className); \
40    LOG_FATAL_IF(! var, "Unable to find class " className);
41
42#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
43    var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
44    LOG_FATAL_IF(! var, "Unable to find field " fieldName);
45
46#define GET_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
47    var = env->GetMethodID(clazz, fieldName, fieldDescriptor); \
48    LOG_FATAL_IF(! var, "Unable to find method " fieldName);
49
50#define GET_STATIC_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
51    var = env->GetStaticFieldID(clazz, fieldName, fieldDescriptor); \
52    LOG_FATAL_IF(! var, "Unable to find field " fieldName);
53
54#define GET_STATIC_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
55    var = env->GetStaticMethodID(clazz, fieldName, fieldDescriptor); \
56    LOG_FATAL_IF(! var, "Unable to find static method " fieldName);
57
58
59struct RequestFields {
60    jfieldID data;
61    jfieldID defaultUrl;
62};
63
64struct ArrayListFields {
65    jmethodID init;
66    jmethodID add;
67};
68
69struct HashmapFields {
70    jmethodID init;
71    jmethodID get;
72    jmethodID put;
73    jmethodID entrySet;
74};
75
76struct SetFields {
77    jmethodID iterator;
78};
79
80struct IteratorFields {
81    jmethodID next;
82    jmethodID hasNext;
83};
84
85struct EntryFields {
86    jmethodID getKey;
87    jmethodID getValue;
88};
89
90struct EventTypes {
91    jint kEventProvisionRequired;
92    jint kEventKeyRequired;
93    jint kEventKeyExpired;
94    jint kEventVendorDefined;
95} gEventTypes;
96
97struct KeyTypes {
98    jint kKeyTypeStreaming;
99    jint kKeyTypeOffline;
100    jint kKeyTypeRelease;
101} gKeyTypes;
102
103struct fields_t {
104    jfieldID context;
105    jmethodID post_event;
106    RequestFields keyRequest;
107    RequestFields provisionRequest;
108    ArrayListFields arraylist;
109    HashmapFields hashmap;
110    SetFields set;
111    IteratorFields iterator;
112    EntryFields entry;
113};
114
115static fields_t gFields;
116
117// ----------------------------------------------------------------------------
118// ref-counted object for callbacks
119class JNIDrmListener: public DrmListener
120{
121public:
122    JNIDrmListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
123    ~JNIDrmListener();
124    virtual void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj = NULL);
125private:
126    JNIDrmListener();
127    jclass      mClass;     // Reference to MediaDrm class
128    jobject     mObject;    // Weak ref to MediaDrm Java object to call on
129};
130
131JNIDrmListener::JNIDrmListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
132{
133    // Hold onto the MediaDrm class for use in calling the static method
134    // that posts events to the application thread.
135    jclass clazz = env->GetObjectClass(thiz);
136    if (clazz == NULL) {
137        ALOGE("Can't find android/media/MediaDrm");
138        jniThrowException(env, "java/lang/Exception",
139                          "Can't find android/media/MediaDrm");
140        return;
141    }
142    mClass = (jclass)env->NewGlobalRef(clazz);
143
144    // We use a weak reference so the MediaDrm object can be garbage collected.
145    // The reference is only used as a proxy for callbacks.
146    mObject  = env->NewGlobalRef(weak_thiz);
147}
148
149JNIDrmListener::~JNIDrmListener()
150{
151    // remove global references
152    JNIEnv *env = AndroidRuntime::getJNIEnv();
153    env->DeleteGlobalRef(mObject);
154    env->DeleteGlobalRef(mClass);
155}
156
157void JNIDrmListener::notify(DrmPlugin::EventType eventType, int extra,
158                            const Parcel *obj)
159{
160    jint jeventType;
161
162    // translate DrmPlugin event types into their java equivalents
163    switch(eventType) {
164        case DrmPlugin::kDrmPluginEventProvisionRequired:
165            jeventType = gEventTypes.kEventProvisionRequired;
166            break;
167        case DrmPlugin::kDrmPluginEventKeyNeeded:
168            jeventType = gEventTypes.kEventKeyRequired;
169            break;
170        case DrmPlugin::kDrmPluginEventKeyExpired:
171            jeventType = gEventTypes.kEventKeyExpired;
172            break;
173        case DrmPlugin::kDrmPluginEventVendorDefined:
174            jeventType = gEventTypes.kEventVendorDefined;
175            break;
176        default:
177            ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType);
178            return;
179    }
180
181    JNIEnv *env = AndroidRuntime::getJNIEnv();
182    if (obj && obj->dataSize() > 0) {
183        jobject jParcel = createJavaParcelObject(env);
184        if (jParcel != NULL) {
185            Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
186            nativeParcel->setData(obj->data(), obj->dataSize());
187            env->CallStaticVoidMethod(mClass, gFields.post_event, mObject,
188                    jeventType, extra, jParcel);
189        }
190    }
191
192    if (env->ExceptionCheck()) {
193        ALOGW("An exception occurred while notifying an event.");
194        LOGW_EX(env);
195        env->ExceptionClear();
196    }
197}
198
199
200static bool throwExceptionAsNecessary(
201        JNIEnv *env, status_t err, const char *msg = NULL) {
202
203    const char *drmMessage = NULL;
204
205    switch(err) {
206    case ERROR_DRM_UNKNOWN:
207        drmMessage = "General DRM error";
208        break;
209    case ERROR_DRM_NO_LICENSE:
210        drmMessage = "No license";
211        break;
212    case ERROR_DRM_LICENSE_EXPIRED:
213        drmMessage = "License expired";
214        break;
215    case ERROR_DRM_SESSION_NOT_OPENED:
216        drmMessage = "Session not opened";
217        break;
218    case ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED:
219        drmMessage = "Not initialized";
220        break;
221    case ERROR_DRM_DECRYPT:
222        drmMessage = "Decrypt error";
223        break;
224    case ERROR_DRM_CANNOT_HANDLE:
225        drmMessage = "Unsupported scheme or data format";
226        break;
227    case ERROR_DRM_TAMPER_DETECTED:
228        drmMessage = "Invalid state";
229        break;
230    default:
231        break;
232    }
233
234    String8 vendorMessage;
235    if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
236        vendorMessage.format("DRM vendor-defined error: %d", err);
237        drmMessage = vendorMessage.string();
238    }
239
240    if (err == BAD_VALUE) {
241        jniThrowException(env, "java/lang/IllegalArgumentException", msg);
242        return true;
243    } else if (err == ERROR_DRM_NOT_PROVISIONED) {
244        jniThrowException(env, "android/media/NotProvisionedException", msg);
245        return true;
246    } else if (err == ERROR_DRM_RESOURCE_BUSY) {
247        jniThrowException(env, "android/media/ResourceBusyException", msg);
248        return true;
249    } else if (err == ERROR_DRM_DEVICE_REVOKED) {
250        jniThrowException(env, "android/media/DeniedByServerException", msg);
251        return true;
252    } else if (err != OK) {
253        String8 errbuf;
254        if (drmMessage != NULL) {
255            if (msg == NULL) {
256                msg = drmMessage;
257            } else {
258                errbuf.format("%s: %s", msg, drmMessage);
259                msg = errbuf.string();
260            }
261        }
262        ALOGE("Illegal state exception: %s", msg);
263        jniThrowException(env, "java/lang/IllegalStateException", msg);
264        return true;
265    }
266    return false;
267}
268
269static sp<IDrm> GetDrm(JNIEnv *env, jobject thiz) {
270    JDrm *jdrm = (JDrm *)env->GetLongField(thiz, gFields.context);
271    return jdrm ? jdrm->getDrm() : NULL;
272}
273
274JDrm::JDrm(
275        JNIEnv *env, jobject thiz, const uint8_t uuid[16]) {
276    mObject = env->NewWeakGlobalRef(thiz);
277    mDrm = MakeDrm(uuid);
278    if (mDrm != NULL) {
279        mDrm->setListener(this);
280    }
281}
282
283JDrm::~JDrm() {
284    mDrm.clear();
285
286    JNIEnv *env = AndroidRuntime::getJNIEnv();
287
288    env->DeleteWeakGlobalRef(mObject);
289    mObject = NULL;
290}
291
292// static
293sp<IDrm> JDrm::MakeDrm() {
294    sp<IServiceManager> sm = defaultServiceManager();
295
296    sp<IBinder> binder =
297        sm->getService(String16("media.player"));
298
299    sp<IMediaPlayerService> service =
300        interface_cast<IMediaPlayerService>(binder);
301
302    if (service == NULL) {
303        return NULL;
304    }
305
306    sp<IDrm> drm = service->makeDrm();
307
308    if (drm == NULL || (drm->initCheck() != OK && drm->initCheck() != NO_INIT)) {
309        return NULL;
310    }
311
312    return drm;
313}
314
315// static
316sp<IDrm> JDrm::MakeDrm(const uint8_t uuid[16]) {
317    sp<IDrm> drm = MakeDrm();
318
319    if (drm == NULL) {
320        return NULL;
321    }
322
323    status_t err = drm->createPlugin(uuid);
324
325    if (err != OK) {
326        return NULL;
327    }
328
329    return drm;
330}
331
332status_t JDrm::setListener(const sp<DrmListener>& listener) {
333    Mutex::Autolock lock(mLock);
334    mListener = listener;
335    return OK;
336}
337
338void JDrm::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) {
339    sp<DrmListener> listener;
340    mLock.lock();
341    listener = mListener;
342    mLock.unlock();
343
344    if (listener != NULL) {
345        Mutex::Autolock lock(mNotifyLock);
346        listener->notify(eventType, extra, obj);
347    }
348}
349
350
351// static
352bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) {
353    sp<IDrm> drm = MakeDrm();
354
355    if (drm == NULL) {
356        return false;
357    }
358
359    return drm->isCryptoSchemeSupported(uuid, mimeType);
360}
361
362status_t JDrm::initCheck() const {
363    return mDrm == NULL ? NO_INIT : OK;
364}
365
366// JNI conversion utilities
367static Vector<uint8_t> JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArray) {
368    Vector<uint8_t> vector;
369    size_t length = env->GetArrayLength(byteArray);
370    vector.insertAt((size_t)0, length);
371    env->GetByteArrayRegion(byteArray, 0, length, (jbyte *)vector.editArray());
372    return vector;
373}
374
375static jbyteArray VectorToJByteArray(JNIEnv *env, Vector<uint8_t> const &vector) {
376    size_t length = vector.size();
377    jbyteArray result = env->NewByteArray(length);
378    if (result != NULL) {
379        env->SetByteArrayRegion(result, 0, length, (jbyte *)vector.array());
380    }
381    return result;
382}
383
384static String8 JStringToString8(JNIEnv *env, jstring const &jstr) {
385    String8 result;
386
387    const char *s = env->GetStringUTFChars(jstr, NULL);
388    if (s) {
389        result = s;
390        env->ReleaseStringUTFChars(jstr, s);
391    }
392    return result;
393}
394
395/*
396    import java.util.HashMap;
397    import java.util.Set;
398    import java.Map.Entry;
399    import jav.util.Iterator;
400
401    HashMap<k, v> hm;
402    Set<Entry<k, v> > s = hm.entrySet();
403    Iterator i = s.iterator();
404    Entry e = s.next();
405*/
406
407static KeyedVector<String8, String8> HashMapToKeyedVector(JNIEnv *env, jobject &hashMap) {
408    jclass clazz;
409    FIND_CLASS(clazz, "java/lang/String");
410    KeyedVector<String8, String8> keyedVector;
411
412    jobject entrySet = env->CallObjectMethod(hashMap, gFields.hashmap.entrySet);
413    if (entrySet) {
414        jobject iterator = env->CallObjectMethod(entrySet, gFields.set.iterator);
415        if (iterator) {
416            jboolean hasNext = env->CallBooleanMethod(iterator, gFields.iterator.hasNext);
417            while (hasNext) {
418                jobject entry = env->CallObjectMethod(iterator, gFields.iterator.next);
419                if (entry) {
420                    jobject obj = env->CallObjectMethod(entry, gFields.entry.getKey);
421                    if (!env->IsInstanceOf(obj, clazz)) {
422                        jniThrowException(env, "java/lang/IllegalArgumentException",
423                                          "HashMap key is not a String");
424                    }
425                    jstring jkey = static_cast<jstring>(obj);
426
427                    obj = env->CallObjectMethod(entry, gFields.entry.getValue);
428                    if (!env->IsInstanceOf(obj, clazz)) {
429                        jniThrowException(env, "java/lang/IllegalArgumentException",
430                                          "HashMap value is not a String");
431                    }
432                    jstring jvalue = static_cast<jstring>(obj);
433
434                    String8 key = JStringToString8(env, jkey);
435                    String8 value = JStringToString8(env, jvalue);
436                    keyedVector.add(key, value);
437
438                    env->DeleteLocalRef(jkey);
439                    env->DeleteLocalRef(jvalue);
440                    hasNext = env->CallBooleanMethod(iterator, gFields.iterator.hasNext);
441                }
442                env->DeleteLocalRef(entry);
443            }
444            env->DeleteLocalRef(iterator);
445        }
446        env->DeleteLocalRef(entrySet);
447    }
448    return keyedVector;
449}
450
451static jobject KeyedVectorToHashMap (JNIEnv *env, KeyedVector<String8, String8> const &map) {
452    jclass clazz;
453    FIND_CLASS(clazz, "java/util/HashMap");
454    jobject hashMap = env->NewObject(clazz, gFields.hashmap.init);
455    for (size_t i = 0; i < map.size(); ++i) {
456        jstring jkey = env->NewStringUTF(map.keyAt(i).string());
457        jstring jvalue = env->NewStringUTF(map.valueAt(i).string());
458        env->CallObjectMethod(hashMap, gFields.hashmap.put, jkey, jvalue);
459        env->DeleteLocalRef(jkey);
460        env->DeleteLocalRef(jvalue);
461    }
462    return hashMap;
463}
464
465static jobject ListOfVectorsToArrayListOfByteArray(JNIEnv *env,
466                                                   List<Vector<uint8_t> > list) {
467    jclass clazz;
468    FIND_CLASS(clazz, "java/util/ArrayList");
469    jobject arrayList = env->NewObject(clazz, gFields.arraylist.init);
470    List<Vector<uint8_t> >::iterator iter = list.begin();
471    while (iter != list.end()) {
472        jbyteArray byteArray = VectorToJByteArray(env, *iter);
473        env->CallBooleanMethod(arrayList, gFields.arraylist.add, byteArray);
474        env->DeleteLocalRef(byteArray);
475        iter++;
476    }
477
478    return arrayList;
479}
480
481}  // namespace android
482
483using namespace android;
484
485static sp<JDrm> setDrm(
486        JNIEnv *env, jobject thiz, const sp<JDrm> &drm) {
487    sp<JDrm> old = (JDrm *)env->GetLongField(thiz, gFields.context);
488    if (drm != NULL) {
489        drm->incStrong(thiz);
490    }
491    if (old != NULL) {
492        old->decStrong(thiz);
493    }
494    env->SetIntField(thiz, gFields.context, (int)drm.get());
495
496    return old;
497}
498
499static bool CheckSession(JNIEnv *env, const sp<IDrm> &drm, jbyteArray const &jsessionId)
500{
501    if (drm == NULL) {
502        jniThrowException(env, "java/lang/IllegalStateException", "MediaDrm obj is null");
503        return false;
504    }
505
506    if (jsessionId == NULL) {
507        jniThrowException(env, "java/lang/IllegalArgumentException", "sessionId is null");
508        return false;
509    }
510    return true;
511}
512
513static void android_media_MediaDrm_release(JNIEnv *env, jobject thiz) {
514    sp<JDrm> drm = setDrm(env, thiz, NULL);
515    if (drm != NULL) {
516        drm->setListener(NULL);
517    }
518}
519
520static void android_media_MediaDrm_native_init(JNIEnv *env) {
521    jclass clazz;
522    FIND_CLASS(clazz, "android/media/MediaDrm");
523    GET_FIELD_ID(gFields.context, clazz, "mNativeContext", "J");
524    GET_STATIC_METHOD_ID(gFields.post_event, clazz, "postEventFromNative",
525                         "(Ljava/lang/Object;IILjava/lang/Object;)V");
526
527    jfieldID field;
528    GET_STATIC_FIELD_ID(field, clazz, "EVENT_PROVISION_REQUIRED", "I");
529    gEventTypes.kEventProvisionRequired = env->GetStaticIntField(clazz, field);
530    GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_REQUIRED", "I");
531    gEventTypes.kEventKeyRequired = env->GetStaticIntField(clazz, field);
532    GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_EXPIRED", "I");
533    gEventTypes.kEventKeyExpired = env->GetStaticIntField(clazz, field);
534    GET_STATIC_FIELD_ID(field, clazz, "EVENT_VENDOR_DEFINED", "I");
535    gEventTypes.kEventVendorDefined = env->GetStaticIntField(clazz, field);
536
537    GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_STREAMING", "I");
538    gKeyTypes.kKeyTypeStreaming = env->GetStaticIntField(clazz, field);
539    GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_OFFLINE", "I");
540    gKeyTypes.kKeyTypeOffline = env->GetStaticIntField(clazz, field);
541    GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_RELEASE", "I");
542    gKeyTypes.kKeyTypeRelease = env->GetStaticIntField(clazz, field);
543
544    FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
545    GET_FIELD_ID(gFields.keyRequest.data, clazz, "mData", "[B");
546    GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
547
548    FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest");
549    GET_FIELD_ID(gFields.provisionRequest.data, clazz, "mData", "[B");
550    GET_FIELD_ID(gFields.provisionRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
551
552    FIND_CLASS(clazz, "java/util/ArrayList");
553    GET_METHOD_ID(gFields.arraylist.init, clazz, "<init>", "()V");
554    GET_METHOD_ID(gFields.arraylist.add, clazz, "add", "(Ljava/lang/Object;)Z");
555
556    FIND_CLASS(clazz, "java/util/HashMap");
557    GET_METHOD_ID(gFields.hashmap.init, clazz, "<init>", "()V");
558    GET_METHOD_ID(gFields.hashmap.get, clazz, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
559    GET_METHOD_ID(gFields.hashmap.put, clazz, "put",
560                  "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
561    GET_METHOD_ID(gFields.hashmap.entrySet, clazz, "entrySet", "()Ljava/util/Set;");
562
563    FIND_CLASS(clazz, "java/util/Set");
564    GET_METHOD_ID(gFields.set.iterator, clazz, "iterator", "()Ljava/util/Iterator;");
565
566    FIND_CLASS(clazz, "java/util/Iterator");
567    GET_METHOD_ID(gFields.iterator.next, clazz, "next", "()Ljava/lang/Object;");
568    GET_METHOD_ID(gFields.iterator.hasNext, clazz, "hasNext", "()Z");
569
570    FIND_CLASS(clazz, "java/util/Map$Entry");
571    GET_METHOD_ID(gFields.entry.getKey, clazz, "getKey", "()Ljava/lang/Object;");
572    GET_METHOD_ID(gFields.entry.getValue, clazz, "getValue", "()Ljava/lang/Object;");
573}
574
575static void android_media_MediaDrm_native_setup(
576        JNIEnv *env, jobject thiz,
577        jobject weak_this, jbyteArray uuidObj) {
578
579    if (uuidObj == NULL) {
580        jniThrowException(env, "java/lang/IllegalArgumentException", "uuid is null");
581        return;
582    }
583
584    Vector<uint8_t> uuid = JByteArrayToVector(env, uuidObj);
585
586    if (uuid.size() != 16) {
587        jniThrowException(env, "java/lang/IllegalArgumentException",
588                          "invalid UUID size, expected 16 bytes");
589        return;
590    }
591
592    sp<JDrm> drm = new JDrm(env, thiz, uuid.array());
593
594    status_t err = drm->initCheck();
595
596    if (err != OK) {
597        jniThrowException(
598                env,
599                "android/media/UnsupportedSchemeException",
600                "Failed to instantiate drm object.");
601        return;
602    }
603
604    sp<JNIDrmListener> listener = new JNIDrmListener(env, thiz, weak_this);
605    drm->setListener(listener);
606    setDrm(env, thiz, drm);
607}
608
609static void android_media_MediaDrm_native_finalize(
610        JNIEnv *env, jobject thiz) {
611    android_media_MediaDrm_release(env, thiz);
612}
613
614static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative(
615    JNIEnv *env, jobject thiz, jbyteArray uuidObj, jstring jmimeType) {
616
617    if (uuidObj == NULL) {
618        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
619        return false;
620    }
621
622    Vector<uint8_t> uuid = JByteArrayToVector(env, uuidObj);
623
624    if (uuid.size() != 16) {
625        jniThrowException(
626                env,
627                "java/lang/IllegalArgumentException",
628                "invalid UUID size, expected 16 bytes");
629        return false;
630    }
631
632    String8 mimeType;
633    if (jmimeType != NULL) {
634        mimeType = JStringToString8(env, jmimeType);
635    }
636
637    return JDrm::IsCryptoSchemeSupported(uuid.array(), mimeType);
638}
639
640static jbyteArray android_media_MediaDrm_openSession(
641    JNIEnv *env, jobject thiz) {
642    sp<IDrm> drm = GetDrm(env, thiz);
643
644    if (drm == NULL) {
645        jniThrowException(env, "java/lang/IllegalStateException",
646                          "MediaDrm obj is null");
647        return NULL;
648    }
649
650    Vector<uint8_t> sessionId;
651    status_t err = drm->openSession(sessionId);
652
653    if (throwExceptionAsNecessary(env, err, "Failed to open session")) {
654        return NULL;
655    }
656
657    return VectorToJByteArray(env, sessionId);
658}
659
660static void android_media_MediaDrm_closeSession(
661    JNIEnv *env, jobject thiz, jbyteArray jsessionId) {
662    sp<IDrm> drm = GetDrm(env, thiz);
663
664    if (!CheckSession(env, drm, jsessionId)) {
665        return;
666    }
667
668    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
669
670    status_t err = drm->closeSession(sessionId);
671
672    throwExceptionAsNecessary(env, err, "Failed to close session");
673}
674
675static jobject android_media_MediaDrm_getKeyRequest(
676    JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jinitData,
677    jstring jmimeType, jint jkeyType, jobject joptParams) {
678    sp<IDrm> drm = GetDrm(env, thiz);
679
680    if (!CheckSession(env, drm, jsessionId)) {
681        return NULL;
682    }
683
684    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
685
686    Vector<uint8_t> initData;
687    if (jinitData != NULL) {
688        initData = JByteArrayToVector(env, jinitData);
689    }
690
691    String8 mimeType;
692    if (jmimeType != NULL) {
693        mimeType = JStringToString8(env, jmimeType);
694    }
695
696    DrmPlugin::KeyType keyType;
697    if (jkeyType == gKeyTypes.kKeyTypeStreaming) {
698        keyType = DrmPlugin::kKeyType_Streaming;
699    } else if (jkeyType == gKeyTypes.kKeyTypeOffline) {
700        keyType = DrmPlugin::kKeyType_Offline;
701    } else if (jkeyType == gKeyTypes.kKeyTypeRelease) {
702        keyType = DrmPlugin::kKeyType_Release;
703    } else {
704        jniThrowException(env, "java/lang/IllegalArgumentException",
705                          "invalid keyType");
706        return NULL;
707    }
708
709    KeyedVector<String8, String8> optParams;
710    if (joptParams != NULL) {
711        optParams = HashMapToKeyedVector(env, joptParams);
712    }
713
714    Vector<uint8_t> request;
715    String8 defaultUrl;
716
717    status_t err = drm->getKeyRequest(sessionId, initData, mimeType,
718                                          keyType, optParams, request, defaultUrl);
719
720    if (throwExceptionAsNecessary(env, err, "Failed to get key request")) {
721        return NULL;
722    }
723
724    // Fill out return obj
725    jclass clazz;
726    FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
727
728    jobject keyObj = NULL;
729
730    if (clazz) {
731        keyObj = env->AllocObject(clazz);
732        jbyteArray jrequest = VectorToJByteArray(env, request);
733        env->SetObjectField(keyObj, gFields.keyRequest.data, jrequest);
734
735        jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string());
736        env->SetObjectField(keyObj, gFields.keyRequest.defaultUrl, jdefaultUrl);
737    }
738
739    return keyObj;
740}
741
742static jbyteArray android_media_MediaDrm_provideKeyResponse(
743    JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jresponse) {
744    sp<IDrm> drm = GetDrm(env, thiz);
745
746    if (!CheckSession(env, drm, jsessionId)) {
747        return NULL;
748    }
749
750    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
751
752    if (jresponse == NULL) {
753        jniThrowException(env, "java/lang/IllegalArgumentException",
754                          "key response is null");
755        return NULL;
756    }
757    Vector<uint8_t> response(JByteArrayToVector(env, jresponse));
758    Vector<uint8_t> keySetId;
759
760    status_t err = drm->provideKeyResponse(sessionId, response, keySetId);
761
762    if (throwExceptionAsNecessary(env, err, "Failed to handle key response")) {
763        return NULL;
764    }
765    return VectorToJByteArray(env, keySetId);
766}
767
768static void android_media_MediaDrm_removeKeys(
769    JNIEnv *env, jobject thiz, jbyteArray jkeysetId) {
770    sp<IDrm> drm = GetDrm(env, thiz);
771
772    if (jkeysetId == NULL) {
773        jniThrowException(env, "java/lang/IllegalArgumentException",
774                          "keySetId is null");
775        return;
776    }
777
778    Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId));
779
780    status_t err = drm->removeKeys(keySetId);
781
782    throwExceptionAsNecessary(env, err, "Failed to remove keys");
783}
784
785static void android_media_MediaDrm_restoreKeys(
786    JNIEnv *env, jobject thiz, jbyteArray jsessionId,
787    jbyteArray jkeysetId) {
788
789    sp<IDrm> drm = GetDrm(env, thiz);
790
791    if (!CheckSession(env, drm, jsessionId)) {
792        return;
793    }
794
795    if (jkeysetId == NULL) {
796        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
797        return;
798    }
799
800    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
801    Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId));
802
803    status_t err = drm->restoreKeys(sessionId, keySetId);
804
805    throwExceptionAsNecessary(env, err, "Failed to restore keys");
806}
807
808static jobject android_media_MediaDrm_queryKeyStatus(
809    JNIEnv *env, jobject thiz, jbyteArray jsessionId) {
810    sp<IDrm> drm = GetDrm(env, thiz);
811
812    if (!CheckSession(env, drm, jsessionId)) {
813        return NULL;
814    }
815    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
816
817    KeyedVector<String8, String8> infoMap;
818
819    status_t err = drm->queryKeyStatus(sessionId, infoMap);
820
821    if (throwExceptionAsNecessary(env, err, "Failed to query key status")) {
822        return NULL;
823    }
824
825    return KeyedVectorToHashMap(env, infoMap);
826}
827
828static jobject android_media_MediaDrm_getProvisionRequest(
829    JNIEnv *env, jobject thiz) {
830    sp<IDrm> drm = GetDrm(env, thiz);
831
832    if (drm == NULL) {
833        jniThrowException(env, "java/lang/IllegalStateException",
834                          "MediaDrm obj is null");
835        return NULL;
836    }
837
838    Vector<uint8_t> request;
839    String8 defaultUrl;
840
841    status_t err = drm->getProvisionRequest(request, defaultUrl);
842
843    if (throwExceptionAsNecessary(env, err, "Failed to get provision request")) {
844        return NULL;
845    }
846
847    // Fill out return obj
848    jclass clazz;
849    FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest");
850
851    jobject provisionObj = NULL;
852
853    if (clazz) {
854        provisionObj = env->AllocObject(clazz);
855        jbyteArray jrequest = VectorToJByteArray(env, request);
856        env->SetObjectField(provisionObj, gFields.provisionRequest.data, jrequest);
857
858        jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string());
859        env->SetObjectField(provisionObj, gFields.provisionRequest.defaultUrl, jdefaultUrl);
860    }
861
862    return provisionObj;
863}
864
865static void android_media_MediaDrm_provideProvisionResponse(
866    JNIEnv *env, jobject thiz, jbyteArray jresponse) {
867    sp<IDrm> drm = GetDrm(env, thiz);
868
869    if (drm == NULL) {
870        jniThrowException(env, "java/lang/IllegalStateException",
871                          "MediaDrm obj is null");
872        return;
873    }
874
875    if (jresponse == NULL) {
876        jniThrowException(env, "java/lang/IllegalArgumentException",
877                          "provision response is null");
878        return;
879    }
880
881    Vector<uint8_t> response(JByteArrayToVector(env, jresponse));
882
883    status_t err = drm->provideProvisionResponse(response);
884
885    throwExceptionAsNecessary(env, err, "Failed to handle provision response");
886}
887
888static jobject android_media_MediaDrm_getSecureStops(
889    JNIEnv *env, jobject thiz) {
890    sp<IDrm> drm = GetDrm(env, thiz);
891
892    if (drm == NULL) {
893        jniThrowException(env, "java/lang/IllegalStateException",
894                          "MediaDrm obj is null");
895        return NULL;
896    }
897
898    List<Vector<uint8_t> > secureStops;
899
900    status_t err = drm->getSecureStops(secureStops);
901
902    if (throwExceptionAsNecessary(env, err, "Failed to get secure stops")) {
903        return NULL;
904    }
905
906    return ListOfVectorsToArrayListOfByteArray(env, secureStops);
907}
908
909static void android_media_MediaDrm_releaseSecureStops(
910    JNIEnv *env, jobject thiz, jbyteArray jssRelease) {
911    sp<IDrm> drm = GetDrm(env, thiz);
912
913    if (drm == NULL) {
914        jniThrowException(env, "java/lang/IllegalStateException",
915                          "MediaDrm obj is null");
916        return;
917    }
918
919    Vector<uint8_t> ssRelease(JByteArrayToVector(env, jssRelease));
920
921    status_t err = drm->releaseSecureStops(ssRelease);
922
923    throwExceptionAsNecessary(env, err, "Failed to release secure stops");
924}
925
926static jstring android_media_MediaDrm_getPropertyString(
927    JNIEnv *env, jobject thiz, jstring jname) {
928    sp<IDrm> drm = GetDrm(env, thiz);
929
930    if (drm == NULL) {
931        jniThrowException(env, "java/lang/IllegalStateException",
932                          "MediaDrm obj is null");
933        return NULL;
934    }
935
936    if (jname == NULL) {
937        jniThrowException(env, "java/lang/IllegalArgumentException",
938                          "property name String is null");
939        return NULL;
940    }
941
942    String8 name = JStringToString8(env, jname);
943    String8 value;
944
945    status_t err = drm->getPropertyString(name, value);
946
947    if (throwExceptionAsNecessary(env, err, "Failed to get property")) {
948        return NULL;
949    }
950
951    return env->NewStringUTF(value.string());
952}
953
954static jbyteArray android_media_MediaDrm_getPropertyByteArray(
955    JNIEnv *env, jobject thiz, jstring jname) {
956    sp<IDrm> drm = GetDrm(env, thiz);
957
958    if (drm == NULL) {
959        jniThrowException(env, "java/lang/IllegalStateException",
960                          "MediaDrm obj is null");
961        return NULL;
962    }
963
964    if (jname == NULL) {
965        jniThrowException(env, "java/lang/IllegalArgumentException",
966                          "property name String is null");
967        return NULL;
968    }
969
970    String8 name = JStringToString8(env, jname);
971    Vector<uint8_t> value;
972
973    status_t err = drm->getPropertyByteArray(name, value);
974
975    if (throwExceptionAsNecessary(env, err, "Failed to get property")) {
976        return NULL;
977    }
978
979    return VectorToJByteArray(env, value);
980}
981
982static void android_media_MediaDrm_setPropertyString(
983    JNIEnv *env, jobject thiz, jstring jname, jstring jvalue) {
984    sp<IDrm> drm = GetDrm(env, thiz);
985
986    if (drm == NULL) {
987        jniThrowException(env, "java/lang/IllegalStateException",
988                          "MediaDrm obj is null");
989        return;
990    }
991
992    if (jname == NULL) {
993        jniThrowException(env, "java/lang/IllegalArgumentException",
994                          "property name String is null");
995        return;
996    }
997
998    if (jvalue == NULL) {
999        jniThrowException(env, "java/lang/IllegalArgumentException",
1000                          "property value String is null");
1001        return;
1002    }
1003
1004    String8 name = JStringToString8(env, jname);
1005    String8 value = JStringToString8(env, jvalue);
1006
1007    status_t err = drm->setPropertyString(name, value);
1008
1009    throwExceptionAsNecessary(env, err, "Failed to set property");
1010}
1011
1012static void android_media_MediaDrm_setPropertyByteArray(
1013    JNIEnv *env, jobject thiz, jstring jname, jbyteArray jvalue) {
1014    sp<IDrm> drm = GetDrm(env, thiz);
1015
1016    if (drm == NULL) {
1017        jniThrowException(env, "java/lang/IllegalStateException",
1018                          "MediaDrm obj is null");
1019        return;
1020    }
1021
1022    if (jname == NULL) {
1023        jniThrowException(env, "java/lang/IllegalArgumentException",
1024                          "property name String is null");
1025        return;
1026    }
1027
1028    if (jvalue == NULL) {
1029        jniThrowException(env, "java/lang/IllegalArgumentException",
1030                          "property value byte array is null");
1031        return;
1032    }
1033
1034    String8 name = JStringToString8(env, jname);
1035    Vector<uint8_t> value = JByteArrayToVector(env, jvalue);
1036
1037    status_t err = drm->setPropertyByteArray(name, value);
1038
1039    throwExceptionAsNecessary(env, err, "Failed to set property");
1040}
1041
1042static void android_media_MediaDrm_setCipherAlgorithmNative(
1043    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1044    jstring jalgorithm) {
1045
1046    sp<IDrm> drm = GetDrm(env, jdrm);
1047
1048    if (!CheckSession(env, drm, jsessionId)) {
1049        return;
1050    }
1051
1052    if (jalgorithm == NULL) {
1053        jniThrowException(env, "java/lang/IllegalArgumentException",
1054                          "algorithm String is null");
1055        return;
1056    }
1057
1058    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1059    String8 algorithm = JStringToString8(env, jalgorithm);
1060
1061    status_t err = drm->setCipherAlgorithm(sessionId, algorithm);
1062
1063    throwExceptionAsNecessary(env, err, "Failed to set cipher algorithm");
1064}
1065
1066static void android_media_MediaDrm_setMacAlgorithmNative(
1067    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1068    jstring jalgorithm) {
1069
1070    sp<IDrm> drm = GetDrm(env, jdrm);
1071
1072    if (!CheckSession(env, drm, jsessionId)) {
1073        return;
1074    }
1075
1076    if (jalgorithm == NULL) {
1077        jniThrowException(env, "java/lang/IllegalArgumentException",
1078                          "algorithm String is null");
1079        return;
1080    }
1081
1082    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1083    String8 algorithm = JStringToString8(env, jalgorithm);
1084
1085    status_t err = drm->setMacAlgorithm(sessionId, algorithm);
1086
1087    throwExceptionAsNecessary(env, err, "Failed to set mac algorithm");
1088}
1089
1090
1091static jbyteArray android_media_MediaDrm_encryptNative(
1092    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1093    jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) {
1094
1095    sp<IDrm> drm = GetDrm(env, jdrm);
1096
1097    if (!CheckSession(env, drm, jsessionId)) {
1098        return NULL;
1099    }
1100
1101    if (jkeyId == NULL || jinput == NULL || jiv == NULL) {
1102        jniThrowException(env, "java/lang/IllegalArgumentException",
1103                          "required argument is null");
1104        return NULL;
1105    }
1106
1107    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1108    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1109    Vector<uint8_t> input(JByteArrayToVector(env, jinput));
1110    Vector<uint8_t> iv(JByteArrayToVector(env, jiv));
1111    Vector<uint8_t> output;
1112
1113    status_t err = drm->encrypt(sessionId, keyId, input, iv, output);
1114
1115    if (throwExceptionAsNecessary(env, err, "Failed to encrypt")) {
1116        return NULL;
1117    }
1118
1119    return VectorToJByteArray(env, output);
1120}
1121
1122static jbyteArray android_media_MediaDrm_decryptNative(
1123    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1124    jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) {
1125
1126    sp<IDrm> drm = GetDrm(env, jdrm);
1127
1128    if (!CheckSession(env, drm, jsessionId)) {
1129        return NULL;
1130    }
1131
1132    if (jkeyId == NULL || jinput == NULL || jiv == NULL) {
1133        jniThrowException(env, "java/lang/IllegalArgumentException",
1134                          "required argument is null");
1135        return NULL;
1136    }
1137
1138    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1139    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1140    Vector<uint8_t> input(JByteArrayToVector(env, jinput));
1141    Vector<uint8_t> iv(JByteArrayToVector(env, jiv));
1142    Vector<uint8_t> output;
1143
1144    status_t err = drm->decrypt(sessionId, keyId, input, iv, output);
1145    if (throwExceptionAsNecessary(env, err, "Failed to decrypt")) {
1146        return NULL;
1147    }
1148
1149    return VectorToJByteArray(env, output);
1150}
1151
1152static jbyteArray android_media_MediaDrm_signNative(
1153    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1154    jbyteArray jkeyId, jbyteArray jmessage) {
1155
1156    sp<IDrm> drm = GetDrm(env, jdrm);
1157
1158    if (!CheckSession(env, drm, jsessionId)) {
1159        return NULL;
1160    }
1161
1162    if (jkeyId == NULL || jmessage == NULL) {
1163        jniThrowException(env, "java/lang/IllegalArgumentException",
1164                          "required argument is null");
1165        return NULL;
1166    }
1167
1168    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1169    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1170    Vector<uint8_t> message(JByteArrayToVector(env, jmessage));
1171    Vector<uint8_t> signature;
1172
1173    status_t err = drm->sign(sessionId, keyId, message, signature);
1174
1175    if (throwExceptionAsNecessary(env, err, "Failed to sign")) {
1176        return NULL;
1177    }
1178
1179    return VectorToJByteArray(env, signature);
1180}
1181
1182static jboolean android_media_MediaDrm_verifyNative(
1183    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1184    jbyteArray jkeyId, jbyteArray jmessage, jbyteArray jsignature) {
1185
1186    sp<IDrm> drm = GetDrm(env, jdrm);
1187
1188    if (!CheckSession(env, drm, jsessionId)) {
1189        return false;
1190    }
1191
1192    if (jkeyId == NULL || jmessage == NULL || jsignature == NULL) {
1193        jniThrowException(env, "java/lang/IllegalArgumentException",
1194                          "required argument is null");
1195        return false;
1196    }
1197
1198    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1199    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1200    Vector<uint8_t> message(JByteArrayToVector(env, jmessage));
1201    Vector<uint8_t> signature(JByteArrayToVector(env, jsignature));
1202    bool match;
1203
1204    status_t err = drm->verify(sessionId, keyId, message, signature, match);
1205
1206    throwExceptionAsNecessary(env, err, "Failed to verify");
1207    return match;
1208}
1209
1210
1211static JNINativeMethod gMethods[] = {
1212    { "release", "()V", (void *)android_media_MediaDrm_release },
1213    { "native_init", "()V", (void *)android_media_MediaDrm_native_init },
1214
1215    { "native_setup", "(Ljava/lang/Object;[B)V",
1216      (void *)android_media_MediaDrm_native_setup },
1217
1218    { "native_finalize", "()V",
1219      (void *)android_media_MediaDrm_native_finalize },
1220
1221    { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;)Z",
1222      (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative },
1223
1224    { "openSession", "()[B",
1225      (void *)android_media_MediaDrm_openSession },
1226
1227    { "closeSession", "([B)V",
1228      (void *)android_media_MediaDrm_closeSession },
1229
1230    { "getKeyRequest", "([B[BLjava/lang/String;ILjava/util/HashMap;)"
1231      "Landroid/media/MediaDrm$KeyRequest;",
1232      (void *)android_media_MediaDrm_getKeyRequest },
1233
1234    { "provideKeyResponse", "([B[B)[B",
1235      (void *)android_media_MediaDrm_provideKeyResponse },
1236
1237    { "removeKeys", "([B)V",
1238      (void *)android_media_MediaDrm_removeKeys },
1239
1240    { "restoreKeys", "([B[B)V",
1241      (void *)android_media_MediaDrm_restoreKeys },
1242
1243    { "queryKeyStatus", "([B)Ljava/util/HashMap;",
1244      (void *)android_media_MediaDrm_queryKeyStatus },
1245
1246    { "getProvisionRequest", "()Landroid/media/MediaDrm$ProvisionRequest;",
1247      (void *)android_media_MediaDrm_getProvisionRequest },
1248
1249    { "provideProvisionResponse", "([B)V",
1250      (void *)android_media_MediaDrm_provideProvisionResponse },
1251
1252    { "getSecureStops", "()Ljava/util/List;",
1253      (void *)android_media_MediaDrm_getSecureStops },
1254
1255    { "releaseSecureStops", "([B)V",
1256      (void *)android_media_MediaDrm_releaseSecureStops },
1257
1258    { "getPropertyString", "(Ljava/lang/String;)Ljava/lang/String;",
1259      (void *)android_media_MediaDrm_getPropertyString },
1260
1261    { "getPropertyByteArray", "(Ljava/lang/String;)[B",
1262      (void *)android_media_MediaDrm_getPropertyByteArray },
1263
1264    { "setPropertyString", "(Ljava/lang/String;Ljava/lang/String;)V",
1265      (void *)android_media_MediaDrm_setPropertyString },
1266
1267    { "setPropertyByteArray", "(Ljava/lang/String;[B)V",
1268      (void *)android_media_MediaDrm_setPropertyByteArray },
1269
1270    { "setCipherAlgorithmNative",
1271      "(Landroid/media/MediaDrm;[BLjava/lang/String;)V",
1272      (void *)android_media_MediaDrm_setCipherAlgorithmNative },
1273
1274    { "setMacAlgorithmNative",
1275      "(Landroid/media/MediaDrm;[BLjava/lang/String;)V",
1276      (void *)android_media_MediaDrm_setMacAlgorithmNative },
1277
1278    { "encryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B",
1279      (void *)android_media_MediaDrm_encryptNative },
1280
1281    { "decryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B",
1282      (void *)android_media_MediaDrm_decryptNative },
1283
1284    { "signNative", "(Landroid/media/MediaDrm;[B[B[B)[B",
1285      (void *)android_media_MediaDrm_signNative },
1286
1287    { "verifyNative", "(Landroid/media/MediaDrm;[B[B[B[B)Z",
1288      (void *)android_media_MediaDrm_verifyNative },
1289};
1290
1291int register_android_media_Drm(JNIEnv *env) {
1292    return AndroidRuntime::registerNativeMethods(env,
1293                "android/media/MediaDrm", gMethods, NELEM(gMethods));
1294}
1295
1296