android_media_MediaDrm.cpp revision 8117d8f7023f8981bc4b2651efed5b28104d83d3
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_RESOURCE_BUSY) {
246        jniThrowException(env, "android/media/ResourceBusyException", msg);
247        return true;
248    } else if (err == ERROR_DRM_DEVICE_REVOKED) {
249        jniThrowException(env, "android/media/DeniedByServerException", msg);
250        return true;
251    } else if (err != OK) {
252        String8 errbuf;
253        if (drmMessage != NULL) {
254            if (msg == NULL) {
255                msg = drmMessage;
256            } else {
257                errbuf.format("%s: %s", msg, drmMessage);
258                msg = errbuf.string();
259            }
260        }
261        ALOGE("Illegal state exception: %s", msg);
262        jniThrowException(env, "java/lang/IllegalStateException", msg);
263        return true;
264    }
265    return false;
266}
267
268static sp<IDrm> GetDrm(JNIEnv *env, jobject thiz) {
269    JDrm *jdrm = (JDrm *)env->GetIntField(thiz, gFields.context);
270    return jdrm ? jdrm->getDrm() : NULL;
271}
272
273JDrm::JDrm(
274        JNIEnv *env, jobject thiz, const uint8_t uuid[16]) {
275    mObject = env->NewWeakGlobalRef(thiz);
276    mDrm = MakeDrm(uuid);
277    if (mDrm != NULL) {
278        mDrm->setListener(this);
279    }
280}
281
282JDrm::~JDrm() {
283    mDrm.clear();
284
285    JNIEnv *env = AndroidRuntime::getJNIEnv();
286
287    env->DeleteWeakGlobalRef(mObject);
288    mObject = NULL;
289}
290
291// static
292sp<IDrm> JDrm::MakeDrm() {
293    sp<IServiceManager> sm = defaultServiceManager();
294
295    sp<IBinder> binder =
296        sm->getService(String16("media.player"));
297
298    sp<IMediaPlayerService> service =
299        interface_cast<IMediaPlayerService>(binder);
300
301    if (service == NULL) {
302        return NULL;
303    }
304
305    sp<IDrm> drm = service->makeDrm();
306
307    if (drm == NULL || (drm->initCheck() != OK && drm->initCheck() != NO_INIT)) {
308        return NULL;
309    }
310
311    return drm;
312}
313
314// static
315sp<IDrm> JDrm::MakeDrm(const uint8_t uuid[16]) {
316    sp<IDrm> drm = MakeDrm();
317
318    if (drm == NULL) {
319        return NULL;
320    }
321
322    status_t err = drm->createPlugin(uuid);
323
324    if (err != OK) {
325        return NULL;
326    }
327
328    return drm;
329}
330
331status_t JDrm::setListener(const sp<DrmListener>& listener) {
332    Mutex::Autolock lock(mLock);
333    mListener = listener;
334    return OK;
335}
336
337void JDrm::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) {
338    sp<DrmListener> listener;
339    mLock.lock();
340    listener = mListener;
341    mLock.unlock();
342
343    if (listener != NULL) {
344        Mutex::Autolock lock(mNotifyLock);
345        listener->notify(eventType, extra, obj);
346    }
347}
348
349
350// static
351bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16]) {
352    sp<IDrm> drm = MakeDrm();
353
354    if (drm == NULL) {
355        return false;
356    }
357
358    return drm->isCryptoSchemeSupported(uuid);
359}
360
361status_t JDrm::initCheck() const {
362    return mDrm == NULL ? NO_INIT : OK;
363}
364
365// JNI conversion utilities
366static Vector<uint8_t> JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArray) {
367    Vector<uint8_t> vector;
368    size_t length = env->GetArrayLength(byteArray);
369    vector.insertAt((size_t)0, length);
370    env->GetByteArrayRegion(byteArray, 0, length, (jbyte *)vector.editArray());
371    return vector;
372}
373
374static jbyteArray VectorToJByteArray(JNIEnv *env, Vector<uint8_t> const &vector) {
375    size_t length = vector.size();
376    jbyteArray result = env->NewByteArray(length);
377    if (result != NULL) {
378        env->SetByteArrayRegion(result, 0, length, (jbyte *)vector.array());
379    }
380    return result;
381}
382
383static String8 JStringToString8(JNIEnv *env, jstring const &jstr) {
384    String8 result;
385
386    const char *s = env->GetStringUTFChars(jstr, NULL);
387    if (s) {
388        result = s;
389        env->ReleaseStringUTFChars(jstr, s);
390    }
391    return result;
392}
393
394/*
395    import java.util.HashMap;
396    import java.util.Set;
397    import java.Map.Entry;
398    import jav.util.Iterator;
399
400    HashMap<k, v> hm;
401    Set<Entry<k, v> > s = hm.entrySet();
402    Iterator i = s.iterator();
403    Entry e = s.next();
404*/
405
406static KeyedVector<String8, String8> HashMapToKeyedVector(JNIEnv *env, jobject &hashMap) {
407    jclass clazz;
408    FIND_CLASS(clazz, "java/lang/String");
409    KeyedVector<String8, String8> keyedVector;
410
411    jobject entrySet = env->CallObjectMethod(hashMap, gFields.hashmap.entrySet);
412    if (entrySet) {
413        jobject iterator = env->CallObjectMethod(entrySet, gFields.set.iterator);
414        if (iterator) {
415            jboolean hasNext = env->CallBooleanMethod(iterator, gFields.iterator.hasNext);
416            while (hasNext) {
417                jobject entry = env->CallObjectMethod(iterator, gFields.iterator.next);
418                if (entry) {
419                    jobject obj = env->CallObjectMethod(entry, gFields.entry.getKey);
420                    if (!env->IsInstanceOf(obj, clazz)) {
421                        jniThrowException(env, "java/lang/IllegalArgumentException",
422                                          "HashMap key is not a String");
423                    }
424                    jstring jkey = static_cast<jstring>(obj);
425
426                    obj = env->CallObjectMethod(entry, gFields.entry.getValue);
427                    if (!env->IsInstanceOf(obj, clazz)) {
428                        jniThrowException(env, "java/lang/IllegalArgumentException",
429                                          "HashMap value is not a String");
430                    }
431                    jstring jvalue = static_cast<jstring>(obj);
432
433                    String8 key = JStringToString8(env, jkey);
434                    String8 value = JStringToString8(env, jvalue);
435                    keyedVector.add(key, value);
436
437                    env->DeleteLocalRef(jkey);
438                    env->DeleteLocalRef(jvalue);
439                    hasNext = env->CallBooleanMethod(iterator, gFields.iterator.hasNext);
440                }
441                env->DeleteLocalRef(entry);
442            }
443            env->DeleteLocalRef(iterator);
444        }
445        env->DeleteLocalRef(entrySet);
446    }
447    return keyedVector;
448}
449
450static jobject KeyedVectorToHashMap (JNIEnv *env, KeyedVector<String8, String8> const &map) {
451    jclass clazz;
452    FIND_CLASS(clazz, "java/util/HashMap");
453    jobject hashMap = env->NewObject(clazz, gFields.hashmap.init);
454    for (size_t i = 0; i < map.size(); ++i) {
455        jstring jkey = env->NewStringUTF(map.keyAt(i).string());
456        jstring jvalue = env->NewStringUTF(map.valueAt(i).string());
457        env->CallObjectMethod(hashMap, gFields.hashmap.put, jkey, jvalue);
458        env->DeleteLocalRef(jkey);
459        env->DeleteLocalRef(jvalue);
460    }
461    return hashMap;
462}
463
464static jobject ListOfVectorsToArrayListOfByteArray(JNIEnv *env,
465                                                   List<Vector<uint8_t> > list) {
466    jclass clazz;
467    FIND_CLASS(clazz, "java/util/ArrayList");
468    jobject arrayList = env->NewObject(clazz, gFields.arraylist.init);
469    List<Vector<uint8_t> >::iterator iter = list.begin();
470    while (iter != list.end()) {
471        jbyteArray byteArray = VectorToJByteArray(env, *iter);
472        env->CallBooleanMethod(arrayList, gFields.arraylist.add, byteArray);
473        env->DeleteLocalRef(byteArray);
474        iter++;
475    }
476
477    return arrayList;
478}
479
480}  // namespace android
481
482using namespace android;
483
484static sp<JDrm> setDrm(
485        JNIEnv *env, jobject thiz, const sp<JDrm> &drm) {
486    sp<JDrm> old = (JDrm *)env->GetIntField(thiz, gFields.context);
487    if (drm != NULL) {
488        drm->incStrong(thiz);
489    }
490    if (old != NULL) {
491        old->decStrong(thiz);
492    }
493    env->SetIntField(thiz, gFields.context, (int)drm.get());
494
495    return old;
496}
497
498static bool CheckSession(JNIEnv *env, const sp<IDrm> &drm, jbyteArray const &jsessionId)
499{
500    if (drm == NULL) {
501        jniThrowException(env, "java/lang/IllegalStateException", "MediaDrm obj is null");
502        return false;
503    }
504
505    if (jsessionId == NULL) {
506        jniThrowException(env, "java/lang/IllegalArgumentException", "sessionId is null");
507        return false;
508    }
509    return true;
510}
511
512static void android_media_MediaDrm_release(JNIEnv *env, jobject thiz) {
513    sp<JDrm> drm = setDrm(env, thiz, NULL);
514    if (drm != NULL) {
515        drm->setListener(NULL);
516    }
517}
518
519static void android_media_MediaDrm_native_init(JNIEnv *env) {
520    jclass clazz;
521    FIND_CLASS(clazz, "android/media/MediaDrm");
522    GET_FIELD_ID(gFields.context, clazz, "mNativeContext", "I");
523    GET_STATIC_METHOD_ID(gFields.post_event, clazz, "postEventFromNative",
524                         "(Ljava/lang/Object;IILjava/lang/Object;)V");
525
526    jfieldID field;
527    GET_STATIC_FIELD_ID(field, clazz, "EVENT_PROVISION_REQUIRED", "I");
528    gEventTypes.kEventProvisionRequired = env->GetStaticIntField(clazz, field);
529    GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_REQUIRED", "I");
530    gEventTypes.kEventKeyRequired = env->GetStaticIntField(clazz, field);
531    GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_EXPIRED", "I");
532    gEventTypes.kEventKeyExpired = env->GetStaticIntField(clazz, field);
533    GET_STATIC_FIELD_ID(field, clazz, "EVENT_VENDOR_DEFINED", "I");
534    gEventTypes.kEventVendorDefined = env->GetStaticIntField(clazz, field);
535
536    GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_STREAMING", "I");
537    gKeyTypes.kKeyTypeStreaming = env->GetStaticIntField(clazz, field);
538    GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_OFFLINE", "I");
539    gKeyTypes.kKeyTypeOffline = env->GetStaticIntField(clazz, field);
540    GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_RELEASE", "I");
541    gKeyTypes.kKeyTypeRelease = env->GetStaticIntField(clazz, field);
542
543    FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
544    GET_FIELD_ID(gFields.keyRequest.data, clazz, "mData", "[B");
545    GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
546
547    FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest");
548    GET_FIELD_ID(gFields.provisionRequest.data, clazz, "mData", "[B");
549    GET_FIELD_ID(gFields.provisionRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
550
551    FIND_CLASS(clazz, "java/util/ArrayList");
552    GET_METHOD_ID(gFields.arraylist.init, clazz, "<init>", "()V");
553    GET_METHOD_ID(gFields.arraylist.add, clazz, "add", "(Ljava/lang/Object;)Z");
554
555    FIND_CLASS(clazz, "java/util/HashMap");
556    GET_METHOD_ID(gFields.hashmap.init, clazz, "<init>", "()V");
557    GET_METHOD_ID(gFields.hashmap.get, clazz, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
558    GET_METHOD_ID(gFields.hashmap.put, clazz, "put",
559                  "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
560    GET_METHOD_ID(gFields.hashmap.entrySet, clazz, "entrySet", "()Ljava/util/Set;");
561
562    FIND_CLASS(clazz, "java/util/Set");
563    GET_METHOD_ID(gFields.set.iterator, clazz, "iterator", "()Ljava/util/Iterator;");
564
565    FIND_CLASS(clazz, "java/util/Iterator");
566    GET_METHOD_ID(gFields.iterator.next, clazz, "next", "()Ljava/lang/Object;");
567    GET_METHOD_ID(gFields.iterator.hasNext, clazz, "hasNext", "()Z");
568
569    FIND_CLASS(clazz, "java/util/Map$Entry");
570    GET_METHOD_ID(gFields.entry.getKey, clazz, "getKey", "()Ljava/lang/Object;");
571    GET_METHOD_ID(gFields.entry.getValue, clazz, "getValue", "()Ljava/lang/Object;");
572}
573
574static void android_media_MediaDrm_native_setup(
575        JNIEnv *env, jobject thiz,
576        jobject weak_this, jbyteArray uuidObj) {
577
578    if (uuidObj == NULL) {
579        jniThrowException(env, "java/lang/IllegalArgumentException", "uuid is null");
580        return;
581    }
582
583    Vector<uint8_t> uuid = JByteArrayToVector(env, uuidObj);
584
585    if (uuid.size() != 16) {
586        jniThrowException(env, "java/lang/IllegalArgumentException",
587                          "invalid UUID size, expected 16 bytes");
588        return;
589    }
590
591    sp<JDrm> drm = new JDrm(env, thiz, uuid.array());
592
593    status_t err = drm->initCheck();
594
595    if (err != OK) {
596        jniThrowException(
597                env,
598                "android/media/UnsupportedSchemeException",
599                "Failed to instantiate drm object.");
600        return;
601    }
602
603    sp<JNIDrmListener> listener = new JNIDrmListener(env, thiz, weak_this);
604    drm->setListener(listener);
605    setDrm(env, thiz, drm);
606}
607
608static void android_media_MediaDrm_native_finalize(
609        JNIEnv *env, jobject thiz) {
610    android_media_MediaDrm_release(env, thiz);
611}
612
613static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative(
614        JNIEnv *env, jobject thiz, jbyteArray uuidObj) {
615
616    if (uuidObj == NULL) {
617        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
618        return false;
619    }
620
621    Vector<uint8_t> uuid = JByteArrayToVector(env, uuidObj);
622
623    if (uuid.size() != 16) {
624        jniThrowException(
625                env,
626                "java/lang/IllegalArgumentException",
627                "invalid UUID size, expected 16 bytes");
628        return false;
629    }
630
631    return JDrm::IsCryptoSchemeSupported(uuid.array());
632}
633
634static jbyteArray android_media_MediaDrm_openSession(
635    JNIEnv *env, jobject thiz) {
636    sp<IDrm> drm = GetDrm(env, thiz);
637
638    if (drm == NULL) {
639        jniThrowException(env, "java/lang/IllegalStateException",
640                          "MediaDrm obj is null");
641        return NULL;
642    }
643
644    Vector<uint8_t> sessionId;
645    status_t err = drm->openSession(sessionId);
646
647    if (throwExceptionAsNecessary(env, err, "Failed to open session")) {
648        return NULL;
649    }
650
651    return VectorToJByteArray(env, sessionId);
652}
653
654static void android_media_MediaDrm_closeSession(
655    JNIEnv *env, jobject thiz, jbyteArray jsessionId) {
656    sp<IDrm> drm = GetDrm(env, thiz);
657
658    if (!CheckSession(env, drm, jsessionId)) {
659        return;
660    }
661
662    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
663
664    status_t err = drm->closeSession(sessionId);
665
666    throwExceptionAsNecessary(env, err, "Failed to close session");
667}
668
669static jobject android_media_MediaDrm_getKeyRequest(
670    JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jinitData,
671    jstring jmimeType, jint jkeyType, jobject joptParams) {
672    sp<IDrm> drm = GetDrm(env, thiz);
673
674    if (!CheckSession(env, drm, jsessionId)) {
675        return NULL;
676    }
677
678    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
679
680    Vector<uint8_t> initData;
681    if (jinitData != NULL) {
682        initData = JByteArrayToVector(env, jinitData);
683    }
684
685    String8 mimeType;
686    if (jmimeType != NULL) {
687        mimeType = JStringToString8(env, jmimeType);
688    }
689
690    DrmPlugin::KeyType keyType;
691    if (jkeyType == gKeyTypes.kKeyTypeStreaming) {
692        keyType = DrmPlugin::kKeyType_Streaming;
693    } else if (jkeyType == gKeyTypes.kKeyTypeOffline) {
694        keyType = DrmPlugin::kKeyType_Offline;
695    } else if (jkeyType == gKeyTypes.kKeyTypeRelease) {
696        keyType = DrmPlugin::kKeyType_Release;
697    } else {
698        jniThrowException(env, "java/lang/IllegalArgumentException",
699                          "invalid keyType");
700        return NULL;
701    }
702
703    KeyedVector<String8, String8> optParams;
704    if (joptParams != NULL) {
705        optParams = HashMapToKeyedVector(env, joptParams);
706    }
707
708    Vector<uint8_t> request;
709    String8 defaultUrl;
710
711    status_t err = drm->getKeyRequest(sessionId, initData, mimeType,
712                                          keyType, optParams, request, defaultUrl);
713
714    if (throwExceptionAsNecessary(env, err, "Failed to get key request")) {
715        return NULL;
716    }
717
718    // Fill out return obj
719    jclass clazz;
720    FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
721
722    jobject keyObj = NULL;
723
724    if (clazz) {
725        keyObj = env->AllocObject(clazz);
726        jbyteArray jrequest = VectorToJByteArray(env, request);
727        env->SetObjectField(keyObj, gFields.keyRequest.data, jrequest);
728
729        jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string());
730        env->SetObjectField(keyObj, gFields.keyRequest.defaultUrl, jdefaultUrl);
731    }
732
733    return keyObj;
734}
735
736static jbyteArray android_media_MediaDrm_provideKeyResponse(
737    JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jresponse) {
738    sp<IDrm> drm = GetDrm(env, thiz);
739
740    if (!CheckSession(env, drm, jsessionId)) {
741        return NULL;
742    }
743
744    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
745
746    if (jresponse == NULL) {
747        jniThrowException(env, "java/lang/IllegalArgumentException",
748                          "key response is null");
749        return NULL;
750    }
751    Vector<uint8_t> response(JByteArrayToVector(env, jresponse));
752    Vector<uint8_t> keySetId;
753
754    status_t err = drm->provideKeyResponse(sessionId, response, keySetId);
755
756    if (throwExceptionAsNecessary(env, err, "Failed to handle key response")) {
757        return NULL;
758    }
759    return VectorToJByteArray(env, keySetId);
760}
761
762static void android_media_MediaDrm_removeKeys(
763    JNIEnv *env, jobject thiz, jbyteArray jkeysetId) {
764    sp<IDrm> drm = GetDrm(env, thiz);
765
766    if (jkeysetId == NULL) {
767        jniThrowException(env, "java/lang/IllegalArgumentException",
768                          "keySetId is null");
769        return;
770    }
771
772    Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId));
773
774    status_t err = drm->removeKeys(keySetId);
775
776    throwExceptionAsNecessary(env, err, "Failed to remove keys");
777}
778
779static void android_media_MediaDrm_restoreKeys(
780    JNIEnv *env, jobject thiz, jbyteArray jsessionId,
781    jbyteArray jkeysetId) {
782
783    sp<IDrm> drm = GetDrm(env, thiz);
784
785    if (!CheckSession(env, drm, jsessionId)) {
786        return;
787    }
788
789    if (jkeysetId == NULL) {
790        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
791        return;
792    }
793
794    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
795    Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId));
796
797    status_t err = drm->restoreKeys(sessionId, keySetId);
798
799    throwExceptionAsNecessary(env, err, "Failed to restore keys");
800}
801
802static jobject android_media_MediaDrm_queryKeyStatus(
803    JNIEnv *env, jobject thiz, jbyteArray jsessionId) {
804    sp<IDrm> drm = GetDrm(env, thiz);
805
806    if (!CheckSession(env, drm, jsessionId)) {
807        return NULL;
808    }
809    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
810
811    KeyedVector<String8, String8> infoMap;
812
813    status_t err = drm->queryKeyStatus(sessionId, infoMap);
814
815    if (throwExceptionAsNecessary(env, err, "Failed to query key status")) {
816        return NULL;
817    }
818
819    return KeyedVectorToHashMap(env, infoMap);
820}
821
822static jobject android_media_MediaDrm_getProvisionRequest(
823    JNIEnv *env, jobject thiz) {
824    sp<IDrm> drm = GetDrm(env, thiz);
825
826    if (drm == NULL) {
827        jniThrowException(env, "java/lang/IllegalStateException",
828                          "MediaDrm obj is null");
829        return NULL;
830    }
831
832    Vector<uint8_t> request;
833    String8 defaultUrl;
834
835    status_t err = drm->getProvisionRequest(request, defaultUrl);
836
837    if (throwExceptionAsNecessary(env, err, "Failed to get provision request")) {
838        return NULL;
839    }
840
841    // Fill out return obj
842    jclass clazz;
843    FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest");
844
845    jobject provisionObj = NULL;
846
847    if (clazz) {
848        provisionObj = env->AllocObject(clazz);
849        jbyteArray jrequest = VectorToJByteArray(env, request);
850        env->SetObjectField(provisionObj, gFields.provisionRequest.data, jrequest);
851
852        jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string());
853        env->SetObjectField(provisionObj, gFields.provisionRequest.defaultUrl, jdefaultUrl);
854    }
855
856    return provisionObj;
857}
858
859static void android_media_MediaDrm_provideProvisionResponse(
860    JNIEnv *env, jobject thiz, jbyteArray jresponse) {
861    sp<IDrm> drm = GetDrm(env, thiz);
862
863    if (drm == NULL) {
864        jniThrowException(env, "java/lang/IllegalStateException",
865                          "MediaDrm obj is null");
866        return;
867    }
868
869    if (jresponse == NULL) {
870        jniThrowException(env, "java/lang/IllegalArgumentException",
871                          "provision response is null");
872        return;
873    }
874
875    Vector<uint8_t> response(JByteArrayToVector(env, jresponse));
876
877    status_t err = drm->provideProvisionResponse(response);
878
879    throwExceptionAsNecessary(env, err, "Failed to handle provision response");
880}
881
882static jobject android_media_MediaDrm_getSecureStops(
883    JNIEnv *env, jobject thiz) {
884    sp<IDrm> drm = GetDrm(env, thiz);
885
886    if (drm == NULL) {
887        jniThrowException(env, "java/lang/IllegalStateException",
888                          "MediaDrm obj is null");
889        return NULL;
890    }
891
892    List<Vector<uint8_t> > secureStops;
893
894    status_t err = drm->getSecureStops(secureStops);
895
896    if (throwExceptionAsNecessary(env, err, "Failed to get secure stops")) {
897        return NULL;
898    }
899
900    return ListOfVectorsToArrayListOfByteArray(env, secureStops);
901}
902
903static void android_media_MediaDrm_releaseSecureStops(
904    JNIEnv *env, jobject thiz, jbyteArray jssRelease) {
905    sp<IDrm> drm = GetDrm(env, thiz);
906
907    if (drm == NULL) {
908        jniThrowException(env, "java/lang/IllegalStateException",
909                          "MediaDrm obj is null");
910        return;
911    }
912
913    Vector<uint8_t> ssRelease(JByteArrayToVector(env, jssRelease));
914
915    status_t err = drm->releaseSecureStops(ssRelease);
916
917    throwExceptionAsNecessary(env, err, "Failed to release secure stops");
918}
919
920static jstring android_media_MediaDrm_getPropertyString(
921    JNIEnv *env, jobject thiz, jstring jname) {
922    sp<IDrm> drm = GetDrm(env, thiz);
923
924    if (drm == NULL) {
925        jniThrowException(env, "java/lang/IllegalStateException",
926                          "MediaDrm obj is null");
927        return NULL;
928    }
929
930    if (jname == NULL) {
931        jniThrowException(env, "java/lang/IllegalArgumentException",
932                          "property name String is null");
933        return NULL;
934    }
935
936    String8 name = JStringToString8(env, jname);
937    String8 value;
938
939    status_t err = drm->getPropertyString(name, value);
940
941    if (throwExceptionAsNecessary(env, err, "Failed to get property")) {
942        return NULL;
943    }
944
945    return env->NewStringUTF(value.string());
946}
947
948static jbyteArray android_media_MediaDrm_getPropertyByteArray(
949    JNIEnv *env, jobject thiz, jstring jname) {
950    sp<IDrm> drm = GetDrm(env, thiz);
951
952    if (drm == NULL) {
953        jniThrowException(env, "java/lang/IllegalStateException",
954                          "MediaDrm obj is null");
955        return NULL;
956    }
957
958    if (jname == NULL) {
959        jniThrowException(env, "java/lang/IllegalArgumentException",
960                          "property name String is null");
961        return NULL;
962    }
963
964    String8 name = JStringToString8(env, jname);
965    Vector<uint8_t> value;
966
967    status_t err = drm->getPropertyByteArray(name, value);
968
969    if (throwExceptionAsNecessary(env, err, "Failed to get property")) {
970        return NULL;
971    }
972
973    return VectorToJByteArray(env, value);
974}
975
976static void android_media_MediaDrm_setPropertyString(
977    JNIEnv *env, jobject thiz, jstring jname, jstring jvalue) {
978    sp<IDrm> drm = GetDrm(env, thiz);
979
980    if (drm == NULL) {
981        jniThrowException(env, "java/lang/IllegalStateException",
982                          "MediaDrm obj is null");
983        return;
984    }
985
986    if (jname == NULL) {
987        jniThrowException(env, "java/lang/IllegalArgumentException",
988                          "property name String is null");
989        return;
990    }
991
992    if (jvalue == NULL) {
993        jniThrowException(env, "java/lang/IllegalArgumentException",
994                          "property value String is null");
995        return;
996    }
997
998    String8 name = JStringToString8(env, jname);
999    String8 value = JStringToString8(env, jvalue);
1000
1001    status_t err = drm->setPropertyString(name, value);
1002
1003    throwExceptionAsNecessary(env, err, "Failed to set property");
1004}
1005
1006static void android_media_MediaDrm_setPropertyByteArray(
1007    JNIEnv *env, jobject thiz, jstring jname, jbyteArray jvalue) {
1008    sp<IDrm> drm = GetDrm(env, thiz);
1009
1010    if (drm == NULL) {
1011        jniThrowException(env, "java/lang/IllegalStateException",
1012                          "MediaDrm obj is null");
1013        return;
1014    }
1015
1016    if (jname == NULL) {
1017        jniThrowException(env, "java/lang/IllegalArgumentException",
1018                          "property name String is null");
1019        return;
1020    }
1021
1022    if (jvalue == NULL) {
1023        jniThrowException(env, "java/lang/IllegalArgumentException",
1024                          "property value byte array is null");
1025        return;
1026    }
1027
1028    String8 name = JStringToString8(env, jname);
1029    Vector<uint8_t> value = JByteArrayToVector(env, jvalue);
1030
1031    status_t err = drm->setPropertyByteArray(name, value);
1032
1033    throwExceptionAsNecessary(env, err, "Failed to set property");
1034}
1035
1036static void android_media_MediaDrm_setCipherAlgorithmNative(
1037    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1038    jstring jalgorithm) {
1039
1040    sp<IDrm> drm = GetDrm(env, jdrm);
1041
1042    if (!CheckSession(env, drm, jsessionId)) {
1043        return;
1044    }
1045
1046    if (jalgorithm == NULL) {
1047        jniThrowException(env, "java/lang/IllegalArgumentException",
1048                          "algorithm String is null");
1049        return;
1050    }
1051
1052    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1053    String8 algorithm = JStringToString8(env, jalgorithm);
1054
1055    status_t err = drm->setCipherAlgorithm(sessionId, algorithm);
1056
1057    throwExceptionAsNecessary(env, err, "Failed to set cipher algorithm");
1058}
1059
1060static void android_media_MediaDrm_setMacAlgorithmNative(
1061    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1062    jstring jalgorithm) {
1063
1064    sp<IDrm> drm = GetDrm(env, jdrm);
1065
1066    if (!CheckSession(env, drm, jsessionId)) {
1067        return;
1068    }
1069
1070    if (jalgorithm == NULL) {
1071        jniThrowException(env, "java/lang/IllegalArgumentException",
1072                          "algorithm String is null");
1073        return;
1074    }
1075
1076    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1077    String8 algorithm = JStringToString8(env, jalgorithm);
1078
1079    status_t err = drm->setMacAlgorithm(sessionId, algorithm);
1080
1081    throwExceptionAsNecessary(env, err, "Failed to set mac algorithm");
1082}
1083
1084
1085static jbyteArray android_media_MediaDrm_encryptNative(
1086    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1087    jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) {
1088
1089    sp<IDrm> drm = GetDrm(env, jdrm);
1090
1091    if (!CheckSession(env, drm, jsessionId)) {
1092        return NULL;
1093    }
1094
1095    if (jkeyId == NULL || jinput == NULL || jiv == NULL) {
1096        jniThrowException(env, "java/lang/IllegalArgumentException",
1097                          "required argument is null");
1098        return NULL;
1099    }
1100
1101    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1102    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1103    Vector<uint8_t> input(JByteArrayToVector(env, jinput));
1104    Vector<uint8_t> iv(JByteArrayToVector(env, jiv));
1105    Vector<uint8_t> output;
1106
1107    status_t err = drm->encrypt(sessionId, keyId, input, iv, output);
1108
1109    if (throwExceptionAsNecessary(env, err, "Failed to encrypt")) {
1110        return NULL;
1111    }
1112
1113    return VectorToJByteArray(env, output);
1114}
1115
1116static jbyteArray android_media_MediaDrm_decryptNative(
1117    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1118    jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) {
1119
1120    sp<IDrm> drm = GetDrm(env, jdrm);
1121
1122    if (!CheckSession(env, drm, jsessionId)) {
1123        return NULL;
1124    }
1125
1126    if (jkeyId == NULL || jinput == NULL || jiv == NULL) {
1127        jniThrowException(env, "java/lang/IllegalArgumentException",
1128                          "required argument is null");
1129        return NULL;
1130    }
1131
1132    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1133    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1134    Vector<uint8_t> input(JByteArrayToVector(env, jinput));
1135    Vector<uint8_t> iv(JByteArrayToVector(env, jiv));
1136    Vector<uint8_t> output;
1137
1138    status_t err = drm->decrypt(sessionId, keyId, input, iv, output);
1139    if (throwExceptionAsNecessary(env, err, "Failed to decrypt")) {
1140        return NULL;
1141    }
1142
1143    return VectorToJByteArray(env, output);
1144}
1145
1146static jbyteArray android_media_MediaDrm_signNative(
1147    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1148    jbyteArray jkeyId, jbyteArray jmessage) {
1149
1150    sp<IDrm> drm = GetDrm(env, jdrm);
1151
1152    if (!CheckSession(env, drm, jsessionId)) {
1153        return NULL;
1154    }
1155
1156    if (jkeyId == NULL || jmessage == NULL) {
1157        jniThrowException(env, "java/lang/IllegalArgumentException",
1158                          "required argument is null");
1159        return NULL;
1160    }
1161
1162    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1163    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1164    Vector<uint8_t> message(JByteArrayToVector(env, jmessage));
1165    Vector<uint8_t> signature;
1166
1167    status_t err = drm->sign(sessionId, keyId, message, signature);
1168
1169    if (throwExceptionAsNecessary(env, err, "Failed to sign")) {
1170        return NULL;
1171    }
1172
1173    return VectorToJByteArray(env, signature);
1174}
1175
1176static jboolean android_media_MediaDrm_verifyNative(
1177    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1178    jbyteArray jkeyId, jbyteArray jmessage, jbyteArray jsignature) {
1179
1180    sp<IDrm> drm = GetDrm(env, jdrm);
1181
1182    if (!CheckSession(env, drm, jsessionId)) {
1183        return false;
1184    }
1185
1186    if (jkeyId == NULL || jmessage == NULL || jsignature == NULL) {
1187        jniThrowException(env, "java/lang/IllegalArgumentException",
1188                          "required argument is null");
1189        return false;
1190    }
1191
1192    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1193    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1194    Vector<uint8_t> message(JByteArrayToVector(env, jmessage));
1195    Vector<uint8_t> signature(JByteArrayToVector(env, jsignature));
1196    bool match;
1197
1198    status_t err = drm->verify(sessionId, keyId, message, signature, match);
1199
1200    throwExceptionAsNecessary(env, err, "Failed to verify");
1201    return match;
1202}
1203
1204
1205static JNINativeMethod gMethods[] = {
1206    { "release", "()V", (void *)android_media_MediaDrm_release },
1207    { "native_init", "()V", (void *)android_media_MediaDrm_native_init },
1208
1209    { "native_setup", "(Ljava/lang/Object;[B)V",
1210      (void *)android_media_MediaDrm_native_setup },
1211
1212    { "native_finalize", "()V",
1213      (void *)android_media_MediaDrm_native_finalize },
1214
1215    { "isCryptoSchemeSupportedNative", "([B)Z",
1216      (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative },
1217
1218    { "openSession", "()[B",
1219      (void *)android_media_MediaDrm_openSession },
1220
1221    { "closeSession", "([B)V",
1222      (void *)android_media_MediaDrm_closeSession },
1223
1224    { "getKeyRequest", "([B[BLjava/lang/String;ILjava/util/HashMap;)"
1225      "Landroid/media/MediaDrm$KeyRequest;",
1226      (void *)android_media_MediaDrm_getKeyRequest },
1227
1228    { "provideKeyResponse", "([B[B)[B",
1229      (void *)android_media_MediaDrm_provideKeyResponse },
1230
1231    { "removeKeys", "([B)V",
1232      (void *)android_media_MediaDrm_removeKeys },
1233
1234    { "restoreKeys", "([B[B)V",
1235      (void *)android_media_MediaDrm_restoreKeys },
1236
1237    { "queryKeyStatus", "([B)Ljava/util/HashMap;",
1238      (void *)android_media_MediaDrm_queryKeyStatus },
1239
1240    { "getProvisionRequest", "()Landroid/media/MediaDrm$ProvisionRequest;",
1241      (void *)android_media_MediaDrm_getProvisionRequest },
1242
1243    { "provideProvisionResponse", "([B)V",
1244      (void *)android_media_MediaDrm_provideProvisionResponse },
1245
1246    { "getSecureStops", "()Ljava/util/List;",
1247      (void *)android_media_MediaDrm_getSecureStops },
1248
1249    { "releaseSecureStops", "([B)V",
1250      (void *)android_media_MediaDrm_releaseSecureStops },
1251
1252    { "getPropertyString", "(Ljava/lang/String;)Ljava/lang/String;",
1253      (void *)android_media_MediaDrm_getPropertyString },
1254
1255    { "getPropertyByteArray", "(Ljava/lang/String;)[B",
1256      (void *)android_media_MediaDrm_getPropertyByteArray },
1257
1258    { "setPropertyString", "(Ljava/lang/String;Ljava/lang/String;)V",
1259      (void *)android_media_MediaDrm_setPropertyString },
1260
1261    { "setPropertyByteArray", "(Ljava/lang/String;[B)V",
1262      (void *)android_media_MediaDrm_setPropertyByteArray },
1263
1264    { "setCipherAlgorithmNative",
1265      "(Landroid/media/MediaDrm;[BLjava/lang/String;)V",
1266      (void *)android_media_MediaDrm_setCipherAlgorithmNative },
1267
1268    { "setMacAlgorithmNative",
1269      "(Landroid/media/MediaDrm;[BLjava/lang/String;)V",
1270      (void *)android_media_MediaDrm_setMacAlgorithmNative },
1271
1272    { "encryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B",
1273      (void *)android_media_MediaDrm_encryptNative },
1274
1275    { "decryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B",
1276      (void *)android_media_MediaDrm_decryptNative },
1277
1278    { "signNative", "(Landroid/media/MediaDrm;[B[B[B)[B",
1279      (void *)android_media_MediaDrm_signNative },
1280
1281    { "verifyNative", "(Landroid/media/MediaDrm;[B[B[B[B)Z",
1282      (void *)android_media_MediaDrm_verifyNative },
1283};
1284
1285int register_android_media_Drm(JNIEnv *env) {
1286    return AndroidRuntime::registerNativeMethods(env,
1287                "android/media/MediaDrm", gMethods, NELEM(gMethods));
1288}
1289
1290