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