[Media] Add a MediaController.Callback that will disconnect us when

onSessionDestroyed is triggered.

Bug: 225403871
Test: ResumeMediaBrowserTest
Change-Id: I2626a6af7d09f12184b2dda88b28838b51a247c6
This commit is contained in:
Caitlin Cassidy
2022-05-03 21:27:00 +00:00
parent 4c0c32d03c
commit 3a5d2ebeb9
3 changed files with 119 additions and 3 deletions

View File

@@ -52,8 +52,10 @@ public class ResumeMediaBrowser {
private final MediaBrowserFactory mBrowserFactory;
private final ResumeMediaBrowserLogger mLogger;
private final ComponentName mComponentName;
private final MediaController.Callback mMediaControllerCallback = new SessionDestroyCallback();
private MediaBrowser mMediaBrowser;
@Nullable private MediaController mMediaController;
/**
* Initialize a new media browser
@@ -90,6 +92,7 @@ public class ResumeMediaBrowser {
mComponentName,
mConnectionCallback,
rootHints);
updateMediaController();
mLogger.logConnection(mComponentName, "findRecentMedia");
mMediaBrowser.connect();
}
@@ -154,7 +157,8 @@ public class ResumeMediaBrowser {
@Override
public void onConnected() {
Log.d(TAG, "Service connected for " + mComponentName);
if (mMediaBrowser != null && mMediaBrowser.isConnected()) {
updateMediaController();
if (isBrowserConnected()) {
String root = mMediaBrowser.getRoot();
if (!TextUtils.isEmpty(root)) {
if (mCallback != null) {
@@ -207,6 +211,7 @@ public class ResumeMediaBrowser {
mMediaBrowser.disconnect();
}
mMediaBrowser = null;
updateMediaController();
}
/**
@@ -225,7 +230,8 @@ public class ResumeMediaBrowser {
@Override
public void onConnected() {
Log.d(TAG, "Connected for restart " + mMediaBrowser.isConnected());
if (mMediaBrowser == null || !mMediaBrowser.isConnected()) {
updateMediaController();
if (!isBrowserConnected()) {
if (mCallback != null) {
mCallback.onError();
}
@@ -259,6 +265,7 @@ public class ResumeMediaBrowser {
disconnect();
}
}, rootHints);
updateMediaController();
mLogger.logConnection(mComponentName, "restart");
mMediaBrowser.connect();
}
@@ -273,7 +280,7 @@ public class ResumeMediaBrowser {
* @return the token, or null if the MediaBrowser is null or disconnected
*/
public MediaSession.Token getToken() {
if (mMediaBrowser == null || !mMediaBrowser.isConnected()) {
if (!isBrowserConnected()) {
return null;
}
return mMediaBrowser.getSessionToken();
@@ -305,10 +312,39 @@ public class ResumeMediaBrowser {
mComponentName,
mConnectionCallback,
rootHints);
updateMediaController();
mLogger.logConnection(mComponentName, "testConnection");
mMediaBrowser.connect();
}
/** Updates mMediaController based on our current browser values. */
private void updateMediaController() {
MediaSession.Token controllerToken =
mMediaController != null ? mMediaController.getSessionToken() : null;
MediaSession.Token currentToken = getToken();
boolean areEqual = (controllerToken == null && currentToken == null)
|| (controllerToken != null && controllerToken.equals(currentToken));
if (areEqual) {
return;
}
// Whenever the token changes, un-register the callback on the old controller (if we have
// one) and create a new controller with the callback attached.
if (mMediaController != null) {
mMediaController.unregisterCallback(mMediaControllerCallback);
}
if (currentToken != null) {
mMediaController = createMediaController(currentToken);
mMediaController.registerCallback(mMediaControllerCallback);
} else {
mMediaController = null;
}
}
private boolean isBrowserConnected() {
return mMediaBrowser != null && mMediaBrowser.isConnected();
}
/**
* Interface to handle results from ResumeMediaBrowser
*/
@@ -335,4 +371,12 @@ public class ResumeMediaBrowser {
ResumeMediaBrowser browser) {
}
}
private class SessionDestroyCallback extends MediaController.Callback {
@Override
public void onSessionDestroyed() {
mLogger.logSessionDestroyed(isBrowserConnected(), mComponentName);
disconnect();
}
}
}

