Merge "Allow visible background users on default display."
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index be1bc02..7a460ee 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2073,6 +2073,7 @@
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isUserTypeEnabled(@NonNull String);
+ method public boolean isVisibleBackgroundUsersOnDefaultDisplaySupported();
method public boolean isVisibleBackgroundUsersSupported();
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo preCreateUser(@NonNull String) throws android.os.UserManager.UserOperationException;
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index d63d87d..0efd264 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2976,6 +2976,29 @@
}
/**
+ * @hide
+ */
+ public static boolean isVisibleBackgroundUsersOnDefaultDisplayEnabled() {
+ return SystemProperties.getBoolean("fw.visible_bg_users_on_default_display",
+ Resources.getSystem()
+ .getBoolean(R.bool.config_multiuserVisibleBackgroundUsersOnDefaultDisplay));
+ }
+
+ /**
+ * Returns whether the device allows (full) users to be started in background visible in the
+ * {@link android.view.Display#DEFAULT_DISPLAY default display}.
+ *
+ * @return {@code false} for most devices, except passenger-only automotive build (i.e., when
+ * Android runs in a separate system in the back seat to manage the passenger displays).
+ *
+ * @hide
+ */
+ @TestApi
+ public boolean isVisibleBackgroundUsersOnDefaultDisplaySupported() {
+ return isVisibleBackgroundUsersOnDefaultDisplayEnabled();
+ }
+
+ /**
* Checks if the user is visible at the moment.
*
* <p>Roughly speaking, a "visible user" is a user that can present UI on at least one display.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 263d431..37d5b84 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2735,6 +2735,11 @@
Should be false for most devices, except automotive vehicle with passenger displays. -->
<bool name="config_multiuserVisibleBackgroundUsers">false</bool>
+ <!-- Whether the device allows users to start in background visible on the default display.
+ Should be false for most devices, except passenger-only automotive build (i.e., when
+ Android runs in a separate system in the back seat to manage the passenger displays) -->
+ <bool name="config_multiuserVisibleBackgroundUsersOnDefaultDisplay">false</bool>
+
<!-- Whether to automatically switch to the designated Dock User (the user chosen for
displaying dreams, etc.) after a timeout when the device is docked. -->
<bool name="config_enableTimeoutToDockUserWhenDocked">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fbed371..2af91e1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -473,6 +473,7 @@
<java-symbol type="integer" name="config_multiuserMaxRunningUsers" />
<java-symbol type="bool" name="config_multiuserDelayUserDataLocking" />
<java-symbol type="bool" name="config_multiuserVisibleBackgroundUsers" />
+ <java-symbol type="bool" name="config_multiuserVisibleBackgroundUsersOnDefaultDisplay" />
<java-symbol type="bool" name="config_enableTimeoutToDockUserWhenDocked" />
<java-symbol type="integer" name="config_userTypePackageWhitelistMode"/>
<java-symbol type="xml" name="config_user_types" />
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 57a89e3..423a090 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18974,14 +18974,20 @@
return null;
}
- // Starts with all displays but DEFAULT_DISPLAY
- int[] displayIds = new int[allDisplays.length - 1];
+ boolean allowOnDefaultDisplay = UserManager
+ .isVisibleBackgroundUsersOnDefaultDisplayEnabled();
+ int displaysSize = allDisplays.length;
+ if (!allowOnDefaultDisplay) {
+ displaysSize--;
+ }
+ int[] displayIds = new int[displaysSize];
- // TODO(b/247592632): check for other properties like isSecure or proper display type
int numberValidDisplays = 0;
for (Display display : allDisplays) {
int displayId = display.getDisplayId();
- if (display.isValid() && displayId != Display.DEFAULT_DISPLAY) {
+ // TODO(b/247592632): check other properties like isSecure or proper display type
+ if (display.isValid()
+ && (allowOnDefaultDisplay || displayId != Display.DEFAULT_DISPLAY)) {
displayIds[numberValidDisplays++] = displayId;
}
}
@@ -18993,14 +18999,15 @@
// STOPSHIP: if not removed, it should at least be unit tested
String testingProp = "fw.display_ids_for_starting_users_for_testing_purposes";
int displayId = SystemProperties.getInt(testingProp, Display.DEFAULT_DISPLAY);
- if (displayId != Display.DEFAULT_DISPLAY && displayId > 0) {
- Slogf.w(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): no valid "
+ if (allowOnDefaultDisplay && displayId == Display.DEFAULT_DISPLAY
+ || displayId > 0) {
+ Slogf.w(TAG, "getDisplayIdsForStartingVisibleBackgroundUsers(): no valid "
+ "display found, but returning %d as set by property %s", displayId,
testingProp);
return new int[] { displayId };
}
- Slogf.e(TAG, "getDisplayIdsForStartingBackgroundUsers(): no valid display on %s",
- Arrays.toString(allDisplays));
+ Slogf.e(TAG, "getDisplayIdsForStartingVisibleBackgroundUsers(): no valid display"
+ + " on %s", Arrays.toString(allDisplays));
return null;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index eb3be54..26a990c 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -52,12 +52,14 @@
// TODO(b/248408342): Move keep annotation to the method referencing these fields reflectively.
@Keep public static final int USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE = 1;
@Keep public static final int USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE = 2;
+ @Keep public static final int USER_ASSIGNMENT_RESULT_SUCCESS_ALREADY_VISIBLE = 3;
@Keep public static final int USER_ASSIGNMENT_RESULT_FAILURE = -1;
private static final String PREFIX_USER_ASSIGNMENT_RESULT = "USER_ASSIGNMENT_RESULT_";
@IntDef(flag = false, prefix = {PREFIX_USER_ASSIGNMENT_RESULT}, value = {
USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE,
USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE,
+ USER_ASSIGNMENT_RESULT_SUCCESS_ALREADY_VISIBLE,
USER_ASSIGNMENT_RESULT_FAILURE
})
public @interface UserAssignmentResult {}
diff --git a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
index 5bdcbb9..50a1d90 100644
--- a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
+++ b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
+import android.content.res.Resources;
import android.os.Binder;
import android.os.Build;
import android.os.Process;
@@ -36,6 +37,7 @@
import android.util.IndentingPrintWriter;
import android.util.Slog;
+import com.android.internal.R;
import com.android.internal.os.RoSystemProperties;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
@@ -103,6 +105,9 @@
pw.println();
pw.println(" is-headless-system-user-mode [-v | --verbose]");
pw.println(" Checks whether the device uses headless system user mode.");
+ pw.println(" is-visible-background-users-on-default-display-supported [-v | --verbose]");
+ pw.println(" Checks whether the device allows users to be start visible on background "
+ + "in the default display.");
pw.println(" It returns the effective mode, even when using emulation");
pw.println(" (to get the real mode as well, use -v or --verbose)");
pw.println();
@@ -129,6 +134,8 @@
return runSetSystemUserModeEmulation();
case "is-headless-system-user-mode":
return runIsHeadlessSystemUserMode();
+ case "is-visible-background-users-on-default-display-supported":
+ return runIsVisibleBackgroundUserOnDefaultDisplaySupported();
case "is-user-visible":
return runIsUserVisible();
default:
@@ -431,19 +438,47 @@
return -1;
}
}
-
- boolean isHsum = mService.isHeadlessSystemUserMode();
+ boolean effective = mService.isHeadlessSystemUserMode();
if (!verbose) {
// NOTE: do not change output below, as it's used by ITestDevice
// (it's ok to change the verbose option though)
- pw.println(isHsum);
+ pw.println(effective);
} else {
- pw.printf("effective=%b real=%b\n", isHsum,
+ pw.printf("effective=%b real=%b\n", effective,
RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER);
}
return 0;
}
+ private int runIsVisibleBackgroundUserOnDefaultDisplaySupported() {
+ PrintWriter pw = getOutPrintWriter();
+
+ boolean verbose = false;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-v":
+ case "--verbose":
+ verbose = true;
+ break;
+ default:
+ pw.println("Invalid option: " + opt);
+ return -1;
+ }
+ }
+
+ boolean effective = UserManager.isVisibleBackgroundUsersOnDefaultDisplayEnabled();
+ if (!verbose) {
+ // NOTE: do not change output below, as it's used by ITestDevice
+ // (it's ok to change the verbose option though)
+ pw.println(effective);
+ } else {
+ pw.printf("effective=%b real=%b\n", effective, Resources.getSystem()
+ .getBoolean(R.bool.config_multiuserVisibleBackgroundUsersOnDefaultDisplay));
+ }
+ return 0;
+ }
+
/**
* Gets the {@link UserManager} associated with the context of the given user.
*/
diff --git a/services/core/java/com/android/server/pm/UserVisibilityMediator.java b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
index 0a181a4..fe8a500 100644
--- a/services/core/java/com/android/server/pm/UserVisibilityMediator.java
+++ b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
@@ -22,6 +22,7 @@
import static android.view.Display.INVALID_DISPLAY;
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE;
+import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_ALREADY_VISIBLE;
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE;
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE;
import static com.android.server.pm.UserManagerInternal.USER_START_MODE_BACKGROUND;
@@ -163,8 +164,7 @@
UserVisibilityMediator(Handler handler) {
this(UserManager.isVisibleBackgroundUsersEnabled(),
- // TODO(b/261538232): get visibleBackgroundUserOnDefaultDisplayAllowed from UM
- /* visibleBackgroundUserOnDefaultDisplayAllowed= */ false, handler);
+ UserManager.isVisibleBackgroundUsersOnDefaultDisplayEnabled(), handler);
}
@VisibleForTesting
@@ -230,7 +230,8 @@
Slogf.d(TAG, "result of getUserVisibilityOnStartLocked(%s)",
userAssignmentResultToString(result));
}
- if (result == USER_ASSIGNMENT_RESULT_FAILURE) {
+ if (result == USER_ASSIGNMENT_RESULT_FAILURE
+ || result == USER_ASSIGNMENT_RESULT_SUCCESS_ALREADY_VISIBLE) {
return result;
}
@@ -316,11 +317,19 @@
}
boolean visibleBackground = userStartMode == USER_START_MODE_BACKGROUND_VISIBLE;
- if (displayId == DEFAULT_DISPLAY && visibleBackground
- && !mVisibleBackgroundUserOnDefaultDisplayAllowed
- && !isProfile(userId, profileGroupId)) {
- Slogf.wtf(TAG, "cannot start full user (%d) visible on default display", userId);
- return USER_ASSIGNMENT_RESULT_FAILURE;
+ if (displayId == DEFAULT_DISPLAY && visibleBackground) {
+ if (mVisibleBackgroundUserOnDefaultDisplayAllowed && isCurrentUserLocked(userId)) {
+ // Shouldn't happen - UserController returns before calling this method
+ Slogf.wtf(TAG, "trying to start current user (%d) visible in background on default"
+ + " display", userId);
+ return USER_ASSIGNMENT_RESULT_SUCCESS_ALREADY_VISIBLE;
+
+ }
+ if (!mVisibleBackgroundUserOnDefaultDisplayAllowed
+ && !isProfile(userId, profileGroupId)) {
+ Slogf.wtf(TAG, "cannot start full user (%d) visible on default display", userId);
+ return USER_ASSIGNMENT_RESULT_FAILURE;
+ }
}
boolean foreground = userStartMode == USER_START_MODE_FOREGROUND;
@@ -406,7 +415,7 @@
// parent is the current user, as the current user is always assigned to the
// DEFAULT_DISPLAY).
if (DBG) {
- Slogf.d(TAG, "ignoring mapping for default display for user %d starting as %s",
+ Slogf.d(TAG, "Ignoring mapping for default display for user %d starting as %s",
userId, userStartModeToString(userStartMode));
}
return SECONDARY_DISPLAY_MAPPING_NOT_NEEDED;
@@ -1007,6 +1016,15 @@
}
}
+ @GuardedBy("mLock")
+ private boolean isCurrentUserLocked(@UserIdInt int userId) {
+ // Special case as NO_PROFILE_GROUP_ID == USER_NULL
+ if (userId == USER_NULL || mCurrentUserId == USER_NULL) {
+ return false;
+ }
+ return mCurrentUserId == userId;
+ }
+
private boolean isCurrentUserOrRunningProfileOfCurrentUser(@UserIdInt int userId) {
synchronized (mLock) {
// Special case as NO_PROFILE_GROUP_ID == USER_NULL
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUPANDTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUPANDTest.java
index f69054b..8979585 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUPANDTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUPANDTest.java
@@ -20,6 +20,7 @@
import static android.view.Display.INVALID_DISPLAY;
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE;
+import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_ALREADY_VISIBLE;
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE;
import static com.android.server.pm.UserManagerInternal.USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE;
import static com.android.server.pm.UserVisibilityChangedEvent.onInvisible;
@@ -129,7 +130,28 @@
}
@Test
- public void testStartVisibleBgProfile_onDefaultDisplay_whenParentIsStartedVisibleOnBg()
+ public void
+ testStartVisibleBgProfile_onDefaultDisplay_whenParentIsStartedVisibleOnBgOnSecondaryDisplay()
+ throws Exception {
+ AsyncUserVisibilityListener listener = addListenerForEvents(onVisible(PARENT_USER_ID));
+ startUserInSecondaryDisplay(PARENT_USER_ID, OTHER_SECONDARY_DISPLAY_ID);
+
+ int result = mMediator.assignUserToDisplayOnStart(PROFILE_USER_ID, PARENT_USER_ID,
+ BG_VISIBLE, DEFAULT_DISPLAY);
+ assertStartUserResult(result, USER_ASSIGNMENT_RESULT_FAILURE);
+
+ expectUserIsNotVisibleAtAll(PROFILE_USER_ID);
+ expectNoDisplayAssignedToUser(PROFILE_USER_ID);
+ expectUserAssignedToDisplay(OTHER_SECONDARY_DISPLAY_ID, PARENT_USER_ID);
+
+ assertInvisibleUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID);
+
+ listener.verify();
+ }
+
+ @Test
+ public void
+ testStartVisibleBgProfile_onDefaultDisplay_whenParentIsStartedVisibleOnBgOnDefaultDisplay()
throws Exception {
AsyncUserVisibilityListener listener = addListenerForEvents(
onVisible(PARENT_USER_ID),
@@ -183,4 +205,25 @@
assertUserCannotBeAssignedExtraDisplay(PROFILE_USER_ID, OTHER_SECONDARY_DISPLAY_ID);
listener.verify();
}
+
+ @Test
+ public void testStartVisibleBgUser_onDefaultDisplay_currentUserId() throws Exception {
+ int currentUserId = INITIAL_CURRENT_USER_ID;
+
+ AsyncUserVisibilityListener listener = addListenerForNoEvents();
+
+ int result = mMediator.assignUserToDisplayOnStart(currentUserId, currentUserId,
+ BG_VISIBLE, DEFAULT_DISPLAY);
+ assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_ALREADY_VISIBLE);
+
+ // Assert current user visibility
+ expectUserIsVisible(currentUserId);
+ expectUserIsVisibleOnDisplay(currentUserId, DEFAULT_DISPLAY);
+ expectUserIsNotVisibleOnDisplay(currentUserId, INVALID_DISPLAY);
+ expectDisplayAssignedToUser(currentUserId, DEFAULT_DISPLAY);
+
+ assertUserCanBeAssignedExtraDisplay(currentUserId, OTHER_SECONDARY_DISPLAY_ID);
+
+ listener.verify();
+ }
}