Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cd18ace5 authored by Kyunglyul Hyun's avatar Kyunglyul Hyun
Browse files

Media: Add group id for media router to sync

This CL adds MediaRouter.setRouterGroupId() methods to set the "group id"
of a media router.

Media routers that have the same group id synchronize their
selected route.

For example, if System UI and Settings use the group id, you can see
"connected" status from System UI even if the cast begins from Settings
, and vice versa.

Bug: 112826114
Bug: 131385091
Bug: 130345243

Test: manually w/ setting the same group id for Settings and System UI.
Change-Id: I9d4e061b57f52d7b2bec622b5f02068f3d11c133
parent 2c402057
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -22,4 +22,5 @@ package android.media;
oneway interface IMediaRouterClient {
    void onStateChanged();
    void onRestoreRoute();
    void onSelectedRouteChanged(String routeId);
}
+2 −0
Original line number Diff line number Diff line
@@ -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);

+61 −0
Original line number Diff line number Diff line
@@ -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>();

@@ -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
+122 −1
Original line number Diff line number Diff line
@@ -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) {
@@ -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;
@@ -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;
@@ -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++) {