Add workarounds for NoSuchMethodError
from DRM framework exceptions
Issue: androidx/media#1145 #minor-release PiperOrigin-RevId: 613573868
This commit is contained in:
parent
f02dc8e528
commit
a604600126
@ -48,6 +48,10 @@
|
||||
* Metadata:
|
||||
* Image:
|
||||
* DRM:
|
||||
* Work around a `NoSuchMethodError` which can be thrown by the `MediaDrm`
|
||||
framework instead of `ResourceBusyException` or
|
||||
`NotProvisionedException` on some Android 14 devices
|
||||
([#1145](https://github.com/androidx/media/issues/1145)).
|
||||
* DataSource:
|
||||
* Implement support for `android.resource://package/id` raw resource URIs
|
||||
where `package` is different to the package of the current application.
|
||||
|
@ -394,9 +394,14 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
return true;
|
||||
} catch (NotProvisionedException e) {
|
||||
provisioningManager.provisionRequired(this);
|
||||
} catch (Exception e) {
|
||||
} catch (Exception | NoSuchMethodError e) {
|
||||
// Work around b/291440132.
|
||||
if (DrmUtil.isFailureToConstructNotProvisionedException(e)) {
|
||||
provisioningManager.provisionRequired(this);
|
||||
} else {
|
||||
onError(e, DrmUtil.ERROR_SOURCE_EXO_MEDIA_DRM);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -472,7 +477,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
try {
|
||||
mediaDrm.restoreKeys(sessionId, offlineLicenseKeySetId);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
} catch (Exception | NoSuchMethodError e) {
|
||||
onError(e, DrmUtil.ERROR_SOURCE_EXO_MEDIA_DRM);
|
||||
}
|
||||
return false;
|
||||
@ -492,7 +497,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
currentKeyRequest = mediaDrm.getKeyRequest(scope, schemeDatas, type, keyRequestParameters);
|
||||
Util.castNonNull(requestHandler)
|
||||
.post(MSG_KEYS, Assertions.checkNotNull(currentKeyRequest), allowRetry);
|
||||
} catch (Exception e) {
|
||||
} catch (Exception | NoSuchMethodError e) {
|
||||
onKeysError(e, /* thrownByExoMediaDrm= */ true);
|
||||
}
|
||||
}
|
||||
@ -504,8 +509,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
}
|
||||
currentKeyRequest = null;
|
||||
|
||||
if (response instanceof Exception) {
|
||||
onKeysError((Exception) response, /* thrownByExoMediaDrm= */ false);
|
||||
if (response instanceof Exception || response instanceof NoSuchMethodError) {
|
||||
onKeysError((Throwable) response, /* thrownByExoMediaDrm= */ false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -526,7 +531,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
state = STATE_OPENED_WITH_KEYS;
|
||||
dispatchEvent(DrmSessionEventListener.EventDispatcher::drmKeysLoaded);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (Exception | NoSuchMethodError e) {
|
||||
onKeysError(e, /* thrownByExoMediaDrm= */ true);
|
||||
}
|
||||
}
|
||||
@ -538,8 +543,12 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
}
|
||||
}
|
||||
|
||||
private void onKeysError(Exception e, boolean thrownByExoMediaDrm) {
|
||||
if (e instanceof NotProvisionedException) {
|
||||
/**
|
||||
* @param e Must be an instance of either {@link Exception} or {@link Error}.
|
||||
*/
|
||||
private void onKeysError(Throwable e, boolean thrownByExoMediaDrm) {
|
||||
if (e instanceof NotProvisionedException
|
||||
|| DrmUtil.isFailureToConstructNotProvisionedException(e)) {
|
||||
provisioningManager.provisionRequired(this);
|
||||
} else {
|
||||
onError(
|
||||
@ -550,11 +559,24 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
}
|
||||
}
|
||||
|
||||
private void onError(Exception e, @DrmUtil.ErrorSource int errorSource) {
|
||||
/**
|
||||
* @param e Must be an instance of either {@link Exception} or {@link Error}.
|
||||
*/
|
||||
private void onError(Throwable e, @DrmUtil.ErrorSource int errorSource) {
|
||||
lastException =
|
||||
new DrmSessionException(e, DrmUtil.getErrorCodeForMediaDrmException(e, errorSource));
|
||||
Log.e(TAG, "DRM session error", e);
|
||||
dispatchEvent(eventDispatcher -> eventDispatcher.drmSessionManagerError(e));
|
||||
if (e instanceof Exception) {
|
||||
dispatchEvent(eventDispatcher -> eventDispatcher.drmSessionManagerError((Exception) e));
|
||||
} else if (e instanceof Error) {
|
||||
// Re-throw all Error types except a NoSuchMethodError caused by b/291440132.
|
||||
if (!DrmUtil.isFailureToConstructResourceBusyException(e)
|
||||
&& !DrmUtil.isFailureToConstructNotProvisionedException(e)) {
|
||||
throw (Error) e;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected Throwable subclass", e);
|
||||
}
|
||||
if (state != STATE_OPENED_WITH_KEYS) {
|
||||
state = STATE_ERROR;
|
||||
}
|
||||
|
@ -655,8 +655,12 @@ public class DefaultDrmSessionManager implements DrmSessionManager {
|
||||
}
|
||||
|
||||
private static boolean acquisitionFailedIndicatingResourceShortage(DrmSession session) {
|
||||
return session.getState() == DrmSession.STATE_ERROR
|
||||
&& checkNotNull(session.getError()).getCause() instanceof ResourceBusyException;
|
||||
if (session.getState() != DrmSession.STATE_ERROR) {
|
||||
return false;
|
||||
}
|
||||
@Nullable Throwable cause = checkNotNull(session.getError()).getCause();
|
||||
return cause instanceof ResourceBusyException
|
||||
|| DrmUtil.isFailureToConstructResourceBusyException(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,6 +25,7 @@ import android.media.DeniedByServerException;
|
||||
import android.media.MediaDrm;
|
||||
import android.media.MediaDrmResetException;
|
||||
import android.media.NotProvisionedException;
|
||||
import android.media.ResourceBusyException;
|
||||
import androidx.annotation.DoNotInline;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -75,12 +76,13 @@ public final class DrmUtil {
|
||||
* exception.
|
||||
*/
|
||||
public static @PlaybackException.ErrorCode int getErrorCodeForMediaDrmException(
|
||||
Exception exception, @ErrorSource int errorSource) {
|
||||
Throwable exception, @ErrorSource int errorSource) {
|
||||
if (Util.SDK_INT >= 21 && Api21.isMediaDrmStateException(exception)) {
|
||||
return Api21.mediaDrmStateExceptionToErrorCode(exception);
|
||||
} else if (Util.SDK_INT >= 23 && Api23.isMediaDrmResetException(exception)) {
|
||||
return PlaybackException.ERROR_CODE_DRM_SYSTEM_ERROR;
|
||||
} else if (exception instanceof NotProvisionedException) {
|
||||
} else if (exception instanceof NotProvisionedException
|
||||
|| isFailureToConstructNotProvisionedException(exception)) {
|
||||
return PlaybackException.ERROR_CODE_DRM_PROVISIONING_FAILED;
|
||||
} else if (exception instanceof DeniedByServerException) {
|
||||
return PlaybackException.ERROR_CODE_DRM_DEVICE_REVOKED;
|
||||
@ -104,6 +106,28 @@ public final class DrmUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if {@code e} represents a failure to construct a {@link NotProvisionedException}.
|
||||
* See b/291440132.
|
||||
*/
|
||||
public static boolean isFailureToConstructNotProvisionedException(@Nullable Throwable e) {
|
||||
return Util.SDK_INT == 34
|
||||
&& e instanceof NoSuchMethodError
|
||||
&& e.getMessage() != null
|
||||
&& e.getMessage().contains("Landroid/media/NotProvisionedException;.<init>(");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if {@code e} represents a failure to construct a {@link ResourceBusyException}.
|
||||
* See b/291440132.
|
||||
*/
|
||||
public static boolean isFailureToConstructResourceBusyException(@Nullable Throwable e) {
|
||||
return Util.SDK_INT == 34
|
||||
&& e instanceof NoSuchMethodError
|
||||
&& e.getMessage() != null
|
||||
&& e.getMessage().contains("Landroid/media/ResourceBusyException;.<init>(");
|
||||
}
|
||||
|
||||
// Internal classes.
|
||||
|
||||
@RequiresApi(21)
|
||||
|
@ -40,6 +40,7 @@ import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowLooper;
|
||||
|
||||
/** Tests for {@link DefaultDrmSessionManager} and {@link DefaultDrmSession}. */
|
||||
@ -258,6 +259,21 @@ public class DefaultDrmSessionManagerTest {
|
||||
|
||||
@Test(timeout = 10_000)
|
||||
public void maxConcurrentSessionsExceeded_allKeepAliveSessionsEagerlyReleased() throws Exception {
|
||||
maxConcurrentSessionsExceededAllKeepAliveSessionsEagerlyReleased(
|
||||
/* throwNoSuchMethodErrorForResourceBusy= */ false);
|
||||
}
|
||||
|
||||
/** Testing workarounds for b/291440132. */
|
||||
@Config(sdk = 34)
|
||||
@Test(timeout = 10_000)
|
||||
public void maxConcurrentSessionsExceeded_allKeepAliveSessionsEagerlyReleased_noSuchMethodError()
|
||||
throws Exception {
|
||||
maxConcurrentSessionsExceededAllKeepAliveSessionsEagerlyReleased(
|
||||
/* throwNoSuchMethodErrorForResourceBusy= */ true);
|
||||
}
|
||||
|
||||
private static void maxConcurrentSessionsExceededAllKeepAliveSessionsEagerlyReleased(
|
||||
boolean throwNoSuchMethodErrorForResourceBusy) {
|
||||
ImmutableList<DrmInitData.SchemeData> secondSchemeDatas =
|
||||
ImmutableList.of(DRM_SCHEME_DATAS.get(0).copyWithData(TestUtil.createByteArray(4, 5, 6)));
|
||||
FakeExoMediaDrm.LicenseServer licenseServer =
|
||||
@ -267,7 +283,13 @@ public class DefaultDrmSessionManagerTest {
|
||||
DrmSessionManager drmSessionManager =
|
||||
new DefaultDrmSessionManager.Builder()
|
||||
.setUuidAndExoMediaDrmProvider(
|
||||
DRM_SCHEME_UUID, uuid -> new FakeExoMediaDrm(/* maxConcurrentSessions= */ 1))
|
||||
DRM_SCHEME_UUID,
|
||||
uuid ->
|
||||
new FakeExoMediaDrm.Builder()
|
||||
.setMaxConcurrentSessions(1)
|
||||
.throwNoSuchMethodErrorForProvisioningAndResourceBusy(
|
||||
throwNoSuchMethodErrorForResourceBusy)
|
||||
.build())
|
||||
.setSessionKeepaliveMs(10_000)
|
||||
.setMultiSession(true)
|
||||
.build(/* mediaDrmCallback= */ licenseServer);
|
||||
@ -298,6 +320,23 @@ public class DefaultDrmSessionManagerTest {
|
||||
@Test(timeout = 10_000)
|
||||
public void maxConcurrentSessionsExceeded_allPreacquiredAndKeepaliveSessionsEagerlyReleased()
|
||||
throws Exception {
|
||||
maxConcurrentSessionsExceededAllPreacquiredAndKeepaliveSessionsEagerlyReleased(
|
||||
/* throwNoSuchMethodErrorForResourceBusy= */ false);
|
||||
}
|
||||
|
||||
/** Testing workarounds for b/291440132. */
|
||||
@Config(sdk = 34)
|
||||
@Test(timeout = 10_000)
|
||||
public void
|
||||
maxConcurrentSessionsExceeded_allPreacquiredAndKeepaliveSessionsEagerlyReleased_noSuchMethodError()
|
||||
throws Exception {
|
||||
maxConcurrentSessionsExceededAllPreacquiredAndKeepaliveSessionsEagerlyReleased(
|
||||
/* throwNoSuchMethodErrorForResourceBusy= */ true);
|
||||
}
|
||||
|
||||
private static void
|
||||
maxConcurrentSessionsExceededAllPreacquiredAndKeepaliveSessionsEagerlyReleased(
|
||||
boolean throwNoSuchMethodErrorForResourceBusy) {
|
||||
ImmutableList<DrmInitData.SchemeData> secondSchemeDatas =
|
||||
ImmutableList.of(DRM_SCHEME_DATAS.get(0).copyWithData(TestUtil.createByteArray(4, 5, 6)));
|
||||
FakeExoMediaDrm.LicenseServer licenseServer =
|
||||
@ -308,7 +347,12 @@ public class DefaultDrmSessionManagerTest {
|
||||
new DefaultDrmSessionManager.Builder()
|
||||
.setUuidAndExoMediaDrmProvider(
|
||||
DRM_SCHEME_UUID,
|
||||
uuid -> new FakeExoMediaDrm.Builder().setMaxConcurrentSessions(1).build())
|
||||
uuid ->
|
||||
new FakeExoMediaDrm.Builder()
|
||||
.setMaxConcurrentSessions(1)
|
||||
.throwNoSuchMethodErrorForProvisioningAndResourceBusy(
|
||||
throwNoSuchMethodErrorForResourceBusy)
|
||||
.build())
|
||||
.setSessionKeepaliveMs(10_000)
|
||||
.setMultiSession(true)
|
||||
.build(/* mediaDrmCallback= */ licenseServer);
|
||||
@ -606,6 +650,22 @@ public class DefaultDrmSessionManagerTest {
|
||||
@Test
|
||||
public void
|
||||
deviceNotProvisioned_exceptionThrownFromOpenSession_provisioningDoneAndOpenSessionRetried() {
|
||||
deviceNotProvisionedExceptionThrownFromOpenSessionProvisioningDoneAndOpenSessionRetried(
|
||||
/* throwNoSuchMethodErrorForNotProvisioned= */ false);
|
||||
}
|
||||
|
||||
/** Testing workarounds for b/291440132. */
|
||||
@Config(sdk = 34)
|
||||
@Test
|
||||
public void
|
||||
deviceNotProvisioned_exceptionThrownFromOpenSession_provisioningDoneAndOpenSessionRetried_noSuchMethodError() {
|
||||
deviceNotProvisionedExceptionThrownFromOpenSessionProvisioningDoneAndOpenSessionRetried(
|
||||
/* throwNoSuchMethodErrorForNotProvisioned= */ true);
|
||||
}
|
||||
|
||||
private static void
|
||||
deviceNotProvisionedExceptionThrownFromOpenSessionProvisioningDoneAndOpenSessionRetried(
|
||||
boolean throwNoSuchMethodErrorForNotProvisioned) {
|
||||
FakeExoMediaDrm.LicenseServer licenseServer =
|
||||
FakeExoMediaDrm.LicenseServer.allowingSchemeDatas(DRM_SCHEME_DATAS);
|
||||
|
||||
@ -613,7 +673,12 @@ public class DefaultDrmSessionManagerTest {
|
||||
new DefaultDrmSessionManager.Builder()
|
||||
.setUuidAndExoMediaDrmProvider(
|
||||
DRM_SCHEME_UUID,
|
||||
uuid -> new FakeExoMediaDrm.Builder().setProvisionsRequired(1).build())
|
||||
uuid ->
|
||||
new FakeExoMediaDrm.Builder()
|
||||
.setProvisionsRequired(1)
|
||||
.throwNoSuchMethodErrorForProvisioningAndResourceBusy(
|
||||
throwNoSuchMethodErrorForNotProvisioned)
|
||||
.build())
|
||||
.build(/* mediaDrmCallback= */ licenseServer);
|
||||
drmSessionManager.setPlayer(/* playbackLooper= */ Looper.myLooper(), PlayerId.UNSET);
|
||||
drmSessionManager.prepare();
|
||||
@ -635,6 +700,22 @@ public class DefaultDrmSessionManagerTest {
|
||||
@Test
|
||||
public void
|
||||
deviceNotProvisioned_exceptionThrownFromGetKeyRequest_provisioningDoneAndOpenSessionRetried() {
|
||||
deviceNotProvisionedExceptionThrownFromGetKeyRequestProvisioningDoneAndOpenSessionRetried(
|
||||
/* throwNoSuchMethodErrorForNotProvisioned= */ false);
|
||||
}
|
||||
|
||||
/** Testing workarounds for b/291440132. */
|
||||
@Config(sdk = 34)
|
||||
@Test
|
||||
public void
|
||||
deviceNotProvisioned_exceptionThrownFromGetKeyRequest_provisioningDoneAndOpenSessionRetried_noSuchMethodError() {
|
||||
deviceNotProvisionedExceptionThrownFromGetKeyRequestProvisioningDoneAndOpenSessionRetried(
|
||||
/* throwNoSuchMethodErrorForNotProvisioned= */ true);
|
||||
}
|
||||
|
||||
private static void
|
||||
deviceNotProvisionedExceptionThrownFromGetKeyRequestProvisioningDoneAndOpenSessionRetried(
|
||||
boolean throwNoSuchMethodErrorForNotProvisioned) {
|
||||
FakeExoMediaDrm.LicenseServer licenseServer =
|
||||
FakeExoMediaDrm.LicenseServer.allowingSchemeDatas(DRM_SCHEME_DATAS);
|
||||
|
||||
@ -646,6 +727,8 @@ public class DefaultDrmSessionManagerTest {
|
||||
new FakeExoMediaDrm.Builder()
|
||||
.setProvisionsRequired(1)
|
||||
.throwNotProvisionedExceptionFromGetKeyRequest()
|
||||
.throwNoSuchMethodErrorForProvisioningAndResourceBusy(
|
||||
throwNoSuchMethodErrorForNotProvisioned)
|
||||
.build())
|
||||
.build(/* mediaDrmCallback= */ licenseServer);
|
||||
drmSessionManager.setPlayer(/* playbackLooper= */ Looper.myLooper(), PlayerId.UNSET);
|
||||
@ -665,6 +748,21 @@ public class DefaultDrmSessionManagerTest {
|
||||
|
||||
@Test
|
||||
public void deviceNotProvisioned_doubleProvisioningHandledAndOpenSessionRetried() {
|
||||
deviceNotProvisionedDoubleProvisioningHandledAndOpenSessionRetried(
|
||||
/* throwNoSuchMethodErrorForNotProvisioned= */ false);
|
||||
}
|
||||
|
||||
/** Testing workarounds for b/291440132. */
|
||||
@Config(sdk = 34)
|
||||
@Test
|
||||
public void
|
||||
deviceNotProvisioned_doubleProvisioningHandledAndOpenSessionRetried_noSuchMethodError() {
|
||||
deviceNotProvisionedDoubleProvisioningHandledAndOpenSessionRetried(
|
||||
/* throwNoSuchMethodErrorForNotProvisioned= */ true);
|
||||
}
|
||||
|
||||
private static void deviceNotProvisionedDoubleProvisioningHandledAndOpenSessionRetried(
|
||||
boolean throwNoSuchMethodErrorForNotProvisioned) {
|
||||
FakeExoMediaDrm.LicenseServer licenseServer =
|
||||
FakeExoMediaDrm.LicenseServer.allowingSchemeDatas(DRM_SCHEME_DATAS);
|
||||
|
||||
@ -672,7 +770,12 @@ public class DefaultDrmSessionManagerTest {
|
||||
new DefaultDrmSessionManager.Builder()
|
||||
.setUuidAndExoMediaDrmProvider(
|
||||
DRM_SCHEME_UUID,
|
||||
uuid -> new FakeExoMediaDrm.Builder().setProvisionsRequired(2).build())
|
||||
uuid ->
|
||||
new FakeExoMediaDrm.Builder()
|
||||
.setProvisionsRequired(2)
|
||||
.throwNoSuchMethodErrorForProvisioningAndResourceBusy(
|
||||
throwNoSuchMethodErrorForNotProvisioned)
|
||||
.build())
|
||||
.build(/* mediaDrmCallback= */ licenseServer);
|
||||
drmSessionManager.setPlayer(/* playbackLooper= */ Looper.myLooper(), PlayerId.UNSET);
|
||||
drmSessionManager.prepare();
|
||||
@ -693,6 +796,20 @@ public class DefaultDrmSessionManagerTest {
|
||||
|
||||
@Test
|
||||
public void keyResponseIndicatesProvisioningRequired_provisioningDone() {
|
||||
keyResponseIndicatesProvisioningRequiredProvisioningDone(
|
||||
/* throwNoSuchMethodErrorForNotProvisioned= */ false);
|
||||
}
|
||||
|
||||
/** Testing workarounds for b/291440132. */
|
||||
@Config(sdk = 34)
|
||||
@Test
|
||||
public void keyResponseIndicatesProvisioningRequired_provisioningDone_noSuchMethodError() {
|
||||
keyResponseIndicatesProvisioningRequiredProvisioningDone(
|
||||
/* throwNoSuchMethodErrorForNotProvisioned= */ true);
|
||||
}
|
||||
|
||||
private static void keyResponseIndicatesProvisioningRequiredProvisioningDone(
|
||||
boolean throwNoSuchMethodErrorForNotProvisioned) {
|
||||
FakeExoMediaDrm.LicenseServer licenseServer =
|
||||
FakeExoMediaDrm.LicenseServer.requiringProvisioningThenAllowingSchemeDatas(
|
||||
DRM_SCHEME_DATAS);
|
||||
@ -700,7 +817,12 @@ public class DefaultDrmSessionManagerTest {
|
||||
DefaultDrmSessionManager drmSessionManager =
|
||||
new DefaultDrmSessionManager.Builder()
|
||||
.setUuidAndExoMediaDrmProvider(
|
||||
DRM_SCHEME_UUID, uuid -> new FakeExoMediaDrm.Builder().build())
|
||||
DRM_SCHEME_UUID,
|
||||
uuid ->
|
||||
new FakeExoMediaDrm.Builder()
|
||||
.throwNoSuchMethodErrorForProvisioningAndResourceBusy(
|
||||
throwNoSuchMethodErrorForNotProvisioned)
|
||||
.build())
|
||||
.build(/* mediaDrmCallback= */ licenseServer);
|
||||
drmSessionManager.setPlayer(/* playbackLooper= */ Looper.myLooper(), PlayerId.UNSET);
|
||||
drmSessionManager.prepare();
|
||||
@ -719,10 +841,29 @@ public class DefaultDrmSessionManagerTest {
|
||||
|
||||
@Test
|
||||
public void provisioningUndoneWhileManagerIsActive_deviceReprovisioned() {
|
||||
provisioningUndoneWhileManagerIsActiveDeviceReprovisioned(
|
||||
/* throwNoSuchMethodErrorForNotProvisioned= */ false);
|
||||
}
|
||||
|
||||
/** Testing workarounds for b/291440132. */
|
||||
@Config(sdk = 34)
|
||||
@Test
|
||||
public void provisioningUndoneWhileManagerIsActive_deviceReprovisioned_noSuchMethodError() {
|
||||
provisioningUndoneWhileManagerIsActiveDeviceReprovisioned(
|
||||
/* throwNoSuchMethodErrorForNotProvisioned= */ true);
|
||||
}
|
||||
|
||||
private static void provisioningUndoneWhileManagerIsActiveDeviceReprovisioned(
|
||||
boolean throwNoSuchMethodErrorForNotProvisioned) {
|
||||
FakeExoMediaDrm.LicenseServer licenseServer =
|
||||
FakeExoMediaDrm.LicenseServer.allowingSchemeDatas(DRM_SCHEME_DATAS);
|
||||
|
||||
FakeExoMediaDrm mediaDrm = new FakeExoMediaDrm.Builder().setProvisionsRequired(2).build();
|
||||
FakeExoMediaDrm mediaDrm =
|
||||
new FakeExoMediaDrm.Builder()
|
||||
.setProvisionsRequired(2)
|
||||
.throwNoSuchMethodErrorForProvisioningAndResourceBusy(
|
||||
throwNoSuchMethodErrorForNotProvisioned)
|
||||
.build();
|
||||
DefaultDrmSessionManager drmSessionManager =
|
||||
new DefaultDrmSessionManager.Builder()
|
||||
.setUuidAndExoMediaDrmProvider(DRM_SCHEME_UUID, new AppManagedProvider(mediaDrm))
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
package androidx.media3.test.utils;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkState;
|
||||
|
||||
import android.media.DeniedByServerException;
|
||||
import android.media.MediaCryptoException;
|
||||
import android.media.MediaDrmException;
|
||||
@ -72,6 +74,7 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
||||
private int provisionsRequired;
|
||||
private boolean throwNotProvisionedExceptionFromGetKeyRequest;
|
||||
private int maxConcurrentSessions;
|
||||
private boolean throwNoSuchMethodErrorForProvisioningAndResourceBusy;
|
||||
|
||||
/** Constructs an instance. */
|
||||
public Builder() {
|
||||
@ -119,6 +122,26 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link FakeExoMediaDrm} to throw {@link NoSuchMethodError} instead of {@link
|
||||
* NotProvisionedException} or {@link ResourceBusyException}.
|
||||
*
|
||||
* <p>This simulates a framework bug (b/291440132) introduced in API 34 and resolved by
|
||||
* http://r.android.com/2770659, allowing us to test workarounds for the bug.
|
||||
*
|
||||
* <p>The default is {@code false}.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Builder throwNoSuchMethodErrorForProvisioningAndResourceBusy(
|
||||
boolean throwNoSuchMethodErrorForProvisioningAndResourceBusy) {
|
||||
checkState(
|
||||
!throwNoSuchMethodErrorForProvisioningAndResourceBusy || Util.SDK_INT == 34,
|
||||
"The framework bug recreated by this method only exists on API 34.");
|
||||
this.throwNoSuchMethodErrorForProvisioningAndResourceBusy =
|
||||
throwNoSuchMethodErrorForProvisioningAndResourceBusy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum number of concurrent sessions the {@link FakeExoMediaDrm} will support.
|
||||
*
|
||||
@ -143,6 +166,7 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
||||
enforceValidKeyResponses,
|
||||
provisionsRequired,
|
||||
throwNotProvisionedExceptionFromGetKeyRequest,
|
||||
throwNoSuchMethodErrorForProvisioningAndResourceBusy,
|
||||
maxConcurrentSessions);
|
||||
}
|
||||
}
|
||||
@ -170,6 +194,7 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
||||
private final int provisionsRequired;
|
||||
private final int maxConcurrentSessions;
|
||||
private final boolean throwNotProvisionedExceptionFromGetKeyRequest;
|
||||
private final boolean throwNoSuchMethodErrorForProvisioningAndResourceBusy;
|
||||
private final Map<String, byte[]> byteProperties;
|
||||
private final Map<String, String> stringProperties;
|
||||
private final Set<List<Byte>> openSessionIds;
|
||||
@ -184,12 +209,9 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
||||
* @deprecated Use {@link Builder} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("deprecation") // Using deprecated constructor to reduce duplication.
|
||||
public FakeExoMediaDrm() {
|
||||
this(
|
||||
/* enforceValidKeyResponses= */ true,
|
||||
/* provisionsRequired= */ 0,
|
||||
/* throwNotProvisionedExceptionFromGetKeyRequest= */ false,
|
||||
/* maxConcurrentSessions= */ Integer.MAX_VALUE);
|
||||
this(/* maxConcurrentSessions= */ Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -201,6 +223,7 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
||||
/* enforceValidKeyResponses= */ true,
|
||||
/* provisionsRequired= */ 0,
|
||||
/* throwNotProvisionedExceptionFromGetKeyRequest= */ false,
|
||||
/* throwNoSuchMethodErrorForProvisioningAndResourceBusy= */ false,
|
||||
maxConcurrentSessions);
|
||||
}
|
||||
|
||||
@ -208,12 +231,15 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
||||
boolean enforceValidKeyResponses,
|
||||
int provisionsRequired,
|
||||
boolean throwNotProvisionedExceptionFromGetKeyRequest,
|
||||
boolean throwNoSuchMethodErrorForProvisioningAndResourceBusy,
|
||||
int maxConcurrentSessions) {
|
||||
this.enforceValidKeyResponses = enforceValidKeyResponses;
|
||||
this.provisionsRequired = provisionsRequired;
|
||||
this.maxConcurrentSessions = maxConcurrentSessions;
|
||||
this.throwNotProvisionedExceptionFromGetKeyRequest =
|
||||
throwNotProvisionedExceptionFromGetKeyRequest;
|
||||
this.throwNoSuchMethodErrorForProvisioningAndResourceBusy =
|
||||
throwNoSuchMethodErrorForProvisioningAndResourceBusy;
|
||||
byteProperties = new HashMap<>();
|
||||
stringProperties = new HashMap<>();
|
||||
openSessionIds = new HashSet<>();
|
||||
@ -244,11 +270,17 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
||||
public byte[] openSession() throws MediaDrmException {
|
||||
Assertions.checkState(referenceCount > 0);
|
||||
if (!throwNotProvisionedExceptionFromGetKeyRequest && provisionsReceived < provisionsRequired) {
|
||||
throw new NotProvisionedException("Not provisioned.");
|
||||
throwNotProvisionedException();
|
||||
}
|
||||
if (openSessionIds.size() >= maxConcurrentSessions) {
|
||||
if (throwNoSuchMethodErrorForProvisioningAndResourceBusy) {
|
||||
throw new NoSuchMethodError(
|
||||
"no non-static method"
|
||||
+ " \"Landroid/media/ResourceBusyException;.<init>(Ljava/lang/String;III)V\"");
|
||||
} else {
|
||||
throw new ResourceBusyException("Too many sessions open. max=" + maxConcurrentSessions);
|
||||
}
|
||||
}
|
||||
byte[] sessionId =
|
||||
TestUtil.buildTestData(/* length= */ 10, sessionIdGenerator.incrementAndGet());
|
||||
if (!openSessionIds.add(toByteList(sessionId))) {
|
||||
@ -280,7 +312,7 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
||||
}
|
||||
Assertions.checkArgument(keyType == KEY_TYPE_STREAMING, "Unrecognised keyType: " + keyType);
|
||||
if (throwNotProvisionedExceptionFromGetKeyRequest && provisionsReceived < provisionsRequired) {
|
||||
throw new NotProvisionedException("Not provisioned.");
|
||||
throwNotProvisionedException();
|
||||
}
|
||||
Assertions.checkState(openSessionIds.contains(toByteList(scope)));
|
||||
Assertions.checkNotNull(schemeDatas);
|
||||
@ -306,7 +338,7 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
||||
throw new DeniedByServerException("Key request denied");
|
||||
}
|
||||
if (responseAsList.equals(PROVISIONING_REQUIRED_RESPONSE)) {
|
||||
throw new NotProvisionedException("Provisioning required");
|
||||
throwNotProvisionedException();
|
||||
}
|
||||
if (enforceValidKeyResponses && !responseAsList.equals(VALID_KEY_RESPONSE)) {
|
||||
throw new IllegalArgumentException(
|
||||
@ -451,6 +483,16 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
|
||||
provisionsReceived = 0;
|
||||
}
|
||||
|
||||
private void throwNotProvisionedException() throws NotProvisionedException {
|
||||
if (throwNoSuchMethodErrorForProvisioningAndResourceBusy) {
|
||||
throw new NoSuchMethodError(
|
||||
"no non-static method"
|
||||
+ " \"Landroid/media/NotProvisionedException;.<init>(Ljava/lang/String;III)V\"");
|
||||
} else {
|
||||
throw new NotProvisionedException("Not provisioned.");
|
||||
}
|
||||
}
|
||||
|
||||
private static ImmutableList<Byte> toByteList(byte[] byteArray) {
|
||||
return ImmutableList.copyOf(Bytes.asList(byteArray));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user