Merge "[Media] Add a MediaController.Callback that will disconnect us when onSessionDestroyed is triggered." into tm-dev

This commit is contained in:
Caitlin Cassidy
2022-05-10 17:51:44 +00:00
committed by Android (Google) Code Review
3 changed files with 119 additions and 3 deletions

View File

@@ -52,8 +52,10 @@ public class ResumeMediaBrowser {
private final MediaBrowserFactory mBrowserFactory; private final MediaBrowserFactory mBrowserFactory;
private final ResumeMediaBrowserLogger mLogger; private final ResumeMediaBrowserLogger mLogger;
private final ComponentName mComponentName; private final ComponentName mComponentName;
private final MediaController.Callback mMediaControllerCallback = new SessionDestroyCallback();
private MediaBrowser mMediaBrowser; private MediaBrowser mMediaBrowser;
@Nullable private MediaController mMediaController;
/** /**
* Initialize a new media browser * Initialize a new media browser
@@ -90,6 +92,7 @@ public class ResumeMediaBrowser {
mComponentName, mComponentName,
mConnectionCallback, mConnectionCallback,
rootHints); rootHints);
updateMediaController();
mLogger.logConnection(mComponentName, "findRecentMedia"); mLogger.logConnection(mComponentName, "findRecentMedia");
mMediaBrowser.connect(); mMediaBrowser.connect();
} }
@@ -154,7 +157,8 @@ public class ResumeMediaBrowser {
@Override @Override
public void onConnected() { public void onConnected() {
Log.d(TAG, "Service connected for " + mComponentName); Log.d(TAG, "Service connected for " + mComponentName);
if (mMediaBrowser != null && mMediaBrowser.isConnected()) { updateMediaController();
if (isBrowserConnected()) {
String root = mMediaBrowser.getRoot(); String root = mMediaBrowser.getRoot();
if (!TextUtils.isEmpty(root)) { if (!TextUtils.isEmpty(root)) {
if (mCallback != null) { if (mCallback != null) {
@@ -207,6 +211,7 @@ public class ResumeMediaBrowser {
mMediaBrowser.disconnect(); mMediaBrowser.disconnect();
} }
mMediaBrowser = null; mMediaBrowser = null;
updateMediaController();
} }
/** /**
@@ -225,7 +230,8 @@ public class ResumeMediaBrowser {
@Override @Override
public void onConnected() { public void onConnected() {
Log.d(TAG, "Connected for restart " + mMediaBrowser.isConnected()); Log.d(TAG, "Connected for restart " + mMediaBrowser.isConnected());
if (mMediaBrowser == null || !mMediaBrowser.isConnected()) { updateMediaController();
if (!isBrowserConnected()) {
if (mCallback != null) { if (mCallback != null) {
mCallback.onError(); mCallback.onError();
} }
@@ -259,6 +265,7 @@ public class ResumeMediaBrowser {
disconnect(); disconnect();
} }
}, rootHints); }, rootHints);
updateMediaController();
mLogger.logConnection(mComponentName, "restart"); mLogger.logConnection(mComponentName, "restart");
mMediaBrowser.connect(); mMediaBrowser.connect();
} }
@@ -273,7 +280,7 @@ public class ResumeMediaBrowser {
* @return the token, or null if the MediaBrowser is null or disconnected * @return the token, or null if the MediaBrowser is null or disconnected
*/ */
public MediaSession.Token getToken() { public MediaSession.Token getToken() {
if (mMediaBrowser == null || !mMediaBrowser.isConnected()) { if (!isBrowserConnected()) {
return null; return null;
} }
return mMediaBrowser.getSessionToken(); return mMediaBrowser.getSessionToken();
@@ -305,10 +312,39 @@ public class ResumeMediaBrowser {
mComponentName, mComponentName,
mConnectionCallback, mConnectionCallback,
rootHints); rootHints);
updateMediaController();
mLogger.logConnection(mComponentName, "testConnection"); mLogger.logConnection(mComponentName, "testConnection");
mMediaBrowser.connect(); 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 * Interface to handle results from ResumeMediaBrowser
*/ */
@@ -335,4 +371,12 @@ public class ResumeMediaBrowser {
ResumeMediaBrowser browser) { 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" } { "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" private const val TAG = "MediaBrowser"

View File

@@ -35,6 +35,7 @@ import org.mockito.ArgumentCaptor
import org.mockito.Captor import org.mockito.Captor
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito import org.mockito.Mockito
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever 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 connectionCallback: ArgumentCaptor<MediaBrowser.ConnectionCallback>
@Captor lateinit var subscriptionCallback: ArgumentCaptor<MediaBrowser.SubscriptionCallback> @Captor lateinit var subscriptionCallback: ArgumentCaptor<MediaBrowser.SubscriptionCallback>
@Captor lateinit var mediaControllerCallback: ArgumentCaptor<MediaController.Callback>
@Before @Before
fun setUp() { fun setUp() {
@@ -82,6 +84,7 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
.thenReturn(browser) .thenReturn(browser)
whenever(mediaController.transportControls).thenReturn(transportControls) whenever(mediaController.transportControls).thenReturn(transportControls)
whenever(mediaController.sessionToken).thenReturn(token)
resumeBrowser = TestableResumeMediaBrowser( resumeBrowser = TestableResumeMediaBrowser(
context, context,
@@ -136,6 +139,22 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
verify(callback).addTrack(eq(description), eq(component), eq(resumeBrowser)) 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 @Test
fun testConnection_calledTwice_oldBrowserDisconnected() { fun testConnection_calledTwice_oldBrowserDisconnected() {
val oldBrowser = mock<MediaBrowser>() val oldBrowser = mock<MediaBrowser>()
@@ -187,6 +206,22 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
verify(callback).onConnected() 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 @Test
fun testFindRecentMedia_calledTwice_oldBrowserDisconnected() { fun testFindRecentMedia_calledTwice_oldBrowserDisconnected() {
val oldBrowser = mock<MediaBrowser>() val oldBrowser = mock<MediaBrowser>()
@@ -260,6 +295,22 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
verify(transportControls).play() 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 @Test
fun testRestart_calledTwice_oldBrowserDisconnected() { fun testRestart_calledTwice_oldBrowserDisconnected() {
val oldBrowser = mock<MediaBrowser>() val oldBrowser = mock<MediaBrowser>()