Loading media/java/android/media/MediaRouter2.java +171 −40 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import java.util.stream.Collectors; public final class MediaRouter2 { private static final String TAG = "MR2"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final Object sSystemRouterLock = new Object(); private static final Object sRouterLock = new Object(); // The maximum time for the old routing controller available after transfer. Loading @@ -67,8 +68,8 @@ public final class MediaRouter2 { // The manager request ID representing that no manager is involved. private static final long MANAGER_REQUEST_ID_NONE = MediaRoute2ProviderService.REQUEST_ID_NONE; @GuardedBy("sRouterLock") private static Map<String, MediaRouter2> sMediaRouter2Map = new ArrayMap<>(); @GuardedBy("sSystemRouterLock") private static Map<String, MediaRouter2> sSystemMediaRouter2Map = new ArrayMap<>(); private static MediaRouter2Manager sManager; @GuardedBy("sRouterLock") Loading @@ -76,6 +77,7 @@ public final class MediaRouter2 { private final Context mContext; private final IMediaRouterService mMediaRouterService; private final Object mLock = new Object(); private final CopyOnWriteArrayList<RouteCallbackRecord> mRouteCallbackRecords = new CopyOnWriteArrayList<>(); Loading @@ -88,27 +90,29 @@ public final class MediaRouter2 { new CopyOnWriteArrayList<>(); private final String mClientPackageName; private final ManagerCallback mManagerCallback; private final String mPackageName; @GuardedBy("sRouterLock") @GuardedBy("mLock") final Map<String, MediaRoute2Info> mRoutes = new ArrayMap<>(); final RoutingController mSystemController; @GuardedBy("sRouterLock") @GuardedBy("mLock") private RouteDiscoveryPreference mDiscoveryPreference = RouteDiscoveryPreference.EMPTY; // TODO: Make MediaRouter2 is always connected to the MediaRouterService. @GuardedBy("sRouterLock") @GuardedBy("mLock") MediaRouter2Stub mStub; @GuardedBy("sRouterLock") @GuardedBy("mLock") private final Map<String, RoutingController> mNonSystemRoutingControllers = new ArrayMap<>(); private final AtomicInteger mNextRequestId = new AtomicInteger(1); final Handler mHandler; @GuardedBy("sRouterLock") @GuardedBy("mLock") private boolean mShouldUpdateRoutes = true; private volatile List<MediaRoute2Info> mFilteredRoutes = Collections.emptyList(); private volatile OnGetControllerHintsListener mOnGetControllerHintsListener; Loading @@ -130,6 +134,11 @@ public final class MediaRouter2 { /** * Gets an instance of the media router which controls the app's media routing. * Returns {@code null} if the given package name is invalid. * <p> * Note: For media routers created with this method, the discovery preference passed to * {@link #registerRouteCallback} will have no effect. The callback will be called accordingly * with the client app's discovery preference. Therefore, it is recommended to pass * {@link RouteDiscoveryPreference#EMPTY} there. * * @param clientPackageName the package name of the app to control * @hide Loading @@ -149,15 +158,17 @@ public final class MediaRouter2 { return null; } synchronized (sRouterLock) { MediaRouter2 instance = sMediaRouter2Map.get(clientPackageName); synchronized (sSystemRouterLock) { MediaRouter2 instance = sSystemMediaRouter2Map.get(clientPackageName); if (instance == null) { // TODO: Add permission check here using MODIFY_AUDIO_ROUTING. if (sManager == null) { sManager = MediaRouter2Manager.getInstance(context.getApplicationContext()); } instance = new MediaRouter2(context, clientPackageName); sMediaRouter2Map.put(clientPackageName, instance); sSystemMediaRouter2Map.put(clientPackageName, instance); // TODO: Remove router instance once it is not needed. instance.registerManagerCallback(); } return instance; } Loading Loading @@ -192,11 +203,14 @@ public final class MediaRouter2 { } mSystemController = new SystemRoutingController(currentSystemSessionInfo); // Only used by system MediaRouter2. mClientPackageName = null; mManagerCallback = null; } private MediaRouter2(Context context, String clientPackageName) { mClientPackageName = clientPackageName; mManagerCallback = new ManagerCallback(); mContext = context; mMediaRouterService = null; mPackageName = null; Loading @@ -220,8 +234,8 @@ public final class MediaRouter2 { } /** * Gets the target package name of the app which this media router controls. * This is only non-null when the router instance is created with the target package name. * Gets the client package name of the app which this media router controls. * This is only non-null when the router instance is created with the client package name. * * @see #getInstance(Context, String) * @hide Loading @@ -245,6 +259,9 @@ public final class MediaRouter2 { Objects.requireNonNull(executor, "executor must not be null"); Objects.requireNonNull(routeCallback, "callback must not be null"); Objects.requireNonNull(preference, "preference must not be null"); if (isSystemRouter()) { preference = RouteDiscoveryPreference.EMPTY; } RouteCallbackRecord record = new RouteCallbackRecord(executor, routeCallback, preference); Loading @@ -253,7 +270,11 @@ public final class MediaRouter2 { // is happening but it's okay because either this or the other registration should be done. mRouteCallbackRecords.addIfAbsent(record); synchronized (sRouterLock) { if (isSystemRouter()) { return; } synchronized (mLock) { if (mStub == null) { MediaRouter2Stub stub = new MediaRouter2Stub(); try { Loading Loading @@ -289,7 +310,11 @@ public final class MediaRouter2 { return; } synchronized (sRouterLock) { if (isSystemRouter()) { return; } synchronized (mLock) { if (mStub == null) { return; } Loading Loading @@ -325,23 +350,38 @@ public final class MediaRouter2 { return true; } /** * Gets the list of all discovered routes. * This list includes the routes that are not related to the client app. * <p> * This will return an empty list for non-system media routers. * * @hide */ //@SystemApi public List<MediaRoute2Info> getAllRoutes() { if (isSystemRouter()) { return sManager.getAllRoutes(); } return Collections.emptyList(); } /** * Gets the unmodifiable list of {@link MediaRoute2Info routes} currently * known to the media router. * <p> * Please note that the list can be changed before callbacks are invoked. * </p> * * @return the list of routes that contains at least one of the route features in discovery * preferences registered by the application */ @NonNull public List<MediaRoute2Info> getRoutes() { if (mClientPackageName != null) { if (isSystemRouter()) { return sManager.getAvailableRoutes(mClientPackageName); } synchronized (sRouterLock) { synchronized (mLock) { if (mShouldUpdateRoutes) { mShouldUpdateRoutes = false; Loading Loading @@ -449,7 +489,7 @@ public final class MediaRouter2 { * @see TransferCallback#onTransferFailure */ public void transferTo(@NonNull MediaRoute2Info route) { if (mClientPackageName != null) { if (isSystemRouter()) { sManager.selectRoute(mClientPackageName, route); return; } Loading @@ -464,7 +504,7 @@ public final class MediaRouter2 { * controls the media routing, this method is a no-op. */ public void stop() { if (mClientPackageName != null) { if (isSystemRouter()) { List<RoutingSessionInfo> sessionInfos = sManager.getRoutingSessions(mClientPackageName); RoutingSessionInfo sessionToRelease = sessionInfos.get(sessionInfos.size() - 1); sManager.releaseSession(sessionToRelease); Loading @@ -481,7 +521,7 @@ public final class MediaRouter2 { */ //@SystemApi public void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) { if (mClientPackageName != null) { if (isSystemRouter()) { sManager.transfer(controller.getRoutingSessionInfo(), route); return; } Loading @@ -490,7 +530,7 @@ public final class MediaRouter2 { Objects.requireNonNull(route, "route must not be null"); boolean routeFound; synchronized (sRouterLock) { synchronized (mLock) { // TODO: Check thread-safety routeFound = mRoutes.containsKey(route.getId()); } Loading Loading @@ -526,7 +566,7 @@ public final class MediaRouter2 { } MediaRouter2Stub stub; synchronized (sRouterLock) { synchronized (mLock) { stub = mStub; } if (stub != null) { Loading Loading @@ -576,7 +616,7 @@ public final class MediaRouter2 { public List<RoutingController> getControllers() { // TODO: Do not create the controller instances every time, // Instead, update the list using the sessions' ID and session related callbacks. if (mClientPackageName != null) { if (isSystemRouter()) { return sManager.getRoutingSessions(mClientPackageName).stream() .map(info -> new RoutingController(info)) .collect(Collectors.toList()); Loading @@ -584,7 +624,7 @@ public final class MediaRouter2 { List<RoutingController> result = new ArrayList<>(); result.add(0, mSystemController); synchronized (sRouterLock) { synchronized (mLock) { result.addAll(mNonSystemRoutingControllers.values()); } return result; Loading @@ -603,7 +643,7 @@ public final class MediaRouter2 { Objects.requireNonNull(route, "route must not be null"); MediaRouter2Stub stub; synchronized (sRouterLock) { synchronized (mLock) { stub = mStub; } if (stub != null) { Loading @@ -627,7 +667,7 @@ public final class MediaRouter2 { List<MediaRoute2Info> removedRoutes = new ArrayList<>(); List<MediaRoute2Info> changedRoutes = new ArrayList<>(); synchronized (sRouterLock) { synchronized (mLock) { List<String> currentRoutesIds = currentRoutes.stream().map(MediaRoute2Info::getId) .collect(Collectors.toList()); Loading Loading @@ -685,7 +725,7 @@ public final class MediaRouter2 { void addRoutesOnHandler(List<MediaRoute2Info> routes) { List<MediaRoute2Info> addedRoutes = new ArrayList<>(); synchronized (sRouterLock) { synchronized (mLock) { for (MediaRoute2Info route : routes) { mRoutes.put(route.getId(), route); if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) { Loading @@ -701,7 +741,7 @@ public final class MediaRouter2 { void removeRoutesOnHandler(List<MediaRoute2Info> routes) { List<MediaRoute2Info> removedRoutes = new ArrayList<>(); synchronized (sRouterLock) { synchronized (mLock) { for (MediaRoute2Info route : routes) { mRoutes.remove(route.getId()); if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) { Loading @@ -717,7 +757,7 @@ public final class MediaRouter2 { void changeRoutesOnHandler(List<MediaRoute2Info> routes) { List<MediaRoute2Info> changedRoutes = new ArrayList<>(); synchronized (sRouterLock) { synchronized (mLock) { for (MediaRoute2Info route : routes) { mRoutes.put(route.getId(), route); if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) { Loading Loading @@ -789,7 +829,7 @@ public final class MediaRouter2 { newController.setRoutingSessionInfo(sessionInfo); } else { newController = new RoutingController(sessionInfo); synchronized (sRouterLock) { synchronized (mLock) { mNonSystemRoutingControllers.put(newController.getId(), newController); } } Loading @@ -812,7 +852,7 @@ public final class MediaRouter2 { } RoutingController matchingController; synchronized (sRouterLock) { synchronized (mLock) { matchingController = mNonSystemRoutingControllers.get(sessionInfo.getId()); } Loading Loading @@ -840,7 +880,7 @@ public final class MediaRouter2 { } RoutingController matchingController; synchronized (sRouterLock) { synchronized (mLock) { matchingController = mNonSystemRoutingControllers.get(sessionInfo.getId()); } Loading Loading @@ -868,7 +908,7 @@ public final class MediaRouter2 { if (oldSession.isSystemSession()) { controller = getSystemController(); } else { synchronized (sRouterLock) { synchronized (mLock) { controller = mNonSystemRoutingControllers.get(oldSession.getId()); } } Loading @@ -878,6 +918,22 @@ public final class MediaRouter2 { requestCreateController(controller, route, managerRequestId); } /** * Returns whether this router is created with {@link #getInstance(Context, String)}. * This kind of router can control the target app's media routing. */ private boolean isSystemRouter() { return mClientPackageName != null; } /** * Registers {@link MediaRouter2Manager.Callback} for getting events. */ private void registerManagerCallback() { // Using direct executor here, since MediaRouter2Manager also posts to the main handler. sManager.registerCallback(Runnable::run, mManagerCallback); } private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes, RouteDiscoveryPreference discoveryRequest) { return routes.stream() Loading Loading @@ -1236,7 +1292,7 @@ public final class MediaRouter2 { } MediaRouter2Stub stub; synchronized (sRouterLock) { synchronized (mLock) { stub = mStub; } if (stub != null) { Loading Loading @@ -1283,7 +1339,7 @@ public final class MediaRouter2 { } MediaRouter2Stub stub; synchronized (sRouterLock) { synchronized (mLock) { stub = mStub; } if (stub != null) { Loading Loading @@ -1318,7 +1374,7 @@ public final class MediaRouter2 { } MediaRouter2Stub stub; synchronized (sRouterLock) { synchronized (mLock) { stub = mStub; } if (stub != null) { Loading Loading @@ -1352,7 +1408,7 @@ public final class MediaRouter2 { return; } MediaRouter2Stub stub; synchronized (sRouterLock) { synchronized (mLock) { stub = mStub; } if (stub != null) { Loading Loading @@ -1386,7 +1442,7 @@ public final class MediaRouter2 { mState = CONTROLLER_STATE_RELEASING; } synchronized (sRouterLock) { synchronized (mLock) { // It could happen if the controller is released by the another thread // in between two locks if (!mNonSystemRoutingControllers.remove(getId(), this)) { Loading Loading @@ -1415,7 +1471,7 @@ public final class MediaRouter2 { mState = CONTROLLER_STATE_RELEASED; } synchronized (sRouterLock) { synchronized (mLock) { mNonSystemRoutingControllers.remove(getId(), this); if (shouldReleaseSession && mStub != null) { Loading Loading @@ -1483,7 +1539,7 @@ public final class MediaRouter2 { } private List<MediaRoute2Info> getRoutesWithIds(List<String> routeIds) { synchronized (sRouterLock) { synchronized (mLock) { return routeIds.stream().map(mRoutes::get) .filter(Objects::nonNull) .collect(Collectors.toList()); Loading Loading @@ -1665,4 +1721,79 @@ public final class MediaRouter2 { MediaRouter2.this, oldSession, route, managerRequestId)); } } class ManagerCallback implements MediaRouter2Manager.Callback { @Override public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) { List<MediaRoute2Info> filteredRoutes = sManager.filterRoutesForPackage(routes, mClientPackageName); if (filteredRoutes.isEmpty()) { return; } for (RouteCallbackRecord record: mRouteCallbackRecords) { record.mExecutor.execute( () -> record.mRouteCallback.onRoutesAdded(filteredRoutes)); } } @Override public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) { List<MediaRoute2Info> filteredRoutes = sManager.filterRoutesForPackage(routes, mClientPackageName); if (filteredRoutes.isEmpty()) { return; } for (RouteCallbackRecord record: mRouteCallbackRecords) { record.mExecutor.execute( () -> record.mRouteCallback.onRoutesRemoved(filteredRoutes)); } } @Override public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) { List<MediaRoute2Info> filteredRoutes = sManager.filterRoutesForPackage(routes, mClientPackageName); if (filteredRoutes.isEmpty()) { return; } for (RouteCallbackRecord record: mRouteCallbackRecords) { record.mExecutor.execute( () -> record.mRouteCallback.onRoutesChanged(filteredRoutes)); } } @Override public void onSessionUpdated(@NonNull RoutingSessionInfo session) { // TODO: Call ControllerCallback.onControllerUpdated } @Override public void onTransferred(@NonNull RoutingSessionInfo oldSession, @Nullable RoutingSessionInfo newSession) { // TODO: Call TransferCallback.onTransfer } @Override public void onTransferFailed(@NonNull RoutingSessionInfo session, @NonNull MediaRoute2Info route) { // TODO: Call TransferCallback.onTransferFailure } @Override public void onSessionReleased(@NonNull RoutingSessionInfo session) { // TODO: Call TransferCallback.onStop() } @Override public void onPreferredFeaturesChanged(@NonNull String packageName, @NonNull List<String> preferredFeatures) { // Does nothing. } @Override public void onRequestFailed(int reason) { // Does nothing. } } } media/java/android/media/MediaRouter2Manager.java +30 −0 Original line number Diff line number Diff line Loading @@ -236,6 +236,36 @@ public final class MediaRouter2Manager { return routes; } /** * Returns a list of routes which are related to the given package name in the given route list. */ @NonNull public List<MediaRoute2Info> filterRoutesForPackage(@NonNull List<MediaRoute2Info> routes, @NonNull String packageName) { Objects.requireNonNull(routes, "routes must not be null"); Objects.requireNonNull(packageName, "packageName must not be null"); List<RoutingSessionInfo> sessions = getRoutingSessions(packageName); RoutingSessionInfo sessionInfo = sessions.get(sessions.size() - 1); List<MediaRoute2Info> result = new ArrayList<>(); List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName); if (preferredFeatures == null) { preferredFeatures = Collections.emptyList(); } synchronized (mRoutesLock) { for (MediaRoute2Info route : routes) { if (route.hasAnyFeatures(preferredFeatures) || sessionInfo.getSelectedRoutes().contains(route.getId()) || sessionInfo.getTransferableRoutes().contains(route.getId())) { result.add(route); } } } return result; } /** * Gets the system routing session associated with no specific application. */ Loading Loading
media/java/android/media/MediaRouter2.java +171 −40 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import java.util.stream.Collectors; public final class MediaRouter2 { private static final String TAG = "MR2"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final Object sSystemRouterLock = new Object(); private static final Object sRouterLock = new Object(); // The maximum time for the old routing controller available after transfer. Loading @@ -67,8 +68,8 @@ public final class MediaRouter2 { // The manager request ID representing that no manager is involved. private static final long MANAGER_REQUEST_ID_NONE = MediaRoute2ProviderService.REQUEST_ID_NONE; @GuardedBy("sRouterLock") private static Map<String, MediaRouter2> sMediaRouter2Map = new ArrayMap<>(); @GuardedBy("sSystemRouterLock") private static Map<String, MediaRouter2> sSystemMediaRouter2Map = new ArrayMap<>(); private static MediaRouter2Manager sManager; @GuardedBy("sRouterLock") Loading @@ -76,6 +77,7 @@ public final class MediaRouter2 { private final Context mContext; private final IMediaRouterService mMediaRouterService; private final Object mLock = new Object(); private final CopyOnWriteArrayList<RouteCallbackRecord> mRouteCallbackRecords = new CopyOnWriteArrayList<>(); Loading @@ -88,27 +90,29 @@ public final class MediaRouter2 { new CopyOnWriteArrayList<>(); private final String mClientPackageName; private final ManagerCallback mManagerCallback; private final String mPackageName; @GuardedBy("sRouterLock") @GuardedBy("mLock") final Map<String, MediaRoute2Info> mRoutes = new ArrayMap<>(); final RoutingController mSystemController; @GuardedBy("sRouterLock") @GuardedBy("mLock") private RouteDiscoveryPreference mDiscoveryPreference = RouteDiscoveryPreference.EMPTY; // TODO: Make MediaRouter2 is always connected to the MediaRouterService. @GuardedBy("sRouterLock") @GuardedBy("mLock") MediaRouter2Stub mStub; @GuardedBy("sRouterLock") @GuardedBy("mLock") private final Map<String, RoutingController> mNonSystemRoutingControllers = new ArrayMap<>(); private final AtomicInteger mNextRequestId = new AtomicInteger(1); final Handler mHandler; @GuardedBy("sRouterLock") @GuardedBy("mLock") private boolean mShouldUpdateRoutes = true; private volatile List<MediaRoute2Info> mFilteredRoutes = Collections.emptyList(); private volatile OnGetControllerHintsListener mOnGetControllerHintsListener; Loading @@ -130,6 +134,11 @@ public final class MediaRouter2 { /** * Gets an instance of the media router which controls the app's media routing. * Returns {@code null} if the given package name is invalid. * <p> * Note: For media routers created with this method, the discovery preference passed to * {@link #registerRouteCallback} will have no effect. The callback will be called accordingly * with the client app's discovery preference. Therefore, it is recommended to pass * {@link RouteDiscoveryPreference#EMPTY} there. * * @param clientPackageName the package name of the app to control * @hide Loading @@ -149,15 +158,17 @@ public final class MediaRouter2 { return null; } synchronized (sRouterLock) { MediaRouter2 instance = sMediaRouter2Map.get(clientPackageName); synchronized (sSystemRouterLock) { MediaRouter2 instance = sSystemMediaRouter2Map.get(clientPackageName); if (instance == null) { // TODO: Add permission check here using MODIFY_AUDIO_ROUTING. if (sManager == null) { sManager = MediaRouter2Manager.getInstance(context.getApplicationContext()); } instance = new MediaRouter2(context, clientPackageName); sMediaRouter2Map.put(clientPackageName, instance); sSystemMediaRouter2Map.put(clientPackageName, instance); // TODO: Remove router instance once it is not needed. instance.registerManagerCallback(); } return instance; } Loading Loading @@ -192,11 +203,14 @@ public final class MediaRouter2 { } mSystemController = new SystemRoutingController(currentSystemSessionInfo); // Only used by system MediaRouter2. mClientPackageName = null; mManagerCallback = null; } private MediaRouter2(Context context, String clientPackageName) { mClientPackageName = clientPackageName; mManagerCallback = new ManagerCallback(); mContext = context; mMediaRouterService = null; mPackageName = null; Loading @@ -220,8 +234,8 @@ public final class MediaRouter2 { } /** * Gets the target package name of the app which this media router controls. * This is only non-null when the router instance is created with the target package name. * Gets the client package name of the app which this media router controls. * This is only non-null when the router instance is created with the client package name. * * @see #getInstance(Context, String) * @hide Loading @@ -245,6 +259,9 @@ public final class MediaRouter2 { Objects.requireNonNull(executor, "executor must not be null"); Objects.requireNonNull(routeCallback, "callback must not be null"); Objects.requireNonNull(preference, "preference must not be null"); if (isSystemRouter()) { preference = RouteDiscoveryPreference.EMPTY; } RouteCallbackRecord record = new RouteCallbackRecord(executor, routeCallback, preference); Loading @@ -253,7 +270,11 @@ public final class MediaRouter2 { // is happening but it's okay because either this or the other registration should be done. mRouteCallbackRecords.addIfAbsent(record); synchronized (sRouterLock) { if (isSystemRouter()) { return; } synchronized (mLock) { if (mStub == null) { MediaRouter2Stub stub = new MediaRouter2Stub(); try { Loading Loading @@ -289,7 +310,11 @@ public final class MediaRouter2 { return; } synchronized (sRouterLock) { if (isSystemRouter()) { return; } synchronized (mLock) { if (mStub == null) { return; } Loading Loading @@ -325,23 +350,38 @@ public final class MediaRouter2 { return true; } /** * Gets the list of all discovered routes. * This list includes the routes that are not related to the client app. * <p> * This will return an empty list for non-system media routers. * * @hide */ //@SystemApi public List<MediaRoute2Info> getAllRoutes() { if (isSystemRouter()) { return sManager.getAllRoutes(); } return Collections.emptyList(); } /** * Gets the unmodifiable list of {@link MediaRoute2Info routes} currently * known to the media router. * <p> * Please note that the list can be changed before callbacks are invoked. * </p> * * @return the list of routes that contains at least one of the route features in discovery * preferences registered by the application */ @NonNull public List<MediaRoute2Info> getRoutes() { if (mClientPackageName != null) { if (isSystemRouter()) { return sManager.getAvailableRoutes(mClientPackageName); } synchronized (sRouterLock) { synchronized (mLock) { if (mShouldUpdateRoutes) { mShouldUpdateRoutes = false; Loading Loading @@ -449,7 +489,7 @@ public final class MediaRouter2 { * @see TransferCallback#onTransferFailure */ public void transferTo(@NonNull MediaRoute2Info route) { if (mClientPackageName != null) { if (isSystemRouter()) { sManager.selectRoute(mClientPackageName, route); return; } Loading @@ -464,7 +504,7 @@ public final class MediaRouter2 { * controls the media routing, this method is a no-op. */ public void stop() { if (mClientPackageName != null) { if (isSystemRouter()) { List<RoutingSessionInfo> sessionInfos = sManager.getRoutingSessions(mClientPackageName); RoutingSessionInfo sessionToRelease = sessionInfos.get(sessionInfos.size() - 1); sManager.releaseSession(sessionToRelease); Loading @@ -481,7 +521,7 @@ public final class MediaRouter2 { */ //@SystemApi public void transfer(@NonNull RoutingController controller, @NonNull MediaRoute2Info route) { if (mClientPackageName != null) { if (isSystemRouter()) { sManager.transfer(controller.getRoutingSessionInfo(), route); return; } Loading @@ -490,7 +530,7 @@ public final class MediaRouter2 { Objects.requireNonNull(route, "route must not be null"); boolean routeFound; synchronized (sRouterLock) { synchronized (mLock) { // TODO: Check thread-safety routeFound = mRoutes.containsKey(route.getId()); } Loading Loading @@ -526,7 +566,7 @@ public final class MediaRouter2 { } MediaRouter2Stub stub; synchronized (sRouterLock) { synchronized (mLock) { stub = mStub; } if (stub != null) { Loading Loading @@ -576,7 +616,7 @@ public final class MediaRouter2 { public List<RoutingController> getControllers() { // TODO: Do not create the controller instances every time, // Instead, update the list using the sessions' ID and session related callbacks. if (mClientPackageName != null) { if (isSystemRouter()) { return sManager.getRoutingSessions(mClientPackageName).stream() .map(info -> new RoutingController(info)) .collect(Collectors.toList()); Loading @@ -584,7 +624,7 @@ public final class MediaRouter2 { List<RoutingController> result = new ArrayList<>(); result.add(0, mSystemController); synchronized (sRouterLock) { synchronized (mLock) { result.addAll(mNonSystemRoutingControllers.values()); } return result; Loading @@ -603,7 +643,7 @@ public final class MediaRouter2 { Objects.requireNonNull(route, "route must not be null"); MediaRouter2Stub stub; synchronized (sRouterLock) { synchronized (mLock) { stub = mStub; } if (stub != null) { Loading @@ -627,7 +667,7 @@ public final class MediaRouter2 { List<MediaRoute2Info> removedRoutes = new ArrayList<>(); List<MediaRoute2Info> changedRoutes = new ArrayList<>(); synchronized (sRouterLock) { synchronized (mLock) { List<String> currentRoutesIds = currentRoutes.stream().map(MediaRoute2Info::getId) .collect(Collectors.toList()); Loading Loading @@ -685,7 +725,7 @@ public final class MediaRouter2 { void addRoutesOnHandler(List<MediaRoute2Info> routes) { List<MediaRoute2Info> addedRoutes = new ArrayList<>(); synchronized (sRouterLock) { synchronized (mLock) { for (MediaRoute2Info route : routes) { mRoutes.put(route.getId(), route); if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) { Loading @@ -701,7 +741,7 @@ public final class MediaRouter2 { void removeRoutesOnHandler(List<MediaRoute2Info> routes) { List<MediaRoute2Info> removedRoutes = new ArrayList<>(); synchronized (sRouterLock) { synchronized (mLock) { for (MediaRoute2Info route : routes) { mRoutes.remove(route.getId()); if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) { Loading @@ -717,7 +757,7 @@ public final class MediaRouter2 { void changeRoutesOnHandler(List<MediaRoute2Info> routes) { List<MediaRoute2Info> changedRoutes = new ArrayList<>(); synchronized (sRouterLock) { synchronized (mLock) { for (MediaRoute2Info route : routes) { mRoutes.put(route.getId(), route); if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) { Loading Loading @@ -789,7 +829,7 @@ public final class MediaRouter2 { newController.setRoutingSessionInfo(sessionInfo); } else { newController = new RoutingController(sessionInfo); synchronized (sRouterLock) { synchronized (mLock) { mNonSystemRoutingControllers.put(newController.getId(), newController); } } Loading @@ -812,7 +852,7 @@ public final class MediaRouter2 { } RoutingController matchingController; synchronized (sRouterLock) { synchronized (mLock) { matchingController = mNonSystemRoutingControllers.get(sessionInfo.getId()); } Loading Loading @@ -840,7 +880,7 @@ public final class MediaRouter2 { } RoutingController matchingController; synchronized (sRouterLock) { synchronized (mLock) { matchingController = mNonSystemRoutingControllers.get(sessionInfo.getId()); } Loading Loading @@ -868,7 +908,7 @@ public final class MediaRouter2 { if (oldSession.isSystemSession()) { controller = getSystemController(); } else { synchronized (sRouterLock) { synchronized (mLock) { controller = mNonSystemRoutingControllers.get(oldSession.getId()); } } Loading @@ -878,6 +918,22 @@ public final class MediaRouter2 { requestCreateController(controller, route, managerRequestId); } /** * Returns whether this router is created with {@link #getInstance(Context, String)}. * This kind of router can control the target app's media routing. */ private boolean isSystemRouter() { return mClientPackageName != null; } /** * Registers {@link MediaRouter2Manager.Callback} for getting events. */ private void registerManagerCallback() { // Using direct executor here, since MediaRouter2Manager also posts to the main handler. sManager.registerCallback(Runnable::run, mManagerCallback); } private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes, RouteDiscoveryPreference discoveryRequest) { return routes.stream() Loading Loading @@ -1236,7 +1292,7 @@ public final class MediaRouter2 { } MediaRouter2Stub stub; synchronized (sRouterLock) { synchronized (mLock) { stub = mStub; } if (stub != null) { Loading Loading @@ -1283,7 +1339,7 @@ public final class MediaRouter2 { } MediaRouter2Stub stub; synchronized (sRouterLock) { synchronized (mLock) { stub = mStub; } if (stub != null) { Loading Loading @@ -1318,7 +1374,7 @@ public final class MediaRouter2 { } MediaRouter2Stub stub; synchronized (sRouterLock) { synchronized (mLock) { stub = mStub; } if (stub != null) { Loading Loading @@ -1352,7 +1408,7 @@ public final class MediaRouter2 { return; } MediaRouter2Stub stub; synchronized (sRouterLock) { synchronized (mLock) { stub = mStub; } if (stub != null) { Loading Loading @@ -1386,7 +1442,7 @@ public final class MediaRouter2 { mState = CONTROLLER_STATE_RELEASING; } synchronized (sRouterLock) { synchronized (mLock) { // It could happen if the controller is released by the another thread // in between two locks if (!mNonSystemRoutingControllers.remove(getId(), this)) { Loading Loading @@ -1415,7 +1471,7 @@ public final class MediaRouter2 { mState = CONTROLLER_STATE_RELEASED; } synchronized (sRouterLock) { synchronized (mLock) { mNonSystemRoutingControllers.remove(getId(), this); if (shouldReleaseSession && mStub != null) { Loading Loading @@ -1483,7 +1539,7 @@ public final class MediaRouter2 { } private List<MediaRoute2Info> getRoutesWithIds(List<String> routeIds) { synchronized (sRouterLock) { synchronized (mLock) { return routeIds.stream().map(mRoutes::get) .filter(Objects::nonNull) .collect(Collectors.toList()); Loading Loading @@ -1665,4 +1721,79 @@ public final class MediaRouter2 { MediaRouter2.this, oldSession, route, managerRequestId)); } } class ManagerCallback implements MediaRouter2Manager.Callback { @Override public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) { List<MediaRoute2Info> filteredRoutes = sManager.filterRoutesForPackage(routes, mClientPackageName); if (filteredRoutes.isEmpty()) { return; } for (RouteCallbackRecord record: mRouteCallbackRecords) { record.mExecutor.execute( () -> record.mRouteCallback.onRoutesAdded(filteredRoutes)); } } @Override public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) { List<MediaRoute2Info> filteredRoutes = sManager.filterRoutesForPackage(routes, mClientPackageName); if (filteredRoutes.isEmpty()) { return; } for (RouteCallbackRecord record: mRouteCallbackRecords) { record.mExecutor.execute( () -> record.mRouteCallback.onRoutesRemoved(filteredRoutes)); } } @Override public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) { List<MediaRoute2Info> filteredRoutes = sManager.filterRoutesForPackage(routes, mClientPackageName); if (filteredRoutes.isEmpty()) { return; } for (RouteCallbackRecord record: mRouteCallbackRecords) { record.mExecutor.execute( () -> record.mRouteCallback.onRoutesChanged(filteredRoutes)); } } @Override public void onSessionUpdated(@NonNull RoutingSessionInfo session) { // TODO: Call ControllerCallback.onControllerUpdated } @Override public void onTransferred(@NonNull RoutingSessionInfo oldSession, @Nullable RoutingSessionInfo newSession) { // TODO: Call TransferCallback.onTransfer } @Override public void onTransferFailed(@NonNull RoutingSessionInfo session, @NonNull MediaRoute2Info route) { // TODO: Call TransferCallback.onTransferFailure } @Override public void onSessionReleased(@NonNull RoutingSessionInfo session) { // TODO: Call TransferCallback.onStop() } @Override public void onPreferredFeaturesChanged(@NonNull String packageName, @NonNull List<String> preferredFeatures) { // Does nothing. } @Override public void onRequestFailed(int reason) { // Does nothing. } } }
media/java/android/media/MediaRouter2Manager.java +30 −0 Original line number Diff line number Diff line Loading @@ -236,6 +236,36 @@ public final class MediaRouter2Manager { return routes; } /** * Returns a list of routes which are related to the given package name in the given route list. */ @NonNull public List<MediaRoute2Info> filterRoutesForPackage(@NonNull List<MediaRoute2Info> routes, @NonNull String packageName) { Objects.requireNonNull(routes, "routes must not be null"); Objects.requireNonNull(packageName, "packageName must not be null"); List<RoutingSessionInfo> sessions = getRoutingSessions(packageName); RoutingSessionInfo sessionInfo = sessions.get(sessions.size() - 1); List<MediaRoute2Info> result = new ArrayList<>(); List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName); if (preferredFeatures == null) { preferredFeatures = Collections.emptyList(); } synchronized (mRoutesLock) { for (MediaRoute2Info route : routes) { if (route.hasAnyFeatures(preferredFeatures) || sessionInfo.getSelectedRoutes().contains(route.getId()) || sessionInfo.getTransferableRoutes().contains(route.getId())) { result.add(route); } } } return result; } /** * Gets the system routing session associated with no specific application. */ Loading