diff options
6 files changed, 216 insertions, 56 deletions
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index 1b713b6ab74b..81213b943c81 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -40,6 +40,7 @@ interface IMediaRouterService { void setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit); void requestSetVolume(IMediaRouterClient client, String routeId, int volume); void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction); + void setControlCategories(IMediaRouterClient client, in List<String> categories); // Methods for media router 2 void registerClient2(IMediaRouter2Client client, String packageName); @@ -52,7 +53,7 @@ interface IMediaRouterService { * @param route the route to be selected */ void selectRoute2(IMediaRouter2Client client, in @nullable MediaRoute2Info route); - void setControlCategories(IMediaRouter2Client client, in List<String> categories); + void setControlCategories2(IMediaRouter2Client client, in List<String> categories); void registerManager(IMediaRouter2Manager manager, String packageName); void unregisterManager(IMediaRouter2Manager manager); diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index d72231f40dcf..9cb78696f19b 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -49,6 +49,8 @@ import android.view.Display; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Objects; @@ -82,6 +84,7 @@ public class MediaRouter { final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>(); + List<String> mControlCategories = Collections.emptyList(); final RouteCategory mSystemCategory; @@ -358,6 +361,18 @@ public class MediaRouter { return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION); } + public void setControlCategories(Collection<String> controlCategories) { + List<String> newControlCategories = new ArrayList<>(controlCategories); + mControlCategories = newControlCategories; + if (mClient != null) { + try { + mMediaRouterService.setControlCategories(mClient, newControlCategories); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to set control categories.", ex); + } + } + } + private void updatePresentationDisplays(int changedDisplayId) { final int count = mRoutes.size(); for (int i = 0; i < count; i++) { @@ -406,6 +421,7 @@ public class MediaRouter { try { Client client = new Client(); mMediaRouterService.registerClientAsUser(client, mPackageName, userId); + mMediaRouterService.setControlCategories(client, mControlCategories); mClient = client; } catch (RemoteException ex) { Log.e(TAG, "Unable to register media router client.", ex); @@ -1302,6 +1318,19 @@ public class MediaRouter { sStatic.rebindAsUser(userId); } + /** + * Sets the control categories of the application. + * Routes that support at least one of the given control categories only exists and are handled + * by the media router. + * + * @hide + */ + public void setControlCategories(@NonNull Collection<String> controlCategories) { + Objects.requireNonNull(controlCategories, "control categories must not be null"); + + sStatic.setControlCategories(controlCategories); + } + static void updateRoute(final RouteInfo info) { dispatchRouteChanged(info); } diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 8e29e34caa14..ed35ef6a7ac7 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -129,7 +129,7 @@ public class MediaRouter2 { Client client = new Client(); try { mMediaRouterService.registerClient2(client, mPackageName); - mMediaRouterService.setControlCategories(client, mControlCategories); + mMediaRouterService.setControlCategories2(client, mControlCategories); mClient = client; } catch (RemoteException ex) { Log.e(TAG, "Unable to register media router.", ex); @@ -188,7 +188,7 @@ public class MediaRouter2 { } if (client != null) { try { - mMediaRouterService.setControlCategories(client, newControlCategories); + mMediaRouterService.setControlCategories2(client, newControlCategories); } catch (RemoteException ex) { Log.e(TAG, "Unable to set control categories.", ex); } diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java index 3abf0a4941e6..a3ed07a6dee3 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.content.Intent; import android.media.MediaRoute2Info; +import android.media.MediaRouter; import android.media.MediaRouter2; import android.media.MediaRouter2Manager; import android.support.test.InstrumentationRegistry; @@ -72,7 +73,8 @@ public class MediaRouterManagerTest { private Context mContext; private MediaRouter2Manager mManager; - private MediaRouter2 mRouter; + private MediaRouter mRouter; + private MediaRouter2 mRouter2; private Executor mExecutor; private String mPackageName; @@ -89,7 +91,8 @@ public class MediaRouterManagerTest { public void setUp() throws Exception { mContext = InstrumentationRegistry.getTargetContext(); mManager = MediaRouter2Manager.getInstance(mContext); - mRouter = MediaRouter2.getInstance(mContext); + mRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE); + mRouter2 = MediaRouter2.getInstance(mContext); //TODO: If we need to support thread pool executors, change this to thread pool executor. mExecutor = Executors.newSingleThreadExecutor(); mPackageName = mContext.getPackageName(); @@ -131,12 +134,12 @@ public class MediaRouterManagerTest { //TODO: Figure out a more proper way to test. // (Control requests shouldn't be used in this way.) - mRouter.setControlCategories(CONTROL_CATEGORIES_ALL); - mRouter.registerCallback(mExecutor, mockRouterCallback); - mRouter.sendControlRequest( + mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL); + mRouter2.registerCallback(mExecutor, mockRouterCallback); + mRouter2.sendControlRequest( new MediaRoute2Info.Builder(ROUTE_ID2, ROUTE_NAME2).build(), new Intent(ACTION_REMOVE_ROUTE)); - mRouter.unregisterCallback(mockRouterCallback); + mRouter2.unregisterCallback(mockRouterCallback); verify(mockCallback, timeout(TIMEOUT_MS)).onRouteRemoved(argThat( (MediaRoute2Info info) -> @@ -148,15 +151,41 @@ public class MediaRouterManagerTest { * Tests if we get proper routes for application that has special control category. */ @Test + public void testControlCategoryWithMediaRouter() throws Exception { + MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class); + mManager.registerCallback(mExecutor, mockCallback); + + MediaRouter.Callback mockRouterCallback = mock(MediaRouter.Callback.class); + + mRouter.setControlCategories(CONTROL_CATEGORIES_SPECIAL); + mRouter.addCallback(MediaRouter.ROUTE_TYPE_USER, mockRouterCallback); + + verify(mockCallback, timeout(TIMEOUT_MS)) + .onRoutesChanged(argThat(routes -> routes.size() > 0)); + + Map<String, MediaRoute2Info> routes = + createRouteMap(mManager.getAvailableRoutes(mPackageName)); + + Assert.assertEquals(1, routes.size()); + Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY)); + + mRouter.removeCallback(mockRouterCallback); + mManager.unregisterCallback(mockCallback); + } + + /** + * Tests if we get proper routes for application that has special control category. + */ + @Test public void testControlCategory() throws Exception { MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class); mManager.registerCallback(mExecutor, mockCallback); MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class); - mRouter.setControlCategories(CONTROL_CATEGORIES_SPECIAL); - mRouter.registerCallback(mExecutor, mockRouterCallback); - mRouter.unregisterCallback(mockRouterCallback); + mRouter2.setControlCategories(CONTROL_CATEGORIES_SPECIAL); + mRouter2.registerCallback(mExecutor, mockRouterCallback); + mRouter2.unregisterCallback(mockRouterCallback); verify(mockCallback, timeout(TIMEOUT_MS)) .onRoutesChanged(argThat(routes -> routes.size() > 0)); @@ -177,15 +206,15 @@ public class MediaRouterManagerTest { public void testGetRoutes() throws Exception { MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class); - mRouter.setControlCategories(CONTROL_CATEGORIES_SPECIAL); - mRouter.registerCallback(mExecutor, mockCallback); + mRouter2.setControlCategories(CONTROL_CATEGORIES_SPECIAL); + mRouter2.registerCallback(mExecutor, mockCallback); verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce()) .onRoutesChanged(argThat(routes -> routes.size() > 0)); - Map<String, MediaRoute2Info> routes = createRouteMap(mRouter.getRoutes()); + Map<String, MediaRoute2Info> routes = createRouteMap(mRouter2.getRoutes()); Assert.assertEquals(1, routes.size()); Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY)); - mRouter.unregisterCallback(mockCallback); + mRouter2.unregisterCallback(mockCallback); } @Test @@ -194,8 +223,8 @@ public class MediaRouterManagerTest { MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class); mManager.registerCallback(mExecutor, managerCallback); - mRouter.setControlCategories(CONTROL_CATEGORIES_ALL); - mRouter.registerCallback(mExecutor, mockRouterCallback); + mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL); + mRouter2.registerCallback(mExecutor, mockRouterCallback); verify(managerCallback, timeout(TIMEOUT_MS)) .onRoutesChanged(argThat(routes -> routes.size() > 0)); @@ -211,7 +240,7 @@ public class MediaRouterManagerTest { .onRouteAdded(argThat(route -> route.equals(routeToSelect))); mManager.unregisterCallback(managerCallback); - mRouter.unregisterCallback(mockRouterCallback); + mRouter2.unregisterCallback(mockRouterCallback); } /** @@ -223,8 +252,8 @@ public class MediaRouterManagerTest { MediaRouter2.Callback routerCallback = mock(MediaRouter2.Callback.class); mManager.registerCallback(mExecutor, managerCallback); - mRouter.setControlCategories(CONTROL_CATEGORIES_ALL); - mRouter.registerCallback(mExecutor, routerCallback); + mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL); + mRouter2.registerCallback(mExecutor, routerCallback); verify(managerCallback, timeout(TIMEOUT_MS)) .onRoutesChanged(argThat(routes -> routes.size() > 0)); @@ -248,7 +277,7 @@ public class MediaRouterManagerTest { .onRouteChanged(argThat(routeInfo -> TextUtils.equals(ROUTE_ID2, routeInfo.getId()) && TextUtils.equals(routeInfo.getClientPackageName(), null))); - mRouter.unregisterCallback(routerCallback); + mRouter2.unregisterCallback(routerCallback); mManager.unregisterCallback(managerCallback); } diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 043c834230d1..668f2be158c6 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -26,6 +26,7 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.media.IMediaRouter2Client; import android.media.IMediaRouter2Manager; +import android.media.IMediaRouterClient; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.os.Binder; @@ -86,7 +87,7 @@ class MediaRouter2ServiceImpl { final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - registerClientLocked(client, uid, pid, packageName, userId, trusted); + registerClient2Locked(client, uid, pid, packageName, userId, trusted); } } finally { Binder.restoreCallingIdentity(token); @@ -99,7 +100,7 @@ class MediaRouter2ServiceImpl { final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - unregisterClientLocked(client, false); + unregisterClient2Locked(client, false); } } finally { Binder.restoreCallingIdentity(token); @@ -155,14 +156,30 @@ class MediaRouter2ServiceImpl { } } - public void setControlCategories(@NonNull IMediaRouter2Client client, + //TODO: What would happen if a media app used MediaRouter and MediaRouter2 simultaneously? + public void setControlCategories(@NonNull IMediaRouterClient client, + @Nullable List<String> categories) { + Objects.requireNonNull(client, "client must not be null"); + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + ClientRecord clientRecord = mAllClientRecords.get(client.asBinder()); + setControlCategoriesLocked(clientRecord, categories); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public void setControlCategories2(@NonNull IMediaRouter2Client client, @Nullable List<String> categories) { Objects.requireNonNull(client, "client must not be null"); final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - setControlCategoriesLocked(client, categories); + ClientRecord clientRecord = mAllClientRecords.get(client.asBinder()); + setControlCategoriesLocked(clientRecord, categories); } } finally { Binder.restoreCallingIdentity(token); @@ -174,7 +191,7 @@ class MediaRouter2ServiceImpl { final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { - selectRoute2Locked(client, route); + selectRoute2Locked(mAllClientRecords.get(client.asBinder()), route); } } finally { Binder.restoreCallingIdentity(token); @@ -193,6 +210,36 @@ class MediaRouter2ServiceImpl { } } + public void registerClient(@NonNull IMediaRouterClient client, @NonNull String packageName) { + Objects.requireNonNull(client, "client must not be null"); + + final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); + final int userId = UserHandle.getUserId(uid); + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + registerClient1Locked(client, packageName, userId); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public void unregisterClient(@NonNull IMediaRouterClient client) { + Objects.requireNonNull(client, "client must not be null"); + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + unregisterClient1Locked(client); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + //TODO: Review this is handling multi-user properly. void switchUser() { synchronized (mLock) { @@ -217,9 +264,9 @@ class MediaRouter2ServiceImpl { } } - void clientDied(ClientRecord clientRecord) { + void clientDied(Client2Record clientRecord) { synchronized (mLock) { - unregisterClientLocked(clientRecord.mClient, true); + unregisterClient2Locked(clientRecord.mClient, true); } } @@ -229,18 +276,18 @@ class MediaRouter2ServiceImpl { } } - private void registerClientLocked(IMediaRouter2Client client, + private void registerClient2Locked(IMediaRouter2Client client, int uid, int pid, String packageName, int userId, boolean trusted) { final IBinder binder = client.asBinder(); - ClientRecord clientRecord = mAllClientRecords.get(binder); - if (clientRecord == null) { + if (mAllClientRecords.get(binder) == null) { boolean newUser = false; UserRecord userRecord = mUserRecords.get(userId); if (userRecord == null) { userRecord = new UserRecord(userId); newUser = true; } - clientRecord = new ClientRecord(userRecord, client, uid, pid, packageName, trusted); + Client2Record clientRecord = new Client2Record(userRecord, client, uid, pid, + packageName, trusted); try { binder.linkToDeath(clientRecord, 0); } catch (RemoteException ex) { @@ -261,8 +308,8 @@ class MediaRouter2ServiceImpl { } } - private void unregisterClientLocked(IMediaRouter2Client client, boolean died) { - ClientRecord clientRecord = mAllClientRecords.remove(client.asBinder()); + private void unregisterClient2Locked(IMediaRouter2Client client, boolean died) { + Client2Record clientRecord = (Client2Record) mAllClientRecords.remove(client.asBinder()); if (clientRecord != null) { UserRecord userRecord = clientRecord.mUserRecord; userRecord.mClientRecords.remove(clientRecord); @@ -272,8 +319,7 @@ class MediaRouter2ServiceImpl { } } - private void selectRoute2Locked(IMediaRouter2Client client, MediaRoute2Info route) { - ClientRecord clientRecord = mAllClientRecords.get(client.asBinder()); + private void selectRoute2Locked(ClientRecord clientRecord, MediaRoute2Info route) { if (clientRecord != null) { MediaRoute2Info oldRoute = clientRecord.mSelectedRoute; clientRecord.mSelectedRoute = route; @@ -294,10 +340,7 @@ class MediaRouter2ServiceImpl { } } - private void setControlCategoriesLocked(IMediaRouter2Client client, List<String> categories) { - final IBinder binder = client.asBinder(); - ClientRecord clientRecord = mAllClientRecords.get(binder); - + private void setControlCategoriesLocked(ClientRecord clientRecord, List<String> categories) { if (clientRecord != null) { clientRecord.mControlCategories = categories; @@ -349,9 +392,7 @@ class MediaRouter2ServiceImpl { obtainMessage(UserHandler::notifyProviderInfosUpdatedToManager, userRecord.mHandler, manager)); - final int count = userRecord.mClientRecords.size(); - for (int i = 0; i < count; i++) { - ClientRecord clientRecord = userRecord.mClientRecords.get(i); + for (ClientRecord clientRecord : userRecord.mClientRecords) { clientRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::updateClientUsage, clientRecord.mUserRecord.mHandler, clientRecord)); @@ -378,7 +419,7 @@ class MediaRouter2ServiceImpl { Slog.w(TAG, "Ignoring route selection for unknown client."); } if (clientRecord != null && managerRecord.mTrusted) { - selectRoute2Locked(clientRecord.mClient, route); + selectRoute2Locked(clientRecord, route); } } } @@ -409,6 +450,37 @@ class MediaRouter2ServiceImpl { } } + private void registerClient1Locked(IMediaRouterClient client, String packageName, + int userId) { + final IBinder binder = client.asBinder(); + if (mAllClientRecords.get(binder) == null) { + boolean newUser = false; + UserRecord userRecord = mUserRecords.get(userId); + if (userRecord == null) { + userRecord = new UserRecord(userId); + newUser = true; + } + ClientRecord clientRecord = new Client1Record(userRecord, client, packageName); + + if (newUser) { + mUserRecords.put(userId, userRecord); + initializeUserLocked(userRecord); + } + + userRecord.mClientRecords.add(clientRecord); + mAllClientRecords.put(binder, clientRecord); + } + } + + private void unregisterClient1Locked(IMediaRouterClient client) { + ClientRecord clientRecord = mAllClientRecords.remove(client.asBinder()); + if (clientRecord != null) { + UserRecord userRecord = clientRecord.mUserRecord; + userRecord.mClientRecords.remove(clientRecord); + disposeUserIfNeededLocked(userRecord); + } + } + final class UserRecord { public final int mUserId; final ArrayList<ClientRecord> mClientRecords = new ArrayList<>(); @@ -430,25 +502,43 @@ class MediaRouter2ServiceImpl { } } - final class ClientRecord implements IBinder.DeathRecipient { + class ClientRecord { public final UserRecord mUserRecord; + public final String mPackageName; + public List<String> mControlCategories; + public MediaRoute2Info mSelectedRoute; + + ClientRecord(UserRecord userRecord, String packageName) { + mUserRecord = userRecord; + mPackageName = packageName; + mControlCategories = Collections.emptyList(); + } + } + + final class Client1Record extends ClientRecord { + public final IMediaRouterClient mClient; + + Client1Record(UserRecord userRecord, IMediaRouterClient client, + String packageName) { + super(userRecord, packageName); + mClient = client; + } + } + + final class Client2Record extends ClientRecord + implements IBinder.DeathRecipient { public final IMediaRouter2Client mClient; public final int mUid; public final int mPid; - public final String mPackageName; public final boolean mTrusted; - public List<String> mControlCategories; - public MediaRoute2Info mSelectedRoute; - ClientRecord(UserRecord userRecord, IMediaRouter2Client client, + Client2Record(UserRecord userRecord, IMediaRouter2Client client, int uid, int pid, String packageName, boolean trusted) { - mUserRecord = userRecord; + super(userRecord, packageName); mClient = client; mUid = uid; mPid = pid; - mPackageName = packageName; mTrusted = trusted; - mControlCategories = Collections.emptyList(); } public void dispose() { @@ -622,7 +712,9 @@ class MediaRouter2ServiceImpl { managers.add(managerRecord.mManager); } for (ClientRecord clientRecord : mUserRecord.mClientRecords) { - clients.add(clientRecord.mClient); + if (clientRecord instanceof Client2Record) { + clients.add(((Client2Record) clientRecord).mClient); + } } } for (IMediaRouter2Manager manager : managers) { diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index 2670cd8247ac..796a25d7e295 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -249,6 +249,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub synchronized (mLock) { registerClientLocked(client, uid, pid, packageName, resolvedUserId, trusted); } + mService2.registerClient(client, packageName); } finally { Binder.restoreCallingIdentity(token); } @@ -289,6 +290,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub synchronized (mLock) { unregisterClientLocked(client, false); } + mService2.unregisterClient(client); } finally { Binder.restoreCallingIdentity(token); } @@ -395,6 +397,12 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override + public void setControlCategories(IMediaRouterClient client, List<String> controlCategories) { + mService2.setControlCategories(client, controlCategories); + } + + // Binder call + @Override public void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction) { if (client == null) { throw new IllegalArgumentException("client must not be null"); @@ -487,8 +495,8 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override - public void setControlCategories(IMediaRouter2Client client, List<String> categories) { - mService2.setControlCategories(client, categories); + public void setControlCategories2(IMediaRouter2Client client, List<String> categories) { + mService2.setControlCategories2(client, categories); } void restoreBluetoothA2dp() { @@ -561,6 +569,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub synchronized (mLock) { unregisterClientLocked(clientRecord.mClient, true); } + mService2.unregisterClient(clientRecord.mClient); } private void registerClientLocked(IMediaRouterClient client, |