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