• 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.view.Display.DEFAULT_DISPLAY;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.content.Context;
24 import android.hardware.devicestate.DeviceStateManager;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.os.Message;
28 import android.os.PowerManager;
29 import android.os.SystemClock;
30 import android.os.SystemProperties;
31 import android.text.TextUtils;
32 import android.util.IndentingPrintWriter;
33 import android.util.Slog;
34 import android.util.SparseArray;
35 import android.util.SparseBooleanArray;
36 import android.util.SparseIntArray;
37 import android.view.Display;
38 import android.view.DisplayAddress;
39 import android.view.DisplayInfo;
40 
41 import com.android.internal.annotations.VisibleForTesting;
42 import com.android.server.display.layout.Layout;
43 
44 import java.io.PrintWriter;
45 import java.util.Arrays;
46 import java.util.function.Consumer;
47 
48 /**
49  * Responsible for creating {@link LogicalDisplay}s and associating them to the
50  * {@link DisplayDevice} objects supplied through {@link DisplayAdapter.Listener}.
51  *
52  * Additionally this class will keep track of which {@link DisplayGroup} each
53  * {@link LogicalDisplay} belongs to.
54  *
55  * For devices with a single internal display, the mapping is done once and left
56  * alone. For devices with multiple built-in displays, such as foldable devices,
57  * {@link LogicalDisplay}s can be remapped to different {@link DisplayDevice}s.
58  */
59 class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
60     private static final String TAG = "LogicalDisplayMapper";
61 
62     private static final boolean DEBUG = false;
63 
64     public static final int LOGICAL_DISPLAY_EVENT_ADDED = 1;
65     public static final int LOGICAL_DISPLAY_EVENT_CHANGED = 2;
66     public static final int LOGICAL_DISPLAY_EVENT_REMOVED = 3;
67     public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 4;
68     public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 5;
69     public static final int LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION = 6;
70 
71     public static final int DISPLAY_GROUP_EVENT_ADDED = 1;
72     public static final int DISPLAY_GROUP_EVENT_CHANGED = 2;
73     public static final int DISPLAY_GROUP_EVENT_REMOVED = 3;
74 
75     private static final int TIMEOUT_STATE_TRANSITION_MILLIS = 500;
76 
77     private static final int MSG_TRANSITION_TO_PENDING_DEVICE_STATE = 1;
78 
79     private static final int UPDATE_STATE_NEW = 0;
80     private static final int UPDATE_STATE_TRANSITION = 1;
81     private static final int UPDATE_STATE_UPDATED = 2;
82 
83     /**
84      * Temporary display info, used for comparing display configurations.
85      */
86     private final DisplayInfo mTempDisplayInfo = new DisplayInfo();
87     private final DisplayInfo mTempNonOverrideDisplayInfo = new DisplayInfo();
88 
89     /**
90      * True if the display mapper service should pretend there is only one display
91      * and only tell applications about the existence of the default logical display.
92      * The display manager can still mirror content to secondary displays but applications
93      * cannot present unique content on those displays.
94      * Used for demonstration purposes only.
95      */
96     private final boolean mSingleDisplayDemoMode;
97 
98     /**
99      * True if the device can have more than one internal display on at a time.
100      */
101     private final boolean mSupportsConcurrentInternalDisplays;
102 
103     /**
104      * Wake the device when transitioning into these device state.
105      */
106     private final SparseBooleanArray mDeviceStatesOnWhichToWakeUp;
107 
108     /**
109      * Sleep the device when transitioning into these device state.
110      */
111     private final SparseBooleanArray mDeviceStatesOnWhichToSleep;
112 
113     /**
114      * Map of all logical displays indexed by logical display id.
115      * Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache.
116      * TODO: multi-display - Move the aforementioned comment?
117      */
118     private final SparseArray<LogicalDisplay> mLogicalDisplays =
119             new SparseArray<LogicalDisplay>();
120 
121     /** Map of all display groups indexed by display group id. */
122     private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>();
123 
124     private final DisplayDeviceRepository mDisplayDeviceRepo;
125     private final DeviceStateToLayoutMap mDeviceStateToLayoutMap;
126     private final Listener mListener;
127     private final DisplayManagerService.SyncRoot mSyncRoot;
128     private final LogicalDisplayMapperHandler mHandler;
129     private final PowerManager mPowerManager;
130 
131     /**
132      * Has an entry for every logical display that the rest of the system has been notified about.
133      * Any entry in here requires us to send a {@link  LOGICAL_DISPLAY_EVENT_REMOVED} event when it
134      * is deleted or {@link  LOGICAL_DISPLAY_EVENT_CHANGED} when it is changed. The values are any
135      * of the {@code UPDATE_STATE_*} constant types.
136      */
137     private final SparseIntArray mUpdatedLogicalDisplays = new SparseIntArray();
138 
139     /**
140      * Keeps track of all the display groups that we already told other people about. IOW, if a
141      * display group is in this array, then we *must* send change and remove notifications for it
142      * because other components know about them. Also, what this array stores is a change counter
143      * for each group, so we know if the group itself has changes since we last sent out a
144      * notification.  See {@link DisplayGroup#getChangeCountLocked}.
145      */
146     private final SparseIntArray mUpdatedDisplayGroups = new SparseIntArray();
147 
148     /**
149      * Array used in {@link #updateLogicalDisplaysLocked} to track events that need to be sent out.
150      */
151     private final SparseIntArray mLogicalDisplaysToUpdate = new SparseIntArray();
152 
153     /**
154      * Array used in {@link #updateLogicalDisplaysLocked} to track events that need to be sent out.
155      */
156     private final SparseIntArray mDisplayGroupsToUpdate = new SparseIntArray();
157 
158     private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
159     private Layout mCurrentLayout = null;
160     private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
161     private int mPendingDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
162     private int mDeviceStateToBeAppliedAfterBoot = DeviceStateManager.INVALID_DEVICE_STATE;
163     private boolean mBootCompleted = false;
164     private boolean mInteractive;
165 
LogicalDisplayMapper(@onNull Context context, @NonNull DisplayDeviceRepository repo, @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot, @NonNull Handler handler)166     LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
167             @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
168             @NonNull Handler handler) {
169         this(context, repo, listener, syncRoot, handler, new DeviceStateToLayoutMap());
170     }
171 
LogicalDisplayMapper(@onNull Context context, @NonNull DisplayDeviceRepository repo, @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot, @NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap)172     LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
173             @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
174             @NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap) {
175         mSyncRoot = syncRoot;
176         mPowerManager = context.getSystemService(PowerManager.class);
177         mInteractive = mPowerManager.isInteractive();
178         mHandler = new LogicalDisplayMapperHandler(handler.getLooper());
179         mDisplayDeviceRepo = repo;
180         mListener = listener;
181         mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
182         mSupportsConcurrentInternalDisplays = context.getResources().getBoolean(
183                 com.android.internal.R.bool.config_supportsConcurrentInternalDisplays);
184         mDeviceStatesOnWhichToWakeUp = toSparseBooleanArray(context.getResources().getIntArray(
185                 com.android.internal.R.array.config_deviceStatesOnWhichToWakeUp));
186         mDeviceStatesOnWhichToSleep = toSparseBooleanArray(context.getResources().getIntArray(
187                 com.android.internal.R.array.config_deviceStatesOnWhichToSleep));
188         mDisplayDeviceRepo.addListener(this);
189         mDeviceStateToLayoutMap = deviceStateToLayoutMap;
190     }
191 
192     @Override
onDisplayDeviceEventLocked(DisplayDevice device, int event)193     public void onDisplayDeviceEventLocked(DisplayDevice device, int event) {
194         switch (event) {
195             case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_ADDED:
196                 if (DEBUG) {
197                     Slog.d(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked());
198                 }
199                 handleDisplayDeviceAddedLocked(device);
200                 break;
201 
202             case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_CHANGED:
203                 if (DEBUG) {
204                     Slog.d(TAG, "Display device changed: " + device.getDisplayDeviceInfoLocked());
205                 }
206                 finishStateTransitionLocked(false /*force*/);
207                 updateLogicalDisplaysLocked();
208                 break;
209 
210             case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_REMOVED:
211                 if (DEBUG) {
212                     Slog.d(TAG, "Display device removed: " + device.getDisplayDeviceInfoLocked());
213                 }
214                 handleDisplayDeviceRemovedLocked(device);
215                 updateLogicalDisplaysLocked();
216                 break;
217         }
218     }
219 
220     @Override
onTraversalRequested()221     public void onTraversalRequested() {
222         mListener.onTraversalRequested();
223     }
224 
getDisplayLocked(int displayId)225     public LogicalDisplay getDisplayLocked(int displayId) {
226         return getDisplayLocked(displayId, /* includeDisabled= */ true);
227     }
228 
getDisplayLocked(int displayId, boolean includeDisabled)229     public LogicalDisplay getDisplayLocked(int displayId, boolean includeDisabled) {
230         LogicalDisplay display = mLogicalDisplays.get(displayId);
231         if (display == null || display.isEnabledLocked() || includeDisabled) {
232             return display;
233         }
234         return null;
235     }
236 
getDisplayLocked(DisplayDevice device)237     public LogicalDisplay getDisplayLocked(DisplayDevice device) {
238         return getDisplayLocked(device, /* includeDisabled= */ true);
239     }
240 
getDisplayLocked(DisplayDevice device, boolean includeDisabled)241     public LogicalDisplay getDisplayLocked(DisplayDevice device, boolean includeDisabled) {
242         if (device == null) {
243             return null;
244         }
245         final int count = mLogicalDisplays.size();
246         for (int i = 0; i < count; i++) {
247             final LogicalDisplay display = mLogicalDisplays.valueAt(i);
248             if (display.getPrimaryDisplayDeviceLocked() == device) {
249                 if (display.isEnabledLocked() || includeDisabled) {
250                     return display;
251                 }
252                 return null;
253             }
254         }
255         return null;
256     }
257 
getDisplayIdsLocked(int callingUid, boolean includeDisabled)258     public int[] getDisplayIdsLocked(int callingUid, boolean includeDisabled) {
259         final int count = mLogicalDisplays.size();
260         int[] displayIds = new int[count];
261         int n = 0;
262         for (int i = 0; i < count; i++) {
263             LogicalDisplay display = mLogicalDisplays.valueAt(i);
264             if (display.isEnabledLocked() || includeDisabled) {
265                 DisplayInfo info = display.getDisplayInfoLocked();
266                 if (info.hasAccess(callingUid)) {
267                     displayIds[n++] = mLogicalDisplays.keyAt(i);
268                 }
269             }
270         }
271         if (n != count) {
272             displayIds = Arrays.copyOfRange(displayIds, 0, n);
273         }
274         return displayIds;
275     }
276 
forEachLocked(Consumer<LogicalDisplay> consumer)277     public void forEachLocked(Consumer<LogicalDisplay> consumer) {
278         final int count = mLogicalDisplays.size();
279         for (int i = 0; i < count; i++) {
280             consumer.accept(mLogicalDisplays.valueAt(i));
281         }
282     }
283 
284     @VisibleForTesting
getDisplayGroupIdFromDisplayIdLocked(int displayId)285     public int getDisplayGroupIdFromDisplayIdLocked(int displayId) {
286         final LogicalDisplay display = getDisplayLocked(displayId);
287         if (display == null) {
288             return Display.INVALID_DISPLAY_GROUP;
289         }
290 
291         final int size = mDisplayGroups.size();
292         for (int i = 0; i < size; i++) {
293             final DisplayGroup displayGroup = mDisplayGroups.valueAt(i);
294             if (displayGroup.containsLocked(display)) {
295                 return mDisplayGroups.keyAt(i);
296             }
297         }
298 
299         return Display.INVALID_DISPLAY_GROUP;
300     }
301 
getDisplayGroupLocked(int groupId)302     public DisplayGroup getDisplayGroupLocked(int groupId) {
303         return mDisplayGroups.get(groupId);
304     }
305 
306     /**
307      * Returns the {@link DisplayInfo} for this device state, indicated by the given display id. The
308      * DisplayInfo represents the attributes of the indicated display in the layout associated with
309      * this state. This is used to get display information for various displays in various states;
310      * e.g. to help apps preload resources for the possible display states.
311      *
312      * @param deviceState the state to query possible layouts for
313      * @param displayId   the display id to retrieve
314      * @return {@code null} if no corresponding {@link DisplayInfo} could be found, or the
315      * {@link DisplayInfo} with a matching display id.
316      */
317     @Nullable
getDisplayInfoForStateLocked(int deviceState, int displayId)318     public DisplayInfo getDisplayInfoForStateLocked(int deviceState, int displayId) {
319         // Retrieve the layout for this particular state.
320         final Layout layout = mDeviceStateToLayoutMap.get(deviceState);
321         if (layout == null) {
322             return null;
323         }
324         // Retrieve the details of the given display within this layout.
325         Layout.Display display = layout.getById(displayId);
326         if (display == null) {
327             return null;
328         }
329         // Retrieve the display info for the display that matches the display id.
330         final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(display.getAddress());
331         if (device == null) {
332             Slog.w(TAG, "The display device (" + display.getAddress() + "), is not available"
333                     + " for the display state " + mDeviceState);
334             return null;
335         }
336         LogicalDisplay logicalDisplay = getDisplayLocked(device, /* includeDisabled= */ true);
337         if (logicalDisplay == null) {
338             Slog.w(TAG, "The logical display associated with address (" + display.getAddress()
339                     + "), is not available for the display state " + mDeviceState);
340             return null;
341         }
342         DisplayInfo displayInfo = new DisplayInfo(logicalDisplay.getDisplayInfoLocked());
343         displayInfo.displayId = displayId;
344         return displayInfo;
345     }
346 
dumpLocked(PrintWriter pw)347     public void dumpLocked(PrintWriter pw) {
348         pw.println("LogicalDisplayMapper:");
349         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
350         ipw.increaseIndent();
351 
352         ipw.println("mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
353         ipw.println("mCurrentLayout=" + mCurrentLayout);
354         ipw.println("mDeviceStatesOnWhichToWakeUp=" + mDeviceStatesOnWhichToWakeUp);
355         ipw.println("mDeviceStatesOnWhichToSleep=" + mDeviceStatesOnWhichToSleep);
356         ipw.println("mInteractive=" + mInteractive);
357         ipw.println("mBootCompleted=" + mBootCompleted);
358 
359         ipw.println();
360         ipw.println("mDeviceState=" + mDeviceState);
361         ipw.println("mPendingDeviceState=" + mPendingDeviceState);
362         ipw.println("mDeviceStateToBeAppliedAfterBoot=" + mDeviceStateToBeAppliedAfterBoot);
363 
364         final int logicalDisplayCount = mLogicalDisplays.size();
365         ipw.println();
366         ipw.println("Logical Displays: size=" + logicalDisplayCount);
367         for (int i = 0; i < logicalDisplayCount; i++) {
368             int displayId = mLogicalDisplays.keyAt(i);
369             LogicalDisplay display = mLogicalDisplays.valueAt(i);
370             ipw.println("Display " + displayId + ":");
371             ipw.increaseIndent();
372             display.dumpLocked(ipw);
373             ipw.decreaseIndent();
374             ipw.println();
375         }
376         mDeviceStateToLayoutMap.dumpLocked(ipw);
377     }
378 
setDeviceStateLocked(int state, boolean isOverrideActive)379     void setDeviceStateLocked(int state, boolean isOverrideActive) {
380         if (!mBootCompleted) {
381             // The boot animation might still be in progress, we do not want to switch states now
382             // as the boot animation would end up with an incorrect size.
383             if (DEBUG) {
384                 Slog.d(TAG, "Postponing transition to state: " + mPendingDeviceState
385                         + " until boot is completed");
386             }
387             mDeviceStateToBeAppliedAfterBoot = state;
388             return;
389         }
390 
391         Slog.i(TAG, "Requesting Transition to state: " + state + ", from state=" + mDeviceState
392                 + ", interactive=" + mInteractive + ", mBootCompleted=" + mBootCompleted);
393         // As part of a state transition, we may need to turn off some displays temporarily so that
394         // the transition is smooth. Plus, on some devices, only one internal displays can be
395         // on at a time. We use LogicalDisplay.setIsInTransition to mark a display that needs to be
396         // temporarily turned off.
397         resetLayoutLocked(mDeviceState, state, /* isStateChangeStarting= */ true);
398         mPendingDeviceState = state;
399         mDeviceStateToBeAppliedAfterBoot = DeviceStateManager.INVALID_DEVICE_STATE;
400         final boolean wakeDevice = shouldDeviceBeWoken(mPendingDeviceState, mDeviceState,
401                 mInteractive, mBootCompleted);
402         final boolean sleepDevice = shouldDeviceBePutToSleep(mPendingDeviceState, mDeviceState,
403                 isOverrideActive, mInteractive, mBootCompleted);
404 
405         // If all displays are off already, we can just transition here, unless we are trying to
406         // wake or sleep the device as part of this transition. In that case defer the final
407         // transition until later once the device is awake/asleep.
408         if (areAllTransitioningDisplaysOffLocked() && !wakeDevice && !sleepDevice) {
409             transitionToPendingStateLocked();
410             return;
411         }
412 
413         if (DEBUG) {
414             Slog.d(TAG, "Postponing transition to state: " + mPendingDeviceState);
415         }
416         // Send the transitioning phase updates to DisplayManager so that the displays can
417         // start turning OFF in preparation for the new layout.
418         updateLogicalDisplaysLocked();
419 
420         if (wakeDevice || sleepDevice) {
421             if (wakeDevice) {
422                 // We already told the displays to turn off, now we need to wake the device as
423                 // we transition to this new state. We do it here so that the waking happens
424                 // between the transition from one layout to another.
425                 mHandler.post(() -> {
426                     mPowerManager.wakeUp(SystemClock.uptimeMillis(),
427                             PowerManager.WAKE_REASON_UNFOLD_DEVICE, "server.display:unfold");
428                 });
429             } else if (sleepDevice) {
430                 // Send the device to sleep when required.
431                 mHandler.post(() -> {
432                     mPowerManager.goToSleep(SystemClock.uptimeMillis(),
433                             PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD,
434                             PowerManager.GO_TO_SLEEP_FLAG_SOFT_SLEEP);
435                 });
436             }
437         }
438 
439         mHandler.sendEmptyMessageDelayed(MSG_TRANSITION_TO_PENDING_DEVICE_STATE,
440                 TIMEOUT_STATE_TRANSITION_MILLIS);
441     }
442 
onBootCompleted()443     void onBootCompleted() {
444         synchronized (mSyncRoot) {
445             mBootCompleted = true;
446             if (mDeviceStateToBeAppliedAfterBoot != DeviceStateManager.INVALID_DEVICE_STATE) {
447                 setDeviceStateLocked(mDeviceStateToBeAppliedAfterBoot,
448                         /* isOverrideActive= */ false);
449             }
450         }
451     }
452 
onEarlyInteractivityChange(boolean interactive)453     void onEarlyInteractivityChange(boolean interactive) {
454         synchronized (mSyncRoot) {
455             if (mInteractive != interactive) {
456                 mInteractive = interactive;
457                 finishStateTransitionLocked(false /*force*/);
458             }
459         }
460     }
461 
462     /**
463      * Returns if the device should be woken up or not. Called to check if the device state we are
464      * moving to is one that should awake the device, as well as if we are moving from a device
465      * state that shouldn't have been already woken from.
466      *
467      * @param pendingState device state we are moving to
468      * @param currentState device state we are currently in
469      * @param isInteractive if the device is in an interactive state
470      * @param isBootCompleted is the device fully booted
471      *
472      * @see #shouldDeviceBePutToSleep
473      * @see #setDeviceStateLocked
474      */
475     @VisibleForTesting
shouldDeviceBeWoken(int pendingState, int currentState, boolean isInteractive, boolean isBootCompleted)476     boolean shouldDeviceBeWoken(int pendingState, int currentState, boolean isInteractive,
477             boolean isBootCompleted) {
478         return mDeviceStatesOnWhichToWakeUp.get(pendingState)
479                 && !mDeviceStatesOnWhichToWakeUp.get(currentState)
480                 && !isInteractive && isBootCompleted;
481     }
482 
483     /**
484      * Returns if the device should be put to sleep or not.
485      *
486      * Includes a check to verify that the device state that we are moving to, {@code pendingState},
487      * is the same as the physical state of the device, {@code baseState}. Different values for
488      * these parameters indicate a device state override is active, and we shouldn't put the device
489      * to sleep to provide a better user experience.
490      *
491      * @param pendingState device state we are moving to
492      * @param currentState device state we are currently in
493      * @param isOverrideActive if a device state override is currently active or not
494      * @param isInteractive if the device is in an interactive state
495      * @param isBootCompleted is the device fully booted
496      *
497      * @see #shouldDeviceBeWoken
498      * @see #setDeviceStateLocked
499      */
500     @VisibleForTesting
shouldDeviceBePutToSleep(int pendingState, int currentState, boolean isOverrideActive, boolean isInteractive, boolean isBootCompleted)501     boolean shouldDeviceBePutToSleep(int pendingState, int currentState, boolean isOverrideActive,
502             boolean isInteractive, boolean isBootCompleted) {
503         return currentState != DeviceStateManager.INVALID_DEVICE_STATE
504                 && mDeviceStatesOnWhichToSleep.get(pendingState)
505                 && !mDeviceStatesOnWhichToSleep.get(currentState)
506                 && !isOverrideActive
507                 && isInteractive && isBootCompleted;
508     }
509 
areAllTransitioningDisplaysOffLocked()510     private boolean areAllTransitioningDisplaysOffLocked() {
511         final int count = mLogicalDisplays.size();
512         for (int i = 0; i < count; i++) {
513             final LogicalDisplay display = mLogicalDisplays.valueAt(i);
514             if (!display.isInTransitionLocked()) {
515                 continue;
516             }
517 
518             final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
519             if (device != null) {
520                 final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
521                 if (info.state != Display.STATE_OFF) {
522                     return false;
523                 }
524             }
525         }
526         return true;
527     }
528 
transitionToPendingStateLocked()529     private void transitionToPendingStateLocked() {
530         resetLayoutLocked(mDeviceState, mPendingDeviceState, /* isStateChangeStarting= */ false);
531         mDeviceState = mPendingDeviceState;
532         mPendingDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
533         applyLayoutLocked();
534         updateLogicalDisplaysLocked();
535     }
536 
finishStateTransitionLocked(boolean force)537     private void finishStateTransitionLocked(boolean force) {
538         if (mPendingDeviceState == DeviceStateManager.INVALID_DEVICE_STATE) {
539             return;
540         }
541 
542         final boolean waitingToWakeDevice = mDeviceStatesOnWhichToWakeUp.get(mPendingDeviceState)
543                 && !mDeviceStatesOnWhichToWakeUp.get(mDeviceState)
544                 && !mInteractive && mBootCompleted;
545         final boolean waitingToSleepDevice = mDeviceStatesOnWhichToSleep.get(mPendingDeviceState)
546                 && !mDeviceStatesOnWhichToSleep.get(mDeviceState)
547                 && mInteractive && mBootCompleted;
548 
549         final boolean displaysOff = areAllTransitioningDisplaysOffLocked();
550         final boolean isReadyToTransition = displaysOff && !waitingToWakeDevice
551                 && !waitingToSleepDevice;
552 
553         if (isReadyToTransition || force) {
554             transitionToPendingStateLocked();
555             mHandler.removeMessages(MSG_TRANSITION_TO_PENDING_DEVICE_STATE);
556         } else if (DEBUG) {
557             Slog.d(TAG, "Not yet ready to transition to state=" + mPendingDeviceState
558                     + " with displays-off=" + displaysOff + ", force=" + force
559                     + ", mInteractive=" + mInteractive + ", isReady=" + isReadyToTransition);
560         }
561     }
562 
handleDisplayDeviceAddedLocked(DisplayDevice device)563     private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
564         DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
565         // The default Display needs to have additional initialization.
566         // This initializes a default dynamic display layout for the default
567         // device, which is used as a fallback in case no static layout definitions
568         // exist or cannot be loaded.
569         if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY) != 0) {
570             initializeDefaultDisplayDeviceLocked(device);
571         }
572 
573         // Create a logical display for the new display device
574         LogicalDisplay display = createNewLogicalDisplayLocked(
575                 device, Layout.assignDisplayIdLocked(false /*isDefault*/));
576 
577         applyLayoutLocked();
578         updateLogicalDisplaysLocked();
579     }
580 
handleDisplayDeviceRemovedLocked(DisplayDevice device)581     private void handleDisplayDeviceRemovedLocked(DisplayDevice device) {
582         final Layout layout = mDeviceStateToLayoutMap.get(DeviceStateToLayoutMap.STATE_DEFAULT);
583         Layout.Display layoutDisplay = layout.getById(DEFAULT_DISPLAY);
584         if (layoutDisplay == null) {
585             return;
586         }
587         DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
588 
589         if (layoutDisplay.getAddress().equals(deviceInfo.address)) {
590             layout.removeDisplayLocked(DEFAULT_DISPLAY);
591 
592             // Need to find another local display and make it default
593             for (int i = 0; i < mLogicalDisplays.size(); i++) {
594                 LogicalDisplay nextDisplay = mLogicalDisplays.valueAt(i);
595                 DisplayDevice nextDevice = nextDisplay.getPrimaryDisplayDeviceLocked();
596                 if (nextDevice == null) {
597                     continue;
598                 }
599                 DisplayDeviceInfo nextDeviceInfo = nextDevice.getDisplayDeviceInfoLocked();
600 
601                 if ((nextDeviceInfo.flags
602                         & DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY) != 0
603                         && !nextDeviceInfo.address.equals(deviceInfo.address)) {
604                     layout.createDisplayLocked(nextDeviceInfo.address,
605                             /* isDefault= */ true, /* isEnabled= */ true);
606                     applyLayoutLocked();
607                     return;
608                 }
609             }
610         }
611     }
612 
613     /**
614      * Updates the rest of the display system once all the changes are applied for display
615      * devices and logical displays. The includes releasing invalid/empty LogicalDisplays,
616      * creating/adjusting/removing DisplayGroups, and notifying the rest of the system of the
617      * relevant changes.
618      */
updateLogicalDisplaysLocked()619     private void updateLogicalDisplaysLocked() {
620         // Go through all the displays and figure out if they need to be updated.
621         // Loops in reverse so that displays can be removed during the loop without affecting the
622         // rest of the loop.
623         for (int i = mLogicalDisplays.size() - 1; i >= 0; i--) {
624             final int displayId = mLogicalDisplays.keyAt(i);
625             LogicalDisplay display = mLogicalDisplays.valueAt(i);
626 
627             mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
628             display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
629 
630             display.updateLocked(mDisplayDeviceRepo);
631             final DisplayInfo newDisplayInfo = display.getDisplayInfoLocked();
632             final int updateState = mUpdatedLogicalDisplays.get(displayId, UPDATE_STATE_NEW);
633             final boolean wasPreviouslyUpdated = updateState != UPDATE_STATE_NEW;
634 
635             // The display is no longer valid and needs to be removed.
636             if (!display.isValidLocked()) {
637                 mUpdatedLogicalDisplays.delete(displayId);
638 
639                 // Remove from group
640                 final DisplayGroup displayGroup = getDisplayGroupLocked(
641                         getDisplayGroupIdFromDisplayIdLocked(displayId));
642                 if (displayGroup != null) {
643                     displayGroup.removeDisplayLocked(display);
644                 }
645 
646                 if (wasPreviouslyUpdated) {
647                     // The display isn't actually removed from our internal data structures until
648                     // after the notification is sent; see {@link #sendUpdatesForDisplaysLocked}.
649                     Slog.i(TAG, "Removing display: " + displayId);
650                     mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_REMOVED);
651                 } else {
652                     // This display never left this class, safe to remove without notification
653                     mLogicalDisplays.removeAt(i);
654                 }
655                 continue;
656 
657             // The display is new.
658             } else if (!wasPreviouslyUpdated) {
659                 Slog.i(TAG, "Adding new display: " + displayId + ": " + newDisplayInfo);
660                 assignDisplayGroupLocked(display);
661                 mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_ADDED);
662 
663             // Underlying displays device has changed to a different one.
664             } else if (!TextUtils.equals(mTempDisplayInfo.uniqueId, newDisplayInfo.uniqueId)) {
665                 // FLAG_OWN_DISPLAY_GROUP could have changed, recalculate just in case
666                 assignDisplayGroupLocked(display);
667                 mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_SWAPPED);
668 
669             // Something about the display device has changed.
670             } else if (!mTempDisplayInfo.equals(newDisplayInfo)) {
671                 // FLAG_OWN_DISPLAY_GROUP could have changed, recalculate just in case
672                 assignDisplayGroupLocked(display);
673                 mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
674 
675             // The display is involved in a display layout transition
676             } else if (updateState == UPDATE_STATE_TRANSITION) {
677                 mLogicalDisplaysToUpdate.put(displayId,
678                         LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION);
679 
680             // Display frame rate overrides changed.
681             } else if (!display.getPendingFrameRateOverrideUids().isEmpty()) {
682                 mLogicalDisplaysToUpdate.put(
683                         displayId, LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
684 
685             // Non-override display values changed.
686             } else {
687                 // While application shouldn't know nor care about the non-overridden info, we
688                 // still need to let WindowManager know so it can update its own internal state for
689                 // things like display cutouts.
690                 display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
691                 if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) {
692                     mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
693                 }
694             }
695 
696             mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_UPDATED);
697         }
698 
699         // Go through the groups and do the same thing. We do this after displays since group
700         // information can change in the previous loop.
701         // Loops in reverse so that groups can be removed during the loop without affecting the
702         // rest of the loop.
703         for (int i = mDisplayGroups.size() - 1; i >= 0; i--) {
704             final int groupId = mDisplayGroups.keyAt(i);
705             final DisplayGroup group = mDisplayGroups.valueAt(i);
706             final boolean wasPreviouslyUpdated = mUpdatedDisplayGroups.indexOfKey(groupId) > -1;
707             final int changeCount = group.getChangeCountLocked();
708 
709             if (group.isEmptyLocked()) {
710                 mUpdatedDisplayGroups.delete(groupId);
711                 if (wasPreviouslyUpdated) {
712                     mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_REMOVED);
713                 }
714                 continue;
715             } else if (!wasPreviouslyUpdated) {
716                 mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_ADDED);
717             } else if (mUpdatedDisplayGroups.get(groupId) != changeCount) {
718                 mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_CHANGED);
719             }
720             mUpdatedDisplayGroups.put(groupId, changeCount);
721         }
722 
723         // Send the display and display group updates in order by message type. This is important
724         // to ensure that addition and removal notifications happen in the right order.
725         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION);
726         sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_ADDED);
727         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_REMOVED);
728         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_CHANGED);
729         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
730         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_SWAPPED);
731         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_ADDED);
732         sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_CHANGED);
733         sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_REMOVED);
734 
735         mLogicalDisplaysToUpdate.clear();
736         mDisplayGroupsToUpdate.clear();
737     }
738 
739     /**
740      * Send the specified message for all relevant displays in the specified display-to-message map.
741      */
sendUpdatesForDisplaysLocked(int msg)742     private void sendUpdatesForDisplaysLocked(int msg) {
743         for (int i = mLogicalDisplaysToUpdate.size() - 1; i >= 0; --i) {
744             final int currMsg = mLogicalDisplaysToUpdate.valueAt(i);
745             if (currMsg != msg) {
746                 continue;
747             }
748 
749             final int id = mLogicalDisplaysToUpdate.keyAt(i);
750             final LogicalDisplay display = getDisplayLocked(id);
751             if (DEBUG) {
752                 final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
753                 final String uniqueId = device == null ? "null" : device.getUniqueId();
754                 Slog.d(TAG, "Sending " + displayEventToString(msg) + " for display=" + id
755                         + " with device=" + uniqueId);
756             }
757             mListener.onLogicalDisplayEventLocked(display, msg);
758             if (msg == LOGICAL_DISPLAY_EVENT_REMOVED) {
759                 // We wait until we sent the EVENT_REMOVED event before actually removing the
760                 // display.
761                 mLogicalDisplays.delete(id);
762             }
763         }
764     }
765 
766     /**
767      * Send the specified message for all relevant display groups in the specified message map.
768      */
sendUpdatesForGroupsLocked(int msg)769     private void sendUpdatesForGroupsLocked(int msg) {
770         for (int i = mDisplayGroupsToUpdate.size() - 1; i >= 0; --i) {
771             final int currMsg = mDisplayGroupsToUpdate.valueAt(i);
772             if (currMsg != msg) {
773                 continue;
774             }
775 
776             final int id = mDisplayGroupsToUpdate.keyAt(i);
777             mListener.onDisplayGroupEventLocked(id, msg);
778             if (msg == DISPLAY_GROUP_EVENT_REMOVED) {
779                 // We wait until we sent the EVENT_REMOVED event before actually removing the
780                 // group.
781                 mDisplayGroups.delete(id);
782             }
783         }
784     }
785 
assignDisplayGroupLocked(LogicalDisplay display)786     private void assignDisplayGroupLocked(LogicalDisplay display) {
787         final int displayId = display.getDisplayIdLocked();
788 
789         // Get current display group data
790         int groupId = getDisplayGroupIdFromDisplayIdLocked(displayId);
791         final DisplayGroup oldGroup = getDisplayGroupLocked(groupId);
792 
793         // Get the new display group if a change is needed
794         final DisplayInfo info = display.getDisplayInfoLocked();
795         final boolean needsOwnDisplayGroup = (info.flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0;
796         final boolean hasOwnDisplayGroup = groupId != Display.DEFAULT_DISPLAY_GROUP;
797         if (groupId == Display.INVALID_DISPLAY_GROUP
798                 || hasOwnDisplayGroup != needsOwnDisplayGroup) {
799             groupId = assignDisplayGroupIdLocked(needsOwnDisplayGroup);
800         }
801 
802         // Create a new group if needed
803         DisplayGroup newGroup = getDisplayGroupLocked(groupId);
804         if (newGroup == null) {
805             newGroup = new DisplayGroup(groupId);
806             mDisplayGroups.append(groupId, newGroup);
807         }
808         if (oldGroup != newGroup) {
809             if (oldGroup != null) {
810                 oldGroup.removeDisplayLocked(display);
811             }
812             newGroup.addDisplayLocked(display);
813             display.updateDisplayGroupIdLocked(groupId);
814             Slog.i(TAG, "Setting new display group " + groupId + " for display "
815                     + displayId + ", from previous group: "
816                     + (oldGroup != null ? oldGroup.getGroupId() : "null"));
817         }
818     }
819 
820     /**
821      * Goes through all the displays used in the layouts for the specified {@code fromState} and
822      * {@code toState} and un/marks them for transition. When a new layout is requested, we
823      * mark the displays that will change into a transitional phase so that they can all be turned
824      * OFF. Once all are confirmed OFF, then this method gets called again to reset transition
825      * marker. This helps to ensure that all display-OFF requests are made before
826      * display-ON which in turn hides any resizing-jank windows might incur when switching displays.
827      *
828      * @param fromState The state we are switching from.
829      * @param toState The state we are switching to.
830      * @param isStateChangeStarting Indicates whether to start or end Transition phase.
831      */
resetLayoutLocked(int fromState, int toState, boolean isStateChangeStarting)832     private void resetLayoutLocked(int fromState, int toState, boolean isStateChangeStarting) {
833         final Layout fromLayout = mDeviceStateToLayoutMap.get(fromState);
834         final Layout toLayout = mDeviceStateToLayoutMap.get(toState);
835 
836         final int count = mLogicalDisplays.size();
837         for (int i = 0; i < count; i++) {
838             final LogicalDisplay logicalDisplay = mLogicalDisplays.valueAt(i);
839             final int displayId = logicalDisplay.getDisplayIdLocked();
840             final DisplayDevice device = logicalDisplay.getPrimaryDisplayDeviceLocked();
841             if (device == null) {
842                 // If there's no device, then the logical display is due to be removed. Ignore it.
843                 continue;
844             }
845 
846             // Grab the display associations this display-device has in the old layout and the
847             // new layout.
848             final DisplayAddress address = device.getDisplayDeviceInfoLocked().address;
849 
850             // Virtual displays do not have addresses, so account for nulls.
851             final Layout.Display fromDisplay =
852                     address != null ? fromLayout.getByAddress(address) : null;
853             final Layout.Display toDisplay =
854                     address != null ? toLayout.getByAddress(address) : null;
855 
856             // If the display is in one of the layouts but not the other, then the content will
857             // change, so in this case we also want to blank the displays to avoid jank.
858             final boolean displayNotInBothLayouts = (fromDisplay == null) != (toDisplay == null);
859 
860             // If a layout doesn't mention a display-device at all, then the display-device defaults
861             // to enabled. This is why we treat null as "enabled" in the code below.
862             final boolean wasEnabled = fromDisplay == null || fromDisplay.isEnabled();
863             final boolean willBeEnabled = toDisplay == null || toDisplay.isEnabled();
864 
865             final boolean deviceHasNewLogicalDisplayId = fromDisplay != null && toDisplay != null
866                     && fromDisplay.getLogicalDisplayId() != toDisplay.getLogicalDisplayId();
867 
868             // We consider a display-device as changing/transition if
869             // 1) It's already marked as transitioning
870             // 2) It's going from enabled to disabled, or vice versa
871             // 3) It's enabled, but it's mapped to a new logical display ID. To the user this
872             //    would look like apps moving from one screen to another since task-stacks stay
873             //    with the logical display [ID].
874             // 4) It's in one layout but not the other, so the content will change.
875             final boolean isTransitioning =
876                     logicalDisplay.isInTransitionLocked()
877                     || (wasEnabled != willBeEnabled)
878                     || deviceHasNewLogicalDisplayId
879                     || displayNotInBothLayouts;
880 
881             if (isTransitioning) {
882                 if (isStateChangeStarting != logicalDisplay.isInTransitionLocked()) {
883                     Slog.i(TAG, "Set isInTransition on display " + displayId + ": "
884                             + isStateChangeStarting);
885                 }
886                 // This will either mark the display as "transitioning" if we are starting to change
887                 // the device state, or remove the transitioning marker if the state change is
888                 // ending.
889                 logicalDisplay.setIsInTransitionLocked(isStateChangeStarting);
890                 mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_TRANSITION);
891             }
892         }
893     }
894 
895     /**
896      * Apply (or reapply) the currently selected display layout.
897      */
applyLayoutLocked()898     private void applyLayoutLocked() {
899         final Layout oldLayout = mCurrentLayout;
900         mCurrentLayout = mDeviceStateToLayoutMap.get(mDeviceState);
901         Slog.i(TAG, "Applying layout: " + mCurrentLayout + ", Previous layout: " + oldLayout);
902 
903         // Go through each of the displays in the current layout set.
904         final int size = mCurrentLayout.size();
905         for (int i = 0; i < size; i++) {
906             final Layout.Display displayLayout = mCurrentLayout.getAt(i);
907 
908             // If the underlying display-device we want to use for this display
909             // doesn't exist, then skip it. This can happen at startup as display-devices
910             // trickle in one at a time. When the new display finally shows up, the layout is
911             // recalculated so that the display is properly added to the current layout.
912             final DisplayAddress address = displayLayout.getAddress();
913             final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(address);
914             if (device == null) {
915                 Slog.w(TAG, "The display device (" + address + "), is not available"
916                         + " for the display state " + mDeviceState);
917                 continue;
918             }
919 
920             // Now that we have a display-device, we need a LogicalDisplay to map it to. Find the
921             // right one, if it doesn't exist, create a new one.
922             final int logicalDisplayId = displayLayout.getLogicalDisplayId();
923             LogicalDisplay newDisplay = getDisplayLocked(logicalDisplayId);
924             if (newDisplay == null) {
925                 newDisplay = createNewLogicalDisplayLocked(
926                         null /*displayDevice*/, logicalDisplayId);
927             }
928 
929             // Now swap the underlying display devices between the old display and the new display
930             final LogicalDisplay oldDisplay = getDisplayLocked(device);
931             if (newDisplay != oldDisplay) {
932                 newDisplay.swapDisplaysLocked(oldDisplay);
933             }
934 
935             setEnabledLocked(newDisplay, displayLayout.isEnabled());
936         }
937 
938     }
939 
940 
941     /**
942      * Creates a new logical display for the specified device and display Id and adds it to the list
943      * of logical displays.
944      *
945      * @param device The device to associate with the LogicalDisplay.
946      * @param displayId The display ID to give the new display. If invalid, a new ID is assigned.
947      * @return The new logical display if created, null otherwise.
948      */
createNewLogicalDisplayLocked(DisplayDevice device, int displayId)949     private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice device, int displayId) {
950         final int layerStack = assignLayerStackLocked(displayId);
951         final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
952         display.updateLocked(mDisplayDeviceRepo);
953 
954         final DisplayInfo info = display.getDisplayInfoLocked();
955         if (info.type == Display.TYPE_INTERNAL && mDeviceStateToLayoutMap.size() > 1) {
956             // If this is an internal display and the device uses a display layout configuration,
957             // the display should be disabled as later we will receive a device state update, which
958             // will tell us which internal displays should be enabled and which should be disabled.
959             display.setEnabledLocked(false);
960         }
961 
962         mLogicalDisplays.put(displayId, display);
963         return display;
964     }
965 
setEnabledLocked(LogicalDisplay display, boolean isEnabled)966     private void setEnabledLocked(LogicalDisplay display, boolean isEnabled) {
967         final int displayId = display.getDisplayIdLocked();
968         final DisplayInfo info = display.getDisplayInfoLocked();
969 
970         final boolean disallowSecondaryDisplay = mSingleDisplayDemoMode
971                 && (info.type != Display.TYPE_INTERNAL);
972         if (isEnabled && disallowSecondaryDisplay) {
973             Slog.i(TAG, "Not creating a logical display for a secondary display because single"
974                     + " display demo mode is enabled: " + display.getDisplayInfoLocked());
975             isEnabled = false;
976         }
977 
978         if (display.isEnabledLocked() != isEnabled) {
979             Slog.i(TAG, "SetEnabled on display " + displayId + ": " + isEnabled);
980             display.setEnabledLocked(isEnabled);
981         }
982     }
983 
assignDisplayGroupIdLocked(boolean isOwnDisplayGroup)984     private int assignDisplayGroupIdLocked(boolean isOwnDisplayGroup) {
985         return isOwnDisplayGroup ? mNextNonDefaultGroupId++ : Display.DEFAULT_DISPLAY_GROUP;
986     }
987 
initializeDefaultDisplayDeviceLocked(DisplayDevice device)988     private void initializeDefaultDisplayDeviceLocked(DisplayDevice device) {
989         // We always want to make sure that our default layout creates a logical
990         // display for the default display device that is found.
991         // To that end, when we are notified of a new default display, we add it to
992         // the default layout definition if it is not already there.
993         final Layout layout = mDeviceStateToLayoutMap.get(DeviceStateToLayoutMap.STATE_DEFAULT);
994         if (layout.getById(DEFAULT_DISPLAY) != null) {
995             // The layout should only have one default display
996             return;
997         }
998         final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
999         layout.createDisplayLocked(info.address, /* isDefault= */ true, /* isEnabled= */ true);
1000     }
1001 
assignLayerStackLocked(int displayId)1002     private int assignLayerStackLocked(int displayId) {
1003         // Currently layer stacks and display ids are the same.
1004         // This need not be the case.
1005         return displayId;
1006     }
1007 
toSparseBooleanArray(int[] input)1008     private SparseBooleanArray toSparseBooleanArray(int[] input) {
1009         final SparseBooleanArray retval = new SparseBooleanArray(2);
1010         for (int i = 0; input != null && i < input.length; i++) {
1011             retval.put(input[i], true);
1012         }
1013         return retval;
1014     }
1015 
displayEventToString(int msg)1016     private String displayEventToString(int msg) {
1017         switch(msg) {
1018             case LOGICAL_DISPLAY_EVENT_ADDED:
1019                 return "added";
1020             case LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION:
1021                 return "transition";
1022             case LOGICAL_DISPLAY_EVENT_CHANGED:
1023                 return "changed";
1024             case LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED:
1025                 return "framerate_override";
1026             case LOGICAL_DISPLAY_EVENT_SWAPPED:
1027                 return "swapped";
1028             case LOGICAL_DISPLAY_EVENT_REMOVED:
1029                 return "removed";
1030         }
1031         return null;
1032     }
1033 
1034     public interface Listener {
onLogicalDisplayEventLocked(LogicalDisplay display, int event)1035         void onLogicalDisplayEventLocked(LogicalDisplay display, int event);
onDisplayGroupEventLocked(int groupId, int event)1036         void onDisplayGroupEventLocked(int groupId, int event);
onTraversalRequested()1037         void onTraversalRequested();
1038     }
1039 
1040     private class LogicalDisplayMapperHandler extends Handler {
LogicalDisplayMapperHandler(Looper looper)1041         LogicalDisplayMapperHandler(Looper looper) {
1042             super(looper, null, true /*async*/);
1043         }
1044 
1045         @Override
handleMessage(Message msg)1046         public void handleMessage(Message msg) {
1047             switch (msg.what) {
1048                 case MSG_TRANSITION_TO_PENDING_DEVICE_STATE:
1049                     synchronized (mSyncRoot) {
1050                         finishStateTransitionLocked(true /*force*/);
1051                     }
1052                     break;
1053             }
1054         }
1055     }
1056 }
1057