diff options
7 files changed, 327 insertions, 147 deletions
diff --git a/media/java/android/media/IMediaRouter2Client.aidl b/media/java/android/media/IMediaRouter2Client.aidl index 26184af699db..72c33f994349 100644 --- a/media/java/android/media/IMediaRouter2Client.aidl +++ b/media/java/android/media/IMediaRouter2Client.aidl @@ -16,12 +16,14 @@ package android.media; -import android.media.MediaRoute2ProviderInfo; +import android.media.MediaRoute2Info; /** * @hide */ oneway interface IMediaRouter2Client { void notifyRestoreRoute(); - void notifyProviderInfosUpdated(in List<MediaRoute2ProviderInfo> providers); + void notifyRoutesAdded(in List<MediaRoute2Info> routes); + void notifyRoutesRemoved(in List<MediaRoute2Info> routes); + void notifyRoutesChanged(in List<MediaRoute2Info> routes); } diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 59bd96ffb1bd..457ccb788d0d 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -314,7 +314,7 @@ public final class MediaRoute2Info implements Parcelable { List<String> mSupportedCategories; int mVolume; int mVolumeMax; - int mVolumeHandling; + int mVolumeHandling = PLAYBACK_VOLUME_FIXED; Bundle mExtras; public Builder(@NonNull String id, @NonNull String name) { diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index b52e2d647e5a..ce18ab3b3c83 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -33,7 +33,9 @@ import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; @@ -51,23 +53,23 @@ public class MediaRouter2 { @GuardedBy("sLock") private static MediaRouter2 sInstance; - private Context mContext; + private final Context mContext; private final IMediaRouterService mMediaRouterService; private final CopyOnWriteArrayList<CallbackRecord> mCallbackRecords = new CopyOnWriteArrayList<>(); - @GuardedBy("sLock") - private List<String> mControlCategories = Collections.emptyList(); - @GuardedBy("sLock") - private Client mClient; private final String mPackageName; - final Handler mHandler; + private final Map<String, MediaRoute2Info> mRoutes = new HashMap<>(); - List<MediaRoute2ProviderInfo> mProviders = Collections.emptyList(); - volatile List<MediaRoute2Info> mRoutes = Collections.emptyList(); + private volatile List<String> mControlCategories = Collections.emptyList(); - MediaRoute2Info mSelectedRoute; + private MediaRoute2Info mSelectedRoute; + @GuardedBy("sLock") + private Client mClient; + + final Handler mHandler; + volatile List<MediaRoute2Info> mFilteredRoutes = Collections.emptyList(); /** * Gets an instance of the media router associated with the context. @@ -137,6 +139,7 @@ public class MediaRouter2 { } } } + //TODO: Is it thread-safe? record.notifyRoutes(); //TODO: Update discovery request here. @@ -181,31 +184,22 @@ public class MediaRouter2 { public void setControlCategories(@NonNull Collection<String> controlCategories) { Objects.requireNonNull(controlCategories, "control categories must not be null"); - Client client; - List<String> newControlCategories = new ArrayList<>(controlCategories); - synchronized (sLock) { - mControlCategories = newControlCategories; - client = mClient; - } - if (client != null) { - try { - mMediaRouterService.setControlCategories2(client, newControlCategories); - } catch (RemoteException ex) { - Log.e(TAG, "Unable to set control categories.", ex); - } - } - mHandler.sendMessage(obtainMessage(MediaRouter2::refreshAndNotifyRoutes, this)); + // To ensure invoking callbacks correctly according to control categories + mHandler.sendMessage(obtainMessage(MediaRouter2::setControlCategoriesOnHandler, + MediaRouter2.this, new ArrayList<>(controlCategories))); } + /** - * Gets the list of {@link MediaRoute2Info routes} currently known to the media router. + * Gets the unmodifiable list of {@link MediaRoute2Info routes} currently + * known to the media router. * * @return the list of routes that support at least one of the control categories set by * the application */ @NonNull public List<MediaRoute2Info> getRoutes() { - return mRoutes; + return mFilteredRoutes; } /** @@ -326,77 +320,121 @@ public class MediaRouter2 { return -1; } - void onProviderInfosUpdated(List<MediaRoute2ProviderInfo> providers) { - if (providers == null) { - Log.w(TAG, "Providers info is null."); - return; - } - - mProviders = providers; - refreshAndNotifyRoutes(); - } - - void refreshAndNotifyRoutes() { - ArrayList<MediaRoute2Info> routes = new ArrayList<>(); + private void setControlCategoriesOnHandler(List<String> newControlCategories) { + List<String> prevControlCategories = mControlCategories; + List<MediaRoute2Info> addedRoutes = new ArrayList<>(); + List<MediaRoute2Info> removedRoutes = new ArrayList<>(); + List<MediaRoute2Info> filteredRoutes = new ArrayList<>(); - List<String> controlCategories; + mControlCategories = newControlCategories; + Client client; synchronized (sLock) { - controlCategories = mControlCategories; + client = mClient; + } + if (client != null) { + try { + mMediaRouterService.setControlCategories2(client, mControlCategories); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to set control categories.", ex); + } } - for (MediaRoute2ProviderInfo provider : mProviders) { - updateProvider(provider, controlCategories, routes); + for (MediaRoute2Info route : mRoutes.values()) { + boolean preSupported = route.supportsControlCategory(prevControlCategories); + boolean postSupported = route.supportsControlCategory(newControlCategories); + if (postSupported) { + filteredRoutes.add(route); + } + if (preSupported == postSupported) { + continue; + } + if (preSupported) { + removedRoutes.add(route); + } else { + addedRoutes.add(route); + } } + mFilteredRoutes = Collections.unmodifiableList(filteredRoutes); - //TODO: Can orders be changed? - if (!Objects.equals(mRoutes, routes)) { - mRoutes = Collections.unmodifiableList(routes); - notifyRouteListChanged(mRoutes); + if (removedRoutes.size() > 0) { + notifyRoutesRemoved(removedRoutes); + } + if (addedRoutes.size() > 0) { + notifyRoutesAdded(addedRoutes); } } - void updateProvider(MediaRoute2ProviderInfo provider, List<String> controlCategories, - List<MediaRoute2Info> outRoutes) { - if (provider == null || !provider.isValid()) { - Log.w(TAG, "Ignoring invalid provider : " + provider); - return; + void addRoutesOnHandler(List<MediaRoute2Info> routes) { + List<MediaRoute2Info> addedRoutes = new ArrayList<>(); + for (MediaRoute2Info route : routes) { + mRoutes.put(route.getUniqueId(), route); + if (route.supportsControlCategory(mControlCategories)) { + addedRoutes.add(route); + } } + if (addedRoutes.size() > 0) { + refreshFilteredRoutes(); + notifyRoutesAdded(addedRoutes); + } + } - final Collection<MediaRoute2Info> routes = provider.getRoutes(); + void removeRoutesOnHandler(List<MediaRoute2Info> routes) { + List<MediaRoute2Info> removedRoutes = new ArrayList<>(); for (MediaRoute2Info route : routes) { - if (!route.isValid()) { - Log.w(TAG, "Ignoring invalid route : " + route); - continue; + mRoutes.remove(route.getUniqueId()); + if (route.supportsControlCategory(mControlCategories)) { + removedRoutes.add(route); } - if (!route.supportsControlCategory(controlCategories)) { - continue; + } + if (removedRoutes.size() > 0) { + refreshFilteredRoutes(); + notifyRoutesRemoved(removedRoutes); + } + } + + void changeRoutesOnHandler(List<MediaRoute2Info> routes) { + List<MediaRoute2Info> changedRoutes = new ArrayList<>(); + for (MediaRoute2Info route : routes) { + mRoutes.put(route.getUniqueId(), route); + if (route.supportsControlCategory(mControlCategories)) { + changedRoutes.add(route); } - MediaRoute2Info preRoute = findRouteById(route.getId()); - if (!route.equals(preRoute)) { - notifyRouteChanged(route); + } + if (changedRoutes.size() > 0) { + refreshFilteredRoutes(); + notifyRoutesChanged(changedRoutes); + } + } + + private void refreshFilteredRoutes() { + List<MediaRoute2Info> filteredRoutes = new ArrayList<>(); + + for (MediaRoute2Info route : mRoutes.values()) { + if (route.supportsControlCategory(mControlCategories)) { + filteredRoutes.add(route); } - outRoutes.add(route); } + mFilteredRoutes = Collections.unmodifiableList(filteredRoutes); } - MediaRoute2Info findRouteById(String id) { - for (MediaRoute2Info route : mRoutes) { - if (route.getId().equals(id)) return route; + private void notifyRoutesAdded(List<MediaRoute2Info> routes) { + for (CallbackRecord record: mCallbackRecords) { + record.mExecutor.execute( + () -> record.mCallback.onRoutesAdded(routes)); } - return null; } - void notifyRouteListChanged(List<MediaRoute2Info> routes) { + private void notifyRoutesRemoved(List<MediaRoute2Info> routes) { for (CallbackRecord record: mCallbackRecords) { record.mExecutor.execute( - () -> record.mCallback.onRoutesChanged(routes)); + () -> record.mCallback.onRoutesRemoved(routes)); } } - void notifyRouteChanged(MediaRoute2Info route) { + private void notifyRoutesChanged(List<MediaRoute2Info> routes) { for (CallbackRecord record: mCallbackRecords) { record.mExecutor.execute( - () -> record.mCallback.onRouteChanged(route)); + () -> record.mCallback.onRoutesChanged(routes)); } } @@ -405,23 +443,22 @@ public class MediaRouter2 { */ public static class Callback { //TODO: clean up these callbacks - /** - * Called when a route is added. - */ - public void onRouteAdded(MediaRoute2Info routeInfo) {} /** - * Called when a route is changed. + * Called when routes are added. + * @param routes the list of routes that have been added. It's never empty. */ - public void onRouteChanged(MediaRoute2Info routeInfo) {} + public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {} /** - * Called when a route is removed. + * Called when routes are removed. + * @param routes the list of routes that have been removed. It's never empty. */ - public void onRouteRemoved(MediaRoute2Info routeInfo) {} + public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {} /** - * Called when the list of routes is changed. + * Called when routes are changed. + * @param routes the list of routes that have been changed. It's never empty. */ public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {} } @@ -436,11 +473,10 @@ public class MediaRouter2 { } void notifyRoutes() { - final List<MediaRoute2Info> routes = mRoutes; + final List<MediaRoute2Info> routes = mFilteredRoutes; // notify only when bound to media router service. - //TODO: Correct the condition when control category, default route, .. are finalized. if (routes.size() > 0) { - mExecutor.execute(() -> mCallback.onRoutesChanged(routes)); + mExecutor.execute(() -> mCallback.onRoutesAdded(routes)); } } } @@ -450,9 +486,21 @@ public class MediaRouter2 { public void notifyRestoreRoute() throws RemoteException {} @Override - public void notifyProviderInfosUpdated(List<MediaRoute2ProviderInfo> info) { - mHandler.sendMessage(obtainMessage(MediaRouter2::onProviderInfosUpdated, - MediaRouter2.this, info)); + public void notifyRoutesAdded(List<MediaRoute2Info> routes) { + mHandler.sendMessage(obtainMessage(MediaRouter2::addRoutesOnHandler, + MediaRouter2.this, routes)); + } + + @Override + public void notifyRoutesRemoved(List<MediaRoute2Info> routes) { + mHandler.sendMessage(obtainMessage(MediaRouter2::removeRoutesOnHandler, + MediaRouter2.this, routes)); + } + + @Override + public void notifyRoutesChanged(List<MediaRoute2Info> routes) { + mHandler.sendMessage(obtainMessage(MediaRouter2::changeRoutesOnHandler, + MediaRouter2.this, routes)); } } } diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 0d7b6ff0ea91..7e848a0bdf90 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -67,7 +67,7 @@ public class MediaRouter2Manager { @NonNull List<MediaRoute2Info> mRoutes = Collections.emptyList(); @NonNull - ConcurrentMap<String, List<String>> mControlCategoryMap = new ConcurrentHashMap<>(); + final ConcurrentMap<String, List<String>> mControlCategoryMap = new ConcurrentHashMap<>(); /** * Gets an instance of media router manager that controls media route of other applications. @@ -427,6 +427,8 @@ public class MediaRouter2Manager { * A client may refresh available routes for each application. */ public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {} + + //TODO: add onControlCategoriesChanged to notify available routes are changed } final class CallbackRecord { diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java index ca43d04573f3..acf899881600 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java @@ -22,6 +22,7 @@ import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.timeout; @@ -272,29 +273,28 @@ public class MediaRouterManagerTest { @Test public void testControlVolumeWithRouter() throws Exception { - MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class); - Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_ALL); - mRouter2.registerCallback(mExecutor, mockCallback); MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME); int originalVolume = volRoute.getVolume(); int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1); - int targetVolume = originalVolume + deltaVolume; - - mRouter2.requestSetVolume(volRoute, targetVolume); - verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce()) - .onRouteChanged(argThat(route -> - route.getId().equals(volRoute.getId()) - && route.getVolume() == targetVolume)); - - mRouter2.requestUpdateVolume(volRoute, -deltaVolume); - verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce()) - .onRouteChanged(argThat(route -> - route.getId().equals(volRoute.getId()) - && route.getVolume() == originalVolume)); - mRouter2.unregisterCallback(mockCallback); + CountDownLatch latch1 = new CountDownLatch(1); + MediaRouter2.Callback callback1 = + createVolumeChangeCallback(ROUTE_ID_VARIABLE_VOLUME, + originalVolume + deltaVolume, latch1); + mRouter2.registerCallback(mExecutor, callback1); + mRouter2.requestUpdateVolume(volRoute, deltaVolume); + assertTrue(latch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + mRouter2.unregisterCallback(callback1); + + CountDownLatch latch2 = new CountDownLatch(1); + MediaRouter2.Callback callback2 = + createVolumeChangeCallback(ROUTE_ID_VARIABLE_VOLUME, originalVolume, latch2); + mRouter2.registerCallback(mExecutor, callback2); + mRouter2.requestSetVolume(volRoute, originalVolume); + assertTrue(latch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + mRouter2.unregisterCallback(callback1); } @Test @@ -312,13 +312,13 @@ public class MediaRouterManagerTest { int targetVolume = originalVolume + deltaVolume; mManager.requestSetVolume(volRoute, targetVolume); - verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce()) + verify(managerCallback, timeout(TIMEOUT_MS).atLeastOnce()) .onRouteChanged(argThat(route -> route.getId().equals(volRoute.getId()) && route.getVolume() == targetVolume)); mManager.requestUpdateVolume(volRoute, -deltaVolume); - verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce()) + verify(managerCallback, timeout(TIMEOUT_MS).atLeastOnce()) .onRouteChanged(argThat(route -> route.getId().equals(volRoute.getId()) && route.getVolume() == originalVolume)); @@ -347,14 +347,14 @@ public class MediaRouterManagerTest { CountDownLatch latch = new CountDownLatch(1); MediaRouter2.Callback callback = new MediaRouter2.Callback() { @Override - public void onRoutesChanged(List<MediaRoute2Info> routes) { - if (routes.size() > 0) latch.countDown(); + public void onRoutesAdded(List<MediaRoute2Info> added) { + if (added.size() > 0) latch.countDown(); } }; mRouter2.setControlCategories(controlCategories); mRouter2.registerCallback(mExecutor, callback); try { - latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); + assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); return createRouteMap(mRouter2.getRoutes()); } finally { mRouter2.unregisterCallback(callback); @@ -370,14 +370,18 @@ public class MediaRouterManagerTest { MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() { @Override public void onRoutesChanged(List<MediaRoute2Info> routes) { - if (routes.size() > 0) latch.countDown(); + if (routes.size() > 0) { + latch.countDown(); + } } }; mManager.registerCallback(mExecutor, managerCallback); mRouter2.setControlCategories(controlCategories); mRouter2.registerCallback(mExecutor, routerCallback); try { - latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); + assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + //TODO: currently this returns empty list occasionally. + //Maybe due to control category is not set yet return createRouteMap(mManager.getAvailableRoutes(mPackageName)); } finally { mRouter2.unregisterCallback(routerCallback); @@ -385,6 +389,20 @@ public class MediaRouterManagerTest { } } + MediaRouter2.Callback createVolumeChangeCallback(String routeId, + int targetVolume, CountDownLatch latch) { + MediaRouter2.Callback callback = new MediaRouter2.Callback() { + @Override + public void onRoutesChanged(List<MediaRoute2Info> changed) { + MediaRoute2Info volRoute = createRouteMap(changed).get(routeId); + if (volRoute != null && volRoute.getVolume() == targetVolume) { + latch.countDown(); + } + } + }; + return callback; + } + // Helper for getting routes easily static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) { Map<String, MediaRoute2Info> routeMap = new HashMap<>(); diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java index 4816ceb5d76c..626bf1cc361b 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java @@ -119,6 +119,11 @@ final class MediaRoute2ProviderProxy implements ServiceConnection { } } + @NonNull + public String getUniqueId() { + return mUniqueId; + } + @Nullable public MediaRoute2ProviderInfo getProviderInfo() { return mProviderInfo; @@ -294,7 +299,7 @@ final class MediaRoute2ProviderProxy implements ServiceConnection { } public interface Callback { - void onProviderStateChanged(MediaRoute2ProviderProxy provider); + void onProviderStateChanged(@NonNull MediaRoute2ProviderProxy provider); } private final class Connection implements DeathRecipient { diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 361dc3673665..44642d4f85cd 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -47,9 +47,12 @@ import com.android.internal.util.function.pooled.PooledLambda; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; /** * TODO: Merge this to MediaRouterService once it's finished. @@ -357,8 +360,7 @@ class MediaRouter2ServiceImpl { mAllClientRecords.put(binder, clientRecord); userRecord.mHandler.sendMessage( - obtainMessage(UserHandler::notifyProviderInfosUpdatedToClient, - userRecord.mHandler, client)); + obtainMessage(UserHandler::notifyRoutesToClient, userRecord.mHandler, client)); } } @@ -586,6 +588,7 @@ class MediaRouter2ServiceImpl { final class UserRecord { public final int mUserId; + //TODO: make records private for thread-safety final ArrayList<ClientRecord> mClientRecords = new ArrayList<>(); final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>(); final UserHandler mHandler; @@ -705,7 +708,7 @@ class MediaRouter2ServiceImpl { //TODO: Make this thread-safe. private final ArrayList<MediaRoute2ProviderProxy> mMediaProviders = new ArrayList<>(); - private List<MediaRoute2ProviderInfo> mProviderInfos; + private final List<MediaRoute2ProviderInfo> mProviderInfos = new ArrayList<>(); private boolean mRunning; private boolean mProviderInfosUpdateScheduled; @@ -745,14 +748,89 @@ class MediaRouter2ServiceImpl { } @Override - public void onProviderStateChanged(MediaRoute2ProviderProxy provider) { - updateProvider(provider); + public void onProviderStateChanged(@NonNull MediaRoute2ProviderProxy provider) { + sendMessage(PooledLambda.obtainMessage(UserHandler::updateProvider, this, provider)); } private void updateProvider(MediaRoute2ProviderProxy provider) { + int providerIndex = getProviderInfoIndex(provider.getUniqueId()); + MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo(); + MediaRoute2ProviderInfo prevInfo = + (providerIndex < 0) ? null : mProviderInfos.get(providerIndex); + + if (Objects.equals(prevInfo, providerInfo)) return; + + if (prevInfo == null) { + mProviderInfos.add(providerInfo); + Collection<MediaRoute2Info> addedRoutes = providerInfo.getRoutes(); + if (addedRoutes.size() > 0) { + sendMessage(PooledLambda.obtainMessage(UserHandler::notifyRoutesAddedToClients, + this, getClients(), new ArrayList<>(addedRoutes))); + } + } else if (providerInfo == null) { + mProviderInfos.remove(prevInfo); + Collection<MediaRoute2Info> removedRoutes = prevInfo.getRoutes(); + if (removedRoutes.size() > 0) { + sendMessage(PooledLambda.obtainMessage( + UserHandler::notifyRoutesRemovedToClients, + this, getClients(), new ArrayList<>(removedRoutes))); + } + } else { + mProviderInfos.set(providerIndex, providerInfo); + List<MediaRoute2Info> addedRoutes = new ArrayList<>(); + List<MediaRoute2Info> removedRoutes = new ArrayList<>(); + List<MediaRoute2Info> changedRoutes = new ArrayList<>(); + + final Collection<MediaRoute2Info> currentRoutes = providerInfo.getRoutes(); + final Set<String> updatedRouteIds = new HashSet<>(); + + for (MediaRoute2Info route : currentRoutes) { + if (!route.isValid()) { + Slog.w(TAG, "Ignoring invalid route : " + route); + continue; + } + MediaRoute2Info prevRoute = prevInfo.getRoute(route.getId()); + + if (prevRoute != null) { + if (!Objects.equals(prevRoute, route)) { + changedRoutes.add(route); + } + updatedRouteIds.add(route.getId()); + } else { + addedRoutes.add(route); + } + } + + for (MediaRoute2Info prevRoute : prevInfo.getRoutes()) { + if (!updatedRouteIds.contains(prevRoute.getId())) { + removedRoutes.add(prevRoute); + } + } + + List<IMediaRouter2Client> clients = getClients(); + if (addedRoutes.size() > 0) { + notifyRoutesAddedToClients(clients, addedRoutes); + } + if (removedRoutes.size() > 0) { + notifyRoutesRemovedToClients(clients, removedRoutes); + } + if (changedRoutes.size() > 0) { + notifyRoutesChangedToClients(clients, changedRoutes); + } + } scheduleUpdateProviderInfos(); } + private int getProviderInfoIndex(String providerId) { + for (int i = 0; i < mProviderInfos.size(); i++) { + MediaRoute2ProviderInfo providerInfo = mProviderInfos.get(i); + if (TextUtils.equals(providerInfo.getUniqueId(), providerId)) { + return i; + } + } + return -1; + } + private void selectRoute(ClientRecord clientRecord, MediaRoute2Info route) { if (route != null) { MediaRoute2ProviderProxy provider = findProvider(route.getProviderId()); @@ -803,6 +881,7 @@ class MediaRouter2ServiceImpl { } } + //TODO: should be replaced into notifyRoutes...ToManagers private void updateProviderInfos() { mProviderInfosUpdateScheduled = false; @@ -810,55 +889,83 @@ class MediaRouter2ServiceImpl { if (service == null) { return; } - final List<MediaRoute2ProviderInfo> providers = new ArrayList<>(); - for (MediaRoute2ProviderProxy mediaProvider : mMediaProviders) { - final MediaRoute2ProviderInfo providerInfo = - mediaProvider.getProviderInfo(); - if (providerInfo == null || !providerInfo.isValid()) { - Slog.w(TAG, "Ignoring invalid provider info : " + providerInfo); - } else { - providers.add(providerInfo); - } - } - mProviderInfos = providers; final List<IMediaRouter2Manager> managers = new ArrayList<>(); - final List<IMediaRouter2Client> clients = new ArrayList<>(); synchronized (service.mLock) { for (ManagerRecord managerRecord : mUserRecord.mManagerRecords) { managers.add(managerRecord.mManager); } + } + for (IMediaRouter2Manager manager : managers) { + notifyProviderInfosUpdatedToManager(manager); + } + } + + private List<IMediaRouter2Client> getClients() { + final List<IMediaRouter2Client> clients = new ArrayList<>(); + MediaRouter2ServiceImpl service = mServiceRef.get(); + if (service == null) { + return clients; + } + synchronized (service.mLock) { for (ClientRecord clientRecord : mUserRecord.mClientRecords) { if (clientRecord instanceof Client2Record) { clients.add(((Client2Record) clientRecord).mClient); } } } - for (IMediaRouter2Manager manager : managers) { - notifyProviderInfosUpdatedToManager(manager); - } - for (IMediaRouter2Client client : clients) { - notifyProviderInfosUpdatedToClient(client); - } + return clients; } - private void notifyProviderInfosUpdatedToClient(IMediaRouter2Client client) { - if (mProviderInfos == null) { - scheduleUpdateProviderInfos(); + private void notifyRoutesToClient(IMediaRouter2Client client) { + List<MediaRoute2Info> routes = new ArrayList<>(); + for (MediaRoute2ProviderInfo providerInfo : mProviderInfos) { + routes.addAll(providerInfo.getRoutes()); + } + if (routes.size() == 0) { return; } try { - client.notifyProviderInfosUpdated(mProviderInfos); + client.notifyRoutesAdded(routes); } catch (RemoteException ex) { - Slog.w(TAG, "Failed to notify provider infos updated. Client probably died."); + Slog.w(TAG, "Failed to notify all routes. Client probably died.", ex); } } - private void notifyProviderInfosUpdatedToManager(IMediaRouter2Manager manager) { - if (mProviderInfos == null) { - scheduleUpdateProviderInfos(); - return; + private void notifyRoutesAddedToClients(List<IMediaRouter2Client> clients, + List<MediaRoute2Info> routes) { + for (IMediaRouter2Client client : clients) { + try { + client.notifyRoutesAdded(routes); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify routes added. Client probably died.", ex); + } + } + } + + private void notifyRoutesRemovedToClients(List<IMediaRouter2Client> clients, + List<MediaRoute2Info> routes) { + for (IMediaRouter2Client client : clients) { + try { + client.notifyRoutesRemoved(routes); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify routes removed. Client probably died.", ex); + } } + } + + private void notifyRoutesChangedToClients(List<IMediaRouter2Client> clients, + List<MediaRoute2Info> routes) { + for (IMediaRouter2Client client : clients) { + try { + client.notifyRoutesChanged(routes); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify routes changed. Client probably died.", ex); + } + } + } + + private void notifyProviderInfosUpdatedToManager(IMediaRouter2Manager manager) { try { manager.notifyProviderInfosUpdated(mProviderInfos); } catch (RemoteException ex) { @@ -891,9 +998,7 @@ class MediaRouter2ServiceImpl { private MediaRoute2ProviderProxy findProvider(String providerId) { for (MediaRoute2ProviderProxy provider : mMediaProviders) { - final MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo(); - if (providerInfo != null - && TextUtils.equals(providerInfo.getUniqueId(), providerId)) { + if (TextUtils.equals(provider.getUniqueId(), providerId)) { return provider; } } |