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