MediaRouter: send control hints from provider to client

This CL enables providers to send control hints to the clients
so connections can be established between clients and providers.

This CL also adds a hidden flag such that MR2 can notice whether
the route is selected by itself or MR2Manager.

Uses of Mockito is completely removed from the test for better test
environment.

Callbacks will be unregistered automatically when a test is
ended so there will be no unregistered callbacks remaining after
test failures.

Test: atest mediaroutertest
Change-Id: I1411bad3ddd7890fbf76020107f870daa2413077
This commit is contained in:
Kyunglyul Hyun
2019-11-11 13:56:28 +09:00
parent 3ef13e045c
commit f7d5e044a8
9 changed files with 233 additions and 107 deletions

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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);