From 5e05e2ec2263a426f5eee70ad77e0ea89c4f0449 Mon Sep 17 00:00:00 2001 From: bachinger Date: Mon, 25 Sep 2023 07:47:48 -0700 Subject: [PATCH] Pass down ControllerInfo from service to session when connecting With this change, the `ControllerInfo` passed to `MediaSessionService.onGetSession(ControllerInfo)` is the same instance that is passed later to all callback methods of `MediaSession.Callback`. PiperOrigin-RevId: 568216855 --- .../androidx/media3/session/MediaSession.java | 17 +-- .../media3/session/MediaSessionImpl.java | 18 +-- .../media3/session/MediaSessionService.java | 11 +- .../media3/session/MediaSessionStub.java | 40 +++--- .../session/MediaLibraryServiceTest.java | 117 ++++++++++++++++++ .../session/MediaSessionServiceTest.java | 110 ++++++++++++++-- .../src/main/AndroidManifest.xml | 18 ++- .../session/LocalMockMediaLibraryService.java | 19 +++ .../session/MockMediaLibraryService.java | 60 +++++++-- .../session/MockMediaSessionService.java | 7 +- 10 files changed, 325 insertions(+), 92 deletions(-) create mode 100644 libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaLibraryServiceTest.java create mode 100644 libraries/test_session_current/src/main/java/androidx/media3/session/LocalMockMediaLibraryService.java diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java index e85d47adf3..546e8179ef 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java @@ -1030,21 +1030,8 @@ public class MediaSession { /** Handles the controller's connection request from {@link MediaSessionService}. */ /* package */ final void handleControllerConnectionFromService( - IMediaController controller, - int controllerVersion, - int controllerInterfaceVersion, - String packageName, - int pid, - int uid, - Bundle connectionHints) { - impl.connectFromService( - controller, - controllerVersion, - controllerInterfaceVersion, - packageName, - pid, - uid, - connectionHints); + IMediaController controller, ControllerInfo controllerInfo) { + impl.connectFromService(controller, controllerInfo); } /* package */ final IBinder getLegacyBrowserServiceBinder() { diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java index eb6408003d..1016f53980 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java @@ -641,22 +641,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; "Callback.onSetMediaItems must return a non-null future"); } - public void connectFromService( - IMediaController caller, - int controllerVersion, - int controllerInterfaceVersion, - String packageName, - int pid, - int uid, - Bundle connectionHints) { - sessionStub.connect( - caller, - controllerVersion, - controllerInterfaceVersion, - packageName, - pid, - uid, - checkStateNotNull(connectionHints)); + public void connectFromService(IMediaController caller, ControllerInfo controllerInfo) { + sessionStub.connect(caller, controllerInfo); } public MediaSessionCompat getSessionCompat() { diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java index 90fb1b4619..b3963c57b1 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java @@ -676,7 +676,7 @@ public abstract class MediaSessionService extends Service { request.libraryVersion, request.controllerInterfaceVersion, isTrusted, - /* cb= */ null, + new MediaSessionStub.Controller2Cb(caller), request.connectionHints); @Nullable MediaSession session; @@ -689,14 +689,7 @@ public abstract class MediaSessionService extends Service { service.addSession(session); shouldNotifyDisconnected = false; - session.handleControllerConnectionFromService( - caller, - request.libraryVersion, - request.controllerInterfaceVersion, - request.packageName, - pid, - uid, - request.connectionHints); + session.handleControllerConnectionFromService(caller, controllerInfo); } catch (Exception e) { // Don't propagate exception in service to the controller. Log.w(TAG, "Failed to add a session to session service", e); diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java index 8c272ef0f7..6b900a99c6 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java @@ -440,24 +440,8 @@ import java.util.concurrent.ExecutionException; return mediaItemIndex; } - public void connect( - IMediaController caller, - int controllerVersion, - int controllerInterfaceVersion, - String callingPackage, - int pid, - int uid, - Bundle connectionHints) { - MediaSessionManager.RemoteUserInfo remoteUserInfo = - new MediaSessionManager.RemoteUserInfo(callingPackage, pid, uid); - ControllerInfo controllerInfo = - new ControllerInfo( - remoteUserInfo, - controllerVersion, - controllerInterfaceVersion, - sessionManager.isTrustedForMediaControl(remoteUserInfo), - new Controller2Cb(caller), - connectionHints); + @SuppressWarnings("UngroupedOverloads") // Overload belongs to AIDL interface that is separated. + public void connect(IMediaController caller, ControllerInfo controllerInfo) { @Nullable MediaSessionImpl sessionImpl = this.sessionImpl.get(); if (sessionImpl == null || sessionImpl.isReleased()) { try { @@ -612,14 +596,18 @@ import java.util.concurrent.ExecutionException; // If it's the case, use PID from the ConnectionRequest. int pid = (callingPid != 0) ? callingPid : request.pid; try { - connect( - caller, - request.libraryVersion, - request.controllerInterfaceVersion, - request.packageName, - pid, - uid, - request.connectionHints); + + MediaSessionManager.RemoteUserInfo remoteUserInfo = + new MediaSessionManager.RemoteUserInfo(request.packageName, pid, uid); + ControllerInfo controllerInfo = + new ControllerInfo( + remoteUserInfo, + request.libraryVersion, + request.controllerInterfaceVersion, + sessionManager.isTrustedForMediaControl(remoteUserInfo), + new MediaSessionStub.Controller2Cb(caller), + request.connectionHints); + connect(caller, controllerInfo); } finally { Binder.restoreCallingIdentity(token); } diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaLibraryServiceTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaLibraryServiceTest.java new file mode 100644 index 0000000000..48f94c1a61 --- /dev/null +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaLibraryServiceTest.java @@ -0,0 +1,117 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.session; + +import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS; +import static com.google.common.truth.Truth.assertThat; + +import android.content.ComponentName; +import android.content.Context; +import androidx.media3.common.MediaItem; +import androidx.media3.common.MediaMetadata; +import androidx.media3.common.Player; +import androidx.media3.exoplayer.ExoPlayer; +import androidx.media3.session.MediaLibraryService.MediaLibrarySession; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.MediumTest; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for {@link MediaLibraryService}. */ +@RunWith(AndroidJUnit4.class) +@MediumTest +public class MediaLibraryServiceTest { + + @Rule public final RemoteControllerTestRule controllerTestRule = new RemoteControllerTestRule(); + + private Context context; + private SessionToken token; + + @Before + public void setUp() { + TestServiceRegistry.getInstance().cleanUp(); + context = ApplicationProvider.getApplicationContext(); + token = + new SessionToken(context, new ComponentName(context, LocalMockMediaLibraryService.class)); + } + + @After + public void cleanUp() { + TestServiceRegistry.getInstance().cleanUp(); + } + + @Test + public void onConnect_controllerInfo_sameInstanceInOnGetSessionAndCallback() throws Exception { + TestServiceRegistry testServiceRegistry = TestServiceRegistry.getInstance(); + List onGetSessionControllerInfos = new ArrayList<>(); + List browserCommandControllerInfos = new ArrayList<>(); + AtomicReference session = new AtomicReference<>(); + testServiceRegistry.setOnGetSessionHandler( + controllerInfo -> { + MockMediaLibraryService service = + (MockMediaLibraryService) testServiceRegistry.getServiceInstance(); + // The controllerInfo passed to the onGetSession of the service. + onGetSessionControllerInfos.add(controllerInfo); + Player player = new ExoPlayer.Builder(context).build(); + MediaLibrarySession.Callback callback = + new MediaLibrarySession.Callback() { + @Override + public ListenableFuture> onGetItem( + MediaLibrarySession session, + MediaSession.ControllerInfo browser, + String mediaId) { + browserCommandControllerInfos.add(browser); + return Futures.immediateFuture( + LibraryResult.ofItem( + new MediaItem.Builder() + .setMediaId("media-id-321") + .setMediaMetadata( + new MediaMetadata.Builder() + .setIsPlayable(false) + .setIsBrowsable(true) + .build()) + .build(), + /* params= */ null)); + } + }; + session.set(new MediaLibrarySession.Builder(service, player, callback).build()); + return session.get(); + }); + // Create the remote browser to start the service. + RemoteMediaBrowser browser = controllerTestRule.createRemoteBrowser(token); + // Get the started service instance after creation. + MockMediaLibraryService service = + (MockMediaLibraryService) testServiceRegistry.getServiceInstance(); + + assertThat(browser.getItem("mediaId").resultCode).isEqualTo(LibraryResult.RESULT_SUCCESS); + browser.release(); + + service.blockUntilAllControllersUnbind(TIMEOUT_MS); + assertThat(onGetSessionControllerInfos).hasSize(1); + assertThat(browserCommandControllerInfos).hasSize(1); + assertThat(onGetSessionControllerInfos.get(0)) + .isSameInstanceAs(browserCommandControllerInfos.get(0)); + } +} diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionServiceTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionServiceTest.java index cea6c53872..b328c60f16 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionServiceTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionServiceTest.java @@ -29,6 +29,7 @@ import android.os.Handler; import android.os.Looper; import android.support.v4.media.session.MediaControllerCompat; import android.support.v4.media.session.PlaybackStateCompat; +import androidx.media3.common.ForwardingPlayer; import androidx.media3.common.MediaItem; import androidx.media3.common.Player; import androidx.media3.common.util.ConditionVariable; @@ -52,6 +53,7 @@ import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import org.junit.After; import org.junit.Before; import org.junit.ClassRule; @@ -66,23 +68,20 @@ public class MediaSessionServiceTest { @ClassRule public static MainLooperTestRule mainLooperTestRule = new MainLooperTestRule(); + @Rule public final RemoteControllerTestRule controllerTestRule = new RemoteControllerTestRule(); + @Rule public final MediaSessionTestRule sessionTestRule = new MediaSessionTestRule(); + @Rule public final HandlerThreadTestRule threadTestRule = new HandlerThreadTestRule("MediaSessionServiceTest"); - @Rule public final RemoteControllerTestRule controllerTestRule = new RemoteControllerTestRule(); - - @Rule public final MediaSessionTestRule sessionTestRule = new MediaSessionTestRule(); - private Context context; - private Looper looper; private SessionToken token; @Before public void setUp() { TestServiceRegistry.getInstance().cleanUp(); context = ApplicationProvider.getApplicationContext(); - looper = threadTestRule.getHandler().getLooper(); token = new SessionToken(context, new ComponentName(context, LocalMockMediaSessionService.class)); } @@ -92,6 +91,91 @@ public class MediaSessionServiceTest { TestServiceRegistry.getInstance().cleanUp(); } + @Test + public void onConnect_controllerInfo_sameInstanceFromServiceToConnectedControllerManager() + throws Exception { + TestServiceRegistry testServiceRegistry = TestServiceRegistry.getInstance(); + List onGetSessionControllerInfos = new ArrayList<>(); + List onConnectControllerInfos = new ArrayList<>(); + List playbackCommandControllerInfos = new ArrayList<>(); + List onDisconnectedCommandControllerInfos = new ArrayList<>(); + AtomicReference session = new AtomicReference<>(); + testServiceRegistry.setOnGetSessionHandler( + controllerInfo -> { + // The controllerInfo passed to the onGetSession of the service. + onGetSessionControllerInfos.add(controllerInfo); + Player player = new ExoPlayer.Builder(context).build(); + ForwardingPlayer forwardingPlayer = + new ForwardingPlayer(player) { + @Override + public void setRepeatMode(@Player.RepeatMode int repeatMode) { + // The controllerInfo assigned for the current player command request. + playbackCommandControllerInfos.add( + session.get().getControllerForCurrentRequest()); + super.setRepeatMode(repeatMode); + } + }; + session.set( + new MediaSession.Builder(context, forwardingPlayer) + .setCallback( + new MediaSession.Callback() { + @Override + public MediaSession.ConnectionResult onConnect( + MediaSession session, ControllerInfo controller) { + if (!session.isMediaNotificationController(controller)) { + // The controllerInfo passed to MediaSession.Callback.onConnect() + onConnectControllerInfos.add(controllerInfo); + } + return MediaSession.Callback.super.onConnect(session, controller); + } + + @Override + public void onDisconnected( + MediaSession session, ControllerInfo controller) { + if (!session.isMediaNotificationController(controller)) { + // The controllerInfo when disconnecting. + onDisconnectedCommandControllerInfos.add(controller); + } + MediaSession.Callback.super.onDisconnected(session, controller); + } + }) + .build()); + return session.get(); + }); + // Create the remote controller to start the service. + RemoteMediaController controller = + controllerTestRule.createRemoteController( + token, /* waitForConnection= */ true, /* connectionHints= */ null); + // Get the started service instance after creation. + MockMediaSessionService service = + (MockMediaSessionService) testServiceRegistry.getServiceInstance(); + controller.setRepeatMode(Player.REPEAT_MODE_ONE); + List connectedControllerManagerControllerInfos = new ArrayList<>(); + for (ControllerInfo controllerInfo : session.get().getConnectedControllers()) { + if (!session.get().isMediaNotificationController(controllerInfo)) { + // The controllerInfo in the connected controller manager. + connectedControllerManagerControllerInfos.add(controllerInfo); + } + } + + controller.release(); + + service.blockUntilAllControllersUnbind(TIMEOUT_MS); + assertThat(onGetSessionControllerInfos).hasSize(1); + assertThat(onGetSessionControllerInfos).isEqualTo(onConnectControllerInfos); + assertThat(onGetSessionControllerInfos).isEqualTo(playbackCommandControllerInfos); + assertThat(onGetSessionControllerInfos).isEqualTo(onDisconnectedCommandControllerInfos); + assertThat(onGetSessionControllerInfos).isEqualTo(connectedControllerManagerControllerInfos); + assertThat(onGetSessionControllerInfos.get(0)) + .isSameInstanceAs(onConnectControllerInfos.get(0)); + assertThat(onGetSessionControllerInfos.get(0)) + .isSameInstanceAs(playbackCommandControllerInfos.get(0)); + assertThat(onGetSessionControllerInfos.get(0)) + .isSameInstanceAs(onDisconnectedCommandControllerInfos.get(0)); + assertThat(onGetSessionControllerInfos.get(0)) + .isSameInstanceAs(connectedControllerManagerControllerInfos.get(0)); + } + @Test public void controllerRelease_keepsControllerBoundUntilCommandsHandled() throws Exception { TestServiceRegistry testServiceRegistry = TestServiceRegistry.getInstance(); @@ -291,7 +375,10 @@ public class MediaSessionServiceTest { MediaSession testSession = sessionTestRule.ensureReleaseAfterTest( new MediaSession.Builder( - context, new MockPlayer.Builder().setApplicationLooper(looper).build()) + context, + new MockPlayer.Builder() + .setApplicationLooper(threadTestRule.getHandler().getLooper()) + .build()) .setId("testOnGetSession_returnsSession") .setCallback( new MediaSession.Callback() { @@ -370,7 +457,9 @@ public class MediaSessionServiceTest { public void onGetSession_rejectsConnection() throws Exception { TestServiceRegistry.getInstance().setOnGetSessionHandler(controllerInfo -> null); ListenableFuture future = - new MediaController.Builder(context, token).setApplicationLooper(looper).buildAsync(); + new MediaController.Builder(context, token) + .setApplicationLooper(threadTestRule.getHandler().getLooper()) + .buildAsync(); ExecutionException thrown = assertThrows(ExecutionException.class, () -> future.get(TIMEOUT_MS, MILLISECONDS)); @@ -481,7 +570,10 @@ public class MediaSessionServiceTest { private MediaSession createMediaSession(String id) { return sessionTestRule.ensureReleaseAfterTest( new MediaSession.Builder( - context, new MockPlayer.Builder().setApplicationLooper(looper).build()) + context, + new MockPlayer.Builder() + .setApplicationLooper(threadTestRule.getHandler().getLooper()) + .build()) .setId(id) .build()); } diff --git a/libraries/test_session_current/src/main/AndroidManifest.xml b/libraries/test_session_current/src/main/AndroidManifest.xml index b873a58120..df611c5054 100644 --- a/libraries/test_session_current/src/main/AndroidManifest.xml +++ b/libraries/test_session_current/src/main/AndroidManifest.xml @@ -90,17 +90,25 @@ + android:foregroundServiceType="mediaPlayback" + android:exported="true"> + android:foregroundServiceType="mediaPlayback" + android:exported="true" + android:process=":remote"> + + + + + + diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/LocalMockMediaLibraryService.java b/libraries/test_session_current/src/main/java/androidx/media3/session/LocalMockMediaLibraryService.java new file mode 100644 index 0000000000..5f1f85db9b --- /dev/null +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/LocalMockMediaLibraryService.java @@ -0,0 +1,19 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.session; + +/** {@link MockMediaLibraryService} running on a local process. */ +public class LocalMockMediaLibraryService extends MockMediaLibraryService {} 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 4407a6b875..5192822e71 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 @@ -60,15 +60,16 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.HandlerThread; +import android.os.IBinder; import androidx.annotation.GuardedBy; import androidx.annotation.Nullable; import androidx.media3.common.MediaItem; import androidx.media3.common.MediaMetadata; +import androidx.media3.common.util.ConditionVariable; import androidx.media3.common.util.Log; import androidx.media3.common.util.Util; import androidx.media3.session.MediaSession.ControllerInfo; import androidx.media3.test.session.common.CommonConstants; -import androidx.media3.test.session.common.TestHandler; import androidx.media3.test.session.common.TestUtils; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.Futures; @@ -77,6 +78,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; /** A mock MediaLibraryService */ public class MockMediaLibraryService extends MediaLibraryService { @@ -119,9 +122,36 @@ public class MockMediaLibraryService extends MediaLibraryService { @Nullable private static byte[] testArtworkData; - MediaLibrarySession session; - TestHandler handler; - HandlerThread handlerThread; + private final AtomicInteger boundControllerCount; + private final ConditionVariable allControllersUnbound; + + @Nullable MediaLibrarySession session; + @Nullable HandlerThread handlerThread; + + public MockMediaLibraryService() { + boundControllerCount = new AtomicInteger(/* initialValue= */ 0); + allControllersUnbound = new ConditionVariable(); + allControllersUnbound.open(); + } + + /** Returns whether at least one controller is bound to this service. */ + public boolean hasBoundController() { + return !allControllersUnbound.isOpen(); + } + + /** + * Blocks until all bound controllers unbind. + * + * @param timeoutMs The block timeout in milliseconds. + * @throws TimeoutException If the block timed out. + * @throws InterruptedException If the block was interrupted. + */ + public void blockUntilAllControllersUnbind(long timeoutMs) + throws TimeoutException, InterruptedException { + if (!allControllersUnbound.block(timeoutMs)) { + throw new TimeoutException(); + } + } @Override public void onCreate() { @@ -129,7 +159,21 @@ public class MockMediaLibraryService extends MediaLibraryService { super.onCreate(); handlerThread = new HandlerThread(TAG); handlerThread.start(); - handler = new TestHandler(handlerThread.getLooper()); + } + + @Override + public IBinder onBind(@Nullable Intent intent) { + boundControllerCount.incrementAndGet(); + allControllersUnbound.close(); + return super.onBind(intent); + } + + @Override + public boolean onUnbind(Intent intent) { + if (boundControllerCount.decrementAndGet() == 0) { + allControllersUnbound.open(); + } + return super.onUnbind(intent); } @Override @@ -141,9 +185,9 @@ public class MockMediaLibraryService extends MediaLibraryService { } TestServiceRegistry.getInstance().cleanUp(); if (Util.SDK_INT >= 18) { - handler.getLooper().quitSafely(); + handlerThread.quitSafely(); } else { - handler.getLooper().quit(); + handlerThread.quit(); } } @@ -157,7 +201,7 @@ public class MockMediaLibraryService extends MediaLibraryService { if (session == null) { MockPlayer player = - new MockPlayer.Builder().setApplicationLooper(handler.getLooper()).build(); + new MockPlayer.Builder().setApplicationLooper(handlerThread.getLooper()).build(); MediaLibrarySession.Callback callback = registry.getSessionCallback(); session = diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MockMediaSessionService.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MockMediaSessionService.java index 57e0bc6f65..12d271716a 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/MockMediaSessionService.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MockMediaSessionService.java @@ -34,11 +34,10 @@ public class MockMediaSessionService extends MediaSessionService { public static final String ID = "TestSession"; private final AtomicInteger boundControllerCount; + private final ConditionVariable allControllersUnbound; - public MediaSession session; - - private HandlerThread handlerThread; - private ConditionVariable allControllersUnbound; + @Nullable public MediaSession session; + @Nullable private HandlerThread handlerThread; public MockMediaSessionService() { boundControllerCount = new AtomicInteger(/* initialValue= */ 0);