Merge "Change time format for default clock in keyguard" into jb-mr2-dev
diff --git a/docs/html/guide/components/fragments.jd b/docs/html/guide/components/fragments.jd
index 7747b31..32c9f99 100644
--- a/docs/html/guide/components/fragments.jd
+++ b/docs/html/guide/components/fragments.jd
@@ -172,7 +172,7 @@
 
 <p>Most applications should implement at least these three methods for every fragment, but there are
 several other callback methods you should also use to handle various stages of the
-fragment lifecycle. All the lifecycle callback methods are discussed more later, in the section
+fragment lifecycle. All the lifecycle callback methods are discussed in more detail in the section
 about <a href="#Lifecycle">Handling the Fragment Lifecycle</a>.</p>
 
 
diff --git a/docs/html/guide/topics/graphics/prop-animation.jd b/docs/html/guide/topics/graphics/prop-animation.jd
index b733624..49d7bb8 100644
--- a/docs/html/guide/topics/graphics/prop-animation.jd
+++ b/docs/html/guide/topics/graphics/prop-animation.jd
@@ -479,7 +479,7 @@
     </li>
 
     <li>Depending on what property or object you are animating, you might need to call the {@link
-    android.view.View#invalidate invalidate()} method on a View force the screen to redraw itself with the
+    android.view.View#invalidate invalidate()} method on a View to force the screen to redraw itself with the
     updated animated values. You do this in the
     {@link android.animation.ValueAnimator.AnimatorUpdateListener#onAnimationUpdate onAnimationUpdate()}
     callback. For example, animating the color property of a Drawable object only cause updates to the
@@ -825,7 +825,7 @@
 
   <h2 id="views">Animating Views</h2>
 
-  <p>The property animation system allow streamlined animation of View objects and offerse
+  <p>The property animation system allow streamlined animation of View objects and offers
   a few advantages over the view animation system. The view
   animation system transformed View objects by changing the way that they were drawn. This was
   handled in the container of each View, because the View itself had no properties to manipulate.
diff --git a/docs/html/guide/topics/resources/accessing-resources.jd b/docs/html/guide/topics/resources/accessing-resources.jd
index 0673b6f..8f99653 100644
--- a/docs/html/guide/topics/resources/accessing-resources.jd
+++ b/docs/html/guide/topics/resources/accessing-resources.jd
@@ -50,7 +50,7 @@
 <p>When your application is compiled, {@code aapt} generates the {@code R} class, which contains
 resource IDs for all the resources in your {@code
 res/} directory. For each type of resource, there is an {@code R} subclass (for example,
-{@code R.drawable} for all drawable resources) and for each resource of that type, there is a static
+{@code R.drawable} for all drawable resources), and for each resource of that type, there is a static
 integer (for example, {@code R.drawable.icon}). This integer is the resource ID that you can use
 to retrieve your resource.</p>
 
@@ -68,7 +68,7 @@
 
 <p>There are two ways you can access a resource:</p>
 <ul>
-  <li><strong>In code:</strong> Using an static integer from a sub-class of your {@code R}
+  <li><strong>In code:</strong> Using a static integer from a sub-class of your {@code R}
 class, such as:
     <pre class="classic no-pretty-print">R.string.hello</pre>
     <p>{@code string} is the resource type and {@code hello} is the resource name. There are many
@@ -264,11 +264,13 @@
     android:text=&quot;&#64;string/hello&quot; /&gt;
 </pre>
 
-<p class="note"><strong>Note:</strong> You should use string resources at all times, so that your
-application can be localized for other languages. For information about creating alternative
+<p class="note"><strong>Note:</strong> You should use string resources at 
+all times, so that your application can be localized for other languages. 
+For information about creating alternative
 resources (such as localized strings), see <a
 href="providing-resources.html#AlternativeResources">Providing Alternative
-Resources</a>.</p>
+Resources</a>. For a complete guide to localizing your application for other languages,
+see <a href="localization.html">Localization</a>.</p>
 
 <p>You can even use resources in XML to create aliases. For example, you can create a
 drawable resource that is an alias for another drawable resource:</p>
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index b311b7f..5097cc4 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -376,7 +376,7 @@
 screen area. Specifically, the device's smallestWidth is the shortest of the screen's available
 height and width (you may also think of it as the "smallest possible width" for the screen). You can
 use this qualifier to ensure that, regardless of the screen's current orientation, your
-application's has at least {@code &lt;N&gt;} dps of width available for it UI.</p>
+application has at least {@code &lt;N&gt;} dps of width available for its UI.</p>
         <p>For example, if your layout requires that its smallest dimension of screen area be at
 least 600 dp at all times, then you can use this qualifer to create the layout resources, {@code
 res/layout-sw600dp/}. The system will use these resources only when the smallest dimension of
diff --git a/docs/html/guide/topics/search/search-dialog.jd b/docs/html/guide/topics/search/search-dialog.jd
index b9a26d6..e24681a 100644
--- a/docs/html/guide/topics/search/search-dialog.jd
+++ b/docs/html/guide/topics/search/search-dialog.jd
@@ -722,6 +722,7 @@
     // Get the SearchView and set the searchable configuration
     SearchManager searchManager = (SearchManager) {@link android.app.Activity#getSystemService getSystemService}(Context.SEARCH_SERVICE);
     SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
+    // Assumes current activity is the searchable activity
     searchView.setSearchableInfo(searchManager.getSearchableInfo({@link android.app.Activity#getComponentName()}));
     searchView.setIconifiedByDefault(false); // Do not iconify the widget; expand it by default
 
diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd
index 678a512..db09e7d 100644
--- a/docs/html/guide/topics/ui/actionbar.jd
+++ b/docs/html/guide/topics/ui/actionbar.jd
@@ -674,7 +674,7 @@
 view collapsible by adding {@code "collapseActionView"} to the {@code android:showAsAction}
 attribute, as shown in the XML above.</p>
 
-<p>Because the system will expand the action view when the user selects the item, so you
+<p>Because the system will expand the action view when the user selects the item, you
 <em>do not</em> need to respond to the item in the {@link
 android.app.Activity#onOptionsItemSelected onOptionsItemSelected} callback. The system still calls
 {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} when the user selects it,
diff --git a/docs/html/tools/debugging/ddms.jd b/docs/html/tools/debugging/ddms.jd
index 3d6324b..f641aad 100644
--- a/docs/html/tools/debugging/ddms.jd
+++ b/docs/html/tools/debugging/ddms.jd
@@ -54,7 +54,7 @@
   <p>When DDMS starts, it connects to <a href="{@docRoot}tools/help/adb.html">adb</a>.
   When a device is connected, a VM monitoring service is created between
   <code>adb</code> and DDMS, which notifies DDMS when a VM on the device is started or terminated. Once a VM
-  is running, DDMS retrieves the the VM's process ID (pid), via <code>adb</code>, and opens a connection to the
+  is running, DDMS retrieves the VM's process ID (pid), via <code>adb</code>, and opens a connection to the
   VM's debugger, through the adb daemon (adbd) on the device. DDMS can now talk to the VM using a
   custom wire protocol.</p>
 
diff --git a/docs/html/tools/device.jd b/docs/html/tools/device.jd
index 9bdaf47..c7827b2 100644
--- a/docs/html/tools/device.jd
+++ b/docs/html/tools/device.jd
@@ -30,7 +30,7 @@
 you don't yet have a device, check with the service providers in your area to determine which
 Android-powered devices are available.</p>
 
-<p>If you want a SIM-unlocked phone, then you might consider the Google Nexus S. To find a place
+<p>If you want a SIM-unlocked phone, then you might consider a Nexus phone. To find a place
 to purchase the Nexus S and other Android-powered devices, visit <a
 href="http://www.google.com/phone/detail/nexus-s">google.com/phone</a>.</p>
 
diff --git a/docs/html/tools/projects/index.jd b/docs/html/tools/projects/index.jd
index 6a49ac9..439d3be 100644
--- a/docs/html/tools/projects/index.jd
+++ b/docs/html/tools/projects/index.jd
@@ -68,12 +68,12 @@
     <code>src<em>/your/package/namespace/ActivityName</em>.java</code>. All other source code
      files (such as <code>.java</code> or <code>.aidl</code> files) go here as well.</dd>
 
-    <dt><code>bin</code></dt>
+    <dt><code>bin/</code></dt>
 
     <dd>Output directory of the build. This is where you can find the final <code>.apk</code> file and other
     compiled resources.</dd>
 
-    <dt><code>jni</code></dt>
+    <dt><code>jni/</code></dt>
 
     <dd>Contains native code sources developed using the Android NDK. For more information, see the
     <a href="{@docRoot}tools/sdk/ndk/index.html">Android NDK documentation</a>.</dd>
@@ -88,7 +88,7 @@
     <dd>This is empty. You can use it to store raw asset files. Files that you save here are
     compiled into an <code>.apk</code> file as-is, and the original filename is preserved. You can navigate this
     directory in the same way as a typical file system using URIs and read files as a stream of
-    bytes using the the {@link android.content.res.AssetManager}. For example, this is a good
+    bytes using the {@link android.content.res.AssetManager}. For example, this is a good
     location for textures and game data.</dd>
 
     <dt><code>res/</code></dt>
@@ -114,7 +114,7 @@
         <dt><code>drawable/</code></dt>
 
         <dd>For bitmap files (PNG, JPEG, or GIF), 9-Patch image files, and XML files that describe
-        Drawable shapes or a Drawable objects that contain multiple states (normal, pressed, or
+        Drawable shapes or Drawable objects that contain multiple states (normal, pressed, or
         focused). See the <a href=
         "{@docRoot}guide/topics/resources/drawable-resource.html">Drawable</a> resource type.</dd>
 
@@ -251,7 +251,7 @@
     code and resources as a standard Android project, stored in the same way. For example, source
     code in the library project can access its own resources through its <code>R</code> class.</p>
 
-    <p>However, a library project differs from an standard Android application project in that you
+    <p>However, a library project differs from a standard Android application project in that you
     cannot compile it directly to its own <code>.apk</code> and run it on an Android device.
     Similarly, you cannot export the library project to a self-contained JAR file, as you would do
     for a true library. Instead, you must compile the library indirectly, by referencing the
diff --git a/docs/html/tools/testing/testing_android.jd b/docs/html/tools/testing/testing_android.jd
index acf5ec2..10843e8 100644
--- a/docs/html/tools/testing/testing_android.jd
+++ b/docs/html/tools/testing/testing_android.jd
@@ -111,14 +111,14 @@
     </li>
     <li>
         The SDK tools for building and tests are available in Eclipse with ADT, and also in
-        command-line form for use with other IDES. These tools get information from the project of
+        command-line form for use with other IDEs. These tools get information from the project of
         the application under test and use this information to automatically create the build files,
         manifest file, and directory structure for the test package.
     </li>
     <li>
         The SDK also provides
   <a href="{@docRoot}tools/help/monkeyrunner_concepts.html">monkeyrunner</a>, an API
-        testing devices with Python programs, and <a
+        for testing devices with Python programs, and <a
         href="{@docRoot}tools/help/monkey.html">UI/Application Exerciser Monkey</a>,
         a command-line tool for stress-testing UIs by sending pseudo-random events to a device.
     </li>
diff --git a/docs/html/tools/workflow/index.jd b/docs/html/tools/workflow/index.jd
index 5ae06e6..784b212 100644
--- a/docs/html/tools/workflow/index.jd
+++ b/docs/html/tools/workflow/index.jd
@@ -34,7 +34,7 @@
   </li>
   <li><strong>Development</strong>
     <p>During this phase you set up and develop your Android project, which contains all of the
-    source code and resource files for your application. For more informations, see
+    source code and resource files for your application. For more information, see
     <a href="{@docRoot}tools/projects/index.html">Create an Android project</a>.</p>
   </li>
   <li><strong>Debugging and Testing</strong>
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) {