From 50ef2008928395975ad057c3c127ed401c6ad301 Mon Sep 17 00:00:00 2001 From: Varun Shah Date: Mon, 4 Mar 2019 16:41:12 -0800 Subject: [PATCH] Added UserManager#getUserSwitchability. The new SystemApi indicates whether user switching is currently allowed or not. This API provides more detail than the already existing UserManager#canSwitchUsers (now deprecated) by telling the user why users cannot be switched via a flag that's returned. Bug: 124883455 Test: atest android.server.pm.UserManagerTest#testGetUserSwitchability Change-Id: Ib3ac2f063f3638a172fa339fd2330604b910c7f4 --- api/system-current.txt | 6 +- core/java/android/os/UserManager.java | 89 +++++++++++++++++-- .../am/ActivityManagerShellCommand.java | 5 +- .../android/server/pm/UserManagerTest.java | 6 ++ 4 files changed, 97 insertions(+), 9 deletions(-) diff --git a/api/system-current.txt b/api/system-current.txt index 4a7b26c6f813a..316f7b84c85c7 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5602,7 +5602,6 @@ package android.os { } public class UserManager { - method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean canSwitchUsers(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void clearSeedAccountData(); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName(); @@ -5612,6 +5611,7 @@ package android.os { method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.graphics.Bitmap getUserIcon(); method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String, android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List getUserRestrictionSources(String, android.os.UserHandle); + method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public int getUserSwitchability(); method public boolean hasRestrictedProfiles(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isAdminUser(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isGuestUser(); @@ -5629,6 +5629,10 @@ package android.os { field public static final int RESTRICTION_SOURCE_DEVICE_OWNER = 2; // 0x2 field public static final int RESTRICTION_SOURCE_PROFILE_OWNER = 4; // 0x4 field public static final int RESTRICTION_SOURCE_SYSTEM = 1; // 0x1 + field public static final int SWITCHABILITY_STATUS_OK = 0; // 0x0 + field public static final int SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED = 4; // 0x4 + field public static final int SWITCHABILITY_STATUS_USER_IN_CALL = 1; // 0x1 + field public static final int SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED = 2; // 0x2 } public static final class UserManager.EnforcingUser implements android.os.Parcelable { diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 42633778a3213..372587910f422 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -25,6 +25,7 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; +import android.annotation.UnsupportedAppUsage; import android.annotation.UserIdInt; import android.annotation.WorkerThread; import android.app.Activity; @@ -1101,6 +1102,47 @@ public class UserManager { */ public static final int USER_CREATION_FAILED_NO_MORE_USERS = Activity.RESULT_FIRST_USER + 1; + /** + * Indicates that users are switchable. + * @hide + */ + @SystemApi + public static final int SWITCHABILITY_STATUS_OK = 0; + + /** + * Indicated that the user is in a phone call. + * @hide + */ + @SystemApi + public static final int SWITCHABILITY_STATUS_USER_IN_CALL = 1 << 0; + + /** + * Indicates that user switching is disallowed ({@link #DISALLOW_USER_SWITCH} is set). + * @hide + */ + @SystemApi + public static final int SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED = 1 << 1; + + /** + * Indicates that the system user is locked and user switching is not allowed. + * @hide + */ + @SystemApi + public static final int SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED = 1 << 2; + + /** + * Result returned in {@link #getUserSwitchability()} indicating user swichability. + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "SWITCHABILITY_STATUS_" }, value = { + SWITCHABILITY_STATUS_OK, + SWITCHABILITY_STATUS_USER_IN_CALL, + SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED, + SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED + }) + public @interface UserSwitchabilityResult {} + /** * Indicates user operation is successful. */ @@ -1223,14 +1265,13 @@ public class UserManager { } /** - * Returns whether switching users is currently allowed. - *

For instance switching users is not allowed if the current user is in a phone call, - * system user hasn't been unlocked yet, or {@link #DISALLOW_USER_SWITCH} is set. + * @deprecated use {@link #getUserSwitchability()} instead. + * + * @removed * @hide */ - @SystemApi - @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, - android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) + @Deprecated + @UnsupportedAppUsage public boolean canSwitchUsers() { boolean allowUserSwitchingWhenSystemUserLocked = Settings.Global.getInt( mContext.getContentResolver(), @@ -1243,6 +1284,42 @@ public class UserManager { && !isUserSwitchDisallowed; } + /** + * Returns whether switching users is currently allowed. + *

+ * Switching users is not allowed in the following cases: + *

  • the user is in a phone call
  • + *
  • {@link #DISALLOW_USER_SWITCH} is set
  • + *
  • system user hasn't been unlocked yet
  • + * + * @return A {@link UserSwitchabilityResult} flag indicating if the user is switchable. + * @hide + */ + @SystemApi + @RequiresPermission(allOf = {Manifest.permission.READ_PHONE_STATE, + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) + public @UserSwitchabilityResult int getUserSwitchability() { + final boolean allowUserSwitchingWhenSystemUserLocked = Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED, 0) != 0; + final boolean systemUserUnlocked = isUserUnlocked(UserHandle.SYSTEM); + final TelephonyManager tm = + (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + + int flags = SWITCHABILITY_STATUS_OK; + if (tm.getCallState() != TelephonyManager.CALL_STATE_IDLE) { + flags |= SWITCHABILITY_STATUS_USER_IN_CALL; + } + if (hasUserRestriction(DISALLOW_USER_SWITCH)) { + flags |= SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED; + } + if (!allowUserSwitchingWhenSystemUserLocked && !systemUserUnlocked) { + flags |= SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED; + } + return flags; + } + /** * Returns the user handle for the user that this process is running under. * diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 85322d611f418..57ce98c14554d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -1674,8 +1674,9 @@ final class ActivityManagerShellCommand extends ShellCommand { int runSwitchUser(PrintWriter pw) throws RemoteException { UserManager userManager = mInternal.mContext.getSystemService(UserManager.class); - if (!userManager.canSwitchUsers()) { - getErrPrintWriter().println("Error: disallowed switching user"); + final int userSwitchable = userManager.getUserSwitchability(); + if (userSwitchable != UserManager.SWITCHABILITY_STATUS_OK) { + getErrPrintWriter().println("Error: " + userSwitchable); return -1; } String user = getNextArgRequired(); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java index 3ebc6ad9b9067..0de807d07311d 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -520,6 +520,12 @@ public class UserManagerTest extends AndroidTestCase { } } + public void testGetUserSwitchability() { + int userSwitchable = mUserManager.getUserSwitchability(); + assertEquals("Expected users to be switchable", UserManager.SWITCHABILITY_STATUS_OK, + userSwitchable); + } + @LargeTest public void testSwitchUser() { ActivityManager am = getContext().getSystemService(ActivityManager.class);