android_media_MediaDrm.cpp revision 5ffbae64920bf38e16c5831996f00c3d28ae1186
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 != OK) {
312        String8 errbuf;
313        if (drmMessage != NULL) {
314            if (msg == NULL) {
315                msg = drmMessage;
316            } else {
317                errbuf = String8::format("%s: %s", msg, drmMessage);
318                msg = errbuf.string();
319            }
320        }
321        throwStateException(env, msg, err);
322        return true;
323    }
324    return false;
325}
326
327static sp<IDrm> GetDrm(JNIEnv *env, jobject thiz) {
328    JDrm *jdrm = (JDrm *)env->GetLongField(thiz, gFields.context);
329    return jdrm ? jdrm->getDrm() : NULL;
330}
331
332JDrm::JDrm(
333        JNIEnv *env, jobject thiz, const uint8_t uuid[16]) {
334    mObject = env->NewWeakGlobalRef(thiz);
335    mDrm = MakeDrm(uuid);
336    if (mDrm != NULL) {
337        mDrm->setListener(this);
338    }
339}
340
341JDrm::~JDrm() {
342    JNIEnv *env = AndroidRuntime::getJNIEnv();
343
344    env->DeleteWeakGlobalRef(mObject);
345    mObject = NULL;
346}
347
348// static
349sp<IDrm> JDrm::MakeDrm() {
350    sp<IServiceManager> sm = defaultServiceManager();
351
352    sp<IBinder> binder =
353        sm->getService(String16("media.player"));
354
355    sp<IMediaPlayerService> service =
356        interface_cast<IMediaPlayerService>(binder);
357
358    if (service == NULL) {
359        return NULL;
360    }
361
362    sp<IDrm> drm = service->makeDrm();
363
364    if (drm == NULL || (drm->initCheck() != OK && drm->initCheck() != NO_INIT)) {
365        return NULL;
366    }
367
368    return drm;
369}
370
371// static
372sp<IDrm> JDrm::MakeDrm(const uint8_t uuid[16]) {
373    sp<IDrm> drm = MakeDrm();
374
375    if (drm == NULL) {
376        return NULL;
377    }
378
379    status_t err = drm->createPlugin(uuid);
380
381    if (err != OK) {
382        return NULL;
383    }
384
385    return drm;
386}
387
388status_t JDrm::setListener(const sp<DrmListener>& listener) {
389    Mutex::Autolock lock(mLock);
390    mListener = listener;
391    return OK;
392}
393
394void JDrm::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) {
395    sp<DrmListener> listener;
396    mLock.lock();
397    listener = mListener;
398    mLock.unlock();
399
400    if (listener != NULL) {
401        Mutex::Autolock lock(mNotifyLock);
402        listener->notify(eventType, extra, obj);
403    }
404}
405
406void JDrm::disconnect() {
407    if (mDrm != NULL) {
408        mDrm->destroyPlugin();
409        mDrm.clear();
410    }
411}
412
413
414// static
415bool JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType) {
416    sp<IDrm> drm = MakeDrm();
417
418    if (drm == NULL) {
419        return false;
420    }
421
422    return drm->isCryptoSchemeSupported(uuid, mimeType);
423}
424
425status_t JDrm::initCheck() const {
426    return mDrm == NULL ? NO_INIT : OK;
427}
428
429// JNI conversion utilities
430static Vector<uint8_t> JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArray) {
431    Vector<uint8_t> vector;
432    size_t length = env->GetArrayLength(byteArray);
433    vector.insertAt((size_t)0, length);
434    env->GetByteArrayRegion(byteArray, 0, length, (jbyte *)vector.editArray());
435    return vector;
436}
437
438static jbyteArray VectorToJByteArray(JNIEnv *env, Vector<uint8_t> const &vector) {
439    size_t length = vector.size();
440    jbyteArray result = env->NewByteArray(length);
441    if (result != NULL) {
442        env->SetByteArrayRegion(result, 0, length, (jbyte *)vector.array());
443    }
444    return result;
445}
446
447static String8 JStringToString8(JNIEnv *env, jstring const &jstr) {
448    String8 result;
449
450    const char *s = env->GetStringUTFChars(jstr, NULL);
451    if (s) {
452        result = s;
453        env->ReleaseStringUTFChars(jstr, s);
454    }
455    return result;
456}
457
458/*
459    import java.util.HashMap;
460    import java.util.Set;
461    import java.Map.Entry;
462    import jav.util.Iterator;
463
464    HashMap<k, v> hm;
465    Set<Entry<k, v> > s = hm.entrySet();
466    Iterator i = s.iterator();
467    Entry e = s.next();
468*/
469
470static KeyedVector<String8, String8> HashMapToKeyedVector(
471    JNIEnv *env, jobject &hashMap, bool* pIsOK) {
472    jclass clazz = gFields.stringClassId;
473    KeyedVector<String8, String8> keyedVector;
474    *pIsOK = true;
475
476    jobject entrySet = env->CallObjectMethod(hashMap, gFields.hashmap.entrySet);
477    if (entrySet) {
478        jobject iterator = env->CallObjectMethod(entrySet, gFields.set.iterator);
479        if (iterator) {
480            jboolean hasNext = env->CallBooleanMethod(iterator, gFields.iterator.hasNext);
481            while (hasNext) {
482                jobject entry = env->CallObjectMethod(iterator, gFields.iterator.next);
483                if (entry) {
484                    jobject obj = env->CallObjectMethod(entry, gFields.entry.getKey);
485                    if (obj == NULL || !env->IsInstanceOf(obj, clazz)) {
486                        jniThrowException(env, "java/lang/IllegalArgumentException",
487                                          "HashMap key is not a String");
488                        env->DeleteLocalRef(entry);
489                        *pIsOK = false;
490                        break;
491                    }
492                    jstring jkey = static_cast<jstring>(obj);
493
494                    obj = env->CallObjectMethod(entry, gFields.entry.getValue);
495                    if (obj == NULL || !env->IsInstanceOf(obj, clazz)) {
496                        jniThrowException(env, "java/lang/IllegalArgumentException",
497                                          "HashMap value is not a String");
498                        env->DeleteLocalRef(entry);
499                        *pIsOK = false;
500                        break;
501                    }
502                    jstring jvalue = static_cast<jstring>(obj);
503
504                    String8 key = JStringToString8(env, jkey);
505                    String8 value = JStringToString8(env, jvalue);
506                    keyedVector.add(key, value);
507
508                    env->DeleteLocalRef(jkey);
509                    env->DeleteLocalRef(jvalue);
510                    hasNext = env->CallBooleanMethod(iterator, gFields.iterator.hasNext);
511                }
512                env->DeleteLocalRef(entry);
513            }
514            env->DeleteLocalRef(iterator);
515        }
516        env->DeleteLocalRef(entrySet);
517    }
518    return keyedVector;
519}
520
521static jobject KeyedVectorToHashMap (JNIEnv *env, KeyedVector<String8, String8> const &map) {
522    jclass clazz = gFields.hashmapClassId;
523    jobject hashMap = env->NewObject(clazz, gFields.hashmap.init);
524    for (size_t i = 0; i < map.size(); ++i) {
525        jstring jkey = env->NewStringUTF(map.keyAt(i).string());
526        jstring jvalue = env->NewStringUTF(map.valueAt(i).string());
527        env->CallObjectMethod(hashMap, gFields.hashmap.put, jkey, jvalue);
528        env->DeleteLocalRef(jkey);
529        env->DeleteLocalRef(jvalue);
530    }
531    return hashMap;
532}
533
534static jobject ListOfVectorsToArrayListOfByteArray(JNIEnv *env,
535                                                   List<Vector<uint8_t> > list) {
536    jclass clazz = gFields.arraylistClassId;
537    jobject arrayList = env->NewObject(clazz, gFields.arraylist.init);
538    List<Vector<uint8_t> >::iterator iter = list.begin();
539    while (iter != list.end()) {
540        jbyteArray byteArray = VectorToJByteArray(env, *iter);
541        env->CallBooleanMethod(arrayList, gFields.arraylist.add, byteArray);
542        env->DeleteLocalRef(byteArray);
543        iter++;
544    }
545
546    return arrayList;
547}
548
549}  // namespace android
550
551using namespace android;
552
553static sp<JDrm> setDrm(
554        JNIEnv *env, jobject thiz, const sp<JDrm> &drm) {
555    sp<JDrm> old = (JDrm *)env->GetLongField(thiz, gFields.context);
556    if (drm != NULL) {
557        drm->incStrong(thiz);
558    }
559    if (old != NULL) {
560        old->decStrong(thiz);
561    }
562    env->SetLongField(thiz, gFields.context, reinterpret_cast<jlong>(drm.get()));
563
564    return old;
565}
566
567static bool CheckSession(JNIEnv *env, const sp<IDrm> &drm, jbyteArray const &jsessionId)
568{
569    if (drm == NULL) {
570        jniThrowException(env, "java/lang/IllegalStateException", "MediaDrm obj is null");
571        return false;
572    }
573
574    if (jsessionId == NULL) {
575        jniThrowException(env, "java/lang/IllegalArgumentException", "sessionId is null");
576        return false;
577    }
578    return true;
579}
580
581static void android_media_MediaDrm_release(JNIEnv *env, jobject thiz) {
582    sp<JDrm> drm = setDrm(env, thiz, NULL);
583    if (drm != NULL) {
584        drm->setListener(NULL);
585        drm->disconnect();
586    }
587}
588
589static void android_media_MediaDrm_native_init(JNIEnv *env) {
590    jclass clazz;
591    FIND_CLASS(clazz, "android/media/MediaDrm");
592    GET_FIELD_ID(gFields.context, clazz, "mNativeContext", "J");
593    GET_STATIC_METHOD_ID(gFields.post_event, clazz, "postEventFromNative",
594                         "(Ljava/lang/Object;IIILjava/lang/Object;)V");
595
596    jfieldID field;
597    GET_STATIC_FIELD_ID(field, clazz, "EVENT_PROVISION_REQUIRED", "I");
598    gEventTypes.kEventProvisionRequired = env->GetStaticIntField(clazz, field);
599    GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_REQUIRED", "I");
600    gEventTypes.kEventKeyRequired = env->GetStaticIntField(clazz, field);
601    GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_EXPIRED", "I");
602    gEventTypes.kEventKeyExpired = env->GetStaticIntField(clazz, field);
603    GET_STATIC_FIELD_ID(field, clazz, "EVENT_VENDOR_DEFINED", "I");
604    gEventTypes.kEventVendorDefined = env->GetStaticIntField(clazz, field);
605    GET_STATIC_FIELD_ID(field, clazz, "EVENT_SESSION_RECLAIMED", "I");
606    gEventTypes.kEventSessionReclaimed = env->GetStaticIntField(clazz, field);
607
608    GET_STATIC_FIELD_ID(field, clazz, "DRM_EVENT", "I");
609    gEventWhat.kWhatDrmEvent = env->GetStaticIntField(clazz, field);
610    GET_STATIC_FIELD_ID(field, clazz, "EXPIRATION_UPDATE", "I");
611    gEventWhat.kWhatExpirationUpdate = env->GetStaticIntField(clazz, field);
612    GET_STATIC_FIELD_ID(field, clazz, "KEY_STATUS_CHANGE", "I");
613    gEventWhat.kWhatKeyStatusChange = env->GetStaticIntField(clazz, field);
614
615    GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_STREAMING", "I");
616    gKeyTypes.kKeyTypeStreaming = env->GetStaticIntField(clazz, field);
617    GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_OFFLINE", "I");
618    gKeyTypes.kKeyTypeOffline = env->GetStaticIntField(clazz, field);
619    GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_RELEASE", "I");
620    gKeyTypes.kKeyTypeRelease = env->GetStaticIntField(clazz, field);
621
622    GET_STATIC_FIELD_ID(field, clazz, "CERTIFICATE_TYPE_NONE", "I");
623    gCertificateTypes.kCertificateTypeNone = env->GetStaticIntField(clazz, field);
624    GET_STATIC_FIELD_ID(field, clazz, "CERTIFICATE_TYPE_X509", "I");
625    gCertificateTypes.kCertificateTypeX509 = env->GetStaticIntField(clazz, field);
626
627    FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
628    GET_FIELD_ID(gFields.keyRequest.data, clazz, "mData", "[B");
629    GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
630    GET_FIELD_ID(gFields.keyRequest.requestType, clazz, "mRequestType", "I");
631
632    GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_INITIAL", "I");
633    gKeyRequestTypes.kKeyRequestTypeInitial = env->GetStaticIntField(clazz, field);
634    GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RENEWAL", "I");
635    gKeyRequestTypes.kKeyRequestTypeRenewal = env->GetStaticIntField(clazz, field);
636    GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RELEASE", "I");
637    gKeyRequestTypes.kKeyRequestTypeRelease = env->GetStaticIntField(clazz, field);
638
639    FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest");
640    GET_FIELD_ID(gFields.provisionRequest.data, clazz, "mData", "[B");
641    GET_FIELD_ID(gFields.provisionRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
642
643    FIND_CLASS(clazz, "android/media/MediaDrm$Certificate");
644    GET_FIELD_ID(gFields.certificate.wrappedPrivateKey, clazz, "mWrappedKey", "[B");
645    GET_FIELD_ID(gFields.certificate.certificateData, clazz, "mCertificateData", "[B");
646    gFields.certificateClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
647
648    FIND_CLASS(clazz, "java/util/ArrayList");
649    GET_METHOD_ID(gFields.arraylist.init, clazz, "<init>", "()V");
650    GET_METHOD_ID(gFields.arraylist.add, clazz, "add", "(Ljava/lang/Object;)Z");
651
652    FIND_CLASS(clazz, "java/util/HashMap");
653    GET_METHOD_ID(gFields.hashmap.init, clazz, "<init>", "()V");
654    GET_METHOD_ID(gFields.hashmap.get, clazz, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
655    GET_METHOD_ID(gFields.hashmap.put, clazz, "put",
656                  "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
657    GET_METHOD_ID(gFields.hashmap.entrySet, clazz, "entrySet", "()Ljava/util/Set;");
658
659    FIND_CLASS(clazz, "java/util/Set");
660    GET_METHOD_ID(gFields.set.iterator, clazz, "iterator", "()Ljava/util/Iterator;");
661
662    FIND_CLASS(clazz, "java/util/Iterator");
663    GET_METHOD_ID(gFields.iterator.next, clazz, "next", "()Ljava/lang/Object;");
664    GET_METHOD_ID(gFields.iterator.hasNext, clazz, "hasNext", "()Z");
665
666    FIND_CLASS(clazz, "java/util/Map$Entry");
667    GET_METHOD_ID(gFields.entry.getKey, clazz, "getKey", "()Ljava/lang/Object;");
668    GET_METHOD_ID(gFields.entry.getValue, clazz, "getValue", "()Ljava/lang/Object;");
669
670    FIND_CLASS(clazz, "java/util/HashMap");
671    gFields.hashmapClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
672
673    FIND_CLASS(clazz, "java/lang/String");
674    gFields.stringClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
675
676    FIND_CLASS(clazz, "java/util/ArrayList");
677    gFields.arraylistClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
678
679    FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
680    GET_METHOD_ID(gFields.stateException.init, clazz, "<init>", "(ILjava/lang/String;)V");
681    gFields.stateException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
682}
683
684static void android_media_MediaDrm_native_setup(
685        JNIEnv *env, jobject thiz,
686        jobject weak_this, jbyteArray uuidObj) {
687
688    if (uuidObj == NULL) {
689        jniThrowException(env, "java/lang/IllegalArgumentException", "uuid is null");
690        return;
691    }
692
693    Vector<uint8_t> uuid = JByteArrayToVector(env, uuidObj);
694
695    if (uuid.size() != 16) {
696        jniThrowException(env, "java/lang/IllegalArgumentException",
697                          "invalid UUID size, expected 16 bytes");
698        return;
699    }
700
701    sp<JDrm> drm = new JDrm(env, thiz, uuid.array());
702
703    status_t err = drm->initCheck();
704
705    if (err != OK) {
706        jniThrowException(
707                env,
708                "android/media/UnsupportedSchemeException",
709                "Failed to instantiate drm object.");
710        return;
711    }
712
713    sp<JNIDrmListener> listener = new JNIDrmListener(env, thiz, weak_this);
714    drm->setListener(listener);
715    setDrm(env, thiz, drm);
716}
717
718static void android_media_MediaDrm_native_finalize(
719        JNIEnv *env, jobject thiz) {
720    android_media_MediaDrm_release(env, thiz);
721}
722
723static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative(
724    JNIEnv *env, jobject /* thiz */, jbyteArray uuidObj, jstring jmimeType) {
725
726    if (uuidObj == NULL) {
727        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
728        return false;
729    }
730
731    Vector<uint8_t> uuid = JByteArrayToVector(env, uuidObj);
732
733    if (uuid.size() != 16) {
734        jniThrowException(
735                env,
736                "java/lang/IllegalArgumentException",
737                "invalid UUID size, expected 16 bytes");
738        return false;
739    }
740
741    String8 mimeType;
742    if (jmimeType != NULL) {
743        mimeType = JStringToString8(env, jmimeType);
744    }
745
746    return JDrm::IsCryptoSchemeSupported(uuid.array(), mimeType);
747}
748
749static jbyteArray android_media_MediaDrm_openSession(
750    JNIEnv *env, jobject thiz) {
751    sp<IDrm> drm = GetDrm(env, thiz);
752
753    if (drm == NULL) {
754        jniThrowException(env, "java/lang/IllegalStateException",
755                          "MediaDrm obj is null");
756        return NULL;
757    }
758
759    Vector<uint8_t> sessionId;
760    status_t err = drm->openSession(sessionId);
761
762    if (throwExceptionAsNecessary(env, err, "Failed to open session")) {
763        return NULL;
764    }
765
766    return VectorToJByteArray(env, sessionId);
767}
768
769static void android_media_MediaDrm_closeSession(
770    JNIEnv *env, jobject thiz, jbyteArray jsessionId) {
771    sp<IDrm> drm = GetDrm(env, thiz);
772
773    if (!CheckSession(env, drm, jsessionId)) {
774        return;
775    }
776
777    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
778
779    status_t err = drm->closeSession(sessionId);
780
781    throwExceptionAsNecessary(env, err, "Failed to close session");
782}
783
784static jobject android_media_MediaDrm_getKeyRequest(
785    JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jinitData,
786    jstring jmimeType, jint jkeyType, jobject joptParams) {
787    sp<IDrm> drm = GetDrm(env, thiz);
788
789    if (!CheckSession(env, drm, jsessionId)) {
790        return NULL;
791    }
792
793    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
794
795    Vector<uint8_t> initData;
796    if (jinitData != NULL) {
797        initData = JByteArrayToVector(env, jinitData);
798    }
799
800    String8 mimeType;
801    if (jmimeType != NULL) {
802        mimeType = JStringToString8(env, jmimeType);
803    }
804
805    DrmPlugin::KeyType keyType;
806    if (jkeyType == gKeyTypes.kKeyTypeStreaming) {
807        keyType = DrmPlugin::kKeyType_Streaming;
808    } else if (jkeyType == gKeyTypes.kKeyTypeOffline) {
809        keyType = DrmPlugin::kKeyType_Offline;
810    } else if (jkeyType == gKeyTypes.kKeyTypeRelease) {
811        keyType = DrmPlugin::kKeyType_Release;
812    } else {
813        jniThrowException(env, "java/lang/IllegalArgumentException",
814                          "invalid keyType");
815        return NULL;
816    }
817
818    KeyedVector<String8, String8> optParams;
819    if (joptParams != NULL) {
820        bool isOK;
821        optParams = HashMapToKeyedVector(env, joptParams, &isOK);
822        if (!isOK) {
823            return NULL;
824        }
825    }
826
827    Vector<uint8_t> request;
828    String8 defaultUrl;
829    DrmPlugin::KeyRequestType keyRequestType;
830
831    status_t err = drm->getKeyRequest(sessionId, initData, mimeType,
832            keyType, optParams, request, defaultUrl, &keyRequestType);
833
834    if (throwExceptionAsNecessary(env, err, "Failed to get key request")) {
835        return NULL;
836    }
837
838    // Fill out return obj
839    jclass clazz;
840    FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
841
842    jobject keyObj = NULL;
843
844    if (clazz) {
845        keyObj = env->AllocObject(clazz);
846        jbyteArray jrequest = VectorToJByteArray(env, request);
847        env->SetObjectField(keyObj, gFields.keyRequest.data, jrequest);
848
849        jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string());
850        env->SetObjectField(keyObj, gFields.keyRequest.defaultUrl, jdefaultUrl);
851
852        switch (keyRequestType) {
853            case DrmPlugin::kKeyRequestType_Initial:
854                env->SetIntField(keyObj, gFields.keyRequest.requestType,
855                        gKeyRequestTypes.kKeyRequestTypeInitial);
856                break;
857            case DrmPlugin::kKeyRequestType_Renewal:
858                env->SetIntField(keyObj, gFields.keyRequest.requestType,
859                        gKeyRequestTypes.kKeyRequestTypeRenewal);
860                break;
861            case DrmPlugin::kKeyRequestType_Release:
862                env->SetIntField(keyObj, gFields.keyRequest.requestType,
863                        gKeyRequestTypes.kKeyRequestTypeRelease);
864                break;
865            default:
866                throwStateException(env, "DRM plugin failure: unknown key request type",
867                        ERROR_DRM_UNKNOWN);
868                break;
869        }
870    }
871
872    return keyObj;
873}
874
875static jbyteArray android_media_MediaDrm_provideKeyResponse(
876    JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jresponse) {
877    sp<IDrm> drm = GetDrm(env, thiz);
878
879    if (!CheckSession(env, drm, jsessionId)) {
880        return NULL;
881    }
882
883    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
884
885    if (jresponse == NULL) {
886        jniThrowException(env, "java/lang/IllegalArgumentException",
887                          "key response is null");
888        return NULL;
889    }
890    Vector<uint8_t> response(JByteArrayToVector(env, jresponse));
891    Vector<uint8_t> keySetId;
892
893    status_t err = drm->provideKeyResponse(sessionId, response, keySetId);
894
895    if (throwExceptionAsNecessary(env, err, "Failed to handle key response")) {
896        return NULL;
897    }
898    return VectorToJByteArray(env, keySetId);
899}
900
901static void android_media_MediaDrm_removeKeys(
902    JNIEnv *env, jobject thiz, jbyteArray jkeysetId) {
903    sp<IDrm> drm = GetDrm(env, thiz);
904
905    if (jkeysetId == NULL) {
906        jniThrowException(env, "java/lang/IllegalArgumentException",
907                          "keySetId is null");
908        return;
909    }
910
911    Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId));
912
913    status_t err = drm->removeKeys(keySetId);
914
915    throwExceptionAsNecessary(env, err, "Failed to remove keys");
916}
917
918static void android_media_MediaDrm_restoreKeys(
919    JNIEnv *env, jobject thiz, jbyteArray jsessionId,
920    jbyteArray jkeysetId) {
921
922    sp<IDrm> drm = GetDrm(env, thiz);
923
924    if (!CheckSession(env, drm, jsessionId)) {
925        return;
926    }
927
928    if (jkeysetId == NULL) {
929        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
930        return;
931    }
932
933    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
934    Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId));
935
936    status_t err = drm->restoreKeys(sessionId, keySetId);
937
938    throwExceptionAsNecessary(env, err, "Failed to restore keys");
939}
940
941static jobject android_media_MediaDrm_queryKeyStatus(
942    JNIEnv *env, jobject thiz, jbyteArray jsessionId) {
943    sp<IDrm> drm = GetDrm(env, thiz);
944
945    if (!CheckSession(env, drm, jsessionId)) {
946        return NULL;
947    }
948    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
949
950    KeyedVector<String8, String8> infoMap;
951
952    status_t err = drm->queryKeyStatus(sessionId, infoMap);
953
954    if (throwExceptionAsNecessary(env, err, "Failed to query key status")) {
955        return NULL;
956    }
957
958    return KeyedVectorToHashMap(env, infoMap);
959}
960
961static jobject android_media_MediaDrm_getProvisionRequestNative(
962    JNIEnv *env, jobject thiz, jint jcertType, jstring jcertAuthority) {
963    sp<IDrm> drm = GetDrm(env, thiz);
964
965    if (drm == NULL) {
966        jniThrowException(env, "java/lang/IllegalStateException",
967                          "MediaDrm obj is null");
968        return NULL;
969    }
970
971    Vector<uint8_t> request;
972    String8 defaultUrl;
973
974    String8 certType;
975    if (jcertType == gCertificateTypes.kCertificateTypeX509) {
976        certType = "X.509";
977    } else if (jcertType == gCertificateTypes.kCertificateTypeNone) {
978        certType = "none";
979    } else {
980        certType = "invalid";
981    }
982
983    String8 certAuthority = JStringToString8(env, jcertAuthority);
984    status_t err = drm->getProvisionRequest(certType, certAuthority, request, defaultUrl);
985
986    if (throwExceptionAsNecessary(env, err, "Failed to get provision request")) {
987        return NULL;
988    }
989
990    // Fill out return obj
991    jclass clazz;
992    FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest");
993
994    jobject provisionObj = NULL;
995
996    if (clazz) {
997        provisionObj = env->AllocObject(clazz);
998        jbyteArray jrequest = VectorToJByteArray(env, request);
999        env->SetObjectField(provisionObj, gFields.provisionRequest.data, jrequest);
1000
1001        jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string());
1002        env->SetObjectField(provisionObj, gFields.provisionRequest.defaultUrl, jdefaultUrl);
1003    }
1004
1005    return provisionObj;
1006}
1007
1008static jobject android_media_MediaDrm_provideProvisionResponseNative(
1009    JNIEnv *env, jobject thiz, jbyteArray jresponse) {
1010    sp<IDrm> drm = GetDrm(env, thiz);
1011
1012    if (drm == NULL) {
1013        jniThrowException(env, "java/lang/IllegalStateException",
1014                          "MediaDrm obj is null");
1015        return NULL;
1016    }
1017
1018    if (jresponse == NULL) {
1019        jniThrowException(env, "java/lang/IllegalArgumentException",
1020                          "provision response is null");
1021        return NULL;
1022    }
1023
1024    Vector<uint8_t> response(JByteArrayToVector(env, jresponse));
1025    Vector<uint8_t> certificate, wrappedKey;
1026
1027    status_t err = drm->provideProvisionResponse(response, certificate, wrappedKey);
1028
1029    // Fill out return obj
1030    jclass clazz = gFields.certificateClassId;
1031
1032    jobject certificateObj = NULL;
1033
1034    if (clazz && certificate.size() && wrappedKey.size()) {
1035        certificateObj = env->AllocObject(clazz);
1036        jbyteArray jcertificate = VectorToJByteArray(env, certificate);
1037        env->SetObjectField(certificateObj, gFields.certificate.certificateData, jcertificate);
1038
1039        jbyteArray jwrappedKey = VectorToJByteArray(env, wrappedKey);
1040        env->SetObjectField(certificateObj, gFields.certificate.wrappedPrivateKey, jwrappedKey);
1041    }
1042
1043    throwExceptionAsNecessary(env, err, "Failed to handle provision response");
1044    return certificateObj;
1045}
1046
1047static void android_media_MediaDrm_unprovisionDeviceNative(
1048    JNIEnv *env, jobject thiz) {
1049    sp<IDrm> drm = GetDrm(env, thiz);
1050
1051    if (drm == NULL) {
1052        jniThrowException(env, "java/lang/IllegalStateException",
1053                          "MediaDrm obj is null");
1054        return;
1055    }
1056
1057    status_t err = drm->unprovisionDevice();
1058
1059    throwExceptionAsNecessary(env, err, "Failed to handle provision response");
1060    return;
1061}
1062
1063static jobject android_media_MediaDrm_getSecureStops(
1064    JNIEnv *env, jobject thiz) {
1065    sp<IDrm> drm = GetDrm(env, thiz);
1066
1067    if (drm == NULL) {
1068        jniThrowException(env, "java/lang/IllegalStateException",
1069                          "MediaDrm obj is null");
1070        return NULL;
1071    }
1072
1073    List<Vector<uint8_t> > secureStops;
1074
1075    status_t err = drm->getSecureStops(secureStops);
1076
1077    if (throwExceptionAsNecessary(env, err, "Failed to get secure stops")) {
1078        return NULL;
1079    }
1080
1081    return ListOfVectorsToArrayListOfByteArray(env, secureStops);
1082}
1083
1084static jbyteArray android_media_MediaDrm_getSecureStop(
1085    JNIEnv *env, jobject thiz, jbyteArray ssid) {
1086    sp<IDrm> drm = GetDrm(env, thiz);
1087
1088    if (drm == NULL) {
1089        jniThrowException(env, "java/lang/IllegalStateException",
1090                          "MediaDrm obj is null");
1091        return NULL;
1092    }
1093
1094    Vector<uint8_t> secureStop;
1095
1096    status_t err = drm->getSecureStop(JByteArrayToVector(env, ssid), secureStop);
1097
1098    if (throwExceptionAsNecessary(env, err, "Failed to get secure stop")) {
1099        return NULL;
1100    }
1101
1102    return VectorToJByteArray(env, secureStop);
1103}
1104
1105static void android_media_MediaDrm_releaseSecureStops(
1106    JNIEnv *env, jobject thiz, jbyteArray jssRelease) {
1107    sp<IDrm> drm = GetDrm(env, thiz);
1108
1109    if (drm == NULL) {
1110        jniThrowException(env, "java/lang/IllegalStateException",
1111                          "MediaDrm obj is null");
1112        return;
1113    }
1114
1115    Vector<uint8_t> ssRelease(JByteArrayToVector(env, jssRelease));
1116
1117    status_t err = drm->releaseSecureStops(ssRelease);
1118
1119    throwExceptionAsNecessary(env, err, "Failed to release secure stops");
1120}
1121
1122static void android_media_MediaDrm_releaseAllSecureStops(
1123    JNIEnv *env, jobject thiz) {
1124    sp<IDrm> drm = GetDrm(env, thiz);
1125
1126    if (drm == NULL) {
1127        jniThrowException(env, "java/lang/IllegalStateException",
1128                          "MediaDrm obj is null");
1129        return;
1130    }
1131
1132    status_t err = drm->releaseAllSecureStops();
1133
1134    throwExceptionAsNecessary(env, err, "Failed to release all secure stops");
1135}
1136
1137static jstring android_media_MediaDrm_getPropertyString(
1138    JNIEnv *env, jobject thiz, jstring jname) {
1139    sp<IDrm> drm = GetDrm(env, thiz);
1140
1141    if (drm == NULL) {
1142        jniThrowException(env, "java/lang/IllegalStateException",
1143                          "MediaDrm obj is null");
1144        return NULL;
1145    }
1146
1147    if (jname == NULL) {
1148        jniThrowException(env, "java/lang/IllegalArgumentException",
1149                          "property name String is null");
1150        return NULL;
1151    }
1152
1153    String8 name = JStringToString8(env, jname);
1154    String8 value;
1155
1156    status_t err = drm->getPropertyString(name, value);
1157
1158    if (throwExceptionAsNecessary(env, err, "Failed to get property")) {
1159        return NULL;
1160    }
1161
1162    return env->NewStringUTF(value.string());
1163}
1164
1165static jbyteArray android_media_MediaDrm_getPropertyByteArray(
1166    JNIEnv *env, jobject thiz, jstring jname) {
1167    sp<IDrm> drm = GetDrm(env, thiz);
1168
1169    if (drm == NULL) {
1170        jniThrowException(env, "java/lang/IllegalStateException",
1171                          "MediaDrm obj is null");
1172        return NULL;
1173    }
1174
1175    if (jname == NULL) {
1176        jniThrowException(env, "java/lang/IllegalArgumentException",
1177                          "property name String is null");
1178        return NULL;
1179    }
1180
1181    String8 name = JStringToString8(env, jname);
1182    Vector<uint8_t> value;
1183
1184    status_t err = drm->getPropertyByteArray(name, value);
1185
1186    if (throwExceptionAsNecessary(env, err, "Failed to get property")) {
1187        return NULL;
1188    }
1189
1190    return VectorToJByteArray(env, value);
1191}
1192
1193static void android_media_MediaDrm_setPropertyString(
1194    JNIEnv *env, jobject thiz, jstring jname, jstring jvalue) {
1195    sp<IDrm> drm = GetDrm(env, thiz);
1196
1197    if (drm == NULL) {
1198        jniThrowException(env, "java/lang/IllegalStateException",
1199                          "MediaDrm obj is null");
1200        return;
1201    }
1202
1203    if (jname == NULL) {
1204        jniThrowException(env, "java/lang/IllegalArgumentException",
1205                          "property name String is null");
1206        return;
1207    }
1208
1209    if (jvalue == NULL) {
1210        jniThrowException(env, "java/lang/IllegalArgumentException",
1211                          "property value String is null");
1212        return;
1213    }
1214
1215    String8 name = JStringToString8(env, jname);
1216    String8 value = JStringToString8(env, jvalue);
1217
1218    status_t err = drm->setPropertyString(name, value);
1219
1220    throwExceptionAsNecessary(env, err, "Failed to set property");
1221}
1222
1223static void android_media_MediaDrm_setPropertyByteArray(
1224    JNIEnv *env, jobject thiz, jstring jname, jbyteArray jvalue) {
1225    sp<IDrm> drm = GetDrm(env, thiz);
1226
1227    if (drm == NULL) {
1228        jniThrowException(env, "java/lang/IllegalStateException",
1229                          "MediaDrm obj is null");
1230        return;
1231    }
1232
1233    if (jname == NULL) {
1234        jniThrowException(env, "java/lang/IllegalArgumentException",
1235                          "property name String is null");
1236        return;
1237    }
1238
1239    if (jvalue == NULL) {
1240        jniThrowException(env, "java/lang/IllegalArgumentException",
1241                          "property value byte array is null");
1242        return;
1243    }
1244
1245    String8 name = JStringToString8(env, jname);
1246    Vector<uint8_t> value = JByteArrayToVector(env, jvalue);
1247
1248    status_t err = drm->setPropertyByteArray(name, value);
1249
1250    throwExceptionAsNecessary(env, err, "Failed to set property");
1251}
1252
1253static void android_media_MediaDrm_setCipherAlgorithmNative(
1254    JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
1255    jstring jalgorithm) {
1256
1257    sp<IDrm> drm = GetDrm(env, jdrm);
1258
1259    if (!CheckSession(env, drm, jsessionId)) {
1260        return;
1261    }
1262
1263    if (jalgorithm == NULL) {
1264        jniThrowException(env, "java/lang/IllegalArgumentException",
1265                          "algorithm String is null");
1266        return;
1267    }
1268
1269    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1270    String8 algorithm = JStringToString8(env, jalgorithm);
1271
1272    status_t err = drm->setCipherAlgorithm(sessionId, algorithm);
1273
1274    throwExceptionAsNecessary(env, err, "Failed to set cipher algorithm");
1275}
1276
1277static void android_media_MediaDrm_setMacAlgorithmNative(
1278    JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
1279    jstring jalgorithm) {
1280
1281    sp<IDrm> drm = GetDrm(env, jdrm);
1282
1283    if (!CheckSession(env, drm, jsessionId)) {
1284        return;
1285    }
1286
1287    if (jalgorithm == NULL) {
1288        jniThrowException(env, "java/lang/IllegalArgumentException",
1289                          "algorithm String is null");
1290        return;
1291    }
1292
1293    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1294    String8 algorithm = JStringToString8(env, jalgorithm);
1295
1296    status_t err = drm->setMacAlgorithm(sessionId, algorithm);
1297
1298    throwExceptionAsNecessary(env, err, "Failed to set mac algorithm");
1299}
1300
1301
1302static jbyteArray android_media_MediaDrm_encryptNative(
1303    JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
1304    jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) {
1305
1306    sp<IDrm> drm = GetDrm(env, jdrm);
1307
1308    if (!CheckSession(env, drm, jsessionId)) {
1309        return NULL;
1310    }
1311
1312    if (jkeyId == NULL || jinput == NULL || jiv == NULL) {
1313        jniThrowException(env, "java/lang/IllegalArgumentException",
1314                          "required argument is null");
1315        return NULL;
1316    }
1317
1318    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1319    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1320    Vector<uint8_t> input(JByteArrayToVector(env, jinput));
1321    Vector<uint8_t> iv(JByteArrayToVector(env, jiv));
1322    Vector<uint8_t> output;
1323
1324    status_t err = drm->encrypt(sessionId, keyId, input, iv, output);
1325
1326    if (throwExceptionAsNecessary(env, err, "Failed to encrypt")) {
1327        return NULL;
1328    }
1329
1330    return VectorToJByteArray(env, output);
1331}
1332
1333static jbyteArray android_media_MediaDrm_decryptNative(
1334    JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
1335    jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) {
1336
1337    sp<IDrm> drm = GetDrm(env, jdrm);
1338
1339    if (!CheckSession(env, drm, jsessionId)) {
1340        return NULL;
1341    }
1342
1343    if (jkeyId == NULL || jinput == NULL || jiv == NULL) {
1344        jniThrowException(env, "java/lang/IllegalArgumentException",
1345                          "required argument is null");
1346        return NULL;
1347    }
1348
1349    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1350    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1351    Vector<uint8_t> input(JByteArrayToVector(env, jinput));
1352    Vector<uint8_t> iv(JByteArrayToVector(env, jiv));
1353    Vector<uint8_t> output;
1354
1355    status_t err = drm->decrypt(sessionId, keyId, input, iv, output);
1356    if (throwExceptionAsNecessary(env, err, "Failed to decrypt")) {
1357        return NULL;
1358    }
1359
1360    return VectorToJByteArray(env, output);
1361}
1362
1363static jbyteArray android_media_MediaDrm_signNative(
1364    JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
1365    jbyteArray jkeyId, jbyteArray jmessage) {
1366
1367    sp<IDrm> drm = GetDrm(env, jdrm);
1368
1369    if (!CheckSession(env, drm, jsessionId)) {
1370        return NULL;
1371    }
1372
1373    if (jkeyId == NULL || jmessage == NULL) {
1374        jniThrowException(env, "java/lang/IllegalArgumentException",
1375                          "required argument is null");
1376        return NULL;
1377    }
1378
1379    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1380    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1381    Vector<uint8_t> message(JByteArrayToVector(env, jmessage));
1382    Vector<uint8_t> signature;
1383
1384    status_t err = drm->sign(sessionId, keyId, message, signature);
1385
1386    if (throwExceptionAsNecessary(env, err, "Failed to sign")) {
1387        return NULL;
1388    }
1389
1390    return VectorToJByteArray(env, signature);
1391}
1392
1393static jboolean android_media_MediaDrm_verifyNative(
1394    JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
1395    jbyteArray jkeyId, jbyteArray jmessage, jbyteArray jsignature) {
1396
1397    sp<IDrm> drm = GetDrm(env, jdrm);
1398
1399    if (!CheckSession(env, drm, jsessionId)) {
1400        return false;
1401    }
1402
1403    if (jkeyId == NULL || jmessage == NULL || jsignature == NULL) {
1404        jniThrowException(env, "java/lang/IllegalArgumentException",
1405                          "required argument is null");
1406        return false;
1407    }
1408
1409    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1410    Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId));
1411    Vector<uint8_t> message(JByteArrayToVector(env, jmessage));
1412    Vector<uint8_t> signature(JByteArrayToVector(env, jsignature));
1413    bool match;
1414
1415    status_t err = drm->verify(sessionId, keyId, message, signature, match);
1416
1417    throwExceptionAsNecessary(env, err, "Failed to verify");
1418    return match;
1419}
1420
1421
1422static jbyteArray android_media_MediaDrm_signRSANative(
1423    JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
1424    jstring jalgorithm, jbyteArray jwrappedKey, jbyteArray jmessage) {
1425
1426    sp<IDrm> drm = GetDrm(env, jdrm);
1427
1428    if (!CheckSession(env, drm, jsessionId)) {
1429        return NULL;
1430    }
1431
1432    if (jalgorithm == NULL || jwrappedKey == NULL || jmessage == NULL) {
1433        jniThrowException(env, "java/lang/IllegalArgumentException",
1434                          "required argument is null");
1435        return NULL;
1436    }
1437
1438    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
1439    String8 algorithm = JStringToString8(env, jalgorithm);
1440    Vector<uint8_t> wrappedKey(JByteArrayToVector(env, jwrappedKey));
1441    Vector<uint8_t> message(JByteArrayToVector(env, jmessage));
1442    Vector<uint8_t> signature;
1443
1444    status_t err = drm->signRSA(sessionId, algorithm, message, wrappedKey, signature);
1445
1446    if (throwExceptionAsNecessary(env, err, "Failed to sign")) {
1447        return NULL;
1448    }
1449
1450    return VectorToJByteArray(env, signature);
1451}
1452
1453
1454static JNINativeMethod gMethods[] = {
1455    { "release", "()V", (void *)android_media_MediaDrm_release },
1456    { "native_init", "()V", (void *)android_media_MediaDrm_native_init },
1457
1458    { "native_setup", "(Ljava/lang/Object;[B)V",
1459      (void *)android_media_MediaDrm_native_setup },
1460
1461    { "native_finalize", "()V",
1462      (void *)android_media_MediaDrm_native_finalize },
1463
1464    { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;)Z",
1465      (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative },
1466
1467    { "openSession", "()[B",
1468      (void *)android_media_MediaDrm_openSession },
1469
1470    { "closeSession", "([B)V",
1471      (void *)android_media_MediaDrm_closeSession },
1472
1473    { "getKeyRequest", "([B[BLjava/lang/String;ILjava/util/HashMap;)"
1474      "Landroid/media/MediaDrm$KeyRequest;",
1475      (void *)android_media_MediaDrm_getKeyRequest },
1476
1477    { "provideKeyResponse", "([B[B)[B",
1478      (void *)android_media_MediaDrm_provideKeyResponse },
1479
1480    { "removeKeys", "([B)V",
1481      (void *)android_media_MediaDrm_removeKeys },
1482
1483    { "restoreKeys", "([B[B)V",
1484      (void *)android_media_MediaDrm_restoreKeys },
1485
1486    { "queryKeyStatus", "([B)Ljava/util/HashMap;",
1487      (void *)android_media_MediaDrm_queryKeyStatus },
1488
1489    { "getProvisionRequestNative", "(ILjava/lang/String;)Landroid/media/MediaDrm$ProvisionRequest;",
1490      (void *)android_media_MediaDrm_getProvisionRequestNative },
1491
1492    { "provideProvisionResponseNative", "([B)Landroid/media/MediaDrm$Certificate;",
1493      (void *)android_media_MediaDrm_provideProvisionResponseNative },
1494
1495    { "unprovisionDevice", "()V",
1496      (void *)android_media_MediaDrm_unprovisionDeviceNative },
1497
1498    { "getSecureStops", "()Ljava/util/List;",
1499      (void *)android_media_MediaDrm_getSecureStops },
1500
1501    { "getSecureStop", "([B)[B",
1502      (void *)android_media_MediaDrm_getSecureStop },
1503
1504    { "releaseSecureStops", "([B)V",
1505      (void *)android_media_MediaDrm_releaseSecureStops },
1506
1507    { "releaseAllSecureStops", "()V",
1508      (void *)android_media_MediaDrm_releaseAllSecureStops },
1509
1510    { "getPropertyString", "(Ljava/lang/String;)Ljava/lang/String;",
1511      (void *)android_media_MediaDrm_getPropertyString },
1512
1513    { "getPropertyByteArray", "(Ljava/lang/String;)[B",
1514      (void *)android_media_MediaDrm_getPropertyByteArray },
1515
1516    { "setPropertyString", "(Ljava/lang/String;Ljava/lang/String;)V",
1517      (void *)android_media_MediaDrm_setPropertyString },
1518
1519    { "setPropertyByteArray", "(Ljava/lang/String;[B)V",
1520      (void *)android_media_MediaDrm_setPropertyByteArray },
1521
1522    { "setCipherAlgorithmNative",
1523      "(Landroid/media/MediaDrm;[BLjava/lang/String;)V",
1524      (void *)android_media_MediaDrm_setCipherAlgorithmNative },
1525
1526    { "setMacAlgorithmNative",
1527      "(Landroid/media/MediaDrm;[BLjava/lang/String;)V",
1528      (void *)android_media_MediaDrm_setMacAlgorithmNative },
1529
1530    { "encryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B",
1531      (void *)android_media_MediaDrm_encryptNative },
1532
1533    { "decryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B",
1534      (void *)android_media_MediaDrm_decryptNative },
1535
1536    { "signNative", "(Landroid/media/MediaDrm;[B[B[B)[B",
1537      (void *)android_media_MediaDrm_signNative },
1538
1539    { "verifyNative", "(Landroid/media/MediaDrm;[B[B[B[B)Z",
1540      (void *)android_media_MediaDrm_verifyNative },
1541
1542    { "signRSANative", "(Landroid/media/MediaDrm;[BLjava/lang/String;[B[B)[B",
1543      (void *)android_media_MediaDrm_signRSANative },
1544};
1545
1546int register_android_media_Drm(JNIEnv *env) {
1547    return AndroidRuntime::registerNativeMethods(env,
1548                "android/media/MediaDrm", gMethods, NELEM(gMethods));
1549}
1550