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