Merge "MediaRouter: send control hints from provider to client"
This commit is contained in:
committed by
Android (Google) Code Review
commit
add2daa0ab
@@ -17,10 +17,13 @@
|
||||
package android.media;
|
||||
|
||||
import android.media.MediaRoute2ProviderInfo;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
oneway interface IMediaRoute2ProviderClient {
|
||||
void updateProviderInfo(in MediaRoute2ProviderInfo info);
|
||||
void notifyRouteSelected(String packageName, String routeId, in Bundle controlHints, int seq);
|
||||
}
|
||||
|
||||
@@ -18,14 +18,19 @@ package android.media;
|
||||
|
||||
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -44,7 +49,7 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
public IBinder onBind(@NonNull Intent intent) {
|
||||
//TODO: Allow binding from media router service only?
|
||||
if (SERVICE_INTERFACE.equals(intent.getAction())) {
|
||||
if (mStub == null) {
|
||||
@@ -57,11 +62,17 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
|
||||
/**
|
||||
* Called when selectRoute is called on a route of the provider.
|
||||
* Once the route is ready to be used , call {@link #notifyRouteSelected(SelectToken, Bundle)}
|
||||
* to notify that.
|
||||
*
|
||||
* @param packageName the package name of the application that selected the route
|
||||
* @param routeId the id of the route being selected
|
||||
* @param token token that contains select info
|
||||
*
|
||||
* @see #notifyRouteSelected
|
||||
*/
|
||||
public abstract void onSelectRoute(String packageName, String routeId);
|
||||
public abstract void onSelectRoute(@NonNull String packageName, @NonNull String routeId,
|
||||
@NonNull SelectToken token);
|
||||
|
||||
/**
|
||||
* Called when unselectRoute is called on a route of the provider.
|
||||
@@ -69,7 +80,7 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
* @param packageName the package name of the application that has selected the route.
|
||||
* @param routeId the id of the route being unselected
|
||||
*/
|
||||
public abstract void onUnselectRoute(String packageName, String routeId);
|
||||
public abstract void onUnselectRoute(@NonNull String packageName, @NonNull String routeId);
|
||||
|
||||
/**
|
||||
* Called when sendControlRequest is called on a route of the provider
|
||||
@@ -78,21 +89,21 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
* @param request the media control request intent
|
||||
*/
|
||||
//TODO: Discuss what to use for request (e.g., Intent? Request class?)
|
||||
public abstract void onControlRequest(String routeId, Intent request);
|
||||
public abstract void onControlRequest(@NonNull String routeId, @NonNull Intent request);
|
||||
|
||||
/**
|
||||
* Called when requestSetVolume is called on a route of the provider
|
||||
* @param routeId the id of the route
|
||||
* @param volume the target volume
|
||||
*/
|
||||
public abstract void onSetVolume(String routeId, int volume);
|
||||
public abstract void onSetVolume(@NonNull String routeId, int volume);
|
||||
|
||||
/**
|
||||
* Called when requestUpdateVolume is called on a route of the provider
|
||||
* @param routeId id of the route
|
||||
* @param delta the delta to add to the current volume
|
||||
*/
|
||||
public abstract void onUpdateVolume(String routeId, int delta);
|
||||
public abstract void onUpdateVolume(@NonNull String routeId, int delta);
|
||||
|
||||
/**
|
||||
* Updates provider info and publishes routes
|
||||
@@ -102,6 +113,29 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
publishState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the client of that the selected route is ready for use. If the selected route can be
|
||||
* controlled, pass a {@link Bundle} that contains how to control it.
|
||||
*
|
||||
* @param token token passed in {@link #onSelectRoute}
|
||||
* @param controlHints a {@link Bundle} that contains how to control the given route.
|
||||
* Pass {@code null} if the route is not available.
|
||||
*/
|
||||
public final void notifyRouteSelected(@NonNull SelectToken token,
|
||||
@Nullable Bundle controlHints) {
|
||||
Objects.requireNonNull(token, "token must not be null");
|
||||
|
||||
if (mClient == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mClient.notifyRouteSelected(token.mPackageName, token.mRouteId,
|
||||
controlHints, token.mSeq);
|
||||
} catch (RemoteException ex) {
|
||||
Log.w(TAG, "Failed to notify route selected");
|
||||
}
|
||||
}
|
||||
|
||||
void setClient(IMediaRoute2ProviderClient client) {
|
||||
mClient = client;
|
||||
publishState();
|
||||
@@ -118,6 +152,23 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route selection information.
|
||||
*
|
||||
* @see #notifyRouteSelected
|
||||
*/
|
||||
public final class SelectToken {
|
||||
final String mPackageName;
|
||||
final String mRouteId;
|
||||
final int mSeq;
|
||||
|
||||
SelectToken(String packageName, String routeId, int seq) {
|
||||
mPackageName = packageName;
|
||||
mRouteId = routeId;
|
||||
mSeq = seq;
|
||||
}
|
||||
}
|
||||
|
||||
final class ProviderStub extends IMediaRoute2Provider.Stub {
|
||||
ProviderStub() { }
|
||||
|
||||
@@ -129,10 +180,10 @@ public abstract class MediaRoute2ProviderService extends Service {
|
||||
|
||||
@Override
|
||||
public void requestSelectRoute(String packageName, String id, int seq) {
|
||||
// TODO: When introducing MediaRoute2ProviderService#sendConnectionHints(),
|
||||
// use the sequence number here properly.
|
||||
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelectRoute,
|
||||
MediaRoute2ProviderService.this, packageName, id));
|
||||
MediaRoute2ProviderService.this, packageName, id,
|
||||
new SelectToken(packageName, id, seq)));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1318,6 +1318,7 @@ public class MediaRouter {
|
||||
sStatic.rebindAsUser(userId);
|
||||
}
|
||||
|
||||
//TODO: remove this and Client1Record in MediaRouter2ServiceImpl.
|
||||
/**
|
||||
* Sets the control categories of the application.
|
||||
* Routes that support at least one of the given control categories only exists and are handled
|
||||
|
||||
@@ -57,7 +57,8 @@ public class MediaRouter2 {
|
||||
@IntDef(value = {
|
||||
SELECT_REASON_UNKNOWN,
|
||||
SELECT_REASON_USER_SELECTED,
|
||||
SELECT_REASON_FALLBACK})
|
||||
SELECT_REASON_FALLBACK,
|
||||
SELECT_REASON_SYSTEM_SELECTED})
|
||||
public @interface SelectReason {}
|
||||
|
||||
/**
|
||||
@@ -80,6 +81,13 @@ public class MediaRouter2 {
|
||||
*/
|
||||
public static final int SELECT_REASON_FALLBACK = 2;
|
||||
|
||||
/**
|
||||
* This is passed from {@link com.android.server.media.MediaRouterService} when the route
|
||||
* is selected in response to a request from other apps (e.g. System UI).
|
||||
* @hide
|
||||
*/
|
||||
public static final int SELECT_REASON_SYSTEM_SELECTED = 3;
|
||||
|
||||
private static final String TAG = "MR2";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
private static final Object sLock = new Object();
|
||||
@@ -485,6 +493,9 @@ public class MediaRouter2 {
|
||||
}
|
||||
mSelectingRoute = null;
|
||||
}
|
||||
if (reason == SELECT_REASON_SYSTEM_SELECTED) {
|
||||
reason = SELECT_REASON_USER_SELECTED;
|
||||
}
|
||||
mSelectedRoute = route;
|
||||
notifyRouteSelected(route, reason, controlHints);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.content.Intent;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRoute2ProviderInfo;
|
||||
import android.media.MediaRoute2ProviderService;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -95,7 +96,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectRoute(String packageName, String routeId) {
|
||||
public void onSelectRoute(String packageName, String routeId, SelectToken token) {
|
||||
MediaRoute2Info route = mRoutes.get(routeId);
|
||||
if (route == null) {
|
||||
return;
|
||||
@@ -104,6 +105,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
|
||||
.setClientPackageName(packageName)
|
||||
.build());
|
||||
publishRoutes();
|
||||
notifyRouteSelected(token, Bundle.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,23 +23,19 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRouter2;
|
||||
import android.media.MediaRouter2Manager;
|
||||
import android.os.Bundle;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -95,9 +91,14 @@ public class MediaRouterManagerTest {
|
||||
private Executor mExecutor;
|
||||
private String mPackageName;
|
||||
|
||||
private final List<MediaRouter2Manager.Callback> mManagerCallbacks = new ArrayList<>();
|
||||
private final List<MediaRouter2.Callback> mRouterCallbacks = new ArrayList<>();
|
||||
private Map<String, MediaRoute2Info> mRoutes;
|
||||
|
||||
private static final List<String> CATEGORIES_ALL = new ArrayList();
|
||||
private static final List<String> CATEGORIES_SPECIAL = new ArrayList();
|
||||
private static final List<String> CATEGORIES_LIVE_AUDIO = new ArrayList<>();
|
||||
|
||||
static {
|
||||
CATEGORIES_ALL.add(CATEGORY_SAMPLE);
|
||||
CATEGORIES_ALL.add(CATEGORY_SPECIAL);
|
||||
@@ -108,6 +109,7 @@ public class MediaRouterManagerTest {
|
||||
CATEGORIES_LIVE_AUDIO.add(CATEGORY_LIVE_AUDIO);
|
||||
}
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mContext = InstrumentationRegistry.getTargetContext();
|
||||
@@ -116,6 +118,16 @@ public class MediaRouterManagerTest {
|
||||
//TODO: If we need to support thread pool executors, change this to thread pool executor.
|
||||
mExecutor = Executors.newSingleThreadExecutor();
|
||||
mPackageName = mContext.getPackageName();
|
||||
|
||||
// ensure media router 2 client
|
||||
addRouterCallback(new MediaRouter2.Callback());
|
||||
mRoutes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
// unregister callbacks
|
||||
clearCallbacks();
|
||||
}
|
||||
|
||||
//TODO: Move to a separate file
|
||||
@@ -132,10 +144,13 @@ public class MediaRouterManagerTest {
|
||||
assertNotEquals(routeInfo1, routeInfo3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if routes are added correctly when a new callback is registered.
|
||||
*/
|
||||
@Test
|
||||
public void testOnRoutesAdded() throws Exception {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
MediaRouter2Manager.Callback callback = new MediaRouter2Manager.Callback() {
|
||||
addManagerCallback(new MediaRouter2Manager.Callback() {
|
||||
@Override
|
||||
public void onRoutesAdded(List<MediaRoute2Info> routes) {
|
||||
assertTrue(routes.size() > 0);
|
||||
@@ -145,27 +160,15 @@ public class MediaRouterManagerTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
mManager.registerCallback(mExecutor, callback);
|
||||
});
|
||||
|
||||
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
|
||||
mManager.unregisterCallback(callback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnRoutesRemoved() throws Exception {
|
||||
MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class);
|
||||
mManager.registerCallback(mExecutor, mockCallback);
|
||||
|
||||
MediaRouter2.Callback routerCallback = new MediaRouter2.Callback();
|
||||
mRouter2.registerCallback(mExecutor, routerCallback);
|
||||
|
||||
Map<String, MediaRoute2Info> routes =
|
||||
waitAndGetRoutesWithManager(CATEGORIES_ALL);
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
MediaRouter2Manager.Callback callback = new MediaRouter2Manager.Callback() {
|
||||
addManagerCallback(new MediaRouter2Manager.Callback() {
|
||||
@Override
|
||||
public void onRoutesRemoved(List<MediaRoute2Info> routes) {
|
||||
assertTrue(routes.size() > 0);
|
||||
@@ -175,16 +178,12 @@ public class MediaRouterManagerTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
mManager.registerCallback(mExecutor, callback);
|
||||
});
|
||||
|
||||
//TODO: Figure out a more proper way to test.
|
||||
// (Control requests shouldn't be used in this way.)
|
||||
mRouter2.sendControlRequest(routes.get(ROUTE_ID2), new Intent(ACTION_REMOVE_ROUTE));
|
||||
mRouter2.sendControlRequest(mRoutes.get(ROUTE_ID2), new Intent(ACTION_REMOVE_ROUTE));
|
||||
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
|
||||
mRouter2.unregisterCallback(routerCallback);
|
||||
mManager.unregisterCallback(mockCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,16 +191,10 @@ public class MediaRouterManagerTest {
|
||||
*/
|
||||
@Test
|
||||
public void testControlCategory() throws Exception {
|
||||
MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class);
|
||||
mManager.registerCallback(mExecutor, mockCallback);
|
||||
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_SPECIAL);
|
||||
|
||||
Map<String, MediaRoute2Info> routes =
|
||||
waitAndGetRoutesWithManager(CATEGORIES_SPECIAL);
|
||||
|
||||
Assert.assertEquals(1, routes.size());
|
||||
Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
|
||||
|
||||
mManager.unregisterCallback(mockCallback);
|
||||
assertEquals(1, routes.size());
|
||||
assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,37 +202,60 @@ public class MediaRouterManagerTest {
|
||||
*/
|
||||
@Test
|
||||
public void testGetRoutes() throws Exception {
|
||||
MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
|
||||
mRouter2.registerCallback(mExecutor, mockCallback);
|
||||
|
||||
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_SPECIAL);
|
||||
|
||||
Assert.assertEquals(1, routes.size());
|
||||
Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
|
||||
|
||||
mRouter2.unregisterCallback(mockCallback);
|
||||
assertEquals(1, routes.size());
|
||||
assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if MR2.Callback.onRouteSelected is called when a route is selected from MR2Manager.
|
||||
*/
|
||||
@Test
|
||||
public void testOnRouteSelected() throws Exception {
|
||||
MediaRouter2.Callback routerCallback = new MediaRouter2.Callback();
|
||||
MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class);
|
||||
public void testRouterOnRouteSelected() throws Exception {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
mManager.registerCallback(mExecutor, managerCallback);
|
||||
mRouter2.registerCallback(mExecutor, routerCallback);
|
||||
addRouterCallback(new MediaRouter2.Callback() {
|
||||
@Override
|
||||
public void onRouteSelected(MediaRoute2Info route, int reason, Bundle controlHints) {
|
||||
if (route != null && TextUtils.equals(route.getId(), ROUTE_ID1)) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
|
||||
|
||||
MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
|
||||
MediaRoute2Info routeToSelect = mRoutes.get(ROUTE_ID1);
|
||||
assertNotNull(routeToSelect);
|
||||
|
||||
mManager.selectRoute(mPackageName, routeToSelect);
|
||||
verify(managerCallback, timeout(TIMEOUT_MS))
|
||||
.onRouteSelected(eq(mPackageName),
|
||||
argThat(route -> route != null && route.equals(routeToSelect)));
|
||||
|
||||
mRouter2.unregisterCallback(routerCallback);
|
||||
mManager.unregisterCallback(managerCallback);
|
||||
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if MR2Manager.Callback.onRouteSelected is called
|
||||
* when a route is selected by MR2Manager.
|
||||
*/
|
||||
@Test
|
||||
public void testManagerOnRouteSelected() throws Exception {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
addManagerCallback(new MediaRouter2Manager.Callback() {
|
||||
@Override
|
||||
public void onRouteSelected(String packageName, MediaRoute2Info route) {
|
||||
if (TextUtils.equals(mPackageName, packageName)
|
||||
&& route != null && TextUtils.equals(route.getId(), ROUTE_ID1)) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
MediaRoute2Info routeToSelect = mRoutes.get(ROUTE_ID1);
|
||||
assertNotNull(routeToSelect);
|
||||
|
||||
mManager.selectRoute(mPackageName, routeToSelect);
|
||||
|
||||
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,19 +263,13 @@ public class MediaRouterManagerTest {
|
||||
*/
|
||||
@Test
|
||||
public void testSingleProviderSelect() throws Exception {
|
||||
MediaRouter2.Callback routerCallback = mock(MediaRouter2.Callback.class);
|
||||
|
||||
mRouter2.registerCallback(mExecutor, routerCallback);
|
||||
|
||||
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
|
||||
|
||||
awaitOnRouteChangedManager(
|
||||
() -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1)),
|
||||
() -> mManager.selectRoute(mPackageName, mRoutes.get(ROUTE_ID1)),
|
||||
ROUTE_ID1,
|
||||
route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
|
||||
|
||||
awaitOnRouteChangedManager(
|
||||
() -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID2)),
|
||||
() -> mManager.selectRoute(mPackageName, mRoutes.get(ROUTE_ID2)),
|
||||
ROUTE_ID2,
|
||||
route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
|
||||
|
||||
@@ -267,8 +277,6 @@ public class MediaRouterManagerTest {
|
||||
() -> mManager.unselectRoute(mPackageName),
|
||||
ROUTE_ID2,
|
||||
route -> TextUtils.equals(route.getClientPackageName(), null));
|
||||
|
||||
mRouter2.unregisterCallback(routerCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -292,12 +300,7 @@ public class MediaRouterManagerTest {
|
||||
|
||||
@Test
|
||||
public void testControlVolumeWithManager() throws Exception {
|
||||
MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
|
||||
|
||||
mRouter2.registerCallback(mExecutor, mockCallback);
|
||||
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
|
||||
|
||||
MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
|
||||
MediaRoute2Info volRoute = mRoutes.get(ROUTE_ID_VARIABLE_VOLUME);
|
||||
int originalVolume = volRoute.getVolume();
|
||||
int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1);
|
||||
|
||||
@@ -310,24 +313,16 @@ public class MediaRouterManagerTest {
|
||||
() -> mManager.requestSetVolume(volRoute, originalVolume),
|
||||
ROUTE_ID_VARIABLE_VOLUME,
|
||||
(route -> route.getVolume() == originalVolume));
|
||||
|
||||
mRouter2.unregisterCallback(mockCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVolumeHandling() throws Exception {
|
||||
MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
|
||||
mRouter2.registerCallback(mExecutor, mockCallback);
|
||||
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_ALL);
|
||||
|
||||
MediaRoute2Info fixedVolumeRoute = routes.get(ROUTE_ID_FIXED_VOLUME);
|
||||
MediaRoute2Info variableVolumeRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
|
||||
MediaRoute2Info fixedVolumeRoute = mRoutes.get(ROUTE_ID_FIXED_VOLUME);
|
||||
MediaRoute2Info variableVolumeRoute = mRoutes.get(ROUTE_ID_VARIABLE_VOLUME);
|
||||
|
||||
assertEquals(PLAYBACK_VOLUME_FIXED, fixedVolumeRoute.getVolumeHandling());
|
||||
assertEquals(PLAYBACK_VOLUME_VARIABLE, variableVolumeRoute.getVolumeHandling());
|
||||
assertEquals(VOLUME_MAX, variableVolumeRoute.getVolumeMax());
|
||||
|
||||
mRouter2.unregisterCallback(mockCallback);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -368,6 +363,7 @@ public class MediaRouterManagerTest {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onControlCategoriesChanged(String packageName) {
|
||||
if (TextUtils.equals(mPackageName, packageName)) {
|
||||
@@ -401,7 +397,7 @@ public class MediaRouterManagerTest {
|
||||
};
|
||||
mRouter2.registerCallback(mExecutor, callback);
|
||||
try {
|
||||
new Thread(task).start();
|
||||
task.run();
|
||||
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
} finally {
|
||||
mRouter2.unregisterCallback(callback);
|
||||
@@ -422,7 +418,7 @@ public class MediaRouterManagerTest {
|
||||
};
|
||||
mManager.registerCallback(mExecutor, callback);
|
||||
try {
|
||||
new Thread(task).start();
|
||||
task.run();
|
||||
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
|
||||
} finally {
|
||||
mManager.unregisterCallback(callback);
|
||||
@@ -433,9 +429,31 @@ public class MediaRouterManagerTest {
|
||||
static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
|
||||
Map<String, MediaRoute2Info> routeMap = new HashMap<>();
|
||||
for (MediaRoute2Info route : routes) {
|
||||
// intentionally not route.getUniqueId() for convenience.
|
||||
// intentionally not using route.getUniqueId() for convenience.
|
||||
routeMap.put(route.getId(), route);
|
||||
}
|
||||
return routeMap;
|
||||
}
|
||||
|
||||
private void addManagerCallback(MediaRouter2Manager.Callback callback) {
|
||||
mManagerCallbacks.add(callback);
|
||||
mManager.registerCallback(mExecutor, callback);
|
||||
}
|
||||
|
||||
private void addRouterCallback(MediaRouter2.Callback callback) {
|
||||
mRouterCallbacks.add(callback);
|
||||
mRouter2.registerCallback(mExecutor, callback);
|
||||
}
|
||||
|
||||
private void clearCallbacks() {
|
||||
for (MediaRouter2Manager.Callback callback : mManagerCallbacks) {
|
||||
mManager.unregisterCallback(callback);
|
||||
}
|
||||
mManagerCallbacks.clear();
|
||||
|
||||
for (MediaRouter2.Callback callback : mRouterCallbacks) {
|
||||
mRouter2.unregisterCallback(callback);
|
||||
}
|
||||
mRouterCallbacks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRoute2ProviderInfo;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -29,7 +30,7 @@ abstract class MediaRoute2Provider {
|
||||
final ComponentName mComponentName;
|
||||
final String mUniqueId;
|
||||
|
||||
private Callback mCallback;
|
||||
Callback mCallback;
|
||||
private MediaRoute2ProviderInfo mProviderInfo;
|
||||
|
||||
MediaRoute2Provider(@NonNull ComponentName componentName) {
|
||||
@@ -77,6 +78,9 @@ abstract class MediaRoute2Provider {
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
void onProviderStateChanged(MediaRoute2Provider provider);
|
||||
void onProviderStateChanged(@Nullable MediaRoute2Provider provider);
|
||||
void onRouteSelected(@NonNull MediaRoute2ProviderProxy provider,
|
||||
@NonNull String clientPackageName, @NonNull MediaRoute2Info route,
|
||||
@Nullable Bundle controlHints, int seq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.media.IMediaRoute2ProviderClient;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.MediaRoute2ProviderInfo;
|
||||
import android.media.MediaRoute2ProviderService;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.IBinder.DeathRecipient;
|
||||
@@ -253,6 +254,20 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
|
||||
setAndNotifyProviderInfo(info);
|
||||
}
|
||||
|
||||
private void onRouteSelected(Connection connection,
|
||||
String packageName, String routeId, Bundle controlHints, int seq) {
|
||||
if (mActiveConnection != connection) {
|
||||
return;
|
||||
}
|
||||
MediaRoute2ProviderInfo providerInfo = getProviderInfo();
|
||||
MediaRoute2Info route = (providerInfo == null) ? null : providerInfo.getRoute(routeId);
|
||||
if (route == null) {
|
||||
Slog.w(TAG, this + ": Unknown route " + routeId + " is selected from remove provider");
|
||||
return;
|
||||
}
|
||||
mCallback.onRouteSelected(this, packageName, route, controlHints, seq);
|
||||
}
|
||||
|
||||
private void disconnect() {
|
||||
if (mActiveConnection != null) {
|
||||
mConnectionReady = false;
|
||||
@@ -341,6 +356,11 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
|
||||
void postProviderInfoUpdated(MediaRoute2ProviderInfo info) {
|
||||
mHandler.post(() -> onProviderInfoUpdated(Connection.this, info));
|
||||
}
|
||||
|
||||
void postRouteSelected(String packageName, String routeId, Bundle controlHints, int seq) {
|
||||
mHandler.post(() -> onRouteSelected(Connection.this,
|
||||
packageName, routeId, controlHints, seq));
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ProviderClient extends IMediaRoute2ProviderClient.Stub {
|
||||
@@ -361,5 +381,15 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
|
||||
connection.postProviderInfoUpdated(info);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyRouteSelected(String packageName, String routeId,
|
||||
Bundle controlHints, int seq) {
|
||||
Connection connection = mConnectionRef.get();
|
||||
if (connection != null) {
|
||||
connection.postRouteSelected(packageName, routeId, controlHints, seq);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ class MediaRouter2ServiceImpl {
|
||||
@GuardedBy("mLock")
|
||||
private int mCurrentUserId = -1;
|
||||
@GuardedBy("mLock")
|
||||
private int mSelectRouteRequestSequenceNumber = 0;
|
||||
private int mSelectRouteRequestSequenceNumber = 1;
|
||||
|
||||
MediaRouter2ServiceImpl(Context context) {
|
||||
mContext = context;
|
||||
@@ -218,7 +218,7 @@ class MediaRouter2ServiceImpl {
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
requestSelectRoute2Locked(mAllClientRecords.get(client.asBinder()), route);
|
||||
requestSelectRoute2Locked(mAllClientRecords.get(client.asBinder()), false, route);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
@@ -399,10 +399,12 @@ class MediaRouter2ServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private void requestSelectRoute2Locked(ClientRecord clientRecord, MediaRoute2Info route) {
|
||||
private void requestSelectRoute2Locked(ClientRecord clientRecord, boolean selectedByManager,
|
||||
MediaRoute2Info route) {
|
||||
if (clientRecord != null) {
|
||||
MediaRoute2Info oldRoute = clientRecord.mSelectedRoute;
|
||||
clientRecord.mSelectingRoute = route;
|
||||
clientRecord.mIsManagerSelecting = selectedByManager;
|
||||
|
||||
UserHandler handler = clientRecord.mUserRecord.mHandler;
|
||||
//TODO: Handle transfer instead of unselect and select
|
||||
@@ -417,7 +419,6 @@ class MediaRouter2ServiceImpl {
|
||||
handler.sendMessage(obtainMessage(
|
||||
UserHandler::requestSelectRoute, handler, clientRecord.mPackageName,
|
||||
route, seq));
|
||||
|
||||
// Remove all previous timeout messages
|
||||
for (int previousSeq : clientRecord.mSelectRouteSequenceNumbers) {
|
||||
clientRecord.mUserRecord.mHandler.removeMessages(previousSeq);
|
||||
@@ -543,7 +544,7 @@ class MediaRouter2ServiceImpl {
|
||||
Slog.w(TAG, "Ignoring route selection for unknown client.");
|
||||
}
|
||||
if (clientRecord != null && managerRecord.mTrusted) {
|
||||
requestSelectRoute2Locked(clientRecord, route);
|
||||
requestSelectRoute2Locked(clientRecord, true, route);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -656,7 +657,9 @@ class MediaRouter2ServiceImpl {
|
||||
public final UserRecord mUserRecord;
|
||||
public final String mPackageName;
|
||||
public final List<Integer> mSelectRouteSequenceNumbers;
|
||||
|
||||
public List<String> mControlCategories;
|
||||
public boolean mIsManagerSelecting;
|
||||
public MediaRoute2Info mSelectingRoute;
|
||||
public MediaRoute2Info mSelectedRoute;
|
||||
|
||||
@@ -802,9 +805,8 @@ class MediaRouter2ServiceImpl {
|
||||
sendMessage(PooledLambda.obtainMessage(UserHandler::updateProvider, this, provider));
|
||||
}
|
||||
|
||||
// TODO: When introducing MediaRoute2ProviderService#sendControlHints(),
|
||||
// Make this method to be called.
|
||||
public void onRouteSelectionRequestHandled(@NonNull MediaRoute2ProviderProxy provider,
|
||||
@Override
|
||||
public void onRouteSelected(@NonNull MediaRoute2ProviderProxy provider,
|
||||
String clientPackageName, MediaRoute2Info route, Bundle controlHints, int seq) {
|
||||
sendMessage(PooledLambda.obtainMessage(
|
||||
UserHandler::updateSelectedRoute, this, provider, clientPackageName, route,
|
||||
@@ -917,6 +919,8 @@ class MediaRouter2ServiceImpl {
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: handle a case such that controlHints is null. (How should we notify MR2?)
|
||||
|
||||
if (clientRecord.mSelectingRoute == null || !TextUtils.equals(
|
||||
clientRecord.mSelectingRoute.getUniqueId(), selectedRoute.getUniqueId())) {
|
||||
Log.w(TAG, "Ignoring invalid updateSelectedRoute call. selectingRoute="
|
||||
@@ -929,7 +933,9 @@ class MediaRouter2ServiceImpl {
|
||||
|
||||
notifyRouteSelectedToClient(((Client2Record) clientRecord).mClient,
|
||||
selectedRoute,
|
||||
MediaRouter2.SELECT_REASON_USER_SELECTED,
|
||||
clientRecord.mIsManagerSelecting
|
||||
? MediaRouter2.SELECT_REASON_SYSTEM_SELECTED :
|
||||
MediaRouter2.SELECT_REASON_USER_SELECTED,
|
||||
controlHints);
|
||||
updateClientUsage(clientRecord);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user