android_media_MediaDrm.cpp revision 3ed38266c1647c6219ae5ad89cb3f867cf66caaa
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    throwExceptionAsNecessary(env, err, "Failed to handle key response");
757    return VectorToJByteArray(env, keySetId);
758}
759
760static void android_media_MediaDrm_removeKeys(
761    JNIEnv *env, jobject thiz, jbyteArray jkeysetId) {
762    sp<IDrm> drm = GetDrm(env, thiz);
763
764    if (jkeysetId == NULL) {
765        jniThrowException(env, "java/lang/IllegalArgumentException",
766                          "keySetId is null");
767        return;
768    }
769
770    Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId));
771
772    status_t err = drm->removeKeys(keySetId);
773
774    throwExceptionAsNecessary(env, err, "Failed to remove keys");
775}
776
777static void android_media_MediaDrm_restoreKeys(
778    JNIEnv *env, jobject thiz, jbyteArray jsessionId,
779    jbyteArray jkeysetId) {
780
781    sp<IDrm> drm = GetDrm(env, thiz);
782
783    if (!CheckSession(env, drm, jsessionId)) {
784        return;
785    }
786
787    if (jkeysetId == NULL) {
788        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
789        return;
790    }
791
792    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
793    Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId));
794
795    status_t err = drm->restoreKeys(sessionId, keySetId);
796
797    throwExceptionAsNecessary(env, err, "Failed to restore keys");
798}
799
800static jobject android_media_MediaDrm_queryKeyStatus(
801    JNIEnv *env, jobject thiz, jbyteArray jsessionId) {
802    sp<IDrm> drm = GetDrm(env, thiz);
803
804    if (!CheckSession(env, drm, jsessionId)) {
805        return NULL;
806    }
807    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
808
809    KeyedVector<String8, String8> infoMap;
810
811    status_t err = drm->queryKeyStatus(sessionId, infoMap);
812
813    if (throwExceptionAsNecessary(env, err, "Failed to query key status")) {
814        return NULL;
815    }
816
817    return KeyedVectorToHashMap(env, infoMap);
818}
819
820static jobject android_media_MediaDrm_getProvisionRequest(
821    JNIEnv *env, jobject thiz) {
822    sp<IDrm> drm = GetDrm(env, thiz);
823
824    if (drm == NULL) {
825        jniThrowException(env, "java/lang/IllegalStateException",
826                          "MediaDrm obj is null");
827        return NULL;
828    }
829
830    Vector<uint8_t> request;
831    String8 defaultUrl;
832
833    status_t err = drm->getProvisionRequest(request, defaultUrl);
834
835    if (throwExceptionAsNecessary(env, err, "Failed to get provision request")) {
836        return NULL;
837    }
838
839    // Fill out return obj
840    jclass clazz;
841    FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest");
842
843    jobject provisionObj = NULL;
844
845    if (clazz) {
846        provisionObj = env->AllocObject(clazz);
847        jbyteArray jrequest = VectorToJByteArray(env, request);
848        env->SetObjectField(provisionObj, gFields.provisionRequest.data, jrequest);
849
850        jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string());
851        env->SetObjectField(provisionObj, gFields.provisionRequest.defaultUrl, jdefaultUrl);
852    }
853
854    return provisionObj;
855}
856
857static void android_media_MediaDrm_provideProvisionResponse(
858    JNIEnv *env, jobject thiz, jbyteArray jresponse) {
859    sp<IDrm> drm = GetDrm(env, thiz);
860
861    if (drm == NULL) {
862        jniThrowException(env, "java/lang/IllegalStateException",
863                          "MediaDrm obj is null");
864        return;
865    }
866
867    if (jresponse == NULL) {
868        jniThrowException(env, "java/lang/IllegalArgumentException",
869                          "provision response is null");
870        return;
871    }
872
873    Vector<uint8_t> response(JByteArrayToVector(env, jresponse));
874
875    status_t err = drm->provideProvisionResponse(response);
876
877    throwExceptionAsNecessary(env, err, "Failed to handle provision response");
878}
879
880static jobject android_media_MediaDrm_getSecureStops(
881    JNIEnv *env, jobject thiz) {
882    sp<IDrm> drm = GetDrm(env, thiz);
883
884    if (drm == NULL) {
885        jniThrowException(env, "java/lang/IllegalStateException",
886                          "MediaDrm obj is null");
887        return NULL;
888    }
889
890    List<Vector<uint8_t> > secureStops;
891
892    status_t err = drm->getSecureStops(secureStops);
893
894    if (throwExceptionAsNecessary(env, err, "Failed to get secure stops")) {
895        return NULL;
896    }
897
898    return ListOfVectorsToArrayListOfByteArray(env, secureStops);
899}
900
901static void android_media_MediaDrm_releaseSecureStops(
902    JNIEnv *env, jobject thiz, jbyteArray jssRelease) {
903    sp<IDrm> drm = GetDrm(env, thiz);
904
905    if (drm == NULL) {
906        jniThrowException(env, "java/lang/IllegalStateException",
907                          "MediaDrm obj is null");
908        return;
909    }
910
911    Vector<uint8_t> ssRelease(JByteArrayToVector(env, jssRelease));
912
913    status_t err = drm->releaseSecureStops(ssRelease);
914
915    throwExceptionAsNecessary(env, err, "Failed to release secure stops");
916}
917
918static jstring android_media_MediaDrm_getPropertyString(
919    JNIEnv *env, jobject thiz, jstring jname) {
920    sp<IDrm> drm = GetDrm(env, thiz);
921
922    if (drm == NULL) {
923        jniThrowException(env, "java/lang/IllegalStateException",
924                          "MediaDrm obj is null");
925        return NULL;
926    }
927
928    if (jname == NULL) {
929        jniThrowException(env, "java/lang/IllegalArgumentException",
930                          "property name String is null");
931        return NULL;
932    }
933
934    String8 name = JStringToString8(env, jname);
935    String8 value;
936
937    status_t err = drm->getPropertyString(name, value);
938
939    if (throwExceptionAsNecessary(env, err, "Failed to get property")) {
940        return NULL;
941    }
942
943    return env->NewStringUTF(value.string());
944}
945
946static jbyteArray android_media_MediaDrm_getPropertyByteArray(
947    JNIEnv *env, jobject thiz, jstring jname) {
948    sp<IDrm> drm = GetDrm(env, thiz);
949
950    if (drm == NULL) {
951        jniThrowException(env, "java/lang/IllegalStateException",
952                          "MediaDrm obj is null");
953        return NULL;
954    }
955
956    if (jname == NULL) {
957        jniThrowException(env, "java/lang/IllegalArgumentException",
958                          "property name String is null");
959        return NULL;
960    }
961
962    String8 name = JStringToString8(env, jname);
963    Vector<uint8_t> value;
964
965    status_t err = drm->getPropertyByteArray(name, value);
966
967    if (throwExceptionAsNecessary(env, err, "Failed to get property")) {
968        return NULL;
969    }
970
971    return VectorToJByteArray(env, value);
972}
973
974static void android_media_MediaDrm_setPropertyString(
975    JNIEnv *env, jobject thiz, jstring jname, jstring jvalue) {
976    sp<IDrm> drm = GetDrm(env, thiz);
977
978    if (drm == NULL) {
979        jniThrowException(env, "java/lang/IllegalStateException",
980                          "MediaDrm obj is null");
981        return;
982    }
983
984    if (jname == NULL) {
985        jniThrowException(env, "java/lang/IllegalArgumentException",
986                          "property name String is null");
987        return;
988    }
989
990    if (jvalue == NULL) {
991        jniThrowException(env, "java/lang/IllegalArgumentException",
992                          "property value String is null");
993        return;
994    }
995
996    String8 name = JStringToString8(env, jname);
997    String8 value = JStringToString8(env, jvalue);
998
999    status_t err = drm->setPropertyString(name, value);
1000
1001    throwExceptionAsNecessary(env, err, "Failed to set property");
1002}
1003
1004static void android_media_MediaDrm_setPropertyByteArray(
1005    JNIEnv *env, jobject thiz, jstring jname, jbyteArray jvalue) {
1006    sp<IDrm> drm = GetDrm(env, thiz);
1007
1008    if (drm == NULL) {
1009        jniThrowException(env, "java/lang/IllegalStateException",
1010                          "MediaDrm obj is null");
1011        return;
1012    }
1013
1014    if (jname == NULL) {
1015        jniThrowException(env, "java/lang/IllegalArgumentException",
1016                          "property name String is null");
1017        return;
1018    }
1019
1020    if (jvalue == NULL) {
1021        jniThrowException(env, "java/lang/IllegalArgumentException",
1022                          "property value byte array is null");
1023        return;
1024    }
1025
1026    String8 name = JStringToString8(env, jname);
1027    Vector<uint8_t> value = JByteArrayToVector(env, jvalue);
1028
1029    status_t err = drm->setPropertyByteArray(name, value);
1030
1031    throwExceptionAsNecessary(env, err, "Failed to set property");
1032}
1033
1034static void android_media_MediaDrm_setCipherAlgorithmNative(
1035    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1036    jstring jalgorithm) {
1037
1038    sp<IDrm> drm = GetDrm(env, jdrm);
1039
1040    if (!CheckSession(env, drm, jsessionId)) {
1041        return;
1042    }
1043
1044    if (jalgorithm == NULL) {
1045        jniThrowException(env, "java/lang/IllegalArgumentException",
1046                          "algorithm String is null");
1047        return;
1048    }
1049
1050    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1051    String8 algorithm = JStringToString8(env, jalgorithm);
1052
1053    status_t err = drm->setCipherAlgorithm(sessionId, algorithm);
1054
1055    throwExceptionAsNecessary(env, err, "Failed to set cipher algorithm");
1056}
1057
1058static void android_media_MediaDrm_setMacAlgorithmNative(
1059    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1060    jstring jalgorithm) {
1061
1062    sp<IDrm> drm = GetDrm(env, jdrm);
1063
1064    if (!CheckSession(env, drm, jsessionId)) {
1065        return;
1066    }
1067
1068    if (jalgorithm == NULL) {
1069        jniThrowException(env, "java/lang/IllegalArgumentException",
1070                          "algorithm String is null");
1071        return;
1072    }
1073
1074    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1075    String8 algorithm = JStringToString8(env, jalgorithm);
1076
1077    status_t err = drm->setMacAlgorithm(sessionId, algorithm);
1078
1079    throwExceptionAsNecessary(env, err, "Failed to set mac algorithm");
1080}
1081
1082
1083static jbyteArray android_media_MediaDrm_encryptNative(
1084    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1085    jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) {
1086
1087    sp<IDrm> drm = GetDrm(env, jdrm);
1088
1089    if (!CheckSession(env, drm, jsessionId)) {
1090        return NULL;
1091    }
1092
1093    if (jkeyId == NULL || jinput == NULL || jiv == NULL) {
1094        jniThrowException(env, "java/lang/IllegalArgumentException",
1095                          "required argument is null");
1096        return NULL;
1097    }
1098
1099    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1100    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1101    Vector<uint8_t> input(JByteArrayToVector(env, jinput));
1102    Vector<uint8_t> iv(JByteArrayToVector(env, jiv));
1103    Vector<uint8_t> output;
1104
1105    status_t err = drm->encrypt(sessionId, keyId, input, iv, output);
1106
1107    throwExceptionAsNecessary(env, err, "Failed to encrypt");
1108
1109    return VectorToJByteArray(env, output);
1110}
1111
1112static jbyteArray android_media_MediaDrm_decryptNative(
1113    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1114    jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) {
1115
1116    sp<IDrm> drm = GetDrm(env, jdrm);
1117
1118    if (!CheckSession(env, drm, jsessionId)) {
1119        return NULL;
1120    }
1121
1122    if (jkeyId == NULL || jinput == NULL || jiv == NULL) {
1123        jniThrowException(env, "java/lang/IllegalArgumentException",
1124                          "required argument is null");
1125        return NULL;
1126    }
1127
1128    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1129    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1130    Vector<uint8_t> input(JByteArrayToVector(env, jinput));
1131    Vector<uint8_t> iv(JByteArrayToVector(env, jiv));
1132    Vector<uint8_t> output;
1133
1134    status_t err = drm->decrypt(sessionId, keyId, input, iv, output);
1135    throwExceptionAsNecessary(env, err, "Failed to decrypt");
1136
1137    return VectorToJByteArray(env, output);
1138}
1139
1140static jbyteArray android_media_MediaDrm_signNative(
1141    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1142    jbyteArray jkeyId, jbyteArray jmessage) {
1143
1144    sp<IDrm> drm = GetDrm(env, jdrm);
1145
1146    if (!CheckSession(env, drm, jsessionId)) {
1147        return NULL;
1148    }
1149
1150    if (jkeyId == NULL || jmessage == NULL) {
1151        jniThrowException(env, "java/lang/IllegalArgumentException",
1152                          "required argument is null");
1153        return NULL;
1154    }
1155
1156    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1157    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1158    Vector<uint8_t> message(JByteArrayToVector(env, jmessage));
1159    Vector<uint8_t> signature;
1160
1161    status_t err = drm->sign(sessionId, keyId, message, signature);
1162
1163    throwExceptionAsNecessary(env, err, "Failed to sign");
1164
1165    return VectorToJByteArray(env, signature);
1166}
1167
1168static jboolean android_media_MediaDrm_verifyNative(
1169    JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId,
1170    jbyteArray jkeyId, jbyteArray jmessage, jbyteArray jsignature) {
1171
1172    sp<IDrm> drm = GetDrm(env, jdrm);
1173
1174    if (!CheckSession(env, drm, jsessionId)) {
1175        return false;
1176    }
1177
1178    if (jkeyId == NULL || jmessage == NULL || jsignature == NULL) {
1179        jniThrowException(env, "java/lang/IllegalArgumentException",
1180                          "required argument is null");
1181        return false;
1182    }
1183
1184    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1185    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1186    Vector<uint8_t> message(JByteArrayToVector(env, jmessage));
1187    Vector<uint8_t> signature(JByteArrayToVector(env, jsignature));
1188    bool match;
1189
1190    status_t err = drm->verify(sessionId, keyId, message, signature, match);
1191
1192    throwExceptionAsNecessary(env, err, "Failed to verify");
1193    return match;
1194}
1195
1196
1197static JNINativeMethod gMethods[] = {
1198    { "release", "()V", (void *)android_media_MediaDrm_release },
1199    { "native_init", "()V", (void *)android_media_MediaDrm_native_init },
1200
1201    { "native_setup", "(Ljava/lang/Object;[B)V",
1202      (void *)android_media_MediaDrm_native_setup },
1203
1204    { "native_finalize", "()V",
1205      (void *)android_media_MediaDrm_native_finalize },
1206
1207    { "isCryptoSchemeSupportedNative", "([B)Z",
1208      (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative },
1209
1210    { "openSession", "()[B",
1211      (void *)android_media_MediaDrm_openSession },
1212
1213    { "closeSession", "([B)V",
1214      (void *)android_media_MediaDrm_closeSession },
1215
1216    { "getKeyRequest", "([B[BLjava/lang/String;ILjava/util/HashMap;)"
1217      "Landroid/media/MediaDrm$KeyRequest;",
1218      (void *)android_media_MediaDrm_getKeyRequest },
1219
1220    { "provideKeyResponse", "([B[B)[B",
1221      (void *)android_media_MediaDrm_provideKeyResponse },
1222
1223    { "removeKeys", "([B)V",
1224      (void *)android_media_MediaDrm_removeKeys },
1225
1226    { "restoreKeys", "([B[B)V",
1227      (void *)android_media_MediaDrm_restoreKeys },
1228
1229    { "queryKeyStatus", "([B)Ljava/util/HashMap;",
1230      (void *)android_media_MediaDrm_queryKeyStatus },
1231
1232    { "getProvisionRequest", "()Landroid/media/MediaDrm$ProvisionRequest;",
1233      (void *)android_media_MediaDrm_getProvisionRequest },
1234
1235    { "provideProvisionResponse", "([B)V",
1236      (void *)android_media_MediaDrm_provideProvisionResponse },
1237
1238    { "getSecureStops", "()Ljava/util/List;",
1239      (void *)android_media_MediaDrm_getSecureStops },
1240
1241    { "releaseSecureStops", "([B)V",
1242      (void *)android_media_MediaDrm_releaseSecureStops },
1243
1244    { "getPropertyString", "(Ljava/lang/String;)Ljava/lang/String;",
1245      (void *)android_media_MediaDrm_getPropertyString },
1246
1247    { "getPropertyByteArray", "(Ljava/lang/String;)[B",
1248      (void *)android_media_MediaDrm_getPropertyByteArray },
1249
1250    { "setPropertyString", "(Ljava/lang/String;Ljava/lang/String;)V",
1251      (void *)android_media_MediaDrm_setPropertyString },
1252
1253    { "setPropertyByteArray", "(Ljava/lang/String;[B)V",
1254      (void *)android_media_MediaDrm_setPropertyByteArray },
1255
1256    { "setCipherAlgorithmNative",
1257      "(Landroid/media/MediaDrm;[BLjava/lang/String;)V",
1258      (void *)android_media_MediaDrm_setCipherAlgorithmNative },
1259
1260    { "setMacAlgorithmNative",
1261      "(Landroid/media/MediaDrm;[BLjava/lang/String;)V",
1262      (void *)android_media_MediaDrm_setMacAlgorithmNative },
1263
1264    { "encryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B",
1265      (void *)android_media_MediaDrm_encryptNative },
1266
1267    { "decryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B",
1268      (void *)android_media_MediaDrm_decryptNative },
1269
1270    { "signNative", "(Landroid/media/MediaDrm;[B[B[B)[B",
1271      (void *)android_media_MediaDrm_signNative },
1272
1273    { "verifyNative", "(Landroid/media/MediaDrm;[B[B[B[B)Z",
1274      (void *)android_media_MediaDrm_verifyNative },
1275};
1276
1277int register_android_media_Drm(JNIEnv *env) {
1278    return AndroidRuntime::registerNativeMethods(env,
1279                "android/media/MediaDrm", gMethods, NELEM(gMethods));
1280}
1281
1282