From 16b8cffb2893c10c35788191847500004da466d1 Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Sat, 30 Mar 2013 16:26:13 -0700 Subject: [PATCH] MediaDrm API update Clarify offline usage of sessions and keys and implement implement CryptoSession to support additional crypto use cases. Change-Id: Id3f8c706e9e3034b09af8e2a6a2f26bd74a49f93 --- media/java/android/media/MediaDrm.java | 227 +++++++++++++++----- media/jni/android_media_MediaDrm.cpp | 280 +++++++++++++++++++++---- 2 files changed, 420 insertions(+), 87 deletions(-) diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 4561d3fb63672..3cdf261d2242d 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -28,8 +28,8 @@ import android.os.Bundle; import android.util.Log; /** - * MediaDrm class can be used in conjunction with {@link android.media.MediaCrypto} - * to obtain licenses for decoding encrypted media data. + * MediaDrm can be used in conjunction with {@link android.media.MediaCrypto} + * to obtain keys for decrypting protected media data. * * Crypto schemes are assigned 16 byte UUIDs, * the method {@link #isCryptoSchemeSupported} can be used to query if a given @@ -131,11 +131,15 @@ public final class MediaDrm { void onEvent(MediaDrm md, byte[] sessionId, int event, int extra, byte[] data); } + public static final int MEDIA_DRM_EVENT_PROVISION_REQUIRED = 1; + public static final int MEDIA_DRM_EVENT_KEY_REQUIRED = 2; + public static final int MEDIA_DRM_EVENT_KEY_EXPIRED = 3; + public static final int MEDIA_DRM_EVENT_VENDOR_DEFINED = 4; + /* Do not change these values without updating their counterparts * in include/media/mediadrm.h! */ private static final int DRM_EVENT = 200; - private class EventHandler extends Handler { private MediaDrm mMediaDrm; @@ -197,68 +201,88 @@ public final class MediaDrm { public native byte[] openSession() throws MediaDrmException; /** - * Close a session on the MediaDrm object. + * Close a session on the MediaDrm object that was previously opened + * with {@link #openSession}. */ public native void closeSession(byte[] sessionId) throws MediaDrmException; - public static final int MEDIA_DRM_LICENSE_TYPE_STREAMING = 1; - public static final int MEDIA_DRM_LICENSE_TYPE_OFFLINE = 2; + public static final int MEDIA_DRM_KEY_TYPE_STREAMING = 1; + public static final int MEDIA_DRM_KEY_TYPE_OFFLINE = 2; - public final class LicenseRequest { - public LicenseRequest() {} + public final class KeyRequest { + public KeyRequest() {} public byte[] data; public String defaultUrl; }; /** - * A license request/response exchange occurs between the app and a License - * Server to obtain the keys required to decrypt the content. getLicenseRequest() - * is used to obtain an opaque license request byte array that is delivered to the - * license server. The opaque license request byte array is returned in - * LicenseReqeust.data. The recommended URL to deliver the license request to is - * returned in LicenseRequest.defaultUrl + * A key request/response exchange occurs between the app and a license + * server to obtain the keys to decrypt encrypted content. getKeyRequest() + * is used to obtain an opaque key request byte array that is delivered to the + * license server. The opaque key request byte array is returned in + * KeyRequest.data. The recommended URL to deliver the key request to is + * returned in KeyRequest.defaultUrl. + * + * After the app has received the key request response from the server, + * it should deliver to the response to the DRM engine plugin using the method + * {@link #provideKeyResponse}. * * @param sessonId the session ID for the drm session * @param init container-specific data, its meaning is interpreted based on the * mime type provided in the mimeType parameter. It could contain, for example, * the content ID, key ID or other data obtained from the content metadata that is - * required in generating the license request. + * required in generating the key request. * @param mimeType identifies the mime type of the content - * @param licenseType specifes if the license is for streaming or offline content - * @param optionalParameters are included in the license server request message to + * @param keyType specifes if the request is for streaming or offline content + * @param optionalParameters are included in the key request message to * allow a client application to provide additional message parameters to the server. */ - public native LicenseRequest getLicenseRequest( byte[] sessionId, byte[] init, - String mimeType, int licenseType, - HashMap optionalParameters ) + public native KeyRequest getKeyRequest(byte[] sessionId, byte[] init, + String mimeType, int keyType, + HashMap optionalParameters) throws MediaDrmException; /** - * After a license response is received by the app, it is provided to the DRM plugin - * using provideLicenseResponse. + * A key response is received from the license server by the app, then it is + * provided to the DRM engine plugin using provideKeyResponse. The byte array + * returned is a keySetId that can be used to later restore the keys to a new + * session with the method {@link restoreKeys}, enabling offline key use. * * @param sessionId the session ID for the DRM session * @param response the byte array response from the server */ - public native void provideLicenseResponse( byte[] sessionId, byte[] response ) + public native byte[] provideKeyResponse(byte[] sessionId, byte[] response) throws MediaDrmException; /** - * Remove the keys associated with a license for a session + * Restore persisted offline keys into a new session. keySetId identifies the + * keys to load, obtained from a prior call to {@link provideKeyResponse}. + * * @param sessionId the session ID for the DRM session + * @param keySetId identifies the saved key set to restore */ - public native void removeLicense( byte[] sessionId ) throws MediaDrmException; + public native void restoreKeys(byte[] sessionId, byte[] keySetId) + throws MediaDrmException; /** - * Request an informative description of the license for the session. The status is + * Remove the persisted keys associated with an offline license. Keys are persisted + * when {@link provideKeyResponse} is called with keys obtained from the method + * {@link getKeyRequest} using keyType = MEDIA_DRM_KEY_TYPE_OFFLINE. + * + * @param keySetId identifies the saved key set to remove + */ + public native void removeKeys(byte[] keySetId) throws MediaDrmException; + + /** + * Request an informative description of the key status for the session. The status is * in the form of {name, value} pairs. Since DRM license policies vary by vendor, * the specific status field names are determined by each DRM vendor. Refer to your * DRM provider documentation for definitions of the field names for a particular - * DrmEngine. + * DRM engine plugin. * * @param sessionId the session ID for the DRM session */ - public native HashMap queryLicenseStatus( byte[] sessionId ) + public native HashMap queryKeyStatus(byte[] sessionId) throws MediaDrmException; public final class ProvisionRequest { @@ -269,22 +293,23 @@ public final class MediaDrm { /** * A provision request/response exchange occurs between the app and a provisioning - * server to retrieve a device certificate. getProvisionRequest is used to obtain - * an opaque license request byte array that is delivered to the provisioning server. - * The opaque provision request byte array is returned in ProvisionRequest.data - * The recommended URL to deliver the license request to is returned in - * ProvisionRequest.defaultUrl. + * server to retrieve a device certificate. If provisionining is required, the + * MEDIA_DRM_EVENT_PROVISION_REQUIRED event will be sent to the event handler. + * getProvisionRequest is used to obtain the opaque provision request byte array that + * should be delivered to the provisioning server. The provision request byte array + * is returned in ProvisionRequest.data. The recommended URL to deliver the provision + * request to is returned in ProvisionRequest.defaultUrl. */ public native ProvisionRequest getProvisionRequest() throws MediaDrmException; /** * After a provision response is received by the app, it is provided to the DRM - * plugin using this method. + * engine plugin using this method. * * @param response the opaque provisioning response byte array to provide to the - * DrmEngine. + * DRM engine plugin. */ - public native void provideProvisionResponse( byte[] response ) + public native void provideProvisionResponse(byte[] response) throws MediaDrmException; /** @@ -314,38 +339,140 @@ public final class MediaDrm { * * @param ssRelease the server response indicating which secure stops to release */ - public native void releaseSecureStops( byte[] ssRelease ) + public native void releaseSecureStops(byte[] ssRelease) throws MediaDrmException; /** - * Read a Drm plugin property value, given the property name string. There are several - * forms of property access functions, depending on the data type returned. + * Read a DRM engine plugin property value, given the property name string. There are + * several forms of property access functions, depending on the data type returned. * * Standard fields names are: - * vendor String - identifies the maker of the plugin - * version String - identifies the version of the plugin - * description String - describes the plugin + * vendor String - identifies the maker of the DRM engine plugin + * version String - identifies the version of the DRM engine plugin + * description String - describes the DRM engine plugin * deviceUniqueId byte[] - The device unique identifier is established during device - * provisioning and provides a means of uniquely identifying - * each device + * provisioning and provides a means of uniquely identifying + * each device + * algorithms String - a comma-separate list of cipher and mac algorithms supported + * by CryptoSession. The list may be empty if the DRM engine + * plugin does not support CryptoSession operations. */ - public native String getPropertyString( String propertyName ) + public native String getPropertyString(String propertyName) throws MediaDrmException; - public native byte[] getPropertyByteArray( String propertyName ) + public native byte[] getPropertyByteArray(String propertyName) throws MediaDrmException; /** - * Write a Drm plugin property value. There are several forms of property setting - * functions, depending on the data type being set. + * Write a DRM engine plugin property value. There are several forms of + * property setting functions, depending on the data type being set. */ - public native void setPropertyString( String propertyName, String value ) + public native void setPropertyString(String propertyName, String value) throws MediaDrmException; - public native void setPropertyByteArray( String propertyName, byte[] value ) + public native void setPropertyByteArray(String propertyName, byte[] value) throws MediaDrmException; + /** + * In addition to supporting decryption of DASH Common Encrypted Media, the + * MediaDrm APIs provide the ability to securely deliver session keys from + * an operator's session key server to a client device, based on the factory-installed + * root of trust, and provide the ability to do encrypt, decrypt, sign and verify + * with the session key on arbitrary user data. + * + * The CryptoSession class implements generic encrypt/decrypt/sign/verify methods + * based on the established session keys. These keys are exchanged using the + * getKeyRequest/provideKeyResponse methods. + * + * Applications of this capability could include securing various types of + * purchased or private content, such as applications, books and other media, + * photos or media delivery protocols. + * + * Operators can create session key servers that are functionally similar to a + * license key server, except that instead of receiving license key requests and + * providing encrypted content keys which are used specifically to decrypt A/V media + * content, the session key server receives session key requests and provides + * encrypted session keys which can be used for general purpose crypto operations. + */ + + private static final native void setCipherAlgorithmNative(MediaDrm drm, byte[] sessionId, + String algorithm); + + private static final native void setMacAlgorithmNative(MediaDrm drm, byte[] sessionId, + String algorithm); + + private static final native byte[] encryptNative(MediaDrm drm, byte[] sessionId, + byte[] keyId, byte[] input, byte[] iv); + + private static final native byte[] decryptNative(MediaDrm drm, byte[] sessionId, + byte[] keyId, byte[] input, byte[] iv); + + private static final native byte[] signNative(MediaDrm drm, byte[] sessionId, + byte[] keyId, byte[] message); + + private static final native boolean verifyNative(MediaDrm drm, byte[] sessionId, + byte[] keyId, byte[] message, + byte[] signature); + + public final class CryptoSession { + private MediaDrm mDrm; + private byte[] mSessionId; + + /** + * Construct a CryptoSession which can be used to encrypt, decrypt, + * sign and verify messages or data using the session keys established + * for the session using methods {@link getKeyRequest} and + * {@link provideKeyResponse} using a session key server. + * + * @param sessionId the session ID for the session containing keys + * to be used for encrypt, decrypt, sign and/or verify + * + * @param cipherAlgorithm the algorithm to use for encryption and + * decryption ciphers. The algorithm string conforms to JCA Standard + * Names for Cipher Transforms and is case insensitive. For example + * "AES/CBC/PKCS5Padding". + * + * @param macAlgorithm the algorithm to use for sign and verify + * The algorithm string conforms to JCA Standard Names for Mac + * Algorithms and is case insensitive. For example "HmacSHA256". + * + * The list of supported algorithms for a DRM engine plugin can be obtained + * using the method {@link getPropertyString("algorithms")} + */ + + public CryptoSession(MediaDrm drm, byte[] sessionId, + String cipherAlgorithm, String macAlgorithm) + throws MediaDrmException { + mSessionId = sessionId; + mDrm = drm; + setCipherAlgorithmNative(drm, sessionId, cipherAlgorithm); + setMacAlgorithmNative(drm, sessionId, macAlgorithm); + } + + public byte[] encrypt(byte[] keyid, byte[] input, byte[] iv) { + return encryptNative(mDrm, mSessionId, keyid, input, iv); + } + + public byte[] decrypt(byte[] keyid, byte[] input, byte[] iv) { + return decryptNative(mDrm, mSessionId, keyid, input, iv); + } + + public byte[] sign(byte[] keyid, byte[] message) { + return signNative(mDrm, mSessionId, keyid, message); + } + public boolean verify(byte[] keyid, byte[] message, byte[] signature) { + return verifyNative(mDrm, mSessionId, keyid, message, signature); + } + }; + + public CryptoSession getCryptoSession(byte[] sessionId, + String cipherAlgorithm, + String macAlgorithm) + throws MediaDrmException { + return new CryptoSession(this, sessionId, cipherAlgorithm, macAlgorithm); + } + @Override protected void finalize() { native_finalize(); diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp index 9938f7633b140..1618edf2b34cb 100644 --- a/media/jni/android_media_MediaDrm.cpp +++ b/media/jni/android_media_MediaDrm.cpp @@ -76,7 +76,7 @@ struct EntryFields { struct fields_t { jfieldID context; - RequestFields licenseRequest; + RequestFields keyRequest; RequestFields provisionRequest; ArrayListFields arraylist; HashmapFields hashmap; @@ -204,6 +204,7 @@ static String8 JStringToString8(JNIEnv *env, jstring const &jstr) { } return result; } + /* import java.util.HashMap; import java.util.Set; @@ -329,9 +330,9 @@ static void android_media_MediaDrm_native_init(JNIEnv *env) { FIND_CLASS(clazz, "android/media/MediaDrm"); GET_FIELD_ID(gFields.context, clazz, "mNativeContext", "I"); - FIND_CLASS(clazz, "android/media/MediaDrm$LicenseRequest"); - GET_FIELD_ID(gFields.licenseRequest.data, clazz, "data", "[B"); - GET_FIELD_ID(gFields.licenseRequest.defaultUrl, clazz, "defaultUrl", "Ljava/lang/String;"); + FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest"); + GET_FIELD_ID(gFields.keyRequest.data, clazz, "data", "[B"); + GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "defaultUrl", "Ljava/lang/String;"); FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest"); GET_FIELD_ID(gFields.provisionRequest.data, clazz, "data", "[B"); @@ -451,9 +452,9 @@ static void android_media_MediaDrm_closeSession( throwExceptionAsNecessary(env, err, "Failed to close session"); } -static jobject android_media_MediaDrm_getLicenseRequest( +static jobject android_media_MediaDrm_getKeyRequest( JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jinitData, - jstring jmimeType, jint jlicenseType, jobject joptParams) { + jstring jmimeType, jint jkeyType, jobject joptParams) { sp drm = GetDrm(env, thiz); if (!CheckSession(env, drm, jsessionId)) { @@ -472,7 +473,7 @@ static jobject android_media_MediaDrm_getLicenseRequest( mimeType = JStringToString8(env, jmimeType); } - DrmPlugin::LicenseType licenseType = (DrmPlugin::LicenseType)jlicenseType; + DrmPlugin::KeyType keyType = (DrmPlugin::KeyType)jkeyType; KeyedVector optParams; if (joptParams != NULL) { @@ -482,68 +483,94 @@ static jobject android_media_MediaDrm_getLicenseRequest( Vector request; String8 defaultUrl; - status_t err = drm->getLicenseRequest(sessionId, initData, mimeType, - licenseType, optParams, request, defaultUrl); + status_t err = drm->getKeyRequest(sessionId, initData, mimeType, + keyType, optParams, request, defaultUrl); - if (throwExceptionAsNecessary(env, err, "Failed to get license request")) { + if (throwExceptionAsNecessary(env, err, "Failed to get key request")) { return NULL; } // Fill out return obj jclass clazz; - FIND_CLASS(clazz, "android/media/MediaDrm$LicenseRequest"); + FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest"); - jobject licenseObj = NULL; + jobject keyObj = NULL; if (clazz) { - licenseObj = env->AllocObject(clazz); + keyObj = env->AllocObject(clazz); jbyteArray jrequest = VectorToJByteArray(env, request); - env->SetObjectField(licenseObj, gFields.licenseRequest.data, jrequest); + env->SetObjectField(keyObj, gFields.keyRequest.data, jrequest); jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string()); - env->SetObjectField(licenseObj, gFields.licenseRequest.defaultUrl, jdefaultUrl); + env->SetObjectField(keyObj, gFields.keyRequest.defaultUrl, jdefaultUrl); } - return licenseObj; + return keyObj; } -static void android_media_MediaDrm_provideLicenseResponse( +static jbyteArray android_media_MediaDrm_provideKeyResponse( JNIEnv *env, jobject thiz, jbyteArray jsessionId, jbyteArray jresponse) { sp drm = GetDrm(env, thiz); if (!CheckSession(env, drm, jsessionId)) { - return; + return NULL; } Vector sessionId(JByteArrayToVector(env, jsessionId)); if (jresponse == NULL) { jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return; + return NULL; } Vector response(JByteArrayToVector(env, jresponse)); + Vector keySetId; - status_t err = drm->provideLicenseResponse(sessionId, response); + status_t err = drm->provideKeyResponse(sessionId, response, keySetId); - throwExceptionAsNecessary(env, err, "Failed to handle license response"); + throwExceptionAsNecessary(env, err, "Failed to handle key response"); + return VectorToJByteArray(env, keySetId); } -static void android_media_MediaDrm_removeLicense( - JNIEnv *env, jobject thiz, jbyteArray jsessionId) { +static void android_media_MediaDrm_removeKeys( + JNIEnv *env, jobject thiz, jbyteArray jkeysetId) { + sp drm = GetDrm(env, thiz); + + if (jkeysetId == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + Vector keySetId(JByteArrayToVector(env, jkeysetId)); + + status_t err = drm->removeKeys(keySetId); + + throwExceptionAsNecessary(env, err, "Failed to remove keys"); +} + +static void android_media_MediaDrm_restoreKeys( + JNIEnv *env, jobject thiz, jbyteArray jsessionId, + jbyteArray jkeysetId) { + sp drm = GetDrm(env, thiz); if (!CheckSession(env, drm, jsessionId)) { return; } + if (jkeysetId == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + Vector sessionId(JByteArrayToVector(env, jsessionId)); + Vector keySetId(JByteArrayToVector(env, jkeysetId)); - status_t err = drm->removeLicense(sessionId); + status_t err = drm->restoreKeys(sessionId, keySetId); - throwExceptionAsNecessary(env, err, "Failed to remove license"); + throwExceptionAsNecessary(env, err, "Failed to restore keys"); } -static jobject android_media_MediaDrm_queryLicenseStatus( +static jobject android_media_MediaDrm_queryKeyStatus( JNIEnv *env, jobject thiz, jbyteArray jsessionId) { sp drm = GetDrm(env, thiz); @@ -554,9 +581,9 @@ static jobject android_media_MediaDrm_queryLicenseStatus( KeyedVector infoMap; - status_t err = drm->queryLicenseStatus(sessionId, infoMap); + status_t err = drm->queryKeyStatus(sessionId, infoMap); - if (throwExceptionAsNecessary(env, err, "Failed to query license")) { + if (throwExceptionAsNecessary(env, err, "Failed to query key status")) { return NULL; } @@ -752,6 +779,162 @@ static void android_media_MediaDrm_setPropertyByteArray( throwExceptionAsNecessary(env, err, "Failed to set property"); } +static void android_media_MediaDrm_setCipherAlgorithmNative( + JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, + jstring jalgorithm) { + + sp drm = GetDrm(env, jdrm); + + if (!CheckSession(env, drm, jsessionId)) { + return; + } + + if (jalgorithm == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + Vector sessionId(JByteArrayToVector(env, jsessionId)); + String8 algorithm = JStringToString8(env, jalgorithm); + + status_t err = drm->setCipherAlgorithm(sessionId, algorithm); + + throwExceptionAsNecessary(env, err, "Failed to set cipher algorithm"); +} + +static void android_media_MediaDrm_setMacAlgorithmNative( + JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, + jstring jalgorithm) { + + sp drm = GetDrm(env, jdrm); + + if (!CheckSession(env, drm, jsessionId)) { + return; + } + + if (jalgorithm == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + Vector sessionId(JByteArrayToVector(env, jsessionId)); + String8 algorithm = JStringToString8(env, jalgorithm); + + status_t err = drm->setMacAlgorithm(sessionId, algorithm); + + throwExceptionAsNecessary(env, 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 drm = GetDrm(env, jdrm); + + if (!CheckSession(env, drm, jsessionId)) { + return NULL; + } + + if (jkeyId == NULL || jinput == NULL || jiv == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return NULL; + } + + Vector sessionId(JByteArrayToVector(env, jsessionId)); + Vector keyId(JByteArrayToVector(env, jkeyId)); + Vector input(JByteArrayToVector(env, jinput)); + Vector iv(JByteArrayToVector(env, jiv)); + Vector output; + + status_t err = drm->encrypt(sessionId, keyId, input, iv, output); + + throwExceptionAsNecessary(env, err, "Failed to encrypt"); + + 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 drm = GetDrm(env, jdrm); + + if (!CheckSession(env, drm, jsessionId)) { + return NULL; + } + + if (jkeyId == NULL || jinput == NULL || jiv == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return NULL; + } + + Vector sessionId(JByteArrayToVector(env, jsessionId)); + Vector keyId(JByteArrayToVector(env, jkeyId)); + Vector input(JByteArrayToVector(env, jinput)); + Vector iv(JByteArrayToVector(env, jiv)); + Vector output; + + status_t err = drm->decrypt(sessionId, keyId, input, iv, output); + throwExceptionAsNecessary(env, err, "Failed to decrypt"); + + return VectorToJByteArray(env, output); +} + +static jbyteArray android_media_MediaDrm_signNative( + JNIEnv *env, jobject thiz, jobject jdrm, jbyteArray jsessionId, + jbyteArray jkeyId, jbyteArray jmessage) { + + sp drm = GetDrm(env, jdrm); + + if (!CheckSession(env, drm, jsessionId)) { + return NULL; + } + + if (jkeyId == NULL || jmessage == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return NULL; + } + + Vector sessionId(JByteArrayToVector(env, jsessionId)); + Vector keyId(JByteArrayToVector(env, jkeyId)); + Vector message(JByteArrayToVector(env, jmessage)); + Vector signature; + + status_t err = drm->sign(sessionId, keyId, message, signature); + + throwExceptionAsNecessary(env, err, "Failed to sign"); + + 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 drm = GetDrm(env, jdrm); + + if (!CheckSession(env, drm, jsessionId)) { + return false; + } + + if (jkeyId == NULL || jmessage == NULL || jsignature == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return false; + } + + Vector sessionId(JByteArrayToVector(env, jsessionId)); + Vector keyId(JByteArrayToVector(env, jkeyId)); + Vector message(JByteArrayToVector(env, jmessage)); + Vector signature(JByteArrayToVector(env, jsignature)); + bool match; + + status_t err = drm->verify(sessionId, keyId, message, signature, match); + + throwExceptionAsNecessary(env, err, "Failed to verify"); + return match; +} + static JNINativeMethod gMethods[] = { { "release", "()V", (void *)android_media_MediaDrm_release }, @@ -772,18 +955,21 @@ static JNINativeMethod gMethods[] = { { "closeSession", "([B)V", (void *)android_media_MediaDrm_closeSession }, - { "getLicenseRequest", "([B[BLjava/lang/String;ILjava/util/HashMap;)" - "Landroid/media/MediaDrm$LicenseRequest;", - (void *)android_media_MediaDrm_getLicenseRequest }, + { "getKeyRequest", "([B[BLjava/lang/String;ILjava/util/HashMap;)" + "Landroid/media/MediaDrm$KeyRequest;", + (void *)android_media_MediaDrm_getKeyRequest }, - { "provideLicenseResponse", "([B[B)V", - (void *)android_media_MediaDrm_provideLicenseResponse }, + { "provideKeyResponse", "([B[B)[B", + (void *)android_media_MediaDrm_provideKeyResponse }, - { "removeLicense", "([B)V", - (void *)android_media_MediaDrm_removeLicense }, + { "removeKeys", "([B)V", + (void *)android_media_MediaDrm_removeKeys }, - { "queryLicenseStatus", "([B)Ljava/util/HashMap;", - (void *)android_media_MediaDrm_queryLicenseStatus }, + { "restoreKeys", "([B[B)V", + (void *)android_media_MediaDrm_restoreKeys }, + + { "queryKeyStatus", "([B)Ljava/util/HashMap;", + (void *)android_media_MediaDrm_queryKeyStatus }, { "getProvisionRequest", "()Landroid/media/MediaDrm$ProvisionRequest;", (void *)android_media_MediaDrm_getProvisionRequest }, @@ -808,6 +994,26 @@ static JNINativeMethod gMethods[] = { { "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 }, }; int register_android_media_Drm(JNIEnv *env) {