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