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