summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jeff Tinker <jtinker@google.com> 2018-10-10 16:10:43 -0700
committer Jeff Tinker <jtinker@google.com> 2018-11-08 17:30:57 -0800
commit55d26249e14e930e7d92646332b7e4fc5ebf981a (patch)
tree43cd9716d5aa026843f7ac3a476b7a72a4eb62f5
parentb8d667222eda3a5eeec93e954ceb60318922e317 (diff)
Add new offline management APIs to MediaDrm
bug:110838441 bug:117570956 bug:116252891 test: android.media.cts.MediaDrmClearkeyTest#testOfflineKeyManagement make offline-sdk-docs Change-Id: I5561502c308fbdc2b669120c3c7e8c0544b13b59
-rwxr-xr-xapi/current.txt6
-rw-r--r--media/java/android/media/MediaDrm.java81
-rw-r--r--media/jni/android_media_MediaDrm.cpp104
3 files changed, 179 insertions, 12 deletions
diff --git a/api/current.txt b/api/current.txt
index 36b0fd7e346d..0d79c203230b 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -24190,6 +24190,8 @@ package android.media {
method public static int getMaxSecurityLevel();
method public int getMaxSessionCount();
method public android.os.PersistableBundle getMetrics();
+ method public java.util.List<byte[]> getOfflineLicenseKeySetIds();
+ method public int getOfflineLicenseState(byte[]);
method public int getOpenSessionCount();
method public byte[] getPropertyByteArray(java.lang.String);
method public java.lang.String getPropertyString(java.lang.String);
@@ -24210,6 +24212,7 @@ package android.media {
method public void releaseSecureStops(byte[]);
method public void removeAllSecureStops();
method public void removeKeys(byte[]);
+ method public void removeOfflineLicense(byte[]);
method public void removeSecureStop(byte[]);
method public void restoreKeys(byte[], byte[]);
method public void setOnEventListener(android.media.MediaDrm.OnEventListener);
@@ -24232,6 +24235,9 @@ package android.media {
field public static final int KEY_TYPE_OFFLINE = 2; // 0x2
field public static final int KEY_TYPE_RELEASE = 3; // 0x3
field public static final int KEY_TYPE_STREAMING = 1; // 0x1
+ field public static final int OFFLINE_LICENSE_INACTIVE = 2; // 0x2
+ field public static final int OFFLINE_LICENSE_STATE_UNKNOWN = 0; // 0x0
+ field public static final int OFFLINE_LICENSE_USABLE = 1; // 0x1
field public static final java.lang.String PROPERTY_ALGORITHMS = "algorithms";
field public static final java.lang.String PROPERTY_DESCRIPTION = "description";
field public static final java.lang.String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index ee12b913765a..24b7f36e228b 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -979,6 +979,87 @@ public final class MediaDrm implements AutoCloseable {
throws DeniedByServerException;
/**
+ * The keys in an offline license allow protected content to be played even
+ * if the device is not connected to a network. Offline licenses are stored
+ * on the device after a key request/response exchange when the key request
+ * KeyType is OFFLINE. Normally each app is responsible for keeping track of
+ * the keySetIds it has created. If an app loses the keySetId for any stored
+ * licenses that it created, however, it must be able to recover the stored
+ * keySetIds so those licenses can be removed when they expire or when the
+ * app is uninstalled.
+ * <p>
+ * This method returns a list of the keySetIds for all offline licenses.
+ * The offline license keySetId may be used to query the status of an
+ * offline license with {@link #getOfflineLicenseState} or remove it with
+ * {@link #removeOfflineLicense}.
+ *
+ * @return a list of offline license keySetIds
+ */
+ @NonNull
+ public native List<byte[]> getOfflineLicenseKeySetIds();
+
+ /**
+ * Normally offline licenses are released using a key request/response
+ * exchange using {@link #getKeyRequest} where the key type is
+ * KEY_TYPE_RELEASE, followed by {@link #provideKeyResponse}. This allows
+ * the server to cryptographically confirm that the license has been removed
+ * and then adjust the count of offline licenses allocated to the device.
+ * <p>
+ * In some exceptional situations it may be necessary to directly remove
+ * offline licenses without notifying the server, which may be performed
+ * using this method.
+ *
+ * @param keySetId the id of the offline license to remove
+ * @throws IllegalArgumentException if the keySetId does not refer to an
+ * offline license.
+ */
+ public native void removeOfflineLicense(@NonNull byte[] keySetId);
+
+ /**
+ * Offline license state is unknown, an error occurred while trying
+ * to access it.
+ */
+ public static final int OFFLINE_LICENSE_STATE_UNKNOWN = 0;
+
+ /**
+ * Offline license state is usable, the keys may be used for decryption.
+ */
+ public static final int OFFLINE_LICENSE_USABLE = 1;
+
+ /**
+ * Offline license state is inactive, the keys have been marked for
+ * release using {@link #getKeyRequest} with KEY_TYPE_RELEASE but the
+ * key response has not been received.
+ */
+ public static final int OFFLINE_LICENSE_INACTIVE = 2;
+
+ /** @hide */
+ @IntDef({
+ OFFLINE_LICENSE_STATE_UNKNOWN,
+ OFFLINE_LICENSE_USABLE,
+ OFFLINE_LICENSE_INACTIVE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface OfflineLicenseState {}
+
+ /**
+ * Request the state of an offline license. An offline license may be usable
+ * or inactive. The keys in a usable offline license are available for
+ * decryption. When the offline license state is inactive, the keys have
+ * been marked for release using {@link #getKeyRequest} with
+ * KEY_TYPE_RELEASE but the key response has not been received. The keys in
+ * an inactive offline license are not usable for decryption.
+ *
+ * @param keySetId selects the offline license
+ * @return the offline license state, one of {@link #OFFLINE_LICENSE_USABLE},
+ * {@link #OFFLINE_LICENSE_INACTIVE} or {@link #OFFLINE_LICENSE_STATE_UNKNOWN}.
+ * @throws IllegalArgumentException if the keySetId does not refer to an
+ * offline license.
+ */
+ @OfflineLicenseState
+ public native int getOfflineLicenseState(@NonNull byte[] keySetId);
+
+ /**
* Secure stops are a way to enforce limits on the number of concurrent
* streams per subscriber across devices. They provide secure monitoring of
* the lifetime of content decryption keys in MediaDrm sessions.
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 8637adae3aa5..be71dad571bb 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -161,6 +161,12 @@ struct SecurityLevels {
jint kSecurityLevelHwSecureAll;
} gSecurityLevels;
+struct OfflineLicenseState {
+ jint kOfflineLicenseStateUsable;
+ jint kOfflineLicenseStateInactive;
+ jint kOfflineLicenseStateUnknown;
+} gOfflineLicenseStates;
+
struct fields_t {
jfieldID context;
@@ -740,6 +746,15 @@ static void android_media_MediaDrm_native_init(JNIEnv *env) {
GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_ALL", "I");
gSecurityLevels.kSecurityLevelHwSecureAll = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_USABLE", "I");
+ gOfflineLicenseStates.kOfflineLicenseStateUsable = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_INACTIVE", "I");
+ gOfflineLicenseStates.kOfflineLicenseStateInactive = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_STATE_UNKNOWN", "I");
+ gOfflineLicenseStates.kOfflineLicenseStateUnknown = env->GetStaticIntField(clazz, field);
+
+ GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_CRYPTO", "I");
+
jmethodID getMaxSecurityLevel;
GET_STATIC_METHOD_ID(getMaxSecurityLevel, clazz, "getMaxSecurityLevel", "()I");
gSecurityLevels.kSecurityLevelMax = env->CallStaticIntMethod(clazz, getMaxSecurityLevel);
@@ -890,9 +905,7 @@ static jbyteArray android_media_MediaDrm_openSession(
JNIEnv *env, jobject thiz, jint jlevel) {
sp<IDrm> drm = GetDrm(env, thiz);
- if (drm == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "MediaDrm obj is null");
+ if (!CheckDrm(env, drm)) {
return NULL;
}
@@ -1070,6 +1083,10 @@ static void android_media_MediaDrm_removeKeys(
JNIEnv *env, jobject thiz, jbyteArray jkeysetId) {
sp<IDrm> drm = GetDrm(env, thiz);
+ if (!CheckDrm(env, drm)) {
+ return;
+ }
+
if (jkeysetId == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException",
"keySetId is null");
@@ -1231,9 +1248,7 @@ static jobject android_media_MediaDrm_getSecureStopIds(
JNIEnv *env, jobject thiz) {
sp<IDrm> drm = GetDrm(env, thiz);
- if (drm == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "MediaDrm obj is null");
+ if (!CheckDrm(env, drm)) {
return NULL;
}
@@ -1286,9 +1301,7 @@ static void android_media_MediaDrm_removeSecureStop(
JNIEnv *env, jobject thiz, jbyteArray ssid) {
sp<IDrm> drm = GetDrm(env, thiz);
- if (drm == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "MediaDrm obj is null");
+ if (!CheckDrm(env, drm)) {
return;
}
@@ -1437,6 +1450,65 @@ static jint android_media_MediaDrm_getSecurityLevel(JNIEnv *env,
}
}
+static jobject android_media_MediaDrm_getOfflineLicenseKeySetIds(
+ JNIEnv *env, jobject thiz) {
+ sp<IDrm> drm = GetDrm(env, thiz);
+
+ if (!CheckDrm(env, drm)) {
+ return NULL;
+ }
+
+ List<Vector<uint8_t> > keySetIds;
+
+ status_t err = drm->getOfflineLicenseKeySetIds(keySetIds);
+
+ if (throwExceptionAsNecessary(env, err, "Failed to get offline key set Ids")) {
+ return NULL;
+ }
+
+ return ListOfVectorsToArrayListOfByteArray(env, keySetIds);
+}
+
+static void android_media_MediaDrm_removeOfflineLicense(
+ JNIEnv *env, jobject thiz, jbyteArray keySetId) {
+ sp<IDrm> drm = GetDrm(env, thiz);
+
+ if (!CheckDrm(env, drm)) {
+ return;
+ }
+
+ status_t err = drm->removeOfflineLicense(JByteArrayToVector(env, keySetId));
+
+ throwExceptionAsNecessary(env, err, "Failed to remove offline license");
+}
+
+static jint android_media_MediaDrm_getOfflineLicenseState(JNIEnv *env,
+ jobject thiz, jbyteArray jkeySetId) {
+ sp<IDrm> drm = GetDrm(env, thiz);
+
+ if (!CheckDrm(env, drm)) {
+ return gOfflineLicenseStates.kOfflineLicenseStateUnknown;
+ }
+
+ Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeySetId));
+
+ DrmPlugin::OfflineLicenseState state = DrmPlugin::kOfflineLicenseStateUnknown;
+
+ status_t err = drm->getOfflineLicenseState(keySetId, &state);
+
+ if (throwExceptionAsNecessary(env, err, "Failed to get offline license state")) {
+ return gOfflineLicenseStates.kOfflineLicenseStateUnknown;
+ }
+
+ switch(state) {
+ case DrmPlugin::kOfflineLicenseStateUsable:
+ return gOfflineLicenseStates.kOfflineLicenseStateUsable;
+ case DrmPlugin::kOfflineLicenseStateInactive:
+ return gOfflineLicenseStates.kOfflineLicenseStateInactive;
+ default:
+ return gOfflineLicenseStates.kOfflineLicenseStateUnknown;
+ }
+}
static jstring android_media_MediaDrm_getPropertyString(
JNIEnv *env, jobject thiz, jstring jname) {
@@ -1718,9 +1790,8 @@ static jobject
android_media_MediaDrm_native_getMetrics(JNIEnv *env, jobject thiz)
{
sp<IDrm> drm = GetDrm(env, thiz);
- if (drm == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "MediaDrm obj is null");
+
+ if (!CheckDrm(env, drm)) {
return NULL;
}
@@ -1839,6 +1910,15 @@ static const JNINativeMethod gMethods[] = {
{ "getSecurityLevel", "([B)I",
(void *)android_media_MediaDrm_getSecurityLevel },
+ { "removeOfflineLicense", "([B)V",
+ (void *)android_media_MediaDrm_removeOfflineLicense },
+
+ { "getOfflineLicenseKeySetIds", "()Ljava/util/List;",
+ (void *)android_media_MediaDrm_getOfflineLicenseKeySetIds },
+
+ { "getOfflineLicenseState", "([B)I",
+ (void *)android_media_MediaDrm_getOfflineLicenseState },
+
{ "getPropertyString", "(Ljava/lang/String;)Ljava/lang/String;",
(void *)android_media_MediaDrm_getPropertyString },