From f459bec188031e11a103f5a1ad4d64091583b139 Mon Sep 17 00:00:00 2001 From: Kyunglyul Hyun Date: Fri, 18 Feb 2022 23:19:14 +0900 Subject: [PATCH] Mediarouter: clean up callbacks According to change in RouteDiscoveryPreference, this CL corrects MediaRouter2.RouteCallback callbacks Bug: 193631822 Test: atest MediaRouter2Test MediaRouter2HostTest Change-Id: Ie1190c52d325d1122e4cca571d1d26b1f8eb175a --- media/java/android/media/MediaRouter2.java | 912 +++++++++++---------- 1 file changed, 470 insertions(+), 442 deletions(-) diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 311476cbd489c..47e402f73f401 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -76,6 +76,7 @@ public final class MediaRouter2 { @GuardedBy("sSystemRouterLock") private static Map sSystemMediaRouter2Map = new ArrayMap<>(); + private static MediaRouter2Manager sManager; @GuardedBy("sRouterLock") @@ -119,14 +120,12 @@ public final class MediaRouter2 { private final AtomicInteger mNextRequestId = new AtomicInteger(1); final Handler mHandler; - @GuardedBy("mLock") - private boolean mShouldUpdateRoutes = true; + + private volatile ArrayMap mPreviousRoutes = new ArrayMap<>(); private volatile List mFilteredRoutes = Collections.emptyList(); private volatile OnGetControllerHintsListener mOnGetControllerHintsListener; - /** - * Gets an instance of the media router associated with the context. - */ + /** Gets an instance of the media router associated with the context. */ @NonNull public static MediaRouter2 getInstance(@NonNull Context context) { Objects.requireNonNull(context, "context must not be null"); @@ -139,29 +138,31 @@ public final class MediaRouter2 { } /** - * Gets an instance of the system media router which controls the app's media routing. - * Returns {@code null} if the given package name is invalid. - * There are several things to note when using the media routers created with this method. - *

- * First of all, 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. - *

- * Also, do not keep/compare the instances of the {@link RoutingController}, since they are + * Gets an instance of the system media router which controls the app's media routing. Returns + * {@code null} if the given package name is invalid. There are several things to note when + * using the media routers created with this method. + * + *

First of all, 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. + * + *

Also, do not keep/compare the instances of the {@link RoutingController}, since they are * always newly created with the latest session information whenever below methods are called: + * *

    - *
  • {@link #getControllers()}
  • - *
  • {@link #getController(String)}}
  • - *
  • {@link TransferCallback#onTransfer(RoutingController, RoutingController)}
  • - *
  • {@link TransferCallback#onStop(RoutingController)}
  • - *
  • {@link ControllerCallback#onControllerUpdated(RoutingController)}
  • + *
  • {@link #getControllers()} + *
  • {@link #getController(String)}} + *
  • {@link TransferCallback#onTransfer(RoutingController, RoutingController)} + *
  • {@link TransferCallback#onStop(RoutingController)} + *
  • {@link ControllerCallback#onControllerUpdated(RoutingController)} *
+ * * Therefore, in order to track the current routing status, keep the controller's ID instead, - * and use {@link #getController(String)} and {@link #getSystemController()} for - * getting controllers. - *

- * Finally, it will have no effect to call {@link #setOnGetControllerHintsListener}. + * and use {@link #getController(String)} and {@link #getSystemController()} for getting + * controllers. + * + *

Finally, it will have no effect to call {@link #setOnGetControllerHintsListener}. * * @param clientPackageName the package name of the app to control * @throws SecurityException if the caller doesn't have MODIFY_AUDIO_ROUTING permission. @@ -170,15 +171,16 @@ public final class MediaRouter2 { @SystemApi @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) @Nullable - public static MediaRouter2 getInstance(@NonNull Context context, - @NonNull String clientPackageName) { + public static MediaRouter2 getInstance( + @NonNull Context context, @NonNull String clientPackageName) { Objects.requireNonNull(context, "context must not be null"); Objects.requireNonNull(clientPackageName, "clientPackageName must not be null"); // Note: Even though this check could be somehow bypassed, the other permission checks // in system server will not allow MediaRouter2Manager to be registered. - IMediaRouterService serviceBinder = IMediaRouterService.Stub.asInterface( - ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE)); + IMediaRouterService serviceBinder = + IMediaRouterService.Stub.asInterface( + ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE)); try { // SecurityException will be thrown if there's no permission. serviceBinder.enforceMediaContentControlPermission(); @@ -212,17 +214,17 @@ public final class MediaRouter2 { /** * Starts scanning remote routes. - *

- * Route discovery can happen even when the {@link #startScan()} is not called. - * This is because the scanning could be started before by other apps. - * Therefore, calling this method after calling {@link #stopScan()} does not necessarily mean - * that the routes found before are removed and added again. - *

- * Use {@link RouteCallback} to get the route related events. - *

- * Note that calling start/stopScan is applied to all system routers in the same process. - *

- * This will be no-op for non-system media routers. + * + *

Route discovery can happen even when the {@link #startScan()} is not called. This is + * because the scanning could be started before by other apps. Therefore, calling this method + * after calling {@link #stopScan()} does not necessarily mean that the routes found before are + * removed and added again. + * + *

Use {@link RouteCallback} to get the route related events. + * + *

Note that calling start/stopScan is applied to all system routers in the same process. + * + *

This will be no-op for non-system media routers. * * @see #stopScan() * @see #getInstance(Context, String) @@ -238,18 +240,17 @@ public final class MediaRouter2 { /** * Stops scanning remote routes to reduce resource consumption. - *

- * Route discovery can be continued even after this method is called. - * This is because the scanning is only turned off when all the apps stop scanning. - * Therefore, calling this method does not necessarily mean the routes are removed. - * Also, for the same reason it does not mean that {@link RouteCallback#onRoutesAdded(List)} - * is not called afterwards. - *

- * Use {@link RouteCallback} to get the route related events. - *

- * Note that calling start/stopScan is applied to all system routers in the same process. - *

- * This will be no-op for non-system media routers. + * + *

Route discovery can be continued even after this method is called. This is because the + * scanning is only turned off when all the apps stop scanning. Therefore, calling this method + * does not necessarily mean the routes are removed. Also, for the same reason it does not mean + * that {@link RouteCallback#onRoutesAdded(List)} is not called afterwards. + * + *

Use {@link RouteCallback} to get the route related events. + * + *

Note that calling start/stopScan is applied to all system routers in the same process. + * + *

This will be no-op for non-system media routers. * * @see #startScan() * @see #getInstance(Context, String) @@ -265,8 +266,9 @@ public final class MediaRouter2 { private MediaRouter2(Context appContext) { mContext = appContext; - mMediaRouterService = IMediaRouterService.Stub.asInterface( - ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE)); + mMediaRouterService = + IMediaRouterService.Stub.asInterface( + ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE)); mPackageName = mContext.getPackageName(); mHandler = new Handler(Looper.getMainLooper()); @@ -302,9 +304,10 @@ public final class MediaRouter2 { mClientPackageName = clientPackageName; mManagerCallback = new ManagerCallback(); mHandler = new Handler(Looper.getMainLooper()); - mSystemController = new SystemRoutingController( - ensureClientPackageNameForSystemSession( - sManager.getSystemRoutingSession(clientPackageName))); + mSystemController = + new SystemRoutingController( + ensureClientPackageNameForSystemSession( + sManager.getSystemRoutingSession(clientPackageName))); mDiscoveryPreference = sManager.getDiscoveryPreference(clientPackageName); updateAllRoutesFromManager(); @@ -318,8 +321,8 @@ public final class MediaRouter2 { * * @hide */ - static boolean checkRouteListContainsRouteId(@NonNull List routeList, - @NonNull String routeId) { + static boolean checkRouteListContainsRouteId( + @NonNull List routeList, @NonNull String routeId) { for (MediaRoute2Info info : routeList) { if (TextUtils.equals(routeId, info.getId())) { return true; @@ -330,8 +333,8 @@ public final class MediaRouter2 { /** * Gets the client package name of the app which this media router controls. - *

- * This will return null for non-system media routers. + * + *

This will return null for non-system media routers. * * @see #getInstance(Context, String) * @hide @@ -344,12 +347,12 @@ public final class MediaRouter2 { /** * Registers a callback to discover routes and to receive events when they change. - *

- * If the specified callback is already registered, its registration will be updated for the + * + *

If the specified callback is already registered, its registration will be updated for the * given {@link Executor executor} and {@link RouteDiscoveryPreference discovery preference}. - *

*/ - public void registerRouteCallback(@NonNull @CallbackExecutor Executor executor, + public void registerRouteCallback( + @NonNull @CallbackExecutor Executor executor, @NonNull RouteCallback routeCallback, @NonNull RouteDiscoveryPreference preference) { Objects.requireNonNull(executor, "executor must not be null"); @@ -391,8 +394,8 @@ public final class MediaRouter2 { } /** - * Unregisters the given callback. The callback will no longer receive events. - * If the callback has not been added or been removed already, it is ignored. + * Unregisters the given callback. The callback will no longer receive events. If the callback + * has not been added or been removed already, it is ignored. * * @param routeCallback the callback to unregister * @see #registerRouteCallback @@ -400,8 +403,7 @@ public final class MediaRouter2 { public void unregisterRouteCallback(@NonNull RouteCallback routeCallback) { Objects.requireNonNull(routeCallback, "callback must not be null"); - if (!mRouteCallbackRecords.remove( - new RouteCallbackRecord(null, routeCallback, null))) { + if (!mRouteCallbackRecords.remove(new RouteCallbackRecord(null, routeCallback, null))) { Log.w(TAG, "unregisterRouteCallback: Ignoring unknown callback"); return; } @@ -416,8 +418,7 @@ public final class MediaRouter2 { } if (updateDiscoveryPreferenceIfNeededLocked()) { try { - mMediaRouterService.setDiscoveryRequestWithRouter2( - mStub, mDiscoveryPreference); + mMediaRouterService.setDiscoveryRequestWithRouter2(mStub, mDiscoveryPreference); } catch (RemoteException ex) { Log.e(TAG, "unregisterRouteCallback: Unable to set discovery request.", ex); } @@ -430,27 +431,28 @@ public final class MediaRouter2 { } mStub = null; } - mShouldUpdateRoutes = true; } } + @GuardedBy("mLock") private boolean updateDiscoveryPreferenceIfNeededLocked() { RouteDiscoveryPreference newDiscoveryPreference = new RouteDiscoveryPreference.Builder( mRouteCallbackRecords.stream().map(record -> record.mPreference).collect( Collectors.toList())).build(); + if (Objects.equals(mDiscoveryPreference, newDiscoveryPreference)) { return false; } mDiscoveryPreference = newDiscoveryPreference; - mShouldUpdateRoutes = true; + updateFilteredRoutesLocked(); return true; } /** - * Gets the list of all discovered routes. - * This list includes the routes that are not related to the client app. - *

- * This will return an empty list for non-system media routers. + * Gets the list of all discovered routes. This list includes the routes that are not related to + * the client app. + * + *

This will return an empty list for non-system media routers. * * @hide */ @@ -464,25 +466,19 @@ public final class MediaRouter2 { } /** - * Gets the unmodifiable list of {@link MediaRoute2Info routes} currently - * known to the media router. - *

- * Please note that the list can be changed before callbacks are invoked. - *

+ * Gets the unmodifiable list of {@link MediaRoute2Info routes} currently known to the media + * router. + * + *

Please note that the list can be changed before callbacks are invoked. + * * @return the list of routes that contains at least one of the route features in discovery - * preferences registered by the application + * preferences registered by the application */ @NonNull public List getRoutes() { synchronized (mLock) { - if (mShouldUpdateRoutes) { - mShouldUpdateRoutes = false; - - mFilteredRoutes = Collections.unmodifiableList( - filterRoutes(List.copyOf(mRoutes.values()), mDiscoveryPreference)); - } + return mFilteredRoutes; } - return mFilteredRoutes; } /** @@ -493,8 +489,8 @@ public final class MediaRouter2 { * @param callback the callback to register * @see #unregisterTransferCallback */ - public void registerTransferCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull TransferCallback callback) { + public void registerTransferCallback( + @NonNull @CallbackExecutor Executor executor, @NonNull TransferCallback callback) { Objects.requireNonNull(executor, "executor must not be null"); Objects.requireNonNull(callback, "callback must not be null"); @@ -522,12 +518,13 @@ public final class MediaRouter2 { } /** - * Registers a {@link ControllerCallback}. - * If you register the same callback twice or more, it will be ignored. + * Registers a {@link ControllerCallback}. If you register the same callback twice or more, it + * will be ignored. + * * @see #unregisterControllerCallback(ControllerCallback) */ - public void registerControllerCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull ControllerCallback callback) { + public void registerControllerCallback( + @NonNull @CallbackExecutor Executor executor, @NonNull ControllerCallback callback) { Objects.requireNonNull(executor, "executor must not be null"); Objects.requireNonNull(callback, "callback must not be null"); @@ -539,12 +536,12 @@ public final class MediaRouter2 { } /** - * Unregisters a {@link ControllerCallback}. The callback will no longer receive - * events. If the callback has not been added or been removed already, it is ignored. + * Unregisters a {@link ControllerCallback}. The callback will no longer receive events. + * If the callback has not been added or been removed already, it is ignored. + * * @see #registerControllerCallback(Executor, ControllerCallback) */ - public void unregisterControllerCallback( - @NonNull ControllerCallback callback) { + public void unregisterControllerCallback(@NonNull ControllerCallback callback) { Objects.requireNonNull(callback, "callback must not be null"); if (!mControllerCallbackRecords.remove(new ControllerCallbackRecord(null, callback))) { @@ -559,7 +556,7 @@ public final class MediaRouter2 { * {@link #transferTo(MediaRoute2Info)}. * * @param listener A listener to send optional app-specific hints when creating a controller. - * {@code null} for unset. + * {@code null} for unset. */ public void setOnGetControllerHintsListener(@Nullable OnGetControllerHintsListener listener) { if (isSystemRouter()) { @@ -569,13 +566,11 @@ public final class MediaRouter2 { } /** - * Transfers the current media to the given route. - * If it's necessary a new {@link RoutingController} is created or it is handled within - * the current routing controller. + * Transfers the current media to the given route. If it's necessary a new + * {@link RoutingController} is created or it is handled within the current routing controller. * * @param route the route you want to transfer the current media to. Pass {@code null} to * stop routing of the current media. - * * @see TransferCallback#onTransfer * @see TransferCallback#onTransferFailure */ @@ -622,8 +617,8 @@ public final class MediaRouter2 { /** * Transfers the media of a routing controller to the given route. - *

- * This will be no-op for non-system media routers. + * + *

This will be no-op for non-system media routers. * * @param controller a routing controller controlling media routing. * @param route the route you want to transfer the media to. @@ -638,13 +633,15 @@ public final class MediaRouter2 { } } - void requestCreateController(@NonNull RoutingController controller, - @NonNull MediaRoute2Info route, long managerRequestId) { + void requestCreateController( + @NonNull RoutingController controller, + @NonNull MediaRoute2Info route, + long managerRequestId) { final int requestId = mNextRequestId.getAndIncrement(); - ControllerCreationRequest request = new ControllerCreationRequest(requestId, - managerRequestId, route, controller); + ControllerCreationRequest request = + new ControllerCreationRequest(requestId, managerRequestId, route, controller); mControllerCreationRequests.add(request); OnGetControllerHintsListener listener = mOnGetControllerHintsListener; @@ -663,11 +660,15 @@ public final class MediaRouter2 { if (stub != null) { try { mMediaRouterService.requestCreateSessionWithRouter2( - stub, requestId, managerRequestId, - controller.getRoutingSessionInfo(), route, controllerHints); + stub, + requestId, + managerRequestId, + controller.getRoutingSessionInfo(), + route, + controllerHints); } catch (RemoteException ex) { Log.e(TAG, "createControllerForTransfer: " - + "Failed to request for creating a controller.", ex); + + "Failed to request for creating a controller.", ex); mControllerCreationRequests.remove(request); if (managerRequestId == MANAGER_REQUEST_ID_NONE) { notifyTransferFailure(route); @@ -685,11 +686,11 @@ public final class MediaRouter2 { /** * Gets a {@link RoutingController} which can control the routes provided by system. * e.g. Phone speaker, wired headset, Bluetooth, etc. - *

- * Note: The system controller can't be released. Calling {@link RoutingController#release()} + * + *

Note: The system controller can't be released. Calling {@link RoutingController#release()} * will be ignored. - *

- * This method always returns the same instance. + * + *

This method always returns the same instance. */ @NonNull public RoutingController getSystemController() { @@ -714,8 +715,8 @@ public final class MediaRouter2 { /** * Gets the list of currently active {@link RoutingController routing controllers} on which * media can be played. - *

- * Note: The list returned here will never be empty. The first element in the list is + * + *

Note: The list returned here will never be empty. The first element in the list is * always the {@link #getSystemController() system controller}. */ @NonNull @@ -750,8 +751,8 @@ public final class MediaRouter2 { /** * Requests a volume change for the route asynchronously. * It may have no effect if the route is currently not selected. - *

- * This will be no-op for non-system media routers. + * + *

This will be no-op for non-system media routers. * * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}. * @see #getInstance(Context, String) @@ -769,65 +770,20 @@ public final class MediaRouter2 { // If this API needs to be public, use IMediaRouterService#setRouteVolumeWithRouter2() } - void syncRoutesOnHandler(List currentRoutes, - RoutingSessionInfo currentSystemSessionInfo) { + void syncRoutesOnHandler( + List currentRoutes, RoutingSessionInfo currentSystemSessionInfo) { if (currentRoutes == null || currentRoutes.isEmpty() || currentSystemSessionInfo == null) { Log.e(TAG, "syncRoutesOnHandler: Received wrong data. currentRoutes=" + currentRoutes + ", currentSystemSessionInfo=" + currentSystemSessionInfo); return; } - List addedRoutes = new ArrayList<>(); - List removedRoutes = new ArrayList<>(); - List changedRoutes = new ArrayList<>(); - synchronized (mLock) { - List currentRoutesIds = currentRoutes.stream().map(MediaRoute2Info::getId) - .collect(Collectors.toList()); - - for (String routeId : mRoutes.keySet()) { - if (!currentRoutesIds.contains(routeId)) { - // This route is removed while the callback is unregistered. - MediaRoute2Info route = mRoutes.get(routeId); - if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) { - removedRoutes.add(mRoutes.get(routeId)); - } - } - } - - for (MediaRoute2Info route : currentRoutes) { - if (mRoutes.containsKey(route.getId())) { - if (!route.equals(mRoutes.get(route.getId()))) { - // This route is changed while the callback is unregistered. - if (route.hasAnyFeatures( - mDiscoveryPreference.getPreferredFeatures())) { - changedRoutes.add(route); - } - } - } else { - // This route is added while the callback is unregistered. - if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) { - addedRoutes.add(route); - } - } - } - mRoutes.clear(); for (MediaRoute2Info route : currentRoutes) { mRoutes.put(route.getId(), route); } - - mShouldUpdateRoutes = true; - } - - if (!addedRoutes.isEmpty()) { - notifyRoutesAdded(addedRoutes); - } - if (!removedRoutes.isEmpty()) { - notifyRoutesRemoved(removedRoutes); - } - if (!changedRoutes.isEmpty()) { - notifyRoutesChanged(changedRoutes); + updateFilteredRoutesLocked(); } RoutingSessionInfo oldInfo = mSystemController.getRoutingSessionInfo(); @@ -837,35 +793,66 @@ public final class MediaRouter2 { } } - void addRoutesOnHandler(List routes) { + void dispatchFilteredRoutesChangedLocked(List newRoutes) { List addedRoutes = new ArrayList<>(); + List removedRoutes = new ArrayList<>(); + List changedRoutes = new ArrayList<>(); + + Set newRouteIds = + newRoutes.stream().map(MediaRoute2Info::getId).collect(Collectors.toSet()); + + for (MediaRoute2Info route : newRoutes) { + MediaRoute2Info prevRoute = mPreviousRoutes.get(route.getId()); + if (prevRoute == null) { + addedRoutes.add(route); + } else if (!prevRoute.equals(route)) { + changedRoutes.add(route); + } + } + + for (int i = 0; i < mPreviousRoutes.size(); i++) { + if (!newRouteIds.contains(mPreviousRoutes.keyAt(i))) { + removedRoutes.add(mPreviousRoutes.valueAt(i)); + } + } + + // update previous routes + for (MediaRoute2Info route : removedRoutes) { + mPreviousRoutes.remove(route.getId()); + } + for (MediaRoute2Info route : addedRoutes) { + mPreviousRoutes.put(route.getId(), route); + } + for (MediaRoute2Info route : changedRoutes) { + mPreviousRoutes.put(route.getId(), route); + } + + if (!addedRoutes.isEmpty()) { + notifyRoutesAdded(addedRoutes); + } + if (!removedRoutes.isEmpty()) { + notifyRoutesRemoved(removedRoutes); + } + if (!changedRoutes.isEmpty()) { + notifyRoutesChanged(changedRoutes); + } + } + + void addRoutesOnHandler(List routes) { synchronized (mLock) { for (MediaRoute2Info route : routes) { mRoutes.put(route.getId(), route); - if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) { - addedRoutes.add(route); - } } - mShouldUpdateRoutes = true; - } - if (!addedRoutes.isEmpty()) { - notifyRoutesAdded(addedRoutes); + updateFilteredRoutesLocked(); } } void removeRoutesOnHandler(List routes) { - List removedRoutes = new ArrayList<>(); synchronized (mLock) { for (MediaRoute2Info route : routes) { mRoutes.remove(route.getId()); - if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) { - removedRoutes.add(route); - } } - mShouldUpdateRoutes = true; - } - if (!removedRoutes.isEmpty()) { - notifyRoutesRemoved(removedRoutes); + updateFilteredRoutesLocked(); } } @@ -874,23 +861,27 @@ public final class MediaRouter2 { synchronized (mLock) { for (MediaRoute2Info route : routes) { mRoutes.put(route.getId(), route); - if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) { - changedRoutes.add(route); - } } - mShouldUpdateRoutes = true; - } - if (!changedRoutes.isEmpty()) { - notifyRoutesChanged(changedRoutes); + updateFilteredRoutesLocked(); } } + /** Updates filtered routes and dispatch callbacks */ + @GuardedBy("mLock") + void updateFilteredRoutesLocked() { + mFilteredRoutes = + Collections.unmodifiableList( + filterRoutesWithCompositePreferenceLocked(List.copyOf(mRoutes.values()))); + mHandler.sendMessage( + obtainMessage(MediaRouter2::dispatchFilteredRoutesChangedLocked, + this, mFilteredRoutes)); + } + /** - * Creates a controller and calls the {@link TransferCallback#onTransfer}. - * If the controller creation has failed, then it calls - * {@link TransferCallback#onTransferFailure}. - *

- * Pass {@code null} to sessionInfo for the failure case. + * Creates a controller and calls the {@link TransferCallback#onTransfer}. If the controller + * creation has failed, then it calls {@link TransferCallback#onTransferFailure}. + * + *

Pass {@code null} to sessionInfo for the failure case. */ void createControllerOnHandler(int requestId, @Nullable RoutingSessionInfo sessionInfo) { ControllerCreationRequest matchingRequest = null; @@ -913,12 +904,15 @@ public final class MediaRouter2 { if (sessionInfo == null) { notifyTransferFailure(requestedRoute); return; - } else if (!TextUtils.equals(requestedRoute.getProviderId(), - sessionInfo.getProviderId())) { - Log.w(TAG, "The session's provider ID does not match the requested route's. " - + "(requested route's providerId=" + requestedRoute.getProviderId() - + ", actual providerId=" + sessionInfo.getProviderId() - + ")"); + } else if (!TextUtils.equals(requestedRoute.getProviderId(), sessionInfo.getProviderId())) { + Log.w( + TAG, + "The session's provider ID does not match the requested route's. " + + "(requested route's providerId=" + + requestedRoute.getProviderId() + + ", actual providerId=" + + sessionInfo.getProviderId() + + ")"); notifyTransferFailure(requestedRoute); return; } @@ -927,9 +921,12 @@ public final class MediaRouter2 { // When the old controller is released before transferred, treat it as a failure. // This could also happen when transfer is requested twice or more. if (!oldController.scheduleRelease()) { - Log.w(TAG, "createControllerOnHandler: " - + "Ignoring controller creation for released old controller. " - + "oldController=" + oldController); + Log.w( + TAG, + "createControllerOnHandler: " + + "Ignoring controller creation for released old controller. " + + "oldController=" + + oldController); if (!sessionInfo.isSystemSession()) { new RoutingController(sessionInfo).release(); } @@ -971,15 +968,21 @@ public final class MediaRouter2 { } if (matchingController == null) { - Log.w(TAG, "updateControllerOnHandler: Matching controller not found. uniqueSessionId=" - + sessionInfo.getId()); + Log.w( + TAG, + "updateControllerOnHandler: Matching controller not found. uniqueSessionId=" + + sessionInfo.getId()); return; } RoutingSessionInfo oldInfo = matchingController.getRoutingSessionInfo(); if (!TextUtils.equals(oldInfo.getProviderId(), sessionInfo.getProviderId())) { - Log.w(TAG, "updateControllerOnHandler: Provider IDs are not matched. old=" - + oldInfo.getProviderId() + ", new=" + sessionInfo.getProviderId()); + Log.w( + TAG, + "updateControllerOnHandler: Provider IDs are not matched. old=" + + oldInfo.getProviderId() + + ", new=" + + sessionInfo.getProviderId()); return; } @@ -1000,24 +1003,31 @@ public final class MediaRouter2 { if (matchingController == null) { if (DEBUG) { - Log.d(TAG, "releaseControllerOnHandler: Matching controller not found. " - + "uniqueSessionId=" + sessionInfo.getId()); + Log.d( + TAG, + "releaseControllerOnHandler: Matching controller not found. " + + "uniqueSessionId=" + + sessionInfo.getId()); } return; } RoutingSessionInfo oldInfo = matchingController.getRoutingSessionInfo(); if (!TextUtils.equals(oldInfo.getProviderId(), sessionInfo.getProviderId())) { - Log.w(TAG, "releaseControllerOnHandler: Provider IDs are not matched. old=" - + oldInfo.getProviderId() + ", new=" + sessionInfo.getProviderId()); + Log.w( + TAG, + "releaseControllerOnHandler: Provider IDs are not matched. old=" + + oldInfo.getProviderId() + + ", new=" + + sessionInfo.getProviderId()); return; } matchingController.releaseInternal(/* shouldReleaseSession= */ false); } - void onRequestCreateControllerByManagerOnHandler(RoutingSessionInfo oldSession, - MediaRoute2Info route, long managerRequestId) { + void onRequestCreateControllerByManagerOnHandler( + RoutingSessionInfo oldSession, MediaRoute2Info route, long managerRequestId) { RoutingController controller; if (oldSession.isSystemSession()) { controller = getSystemController(); @@ -1033,17 +1043,17 @@ public final class MediaRouter2 { } /** - * Returns whether this router is created with {@link #getInstance(Context, String)}. - * This kind of router can control the target app's media routing. + * 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; } /** - * Returns a {@link RoutingSessionInfo} which has the client package name. - * The client package name is set only when the given sessionInfo doesn't have it. - * Should only used for system media routers. + * Returns a {@link RoutingSessionInfo} which has the client package name. The client package + * name is set only when the given sessionInfo doesn't have it. Should only used for system + * media routers. */ private RoutingSessionInfo ensureClientPackageNameForSystemSession( @NonNull RoutingSessionInfo sessionInfo) { @@ -1057,46 +1067,68 @@ public final class MediaRouter2 { .build(); } - private List getSortedRoutes(List routes, - RouteDiscoveryPreference preference) { - if (!preference.shouldRemoveDuplicates()) { + private List getSortedRoutes( + List routes, List packageOrder) { + if (packageOrder.isEmpty()) { return routes; } Map packagePriority = new ArrayMap<>(); - int count = preference.getDeduplicationPackageOrder().size(); + int count = packageOrder.size(); for (int i = 0; i < count; i++) { // the last package will have 1 as the priority - packagePriority.put(preference.getDeduplicationPackageOrder().get(i), count - i); + packagePriority.put(packageOrder.get(i), count - i); } ArrayList sortedRoutes = new ArrayList<>(routes); // take the negative for descending order - sortedRoutes.sort(Comparator.comparingInt( - r -> -packagePriority.getOrDefault(r.getPackageName(), 0))); + sortedRoutes.sort( + Comparator.comparingInt(r -> -packagePriority.getOrDefault(r.getPackageName(), 0))); return sortedRoutes; } - private List filterRoutes(List routes, - RouteDiscoveryPreference discoveryPreference) { + @GuardedBy("mLock") + private List filterRoutesWithCompositePreferenceLocked( + List routes) { Set deduplicationIdSet = new ArraySet<>(); List filteredRoutes = new ArrayList<>(); - for (MediaRoute2Info route : getSortedRoutes(routes, discoveryPreference)) { + for (MediaRoute2Info route : + getSortedRoutes(routes, mDiscoveryPreference.getDeduplicationPackageOrder())) { + if (!route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) { + continue; + } + if (!mDiscoveryPreference.getAllowedPackages().isEmpty() + && (route.getPackageName() == null + || !mDiscoveryPreference + .getAllowedPackages() + .contains(route.getPackageName()))) { + continue; + } + if (mDiscoveryPreference.shouldRemoveDuplicates()) { + if (!Collections.disjoint(deduplicationIdSet, route.getDeduplicationIds())) { + continue; + } + deduplicationIdSet.addAll(route.getDeduplicationIds()); + } + filteredRoutes.add(route); + } + return filteredRoutes; + } + + private List filterRoutesWithIndividualPreference( + List routes, RouteDiscoveryPreference discoveryPreference) { + List filteredRoutes = new ArrayList<>(); + for (MediaRoute2Info route : routes) { if (!route.hasAnyFeatures(discoveryPreference.getPreferredFeatures())) { continue; } if (!discoveryPreference.getAllowedPackages().isEmpty() && (route.getPackageName() == null - || !discoveryPreference.getAllowedPackages() - .contains(route.getPackageName()))) { + || !discoveryPreference + .getAllowedPackages() + .contains(route.getPackageName()))) { continue; } - if (discoveryPreference.shouldRemoveDuplicates()) { - if (!Collections.disjoint(deduplicationIdSet, route.getDeduplicationIds())) { - continue; - } - deduplicationIdSet.addAll(route.getDeduplicationIds()); - } filteredRoutes.add(route); } return filteredRoutes; @@ -1111,23 +1143,24 @@ public final class MediaRouter2 { for (MediaRoute2Info route : sManager.getAllRoutes()) { mRoutes.put(route.getId(), route); } - mShouldUpdateRoutes = true; + updateFilteredRoutesLocked(); } } private void notifyRoutesAdded(List routes) { - for (RouteCallbackRecord record: mRouteCallbackRecords) { - List filteredRoutes = filterRoutes(routes, record.mPreference); + for (RouteCallbackRecord record : mRouteCallbackRecords) { + List filteredRoutes = + filterRoutesWithIndividualPreference(routes, record.mPreference); if (!filteredRoutes.isEmpty()) { - record.mExecutor.execute( - () -> record.mRouteCallback.onRoutesAdded(filteredRoutes)); + record.mExecutor.execute(() -> record.mRouteCallback.onRoutesAdded(filteredRoutes)); } } } private void notifyRoutesRemoved(List routes) { - for (RouteCallbackRecord record: mRouteCallbackRecords) { - List filteredRoutes = filterRoutes(routes, record.mPreference); + for (RouteCallbackRecord record : mRouteCallbackRecords) { + List filteredRoutes = + filterRoutesWithIndividualPreference(routes, record.mPreference); if (!filteredRoutes.isEmpty()) { record.mExecutor.execute( () -> record.mRouteCallback.onRoutesRemoved(filteredRoutes)); @@ -1136,8 +1169,9 @@ public final class MediaRouter2 { } private void notifyRoutesChanged(List routes) { - for (RouteCallbackRecord record: mRouteCallbackRecords) { - List filteredRoutes = filterRoutes(routes, record.mPreference); + for (RouteCallbackRecord record : mRouteCallbackRecords) { + List filteredRoutes = + filterRoutesWithIndividualPreference(routes, record.mPreference); if (!filteredRoutes.isEmpty()) { record.mExecutor.execute( () -> record.mRouteCallback.onRoutesChanged(filteredRoutes)); @@ -1146,46 +1180,42 @@ public final class MediaRouter2 { } private void notifyPreferredFeaturesChanged(List features) { - for (RouteCallbackRecord record: mRouteCallbackRecords) { + for (RouteCallbackRecord record : mRouteCallbackRecords) { record.mExecutor.execute( () -> record.mRouteCallback.onPreferredFeaturesChanged(features)); } } private void notifyTransfer(RoutingController oldController, RoutingController newController) { - for (TransferCallbackRecord record: mTransferCallbackRecords) { + for (TransferCallbackRecord record : mTransferCallbackRecords) { record.mExecutor.execute( () -> record.mTransferCallback.onTransfer(oldController, newController)); } } private void notifyTransferFailure(MediaRoute2Info route) { - for (TransferCallbackRecord record: mTransferCallbackRecords) { - record.mExecutor.execute( - () -> record.mTransferCallback.onTransferFailure(route)); + for (TransferCallbackRecord record : mTransferCallbackRecords) { + record.mExecutor.execute(() -> record.mTransferCallback.onTransferFailure(route)); } } private void notifyStop(RoutingController controller) { - for (TransferCallbackRecord record: mTransferCallbackRecords) { - record.mExecutor.execute( - () -> record.mTransferCallback.onStop(controller)); + for (TransferCallbackRecord record : mTransferCallbackRecords) { + record.mExecutor.execute(() -> record.mTransferCallback.onStop(controller)); } } private void notifyControllerUpdated(RoutingController controller) { - for (ControllerCallbackRecord record: mControllerCallbackRecords) { + for (ControllerCallbackRecord record : mControllerCallbackRecords) { record.mExecutor.execute(() -> record.mCallback.onControllerUpdated(controller)); } } - /** - * Callback for receiving events about media route discovery. - */ + /** Callback for receiving events about media route discovery. */ public abstract static class RouteCallback { /** - * Called when routes are added. Whenever you registers a callback, this will - * be invoked with known routes. + * Called when routes are added. Whenever you registers a callback, this will be invoked + * with known routes. * * @param routes the list of routes that have been added. It's never empty. */ @@ -1199,17 +1229,17 @@ public final class MediaRouter2 { public void onRoutesRemoved(@NonNull List routes) {} /** - * Called when routes are changed. For example, it is called when the route's name - * or volume have been changed. + * Called when routes are changed. For example, it is called when the route's name or volume + * have been changed. * * @param routes the list of routes that have been changed. It's never empty. */ public void onRoutesChanged(@NonNull List routes) {} /** - * Called when the client app's preferred features are changed. - * When this is called, it is recommended to {@link #getRoutes()} to get the routes - * that are currently available to the app. + * Called when the client app's preferred features are changed. When this is called, it is + * recommended to {@link #getRoutes()} to get the routes that are currently available to the + * app. * * @param preferredFeatures the new preferred features set by the application * @hide @@ -1218,26 +1248,25 @@ public final class MediaRouter2 { public void onPreferredFeaturesChanged(@NonNull List preferredFeatures) {} } - /** - * Callback for receiving events on media transfer. - */ + /** Callback for receiving events on media transfer. */ public abstract static class TransferCallback { /** - * Called when a media is transferred between two different routing controllers. - * This can happen by calling {@link #transferTo(MediaRoute2Info)}. - *

Override this to start playback with {@code newController}. You may want to get - * the status of the media that is being played with {@code oldController} and resume it - * continuously with {@code newController}. - * After this is called, any callbacks with {@code oldController} will not be invoked - * unless {@code oldController} is the {@link #getSystemController() system controller}. - * You need to {@link RoutingController#release() release} {@code oldController} before - * playing the media with {@code newController}. + * Called when a media is transferred between two different routing controllers. This can + * happen by calling {@link #transferTo(MediaRoute2Info)}. + * + *

Override this to start playback with {@code newController}. You may want to get the + * status of the media that is being played with {@code oldController} and resume it + * continuously with {@code newController}. After this is called, any callbacks with {@code + * oldController} will not be invoked unless {@code oldController} is the {@link + * #getSystemController() system controller}. You need to {@link RoutingController#release() + * release} {@code oldController} before playing the media with {@code newController}. * * @param oldController the previous controller that controlled routing * @param newController the new controller to control routing * @see #transferTo(MediaRoute2Info) */ - public void onTransfer(@NonNull RoutingController oldController, + public void onTransfer( + @NonNull RoutingController oldController, @NonNull RoutingController newController) {} /** @@ -1248,61 +1277,58 @@ public final class MediaRouter2 { public void onTransferFailure(@NonNull MediaRoute2Info requestedRoute) {} /** - * Called when a media routing stops. It can be stopped by a user or a provider. - * App should not continue playing media locally when this method is called. - * The {@code controller} is released before this method is called. + * Called when a media routing stops. It can be stopped by a user or a provider. App should + * not continue playing media locally when this method is called. The {@code controller} is + * released before this method is called. * * @param controller the controller that controlled the stopped media routing */ - public void onStop(@NonNull RoutingController controller) { } + public void onStop(@NonNull RoutingController controller) {} } /** - * A listener interface to send optional app-specific hints when creating a - * {@link RoutingController}. + * A listener interface to send optional app-specific hints when creating a {@link + * RoutingController}. */ public interface OnGetControllerHintsListener { /** - * Called when the {@link MediaRouter2} or the system is about to request - * a media route provider service to create a controller with the given route. - * The {@link Bundle} returned here will be sent to media route provider service as a hint. - *

- * Since controller creation can be requested by the {@link MediaRouter2} and the system, - * set the listener as soon as possible after acquiring {@link MediaRouter2} instance. - * The method will be called on the same thread that calls - * {@link #transferTo(MediaRoute2Info)} or the main thread if it is requested by the system. + * Called when the {@link MediaRouter2} or the system is about to request a media route + * provider service to create a controller with the given route. The {@link Bundle} returned + * here will be sent to media route provider service as a hint. + * + *

Since controller creation can be requested by the {@link MediaRouter2} and the system, + * set the listener as soon as possible after acquiring {@link MediaRouter2} instance. The + * method will be called on the same thread that calls {@link #transferTo(MediaRoute2Info)} + * or the main thread if it is requested by the system. * * @param route the route to create a controller with - * @return An optional bundle of app-specific arguments to send to the provider, - * or {@code null} if none. The contents of this bundle may affect the result of - * controller creation. + * @return An optional bundle of app-specific arguments to send to the provider, or {@code + * null} if none. The contents of this bundle may affect the result of controller + * creation. * @see MediaRoute2ProviderService#onCreateSession(long, String, String, Bundle) */ @Nullable Bundle onGetControllerHints(@NonNull MediaRoute2Info route); } - /** - * Callback for receiving {@link RoutingController} updates. - */ + /** Callback for receiving {@link RoutingController} updates. */ public abstract static class ControllerCallback { /** - * Called when a controller is updated. (e.g., when the selected routes of the - * controller is changed or when the volume of the controller is changed.) + * Called when a controller is updated. (e.g., when the selected routes of the controller is + * changed or when the volume of the controller is changed.) * - * @param controller the updated controller. It may be the - * {@link #getSystemController() system controller}. + * @param controller the updated controller. It may be the {@link #getSystemController() + * system controller}. * @see #getSystemController() */ - public void onControllerUpdated(@NonNull RoutingController controller) { } + public void onControllerUpdated(@NonNull RoutingController controller) {} } /** - * A class to control media routing session in media route provider. - * For example, selecting/deselecting/transferring to routes of a session can be done through - * this. Instances are created when - * {@link TransferCallback#onTransfer(RoutingController, RoutingController)} is called, - * which is invoked after {@link #transferTo(MediaRoute2Info)} is called. + * A class to control media routing session in media route provider. For example, + * selecting/deselecting/transferring to routes of a session can be done through this. Instances + * are created when {@link TransferCallback#onTransfer(RoutingController, RoutingController)} is + * called, which is invoked after {@link #transferTo(MediaRoute2Info)} is called. */ public class RoutingController { private final Object mControllerLock = new Object(); @@ -1339,8 +1365,8 @@ public final class MediaRouter2 { } /** - * Gets the original session ID set by - * {@link RoutingSessionInfo.Builder#Builder(String, String)}. + * Gets the original session ID set by {@link RoutingSessionInfo.Builder#Builder(String, + * String)}. * * @hide */ @@ -1353,8 +1379,8 @@ public final class MediaRouter2 { } /** - * Gets the control hints used to control routing session if available. - * It is set by the media route provider. + * Gets the control hints used to control routing session if available. It is set by the + * media route provider. */ @Nullable public Bundle getControlHints() { @@ -1401,11 +1427,12 @@ public final class MediaRouter2 { /** * Gets the information about how volume is handled on the session. - *

Please note that you may not control the volume of the session even when - * you can control the volume of each selected route in the session. * - * @return {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or - * {@link MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE} + *

Please note that you may not control the volume of the session even when you can + * control the volume of each selected route in the session. + * + * @return {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or {@link + * MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE} */ @MediaRoute2Info.PlaybackVolume public int getVolumeHandling() { @@ -1414,9 +1441,7 @@ public final class MediaRouter2 { } } - /** - * Gets the maximum volume of the session. - */ + /** Gets the maximum volume of the session. */ public int getVolumeMax() { synchronized (mControllerLock) { return mSessionInfo.getVolumeMax(); @@ -1425,11 +1450,10 @@ public final class MediaRouter2 { /** * Gets the current volume of the session. - *

- * When it's available, it represents the volume of routing session, which is a group - * of selected routes. Use {@link MediaRoute2Info#getVolume()} - * to get the volume of a route, - *

+ * + *

When it's available, it represents the volume of routing session, which is a group of + * selected routes. Use {@link MediaRoute2Info#getVolume()} to get the volume of a route, + * * @see MediaRoute2Info#getVolume() */ public int getVolume() { @@ -1439,9 +1463,9 @@ public final class MediaRouter2 { } /** - * Returns true if this controller is released, false otherwise. - * If it is released, then all other getters from this instance may return invalid values. - * Also, any operations to this instance will be ignored once released. + * Returns true if this controller is released, false otherwise. If it is released, then all + * other getters from this instance may return invalid values. Also, any operations to this + * instance will be ignored once released. * * @see #release */ @@ -1454,14 +1478,16 @@ public final class MediaRouter2 { /** * Selects a route for the remote session. After a route is selected, the media is expected * to be played to the all the selected routes. This is different from {@link - * MediaRouter2#transferTo(MediaRoute2Info)} transferring to a route}, - * where the media is expected to 'move' from one route to another. - *

- * The given route must satisfy all of the following conditions: + * MediaRouter2#transferTo(MediaRoute2Info)} transferring to a route}, where the media is + * expected to 'move' from one route to another. + * + *

The given route must satisfy all of the following conditions: + * *

    - *
  • It should not be included in {@link #getSelectedRoutes()}
  • - *
  • It should be included in {@link #getSelectableRoutes()}
  • + *
  • It should not be included in {@link #getSelectedRoutes()} + *
  • It should be included in {@link #getSelectableRoutes()} *
+ * * If the route doesn't meet any of above conditions, it will be ignored. * * @see #deselectRoute(MediaRoute2Info) @@ -1509,12 +1535,14 @@ public final class MediaRouter2 { /** * Deselects a route from the remote session. After a route is deselected, the media is * expected to be stopped on the deselected route. - *

- * The given route must satisfy all of the following conditions: + * + *

The given route must satisfy all of the following conditions: + * *

    - *
  • It should be included in {@link #getSelectedRoutes()}
  • - *
  • It should be included in {@link #getDeselectableRoutes()}
  • + *
  • It should be included in {@link #getSelectedRoutes()} + *
  • It should be included in {@link #getDeselectableRoutes()} *
+ * * If the route doesn't meet any of above conditions, it will be ignored. * * @see #getSelectedRoutes() @@ -1559,8 +1587,8 @@ public final class MediaRouter2 { } /** - * Transfers to a given route for the remote session. The given route must be included - * in {@link RoutingSessionInfo#getTransferableRoutes()}. + * Transfers to a given route for the remote session. The given route must be included in + * {@link RoutingSessionInfo#getTransferableRoutes()}. * * @see RoutingSessionInfo#getSelectedRoutes() * @see RoutingSessionInfo#getTransferableRoutes() @@ -1597,7 +1625,7 @@ public final class MediaRouter2 { * Requests a volume change for the remote session asynchronously. * * @param volume The new volume value between 0 and {@link RoutingController#getVolumeMax} - * (inclusive). + * (inclusive). * @see #getVolume() */ public void setVolume(int volume) { @@ -1634,9 +1662,9 @@ public final class MediaRouter2 { } /** - * Releases this controller and the corresponding session. - * Any operations on this controller after calling this method will be ignored. - * The devices that are playing media will stop playing it. + * Releases this controller and the corresponding session. Any operations on this controller + * after calling this method will be ignored. The devices that are playing media will stop + * playing it. */ public void release() { releaseInternal(/* shouldReleaseSession= */ true); @@ -1644,8 +1672,9 @@ public final class MediaRouter2 { /** * Schedules release of the controller. + * * @return {@code true} if it's successfully scheduled, {@code false} if it's already - * scheduled to be released or released. + * scheduled to be released or released. */ boolean scheduleRelease() { synchronized (mControllerLock) { @@ -1701,11 +1730,15 @@ public final class MediaRouter2 { } if (shouldNotifyStop) { - mHandler.sendMessage(obtainMessage(MediaRouter2::notifyStop, MediaRouter2.this, - RoutingController.this)); + mHandler.sendMessage( + obtainMessage( + MediaRouter2::notifyStop, + MediaRouter2.this, + RoutingController.this)); } - if (mRouteCallbackRecords.isEmpty() && mNonSystemRoutingControllers.isEmpty() + if (mRouteCallbackRecords.isEmpty() + && mNonSystemRoutingControllers.isEmpty() && mStub != null) { try { mMediaRouterService.unregisterRouter2(mStub); @@ -1720,26 +1753,34 @@ public final class MediaRouter2 { @Override public String toString() { // To prevent logging spam, we only print the ID of each route. - List selectedRoutes = getSelectedRoutes().stream() - .map(MediaRoute2Info::getId).collect(Collectors.toList()); - List selectableRoutes = getSelectableRoutes().stream() - .map(MediaRoute2Info::getId).collect(Collectors.toList()); - List deselectableRoutes = getDeselectableRoutes().stream() - .map(MediaRoute2Info::getId).collect(Collectors.toList()); + List selectedRoutes = + getSelectedRoutes().stream() + .map(MediaRoute2Info::getId) + .collect(Collectors.toList()); + List selectableRoutes = + getSelectableRoutes().stream() + .map(MediaRoute2Info::getId) + .collect(Collectors.toList()); + List deselectableRoutes = + getDeselectableRoutes().stream() + .map(MediaRoute2Info::getId) + .collect(Collectors.toList()); - StringBuilder result = new StringBuilder() - .append("RoutingController{ ") - .append("id=").append(getId()) - .append(", selectedRoutes={") - .append(selectedRoutes) - .append("}") - .append(", selectableRoutes={") - .append(selectableRoutes) - .append("}") - .append(", deselectableRoutes={") - .append(deselectableRoutes) - .append("}") - .append(" }"); + StringBuilder result = + new StringBuilder() + .append("RoutingController{ ") + .append("id=") + .append(getId()) + .append(", selectedRoutes={") + .append(selectedRoutes) + .append("}") + .append(", selectableRoutes={") + .append(selectableRoutes) + .append("}") + .append(", deselectableRoutes={") + .append(deselectableRoutes) + .append("}") + .append(" }"); return result.toString(); } @@ -1764,7 +1805,8 @@ public final class MediaRouter2 { } synchronized (mLock) { - return routeIds.stream().map(mRoutes::get) + return routeIds.stream() + .map(mRoutes::get) .filter(Objects::nonNull) .collect(Collectors.toList()); } @@ -1799,7 +1841,9 @@ public final class MediaRouter2 { public final RouteCallback mRouteCallback; public final RouteDiscoveryPreference mPreference; - RouteCallbackRecord(@Nullable Executor executor, @NonNull RouteCallback routeCallback, + RouteCallbackRecord( + @Nullable Executor executor, + @NonNull RouteCallback routeCallback, @Nullable RouteDiscoveryPreference preference) { mRouteCallback = routeCallback; mExecutor = executor; @@ -1827,8 +1871,8 @@ public final class MediaRouter2 { public final Executor mExecutor; public final TransferCallback mTransferCallback; - TransferCallbackRecord(@NonNull Executor executor, - @NonNull TransferCallback transferCallback) { + TransferCallbackRecord( + @NonNull Executor executor, @NonNull TransferCallback transferCallback) { mTransferCallback = transferCallback; mExecutor = executor; } @@ -1854,8 +1898,8 @@ public final class MediaRouter2 { public final Executor mExecutor; public final ControllerCallback mCallback; - ControllerCallbackRecord(@Nullable Executor executor, - @NonNull ControllerCallback callback) { + ControllerCallbackRecord( + @Nullable Executor executor, @NonNull ControllerCallback callback) { mCallback = callback; mExecutor = executor; } @@ -1883,66 +1927,87 @@ public final class MediaRouter2 { public final MediaRoute2Info mRoute; public final RoutingController mOldController; - ControllerCreationRequest(int requestId, long managerRequestId, - @NonNull MediaRoute2Info route, @NonNull RoutingController oldController) { + ControllerCreationRequest( + int requestId, + long managerRequestId, + @NonNull MediaRoute2Info route, + @NonNull RoutingController oldController) { mRequestId = requestId; mManagerRequestId = managerRequestId; mRoute = Objects.requireNonNull(route, "route must not be null"); - mOldController = Objects.requireNonNull(oldController, - "oldController must not be null"); + mOldController = + Objects.requireNonNull(oldController, "oldController must not be null"); } } class MediaRouter2Stub extends IMediaRouter2.Stub { @Override - public void notifyRouterRegistered(List currentRoutes, - RoutingSessionInfo currentSystemSessionInfo) { - mHandler.sendMessage(obtainMessage(MediaRouter2::syncRoutesOnHandler, - MediaRouter2.this, currentRoutes, currentSystemSessionInfo)); + public void notifyRouterRegistered( + List currentRoutes, RoutingSessionInfo currentSystemSessionInfo) { + mHandler.sendMessage( + obtainMessage( + MediaRouter2::syncRoutesOnHandler, + MediaRouter2.this, + currentRoutes, + currentSystemSessionInfo)); } @Override public void notifyRoutesAdded(List routes) { - mHandler.sendMessage(obtainMessage(MediaRouter2::addRoutesOnHandler, - MediaRouter2.this, routes)); + mHandler.sendMessage( + obtainMessage(MediaRouter2::addRoutesOnHandler, MediaRouter2.this, routes)); } @Override public void notifyRoutesRemoved(List routes) { - mHandler.sendMessage(obtainMessage(MediaRouter2::removeRoutesOnHandler, - MediaRouter2.this, routes)); + mHandler.sendMessage( + obtainMessage(MediaRouter2::removeRoutesOnHandler, MediaRouter2.this, routes)); } @Override public void notifyRoutesChanged(List routes) { - mHandler.sendMessage(obtainMessage(MediaRouter2::changeRoutesOnHandler, - MediaRouter2.this, routes)); + mHandler.sendMessage( + obtainMessage(MediaRouter2::changeRoutesOnHandler, MediaRouter2.this, routes)); } @Override public void notifySessionCreated(int requestId, @Nullable RoutingSessionInfo sessionInfo) { - mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler, - MediaRouter2.this, requestId, sessionInfo)); + mHandler.sendMessage( + obtainMessage( + MediaRouter2::createControllerOnHandler, + MediaRouter2.this, + requestId, + sessionInfo)); } @Override public void notifySessionInfoChanged(@Nullable RoutingSessionInfo sessionInfo) { - mHandler.sendMessage(obtainMessage(MediaRouter2::updateControllerOnHandler, - MediaRouter2.this, sessionInfo)); + mHandler.sendMessage( + obtainMessage( + MediaRouter2::updateControllerOnHandler, + MediaRouter2.this, + sessionInfo)); } @Override public void notifySessionReleased(RoutingSessionInfo sessionInfo) { - mHandler.sendMessage(obtainMessage(MediaRouter2::releaseControllerOnHandler, - MediaRouter2.this, sessionInfo)); + mHandler.sendMessage( + obtainMessage( + MediaRouter2::releaseControllerOnHandler, + MediaRouter2.this, + sessionInfo)); } @Override - public void requestCreateSessionByManager(long managerRequestId, - RoutingSessionInfo oldSession, MediaRoute2Info route) { - mHandler.sendMessage(obtainMessage( - MediaRouter2::onRequestCreateControllerByManagerOnHandler, - MediaRouter2.this, oldSession, route, managerRequestId)); + public void requestCreateSessionByManager( + long managerRequestId, RoutingSessionInfo oldSession, MediaRoute2Info route) { + mHandler.sendMessage( + obtainMessage( + MediaRouter2::onRequestCreateControllerByManagerOnHandler, + MediaRouter2.this, + oldSession, + route, + managerRequestId)); } } @@ -1952,57 +2017,21 @@ public final class MediaRouter2 { @Override public void onRoutesAdded(@NonNull List routes) { updateAllRoutesFromManager(); - - List filteredRoutes; - synchronized (mLock) { - filteredRoutes = filterRoutes(routes, mDiscoveryPreference); - } - if (filteredRoutes.isEmpty()) { - return; - } - for (RouteCallbackRecord record: mRouteCallbackRecords) { - record.mExecutor.execute( - () -> record.mRouteCallback.onRoutesAdded(filteredRoutes)); - } } @Override public void onRoutesRemoved(@NonNull List routes) { updateAllRoutesFromManager(); - - List filteredRoutes; - synchronized (mLock) { - filteredRoutes = filterRoutes(routes, mDiscoveryPreference); - } - if (filteredRoutes.isEmpty()) { - return; - } - for (RouteCallbackRecord record: mRouteCallbackRecords) { - record.mExecutor.execute( - () -> record.mRouteCallback.onRoutesRemoved(filteredRoutes)); - } } @Override public void onRoutesChanged(@NonNull List routes) { updateAllRoutesFromManager(); - - List filteredRoutes; - synchronized (mLock) { - filteredRoutes = filterRoutes(routes, mDiscoveryPreference); - } - if (filteredRoutes.isEmpty()) { - return; - } - for (RouteCallbackRecord record: mRouteCallbackRecords) { - record.mExecutor.execute( - () -> record.mRouteCallback.onRoutesChanged(filteredRoutes)); - } } @Override - public void onTransferred(@NonNull RoutingSessionInfo oldSession, - @NonNull RoutingSessionInfo newSession) { + public void onTransferred( + @NonNull RoutingSessionInfo oldSession, @NonNull RoutingSessionInfo newSession) { if (!oldSession.isSystemSession() && !TextUtils.equals(mClientPackageName, oldSession.getClientPackageName())) { return; @@ -2018,7 +2047,6 @@ public final class MediaRouter2 { return; } - RoutingController oldController; if (oldSession.isSystemSession()) { mSystemController.setRoutingSessionInfo( @@ -2041,8 +2069,8 @@ public final class MediaRouter2 { } @Override - public void onTransferFailed(@NonNull RoutingSessionInfo session, - @NonNull MediaRoute2Info route) { + public void onTransferFailed( + @NonNull RoutingSessionInfo session, @NonNull MediaRoute2Info route) { if (!session.isSystemSession() && !TextUtils.equals(mClientPackageName, session.getClientPackageName())) { return; @@ -2083,8 +2111,8 @@ public final class MediaRouter2 { } @Override - public void onDiscoveryPreferenceChanged(@NonNull String packageName, - @NonNull RouteDiscoveryPreference preference) { + public void onDiscoveryPreferenceChanged( + @NonNull String packageName, @NonNull RouteDiscoveryPreference preference) { if (!TextUtils.equals(mClientPackageName, packageName)) { return; }