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_runtime/Log.h"
25#include "android_os_Parcel.h"
26#include "jni.h"
27#include "JNIHelp.h"
28
29#include <binder/IServiceManager.h>
30#include <binder/Parcel.h>
31#include <media/IDrm.h>
32#include <media/IMediaPlayerService.h>
33#include <media/stagefright/foundation/ADebug.h>
34#include <media/stagefright/MediaErrors.h>
35
36namespace android {
37
38#define FIND_CLASS(var, className) \
39    var = env->FindClass(className); \
40    LOG_FATAL_IF(! var, "Unable to find class " className);
41
42#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
43    var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
44    LOG_FATAL_IF(! var, "Unable to find field " fieldName);
45
46#define GET_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
47    var = env->GetMethodID(clazz, fieldName, fieldDescriptor); \
48    LOG_FATAL_IF(! var, "Unable to find method " fieldName);
49
50#define GET_STATIC_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
51    var = env->GetStaticFieldID(clazz, fieldName, fieldDescriptor); \
52    LOG_FATAL_IF(! var, "Unable to find field " fieldName);
53
54#define GET_STATIC_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
55    var = env->GetStaticMethodID(clazz, fieldName, fieldDescriptor); \
56    LOG_FATAL_IF(! var, "Unable to find static method " fieldName);
57
58
59struct RequestFields {
60    jfieldID data;
61    jfieldID defaultUrl;
62    jfieldID requestType;
63};
64
65struct ArrayListFields {
66    jmethodID init;
67    jmethodID add;
68};
69
70struct HashmapFields {
71    jmethodID init;
72    jmethodID get;
73    jmethodID put;
74    jmethodID entrySet;
75};
76
77struct SetFields {
78    jmethodID iterator;
79};
80
81struct IteratorFields {
82    jmethodID next;
83    jmethodID hasNext;
84};
85
86struct EntryFields {
87    jmethodID getKey;
88    jmethodID getValue;
89};
90
91struct EventTypes {
92    jint kEventProvisionRequired;
93    jint kEventKeyRequired;
94    jint kEventKeyExpired;
95    jint kEventVendorDefined;
96    jint kEventSessionReclaimed;
97} gEventTypes;
98
99struct EventWhat {
100    jint kWhatDrmEvent;
101    jint kWhatExpirationUpdate;
102    jint kWhatKeyStatusChange;
103} gEventWhat;
104
105struct KeyTypes {
106    jint kKeyTypeStreaming;
107    jint kKeyTypeOffline;
108    jint kKeyTypeRelease;
109} gKeyTypes;
110
111struct KeyRequestTypes {
112    jint kKeyRequestTypeInitial;
113    jint kKeyRequestTypeRenewal;
114    jint kKeyRequestTypeRelease;
115} gKeyRequestTypes;
116
117struct CertificateTypes {
118    jint kCertificateTypeNone;
119    jint kCertificateTypeX509;
120} gCertificateTypes;
121
122struct CertificateFields {
123    jfieldID wrappedPrivateKey;
124    jfieldID certificateData;
125};
126
127struct StateExceptionFields {
128    jmethodID init;
129    jclass classId;
130};
131
132struct fields_t {
133    jfieldID context;
134    jmethodID post_event;
135    RequestFields keyRequest;
136    RequestFields provisionRequest;
137    ArrayListFields arraylist;
138    HashmapFields hashmap;
139    SetFields set;
140    IteratorFields iterator;
141    EntryFields entry;
142    CertificateFields certificate;
143    StateExceptionFields stateException;
144    jclass certificateClassId;
145    jclass hashmapClassId;
146    jclass arraylistClassId;
147    jclass stringClassId;
148};
149
150static fields_t gFields;
151
152// ----------------------------------------------------------------------------
153// ref-counted object for callbacks
154class JNIDrmListener: public DrmListener
155{
156public:
157    JNIDrmListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
158    ~JNIDrmListener();
159    virtual void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj = NULL);
160private:
161    JNIDrmListener();
162    jclass      mClass;     // Reference to MediaDrm class
163    jobject     mObject;    // Weak ref to MediaDrm Java object to call on
164};
165
166JNIDrmListener::JNIDrmListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
167{
168    // Hold onto the MediaDrm class for use in calling the static method
169    // that posts events to the application thread.
170    jclass clazz = env->GetObjectClass(thiz);
171    if (clazz == NULL) {
172        ALOGE("Can't find android/media/MediaDrm");
173        jniThrowException(env, "java/lang/Exception",
174                          "Can't find android/media/MediaDrm");
175        return;
176    }
177    mClass = (jclass)env->NewGlobalRef(clazz);
178
179    // We use a weak reference so the MediaDrm object can be garbage collected.
180    // The reference is only used as a proxy for callbacks.
181    mObject  = env->NewGlobalRef(weak_thiz);
182}
183
184JNIDrmListener::~JNIDrmListener()
185{
186    // remove global references
187    JNIEnv *env = AndroidRuntime::getJNIEnv();
188    env->DeleteGlobalRef(mObject);
189    env->DeleteGlobalRef(mClass);
190}
191
192void JNIDrmListener::notify(DrmPlugin::EventType eventType, int extra,
193                            const Parcel *obj)
194{
195    jint jwhat;
196    jint jeventType = 0;
197
198    // translate DrmPlugin event types into their java equivalents
199    switch (eventType) {
200        case DrmPlugin::kDrmPluginEventProvisionRequired:
201            jwhat = gEventWhat.kWhatDrmEvent;
202            jeventType = gEventTypes.kEventProvisionRequired;
203            break;
204        case DrmPlugin::kDrmPluginEventKeyNeeded:
205            jwhat = gEventWhat.kWhatDrmEvent;
206            jeventType = gEventTypes.kEventKeyRequired;
207            break;
208        case DrmPlugin::kDrmPluginEventKeyExpired:
209            jwhat = gEventWhat.kWhatDrmEvent;
210            jeventType = gEventTypes.kEventKeyExpired;
211            break;
212        case DrmPlugin::kDrmPluginEventVendorDefined:
213            jwhat = gEventWhat.kWhatDrmEvent;
214            jeventType = gEventTypes.kEventVendorDefined;
215            break;
216        case DrmPlugin::kDrmPluginEventSessionReclaimed:
217            jwhat = gEventWhat.kWhatDrmEvent;
218            jeventType = gEventTypes.kEventSessionReclaimed;
219            break;
220        case DrmPlugin::kDrmPluginEventExpirationUpdate:
221            jwhat = gEventWhat.kWhatExpirationUpdate;
222            break;
223         case DrmPlugin::kDrmPluginEventKeysChange:
224            jwhat = gEventWhat.kWhatKeyStatusChange;
225            break;
226        default:
227            ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType);
228            return;
229    }
230
231    JNIEnv *env = AndroidRuntime::getJNIEnv();
232    if (obj && obj->dataSize() > 0) {
233        jobject jParcel = createJavaParcelObject(env);
234        if (jParcel != NULL) {
235            Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
236            nativeParcel->setData(obj->data(), obj->dataSize());
237            env->CallStaticVoidMethod(mClass, gFields.post_event, mObject,
238                    jwhat, jeventType, extra, jParcel);
239            env->DeleteLocalRef(jParcel);
240        }
241    }
242
243    if (env->ExceptionCheck()) {
244        ALOGW("An exception occurred while notifying an event.");
245        LOGW_EX(env);
246        env->ExceptionClear();
247    }
248}
249
250static void throwStateException(JNIEnv *env, const char *msg, status_t err) {
251    ALOGE("Illegal state exception: %s (%d)", msg, err);
252
253    jobject exception = env->NewObject(gFields.stateException.classId,
254            gFields.stateException.init, static_cast<int>(err),
255            env->NewStringUTF(msg));
256    env->Throw(static_cast<jthrowable>(exception));
257}
258
259static bool throwExceptionAsNecessary(
260        JNIEnv *env, status_t err, const char *msg = NULL) {
261
262    const char *drmMessage = NULL;
263
264    switch (err) {
265    case ERROR_DRM_UNKNOWN:
266        drmMessage = "General DRM error";
267        break;
268    case ERROR_DRM_NO_LICENSE:
269        drmMessage = "No license";
270        break;
271    case ERROR_DRM_LICENSE_EXPIRED:
272        drmMessage = "License expired";
273        break;
274    case ERROR_DRM_SESSION_NOT_OPENED:
275        drmMessage = "Session not opened";
276        break;
277    case ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED:
278        drmMessage = "Not initialized";
279        break;
280    case ERROR_DRM_DECRYPT:
281        drmMessage = "Decrypt error";
282        break;
283    case ERROR_DRM_CANNOT_HANDLE:
284        drmMessage = "Unsupported scheme or data format";
285        break;
286    case ERROR_DRM_TAMPER_DETECTED:
287        drmMessage = "Invalid state";
288        break;
289    default:
290        break;
291    }
292
293    String8 vendorMessage;
294    if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
295        vendorMessage = String8::format("DRM vendor-defined error: %d", err);
296        drmMessage = vendorMessage.string();
297    }
298
299    if (err == BAD_VALUE) {
300        jniThrowException(env, "java/lang/IllegalArgumentException", msg);
301        return true;
302    } else if (err == ERROR_DRM_NOT_PROVISIONED) {
303        jniThrowException(env, "android/media/NotProvisionedException", msg);
304        return true;
305    } else if (err == ERROR_DRM_RESOURCE_BUSY) {
306        jniThrowException(env, "android/media/ResourceBusyException", msg);
307        return true;
308    } else if (err == ERROR_DRM_DEVICE_REVOKED) {
309        jniThrowException(env, "android/media/DeniedByServerException", msg);
310        return true;
311    } else if (err == DEAD_OBJECT) {
312        jniThrowException(env, "android/media/MediaDrmResetException",
313                "mediaserver died");
314        return true;
315    } else if (err != OK) {
316        String8 errbuf;
317        if (drmMessage != NULL) {
318            if (msg == NULL) {
319                msg = drmMessage;
320            } else {
321                errbuf = String8::format("%s: %s", msg, drmMessage);
322                msg = errbuf.string();
323            }
324        }
325        throwStateException(env, msg, err);
326        return true;
327    }
328    return false;
329}
330
331static sp<IDrm> GetDrm(JNIEnv *env, jobject thiz) {
332    JDrm *jdrm = (JDrm *)env->GetLongField(thiz, gFields.context);
333    return jdrm ? jdrm->getDrm() : NULL;
334}
335
336JDrm::JDrm(
337        JNIEnv *env, jobject thiz, const uint8_t uuid[16]) {
338    mObject = env->NewWeakGlobalRef(thiz);
339    mDrm = MakeDrm(uuid);
340    if (mDrm != NULL) {
341        mDrm->setListener(this);
342    }
343}
344
345JDrm::~JDrm() {
346    JNIEnv *env = AndroidRuntime::getJNIEnv();
347
348    env->DeleteWeakGlobalRef(mObject);
349    mObject = NULL;
350}
351
352// static
353sp<IDrm> JDrm::MakeDrm() {
354    sp<IServiceManager> sm = defaultServiceManager();
355
356    sp<IBinder> binder =
357        sm->getService(String16("media.player"));
358
359    sp<IMediaPlayerService> service =
360        interface_cast<IMediaPlayerService>(binder);
361
362    if (service == NULL) {
363        return NULL;
364    }
365
366    sp<IDrm> drm = service->makeDrm();
367
368    if (drm == NULL || (drm->initCheck() != OK && drm->initCheck() != NO_INIT)) {
369        return NULL;
370    }
371
372    return drm;
373}
374
375// static
376sp<IDrm> JDrm::MakeDrm(const uint8_t uuid[16]) {
377    sp<IDrm> drm = MakeDrm();
378
379    if (drm == NULL) {
380        return NULL;
381    }
382
383    status_t err = drm->createPlugin(uuid);
384
385    if (err != OK) {
386        return NULL;
387    }
388
389    return drm;
390}
391
392status_t JDrm::setListener(const sp<DrmListener>& listener) {
393    Mutex::Autolock lock(mLock);
394    mListener = listener;
395    return OK;
396}
397
398void JDrm::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) {
399    sp<DrmListener> listener;
400    mLock.lock();
401    listener = mListener;
402    mLock.unlock();
403
404    if (listener != NULL) {
405        Mutex::Autolock lock(mNotifyLock);
406        listener->notify(eventType, extra, obj);
407    }
408}
409
410void JDrm::disconnect() {
411    if (mDrm != NULL) {
412        mDrm->destroyPlugin();
413        mDrm.clear();
414    }
415}
416
417
418// static
419bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) {
420    sp<IDrm> drm = MakeDrm();
421
422    if (drm == NULL) {
423        return false;
424    }
425
426    return drm->isCryptoSchemeSupported(uuid, mimeType);
427}
428
429status_t JDrm::initCheck() const {
430    return mDrm == NULL ? NO_INIT : OK;
431}
432
433// JNI conversion utilities
434static Vector<uint8_t> JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArray) {
435    Vector<uint8_t> vector;
436    size_t length = env->GetArrayLength(byteArray);
437    vector.insertAt((size_t)0, length);
438    env->GetByteArrayRegion(byteArray, 0, length, (jbyte *)vector.editArray());
439    return vector;
440}
441
442static jbyteArray VectorToJByteArray(JNIEnv *env, Vector<uint8_t> const &vector) {
443    size_t length = vector.size();
444    jbyteArray result = env->NewByteArray(length);
445    if (result != NULL) {
446        env->SetByteArrayRegion(result, 0, length, (jbyte *)vector.array());
447    }
448    return result;
449}
450
451static String8 JStringToString8(JNIEnv *env, jstring const &jstr) {
452    String8 result;
453
454    const char *s = env->GetStringUTFChars(jstr, NULL);
455    if (s) {
456        result = s;
457        env->ReleaseStringUTFChars(jstr, s);
458    }
459    return result;
460}
461
462/*
463    import java.util.HashMap;
464    import java.util.Set;
465    import java.Map.Entry;
466    import jav.util.Iterator;
467
468    HashMap<k, v> hm;
469    Set<Entry<k, v> > s = hm.entrySet();
470    Iterator i = s.iterator();
471    Entry e = s.next();
472*/
473
474static KeyedVector<String8, String8> HashMapToKeyedVector(
475    JNIEnv *env, jobject &hashMap, bool* pIsOK) {
476    jclass clazz = gFields.stringClassId;
477    KeyedVector<String8, String8> keyedVector;
478    *pIsOK = true;
479
480    jobject entrySet = env->CallObjectMethod(hashMap, gFields.hashmap.entrySet);
481    if (entrySet) {
482        jobject iterator = env->CallObjectMethod(entrySet, gFields.set.iterator);
483        if (iterator) {
484            jboolean hasNext = env->CallBooleanMethod(iterator, gFields.iterator.hasNext);
485            while (hasNext) {
486                jobject entry = env->CallObjectMethod(iterator, gFields.iterator.next);
487                if (entry) {
488                    jobject obj = env->CallObjectMethod(entry, gFields.entry.getKey);
489                    if (obj == NULL || !env->IsInstanceOf(obj, clazz)) {
490                        jniThrowException(env, "java/lang/IllegalArgumentException",
491                                          "HashMap key is not a String");
492                        env->DeleteLocalRef(entry);
493                        *pIsOK = false;
494                        break;
495                    }
496                    jstring jkey = static_cast<jstring>(obj);
497
498                    obj = env->CallObjectMethod(entry, gFields.entry.getValue);
499                    if (obj == NULL || !env->IsInstanceOf(obj, clazz)) {
500                        jniThrowException(env, "java/lang/IllegalArgumentException",
501                                          "HashMap value is not a String");
502                        env->DeleteLocalRef(entry);
503                        *pIsOK = false;
504                        break;
505                    }
506                    jstring jvalue = static_cast<jstring>(obj);
507
508                    String8 key = JStringToString8(env, jkey);
509                    String8 value = JStringToString8(env, jvalue);
510                    keyedVector.add(key, value);
511
512                    env->DeleteLocalRef(jkey);
513                    env->DeleteLocalRef(jvalue);
514                    hasNext = env->CallBooleanMethod(iterator, gFields.iterator.hasNext);
515                }
516                env->DeleteLocalRef(entry);
517            }
518            env->DeleteLocalRef(iterator);
519        }
520        env->DeleteLocalRef(entrySet);
521    }
522    return keyedVector;
523}
524
525static jobject KeyedVectorToHashMap (JNIEnv *env, KeyedVector<String8, String8> const &map) {
526    jclass clazz = gFields.hashmapClassId;
527    jobject hashMap = env->NewObject(clazz, gFields.hashmap.init);
528    for (size_t i = 0; i < map.size(); ++i) {
529        jstring jkey = env->NewStringUTF(map.keyAt(i).string());
530        jstring jvalue = env->NewStringUTF(map.valueAt(i).string());
531        env->CallObjectMethod(hashMap, gFields.hashmap.put, jkey, jvalue);
532        env->DeleteLocalRef(jkey);
533        env->DeleteLocalRef(jvalue);
534    }
535    return hashMap;
536}
537
538static jobject ListOfVectorsToArrayListOfByteArray(JNIEnv *env,
539                                                   List<Vector<uint8_t> > list) {
540    jclass clazz = gFields.arraylistClassId;
541    jobject arrayList = env->NewObject(clazz, gFields.arraylist.init);
542    List<Vector<uint8_t> >::iterator iter = list.begin();
543    while (iter != list.end()) {
544        jbyteArray byteArray = VectorToJByteArray(env, *iter);
545        env->CallBooleanMethod(arrayList, gFields.arraylist.add, byteArray);
546        env->DeleteLocalRef(byteArray);
547        iter++;
548    }
549
550    return arrayList;
551}
552
553}  // namespace android
554
555using namespace android;
556
557static sp<JDrm> setDrm(
558        JNIEnv *env, jobject thiz, const sp<JDrm> &drm) {
559    sp<JDrm> old = (JDrm *)env->GetLongField(thiz, gFields.context);
560    if (drm != NULL) {
561        drm->incStrong(thiz);
562    }
563    if (old != NULL) {
564        old->decStrong(thiz);
565    }
566    env->SetLongField(thiz, gFields.context, reinterpret_cast<jlong>(drm.get()));
567
568    return old;
569}
570
571static bool CheckSession(JNIEnv *env, const sp<IDrm> &drm, jbyteArray const &jsessionId)
572{
573    if (drm == NULL) {
574        jniThrowException(env, "java/lang/IllegalStateException", "MediaDrm obj is null");
575        return false;
576    }
577
578    if (jsessionId == NULL) {
579        jniThrowException(env, "java/lang/IllegalArgumentException", "sessionId is null");
580        return false;
581    }
582    return true;
583}
584
585static void android_media_MediaDrm_release(JNIEnv *env, jobject thiz) {
586    sp<JDrm> drm = setDrm(env, thiz, NULL);
587    if (drm != NULL) {
588        drm->setListener(NULL);
589        drm->disconnect();
590    }
591}
592
593static void android_media_MediaDrm_native_init(JNIEnv *env) {
594    jclass clazz;
595    FIND_CLASS(clazz, "android/media/MediaDrm");
596    GET_FIELD_ID(gFields.context, clazz, "mNativeContext", "J");
597    GET_STATIC_METHOD_ID(gFields.post_event, clazz, "postEventFromNative",
598                         "(Ljava/lang/Object;IIILjava/lang/Object;)V");
599
600    jfieldID field;
601    GET_STATIC_FIELD_ID(field, clazz, "EVENT_PROVISION_REQUIRED", "I");
602    gEventTypes.kEventProvisionRequired = env->GetStaticIntField(clazz, field);
603    GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_REQUIRED", "I");
604    gEventTypes.kEventKeyRequired = env->GetStaticIntField(clazz, field);
605    GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_EXPIRED", "I");
606    gEventTypes.kEventKeyExpired = env->GetStaticIntField(clazz, field);
607    GET_STATIC_FIELD_ID(field, clazz, "EVENT_VENDOR_DEFINED", "I");
608    gEventTypes.kEventVendorDefined = env->GetStaticIntField(clazz, field);
609    GET_STATIC_FIELD_ID(field, clazz, "EVENT_SESSION_RECLAIMED", "I");
610    gEventTypes.kEventSessionReclaimed = env->GetStaticIntField(clazz, field);
611
612    GET_STATIC_FIELD_ID(field, clazz, "DRM_EVENT", "I");
613    gEventWhat.kWhatDrmEvent = env->GetStaticIntField(clazz, field);
614    GET_STATIC_FIELD_ID(field, clazz, "EXPIRATION_UPDATE", "I");
615    gEventWhat.kWhatExpirationUpdate = env->GetStaticIntField(clazz, field);
616    GET_STATIC_FIELD_ID(field, clazz, "KEY_STATUS_CHANGE", "I");
617    gEventWhat.kWhatKeyStatusChange = env->GetStaticIntField(clazz, field);
618
619    GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_STREAMING", "I");
620    gKeyTypes.kKeyTypeStreaming = env->GetStaticIntField(clazz, field);
621    GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_OFFLINE", "I");
622    gKeyTypes.kKeyTypeOffline = env->GetStaticIntField(clazz, field);
623    GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_RELEASE", "I");
624    gKeyTypes.kKeyTypeRelease = env->GetStaticIntField(clazz, field);
625
626    GET_STATIC_FIELD_ID(field, clazz, "CERTIFICATE_TYPE_NONE", "I");
627    gCertificateTypes.kCertificateTypeNone = env->GetStaticIntField(clazz, field);
628    GET_STATIC_FIELD_ID(field, clazz, "CERTIFICATE_TYPE_X509", "I");
629    gCertificateTypes.kCertificateTypeX509 = env->GetStaticIntField(clazz, field);
630
631    FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
632    GET_FIELD_ID(gFields.keyRequest.data, clazz, "mData", "[B");
633    GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
634    GET_FIELD_ID(gFields.keyRequest.requestType, clazz, "mRequestType", "I");
635
636    GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_INITIAL", "I");
637    gKeyRequestTypes.kKeyRequestTypeInitial = env->GetStaticIntField(clazz, field);
638    GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RENEWAL", "I");
639    gKeyRequestTypes.kKeyRequestTypeRenewal = env->GetStaticIntField(clazz, field);
640    GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RELEASE", "I");
641    gKeyRequestTypes.kKeyRequestTypeRelease = env->GetStaticIntField(clazz, field);
642
643    FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest");
644    GET_FIELD_ID(gFields.provisionRequest.data, clazz, "mData", "[B");
645    GET_FIELD_ID(gFields.provisionRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
646
647    FIND_CLASS(clazz, "android/media/MediaDrm$Certificate");
648    GET_FIELD_ID(gFields.certificate.wrappedPrivateKey, clazz, "mWrappedKey", "[B");
649    GET_FIELD_ID(gFields.certificate.certificateData, clazz, "mCertificateData", "[B");
650    gFields.certificateClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
651
652    FIND_CLASS(clazz, "java/util/ArrayList");
653    GET_METHOD_ID(gFields.arraylist.init, clazz, "<init>", "()V");
654    GET_METHOD_ID(gFields.arraylist.add, clazz, "add", "(Ljava/lang/Object;)Z");
655
656    FIND_CLASS(clazz, "java/util/HashMap");
657    GET_METHOD_ID(gFields.hashmap.init, clazz, "<init>", "()V");
658    GET_METHOD_ID(gFields.hashmap.get, clazz, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
659    GET_METHOD_ID(gFields.hashmap.put, clazz, "put",
660                  "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
661    GET_METHOD_ID(gFields.hashmap.entrySet, clazz, "entrySet", "()Ljava/util/Set;");
662
663    FIND_CLASS(clazz, "java/util/Set");
664    GET_METHOD_ID(gFields.set.iterator, clazz, "iterator", "()Ljava/util/Iterator;");
665
666    FIND_CLASS(clazz, "java/util/Iterator");
667    GET_METHOD_ID(gFields.iterator.next, clazz, "next", "()Ljava/lang/Object;");
668    GET_METHOD_ID(gFields.iterator.hasNext, clazz, "hasNext", "()Z");
669
670    FIND_CLASS(clazz, "java/util/Map$Entry");
671    GET_METHOD_ID(gFields.entry.getKey, clazz, "getKey", "()Ljava/lang/Object;");
672    GET_METHOD_ID(gFields.entry.getValue, clazz, "getValue", "()Ljava/lang/Object;");
673
674    FIND_CLASS(clazz, "java/util/HashMap");
675    gFields.hashmapClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
676
677    FIND_CLASS(clazz, "java/lang/String");
678    gFields.stringClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
679
680    FIND_CLASS(clazz, "java/util/ArrayList");
681    gFields.arraylistClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
682
683    FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
684    GET_METHOD_ID(gFields.stateException.init, clazz, "<init>", "(ILjava/lang/String;)V");
685    gFields.stateException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
686}
687
688static void android_media_MediaDrm_native_setup(
689        JNIEnv *env, jobject thiz,
690        jobject weak_this, jbyteArray uuidObj) {
691
692    if (uuidObj == NULL) {
693        jniThrowException(env, "java/lang/IllegalArgumentException", "uuid is null");
694        return;
695    }
696
697    Vector<uint8_t> uuid = JByteArrayToVector(env, uuidObj);
698
699    if (uuid.size() != 16) {
700        jniThrowException(env, "java/lang/IllegalArgumentException",
701                          "invalid UUID size, expected 16 bytes");
702        return;
703    }
704
705    sp<JDrm> drm = new JDrm(env, thiz, uuid.array());
706
707    status_t err = drm->initCheck();
708
709    if (err != OK) {
710        jniThrowException(
711                env,
712                "android/media/UnsupportedSchemeException",
713                "Failed to instantiate drm object.");
714        return;
715    }
716
717    sp<JNIDrmListener> listener = new JNIDrmListener(env, thiz, weak_this);
718    drm->setListener(listener);
719    setDrm(env, thiz, drm);
720}
721
722static void android_media_MediaDrm_native_finalize(
723        JNIEnv *env, jobject thiz) {
724    android_media_MediaDrm_release(env, thiz);
725}
726
727static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative(
728    JNIEnv *env, jobject /* thiz */, jbyteArray uuidObj, jstring jmimeType) {
729
730    if (uuidObj == NULL) {
731        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
732        return false;
733    }
734
735    Vector<uint8_t> uuid = JByteArrayToVector(env, uuidObj);
736
737    if (uuid.size() != 16) {
738        jniThrowException(
739                env,
740                "java/lang/IllegalArgumentException",
741                "invalid UUID size, expected 16 bytes");
742        return false;
743    }
744
745    String8 mimeType;
746    if (jmimeType != NULL) {
747        mimeType = JStringToString8(env, jmimeType);
748    }
749
750    return JDrm::IsCryptoSchemeSupported(uuid.array(), mimeType);
751}
752
753static jbyteArray android_media_MediaDrm_openSession(
754    JNIEnv *env, jobject thiz) {
755    sp<IDrm> drm = GetDrm(env, thiz);
756
757    if (drm == NULL) {
758        jniThrowException(env, "java/lang/IllegalStateException",
759                          "MediaDrm obj is null");
760        return NULL;
761    }
762
763    Vector<uint8_t> sessionId;
764    status_t err = drm->openSession(sessionId);
765
766    if (throwExceptionAsNecessary(env, err, "Failed to open session")) {
767        return NULL;
768    }
769
770    return VectorToJByteArray(env, sessionId);
771}
772
773static void android_media_MediaDrm_closeSession(
774    JNIEnv *env, jobject thiz, jbyteArray jsessionId) {
775    sp<IDrm> drm = GetDrm(env, thiz);
776
777    if (!CheckSession(env, drm, jsessionId)) {
778        return;
779    }
780
781    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
782
783    status_t err = drm->closeSession(sessionId);
784
785    throwExceptionAsNecessary(env, err, "Failed to close session");
786}
787
788static jobject android_media_MediaDrm_getKeyRequest(
789    JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jinitData,
790    jstring jmimeType, jint jkeyType, jobject joptParams) {
791    sp<IDrm> drm = GetDrm(env, thiz);
792
793    if (!CheckSession(env, drm, jsessionId)) {
794        return NULL;
795    }
796
797    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
798
799    Vector<uint8_t> initData;
800    if (jinitData != NULL) {
801        initData = JByteArrayToVector(env, jinitData);
802    }
803
804    String8 mimeType;
805    if (jmimeType != NULL) {
806        mimeType = JStringToString8(env, jmimeType);
807    }
808
809    DrmPlugin::KeyType keyType;
810    if (jkeyType == gKeyTypes.kKeyTypeStreaming) {
811        keyType = DrmPlugin::kKeyType_Streaming;
812    } else if (jkeyType == gKeyTypes.kKeyTypeOffline) {
813        keyType = DrmPlugin::kKeyType_Offline;
814    } else if (jkeyType == gKeyTypes.kKeyTypeRelease) {
815        keyType = DrmPlugin::kKeyType_Release;
816    } else {
817        jniThrowException(env, "java/lang/IllegalArgumentException",
818                          "invalid keyType");
819        return NULL;
820    }
821
822    KeyedVector<String8, String8> optParams;
823    if (joptParams != NULL) {
824        bool isOK;
825        optParams = HashMapToKeyedVector(env, joptParams, &isOK);
826        if (!isOK) {
827            return NULL;
828        }
829    }
830
831    Vector<uint8_t> request;
832    String8 defaultUrl;
833    DrmPlugin::KeyRequestType keyRequestType;
834
835    status_t err = drm->getKeyRequest(sessionId, initData, mimeType,
836            keyType, optParams, request, defaultUrl, &keyRequestType);
837
838    if (throwExceptionAsNecessary(env, err, "Failed to get key request")) {
839        return NULL;
840    }
841
842    // Fill out return obj
843    jclass clazz;
844    FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
845
846    jobject keyObj = NULL;
847
848    if (clazz) {
849        keyObj = env->AllocObject(clazz);
850        jbyteArray jrequest = VectorToJByteArray(env, request);
851        env->SetObjectField(keyObj, gFields.keyRequest.data, jrequest);
852
853        jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string());
854        env->SetObjectField(keyObj, gFields.keyRequest.defaultUrl, jdefaultUrl);
855
856        switch (keyRequestType) {
857            case DrmPlugin::kKeyRequestType_Initial:
858                env->SetIntField(keyObj, gFields.keyRequest.requestType,
859                        gKeyRequestTypes.kKeyRequestTypeInitial);
860                break;
861            case DrmPlugin::kKeyRequestType_Renewal:
862                env->SetIntField(keyObj, gFields.keyRequest.requestType,
863                        gKeyRequestTypes.kKeyRequestTypeRenewal);
864                break;
865            case DrmPlugin::kKeyRequestType_Release:
866                env->SetIntField(keyObj, gFields.keyRequest.requestType,
867                        gKeyRequestTypes.kKeyRequestTypeRelease);
868                break;
869            default:
870                throwStateException(env, "DRM plugin failure: unknown key request type",
871                        ERROR_DRM_UNKNOWN);
872                break;
873        }
874    }
875
876    return keyObj;
877}
878
879static jbyteArray android_media_MediaDrm_provideKeyResponse(
880    JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jresponse) {
881    sp<IDrm> drm = GetDrm(env, thiz);
882
883    if (!CheckSession(env, drm, jsessionId)) {
884        return NULL;
885    }
886
887    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
888
889    if (jresponse == NULL) {
890        jniThrowException(env, "java/lang/IllegalArgumentException",
891                          "key response is null");
892        return NULL;
893    }
894    Vector<uint8_t> response(JByteArrayToVector(env, jresponse));
895    Vector<uint8_t> keySetId;
896
897    status_t err = drm->provideKeyResponse(sessionId, response, keySetId);
898
899    if (throwExceptionAsNecessary(env, err, "Failed to handle key response")) {
900        return NULL;
901    }
902    return VectorToJByteArray(env, keySetId);
903}
904
905static void android_media_MediaDrm_removeKeys(
906    JNIEnv *env, jobject thiz, jbyteArray jkeysetId) {
907    sp<IDrm> drm = GetDrm(env, thiz);
908
909    if (jkeysetId == NULL) {
910        jniThrowException(env, "java/lang/IllegalArgumentException",
911                          "keySetId is null");
912        return;
913    }
914
915    Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId));
916
917    status_t err = drm->removeKeys(keySetId);
918
919    throwExceptionAsNecessary(env, err, "Failed to remove keys");
920}
921
922static void android_media_MediaDrm_restoreKeys(
923    JNIEnv *env, jobject thiz, jbyteArray jsessionId,
924    jbyteArray jkeysetId) {
925
926    sp<IDrm> drm = GetDrm(env, thiz);
927
928    if (!CheckSession(env, drm, jsessionId)) {
929        return;
930    }
931
932    if (jkeysetId == NULL) {
933        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
934        return;
935    }
936
937    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
938    Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId));
939
940    status_t err = drm->restoreKeys(sessionId, keySetId);
941
942    throwExceptionAsNecessary(env, err, "Failed to restore keys");
943}
944
945static jobject android_media_MediaDrm_queryKeyStatus(
946    JNIEnv *env, jobject thiz, jbyteArray jsessionId) {
947    sp<IDrm> drm = GetDrm(env, thiz);
948
949    if (!CheckSession(env, drm, jsessionId)) {
950        return NULL;
951    }
952    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
953
954    KeyedVector<String8, String8> infoMap;
955
956    status_t err = drm->queryKeyStatus(sessionId, infoMap);
957
958    if (throwExceptionAsNecessary(env, err, "Failed to query key status")) {
959        return NULL;
960    }
961
962    return KeyedVectorToHashMap(env, infoMap);
963}
964
965static jobject android_media_MediaDrm_getProvisionRequestNative(
966    JNIEnv *env, jobject thiz, jint jcertType, jstring jcertAuthority) {
967    sp<IDrm> drm = GetDrm(env, thiz);
968
969    if (drm == NULL) {
970        jniThrowException(env, "java/lang/IllegalStateException",
971                          "MediaDrm obj is null");
972        return NULL;
973    }
974
975    Vector<uint8_t> request;
976    String8 defaultUrl;
977
978    String8 certType;
979    if (jcertType == gCertificateTypes.kCertificateTypeX509) {
980        certType = "X.509";
981    } else if (jcertType == gCertificateTypes.kCertificateTypeNone) {
982        certType = "none";
983    } else {
984        certType = "invalid";
985    }
986
987    String8 certAuthority = JStringToString8(env, jcertAuthority);
988    status_t err = drm->getProvisionRequest(certType, certAuthority, request, defaultUrl);
989
990    if (throwExceptionAsNecessary(env, err, "Failed to get provision request")) {
991        return NULL;
992    }
993
994    // Fill out return obj
995    jclass clazz;
996    FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest");
997
998    jobject provisionObj = NULL;
999
1000    if (clazz) {
1001        provisionObj = env->AllocObject(clazz);
1002        jbyteArray jrequest = VectorToJByteArray(env, request);
1003        env->SetObjectField(provisionObj, gFields.provisionRequest.data, jrequest);
1004
1005        jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string());
1006        env->SetObjectField(provisionObj, gFields.provisionRequest.defaultUrl, jdefaultUrl);
1007    }
1008
1009    return provisionObj;
1010}
1011
1012static jobject android_media_MediaDrm_provideProvisionResponseNative(
1013    JNIEnv *env, jobject thiz, jbyteArray jresponse) {
1014    sp<IDrm> drm = GetDrm(env, thiz);
1015
1016    if (drm == NULL) {
1017        jniThrowException(env, "java/lang/IllegalStateException",
1018                          "MediaDrm obj is null");
1019        return NULL;
1020    }
1021
1022    if (jresponse == NULL) {
1023        jniThrowException(env, "java/lang/IllegalArgumentException",
1024                          "provision response is null");
1025        return NULL;
1026    }
1027
1028    Vector<uint8_t> response(JByteArrayToVector(env, jresponse));
1029    Vector<uint8_t> certificate, wrappedKey;
1030
1031    status_t err = drm->provideProvisionResponse(response, certificate, wrappedKey);
1032
1033    // Fill out return obj
1034    jclass clazz = gFields.certificateClassId;
1035
1036    jobject certificateObj = NULL;
1037
1038    if (clazz && certificate.size() && wrappedKey.size()) {
1039        certificateObj = env->AllocObject(clazz);
1040        jbyteArray jcertificate = VectorToJByteArray(env, certificate);
1041        env->SetObjectField(certificateObj, gFields.certificate.certificateData, jcertificate);
1042
1043        jbyteArray jwrappedKey = VectorToJByteArray(env, wrappedKey);
1044        env->SetObjectField(certificateObj, gFields.certificate.wrappedPrivateKey, jwrappedKey);
1045    }
1046
1047    throwExceptionAsNecessary(env, err, "Failed to handle provision response");
1048    return certificateObj;
1049}
1050
1051static void android_media_MediaDrm_unprovisionDeviceNative(
1052    JNIEnv *env, jobject thiz) {
1053    sp<IDrm> drm = GetDrm(env, thiz);
1054
1055    if (drm == NULL) {
1056        jniThrowException(env, "java/lang/IllegalStateException",
1057                          "MediaDrm obj is null");
1058        return;
1059    }
1060
1061    status_t err = drm->unprovisionDevice();
1062
1063    throwExceptionAsNecessary(env, err, "Failed to handle provision response");
1064    return;
1065}
1066
1067static jobject android_media_MediaDrm_getSecureStops(
1068    JNIEnv *env, jobject thiz) {
1069    sp<IDrm> drm = GetDrm(env, thiz);
1070
1071    if (drm == NULL) {
1072        jniThrowException(env, "java/lang/IllegalStateException",
1073                          "MediaDrm obj is null");
1074        return NULL;
1075    }
1076
1077    List<Vector<uint8_t> > secureStops;
1078
1079    status_t err = drm->getSecureStops(secureStops);
1080
1081    if (throwExceptionAsNecessary(env, err, "Failed to get secure stops")) {
1082        return NULL;
1083    }
1084
1085    return ListOfVectorsToArrayListOfByteArray(env, secureStops);
1086}
1087
1088static jbyteArray android_media_MediaDrm_getSecureStop(
1089    JNIEnv *env, jobject thiz, jbyteArray ssid) {
1090    sp<IDrm> drm = GetDrm(env, thiz);
1091
1092    if (drm == NULL) {
1093        jniThrowException(env, "java/lang/IllegalStateException",
1094                          "MediaDrm obj is null");
1095        return NULL;
1096    }
1097
1098    Vector<uint8_t> secureStop;
1099
1100    status_t err = drm->getSecureStop(JByteArrayToVector(env, ssid), secureStop);
1101
1102    if (throwExceptionAsNecessary(env, err, "Failed to get secure stop")) {
1103        return NULL;
1104    }
1105
1106    return VectorToJByteArray(env, secureStop);
1107}
1108
1109static void android_media_MediaDrm_releaseSecureStops(
1110    JNIEnv *env, jobject thiz, jbyteArray jssRelease) {
1111    sp<IDrm> drm = GetDrm(env, thiz);
1112
1113    if (drm == NULL) {
1114        jniThrowException(env, "java/lang/IllegalStateException",
1115                          "MediaDrm obj is null");
1116        return;
1117    }
1118
1119    Vector<uint8_t> ssRelease(JByteArrayToVector(env, jssRelease));
1120
1121    status_t err = drm->releaseSecureStops(ssRelease);
1122
1123    throwExceptionAsNecessary(env, err, "Failed to release secure stops");
1124}
1125
1126static void android_media_MediaDrm_releaseAllSecureStops(
1127    JNIEnv *env, jobject thiz) {
1128    sp<IDrm> drm = GetDrm(env, thiz);
1129
1130    if (drm == NULL) {
1131        jniThrowException(env, "java/lang/IllegalStateException",
1132                          "MediaDrm obj is null");
1133        return;
1134    }
1135
1136    status_t err = drm->releaseAllSecureStops();
1137
1138    throwExceptionAsNecessary(env, err, "Failed to release all secure stops");
1139}
1140
1141static jstring android_media_MediaDrm_getPropertyString(
1142    JNIEnv *env, jobject thiz, jstring jname) {
1143    sp<IDrm> drm = GetDrm(env, thiz);
1144
1145    if (drm == NULL) {
1146        jniThrowException(env, "java/lang/IllegalStateException",
1147                          "MediaDrm obj is null");
1148        return NULL;
1149    }
1150
1151    if (jname == NULL) {
1152        jniThrowException(env, "java/lang/IllegalArgumentException",
1153                          "property name String is null");
1154        return NULL;
1155    }
1156
1157    String8 name = JStringToString8(env, jname);
1158    String8 value;
1159
1160    status_t err = drm->getPropertyString(name, value);
1161
1162    if (throwExceptionAsNecessary(env, err, "Failed to get property")) {
1163        return NULL;
1164    }
1165
1166    return env->NewStringUTF(value.string());
1167}
1168
1169static jbyteArray android_media_MediaDrm_getPropertyByteArray(
1170    JNIEnv *env, jobject thiz, jstring jname) {
1171    sp<IDrm> drm = GetDrm(env, thiz);
1172
1173    if (drm == NULL) {
1174        jniThrowException(env, "java/lang/IllegalStateException",
1175                          "MediaDrm obj is null");
1176        return NULL;
1177    }
1178
1179    if (jname == NULL) {
1180        jniThrowException(env, "java/lang/IllegalArgumentException",
1181                          "property name String is null");
1182        return NULL;
1183    }
1184
1185    String8 name = JStringToString8(env, jname);
1186    Vector<uint8_t> value;
1187
1188    status_t err = drm->getPropertyByteArray(name, value);
1189
1190    if (throwExceptionAsNecessary(env, err, "Failed to get property")) {
1191        return NULL;
1192    }
1193
1194    return VectorToJByteArray(env, value);
1195}
1196
1197static void android_media_MediaDrm_setPropertyString(
1198    JNIEnv *env, jobject thiz, jstring jname, jstring jvalue) {
1199    sp<IDrm> drm = GetDrm(env, thiz);
1200
1201    if (drm == NULL) {
1202        jniThrowException(env, "java/lang/IllegalStateException",
1203                          "MediaDrm obj is null");
1204        return;
1205    }
1206
1207    if (jname == NULL) {
1208        jniThrowException(env, "java/lang/IllegalArgumentException",
1209                          "property name String is null");
1210        return;
1211    }
1212
1213    if (jvalue == NULL) {
1214        jniThrowException(env, "java/lang/IllegalArgumentException",
1215                          "property value String is null");
1216        return;
1217    }
1218
1219    String8 name = JStringToString8(env, jname);
1220    String8 value = JStringToString8(env, jvalue);
1221
1222    status_t err = drm->setPropertyString(name, value);
1223
1224    throwExceptionAsNecessary(env, err, "Failed to set property");
1225}
1226
1227static void android_media_MediaDrm_setPropertyByteArray(
1228    JNIEnv *env, jobject thiz, jstring jname, jbyteArray jvalue) {
1229    sp<IDrm> drm = GetDrm(env, thiz);
1230
1231    if (drm == NULL) {
1232        jniThrowException(env, "java/lang/IllegalStateException",
1233                          "MediaDrm obj is null");
1234        return;
1235    }
1236
1237    if (jname == NULL) {
1238        jniThrowException(env, "java/lang/IllegalArgumentException",
1239                          "property name String is null");
1240        return;
1241    }
1242
1243    if (jvalue == NULL) {
1244        jniThrowException(env, "java/lang/IllegalArgumentException",
1245                          "property value byte array is null");
1246        return;
1247    }
1248
1249    String8 name = JStringToString8(env, jname);
1250    Vector<uint8_t> value = JByteArrayToVector(env, jvalue);
1251
1252    status_t err = drm->setPropertyByteArray(name, value);
1253
1254    throwExceptionAsNecessary(env, err, "Failed to set property");
1255}
1256
1257static void android_media_MediaDrm_setCipherAlgorithmNative(
1258    JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
1259    jstring jalgorithm) {
1260
1261    sp<IDrm> drm = GetDrm(env, jdrm);
1262
1263    if (!CheckSession(env, drm, jsessionId)) {
1264        return;
1265    }
1266
1267    if (jalgorithm == NULL) {
1268        jniThrowException(env, "java/lang/IllegalArgumentException",
1269                          "algorithm String is null");
1270        return;
1271    }
1272
1273    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1274    String8 algorithm = JStringToString8(env, jalgorithm);
1275
1276    status_t err = drm->setCipherAlgorithm(sessionId, algorithm);
1277
1278    throwExceptionAsNecessary(env, err, "Failed to set cipher algorithm");
1279}
1280
1281static void android_media_MediaDrm_setMacAlgorithmNative(
1282    JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
1283    jstring jalgorithm) {
1284
1285    sp<IDrm> drm = GetDrm(env, jdrm);
1286
1287    if (!CheckSession(env, drm, jsessionId)) {
1288        return;
1289    }
1290
1291    if (jalgorithm == NULL) {
1292        jniThrowException(env, "java/lang/IllegalArgumentException",
1293                          "algorithm String is null");
1294        return;
1295    }
1296
1297    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1298    String8 algorithm = JStringToString8(env, jalgorithm);
1299
1300    status_t err = drm->setMacAlgorithm(sessionId, algorithm);
1301
1302    throwExceptionAsNecessary(env, err, "Failed to set mac algorithm");
1303}
1304
1305
1306static jbyteArray android_media_MediaDrm_encryptNative(
1307    JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
1308    jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) {
1309
1310    sp<IDrm> drm = GetDrm(env, jdrm);
1311
1312    if (!CheckSession(env, drm, jsessionId)) {
1313        return NULL;
1314    }
1315
1316    if (jkeyId == NULL || jinput == NULL || jiv == NULL) {
1317        jniThrowException(env, "java/lang/IllegalArgumentException",
1318                          "required argument is null");
1319        return NULL;
1320    }
1321
1322    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1323    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1324    Vector<uint8_t> input(JByteArrayToVector(env, jinput));
1325    Vector<uint8_t> iv(JByteArrayToVector(env, jiv));
1326    Vector<uint8_t> output;
1327
1328    status_t err = drm->encrypt(sessionId, keyId, input, iv, output);
1329
1330    if (throwExceptionAsNecessary(env, err, "Failed to encrypt")) {
1331        return NULL;
1332    }
1333
1334    return VectorToJByteArray(env, output);
1335}
1336
1337static jbyteArray android_media_MediaDrm_decryptNative(
1338    JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
1339    jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) {
1340
1341    sp<IDrm> drm = GetDrm(env, jdrm);
1342
1343    if (!CheckSession(env, drm, jsessionId)) {
1344        return NULL;
1345    }
1346
1347    if (jkeyId == NULL || jinput == NULL || jiv == NULL) {
1348        jniThrowException(env, "java/lang/IllegalArgumentException",
1349                          "required argument is null");
1350        return NULL;
1351    }
1352
1353    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1354    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1355    Vector<uint8_t> input(JByteArrayToVector(env, jinput));
1356    Vector<uint8_t> iv(JByteArrayToVector(env, jiv));
1357    Vector<uint8_t> output;
1358
1359    status_t err = drm->decrypt(sessionId, keyId, input, iv, output);
1360    if (throwExceptionAsNecessary(env, err, "Failed to decrypt")) {
1361        return NULL;
1362    }
1363
1364    return VectorToJByteArray(env, output);
1365}
1366
1367static jbyteArray android_media_MediaDrm_signNative(
1368    JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
1369    jbyteArray jkeyId, jbyteArray jmessage) {
1370
1371    sp<IDrm> drm = GetDrm(env, jdrm);
1372
1373    if (!CheckSession(env, drm, jsessionId)) {
1374        return NULL;
1375    }
1376
1377    if (jkeyId == NULL || jmessage == NULL) {
1378        jniThrowException(env, "java/lang/IllegalArgumentException",
1379                          "required argument is null");
1380        return NULL;
1381    }
1382
1383    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1384    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1385    Vector<uint8_t> message(JByteArrayToVector(env, jmessage));
1386    Vector<uint8_t> signature;
1387
1388    status_t err = drm->sign(sessionId, keyId, message, signature);
1389
1390    if (throwExceptionAsNecessary(env, err, "Failed to sign")) {
1391        return NULL;
1392    }
1393
1394    return VectorToJByteArray(env, signature);
1395}
1396
1397static jboolean android_media_MediaDrm_verifyNative(
1398    JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
1399    jbyteArray jkeyId, jbyteArray jmessage, jbyteArray jsignature) {
1400
1401    sp<IDrm> drm = GetDrm(env, jdrm);
1402
1403    if (!CheckSession(env, drm, jsessionId)) {
1404        return false;
1405    }
1406
1407    if (jkeyId == NULL || jmessage == NULL || jsignature == NULL) {
1408        jniThrowException(env, "java/lang/IllegalArgumentException",
1409                          "required argument is null");
1410        return false;
1411    }
1412
1413    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1414    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1415    Vector<uint8_t> message(JByteArrayToVector(env, jmessage));
1416    Vector<uint8_t> signature(JByteArrayToVector(env, jsignature));
1417    bool match;
1418
1419    status_t err = drm->verify(sessionId, keyId, message, signature, match);
1420
1421    throwExceptionAsNecessary(env, err, "Failed to verify");
1422    return match;
1423}
1424
1425
1426static jbyteArray android_media_MediaDrm_signRSANative(
1427    JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
1428    jstring jalgorithm, jbyteArray jwrappedKey, jbyteArray jmessage) {
1429
1430    sp<IDrm> drm = GetDrm(env, jdrm);
1431
1432    if (!CheckSession(env, drm, jsessionId)) {
1433        return NULL;
1434    }
1435
1436    if (jalgorithm == NULL || jwrappedKey == NULL || jmessage == NULL) {
1437        jniThrowException(env, "java/lang/IllegalArgumentException",
1438                          "required argument is null");
1439        return NULL;
1440    }
1441
1442    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1443    String8 algorithm = JStringToString8(env, jalgorithm);
1444    Vector<uint8_t> wrappedKey(JByteArrayToVector(env, jwrappedKey));
1445    Vector<uint8_t> message(JByteArrayToVector(env, jmessage));
1446    Vector<uint8_t> signature;
1447
1448    status_t err = drm->signRSA(sessionId, algorithm, message, wrappedKey, signature);
1449
1450    if (throwExceptionAsNecessary(env, err, "Failed to sign")) {
1451        return NULL;
1452    }
1453
1454    return VectorToJByteArray(env, signature);
1455}
1456
1457
1458static JNINativeMethod gMethods[] = {
1459    { "release", "()V", (void *)android_media_MediaDrm_release },
1460    { "native_init", "()V", (void *)android_media_MediaDrm_native_init },
1461
1462    { "native_setup", "(Ljava/lang/Object;[B)V",
1463      (void *)android_media_MediaDrm_native_setup },
1464
1465    { "native_finalize", "()V",
1466      (void *)android_media_MediaDrm_native_finalize },
1467
1468    { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;)Z",
1469      (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative },
1470
1471    { "openSession", "()[B",
1472      (void *)android_media_MediaDrm_openSession },
1473
1474    { "closeSession", "([B)V",
1475      (void *)android_media_MediaDrm_closeSession },
1476
1477    { "getKeyRequest", "([B[BLjava/lang/String;ILjava/util/HashMap;)"
1478      "Landroid/media/MediaDrm$KeyRequest;",
1479      (void *)android_media_MediaDrm_getKeyRequest },
1480
1481    { "provideKeyResponse", "([B[B)[B",
1482      (void *)android_media_MediaDrm_provideKeyResponse },
1483
1484    { "removeKeys", "([B)V",
1485      (void *)android_media_MediaDrm_removeKeys },
1486
1487    { "restoreKeys", "([B[B)V",
1488      (void *)android_media_MediaDrm_restoreKeys },
1489
1490    { "queryKeyStatus", "([B)Ljava/util/HashMap;",
1491      (void *)android_media_MediaDrm_queryKeyStatus },
1492
1493    { "getProvisionRequestNative", "(ILjava/lang/String;)Landroid/media/MediaDrm$ProvisionRequest;",
1494      (void *)android_media_MediaDrm_getProvisionRequestNative },
1495
1496    { "provideProvisionResponseNative", "([B)Landroid/media/MediaDrm$Certificate;",
1497      (void *)android_media_MediaDrm_provideProvisionResponseNative },
1498
1499    { "unprovisionDevice", "()V",
1500      (void *)android_media_MediaDrm_unprovisionDeviceNative },
1501
1502    { "getSecureStops", "()Ljava/util/List;",
1503      (void *)android_media_MediaDrm_getSecureStops },
1504
1505    { "getSecureStop", "([B)[B",
1506      (void *)android_media_MediaDrm_getSecureStop },
1507
1508    { "releaseSecureStops", "([B)V",
1509      (void *)android_media_MediaDrm_releaseSecureStops },
1510
1511    { "releaseAllSecureStops", "()V",
1512      (void *)android_media_MediaDrm_releaseAllSecureStops },
1513
1514    { "getPropertyString", "(Ljava/lang/String;)Ljava/lang/String;",
1515      (void *)android_media_MediaDrm_getPropertyString },
1516
1517    { "getPropertyByteArray", "(Ljava/lang/String;)[B",
1518      (void *)android_media_MediaDrm_getPropertyByteArray },
1519
1520    { "setPropertyString", "(Ljava/lang/String;Ljava/lang/String;)V",
1521      (void *)android_media_MediaDrm_setPropertyString },
1522
1523    { "setPropertyByteArray", "(Ljava/lang/String;[B)V",
1524      (void *)android_media_MediaDrm_setPropertyByteArray },
1525
1526    { "setCipherAlgorithmNative",
1527      "(Landroid/media/MediaDrm;[BLjava/lang/String;)V",
1528      (void *)android_media_MediaDrm_setCipherAlgorithmNative },
1529
1530    { "setMacAlgorithmNative",
1531      "(Landroid/media/MediaDrm;[BLjava/lang/String;)V",
1532      (void *)android_media_MediaDrm_setMacAlgorithmNative },
1533
1534    { "encryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B",
1535      (void *)android_media_MediaDrm_encryptNative },
1536
1537    { "decryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B",
1538      (void *)android_media_MediaDrm_decryptNative },
1539
1540    { "signNative", "(Landroid/media/MediaDrm;[B[B[B)[B",
1541      (void *)android_media_MediaDrm_signNative },
1542
1543    { "verifyNative", "(Landroid/media/MediaDrm;[B[B[B[B)Z",
1544      (void *)android_media_MediaDrm_verifyNative },
1545
1546    { "signRSANative", "(Landroid/media/MediaDrm;[BLjava/lang/String;[B[B)[B",
1547      (void *)android_media_MediaDrm_signRSANative },
1548};
1549
1550int register_android_media_Drm(JNIEnv *env) {
1551    return AndroidRuntime::registerNativeMethods(env,
1552                "android/media/MediaDrm", gMethods, NELEM(gMethods));
1553}
1554