Commit 849c3074 by ibaker Committed by Oliver Woodman

Move `requiresSecureDecoder` logic into `ExoMediaDrm`

The result is plumbed back to `MediaCodecRenderer` via a new
`DrmSession#requiresSecureDecoder` method.

This allows us to use the `MediaDrm#requiresSecureDecoder` method added
in Android 12:
https://developer.android.com/reference/android/media/MediaDrm#requiresSecureDecoder(java.lang.String)

This change also removes
`FrameworkMediaCrypto#forceAllowInsecureDecoderComponents`, replacing it
with equivalent logic in `FrameworkMediaDrm#requiresSecureDecoder`.

PiperOrigin-RevId: 389616038
parent 0097a79c
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.drm; package com.google.android.exoplayer2.drm;
import static com.google.android.exoplayer2.util.Assertions.checkState; import static com.google.android.exoplayer2.util.Assertions.checkState;
import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull;
import static java.lang.Math.min; import static java.lang.Math.min;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
...@@ -286,6 +287,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -286,6 +287,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
} }
@Override @Override
public boolean requiresSecureDecoder(String mimeType) {
return mediaDrm.requiresSecureDecoder(checkStateNotNull(sessionId), mimeType);
}
@Override
public void acquire(@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher) { public void acquire(@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher) {
checkState(referenceCount >= 0); checkState(referenceCount >= 0);
if (eventDispatcher != null) { if (eventDispatcher != null) {
......
...@@ -138,6 +138,15 @@ public interface DrmSession { ...@@ -138,6 +138,15 @@ public interface DrmSession {
byte[] getOfflineLicenseKeySetId(); byte[] getOfflineLicenseKeySetId();
/** /**
* Returns whether this session requires use of a secure decoder for the given MIME type. Assumes
* a license policy that requires the highest level of security supported by the session.
*
* <p>The session must be in {@link #getState() state} {@link #STATE_OPENED} or {@link
* #STATE_OPENED_WITH_KEYS}.
*/
boolean requiresSecureDecoder(String mimeType);
/**
* Increments the reference count. When the caller no longer needs to use the instance, it must * Increments the reference count. When the caller no longer needs to use the instance, it must
* call {@link #release(DrmSessionEventListener.EventDispatcher)} to decrement the reference * call {@link #release(DrmSessionEventListener.EventDispatcher)} to decrement the reference
* count. * count.
......
...@@ -94,6 +94,12 @@ public final class DummyExoMediaDrm implements ExoMediaDrm { ...@@ -94,6 +94,12 @@ public final class DummyExoMediaDrm implements ExoMediaDrm {
} }
@Override @Override
public boolean requiresSecureDecoder(byte[] sessionId, String mimeType) {
// Should not be invoked. No session should exist.
throw new IllegalStateException();
}
@Override
public void acquire() { public void acquire() {
// Do nothing. // Do nothing.
} }
......
...@@ -70,6 +70,11 @@ public final class ErrorStateDrmSession implements DrmSession { ...@@ -70,6 +70,11 @@ public final class ErrorStateDrmSession implements DrmSession {
} }
@Override @Override
public boolean requiresSecureDecoder(String mimeType) {
return false;
}
@Override
public void acquire(@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher) { public void acquire(@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher) {
// Do nothing. // Do nothing.
} }
......
...@@ -461,6 +461,15 @@ public interface ExoMediaDrm { ...@@ -461,6 +461,15 @@ public interface ExoMediaDrm {
Map<String, String> queryKeyStatus(byte[] sessionId); Map<String, String> queryKeyStatus(byte[] sessionId);
/** /**
* Returns whether the given session requires use of a secure decoder for the given MIME type.
* Assumes a license policy that requires the highest level of security supported by the session.
*
* @param sessionId The ID of the session.
* @param mimeType The content MIME type to query.
*/
boolean requiresSecureDecoder(byte[] sessionId, String mimeType);
/**
* Increments the reference count. When the caller no longer needs to use the instance, it must * Increments the reference count. When the caller no longer needs to use the instance, it must
* call {@link #release()} to decrement the reference count. * call {@link #release()} to decrement the reference count.
* *
......
...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.drm; ...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.drm;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.media.DeniedByServerException; import android.media.DeniedByServerException;
import android.media.MediaCrypto;
import android.media.MediaCryptoException; import android.media.MediaCryptoException;
import android.media.MediaDrm; import android.media.MediaDrm;
import android.media.MediaDrmException; import android.media.MediaDrmException;
...@@ -24,6 +25,7 @@ import android.media.NotProvisionedException; ...@@ -24,6 +25,7 @@ import android.media.NotProvisionedException;
import android.media.UnsupportedSchemeException; import android.media.UnsupportedSchemeException;
import android.os.PersistableBundle; import android.os.PersistableBundle;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.DoNotInline;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
...@@ -245,6 +247,26 @@ public final class FrameworkMediaDrm implements ExoMediaDrm { ...@@ -245,6 +247,26 @@ public final class FrameworkMediaDrm implements ExoMediaDrm {
} }
@Override @Override
public boolean requiresSecureDecoder(byte[] sessionId, String mimeType) {
if (Util.SDK_INT >= 31) {
return Api31.requiresSecureDecoder(mediaDrm, mimeType);
}
MediaCrypto mediaCrypto;
try {
mediaCrypto = new MediaCrypto(uuid, sessionId);
} catch (MediaCryptoException e) {
// This shouldn't happen, but if it does then assume that a secure decoder may be required.
return true;
}
try {
return mediaCrypto.requiresSecureDecoderComponent(mimeType);
} finally {
mediaCrypto.release();
}
}
@Override
public synchronized void acquire() { public synchronized void acquire() {
Assertions.checkState(referenceCount > 0); Assertions.checkState(referenceCount > 0);
referenceCount++; referenceCount++;
...@@ -476,4 +498,12 @@ public final class FrameworkMediaDrm implements ExoMediaDrm { ...@@ -476,4 +498,12 @@ public final class FrameworkMediaDrm implements ExoMediaDrm {
newData.put(xmlWithMockLaUrl.getBytes(Charsets.UTF_16LE)); newData.put(xmlWithMockLaUrl.getBytes(Charsets.UTF_16LE));
return newData.array(); return newData.array();
} }
@RequiresApi(31)
private static class Api31 {
@DoNotInline
public static boolean requiresSecureDecoder(MediaDrm mediaDrm, String mimeType) {
return mediaDrm.requiresSecureDecoder(mimeType);
}
}
} }
...@@ -2099,7 +2099,14 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -2099,7 +2099,14 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
// the case is to occur, so we re-initialize in this case. // the case is to occur, so we re-initialize in this case.
return true; return true;
} }
if (!codecInfo.secure && maybeRequiresSecureDecoder(newMediaCrypto, newFormat)) {
boolean requiresSecureDecoder;
if (newMediaCrypto.forceAllowInsecureDecoderComponents) {
requiresSecureDecoder = false;
} else {
requiresSecureDecoder = newSession.requiresSecureDecoder(newFormat.sampleMimeType);
}
if (!codecInfo.secure && requiresSecureDecoder) {
// Re-initialization is required because newSession might require switching to the secure // Re-initialization is required because newSession might require switching to the secure
// output path. // output path.
return true; return true;
...@@ -2108,32 +2115,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -2108,32 +2115,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
return false; return false;
} }
/**
* Returns whether a {@link DrmSession} may require a secure decoder for a given {@link Format}.
*
* @param sessionMediaCrypto The {@link DrmSession}'s {@link FrameworkMediaCrypto}.
* @param format The {@link Format}.
* @return Whether a secure decoder may be required.
*/
private boolean maybeRequiresSecureDecoder(
FrameworkMediaCrypto sessionMediaCrypto, Format format) {
if (sessionMediaCrypto.forceAllowInsecureDecoderComponents) {
return false;
}
MediaCrypto mediaCrypto;
try {
mediaCrypto = new MediaCrypto(sessionMediaCrypto.uuid, sessionMediaCrypto.sessionId);
} catch (MediaCryptoException e) {
// This shouldn't happen, but if it does then assume that a secure decoder may be required.
return true;
}
try {
return mediaCrypto.requiresSecureDecoderComponent(format.sampleMimeType);
} finally {
mediaCrypto.release();
}
}
private void reinitializeCodec() throws ExoPlaybackException { private void reinitializeCodec() throws ExoPlaybackException {
releaseCodec(); releaseCodec();
maybeInitCodecOrBypass(); maybeInitCodecOrBypass();
......
...@@ -273,6 +273,11 @@ public final class FakeExoMediaDrm implements ExoMediaDrm { ...@@ -273,6 +273,11 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
} }
@Override @Override
public boolean requiresSecureDecoder(byte[] sessionId, String mimeType) {
return false;
}
@Override
public void acquire() { public void acquire() {
Assertions.checkState(referenceCount > 0); Assertions.checkState(referenceCount > 0);
referenceCount++; referenceCount++;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment