diff --git a/media/java/android/media/IMediaRoute2ProviderClient.aidl b/media/java/android/media/IMediaRoute2ProviderClient.aidl index 6f44d45c0a12b..f4fb7f450fb8f 100644 --- a/media/java/android/media/IMediaRoute2ProviderClient.aidl +++ b/media/java/android/media/IMediaRoute2ProviderClient.aidl @@ -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); } diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java index 386d2dc54a883..1b6183e361c8f 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -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 diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index 9cb78696f19b4..7b15d95545344 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -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 diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 94ac77af3ac35..35cb066f97905 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -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); } diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java index f4f8d0b736581..6650f9618638e 100644 --- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java +++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java @@ -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 diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java index d0f7c780ceb22..c70ad8d8755c2 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java @@ -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 mManagerCallbacks = new ArrayList<>(); + private final List mRouterCallbacks = new ArrayList<>(); + private Map mRoutes; + private static final List CATEGORIES_ALL = new ArrayList(); private static final List CATEGORIES_SPECIAL = new ArrayList(); private static final List 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 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 routes = - waitAndGetRoutesWithManager(CATEGORIES_ALL); - CountDownLatch latch = new CountDownLatch(1); - MediaRouter2Manager.Callback callback = new MediaRouter2Manager.Callback() { + addManagerCallback(new MediaRouter2Manager.Callback() { @Override public void onRoutesRemoved(List 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 routes = waitAndGetRoutesWithManager(CATEGORIES_SPECIAL); - Map 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 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 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 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 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 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 createRouteMap(List routes) { Map 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(); + } } diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index 91c9253269a37..9a49c166e2b2c 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -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); } } diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java index 3b6580ad73578..a5abb1835e7bb 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java @@ -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); + } + } + } } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 2cf920d1aba45..2c478dff46795 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -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 mSelectRouteSequenceNumbers; + public List 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);