diff options
4 files changed, 186 insertions, 1 deletions
diff --git a/media/java/android/media/IMediaRouterClient.aidl b/media/java/android/media/IMediaRouterClient.aidl index 08344f188098..240ae796f957 100644 --- a/media/java/android/media/IMediaRouterClient.aidl +++ b/media/java/android/media/IMediaRouterClient.aidl @@ -22,4 +22,5 @@ package android.media; oneway interface IMediaRouterClient { void onStateChanged(); void onRestoreRoute(); + void onSelectedRouteChanged(String routeId); } diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index 3308fc929b03..04c2d074722d 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -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); diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index 3444e9277949..d72231f40dcf 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -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; @@ -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); } @@ -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; @@ -619,6 +645,15 @@ public class MediaRouter { } }); } + + @Override + public void onSelectedRouteChanged(String routeId) { + mHandler.post(() -> { + if (Client.this == mClient) { + updateSelectedRouteForId(routeId); + } + }); + } } } @@ -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>(); @@ -848,6 +890,25 @@ public class MediaRouter { } /** + * 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 * additional route types specified. diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index 23d3ce063a3e..7b9fff55b654 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -248,6 +248,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) { if (client == null) { throw new IllegalArgumentException("client must not be null"); @@ -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 } @@ -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(); + } + } } } } @@ -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) { @@ -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. @@ -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; @@ -759,7 +825,26 @@ public final class MediaRouterService extends IMediaRouterService.Stub }, 1000)) { pw.println(indent + "<could not dump handler state>"); } - } + } + + 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() { @@ -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; @@ -867,6 +953,10 @@ public final class MediaRouterService extends IMediaRouterService.Stub connectionTimedOut(); break; } + case MSG_UPDATE_SELECTED_ROUTE: { + updateSelectedRoute((String) msg.obj); + break; + } } } @@ -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++) { |