Loading media/java/android/media/IMediaRouterClient.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -22,4 +22,5 @@ package android.media; oneway interface IMediaRouterClient { void onStateChanged(); void onRestoreRoute(); void onSelectedRouteChanged(String routeId); } media/java/android/media/IMediaRouterService.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ interface IMediaRouterService { void registerClientAsUser(IMediaRouterClient client, String packageName, int userId); void unregisterClient(IMediaRouterClient client); void registerClientGroupId(IMediaRouterClient client, String groupId); MediaRouterClientState getState(IMediaRouterClient client); boolean isPlaybackActive(IMediaRouterClient client); Loading media/java/android/media/MediaRouter.java +61 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.Manifest; import android.annotation.DrawableRes; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; Loading Loading @@ -343,6 +344,16 @@ public class MediaRouter { updatePresentationDisplays(displayId); } public void setRouterGroupId(String groupId) { if (mClient != null) { try { mMediaRouterService.registerClientGroupId(mClient, groupId); } catch (RemoteException ex) { Log.e(TAG, "Unable to register group ID of the client.", ex); } } } public Display[] getAllPresentationDisplays() { return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION); } Loading @@ -358,6 +369,21 @@ public class MediaRouter { } } void updateSelectedRouteForId(String routeId) { RouteInfo selectedRoute = isBluetoothA2dpOn() ? mBluetoothA2dpRoute : mDefaultAudioVideo; final int count = mRoutes.size(); for (int i = 0; i < count; i++) { final RouteInfo route = mRoutes.get(i); if (TextUtils.equals(route.mGlobalRouteId, routeId)) { selectedRoute = route; } } if (selectedRoute != mSelectedRoute) { selectRouteStatic(selectedRoute.mSupportedTypes, selectedRoute, false); } } void setSelectedRoute(RouteInfo info, boolean explicit) { // Must be non-reentrant. mSelectedRoute = info; Loading Loading @@ -619,6 +645,15 @@ public class MediaRouter { } }); } @Override public void onSelectedRouteChanged(String routeId) { mHandler.post(() -> { if (Client.this == mClient) { updateSelectedRouteForId(routeId); } }); } } } Loading Loading @@ -728,6 +763,13 @@ public class MediaRouter { */ public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0; /** * The route group id used for sharing the selected mirroring device. * System UI and Settings use this to synchronize their mirroring status. * @hide */ public static final String MIRRORING_GROUP_ID = "android.media.mirroring_group"; // Maps application contexts static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>(); Loading Loading @@ -847,6 +889,25 @@ public class MediaRouter { return false; } /** * Sets the group ID of the router. * Media routers with the same ID acts as if they were a single media router. * For example, if a media router selects a route, the selected route of routers * with the same group ID will be changed automatically. * * Two routers in a group are supposed to use the same route types. * * System UI and Settings use this to synchronize their mirroring status. * Do not set the router group id unless it's necessary. * * {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY} permission is required to * call this method. * @hide */ public void setRouterGroupId(@Nullable String groupId) { sStatic.setRouterGroupId(groupId); } /** * Add a callback to listen to events about specific kinds of media routes. * If the specified callback is already registered, its registration will be updated for any Loading services/core/java/com/android/server/media/MediaRouterService.java +122 −1 Original line number Diff line number Diff line Loading @@ -246,6 +246,29 @@ public final class MediaRouterService extends IMediaRouterService.Stub } } // Binder call @Override public void registerClientGroupId(IMediaRouterClient client, String groupId) { if (client == null) { throw new NullPointerException("client must not be null"); } if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) != PackageManager.PERMISSION_GRANTED) { Log.w(TAG, "Ignoring client group request because " + "the client doesn't have the CONFIGURE_WIFI_DISPLAY permission."); return; } final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { registerClientGroupIdLocked(client, groupId); } } finally { Binder.restoreCallingIdentity(token); } } // Binder call @Override public void unregisterClient(IMediaRouterClient client) { Loading Loading @@ -502,11 +525,37 @@ public final class MediaRouterService extends IMediaRouterService.Stub } } private void registerClientGroupIdLocked(IMediaRouterClient client, String groupId) { final IBinder binder = client.asBinder(); ClientRecord clientRecord = mAllClientRecords.get(binder); if (clientRecord == null) { Log.w(TAG, "Ignoring group id register request of a unregistered client."); return; } if (TextUtils.equals(clientRecord.mGroupId, groupId)) { return; } UserRecord userRecord = clientRecord.mUserRecord; if (clientRecord.mGroupId != null) { userRecord.removeFromGroup(clientRecord.mGroupId, clientRecord); } clientRecord.mGroupId = groupId; if (groupId != null) { userRecord.addToGroup(groupId, clientRecord); userRecord.mHandler.obtainMessage(UserHandler.MSG_UPDATE_SELECTED_ROUTE, groupId) .sendToTarget(); } } private void unregisterClientLocked(IMediaRouterClient client, boolean died) { ClientRecord clientRecord = mAllClientRecords.remove(client.asBinder()); if (clientRecord != null) { UserRecord userRecord = clientRecord.mUserRecord; userRecord.mClientRecords.remove(clientRecord); if (clientRecord.mGroupId != null) { userRecord.removeFromGroup(clientRecord.mGroupId, clientRecord); clientRecord.mGroupId = null; } disposeClientLocked(clientRecord, died); disposeUserIfNeededLocked(userRecord); // since client removed from user } Loading Loading @@ -568,6 +617,16 @@ public final class MediaRouterService extends IMediaRouterService.Stub clientRecord.mUserRecord.mHandler.obtainMessage( UserHandler.MSG_SELECT_ROUTE, routeId).sendToTarget(); } if (clientRecord.mGroupId != null) { ClientGroup group = clientRecord.mUserRecord.mClientGroupMap.get(clientRecord.mGroupId); if (group != null) { group.mSelectedRouteId = routeId; clientRecord.mUserRecord.mHandler.obtainMessage( UserHandler.MSG_UPDATE_SELECTED_ROUTE, clientRecord.mGroupId) .sendToTarget(); } } } } } Loading Loading @@ -680,6 +739,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub public int mRouteTypes; public boolean mActiveScan; public String mSelectedRouteId; public String mGroupId; public ClientRecord(UserRecord userRecord, IMediaRouterClient client, int uid, int pid, String packageName, boolean trusted) { Loading Loading @@ -720,6 +780,11 @@ public final class MediaRouterService extends IMediaRouterService.Stub } } final class ClientGroup { public String mSelectedRouteId; public final List<ClientRecord> mClientRecords = new ArrayList<>(); } /** * Information about a particular user. * The contents of this object is guarded by mLock. Loading @@ -729,6 +794,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub public final ArrayList<ClientRecord> mClientRecords = new ArrayList<ClientRecord>(); public final UserHandler mHandler; public MediaRouterClientState mRouterState; private final ArrayMap<String, ClientGroup> mClientGroupMap = new ArrayMap<>(); public UserRecord(int userId) { mUserId = userId; Loading Loading @@ -761,6 +827,25 @@ public final class MediaRouterService extends IMediaRouterService.Stub } } public void addToGroup(String groupId, ClientRecord clientRecord) { ClientGroup group = mClientGroupMap.get(groupId); if (group == null) { group = new ClientGroup(); mClientGroupMap.put(groupId, group); } group.mClientRecords.add(clientRecord); } public void removeFromGroup(String groupId, ClientRecord clientRecord) { ClientGroup group = mClientGroupMap.get(groupId); if (group != null) { group.mClientRecords.remove(clientRecord); if (group.mClientRecords.size() == 0) { mClientGroupMap.remove(groupId); } } } @Override public String toString() { return "User " + mUserId; Loading Loading @@ -791,6 +876,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub public static final int MSG_REQUEST_UPDATE_VOLUME = 7; private static final int MSG_UPDATE_CLIENT_STATE = 8; private static final int MSG_CONNECTION_TIMED_OUT = 9; private static final int MSG_UPDATE_SELECTED_ROUTE = 10; private static final int TIMEOUT_REASON_NOT_AVAILABLE = 1; private static final int TIMEOUT_REASON_CONNECTION_LOST = 2; Loading Loading @@ -867,6 +953,10 @@ public final class MediaRouterService extends IMediaRouterService.Stub connectionTimedOut(); break; } case MSG_UPDATE_SELECTED_ROUTE: { updateSelectedRoute((String) msg.obj); break; } } } Loading Loading @@ -1191,6 +1281,37 @@ public final class MediaRouterService extends IMediaRouterService.Stub } } private void updateSelectedRoute(String groupId) { try { String selectedRouteId = null; synchronized (mService.mLock) { ClientGroup group = mUserRecord.mClientGroupMap.get(groupId); if (group == null) { return; } selectedRouteId = group.mSelectedRouteId; final int count = group.mClientRecords.size(); for (int i = 0; i < count; i++) { ClientRecord clientRecord = group.mClientRecords.get(i); if (!TextUtils.equals(selectedRouteId, clientRecord.mSelectedRouteId)) { mTempClients.add(clientRecord.mClient); } } } final int count = mTempClients.size(); for (int i = 0; i < count; i++) { try { mTempClients.get(i).onSelectedRouteChanged(selectedRouteId); } catch (RemoteException ex) { Slog.w(TAG, "Failed to call onSelectedRouteChanged. Client probably died."); } } } finally { mTempClients.clear(); } } private int findProviderRecord(RemoteDisplayProviderProxy provider) { final int count = mProviderRecords.size(); for (int i = 0; i < count; i++) { Loading Loading
media/java/android/media/IMediaRouterClient.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -22,4 +22,5 @@ package android.media; oneway interface IMediaRouterClient { void onStateChanged(); void onRestoreRoute(); void onSelectedRouteChanged(String routeId); }
media/java/android/media/IMediaRouterService.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ interface IMediaRouterService { void registerClientAsUser(IMediaRouterClient client, String packageName, int userId); void unregisterClient(IMediaRouterClient client); void registerClientGroupId(IMediaRouterClient client, String groupId); MediaRouterClientState getState(IMediaRouterClient client); boolean isPlaybackActive(IMediaRouterClient client); Loading
media/java/android/media/MediaRouter.java +61 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.Manifest; import android.annotation.DrawableRes; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; Loading Loading @@ -343,6 +344,16 @@ public class MediaRouter { updatePresentationDisplays(displayId); } public void setRouterGroupId(String groupId) { if (mClient != null) { try { mMediaRouterService.registerClientGroupId(mClient, groupId); } catch (RemoteException ex) { Log.e(TAG, "Unable to register group ID of the client.", ex); } } } public Display[] getAllPresentationDisplays() { return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION); } Loading @@ -358,6 +369,21 @@ public class MediaRouter { } } void updateSelectedRouteForId(String routeId) { RouteInfo selectedRoute = isBluetoothA2dpOn() ? mBluetoothA2dpRoute : mDefaultAudioVideo; final int count = mRoutes.size(); for (int i = 0; i < count; i++) { final RouteInfo route = mRoutes.get(i); if (TextUtils.equals(route.mGlobalRouteId, routeId)) { selectedRoute = route; } } if (selectedRoute != mSelectedRoute) { selectRouteStatic(selectedRoute.mSupportedTypes, selectedRoute, false); } } void setSelectedRoute(RouteInfo info, boolean explicit) { // Must be non-reentrant. mSelectedRoute = info; Loading Loading @@ -619,6 +645,15 @@ public class MediaRouter { } }); } @Override public void onSelectedRouteChanged(String routeId) { mHandler.post(() -> { if (Client.this == mClient) { updateSelectedRouteForId(routeId); } }); } } } Loading Loading @@ -728,6 +763,13 @@ public class MediaRouter { */ public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0; /** * The route group id used for sharing the selected mirroring device. * System UI and Settings use this to synchronize their mirroring status. * @hide */ public static final String MIRRORING_GROUP_ID = "android.media.mirroring_group"; // Maps application contexts static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>(); Loading Loading @@ -847,6 +889,25 @@ public class MediaRouter { return false; } /** * Sets the group ID of the router. * Media routers with the same ID acts as if they were a single media router. * For example, if a media router selects a route, the selected route of routers * with the same group ID will be changed automatically. * * Two routers in a group are supposed to use the same route types. * * System UI and Settings use this to synchronize their mirroring status. * Do not set the router group id unless it's necessary. * * {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY} permission is required to * call this method. * @hide */ public void setRouterGroupId(@Nullable String groupId) { sStatic.setRouterGroupId(groupId); } /** * Add a callback to listen to events about specific kinds of media routes. * If the specified callback is already registered, its registration will be updated for any Loading
services/core/java/com/android/server/media/MediaRouterService.java +122 −1 Original line number Diff line number Diff line Loading @@ -246,6 +246,29 @@ public final class MediaRouterService extends IMediaRouterService.Stub } } // Binder call @Override public void registerClientGroupId(IMediaRouterClient client, String groupId) { if (client == null) { throw new NullPointerException("client must not be null"); } if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) != PackageManager.PERMISSION_GRANTED) { Log.w(TAG, "Ignoring client group request because " + "the client doesn't have the CONFIGURE_WIFI_DISPLAY permission."); return; } final long token = Binder.clearCallingIdentity(); try { synchronized (mLock) { registerClientGroupIdLocked(client, groupId); } } finally { Binder.restoreCallingIdentity(token); } } // Binder call @Override public void unregisterClient(IMediaRouterClient client) { Loading Loading @@ -502,11 +525,37 @@ public final class MediaRouterService extends IMediaRouterService.Stub } } private void registerClientGroupIdLocked(IMediaRouterClient client, String groupId) { final IBinder binder = client.asBinder(); ClientRecord clientRecord = mAllClientRecords.get(binder); if (clientRecord == null) { Log.w(TAG, "Ignoring group id register request of a unregistered client."); return; } if (TextUtils.equals(clientRecord.mGroupId, groupId)) { return; } UserRecord userRecord = clientRecord.mUserRecord; if (clientRecord.mGroupId != null) { userRecord.removeFromGroup(clientRecord.mGroupId, clientRecord); } clientRecord.mGroupId = groupId; if (groupId != null) { userRecord.addToGroup(groupId, clientRecord); userRecord.mHandler.obtainMessage(UserHandler.MSG_UPDATE_SELECTED_ROUTE, groupId) .sendToTarget(); } } private void unregisterClientLocked(IMediaRouterClient client, boolean died) { ClientRecord clientRecord = mAllClientRecords.remove(client.asBinder()); if (clientRecord != null) { UserRecord userRecord = clientRecord.mUserRecord; userRecord.mClientRecords.remove(clientRecord); if (clientRecord.mGroupId != null) { userRecord.removeFromGroup(clientRecord.mGroupId, clientRecord); clientRecord.mGroupId = null; } disposeClientLocked(clientRecord, died); disposeUserIfNeededLocked(userRecord); // since client removed from user } Loading Loading @@ -568,6 +617,16 @@ public final class MediaRouterService extends IMediaRouterService.Stub clientRecord.mUserRecord.mHandler.obtainMessage( UserHandler.MSG_SELECT_ROUTE, routeId).sendToTarget(); } if (clientRecord.mGroupId != null) { ClientGroup group = clientRecord.mUserRecord.mClientGroupMap.get(clientRecord.mGroupId); if (group != null) { group.mSelectedRouteId = routeId; clientRecord.mUserRecord.mHandler.obtainMessage( UserHandler.MSG_UPDATE_SELECTED_ROUTE, clientRecord.mGroupId) .sendToTarget(); } } } } } Loading Loading @@ -680,6 +739,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub public int mRouteTypes; public boolean mActiveScan; public String mSelectedRouteId; public String mGroupId; public ClientRecord(UserRecord userRecord, IMediaRouterClient client, int uid, int pid, String packageName, boolean trusted) { Loading Loading @@ -720,6 +780,11 @@ public final class MediaRouterService extends IMediaRouterService.Stub } } final class ClientGroup { public String mSelectedRouteId; public final List<ClientRecord> mClientRecords = new ArrayList<>(); } /** * Information about a particular user. * The contents of this object is guarded by mLock. Loading @@ -729,6 +794,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub public final ArrayList<ClientRecord> mClientRecords = new ArrayList<ClientRecord>(); public final UserHandler mHandler; public MediaRouterClientState mRouterState; private final ArrayMap<String, ClientGroup> mClientGroupMap = new ArrayMap<>(); public UserRecord(int userId) { mUserId = userId; Loading Loading @@ -761,6 +827,25 @@ public final class MediaRouterService extends IMediaRouterService.Stub } } public void addToGroup(String groupId, ClientRecord clientRecord) { ClientGroup group = mClientGroupMap.get(groupId); if (group == null) { group = new ClientGroup(); mClientGroupMap.put(groupId, group); } group.mClientRecords.add(clientRecord); } public void removeFromGroup(String groupId, ClientRecord clientRecord) { ClientGroup group = mClientGroupMap.get(groupId); if (group != null) { group.mClientRecords.remove(clientRecord); if (group.mClientRecords.size() == 0) { mClientGroupMap.remove(groupId); } } } @Override public String toString() { return "User " + mUserId; Loading Loading @@ -791,6 +876,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub public static final int MSG_REQUEST_UPDATE_VOLUME = 7; private static final int MSG_UPDATE_CLIENT_STATE = 8; private static final int MSG_CONNECTION_TIMED_OUT = 9; private static final int MSG_UPDATE_SELECTED_ROUTE = 10; private static final int TIMEOUT_REASON_NOT_AVAILABLE = 1; private static final int TIMEOUT_REASON_CONNECTION_LOST = 2; Loading Loading @@ -867,6 +953,10 @@ public final class MediaRouterService extends IMediaRouterService.Stub connectionTimedOut(); break; } case MSG_UPDATE_SELECTED_ROUTE: { updateSelectedRoute((String) msg.obj); break; } } } Loading Loading @@ -1191,6 +1281,37 @@ public final class MediaRouterService extends IMediaRouterService.Stub } } private void updateSelectedRoute(String groupId) { try { String selectedRouteId = null; synchronized (mService.mLock) { ClientGroup group = mUserRecord.mClientGroupMap.get(groupId); if (group == null) { return; } selectedRouteId = group.mSelectedRouteId; final int count = group.mClientRecords.size(); for (int i = 0; i < count; i++) { ClientRecord clientRecord = group.mClientRecords.get(i); if (!TextUtils.equals(selectedRouteId, clientRecord.mSelectedRouteId)) { mTempClients.add(clientRecord.mClient); } } } final int count = mTempClients.size(); for (int i = 0; i < count; i++) { try { mTempClients.get(i).onSelectedRouteChanged(selectedRouteId); } catch (RemoteException ex) { Slog.w(TAG, "Failed to call onSelectedRouteChanged. Client probably died."); } } } finally { mTempClients.clear(); } } private int findProviderRecord(RemoteDisplayProviderProxy provider) { final int count = mProviderRecords.size(); for (int i = 0; i < count; i++) { Loading