diff --git a/core/api/system-current.txt b/core/api/system-current.txt index c47e3b2f0e911..c3e98874b7a39 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -10097,6 +10097,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 b5c1206bdfa4e..494f46ca826ff 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); @@ -3973,6 +4015,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); } @@ -4146,6 +4190,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; @@ -4191,6 +4236,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); @@ -4285,6 +4332,7 @@ public class UserManagerService extends IUserManager.Stub { userData.seedAccountOptions = seedAccountOptions; userData.userProperties = userProperties; userData.setLastRequestQuietModeEnabledMillis(lastRequestQuietModeEnabledTimestamp); + userData.mLastEnteredForegroundTimeMillis = lastEnteredForegroundTime; if (ignorePrepareStorageErrors) { userData.setIgnorePrepareStorageErrors(); } @@ -6204,6 +6252,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, @@ -6428,6 +6481,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:");