Merge "Revert "Revert "Do not send updates for disabled displays.""" into tm-dev
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index b505395..69c6ba9 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -559,18 +559,21 @@
      * @see #DISPLAY_CATEGORY_PRESENTATION
      */
     public Display[] getDisplays(String category) {
-        final int[] displayIds = mGlobal.getDisplayIds();
+        boolean includeDisabledDisplays = (category != null
+                && category.equals(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED));
+        final int[] displayIds = mGlobal.getDisplayIds(includeDisabledDisplays);
         synchronized (mLock) {
             try {
-                if (category == null
-                        || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) {
-                    addAllDisplaysLocked(mTempDisplays, displayIds);
-                } else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
+                if (category != null && category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_EXTERNAL);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL);
+                } else if ((category == null
+                        || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category))) {
+                    // All displays requested.
+                    addAllDisplaysLocked(mTempDisplays, displayIds);
                 }
                 return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
             } finally {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 74356dd..da3a580 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -206,6 +206,16 @@
      */
     @UnsupportedAppUsage
     public int[] getDisplayIds() {
+        return getDisplayIds(/* includeDisabledDisplays= */ false);
+    }
+
+    /**
+     * Gets all valid logical display ids and invalid ones if specified.
+     *
+     * @return An array containing all display ids.
+     */
+    @UnsupportedAppUsage
+    public int[] getDisplayIds(boolean includeDisabledDisplays) {
         try {
             synchronized (mLock) {
                 if (USE_CACHE) {
@@ -214,7 +224,8 @@
                     }
                 }
 
-                int[] displayIds = mDm.getDisplayIds();
+                int[] displayIds =
+                        mDm.getDisplayIds(includeDisabledDisplays);
                 if (USE_CACHE) {
                     mDisplayIdCache = displayIds;
                 }
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index ca3e580..a4115d1 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -36,7 +36,7 @@
 interface IDisplayManager {
     @UnsupportedAppUsage
     DisplayInfo getDisplayInfo(int displayId);
-    int[] getDisplayIds();
+    int[] getDisplayIds(boolean includeDisabled);
 
     boolean isUidPresentOnDisplay(int uid, int displayId);
 
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 6285ef1..d3e2966 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -485,7 +485,7 @@
         mUiHandler = UiThread.getHandler();
         mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore);
         mLogicalDisplayMapper = new LogicalDisplayMapper(mContext, mDisplayDeviceRepo,
-                new LogicalDisplayListener(), mSyncRoot, mHandler);
+                new LogicalDisplayListener(), mSyncRoot, mHandler, new DeviceStateToLayoutMap());
         mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
         mBrightnessSynchronizer = new BrightnessSynchronizer(mContext);
         Resources resources = mContext.getResources();
@@ -945,7 +945,8 @@
 
     private DisplayInfo getDisplayInfoInternal(int displayId, int callingUid) {
         synchronized (mSyncRoot) {
-            final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
+            final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId,
+                    /* includeDisabledDisplays= */ true);
             if (display != null) {
                 final DisplayInfo info =
                         getDisplayInfoForFrameRateOverride(display.getFrameRateOverrides(),
@@ -2128,16 +2129,18 @@
     }
 
     void resetBrightnessConfigurations() {
-        mPersistentDataStore.setBrightnessConfigurationForUser(null, mContext.getUserId(),
-                mContext.getPackageName());
-        mLogicalDisplayMapper.forEachLocked((logicalDisplay -> {
-            if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
-                return;
-            }
-            final String uniqueId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
-            setBrightnessConfigurationForDisplayInternal(null, uniqueId, mContext.getUserId(),
+        synchronized (mSyncRoot) {
+            mPersistentDataStore.setBrightnessConfigurationForUser(null, mContext.getUserId(),
                     mContext.getPackageName());
-        }));
+            mLogicalDisplayMapper.forEachLocked((logicalDisplay -> {
+                if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
+                    return;
+                }
+                String uniqueId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+                setBrightnessConfigurationForDisplayInternal(null, uniqueId, mContext.getUserId(),
+                        mContext.getPackageName());
+            }));
+        }
     }
 
     void setAutoBrightnessLoggingEnabled(boolean enabled) {
@@ -2814,15 +2817,16 @@
         }
 
         /**
-         * Returns the list of all display ids.
+         * Returns the list of all enabled display ids, and disabled ones if specified.
          */
         @Override // Binder call
-        public int[] getDisplayIds() {
+        public int[] getDisplayIds(boolean includeDisabledDisplays) {
             final int callingUid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mSyncRoot) {
-                    return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid);
+                    return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid,
+                            includeDisabledDisplays);
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 70c9e23..34e8e75 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -79,8 +79,12 @@
     private static final int MSG_TRANSITION_TO_PENDING_DEVICE_STATE = 1;
 
     private static final int UPDATE_STATE_NEW = 0;
-    private static final int UPDATE_STATE_TRANSITION = 1;
-    private static final int UPDATE_STATE_UPDATED = 2;
+    private static final int UPDATE_STATE_UPDATED = 1;
+    private static final int UPDATE_STATE_DISABLED = 2;
+
+    private static final int UPDATE_STATE_MASK = 0x3;
+
+    private static final int UPDATE_STATE_FLAG_TRANSITION = 0x100;
 
     /**
      * Temporary display info, used for comparing display configurations.
@@ -166,7 +170,7 @@
 
     LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
             @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
-            @NonNull Handler handler) {
+            @NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap) {
         mSyncRoot = syncRoot;
         mPowerManager = context.getSystemService(PowerManager.class);
         mInteractive = mPowerManager.isInteractive();
@@ -181,7 +185,7 @@
         mDeviceStatesOnWhichToSleep = toSparseBooleanArray(context.getResources().getIntArray(
                 com.android.internal.R.array.config_deviceStatesOnWhichToSleep));
         mDisplayDeviceRepo.addListener(this);
-        mDeviceStateToLayoutMap = new DeviceStateToLayoutMap();
+        mDeviceStateToLayoutMap = deviceStateToLayoutMap;
     }
 
     @Override
@@ -218,10 +222,29 @@
     }
 
     public LogicalDisplay getDisplayLocked(int displayId) {
-        return mLogicalDisplays.get(displayId);
+        return getDisplayLocked(displayId, /* includeDisabled= */ false);
+    }
+
+    LogicalDisplay getDisplayLocked(int displayId, boolean includeDisabled) {
+        LogicalDisplay display = mLogicalDisplays.get(displayId);
+        if (display != null && (display.isEnabled() || includeDisabled)) {
+            return display;
+        }
+        return null;
     }
 
     public LogicalDisplay getDisplayLocked(DisplayDevice device) {
+        return getDisplayLocked(device, /* includeDisabled= */ false);
+    }
+
+    /**
+     * Loops through the existing list of displays and returns one that is associated with the
+     * specified display device.
+     *
+     * @param device The display device that should be associated with the LogicalDisplay.
+     * @param includeDisabled True if this method should return disabled displays as well.
+     */
+    private LogicalDisplay getDisplayLocked(DisplayDevice device, boolean includeDisabled) {
         if (device == null) {
             return null;
         }
@@ -229,18 +252,32 @@
         for (int i = 0; i < count; i++) {
             final LogicalDisplay display = mLogicalDisplays.valueAt(i);
             if (display.getPrimaryDisplayDeviceLocked() == device) {
-                return display;
+                if (display.isEnabled() || includeDisabled) {
+                    return display;
+                } else {
+                    return null;
+                }
             }
         }
         return null;
     }
 
+    // Returns display Ids, defaults to enabled only.
     public int[] getDisplayIdsLocked(int callingUid) {
+        return getDisplayIdsLocked(callingUid, /* includeDisabledDisplays= */ false);
+    }
+
+    // Returns display Ids, specified whether enabled only, or all displays.
+    public int[] getDisplayIdsLocked(int callingUid, boolean includeDisabledDisplays) {
         final int count = mLogicalDisplays.size();
         int[] displayIds = new int[count];
         int n = 0;
         for (int i = 0; i < count; i++) {
             LogicalDisplay display = mLogicalDisplays.valueAt(i);
+            if (!includeDisabledDisplays && !display.isEnabled()) {
+                continue; // Ignore disabled displays.
+            }
+
             DisplayInfo info = display.getDisplayInfoLocked();
             if (info.hasAccess(callingUid)) {
                 displayIds[n++] = mLogicalDisplays.keyAt(i);
@@ -255,7 +292,10 @@
     public void forEachLocked(Consumer<LogicalDisplay> consumer) {
         final int count = mLogicalDisplays.size();
         for (int i = 0; i < count; i++) {
-            consumer.accept(mLogicalDisplays.valueAt(i));
+            LogicalDisplay display = mLogicalDisplays.valueAt(i);
+            if (display.isEnabled()) {
+                consumer.accept(display);
+            }
         }
     }
 
@@ -316,7 +356,8 @@
 
             // Find or create the LogicalDisplay to map the DisplayDevice to.
             final int logicalDisplayId = displayLayout.getLogicalDisplayId();
-            final LogicalDisplay logicalDisplay = getDisplayLocked(logicalDisplayId);
+            final LogicalDisplay logicalDisplay =
+                    getDisplayLocked(logicalDisplayId, /* includeDisabled= */ true);
             if (logicalDisplay == null) {
                 Slog.w(TAG, "The logical display (" + address + "), is not available"
                         + " for the display state " + deviceState);
@@ -452,7 +493,7 @@
     }
 
     /**
-     * Returns if the device should be put to sleep or not.
+     * Returns true if the device should be put to sleep or not.
      *
      * Includes a check to verify that the device state that we are moving to, {@code pendingState},
      * is the same as the physical state of the device, {@code baseState}. Different values for
@@ -598,9 +639,12 @@
             display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
 
             display.updateLocked(mDisplayDeviceRepo);
-            final DisplayInfo newDisplayInfo = display.getDisplayInfoLocked();
-            final int updateState = mUpdatedLogicalDisplays.get(displayId, UPDATE_STATE_NEW);
-            final boolean wasPreviouslyUpdated = updateState != UPDATE_STATE_NEW;
+            DisplayInfo newDisplayInfo = display.getDisplayInfoLocked();
+
+            final int storedState = mUpdatedLogicalDisplays.get(displayId, UPDATE_STATE_NEW);
+            final int updateState = storedState & UPDATE_STATE_MASK;
+            final boolean isTransitioning = (storedState & UPDATE_STATE_FLAG_TRANSITION) != 0;
+            final boolean wasPreviouslyUpdated = updateState == UPDATE_STATE_UPDATED;
 
             // The display is no longer valid and needs to be removed.
             if (!display.isValidLocked()) {
@@ -624,6 +668,35 @@
                 }
                 continue;
 
+            // The display has been newly disabled, we report this as a removed display but
+            // don't actually remove it from our internal list in LogicalDisplayMapper. The reason
+            // is that LogicalDisplayMapper assumes and relies on the fact that every DisplayDevice
+            // has a LogicalDisplay wrapper, but certain displays that are unusable (like the inner
+            // display on a folded foldable device) are not available for use by the system and
+            // we keep them hidden. To do this, we mark those LogicalDisplays as "disabled".
+            // Also, if the display is in TRANSITION but was previously reported as disabled
+            // then keep it unreported.
+            } else if (!display.isEnabled()
+                    || (display.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION
+                        && updateState == UPDATE_STATE_DISABLED)) {
+                mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_DISABLED);
+
+                // If we never told anyone about this display, nothing to do
+                if (!wasPreviouslyUpdated) {
+                    continue;
+                }
+
+                // Remove from group
+                final DisplayGroup displayGroup = getDisplayGroupLocked(
+                        getDisplayGroupIdFromDisplayIdLocked(displayId));
+                if (displayGroup != null) {
+                    displayGroup.removeDisplayLocked(display);
+                }
+
+                Slog.i(TAG, "Removing (disabled) display: " + displayId);
+                mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_REMOVED);
+                continue;
+
             // The display is new.
             } else if (!wasPreviouslyUpdated) {
                 Slog.i(TAG, "Adding new display: " + displayId + ": " + newDisplayInfo);
@@ -643,7 +716,7 @@
                 mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
 
             // The display is involved in a display layout transition
-            } else if (updateState == UPDATE_STATE_TRANSITION) {
+            } else if (isTransitioning) {
                 mLogicalDisplaysToUpdate.put(displayId,
                         LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION);
 
@@ -717,7 +790,7 @@
             }
 
             final int id = mLogicalDisplaysToUpdate.keyAt(i);
-            final LogicalDisplay display = getDisplayLocked(id);
+            final LogicalDisplay display = getDisplayLocked(id, /* includeDisabled= */ true);
             if (DEBUG) {
                 final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
                 final String uniqueId = device == null ? "null" : device.getUniqueId();
@@ -725,7 +798,7 @@
                         + " with device=" + uniqueId);
             }
             mListener.onLogicalDisplayEventLocked(display, msg);
-            if (msg == LOGICAL_DISPLAY_EVENT_REMOVED) {
+            if (msg == LOGICAL_DISPLAY_EVENT_REMOVED && !display.isValidLocked()) {
                 // We wait until we sent the EVENT_REMOVED event before actually removing the
                 // display.
                 mLogicalDisplays.delete(id);
@@ -845,7 +918,8 @@
             if (isTransitioning) {
                 setDisplayPhase(logicalDisplay, phase);
                 if (phase == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) {
-                    mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_TRANSITION);
+                    int oldState = mUpdatedLogicalDisplays.get(displayId, UPDATE_STATE_NEW);
+                    mUpdatedLogicalDisplays.put(displayId, oldState | UPDATE_STATE_FLAG_TRANSITION);
                 }
             }
         }
@@ -879,14 +953,15 @@
             // Now that we have a display-device, we need a LogicalDisplay to map it to. Find the
             // right one, if it doesn't exist, create a new one.
             final int logicalDisplayId = displayLayout.getLogicalDisplayId();
-            LogicalDisplay newDisplay = getDisplayLocked(logicalDisplayId);
+            LogicalDisplay newDisplay =
+                    getDisplayLocked(logicalDisplayId, /* includeDisabled= */ true);
             if (newDisplay == null) {
                 newDisplay = createNewLogicalDisplayLocked(
-                        null /*displayDevice*/, logicalDisplayId);
+                        /* displayDevice= */ null, logicalDisplayId);
             }
 
             // Now swap the underlying display devices between the old display and the new display
-            final LogicalDisplay oldDisplay = getDisplayLocked(device);
+            final LogicalDisplay oldDisplay = getDisplayLocked(device, /* includeDisabled= */ true);
             if (newDisplay != oldDisplay) {
                 newDisplay.swapDisplaysLocked(oldDisplay);
             }
@@ -903,13 +978,14 @@
      * Creates a new logical display for the specified device and display Id and adds it to the list
      * of logical displays.
      *
-     * @param device The device to associate with the LogicalDisplay.
+     * @param displayDevice The displayDevice to associate with the LogicalDisplay.
      * @param displayId The display ID to give the new display. If invalid, a new ID is assigned.
      * @return The new logical display if created, null otherwise.
      */
-    private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice device, int displayId) {
+    private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice displayDevice,
+            int displayId) {
         final int layerStack = assignLayerStackLocked(displayId);
-        final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
+        final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, displayDevice);
         display.updateLocked(mDisplayDeviceRepo);
         mLogicalDisplays.put(displayId, display);
         setDisplayPhase(display, LogicalDisplay.DISPLAY_PHASE_ENABLED);
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 1e97c1c..5f1ff6b 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -287,7 +287,7 @@
 
         when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
 
