Merge "Media: Add group id for media router to sync" into qt-dev
diff --git a/media/java/android/media/IMediaRouterClient.aidl b/media/java/android/media/IMediaRouterClient.aidl
index 08344f1..240ae79 100644
--- a/media/java/android/media/IMediaRouterClient.aidl
+++ b/media/java/android/media/IMediaRouterClient.aidl
@@ -22,4 +22,5 @@
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 3308fc9..04c2d07 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -26,6 +26,8 @@
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 3444e92..d72231f 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -20,6 +20,7 @@
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 @@
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 @@
}
}
+ 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 @@
}
});
}
+
+ @Override
+ public void onSelectedRouteChanged(String routeId) {
+ mHandler.post(() -> {
+ if (Client.this == mClient) {
+ updateSelectedRouteForId(routeId);
+ }
+ });
+ }
}
}
@@ -728,6 +763,13 @@
*/
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 @@
}
/**
+ * 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 23d3ce0..7b9fff5 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 @@
// 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 @@
}
}
+ 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 @@
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 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 @@
}
}
+ 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 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 @@
}, 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 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 @@
connectionTimedOut();
break;
}
+ case MSG_UPDATE_SELECTED_ROUTE: {
+ updateSelectedRoute((String) msg.obj);
+ break;
+ }
}
}
@@ -1191,6 +1281,37 @@
}
}
+ 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++) {