MediaDrm API: Add two exceptions to handle error path scenarios

bug: 8725707
Change-Id: Ia00c495ac591cc2993f4d11ad3480b59833d009e
diff --git a/api/current.txt b/api/current.txt
index d71ab35..214f639 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11497,6 +11497,10 @@
     field public static final int QUALITY_MEDIUM = 1; // 0x1
   }
 
+  public final class DeniedByServerException extends android.media.MediaDrmException {
+    ctor public DeniedByServerException(java.lang.String);
+  }
+
   public class ExifInterface {
     ctor public ExifInterface(java.lang.String) throws java.io.IOException;
     method public double getAltitude(double);
@@ -11818,18 +11822,18 @@
   }
 
   public final class MediaDrm {
-    ctor public MediaDrm(java.util.UUID) throws android.media.MediaDrmException;
+    ctor public MediaDrm(java.util.UUID) throws android.media.UnsupportedSchemeException;
     method public void closeSession(byte[]);
     method public android.media.MediaDrm.CryptoSession getCryptoSession(byte[], java.lang.String, java.lang.String);
-    method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
+    method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>) throws android.media.NotProvisionedException;
     method public byte[] getPropertyByteArray(java.lang.String);
     method public java.lang.String getPropertyString(java.lang.String);
     method public android.media.MediaDrm.ProvisionRequest getProvisionRequest();
     method public java.util.List<byte[]> getSecureStops();
     method public static final boolean isCryptoSchemeSupported(java.util.UUID);
-    method public byte[] openSession();
-    method public byte[] provideKeyResponse(byte[], byte[]);
-    method public void provideProvisionResponse(byte[]);
+    method public byte[] openSession() throws android.media.NotProvisionedException;
+    method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.NotProvisionedException;
+    method public void provideProvisionResponse(byte[]) throws android.media.DeniedByServerException;
     method public java.util.HashMap<java.lang.String, java.lang.String> queryKeyStatus(byte[]);
     method public final void release();
     method public void releaseSecureStops(byte[]);
@@ -11873,7 +11877,7 @@
     method public java.lang.String getDefaultUrl();
   }
 
-  public final class MediaDrmException extends java.lang.Exception {
+  public class MediaDrmException extends java.lang.Exception {
     ctor public MediaDrmException(java.lang.String);
   }
 
@@ -12339,6 +12343,10 @@
     field public static final int SYNC_EVENT_PRESENTATION_COMPLETE = 1; // 0x1
   }
 
+  public final class NotProvisionedException extends android.media.MediaDrmException {
+    ctor public NotProvisionedException(java.lang.String);
+  }
+
   public class RemoteControlClient {
     ctor public RemoteControlClient(android.app.PendingIntent);
     ctor public RemoteControlClient(android.app.PendingIntent, android.os.Looper);
@@ -12581,6 +12589,10 @@
     field public static final int TONE_SUP_RINGTONE = 23; // 0x17
   }
 
