Merge pull request #59 from h6ah4i:fix/cleanup-callbacks-on-media-controller-release

PiperOrigin-RevId: 441253378
This commit is contained in:
Ian Baker 2022-04-26 14:32:15 +01:00
commit 85c09753ef
5 changed files with 119 additions and 6 deletions

View File

@ -65,6 +65,9 @@
([#47](https://github.com/androidx/media/pull/47)). ([#47](https://github.com/androidx/media/pull/47)).
* Add RTP reader for WAV * Add RTP reader for WAV
([#56](https://github.com/androidx/media/pull/56)). ([#56](https://github.com/androidx/media/pull/56)).
* Session:
* Fix NPE in MediaControllerImplLegacy
([#59](https://github.com/androidx/media/pull/59))
* Data sources: * Data sources:
* Rename `DummyDataSource` to `PlaceHolderDataSource`. * Rename `DummyDataSource` to `PlaceHolderDataSource`.
* Remove deprecated symbols: * Remove deprecated symbols:

View File

@ -415,6 +415,7 @@ public class MediaController implements Player {
return; return;
} }
released = true; released = true;
applicationHandler.removeCallbacksAndMessages(null);
try { try {
impl.release(); impl.release();
} catch (Exception e) { } catch (Exception e) {

View File

@ -192,7 +192,7 @@ public class MediaControllerTest {
} }
@Test @Test
public void isConnected_afterDisconnection_returnsFalse() throws Exception { public void isConnected_afterDisconnectionBySessionRelease_returnsFalse() throws Exception {
CountDownLatch disconnectedLatch = new CountDownLatch(1); CountDownLatch disconnectedLatch = new CountDownLatch(1);
MediaController controller = MediaController controller =
controllerTestRule.createController( controllerTestRule.createController(
@ -210,6 +210,43 @@ public class MediaControllerTest {
assertThat(controller.isConnected()).isFalse(); assertThat(controller.isConnected()).isFalse();
} }
@Test
public void isConnected_afterDisconnectionByControllerRelease_returnsFalse() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
MediaController controller =
controllerTestRule.createController(
remoteSession.getToken(),
/* connectionHints= */ null,
new MediaController.Listener() {
@Override
public void onDisconnected(MediaController controller) {
latch.countDown();
}
});
threadTestRule.getHandler().postAndSync(controller::release);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(controller.isConnected()).isFalse();
}
@Test
public void isConnected_afterDisconnectionByControllerReleaseRightAfterCreated_returnsFalse()
throws Exception {
CountDownLatch latch = new CountDownLatch(1);
MediaController controller =
controllerTestRule.createController(
remoteSession.getToken(),
/* connectionHints= */ null,
new MediaController.Listener() {
@Override
public void onDisconnected(MediaController controller) {
latch.countDown();
}
},
/* controllerCreationListener= */ MediaController::release);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(controller.isConnected()).isFalse();
}
@Test @Test
public void close_twice() throws Exception { public void close_twice() throws Exception {
MediaController controller = controllerTestRule.createController(remoteSession.getToken()); MediaController controller = controllerTestRule.createController(remoteSession.getToken());

View File

@ -22,13 +22,16 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.MediaSessionCompat;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.collection.ArrayMap; import androidx.collection.ArrayMap;
import androidx.media3.common.util.Log; import androidx.media3.common.util.Log;
import androidx.media3.test.session.common.HandlerThreadTestRule; import androidx.media3.test.session.common.HandlerThreadTestRule;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutionException;
import org.junit.rules.ExternalResource; import org.junit.rules.ExternalResource;
/** /**
@ -44,6 +47,13 @@ public final class MediaControllerTestRule extends ExternalResource {
private volatile Class<? extends MediaController> controllerType; private volatile Class<? extends MediaController> controllerType;
private volatile long timeoutMs; private volatile long timeoutMs;
/** Listener to get notified when a controller has been created. */
public interface MediaControllerCreationListener {
/** Called immediately after the given controller has been created. */
@MainThread
void onCreated(MediaController controller);
}
public MediaControllerTestRule(HandlerThreadTestRule handlerThreadTestRule) { public MediaControllerTestRule(HandlerThreadTestRule handlerThreadTestRule) {
this.handlerThreadTestRule = handlerThreadTestRule; this.handlerThreadTestRule = handlerThreadTestRule;
controllers = new ArrayMap<>(); controllers = new ArrayMap<>();
@ -96,17 +106,31 @@ public final class MediaControllerTestRule extends ExternalResource {
public MediaController createController( public MediaController createController(
MediaSessionCompat.Token token, @Nullable MediaController.Listener listener) MediaSessionCompat.Token token, @Nullable MediaController.Listener listener)
throws Exception { throws Exception {
return createController(token, listener, /* controllerCreateListener= */ null);
}
/** Creates {@link MediaController} from {@link MediaSessionCompat.Token}. */
public MediaController createController(
MediaSessionCompat.Token token,
@Nullable MediaController.Listener listener,
@Nullable MediaControllerCreationListener controllerCreationListener)
throws Exception {
TestMediaBrowserListener testListener = new TestMediaBrowserListener(listener); TestMediaBrowserListener testListener = new TestMediaBrowserListener(listener);
MediaController controller = createControllerOnHandler(token, testListener); MediaController controller =
createControllerOnHandler(token, testListener, controllerCreationListener);
controllers.put(controller, testListener); controllers.put(controller, testListener);
return controller; return controller;
} }
private MediaController createControllerOnHandler( private MediaController createControllerOnHandler(
MediaSessionCompat.Token token, TestMediaBrowserListener listener) throws Exception { MediaSessionCompat.Token token,
TestMediaBrowserListener listener,
@Nullable MediaControllerCreationListener controllerCreationListener)
throws Exception {
SessionToken sessionToken = SessionToken sessionToken =
SessionToken.createSessionToken(context, token).get(TIMEOUT_MS, MILLISECONDS); SessionToken.createSessionToken(context, token).get(TIMEOUT_MS, MILLISECONDS);
return createControllerOnHandler(sessionToken, /* connectionHints= */ null, listener); return createControllerOnHandler(
sessionToken, /* connectionHints= */ null, listener, controllerCreationListener);
} }
/** Creates {@link MediaController} from {@link SessionToken} with default options. */ /** Creates {@link MediaController} from {@link SessionToken} with default options. */
@ -120,14 +144,29 @@ public final class MediaControllerTestRule extends ExternalResource {
@Nullable Bundle connectionHints, @Nullable Bundle connectionHints,
@Nullable MediaController.Listener listener) @Nullable MediaController.Listener listener)
throws Exception { throws Exception {
return createController(
token, connectionHints, listener, /* controllerCreationListener= */ null);
}
/** Creates {@link MediaController} from {@link SessionToken}. */
public MediaController createController(
SessionToken token,
@Nullable Bundle connectionHints,
@Nullable MediaController.Listener listener,
@Nullable MediaControllerCreationListener controllerCreationListener)
throws Exception {
TestMediaBrowserListener testListener = new TestMediaBrowserListener(listener); TestMediaBrowserListener testListener = new TestMediaBrowserListener(listener);
MediaController controller = createControllerOnHandler(token, connectionHints, testListener); MediaController controller =
createControllerOnHandler(token, connectionHints, testListener, controllerCreationListener);
controllers.put(controller, testListener); controllers.put(controller, testListener);
return controller; return controller;
} }
private MediaController createControllerOnHandler( private MediaController createControllerOnHandler(
SessionToken token, @Nullable Bundle connectionHints, TestMediaBrowserListener listener) SessionToken token,
@Nullable Bundle connectionHints,
TestMediaBrowserListener listener,
@Nullable MediaControllerCreationListener controllerCreationListener)
throws Exception { throws Exception {
// Create controller on the test handler, for changing MediaBrowserCompat's Handler // Create controller on the test handler, for changing MediaBrowserCompat's Handler
// Looper. Otherwise, MediaBrowserCompat will post all the commands to the handler // Looper. Otherwise, MediaBrowserCompat will post all the commands to the handler
@ -153,6 +192,22 @@ public final class MediaControllerTestRule extends ExternalResource {
return builder.buildAsync(); return builder.buildAsync();
} }
}); });
if (controllerCreationListener != null) {
future.addListener(
() -> {
@Nullable MediaController mediaController = null;
try {
mediaController = future.get();
} catch (ExecutionException | InterruptedException e) {
Log.e(TAG, "failed getting a controller", e);
}
if (mediaController != null) {
controllerCreationListener.onCreated(mediaController);
}
},
MoreExecutors.directExecutor());
}
return future.get(timeoutMs, MILLISECONDS); return future.get(timeoutMs, MILLISECONDS);
} }

View File

@ -170,6 +170,23 @@ public class MediaControllerWithMediaSessionCompatTest {
assertThat(controller.isConnected()).isFalse(); assertThat(controller.isConnected()).isFalse();
} }
@Test
public void disconnected_byControllerReleaseRightAfterCreated() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
MediaController controller =
controllerTestRule.createController(
session.getSessionToken(),
new MediaController.Listener() {
@Override
public void onDisconnected(MediaController controller) {
latch.countDown();
}
},
/* controllerCreationListener= */ MediaController::release);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(controller.isConnected()).isFalse();
}
@Test @Test
public void close_twice_doesNotCrash() throws Exception { public void close_twice_doesNotCrash() throws Exception {
MediaController controller = controllerTestRule.createController(session.getSessionToken()); MediaController controller = controllerTestRule.createController(session.getSessionToken());