View File

@@ -48,6 +48,27 @@ class ResumeMediaBrowserLogger @Inject constructor(
},
{ "Disconnecting browser for component $str1" }
)
/**
* Logs that we received a [android.media.session.MediaController.Callback.onSessionDestroyed]
* event.
*
* @param isBrowserConnected true if there's a currently connected
* [android.media.browse.MediaBrowser] and false otherwise.
* @param componentName the component name for the [ResumeMediaBrowser] that triggered this log.
*/
fun logSessionDestroyed(
isBrowserConnected: Boolean,
componentName: ComponentName
) = buffer.log(
TAG,
LogLevel.DEBUG,
{
bool1 = isBrowserConnected
str1 = componentName.toShortString()
},
{ "Session destroyed. Active browser = $bool1. Browser component = $str1." }
)
}
private const val TAG = "MediaBrowser"

View File

@@ -35,6 +35,7 @@ import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
@@ -73,6 +74,7 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
@Captor lateinit var connectionCallback: ArgumentCaptor<MediaBrowser.ConnectionCallback>
@Captor lateinit var subscriptionCallback: ArgumentCaptor<MediaBrowser.SubscriptionCallback>
@Captor lateinit var mediaControllerCallback: ArgumentCaptor<MediaController.Callback>
@Before
fun setUp() {
@@ -82,6 +84,7 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
.thenReturn(browser)
whenever(mediaController.transportControls).thenReturn(transportControls)
whenever(mediaController.sessionToken).thenReturn(token)
resumeBrowser = TestableResumeMediaBrowser(
context,
@@ -136,6 +139,22 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
verify(callback).addTrack(eq(description), eq(component), eq(resumeBrowser))
}
@Test
fun testConnection_thenSessionDestroyed_disconnects() {
// When testConnection is called and we connect successfully
setupBrowserConnection()
resumeBrowser.testConnection()
verify(mediaController).registerCallback(mediaControllerCallback.capture())
reset(browser)
// And a sessionDestroyed event is triggered
mediaControllerCallback.value.onSessionDestroyed()
// Then we disconnect the browser and unregister the callback
verify(browser).disconnect()
verify(mediaController).unregisterCallback(mediaControllerCallback.value)
}
@Test
fun testConnection_calledTwice_oldBrowserDisconnected() {
val oldBrowser = mock<MediaBrowser>()
@@ -187,6 +206,22 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
verify(callback).onConnected()
}
@Test
fun testFindRecentMedia_thenSessionDestroyed_disconnects() {
// When findRecentMedia is called and we connect successfully
setupBrowserConnection()
resumeBrowser.findRecentMedia()
verify(mediaController).registerCallback(mediaControllerCallback.capture())
reset(browser)
// And a sessionDestroyed event is triggered
mediaControllerCallback.value.onSessionDestroyed()
// Then we disconnect the browser and unregister the callback
verify(browser).disconnect()
verify(mediaController).unregisterCallback(mediaControllerCallback.value)
}
@Test
fun testFindRecentMedia_calledTwice_oldBrowserDisconnected() {
val oldBrowser = mock<MediaBrowser>()
@@ -260,6 +295,22 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
verify(transportControls).play()
}
@Test
fun testRestart_thenSessionDestroyed_disconnects() {
// When restart is called and we connect successfully
setupBrowserConnection()
resumeBrowser.restart()
verify(mediaController).registerCallback(mediaControllerCallback.capture())
reset(browser)
// And a sessionDestroyed event is triggered
mediaControllerCallback.value.onSessionDestroyed()
// Then we disconnect the browser and unregister the callback
verify(browser).disconnect()
verify(mediaController).unregisterCallback(mediaControllerCallback.value)
}
@Test
fun testRestart_calledTwice_oldBrowserDisconnected() {
val oldBrowser = mock<MediaBrowser>()