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