2188 lines
72 KiB
C++
2188 lines
72 KiB
C++
/*
|
|
* 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>
|
|
|
|
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 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;
|
|
};
|
|
|
|
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;
|
|
}
|
|
} // 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, status_t err) {
|
|
ALOGE("Illegal state exception: %s (%d)", msg, err);
|
|
|
|
jint jerr = MediaErrorToJavaError(err);
|
|
jobject exception = env->NewObject(gFields.stateException.classId,
|
|
gFields.stateException.init, static_cast<int>(jerr),
|
|
env->NewStringUTF(msg));
|
|
env->Throw(static_cast<jthrowable>(exception));
|
|
}
|
|
|
|
static void throwSessionException(JNIEnv *env, const char *msg, status_t err) {
|
|
ALOGE("Session exception: %s (%d)", msg, 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, static_cast<int>(err),
|
|
env->NewStringUTF(msg));
|
|
|
|
env->SetIntField(exception, gFields.sessionException.errorCode, jErrorCode);
|
|
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, status_t 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) {
|
|
jniThrowException(env, "android/media/NotProvisionedException", msg);
|
|
return true;
|
|
} else if (err == ERROR_DRM_RESOURCE_BUSY) {
|
|
jniThrowException(env, "android/media/ResourceBusyException", msg);
|
|
return true;
|
|
} else if (err == ERROR_DRM_DEVICE_REVOKED) {
|
|
jniThrowException(env, "android/media/DeniedByServerException", 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;
|
|
}
|
|
|
|
status_t 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).string());
|
|
jstring jvalue = env->NewStringUTF(map.valueAt(i).string());
|
|
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>", "(ILjava/lang/String;)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>", "(ILjava/lang/String;)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");
|
|
}
|
|
|
|
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) {
|
|
std::vector<uint8_t> bv;
|
|
for (auto &factory : DrmUtils::MakeDrmFactories()) {
|
|
sp<drm::V1_3::IDrmFactory> factoryV1_3 = drm::V1_3::IDrmFactory::castFrom(factory);
|
|
if (factoryV1_3 == nullptr) {
|
|
continue;
|
|
}
|
|
factoryV1_3->getSupportedCryptoSchemes(
|
|
[&](const hardware::hidl_vec<hardware::hidl_array<uint8_t, 16>>& schemes) {
|
|
for (const auto &scheme : schemes) {
|
|
bv.insert(bv.end(), scheme.data(), scheme.data() + scheme.size());
|
|
}
|
|
});
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
status_t 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));
|
|
|
|
status_t 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;
|
|
|
|
status_t 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.string());
|
|
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;
|
|
|
|
status_t 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));
|
|
|
|
status_t 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));
|
|
|
|
status_t 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;
|
|
|
|
status_t 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);
|
|
status_t 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.string());
|
|
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;
|
|
|
|
status_t 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;
|
|
|
|
status_t 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;
|
|
|
|
status_t 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;
|
|
|
|
status_t 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));
|
|
|
|
status_t 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;
|
|
}
|
|
|
|
status_t 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;
|
|
}
|
|
|
|
status_t 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;
|
|
|
|
status_t 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;
|
|
|
|
status_t 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;
|
|
status_t 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;
|
|
status_t 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;
|
|
|
|
status_t 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;
|
|
|
|
status_t 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;
|
|
}
|
|
|
|
status_t 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;
|
|
|
|
status_t 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;
|
|
|
|
status_t err = drm->getPropertyString(name, value);
|
|
|
|
if (throwExceptionAsNecessary(env, drm, err, "Failed to get property")) {
|
|
return NULL;
|
|
}
|
|
|
|
return env->NewStringUTF(value.string());
|
|
}
|
|
|
|
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;
|
|
|
|
status_t 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);
|
|
|
|
status_t 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);
|
|
|
|
status_t 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);
|
|
|
|
status_t 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);
|
|
|
|
status_t 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;
|
|
|
|
status_t 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;
|
|
|
|
status_t 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;
|
|
|
|
status_t 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;
|
|
|
|
status_t 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));
|
|
status_t 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;
|
|
|
|
status_t 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;
|
|
status_t 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);
|
|
}
|
|
status_t 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;
|
|
status_t 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));
|
|
}
|