Merge "UserVisibilityMediator: allow visible bg users on default display."
diff --git a/services/core/java/com/android/server/pm/UserVisibilityMediator.java b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
index 66d390f..1f935f90 100644
--- a/services/core/java/com/android/server/pm/UserVisibilityMediator.java
+++ b/services/core/java/com/android/server/pm/UserVisibilityMediator.java
@@ -36,6 +36,7 @@
 import android.os.Handler;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.DebugUtils;
 import android.util.Dumpable;
 import android.util.EventLog;
 import android.util.IndentingPrintWriter;
@@ -80,6 +81,7 @@
 
     private static final String TAG = UserVisibilityMediator.class.getSimpleName();
 
+    private static final String PREFIX_SECONDARY_DISPLAY_MAPPING = "SECONDARY_DISPLAY_MAPPING_";
     public static final int SECONDARY_DISPLAY_MAPPING_NEEDED = 1;
     public static final int SECONDARY_DISPLAY_MAPPING_NOT_NEEDED = 2;
     public static final int SECONDARY_DISPLAY_MAPPING_FAILED = -1;
@@ -88,7 +90,7 @@
      * Whether a user / display assignment requires adding an entry to the
      * {@code mUsersOnSecondaryDisplays} map.
      */
-    @IntDef(flag = false, prefix = {"SECONDARY_DISPLAY_MAPPING_"}, value = {
+    @IntDef(flag = false, prefix = {PREFIX_SECONDARY_DISPLAY_MAPPING}, value = {
             SECONDARY_DISPLAY_MAPPING_NEEDED,
             SECONDARY_DISPLAY_MAPPING_NOT_NEEDED,
             SECONDARY_DISPLAY_MAPPING_FAILED
@@ -102,6 +104,7 @@
     private final Object mLock = new Object();
 
     private final boolean mVisibleBackgroundUsersEnabled;
+    private final boolean mVisibleBackgroundUserOnDefaultDisplayAllowed;
 
     @UserIdInt
     @GuardedBy("mLock")
@@ -110,7 +113,7 @@
     /**
      * Map of background users started visible on displays (key is user id, value is display id).
      *
-     * <p>Only set when {@code mUsersOnSecondaryDisplaysEnabled} is {@code true}.
+     * <p>Only set when {@code mVisibleBackgroundUsersEnabled} is {@code true}.
      */
     @Nullable
     @GuardedBy("mLock")
@@ -121,7 +124,7 @@
      * {@link #assignUserToExtraDisplay(int, int)}) displays assigned to user (key is display id,
      * value is user id).
      *
-     * <p>Only set when {@code mUsersOnSecondaryDisplaysEnabled} is {@code true}.
+     * <p>Only set when {@code mVisibleBackgroundUsersEnabled} is {@code true}.
      */
     @Nullable
     @GuardedBy("mLock")
@@ -143,12 +146,17 @@
             new CopyOnWriteArrayList<>();
 
     UserVisibilityMediator(Handler handler) {
-        this(UserManager.isVisibleBackgroundUsersEnabled(), handler);
+        this(UserManager.isVisibleBackgroundUsersEnabled(),
+                // TODO(b/261538232): get visibleBackgroundUserOnDefaultDisplayAllowed from UM
+                /* visibleBackgroundUserOnDefaultDisplayAllowed= */ false, handler);
     }
 
     @VisibleForTesting
-    UserVisibilityMediator(boolean backgroundUsersOnDisplaysEnabled, Handler handler) {
+    UserVisibilityMediator(boolean backgroundUsersOnDisplaysEnabled,
+            boolean visibleBackgroundUserOnDefaultDisplayAllowed, Handler handler) {
         mVisibleBackgroundUsersEnabled = backgroundUsersOnDisplaysEnabled;
+        mVisibleBackgroundUserOnDefaultDisplayAllowed =
+                visibleBackgroundUserOnDefaultDisplayAllowed;
         if (mVisibleBackgroundUsersEnabled) {
             mUsersAssignedToDisplayOnStart = new SparseIntArray();
             mExtraDisplaysAssignedToUsers = new SparseIntArray();
@@ -203,7 +211,12 @@
                 return result;
             }
 
-            int mappingResult = canAssignUserToDisplayLocked(userId, profileGroupId, displayId);
+            int mappingResult = canAssignUserToDisplayLocked(userId, profileGroupId, userStartMode,
+                    displayId);
+            if (DBG) {
+                Slogf.d(TAG, "mapping result: %s",
+                        secondaryDisplayMappingStatusToString(mappingResult));
+            }
             if (mappingResult == SECONDARY_DISPLAY_MAPPING_FAILED) {
                 return USER_ASSIGNMENT_RESULT_FAILURE;
             }
@@ -263,14 +276,16 @@
                     + "(it should be BACKGROUND_USER_VISIBLE", userId, displayId);
             return USER_ASSIGNMENT_RESULT_FAILURE;
         }
-        if (userStartMode == USER_START_MODE_BACKGROUND_VISIBLE
-                && displayId == DEFAULT_DISPLAY && !isProfile(userId, profileGroupId)) {
+
+        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;
         }
 
         boolean foreground = userStartMode == USER_START_MODE_FOREGROUND;
-
         if (displayId != DEFAULT_DISPLAY) {
             if (foreground) {
                 Slogf.w(TAG, "getUserVisibilityOnStartLocked(%d, %d, %b, %d) failed: cannot start "
@@ -309,26 +324,47 @@
         }
 
         return foreground || displayId != DEFAULT_DISPLAY
-                ? USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE
-                : USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE;
+                || (visibleBackground && mVisibleBackgroundUserOnDefaultDisplayAllowed)
+                        ? USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE
+                        : USER_ASSIGNMENT_RESULT_SUCCESS_INVISIBLE;
     }
 
     @GuardedBy("mLock")
     @SecondaryDisplayMappingStatus
     private int canAssignUserToDisplayLocked(@UserIdInt int userId,
-            @UserIdInt int profileGroupId, int displayId) {
-        if (displayId == DEFAULT_DISPLAY
-                && (!mVisibleBackgroundUsersEnabled || !isProfile(userId, profileGroupId))) {
-            // Don't need to do anything because methods (such as isUserVisible()) already
-            // know that the current user (and its profiles) is assigned to the default display.
-            // But on MUMD devices, profiles are only supported in the default display, so it
-            // cannot return yet as it needs to check if the parent is also assigned to the
-            // DEFAULT_DISPLAY (this is done indirectly below when it checks that the profile 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");
+            @UserIdInt int profileGroupId, @UserStartMode int userStartMode, int displayId) {
+        if (displayId == DEFAULT_DISPLAY) {
+            boolean mappingNeeded = false;
+            if (mVisibleBackgroundUserOnDefaultDisplayAllowed
+                    && userStartMode == USER_START_MODE_BACKGROUND_VISIBLE) {
+                int userStartedOnDefaultDisplay = getUserStartedOnDisplay(DEFAULT_DISPLAY);
+                if (userStartedOnDefaultDisplay != USER_NULL) {
+                    Slogf.w(TAG, "getUserVisibilityOnStartLocked(): cannot start user %d visible on"
+                            + " default display because user %d already did so", userId,
+                            userStartedOnDefaultDisplay);
+                    return SECONDARY_DISPLAY_MAPPING_FAILED;
+                }
+                mappingNeeded = true;
             }
-            return SECONDARY_DISPLAY_MAPPING_NOT_NEEDED;
+            if (!mappingNeeded && mVisibleBackgroundUsersEnabled
+                    && isProfile(userId, profileGroupId)) {
+                mappingNeeded = true;
+            }
+
+            if (!mappingNeeded) {
+                // Don't need to do anything because methods (such as isUserVisible()) already
+                // know that the current user (and its profiles) is assigned to the default display.
+                // But on MUMD devices, profiles are only supported in the default display, so it
+                // cannot return yet as it needs to check if the parent is also assigned to the
+                // DEFAULT_DISPLAY (this is done indirectly below when it checks that the profile
+                // 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",
+                            userId, userStartModeToString(userStartMode));
+                }
+                return SECONDARY_DISPLAY_MAPPING_NOT_NEEDED;
+            }
         }
 
         if (userId == UserHandle.USER_SYSTEM) {
@@ -421,13 +457,14 @@
                 return false;
             }
 
-            int userAssignedToDisplay = getUserAssignedToDisplay(displayId,
-                    /* returnCurrentUserByDefault= */ false);
+            // First check if the user started on display
+            int userAssignedToDisplay = getUserStartedOnDisplay(displayId);
             if (userAssignedToDisplay != USER_NULL) {
                 Slogf.w(TAG, "assignUserToExtraDisplay(%d, %d): failed because display was assigned"
                         + " to user %d on start", userId, displayId, userAssignedToDisplay);
                 return false;
             }
+            // Then if was assigned extra
             userAssignedToDisplay = mExtraDisplaysAssignedToUsers.get(userId, USER_NULL);
             if (userAssignedToDisplay != USER_NULL) {
                 Slogf.w(TAG, "assignUserToExtraDisplay(%d, %d): failed because user %d was already "
@@ -435,7 +472,8 @@
                 return false;
             }
             if (DBG) {
-                Slogf.d(TAG, "addding %d -> %d to map", displayId, userId);
+                Slogf.d(TAG, "addding %d -> %d to mExtraDisplaysAssignedToUsers", displayId,
+                        userId);
             }
             mExtraDisplaysAssignedToUsers.put(displayId, userId);
         }
@@ -501,7 +539,7 @@
         mStartedProfileGroupIds.delete(userId);
 
         if (!mVisibleBackgroundUsersEnabled) {
-            // Don't need to do update mUsersOnSecondaryDisplays because methods (such as
+            // Don't need to update mUsersAssignedToDisplayOnStart because methods (such as
             // isUserVisible()) already know that the current user (and their profiles) is
             // assigned to the default display.
             return;
@@ -536,11 +574,10 @@
             return true;
         }
 
-        // Device doesn't support multiple users on multiple displays, so only users checked above
-        // can be visible
         if (!mVisibleBackgroundUsersEnabled) {
             if (DBG) {
-                Slogf.d(TAG, "isUserVisible(%d): false for non-current user on MUMD", userId);
+                Slogf.d(TAG, "isUserVisible(%d): false for non-current user (or its profiles) when"
+                        + " device doesn't support visible background users", userId);
             }
             return false;
         }
@@ -559,15 +596,29 @@
      * See {@link UserManagerInternal#isUserVisible(int, int)}.
      */
     public boolean isUserVisible(@UserIdInt int userId, int displayId) {
-        if (displayId == Display.INVALID_DISPLAY) {
+        if (displayId == INVALID_DISPLAY) {
             return false;
         }
 
-        if (!mVisibleBackgroundUsersEnabled || displayId == Display.DEFAULT_DISPLAY) {
-            // TODO(b/245939659): will need to move the displayId == Display.DEFAULT_DISPLAY outside
-            // once it supports background users on DEFAULT_DISPLAY (for example, passengers in a
-            // no-driver configuration)
-            return isCurrentUserOrRunningProfileOfCurrentUser(userId);
+        // Current user is always visible on:
+        // - Default display
+        // - Secondary displays when device doesn't support visible bg users
+        //   - Or when explicitly added (which is checked below)
+        if (isCurrentUserOrRunningProfileOfCurrentUser(userId)
+                && (displayId == DEFAULT_DISPLAY || !mVisibleBackgroundUsersEnabled)) {
+            if (VERBOSE) {
+                Slogf.v(TAG, "isUserVisible(%d, %d): returning true for current user/profile",
+                        userId, displayId);
+            }
+            return true;
+        }
+
+        if (!mVisibleBackgroundUsersEnabled) {
+            if (DBG) {
+                Slogf.d(TAG, "isUserVisible(%d, %d): returning false as device does not support"
+                        + " visible background users", userId, displayId);
+            }
+            return false;
         }
 
         synchronized (mLock) {
@@ -575,7 +626,8 @@
                 // User assigned to display on start
                 return true;
             }
-            // Check for extra assignment
+
+            // Check for extra display assignment
             return mExtraDisplaysAssignedToUsers.get(displayId, USER_NULL) == userId;
         }
     }
@@ -585,15 +637,31 @@
      */
     public int getDisplayAssignedToUser(@UserIdInt int userId) {
         if (isCurrentUserOrRunningProfileOfCurrentUser(userId)) {
-            return Display.DEFAULT_DISPLAY;
+            if (mVisibleBackgroundUserOnDefaultDisplayAllowed) {
+                // When device supports visible bg users on default display, the default display is
+                // assigned to the current user, unless a user is started visible on it
+                int userStartedOnDefaultDisplay;
+                synchronized (mLock) {
+                    userStartedOnDefaultDisplay = getUserStartedOnDisplay(DEFAULT_DISPLAY);
+                }
+                if (userStartedOnDefaultDisplay != USER_NULL) {
+                    if (DBG) {
+                        Slogf.d(TAG, "getDisplayAssignedToUser(%d): returning INVALID_DISPLAY for "
+                                + "current user user %d was started on DEFAULT_DISPLAY",
+                                userId, userStartedOnDefaultDisplay);
+                    }
+                    return INVALID_DISPLAY;
+                }
+            }
+            return DEFAULT_DISPLAY;
         }
 
         if (!mVisibleBackgroundUsersEnabled) {
-            return Display.INVALID_DISPLAY;
+            return INVALID_DISPLAY;
         }
 
         synchronized (mLock) {
-            return mUsersAssignedToDisplayOnStart.get(userId, Display.INVALID_DISPLAY);
+            return mUsersAssignedToDisplayOnStart.get(userId, INVALID_DISPLAY);
         }
     }
 
@@ -605,13 +673,21 @@
     }
 
     /**
+     * Gets the user explicitly assigned to a display.
+     */
+    private @UserIdInt int getUserStartedOnDisplay(@UserIdInt int displayId) {
+        return getUserAssignedToDisplay(displayId, /* returnCurrentUserByDefault= */ false);
+    }
+
+    /**
      * Gets the user explicitly assigned to a display, or the current user when no user is assigned
      * to it (and {@code returnCurrentUserByDefault} is {@code true}).
      */
     private @UserIdInt int getUserAssignedToDisplay(@UserIdInt int displayId,
             boolean returnCurrentUserByDefault) {
         if (returnCurrentUserByDefault
-                && (displayId == Display.DEFAULT_DISPLAY || !mVisibleBackgroundUsersEnabled)) {
+                && ((displayId == DEFAULT_DISPLAY && !mVisibleBackgroundUserOnDefaultDisplayAllowed
+                || !mVisibleBackgroundUsersEnabled))) {
             return getCurrentUserId();
         }
 
@@ -763,8 +839,10 @@
             ipw.print("Supports visible background users on displays: ");
             ipw.println(mVisibleBackgroundUsersEnabled);
 
-            dumpSparseIntArray(ipw, mUsersAssignedToDisplayOnStart, "user / display", "u", "d");
+            ipw.print("Allows visible background users on default display: ");
+            ipw.println(mVisibleBackgroundUserOnDefaultDisplayAllowed);
 
+            dumpSparseIntArray(ipw, mUsersAssignedToDisplayOnStart, "user / display", "u", "d");
             dumpSparseIntArray(ipw, mExtraDisplaysAssignedToUsers, "extra display / user",
                     "d", "u");
 
@@ -872,4 +950,10 @@
             return mStartedProfileGroupIds.get(userId, NO_PROFILE_GROUP_ID);
         }
     }
+
+    private static String secondaryDisplayMappingStatusToString(
+            @SecondaryDisplayMappingStatus int status) {
+        return DebugUtils.constantToString(UserVisibilityMediator.class,
+                PREFIX_SECONDARY_DISPLAY_MAPPING, status);
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/DumpableDumperRule.java b/services/tests/mockingservicestests/src/com/android/server/DumpableDumperRule.java
index 33275bd..a0687f6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DumpableDumperRule.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DumpableDumperRule.java
@@ -62,14 +62,21 @@
         };
     }
 
-    private void dumpOnFailure(String testName) throws IOException {
+    /**
+     * Logs all dumpables.
+     */
+    public void dump(String reason) {
         if (mDumpables.isEmpty()) {
             return;
         }
-        Log.w(TAG, "Dumping " + mDumpables.size() + " dumpables on failure of " + testName);
+        Log.w(TAG, "Dumping " + mDumpables.size() + " dumpable(s). Reason: " + reason);
         mDumpables.forEach(d -> logDumpable(d));
     }
 
+    private void dumpOnFailure(String testName) throws IOException {
+        dump("failure of " + testName);
+    }
+
     private void logDumpable(Dumpable dumpable) {
         try {
             try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUMDTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUMDTest.java
index 579621c..36c9f2e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUMDTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUMDTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.server.pm;
 
+import org.junit.Test;
+
 /**
  * Tests for {@link UserVisibilityMediator} tests for devices that support concurrent Multiple
  * Users on Multiple Displays (A.K.A {@code MUMD}).
@@ -26,6 +28,19 @@
         extends UserVisibilityMediatorVisibleBackgroundUserTestCase {
 
     public UserVisibilityMediatorMUMDTest() throws Exception {
-        super(/* usersOnSecondaryDisplaysEnabled= */ true);
+        super(/* backgroundUsersOnDisplaysEnabled= */ true,
+                /* backgroundUserOnDefaultDisplayAllowed= */ false);
+    }
+
+    @Test
+    public void testStartVisibleBgUser_onDefaultDisplay() throws Exception {
+        int userId = visibleBgUserCannotBeStartedOnDefaultDisplayTest();
+
+        assertInvisibleUserCannotBeAssignedExtraDisplay(userId, SECONDARY_DISPLAY_ID);
+    }
+
+    @Test
+    public void testStartBgUser_onDefaultDisplay_visible() throws Exception {
+        visibleBgUserCannotBeStartedOnDefaultDisplayTest();
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUPANDTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUPANDTest.java
new file mode 100644
index 0000000..01ce696
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorMUPANDTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+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_VISIBLE;
+import static com.android.server.pm.UserVisibilityChangedEvent.onInvisible;
+import static com.android.server.pm.UserVisibilityChangedEvent.onVisible;
+import static com.android.server.pm.UserVisibilityMediator.INITIAL_CURRENT_USER_ID;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link UserVisibilityMediator} tests for devices that support not only concurrent
+ * Multiple Users on Multiple Displays, but also let background users to be visible in the default
+ * display (A.K.A {@code MUPAND} - MUltiple Passengers, No Driver).
+ *
+ * <p> Run as {@code
+* atest FrameworksMockingServicesTests:com.android.server.pm.UserVisibilityMediatorMUPANDTest}
+ */
+public final class UserVisibilityMediatorMUPANDTest
+        extends UserVisibilityMediatorVisibleBackgroundUserTestCase {
+
+    public UserVisibilityMediatorMUPANDTest() throws Exception {
+        super(/* backgroundUsersOnDisplaysEnabled= */ true,
+                /* backgroundUserOnDefaultDisplayAllowed= */ true);
+    }
+
+    @Test
+    public void testStartVisibleBgUser_onDefaultDisplay_initialCurrentUserId()
+            throws Exception {
+        int currentUserId = INITIAL_CURRENT_USER_ID;
+        int visibleBgUserId = USER_ID;
+        int otherUserId = OTHER_USER_ID;
+
+        AsyncUserVisibilityListener listener = addListenerForEvents(onVisible(visibleBgUserId));
+
+        int result = mMediator.assignUserToDisplayOnStart(visibleBgUserId, visibleBgUserId,
+                BG_VISIBLE, DEFAULT_DISPLAY);
+        assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE);
+        expectVisibleUsers(currentUserId, visibleBgUserId);
+
+        // Assert bg user visibility
+        expectUserIsVisible(visibleBgUserId);
+        expectUserIsVisibleOnDisplay(visibleBgUserId, DEFAULT_DISPLAY);
+        expectUserIsNotVisibleOnDisplay(visibleBgUserId, INVALID_DISPLAY);
+        expectDisplayAssignedToUser(visibleBgUserId, DEFAULT_DISPLAY);
+        expectUserAssignedToDisplay(DEFAULT_DISPLAY, visibleBgUserId);
+
+        // Assert current user visibility
+        expectUserIsVisible(currentUserId);
+        expectUserIsVisibleOnDisplay(currentUserId, DEFAULT_DISPLAY);
+        expectUserIsNotVisibleOnDisplay(currentUserId, INVALID_DISPLAY);
+        expectDisplayAssignedToUser(currentUserId, INVALID_DISPLAY);
+
+        assertUserCanBeAssignedExtraDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID);
+
+        // Make sure another user cannot be started on default display
+        int result2 = mMediator.assignUserToDisplayOnStart(otherUserId, visibleBgUserId,
+                BG_VISIBLE, DEFAULT_DISPLAY);
+        assertStartUserResult(result2, USER_ASSIGNMENT_RESULT_FAILURE,
+                "when user (%d) is starting on default display after it was started by user %d",
+                otherUserId, visibleBgUserId);
+        expectVisibleUsers(currentUserId, visibleBgUserId);
+
+        listener.verify();
+    }
+
+    @Test
+    public void testStartVisibleBgUser_onDefaultDisplay_nonInitialCurrentUserId()
+            throws Exception {
+        int currentUserId = OTHER_USER_ID;
+        int visibleBgUserId = USER_ID;
+        int otherUserId = YET_ANOTHER_USER_ID;
+
+        AsyncUserVisibilityListener listener = addListenerForEvents(
+                onInvisible(INITIAL_CURRENT_USER_ID),
+                onVisible(currentUserId),
+                onVisible(visibleBgUserId));
+        startForegroundUser(currentUserId);
+
+        int result = mMediator.assignUserToDisplayOnStart(visibleBgUserId, visibleBgUserId,
+                BG_VISIBLE, DEFAULT_DISPLAY);
+        assertStartUserResult(result, USER_ASSIGNMENT_RESULT_SUCCESS_VISIBLE);
+        expectVisibleUsers(currentUserId, visibleBgUserId);
+
+        // Assert bg user visibility
+        expectUserIsVisible(visibleBgUserId);
+        expectUserIsVisibleOnDisplay(visibleBgUserId, DEFAULT_DISPLAY);
+        expectUserIsNotVisibleOnDisplay(visibleBgUserId, INVALID_DISPLAY);
+        expectDisplayAssignedToUser(visibleBgUserId, DEFAULT_DISPLAY);
+        expectUserAssignedToDisplay(DEFAULT_DISPLAY, visibleBgUserId);
+
+        // Assert current user visibility
+        expectUserIsVisible(currentUserId);
+        expectUserIsVisibleOnDisplay(currentUserId, DEFAULT_DISPLAY);
+        expectUserIsNotVisibleOnDisplay(currentUserId, INVALID_DISPLAY);
+        expectDisplayAssignedToUser(currentUserId, INVALID_DISPLAY);
+
+        assertUserCanBeAssignedExtraDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID);
+
+        // Make sure another user cannot be started on default display
+        int result2 = mMediator.assignUserToDisplayOnStart(otherUserId, visibleBgUserId,
+                BG_VISIBLE, DEFAULT_DISPLAY);
+        assertStartUserResult(result2, USER_ASSIGNMENT_RESULT_FAILURE,
+                "when user (%d) is starting on default display after it was started by user %d",
+                otherUserId, visibleBgUserId);
+        expectVisibleUsers(currentUserId, visibleBgUserId);
+
+        listener.verify();
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java
index b9ba780..c195064 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorSUSDTest.java
@@ -37,7 +37,15 @@
 public final class UserVisibilityMediatorSUSDTest extends UserVisibilityMediatorTestCase {
 
     public UserVisibilityMediatorSUSDTest() {
-        super(/* usersOnSecondaryDisplaysEnabled= */ false);
+        super(/* backgroundUsersOnDisplaysEnabled= */ false,
+                /* backgroundUserOnDefaultDisplayAllowed= */ false);
+    }
+
+    @Test
+    public void testStartVisibleBgUser_onDefaultDisplay() throws Exception {
+        int userId = visibleBgUserCannotBeStartedOnDefaultDisplayTest();
+
+        assertInvisibleUserCannotBeAssignedExtraDisplay(userId, SECONDARY_DISPLAY_ID);
     }
 
     @Test
@@ -101,6 +109,11 @@
     }
 
     @Test
+    public void testStartBgUser_onDefaultDisplay_visible() throws Exception {
+        visibleBgUserCannotBeStartedOnDefaultDisplayTest();
+    }
+
+    @Test
     public void testStartVisibleBgProfile_onDefaultDisplay_whenParentIsCurrentUser()
             throws Exception {
         AsyncUserVisibilityListener listener = addListenerForEvents(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
index c59834b..5176d68 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorTestCase.java
@@ -79,6 +79,11 @@
     protected static final int OTHER_USER_ID = 666;
 
     /**
+     * Id for yeat another simple user.
+     */
+    protected static final int YET_ANOTHER_USER_ID = 700;
+
+    /**
      * Id for a user that has one profile (whose id is {@link #PROFILE_USER_ID}.
      *
      * <p>You can use {@link #addDefaultProfileAndParent()} to add both of this user to the service.
@@ -110,11 +115,14 @@
     protected AsyncUserVisibilityListener.Factory mListenerFactory;
 
     private final boolean mBackgroundUsersOnDisplaysEnabled;
+    private final boolean mBackgroundUserOnDefaultDisplayAllowed;
 
     protected UserVisibilityMediator mMediator;
 
-    protected UserVisibilityMediatorTestCase(boolean backgroundUsersOnDisplaysEnabled) {
+    protected UserVisibilityMediatorTestCase(boolean backgroundUsersOnDisplaysEnabled,
+            boolean backgroundUserOnDefaultDisplayAllowed) {
         mBackgroundUsersOnDisplaysEnabled = backgroundUsersOnDisplaysEnabled;
+        mBackgroundUserOnDefaultDisplayAllowed = backgroundUserOnDefaultDisplayAllowed;
     }
 
     @Before
@@ -123,7 +131,8 @@
         Thread thread = mHandler.getLooper().getThread();
         Log.i(TAG, "setFixtures(): using thread " + thread + " (from handler " + mHandler + ")");
         mListenerFactory = new AsyncUserVisibilityListener.Factory(mExpect, thread);
-        mMediator = new UserVisibilityMediator(mBackgroundUsersOnDisplaysEnabled, mHandler);
+        mMediator = new UserVisibilityMediator(mBackgroundUsersOnDisplaysEnabled,
+                mBackgroundUserOnDefaultDisplayAllowed, mHandler);
         mDumpableDumperRule.addDumpable(mMediator);
     }
 
@@ -170,14 +179,8 @@
         listener.verify();
     }
 
-    @Test
-    public final void testStartVisibleBgUser_onDefaultDisplay() throws Exception {
-        visibleBgUserCannotBeStartedOnDefaultDisplayTest();
-
-        assertInvisibleUserCannotBeAssignedExtraDisplay(USER_ID, SECONDARY_DISPLAY_ID);
-    }
-
-    protected final void visibleBgUserCannotBeStartedOnDefaultDisplayTest() throws Exception {
+    protected final @UserIdInt int visibleBgUserCannotBeStartedOnDefaultDisplayTest()
+            throws Exception {
         AsyncUserVisibilityListener listener = addListenerForNoEvents();
 
         int result = mMediator.assignUserToDisplayOnStart(USER_ID, USER_ID, BG_VISIBLE,
@@ -188,6 +191,8 @@
         expectNoDisplayAssignedToUser(USER_ID);
 
         listener.verify();
+
+        return USER_ID;
     }
 
     @Test
@@ -504,7 +509,14 @@
     }
 
     protected void assertStartUserResult(int actualResult, int expectedResult) {
-        assertWithMessage("startUser() result (where %s=%s and %s=%s)",
+        assertStartUserResult(actualResult, expectedResult, "");
+    }
+
+    @SuppressWarnings("AnnotateFormatMethod")
+    protected void assertStartUserResult(int actualResult, int expectedResult,
+            String extraMessageFormat, Object... extraMessageArguments) {
+        String extraMessage = String.format(extraMessageFormat, extraMessageArguments);
+        assertWithMessage("startUser() result %s(where %s=%s and %s=%s)", extraMessage,
                 expectedResult, userAssignmentResultToString(expectedResult),
                 actualResult, userAssignmentResultToString(actualResult))
                         .that(actualResult).isEqualTo(expectedResult);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorVisibleBackgroundUserTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorVisibleBackgroundUserTestCase.java
index 627553b..49c6a88 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorVisibleBackgroundUserTestCase.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserVisibilityMediatorVisibleBackgroundUserTestCase.java
@@ -38,9 +38,9 @@
 abstract class UserVisibilityMediatorVisibleBackgroundUserTestCase
         extends UserVisibilityMediatorTestCase {
 
-    UserVisibilityMediatorVisibleBackgroundUserTestCase(boolean backgroundUsersOnDisplaysEnabled)
-            throws Exception {
-        super(backgroundUsersOnDisplaysEnabled);
+    UserVisibilityMediatorVisibleBackgroundUserTestCase(boolean backgroundUsersOnDisplaysEnabled,
+            boolean backgroundUserOnDefaultDisplayAllowed) throws Exception {
+        super(backgroundUsersOnDisplaysEnabled, backgroundUserOnDefaultDisplayAllowed);
     }
 
     @Test