Allow DefaultDRMSession to retry provisioning/key request

For initial DRM provisioning and key request, we allow the requests to be
retried (with increasing delay for each successive retry) before failing.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=171271384
This commit is contained in:
hoangtc 2017-10-06 02:56:52 -07:00 committed by Oliver Woodman
parent f73a5bf692
commit d5101d8d46
2 changed files with 75 additions and 17 deletions

View File

@ -82,6 +82,7 @@ import java.util.UUID;
private final HashMap<String, String> optionalKeyRequestParameters; private final HashMap<String, String> optionalKeyRequestParameters;
private final Handler eventHandler; private final Handler eventHandler;
private final DefaultDrmSessionManager.EventListener eventListener; private final DefaultDrmSessionManager.EventListener eventListener;
private final int initialDrmRequestRetryCount;
/* package */ final MediaDrmCallback callback; /* package */ final MediaDrmCallback callback;
/* package */ final UUID uuid; /* package */ final UUID uuid;
@ -89,7 +90,7 @@ import java.util.UUID;
private @DrmSession.State int state; private @DrmSession.State int state;
private int openCount; private int openCount;
private HandlerThread requestHandlerThread; private HandlerThread requestHandlerThread;
private Handler postRequestHandler; private PostRequestHandler postRequestHandler;
private T mediaCrypto; private T mediaCrypto;
private DrmSessionException lastException; private DrmSessionException lastException;
private byte[] sessionId; private byte[] sessionId;
@ -111,13 +112,16 @@ import java.util.UUID;
* @param playbackLooper The playback looper. * @param playbackLooper The playback looper.
* @param eventHandler The handler to post listener events. * @param eventHandler The handler to post listener events.
* @param eventListener The DRM session manager event listener. * @param eventListener The DRM session manager event listener.
* @param initialDrmRequestRetryCount The number of times to retry for initial provisioning and
* key request before reporting error.
*/ */
public DefaultDrmSession(UUID uuid, ExoMediaDrm<T> mediaDrm, public DefaultDrmSession(UUID uuid, ExoMediaDrm<T> mediaDrm,
ProvisioningManager<T> provisioningManager, byte[] initData, String mimeType, ProvisioningManager<T> provisioningManager, byte[] initData, String mimeType,
@DefaultDrmSessionManager.Mode int mode, byte[] offlineLicenseKeySetId, @DefaultDrmSessionManager.Mode int mode, byte[] offlineLicenseKeySetId,
HashMap<String, String> optionalKeyRequestParameters, MediaDrmCallback callback, HashMap<String, String> optionalKeyRequestParameters, MediaDrmCallback callback,
Looper playbackLooper, Handler eventHandler, Looper playbackLooper, Handler eventHandler,
DefaultDrmSessionManager.EventListener eventListener) { DefaultDrmSessionManager.EventListener eventListener,
int initialDrmRequestRetryCount) {
this.uuid = uuid; this.uuid = uuid;
this.provisioningManager = provisioningManager; this.provisioningManager = provisioningManager;
this.mediaDrm = mediaDrm; this.mediaDrm = mediaDrm;
@ -125,6 +129,7 @@ import java.util.UUID;
this.offlineLicenseKeySetId = offlineLicenseKeySetId; this.offlineLicenseKeySetId = offlineLicenseKeySetId;
this.optionalKeyRequestParameters = optionalKeyRequestParameters; this.optionalKeyRequestParameters = optionalKeyRequestParameters;
this.callback = callback; this.callback = callback;
this.initialDrmRequestRetryCount = initialDrmRequestRetryCount;
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.eventListener = eventListener; this.eventListener = eventListener;
@ -152,7 +157,7 @@ import java.util.UUID;
return; return;
} }
if (openInternal(true)) { if (openInternal(true)) {
doLicense(); doLicense(true);
} }
} }
} }
@ -191,12 +196,12 @@ import java.util.UUID;
public void provision() { public void provision() {
ProvisionRequest request = mediaDrm.getProvisionRequest(); ProvisionRequest request = mediaDrm.getProvisionRequest();
postRequestHandler.obtainMessage(MSG_PROVISION, request).sendToTarget(); postRequestHandler.obtainMessage(MSG_PROVISION, request, true).sendToTarget();
} }
public void onProvisionCompleted() { public void onProvisionCompleted() {
if (openInternal(false)) { if (openInternal(false)) {
doLicense(); doLicense(true);
} }
} }
@ -285,12 +290,12 @@ import java.util.UUID;
provisioningManager.onProvisionCompleted(); provisioningManager.onProvisionCompleted();
} }
private void doLicense() { private void doLicense(boolean allowRetry) {
switch (mode) { switch (mode) {
case DefaultDrmSessionManager.MODE_PLAYBACK: case DefaultDrmSessionManager.MODE_PLAYBACK:
case DefaultDrmSessionManager.MODE_QUERY: case DefaultDrmSessionManager.MODE_QUERY:
if (offlineLicenseKeySetId == null) { if (offlineLicenseKeySetId == null) {
postKeyRequest(ExoMediaDrm.KEY_TYPE_STREAMING); postKeyRequest(ExoMediaDrm.KEY_TYPE_STREAMING, allowRetry);
} else { } else {
if (restoreKeys()) { if (restoreKeys()) {
long licenseDurationRemainingSec = getLicenseDurationRemainingSec(); long licenseDurationRemainingSec = getLicenseDurationRemainingSec();
@ -298,7 +303,7 @@ import java.util.UUID;
&& licenseDurationRemainingSec <= MAX_LICENSE_DURATION_TO_RENEW) { && licenseDurationRemainingSec <= MAX_LICENSE_DURATION_TO_RENEW) {
Log.d(TAG, "Offline license has expired or will expire soon. " Log.d(TAG, "Offline license has expired or will expire soon. "
+ "Remaining seconds: " + licenseDurationRemainingSec); + "Remaining seconds: " + licenseDurationRemainingSec);
postKeyRequest(ExoMediaDrm.KEY_TYPE_OFFLINE); postKeyRequest(ExoMediaDrm.KEY_TYPE_OFFLINE, allowRetry);
} else if (licenseDurationRemainingSec <= 0) { } else if (licenseDurationRemainingSec <= 0) {
onError(new KeysExpiredException()); onError(new KeysExpiredException());
} else { } else {
@ -317,11 +322,11 @@ import java.util.UUID;
break; break;
case DefaultDrmSessionManager.MODE_DOWNLOAD: case DefaultDrmSessionManager.MODE_DOWNLOAD:
if (offlineLicenseKeySetId == null) { if (offlineLicenseKeySetId == null) {
postKeyRequest(ExoMediaDrm.KEY_TYPE_OFFLINE); postKeyRequest(ExoMediaDrm.KEY_TYPE_OFFLINE, allowRetry);
} else { } else {
// Renew // Renew
if (restoreKeys()) { if (restoreKeys()) {
postKeyRequest(ExoMediaDrm.KEY_TYPE_OFFLINE); postKeyRequest(ExoMediaDrm.KEY_TYPE_OFFLINE, allowRetry);
} }
} }
break; break;
@ -329,7 +334,7 @@ import java.util.UUID;
// It's not necessary to restore the key (and open a session to do that) before releasing it // It's not necessary to restore the key (and open a session to do that) before releasing it
// but this serves as a good sanity/fast-failure check. // but this serves as a good sanity/fast-failure check.
if (restoreKeys()) { if (restoreKeys()) {
postKeyRequest(ExoMediaDrm.KEY_TYPE_RELEASE); postKeyRequest(ExoMediaDrm.KEY_TYPE_RELEASE, allowRetry);
} }
break; break;
default: default:
@ -356,12 +361,12 @@ import java.util.UUID;
return Math.min(pair.first, pair.second); return Math.min(pair.first, pair.second);
} }
private void postKeyRequest(int type) { private void postKeyRequest(int type, boolean allowRetry) {
byte[] scope = type == ExoMediaDrm.KEY_TYPE_RELEASE ? offlineLicenseKeySetId : sessionId; byte[] scope = type == ExoMediaDrm.KEY_TYPE_RELEASE ? offlineLicenseKeySetId : sessionId;
try { try {
KeyRequest request = mediaDrm.getKeyRequest(scope, initData, mimeType, type, KeyRequest request = mediaDrm.getKeyRequest(scope, initData, mimeType, type,
optionalKeyRequestParameters); optionalKeyRequestParameters);
postRequestHandler.obtainMessage(MSG_KEYS, request).sendToTarget(); postRequestHandler.obtainMessage(MSG_KEYS, request, allowRetry).sendToTarget();
} catch (Exception e) { } catch (Exception e) {
onKeysError(e); onKeysError(e);
} }
@ -452,7 +457,7 @@ import java.util.UUID;
} }
switch (what) { switch (what) {
case ExoMediaDrm.EVENT_KEY_REQUIRED: case ExoMediaDrm.EVENT_KEY_REQUIRED:
doLicense(); doLicense(false);
break; break;
case ExoMediaDrm.EVENT_KEY_EXPIRED: case ExoMediaDrm.EVENT_KEY_EXPIRED:
// When an already expired key is loaded MediaDrm sends this event immediately. Ignore // When an already expired key is loaded MediaDrm sends this event immediately. Ignore
@ -501,6 +506,11 @@ import java.util.UUID;
super(backgroundLooper); super(backgroundLooper);
} }
Message obtainMessage(int what, Object object, boolean allowRetry) {
return obtainMessage(what, allowRetry ? 1 : 0 /* allow retry*/, 0 /* error count */,
object);
}
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
Object response; Object response;
@ -516,11 +526,33 @@ import java.util.UUID;
throw new RuntimeException(); throw new RuntimeException();
} }
} catch (Exception e) { } catch (Exception e) {
if (maybeRetryRequest(msg)) {
return;
}
response = e; response = e;
} }
postResponseHandler.obtainMessage(msg.what, response).sendToTarget(); postResponseHandler.obtainMessage(msg.what, response).sendToTarget();
} }
private boolean maybeRetryRequest(Message originalMsg) {
boolean allowRetry = originalMsg.arg1 == 1;
if (!allowRetry) {
return false;
}
int errorCount = originalMsg.arg2 + 1;
if (errorCount > initialDrmRequestRetryCount) {
return false;
}
Message retryMsg = Message.obtain(originalMsg);
retryMsg.arg2 = errorCount;
sendMessageDelayed(retryMsg, getRetryDelayMillis(errorCount));
return true;
}
private long getRetryDelayMillis(int errorCount) {
return Math.min((errorCount - 1) * 1000, 5000);
}
} }
} }

