• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.display;
18 
19 import static android.hardware.devicestate.DeviceState.PROPERTY_EMULATED_ONLY;
20 import static android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP;
21 import static android.hardware.devicestate.DeviceState.PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE;
22 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
23 import static android.view.Display.DEFAULT_DISPLAY;
24 
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.content.Context;
28 import android.hardware.devicestate.DeviceState;
29 import android.hardware.devicestate.DeviceStateManager;
30 import android.hardware.devicestate.feature.flags.FeatureFlags;
31 import android.hardware.devicestate.feature.flags.FeatureFlagsImpl;
32 import android.os.Handler;
33 import android.os.Looper;
34 import android.os.Message;
35 import android.os.PowerManager;
36 import android.os.SystemClock;
37 import android.os.SystemProperties;
38 import android.text.TextUtils;
39 import android.util.ArrayMap;
40 import android.util.IndentingPrintWriter;
41 import android.util.Slog;
42 import android.util.SparseArray;
43 import android.util.SparseBooleanArray;
44 import android.util.SparseIntArray;
45 import android.view.Display;
46 import android.view.DisplayAddress;
47 import android.view.DisplayInfo;
48 
49 import com.android.internal.annotations.VisibleForTesting;
50 import com.android.internal.foldables.FoldGracePeriodProvider;
51 import com.android.server.LocalServices;
52 import com.android.server.display.feature.DisplayManagerFlags;
53 import com.android.server.display.layout.DisplayIdProducer;
54 import com.android.server.display.layout.Layout;
55 import com.android.server.display.mode.SyntheticModeManager;
56 import com.android.server.display.utils.DebugUtils;
57 import com.android.server.policy.WindowManagerPolicy;
58 import com.android.server.utils.FoldSettingProvider;
59 
60 import java.io.PrintWriter;
61 import java.util.Arrays;
62 import java.util.function.Consumer;
63 
64 /**
65  * Responsible for creating {@link LogicalDisplay}s and associating them to the
66  * {@link DisplayDevice} objects supplied through {@link DisplayAdapter.Listener}.
67  *
68  * Additionally this class will keep track of which {@link DisplayGroup} each
69  * {@link LogicalDisplay} belongs to.
70  *
71  * For devices with a single internal display, the mapping is done once and left
72  * alone. For devices with multiple built-in displays, such as foldable devices,
73  * {@link LogicalDisplay}s can be remapped to different {@link DisplayDevice}s.
74  */
75 class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
76     private static final String TAG = "LogicalDisplayMapper";
77 
78     // To enable these logs, run:
79     // 'adb shell setprop persist.log.tag.LogicalDisplayMapper DEBUG && adb reboot'
80     private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
81 
82     public static final int LOGICAL_DISPLAY_EVENT_BASE = 0;
83     public static final int LOGICAL_DISPLAY_EVENT_ADDED = 1 << 0;
84     public static final int LOGICAL_DISPLAY_EVENT_BASIC_CHANGED = 1 << 1;
85     public static final int LOGICAL_DISPLAY_EVENT_REMOVED = 1 << 2;
86     public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 1 << 3;
87     public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 1 << 4;
88     public static final int LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION = 1 << 5;
89     public static final int LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED = 1 << 6;
90     public static final int LOGICAL_DISPLAY_EVENT_CONNECTED = 1 << 7;
91     public static final int LOGICAL_DISPLAY_EVENT_DISCONNECTED = 1 << 8;
92     public static final int LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED = 1 << 9;
93     public static final int LOGICAL_DISPLAY_EVENT_STATE_CHANGED = 1 << 10;
94     public static final int LOGICAL_DISPLAY_EVENT_COMMITTED_STATE_CHANGED = 1 << 11;
95 
96 
97     public static final int DISPLAY_GROUP_EVENT_ADDED = 1;
98     public static final int DISPLAY_GROUP_EVENT_CHANGED = 2;
99     public static final int DISPLAY_GROUP_EVENT_REMOVED = 3;
100 
101     private static final int TIMEOUT_STATE_TRANSITION_MILLIS = 500;
102 
103     private static final int MSG_TRANSITION_TO_PENDING_DEVICE_STATE = 1;
104 
105     private static final int UPDATE_STATE_NEW = 0;
106     private static final int UPDATE_STATE_TRANSITION = 1;
107     private static final int UPDATE_STATE_UPDATED = 2;
108 
109     private static int sNextNonDefaultDisplayId = DEFAULT_DISPLAY + 1;
110 
111     /**
112      * Temporary display info, used for comparing display configurations.
113      */
114     private final DisplayInfo mTempDisplayInfo = new DisplayInfo();
115     private final DisplayInfo mTempNonOverrideDisplayInfo = new DisplayInfo();
116 
117     /**
118      * True if the display mapper service should pretend there is only one display
119      * and only tell applications about the existence of the default logical display.
120      * The display manager can still mirror content to secondary displays but applications
121      * cannot present unique content on those displays.
122      * Used for demonstration purposes only.
123      */
124     private final boolean mSingleDisplayDemoMode;
125 
126     /**
127      * True if the device can have more than one internal display on at a time.
128      */
129     private final boolean mSupportsConcurrentInternalDisplays;
130 
131     /**
132      * Wake the device when transitioning into these device state.
133      */
134     private final SparseBooleanArray mDeviceStatesOnWhichToWakeUp;
135 
136     /**
137      * Sleep the device when transitioning into these device state.
138      */
139     private final SparseBooleanArray mDeviceStatesOnWhichToSelectiveSleep;
140 
141     /**
142      * Map of all logical displays indexed by logical display id.
143      * Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache.
144      * TODO: multi-display - Move the aforementioned comment?
145      */
146     private final SparseArray<LogicalDisplay> mLogicalDisplays =
147             new SparseArray<LogicalDisplay>();
148 
149     // Cache whether or not the display was enabled on the last update.
150     private final SparseBooleanArray mDisplaysEnabledCache = new SparseBooleanArray();
151 
152     /** Map of all display groups indexed by display group id. */
153     private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>();
154 
155     /**
156      * Map of display groups which are linked to virtual devices (all displays in the group are
157      * linked to that device). Keyed by virtual device unique id.
158      */
159     private final SparseIntArray mDeviceDisplayGroupIds = new SparseIntArray();
160 
161     /**
162      * Map of display group ids indexed by display group name.
163      */
164     private final ArrayMap<String, Integer> mDisplayGroupIdsByName = new ArrayMap<>();
165 
166     private final DisplayDeviceRepository mDisplayDeviceRepo;
167     private final DeviceStateToLayoutMap mDeviceStateToLayoutMap;
168     private final Listener mListener;
169     private final DisplayManagerService.SyncRoot mSyncRoot;
170     private final LogicalDisplayMapperHandler mHandler;
171     private final FoldSettingProvider mFoldSettingProvider;
172     private final FoldGracePeriodProvider mFoldGracePeriodProvider;
173     private final PowerManager mPowerManager;
174 
175     /**
176      * Has an entry for every logical display that the rest of the system has been notified about.
177      * The values are any of the {@code UPDATE_STATE_*} constant types.
178      */
179     private final SparseIntArray mUpdatedLogicalDisplays = new SparseIntArray();
180 
181     /**
182      * Keeps track of all the display groups that we already told other people about. IOW, if a
183      * display group is in this array, then we *must* send change and remove notifications for it
184      * because other components know about them. Also, what this array stores is a change counter
185      * for each group, so we know if the group itself has changes since we last sent out a
186      * notification.  See {@link DisplayGroup#getChangeCountLocked}.
187      */
188     private final SparseIntArray mUpdatedDisplayGroups = new SparseIntArray();
189 
190     /**
191      * Array used in {@link #updateLogicalDisplaysLocked} to track events that need to be sent out.
192      */
193     private final SparseIntArray mLogicalDisplaysToUpdate = new SparseIntArray();
194 
195     /**
196      * Array used in {@link #updateLogicalDisplaysLocked} to track events that need to be sent out.
197      */
198     private final SparseIntArray mDisplayGroupsToUpdate = new SparseIntArray();
199 
200     /**
201      * ArrayMap of display device unique ID to virtual device ID. Used in {@link
202      * #updateLogicalDisplaysLocked} to establish which Virtual Devices own which Virtual Displays.
203      */
204     private final ArrayMap<String, Integer> mVirtualDeviceDisplayMapping = new ArrayMap<>();
205     private WindowManagerPolicy mWindowManagerPolicy;
206 
207     private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
208     private final DisplayIdProducer mIdProducer = (isDefault) ->
209             isDefault ? DEFAULT_DISPLAY : sNextNonDefaultDisplayId++;
210     private Layout mCurrentLayout = null;
211     private DeviceState mDeviceState = INVALID_DEVICE_STATE;
212     private DeviceState mPendingDeviceState = INVALID_DEVICE_STATE;
213     private DeviceState mDeviceStateToBeAppliedAfterBoot = INVALID_DEVICE_STATE;
214     private boolean mBootCompleted = false;
215     private boolean mInteractive;
216     private final DisplayManagerFlags mFlags;
217     private final SyntheticModeManager mSyntheticModeManager;
218     private final FeatureFlags mDeviceStateManagerFlags;
219 
LogicalDisplayMapper(@onNull Context context, FoldSettingProvider foldSettingProvider, FoldGracePeriodProvider foldGracePeriodProvider, @NonNull DisplayDeviceRepository repo, @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot, @NonNull Handler handler, DisplayManagerFlags flags)220     LogicalDisplayMapper(@NonNull Context context, FoldSettingProvider foldSettingProvider,
221             FoldGracePeriodProvider foldGracePeriodProvider,
222             @NonNull DisplayDeviceRepository repo,
223             @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
224             @NonNull Handler handler, DisplayManagerFlags flags) {
225         this(context, foldSettingProvider, foldGracePeriodProvider, repo, listener, syncRoot,
226                 handler,
227                 new DeviceStateToLayoutMap((isDefault) -> isDefault ? DEFAULT_DISPLAY
228                         : sNextNonDefaultDisplayId++, flags), flags,
229                 new SyntheticModeManager(flags));
230     }
231 
LogicalDisplayMapper(@onNull Context context, FoldSettingProvider foldSettingProvider, FoldGracePeriodProvider foldGracePeriodProvider, @NonNull DisplayDeviceRepository repo, @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot, @NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap, DisplayManagerFlags flags, SyntheticModeManager syntheticModeManager)232     LogicalDisplayMapper(@NonNull Context context, FoldSettingProvider foldSettingProvider,
233             FoldGracePeriodProvider foldGracePeriodProvider,
234             @NonNull DisplayDeviceRepository repo,
235             @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
236             @NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap,
237             DisplayManagerFlags flags, SyntheticModeManager syntheticModeManager) {
238         mSyncRoot = syncRoot;
239         mPowerManager = context.getSystemService(PowerManager.class);
240         mInteractive = mPowerManager.isInteractive();
241         mHandler = new LogicalDisplayMapperHandler(handler.getLooper());
242         mDisplayDeviceRepo = repo;
243         mListener = listener;
244         mFoldSettingProvider = foldSettingProvider;
245         mFoldGracePeriodProvider = foldGracePeriodProvider;
246         mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
247         mSupportsConcurrentInternalDisplays = context.getResources().getBoolean(
248                 com.android.internal.R.bool.config_supportsConcurrentInternalDisplays);
249         mDeviceStatesOnWhichToWakeUp = toSparseBooleanArray(context.getResources().getIntArray(
250                 com.android.internal.R.array.config_deviceStatesOnWhichToWakeUp));
251         mDeviceStatesOnWhichToSelectiveSleep = toSparseBooleanArray(
252                 context.getResources().getIntArray(
253                         com.android.internal.R.array.config_deviceStatesOnWhichToSleep));
254         mDisplayDeviceRepo.addListener(this);
255         mDeviceStateToLayoutMap = deviceStateToLayoutMap;
256         mFlags = flags;
257         mSyntheticModeManager = syntheticModeManager;
258         mDeviceStateManagerFlags = new FeatureFlagsImpl();
259     }
260 
261     @Override
onDisplayDeviceEventLocked(DisplayDevice device, int event)262     public void onDisplayDeviceEventLocked(DisplayDevice device, int event) {
263         switch (event) {
264             case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_ADDED:
265                 if (DEBUG) {
266                     Slog.d(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked());
267                 }
268                 handleDisplayDeviceAddedLocked(device);
269                 break;
270 
271             case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_REMOVED:
272                 if (DEBUG) {
273                     Slog.d(TAG, "Display device removed: " + device.getDisplayDeviceInfoLocked());
274                 }
275                 handleDisplayDeviceRemovedLocked(device);
276                 updateLogicalDisplaysLocked();
277                 break;
278         }
279     }
280 
281     @Override
onDisplayDeviceChangedLocked(DisplayDevice device, int diff)282     public void onDisplayDeviceChangedLocked(DisplayDevice device, int diff) {
283         if (DEBUG) {
284             Slog.d(TAG, "Display device changed: " + device.getDisplayDeviceInfoLocked());
285         }
286         finishStateTransitionLocked(false /*force*/);
287         updateLogicalDisplaysLocked(diff);
288     }
289 
290     @Override
onTraversalRequested()291     public void onTraversalRequested() {
292         mListener.onTraversalRequested();
293     }
294 
onWindowManagerReady()295     public void onWindowManagerReady() {
296         mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
297     }
298 
getDisplayLocked(int displayId)299     public LogicalDisplay getDisplayLocked(int displayId) {
300         return getDisplayLocked(displayId, /* includeDisabled= */ true);
301     }
302 
getDisplayLocked(int displayId, boolean includeDisabled)303     public LogicalDisplay getDisplayLocked(int displayId, boolean includeDisabled) {
304         LogicalDisplay display = mLogicalDisplays.get(displayId);
305         if (display == null || display.isEnabledLocked() || includeDisabled) {
306             return display;
307         }
308         return null;
309     }
310 
getDisplayLocked(DisplayDevice device)311     public LogicalDisplay getDisplayLocked(DisplayDevice device) {
312         return getDisplayLocked(device, /* includeDisabled= */ true);
313     }
314 
getDisplayLocked(DisplayDevice device, boolean includeDisabled)315     public LogicalDisplay getDisplayLocked(DisplayDevice device, boolean includeDisabled) {
316         if (device == null) {
317             return null;
318         }
319         final int count = mLogicalDisplays.size();
320         for (int i = 0; i < count; i++) {
321             final LogicalDisplay display = mLogicalDisplays.valueAt(i);
322             if (display.getPrimaryDisplayDeviceLocked() == device) {
323                 if (display.isEnabledLocked() || includeDisabled) {
324                     return display;
325                 }
326                 return null;
327             }
328         }
329         return null;
330     }
331 
getDisplayIdsLocked(int callingUid, boolean includeDisabled)332     public int[] getDisplayIdsLocked(int callingUid, boolean includeDisabled) {
333         final int count = mLogicalDisplays.size();
334         int[] displayIds = new int[count];
335         int n = 0;
336         for (int i = 0; i < count; i++) {
337             LogicalDisplay display = mLogicalDisplays.valueAt(i);
338             if (display.isEnabledLocked() || includeDisabled) {
339                 DisplayInfo info = display.getDisplayInfoLocked();
340                 if (info.hasAccess(callingUid)) {
341                     displayIds[n++] = mLogicalDisplays.keyAt(i);
342                 }
343             }
344         }
345         if (n != count) {
346             displayIds = Arrays.copyOfRange(displayIds, 0, n);
347         }
348         return displayIds;
349     }
350 
getDisplayIdsForGroupLocked(int groupId)351     public int[] getDisplayIdsForGroupLocked(int groupId) {
352         DisplayGroup displayGroup = mDisplayGroups.get(groupId);
353         if (displayGroup == null) {
354             return new int[]{};
355         }
356         return displayGroup.getIdsLocked();
357     }
358 
getDisplayIdsByGroupIdLocked()359     public SparseArray<int[]> getDisplayIdsByGroupIdLocked() {
360         SparseArray<int[]> displayIdsByGroupIds = new SparseArray<>();
361         for (int i = 0; i < mDisplayGroups.size(); i++) {
362             final int displayGroupId = mDisplayGroups.keyAt(i);
363             displayIdsByGroupIds.put(displayGroupId, getDisplayIdsForGroupLocked(displayGroupId));
364         }
365         return displayIdsByGroupIds;
366     }
367 
forEachLocked(Consumer<LogicalDisplay> consumer)368     public void forEachLocked(Consumer<LogicalDisplay> consumer) {
369         forEachLocked(consumer, /* includeDisabled= */ true);
370     }
371 
forEachLocked(Consumer<LogicalDisplay> consumer, boolean includeDisabled)372     public void forEachLocked(Consumer<LogicalDisplay> consumer, boolean includeDisabled) {
373         final int count = mLogicalDisplays.size();
374         for (int i = 0; i < count; i++) {
375             LogicalDisplay display = mLogicalDisplays.valueAt(i);
376             if (display.isEnabledLocked() || includeDisabled) {
377                 consumer.accept(display);
378             }
379         }
380     }
381 
382     @VisibleForTesting
getDisplayGroupIdFromDisplayIdLocked(int displayId)383     public int getDisplayGroupIdFromDisplayIdLocked(int displayId) {
384         final LogicalDisplay display = getDisplayLocked(displayId);
385         if (display == null) {
386             return Display.INVALID_DISPLAY_GROUP;
387         }
388 
389         final int size = mDisplayGroups.size();
390         for (int i = 0; i < size; i++) {
391             final DisplayGroup displayGroup = mDisplayGroups.valueAt(i);
392             if (displayGroup.containsLocked(display)) {
393                 return mDisplayGroups.keyAt(i);
394             }
395         }
396 
397         return Display.INVALID_DISPLAY_GROUP;
398     }
399 
getDisplayGroupLocked(int groupId)400     public DisplayGroup getDisplayGroupLocked(int groupId) {
401         return mDisplayGroups.get(groupId);
402     }
403 
404     /**
405      * Returns the {@link DisplayInfo} for this device state, indicated by the given display id. The
406      * DisplayInfo represents the attributes of the indicated display in the layout associated with
407      * this state. This is used to get display information for various displays in various states;
408      * e.g. to help apps preload resources for the possible display states.
409      *
410      * @param deviceState the state to query possible layouts for
411      * @param displayId   the display id to retrieve
412      * @return {@code null} if no corresponding {@link DisplayInfo} could be found, or the
413      * {@link DisplayInfo} with a matching display id.
414      */
415     @Nullable
getDisplayInfoForStateLocked(int deviceState, int displayId)416     public DisplayInfo getDisplayInfoForStateLocked(int deviceState, int displayId) {
417         // Retrieve the layout for this particular state.
418         final Layout layout = mDeviceStateToLayoutMap.get(deviceState);
419         if (layout == null) {
420             // TODO(b/352019542): remove the log once b/345960547 is fixed.
421             Slog.d(TAG, "Cannot get layout for given state:" + deviceState);
422             return null;
423         }
424         // Retrieve the details of the given display within this layout.
425         Layout.Display display = layout.getById(displayId);
426         if (display == null) {
427             // TODO(b/352019542): remove the log once b/345960547 is fixed.
428             Slog.d(TAG, "Cannot get display for given layout:" + layout);
429             return null;
430         }
431         // Retrieve the display info for the display that matches the display id.
432         final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(display.getAddress());
433         if (device == null) {
434             Slog.w(TAG, "The display device (" + display.getAddress()
435                     + "), is not available for the display state " + mDeviceState.getIdentifier());
436             return null;
437         }
438         LogicalDisplay logicalDisplay = getDisplayLocked(device, /* includeDisabled= */ true);
439         if (logicalDisplay == null) {
440             Slog.w(TAG, "The logical display associated with address (" + display.getAddress()
441                     + "), is not available for the display state " + mDeviceState);
442             return null;
443         }
444         DisplayInfo displayInfo = new DisplayInfo(logicalDisplay.getDisplayInfoLocked());
445         displayInfo.displayId = displayId;
446         return displayInfo;
447     }
448 
dumpLocked(PrintWriter pw)449     public void dumpLocked(PrintWriter pw) {
450         pw.println("LogicalDisplayMapper:");
451         pw.println("---------------------");
452         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
453         ipw.increaseIndent();
454 
455         ipw.println("mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
456         ipw.println("mCurrentLayout=" + mCurrentLayout);
457         ipw.println("mDeviceStatesOnWhichToWakeUp=" + mDeviceStatesOnWhichToWakeUp);
458         ipw.println("mDeviceStatesOnWhichSelectiveSleep=" + mDeviceStatesOnWhichToSelectiveSleep);
459         ipw.println("mInteractive=" + mInteractive);
460         ipw.println("mBootCompleted=" + mBootCompleted);
461 
462         ipw.println();
463 
464         ipw.println("mDeviceState=" + mDeviceState.getIdentifier());
465         ipw.println("mPendingDeviceState=" + mPendingDeviceState.getIdentifier());
466         ipw.println("mDeviceStateToBeAppliedAfterBoot="
467                 + mDeviceStateToBeAppliedAfterBoot.getIdentifier());
468 
469         final int logicalDisplayCount = mLogicalDisplays.size();
470         ipw.println();
471         ipw.println("Logical Displays: size=" + logicalDisplayCount);
472         for (int i = 0; i < logicalDisplayCount; i++) {
473             int displayId = mLogicalDisplays.keyAt(i);
474             LogicalDisplay display = mLogicalDisplays.valueAt(i);
475             ipw.println("Display " + displayId + ":");
476             ipw.increaseIndent();
477             display.dumpLocked(ipw);
478             ipw.decreaseIndent();
479             ipw.println();
480         }
481 
482         final int displayGroupCount = mDisplayGroups.size();
483         ipw.println();
484         ipw.println("Display Groups: size=" + displayGroupCount);
485         for (int i = 0; i < displayGroupCount; i++) {
486             int groupId = mDisplayGroups.keyAt(i);
487             DisplayGroup displayGroup = mDisplayGroups.valueAt(i);
488             ipw.println("Group " + groupId + ":");
489             ipw.increaseIndent();
490             displayGroup.dumpLocked(ipw);
491             ipw.decreaseIndent();
492             ipw.println();
493         }
494 
495 
496         mDeviceStateToLayoutMap.dumpLocked(ipw);
497     }
498 
499     /**
500      * Creates an association between a displayDevice and a virtual device. Any displays associated
501      * with this virtual device will be grouped together in a single {@link DisplayGroup} unless
502      * created with {@link Display.FLAG_OWN_DISPLAY_GROUP}.
503      *
504      * @param displayDevice the displayDevice to be linked
505      * @param virtualDeviceUniqueId the unique ID of the virtual device.
506      */
associateDisplayDeviceWithVirtualDevice( DisplayDevice displayDevice, int virtualDeviceUniqueId)507     void associateDisplayDeviceWithVirtualDevice(
508             DisplayDevice displayDevice, int virtualDeviceUniqueId) {
509         mVirtualDeviceDisplayMapping.put(displayDevice.getUniqueId(), virtualDeviceUniqueId);
510     }
511 
setDeviceStateLocked(DeviceState state)512     void setDeviceStateLocked(DeviceState state) {
513         if (!mBootCompleted) {
514             // The boot animation might still be in progress, we do not want to switch states now
515             // as the boot animation would end up with an incorrect size.
516             if (DEBUG) {
517                 Slog.d(TAG, "Postponing transition to state: "
518                         + mPendingDeviceState.getIdentifier() + " until boot is completed");
519             }
520             mDeviceStateToBeAppliedAfterBoot = state;
521             return;
522         }
523 
524         // As part of a state transition, we may need to turn off some displays temporarily so that
525         // the transition is smooth. Plus, on some devices, only one internal displays can be
526         // on at a time. We use LogicalDisplay.setIsInTransition to mark a display that needs to be
527         // temporarily turned off.
528         resetLayoutLocked(mDeviceState.getIdentifier(),
529                 state.getIdentifier(), /* transitionValue= */ true);
530         mPendingDeviceState = state;
531         mDeviceStateToBeAppliedAfterBoot = INVALID_DEVICE_STATE;
532         final boolean wakeDevice = shouldDeviceBeWoken(mPendingDeviceState, mDeviceState,
533                 mInteractive, mBootCompleted);
534         final boolean sleepDevice = shouldDeviceBePutToSleep(mPendingDeviceState, mDeviceState,
535                 mInteractive, mBootCompleted);
536 
537         Slog.i(TAG, "Requesting Transition to state: " + state.getIdentifier() + ", from state="
538                 + mDeviceState.getIdentifier() + ", interactive=" + mInteractive
539                 + ", mBootCompleted=" + mBootCompleted + ", wakeDevice=" + wakeDevice
540                 + ", sleepDevice=" + sleepDevice);
541 
542         // If all displays are off already, we can just transition here, unless we are trying to
543         // wake or sleep the device as part of this transition. In that case defer the final
544         // transition until later once the device is awake/asleep.
545         if (areAllTransitioningDisplaysOffLocked() && !wakeDevice && !sleepDevice) {
546             transitionToPendingStateLocked();
547             return;
548         }
549 
550         if (DEBUG) {
551             Slog.d(TAG, "Postponing transition to state: " + mPendingDeviceState.getIdentifier());
552         }
553         // Send the transitioning phase updates to DisplayManager so that the displays can
554         // start turning OFF in preparation for the new layout.
555         updateLogicalDisplaysLocked();
556 
557         if (wakeDevice || sleepDevice) {
558             if (wakeDevice) {
559                 // We already told the displays to turn off, now we need to wake the device as
560                 // we transition to this new state. We do it here so that the waking happens
561                 // between the transition from one layout to another.
562                 mHandler.post(() -> {
563                     mPowerManager.wakeUp(SystemClock.uptimeMillis(),
564                             PowerManager.WAKE_REASON_UNFOLD_DEVICE, "server.display:unfold");
565                 });
566             } else if (sleepDevice) {
567                 // Send the device to sleep when required.
568                 int goToSleepFlag =
569                         mFoldSettingProvider.shouldSleepOnFold() ? 0
570                                 : PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP;
571                 mHandler.post(() -> {
572                     mPowerManager.goToSleep(SystemClock.uptimeMillis(),
573                             PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD,
574                             goToSleepFlag);
575                 });
576             }
577         }
578 
579         mHandler.sendEmptyMessageDelayed(MSG_TRANSITION_TO_PENDING_DEVICE_STATE,
580                 TIMEOUT_STATE_TRANSITION_MILLIS);
581     }
582 
onBootCompleted()583     void onBootCompleted() {
584         synchronized (mSyncRoot) {
585             mBootCompleted = true;
586             if (!mDeviceStateToBeAppliedAfterBoot.equals(INVALID_DEVICE_STATE)) {
587                 setDeviceStateLocked(mDeviceStateToBeAppliedAfterBoot);
588             }
589         }
590     }
591 
onEarlyInteractivityChange(boolean interactive)592     void onEarlyInteractivityChange(boolean interactive) {
593         synchronized (mSyncRoot) {
594             if (mInteractive != interactive) {
595                 mInteractive = interactive;
596                 finishStateTransitionLocked(false /*force*/);
597             }
598         }
599     }
600 
601     /**
602      * Returns if the device should be woken up or not. Called to check if the device state we are
603      * moving to is one that should awake the device, as well as if we are moving from a device
604      * state that shouldn't have been already woken from.
605      *
606      * @param pendingState device state we are moving to
607      * @param currentState device state we are currently in
608      * @param isInteractive if the device is in an interactive state
609      * @param isBootCompleted is the device fully booted
610      *
611      * @see #shouldDeviceBePutToSleep
612      * @see #setDeviceStateLocked
613      */
614     @VisibleForTesting
shouldDeviceBeWoken(DeviceState pendingState, DeviceState currentState, boolean isInteractive, boolean isBootCompleted)615     boolean shouldDeviceBeWoken(DeviceState pendingState, DeviceState currentState,
616             boolean isInteractive, boolean isBootCompleted) {
617         if (mDeviceStateManagerFlags.deviceStatePropertyMigration()) {
618             if (currentState.hasProperties(PROPERTY_EMULATED_ONLY)
619                     && !pendingState.hasProperties(PROPERTY_EMULATED_ONLY)) {
620                 // Do not wake the device, since this transition may occur due to the user pressing
621                 // the power button to exit an emulated state.
622                 return false;
623             }
624 
625             return pendingState.hasProperty(PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE)
626                     && !currentState.equals(INVALID_DEVICE_STATE)
627                     && !currentState.hasProperty(PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE)
628                     && !isInteractive && isBootCompleted;
629         } else {
630             return mDeviceStatesOnWhichToWakeUp.get(pendingState.getIdentifier())
631                     && !mDeviceStatesOnWhichToWakeUp.get(currentState.getIdentifier())
632                     && !isInteractive && isBootCompleted;
633         }
634     }
635 
636     /**
637      * Returns if the device should be put to sleep or not.
638      *
639      * Includes a check to verify that the device state that we are moving to, {@code pendingState},
640      * is the same as the physical state of the device, {@code baseState}. Also if the
641      * 'Stay Awake On Fold' is not enabled. Different values for these parameters indicate a device
642      * state override is active, and we shouldn't put the device to sleep to provide a better user
643      * experience.
644      *
645      * @param pendingState device state we are moving to
646      * @param currentState device state we are currently in
647      * @param isInteractive if the device is in an interactive state
648      * @param isBootCompleted is the device fully booted
649      *
650      * @see #shouldDeviceBeWoken
651      * @see #setDeviceStateLocked
652      */
653     @VisibleForTesting
shouldDeviceBePutToSleep(DeviceState pendingState, DeviceState currentState, boolean isInteractive, boolean isBootCompleted)654     boolean shouldDeviceBePutToSleep(DeviceState pendingState, DeviceState currentState,
655             boolean isInteractive, boolean isBootCompleted) {
656         if (mDeviceStateManagerFlags.deviceStatePropertyMigration()) {
657             return pendingState.hasProperty(PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP)
658                     && !currentState.equals(INVALID_DEVICE_STATE)
659                     && !currentState.hasProperty(PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP)
660                     && isInteractive
661                     && isBootCompleted
662                     && !mFoldSettingProvider.shouldStayAwakeOnFold();
663         } else {
664             return currentState.getIdentifier()
665                     != DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER
666                     && pendingState.getIdentifier()
667                     != DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER
668                     && mDeviceStatesOnWhichToSelectiveSleep.get(pendingState.getIdentifier())
669                     && !mDeviceStatesOnWhichToSelectiveSleep.get(currentState.getIdentifier())
670                     && isInteractive
671                     && isBootCompleted
672                     && !mFoldSettingProvider.shouldStayAwakeOnFold();
673         }
674     }
675 
areAllTransitioningDisplaysOffLocked()676     private boolean areAllTransitioningDisplaysOffLocked() {
677         final int count = mLogicalDisplays.size();
678         for (int i = 0; i < count; i++) {
679             final LogicalDisplay display = mLogicalDisplays.valueAt(i);
680             if (!display.isInTransitionLocked()) {
681                 continue;
682             }
683 
684             final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
685             if (device != null) {
686                 final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
687                 if (info.state != Display.STATE_OFF) {
688                     return false;
689                 }
690             }
691         }
692         return true;
693     }
694 
transitionToPendingStateLocked()695     private void transitionToPendingStateLocked() {
696         resetLayoutLocked(mDeviceState.getIdentifier(),
697                 mPendingDeviceState.getIdentifier(), /* transitionValue= */ false);
698         mDeviceState = mPendingDeviceState;
699         mPendingDeviceState = INVALID_DEVICE_STATE;
700         applyLayoutLocked();
701         updateLogicalDisplaysLocked();
702     }
703 
finishStateTransitionLocked(boolean force)704     private void finishStateTransitionLocked(boolean force) {
705         if (mPendingDeviceState.equals(INVALID_DEVICE_STATE)) {
706             return;
707         }
708 
709         final boolean waitingToWakeDevice = shouldDeviceBeWoken(mPendingDeviceState, mDeviceState,
710                 mInteractive, mBootCompleted);
711         // The device should only wait for sleep if #shouldStayAwakeOnFold method returns false.
712         // If not, device should be marked ready for transition immediately.
713         final boolean waitingToSleepDevice = shouldDeviceBePutToSleep(mPendingDeviceState,
714                 mDeviceState, mInteractive, mBootCompleted) && !shouldStayAwakeOnFold();
715 
716         final boolean displaysOff = areAllTransitioningDisplaysOffLocked();
717         final boolean isReadyToTransition = displaysOff && !waitingToWakeDevice
718                 && !waitingToSleepDevice;
719 
720         if (isReadyToTransition || force) {
721             transitionToPendingStateLocked();
722             mHandler.removeMessages(MSG_TRANSITION_TO_PENDING_DEVICE_STATE);
723         } else if (DEBUG) {
724             Slog.d(TAG, "Not yet ready to transition to state=" + mPendingDeviceState
725                     + " with displays-off=" + displaysOff + ", force=" + force
726                     + ", mInteractive=" + mInteractive + ", isReady=" + isReadyToTransition);
727         }
728     }
729 
handleDisplayDeviceAddedLocked(DisplayDevice device)730     private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
731         DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
732         // The default Display needs to have additional initialization.
733         // This initializes a default dynamic display layout for the default
734         // device, which is used as a fallback in case no static layout definitions
735         // exist or cannot be loaded.
736         if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY) != 0) {
737             initializeDefaultDisplayDeviceLocked(device);
738         }
739 
740         // Create a logical display for the new display device
741         LogicalDisplay display = createNewLogicalDisplayLocked(
742                 device, mIdProducer.getId(/* isDefault= */ false));
743 
744         applyLayoutLocked();
745         updateLogicalDisplaysLocked();
746     }
747 
handleDisplayDeviceRemovedLocked(DisplayDevice device)748     private void handleDisplayDeviceRemovedLocked(DisplayDevice device) {
749         final Layout layout = mDeviceStateToLayoutMap.get(DeviceStateToLayoutMap.STATE_DEFAULT);
750         Layout.Display layoutDisplay = layout.getById(DEFAULT_DISPLAY);
751         if (layoutDisplay == null) {
752             return;
753         }
754         DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
755 
756         // Remove any virtual device mapping which exists for the display.
757         mVirtualDeviceDisplayMapping.remove(device.getUniqueId());
758 
759         if (layoutDisplay.getAddress().equals(deviceInfo.address)) {
760             layout.removeDisplayLocked(DEFAULT_DISPLAY);
761 
762             // Need to find another local display and make it default
763             for (int i = 0; i < mLogicalDisplays.size(); i++) {
764                 LogicalDisplay nextDisplay = mLogicalDisplays.valueAt(i);
765                 DisplayDevice nextDevice = nextDisplay.getPrimaryDisplayDeviceLocked();
766                 if (nextDevice == null) {
767                     continue;
768                 }
769                 DisplayDeviceInfo nextDeviceInfo = nextDevice.getDisplayDeviceInfoLocked();
770 
771                 if ((nextDeviceInfo.flags
772                         & DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY) != 0
773                         && !nextDeviceInfo.address.equals(deviceInfo.address)) {
774                     layout.createDefaultDisplayLocked(nextDeviceInfo.address, mIdProducer);
775                     applyLayoutLocked();
776                     return;
777                 }
778             }
779         }
780     }
781 
782     @VisibleForTesting
updateLogicalDisplays()783     void updateLogicalDisplays() {
784         synchronized (mSyncRoot) {
785             updateLogicalDisplaysLocked();
786         }
787     }
788 
updateLogicalDisplaysLocked()789     void updateLogicalDisplaysLocked() {
790         updateLogicalDisplaysLocked(DisplayDeviceInfo.DIFF_EVERYTHING);
791     }
792 
updateLogicalDisplaysLocked(int diff)793     private void updateLogicalDisplaysLocked(int diff) {
794         updateLogicalDisplaysLocked(diff, /* isSecondLoop= */ false);
795     }
796 
797     /**
798      * Updates the rest of the display system once all the changes are applied for display
799      * devices and logical displays. The includes releasing invalid/empty LogicalDisplays,
800      * creating/adjusting/removing DisplayGroups, and notifying the rest of the system of the
801      * relevant changes.
802      *
803      * @param diff The DisplayDeviceInfo.DIFF_* of what actually changed to enable finer-grained
804      *             display update listeners
805      * @param isSecondLoop If true, this is the second time this is called for the same change.
806      */
updateLogicalDisplaysLocked(int diff, boolean isSecondLoop)807     private void updateLogicalDisplaysLocked(int diff, boolean isSecondLoop) {
808         boolean reloop = false;
809         // Go through all the displays and figure out if they need to be updated.
810         // Loops in reverse so that displays can be removed during the loop without affecting the
811         // rest of the loop.
812         for (int i = mLogicalDisplays.size() - 1; i >= 0; i--) {
813             final int displayId = mLogicalDisplays.keyAt(i);
814             LogicalDisplay display = mLogicalDisplays.valueAt(i);
815             assignDisplayGroupLocked(display);
816 
817             boolean wasDirty = display.isDirtyLocked();
818             mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
819             display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
820 
821             display.updateLocked(mDisplayDeviceRepo, mSyntheticModeManager);
822             final DisplayInfo newDisplayInfo = display.getDisplayInfoLocked();
823             final int updateState = mUpdatedLogicalDisplays.get(displayId, UPDATE_STATE_NEW);
824             final boolean wasPreviouslyUpdated = updateState != UPDATE_STATE_NEW;
825             final boolean wasPreviouslyEnabled = mDisplaysEnabledCache.get(displayId);
826             final boolean isCurrentlyEnabled = display.isEnabledLocked();
827             int logicalDisplayEventMask = mLogicalDisplaysToUpdate
828                     .get(displayId, LOGICAL_DISPLAY_EVENT_BASE);
829             boolean hasBasicInfoChanged =
830                     !mTempDisplayInfo.equals(newDisplayInfo, /* compareOnlyBasicChanges */ true);
831             // The display is no longer valid and needs to be removed.
832             if (!display.isValidLocked()) {
833                 // Remove from group
834                 final DisplayGroup displayGroup = getDisplayGroupLocked(
835                         getDisplayGroupIdFromDisplayIdLocked(displayId));
836                 if (displayGroup != null) {
837                     displayGroup.removeDisplayLocked(display);
838                 }
839 
840                 if (wasPreviouslyUpdated) {
841                     // The display isn't actually removed from our internal data structures until
842                     // after the notification is sent; see {@link #sendUpdatesForDisplaysLocked}.
843                     if (mDisplaysEnabledCache.get(displayId)) {
844                         // We still need to send LOGICAL_DISPLAY_EVENT_DISCONNECTED
845                         reloop = true;
846                         logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_REMOVED;
847                     } else {
848                         mUpdatedLogicalDisplays.delete(displayId);
849                         logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_DISCONNECTED;
850                     }
851                 } else {
852                     // This display never left this class, safe to remove without notification
853                     mLogicalDisplays.removeAt(i);
854                 }
855                 mLogicalDisplaysToUpdate.put(displayId, logicalDisplayEventMask);
856                 continue;
857 
858             // The display is new.
859             } else if (!wasPreviouslyUpdated) {
860                 // We still need to send LOGICAL_DISPLAY_EVENT_ADDED
861                 reloop = true;
862                 logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_CONNECTED;
863             // Underlying displays device has changed to a different one.
864             } else if (!TextUtils.equals(mTempDisplayInfo.uniqueId, newDisplayInfo.uniqueId)) {
865                 logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_SWAPPED;
866 
867             // Something about the display device has changed.
868             } else if (wasPreviouslyEnabled != isCurrentlyEnabled) {
869                 int event = isCurrentlyEnabled ? LOGICAL_DISPLAY_EVENT_ADDED :
870                         LOGICAL_DISPLAY_EVENT_REMOVED;
871                 logicalDisplayEventMask |= event;
872             } else if (wasDirty) {
873                 // If only the hdr/sdr ratio changed, then send just the event for that case
874                 if ((diff == DisplayDeviceInfo.DIFF_HDR_SDR_RATIO)) {
875                     logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED;
876                 } else {
877                     logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_BASIC_CHANGED
878                             | LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED
879                             | LOGICAL_DISPLAY_EVENT_STATE_CHANGED;
880                 }
881             } else if (hasBasicInfoChanged
882                     || mTempDisplayInfo.getRefreshRate() != newDisplayInfo.getRefreshRate()) {
883                 // If only the hdr/sdr ratio changed, then send just the event for that case
884                 if ((diff == DisplayDeviceInfo.DIFF_HDR_SDR_RATIO)) {
885                     logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED;
886                 } else {
887 
888                     if (hasBasicInfoChanged) {
889                         logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_BASIC_CHANGED;
890                     }
891                     logicalDisplayEventMask
892                             |= updateAndGetMaskForDisplayPropertyChanges(newDisplayInfo);
893                 }
894                 // The display is involved in a display layout transition
895             } else if (updateState == UPDATE_STATE_TRANSITION) {
896                 logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION;
897 
898             // Display frame rate overrides changed.
899             } else if (!display.getPendingFrameRateOverrideUids().isEmpty()) {
900                 logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED;
901 
902             // Non-override display values changed.
903             } else {
904                 // While application shouldn't know nor care about the non-overridden info, we
905                 // still need to let WindowManager know so it can update its own internal state for
906                 // things like display cutouts.
907                 display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
908                 if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo,
909                         /* compareOnlyBasicChanges */ true)) {
910                     logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_BASIC_CHANGED;
911                 }
912                 logicalDisplayEventMask
913                         |= updateAndGetMaskForDisplayPropertyChanges(mTempNonOverrideDisplayInfo);
914             }
915             mLogicalDisplaysToUpdate.put(displayId, logicalDisplayEventMask);
916             mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_UPDATED);
917         }
918 
919         // Go through the groups and do the same thing. We do this after displays since group
920         // information can change in the previous loop.
921         // Loops in reverse so that groups can be removed during the loop without affecting the
922         // rest of the loop.
923         for (int i = mDisplayGroups.size() - 1; i >= 0; i--) {
924             final int groupId = mDisplayGroups.keyAt(i);
925             final DisplayGroup group = mDisplayGroups.valueAt(i);
926             final boolean wasPreviouslyUpdated = mUpdatedDisplayGroups.indexOfKey(groupId) > -1;
927             final int changeCount = group.getChangeCountLocked();
928 
929             if (group.isEmptyLocked()) {
930                 mUpdatedDisplayGroups.delete(groupId);
931                 if (wasPreviouslyUpdated) {
932                     mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_REMOVED);
933                 }
934                 continue;
935             } else if (!wasPreviouslyUpdated) {
936                 mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_ADDED);
937             } else if (mUpdatedDisplayGroups.get(groupId) != changeCount) {
938                 mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_CHANGED);
939             }
940             mUpdatedDisplayGroups.put(groupId, changeCount);
941         }
942 
943         // Send the display and display group updates in order by message type. This is important
944         // to ensure that addition and removal notifications happen in the right order.
945         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION);
946         sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_ADDED);
947         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_REMOVED);
948         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_DISCONNECTED);
949         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_BASIC_CHANGED);
950         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED);
951         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_STATE_CHANGED);
952         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_COMMITTED_STATE_CHANGED);
953         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
954         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_SWAPPED);
955         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_CONNECTED);
956         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_ADDED);
957         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED);
958         sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_CHANGED);
959         sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_REMOVED);
960 
961         mLogicalDisplaysToUpdate.clear();
962         mDisplayGroupsToUpdate.clear();
963 
964         if (reloop) {
965             if (isSecondLoop) {
966                 Slog.wtf(TAG, "Trying to loop a third time");
967                 return;
968             }
969             updateLogicalDisplaysLocked(diff, /* isSecondLoop= */ true);
970         }
971     }
972 
973     @VisibleForTesting
updateAndGetMaskForDisplayPropertyChanges(DisplayInfo newDisplayInfo)974     int updateAndGetMaskForDisplayPropertyChanges(DisplayInfo newDisplayInfo) {
975         int mask = LOGICAL_DISPLAY_EVENT_BASE;
976         if (mTempDisplayInfo.getRefreshRate() != newDisplayInfo.getRefreshRate()) {
977             mask |= LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED;
978         }
979 
980         if (mFlags.isDisplayListenerPerformanceImprovementsEnabled()
981                 && mTempDisplayInfo.state != newDisplayInfo.state) {
982             mask |= LOGICAL_DISPLAY_EVENT_STATE_CHANGED;
983         }
984 
985         if (mFlags.isCommittedStateSeparateEventEnabled()
986                 && mTempDisplayInfo.committedState != newDisplayInfo.committedState) {
987             mask |= LOGICAL_DISPLAY_EVENT_COMMITTED_STATE_CHANGED;
988         }
989         return mask;
990     }
991     /**
992      * Send the specified message for all relevant displays in the specified display-to-message map.
993      */
sendUpdatesForDisplaysLocked(int logicalDisplayEvent)994     private void sendUpdatesForDisplaysLocked(int logicalDisplayEvent) {
995         for (int i = mLogicalDisplaysToUpdate.size() - 1; i >= 0; --i) {
996             final int logicalDisplayEventMask = mLogicalDisplaysToUpdate.valueAt(i);
997             if ((logicalDisplayEventMask & logicalDisplayEvent) == 0) {
998                 continue;
999             }
1000 
1001             final int id = mLogicalDisplaysToUpdate.keyAt(i);
1002             final LogicalDisplay display = getDisplayLocked(id);
1003             if (DEBUG) {
1004                 final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
1005                 final String uniqueId = device == null ? "null" : device.getUniqueId();
1006                 Slog.d(TAG, "Sending " + displayEventToString(logicalDisplayEvent) + " for "
1007                         + "display=" + id + " with device=" + uniqueId);
1008             }
1009 
1010             if (logicalDisplayEvent == LOGICAL_DISPLAY_EVENT_ADDED) {
1011                 mDisplaysEnabledCache.put(id, true);
1012             } else if (logicalDisplayEvent == LOGICAL_DISPLAY_EVENT_REMOVED) {
1013                 mDisplaysEnabledCache.delete(id);
1014             }
1015 
1016             mListener.onLogicalDisplayEventLocked(display, logicalDisplayEvent);
1017 
1018             if (logicalDisplayEvent == LOGICAL_DISPLAY_EVENT_DISCONNECTED) {
1019                 mLogicalDisplays.delete(id);
1020             }
1021         }
1022     }
1023 
1024     /**
1025      * Send the specified message for all relevant display groups in the specified message map.
1026      */
sendUpdatesForGroupsLocked(int msg)1027     private void sendUpdatesForGroupsLocked(int msg) {
1028         for (int i = mDisplayGroupsToUpdate.size() - 1; i >= 0; --i) {
1029             final int currMsg = mDisplayGroupsToUpdate.valueAt(i);
1030             if (currMsg != msg) {
1031                 continue;
1032             }
1033 
1034             final int id = mDisplayGroupsToUpdate.keyAt(i);
1035             mListener.onDisplayGroupEventLocked(id, msg);
1036             if (msg == DISPLAY_GROUP_EVENT_REMOVED) {
1037                 // We wait until we sent the EVENT_REMOVED event before actually removing the
1038                 // group.
1039                 mDisplayGroups.delete(id);
1040                 // Remove possible reference to the removed group.
1041                 int deviceIndex = mDeviceDisplayGroupIds.indexOfValue(id);
1042                 if (deviceIndex >= 0) {
1043                     mDeviceDisplayGroupIds.removeAt(deviceIndex);
1044                 }
1045             }
1046         }
1047     }
1048 
1049     /** This method should be called before LogicalDisplay.updateLocked,
1050      * DisplayInfo in LogicalDisplay (display.getDisplayInfoLocked()) is not updated yet,
1051      * and should not be used directly or indirectly in this method */
assignDisplayGroupLocked(LogicalDisplay display)1052     private void assignDisplayGroupLocked(LogicalDisplay display) {
1053         if (!display.isValidLocked()) { // null check for display.mPrimaryDisplayDevice
1054             return;
1055         }
1056         // updated primary device directly from LogicalDisplay (not from DisplayInfo)
1057         final DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
1058         // final in LogicalDisplay
1059         final int displayId = display.getDisplayIdLocked();
1060         final String primaryDisplayUniqueId = displayDevice.getUniqueId();
1061         final Integer linkedDeviceUniqueId =
1062                 mVirtualDeviceDisplayMapping.get(primaryDisplayUniqueId);
1063 
1064         // Get current display group data
1065         int groupId = getDisplayGroupIdFromDisplayIdLocked(displayId);
1066         Integer deviceDisplayGroupId = null;
1067         if (linkedDeviceUniqueId != null
1068                 && mDeviceDisplayGroupIds.indexOfKey(linkedDeviceUniqueId) > 0) {
1069             deviceDisplayGroupId = mDeviceDisplayGroupIds.get(linkedDeviceUniqueId);
1070         }
1071         final DisplayGroup oldGroup = getDisplayGroupLocked(groupId);
1072 
1073         // groupName directly from LogicalDisplay (not from DisplayInfo)
1074         final String groupName = display.getDisplayGroupNameLocked();
1075         // DisplayDeviceInfo is safe to use, it is updated earlier
1076         final DisplayDeviceInfo displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
1077         // Get the new display group if a change is needed, if display group name is empty and
1078         // {@code DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP} is not set, the display is assigned
1079         // to the default display group.
1080         final boolean needsOwnDisplayGroup =
1081                 (displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0
1082                         || !TextUtils.isEmpty(groupName);
1083 
1084         final boolean hasOwnDisplayGroup = groupId != Display.DEFAULT_DISPLAY_GROUP;
1085         final boolean needsDeviceDisplayGroup =
1086                 !needsOwnDisplayGroup && linkedDeviceUniqueId != null;
1087         final boolean hasDeviceDisplayGroup =
1088                 deviceDisplayGroupId != null && groupId == deviceDisplayGroupId;
1089         if (groupId == Display.INVALID_DISPLAY_GROUP
1090                 || hasOwnDisplayGroup != needsOwnDisplayGroup
1091                 || hasDeviceDisplayGroup != needsDeviceDisplayGroup) {
1092             groupId =
1093                     assignDisplayGroupIdLocked(needsOwnDisplayGroup,
1094                             display.getDisplayGroupNameLocked(), needsDeviceDisplayGroup,
1095                             linkedDeviceUniqueId);
1096         }
1097 
1098         // Create a new group if needed
1099         DisplayGroup newGroup = getDisplayGroupLocked(groupId);
1100         if (newGroup == null) {
1101             newGroup = new DisplayGroup(groupId);
1102             mDisplayGroups.append(groupId, newGroup);
1103         }
1104         if (oldGroup != newGroup) {
1105             if (oldGroup != null) {
1106                 oldGroup.removeDisplayLocked(display);
1107             }
1108             newGroup.addDisplayLocked(display);
1109             display.updateDisplayGroupIdLocked(groupId);
1110             Slog.i(TAG, "Setting new display group " + groupId + " for display "
1111                     + displayId + ", from previous group: "
1112                     + (oldGroup != null ? oldGroup.getGroupId() : "null"));
1113         }
1114     }
1115 
1116     /**
1117      * Goes through all the displays used in the layouts for the specified {@code fromState} and
1118      * {@code toState} and un/marks them for transition. When a new layout is requested, we
1119      * mark the displays that will change into a transitional phase so that they can all be turned
1120      * OFF. Once all are confirmed OFF, then this method gets called again to reset transition
1121      * marker. This helps to ensure that all display-OFF requests are made before
1122      * display-ON which in turn hides any resizing-jank windows might incur when switching displays.
1123      *
1124      * @param fromState The state we are switching from.
1125      * @param toState The state we are switching to.
1126      * @param transitionValue The value to mark the transition state: true == transitioning.
1127      */
resetLayoutLocked(int fromState, int toState, boolean transitionValue)1128     private void resetLayoutLocked(int fromState, int toState, boolean transitionValue) {
1129         final Layout fromLayout = mDeviceStateToLayoutMap.get(fromState);
1130         final Layout toLayout = mDeviceStateToLayoutMap.get(toState);
1131 
1132         final int count = mLogicalDisplays.size();
1133         for (int i = 0; i < count; i++) {
1134             final LogicalDisplay logicalDisplay = mLogicalDisplays.valueAt(i);
1135             final int displayId = logicalDisplay.getDisplayIdLocked();
1136             final DisplayDevice device = logicalDisplay.getPrimaryDisplayDeviceLocked();
1137             if (device == null) {
1138                 // If there's no device, then the logical display is due to be removed. Ignore it.
1139                 continue;
1140             }
1141 
1142             // Grab the display associations this display-device has in the old layout and the
1143             // new layout.
1144             final DisplayAddress address = device.getDisplayDeviceInfoLocked().address;
1145 
1146             // Virtual displays do not have addresses, so account for nulls.
1147             final Layout.Display fromDisplay =
1148                     address != null ? fromLayout.getByAddress(address) : null;
1149             final Layout.Display toDisplay =
1150                     address != null ? toLayout.getByAddress(address) : null;
1151 
1152             // If the display is in one of the layouts but not the other, then the content will
1153             // change, so in this case we also want to blank the displays to avoid jank.
1154             final boolean displayNotInBothLayouts = (fromDisplay == null) != (toDisplay == null);
1155 
1156             // If a layout doesn't mention a display-device at all, then the display-device defaults
1157             // to enabled. This is why we treat null as "enabled" in the code below.
1158             final boolean wasEnabled = fromDisplay == null || fromDisplay.isEnabled();
1159             final boolean willBeEnabled = toDisplay == null || toDisplay.isEnabled();
1160 
1161             final boolean deviceHasNewLogicalDisplayId = fromDisplay != null && toDisplay != null
1162                     && fromDisplay.getLogicalDisplayId() != toDisplay.getLogicalDisplayId();
1163 
1164             // We consider a display-device as changing/transition if
1165             // 1) It's already marked as transitioning
1166             // 2) It's going from enabled to disabled, or vice versa
1167             // 3) It's enabled, but it's mapped to a new logical display ID. To the user this
1168             //    would look like apps moving from one screen to another since task-stacks stay
1169             //    with the logical display [ID].
1170             // 4) It's in one layout but not the other, so the content will change.
1171             final boolean isTransitioning =
1172                     logicalDisplay.isInTransitionLocked()
1173                     || (wasEnabled != willBeEnabled)
1174                     || deviceHasNewLogicalDisplayId
1175                     || displayNotInBothLayouts;
1176 
1177             if (isTransitioning) {
1178                 if (transitionValue != logicalDisplay.isInTransitionLocked()) {
1179                     Slog.i(TAG, "Set isInTransition on display " + displayId + ": "
1180                             + transitionValue);
1181                 }
1182                 // This will either mark the display as "transitioning" if we are starting to change
1183                 // the device state, or remove the transitioning marker if the state change is
1184                 // ending.
1185                 logicalDisplay.setIsInTransitionLocked(transitionValue);
1186                 mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_TRANSITION);
1187             }
1188         }
1189     }
1190 
1191     /**
1192      * Apply (or reapply) the currently selected display layout.
1193      */
applyLayoutLocked()1194     private void applyLayoutLocked() {
1195         final Layout oldLayout = mCurrentLayout;
1196         mCurrentLayout = mDeviceStateToLayoutMap.get(mDeviceState.getIdentifier());
1197         Slog.i(TAG, "Applying layout: " + mCurrentLayout + ", Previous layout: " + oldLayout);
1198 
1199         // Go through each of the displays in the current layout set.
1200         final int size = mCurrentLayout.size();
1201         for (int i = 0; i < size; i++) {
1202             final Layout.Display displayLayout = mCurrentLayout.getAt(i);
1203 
1204             // If the underlying display-device we want to use for this display
1205             // doesn't exist, then skip it. This can happen at startup as display-devices
1206             // trickle in one at a time. When the new display finally shows up, the layout is
1207             // recalculated so that the display is properly added to the current layout.
1208             final DisplayAddress address = displayLayout.getAddress();
1209             final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(address);
1210             if (device == null) {
1211                 Slog.w(TAG, "applyLayoutLocked: The display device (" + address + "), is not "
1212                         + "available for the display state " + mDeviceState.getIdentifier());
1213                 continue;
1214             }
1215 
1216             // Now that we have a display-device, we need a LogicalDisplay to map it to. Find the
1217             // right one, if it doesn't exist, create a new one.
1218             final int logicalDisplayId = displayLayout.getLogicalDisplayId();
1219 
1220             LogicalDisplay newDisplay = getDisplayLocked(logicalDisplayId);
1221             boolean newDisplayCreated = false;
1222             if (newDisplay == null) {
1223                 newDisplay = createNewLogicalDisplayLocked(
1224                         null /*displayDevice*/, logicalDisplayId);
1225                 newDisplayCreated = true;
1226             }
1227 
1228             // Now swap the underlying display devices between the old display and the new display
1229             final LogicalDisplay oldDisplay = getDisplayLocked(device);
1230             if (newDisplay != oldDisplay) {
1231                 // Display is swapping, notify WindowManager, so it can prepare for
1232                 // the display switch
1233                 if (!newDisplayCreated && mWindowManagerPolicy != null) {
1234                     mWindowManagerPolicy.onDisplaySwitchStart(newDisplay.getDisplayIdLocked());
1235                 }
1236 
1237                 newDisplay.swapDisplaysLocked(oldDisplay);
1238             }
1239             DisplayDeviceConfig config = device.getDisplayDeviceConfig();
1240 
1241             newDisplay.setDevicePositionLocked(displayLayout.getPosition());
1242             newDisplay.setLeadDisplayLocked(displayLayout.getLeadDisplayId());
1243             newDisplay.updateLayoutLimitedRefreshRateLocked(
1244                     config.getRefreshRange(displayLayout.getRefreshRateZoneId())
1245             );
1246             newDisplay.updateThermalRefreshRateThrottling(
1247                     config.getThermalRefreshRateThrottlingData(
1248                             displayLayout.getRefreshRateThermalThrottlingMapId()
1249                     )
1250             );
1251             setEnabledLocked(newDisplay, displayLayout.isEnabled());
1252             newDisplay.setThermalBrightnessThrottlingDataIdLocked(
1253                     displayLayout.getThermalBrightnessThrottlingMapId() == null
1254                             ? DisplayDeviceConfig.DEFAULT_ID
1255                             : displayLayout.getThermalBrightnessThrottlingMapId());
1256             newDisplay.setPowerThrottlingDataIdLocked(
1257                     displayLayout.getPowerThrottlingMapId() == null
1258                             ? DisplayDeviceConfig.DEFAULT_ID
1259                             : displayLayout.getPowerThrottlingMapId());
1260             newDisplay.setDisplayGroupNameLocked(displayLayout.getDisplayGroupName());
1261         }
1262     }
1263 
1264     /**
1265      * Creates a new logical display for the specified device and display Id and adds it to the list
1266      * of logical displays.
1267      *
1268      * @param device The device to associate with the LogicalDisplay.
1269      * @param displayId The display ID to give the new display. If invalid, a new ID is assigned.
1270      * @return The new logical display if created, null otherwise.
1271      */
createNewLogicalDisplayLocked(DisplayDevice device, int displayId)1272     private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice device, int displayId) {
1273         final int layerStack = assignLayerStackLocked(displayId);
1274         final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device,
1275                 mFlags.isPixelAnisotropyCorrectionInLogicalDisplayEnabled(),
1276                 mFlags.isAlwaysRotateDisplayDeviceEnabled(),
1277                 mFlags.isSyncedResolutionSwitchEnabled());
1278         display.updateLocked(mDisplayDeviceRepo, mSyntheticModeManager);
1279 
1280         final DisplayInfo info = display.getDisplayInfoLocked();
1281         if (info.type == Display.TYPE_INTERNAL && mDeviceStateToLayoutMap.size() > 1) {
1282             // If this is an internal display and the device uses a display layout configuration,
1283             // the display should be disabled as later we will receive a device state update, which
1284             // will tell us which internal displays should be enabled and which should be disabled.
1285             display.setEnabledLocked(false);
1286         }
1287 
1288         mLogicalDisplays.put(displayId, display);
1289         return display;
1290     }
1291 
setEnabledLocked(LogicalDisplay display, boolean isEnabled)1292     void setEnabledLocked(LogicalDisplay display, boolean isEnabled) {
1293         final int displayId = display.getDisplayIdLocked();
1294         final DisplayInfo info = display.getDisplayInfoLocked();
1295 
1296         final boolean disallowSecondaryDisplay = mSingleDisplayDemoMode
1297                 && (info.type != Display.TYPE_INTERNAL);
1298         if (isEnabled && disallowSecondaryDisplay) {
1299             Slog.i(TAG, "Not creating a logical display for a secondary display because single"
1300                     + " display demo mode is enabled: " + display.getDisplayInfoLocked());
1301             isEnabled = false;
1302         }
1303 
1304         if (display.isEnabledLocked() != isEnabled) {
1305             Slog.i(TAG, "SetEnabled on display " + displayId + ": " + isEnabled);
1306             display.setEnabledLocked(isEnabled);
1307         }
1308     }
1309 
assignDisplayGroupIdLocked(boolean isOwnDisplayGroup, String displayGroupName, boolean isDeviceDisplayGroup, Integer linkedDeviceUniqueId)1310     private int assignDisplayGroupIdLocked(boolean isOwnDisplayGroup, String displayGroupName,
1311             boolean isDeviceDisplayGroup, Integer linkedDeviceUniqueId) {
1312         if (isDeviceDisplayGroup && linkedDeviceUniqueId != null) {
1313             int deviceDisplayGroupId = mDeviceDisplayGroupIds.get(linkedDeviceUniqueId);
1314             // A value of 0 indicates that no device display group was found.
1315             if (deviceDisplayGroupId == 0) {
1316                 deviceDisplayGroupId = mNextNonDefaultGroupId++;
1317                 mDeviceDisplayGroupIds.put(linkedDeviceUniqueId, deviceDisplayGroupId);
1318             }
1319             return deviceDisplayGroupId;
1320         }
1321         if (!isOwnDisplayGroup) return Display.DEFAULT_DISPLAY_GROUP;
1322         Integer displayGroupId = mDisplayGroupIdsByName.get(displayGroupName);
1323         if (displayGroupId == null) {
1324             displayGroupId = Integer.valueOf(mNextNonDefaultGroupId++);
1325             mDisplayGroupIdsByName.put(displayGroupName, displayGroupId);
1326         }
1327         return displayGroupId;
1328     }
1329 
initializeDefaultDisplayDeviceLocked(DisplayDevice device)1330     private void initializeDefaultDisplayDeviceLocked(DisplayDevice device) {
1331         // We always want to make sure that our default layout creates a logical
1332         // display for the default display device that is found.
1333         // To that end, when we are notified of a new default display, we add it to
1334         // the default layout definition if it is not already there.
1335         final Layout layout = mDeviceStateToLayoutMap.get(DeviceStateToLayoutMap.STATE_DEFAULT);
1336         if (layout.getById(DEFAULT_DISPLAY) != null) {
1337             // The layout should only have one default display
1338             return;
1339         }
1340         final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
1341         layout.createDefaultDisplayLocked(info.address, mIdProducer);
1342     }
1343 
assignLayerStackLocked(int displayId)1344     private int assignLayerStackLocked(int displayId) {
1345         // Currently layer stacks and display ids are the same.
1346         // This need not be the case.
1347         return displayId;
1348     }
1349 
toSparseBooleanArray(int[] input)1350     private SparseBooleanArray toSparseBooleanArray(int[] input) {
1351         final SparseBooleanArray retval = new SparseBooleanArray(2);
1352         for (int i = 0; input != null && i < input.length; i++) {
1353             retval.put(input[i], true);
1354         }
1355         return retval;
1356     }
1357 
1358     /**
1359      * Returns true if the device would definitely have outer display ON/Stay Awake on fold based on
1360      * the value of `Continue using app on fold` setting
1361      */
shouldStayAwakeOnFold()1362     private boolean shouldStayAwakeOnFold() {
1363         return mFoldSettingProvider.shouldStayAwakeOnFold() || (
1364                 mFoldSettingProvider.shouldSelectiveStayAwakeOnFold()
1365                         && mFoldGracePeriodProvider.isEnabled());
1366     }
1367 
displayEventToString(int msg)1368     private String displayEventToString(int msg) {
1369         switch(msg) {
1370             case LOGICAL_DISPLAY_EVENT_ADDED:
1371                 return "added";
1372             case LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION:
1373                 return "transition";
1374             case LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED:
1375                 return "framerate_override";
1376             case LOGICAL_DISPLAY_EVENT_SWAPPED:
1377                 return "swapped";
1378             case LOGICAL_DISPLAY_EVENT_REMOVED:
1379                 return "removed";
1380             case LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED:
1381                 return "hdr_sdr_ratio_changed";
1382             case LOGICAL_DISPLAY_EVENT_CONNECTED:
1383                 return "connected";
1384             case LOGICAL_DISPLAY_EVENT_DISCONNECTED:
1385                 return "disconnected";
1386             case LOGICAL_DISPLAY_EVENT_STATE_CHANGED:
1387                 return "state_changed";
1388             case LOGICAL_DISPLAY_EVENT_COMMITTED_STATE_CHANGED:
1389                 return "committed_state_changed";
1390             case LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED:
1391                 return "refresh_rate_changed";
1392             case LOGICAL_DISPLAY_EVENT_BASIC_CHANGED:
1393                 return "basic_changed";
1394         }
1395         return null;
1396     }
1397 
setDisplayEnabledLocked(@onNull LogicalDisplay display, boolean enabled)1398     void setDisplayEnabledLocked(@NonNull LogicalDisplay display, boolean enabled) {
1399         boolean isEnabled = display.isEnabledLocked();
1400         if (isEnabled == enabled) {
1401             Slog.w(TAG, "Display is already " + (isEnabled ? "enabled" : "disabled") + ": "
1402                     + display.getDisplayIdLocked());
1403             return;
1404         }
1405         setEnabledLocked(display, enabled);
1406         updateLogicalDisplaysLocked();
1407     }
1408 
1409     public interface Listener {
onLogicalDisplayEventLocked(LogicalDisplay display, int event)1410         void onLogicalDisplayEventLocked(LogicalDisplay display, int event);
onDisplayGroupEventLocked(int groupId, int event)1411         void onDisplayGroupEventLocked(int groupId, int event);
onTraversalRequested()1412         void onTraversalRequested();
1413     }
1414 
1415     private class LogicalDisplayMapperHandler extends Handler {
LogicalDisplayMapperHandler(Looper looper)1416         LogicalDisplayMapperHandler(Looper looper) {
1417             super(looper, null, true /*async*/);
1418         }
1419 
1420         @Override
handleMessage(Message msg)1421         public void handleMessage(Message msg) {
1422             switch (msg.what) {
1423                 case MSG_TRANSITION_TO_PENDING_DEVICE_STATE:
1424                     synchronized (mSyncRoot) {
1425                         finishStateTransitionLocked(true /*force*/);
1426                     }
1427                     break;
1428             }
1429         }
1430     }
1431 }
1432