Commit 3b605848 by aquilescanta Committed by Oliver Woodman

Add a release callback to DefaultDrmSession

In preparation for reference counting in DrmSession

PiperOrigin-RevId: 247428114
parent 0a6f81a2
......@@ -42,20 +42,16 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/**
* A {@link DrmSession} that supports playbacks using {@link ExoMediaDrm}.
*/
/** A {@link DrmSession} that supports playbacks using {@link ExoMediaDrm}. */
@TargetApi(18)
/* package */ class DefaultDrmSession<T extends ExoMediaCrypto> implements DrmSession<T> {
/**
* Manages provisioning requests.
*/
/** Manages provisioning requests. */
public interface ProvisioningManager<T extends ExoMediaCrypto> {
/**
* Called when a session requires provisioning. The manager <em>may</em> call
* {@link #provision()} to have this session perform the provisioning operation. The manager
* Called when a session requires provisioning. The manager <em>may</em> call {@link
* #provision()} to have this session perform the provisioning operation. The manager
* <em>will</em> call {@link DefaultDrmSession#onProvisionCompleted()} when provisioning has
* completed, or {@link DefaultDrmSession#onProvisionError} if provisioning fails.
*
......@@ -70,11 +66,19 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
*/
void onProvisionError(Exception error);
/**
* Called by a session when it successfully completes a provisioning operation.
*/
/** Called by a session when it successfully completes a provisioning operation. */
void onProvisionCompleted();
}
/** Callback to be notified when the session is released. */
public interface ReleaseCallback<T extends ExoMediaCrypto> {
/**
* Called when the session is released.
*
* @param session The session.
*/
void onSessionReleased(DefaultDrmSession<T> session);
}
private static final String TAG = "DefaultDrmSession";
......@@ -88,6 +92,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private final ExoMediaDrm<T> mediaDrm;
private final ProvisioningManager<T> provisioningManager;
private final ReleaseCallback<T> releaseCallback;
private final @DefaultDrmSessionManager.Mode int mode;
private final @Nullable HashMap<String, String> optionalKeyRequestParameters;
private final EventDispatcher<DefaultDrmSessionEventListener> eventDispatcher;
......@@ -115,6 +120,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
* @param uuid The UUID of the drm scheme.
* @param mediaDrm The media DRM.
* @param provisioningManager The manager for provisioning.
* @param releaseCallback The {@link ReleaseCallback}.
* @param schemeDatas DRM scheme datas for this session, or null if an {@code
* offlineLicenseKeySetId} is provided.
* @param mode The DRM mode.
......@@ -131,6 +137,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
UUID uuid,
ExoMediaDrm<T> mediaDrm,
ProvisioningManager<T> provisioningManager,
ReleaseCallback<T> releaseCallback,
@Nullable List<SchemeData> schemeDatas,
@DefaultDrmSessionManager.Mode int mode,
@Nullable byte[] offlineLicenseKeySetId,
......@@ -145,6 +152,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
}
this.uuid = uuid;
this.provisioningManager = provisioningManager;
this.releaseCallback = releaseCallback;
this.mediaDrm = mediaDrm;
this.mode = mode;
if (offlineLicenseKeySetId != null) {
......@@ -178,10 +186,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
}
}
/** @return True if the session is closed and cleaned up, false otherwise. */
// Assigning null to various non-null variables for clean-up. Class won't be used after release.
@SuppressWarnings("assignment.type.incompatible")
public boolean release() {
public void release() {
if (--openCount == 0) {
state = STATE_RELEASED;
postResponseHandler.removeCallbacksAndMessages(null);
......@@ -198,9 +205,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
sessionId = null;
eventDispatcher.dispatch(DefaultDrmSessionEventListener::onDrmSessionReleased);
}
return true;
releaseCallback.onSessionReleased(this);
}
return false;
}
public boolean hasSessionId(byte[] sessionId) {
......@@ -330,8 +336,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
long licenseDurationRemainingSec = getLicenseDurationRemainingSec();
if (mode == DefaultDrmSessionManager.MODE_PLAYBACK
&& licenseDurationRemainingSec <= MAX_LICENSE_DURATION_TO_RENEW) {
Log.d(TAG, "Offline license has expired or will expire soon. "
+ "Remaining seconds: " + licenseDurationRemainingSec);
Log.d(
TAG,
"Offline license has expired or will expire soon. "
+ "Remaining seconds: "
+ licenseDurationRemainingSec);
postKeyRequest(sessionId, ExoMediaDrm.KEY_TYPE_OFFLINE, allowRetry);
} else if (licenseDurationRemainingSec <= 0) {
onError(new KeysExpiredException());
......@@ -415,8 +424,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
} else {
byte[] keySetId = mediaDrm.provideKeyResponse(sessionId, responseData);
if ((mode == DefaultDrmSessionManager.MODE_DOWNLOAD
|| (mode == DefaultDrmSessionManager.MODE_PLAYBACK && offlineLicenseKeySetId != null))
&& keySetId != null && keySetId.length != 0) {
|| (mode == DefaultDrmSessionManager.MODE_PLAYBACK
&& offlineLicenseKeySetId != null))
&& keySetId != null
&& keySetId.length != 0) {
offlineLicenseKeySetId = keySetId;
}
state = STATE_OPENED_WITH_KEYS;
......@@ -480,10 +491,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
break;
default:
break;
}
}
}
@SuppressLint("HandlerLeak")
......@@ -541,6 +550,5 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private long getRetryDelayMillis(int errorCount) {
return Math.min((errorCount - 1) * 1000, 5000);
}
}
}
......@@ -40,12 +40,10 @@ import java.util.HashMap;
import java.util.List;
import java.util.UUID;
/**
* A {@link DrmSessionManager} that supports playbacks using {@link ExoMediaDrm}.
*/
/** A {@link DrmSessionManager} that supports playbacks using {@link ExoMediaDrm}. */
@TargetApi(18)
public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSessionManager<T>,
ProvisioningManager<T> {
public class DefaultDrmSessionManager<T extends ExoMediaCrypto>
implements DrmSessionManager<T>, ProvisioningManager<T> {
/**
* Signals that the {@link DrmInitData} passed to {@link #acquireSession} does not contain does
......@@ -76,9 +74,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
* licenses.
*/
public static final int MODE_PLAYBACK = 0;
/**
* Restores an offline license to allow its status to be queried.
*/
/** Restores an offline license to allow its status to be queried. */
public static final int MODE_QUERY = 1;
/** Downloads an offline license or renews an existing one. */
public static final int MODE_DOWNLOAD = 2;
......@@ -272,8 +268,8 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
/**
* Provides access to {@link ExoMediaDrm#getPropertyString(String)}.
* <p>
* This method may be called when the manager is in any state.
*
* <p>This method may be called when the manager is in any state.
*
* @param key The key to request.
* @return The retrieved property.
......@@ -284,8 +280,8 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
/**
* Provides access to {@link ExoMediaDrm#setPropertyString(String, String)}.
* <p>
* This method may be called when the manager is in any state.
*
* <p>This method may be called when the manager is in any state.
*
* @param key The property to write.
* @param value The value to write.
......@@ -296,8 +292,8 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
/**
* Provides access to {@link ExoMediaDrm#getPropertyByteArray(String)}.
* <p>
* This method may be called when the manager is in any state.
*
* <p>This method may be called when the manager is in any state.
*
* @param key The key to request.
* @return The retrieved property.
......@@ -308,8 +304,8 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
/**
* Provides access to {@link ExoMediaDrm#setPropertyByteArray(String, byte[])}.
* <p>
* This method may be called when the manager is in any state.
*
* <p>This method may be called when the manager is in any state.
*
* @param key The property to write.
* @param value The value to write.
......@@ -373,7 +369,8 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
if (schemeType == null || C.CENC_TYPE_cenc.equals(schemeType)) {
// If there is no scheme information, assume patternless AES-CTR.
return true;
} else if (C.CENC_TYPE_cbc1.equals(schemeType) || C.CENC_TYPE_cbcs.equals(schemeType)
} else if (C.CENC_TYPE_cbc1.equals(schemeType)
|| C.CENC_TYPE_cbcs.equals(schemeType)
|| C.CENC_TYPE_cens.equals(schemeType)) {
// API support for AES-CBC and pattern encryption was added in API 24. However, the
// implementation was not stable until API 25.
......@@ -423,7 +420,8 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
new DefaultDrmSession<>(
uuid,
mediaDrm,
this,
/* provisioningManager= */ this,
/* releaseCallback= */ this::onSessionReleased,
schemeDatas,
mode,
offlineLicenseKeySetId,
......@@ -444,17 +442,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
// Do nothing.
return;
}
DefaultDrmSession<T> drmSession = (DefaultDrmSession<T>) session;
if (drmSession.release()) {
sessions.remove(drmSession);
if (provisioningSessions.size() > 1 && provisioningSessions.get(0) == drmSession) {
// Other sessions were waiting for the released session to complete a provision operation.
// We need to have one of those sessions perform the provision operation instead.
provisioningSessions.get(1).provision();
}
provisioningSessions.remove(drmSession);
}
((DefaultDrmSession<T>) session).release();
}
// ProvisioningManager implementation.
......@@ -490,6 +478,16 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
// Internal methods.
private void onSessionReleased(DefaultDrmSession<T> drmSession) {
sessions.remove(drmSession);
if (provisioningSessions.size() > 1 && provisioningSessions.get(0) == drmSession) {
// Other sessions were waiting for the released session to complete a provision operation.
// We need to have one of those sessions perform the provision operation instead.
provisioningSessions.get(1).provision();
}
provisioningSessions.remove(drmSession);
}
/**
* Extracts {@link SchemeData} instances suitable for the given DRM scheme {@link UUID}.
*
......@@ -506,8 +504,9 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
List<SchemeData> matchingSchemeDatas = new ArrayList<>(drmInitData.schemeDataCount);
for (int i = 0; i < drmInitData.schemeDataCount; i++) {
SchemeData schemeData = drmInitData.get(i);
boolean uuidMatches = schemeData.matches(uuid)
|| (C.CLEARKEY_UUID.equals(uuid) && schemeData.matches(C.COMMON_PSSH_UUID));
boolean uuidMatches =
schemeData.matches(uuid)
|| (C.CLEARKEY_UUID.equals(uuid) && schemeData.matches(C.COMMON_PSSH_UUID));
if (uuidMatches && (schemeData.data != null || allowMissingData)) {
matchingSchemeDatas.add(schemeData);
}
......@@ -536,7 +535,6 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
}
}
}
}
private class MediaDrmEventListener implements OnEventListener<T> {
......@@ -550,7 +548,5 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
@Nullable byte[] data) {
Assertions.checkNotNull(mediaDrmHandler).obtainMessage(event, sessionId).sendToTarget();
}
}
}
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