| /* |
| * Copyright 2013, The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "MediaDrm-JNI" |
| #include <utils/Log.h> |
| |
| #include "android_media_MediaDrm.h" |
| #include "android_media_MediaMetricsJNI.h" |
| #include "android_os_Parcel.h" |
| #include "android_runtime/AndroidRuntime.h" |
| #include "android_runtime/Log.h" |
| #include "android_os_Parcel.h" |
| #include "jni.h" |
| #include <nativehelper/JNIHelp.h> |
| |
| #include <android/hardware/drm/1.3/IDrmFactory.h> |
| #include <binder/Parcel.h> |
| #include <binder/PersistableBundle.h> |
| #include <cutils/properties.h> |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <media/stagefright/MediaErrors.h> |
| #include <mediadrm/DrmMetricsConsumer.h> |
| #include <mediadrm/DrmUtils.h> |
| #include <mediadrm/IDrmMetricsConsumer.h> |
| #include <mediadrm/IDrm.h> |
| #include <utils/Vector.h> |
| #include <map> |
| #include <string> |
| |
| using ::android::os::PersistableBundle; |
| namespace drm = ::android::hardware::drm; |
| |
| namespace android { |
| |
| #define FIND_CLASS(var, className) \ |
| var = env->FindClass(className); \ |
| LOG_FATAL_IF(! (var), "Unable to find class %s", className); |
| |
| #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ |
| var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ |
| LOG_FATAL_IF(! (var), "Unable to find field %s", fieldName); |
| |
| #define GET_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \ |
| var = env->GetMethodID(clazz, fieldName, fieldDescriptor); \ |
| LOG_FATAL_IF(! (var), "Unable to find method %s", fieldName); |
| |
| #define GET_STATIC_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ |
| var = env->GetStaticFieldID(clazz, fieldName, fieldDescriptor); \ |
| LOG_FATAL_IF(! (var), "Unable to find field %s", fieldName); |
| |
| #define GET_STATIC_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \ |
| var = env->GetStaticMethodID(clazz, fieldName, fieldDescriptor); \ |
| LOG_FATAL_IF(! (var), "Unable to find static method %s", fieldName); |
| |
| #define GET_STATIC_OBJECT_FIELD(var, clazz, fieldId) \ |
| var = env->GetStaticObjectField(clazz, fieldId); \ |
| LOG_FATAL_IF(! (var), "Unable to find static object field %p", fieldId); |
| |
| |
| struct RequestFields { |
| jfieldID data; |
| jfieldID defaultUrl; |
| jfieldID requestType; |
| }; |
| |
| struct ArrayListFields { |
| jmethodID init; |
| jmethodID add; |
| }; |
| |
| struct HashmapFields { |
| jmethodID init; |
| jmethodID get; |
| jmethodID put; |
| jmethodID entrySet; |
| }; |
| |
| struct SetFields { |
| jmethodID iterator; |
| }; |
| |
| struct IteratorFields { |
| jmethodID next; |
| jmethodID hasNext; |
| }; |
| |
| struct EntryFields { |
| jmethodID getKey; |
| jmethodID getValue; |
| }; |
| |
| struct EventTypes { |
| jint kEventProvisionRequired; |
| jint kEventKeyRequired; |
| jint kEventKeyExpired; |
| jint kEventVendorDefined; |
| jint kEventSessionReclaimed; |
| } gEventTypes; |
| |
| struct EventWhat { |
| jint kWhatDrmEvent; |
| jint kWhatExpirationUpdate; |
| jint kWhatKeyStatusChange; |
| jint kWhatSessionLostState; |
| } gEventWhat; |
| |
| struct KeyTypes { |
| jint kKeyTypeStreaming; |
| jint kKeyTypeOffline; |
| jint kKeyTypeRelease; |
| } gKeyTypes; |
| |
| struct KeyRequestTypes { |
| jint kKeyRequestTypeInitial; |
| jint kKeyRequestTypeRenewal; |
| jint kKeyRequestTypeRelease; |
| jint kKeyRequestTypeNone; |
| jint kKeyRequestTypeUpdate; |
| } gKeyRequestTypes; |
| |
| struct CertificateTypes { |
| jint kCertificateTypeNone; |
| jint kCertificateTypeX509; |
| } gCertificateTypes; |
| |
| struct CertificateFields { |
| jfieldID wrappedPrivateKey; |
| jfieldID certificateData; |
| }; |
| |
| struct StateExceptionFields { |
| jmethodID init; |
| jclass classId; |
| }; |
| |
| struct SessionExceptionFields { |
| jmethodID init; |
| jclass classId; |
| jfieldID errorCode; |
| }; |
| |
| struct SessionExceptionErrorCodes { |
| jint kErrorUnknown; |
| jint kResourceContention; |
| } gSessionExceptionErrorCodes; |
| |
| struct HDCPLevels { |
| jint kHdcpLevelUnknown; |
| jint kHdcpNone; |
| jint kHdcpV1; |
| jint kHdcpV2; |
| jint kHdcpV2_1; |
| jint kHdcpV2_2; |
| jint kHdcpV2_3; |
| jint kHdcpNoOutput; |
| } gHdcpLevels; |
| |
| struct SecurityLevels { |
| jint kSecurityLevelUnknown; |
| jint kSecurityLevelMax; |
| jint kSecurityLevelSwSecureCrypto; |
| jint kSecurityLevelSwSecureDecode; |
| jint kSecurityLevelHwSecureCrypto; |
| jint kSecurityLevelHwSecureDecode; |
| jint kSecurityLevelHwSecureAll; |
| } gSecurityLevels; |
| |
| struct OfflineLicenseState { |
| jint kOfflineLicenseStateUsable; |
| jint kOfflineLicenseStateReleased; |
| jint kOfflineLicenseStateUnknown; |
| } gOfflineLicenseStates; |
| |
| struct KeyStatusFields { |
| jmethodID init; |
| jclass classId; |
| }; |
| |
| struct LogMessageFields { |
| jmethodID init; |
| jclass classId; |
| }; |
| |
| struct DrmExceptionFields { |
| jmethodID init; |
| jclass classId; |
| }; |
| |
| struct fields_t { |
| jfieldID context; |
| jmethodID post_event; |
| RequestFields keyRequest; |
| RequestFields provisionRequest; |
| ArrayListFields arraylist; |
| HashmapFields hashmap; |
| SetFields set; |
| IteratorFields iterator; |
| EntryFields entry; |
| CertificateFields certificate; |
| StateExceptionFields stateException; |
| SessionExceptionFields sessionException; |
| jclass certificateClassId; |
| jclass hashmapClassId; |
| jclass arraylistClassId; |
| jclass stringClassId; |
| jobject bundleCreator; |
| jmethodID createFromParcelId; |
| jclass parcelCreatorClassId; |
| KeyStatusFields keyStatus; |
| LogMessageFields logMessage; |
| std::map<std::string, DrmExceptionFields> exceptionCtors; |
| }; |
| |
| static fields_t gFields; |
| |
| namespace { |
| |
| jbyteArray hidlVectorToJByteArray(const hardware::hidl_vec<uint8_t> &vector) { |
| JNIEnv *env = AndroidRuntime::getJNIEnv(); |
| size_t length = vector.size(); |
| jbyteArray result = env->NewByteArray(length); |
| if (result != NULL) { |
| env->SetByteArrayRegion(result, 0, length, reinterpret_cast<const jbyte *>(vector.data())); |
| } |
| return result; |
| } |
| |
| jobject hidlLogMessagesToJavaList(JNIEnv *env, const Vector<drm::V1_4::LogMessage> &logs) { |
| jclass clazz = gFields.arraylistClassId; |
| jobject arrayList = env->NewObject(clazz, gFields.arraylist.init); |
| clazz = gFields.logMessage.classId; |
| for (auto log: logs) { |
| jobject jLog = env->NewObject(clazz, gFields.logMessage.init, |
| static_cast<jlong>(log.timeMs), |
| static_cast<jint>(log.priority), |
| env->NewStringUTF(log.message.c_str())); |
| env->CallBooleanMethod(arrayList, gFields.arraylist.add, jLog); |
| } |
| return arrayList; |
| } |
| |
| void resolveDrmExceptionCtor(JNIEnv *env, const char *className) { |
| jclass clazz; |
| jmethodID init; |
| FIND_CLASS(clazz, className); |
| GET_METHOD_ID(init, clazz, "<init>", "(Ljava/lang/String;III)V"); |
| gFields.exceptionCtors[std::string(className)] = { |
| .init = init, |
| .classId = static_cast<jclass>(env->NewGlobalRef(clazz)) |
| }; |
| } |
| |
| void drmThrowException(JNIEnv* env, const char *className, const DrmStatus &err, const char *msg) { |
| using namespace android::jnihelp; |
| |
| if (gFields.exceptionCtors.count(std::string(className)) == 0) { |
| jniThrowException(env, className, msg); |
| } else { |
| jstring _detailMessage = CreateExceptionMsg(env, msg); |
| jobject exception = env->NewObject(gFields.exceptionCtors[std::string(className)].classId, |
| gFields.exceptionCtors[std::string(className)].init, _detailMessage, |
| err.getCdmErr(), err.getOemErr(), err.getContext()); |
| env->Throw(static_cast<jthrowable>(exception)); |
| if (_detailMessage != NULL) { |
| env->DeleteLocalRef(_detailMessage); |
| } |
| } |
| } |
| } // namespace anonymous |
| |
| // ---------------------------------------------------------------------------- |
| // ref-counted object for callbacks |
| class JNIDrmListener: public DrmListener |
| { |
| public: |
| JNIDrmListener(JNIEnv* env, jobject thiz, jobject weak_thiz); |
| ~JNIDrmListener(); |
| virtual void notify(DrmPlugin::EventType eventType, int extra, const ListenerArgs *arg = NULL); |
| private: |
| JNIDrmListener(); |
| jclass mClass; // Reference to MediaDrm class |
| jobject mObject; // Weak ref to MediaDrm Java object to call on |
| }; |
| |
| JNIDrmListener::JNIDrmListener(JNIEnv* env, jobject thiz, jobject weak_thiz) |
| { |
| // Hold onto the MediaDrm class for use in calling the static method |
| // that posts events to the application thread. |
| jclass clazz = env->GetObjectClass(thiz); |
| if (clazz == NULL) { |
| ALOGE("Can't find android/media/MediaDrm"); |
| jniThrowException(env, "java/lang/Exception", |
| "Can't find android/media/MediaDrm"); |
| return; |
| } |
| mClass = (jclass)env->NewGlobalRef(clazz); |
| |
| // We use a weak reference so the MediaDrm object can be garbage collected. |
| // The reference is only used as a proxy for callbacks. |
| mObject = env->NewGlobalRef(weak_thiz); |
| } |
| |
| JNIDrmListener::~JNIDrmListener() |
| { |
| // remove global references |
| JNIEnv *env = AndroidRuntime::getJNIEnv(); |
| env->DeleteGlobalRef(mObject); |
| env->DeleteGlobalRef(mClass); |
| } |
| |
| void JNIDrmListener::notify(DrmPlugin::EventType eventType, int extra, |
| const ListenerArgs *args) |
| { |
| jint jwhat; |
| jint jeventType = 0; |
| |
| // translate DrmPlugin event types into their java equivalents |
| switch (eventType) { |
| case DrmPlugin::kDrmPluginEventProvisionRequired: |
| jwhat = gEventWhat.kWhatDrmEvent; |
| jeventType = gEventTypes.kEventProvisionRequired; |
| break; |
| case DrmPlugin::kDrmPluginEventKeyNeeded: |
| jwhat = gEventWhat.kWhatDrmEvent; |
| jeventType = gEventTypes.kEventKeyRequired; |
| break; |
| case DrmPlugin::kDrmPluginEventKeyExpired: |
| jwhat = gEventWhat.kWhatDrmEvent; |
| jeventType = gEventTypes.kEventKeyExpired; |
| break; |
| case DrmPlugin::kDrmPluginEventVendorDefined: |
| jwhat = gEventWhat.kWhatDrmEvent; |
| jeventType = gEventTypes.kEventVendorDefined; |
| break; |
| case DrmPlugin::kDrmPluginEventSessionReclaimed: |
| jwhat = gEventWhat.kWhatDrmEvent; |
| jeventType = gEventTypes.kEventSessionReclaimed; |
| break; |
| case DrmPlugin::kDrmPluginEventExpirationUpdate: |
| jwhat = gEventWhat.kWhatExpirationUpdate; |
| break; |
| case DrmPlugin::kDrmPluginEventKeysChange: |
| jwhat = gEventWhat.kWhatKeyStatusChange; |
| break; |
| case DrmPlugin::kDrmPluginEventSessionLostState: |
| jwhat = gEventWhat.kWhatSessionLostState; |
| break; |
| default: |
| ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType); |
| return; |
| } |
| |
| JNIEnv *env = AndroidRuntime::getJNIEnv(); |
| if (args) { |
| env->CallStaticVoidMethod(mClass, gFields.post_event, mObject, |
| jwhat, jeventType, extra, |
| args->jSessionId, args->jData, args->jExpirationTime, |
| args->jKeyStatusList, args->jHasNewUsableKey); |
| } |
| |
| if (env->ExceptionCheck()) { |
| ALOGW("An exception occurred while notifying an event."); |
| LOGW_EX(env); |
| env->ExceptionClear(); |
| } |
| } |
| |
| jint MediaErrorToJavaError(status_t err) { |
| #define STATUS_CASE(status) \ |
| case status: \ |
| return J##status |
| |
| switch (err) { |
| STATUS_CASE(ERROR_DRM_UNKNOWN); |
| STATUS_CASE(ERROR_DRM_NO_LICENSE); |
| STATUS_CASE(ERROR_DRM_LICENSE_EXPIRED); |
| STATUS_CASE(ERROR_DRM_RESOURCE_BUSY); |
| STATUS_CASE(ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION); |
| STATUS_CASE(ERROR_DRM_SESSION_NOT_OPENED); |
| STATUS_CASE(ERROR_DRM_CANNOT_HANDLE); |
| STATUS_CASE(ERROR_DRM_INSUFFICIENT_SECURITY); |
| STATUS_CASE(ERROR_DRM_FRAME_TOO_LARGE); |
| STATUS_CASE(ERROR_DRM_SESSION_LOST_STATE); |
| STATUS_CASE(ERROR_DRM_CERTIFICATE_MALFORMED); |
| STATUS_CASE(ERROR_DRM_CERTIFICATE_MISSING); |
| STATUS_CASE(ERROR_DRM_CRYPTO_LIBRARY); |
| STATUS_CASE(ERROR_DRM_GENERIC_OEM); |
| STATUS_CASE(ERROR_DRM_GENERIC_PLUGIN); |
| STATUS_CASE(ERROR_DRM_INIT_DATA); |
| STATUS_CASE(ERROR_DRM_KEY_NOT_LOADED); |
| STATUS_CASE(ERROR_DRM_LICENSE_PARSE); |
| STATUS_CASE(ERROR_DRM_LICENSE_POLICY); |
| STATUS_CASE(ERROR_DRM_LICENSE_RELEASE); |
| STATUS_CASE(ERROR_DRM_LICENSE_REQUEST_REJECTED); |
| STATUS_CASE(ERROR_DRM_LICENSE_RESTORE); |
| STATUS_CASE(ERROR_DRM_LICENSE_STATE); |
| STATUS_CASE(ERROR_DRM_MEDIA_FRAMEWORK); |
| STATUS_CASE(ERROR_DRM_PROVISIONING_CERTIFICATE); |
| STATUS_CASE(ERROR_DRM_PROVISIONING_CONFIG); |
| STATUS_CASE(ERROR_DRM_PROVISIONING_PARSE); |
| STATUS_CASE(ERROR_DRM_PROVISIONING_REQUEST_REJECTED); |
| STATUS_CASE(ERROR_DRM_PROVISIONING_RETRY); |
| STATUS_CASE(ERROR_DRM_RESOURCE_CONTENTION); |
| STATUS_CASE(ERROR_DRM_SECURE_STOP_RELEASE); |
| STATUS_CASE(ERROR_DRM_STORAGE_READ); |
| STATUS_CASE(ERROR_DRM_STORAGE_WRITE); |
| STATUS_CASE(ERROR_DRM_ZERO_SUBSAMPLES); |
| #undef STATUS_CASE |
| } |
| return static_cast<jint>(err); |
| } |
| |
| static void throwStateException(JNIEnv *env, const char *msg, const DrmStatus &err) { |
| ALOGE("Illegal state exception: %s (%d)", msg, static_cast<status_t>(err)); |
| |
| jint jerr = MediaErrorToJavaError(err); |
| jobject exception = env->NewObject(gFields.stateException.classId, |
| gFields.stateException.init, env->NewStringUTF(msg), static_cast<int>(jerr), |
| err.getCdmErr(), err.getOemErr(), err.getContext()); |
| env->Throw(static_cast<jthrowable>(exception)); |
| } |
| |
| static void throwSessionException(JNIEnv *env, const char *msg, const DrmStatus &err) { |
| ALOGE("Session exception: %s (%d)", msg, static_cast<status_t>(err)); |
| |
| jint jErrorCode = 0; |
| switch(err) { |
| case ERROR_DRM_RESOURCE_CONTENTION: |
| jErrorCode = gSessionExceptionErrorCodes.kResourceContention; |
| break; |
| default: |
| break; |
| } |
| |
| jobject exception = env->NewObject(gFields.sessionException.classId, |
| gFields.sessionException.init, |
| env->NewStringUTF(msg), |
| jErrorCode, |
| err.getCdmErr(), |
| err.getOemErr(), |
| err.getContext()); |
| |
| env->Throw(static_cast<jthrowable>(exception)); |
| } |
| |
| static bool isSessionException(status_t err) { |
| return err == ERROR_DRM_RESOURCE_CONTENTION; |
| } |
| |
| static bool throwExceptionAsNecessary( |
| JNIEnv *env, const sp<IDrm> &drm, const DrmStatus &err, const char *msg = NULL) { |
| std::string msgStr; |
| if (drm != NULL && err != OK) { |
| msgStr = DrmUtils::GetExceptionMessage(err, msg, drm); |
| msg = msgStr.c_str(); |
| } |
| |
| if (err == BAD_VALUE || err == ERROR_DRM_CANNOT_HANDLE) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", msg); |
| return true; |
| } else if (err == ERROR_UNSUPPORTED) { |
| jniThrowException(env, "java/lang/UnsupportedOperationException", msg); |
| return true; |
| } else if (err == ERROR_DRM_NOT_PROVISIONED) { |
| drmThrowException(env, "android/media/NotProvisionedException", err, msg); |
| return true; |
| } else if (err == ERROR_DRM_RESOURCE_BUSY) { |
| drmThrowException(env, "android/media/ResourceBusyException", err, msg); |
| return true; |
| } else if (err == ERROR_DRM_DEVICE_REVOKED) { |
| drmThrowException(env, "android/media/DeniedByServerException", err, msg); |
| return true; |
| } else if (err == DEAD_OBJECT) { |
| jniThrowException(env, "android/media/MediaDrmResetException", msg); |
| return true; |
| } else if (isSessionException(err)) { |
| throwSessionException(env, msg, err); |
| return true; |
| } else if (err != OK) { |
| throwStateException(env, msg, err); |
| return true; |
| } |
| return false; |
| } |
| |
| static sp<IDrm> GetDrm(JNIEnv *env, jobject thiz) { |
| JDrm *jdrm = (JDrm *)env->GetLongField(thiz, gFields.context); |
| return jdrm ? jdrm->getDrm() : NULL; |
| } |
| |
| JDrm::JDrm( |
| JNIEnv *env, jobject thiz, const uint8_t uuid[16], |
| const String8 &appPackageName) { |
| mObject = env->NewWeakGlobalRef(thiz); |
| mDrm = MakeDrm(uuid, appPackageName); |
| if (mDrm != NULL) { |
| mDrm->setListener(this); |
| } |
| } |
| |
| JDrm::~JDrm() { |
| JNIEnv *env = AndroidRuntime::getJNIEnv(); |
| |
| env->DeleteWeakGlobalRef(mObject); |
| mObject = NULL; |
| } |
| |
| // static |
| sp<IDrm> JDrm::MakeDrm() { |
| return DrmUtils::MakeDrm(); |
| } |
| |
| // static |
| sp<IDrm> JDrm::MakeDrm(const uint8_t uuid[16], const String8 &appPackageName) { |
| sp<IDrm> drm = MakeDrm(); |
| |
| if (drm == NULL) { |
| return NULL; |
| } |
| |
| DrmStatus err = drm->createPlugin(uuid, appPackageName); |
| |
| if (err != OK) { |
| return NULL; |
| } |
| |
| return drm; |
| } |
| |
| status_t JDrm::setListener(const sp<DrmListener>& listener) { |
| Mutex::Autolock lock(mLock); |
| mListener = listener; |
| return OK; |
| } |
| |
| void JDrm::notify(DrmPlugin::EventType eventType, int extra, const ListenerArgs *args) { |
| sp<DrmListener> listener; |
| mLock.lock(); |
| listener = mListener; |
| mLock.unlock(); |
| |
| if (listener != NULL) { |
| Mutex::Autolock lock(mNotifyLock); |
| listener->notify(eventType, extra, args); |
| } |
| } |
| |
| void JDrm::sendEvent( |
| DrmPlugin::EventType eventType, |
| const hardware::hidl_vec<uint8_t> &sessionId, |
| const hardware::hidl_vec<uint8_t> &data) { |
| ListenerArgs args{ |
| .jSessionId = hidlVectorToJByteArray(sessionId), |
| .jData = hidlVectorToJByteArray(data), |
| }; |
| notify(eventType, 0, &args); |
| } |
| |
| void JDrm::sendExpirationUpdate( |
| const hardware::hidl_vec<uint8_t> &sessionId, |
| int64_t expiryTimeInMS) { |
| ListenerArgs args{ |
| .jSessionId = hidlVectorToJByteArray(sessionId), |
| .jExpirationTime = expiryTimeInMS, |
| }; |
| notify(DrmPlugin::kDrmPluginEventExpirationUpdate, 0, &args); |
| } |
| |
| void JDrm::sendKeysChange( |
| const hardware::hidl_vec<uint8_t> &sessionId, |
| const std::vector<DrmKeyStatus> &keyStatusList, |
| bool hasNewUsableKey) { |
| JNIEnv *env = AndroidRuntime::getJNIEnv(); |
| jclass clazz = gFields.arraylistClassId; |
| jobject arrayList = env->NewObject(clazz, gFields.arraylist.init); |
| clazz = gFields.keyStatus.classId; |
| for (const auto &keyStatus : keyStatusList) { |
| jbyteArray jKeyId(hidlVectorToJByteArray(keyStatus.keyId)); |
| jint jStatusCode(keyStatus.type); |
| jobject jKeyStatus = env->NewObject(clazz, gFields.keyStatus.init, jKeyId, jStatusCode); |
| env->CallBooleanMethod(arrayList, gFields.arraylist.add, jKeyStatus); |
| } |
| ListenerArgs args{ |
| .jSessionId = hidlVectorToJByteArray(sessionId), |
| .jKeyStatusList = arrayList, |
| .jHasNewUsableKey = hasNewUsableKey, |
| }; |
| notify(DrmPlugin::kDrmPluginEventKeysChange, 0, &args); |
| } |
| |
| void JDrm::sendSessionLostState( |
| const hardware::hidl_vec<uint8_t> &sessionId) { |
| ListenerArgs args{ |
| .jSessionId = hidlVectorToJByteArray(sessionId), |
| }; |
| notify(DrmPlugin::kDrmPluginEventSessionLostState, 0, &args); |
| } |
| |
| void JDrm::disconnect() { |
| if (mDrm != NULL) { |
| mDrm->destroyPlugin(); |
| mDrm.clear(); |
| } |
| } |
| |
| |
| // static |
| status_t JDrm::IsCryptoSchemeSupported(const uint8_t uuid[16], const String8 &mimeType, |
| DrmPlugin::SecurityLevel securityLevel, bool *isSupported) { |
| sp<IDrm> drm = MakeDrm(); |
| |
| if (drm == NULL) { |
| return BAD_VALUE; |
| } |
| |
| return drm->isCryptoSchemeSupported(uuid, mimeType, securityLevel, isSupported); |
| } |
| |
| status_t JDrm::initCheck() const { |
| return mDrm == NULL ? NO_INIT : OK; |
| } |
| |
| // JNI conversion utilities |
| static Vector<uint8_t> JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArray) { |
| Vector<uint8_t> vector; |
| size_t length = env->GetArrayLength(byteArray); |
| vector.insertAt((size_t)0, length); |
| env->GetByteArrayRegion(byteArray, 0, length, (jbyte *)vector.editArray()); |
| return vector; |
| } |
| |
| static jbyteArray VectorToJByteArray(JNIEnv *env, Vector<uint8_t> const &vector) { |
| size_t length = vector.size(); |
| jbyteArray result = env->NewByteArray(length); |
| if (result != NULL) { |
| env->SetByteArrayRegion(result, 0, length, (jbyte *)vector.array()); |
| } |
| return result; |
| } |
| |
| static String8 JStringToString8(JNIEnv *env, jstring const &jstr) { |
| String8 result; |
| |
| const char *s = env->GetStringUTFChars(jstr, NULL); |
| if (s) { |
| result = s; |
| env->ReleaseStringUTFChars(jstr, s); |
| } |
| return result; |
| } |
| |
| /* |
| import java.util.HashMap; |
| import java.util.Set; |
| import java.Map.Entry; |
| import jav.util.Iterator; |
| |
| HashMap<k, v> hm; |
| Set<Entry<k, v>> s = hm.entrySet(); |
| Iterator i = s.iterator(); |
| Entry e = s.next(); |
| */ |
| |
| static KeyedVector<String8, String8> HashMapToKeyedVector( |
| JNIEnv *env, jobject &hashMap, bool* pIsOK) { |
| jclass clazz = gFields.stringClassId; |
| KeyedVector<String8, String8> keyedVector; |
| *pIsOK = true; |
| |
| jobject entrySet = env->CallObjectMethod(hashMap, gFields.hashmap.entrySet); |
| if (entrySet) { |
| jobject iterator = env->CallObjectMethod(entrySet, gFields.set.iterator); |
| if (iterator) { |
| jboolean hasNext = env->CallBooleanMethod(iterator, gFields.iterator.hasNext); |
| while (hasNext) { |
| jobject entry = env->CallObjectMethod(iterator, gFields.iterator.next); |
| if (entry) { |
| jobject obj = env->CallObjectMethod(entry, gFields.entry.getKey); |
| if (obj == NULL || !env->IsInstanceOf(obj, clazz)) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "HashMap key is not a String"); |
| env->DeleteLocalRef(entry); |
| *pIsOK = false; |
| break; |
| } |
| jstring jkey = static_cast<jstring>(obj); |
| |
| obj = env->CallObjectMethod(entry, gFields.entry.getValue); |
| if (obj == NULL || !env->IsInstanceOf(obj, clazz)) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "HashMap value is not a String"); |
| env->DeleteLocalRef(entry); |
| *pIsOK = false; |
| break; |
| } |
| jstring jvalue = static_cast<jstring>(obj); |
| |
| String8 key = JStringToString8(env, jkey); |
| String8 value = JStringToString8(env, jvalue); |
| keyedVector.add(key, value); |
| |
| env->DeleteLocalRef(jkey); |
| env->DeleteLocalRef(jvalue); |
| hasNext = env->CallBooleanMethod(iterator, gFields.iterator.hasNext); |
| } |
| env->DeleteLocalRef(entry); |
| } |
| env->DeleteLocalRef(iterator); |
| } |
| env->DeleteLocalRef(entrySet); |
| } |
| return keyedVector; |
| } |
| |
| static jobject KeyedVectorToHashMap (JNIEnv *env, KeyedVector<String8, String8> const &map) { |
| jclass clazz = gFields.hashmapClassId; |
| jobject hashMap = env->NewObject(clazz, gFields.hashmap.init); |
| for (size_t i = 0; i < map.size(); ++i) { |
| jstring jkey = env->NewStringUTF(map.keyAt(i).c_str()); |
| jstring jvalue = env->NewStringUTF(map.valueAt(i).c_str()); |
| env->CallObjectMethod(hashMap, gFields.hashmap.put, jkey, jvalue); |
| env->DeleteLocalRef(jkey); |
| env->DeleteLocalRef(jvalue); |
| } |
| return hashMap; |
| } |
| |
| static jobject ListOfVectorsToArrayListOfByteArray(JNIEnv *env, |
| List<Vector<uint8_t>> list) { |
| jclass clazz = gFields.arraylistClassId; |
| jobject arrayList = env->NewObject(clazz, gFields.arraylist.init); |
| List<Vector<uint8_t>>::iterator iter = list.begin(); |
| while (iter != list.end()) { |
| jbyteArray byteArray = VectorToJByteArray(env, *iter); |
| env->CallBooleanMethod(arrayList, gFields.arraylist.add, byteArray); |
| env->DeleteLocalRef(byteArray); |
| iter++; |
| } |
| |
| return arrayList; |
| } |
| |
| } // namespace android |
| |
| using namespace android; |
| |
| static sp<JDrm> setDrm( |
| JNIEnv *env, jobject thiz, const sp<JDrm> &drm) { |
| sp<JDrm> old = (JDrm *)env->GetLongField(thiz, gFields.context); |
| if (drm != NULL) { |
| drm->incStrong(thiz); |
| } |
| if (old != NULL) { |
| old->decStrong(thiz); |
| } |
| env->SetLongField(thiz, gFields.context, reinterpret_cast<jlong>(drm.get())); |
| |
| return old; |
| } |
| |
| static bool CheckDrm(JNIEnv *env, const sp<IDrm> &drm) { |
| if (drm == NULL) { |
| jniThrowException(env, "java/lang/IllegalStateException", "MediaDrm obj is null"); |
| return false; |
| } |
| return true; |
| } |
| |
| static bool CheckSession(JNIEnv *env, const sp<IDrm> &drm, jbyteArray const &jsessionId) |
| { |
| if (!CheckDrm(env, drm)) { |
| return false; |
| } |
| |
| if (jsessionId == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", "sessionId is null"); |
| return false; |
| } |
| return true; |
| } |
| |
| static void android_media_MediaDrm_native_release(JNIEnv *env, jobject thiz) { |
| sp<JDrm> drm = setDrm(env, thiz, NULL); |
| if (drm != NULL) { |
| drm->setListener(NULL); |
| drm->disconnect(); |
| } |
| } |
| |
| static void android_media_MediaDrm_native_init(JNIEnv *env) { |
| jclass clazz; |
| FIND_CLASS(clazz, "android/media/MediaDrm"); |
| GET_FIELD_ID(gFields.context, clazz, "mNativeContext", "J"); |
| GET_STATIC_METHOD_ID(gFields.post_event, clazz, "postEventFromNative", |
| "(Ljava/lang/Object;III[B[BJLjava/util/List;Z)V"); |
| |
| jfieldID field; |
| GET_STATIC_FIELD_ID(field, clazz, "EVENT_PROVISION_REQUIRED", "I"); |
| gEventTypes.kEventProvisionRequired = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_REQUIRED", "I"); |
| gEventTypes.kEventKeyRequired = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "EVENT_KEY_EXPIRED", "I"); |
| gEventTypes.kEventKeyExpired = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "EVENT_VENDOR_DEFINED", "I"); |
| gEventTypes.kEventVendorDefined = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "EVENT_SESSION_RECLAIMED", "I"); |
| gEventTypes.kEventSessionReclaimed = env->GetStaticIntField(clazz, field); |
| |
| GET_STATIC_FIELD_ID(field, clazz, "DRM_EVENT", "I"); |
| gEventWhat.kWhatDrmEvent = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "EXPIRATION_UPDATE", "I"); |
| gEventWhat.kWhatExpirationUpdate = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "KEY_STATUS_CHANGE", "I"); |
| gEventWhat.kWhatKeyStatusChange = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "SESSION_LOST_STATE", "I"); |
| gEventWhat.kWhatSessionLostState = env->GetStaticIntField(clazz, field); |
| |
| GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_STREAMING", "I"); |
| gKeyTypes.kKeyTypeStreaming = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_OFFLINE", "I"); |
| gKeyTypes.kKeyTypeOffline = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_RELEASE", "I"); |
| gKeyTypes.kKeyTypeRelease = env->GetStaticIntField(clazz, field); |
| |
| GET_STATIC_FIELD_ID(field, clazz, "CERTIFICATE_TYPE_NONE", "I"); |
| gCertificateTypes.kCertificateTypeNone = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "CERTIFICATE_TYPE_X509", "I"); |
| gCertificateTypes.kCertificateTypeX509 = env->GetStaticIntField(clazz, field); |
| |
| GET_STATIC_FIELD_ID(field, clazz, "HDCP_LEVEL_UNKNOWN", "I"); |
| gHdcpLevels.kHdcpLevelUnknown = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "HDCP_NONE", "I"); |
| gHdcpLevels.kHdcpNone = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "HDCP_V1", "I"); |
| gHdcpLevels.kHdcpV1 = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "HDCP_V2", "I"); |
| gHdcpLevels.kHdcpV2 = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "HDCP_V2_1", "I"); |
| gHdcpLevels.kHdcpV2_1 = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "HDCP_V2_2", "I"); |
| gHdcpLevels.kHdcpV2_2 = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "HDCP_V2_3", "I"); |
| gHdcpLevels.kHdcpV2_3 = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "HDCP_NO_DIGITAL_OUTPUT", "I"); |
| gHdcpLevels.kHdcpNoOutput = env->GetStaticIntField(clazz, field); |
| |
| GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_UNKNOWN", "I"); |
| gSecurityLevels.kSecurityLevelUnknown = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_SW_SECURE_CRYPTO", "I"); |
| gSecurityLevels.kSecurityLevelSwSecureCrypto = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_SW_SECURE_DECODE", "I"); |
| gSecurityLevels.kSecurityLevelSwSecureDecode = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_CRYPTO", "I"); |
| gSecurityLevels.kSecurityLevelHwSecureCrypto = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_DECODE", "I"); |
| gSecurityLevels.kSecurityLevelHwSecureDecode = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_ALL", "I"); |
| gSecurityLevels.kSecurityLevelHwSecureAll = env->GetStaticIntField(clazz, field); |
| |
| GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_STATE_USABLE", "I"); |
| gOfflineLicenseStates.kOfflineLicenseStateUsable = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_STATE_RELEASED", "I"); |
| gOfflineLicenseStates.kOfflineLicenseStateReleased = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_STATE_UNKNOWN", "I"); |
| gOfflineLicenseStates.kOfflineLicenseStateUnknown = env->GetStaticIntField(clazz, field); |
| |
| GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_CRYPTO", "I"); |
| |
| jmethodID getMaxSecurityLevel; |
| GET_STATIC_METHOD_ID(getMaxSecurityLevel, clazz, "getMaxSecurityLevel", "()I"); |
| gSecurityLevels.kSecurityLevelMax = env->CallStaticIntMethod(clazz, getMaxSecurityLevel); |
| |
| FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest"); |
| GET_FIELD_ID(gFields.keyRequest.data, clazz, "mData", "[B"); |
| GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;"); |
| GET_FIELD_ID(gFields.keyRequest.requestType, clazz, "mRequestType", "I"); |
| |
| GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_INITIAL", "I"); |
| gKeyRequestTypes.kKeyRequestTypeInitial = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RENEWAL", "I"); |
| gKeyRequestTypes.kKeyRequestTypeRenewal = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RELEASE", "I"); |
| gKeyRequestTypes.kKeyRequestTypeRelease = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_NONE", "I"); |
| gKeyRequestTypes.kKeyRequestTypeNone = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_UPDATE", "I"); |
| gKeyRequestTypes.kKeyRequestTypeUpdate = env->GetStaticIntField(clazz, field); |
| |
| FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest"); |
| GET_FIELD_ID(gFields.provisionRequest.data, clazz, "mData", "[B"); |
| GET_FIELD_ID(gFields.provisionRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;"); |
| |
| FIND_CLASS(clazz, "android/media/MediaDrm$Certificate"); |
| GET_FIELD_ID(gFields.certificate.wrappedPrivateKey, clazz, "mWrappedKey", "[B"); |
| GET_FIELD_ID(gFields.certificate.certificateData, clazz, "mCertificateData", "[B"); |
| gFields.certificateClassId = static_cast<jclass>(env->NewGlobalRef(clazz)); |
| |
| // Metrics-related fields and classes. |
| FIND_CLASS(clazz, "android/os/PersistableBundle"); |
| jfieldID bundleCreatorId; |
| GET_STATIC_FIELD_ID(bundleCreatorId, clazz, "CREATOR", |
| "Landroid/os/Parcelable$Creator;"); |
| jobject bundleCreator; |
| GET_STATIC_OBJECT_FIELD(bundleCreator, clazz, bundleCreatorId); |
| gFields.bundleCreator = static_cast<jobject>(env->NewGlobalRef(bundleCreator)); |
| FIND_CLASS(clazz, "android/os/Parcelable$Creator"); |
| GET_METHOD_ID(gFields.createFromParcelId, clazz, "createFromParcel", |
| "(Landroid/os/Parcel;)Ljava/lang/Object;"); |
| gFields.parcelCreatorClassId = static_cast<jclass>(env->NewGlobalRef(clazz)); |
| |
| FIND_CLASS(clazz, "java/util/ArrayList"); |
| GET_METHOD_ID(gFields.arraylist.init, clazz, "<init>", "()V"); |
| GET_METHOD_ID(gFields.arraylist.add, clazz, "add", "(Ljava/lang/Object;)Z"); |
| |
| FIND_CLASS(clazz, "java/util/HashMap"); |
| GET_METHOD_ID(gFields.hashmap.init, clazz, "<init>", "()V"); |
| GET_METHOD_ID(gFields.hashmap.get, clazz, "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); |
| GET_METHOD_ID(gFields.hashmap.put, clazz, "put", |
| "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); |
| GET_METHOD_ID(gFields.hashmap.entrySet, clazz, "entrySet", "()Ljava/util/Set;"); |
| |
| FIND_CLASS(clazz, "java/util/Set"); |
| GET_METHOD_ID(gFields.set.iterator, clazz, "iterator", "()Ljava/util/Iterator;"); |
| |
| FIND_CLASS(clazz, "java/util/Iterator"); |
| GET_METHOD_ID(gFields.iterator.next, clazz, "next", "()Ljava/lang/Object;"); |
| GET_METHOD_ID(gFields.iterator.hasNext, clazz, "hasNext", "()Z"); |
| |
| FIND_CLASS(clazz, "java/util/Map$Entry"); |
| GET_METHOD_ID(gFields.entry.getKey, clazz, "getKey", "()Ljava/lang/Object;"); |
| GET_METHOD_ID(gFields.entry.getValue, clazz, "getValue", "()Ljava/lang/Object;"); |
| |
| FIND_CLASS(clazz, "java/util/HashMap"); |
| gFields.hashmapClassId = static_cast<jclass>(env->NewGlobalRef(clazz)); |
| |
| FIND_CLASS(clazz, "java/lang/String"); |
| gFields.stringClassId = static_cast<jclass>(env->NewGlobalRef(clazz)); |
| |
| FIND_CLASS(clazz, "java/util/ArrayList"); |
| gFields.arraylistClassId = static_cast<jclass>(env->NewGlobalRef(clazz)); |
| |
| FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException"); |
| GET_METHOD_ID(gFields.stateException.init, clazz, "<init>", "(Ljava/lang/String;IIII)V"); |
| gFields.stateException.classId = static_cast<jclass>(env->NewGlobalRef(clazz)); |
| |
| FIND_CLASS(clazz, "android/media/MediaDrm$SessionException"); |
| GET_METHOD_ID(gFields.sessionException.init, clazz, "<init>", "(Ljava/lang/String;IIII)V"); |
| gFields.sessionException.classId = static_cast<jclass>(env->NewGlobalRef(clazz)); |
| GET_FIELD_ID(gFields.sessionException.errorCode, clazz, "mErrorCode", "I"); |
| |
| GET_STATIC_FIELD_ID(field, clazz, "ERROR_UNKNOWN", "I"); |
| gSessionExceptionErrorCodes.kErrorUnknown = env->GetStaticIntField(clazz, field); |
| GET_STATIC_FIELD_ID(field, clazz, "ERROR_RESOURCE_CONTENTION", "I"); |
| gSessionExceptionErrorCodes.kResourceContention = env->GetStaticIntField(clazz, field); |
| |
| FIND_CLASS(clazz, "android/media/MediaDrm$KeyStatus"); |
| gFields.keyStatus.classId = static_cast<jclass>(env->NewGlobalRef(clazz)); |
| GET_METHOD_ID(gFields.keyStatus.init, clazz, "<init>", "([BI)V"); |
| |
| FIND_CLASS(clazz, "android/media/MediaDrm$LogMessage"); |
| gFields.logMessage.classId = static_cast<jclass>(env->NewGlobalRef(clazz)); |
| GET_METHOD_ID(gFields.logMessage.init, clazz, "<init>", "(JILjava/lang/String;)V"); |
| |
| resolveDrmExceptionCtor(env, "android/media/NotProvisionedException"); |
| resolveDrmExceptionCtor(env, "android/media/ResourceBusyException"); |
| resolveDrmExceptionCtor(env, "android/media/DeniedByServerException"); |
| } |
| |
| static void android_media_MediaDrm_native_setup( |
| JNIEnv *env, jobject thiz, |
| jobject weak_this, jbyteArray uuidObj, jstring jappPackageName) { |
| |
| if (uuidObj == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", "uuid is null"); |
| return; |
| } |
| |
| Vector<uint8_t> uuid = JByteArrayToVector(env, uuidObj); |
| |
| if (uuid.size() != 16) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "invalid UUID size, expected 16 bytes"); |
| return; |
| } |
| |
| String8 packageName; |
| if (jappPackageName == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "application package name cannot be null"); |
| return; |
| } |
| |
| packageName = JStringToString8(env, jappPackageName); |
| sp<JDrm> drm = new JDrm(env, thiz, uuid.array(), packageName); |
| |
| status_t err = drm->initCheck(); |
| |
| if (err != OK) { |
| auto logs(DrmUtils::gLogBuf.getLogs()); |
| auto msg(DrmUtils::GetExceptionMessage(err, "Failed to instantiate drm object", logs)); |
| jniThrowException( |
| env, |
| "android/media/UnsupportedSchemeException", |
| msg.c_str()); |
| return; |
| } |
| |
| sp<JNIDrmListener> listener = new JNIDrmListener(env, thiz, weak_this); |
| drm->setListener(listener); |
| setDrm(env, thiz, drm); |
| } |
| |
| DrmPlugin::SecurityLevel jintToSecurityLevel(jint jlevel) { |
| DrmPlugin::SecurityLevel level; |
| |
| if (jlevel == gSecurityLevels.kSecurityLevelMax) { |
| level = DrmPlugin::kSecurityLevelMax; |
| } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureCrypto) { |
| level = DrmPlugin::kSecurityLevelSwSecureCrypto; |
| } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureDecode) { |
| level = DrmPlugin::kSecurityLevelSwSecureDecode; |
| } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureCrypto) { |
| level = DrmPlugin::kSecurityLevelHwSecureCrypto; |
| } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureDecode) { |
| level = DrmPlugin::kSecurityLevelHwSecureDecode; |
| } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureAll) { |
| level = DrmPlugin::kSecurityLevelHwSecureAll; |
| } else { |
| level = DrmPlugin::kSecurityLevelUnknown; |
| } |
| return level; |
| } |
| |
| static jbyteArray android_media_MediaDrm_getSupportedCryptoSchemesNative(JNIEnv *env) { |
| sp<IDrm> drm = android::DrmUtils::MakeDrm(); |
| if (drm == NULL) return env->NewByteArray(0); |
| |
| std::vector<uint8_t> bv; |
| drm->getSupportedSchemes(bv); |
| jbyteArray jUuidBytes = env->NewByteArray(bv.size()); |
| env->SetByteArrayRegion(jUuidBytes, 0, bv.size(), reinterpret_cast<const jbyte *>(bv.data())); |
| return jUuidBytes; |
| } |
| |
| static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative( |
| JNIEnv *env, jobject /* thiz */, jbyteArray uuidObj, jstring jmimeType, |
| jint jSecurityLevel) { |
| |
| if (uuidObj == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", NULL); |
| return false; |
| } |
| |
| Vector<uint8_t> uuid = JByteArrayToVector(env, uuidObj); |
| |
| if (uuid.size() != 16) { |
| jniThrowException( |
| env, |
| "java/lang/IllegalArgumentException", |
| "invalid UUID size, expected 16 bytes"); |
| return false; |
| } |
| |
| String8 mimeType; |
| if (jmimeType != NULL) { |
| mimeType = JStringToString8(env, jmimeType); |
| } |
| DrmPlugin::SecurityLevel securityLevel = jintToSecurityLevel(jSecurityLevel); |
| |
| bool isSupported; |
| status_t err = JDrm::IsCryptoSchemeSupported(uuid.array(), mimeType, |
| securityLevel, &isSupported); |
| |
| if (throwExceptionAsNecessary(env, NULL, err, "Failed to query crypto scheme support")) { |
| return false; |
| } |
| return isSupported; |
| } |
| |
| static jbyteArray android_media_MediaDrm_openSession( |
| JNIEnv *env, jobject thiz, jint jlevel) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return NULL; |
| } |
| |
| Vector<uint8_t> sessionId; |
| DrmPlugin::SecurityLevel level = jintToSecurityLevel(jlevel); |
| if (level == DrmPlugin::kSecurityLevelUnknown) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid security level"); |
| return NULL; |
| } |
| |
| DrmStatus err = drm->openSession(level, sessionId); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to open session")) { |
| return NULL; |
| } |
| |
| return VectorToJByteArray(env, sessionId); |
| } |
| |
| static void android_media_MediaDrm_closeSession( |
| JNIEnv *env, jobject thiz, jbyteArray jsessionId) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckSession(env, drm, jsessionId)) { |
| return; |
| } |
| |
| Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); |
| |
| DrmStatus err = drm->closeSession(sessionId); |
| |
| throwExceptionAsNecessary(env, drm, err, "Failed to close session"); |
| } |
| |
| static jobject android_media_MediaDrm_getKeyRequest( |
| JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jinitData, |
| jstring jmimeType, jint jkeyType, jobject joptParams) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckSession(env, drm, jsessionId)) { |
| return NULL; |
| } |
| |
| Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); |
| |
| Vector<uint8_t> initData; |
| if (jinitData != NULL) { |
| initData = JByteArrayToVector(env, jinitData); |
| } |
| |
| String8 mimeType; |
| if (jmimeType != NULL) { |
| mimeType = JStringToString8(env, jmimeType); |
| } |
| |
| DrmPlugin::KeyType keyType; |
| if (jkeyType == gKeyTypes.kKeyTypeStreaming) { |
| keyType = DrmPlugin::kKeyType_Streaming; |
| } else if (jkeyType == gKeyTypes.kKeyTypeOffline) { |
| keyType = DrmPlugin::kKeyType_Offline; |
| } else if (jkeyType == gKeyTypes.kKeyTypeRelease) { |
| keyType = DrmPlugin::kKeyType_Release; |
| } else { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "invalid keyType"); |
| return NULL; |
| } |
| |
| KeyedVector<String8, String8> optParams; |
| if (joptParams != NULL) { |
| bool isOK; |
| optParams = HashMapToKeyedVector(env, joptParams, &isOK); |
| if (!isOK) { |
| return NULL; |
| } |
| } |
| |
| Vector<uint8_t> request; |
| String8 defaultUrl; |
| DrmPlugin::KeyRequestType keyRequestType; |
| |
| DrmStatus err = drm->getKeyRequest(sessionId, initData, mimeType, keyType, optParams, request, |
| defaultUrl, &keyRequestType); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to get key request")) { |
| return NULL; |
| } |
| |
| // Fill out return obj |
| jclass clazz; |
| FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest"); |
| |
| jobject keyObj = NULL; |
| |
| if (clazz) { |
| keyObj = env->AllocObject(clazz); |
| jbyteArray jrequest = VectorToJByteArray(env, request); |
| env->SetObjectField(keyObj, gFields.keyRequest.data, jrequest); |
| |
| jstring jdefaultUrl = env->NewStringUTF(defaultUrl.c_str()); |
| env->SetObjectField(keyObj, gFields.keyRequest.defaultUrl, jdefaultUrl); |
| |
| switch (keyRequestType) { |
| case DrmPlugin::kKeyRequestType_Initial: |
| env->SetIntField(keyObj, gFields.keyRequest.requestType, |
| gKeyRequestTypes.kKeyRequestTypeInitial); |
| break; |
| case DrmPlugin::kKeyRequestType_Renewal: |
| env->SetIntField(keyObj, gFields.keyRequest.requestType, |
| gKeyRequestTypes.kKeyRequestTypeRenewal); |
| break; |
| case DrmPlugin::kKeyRequestType_Release: |
| env->SetIntField(keyObj, gFields.keyRequest.requestType, |
| gKeyRequestTypes.kKeyRequestTypeRelease); |
| break; |
| case DrmPlugin::kKeyRequestType_None: |
| env->SetIntField(keyObj, gFields.keyRequest.requestType, |
| gKeyRequestTypes.kKeyRequestTypeNone); |
| break; |
| case DrmPlugin::kKeyRequestType_Update: |
| env->SetIntField(keyObj, gFields.keyRequest.requestType, |
| gKeyRequestTypes.kKeyRequestTypeUpdate); |
| break; |
| |
| default: |
| throwStateException(env, "DRM plugin failure: unknown key request type", |
| ERROR_DRM_UNKNOWN); |
| break; |
| } |
| } |
| |
| return keyObj; |
| } |
| |
| static jbyteArray android_media_MediaDrm_provideKeyResponse( |
| JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jresponse) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckSession(env, drm, jsessionId)) { |
| return NULL; |
| } |
| |
| Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); |
| |
| if (jresponse == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "key response is null"); |
| return NULL; |
| } |
| Vector<uint8_t> response(JByteArrayToVector(env, jresponse)); |
| Vector<uint8_t> keySetId; |
| |
| DrmStatus err = drm->provideKeyResponse(sessionId, response, keySetId); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to handle key response")) { |
| return NULL; |
| } |
| return VectorToJByteArray(env, keySetId); |
| } |
| |
| static void android_media_MediaDrm_removeKeys( |
| JNIEnv *env, jobject thiz, jbyteArray jkeysetId) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return; |
| } |
| |
| if (jkeysetId == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "keySetId is null"); |
| return; |
| } |
| |
| Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId)); |
| |
| DrmStatus err = drm->removeKeys(keySetId); |
| |
| throwExceptionAsNecessary(env, drm, err, "Failed to remove keys"); |
| } |
| |
| static void android_media_MediaDrm_restoreKeys( |
| JNIEnv *env, jobject thiz, jbyteArray jsessionId, |
| jbyteArray jkeysetId) { |
| |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckSession(env, drm, jsessionId)) { |
| return; |
| } |
| |
| if (jkeysetId == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", NULL); |
| return; |
| } |
| |
| Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); |
| Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeysetId)); |
| |
| DrmStatus err = drm->restoreKeys(sessionId, keySetId); |
| |
| throwExceptionAsNecessary(env, drm, err, "Failed to restore keys"); |
| } |
| |
| static jobject android_media_MediaDrm_queryKeyStatus( |
| JNIEnv *env, jobject thiz, jbyteArray jsessionId) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckSession(env, drm, jsessionId)) { |
| return NULL; |
| } |
| Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); |
| |
| KeyedVector<String8, String8> infoMap; |
| |
| DrmStatus err = drm->queryKeyStatus(sessionId, infoMap); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to query key status")) { |
| return NULL; |
| } |
| |
| return KeyedVectorToHashMap(env, infoMap); |
| } |
| |
| static jobject android_media_MediaDrm_getProvisionRequestNative( |
| JNIEnv *env, jobject thiz, jint jcertType, jstring jcertAuthority) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return NULL; |
| } |
| |
| Vector<uint8_t> request; |
| String8 defaultUrl; |
| |
| String8 certType; |
| if (jcertType == gCertificateTypes.kCertificateTypeX509) { |
| certType = "X.509"; |
| } else if (jcertType == gCertificateTypes.kCertificateTypeNone) { |
| certType = "none"; |
| } else { |
| certType = "invalid"; |
| } |
| |
| String8 certAuthority = JStringToString8(env, jcertAuthority); |
| DrmStatus err = drm->getProvisionRequest(certType, certAuthority, request, defaultUrl); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to get provision request")) { |
| return NULL; |
| } |
| |
| // Fill out return obj |
| jclass clazz; |
| FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest"); |
| |
| jobject provisionObj = NULL; |
| |
| if (clazz) { |
| provisionObj = env->AllocObject(clazz); |
| jbyteArray jrequest = VectorToJByteArray(env, request); |
| env->SetObjectField(provisionObj, gFields.provisionRequest.data, jrequest); |
| |
| jstring jdefaultUrl = env->NewStringUTF(defaultUrl.c_str()); |
| env->SetObjectField(provisionObj, gFields.provisionRequest.defaultUrl, jdefaultUrl); |
| } |
| |
| return provisionObj; |
| } |
| |
| static jobject android_media_MediaDrm_provideProvisionResponseNative( |
| JNIEnv *env, jobject thiz, jbyteArray jresponse) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return NULL; |
| } |
| |
| if (jresponse == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "provision response is null"); |
| return NULL; |
| } |
| |
| Vector<uint8_t> response(JByteArrayToVector(env, jresponse)); |
| Vector<uint8_t> certificate, wrappedKey; |
| |
| DrmStatus err = drm->provideProvisionResponse(response, certificate, wrappedKey); |
| |
| // Fill out return obj |
| jclass clazz = gFields.certificateClassId; |
| |
| jobject certificateObj = NULL; |
| |
| if (clazz && certificate.size() && wrappedKey.size()) { |
| certificateObj = env->AllocObject(clazz); |
| jbyteArray jcertificate = VectorToJByteArray(env, certificate); |
| env->SetObjectField(certificateObj, gFields.certificate.certificateData, jcertificate); |
| |
| jbyteArray jwrappedKey = VectorToJByteArray(env, wrappedKey); |
| env->SetObjectField(certificateObj, gFields.certificate.wrappedPrivateKey, jwrappedKey); |
| } |
| |
| throwExceptionAsNecessary(env, drm, err, "Failed to handle provision response"); |
| return certificateObj; |
| } |
| |
| static jobject android_media_MediaDrm_getSecureStops( |
| JNIEnv *env, jobject thiz) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return NULL; |
| } |
| |
| List<Vector<uint8_t>> secureStops; |
| |
| DrmStatus err = drm->getSecureStops(secureStops); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to get secure stops")) { |
| return NULL; |
| } |
| |
| return ListOfVectorsToArrayListOfByteArray(env, secureStops); |
| } |
| |
| static jobject android_media_MediaDrm_getSecureStopIds( |
| JNIEnv *env, jobject thiz) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return NULL; |
| } |
| |
| List<Vector<uint8_t>> secureStopIds; |
| |
| DrmStatus err = drm->getSecureStopIds(secureStopIds); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to get secure stop Ids")) { |
| return NULL; |
| } |
| |
| return ListOfVectorsToArrayListOfByteArray(env, secureStopIds); |
| } |
| |
| static jbyteArray android_media_MediaDrm_getSecureStop( |
| JNIEnv *env, jobject thiz, jbyteArray ssid) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return NULL; |
| } |
| |
| Vector<uint8_t> secureStop; |
| |
| DrmStatus err = drm->getSecureStop(JByteArrayToVector(env, ssid), secureStop); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to get secure stop")) { |
| return NULL; |
| } |
| |
| return VectorToJByteArray(env, secureStop); |
| } |
| |
| static void android_media_MediaDrm_releaseSecureStops( |
| JNIEnv *env, jobject thiz, jbyteArray jssRelease) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return; |
| } |
| |
| Vector<uint8_t> ssRelease(JByteArrayToVector(env, jssRelease)); |
| |
| DrmStatus err = drm->releaseSecureStops(ssRelease); |
| |
| throwExceptionAsNecessary(env, drm, err, "Failed to release secure stops"); |
| } |
| |
| static void android_media_MediaDrm_removeSecureStop( |
| JNIEnv *env, jobject thiz, jbyteArray ssid) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return; |
| } |
| |
| DrmStatus err = drm->removeSecureStop(JByteArrayToVector(env, ssid)); |
| |
| throwExceptionAsNecessary(env, drm, err, "Failed to remove secure stop"); |
| } |
| |
| static void android_media_MediaDrm_removeAllSecureStops( |
| JNIEnv *env, jobject thiz) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return; |
| } |
| |
| DrmStatus err = drm->removeAllSecureStops(); |
| |
| throwExceptionAsNecessary(env, drm, err, "Failed to remove all secure stops"); |
| } |
| |
| |
| static jint HdcpLevelTojint(DrmPlugin::HdcpLevel level) { |
| switch(level) { |
| case DrmPlugin::kHdcpLevelUnknown: |
| return gHdcpLevels.kHdcpLevelUnknown; |
| case DrmPlugin::kHdcpNone: |
| return gHdcpLevels.kHdcpNone; |
| case DrmPlugin::kHdcpV1: |
| return gHdcpLevels.kHdcpV1; |
| case DrmPlugin::kHdcpV2: |
| return gHdcpLevels.kHdcpV2; |
| case DrmPlugin::kHdcpV2_1: |
| return gHdcpLevels.kHdcpV2_1; |
| case DrmPlugin::kHdcpV2_2: |
| return gHdcpLevels.kHdcpV2_2; |
| case DrmPlugin::kHdcpV2_3: |
| return gHdcpLevels.kHdcpV2_3; |
| case DrmPlugin::kHdcpNoOutput: |
| return gHdcpLevels.kHdcpNoOutput; |
| } |
| return gHdcpLevels.kHdcpNone; |
| } |
| |
| static jint android_media_MediaDrm_getConnectedHdcpLevel(JNIEnv *env, |
| jobject thiz) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return gHdcpLevels.kHdcpNone; |
| } |
| |
| DrmPlugin::HdcpLevel connected = DrmPlugin::kHdcpNone; |
| DrmPlugin::HdcpLevel max = DrmPlugin::kHdcpNone; |
| |
| DrmStatus err = drm->getHdcpLevels(&connected, &max); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to get HDCP levels")) { |
| return gHdcpLevels.kHdcpLevelUnknown; |
| } |
| return HdcpLevelTojint(connected); |
| } |
| |
| static jint android_media_MediaDrm_getMaxHdcpLevel(JNIEnv *env, |
| jobject thiz) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return gHdcpLevels.kHdcpLevelUnknown; |
| } |
| |
| DrmPlugin::HdcpLevel connected = DrmPlugin::kHdcpLevelUnknown; |
| DrmPlugin::HdcpLevel max = DrmPlugin::kHdcpLevelUnknown; |
| |
| DrmStatus err = drm->getHdcpLevels(&connected, &max); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to get HDCP levels")) { |
| return gHdcpLevels.kHdcpLevelUnknown; |
| } |
| return HdcpLevelTojint(max); |
| } |
| |
| static jint android_media_MediaDrm_getOpenSessionCount(JNIEnv *env, |
| jobject thiz) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return 0; |
| } |
| |
| uint32_t open = 0, max = 0; |
| DrmStatus err = drm->getNumberOfSessions(&open, &max); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to get number of sessions")) { |
| return 0; |
| } |
| return open; |
| } |
| |
| static jint android_media_MediaDrm_getMaxSessionCount(JNIEnv *env, |
| jobject thiz) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return 0; |
| } |
| |
| uint32_t open = 0, max = 0; |
| DrmStatus err = drm->getNumberOfSessions(&open, &max); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to get number of sessions")) { |
| return 0; |
| } |
| return max; |
| } |
| |
| static jint android_media_MediaDrm_getSecurityLevel(JNIEnv *env, |
| jobject thiz, jbyteArray jsessionId) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckSession(env, drm, jsessionId)) { |
| return gSecurityLevels.kSecurityLevelUnknown; |
| } |
| |
| Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); |
| |
| DrmPlugin::SecurityLevel level = DrmPlugin::kSecurityLevelUnknown; |
| |
| DrmStatus err = drm->getSecurityLevel(sessionId, &level); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to get security level")) { |
| return gSecurityLevels.kSecurityLevelUnknown; |
| } |
| |
| switch(level) { |
| case DrmPlugin::kSecurityLevelSwSecureCrypto: |
| return gSecurityLevels.kSecurityLevelSwSecureCrypto; |
| case DrmPlugin::kSecurityLevelSwSecureDecode: |
| return gSecurityLevels.kSecurityLevelSwSecureDecode; |
| case DrmPlugin::kSecurityLevelHwSecureCrypto: |
| return gSecurityLevels.kSecurityLevelHwSecureCrypto; |
| case DrmPlugin::kSecurityLevelHwSecureDecode: |
| return gSecurityLevels.kSecurityLevelHwSecureDecode; |
| case DrmPlugin::kSecurityLevelHwSecureAll: |
| return gSecurityLevels.kSecurityLevelHwSecureAll; |
| default: |
| return gSecurityLevels.kSecurityLevelUnknown; |
| } |
| } |
| |
| static jobject android_media_MediaDrm_getOfflineLicenseKeySetIds( |
| JNIEnv *env, jobject thiz) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return NULL; |
| } |
| |
| List<Vector<uint8_t> > keySetIds; |
| |
| DrmStatus err = drm->getOfflineLicenseKeySetIds(keySetIds); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to get offline key set Ids")) { |
| return NULL; |
| } |
| |
| return ListOfVectorsToArrayListOfByteArray(env, keySetIds); |
| } |
| |
| static void android_media_MediaDrm_removeOfflineLicense( |
| JNIEnv *env, jobject thiz, jbyteArray keySetId) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return; |
| } |
| |
| DrmStatus err = drm->removeOfflineLicense(JByteArrayToVector(env, keySetId)); |
| |
| throwExceptionAsNecessary(env, drm, err, "Failed to remove offline license"); |
| } |
| |
| static jint android_media_MediaDrm_getOfflineLicenseState(JNIEnv *env, |
| jobject thiz, jbyteArray jkeySetId) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return gOfflineLicenseStates.kOfflineLicenseStateUnknown; |
| } |
| |
| Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeySetId)); |
| |
| DrmPlugin::OfflineLicenseState state = DrmPlugin::kOfflineLicenseStateUnknown; |
| |
| DrmStatus err = drm->getOfflineLicenseState(keySetId, &state); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to get offline license state")) { |
| return gOfflineLicenseStates.kOfflineLicenseStateUnknown; |
| } |
| |
| switch(state) { |
| case DrmPlugin::kOfflineLicenseStateUsable: |
| return gOfflineLicenseStates.kOfflineLicenseStateUsable; |
| case DrmPlugin::kOfflineLicenseStateReleased: |
| return gOfflineLicenseStates.kOfflineLicenseStateReleased; |
| default: |
| return gOfflineLicenseStates.kOfflineLicenseStateUnknown; |
| } |
| } |
| |
| static jstring android_media_MediaDrm_getPropertyString( |
| JNIEnv *env, jobject thiz, jstring jname) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return NULL; |
| } |
| |
| if (jname == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "property name String is null"); |
| return NULL; |
| } |
| |
| String8 name = JStringToString8(env, jname); |
| String8 value; |
| |
| DrmStatus err = drm->getPropertyString(name, value); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to get property")) { |
| return NULL; |
| } |
| |
| return env->NewStringUTF(value.c_str()); |
| } |
| |
| static jbyteArray android_media_MediaDrm_getPropertyByteArray( |
| JNIEnv *env, jobject thiz, jstring jname) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return NULL; |
| } |
| |
| if (jname == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "property name String is null"); |
| return NULL; |
| } |
| |
| String8 name = JStringToString8(env, jname); |
| Vector<uint8_t> value; |
| |
| DrmStatus err = drm->getPropertyByteArray(name, value); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to get property")) { |
| return NULL; |
| } |
| |
| return VectorToJByteArray(env, value); |
| } |
| |
| static void android_media_MediaDrm_setPropertyString( |
| JNIEnv *env, jobject thiz, jstring jname, jstring jvalue) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return; |
| } |
| |
| if (jname == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "property name String is null"); |
| return; |
| } |
| |
| if (jvalue == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "property value String is null"); |
| return; |
| } |
| |
| String8 name = JStringToString8(env, jname); |
| String8 value = JStringToString8(env, jvalue); |
| |
| DrmStatus err = drm->setPropertyString(name, value); |
| |
| throwExceptionAsNecessary(env, drm, err, "Failed to set property"); |
| } |
| |
| static void android_media_MediaDrm_setPropertyByteArray( |
| JNIEnv *env, jobject thiz, jstring jname, jbyteArray jvalue) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return; |
| } |
| |
| if (jname == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "property name String is null"); |
| return; |
| } |
| |
| if (jvalue == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "property value byte array is null"); |
| return; |
| } |
| |
| String8 name = JStringToString8(env, jname); |
| Vector<uint8_t> value = JByteArrayToVector(env, jvalue); |
| |
| DrmStatus err = drm->setPropertyByteArray(name, value); |
| |
| throwExceptionAsNecessary(env, drm, err, "Failed to set property"); |
| } |
| |
| static void android_media_MediaDrm_setCipherAlgorithmNative( |
| JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId, |
| jstring jalgorithm) { |
| |
| sp<IDrm> drm = GetDrm(env, jdrm); |
| |
| if (!CheckSession(env, drm, jsessionId)) { |
| return; |
| } |
| |
| if (jalgorithm == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "algorithm String is null"); |
| return; |
| } |
| |
| Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); |
| String8 algorithm = JStringToString8(env, jalgorithm); |
| |
| DrmStatus err = drm->setCipherAlgorithm(sessionId, algorithm); |
| |
| throwExceptionAsNecessary(env, drm, err, "Failed to set cipher algorithm"); |
| } |
| |
| static void android_media_MediaDrm_setMacAlgorithmNative( |
| JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId, |
| jstring jalgorithm) { |
| |
| sp<IDrm> drm = GetDrm(env, jdrm); |
| |
| if (!CheckSession(env, drm, jsessionId)) { |
| return; |
| } |
| |
| if (jalgorithm == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "algorithm String is null"); |
| return; |
| } |
| |
| Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); |
| String8 algorithm = JStringToString8(env, jalgorithm); |
| |
| DrmStatus err = drm->setMacAlgorithm(sessionId, algorithm); |
| |
| throwExceptionAsNecessary(env, drm, err, "Failed to set mac algorithm"); |
| } |
| |
| |
| static jbyteArray android_media_MediaDrm_encryptNative( |
| JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId, |
| jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) { |
| |
| sp<IDrm> drm = GetDrm(env, jdrm); |
| |
| if (!CheckSession(env, drm, jsessionId)) { |
| return NULL; |
| } |
| |
| if (jkeyId == NULL || jinput == NULL || jiv == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "required argument is null"); |
| return NULL; |
| } |
| |
| Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); |
| Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); |
| Vector<uint8_t> input(JByteArrayToVector(env, jinput)); |
| Vector<uint8_t> iv(JByteArrayToVector(env, jiv)); |
| Vector<uint8_t> output; |
| |
| DrmStatus err = drm->encrypt(sessionId, keyId, input, iv, output); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to encrypt")) { |
| return NULL; |
| } |
| |
| return VectorToJByteArray(env, output); |
| } |
| |
| static jbyteArray android_media_MediaDrm_decryptNative( |
| JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId, |
| jbyteArray jkeyId, jbyteArray jinput, jbyteArray jiv) { |
| |
| sp<IDrm> drm = GetDrm(env, jdrm); |
| |
| if (!CheckSession(env, drm, jsessionId)) { |
| return NULL; |
| } |
| |
| if (jkeyId == NULL || jinput == NULL || jiv == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "required argument is null"); |
| return NULL; |
| } |
| |
| Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); |
| Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); |
| Vector<uint8_t> input(JByteArrayToVector(env, jinput)); |
| Vector<uint8_t> iv(JByteArrayToVector(env, jiv)); |
| Vector<uint8_t> output; |
| |
| DrmStatus err = drm->decrypt(sessionId, keyId, input, iv, output); |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to decrypt")) { |
| return NULL; |
| } |
| |
| return VectorToJByteArray(env, output); |
| } |
| |
| static jbyteArray android_media_MediaDrm_signNative( |
| JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId, |
| jbyteArray jkeyId, jbyteArray jmessage) { |
| |
| sp<IDrm> drm = GetDrm(env, jdrm); |
| |
| if (!CheckSession(env, drm, jsessionId)) { |
| return NULL; |
| } |
| |
| if (jkeyId == NULL || jmessage == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "required argument is null"); |
| return NULL; |
| } |
| |
| Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); |
| Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); |
| Vector<uint8_t> message(JByteArrayToVector(env, jmessage)); |
| Vector<uint8_t> signature; |
| |
| DrmStatus err = drm->sign(sessionId, keyId, message, signature); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to sign")) { |
| return NULL; |
| } |
| |
| return VectorToJByteArray(env, signature); |
| } |
| |
| static jboolean android_media_MediaDrm_verifyNative( |
| JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId, |
| jbyteArray jkeyId, jbyteArray jmessage, jbyteArray jsignature) { |
| |
| sp<IDrm> drm = GetDrm(env, jdrm); |
| |
| if (!CheckSession(env, drm, jsessionId)) { |
| return false; |
| } |
| |
| if (jkeyId == NULL || jmessage == NULL || jsignature == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "required argument is null"); |
| return false; |
| } |
| |
| Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); |
| Vector<uint8_t> keyId(JByteArrayToVector(env, jkeyId)); |
| Vector<uint8_t> message(JByteArrayToVector(env, jmessage)); |
| Vector<uint8_t> signature(JByteArrayToVector(env, jsignature)); |
| bool match; |
| |
| DrmStatus err = drm->verify(sessionId, keyId, message, signature, match); |
| |
| throwExceptionAsNecessary(env, drm, err, "Failed to verify"); |
| return match; |
| } |
| |
| static jobject |
| android_media_MediaDrm_native_getMetrics(JNIEnv *env, jobject thiz) |
| { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| |
| if (!CheckDrm(env, drm)) { |
| return NULL; |
| } |
| |
| // Retrieve current metrics snapshot from drm. |
| PersistableBundle metrics; |
| sp<IDrmMetricsConsumer> consumer(new DrmMetricsConsumer(&metrics)); |
| DrmStatus err = drm->getMetrics(consumer); |
| if (err != OK) { |
| ALOGE("getMetrics failed: %d", (int)err); |
| return (jobject) NULL; |
| } |
| |
| return MediaMetricsJNI::nativeToJavaPersistableBundle(env, &metrics); |
| } |
| |
| static jbyteArray android_media_MediaDrm_signRSANative( |
| JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId, |
| jstring jalgorithm, jbyteArray jwrappedKey, jbyteArray jmessage) { |
| |
| sp<IDrm> drm = GetDrm(env, jdrm); |
| |
| if (!CheckSession(env, drm, jsessionId)) { |
| return NULL; |
| } |
| |
| if (jalgorithm == NULL || jwrappedKey == NULL || jmessage == NULL) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", |
| "required argument is null"); |
| return NULL; |
| } |
| |
| Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); |
| String8 algorithm = JStringToString8(env, jalgorithm); |
| Vector<uint8_t> wrappedKey(JByteArrayToVector(env, jwrappedKey)); |
| Vector<uint8_t> message(JByteArrayToVector(env, jmessage)); |
| Vector<uint8_t> signature; |
| |
| DrmStatus err = drm->signRSA(sessionId, algorithm, message, wrappedKey, signature); |
| |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to sign")) { |
| return NULL; |
| } |
| |
| return VectorToJByteArray(env, signature); |
| } |
| |
| static jboolean android_media_MediaDrm_requiresSecureDecoder( |
| JNIEnv *env, jobject thiz, jstring jmimeType, |
| jint jSecurityLevel) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| if (!CheckDrm(env, drm)) { |
| return JNI_FALSE; |
| } |
| |
| String8 mimeType; |
| if (jmimeType != NULL) { |
| mimeType = JStringToString8(env, jmimeType); |
| } |
| |
| DrmPlugin::SecurityLevel securityLevel = jintToSecurityLevel(jSecurityLevel); |
| if (securityLevel == DrmPlugin::kSecurityLevelUnknown) { |
| jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid security level"); |
| return JNI_FALSE; |
| } |
| |
| bool required = false; |
| DrmStatus err = OK; |
| if (securityLevel == DrmPlugin::kSecurityLevelMax) { |
| err = drm->requiresSecureDecoder(mimeType.c_str(), &required); |
| } else { |
| err = drm->requiresSecureDecoder(mimeType.c_str(), securityLevel, &required); |
| } |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to query secure decoder requirement")) { |
| return false; |
| } |
| return required; |
| } |
| |
| static void android_media_MediaDrm_setPlaybackId( |
| JNIEnv *env, jobject thiz, jbyteArray jsessionId, |
| jstring jplaybackId) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| if (!CheckSession(env, drm, jsessionId)) { |
| return; |
| } |
| |
| Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId)); |
| |
| String8 playbackId; |
| if (jplaybackId != NULL) { |
| playbackId = JStringToString8(env, jplaybackId); |
| } |
| DrmStatus err = drm->setPlaybackId(sessionId, playbackId.c_str()); |
| throwExceptionAsNecessary(env, drm, err, "Failed to set playbackId"); |
| } |
| |
| static jobject android_media_MediaDrm_getLogMessages( |
| JNIEnv *env, jobject thiz) { |
| sp<IDrm> drm = GetDrm(env, thiz); |
| if (!CheckDrm(env, drm)) { |
| return NULL; |
| } |
| |
| Vector<drm::V1_4::LogMessage> logs; |
| DrmStatus err = drm->getLogMessages(logs); |
| ALOGI("drm->getLogMessages %zu logs", logs.size()); |
| if (throwExceptionAsNecessary(env, drm, err, "Failed to get log messages")) { |
| return NULL; |
| } |
| return hidlLogMessagesToJavaList(env, logs); |
| } |
| |
| static const JNINativeMethod gMethods[] = { |
| { "native_release", "()V", (void *)android_media_MediaDrm_native_release }, |
| |
| { "native_init", "()V", (void *)android_media_MediaDrm_native_init }, |
| |
| { "native_setup", "(Ljava/lang/Object;[BLjava/lang/String;)V", |
| (void *)android_media_MediaDrm_native_setup }, |
| |
| { "getSupportedCryptoSchemesNative", "()[B", |
| (void *)android_media_MediaDrm_getSupportedCryptoSchemesNative }, |
| |
| { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;I)Z", |
| (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative }, |
| |
| { "openSessionNative", "(I)[B", |
| (void *)android_media_MediaDrm_openSession }, |
| |
| { "closeSessionNative", "([B)V", |
| (void *)android_media_MediaDrm_closeSession }, |
| |
| { "getKeyRequestNative", "([B[BLjava/lang/String;ILjava/util/HashMap;)" |
| "Landroid/media/MediaDrm$KeyRequest;", |
| (void *)android_media_MediaDrm_getKeyRequest }, |
| |
| { "provideKeyResponse", "([B[B)[B", |
| (void *)android_media_MediaDrm_provideKeyResponse }, |
| |
| { "removeKeys", "([B)V", |
| (void *)android_media_MediaDrm_removeKeys }, |
| |
| { "restoreKeys", "([B[B)V", |
| (void *)android_media_MediaDrm_restoreKeys }, |
| |
| { "queryKeyStatus", "([B)Ljava/util/HashMap;", |
| (void *)android_media_MediaDrm_queryKeyStatus }, |
| |
| { "getProvisionRequestNative", "(ILjava/lang/String;)Landroid/media/MediaDrm$ProvisionRequest;", |
| (void *)android_media_MediaDrm_getProvisionRequestNative }, |
| |
| { "provideProvisionResponseNative", "([B)Landroid/media/MediaDrm$Certificate;", |
| (void *)android_media_MediaDrm_provideProvisionResponseNative }, |
| |
| { "getSecureStops", "()Ljava/util/List;", |
| (void *)android_media_MediaDrm_getSecureStops }, |
| |
| { "getSecureStopIds", "()Ljava/util/List;", |
| (void *)android_media_MediaDrm_getSecureStopIds }, |
| |
| { "getSecureStop", "([B)[B", |
| (void *)android_media_MediaDrm_getSecureStop }, |
| |
| { "releaseSecureStops", "([B)V", |
| (void *)android_media_MediaDrm_releaseSecureStops }, |
| |
| { "removeSecureStop", "([B)V", |
| (void *)android_media_MediaDrm_removeSecureStop }, |
| |
| { "removeAllSecureStops", "()V", |
| (void *)android_media_MediaDrm_removeAllSecureStops }, |
| |
| { "getConnectedHdcpLevel", "()I", |
| (void *)android_media_MediaDrm_getConnectedHdcpLevel }, |
| |
| { "getMaxHdcpLevel", "()I", |
| (void *)android_media_MediaDrm_getMaxHdcpLevel }, |
| |
| { "getOpenSessionCount", "()I", |
| (void *)android_media_MediaDrm_getOpenSessionCount }, |
| |
| { "getMaxSessionCount", "()I", |
| (void *)android_media_MediaDrm_getMaxSessionCount }, |
| |
| { "getSecurityLevel", "([B)I", |
| (void *)android_media_MediaDrm_getSecurityLevel }, |
| |
| { "removeOfflineLicense", "([B)V", |
| (void *)android_media_MediaDrm_removeOfflineLicense }, |
| |
| { "getOfflineLicenseKeySetIds", "()Ljava/util/List;", |
| (void *)android_media_MediaDrm_getOfflineLicenseKeySetIds }, |
| |
| { "getOfflineLicenseState", "([B)I", |
| (void *)android_media_MediaDrm_getOfflineLicenseState }, |
| |
| { "getPropertyString", "(Ljava/lang/String;)Ljava/lang/String;", |
| (void *)android_media_MediaDrm_getPropertyString }, |
| |
| { "getPropertyByteArray", "(Ljava/lang/String;)[B", |
| (void *)android_media_MediaDrm_getPropertyByteArray }, |
| |
| { "setPropertyString", "(Ljava/lang/String;Ljava/lang/String;)V", |
| (void *)android_media_MediaDrm_setPropertyString }, |
| |
| { "setPropertyByteArray", "(Ljava/lang/String;[B)V", |
| (void *)android_media_MediaDrm_setPropertyByteArray }, |
| |
| { "setCipherAlgorithmNative", |
| "(Landroid/media/MediaDrm;[BLjava/lang/String;)V", |
| (void *)android_media_MediaDrm_setCipherAlgorithmNative }, |
| |
| { "setMacAlgorithmNative", |
| "(Landroid/media/MediaDrm;[BLjava/lang/String;)V", |
| (void *)android_media_MediaDrm_setMacAlgorithmNative }, |
| |
| { "encryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B", |
| (void *)android_media_MediaDrm_encryptNative }, |
| |
| { "decryptNative", "(Landroid/media/MediaDrm;[B[B[B[B)[B", |
| (void *)android_media_MediaDrm_decryptNative }, |
| |
| { "signNative", "(Landroid/media/MediaDrm;[B[B[B)[B", |
| (void *)android_media_MediaDrm_signNative }, |
| |
| { "verifyNative", "(Landroid/media/MediaDrm;[B[B[B[B)Z", |
| (void *)android_media_MediaDrm_verifyNative }, |
| |
| { "signRSANative", "(Landroid/media/MediaDrm;[BLjava/lang/String;[B[B)[B", |
| (void *)android_media_MediaDrm_signRSANative }, |
| |
| { "getMetricsNative", "()Landroid/os/PersistableBundle;", |
| (void *)android_media_MediaDrm_native_getMetrics }, |
| |
| { "requiresSecureDecoder", "(Ljava/lang/String;I)Z", |
| (void *)android_media_MediaDrm_requiresSecureDecoder }, |
| |
| { "setPlaybackId", "([BLjava/lang/String;)V", |
| (void *)android_media_MediaDrm_setPlaybackId }, |
| |
| { "getLogMessages", "()Ljava/util/List;", |
| (void *)android_media_MediaDrm_getLogMessages }, |
| }; |
| |
| int register_android_media_Drm(JNIEnv *env) { |
| return AndroidRuntime::registerNativeMethods(env, |
| "android/media/MediaDrm", gMethods, NELEM(gMethods)); |
| } |