View File

@ -97,6 +97,8 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
public static final int MODE_DOWNLOAD = 2; public static final int MODE_DOWNLOAD = 2;
/** Releases an existing offline license. */ /** Releases an existing offline license. */
public static final int MODE_RELEASE = 3; public static final int MODE_RELEASE = 3;
/** Number of times to retry for initial provisioning and key request for reporting error. */
public static final int INITIAL_DRM_REQUEST_RETRY_COUNT = 3;
private final UUID uuid; private final UUID uuid;
private final ExoMediaDrm<T> mediaDrm; private final ExoMediaDrm<T> mediaDrm;
@ -105,6 +107,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
private final Handler eventHandler; private final Handler eventHandler;
private final EventListener eventListener; private final EventListener eventListener;
private final boolean multiSession; private final boolean multiSession;
private final int initialDrmRequestRetryCount;
private final List<DefaultDrmSession<T>> sessions; private final List<DefaultDrmSession<T>> sessions;
private final List<DefaultDrmSession<T>> provisioningSessions; private final List<DefaultDrmSession<T>> provisioningSessions;
@ -176,7 +179,8 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
UUID uuid, MediaDrmCallback callback, HashMap<String, String> optionalKeyRequestParameters, UUID uuid, MediaDrmCallback callback, HashMap<String, String> optionalKeyRequestParameters,
Handler eventHandler, EventListener eventListener) throws UnsupportedDrmException { Handler eventHandler, EventListener eventListener) throws UnsupportedDrmException {
return new DefaultDrmSessionManager<>(uuid, FrameworkMediaDrm.newInstance(uuid), callback, return new DefaultDrmSessionManager<>(uuid, FrameworkMediaDrm.newInstance(uuid), callback,
optionalKeyRequestParameters, eventHandler, eventListener, false); optionalKeyRequestParameters, eventHandler, eventListener, false,
INITIAL_DRM_REQUEST_RETRY_COUNT);
} }
/** /**
@ -193,7 +197,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
HashMap<String, String> optionalKeyRequestParameters, Handler eventHandler, HashMap<String, String> optionalKeyRequestParameters, Handler eventHandler,
EventListener eventListener) { EventListener eventListener) {
this(uuid, mediaDrm, callback, optionalKeyRequestParameters, eventHandler, eventListener, this(uuid, mediaDrm, callback, optionalKeyRequestParameters, eventHandler, eventListener,
false); false, INITIAL_DRM_REQUEST_RETRY_COUNT);
} }
/** /**
@ -211,6 +215,27 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
public DefaultDrmSessionManager(UUID uuid, ExoMediaDrm<T> mediaDrm, MediaDrmCallback callback, public DefaultDrmSessionManager(UUID uuid, ExoMediaDrm<T> mediaDrm, MediaDrmCallback callback,
HashMap<String, String> optionalKeyRequestParameters, Handler eventHandler, HashMap<String, String> optionalKeyRequestParameters, Handler eventHandler,
EventListener eventListener, boolean multiSession) { EventListener eventListener, boolean multiSession) {
this(uuid, mediaDrm, callback, optionalKeyRequestParameters, eventHandler, eventListener,
multiSession, INITIAL_DRM_REQUEST_RETRY_COUNT);
}
/**
* @param uuid The UUID of the drm scheme.
* @param mediaDrm An underlying {@link ExoMediaDrm} for use by the manager.
* @param callback Performs key and provisioning requests.
* @param optionalKeyRequestParameters An optional map of parameters to pass as the last argument
* to {@link ExoMediaDrm#getKeyRequest(byte[], byte[], String, int, HashMap)}. May be null.
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @param multiSession A boolean that specify whether multiple key session support is enabled.
* Default is false.
* @param initialDrmRequestRetryCount The number of times to retry for initial provisioning and
* key request before reporting error.
*/
public DefaultDrmSessionManager(UUID uuid, ExoMediaDrm<T> mediaDrm, MediaDrmCallback callback,
HashMap<String, String> optionalKeyRequestParameters, Handler eventHandler,
EventListener eventListener, boolean multiSession, int initialDrmRequestRetryCount) {
Assertions.checkNotNull(uuid); Assertions.checkNotNull(uuid);
Assertions.checkNotNull(mediaDrm); Assertions.checkNotNull(mediaDrm);
Assertions.checkArgument(!C.COMMON_PSSH_UUID.equals(uuid), "Use C.CLEARKEY_UUID instead"); Assertions.checkArgument(!C.COMMON_PSSH_UUID.equals(uuid), "Use C.CLEARKEY_UUID instead");
@ -221,6 +246,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.eventListener = eventListener; this.eventListener = eventListener;
this.multiSession = multiSession; this.multiSession = multiSession;
this.initialDrmRequestRetryCount = initialDrmRequestRetryCount;
mode = MODE_PLAYBACK; mode = MODE_PLAYBACK;
sessions = new ArrayList<>(); sessions = new ArrayList<>();
provisioningSessions = new ArrayList<>(); provisioningSessions = new ArrayList<>();
@ -377,7 +403,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
// Create a new session. // Create a new session.
session = new DefaultDrmSession<>(uuid, mediaDrm, this, initData, mimeType, mode, session = new DefaultDrmSession<>(uuid, mediaDrm, this, initData, mimeType, mode,
offlineLicenseKeySetId, optionalKeyRequestParameters, callback, playbackLooper, offlineLicenseKeySetId, optionalKeyRequestParameters, callback, playbackLooper,
eventHandler, eventListener); eventHandler, eventListener, initialDrmRequestRetryCount);
sessions.add(session); sessions.add(session);
} }
session.acquire(); session.acquire();