blob: e0b454559456df4a1775fcbefc2edda3217d02c3 [file] [log] [blame]
/*
* Copyright 2019 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 com.android.mediaroutertest;
import static android.media.MediaRoute2Info.CONNECTION_STATE_CONNECTING;
import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER;
import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
import static com.android.mediaroutertest.MediaRouterManagerTest.FEATURES_ALL;
import static com.android.mediaroutertest.MediaRouterManagerTest.FEATURES_SPECIAL;
import static com.android.mediaroutertest.MediaRouterManagerTest.FEATURE_SAMPLE;
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID1;
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID2;
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID3_SESSION_CREATION_FAILED;
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID4_TO_SELECT_AND_DESELECT;
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID5_TO_TRANSFER_TO;
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_SPECIAL_FEATURE;
import static com.android.mediaroutertest.MediaRouterManagerTest.ROUTE_ID_VARIABLE_VOLUME;
import static com.android.mediaroutertest.MediaRouterManagerTest.SYSTEM_PROVIDER_ID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.testng.Assert.assertThrows;
import android.annotation.NonNull;
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2;
import android.media.MediaRouter2.OnGetControllerHintsListener;
import android.media.MediaRouter2.RouteCallback;
import android.media.MediaRouter2.RoutingController;
import android.media.MediaRouter2.RoutingControllerCallback;
import android.media.RouteDiscoveryPreference;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.TextUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MediaRouter2Test {
private static final String TAG = "MediaRouter2Test";
Context mContext;
private MediaRouter2 mRouter2;
private Executor mExecutor;
private static final int TIMEOUT_MS = 5000;
private static final String TEST_KEY = "test_key";
private static final String TEST_VALUE = "test_value";
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
mRouter2 = MediaRouter2.getInstance(mContext);
mExecutor = Executors.newSingleThreadExecutor();
}
@After
public void tearDown() throws Exception {
}
/**
* Tests if we get proper routes for application that has special route type.
*/
@Test
public void testGetRoutes() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(FEATURES_SPECIAL);
assertEquals(1, routes.size());
assertNotNull(routes.get(ROUTE_ID_SPECIAL_FEATURE));
}
@Test
public void testRouteInfoEquality() {
MediaRoute2Info routeInfo = new MediaRoute2Info.Builder("id", "name")
.setDescription("description")
.setClientPackageName("com.android.mediaroutertest")
.setConnectionState(CONNECTION_STATE_CONNECTING)
.setIconUri(new Uri.Builder().path("icon").build())
.setVolume(5)
.setVolumeMax(20)
.addFeature(FEATURE_SAMPLE)
.setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
.setDeviceType(DEVICE_TYPE_REMOTE_SPEAKER)
.build();
MediaRoute2Info routeInfoRebuilt = new MediaRoute2Info.Builder(routeInfo).build();
assertEquals(routeInfo, routeInfoRebuilt);
Parcel parcel = Parcel.obtain();
parcel.writeParcelable(routeInfo, 0);
parcel.setDataPosition(0);
MediaRoute2Info routeInfoFromParcel = parcel.readParcelable(null);
assertEquals(routeInfo, routeInfoFromParcel);
}
@Test
public void testControlVolumeWithRouter() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(FEATURES_ALL);
MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
assertNotNull(volRoute);
int originalVolume = volRoute.getVolume();
int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1);
awaitOnRouteChanged(
() -> mRouter2.requestUpdateVolume(volRoute, deltaVolume),
ROUTE_ID_VARIABLE_VOLUME,
(route -> route.getVolume() == originalVolume + deltaVolume),
FEATURES_ALL);
awaitOnRouteChanged(
() -> mRouter2.requestSetVolume(volRoute, originalVolume),
ROUTE_ID_VARIABLE_VOLUME,
(route -> route.getVolume() == originalVolume),
FEATURES_ALL);
}
@Test
public void testRegisterControllerCallbackWithInvalidArguments() {
Executor executor = mExecutor;
RoutingControllerCallback callback = new RoutingControllerCallback();
// Tests null executor
assertThrows(NullPointerException.class,
() -> mRouter2.registerControllerCallback(null, callback));
// Tests null callback
assertThrows(NullPointerException.class,
() -> mRouter2.registerControllerCallback(executor, null));
}
@Test
public void testUnregisterControllerCallbackWithNullCallback() {
// Tests null callback
assertThrows(NullPointerException.class,
() -> mRouter2.unregisterControllerCallback(null));
}
@Test
public void testRequestCreateControllerWithNullRoute() {
assertThrows(NullPointerException.class,
() -> mRouter2.requestCreateController(null));
}
@Test
public void testRequestCreateControllerSuccess() throws Exception {
final List<String> sampleRouteFeature = new ArrayList<>();
sampleRouteFeature.add(FEATURE_SAMPLE);
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteFeature);
MediaRoute2Info route = routes.get(ROUTE_ID1);
assertNotNull(route);
final CountDownLatch successLatch = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(1);
final List<RoutingController> controllers = new ArrayList<>();
// Create session with this route
RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
@Override
public void onControllerCreated(RoutingController controller) {
assertNotNull(controller);
assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1));
controllers.add(controller);
successLatch.countDown();
}
@Override
public void onControllerCreationFailed(MediaRoute2Info requestedRoute) {
failureLatch.countDown();
}
};
// TODO: Remove this once the MediaRouter2 becomes always connected to the service.
RouteCallback routeCallback = new RouteCallback();
mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
try {
mRouter2.registerControllerCallback(mExecutor, controllerCallback);
mRouter2.requestCreateController(route);
assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// onSessionCreationFailed should not be called.
assertFalse(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterControllerCallback(controllerCallback);
}
}
@Test
public void testRequestCreateControllerFailure() throws Exception {
final List<String> sampleRouteType = new ArrayList<>();
sampleRouteType.add(FEATURE_SAMPLE);
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
MediaRoute2Info route = routes.get(ROUTE_ID3_SESSION_CREATION_FAILED);
assertNotNull(route);
final CountDownLatch successLatch = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(1);
final List<RoutingController> controllers = new ArrayList<>();
// Create session with this route
RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
@Override
public void onControllerCreated(RoutingController controller) {
controllers.add(controller);
successLatch.countDown();
}
@Override
public void onControllerCreationFailed(MediaRoute2Info requestedRoute) {
assertEquals(route, requestedRoute);
failureLatch.countDown();
}
};
// TODO: Remove this once the MediaRouter2 becomes always connected to the service.
RouteCallback routeCallback = new RouteCallback();
mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
try {
mRouter2.registerControllerCallback(mExecutor, controllerCallback);
mRouter2.requestCreateController(route);
assertTrue(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// onSessionCreated should not be called.
assertFalse(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterControllerCallback(controllerCallback);
}
}
@Test
public void testRequestCreateControllerMultipleSessions() throws Exception {
final List<String> sampleRouteType = new ArrayList<>();
sampleRouteType.add(FEATURE_SAMPLE);
final CountDownLatch successLatch = new CountDownLatch(2);
final CountDownLatch failureLatch = new CountDownLatch(1);
final List<RoutingController> createdControllers = new ArrayList<>();
// Create session with this route
RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
@Override
public void onControllerCreated(RoutingController controller) {
createdControllers.add(controller);
successLatch.countDown();
}
@Override
public void onControllerCreationFailed(MediaRoute2Info requestedRoute) {
failureLatch.countDown();
}
};
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
MediaRoute2Info route1 = routes.get(ROUTE_ID1);
MediaRoute2Info route2 = routes.get(ROUTE_ID2);
assertNotNull(route1);
assertNotNull(route2);
// TODO: Remove this once the MediaRouter2 becomes always connected to the service.
RouteCallback routeCallback = new RouteCallback();
mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
try {
mRouter2.registerControllerCallback(mExecutor, controllerCallback);
mRouter2.requestCreateController(route1);
mRouter2.requestCreateController(route2);
assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// onSessionCreationFailed should not be called.
assertFalse(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// Created controllers should have proper info
assertEquals(2, createdControllers.size());
RoutingController controller1 = createdControllers.get(0);
RoutingController controller2 = createdControllers.get(1);
assertNotEquals(controller1.getId(), controller2.getId());
assertTrue(createRouteMap(controller1.getSelectedRoutes()).containsKey(ROUTE_ID1));
assertTrue(createRouteMap(controller2.getSelectedRoutes()).containsKey(ROUTE_ID2));
} finally {
releaseControllers(createdControllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterControllerCallback(controllerCallback);
}
}
@Test
public void testSetOnGetControllerHintsListener() throws Exception {
final List<String> sampleRouteFeature = new ArrayList<>();
sampleRouteFeature.add(FEATURE_SAMPLE);
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteFeature);
MediaRoute2Info route = routes.get(ROUTE_ID1);
assertNotNull(route);
final Bundle createSessionHints = new Bundle();
createSessionHints.putString(TEST_KEY, TEST_VALUE);
final OnGetControllerHintsListener listener = new OnGetControllerHintsListener() {
@Override
public Bundle onGetControllerHints(MediaRoute2Info route) {
return createSessionHints;
}
};
final CountDownLatch successLatch = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(1);
final List<RoutingController> controllers = new ArrayList<>();
// Create session with this route
RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
@Override
public void onControllerCreated(RoutingController controller) {
assertNotNull(controller);
assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1));
// The SampleMediaRoute2ProviderService supposed to set control hints
// with the given creationSessionHints.
Bundle controlHints = controller.getControlHints();
assertNotNull(controlHints);
assertTrue(controlHints.containsKey(TEST_KEY));
assertEquals(TEST_VALUE, controlHints.getString(TEST_KEY));
controllers.add(controller);
successLatch.countDown();
}
@Override
public void onControllerCreationFailed(MediaRoute2Info requestedRoute) {
failureLatch.countDown();
}
};
// TODO: Remove this once the MediaRouter2 becomes always connected to the service.
RouteCallback routeCallback = new RouteCallback();
mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
try {
mRouter2.registerControllerCallback(mExecutor, controllerCallback);
// The SampleMediaRoute2ProviderService supposed to set control hints
// with the given creationSessionHints.
mRouter2.setOnGetControllerHintsListener(listener);
mRouter2.requestCreateController(route);
assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// onSessionCreationFailed should not be called.
assertFalse(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterControllerCallback(controllerCallback);
}
}
@Test
public void testRoutingControllerCallbackIsNotCalledAfterUnregistered() throws Exception {
final List<String> sampleRouteType = new ArrayList<>();
sampleRouteType.add(FEATURE_SAMPLE);
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
MediaRoute2Info route = routes.get(ROUTE_ID1);
assertNotNull(route);
final CountDownLatch successLatch = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(1);
final List<RoutingController> controllers = new ArrayList<>();
// Create session with this route
RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
@Override
public void onControllerCreated(RoutingController controller) {
controllers.add(controller);
successLatch.countDown();
}
@Override
public void onControllerCreationFailed(MediaRoute2Info requestedRoute) {
failureLatch.countDown();
}
};
// TODO: Remove this once the MediaRouter2 becomes always connected to the service.
RouteCallback routeCallback = new RouteCallback();
mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
try {
mRouter2.registerControllerCallback(mExecutor, controllerCallback);
mRouter2.requestCreateController(route);
// Unregisters session callback
mRouter2.unregisterControllerCallback(controllerCallback);
// No session callback methods should be called.
assertFalse(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertFalse(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterControllerCallback(controllerCallback);
}
}
// TODO: Add tests for illegal inputs if needed (e.g. selecting already selected route)
@Test
public void testRoutingControllerSelectAndDeselectRoute() throws Exception {
final List<String> sampleRouteType = new ArrayList<>();
sampleRouteType.add(FEATURE_SAMPLE);
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
assertNotNull(routeToCreateSessionWith);
final CountDownLatch onControllerCreatedLatch = new CountDownLatch(1);
final CountDownLatch onControllerUpdatedLatchForSelect = new CountDownLatch(1);
final CountDownLatch onControllerUpdatedLatchForDeselect = new CountDownLatch(1);
final List<RoutingController> controllers = new ArrayList<>();
// Create session with ROUTE_ID1
RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
@Override
public void onControllerCreated(RoutingController controller) {
assertNotNull(controller);
assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
controllers.add(controller);
onControllerCreatedLatch.countDown();
}
@Override
public void onControllerUpdated(RoutingController controller) {
if (onControllerCreatedLatch.getCount() != 0
|| !TextUtils.equals(
controllers.get(0).getId(), controller.getId())) {
return;
}
if (onControllerUpdatedLatchForSelect.getCount() != 0) {
assertEquals(2, controller.getSelectedRoutes().size());
assertTrue(getRouteIds(controller.getSelectedRoutes())
.contains(ROUTE_ID1));
assertTrue(getRouteIds(controller.getSelectedRoutes())
.contains(ROUTE_ID4_TO_SELECT_AND_DESELECT));
assertFalse(getRouteIds(controller.getSelectableRoutes())
.contains(ROUTE_ID4_TO_SELECT_AND_DESELECT));
onControllerUpdatedLatchForSelect.countDown();
} else {
assertEquals(1, controller.getSelectedRoutes().size());
assertTrue(getRouteIds(controller.getSelectedRoutes())
.contains(ROUTE_ID1));
assertFalse(getRouteIds(controller.getSelectedRoutes())
.contains(ROUTE_ID4_TO_SELECT_AND_DESELECT));
assertTrue(getRouteIds(controller.getSelectableRoutes())
.contains(ROUTE_ID4_TO_SELECT_AND_DESELECT));
onControllerUpdatedLatchForDeselect.countDown();
}
}
};
// TODO: Remove this once the MediaRouter2 becomes always connected to the service.
RouteCallback routeCallback = new RouteCallback();
mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
try {
mRouter2.registerControllerCallback(mExecutor, controllerCallback);
mRouter2.requestCreateController(routeToCreateSessionWith);
assertTrue(onControllerCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(1, controllers.size());
RoutingController controller = controllers.get(0);
assertTrue(getRouteIds(controller.getSelectableRoutes())
.contains(ROUTE_ID4_TO_SELECT_AND_DESELECT));
// Select ROUTE_ID4_TO_SELECT_AND_DESELECT
MediaRoute2Info routeToSelectAndDeselect = routes.get(
ROUTE_ID4_TO_SELECT_AND_DESELECT);
assertNotNull(routeToSelectAndDeselect);
controller.selectRoute(routeToSelectAndDeselect);
assertTrue(onControllerUpdatedLatchForSelect.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
controller.deselectRoute(routeToSelectAndDeselect);
assertTrue(onControllerUpdatedLatchForDeselect.await(
TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterControllerCallback(controllerCallback);
}
}
@Test
public void testRoutingControllerTransferToRoute() throws Exception {
final List<String> sampleRouteType = new ArrayList<>();
sampleRouteType.add(FEATURE_SAMPLE);
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
assertNotNull(routeToCreateSessionWith);
final CountDownLatch onControllerCreatedLatch = new CountDownLatch(1);
final CountDownLatch onControllerUpdatedLatch = new CountDownLatch(1);
final List<RoutingController> controllers = new ArrayList<>();
// Create session with ROUTE_ID1
RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
@Override
public void onControllerCreated(RoutingController controller) {
assertNotNull(controller);
assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
controllers.add(controller);
onControllerCreatedLatch.countDown();
}
@Override
public void onControllerUpdated(RoutingController controller) {
if (onControllerCreatedLatch.getCount() != 0
|| !TextUtils.equals(
controllers.get(0).getId(), controller.getId())) {
return;
}
assertEquals(1, controller.getSelectedRoutes().size());
assertFalse(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
assertTrue(getRouteIds(controller.getSelectedRoutes())
.contains(ROUTE_ID5_TO_TRANSFER_TO));
assertFalse(getRouteIds(controller.getTransferrableRoutes())
.contains(ROUTE_ID5_TO_TRANSFER_TO));
onControllerUpdatedLatch.countDown();
}
};
// TODO: Remove this once the MediaRouter2 becomes always connected to the service.
RouteCallback routeCallback = new RouteCallback();
mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
try {
mRouter2.registerControllerCallback(mExecutor, controllerCallback);
mRouter2.requestCreateController(routeToCreateSessionWith);
assertTrue(onControllerCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(1, controllers.size());
RoutingController controller = controllers.get(0);
assertTrue(getRouteIds(controller.getTransferrableRoutes())
.contains(ROUTE_ID5_TO_TRANSFER_TO));
// Transfer to ROUTE_ID5_TO_TRANSFER_TO
MediaRoute2Info routeToTransferTo = routes.get(ROUTE_ID5_TO_TRANSFER_TO);
assertNotNull(routeToTransferTo);
controller.transferToRoute(routeToTransferTo);
assertTrue(onControllerUpdatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterControllerCallback(controllerCallback);
}
}
// TODO: Add tests for onSessionReleased() when provider releases the session.
@Test
public void testRoutingControllerRelease() throws Exception {
final List<String> sampleRouteType = new ArrayList<>();
sampleRouteType.add(FEATURE_SAMPLE);
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
assertNotNull(routeToCreateSessionWith);
final CountDownLatch onControllerCreatedLatch = new CountDownLatch(1);
final CountDownLatch onControllerUpdatedLatch = new CountDownLatch(1);
final CountDownLatch onControllerReleasedLatch = new CountDownLatch(1);
final List<RoutingController> controllers = new ArrayList<>();
// Create session with ROUTE_ID1
RoutingControllerCallback controllerCallback = new RoutingControllerCallback() {
@Override
public void onControllerCreated(RoutingController controller) {
assertNotNull(controller);
assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
controllers.add(controller);
onControllerCreatedLatch.countDown();
}
@Override
public void onControllerUpdated(RoutingController controller) {
if (onControllerCreatedLatch.getCount() != 0
|| !TextUtils.equals(controllers.get(0).getId(), controller.getId())) {
return;
}
onControllerUpdatedLatch.countDown();
}
@Override
public void onControllerReleased(RoutingController controller) {
if (onControllerCreatedLatch.getCount() != 0
|| !TextUtils.equals(controllers.get(0).getId(), controller.getId())) {
return;
}
onControllerReleasedLatch.countDown();
}
};
// TODO: Remove this once the MediaRouter2 becomes always connected to the service.
RouteCallback routeCallback = new RouteCallback();
mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
try {
mRouter2.registerControllerCallback(mExecutor, controllerCallback);
mRouter2.requestCreateController(routeToCreateSessionWith);
assertTrue(onControllerCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
assertEquals(1, controllers.size());
RoutingController controller = controllers.get(0);
assertTrue(getRouteIds(controller.getTransferrableRoutes())
.contains(ROUTE_ID5_TO_TRANSFER_TO));
// Release controller. Future calls should be ignored.
controller.release();
// Transfer to ROUTE_ID5_TO_TRANSFER_TO
MediaRoute2Info routeToTransferTo = routes.get(ROUTE_ID5_TO_TRANSFER_TO);
assertNotNull(routeToTransferTo);
// This call should be ignored.
// The onSessionInfoChanged() shouldn't be called.
controller.transferToRoute(routeToTransferTo);
assertFalse(onControllerUpdatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
// onControllerReleased should be called.
assertTrue(onControllerReleasedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
releaseControllers(controllers);
mRouter2.unregisterRouteCallback(routeCallback);
mRouter2.unregisterControllerCallback(controllerCallback);
}
}
// TODO: Consider adding tests with bluetooth connection/disconnection.
@Test
public void testGetSystemController() {
final RoutingController systemController = mRouter2.getSystemController();
assertNotNull(systemController);
assertFalse(systemController.isReleased());
}
@Test
public void testControllers() {
List<RoutingController> controllers = mRouter2.getControllers();
assertNotNull(controllers);
assertFalse(controllers.isEmpty());
assertSame(mRouter2.getSystemController(), controllers.get(0));
}
// Helper for getting routes easily
static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
Map<String, MediaRoute2Info> routeMap = new HashMap<>();
for (MediaRoute2Info route : routes) {
routeMap.put(route.getId(), route);
}
return routeMap;
}
Map<String, MediaRoute2Info> waitAndGetRoutes(List<String> routeTypes)
throws Exception {
CountDownLatch latch = new CountDownLatch(1);
// A dummy callback is required to send route type info.
RouteCallback routeCallback = new RouteCallback() {
@Override
public void onRoutesAdded(List<MediaRoute2Info> routes) {
for (int i = 0; i < routes.size(); i++) {
//TODO: use isSystem() or similar method when it's ready
if (!TextUtils.equals(routes.get(i).getProviderId(), SYSTEM_PROVIDER_ID)) {
latch.countDown();
}
}
}
};
mRouter2.registerRouteCallback(mExecutor, routeCallback,
new RouteDiscoveryPreference.Builder(routeTypes, true).build());
try {
latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
return createRouteMap(mRouter2.getRoutes());
} finally {
mRouter2.unregisterRouteCallback(routeCallback);
}
}
static void releaseControllers(@NonNull List<RoutingController> controllers) {
for (RoutingController controller : controllers) {
controller.release();
}
}
/**
* Returns a list of IDs of the given route list.
*/
List<String> getRouteIds(@NonNull List<MediaRoute2Info> routes) {
List<String> result = new ArrayList<>();
for (MediaRoute2Info route : routes) {
result.add(route.getId());
}
return result;
}
void awaitOnRouteChanged(Runnable task, String routeId,
Predicate<MediaRoute2Info> predicate,
List<String> routeTypes) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
RouteCallback routeCallback = new RouteCallback() {
@Override
public void onRoutesChanged(List<MediaRoute2Info> changed) {
MediaRoute2Info route = createRouteMap(changed).get(routeId);
if (route != null && predicate.test(route)) {
latch.countDown();
}
}
};
mRouter2.registerRouteCallback(mExecutor, routeCallback,
new RouteDiscoveryPreference.Builder(routeTypes, true).build());
try {
task.run();
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
mRouter2.unregisterRouteCallback(routeCallback);
}
}
}