-        final int displayIds[] = bs.getDisplayIds();
+        final int[] displayIds = bs.getDisplayIds(/* includeDisabled= */ false);
         final int size = displayIds.length;
         assertTrue(size > 0);
 
@@ -297,7 +297,9 @@
         );
         for (int i = 0; i < size; i++) {
             DisplayInfo info = bs.getDisplayInfo(displayIds[i]);
-            assertTrue(expectedDisplayTypeToViewPortTypeMapping.keySet().contains(info.type));
+            if (info != null) {
+                assertTrue(expectedDisplayTypeToViewPortTypeMapping.keySet().contains(info.type));
+            }
         }
 
         displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -1174,7 +1176,8 @@
             DisplayManagerService.BinderService displayManagerBinderService,
             FakeDisplayDevice displayDevice) {
 
-        final int[] displayIds = displayManagerBinderService.getDisplayIds();
+        final int[] displayIds = displayManagerBinderService.getDisplayIds(
+                /* includeDisabled= */ false);
         assertTrue(displayIds.length > 0);
         int displayId = Display.INVALID_DISPLAY;
         for (int i = 0; i < displayIds.length; i++) {
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index cc68ba8..5b13145 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -33,6 +33,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -53,6 +54,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.display.layout.Layout;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -79,6 +82,7 @@
     private TestLooper mLooper;
     private Handler mHandler;
     private PowerManager mPowerManager;
+    private DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy;
 
     @Mock LogicalDisplayMapper.Listener mListenerMock;
     @Mock Context mContextMock;
@@ -133,8 +137,11 @@
 
         mLooper = new TestLooper();
         mHandler = new Handler(mLooper.getLooper());
+
+        mDeviceStateToLayoutMapSpy = spy(new DeviceStateToLayoutMap());
         mLogicalDisplayMapper = new LogicalDisplayMapper(mContextMock, mDisplayDeviceRepo,
-                mListenerMock, new DisplayManagerService.SyncRoot(), mHandler);
+                mListenerMock, new DisplayManagerService.SyncRoot(), mHandler,
+                mDeviceStateToLayoutMapSpy);
     }
 
 
