diff options
| author | 2018-10-10 16:10:43 -0700 | |
|---|---|---|
| committer | 2018-11-08 17:30:57 -0800 | |
| commit | 55d26249e14e930e7d92646332b7e4fc5ebf981a (patch) | |
| tree | 43cd9716d5aa026843f7ac3a476b7a72a4eb62f5 | |
| parent | b8d667222eda3a5eeec93e954ceb60318922e317 (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-x | api/current.txt | 6 | ||||
| -rw-r--r-- | media/java/android/media/MediaDrm.java | 81 | ||||
| -rw-r--r-- | media/jni/android_media_MediaDrm.cpp | 104 |
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 }, |