Loading media/java/android/media/IMediaRouter2Manager.aidl +3 −1 Original line number Diff line number Diff line Loading @@ -25,5 +25,7 @@ import android.media.MediaRoute2Info; oneway interface IMediaRouter2Manager { void notifyRouteSelected(String packageName, in MediaRoute2Info route); void notifyControlCategoriesChanged(String packageName, in List<String> categories); void notifyProviderInfosUpdated(in List<MediaRoute2ProviderInfo> providers); void notifyRoutesAdded(in List<MediaRoute2Info> routes); void notifyRoutesRemoved(in List<MediaRoute2Info> routes); void notifyRoutesChanged(in List<MediaRoute2Info> routes); } media/java/android/media/MediaRouter2.java +5 −1 Original line number Diff line number Diff line Loading @@ -95,6 +95,10 @@ public class MediaRouter2 { private final String mPackageName; private final Map<String, MediaRoute2Info> mRoutes = new HashMap<>(); //TODO: Use a lock for this to cover the below use case // mRouter.setControlCategories(...); // routes = mRouter.getRoutes(); // The current implementation returns empty list private volatile List<String> mControlCategories = Collections.emptyList(); private MediaRoute2Info mSelectedRoute; Loading Loading @@ -202,6 +206,7 @@ public class MediaRouter2 { } catch (RemoteException ex) { Log.e(TAG, "Unable to unregister media router.", ex); } //TODO: Clean up mRoutes. (onHandler?) mClient = null; } } Loading @@ -222,7 +227,6 @@ public class MediaRouter2 { MediaRouter2.this, new ArrayList<>(controlCategories))); } /** * Gets the unmodifiable list of {@link MediaRoute2Info routes} currently * known to the media router. Loading media/java/android/media/MediaRouter2Manager.java +93 −120 Original line number Diff line number Diff line Loading @@ -25,18 +25,16 @@ import android.content.Context; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; Loading @@ -59,13 +57,11 @@ public class MediaRouter2Manager { private Client mClient; private final IMediaRouterService mMediaRouterService; final Handler mHandler; final List<CallbackRecord> mCallbacks = new CopyOnWriteArrayList<>(); final List<CallbackRecord> mCallbackRecords = new CopyOnWriteArrayList<>(); @SuppressWarnings("WeakerAccess") /* synthetic access */ @NonNull List<MediaRoute2ProviderInfo> mProviders = Collections.emptyList(); @NonNull List<MediaRoute2Info> mRoutes = Collections.emptyList(); private final Object mRoutesLock = new Object(); @GuardedBy("mRoutesLock") private final Map<String, MediaRoute2Info> mRoutes = new HashMap<>(); @NonNull final ConcurrentMap<String, List<String>> mControlCategoryMap = new ConcurrentHashMap<>(); Loading Loading @@ -104,13 +100,13 @@ public class MediaRouter2Manager { Objects.requireNonNull(callback, "callback must not be null"); CallbackRecord callbackRecord; synchronized (mCallbacks) { synchronized (mCallbackRecords) { if (findCallbackRecordIndexLocked(callback) >= 0) { Log.w(TAG, "Ignoring to add the same callback twice."); return; } callbackRecord = new CallbackRecord(executor, callback); mCallbacks.add(callbackRecord); mCallbackRecords.add(callbackRecord); } synchronized (sLock) { Loading @@ -136,32 +132,32 @@ public class MediaRouter2Manager { public void unregisterCallback(@NonNull Callback callback) { Objects.requireNonNull(callback, "callback must not be null"); synchronized (mCallbacks) { synchronized (mCallbackRecords) { final int index = findCallbackRecordIndexLocked(callback); if (index < 0) { Log.w(TAG, "Ignore removing unknown callback. " + callback); return; } mCallbacks.remove(index); mCallbackRecords.remove(index); synchronized (sLock) { if (mCallbacks.size() == 0 && mClient != null) { if (mCallbackRecords.size() == 0 && mClient != null) { try { mMediaRouterService.unregisterManager(mClient); } catch (RemoteException ex) { Log.e(TAG, "Unable to unregister media router manager", ex); } mClient.notifyProviderInfosUpdated(Collections.emptyList()); //TODO: clear mRoutes? mClient = null; } } } } @GuardedBy("mCallbacks") @GuardedBy("mCallbackRecords") private int findCallbackRecordIndexLocked(Callback callback) { final int count = mCallbacks.size(); final int count = mCallbackRecords.size(); for (int i = 0; i < count; i++) { if (mCallbacks.get(i).mCallback == callback) { if (mCallbackRecords.get(i).mCallback == callback) { return i; } } Loading @@ -184,11 +180,14 @@ public class MediaRouter2Manager { return Collections.emptyList(); } List<MediaRoute2Info> routes = new ArrayList<>(); for (MediaRoute2Info route : mRoutes) { synchronized (mRoutesLock) { for (MediaRoute2Info route : mRoutes.values()) { if (route.supportsControlCategory(controlCategories)) { routes.add(route); } } } //TODO: Should we cache this? return routes; } Loading Loading @@ -282,136 +281,96 @@ public class MediaRouter2Manager { } } int findProviderIndex(MediaRoute2ProviderInfo provider) { final int count = mProviders.size(); for (int i = 0; i < count; i++) { if (TextUtils.equals(mProviders.get(i).getUniqueId(), provider.getUniqueId())) { return i; void addRoutesOnHandler(List<MediaRoute2Info> routes) { synchronized (mRoutesLock) { for (MediaRoute2Info route : routes) { mRoutes.put(route.getUniqueId(), route); } } return -1; if (routes.size() > 0) { notifyRoutesAdded(routes); } void updateProvider(@NonNull MediaRoute2ProviderInfo provider) { if (provider == null || !provider.isValid()) { Log.w(TAG, "Ignoring invalid provider : " + provider); return; } final Collection<MediaRoute2Info> routes = provider.getRoutes(); final int index = findProviderIndex(provider); if (index >= 0) { final MediaRoute2ProviderInfo prevProvider = mProviders.get(index); final Set<String> updatedRouteIds = new HashSet<>(); for (MediaRoute2Info routeInfo : routes) { if (!routeInfo.isValid()) { Log.w(TAG, "Ignoring invalid route : " + routeInfo); continue; void removeRoutesOnHandler(List<MediaRoute2Info> routes) { synchronized (mRoutesLock) { for (MediaRoute2Info route : routes) { mRoutes.remove(route.getUniqueId()); } final MediaRoute2Info prevRoute = prevProvider.getRoute(routeInfo.getId()); if (prevRoute == null) { notifyRouteAdded(routeInfo); } else { if (!Objects.equals(prevRoute, routeInfo)) { notifyRouteChanged(routeInfo); } updatedRouteIds.add(routeInfo.getId()); if (routes.size() > 0) { notifyRoutesRemoved(routes); } } final Collection<MediaRoute2Info> prevRoutes = prevProvider.getRoutes(); for (MediaRoute2Info prevRoute : prevRoutes) { if (!updatedRouteIds.contains(prevRoute.getId())) { notifyRouteRemoved(prevRoute); void changeRoutesOnHandler(List<MediaRoute2Info> routes) { synchronized (mRoutesLock) { for (MediaRoute2Info route : routes) { mRoutes.put(route.getUniqueId(), route); } } } else { for (MediaRoute2Info routeInfo: routes) { notifyRouteAdded(routeInfo); } } } void notifyRouteAdded(MediaRoute2Info routeInfo) { for (CallbackRecord record : mCallbacks) { record.mExecutor.execute( () -> record.mCallback.onRouteAdded(routeInfo)); if (routes.size() > 0) { notifyRoutesChanged(routes); } } void notifyRouteChanged(MediaRoute2Info routeInfo) { for (CallbackRecord record : mCallbacks) { private void notifyRoutesAdded(List<MediaRoute2Info> routes) { for (CallbackRecord record: mCallbackRecords) { record.mExecutor.execute( () -> record.mCallback.onRouteChanged(routeInfo)); () -> record.mCallback.onRoutesAdded(routes)); } } void notifyRouteRemoved(MediaRoute2Info routeInfo) { for (CallbackRecord record : mCallbacks) { private void notifyRoutesRemoved(List<MediaRoute2Info> routes) { for (CallbackRecord record: mCallbackRecords) { record.mExecutor.execute( () -> record.mCallback.onRouteRemoved(routeInfo)); () -> record.mCallback.onRoutesRemoved(routes)); } } void notifyRouteListChanged() { for (CallbackRecord record: mCallbacks) { private void notifyRoutesChanged(List<MediaRoute2Info> routes) { for (CallbackRecord record: mCallbackRecords) { record.mExecutor.execute( () -> record.mCallback.onRoutesChanged(mRoutes)); () -> record.mCallback.onRoutesChanged(routes)); } } void notifyProviderInfosUpdated(List<MediaRoute2ProviderInfo> providers) { if (providers == null) { Log.w(TAG, "Providers info is null."); return; } ArrayList<MediaRoute2Info> routes = new ArrayList<>(); for (MediaRoute2ProviderInfo provider : providers) { updateProvider(provider); //TODO: Should we do this in updateProvider()? routes.addAll(provider.getRoutes()); } //TODO: Call notifyRouteRemoved for the routes of the removed providers. //TODO: Filter invalid providers and invalid routes. mProviders = providers; mRoutes = routes; //TODO: Call this when only the list is modified. notifyRouteListChanged(); } void notifyRouteSelected(String packageName, MediaRoute2Info route) { for (CallbackRecord record : mCallbacks) { for (CallbackRecord record : mCallbackRecords) { record.mExecutor.execute(() -> record.mCallback.onRouteSelected(packageName, route)); } } void updateControlCategories(String packageName, List<String> categories) { mControlCategoryMap.put(packageName, categories); for (CallbackRecord record : mCallbackRecords) { record.mExecutor.execute( () -> record.mCallback.onControlCategoriesChanged(packageName)); } } /** * Interface for receiving events about media routing changes. */ public static class Callback { /** * Called when a route is added. * Called when routes are added. * @param routes the list of routes that have been added. It's never empty. */ public void onRouteAdded(@NonNull MediaRoute2Info routeInfo) {} public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {} /** * Called when a route is changed. * Called when routes are removed. * @param routes the list of routes that have been removed. It's never empty. */ public void onRouteChanged(@NonNull MediaRoute2Info routeInfo) {} public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {} /** * Called when a route is removed. * Called when routes are changed. * @param routes the list of routes that have been changed. It's never empty. */ public void onRouteRemoved(@NonNull MediaRoute2Info routeInfo) {} public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {} /** * Called when a route is selected for an application. Loading @@ -422,13 +381,13 @@ public class MediaRouter2Manager { */ public void onRouteSelected(@NonNull String packageName, @Nullable MediaRoute2Info route) {} /** * Called when the list of routes is changed. * A client may refresh available routes for each application. * Called when the control categories of an app is changed. * * @param packageName the package name of the application */ public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {} //TODO: add onControlCategoriesChanged to notify available routes are changed public void onControlCategoriesChanged(@NonNull String packageName) {} } final class CallbackRecord { Loading @@ -441,10 +400,12 @@ public class MediaRouter2Manager { } void notifyRoutes() { mExecutor.execute(() -> mCallback.onRoutesChanged(mRoutes)); for (MediaRoute2Info routeInfo : mRoutes) { mExecutor.execute( () -> mCallback.onRouteAdded(routeInfo)); List<MediaRoute2Info> routes; synchronized (mRoutesLock) { routes = new ArrayList<>(mRoutes.values()); } if (routes.size() > 0) { mExecutor.execute(() -> mCallback.onRoutesAdded(routes)); } } } Loading @@ -463,9 +424,21 @@ public class MediaRouter2Manager { } @Override public void notifyProviderInfosUpdated(List<MediaRoute2ProviderInfo> info) { mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifyProviderInfosUpdated, MediaRouter2Manager.this, info)); public void notifyRoutesAdded(List<MediaRoute2Info> routes) { mHandler.sendMessage(obtainMessage(MediaRouter2Manager::addRoutesOnHandler, MediaRouter2Manager.this, routes)); } @Override public void notifyRoutesRemoved(List<MediaRoute2Info> routes) { mHandler.sendMessage(obtainMessage(MediaRouter2Manager::removeRoutesOnHandler, MediaRouter2Manager.this, routes)); } @Override public void notifyRoutesChanged(List<MediaRoute2Info> routes) { mHandler.sendMessage(obtainMessage(MediaRouter2Manager::changeRoutesOnHandler, MediaRouter2Manager.this, routes)); } } } media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +114 −104 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +62 −32 Original line number Diff line number Diff line Loading @@ -469,7 +469,7 @@ class MediaRouter2ServiceImpl { mAllManagerRecords.put(binder, managerRecord); userRecord.mHandler.sendMessage( obtainMessage(UserHandler::notifyProviderInfosUpdatedToManager, obtainMessage(UserHandler::notifyRoutesToManager, userRecord.mHandler, manager)); for (ClientRecord clientRecord : userRecord.mClientRecords) { Loading Loading @@ -808,17 +808,20 @@ class MediaRouter2ServiceImpl { } List<IMediaRouter2Client> clients = getClients(); List<IMediaRouter2Manager> managers = getManagers(); if (addedRoutes.size() > 0) { notifyRoutesAddedToClients(clients, addedRoutes); notifyRoutesAddedToManagers(managers, addedRoutes); } if (removedRoutes.size() > 0) { notifyRoutesRemovedToClients(clients, removedRoutes); notifyRoutesRemovedToManagers(managers, removedRoutes); } if (changedRoutes.size() > 0) { notifyRoutesChangedToClients(clients, changedRoutes); notifyRoutesChangedToManagers(managers, changedRoutes); } } scheduleUpdateProviderInfos(); } private int getProviderInfoIndex(String providerId) { Loading Loading @@ -874,47 +877,34 @@ class MediaRouter2ServiceImpl { } } private void scheduleUpdateProviderInfos() { if (!mProviderInfosUpdateScheduled) { mProviderInfosUpdateScheduled = true; sendMessage(PooledLambda.obtainMessage(UserHandler::updateProviderInfos, this)); } } //TODO: should be replaced into notifyRoutes...ToManagers private void updateProviderInfos() { mProviderInfosUpdateScheduled = false; private List<IMediaRouter2Client> getClients() { final List<IMediaRouter2Client> clients = new ArrayList<>(); MediaRouter2ServiceImpl service = mServiceRef.get(); if (service == null) { return; return clients; } final List<IMediaRouter2Manager> managers = new ArrayList<>(); synchronized (service.mLock) { for (ManagerRecord managerRecord : mUserRecord.mManagerRecords) { managers.add(managerRecord.mManager); for (ClientRecord clientRecord : mUserRecord.mClientRecords) { if (clientRecord instanceof Client2Record) { clients.add(((Client2Record) clientRecord).mClient); } } for (IMediaRouter2Manager manager : managers) { notifyProviderInfosUpdatedToManager(manager); } return clients; } private List<IMediaRouter2Client> getClients() { final List<IMediaRouter2Client> clients = new ArrayList<>(); private List<IMediaRouter2Manager> getManagers() { final List<IMediaRouter2Manager> managers = new ArrayList<>(); MediaRouter2ServiceImpl service = mServiceRef.get(); if (service == null) { return clients; return managers; } synchronized (service.mLock) { for (ClientRecord clientRecord : mUserRecord.mClientRecords) { if (clientRecord instanceof Client2Record) { clients.add(((Client2Record) clientRecord).mClient); for (ManagerRecord managerRecord : mUserRecord.mManagerRecords) { managers.add(managerRecord.mManager); } } } return clients; return managers; } private void notifyRoutesToClient(IMediaRouter2Client client) { Loading Loading @@ -965,11 +955,51 @@ class MediaRouter2ServiceImpl { } } private void notifyProviderInfosUpdatedToManager(IMediaRouter2Manager manager) { private void notifyRoutesToManager(IMediaRouter2Manager manager) { List<MediaRoute2Info> routes = new ArrayList<>(); for (MediaRoute2ProviderInfo providerInfo : mProviderInfos) { routes.addAll(providerInfo.getRoutes()); } if (routes.size() == 0) { return; } try { manager.notifyProviderInfosUpdated(mProviderInfos); manager.notifyRoutesAdded(routes); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify provider infos updated. Manager probably died."); Slog.w(TAG, "Failed to notify all routes. Manager probably died.", ex); } } private void notifyRoutesAddedToManagers(List<IMediaRouter2Manager> managers, List<MediaRoute2Info> routes) { for (IMediaRouter2Manager manager : managers) { try { manager.notifyRoutesAdded(routes); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify routes added. Manager probably died.", ex); } } } private void notifyRoutesRemovedToManagers(List<IMediaRouter2Manager> managers, List<MediaRoute2Info> routes) { for (IMediaRouter2Manager manager : managers) { try { manager.notifyRoutesRemoved(routes); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify routes removed. Manager probably died.", ex); } } } private void notifyRoutesChangedToManagers(List<IMediaRouter2Manager> managers, List<MediaRoute2Info> routes) { for (IMediaRouter2Manager manager : managers) { try { manager.notifyRoutesChanged(routes); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify routes changed. Manager probably died.", ex); } } } Loading Loading
media/java/android/media/IMediaRouter2Manager.aidl +3 −1 Original line number Diff line number Diff line Loading @@ -25,5 +25,7 @@ import android.media.MediaRoute2Info; oneway interface IMediaRouter2Manager { void notifyRouteSelected(String packageName, in MediaRoute2Info route); void notifyControlCategoriesChanged(String packageName, in List<String> categories); void notifyProviderInfosUpdated(in List<MediaRoute2ProviderInfo> providers); void notifyRoutesAdded(in List<MediaRoute2Info> routes); void notifyRoutesRemoved(in List<MediaRoute2Info> routes); void notifyRoutesChanged(in List<MediaRoute2Info> routes); }
media/java/android/media/MediaRouter2.java +5 −1 Original line number Diff line number Diff line Loading @@ -95,6 +95,10 @@ public class MediaRouter2 { private final String mPackageName; private final Map<String, MediaRoute2Info> mRoutes = new HashMap<>(); //TODO: Use a lock for this to cover the below use case // mRouter.setControlCategories(...); // routes = mRouter.getRoutes(); // The current implementation returns empty list private volatile List<String> mControlCategories = Collections.emptyList(); private MediaRoute2Info mSelectedRoute; Loading Loading @@ -202,6 +206,7 @@ public class MediaRouter2 { } catch (RemoteException ex) { Log.e(TAG, "Unable to unregister media router.", ex); } //TODO: Clean up mRoutes. (onHandler?) mClient = null; } } Loading @@ -222,7 +227,6 @@ public class MediaRouter2 { MediaRouter2.this, new ArrayList<>(controlCategories))); } /** * Gets the unmodifiable list of {@link MediaRoute2Info routes} currently * known to the media router. Loading
media/java/android/media/MediaRouter2Manager.java +93 −120 Original line number Diff line number Diff line Loading @@ -25,18 +25,16 @@ import android.content.Context; import android.os.Handler; import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; Loading @@ -59,13 +57,11 @@ public class MediaRouter2Manager { private Client mClient; private final IMediaRouterService mMediaRouterService; final Handler mHandler; final List<CallbackRecord> mCallbacks = new CopyOnWriteArrayList<>(); final List<CallbackRecord> mCallbackRecords = new CopyOnWriteArrayList<>(); @SuppressWarnings("WeakerAccess") /* synthetic access */ @NonNull List<MediaRoute2ProviderInfo> mProviders = Collections.emptyList(); @NonNull List<MediaRoute2Info> mRoutes = Collections.emptyList(); private final Object mRoutesLock = new Object(); @GuardedBy("mRoutesLock") private final Map<String, MediaRoute2Info> mRoutes = new HashMap<>(); @NonNull final ConcurrentMap<String, List<String>> mControlCategoryMap = new ConcurrentHashMap<>(); Loading Loading @@ -104,13 +100,13 @@ public class MediaRouter2Manager { Objects.requireNonNull(callback, "callback must not be null"); CallbackRecord callbackRecord; synchronized (mCallbacks) { synchronized (mCallbackRecords) { if (findCallbackRecordIndexLocked(callback) >= 0) { Log.w(TAG, "Ignoring to add the same callback twice."); return; } callbackRecord = new CallbackRecord(executor, callback); mCallbacks.add(callbackRecord); mCallbackRecords.add(callbackRecord); } synchronized (sLock) { Loading @@ -136,32 +132,32 @@ public class MediaRouter2Manager { public void unregisterCallback(@NonNull Callback callback) { Objects.requireNonNull(callback, "callback must not be null"); synchronized (mCallbacks) { synchronized (mCallbackRecords) { final int index = findCallbackRecordIndexLocked(callback); if (index < 0) { Log.w(TAG, "Ignore removing unknown callback. " + callback); return; } mCallbacks.remove(index); mCallbackRecords.remove(index); synchronized (sLock) { if (mCallbacks.size() == 0 && mClient != null) { if (mCallbackRecords.size() == 0 && mClient != null) { try { mMediaRouterService.unregisterManager(mClient); } catch (RemoteException ex) { Log.e(TAG, "Unable to unregister media router manager", ex); } mClient.notifyProviderInfosUpdated(Collections.emptyList()); //TODO: clear mRoutes? mClient = null; } } } } @GuardedBy("mCallbacks") @GuardedBy("mCallbackRecords") private int findCallbackRecordIndexLocked(Callback callback) { final int count = mCallbacks.size(); final int count = mCallbackRecords.size(); for (int i = 0; i < count; i++) { if (mCallbacks.get(i).mCallback == callback) { if (mCallbackRecords.get(i).mCallback == callback) { return i; } } Loading @@ -184,11 +180,14 @@ public class MediaRouter2Manager { return Collections.emptyList(); } List<MediaRoute2Info> routes = new ArrayList<>(); for (MediaRoute2Info route : mRoutes) { synchronized (mRoutesLock) { for (MediaRoute2Info route : mRoutes.values()) { if (route.supportsControlCategory(controlCategories)) { routes.add(route); } } } //TODO: Should we cache this? return routes; } Loading Loading @@ -282,136 +281,96 @@ public class MediaRouter2Manager { } } int findProviderIndex(MediaRoute2ProviderInfo provider) { final int count = mProviders.size(); for (int i = 0; i < count; i++) { if (TextUtils.equals(mProviders.get(i).getUniqueId(), provider.getUniqueId())) { return i; void addRoutesOnHandler(List<MediaRoute2Info> routes) { synchronized (mRoutesLock) { for (MediaRoute2Info route : routes) { mRoutes.put(route.getUniqueId(), route); } } return -1; if (routes.size() > 0) { notifyRoutesAdded(routes); } void updateProvider(@NonNull MediaRoute2ProviderInfo provider) { if (provider == null || !provider.isValid()) { Log.w(TAG, "Ignoring invalid provider : " + provider); return; } final Collection<MediaRoute2Info> routes = provider.getRoutes(); final int index = findProviderIndex(provider); if (index >= 0) { final MediaRoute2ProviderInfo prevProvider = mProviders.get(index); final Set<String> updatedRouteIds = new HashSet<>(); for (MediaRoute2Info routeInfo : routes) { if (!routeInfo.isValid()) { Log.w(TAG, "Ignoring invalid route : " + routeInfo); continue; void removeRoutesOnHandler(List<MediaRoute2Info> routes) { synchronized (mRoutesLock) { for (MediaRoute2Info route : routes) { mRoutes.remove(route.getUniqueId()); } final MediaRoute2Info prevRoute = prevProvider.getRoute(routeInfo.getId()); if (prevRoute == null) { notifyRouteAdded(routeInfo); } else { if (!Objects.equals(prevRoute, routeInfo)) { notifyRouteChanged(routeInfo); } updatedRouteIds.add(routeInfo.getId()); if (routes.size() > 0) { notifyRoutesRemoved(routes); } } final Collection<MediaRoute2Info> prevRoutes = prevProvider.getRoutes(); for (MediaRoute2Info prevRoute : prevRoutes) { if (!updatedRouteIds.contains(prevRoute.getId())) { notifyRouteRemoved(prevRoute); void changeRoutesOnHandler(List<MediaRoute2Info> routes) { synchronized (mRoutesLock) { for (MediaRoute2Info route : routes) { mRoutes.put(route.getUniqueId(), route); } } } else { for (MediaRoute2Info routeInfo: routes) { notifyRouteAdded(routeInfo); } } } void notifyRouteAdded(MediaRoute2Info routeInfo) { for (CallbackRecord record : mCallbacks) { record.mExecutor.execute( () -> record.mCallback.onRouteAdded(routeInfo)); if (routes.size() > 0) { notifyRoutesChanged(routes); } } void notifyRouteChanged(MediaRoute2Info routeInfo) { for (CallbackRecord record : mCallbacks) { private void notifyRoutesAdded(List<MediaRoute2Info> routes) { for (CallbackRecord record: mCallbackRecords) { record.mExecutor.execute( () -> record.mCallback.onRouteChanged(routeInfo)); () -> record.mCallback.onRoutesAdded(routes)); } } void notifyRouteRemoved(MediaRoute2Info routeInfo) { for (CallbackRecord record : mCallbacks) { private void notifyRoutesRemoved(List<MediaRoute2Info> routes) { for (CallbackRecord record: mCallbackRecords) { record.mExecutor.execute( () -> record.mCallback.onRouteRemoved(routeInfo)); () -> record.mCallback.onRoutesRemoved(routes)); } } void notifyRouteListChanged() { for (CallbackRecord record: mCallbacks) { private void notifyRoutesChanged(List<MediaRoute2Info> routes) { for (CallbackRecord record: mCallbackRecords) { record.mExecutor.execute( () -> record.mCallback.onRoutesChanged(mRoutes)); () -> record.mCallback.onRoutesChanged(routes)); } } void notifyProviderInfosUpdated(List<MediaRoute2ProviderInfo> providers) { if (providers == null) { Log.w(TAG, "Providers info is null."); return; } ArrayList<MediaRoute2Info> routes = new ArrayList<>(); for (MediaRoute2ProviderInfo provider : providers) { updateProvider(provider); //TODO: Should we do this in updateProvider()? routes.addAll(provider.getRoutes()); } //TODO: Call notifyRouteRemoved for the routes of the removed providers. //TODO: Filter invalid providers and invalid routes. mProviders = providers; mRoutes = routes; //TODO: Call this when only the list is modified. notifyRouteListChanged(); } void notifyRouteSelected(String packageName, MediaRoute2Info route) { for (CallbackRecord record : mCallbacks) { for (CallbackRecord record : mCallbackRecords) { record.mExecutor.execute(() -> record.mCallback.onRouteSelected(packageName, route)); } } void updateControlCategories(String packageName, List<String> categories) { mControlCategoryMap.put(packageName, categories); for (CallbackRecord record : mCallbackRecords) { record.mExecutor.execute( () -> record.mCallback.onControlCategoriesChanged(packageName)); } } /** * Interface for receiving events about media routing changes. */ public static class Callback { /** * Called when a route is added. * Called when routes are added. * @param routes the list of routes that have been added. It's never empty. */ public void onRouteAdded(@NonNull MediaRoute2Info routeInfo) {} public void onRoutesAdded(@NonNull List<MediaRoute2Info> routes) {} /** * Called when a route is changed. * Called when routes are removed. * @param routes the list of routes that have been removed. It's never empty. */ public void onRouteChanged(@NonNull MediaRoute2Info routeInfo) {} public void onRoutesRemoved(@NonNull List<MediaRoute2Info> routes) {} /** * Called when a route is removed. * Called when routes are changed. * @param routes the list of routes that have been changed. It's never empty. */ public void onRouteRemoved(@NonNull MediaRoute2Info routeInfo) {} public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {} /** * Called when a route is selected for an application. Loading @@ -422,13 +381,13 @@ public class MediaRouter2Manager { */ public void onRouteSelected(@NonNull String packageName, @Nullable MediaRoute2Info route) {} /** * Called when the list of routes is changed. * A client may refresh available routes for each application. * Called when the control categories of an app is changed. * * @param packageName the package name of the application */ public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {} //TODO: add onControlCategoriesChanged to notify available routes are changed public void onControlCategoriesChanged(@NonNull String packageName) {} } final class CallbackRecord { Loading @@ -441,10 +400,12 @@ public class MediaRouter2Manager { } void notifyRoutes() { mExecutor.execute(() -> mCallback.onRoutesChanged(mRoutes)); for (MediaRoute2Info routeInfo : mRoutes) { mExecutor.execute( () -> mCallback.onRouteAdded(routeInfo)); List<MediaRoute2Info> routes; synchronized (mRoutesLock) { routes = new ArrayList<>(mRoutes.values()); } if (routes.size() > 0) { mExecutor.execute(() -> mCallback.onRoutesAdded(routes)); } } } Loading @@ -463,9 +424,21 @@ public class MediaRouter2Manager { } @Override public void notifyProviderInfosUpdated(List<MediaRoute2ProviderInfo> info) { mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifyProviderInfosUpdated, MediaRouter2Manager.this, info)); public void notifyRoutesAdded(List<MediaRoute2Info> routes) { mHandler.sendMessage(obtainMessage(MediaRouter2Manager::addRoutesOnHandler, MediaRouter2Manager.this, routes)); } @Override public void notifyRoutesRemoved(List<MediaRoute2Info> routes) { mHandler.sendMessage(obtainMessage(MediaRouter2Manager::removeRoutesOnHandler, MediaRouter2Manager.this, routes)); } @Override public void notifyRoutesChanged(List<MediaRoute2Info> routes) { mHandler.sendMessage(obtainMessage(MediaRouter2Manager::changeRoutesOnHandler, MediaRouter2Manager.this, routes)); } } }
media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +114 −104 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +62 −32 Original line number Diff line number Diff line Loading @@ -469,7 +469,7 @@ class MediaRouter2ServiceImpl { mAllManagerRecords.put(binder, managerRecord); userRecord.mHandler.sendMessage( obtainMessage(UserHandler::notifyProviderInfosUpdatedToManager, obtainMessage(UserHandler::notifyRoutesToManager, userRecord.mHandler, manager)); for (ClientRecord clientRecord : userRecord.mClientRecords) { Loading Loading @@ -808,17 +808,20 @@ class MediaRouter2ServiceImpl { } List<IMediaRouter2Client> clients = getClients(); List<IMediaRouter2Manager> managers = getManagers(); if (addedRoutes.size() > 0) { notifyRoutesAddedToClients(clients, addedRoutes); notifyRoutesAddedToManagers(managers, addedRoutes); } if (removedRoutes.size() > 0) { notifyRoutesRemovedToClients(clients, removedRoutes); notifyRoutesRemovedToManagers(managers, removedRoutes); } if (changedRoutes.size() > 0) { notifyRoutesChangedToClients(clients, changedRoutes); notifyRoutesChangedToManagers(managers, changedRoutes); } } scheduleUpdateProviderInfos(); } private int getProviderInfoIndex(String providerId) { Loading Loading @@ -874,47 +877,34 @@ class MediaRouter2ServiceImpl { } } private void scheduleUpdateProviderInfos() { if (!mProviderInfosUpdateScheduled) { mProviderInfosUpdateScheduled = true; sendMessage(PooledLambda.obtainMessage(UserHandler::updateProviderInfos, this)); } } //TODO: should be replaced into notifyRoutes...ToManagers private void updateProviderInfos() { mProviderInfosUpdateScheduled = false; private List<IMediaRouter2Client> getClients() { final List<IMediaRouter2Client> clients = new ArrayList<>(); MediaRouter2ServiceImpl service = mServiceRef.get(); if (service == null) { return; return clients; } final List<IMediaRouter2Manager> managers = new ArrayList<>(); synchronized (service.mLock) { for (ManagerRecord managerRecord : mUserRecord.mManagerRecords) { managers.add(managerRecord.mManager); for (ClientRecord clientRecord : mUserRecord.mClientRecords) { if (clientRecord instanceof Client2Record) { clients.add(((Client2Record) clientRecord).mClient); } } for (IMediaRouter2Manager manager : managers) { notifyProviderInfosUpdatedToManager(manager); } return clients; } private List<IMediaRouter2Client> getClients() { final List<IMediaRouter2Client> clients = new ArrayList<>(); private List<IMediaRouter2Manager> getManagers() { final List<IMediaRouter2Manager> managers = new ArrayList<>(); MediaRouter2ServiceImpl service = mServiceRef.get(); if (service == null) { return clients; return managers; } synchronized (service.mLock) { for (ClientRecord clientRecord : mUserRecord.mClientRecords) { if (clientRecord instanceof Client2Record) { clients.add(((Client2Record) clientRecord).mClient); for (ManagerRecord managerRecord : mUserRecord.mManagerRecords) { managers.add(managerRecord.mManager); } } } return clients; return managers; } private void notifyRoutesToClient(IMediaRouter2Client client) { Loading Loading @@ -965,11 +955,51 @@ class MediaRouter2ServiceImpl { } } private void notifyProviderInfosUpdatedToManager(IMediaRouter2Manager manager) { private void notifyRoutesToManager(IMediaRouter2Manager manager) { List<MediaRoute2Info> routes = new ArrayList<>(); for (MediaRoute2ProviderInfo providerInfo : mProviderInfos) { routes.addAll(providerInfo.getRoutes()); } if (routes.size() == 0) { return; } try { manager.notifyProviderInfosUpdated(mProviderInfos); manager.notifyRoutesAdded(routes); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify provider infos updated. Manager probably died."); Slog.w(TAG, "Failed to notify all routes. Manager probably died.", ex); } } private void notifyRoutesAddedToManagers(List<IMediaRouter2Manager> managers, List<MediaRoute2Info> routes) { for (IMediaRouter2Manager manager : managers) { try { manager.notifyRoutesAdded(routes); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify routes added. Manager probably died.", ex); } } } private void notifyRoutesRemovedToManagers(List<IMediaRouter2Manager> managers, List<MediaRoute2Info> routes) { for (IMediaRouter2Manager manager : managers) { try { manager.notifyRoutesRemoved(routes); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify routes removed. Manager probably died.", ex); } } } private void notifyRoutesChangedToManagers(List<IMediaRouter2Manager> managers, List<MediaRoute2Info> routes) { for (IMediaRouter2Manager manager : managers) { try { manager.notifyRoutesChanged(routes); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify routes changed. Manager probably died.", ex); } } } Loading