From 65f9addcdd8696d34f7531d161ae39522c237121 Mon Sep 17 00:00:00 2001 From: Santos Cordon Date: Tue, 15 Nov 2022 14:32:08 +0000 Subject: [PATCH] Do not send updates for disabled displays. If a display is disabled, do not share it outside of DisplayManagerService. This will help by not creating any unnecessary updates for displays that are disabled, and also not informing Applications of displays that they are unable to use. Also fixing a concurrency issue with DMS.handleLogicalDisplaySwapped which became apparent with this CL. Deferring the runnable with a Handler.post() caused the display-state-OFF update to happen after a newer change to turn the display ON. This caused the device to startup into a black screen. Removed the unnecessary call (since the displays are now made to both go into the "transition" state) to update the state and removed a lot of the redundant code with handleLogicalDisplayChange() which was called at the end of the method anyway. Bug: 221071695 Test: atest com.server.android.display Test: Manually verify display layouts still successfully change, and also confirm logs no longer hand excessive add/remove display notifications. Change-Id: I003fd91567630a447cf030b6fb83e5cb1041bcb5 Merged-In: I003fd91567630a447cf030b6fb83e5cb1041bcb5 --- .../hardware/display/DisplayManager.java | 12 +- .../display/DisplayManagerGlobal.java | 12 +- .../hardware/display/IDisplayManager.aidl | 2 +- .../server/display/DisplayManagerService.java | 45 ++--- .../server/display/DisplayModeDirector.java | 9 +- .../display/DisplayPowerController.java | 35 ++-- .../server/display/DisplayPowerState.java | 11 +- .../server/display/LogicalDisplay.java | 95 ++++----- .../server/display/LogicalDisplayMapper.java | 101 ++++++---- .../display/DisplayManagerServiceTest.java | 5 +- .../display/LogicalDisplayMapperTest.java | 185 +++++++++++++++++- .../server/display/LogicalDisplayTest.java | 4 +- 12 files changed, 375 insertions(+), 141 deletions(-) diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 8bc11cbc61de9..f94e0313771e6 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -559,18 +559,20 @@ public final class DisplayManager { * @see #DISPLAY_CATEGORY_PRESENTATION */ public Display[] getDisplays(String category) { - final int[] displayIds = mGlobal.getDisplayIds(); + boolean includeDisabled = (category != null + && category.equals(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)); + final int[] displayIds = mGlobal.getDisplayIds(includeDisabled); synchronized (mLock) { try { - if (category == null - || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) { - addAllDisplaysLocked(mTempDisplays, displayIds); - } else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) { + if (DISPLAY_CATEGORY_PRESENTATION.equals(category)) { 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)) { + 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 74356ddecc76d..63dc7c7ed6615 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -206,6 +206,16 @@ public final class DisplayManagerGlobal { */ @UnsupportedAppUsage public int[] getDisplayIds() { + return getDisplayIds(/* includeDisabled= */ false); + } + + /** + * Gets all currently valid logical display ids. + * + * @param includeDisabled True if the returned list of displays includes disabled displays. + * @return An array containing all display ids. + */ + public int[] getDisplayIds(boolean includeDisabled) { try { synchronized (mLock) { if (USE_CACHE) { @@ -214,7 +224,7 @@ public final class DisplayManagerGlobal { } } - int[] displayIds = mDm.getDisplayIds(); + int[] displayIds = mDm.getDisplayIds(includeDisabled); if (USE_CACHE) { mDisplayIdCache = displayIds; } diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index ca3e580944005..a4115d178f6f0 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -36,7 +36,7 @@ import android.view.Surface; 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 1b20e6a47cb82..f9c8f064de968 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1545,7 +1545,7 @@ public final class DisplayManagerService extends SystemService { mSyncRoot.notifyAll(); } - sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED); + sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_ADDED); Runnable work = updateDisplayStateLocked(device); if (work != null) { @@ -1564,7 +1564,7 @@ public final class DisplayManagerService extends SystemService { // We don't bother invalidating the display info caches here because any changes to the // display info will trigger a cache invalidation inside of LogicalDisplay before we hit // this point. - sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); + sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); scheduleTraversalLocked(false); mPersistentDataStore.saveIfNeeded(); @@ -1593,7 +1593,7 @@ public final class DisplayManagerService extends SystemService { mDisplayStates.delete(displayId); mDisplayBrightnesses.delete(displayId); DisplayManagerGlobal.invalidateLocalDisplayInfoCaches(); - sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED); + sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED); scheduleTraversalLocked(false); if (mDisplayWindowPolicyControllers.contains(displayId)) { @@ -1609,24 +1609,13 @@ public final class DisplayManagerService extends SystemService { } private void handleLogicalDisplaySwappedLocked(@NonNull LogicalDisplay display) { - final DisplayDevice device = display.getPrimaryDisplayDeviceLocked(); - final Runnable work = updateDisplayStateLocked(device); - if (work != null) { - mHandler.post(work); - } - final int displayId = display.getDisplayIdLocked(); + handleLogicalDisplayChangedLocked(display); + final int displayId = display.getDisplayIdLocked(); if (displayId == Display.DEFAULT_DISPLAY) { notifyDefaultDisplayDeviceUpdated(display); } - - DisplayPowerController dpc = mDisplayPowerControllers.get(displayId); - if (dpc != null) { - dpc.onDisplayChanged(); - } - mPersistentDataStore.saveIfNeeded(); mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATIONS); - handleLogicalDisplayChangedLocked(display); } private void notifyDefaultDisplayDeviceUpdated(LogicalDisplay display) { @@ -1638,7 +1627,7 @@ public final class DisplayManagerService extends SystemService { final int displayId = display.getDisplayIdLocked(); final DisplayPowerController dpc = mDisplayPowerControllers.get(displayId); if (dpc != null) { - dpc.onDeviceStateTransition(); + dpc.onDisplayChanged(); } } @@ -2348,9 +2337,13 @@ public final class DisplayManagerService extends SystemService { } } - private void sendDisplayEventLocked(int displayId, @DisplayEvent int event) { - Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event); - mHandler.sendMessage(msg); + private void sendDisplayEventLocked(@NonNull LogicalDisplay display, @DisplayEvent int event) { + // Only send updates outside of DisplayManagerService for enabled displays + if (display.isEnabledLocked()) { + int displayId = display.getDisplayIdLocked(); + Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event); + mHandler.sendMessage(msg); + } } private void sendDisplayGroupEvent(int groupId, int event) { @@ -2636,8 +2629,7 @@ public final class DisplayManagerService extends SystemService { } private void handleBrightnessChange(LogicalDisplay display) { - sendDisplayEventLocked(display.getDisplayIdLocked(), - DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED); + sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED); } private DisplayDevice getDeviceForDisplayLocked(int displayId) { @@ -2854,12 +2846,12 @@ public final class DisplayManagerService extends SystemService { * Returns the list of all display ids. */ @Override // Binder call - public int[] getDisplayIds() { + public int[] getDisplayIds(boolean includeDisabled) { final int callingUid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { synchronized (mSyncRoot) { - return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid); + return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid, includeDisabled); } } finally { Binder.restoreCallingIdentity(token); @@ -3337,6 +3329,11 @@ public final class DisplayManagerService extends SystemService { final long token = Binder.clearCallingIdentity(); try { synchronized (mSyncRoot) { + LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked( + displayId, /* includeDisabled= */ false); + if (display == null || !display.isEnabledLocked()) { + return null; + } DisplayPowerController dpc = mDisplayPowerControllers.get(displayId); if (dpc != null) { return dpc.getBrightnessInfo(); diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index c131ed62c6b2b..ecae8330d5321 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -16,6 +16,7 @@ package com.android.server.display; +import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED; import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE; import static android.os.PowerManager.BRIGHTNESS_INVALID; @@ -1457,7 +1458,7 @@ public class DisplayModeDirector { SparseArray modes = new SparseArray<>(); SparseArray defaultModes = new SparseArray<>(); DisplayInfo info = new DisplayInfo(); - Display[] displays = dm.getDisplays(); + Display[] displays = dm.getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED); for (Display d : displays) { final int displayId = d.getDisplayId(); d.getDisplayInfo(info); @@ -2332,7 +2333,8 @@ public class DisplayModeDirector { sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this); synchronized (mSensorObserverLock) { - for (Display d : mDisplayManager.getDisplays()) { + for (Display d : mDisplayManager.getDisplays( + DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) { mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d)); } } @@ -2343,7 +2345,8 @@ public class DisplayModeDirector { } private void recalculateVotesLocked() { - final Display[] displays = mDisplayManager.getDisplays(); + final Display[] displays = mDisplayManager.getDisplays( + DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED); for (Display d : displays) { int displayId = d.getDisplayId(); Vote vote = null; diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index c426e69332c7d..d7bbb109b6f71 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -491,6 +491,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private final String mSuspendBlockerIdProxNegative; private final String mSuspendBlockerIdProxDebounce; + private boolean mIsEnabled; + private boolean mIsInTransition; + /** * Creates the display power controller. */ @@ -512,6 +515,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); mDisplayStatsId = mUniqueDisplayId.hashCode(); + mIsEnabled = logicalDisplay.isEnabledLocked(); + mIsInTransition = logicalDisplay.isInTransitionLocked(); mHandler = new DisplayControllerHandler(handler.getLooper()); mLastBrightnessEvent = new BrightnessEvent(mDisplayId); mTempBrightnessEvent = new BrightnessEvent(mDisplayId); @@ -789,13 +794,30 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final DisplayDeviceConfig config = device.getDisplayDeviceConfig(); final IBinder token = device.getDisplayTokenLocked(); final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); + final boolean isEnabled = mLogicalDisplay.isEnabledLocked(); + final boolean isInTransition = mLogicalDisplay.isInTransitionLocked(); mHandler.post(() -> { + boolean changed = false; if (mDisplayDevice != device) { + changed = true; mDisplayDevice = device; mUniqueDisplayId = uniqueId; mDisplayStatsId = mUniqueDisplayId.hashCode(); mDisplayDeviceConfig = config; loadFromDisplayDeviceConfig(token, info); + + // Since the underlying display-device changed, we really don't know the + // last command that was sent to change it's state. Lets assume it is unknown so + // that we trigger a change immediately. + mPowerState.resetScreenState(); + } + if (mIsEnabled != isEnabled || mIsInTransition != isInTransition) { + changed = true; + mIsEnabled = isEnabled; + mIsInTransition = isInTransition; + } + + if (changed) { if (DEBUG) { Trace.beginAsyncSection("DisplayPowerController#updatePowerState", 0); } @@ -807,15 +829,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call }); } - /** - * Called when the displays are preparing to transition from one device state to another. - * This process involves turning off some displays so we need updatePowerState() to run and - * calculate the new state. - */ - public void onDeviceStateTransition() { - sendUpdatePowerState(); - } - /** * Unregisters all listeners and interrupts all running threads; halting future work. * @@ -1291,8 +1304,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mIgnoreProximityUntilChanged = false; } - if (!mLogicalDisplay.isEnabled() - || mLogicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION + if (!mIsEnabled + || mIsInTransition || mScreenOffBecauseOfProximity) { state = Display.STATE_OFF; } diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 2f22d33f552aa..f650b118b8159 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -145,7 +145,7 @@ final class DisplayPowerState { public void setScreenState(int state) { if (mScreenState != state) { if (DEBUG) { - Slog.d(TAG, "setScreenState: state=" + state); + Slog.w(TAG, "setScreenState: state=" + Display.stateToString(state)); } mScreenState = state; @@ -339,6 +339,15 @@ final class DisplayPowerState { if (mColorFade != null) mColorFade.dump(pw); } + /** + * Resets the screen state to unknown. Useful when the underlying display-device changes for the + * LogicalDisplay and we do not know the last state that was sent to it. + */ + void resetScreenState() { + mScreenState = Display.STATE_UNKNOWN; + mScreenReady = false; + } + private void scheduleScreenUpdate() { if (!mScreenUpdatePending) { mScreenUpdatePending = true; diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index d14902eaf8f58..e6f27c1b0dd95 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -18,7 +18,6 @@ package com.android.server.display; import static com.android.server.display.DisplayDeviceInfo.TOUCH_NONE; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Point; @@ -68,33 +67,6 @@ import java.util.Objects; final class LogicalDisplay { private static final String TAG = "LogicalDisplay"; - /** - * Phase indicating the logical display's existence is hidden from the rest of the framework. - * This can happen if the current layout has specifically requested to keep this display - * disabled. - */ - static final int DISPLAY_PHASE_DISABLED = -1; - - /** - * Phase indicating that the logical display is going through a layout transition. - * When in this phase, other systems can choose to special case power-state handling of a - * display that might be in a transition. - */ - static final int DISPLAY_PHASE_LAYOUT_TRANSITION = 0; - - /** - * The display is exposed to the rest of the system and its power state is determined by a - * power-request from PowerManager. - */ - static final int DISPLAY_PHASE_ENABLED = 1; - - @IntDef(prefix = {"DISPLAY_PHASE" }, value = { - DISPLAY_PHASE_DISABLED, - DISPLAY_PHASE_LAYOUT_TRANSITION, - DISPLAY_PHASE_ENABLED - }) - @interface DisplayPhase {} - // The layer stack we use when the display has been blanked to prevent any // of its content from appearing. private static final int BLANK_LAYER_STACK = -1; @@ -158,14 +130,6 @@ final class LogicalDisplay { private final Rect mTempLayerStackRect = new Rect(); private final Rect mTempDisplayRect = new Rect(); - /** - * Indicates the current phase of the display. Generally, phases supersede any - * requests from PowerManager in DPC's calculation for the display state. Only when the - * phase is ENABLED does PowerManager's request for the display take effect. - */ - @DisplayPhase - private int mPhase = DISPLAY_PHASE_ENABLED; - /** * The UID mappings for refresh rate override */ @@ -181,12 +145,22 @@ final class LogicalDisplay { */ private final SparseArray mTempFrameRateOverride; + // Indicates the display is enabled (allowed to be ON). + private boolean mIsEnabled; + + // Indicates the display is part of a transition from one device-state ({@link + // DeviceStateManager}) to another. Being a "part" of a transition means that either + // the {@link mIsEnabled} is changing, or the underlying mPrimiaryDisplayDevice is changing. + private boolean mIsInTransition; + public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) { mDisplayId = displayId; mLayerStack = layerStack; mPrimaryDisplayDevice = primaryDisplayDevice; mPendingFrameRateOverrideUids = new ArraySet<>(); mTempFrameRateOverride = new SparseArray<>(); + mIsEnabled = true; + mIsInTransition = false; } /** @@ -525,7 +499,7 @@ final class LogicalDisplay { // Prevent displays that are disabled from receiving input. // TODO(b/188914255): Remove once input can dispatch against device vs layerstack. device.setDisplayFlagsLocked(t, - (isEnabled() && device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE) + (isEnabledLocked() && device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE) ? SurfaceControl.DISPLAY_RECEIVES_INPUT : 0); @@ -767,32 +741,45 @@ final class LogicalDisplay { return old; } - public void setPhase(@DisplayPhase int phase) { - mPhase = phase; - } - - /** - * Returns the currently set phase for this LogicalDisplay. Phases are used when transitioning - * from one device state to another. {@see LogicalDisplayMapper}. - */ - @DisplayPhase - public int getPhase() { - return mPhase; - } - /** * @return {@code true} if the LogicalDisplay is enabled or {@code false} * if disabled indicating that the display should be hidden from the rest of the apps and * framework. */ - public boolean isEnabled() { - // DISPLAY_PHASE_LAYOUT_TRANSITION is still considered an 'enabled' phase. - return mPhase == DISPLAY_PHASE_ENABLED || mPhase == DISPLAY_PHASE_LAYOUT_TRANSITION; + public boolean isEnabledLocked() { + return mIsEnabled; + } + + /** + * Sets the display as enabled. + * + * @param enable True if enabled, false otherwise. + */ + public void setEnabledLocked(boolean enabled) { + mIsEnabled = enabled; + } + + /** + * @return {@code true} if the LogicalDisplay is in a transition phase. This is used to indicate + * that we are getting ready to swap the underlying display-device and the display should be + * rendered appropriately to reduce jank. + */ + public boolean isInTransitionLocked() { + return mIsInTransition; + } + + /** + * Sets the transition phase. + * @param isInTransition True if it display is in transition. + */ + public void setIsInTransitionLocked(boolean isInTransition) { + mIsInTransition = isInTransition; } public void dumpLocked(PrintWriter pw) { pw.println("mDisplayId=" + mDisplayId); - pw.println("mPhase=" + mPhase); + pw.println("mIsEnabled=" + mIsEnabled); + pw.println("mIsInTransition=" + mIsInTransition); pw.println("mLayerStack=" + mLayerStack); pw.println("mHasContent=" + mHasContent); pw.println("mDesiredDisplayModeSpecs={" + mDesiredDisplayModeSpecs + "}"); diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index 70c9e23c6af81..778e418204333 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -39,7 +39,6 @@ import android.view.DisplayAddress; import android.view.DisplayInfo; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.display.LogicalDisplay.DisplayPhase; import com.android.server.display.layout.Layout; import java.io.PrintWriter; @@ -167,6 +166,12 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo, @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot, @NonNull Handler handler) { + this(context, repo, listener, syncRoot, handler, new DeviceStateToLayoutMap()); + } + + LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo, + @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot, + @NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap) { mSyncRoot = syncRoot; mPowerManager = context.getSystemService(PowerManager.class); mInteractive = mPowerManager.isInteractive(); @@ -181,7 +186,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mDeviceStatesOnWhichToSleep = toSparseBooleanArray(context.getResources().getIntArray( com.android.internal.R.array.config_deviceStatesOnWhichToSleep)); mDisplayDeviceRepo.addListener(this); - mDeviceStateToLayoutMap = new DeviceStateToLayoutMap(); + mDeviceStateToLayoutMap = deviceStateToLayoutMap; } @Override @@ -218,10 +223,22 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } public LogicalDisplay getDisplayLocked(int displayId) { - return mLogicalDisplays.get(displayId); + return getDisplayLocked(displayId, /* includeDisabled= */ true); + } + + public LogicalDisplay getDisplayLocked(int displayId, boolean includeDisabled) { + LogicalDisplay display = mLogicalDisplays.get(displayId); + if (display == null || display.isEnabledLocked() || includeDisabled) { + return display; + } + return null; } public LogicalDisplay getDisplayLocked(DisplayDevice device) { + return getDisplayLocked(device, /* includeDisabled= */ true); + } + + public LogicalDisplay getDisplayLocked(DisplayDevice device, boolean includeDisabled) { if (device == null) { return null; } @@ -229,21 +246,26 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { for (int i = 0; i < count; i++) { final LogicalDisplay display = mLogicalDisplays.valueAt(i); if (display.getPrimaryDisplayDeviceLocked() == device) { - return display; + if (display.isEnabledLocked() || includeDisabled) { + return display; + } + return null; } } return null; } - public int[] getDisplayIdsLocked(int callingUid) { + public int[] getDisplayIdsLocked(int callingUid, boolean includeDisabled) { 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); - DisplayInfo info = display.getDisplayInfoLocked(); - if (info.hasAccess(callingUid)) { - displayIds[n++] = mLogicalDisplays.keyAt(i); + if (display.isEnabledLocked() || includeDisabled) { + DisplayInfo info = display.getDisplayInfoLocked(); + if (info.hasAccess(callingUid)) { + displayIds[n++] = mLogicalDisplays.keyAt(i); + } } } if (n != count) { @@ -364,14 +386,12 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { void setDeviceStateLocked(int state, boolean isOverrideActive) { Slog.i(TAG, "Requesting Transition to state: " + state + ", from state=" + mDeviceState - + ", interactive=" + mInteractive); + + ", interactive=" + mInteractive + ", mBootCompleted=" + mBootCompleted); // As part of a state transition, we may need to turn off some displays temporarily so that // the transition is smooth. Plus, on some devices, only one internal displays can be - // on at a time. We use DISPLAY_PHASE_LAYOUT_TRANSITION to mark a display that needs to be + // on at a time. We use LogicalDisplay.setIsInTransition to mark a display that needs to be // temporarily turned off. - if (mDeviceState != DeviceStateManager.INVALID_DEVICE_STATE) { - resetLayoutLocked(mDeviceState, state, LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION); - } + resetLayoutLocked(mDeviceState, state, /* isStateChangeStarting= */ true); mPendingDeviceState = state; final boolean wakeDevice = shouldDeviceBeWoken(mPendingDeviceState, mDeviceState, mInteractive, mBootCompleted); @@ -481,7 +501,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { final int count = mLogicalDisplays.size(); for (int i = 0; i < count; i++) { final LogicalDisplay display = mLogicalDisplays.valueAt(i); - if (display.getPhase() != LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) { + if (!display.isInTransitionLocked()) { continue; } @@ -497,7 +517,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { } private void transitionToPendingStateLocked() { - resetLayoutLocked(mDeviceState, mPendingDeviceState, LogicalDisplay.DISPLAY_PHASE_ENABLED); + resetLayoutLocked(mDeviceState, mPendingDeviceState, /* isStateChangeStarting= */ false); mDeviceState = mPendingDeviceState; mPendingDeviceState = DeviceStateManager.INVALID_DEVICE_STATE; applyLayoutLocked(); @@ -789,17 +809,17 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { /** * Goes through all the displays used in the layouts for the specified {@code fromState} and - * {@code toState} and applies the specified {@code phase}. When a new layout is requested, we - * put the displays that will change into a transitional phase so that they can all be turned - * OFF. Once all are confirmed OFF, then this method gets called again to reset the phase to - * normal operation. This helps to ensure that all display-OFF requests are made before + * {@code toState} and un/marks them for transition. When a new layout is requested, we + * mark the displays that will change into a transitional phase so that they can all be turned + * OFF. Once all are confirmed OFF, then this method gets called again to reset transition + * marker. This helps to ensure that all display-OFF requests are made before * display-ON which in turn hides any resizing-jank windows might incur when switching displays. * * @param fromState The state we are switching from. * @param toState The state we are switching to. - * @param phase The new phase to apply to the displays. + * @param isStateChangeStarting Indicates whether to start or end Transition phase. */ - private void resetLayoutLocked(int fromState, int toState, @DisplayPhase int phase) { + private void resetLayoutLocked(int fromState, int toState, boolean isStateChangeStarting) { final Layout fromLayout = mDeviceStateToLayoutMap.get(fromState); final Layout toLayout = mDeviceStateToLayoutMap.get(toState); @@ -817,12 +837,16 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { // new layout. final DisplayAddress address = device.getDisplayDeviceInfoLocked().address; - // Virtual displays do not have addresses. + // Virtual displays do not have addresses, so account for nulls. final Layout.Display fromDisplay = address != null ? fromLayout.getByAddress(address) : null; final Layout.Display toDisplay = address != null ? toLayout.getByAddress(address) : null; + // If the display is in one of the layouts but not the other, then the content will + // change, so in this case we also want to blank the displays to avoid jank. + final boolean displayNotInBothLayouts = (fromDisplay == null) != (toDisplay == null); + // If a layout doesn't mention a display-device at all, then the display-device defaults // to enabled. This is why we treat null as "enabled" in the code below. final boolean wasEnabled = fromDisplay == null || fromDisplay.isEnabled(); @@ -837,16 +861,23 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { // 3) It's enabled, but it's mapped to a new logical display ID. To the user this // would look like apps moving from one screen to another since task-stacks stay // with the logical display [ID]. + // 4) It's in one layout but not the other, so the content will change. final boolean isTransitioning = - (logicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) + logicalDisplay.isInTransitionLocked() || (wasEnabled != willBeEnabled) - || deviceHasNewLogicalDisplayId; + || deviceHasNewLogicalDisplayId + || displayNotInBothLayouts; if (isTransitioning) { - setDisplayPhase(logicalDisplay, phase); - if (phase == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) { - mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_TRANSITION); + if (isStateChangeStarting != logicalDisplay.isInTransitionLocked()) { + Slog.i(TAG, "Set isInTransition on display " + displayId + ": " + + isStateChangeStarting); } + // This will either mark the display as "transitioning" if we are starting to change + // the device state, or remove the transitioning marker if the state change is + // ending. + logicalDisplay.setIsInTransitionLocked(isStateChangeStarting); + mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_TRANSITION); } } } @@ -891,9 +922,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { newDisplay.swapDisplaysLocked(oldDisplay); } - if (!displayLayout.isEnabled()) { - setDisplayPhase(newDisplay, LogicalDisplay.DISPLAY_PHASE_DISABLED); - } + setEnabledLocked(newDisplay, displayLayout.isEnabled()); } } @@ -912,23 +941,25 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device); display.updateLocked(mDisplayDeviceRepo); mLogicalDisplays.put(displayId, display); - setDisplayPhase(display, LogicalDisplay.DISPLAY_PHASE_ENABLED); return display; } - private void setDisplayPhase(LogicalDisplay display, @DisplayPhase int phase) { + private void setEnabledLocked(LogicalDisplay display, boolean isEnabled) { final int displayId = display.getDisplayIdLocked(); final DisplayInfo info = display.getDisplayInfoLocked(); final boolean disallowSecondaryDisplay = mSingleDisplayDemoMode && (info.type != Display.TYPE_INTERNAL); - if (phase != LogicalDisplay.DISPLAY_PHASE_DISABLED && disallowSecondaryDisplay) { + if (isEnabled && disallowSecondaryDisplay) { Slog.i(TAG, "Not creating a logical display for a secondary display because single" + " display demo mode is enabled: " + display.getDisplayInfoLocked()); - phase = LogicalDisplay.DISPLAY_PHASE_DISABLED; + isEnabled = false; } - display.setPhase(phase); + if (display.isEnabledLocked() != isEnabled) { + Slog.i(TAG, "SetEnabled on display " + displayId + ": " + isEnabled); + display.setEnabledLocked(isEnabled); + } } private int assignDisplayGroupIdLocked(boolean isOwnDisplayGroup) { 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 1e97c1c5c5bcf..2edb909258f92 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 @@ public class DisplayManagerServiceTest { when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); - final int displayIds[] = bs.getDisplayIds(); + final int[] displayIds = bs.getDisplayIds(/* includeDisabled= */ true); final int size = displayIds.length; assertTrue(size > 0); @@ -1174,7 +1174,8 @@ public class DisplayManagerServiceTest { DisplayManagerService.BinderService displayManagerBinderService, FakeDisplayDevice displayDevice) { - final int[] displayIds = displayManagerBinderService.getDisplayIds(); + final int[] displayIds = displayManagerBinderService.getDisplayIds( + /* includeDisabled= */ true); 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 cc68ba88f76ef..d515fae4afe28 100644 --- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java +++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java @@ -30,6 +30,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; @@ -53,6 +55,8 @@ import android.view.DisplayInfo; 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; @@ -60,6 +64,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; import java.io.InputStream; import java.io.OutputStream; @@ -85,6 +90,7 @@ public class LogicalDisplayMapperTest { @Mock Resources mResourcesMock; @Mock IPowerManager mIPowerManagerMock; @Mock IThermalService mIThermalServiceMock; + @Spy DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy = new DeviceStateToLayoutMap(); @Captor ArgumentCaptor mDisplayCaptor; @@ -134,7 +140,8 @@ public class LogicalDisplayMapperTest { mLooper = new TestLooper(); mHandler = new Handler(mLooper.getLooper()); mLogicalDisplayMapper = new LogicalDisplayMapper(mContextMock, mDisplayDeviceRepo, - mListenerMock, new DisplayManagerService.SyncRoot(), mHandler); + mListenerMock, new DisplayManagerService.SyncRoot(), mHandler, + mDeviceStateToLayoutMapSpy); } @@ -261,7 +268,8 @@ public class LogicalDisplayMapperTest { add(createDisplayDevice(Display.TYPE_EXTERNAL, 600, 800, 0)); add(createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800, 0)); - int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID); + int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID, + /* includeDisabled= */ true); assertEquals(3, ids.length); Arrays.sort(ids); assertEquals(DEFAULT_DISPLAY, ids[0]); @@ -413,6 +421,178 @@ public class LogicalDisplayMapperTest { /* isBootCompleted= */true)); } + @Test + public void testDeviceStateLocked() { + DisplayDevice device1 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, + DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + DisplayDevice device2 = createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, + DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY); + + Layout layout = new Layout(); + layout.createDisplayLocked(device1.getDisplayDeviceInfoLocked().address, true, true); + layout.createDisplayLocked(device2.getDisplayDeviceInfoLocked().address, false, false); + when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(layout); + + layout = new Layout(); + layout.createDisplayLocked(device1.getDisplayDeviceInfoLocked().address, false, false); + layout.createDisplayLocked(device2.getDisplayDeviceInfoLocked().address, true, true); + when(mDeviceStateToLayoutMapSpy.get(1)).thenReturn(layout); + when(mDeviceStateToLayoutMapSpy.get(2)).thenReturn(layout); + + LogicalDisplay display1 = add(device1); + assertEquals(info(display1).address, info(device1).address); + assertEquals(DEFAULT_DISPLAY, id(display1)); + + LogicalDisplay display2 = add(device2); + assertEquals(info(display2).address, info(device2).address); + // We can only have one default display + assertEquals(DEFAULT_DISPLAY, id(display1)); + + mLogicalDisplayMapper.setDeviceStateLocked(0, false); + mLooper.moveTimeForward(1000); + mLooper.dispatchAll(); + assertTrue(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked()); + assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked()); + assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isInTransitionLocked()); + assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked()); + + mLogicalDisplayMapper.setDeviceStateLocked(1, false); + mLooper.moveTimeForward(1000); + mLooper.dispatchAll(); + assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked()); + assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked()); + assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isInTransitionLocked()); + assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked()); + + mLogicalDisplayMapper.setDeviceStateLocked(2, false); + mLooper.moveTimeForward(1000); + mLooper.dispatchAll(); + assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked()); + assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked()); + assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isInTransitionLocked()); + assertFalse(mLogicalDisplayMapper.getDisplayLocked(device2).isInTransitionLocked()); + } + + @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, false); + assertEquals(3, ids.length); + Arrays.sort(ids); + assertEquals(DEFAULT_DISPLAY, ids[0]); + assertNotNull(mLogicalDisplayMapper.getDisplayLocked(device1, + /* includeDisabled= */ false)); + assertNotNull(mLogicalDisplayMapper.getDisplayLocked(device2, + /* includeDisabled= */ false)); + assertNotNull(mLogicalDisplayMapper.getDisplayLocked(device3, + /* includeDisabled= */ false)); + assertNotNull(mLogicalDisplayMapper.getDisplayLocked( + threeDevicesEnabledLayout.getByAddress(displayAddressOne).getLogicalDisplayId(), + /* includeDisabled= */ false)); + assertNotNull(mLogicalDisplayMapper.getDisplayLocked( + threeDevicesEnabledLayout.getByAddress(displayAddressTwo).getLogicalDisplayId(), + /* includeDisabled= */ false)); + assertNotNull(mLogicalDisplayMapper.getDisplayLocked( + threeDevicesEnabledLayout.getByAddress(displayAddressThree).getLogicalDisplayId(), + /* includeDisabled= */ false)); + + Layout oneDeviceEnabledLayout = new Layout(); + oneDeviceEnabledLayout.createDisplayLocked( + displayAddressOne, + /* isDefault= */ true, + /* isEnabled= */ true); + oneDeviceEnabledLayout.createDisplayLocked( + displayAddressTwo, + /* isDefault= */ false, + /* isEnabled= */ false); + oneDeviceEnabledLayout.createDisplayLocked( + displayAddressThree, + /* isDefault= */ false, + /* isEnabled= */ false); + + when(mDeviceStateToLayoutMapSpy.get(0)).thenReturn(oneDeviceEnabledLayout); + when(mDeviceStateToLayoutMapSpy.get(1)).thenReturn(threeDevicesEnabledLayout); + + // 1) Set the new state + // 2) Mark the displays as STATE_OFF so that it can continue with transition + // 3) Send DISPLAY_DEVICE_EVENT_CHANGE to inform the mapper of the new display state + // 4) Dispatch handler events. + mLogicalDisplayMapper.setDeviceStateLocked(0, false); + mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED); + mLooper.moveTimeForward(1000); + mLooper.dispatchAll(); + final int[] allDisplayIds = mLogicalDisplayMapper.getDisplayIdsLocked( + Process.SYSTEM_UID, false); + if (allDisplayIds.length != 1) { + throw new RuntimeException("Displays: \n" + + mLogicalDisplayMapper.getDisplayLocked(device1).toString() + + "\n" + mLogicalDisplayMapper.getDisplayLocked(device2).toString() + + "\n" + mLogicalDisplayMapper.getDisplayLocked(device3).toString()); + } + // ensure only one display is returned + assertEquals(1, allDisplayIds.length); + assertNotNull(mLogicalDisplayMapper.getDisplayLocked(device1, + /* includeDisabled= */ false)); + assertNull(mLogicalDisplayMapper.getDisplayLocked(device2, + /* includeDisabled= */ false)); + assertNull(mLogicalDisplayMapper.getDisplayLocked(device3, + /* includeDisabled= */ false)); + assertNotNull(mLogicalDisplayMapper.getDisplayLocked( + oneDeviceEnabledLayout.getByAddress(displayAddressOne).getLogicalDisplayId(), + /* includeDisabled= */ false)); + assertNull(mLogicalDisplayMapper.getDisplayLocked( + oneDeviceEnabledLayout.getByAddress(displayAddressTwo).getLogicalDisplayId(), + /* includeDisabled= */ false)); + assertNull(mLogicalDisplayMapper.getDisplayLocked( + oneDeviceEnabledLayout.getByAddress(displayAddressThree).getLogicalDisplayId(), + /* includeDisabled= */ false)); + + // Now do it again to go back to state 1 + mLogicalDisplayMapper.setDeviceStateLocked(1, false); + mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED); + mLooper.moveTimeForward(1000); + mLooper.dispatchAll(); + final int[] threeDisplaysEnabled = mLogicalDisplayMapper.getDisplayIdsLocked( + Process.SYSTEM_UID, false); + + // ensure all three displays are returned + assertEquals(3, threeDisplaysEnabled.length); + } + ///////////////// // Helper Methods ///////////////// @@ -477,6 +657,7 @@ public class LogicalDisplayMapperTest { class TestDisplayDevice extends DisplayDevice { private DisplayDeviceInfo mInfo; private DisplayDeviceInfo mSentInfo; + private int mState; TestDisplayDevice() { super(null, null, "test_display_" + sUniqueTestDisplayId++, mContextMock); diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java index b0738fdb78d0c..50d2a51b4bbe7 100644 --- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java +++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java @@ -128,12 +128,12 @@ public class LogicalDisplayTest { verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT)); reset(t); - mLogicalDisplay.setPhase(LogicalDisplay.DISPLAY_PHASE_DISABLED); + mLogicalDisplay.setEnabledLocked(false); mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); verify(t).setDisplayFlags(any(), eq(0)); reset(t); - mLogicalDisplay.setPhase(LogicalDisplay.DISPLAY_PHASE_ENABLED); + mLogicalDisplay.setEnabledLocked(true); mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT));