Loading core/java/android/app/ActivityManagerInternal.java +6 −0 Original line number Diff line number Diff line Loading @@ -311,6 +311,12 @@ public abstract class ActivityManagerInternal { @PermissionMethod public abstract void enforceCallingPermission(@PermissionName String permission, String func); /** * Returns the current and target user ids as a {@link Pair}. Target user id will be * {@link android.os.UserHandle#USER_NULL} if there is not an ongoing user switch. */ public abstract Pair<Integer, Integer> getCurrentAndTargetUserIds(); /** Returns the current user id. */ public abstract int getCurrentUserId(); Loading services/core/java/com/android/server/am/ActivityManagerService.java +5 −0 Original line number Diff line number Diff line Loading @@ -17363,6 +17363,11 @@ public class ActivityManagerService extends IActivityManager.Stub ActivityManagerService.this.enforceCallingPermission(permission, func); } @Override public Pair<Integer, Integer> getCurrentAndTargetUserIds() { return mUserController.getCurrentAndTargetUserIds(); } @Override public int getCurrentUserId() { return mUserController.getCurrentUserId(); services/core/java/com/android/server/am/UserController.java +6 −0 Original line number Diff line number Diff line Loading @@ -2742,6 +2742,12 @@ class UserController implements Handler.Callback { return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId; } Pair<Integer, Integer> getCurrentAndTargetUserIds() { synchronized (mLock) { return new Pair<>(mCurrentUserId, mTargetUserId); } } @GuardedBy("mLock") private int getCurrentUserIdLU() { return mCurrentUserId; Loading services/core/java/com/android/server/pm/UserManagerService.java +40 −8 Original line number Diff line number Diff line Loading @@ -101,6 +101,7 @@ import android.util.ArraySet; import android.util.AtomicFile; import android.util.IndentingPrintWriter; import android.util.IntArray; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; Loading Loading @@ -1834,6 +1835,27 @@ public class UserManagerService extends IUserManager.Stub { return mUserVisibilityMediator.isUserVisible(userId); } /** * Gets the current and target user ids as a {@link Pair}, calling * {@link ActivityManagerInternal} directly (and without performing any permission check). * * @return ids of current foreground user and the target user. Target user will be * {@link UserHandle#USER_NULL} if there is not an ongoing user switch. And if * {@link ActivityManagerInternal} is not available yet, they will both be * {@link UserHandle#USER_NULL}. */ @VisibleForTesting @NonNull Pair<Integer, Integer> getCurrentAndTargetUserIds() { ActivityManagerInternal activityManagerInternal = getActivityManagerInternal(); if (activityManagerInternal == null) { Slog.w(LOG_TAG, "getCurrentAndTargetUserId() called too early, " + "ActivityManagerInternal is not set yet"); return new Pair<>(UserHandle.USER_NULL, UserHandle.USER_NULL); } return activityManagerInternal.getCurrentAndTargetUserIds(); } /** * Gets the current user id, calling {@link ActivityManagerInternal} directly (and without * performing any permission check). Loading Loading @@ -5426,11 +5448,15 @@ public class UserManagerService extends IUserManager.Stub { final long ident = Binder.clearCallingIdentity(); try { final UserData userData; int currentUser = getCurrentUserId(); if (currentUser == userId) { Pair<Integer, Integer> currentAndTargetUserIds = getCurrentAndTargetUserIds(); if (userId == currentAndTargetUserIds.first) { Slog.w(LOG_TAG, "Current user cannot be removed."); return false; } if (userId == currentAndTargetUserIds.second) { Slog.w(LOG_TAG, "Target user of an ongoing user switch cannot be removed."); return false; } synchronized (mPackagesLock) { synchronized (mUsersLock) { userData = mUsers.get(userId); Loading Loading @@ -5567,9 +5593,10 @@ public class UserManagerService extends IUserManager.Stub { } } // Attempt to immediately remove a non-current user final int currentUser = getCurrentUserId(); if (currentUser != userId) { // Attempt to immediately remove a non-current and non-target user Pair<Integer, Integer> currentAndTargetUserIds = getCurrentAndTargetUserIds(); if (userId != currentAndTargetUserIds.first && userId != currentAndTargetUserIds.second) { // Attempt to remove the user. This will fail if the user is the current user if (removeUserWithProfilesUnchecked(userId)) { return UserManager.REMOVE_RESULT_REMOVED; Loading @@ -5578,9 +5605,14 @@ public class UserManagerService extends IUserManager.Stub { // If the user was not immediately removed, make sure it is marked as ephemeral. // Don't mark as disabled since, per UserInfo.FLAG_DISABLED documentation, an // ephemeral user should only be marked as disabled when its removal is in progress. Slog.i(LOG_TAG, "Unable to immediately remove user " + userId + " (current user is " + currentUser + "). User is set as ephemeral and will be removed on user " + "switch or reboot."); Slog.i(LOG_TAG, TextUtils.formatSimple("Unable to immediately remove user %d " + "(%s is %d). User is set as ephemeral and will be removed on " + "user switch or reboot.", userId, userId == currentAndTargetUserIds.first ? "current user" : "target user of an ongoing user switch", userId)); userData.info.flags |= UserInfo.FLAG_EPHEMERAL; writeUserLP(userData); Loading services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java +18 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import android.os.UserManager; import android.os.storage.StorageManager; import android.provider.Settings; import android.util.Log; import android.util.Pair; import android.util.SparseArray; import androidx.test.annotation.UiThreadTest; Loading Loading @@ -153,6 +154,15 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase { .isEqualTo(UserHandle.USER_NULL); } @Test public void testGetCurrentAndTargetUserIds() { mockCurrentAndTargetUser(USER_ID, OTHER_USER_ID); assertWithMessage("getCurrentAndTargetUserIds()") .that(mUms.getCurrentAndTargetUserIds()) .isEqualTo(new Pair<>(USER_ID, OTHER_USER_ID)); } @Test public void testGetCurrentUserId() { mockCurrentUser(USER_ID); Loading Loading @@ -329,6 +339,14 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase { when(mActivityManagerInternal.getCurrentUserId()).thenReturn(userId); } private void mockCurrentAndTargetUser(@UserIdInt int currentUserId, @UserIdInt int targetUserId) { mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal); when(mActivityManagerInternal.getCurrentAndTargetUserIds()) .thenReturn(new Pair<>(currentUserId, targetUserId)); } private <T> void mockGetLocalService(Class<T> serviceClass, T service) { doReturn(service).when(() -> LocalServices.getService(serviceClass)); } Loading Loading
core/java/android/app/ActivityManagerInternal.java +6 −0 Original line number Diff line number Diff line Loading @@ -311,6 +311,12 @@ public abstract class ActivityManagerInternal { @PermissionMethod public abstract void enforceCallingPermission(@PermissionName String permission, String func); /** * Returns the current and target user ids as a {@link Pair}. Target user id will be * {@link android.os.UserHandle#USER_NULL} if there is not an ongoing user switch. */ public abstract Pair<Integer, Integer> getCurrentAndTargetUserIds(); /** Returns the current user id. */ public abstract int getCurrentUserId(); Loading
services/core/java/com/android/server/am/ActivityManagerService.java +5 −0 Original line number Diff line number Diff line Loading @@ -17363,6 +17363,11 @@ public class ActivityManagerService extends IActivityManager.Stub ActivityManagerService.this.enforceCallingPermission(permission, func); } @Override public Pair<Integer, Integer> getCurrentAndTargetUserIds() { return mUserController.getCurrentAndTargetUserIds(); } @Override public int getCurrentUserId() { return mUserController.getCurrentUserId();
services/core/java/com/android/server/am/UserController.java +6 −0 Original line number Diff line number Diff line Loading @@ -2742,6 +2742,12 @@ class UserController implements Handler.Callback { return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId; } Pair<Integer, Integer> getCurrentAndTargetUserIds() { synchronized (mLock) { return new Pair<>(mCurrentUserId, mTargetUserId); } } @GuardedBy("mLock") private int getCurrentUserIdLU() { return mCurrentUserId; Loading
services/core/java/com/android/server/pm/UserManagerService.java +40 −8 Original line number Diff line number Diff line Loading @@ -101,6 +101,7 @@ import android.util.ArraySet; import android.util.AtomicFile; import android.util.IndentingPrintWriter; import android.util.IntArray; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; Loading Loading @@ -1834,6 +1835,27 @@ public class UserManagerService extends IUserManager.Stub { return mUserVisibilityMediator.isUserVisible(userId); } /** * Gets the current and target user ids as a {@link Pair}, calling * {@link ActivityManagerInternal} directly (and without performing any permission check). * * @return ids of current foreground user and the target user. Target user will be * {@link UserHandle#USER_NULL} if there is not an ongoing user switch. And if * {@link ActivityManagerInternal} is not available yet, they will both be * {@link UserHandle#USER_NULL}. */ @VisibleForTesting @NonNull Pair<Integer, Integer> getCurrentAndTargetUserIds() { ActivityManagerInternal activityManagerInternal = getActivityManagerInternal(); if (activityManagerInternal == null) { Slog.w(LOG_TAG, "getCurrentAndTargetUserId() called too early, " + "ActivityManagerInternal is not set yet"); return new Pair<>(UserHandle.USER_NULL, UserHandle.USER_NULL); } return activityManagerInternal.getCurrentAndTargetUserIds(); } /** * Gets the current user id, calling {@link ActivityManagerInternal} directly (and without * performing any permission check). Loading Loading @@ -5426,11 +5448,15 @@ public class UserManagerService extends IUserManager.Stub { final long ident = Binder.clearCallingIdentity(); try { final UserData userData; int currentUser = getCurrentUserId(); if (currentUser == userId) { Pair<Integer, Integer> currentAndTargetUserIds = getCurrentAndTargetUserIds(); if (userId == currentAndTargetUserIds.first) { Slog.w(LOG_TAG, "Current user cannot be removed."); return false; } if (userId == currentAndTargetUserIds.second) { Slog.w(LOG_TAG, "Target user of an ongoing user switch cannot be removed."); return false; } synchronized (mPackagesLock) { synchronized (mUsersLock) { userData = mUsers.get(userId); Loading Loading @@ -5567,9 +5593,10 @@ public class UserManagerService extends IUserManager.Stub { } } // Attempt to immediately remove a non-current user final int currentUser = getCurrentUserId(); if (currentUser != userId) { // Attempt to immediately remove a non-current and non-target user Pair<Integer, Integer> currentAndTargetUserIds = getCurrentAndTargetUserIds(); if (userId != currentAndTargetUserIds.first && userId != currentAndTargetUserIds.second) { // Attempt to remove the user. This will fail if the user is the current user if (removeUserWithProfilesUnchecked(userId)) { return UserManager.REMOVE_RESULT_REMOVED; Loading @@ -5578,9 +5605,14 @@ public class UserManagerService extends IUserManager.Stub { // If the user was not immediately removed, make sure it is marked as ephemeral. // Don't mark as disabled since, per UserInfo.FLAG_DISABLED documentation, an // ephemeral user should only be marked as disabled when its removal is in progress. Slog.i(LOG_TAG, "Unable to immediately remove user " + userId + " (current user is " + currentUser + "). User is set as ephemeral and will be removed on user " + "switch or reboot."); Slog.i(LOG_TAG, TextUtils.formatSimple("Unable to immediately remove user %d " + "(%s is %d). User is set as ephemeral and will be removed on " + "user switch or reboot.", userId, userId == currentAndTargetUserIds.first ? "current user" : "target user of an ongoing user switch", userId)); userData.info.flags |= UserInfo.FLAG_EPHEMERAL; writeUserLP(userData); Loading
services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java +18 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import android.os.UserManager; import android.os.storage.StorageManager; import android.provider.Settings; import android.util.Log; import android.util.Pair; import android.util.SparseArray; import androidx.test.annotation.UiThreadTest; Loading Loading @@ -153,6 +154,15 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase { .isEqualTo(UserHandle.USER_NULL); } @Test public void testGetCurrentAndTargetUserIds() { mockCurrentAndTargetUser(USER_ID, OTHER_USER_ID); assertWithMessage("getCurrentAndTargetUserIds()") .that(mUms.getCurrentAndTargetUserIds()) .isEqualTo(new Pair<>(USER_ID, OTHER_USER_ID)); } @Test public void testGetCurrentUserId() { mockCurrentUser(USER_ID); Loading Loading @@ -329,6 +339,14 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase { when(mActivityManagerInternal.getCurrentUserId()).thenReturn(userId); } private void mockCurrentAndTargetUser(@UserIdInt int currentUserId, @UserIdInt int targetUserId) { mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal); when(mActivityManagerInternal.getCurrentAndTargetUserIds()) .thenReturn(new Pair<>(currentUserId, targetUserId)); } private <T> void mockGetLocalService(Class<T> serviceClass, T service) { doReturn(service).when(() -> LocalServices.getService(serviceClass)); } Loading