From c59305fbfb558b0e6306b3c3d7d3f02671c71834 Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Wed, 16 Nov 2022 18:02:46 +0000 Subject: [PATCH] Add system API to get the previous user. This adds a system API to retrieve the user that was last in the foreground, apart from the current user. This may be used for example when the current user is removed, to switch to the previous current user. Bug: 259373662 Test: atest UserManagerHostTest Change-Id: Ie21fe2f47a90633d2e2a9cb36f03d7d1bca1f9d5 --- core/api/system-current.txt | 1 + core/java/android/os/IUserManager.aidl | 1 + core/java/android/os/UserManager.java | 37 ++++++++++++ .../android/server/pm/UserManagerService.java | 56 +++++++++++++++++++ 4 files changed, 95 insertions(+) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index b7d6a168130f0..d9534c01ef4a4 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -10050,6 +10050,7 @@ package android.os { method @NonNull public java.util.List getAllProfiles(); method @NonNull public java.util.List getEnabledProfiles(); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public android.os.UserHandle getMainUser(); + method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public android.os.UserHandle getPreviousForegroundUser(); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableProfileCount(@NonNull String); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableUserCount(@NonNull String); diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index eae7ce06008c7..d31540a65f2f3 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -59,6 +59,7 @@ interface IUserManager { ParcelFileDescriptor getUserIcon(int userId); UserInfo getPrimaryUser(); int getMainUserId(); + int getPreviousFullUserToEnterForeground(); List getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated); List getProfiles(int userId, boolean enabledOnly); int[] getProfileIds(int userId, boolean enabledOnly); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 954d1fca4ce07..f76e8cdfb55b9 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -4299,6 +4299,43 @@ public class UserManager { } } + /** + * Returns the user who was last in the foreground, not including the current user and not + * including profiles. + * + *

Returns {@code null} if there is no previous user, for example if there + * is only one full user (i.e. only one user which is not a profile) on the device. + * + *

This method may be used for example to find the user to switch back to if the + * current user is removed, or if creating a new user is aborted. + * + *

Note that reboots do not interrupt this calculation; the previous user need not have + * used the device since it rebooted. + * + *

Note also that on devices that support multiple users on multiple displays, it is possible + * that the returned user will be visible on a secondary display, as the foreground user is the + * one associated with the main display. + * + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS, + android.Manifest.permission.QUERY_USERS + }) + public @Nullable UserHandle getPreviousForegroundUser() { + try { + final int previousUser = mService.getPreviousFullUserToEnterForeground(); + if (previousUser == UserHandle.USER_NULL) { + return null; + } + return UserHandle.of(previousUser); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + /** * Checks whether it's possible to add more users. * diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 02a57b288a066..8b6e785bdd377 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -191,6 +191,7 @@ public class UserManagerService extends IUserManager.Stub { private static final String ATTR_CREATION_TIME = "created"; private static final String ATTR_LAST_LOGGED_IN_TIME = "lastLoggedIn"; private static final String ATTR_LAST_LOGGED_IN_FINGERPRINT = "lastLoggedInFingerprint"; + private static final String ATTR_LAST_ENTERED_FOREGROUND_TIME = "lastEnteredForeground"; private static final String ATTR_SERIAL_NO = "serialNumber"; private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber"; private static final String ATTR_PARTIAL = "partial"; @@ -341,6 +342,9 @@ public class UserManagerService extends IUserManager.Stub { /** Elapsed realtime since boot when the user was unlocked. */ long unlockRealtime; + /** Wall clock time in millis when the user last entered the foreground. */ + long mLastEnteredForegroundTimeMillis; + private long mLastRequestQuietModeEnabledMillis; /** @@ -680,6 +684,10 @@ public class UserManagerService extends IUserManager.Stub { final UserData user = mUms.getUserDataLU(targetUser.getUserIdentifier()); if (user != null) { user.startRealtime = SystemClock.elapsedRealtime(); + if (targetUser.getUserIdentifier() == UserHandle.USER_SYSTEM + && targetUser.isFull()) { + mUms.setLastEnteredForegroundTimeToNow(user); + } } } } @@ -694,6 +702,16 @@ public class UserManagerService extends IUserManager.Stub { } } + @Override + public void onUserSwitching(@NonNull TargetUser from, @NonNull TargetUser to) { + synchronized (mUms.mUsersLock) { + final UserData user = mUms.getUserDataLU(to.getUserIdentifier()); + if (user != null) { + mUms.setLastEnteredForegroundTimeToNow(user); + } + } + } + @Override public void onUserStopping(@NonNull TargetUser targetUser) { synchronized (mUms.mUsersLock) { @@ -920,6 +938,30 @@ public class UserManagerService extends IUserManager.Stub { return UserHandle.USER_NULL; } + @Override + public int getPreviousFullUserToEnterForeground() { + checkQueryOrCreateUsersPermission("get previous user"); + int previousUser = UserHandle.USER_NULL; + long latestEnteredTime = 0; + final int currentUser = getCurrentUserId(); + synchronized (mUsersLock) { + final int userSize = mUsers.size(); + for (int i = 0; i < userSize; i++) { + final UserData userData = mUsers.valueAt(i); + final int userId = userData.info.id; + if (userId != currentUser && userData.info.isFull() && !userData.info.partial + && !mRemovingUserIds.get(userId)) { + final long userEnteredTime = userData.mLastEnteredForegroundTimeMillis; + if (userEnteredTime > latestEnteredTime) { + latestEnteredTime = userEnteredTime; + previousUser = userId; + } + } + } + } + return previousUser; + } + public @NonNull List getUsers(boolean excludeDying) { return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */ true); @@ -3941,6 +3983,8 @@ public class UserManagerService extends IUserManager.Stub { serializer.attribute(null, ATTR_LAST_LOGGED_IN_FINGERPRINT, userInfo.lastLoggedInFingerprint); } + serializer.attributeLong( + null, ATTR_LAST_ENTERED_FOREGROUND_TIME, userData.mLastEnteredForegroundTimeMillis); if (userInfo.iconPath != null) { serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath); } @@ -4114,6 +4158,7 @@ public class UserManagerService extends IUserManager.Stub { long lastLoggedInTime = 0L; long lastRequestQuietModeEnabledTimestamp = 0L; String lastLoggedInFingerprint = null; + long lastEnteredForegroundTime = 0L; int profileGroupId = UserInfo.NO_PROFILE_GROUP_ID; int profileBadge = 0; int restrictedProfileParentId = UserInfo.NO_PROFILE_GROUP_ID; @@ -4159,6 +4204,8 @@ public class UserManagerService extends IUserManager.Stub { lastLoggedInTime = parser.getAttributeLong(null, ATTR_LAST_LOGGED_IN_TIME, 0); lastLoggedInFingerprint = parser.getAttributeValue(null, ATTR_LAST_LOGGED_IN_FINGERPRINT); + lastEnteredForegroundTime = + parser.getAttributeLong(null, ATTR_LAST_ENTERED_FOREGROUND_TIME, 0L); profileGroupId = parser.getAttributeInt(null, ATTR_PROFILE_GROUP_ID, UserInfo.NO_PROFILE_GROUP_ID); profileBadge = parser.getAttributeInt(null, ATTR_PROFILE_BADGE, 0); @@ -4253,6 +4300,7 @@ public class UserManagerService extends IUserManager.Stub { userData.seedAccountOptions = seedAccountOptions; userData.userProperties = userProperties; userData.setLastRequestQuietModeEnabledMillis(lastRequestQuietModeEnabledTimestamp); + userData.mLastEnteredForegroundTimeMillis = lastEnteredForegroundTime; if (ignorePrepareStorageErrors) { userData.setIgnorePrepareStorageErrors(); } @@ -6172,6 +6220,11 @@ public class UserManagerService extends IUserManager.Stub { || someUserHasSeedAccountNoChecks(accountName, accountType)); } + private void setLastEnteredForegroundTimeToNow(@NonNull UserData userData) { + userData.mLastEnteredForegroundTimeMillis = System.currentTimeMillis(); + scheduleWriteUser(userData); + } + @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, @@ -6396,6 +6449,9 @@ public class UserManagerService extends IUserManager.Stub { pw.print(" Unlock time: "); dumpTimeAgo(pw, tempStringBuilder, nowRealtime, userData.unlockRealtime); + pw.print(" Last entered foreground: "); + dumpTimeAgo(pw, tempStringBuilder, now, userData.mLastEnteredForegroundTimeMillis); + pw.print(" Has profile owner: "); pw.println(mIsUserManaged.get(userId)); pw.println(" Restrictions:");