@@ -413,6 +420,86 @@
                 /* isBootCompleted= */true));
     }
 
+    @Test public void testEnabledAndDisabledDisplays() {
+        DisplayAddress displayAddressOne = new TestUtils.TestDisplayAddress();
+        DisplayAddress displayAddressTwo = new TestUtils.TestDisplayAddress();
+        DisplayAddress displayAddressThree = new TestUtils.TestDisplayAddress();
+
+        TestDisplayDevice device1 = createDisplayDevice(displayAddressOne, Display.TYPE_INTERNAL,
+                600, 800,
+                DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
+        TestDisplayDevice device2 = createDisplayDevice(displayAddressTwo, Display.TYPE_INTERNAL,
+                200, 800,
+                DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
+        TestDisplayDevice device3 = createDisplayDevice(displayAddressThree, Display.TYPE_INTERNAL,
+                600, 900, DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
+
+        Layout threeDevicesEnabledLayout = new Layout();
+        threeDevicesEnabledLayout.createDisplayLocked(
+                displayAddressOne,
+                /* isDefault= */ true,
+                /* isEnabled= */ true);
+
+        threeDevicesEnabledLayout.createDisplayLocked(
+                displayAddressTwo,
+                /* isDefault= */ false,
+                /* isEnabled= */ true);
+        threeDevicesEnabledLayout.createDisplayLocked(
+                displayAddressThree,
+                /* isDefault= */ false,
+                /* isEnabled= */ true);
+
+        when(mDeviceStateToLayoutMapSpy.get(DeviceStateToLayoutMap.STATE_DEFAULT))
+                .thenReturn(threeDevicesEnabledLayout);
+
+        LogicalDisplay display1 = add(device1);
+        LogicalDisplay display2 = add(device2);
+        LogicalDisplay display3 = add(device3);
+
+        // ensure 3 displays are returned
+        int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID);
+        assertEquals(3, ids.length);
+        Arrays.sort(ids);
+        assertEquals(DEFAULT_DISPLAY, ids[0]);
+
+        Layout oneDeviceEnabledLayout = new Layout();
+        oneDeviceEnabledLayout.createDisplayLocked(
+                display1.getDisplayInfoLocked().address,
+                /* isDefault= */ true,
+                /* isEnabled= */ true);
+        oneDeviceEnabledLayout.createDisplayLocked(
+                display2.getDisplayInfoLocked().address,
+                /* isDefault= */ false,
+                /* isEnabled= */ false);
+        oneDeviceEnabledLayout.createDisplayLocked(
+                display3.getDisplayInfoLocked().address,
+                /* isDefault= */ false,
+                /* isEnabled= */ false);
+
+        when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(oneDeviceEnabledLayout);
+        when(mDeviceStateToLayoutMapSpy.get(1)).thenReturn(threeDevicesEnabledLayout);
+
+        mLogicalDisplayMapper
+                .setDeviceStateLocked(0, false);
+        mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
+        final int[] allDisplayIds = mLogicalDisplayMapper.getDisplayIdsLocked(
+                Process.SYSTEM_UID, false);
+        mLooper.dispatchAll();
+
+        // ensure only one display is returned
+        assertEquals(1, allDisplayIds.length);
+
+        mLogicalDisplayMapper
+                .setDeviceStateLocked(1, false);
+        mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
+        final int[] threeDisplaysEnabled = mLogicalDisplayMapper.getDisplayIdsLocked(
+                Process.SYSTEM_UID, false);
+        mLooper.dispatchAll();
+
+        // ensure all three displays are returned
+        assertEquals(3, threeDisplaysEnabled.length);
+    }
+
     /////////////////
     // Helper Methods
     /////////////////