diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index e27cbeaab139f..350aa6b2f686e 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -58,6 +58,8 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.function.pooled.PooledLambda; +import com.android.server.LocalServices; +import com.android.server.pm.UserManagerInternal; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -87,6 +89,7 @@ class MediaRouter2ServiceImpl { private static final int PACKAGE_IMPORTANCE_FOR_DISCOVERY = IMPORTANCE_FOREGROUND_SERVICE; private final Context mContext; + private final UserManagerInternal mUserManagerInternal; private final Object mLock = new Object(); final AtomicInteger mNextRouterOrManagerId = new AtomicInteger(1); final ActivityManager mActivityManager; @@ -99,7 +102,7 @@ class MediaRouter2ServiceImpl { @GuardedBy("mLock") private final ArrayMap mAllManagerRecords = new ArrayMap<>(); @GuardedBy("mLock") - private int mCurrentUserId = -1; + private int mCurrentActiveUserId = -1; private final ActivityManager.OnUidImportanceListener mOnUidImportanceListener = (uid, importance) -> { @@ -125,12 +128,13 @@ class MediaRouter2ServiceImpl { } }; - MediaRouter2ServiceImpl(Context context) { + /* package */ MediaRouter2ServiceImpl(Context context) { mContext = context; mActivityManager = mContext.getSystemService(ActivityManager.class); mActivityManager.addOnUidImportanceListener(mOnUidImportanceListener, PACKAGE_IMPORTANCE_FOR_DISCOVERY); mPowerManager = mContext.getSystemService(PowerManager.class); + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); IntentFilter screenOnOffIntentFilter = new IntentFilter(); screenOnOffIntentFilter.addAction(ACTION_SCREEN_ON); @@ -601,25 +605,23 @@ class MediaRouter2ServiceImpl { } } - //TODO(b/136703681): Review this is handling multi-user properly. - void switchUser() { + /* package */ void updateRunningUserAndProfiles(int newActiveUserId) { synchronized (mLock) { - int userId = ActivityManager.getCurrentUser(); - if (mCurrentUserId != userId) { - final int oldUserId = mCurrentUserId; - mCurrentUserId = userId; // do this first - - UserRecord oldUser = mUserRecords.get(oldUserId); - if (oldUser != null) { - oldUser.mHandler.sendMessage( - obtainMessage(UserHandler::stop, oldUser.mHandler)); - disposeUserIfNeededLocked(oldUser); // since no longer current user - } - - UserRecord newUser = mUserRecords.get(userId); - if (newUser != null) { - newUser.mHandler.sendMessage( - obtainMessage(UserHandler::start, newUser.mHandler)); + if (mCurrentActiveUserId != newActiveUserId) { + mCurrentActiveUserId = newActiveUserId; + for (int i = 0; i < mUserRecords.size(); i++) { + int userId = mUserRecords.keyAt(i); + UserRecord userRecord = mUserRecords.valueAt(i); + if (isUserActiveLocked(userId)) { + // userId corresponds to the active user, or one of its profiles. We + // ensure the associated structures are initialized. + userRecord.mHandler.sendMessage( + obtainMessage(UserHandler::start, userRecord.mHandler)); + } else { + userRecord.mHandler.sendMessage( + obtainMessage(UserHandler::stop, userRecord.mHandler)); + disposeUserIfNeededLocked(userRecord); + } } } } @@ -637,11 +639,21 @@ class MediaRouter2ServiceImpl { } } + /** + * Returns {@code true} if the given {@code userId} corresponds to the active user or a profile + * of the active user, returns {@code false} otherwise. + */ + @GuardedBy("mLock") + private boolean isUserActiveLocked(int userId) { + return mUserManagerInternal.getProfileParentId(userId) == mCurrentActiveUserId; + } + //////////////////////////////////////////////////////////////// //// ***Locked methods related to MediaRouter2 //// - Should have @NonNull/@Nullable on all arguments //////////////////////////////////////////////////////////////// + @GuardedBy("mLock") private void registerRouter2Locked(@NonNull IMediaRouter2 router, int uid, int pid, @NonNull String packageName, int userId, boolean hasConfigureWifiDisplayPermission, boolean hasModifyAudioRoutingPermission) { @@ -669,6 +681,7 @@ class MediaRouter2ServiceImpl { userRecord.mHandler, routerRecord)); } + @GuardedBy("mLock") private void unregisterRouter2Locked(@NonNull IMediaRouter2 router, boolean died) { RouterRecord routerRecord = mAllRouterRecords.remove(router.asBinder()); if (routerRecord == null) { @@ -891,6 +904,7 @@ class MediaRouter2ServiceImpl { return sessionInfos; } + @GuardedBy("mLock") private void registerManagerLocked(@NonNull IMediaRouter2Manager manager, int uid, int pid, @NonNull String packageName, int userId) { final IBinder binder = manager.asBinder(); @@ -1125,13 +1139,14 @@ class MediaRouter2ServiceImpl { //// - Should have @NonNull/@Nullable on all arguments //////////////////////////////////////////////////////////// + @GuardedBy("mLock") private UserRecord getOrCreateUserRecordLocked(int userId) { UserRecord userRecord = mUserRecords.get(userId); if (userRecord == null) { userRecord = new UserRecord(userId); mUserRecords.put(userId, userRecord); userRecord.init(); - if (userId == mCurrentUserId) { + if (isUserActiveLocked(userId)) { userRecord.mHandler.sendMessage( obtainMessage(UserHandler::start, userRecord.mHandler)); } @@ -1139,12 +1154,13 @@ class MediaRouter2ServiceImpl { return userRecord; } + @GuardedBy("mLock") private void disposeUserIfNeededLocked(@NonNull UserRecord userRecord) { // If there are no records left and the user is no longer current then go ahead // and purge the user record and all of its associated state. If the user is current // then leave it alone since we might be connected to a route or want to query // the same route information again soon. - if (userRecord.mUserId != mCurrentUserId + if (!isUserActiveLocked(userRecord.mUserId) && userRecord.mRouterRecords.isEmpty() && userRecord.mManagerRecords.isEmpty()) { if (DEBUG) { diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index e61f55381b0c7..440178a1e667e 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -17,7 +17,9 @@ package com.android.server.media; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.app.ActivityManager; +import android.app.UserSwitchObserver; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; @@ -61,7 +63,9 @@ import android.util.SparseArray; import android.util.TimeUtils; import com.android.internal.util.DumpUtils; +import com.android.server.LocalServices; import com.android.server.Watchdog; +import com.android.server.pm.UserManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -102,9 +106,10 @@ public final class MediaRouterService extends IMediaRouterService.Stub // State guarded by mLock. private final Object mLock = new Object(); + private final UserManagerInternal mUserManagerInternal; private final SparseArray mUserRecords = new SparseArray<>(); private final ArrayMap mAllClientRecords = new ArrayMap<>(); - private int mCurrentUserId = -1; + private int mCurrentActiveUserId = -1; private final IAudioService mAudioService; private final AudioPlayerStateMonitor mAudioPlayerStateMonitor; private final Handler mHandler = new Handler(); @@ -130,6 +135,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub mBluetoothA2dpRouteId = res.getString(com.android.internal.R.string.bluetooth_a2dp_audio_route_id); + mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(context); @@ -217,18 +223,27 @@ public final class MediaRouterService extends IMediaRouterService.Stub context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null); } - public void systemRunning() { - IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED); - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(Intent.ACTION_USER_SWITCHED)) { - switchUser(); - } - } - }, filter); - - switchUser(); + /** + * Initializes the MediaRouter service. + * + * @throws RemoteException If an error occurs while registering the {@link UserSwitchObserver}. + */ + @RequiresPermission( + anyOf = { + "android.permission.INTERACT_ACROSS_USERS", + "android.permission.INTERACT_ACROSS_USERS_FULL" + }) + public void systemRunning() throws RemoteException { + ActivityManager.getService() + .registerUserSwitchObserver( + new UserSwitchObserver() { + @Override + public void onUserSwitchComplete(int newUserId) { + updateRunningUserAndProfiles(newUserId); + } + }, + TAG); + updateRunningUserAndProfiles(ActivityManager.getCurrentUser()); } @Override @@ -448,7 +463,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub pw.println("MEDIA ROUTER SERVICE (dumpsys media_router)"); pw.println(); pw.println("Global state"); - pw.println(" mCurrentUserId=" + mCurrentUserId); + pw.println(" mCurrentUserId=" + mCurrentActiveUserId); synchronized (mLock) { final int count = mUserRecords.size(); @@ -702,26 +717,31 @@ public final class MediaRouterService extends IMediaRouterService.Stub } } - void switchUser() { + /** + * Starts all {@link UserRecord user records} associated with the active user (whose ID is + * {@code newActiveUserId}) or the active user's profiles. + * + *

All other records are stopped, and those without associated client records are removed. + */ + private void updateRunningUserAndProfiles(int newActiveUserId) { synchronized (mLock) { - int userId = ActivityManager.getCurrentUser(); - if (mCurrentUserId != userId) { - final int oldUserId = mCurrentUserId; - mCurrentUserId = userId; // do this first - - UserRecord oldUser = mUserRecords.get(oldUserId); - if (oldUser != null) { - oldUser.mHandler.sendEmptyMessage(UserHandler.MSG_STOP); - disposeUserIfNeededLocked(oldUser); // since no longer current user - } - - UserRecord newUser = mUserRecords.get(userId); - if (newUser != null) { - newUser.mHandler.sendEmptyMessage(UserHandler.MSG_START); + if (mCurrentActiveUserId != newActiveUserId) { + mCurrentActiveUserId = newActiveUserId; + for (int i = 0; i < mUserRecords.size(); i++) { + int userId = mUserRecords.keyAt(i); + UserRecord userRecord = mUserRecords.valueAt(i); + if (isUserActiveLocked(userId)) { + // userId corresponds to the active user, or one of its profiles. We + // ensure the associated structures are initialized. + userRecord.mHandler.sendEmptyMessage(UserHandler.MSG_START); + } else { + userRecord.mHandler.sendEmptyMessage(UserHandler.MSG_STOP); + disposeUserIfNeededLocked(userRecord); + } } } } - mService2.switchUser(); + mService2.updateRunningUserAndProfiles(newActiveUserId); } void clientDied(ClientRecord clientRecord) { @@ -776,8 +796,10 @@ public final class MediaRouterService extends IMediaRouterService.Stub clientRecord.mGroupId = groupId; if (groupId != null) { userRecord.addToGroup(groupId, clientRecord); - userRecord.mHandler.obtainMessage(UserHandler.MSG_NOTIFY_GROUP_ROUTE_SELECTED, groupId) - .sendToTarget(); + userRecord + .mHandler + .obtainMessage(UserHandler.MSG_NOTIFY_GROUP_ROUTE_SELECTED, groupId) + .sendToTarget(); } } @@ -863,9 +885,13 @@ public final class MediaRouterService extends IMediaRouterService.Stub clientRecord.mUserRecord.mClientGroupMap.get(clientRecord.mGroupId); if (group != null) { group.mSelectedRouteId = routeId; - clientRecord.mUserRecord.mHandler.obtainMessage( - UserHandler.MSG_NOTIFY_GROUP_ROUTE_SELECTED, clientRecord.mGroupId) - .sendToTarget(); + clientRecord + .mUserRecord + .mHandler + .obtainMessage( + UserHandler.MSG_NOTIFY_GROUP_ROUTE_SELECTED, + clientRecord.mGroupId) + .sendToTarget(); } } } @@ -897,7 +923,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub if (DEBUG) { Slog.d(TAG, userRecord + ": Initialized"); } - if (userRecord.mUserId == mCurrentUserId) { + if (isUserActiveLocked(userRecord.mUserId)) { userRecord.mHandler.sendEmptyMessage(UserHandler.MSG_START); } } @@ -907,8 +933,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub // and purge the user record and all of its associated state. If the user is current // then leave it alone since we might be connected to a route or want to query // the same route information again soon. - if (userRecord.mUserId != mCurrentUserId - && userRecord.mClientRecords.isEmpty()) { + if (!isUserActiveLocked(userRecord.mUserId) && userRecord.mClientRecords.isEmpty()) { if (DEBUG) { Slog.d(TAG, userRecord + ": Disposed"); } @@ -917,6 +942,14 @@ public final class MediaRouterService extends IMediaRouterService.Stub } } + /** + * Returns {@code true} if the given {@code userId} corresponds to the active user or a profile + * of the active user, returns {@code false} otherwise. + */ + private boolean isUserActiveLocked(int userId) { + return mUserManagerInternal.getProfileParentId(userId) == mCurrentActiveUserId; + } + private void initializeClientLocked(ClientRecord clientRecord) { if (DEBUG) { Slog.d(TAG, clientRecord + ": Registered");