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