diff --git a/library/core/src/test/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManagerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManagerTest.java index d58a55e444..cc3c5f0bd7 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManagerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManagerTest.java @@ -43,9 +43,7 @@ import org.robolectric.shadows.ShadowLooper; // TODO: Test more branches: // - Different sources for licenseServerUrl. // - Multiple acquisitions & releases for same keys -> multiple requests. -// - Provisioning. // - Key denial. -// - Handling of ResourceBusyException (indicating session scarcity). @RunWith(AndroidJUnit4.class) public class DefaultDrmSessionManagerTest { @@ -538,6 +536,33 @@ public class DefaultDrmSessionManagerTest { exoMediaDrm.release(); } + @Test + public void deviceNotProvisioned_provisioningDoneAndOpenSessionRetried() { + FakeExoMediaDrm.LicenseServer licenseServer = + FakeExoMediaDrm.LicenseServer.allowingSchemeDatas(DRM_SCHEME_DATAS); + + DefaultDrmSessionManager drmSessionManager = + new DefaultDrmSessionManager.Builder() + .setUuidAndExoMediaDrmProvider( + DRM_SCHEME_UUID, + uuid -> new FakeExoMediaDrm.Builder().setProvisionsRequired(1).build()) + .build(/* mediaDrmCallback= */ licenseServer); + drmSessionManager.prepare(); + DrmSession drmSession = + checkNotNull( + drmSessionManager.acquireSession( + /* playbackLooper= */ checkNotNull(Looper.myLooper()), + /* eventDispatcher= */ null, + FORMAT_WITH_DRM_INIT_DATA)); + // Confirm the device isn't provisioned (otherwise state would be OPENED) + assertThat(drmSession.getState()).isEqualTo(DrmSession.STATE_OPENING); + waitForOpenedWithKeys(drmSession); + + assertThat(drmSession.getState()).isEqualTo(DrmSession.STATE_OPENED_WITH_KEYS); + assertThat(drmSession.queryKeyStatus()) + .containsExactly(FakeExoMediaDrm.KEY_STATUS_KEY, FakeExoMediaDrm.KEY_STATUS_AVAILABLE); + } + @Test public void managerNotPrepared_acquireSessionAndPreacquireSessionFail() throws Exception { FakeExoMediaDrm.LicenseServer licenseServer = @@ -602,10 +627,10 @@ public class DefaultDrmSessionManagerTest { } private static void waitForOpenedWithKeys(DrmSession drmSession) { - // Check the error first, so we get a meaningful failure if there's been an error. - assertThat(drmSession.getError()).isNull(); - assertThat(drmSession.getState()).isEqualTo(DrmSession.STATE_OPENED); while (drmSession.getState() != DrmSession.STATE_OPENED_WITH_KEYS) { + // Check the error first, so we get a meaningful failure if there's been an error. + assertThat(drmSession.getError()).isNull(); + assertThat(drmSession.getState()).isAnyOf(DrmSession.STATE_OPENING, DrmSession.STATE_OPENED); // Allow the key response to be handled. ShadowLooper.idleMainLooper(); } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExoMediaDrm.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExoMediaDrm.java index 14663985ab..608347c569 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExoMediaDrm.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExoMediaDrm.java @@ -39,6 +39,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.primitives.Bytes; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -61,8 +62,59 @@ import java.util.concurrent.atomic.AtomicInteger; @RequiresApi(29) public final class FakeExoMediaDrm implements ExoMediaDrm { + /** Builder for {@link FakeExoMediaDrm} instances. */ + public static class Builder { + private int provisionsRequired; + private int maxConcurrentSessions; + + /** Constructs an instance. */ + public Builder() { + provisionsRequired = 0; + maxConcurrentSessions = Integer.MAX_VALUE; + } + + /** + * Sets how many successful provisioning round trips are needed for the {@link FakeExoMediaDrm} + * to be provisioned. + * + *
An unprovisioned {@link FakeExoMediaDrm} will throw {@link NotProvisionedException} from + * {@link FakeExoMediaDrm#openSession()} until enough valid provisioning responses are passed to + * {@link FakeExoMediaDrm#provideProvisionResponse(byte[])}. + * + *
Defaults to 0 (i.e. device is already provisioned). + */ + public Builder setProvisionsRequired(int provisionsRequired) { + this.provisionsRequired = provisionsRequired; + return this; + } + + /** + * Sets the maximum number of concurrent sessions the {@link FakeExoMediaDrm} will support. + * + *
If this is exceeded then subsequent calls to {@link FakeExoMediaDrm#openSession()} will + * throw {@link ResourceBusyException}. + * + *
Defaults to {@link Integer#MAX_VALUE}.
+ */
+ public Builder setMaxConcurrentSessions(int maxConcurrentSessions) {
+ this.maxConcurrentSessions = maxConcurrentSessions;
+ return this;
+ }
+
+ /**
+ * Returns a {@link FakeExoMediaDrm} instance with an initial reference count of 1. The caller
+ * is responsible for calling {@link FakeExoMediaDrm#release()} when they no longer need the
+ * instance.
+ */
+ public FakeExoMediaDrm build() {
+ return new FakeExoMediaDrm(provisionsRequired, maxConcurrentSessions);
+ }
+ }
+
public static final ProvisionRequest FAKE_PROVISION_REQUEST =
new ProvisionRequest(TestUtil.createByteArray(7, 8, 9), "bar.test");
+ public static final ImmutableList> sessionIdsWithValidKeys;
private final AtomicInteger sessionIdGenerator;
+ private int provisionsReceived;
private int referenceCount;
@Nullable private OnEventListener onEventListener;
- /**
- * Constructs an instance that returns random and unique {@code sessionIds} for subsequent calls
- * to {@link #openSession()} with no limit on the number of concurrent open sessions.
- */
+ /** @deprecated Use {@link Builder} instead. */
+ @Deprecated
public FakeExoMediaDrm() {
this(/* maxConcurrentSessions= */ Integer.MAX_VALUE);
}
- /**
- * Constructs an instance that returns random and unique {@code sessionIds} for subsequent calls
- * to {@link #openSession()} with a limit on the number of concurrent open sessions.
- *
- * @param maxConcurrentSessions The max number of sessions allowed to be open simultaneously.
- */
+ /** @deprecated Use {@link Builder} instead. */
+ @Deprecated
public FakeExoMediaDrm(int maxConcurrentSessions) {
+ this(/* provisionsRequired= */ 0, maxConcurrentSessions);
+ }
+
+ private FakeExoMediaDrm(int provisionsRequired, int maxConcurrentSessions) {
+ this.provisionsRequired = provisionsRequired;
this.maxConcurrentSessions = maxConcurrentSessions;
byteProperties = new HashMap<>();
stringProperties = new HashMap<>();
@@ -129,6 +182,9 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
@Override
public byte[] openSession() throws MediaDrmException {
Assertions.checkState(referenceCount > 0);
+ if (provisionsReceived < provisionsRequired) {
+ throw new NotProvisionedException("Not provisioned.");
+ }
if (openSessionIds.size() >= maxConcurrentSessions) {
throw new ResourceBusyException("Too many sessions open. max=" + maxConcurrentSessions);
}
@@ -200,6 +256,9 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
@Override
public void provideProvisionResponse(byte[] response) throws DeniedByServerException {
Assertions.checkState(referenceCount > 0);
+ if (Bytes.asList(response).equals(VALID_PROVISION_RESPONSE)) {
+ provisionsReceived++;
+ }
}
@Override
@@ -340,7 +399,11 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
@Override
public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request)
throws MediaDrmCallbackException {
- return new byte[0];
+ if (Arrays.equals(request.getData(), FAKE_PROVISION_REQUEST.getData())) {
+ return Bytes.toArray(VALID_PROVISION_RESPONSE);
+ } else {
+ return Util.EMPTY_BYTE_ARRAY;
+ }
}
@Override