From 21022c77be44fefeaa25ef9fb72be7d48e51a0bc Mon Sep 17 00:00:00 2001 From: bachinger Date: Thu, 27 Oct 2022 11:50:03 +0000 Subject: [PATCH] Use MediaBrowserCompat.rootHints as connections hints In Media3 there is the useful concept of connection hints that a client can set when building the session and that are sent to the service and passed to the `Callback.onConnect()` method when the browser connects. These connection hints are then included in the `ControllerInfo` object that later will be passed to every callback method and the implementor can then take decisions specific to these connection hints. These connection hints are not available in media1. However, when an app creates a `MediaBrowserCompat` object, the constructor takes a rootHint object that is sent to `MediaBrowserServiceCompat.onGetRoot()`. This change uses the browser rootHints as the connection hints when creating the `ControllerInfo` for legacy browsers and makes them available to the `MediaLibrarySession.Callback` domain methods in the same way as connection hints of a Media3 browser. PiperOrigin-RevId: 484220748 --- .../MediaLibraryServiceLegacyStub.java | 4 +- .../MediaSessionServiceLegacyStub.java | 7 +- ...wserCompatWithMediaLibraryServiceTest.java | 85 ++++++++++--------- ...wserCompatWithMediaSessionServiceTest.java | 24 +++--- .../session/MockMediaLibraryService.java | 22 ++++- 5 files changed, 86 insertions(+), 56 deletions(-) diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryServiceLegacyStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryServiceLegacyStub.java index 998c255e28..9a90e9478a 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaLibraryServiceLegacyStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibraryServiceLegacyStub.java @@ -317,14 +317,14 @@ import java.util.concurrent.atomic.AtomicReference; } @Override - public ControllerInfo createControllerInfo(RemoteUserInfo remoteUserInfo) { + public ControllerInfo createControllerInfo(RemoteUserInfo remoteUserInfo, Bundle rootHints) { return new ControllerInfo( remoteUserInfo, ControllerInfo.LEGACY_CONTROLLER_VERSION, ControllerInfo.LEGACY_CONTROLLER_INTERFACE_VERSION, getMediaSessionManager().isTrustedForMediaControl(remoteUserInfo), new BrowserLegacyCb(remoteUserInfo), - /* connectionHints= */ Bundle.EMPTY); + /* connectionHints= */ rootHints); } public ControllerCb getBrowserLegacyCbForBroadcast() { diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionServiceLegacyStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionServiceLegacyStub.java index 7cc049ba4b..f845029ed0 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionServiceLegacyStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionServiceLegacyStub.java @@ -61,7 +61,8 @@ import java.util.concurrent.atomic.AtomicReference; public BrowserRoot onGetRoot( String clientPackageName, int clientUid, @Nullable Bundle rootHints) { RemoteUserInfo info = getCurrentBrowserInfo(); - MediaSession.ControllerInfo controller = createControllerInfo(info); + MediaSession.ControllerInfo controller = + createControllerInfo(info, rootHints != null ? rootHints : Bundle.EMPTY); AtomicReference resultReference = new AtomicReference<>(); ConditionVariable haveResult = new ConditionVariable(); @@ -92,14 +93,14 @@ import java.util.concurrent.atomic.AtomicReference; result.sendResult(/* result= */ null); } - public ControllerInfo createControllerInfo(RemoteUserInfo info) { + public ControllerInfo createControllerInfo(RemoteUserInfo info, Bundle rootHints) { return new ControllerInfo( info, ControllerInfo.LEGACY_CONTROLLER_VERSION, ControllerInfo.LEGACY_CONTROLLER_INTERFACE_VERSION, manager.isTrustedForMediaControl(info), /* cb= */ null, - /* connectionHints= */ Bundle.EMPTY); + /* connectionHints= */ rootHints); } public final MediaSessionManager getMediaSessionManager() { diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaLibraryServiceTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaLibraryServiceTest.java index e6b31bd549..b05e2dd455 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaLibraryServiceTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaLibraryServiceTest.java @@ -17,6 +17,7 @@ package androidx.media3.session; import static androidx.media3.session.MediaConstants.EXTRAS_KEY_COMPLETION_STATUS; import static androidx.media3.session.MediaConstants.EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED; +import static androidx.media3.session.MockMediaLibraryService.CONNECTION_HINTS_CUSTOM_LIBRARY_ROOT; import static androidx.media3.test.session.common.CommonConstants.METADATA_ARTWORK_URI; import static androidx.media3.test.session.common.CommonConstants.METADATA_DESCRIPTION; import static androidx.media3.test.session.common.CommonConstants.METADATA_EXTRA_KEY; @@ -93,13 +94,8 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest public void getRoot() throws Exception { // The MockMediaLibraryService gives MediaBrowserConstants.ROOT_ID as root ID, and // MediaBrowserConstants.ROOT_EXTRAS as extras. - handler.postAndSync( - () -> { - browserCompat = - new MediaBrowserCompat( - context, getServiceComponent(), connectionCallback, /* rootHint= */ null); - }); - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); + assertThat(browserCompat.getRoot()).isEqualTo(ROOT_ID); assertThat( browserCompat @@ -113,10 +109,10 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest } @Test - public void getItem_browsable() throws InterruptedException { + public void getItem_browsable() throws Exception { String mediaId = MEDIA_ID_GET_BROWSABLE_ITEM; - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch latch = new CountDownLatch(1); AtomicReference itemRef = new AtomicReference<>(); browserCompat.getItem( @@ -135,10 +131,10 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest } @Test - public void getItem_playable() throws InterruptedException { + public void getItem_playable() throws Exception { String mediaId = MEDIA_ID_GET_PLAYABLE_ITEM; - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch latch = new CountDownLatch(1); AtomicReference itemRef = new AtomicReference<>(); browserCompat.getItem( @@ -157,10 +153,10 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest } @Test - public void getItem_metadata() throws InterruptedException { + public void getItem_metadata() throws Exception { String mediaId = MEDIA_ID_GET_ITEM_WITH_METADATA; - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch latch = new CountDownLatch(1); AtomicReference itemRef = new AtomicReference<>(); browserCompat.getItem( @@ -187,10 +183,10 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest } @Test - public void getItem_nullResult() throws InterruptedException { + public void getItem_nullResult() throws Exception { String mediaId = "random_media_id"; - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch latch = new CountDownLatch(1); browserCompat.getItem( mediaId, @@ -210,9 +206,9 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest } @Test - public void getChildren() throws InterruptedException { + public void getChildren() throws Exception { String testParentId = PARENT_ID; - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch latch = new CountDownLatch(1); List receivedChildren = new ArrayList<>(); final String[] receivedParentId = new String[1]; @@ -252,10 +248,10 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest } @Test - public void getChildren_withLongList() throws InterruptedException { + public void getChildren_withLongList() throws Exception { String testParentId = PARENT_ID_LONG_LIST; - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch latch = new CountDownLatch(1); browserCompat.subscribe( testParentId, @@ -283,14 +279,14 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest } @Test - public void getChildren_withPagination() throws InterruptedException { + public void getChildren_withPagination() throws Exception { String testParentId = PARENT_ID; int page = 4; int pageSize = 10; Bundle extras = new Bundle(); extras.putString(testParentId, testParentId); - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch latch = new CountDownLatch(1); Bundle option = new Bundle(); option.putInt(MediaBrowserCompat.EXTRA_PAGE, page); @@ -327,9 +323,9 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest } @Test - public void getChildren_authErrorResult() throws InterruptedException { + public void getChildren_authErrorResult() throws Exception { String testParentId = PARENT_ID_AUTH_EXPIRED_ERROR; - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch errorLatch = new CountDownLatch(1); browserCompat.subscribe( testParentId, @@ -368,10 +364,10 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest } @Test - public void getChildren_emptyResult() throws InterruptedException { + public void getChildren_emptyResult() throws Exception { String testParentId = PARENT_ID_NO_CHILDREN; - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch latch = new CountDownLatch(1); browserCompat.subscribe( testParentId, @@ -387,10 +383,10 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest } @Test - public void getChildren_nullResult() throws InterruptedException { + public void getChildren_nullResult() throws Exception { String testParentId = PARENT_ID_ERROR; - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch latch = new CountDownLatch(1); browserCompat.subscribe( testParentId, @@ -410,7 +406,7 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest } @Test - public void search() throws InterruptedException { + public void search() throws Exception { String testQuery = SEARCH_QUERY; int page = 4; int pageSize = 10; @@ -419,7 +415,7 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest testExtras.putInt(MediaBrowserCompat.EXTRA_PAGE, page); testExtras.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize); - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch latch = new CountDownLatch(1); browserCompat.search( testQuery, @@ -449,7 +445,7 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest } @Test - public void search_withLongList() throws InterruptedException { + public void search_withLongList() throws Exception { String testQuery = SEARCH_QUERY_LONG_LIST; int page = 0; int pageSize = Integer.MAX_VALUE; @@ -458,7 +454,7 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest testExtras.putInt(MediaBrowserCompat.EXTRA_PAGE, page); testExtras.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize); - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch latch = new CountDownLatch(1); browserCompat.search( testQuery, @@ -482,12 +478,12 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest } @Test - public void search_emptyResult() throws InterruptedException { + public void search_emptyResult() throws Exception { String testQuery = SEARCH_QUERY_EMPTY_RESULT; Bundle testExtras = new Bundle(); testExtras.putString(testQuery, testQuery); - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch latch = new CountDownLatch(1); browserCompat.search( testQuery, @@ -506,12 +502,12 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest } @Test - public void search_error() throws InterruptedException { + public void search_error() throws Exception { String testQuery = SEARCH_QUERY_ERROR; Bundle testExtras = new Bundle(); testExtras.putString(testQuery, testQuery); - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch latch = new CountDownLatch(1); browserCompat.search( testQuery, @@ -534,11 +530,11 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest // TODO: Add test for onCustomCommand() in MediaLibrarySessionLegacyCallbackTest. @Test - public void customAction() throws InterruptedException { + public void customAction() throws Exception { Bundle testArgs = new Bundle(); testArgs.putString("args_key", "args_value"); - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch latch = new CountDownLatch(1); browserCompat.sendCustomAction( CUSTOM_ACTION, @@ -557,11 +553,11 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest // TODO: Add test for onCustomCommand() in MediaLibrarySessionLegacyCallbackTest. @Test - public void customAction_rejected() throws InterruptedException { + public void customAction_rejected() throws Exception { // This action will not be allowed by the library session. String testAction = "random_custom_action"; - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); CountDownLatch latch = new CountDownLatch(1); browserCompat.sendCustomAction( testAction, @@ -576,4 +572,15 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest .that(latch.await(NO_RESPONSE_TIMEOUT_MS, MILLISECONDS)) .isFalse(); } + + @Test + public void rootBrowserHints_usedAsConnectionHints() throws Exception { + Bundle connectionHints = new Bundle(); + connectionHints.putString(CONNECTION_HINTS_CUSTOM_LIBRARY_ROOT, "myLibraryRoot"); + connectAndWait(connectionHints); + + String root = browserCompat.getRoot(); + + assertThat(root).isEqualTo("myLibraryRoot"); + } } diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaSessionServiceTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaSessionServiceTest.java index 64db577d13..12caed0f8e 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaSessionServiceTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaSessionServiceTest.java @@ -23,6 +23,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import android.content.ComponentName; import android.content.Context; +import android.os.Bundle; import android.support.v4.media.MediaBrowserCompat; import android.support.v4.media.session.MediaControllerCompat; import android.support.v4.media.session.PlaybackStateCompat; @@ -63,16 +64,10 @@ public class MediaBrowserCompatWithMediaSessionServiceTest { @Nullable PlaybackStateCompat lastReportedPlaybackStateCompat; @Before - public void setUp() throws Exception { + public void setUp() { context = ApplicationProvider.getApplicationContext(); handler = threadTestRule.getHandler(); connectionCallback = new TestConnectionCallback(); - handler.postAndSync( - () -> { - // Make browser's internal handler to be initialized with test thread. - browserCompat = - new MediaBrowserCompat(context, getServiceComponent(), connectionCallback, null); - }); } @After @@ -87,15 +82,22 @@ public class MediaBrowserCompatWithMediaSessionServiceTest { return MOCK_MEDIA3_SESSION_SERVICE; } - void connectAndWait() throws InterruptedException { + void connectAndWait(Bundle connectionHints) throws Exception { + handler.postAndSync( + () -> { + // Make browser's internal handler to be initialized with test thread. + browserCompat = + new MediaBrowserCompat( + context, getServiceComponent(), connectionCallback, connectionHints); + }); browserCompat.connect(); assertThat(connectionCallback.connectedLatch.await(SERVICE_CONNECTION_TIMEOUT_MS, MILLISECONDS)) .isTrue(); } @Test - public void connect() throws InterruptedException { - connectAndWait(); + public void connect() throws Exception { + connectAndWait(/* connectionHints= */ Bundle.EMPTY); assertThat(connectionCallback.failedLatch.getCount()).isNotEqualTo(0); } @@ -109,7 +111,7 @@ public class MediaBrowserCompatWithMediaSessionServiceTest { @Test public void getSessionToken() throws Exception { - connectAndWait(); + connectAndWait(/* connectionHints= */ Bundle.EMPTY); MediaControllerCompat controller = new MediaControllerCompat(context, browserCompat.getSessionToken()); assertThat(controller.getPackageName()) diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MockMediaLibraryService.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MockMediaLibraryService.java index 0749077056..995b162150 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/MockMediaLibraryService.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MockMediaLibraryService.java @@ -82,6 +82,9 @@ public class MockMediaLibraryService extends MediaLibraryService { /** ID of the session that this service will create. */ public static final String ID = "TestLibrary"; + public static final String CONNECTION_HINTS_CUSTOM_LIBRARY_ROOT = + "CONNECTION_HINTS_CUSTOM_LIBRARY_ROOT"; + public static final MediaItem ROOT_ITEM = new MediaItem.Builder() .setMediaId(ROOT_ID) @@ -192,7 +195,24 @@ public class MockMediaLibraryService extends MediaLibraryService { public ListenableFuture> onGetLibraryRoot( MediaLibrarySession session, ControllerInfo browser, @Nullable LibraryParams params) { assertLibraryParams(params); - return Futures.immediateFuture(LibraryResult.ofItem(ROOT_ITEM, ROOT_PARAMS)); + MediaItem rootItem = ROOT_ITEM; + // Use connection hints to select the library root. + String customLibraryRoot = + browser + .getConnectionHints() + .getString(CONNECTION_HINTS_CUSTOM_LIBRARY_ROOT, /* defaultValue= */ null); + if (customLibraryRoot != null) { + rootItem = + new MediaItem.Builder() + .setMediaId(customLibraryRoot) + .setMediaMetadata( + new MediaMetadata.Builder() + .setFolderType(MediaMetadata.FOLDER_TYPE_ALBUMS) + .setIsPlayable(false) + .build()) + .build(); + } + return Futures.immediateFuture(LibraryResult.ofItem(rootItem, ROOT_PARAMS)); } @Override