Skip to content

Commit

Permalink
Add workarounds for NoSuchMethodError from DRM framework exceptions
Browse files Browse the repository at this point in the history
Issue: androidx#1145

PiperOrigin-RevId: 613573868
(cherry picked from commit a604600)
  • Loading branch information
icbaker authored and l1068 committed Apr 15, 2024
1 parent d022537 commit 86612ef
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 31 deletions.
5 changes: 5 additions & 0 deletions RELEASENOTES.md
Expand Up @@ -28,6 +28,11 @@
Google TV, and Lenovo M10 FHD Plus that causes 60fps H265 streams to be
marked as unsupported
([#966](https://github.com/androidx/media/issues/966)).
* 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)).
* Effect:
* Improved PQ to SDR tone-mapping by converting color spaces.
* Session:
Expand Down
Expand Up @@ -396,8 +396,13 @@ private boolean openInternal() {
return true;
} catch (NotProvisionedException e) {
provisioningManager.provisionRequired(this);
} catch (Exception e) {
onError(e, DrmUtil.ERROR_SOURCE_EXO_MEDIA_DRM);
} 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;
Expand Down Expand Up @@ -474,7 +479,7 @@ private boolean restoreKeys() {
try {
mediaDrm.restoreKeys(sessionId, offlineLicenseKeySetId);
return true;
} catch (Exception e) {
} catch (Exception | NoSuchMethodError e) {
onError(e, DrmUtil.ERROR_SOURCE_EXO_MEDIA_DRM);
}
return false;
Expand All @@ -494,7 +499,7 @@ private void postKeyRequest(byte[] scope, int type, boolean allowRetry) {
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);
}
}
Expand All @@ -506,8 +511,8 @@ private void onKeyResponse(Object request, Object response) {
}
currentKeyRequest = null;

if (response instanceof Exception) {
onKeysError((Exception) response, /* thrownByExoMediaDrm= */ false);
if (response instanceof Exception || response instanceof NoSuchMethodError) {
onKeysError((Throwable) response, /* thrownByExoMediaDrm= */ false);
return;
}

Expand All @@ -528,7 +533,7 @@ private void onKeyResponse(Object request, Object response) {
state = STATE_OPENED_WITH_KEYS;
dispatchEvent(DrmSessionEventListener.EventDispatcher::drmKeysLoaded);
}
} catch (Exception e) {
} catch (Exception | NoSuchMethodError e) {
onKeysError(e, /* thrownByExoMediaDrm= */ true);
}
}
Expand All @@ -540,8 +545,12 @@ private void onKeysRequired() {
}
}

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(
Expand All @@ -552,11 +561,24 @@ private void onKeysError(Exception e, boolean thrownByExoMediaDrm) {
}
}

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;
}
Expand Down
Expand Up @@ -657,11 +657,15 @@ private DefaultDrmSession createAndAcquireSessionWithRetry(
}

private static boolean acquisitionFailedIndicatingResourceShortage(DrmSession session) {
if (session.getState() != DrmSession.STATE_ERROR) {
return false;
}

@Nullable Throwable cause = checkNotNull(session.getError()).getCause();
// ResourceBusyException is only available at API 19, so on earlier versions we
// assume any error indicates resource shortage (ensuring we retry).
return session.getState() == DrmSession.STATE_ERROR
&& (Util.SDK_INT < 19
|| checkNotNull(session.getError()).getCause() instanceof ResourceBusyException);
return Util.SDK_INT < 19 || cause instanceof ResourceBusyException
|| DrmUtil.isFailureToConstructResourceBusyException(cause);
}

/**
Expand Down
Expand Up @@ -25,6 +25,7 @@
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;
Expand Down Expand Up @@ -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 (Util.SDK_INT >= 18 && Api18.isNotProvisionedException(exception)) {
} else if ((Util.SDK_INT >= 18 && Api18.isNotProvisionedException(exception))
|| isFailureToConstructNotProvisionedException(exception)) {
return PlaybackException.ERROR_CODE_DRM_PROVISIONING_FAILED;
} else if (Util.SDK_INT >= 18 && Api18.isDeniedByServerException(exception)) {
return PlaybackException.ERROR_CODE_DRM_DEVICE_REVOKED;
Expand All @@ -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(18)
Expand Down

0 comments on commit 86612ef

Please sign in to comment.