+  public final class UnsupportedSchemeException extends android.media.MediaDrmException {
+    ctor public UnsupportedSchemeException(java.lang.String);
+  }
+
 }
 
 package android.media.audiofx {
diff --git a/media/java/android/media/DeniedByServerException.java b/media/java/android/media/DeniedByServerException.java
new file mode 100644
index 0000000..9c1633a
--- /dev/null
+++ b/media/java/android/media/DeniedByServerException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * Exception thrown when the provisioning server or key server denies a
+ * certficate or license for a device.
+ */
+public final class DeniedByServerException extends MediaDrmException {
+    public DeniedByServerException(String detailMessage) {
+        super(detailMessage);
+    }
+}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index a58fa51..be2d9bc 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -127,11 +127,14 @@
     private static final native boolean isCryptoSchemeSupportedNative(byte[] uuid);
 
     /**
-     * Instantiate a MediaDrm object using opaque, crypto scheme specific
-     * data.
+     * Instantiate a MediaDrm object
+     *
      * @param uuid The UUID of the crypto scheme.
+     *
+     * @throws UnsupportedSchemeException if the device does not support the
+     * specified scheme UUID
      */
-    public MediaDrm(UUID uuid) throws MediaDrmException {
+    public MediaDrm(UUID uuid) throws UnsupportedSchemeException {
         Looper looper;
         if ((looper = Looper.myLooper()) != null) {
             mEventHandler = new EventHandler(this, looper);
@@ -268,8 +271,10 @@
 
     /**
      * Open a new session with the MediaDrm object.  A session ID is returned.
+     *
+     * @throws NotProvisionedException if provisioning is needed
      */
-    public native byte[] openSession();
+    public native byte[] openSession() throws NotProvisionedException;
 
     /**
      * Close a session on the MediaDrm object that was previously opened
@@ -346,10 +351,14 @@
      * keys, which are identified by a keySetId.
      * @param optionalParameters are included in the key request message to
      * allow a client application to provide additional message parameters to the server.
+     *
+     * @throws NotProvisionedException if reprovisioning is needed, due to a
+     * problem with the certifcate
      */
     public native KeyRequest getKeyRequest(byte[] scope, byte[] init,
                                            String mimeType, int keyType,
-                                           HashMap<String, String> optionalParameters);
+                                           HashMap<String, String> optionalParameters)
+        throws NotProvisionedException;
 
 
     /**
@@ -360,8 +369,15 @@
      *
      * @param sessionId the session ID for the DRM session
      * @param response the byte array response from the server
+     *
+     * @throws NotProvisionedException if the response indicates that
+     * reprovisioning is required
+     * @throws DeniedByServerException if the response indicates that the
+     * server rejected the request
      */
-    public native byte[] provideKeyResponse(byte[] sessionId, byte[] response);
+    public native byte[] provideKeyResponse(byte[] sessionId, byte[] response)
+        throws NotProvisionedException, DeniedByServerException;
+
 
     /**
      * Restore persisted offline keys into a new session.  keySetId identifies the
@@ -430,8 +446,12 @@
      *
      * @param response the opaque provisioning response byte array to provide to the
      * DRM engine plugin.
+     *
+     * @throws DeniedByServerException if the response indicates that the
+     * server rejected the request
      */
-    public native void provideProvisionResponse(byte[] response);
+    public native void provideProvisionResponse(byte[] response)
+        throws DeniedByServerException;
 
     /**
      * A means of enforcing limits on the number of concurrent streams per subscriber
diff --git a/media/java/android/media/MediaDrmException.java b/media/java/android/media/MediaDrmException.java
index d6f5ff4..d547574 100644
--- a/media/java/android/media/MediaDrmException.java
+++ b/media/java/android/media/MediaDrmException.java
@@ -17,10 +17,9 @@
 package android.media;
 
 /**
- * Exception thrown if MediaDrm object could not be instantiated for
- * whatever reason.
+ * Base class for MediaDrm exceptions
  */
-public final class MediaDrmException extends Exception {
+public class MediaDrmException extends Exception {
     public MediaDrmException(String detailMessage) {
         super(detailMessage);
     }
diff --git a/media/java/android/media/NotProvisionedException.java b/media/java/android/media/NotProvisionedException.java
new file mode 100644
index 0000000..32b8151
--- /dev/null
+++ b/media/java/android/media/NotProvisionedException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * Exception thrown when an operation on a MediaDrm object is attempted
+ * and the device does not have a certificate.  The app should obtain and
+ * install a certificate using the MediaDrm provisioning methods then retry
+ * the operation.
+ */
+public final class NotProvisionedException extends MediaDrmException {
+    public NotProvisionedException(String detailMessage) {
+        super(detailMessage);
+    }
+}
diff --git a/media/java/android/media/UnsupportedSchemeException.java b/media/java/android/media/UnsupportedSchemeException.java
new file mode 100644
index 0000000..d7b5d47
--- /dev/null
+++ b/media/java/android/media/UnsupportedSchemeException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * Exception thrown when an attempt is made to construct a MediaDrm object
+ * using a crypto scheme UUID that is not supported by the device
+ */
+public final class UnsupportedSchemeException extends MediaDrmException {
+    public UnsupportedSchemeException(String detailMessage) {
+        super(detailMessage);
+    }
+}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index d1b499e..ec88949 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -219,12 +219,6 @@
     case ERROR_DRM_TAMPER_DETECTED:
         drmMessage = "Invalid state";
         break;
-    case ERROR_DRM_NOT_PROVISIONED:
-        drmMessage = "Not provisioned";
-        break;
-    case ERROR_DRM_DEVICE_REVOKED:
-        drmMessage = "Device revoked";
-        break;
     default:
         break;
     }
@@ -238,6 +232,12 @@
     if (err == BAD_VALUE) {
         jniThrowException(env, "java/lang/IllegalArgumentException", msg);
         return true;
+    } else if (err == ERROR_DRM_NOT_PROVISIONED) {
+        jniThrowException(env, "android/media/NotProvisionedException", msg);
+        return true;
+    } else if (err == ERROR_DRM_DEVICE_REVOKED) {
+        jniThrowException(env, "android/media/DeniedByServerException", msg);
+        return true;
     } else if (err != OK) {
         String8 errbuf;
         if (drmMessage != NULL) {
@@ -248,6 +248,7 @@
                 msg = errbuf.string();
             }
         }
+        ALOGE("Illegal state exception: %s", msg);
         jniThrowException(env, "java/lang/IllegalStateException", msg);
         return true;
     }
@@ -574,7 +575,7 @@
     if (err != OK) {
         jniThrowException(
                 env,
-                "android/media/MediaDrmException",
+                "android/media/UnsupportedSchemeException",
                 "Failed to instantiate drm object.");
         return;
     }