Avoid DefaultDrmSessionManager releasing too many session references

Before this fix, if DefaultDrmSessionManager.release() was called while
there was at least one 'external' session reference still active (i.e.
session.referenceCount > 1) then the manager will release it's reference
immediately but when the session's reference count subsequently drops to
1 (due to external references being released) the manager will schedule
a task to release its internal reference *again*.

This change fixes the problem by only scheduling the timed release if
the manager is unreleased. This ensures that the internal references
are only released once.

Issue: #9193

PiperOrigin-RevId: 385580741
This commit is contained in:
ibaker 2021-07-19 18:05:47 +01:00 committed by Ian Baker
parent 0ae71e22f7
commit dbebd279c4
3 changed files with 46 additions and 2 deletions

View File

@ -45,6 +45,10 @@
([#9106](https://github.com/google/ExoPlayer/issues/9106).
* DRM:
* Allow repeated provisioning in `DefaultDrmSession(Manager)`.
* Fix a crash due to `DefaultDrmSessionManager.release()` incorrectly
releasing too many keep-alive `DefaultDrmSession` references, resulting
in `DefaultDrmSession.release()` throwing an `IllegalStateException`
([#9193](https://github.com/google/ExoPlayer/issues/9193)).
* Metadata:
* Fix handling of emsg messages with an unset duration
([#9123](https://github.com/google/ExoPlayer/issues/9123)).

View File

@ -911,8 +911,10 @@ public class DefaultDrmSessionManager implements DrmSessionManager {
@Override
public void onReferenceCountDecremented(DefaultDrmSession session, int newReferenceCount) {
if (newReferenceCount == 1 && sessionKeepaliveMs != C.TIME_UNSET) {
// Only the internal keep-alive reference remains, so we can start the timeout.
if (newReferenceCount == 1 && prepareCallsCount > 0 && sessionKeepaliveMs != C.TIME_UNSET) {
// Only the internal keep-alive reference remains, so we can start the timeout. We only
// do this if the manager isn't released, because a released manager has already released
// all its internal session keep-alive references.
keepaliveSessions.add(session);
checkNotNull(playbackHandler)
.postAtTime(

View File

@ -222,6 +222,44 @@ public class DefaultDrmSessionManagerTest {
exoMediaDrm.release();
}
// https://github.com/google/ExoPlayer/issues/9193
@Test(timeout = 10_000)
public void
managerReleasedBeforeSession_keepaliveEnabled_managerOnlyReleasesOneKeepaliveReference()
throws Exception {
FakeExoMediaDrm.LicenseServer licenseServer =
FakeExoMediaDrm.LicenseServer.allowingSchemeDatas(DRM_SCHEME_DATAS);
FakeExoMediaDrm exoMediaDrm = new FakeExoMediaDrm.Builder().build();
DrmSessionManager drmSessionManager =
new DefaultDrmSessionManager.Builder()
.setUuidAndExoMediaDrmProvider(DRM_SCHEME_UUID, new AppManagedProvider(exoMediaDrm))
.setSessionKeepaliveMs(10_000)
.build(/* mediaDrmCallback= */ licenseServer);
drmSessionManager.prepare();
DrmSession drmSession =
checkNotNull(
drmSessionManager.acquireSession(
/* playbackLooper= */ checkNotNull(Looper.myLooper()),
/* eventDispatcher= */ null,
FORMAT_WITH_DRM_INIT_DATA));
waitForOpenedWithKeys(drmSession);
// Release the manager (there's still an explicit reference to the session from acquireSession).
// This should immediately release the manager's internal keepalive session reference.
drmSessionManager.release();
assertThat(drmSession.getState()).isEqualTo(DrmSession.STATE_OPENED_WITH_KEYS);
// Ensure the manager doesn't release a *second* keepalive session reference after the timer
// expires.
ShadowLooper.idleMainLooper(10, SECONDS);
assertThat(drmSession.getState()).isEqualTo(DrmSession.STATE_OPENED_WITH_KEYS);
// Release the explicit session reference.
drmSession.release(/* eventDispatcher= */ null);
assertThat(drmSession.getState()).isEqualTo(DrmSession.STATE_RELEASED);
}
@Test(timeout = 10_000)
public void maxConcurrentSessionsExceeded_allKeepAliveSessionsEagerlyReleased() throws Exception {
ImmutableList<DrmInitData.SchemeData> secondSchemeDatas =