Add a test for the provisioning flow to DefaultDrmSessionManagerTest
#minor-release PiperOrigin-RevId: 379913814
This commit is contained in:
parent
6c05a469cb
commit
0f23fddeef
@ -43,9 +43,7 @@ import org.robolectric.shadows.ShadowLooper;
|
|||||||
// TODO: Test more branches:
|
// TODO: Test more branches:
|
||||||
// - Different sources for licenseServerUrl.
|
// - Different sources for licenseServerUrl.
|
||||||
// - Multiple acquisitions & releases for same keys -> multiple requests.
|
// - Multiple acquisitions & releases for same keys -> multiple requests.
|
||||||
// - Provisioning.
|
|
||||||
// - Key denial.
|
// - Key denial.
|
||||||
// - Handling of ResourceBusyException (indicating session scarcity).
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class DefaultDrmSessionManagerTest {
|
public class DefaultDrmSessionManagerTest {
|
||||||
|
|
||||||
@ -538,6 +536,33 @@ public class DefaultDrmSessionManagerTest {
|
|||||||
exoMediaDrm.release();
|
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
|
@Test
|
||||||
public void managerNotPrepared_acquireSessionAndPreacquireSessionFail() throws Exception {
|
public void managerNotPrepared_acquireSessionAndPreacquireSessionFail() throws Exception {
|
||||||
FakeExoMediaDrm.LicenseServer licenseServer =
|
FakeExoMediaDrm.LicenseServer licenseServer =
|
||||||
@ -602,10 +627,10 @@ public class DefaultDrmSessionManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void waitForOpenedWithKeys(DrmSession drmSession) {
|
private static void waitForOpenedWithKeys(DrmSession drmSession) {
|
||||||
|
while (drmSession.getState() != DrmSession.STATE_OPENED_WITH_KEYS) {
|
||||||
// Check the error first, so we get a meaningful failure if there's been an error.
|
// Check the error first, so we get a meaningful failure if there's been an error.
|
||||||
assertThat(drmSession.getError()).isNull();
|
assertThat(drmSession.getError()).isNull();
|
||||||
assertThat(drmSession.getState()).isEqualTo(DrmSession.STATE_OPENED);
|
assertThat(drmSession.getState()).isAnyOf(DrmSession.STATE_OPENING, DrmSession.STATE_OPENED);
|
||||||
while (drmSession.getState() != DrmSession.STATE_OPENED_WITH_KEYS) {
|
|
||||||
// Allow the key response to be handled.
|
// Allow the key response to be handled.
|
||||||
ShadowLooper.idleMainLooper();
|
ShadowLooper.idleMainLooper();
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ import com.google.common.collect.ImmutableMap;
|
|||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -61,8 +62,59 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
@RequiresApi(29)
|
@RequiresApi(29)
|
||||||
public final class FakeExoMediaDrm implements ExoMediaDrm {
|
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.
|
||||||
|
*
|
||||||
|
* <p>An unprovisioned {@link FakeExoMediaDrm} will throw {@link NotProvisionedException} from
|
||||||
|
* {@link FakeExoMediaDrm#openSession()} until enough valid provisioning responses are passed to
|
||||||
|
* {@link FakeExoMediaDrm#provideProvisionResponse(byte[])}.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>If this is exceeded then subsequent calls to {@link FakeExoMediaDrm#openSession()} will
|
||||||
|
* throw {@link ResourceBusyException}.
|
||||||
|
*
|
||||||
|
* <p>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 =
|
public static final ProvisionRequest FAKE_PROVISION_REQUEST =
|
||||||
new ProvisionRequest(TestUtil.createByteArray(7, 8, 9), "bar.test");
|
new ProvisionRequest(TestUtil.createByteArray(7, 8, 9), "bar.test");
|
||||||
|
public static final ImmutableList<Byte> VALID_PROVISION_RESPONSE =
|
||||||
|
TestUtil.createByteList(4, 5, 6);
|
||||||
|
|
||||||
/** Key for use with the Map returned from {@link FakeExoMediaDrm#queryKeyStatus(byte[])}. */
|
/** Key for use with the Map returned from {@link FakeExoMediaDrm#queryKeyStatus(byte[])}. */
|
||||||
public static final String KEY_STATUS_KEY = "KEY_STATUS";
|
public static final String KEY_STATUS_KEY = "KEY_STATUS";
|
||||||
@ -74,6 +126,7 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
|||||||
private static final ImmutableList<Byte> VALID_KEY_RESPONSE = TestUtil.createByteList(1, 2, 3);
|
private static final ImmutableList<Byte> VALID_KEY_RESPONSE = TestUtil.createByteList(1, 2, 3);
|
||||||
private static final ImmutableList<Byte> KEY_DENIED_RESPONSE = TestUtil.createByteList(9, 8, 7);
|
private static final ImmutableList<Byte> KEY_DENIED_RESPONSE = TestUtil.createByteList(9, 8, 7);
|
||||||
|
|
||||||
|
private final int provisionsRequired;
|
||||||
private final int maxConcurrentSessions;
|
private final int maxConcurrentSessions;
|
||||||
private final Map<String, byte[]> byteProperties;
|
private final Map<String, byte[]> byteProperties;
|
||||||
private final Map<String, String> stringProperties;
|
private final Map<String, String> stringProperties;
|
||||||
@ -81,24 +134,24 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
|||||||
private final Set<List<Byte>> sessionIdsWithValidKeys;
|
private final Set<List<Byte>> sessionIdsWithValidKeys;
|
||||||
private final AtomicInteger sessionIdGenerator;
|
private final AtomicInteger sessionIdGenerator;
|
||||||
|
|
||||||
|
private int provisionsReceived;
|
||||||
private int referenceCount;
|
private int referenceCount;
|
||||||
@Nullable private OnEventListener onEventListener;
|
@Nullable private OnEventListener onEventListener;
|
||||||
|
|
||||||
/**
|
/** @deprecated Use {@link Builder} instead. */
|
||||||
* Constructs an instance that returns random and unique {@code sessionIds} for subsequent calls
|
@Deprecated
|
||||||
* to {@link #openSession()} with no limit on the number of concurrent open sessions.
|
|
||||||
*/
|
|
||||||
public FakeExoMediaDrm() {
|
public FakeExoMediaDrm() {
|
||||||
this(/* maxConcurrentSessions= */ Integer.MAX_VALUE);
|
this(/* maxConcurrentSessions= */ Integer.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** @deprecated Use {@link Builder} instead. */
|
||||||
* Constructs an instance that returns random and unique {@code sessionIds} for subsequent calls
|
@Deprecated
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
public FakeExoMediaDrm(int maxConcurrentSessions) {
|
public FakeExoMediaDrm(int maxConcurrentSessions) {
|
||||||
|
this(/* provisionsRequired= */ 0, maxConcurrentSessions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FakeExoMediaDrm(int provisionsRequired, int maxConcurrentSessions) {
|
||||||
|
this.provisionsRequired = provisionsRequired;
|
||||||
this.maxConcurrentSessions = maxConcurrentSessions;
|
this.maxConcurrentSessions = maxConcurrentSessions;
|
||||||
byteProperties = new HashMap<>();
|
byteProperties = new HashMap<>();
|
||||||
stringProperties = new HashMap<>();
|
stringProperties = new HashMap<>();
|
||||||
@ -129,6 +182,9 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
|||||||
@Override
|
@Override
|
||||||
public byte[] openSession() throws MediaDrmException {
|
public byte[] openSession() throws MediaDrmException {
|
||||||
Assertions.checkState(referenceCount > 0);
|
Assertions.checkState(referenceCount > 0);
|
||||||
|
if (provisionsReceived < provisionsRequired) {
|
||||||
|
throw new NotProvisionedException("Not provisioned.");
|
||||||
|
}
|
||||||
if (openSessionIds.size() >= maxConcurrentSessions) {
|
if (openSessionIds.size() >= maxConcurrentSessions) {
|
||||||
throw new ResourceBusyException("Too many sessions open. max=" + maxConcurrentSessions);
|
throw new ResourceBusyException("Too many sessions open. max=" + maxConcurrentSessions);
|
||||||
}
|
}
|
||||||
@ -200,6 +256,9 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
|||||||
@Override
|
@Override
|
||||||
public void provideProvisionResponse(byte[] response) throws DeniedByServerException {
|
public void provideProvisionResponse(byte[] response) throws DeniedByServerException {
|
||||||
Assertions.checkState(referenceCount > 0);
|
Assertions.checkState(referenceCount > 0);
|
||||||
|
if (Bytes.asList(response).equals(VALID_PROVISION_RESPONSE)) {
|
||||||
|
provisionsReceived++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -340,7 +399,11 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
|||||||
@Override
|
@Override
|
||||||
public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request)
|
public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request)
|
||||||
throws MediaDrmCallbackException {
|
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
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user