From aba15b69522f66fe2d1e8ead1735f811fc6e81a3 Mon Sep 17 00:00:00 2001 From: Shanuj Shekhar Date: Mon, 25 Sep 2023 11:34:30 -0400 Subject: [PATCH] Add new APIs to ExoMediaDrm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes --- - Added `removeOfflineLicense(byte[])` and `getOfflineLicenseKeySetIds` and consumed them in their implementations Background --- - These APIs will help in addressing an increasing amount of `java.lang.IllegalArgumentException: Failed to restore keys: BAD_VALUE` which is our top playback error in our app - Based on our discussion with Widevine team and [this exoplayer issue](https://github.com/google/ExoPlayer/issues/11202#issuecomment-1708792594) - TL;DR: The failure occurs on startup if the user has 200+ offline licenses, we would like to add the functionality to remove offline licenses **Note: Why we want these APIs in ExoMediaDrm and not in OfflineLicenseHelper** - As per the issue above, we would like to access these 2 public APIs in MediaDrm that don’t exist in `OfflineLicenseHelper` or `ExoMediaDrm` - APIs interested in: - [MediaDrm#removeOfflineLicense()](https://developer.android.com/reference/android/media/MediaDrm#removeOfflineLicense(byte%5B%5D)): To remove offline license - [MediaDrm#getOfflineLicenseKeySetIds()](https://developer.android.com/reference/android/media/MediaDrm#getOfflineLicenseKeySetIds()): To see number of offline licenses on startup - We use `OfflineLicenseHelper` to download license for L1 and we don't interact with `ExoMediaDrm` directly. But for the alternate Widevine integration, we directly depend on `ExoMediaDrm` APIs to override and call CDM Native APIs. - We would like to have the functionality of removing offline licenses for both integration which would need access to above APIs in `ExoMediaDrm`. Links --- - https://github.com/androidx/media/issues/659 --- .../exoplayer/drm/DummyExoMediaDrm.java | 12 ++++++++++ .../media3/exoplayer/drm/ExoMediaDrm.java | 15 +++++++++++++ .../exoplayer/drm/FrameworkMediaDrm.java | 22 +++++++++++++++++++ .../media3/test/utils/FakeExoMediaDrm.java | 11 ++++++++++ 4 files changed, 60 insertions(+) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java index 58efd2f082..24b0086c80 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DummyExoMediaDrm.java @@ -24,6 +24,7 @@ import androidx.media3.common.DrmInitData; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import androidx.media3.decoder.CryptoConfig; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -156,4 +157,15 @@ public final class DummyExoMediaDrm implements ExoMediaDrm { public @C.CryptoType int getCryptoType() { return C.CRYPTO_TYPE_UNSUPPORTED; } + + @Override + public void removeOfflineLicense(byte[] keySetId) { + // Should not be invoked. No session should exist. + throw new IllegalStateException(); + } + + @Override + public List getOfflineLicenseKeySetIds() { + return Collections.emptyList(); + } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java index 9bd4205a7d..6a94bdfe03 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/ExoMediaDrm.java @@ -583,4 +583,19 @@ public interface ExoMediaDrm { */ @C.CryptoType int getCryptoType(); + + /** + * Removes an offline license. + * This method is generally not needed, and should only be used if the preferred approach of passing {@link MediaDrm#KEY_TYPE_RELEASE} to {@link #getKeyRequest} is not possible. + * + * See {@link MediaDrm#removeOfflineLicense(byte[])} for more details. + */ + void removeOfflineLicense(byte[] keySetId); + + /** + * This method returns a list of the keySetIds for all offline licenses. + * + * See {@link MediaDrm#getOfflineLicenseKeySetIds()} for more details. + */ + List getOfflineLicenseKeySetIds(); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java index 9ed07382a9..21047593b9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/FrameworkMediaDrm.java @@ -26,6 +26,7 @@ import android.media.MediaDrmException; import android.media.NotProvisionedException; import android.media.UnsupportedSchemeException; import android.media.metrics.LogSessionId; +import android.os.Build; import android.os.PersistableBundle; import android.text.TextUtils; import androidx.annotation.DoNotInline; @@ -46,6 +47,7 @@ import com.google.common.base.Charsets; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -380,6 +382,26 @@ public final class FrameworkMediaDrm implements ExoMediaDrm { return C.CRYPTO_TYPE_FRAMEWORK; } + @Override + @RequiresApi(Build.VERSION_CODES.Q) + public void removeOfflineLicense(byte[] keySetId) { + if (Util.SDK_INT < Build.VERSION_CODES.Q) { + throw new UnsupportedOperationException(); + } + + mediaDrm.removeOfflineLicense(keySetId); + } + + @Override + @RequiresApi(Build.VERSION_CODES.Q) + public List getOfflineLicenseKeySetIds() { + if (Util.SDK_INT < Build.VERSION_CODES.Q) { + return Collections.emptyList(); + } + + return mediaDrm.getOfflineLicenseKeySetIds(); + } + private static SchemeData getSchemeData(UUID uuid, List schemeDatas) { if (!C.WIDEVINE_UUID.equals(uuid)) { // For non-Widevine CDMs always use the first scheme data. diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java index 4f802f073a..ce0339c889 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeExoMediaDrm.java @@ -43,6 +43,7 @@ import com.google.common.primitives.Bytes; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -413,6 +414,16 @@ public final class FakeExoMediaDrm implements ExoMediaDrm { return FakeCryptoConfig.TYPE; } + @Override + public void removeOfflineLicense(byte[] keySetId) { + throw new UnsupportedOperationException(); + } + + @Override + public List getOfflineLicenseKeySetIds() { + return Collections.emptyList(); + } + // Methods to facilitate testing public int getReferenceCount() {