MediaDrm API update

Clarify offline usage of sessions and keys and implement
implement CryptoSession to support additional crypto use
cases.

Change-Id: Id3f8c706e9e3034b09af8e2a6a2f26bd74a49f93
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 4561d3f..3cdf261 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -28,8 +28,8 @@
 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 @@
         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 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<String, String> optionalParameters )
+    public native KeyRequest getKeyRequest(byte[] sessionId, byte[] init,
+                                           String mimeType, int keyType,
+                                           HashMap<String, String> 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<String, String> queryLicenseStatus( byte[] sessionId )
+    public native HashMap<String, String> queryKeyStatus(byte[] sessionId)
         throws MediaDrmException;
 
     public final class ProvisionRequest {
@@ -269,22 +293,23 @@
 
     /**
      * 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 @@
      *
      * @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 9938f76..1618edf 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -76,7 +76,7 @@
 
 struct fields_t {
     jfieldID context;
-    RequestFields licenseRequest;
+    RequestFields keyRequest;
     RequestFields provisionRequest;
     ArrayListFields arraylist;
     HashmapFields hashmap;
@@ -204,6 +204,7 @@
     }
     return result;
 }
+
 /*
     import java.util.HashMap;
     import java.util.Set;
@@ -329,9 +330,9 @@
     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 @@
     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<IDrm> drm = GetDrm(env, thiz);
 
     if (!CheckSession(env, drm, jsessionId)) {
@@ -472,7 +473,7 @@
         mimeType = JStringToString8(env, jmimeType);
     }
 
-    DrmPlugin::LicenseType licenseType = (DrmPlugin::LicenseType)jlicenseType;
+    DrmPlugin::KeyType keyType = (DrmPlugin::KeyType)jkeyType;
 
     KeyedVector<String8, String8> optParams;
     if (joptParams != NULL) {
@@ -482,68 +483,94 @@
     Vector<uint8_t> 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<IDrm> drm = GetDrm(env, thiz);
 
     if (!CheckSession(env, drm, jsessionId)) {
-        return;
+        return NULL;
     }
 
     Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
 
     if (jresponse == NULL) {
         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
-        return;
+        return NULL;
     }
     Vector<uint8_t> response(JByteArrayToVector(env, jresponse));
+    Vector<uint8_t> 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<IDrm> drm = GetDrm(env, thiz);
+
+    if (jkeysetId == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+
+    Vector<uint8_t> 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<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->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<IDrm> drm = GetDrm(env, thiz);
 
@@ -554,9 +581,9 @@
 
     KeyedVector<String8, String8> 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 @@
     throwExceptionAsNecessary(env, 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", NULL);
+        return;
+    }
+
+    Vector<uint8_t> 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<IDrm> drm = GetDrm(env, jdrm);
+
+    if (!CheckSession(env, drm, jsessionId)) {
+        return;
+    }
+
+    if (jalgorithm == NULL) {
+        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+        return;
+    }
+
+    Vector<uint8_t> 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<IDrm> 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<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);
+
+    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<IDrm> 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<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);
+    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<IDrm> 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<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);
+
+    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<IDrm> 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<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, err, "Failed to verify");
+    return match;
+}
+
 
 static JNINativeMethod gMethods[] = {
     { "release", "()V", (void *)android_media_MediaDrm_release },
@@ -772,18 +955,21 @@
     { "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 @@
 
     { "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) {