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
This commit is contained in:
parent
d965516dc9
commit
5e05e2ec22
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
||||
MediaSessionManager.RemoteUserInfo remoteUserInfo =
|
||||
new MediaSessionManager.RemoteUserInfo(request.packageName, pid, uid);
|
||||
ControllerInfo controllerInfo =
|
||||
new ControllerInfo(
|
||||
remoteUserInfo,
|
||||
request.libraryVersion,
|
||||
request.controllerInterfaceVersion,
|
||||
request.packageName,
|
||||
pid,
|
||||
uid,
|
||||
sessionManager.isTrustedForMediaControl(remoteUserInfo),
|
||||
new MediaSessionStub.Controller2Cb(caller),
|
||||
request.connectionHints);
|
||||
connect(caller, controllerInfo);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
|
@ -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<MediaSession.ControllerInfo> onGetSessionControllerInfos = new ArrayList<>();
|
||||
List<MediaSession.ControllerInfo> browserCommandControllerInfos = new ArrayList<>();
|
||||
AtomicReference<MediaSession> 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<LibraryResult<MediaItem>> 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));
|
||||
}
|
||||
}
|
@ -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<ControllerInfo> onGetSessionControllerInfos = new ArrayList<>();
|
||||
List<ControllerInfo> onConnectControllerInfos = new ArrayList<>();
|
||||
List<ControllerInfo> playbackCommandControllerInfos = new ArrayList<>();
|
||||
List<ControllerInfo> onDisconnectedCommandControllerInfos = new ArrayList<>();
|
||||
AtomicReference<MediaSession> 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<ControllerInfo> 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<MediaController> 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());
|
||||
}
|
||||
|
@ -106,6 +106,14 @@
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service android:name="androidx.media3.session.LocalMockMediaLibraryService"
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="androidx.media3.session.MediaLibraryService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service android:name="androidx.media3.session.MockMediaBrowserServiceCompat"
|
||||
android:exported="true"
|
||||
android:process=":remote">
|
||||
|
@ -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 {}
|
@ -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 =
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user