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 android.annotation.NonNull;
20 import android.content.Context;
21 import android.hardware.devicestate.DeviceStateManager;
22 import android.os.Handler;
23 import android.os.Looper;
24 import android.os.Message;
25 import android.os.PowerManager;
26 import android.os.SystemClock;
27 import android.os.SystemProperties;
28 import android.text.TextUtils;
29 import android.util.IndentingPrintWriter;
30 import android.util.Slog;
31 import android.util.SparseArray;
32 import android.util.SparseIntArray;
33 import android.view.Display;
34 import android.view.DisplayAddress;
35 import android.view.DisplayInfo;
36 
37 import com.android.internal.annotations.VisibleForTesting;
38 import com.android.server.display.LogicalDisplay.DisplayPhase;
39 import com.android.server.display.layout.Layout;
40 
41 import java.io.PrintWriter;
42 import java.util.Arrays;
43 import java.util.function.Consumer;
44 
45 /**
46  * Responsible for creating {@link LogicalDisplay}s and associating them to the
47  * {@link DisplayDevice} objects supplied through {@link DisplayAdapter.Listener}.
48  *
49  * Additionally this class will keep track of which {@link DisplayGroup} each
50  * {@link LogicalDisplay} belongs to.
51  *
52  * For devices with a single internal display, the mapping is done once and left
53  * alone. For devices with multiple built-in displays, such as foldable devices,
54  * {@link LogicalDisplay}s can be remapped to different {@link DisplayDevice}s.
55  */
56 class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
57     private static final String TAG = "LogicalDisplayMapper";
58 
59     private static final boolean DEBUG = false;
60 
61     public static final int LOGICAL_DISPLAY_EVENT_ADDED = 1;
62     public static final int LOGICAL_DISPLAY_EVENT_CHANGED = 2;
63     public static final int LOGICAL_DISPLAY_EVENT_REMOVED = 3;
64     public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 4;
65     public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 5;
66     public static final int LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION = 6;
67 
68     public static final int DISPLAY_GROUP_EVENT_ADDED = 1;
69     public static final int DISPLAY_GROUP_EVENT_CHANGED = 2;
70     public static final int DISPLAY_GROUP_EVENT_REMOVED = 3;
71 
72     private static final int TIMEOUT_STATE_TRANSITION_MILLIS = 300;
73 
74     private static final int MSG_TRANSITION_TO_PENDING_DEVICE_STATE = 1;
75 
76     private static final int UPDATE_STATE_NEW = 0;
77     private static final int UPDATE_STATE_TRANSITION = 1;
78     private static final int UPDATE_STATE_UPDATED = 2;
79 
80     /**
81      * Temporary display info, used for comparing display configurations.
82      */
83     private final DisplayInfo mTempDisplayInfo = new DisplayInfo();
84     private final DisplayInfo mTempNonOverrideDisplayInfo = new DisplayInfo();
85 
86     /**
87      * True if the display mapper service should pretend there is only one display
88      * and only tell applications about the existence of the default logical display.
89      * The display manager can still mirror content to secondary displays but applications
90      * cannot present unique content on those displays.
91      * Used for demonstration purposes only.
92      */
93     private final boolean mSingleDisplayDemoMode;
94 
95     /**
96      * True if the device can have more than one internal display on at a time.
97      */
98     private final boolean mSupportsConcurrentInternalDisplays;
99 
100     /**
101      * Wake the device when transitioning into this device state.
102      */
103     private final int mDeviceStateOnWhichToWakeUp;
104 
105     /**
106      * Map of all logical displays indexed by logical display id.
107      * Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache.
108      * TODO: multi-display - Move the aforementioned comment?
109      */
110     private final SparseArray<LogicalDisplay> mLogicalDisplays =
111             new SparseArray<LogicalDisplay>();
112 
113     /** Map of all display groups indexed by display group id. */
114     private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>();
115 
116     private final DisplayDeviceRepository mDisplayDeviceRepo;
117     private final DeviceStateToLayoutMap mDeviceStateToLayoutMap;
118     private final Listener mListener;
119     private final DisplayManagerService.SyncRoot mSyncRoot;
120     private final LogicalDisplayMapperHandler mHandler;
121     private final PowerManager mPowerManager;
122 
123     /**
124      * Has an entry for every logical display that the rest of the system has been notified about.
125      * Any entry in here requires us to send a {@link  LOGICAL_DISPLAY_EVENT_REMOVED} event when it
126      * is deleted or {@link  LOGICAL_DISPLAY_EVENT_CHANGED} when it is changed. The values are any
127      * of the {@code UPDATE_STATE_*} constant types.
128      */
129     private final SparseIntArray mUpdatedLogicalDisplays = new SparseIntArray();
130 
131     /**
132      * Keeps track of all the display groups that we already told other people about. IOW, if a
133      * display group is in this array, then we *must* send change and remove notifications for it
134      * because other components know about them. Also, what this array stores is a change counter
135      * for each group, so we know if the group itself has changes since we last sent out a
136      * notification.  See {@link DisplayGroup#getChangeCountLocked}.
137      */
138     private final SparseIntArray mUpdatedDisplayGroups = new SparseIntArray();
139 
140     /**
141      * Array used in {@link #updateLogicalDisplaysLocked} to track events that need to be sent out.
142      */
143     private final SparseIntArray mLogicalDisplaysToUpdate = new SparseIntArray();
144 
145     /**
146      * Array used in {@link #updateLogicalDisplaysLocked} to track events that need to be sent out.
147      */
148     private final SparseIntArray mDisplayGroupsToUpdate = new SparseIntArray();
149 
150     private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
151     private Layout mCurrentLayout = null;
152     private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
153     private int mPendingDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
154 
LogicalDisplayMapper(@onNull Context context, @NonNull DisplayDeviceRepository repo, @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot, @NonNull Handler handler)155     LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
156             @NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
157             @NonNull Handler handler) {
158         mSyncRoot = syncRoot;
159         mPowerManager = context.getSystemService(PowerManager.class);
160         mHandler = new LogicalDisplayMapperHandler(handler.getLooper());
161         mDisplayDeviceRepo = repo;
162         mListener = listener;
163         mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
164         mSupportsConcurrentInternalDisplays = context.getResources().getBoolean(
165                 com.android.internal.R.bool.config_supportsConcurrentInternalDisplays);
166         mDeviceStateOnWhichToWakeUp = context.getResources().getInteger(
167                 com.android.internal.R.integer.config_deviceStateOnWhichToWakeUp);
168         mDisplayDeviceRepo.addListener(this);
169         mDeviceStateToLayoutMap = new DeviceStateToLayoutMap();
170     }
171 
172     @Override
onDisplayDeviceEventLocked(DisplayDevice device, int event)173     public void onDisplayDeviceEventLocked(DisplayDevice device, int event) {
174         switch (event) {
175             case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_ADDED:
176                 if (DEBUG) {
177                     Slog.d(TAG, "Display device added: " + device.getDisplayDeviceInfoLocked());
178                 }
179                 handleDisplayDeviceAddedLocked(device);
180                 break;
181 
182             case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_CHANGED:
183                 if (DEBUG) {
184                     Slog.d(TAG, "Display device changed: " + device.getDisplayDeviceInfoLocked());
185                 }
186                 finishStateTransitionLocked(false /*force*/);
187                 updateLogicalDisplaysLocked();
188                 break;
189 
190             case DisplayDeviceRepository.DISPLAY_DEVICE_EVENT_REMOVED:
191                 if (DEBUG) {
192                     Slog.d(TAG, "Display device removed: " + device.getDisplayDeviceInfoLocked());
193                 }
194                 updateLogicalDisplaysLocked();
195                 break;
196         }
197     }
198 
199     @Override
onTraversalRequested()200     public void onTraversalRequested() {
201         mListener.onTraversalRequested();
202     }
203 
getDisplayLocked(int displayId)204     public LogicalDisplay getDisplayLocked(int displayId) {
205         return mLogicalDisplays.get(displayId);
206     }
207 
getDisplayLocked(DisplayDevice device)208     public LogicalDisplay getDisplayLocked(DisplayDevice device) {
209         if (device == null) {
210             return null;
211         }
212         final int count = mLogicalDisplays.size();
213         for (int i = 0; i < count; i++) {
214             final LogicalDisplay display = mLogicalDisplays.valueAt(i);
215             if (display.getPrimaryDisplayDeviceLocked() == device) {
216                 return display;
217             }
218         }
219         return null;
220     }
221 
getDisplayIdsLocked(int callingUid)222     public int[] getDisplayIdsLocked(int callingUid) {
223         final int count = mLogicalDisplays.size();
224         int[] displayIds = new int[count];
225         int n = 0;
226         for (int i = 0; i < count; i++) {
227             LogicalDisplay display = mLogicalDisplays.valueAt(i);
228             DisplayInfo info = display.getDisplayInfoLocked();
229             if (info.hasAccess(callingUid)) {
230                 displayIds[n++] = mLogicalDisplays.keyAt(i);
231             }
232         }
233         if (n != count) {
234             displayIds = Arrays.copyOfRange(displayIds, 0, n);
235         }
236         return displayIds;
237     }
238 
forEachLocked(Consumer<LogicalDisplay> consumer)239     public void forEachLocked(Consumer<LogicalDisplay> consumer) {
240         final int count = mLogicalDisplays.size();
241         for (int i = 0; i < count; i++) {
242             consumer.accept(mLogicalDisplays.valueAt(i));
243         }
244     }
245 
246     @VisibleForTesting
getDisplayGroupIdFromDisplayIdLocked(int displayId)247     public int getDisplayGroupIdFromDisplayIdLocked(int displayId) {
248         final LogicalDisplay display = getDisplayLocked(displayId);
249         if (display == null) {
250             return Display.INVALID_DISPLAY_GROUP;
251         }
252 
253         final int size = mDisplayGroups.size();
254         for (int i = 0; i < size; i++) {
255             final DisplayGroup displayGroup = mDisplayGroups.valueAt(i);
256             if (displayGroup.containsLocked(display)) {
257                 return mDisplayGroups.keyAt(i);
258             }
259         }
260 
261         return Display.INVALID_DISPLAY_GROUP;
262     }
263 
getDisplayGroupLocked(int groupId)264     public DisplayGroup getDisplayGroupLocked(int groupId) {
265         return mDisplayGroups.get(groupId);
266     }
267 
dumpLocked(PrintWriter pw)268     public void dumpLocked(PrintWriter pw) {
269         pw.println("LogicalDisplayMapper:");
270         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
271         ipw.increaseIndent();
272 
273         ipw.println("mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
274         ipw.println("mCurrentLayout=" + mCurrentLayout);
275         ipw.println("mDeviceStateOnWhichToWakeUp=" + mDeviceStateOnWhichToWakeUp);
276 
277         final int logicalDisplayCount = mLogicalDisplays.size();
278         ipw.println();
279         ipw.println("Logical Displays: size=" + logicalDisplayCount);
280         for (int i = 0; i < logicalDisplayCount; i++) {
281             int displayId = mLogicalDisplays.keyAt(i);
282             LogicalDisplay display = mLogicalDisplays.valueAt(i);
283             ipw.println("Display " + displayId + ":");
284             ipw.increaseIndent();
285             display.dumpLocked(ipw);
286             ipw.decreaseIndent();
287             ipw.println();
288         }
289         mDeviceStateToLayoutMap.dumpLocked(ipw);
290     }
291 
setDeviceStateLocked(int state)292     void setDeviceStateLocked(int state) {
293         final boolean isInteractive  = mPowerManager.isInteractive();
294         Slog.i(TAG, "Requesting Transition to state: " + state + ", from state=" + mDeviceState
295                 + ", interactive=" + isInteractive);
296         // As part of a state transition, we may need to turn off some displays temporarily so that
297         // the transition is smooth. Plus, on some devices, only one internal displays can be
298         // on at a time. We use DISPLAY_PHASE_LAYOUT_TRANSITION to mark a display that needs to be
299         // temporarily turned off.
300         if (mDeviceState != DeviceStateManager.INVALID_DEVICE_STATE) {
301             resetLayoutLocked(mDeviceState, state, LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION);
302         }
303         mPendingDeviceState = state;
304         final boolean wakeDevice = mPendingDeviceState == mDeviceStateOnWhichToWakeUp
305                 && !isInteractive;
306 
307         // If all displays are off already, we can just transition here, unless the device is asleep
308         // and we plan on waking it up. In that case, fall through to the call to wakeUp, and defer
309         // the final transition until later once the device is awake.
310         if (areAllTransitioningDisplaysOffLocked() && !wakeDevice) {
311             transitionToPendingStateLocked();
312             return;
313         }
314 
315         if (DEBUG) {
316             Slog.d(TAG, "Postponing transition to state: " + mPendingDeviceState);
317         }
318         // Send the transitioning phase updates to DisplayManager so that the displays can
319         // start turning OFF in preparation for the new layout.
320         updateLogicalDisplaysLocked();
321 
322         if (wakeDevice) {
323             // We already told the displays to turn off, now we need to wake the device as
324             // we transition to this new state. We do it here so that the waking happens between the
325             // transition from one layout to another.
326             mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_UNFOLD_DEVICE,
327                     "server.display:unfold");
328         }
329         mHandler.sendEmptyMessageDelayed(MSG_TRANSITION_TO_PENDING_DEVICE_STATE,
330                 TIMEOUT_STATE_TRANSITION_MILLIS);
331     }
332 
areAllTransitioningDisplaysOffLocked()333     private boolean areAllTransitioningDisplaysOffLocked() {
334         final int count = mLogicalDisplays.size();
335         for (int i = 0; i < count; i++) {
336             final LogicalDisplay display = mLogicalDisplays.valueAt(i);
337             if (display.getPhase() != LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) {
338                 continue;
339             }
340 
341             final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
342             if (device != null) {
343                 final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
344                 if (info.state != Display.STATE_OFF) {
345                     return false;
346                 }
347             }
348         }
349         return true;
350     }
351 
transitionToPendingStateLocked()352     private void transitionToPendingStateLocked() {
353         resetLayoutLocked(mDeviceState, mPendingDeviceState, LogicalDisplay.DISPLAY_PHASE_ENABLED);
354         mDeviceState = mPendingDeviceState;
355         mPendingDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
356         applyLayoutLocked();
357         updateLogicalDisplaysLocked();
358     }
359 
finishStateTransitionLocked(boolean force)360     private void finishStateTransitionLocked(boolean force) {
361         if (mPendingDeviceState == DeviceStateManager.INVALID_DEVICE_STATE) {
362             return;
363         }
364 
365         final boolean displaysOff = areAllTransitioningDisplaysOffLocked();
366         if (displaysOff || force) {
367             transitionToPendingStateLocked();
368             mHandler.removeMessages(MSG_TRANSITION_TO_PENDING_DEVICE_STATE);
369         } else if (DEBUG) {
370             Slog.d(TAG, "Not yet ready to transition to state=" + mPendingDeviceState
371                     + " with displays-off=" + displaysOff + " and force=" + force);
372         }
373     }
374 
handleDisplayDeviceAddedLocked(DisplayDevice device)375     private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
376         DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
377         // Internal Displays need to have additional initialization.
378         // This initializes a default dynamic display layout for INTERNAL
379         // devices, which is used as a fallback in case no static layout definitions
380         // exist or cannot be loaded.
381         if (deviceInfo.type == Display.TYPE_INTERNAL) {
382             initializeInternalDisplayDeviceLocked(device);
383         }
384 
385         // Create a logical display for the new display device
386         LogicalDisplay display = createNewLogicalDisplayLocked(
387                 device, Layout.assignDisplayIdLocked(false /*isDefault*/));
388 
389         applyLayoutLocked();
390         updateLogicalDisplaysLocked();
391     }
392 
393     /**
394      * Updates the rest of the display system once all the changes are applied for display
395      * devices and logical displays. The includes releasing invalid/empty LogicalDisplays,
396      * creating/adjusting/removing DisplayGroups, and notifying the rest of the system of the
397      * relevant changes.
398      */
updateLogicalDisplaysLocked()399     private void updateLogicalDisplaysLocked() {
400         // Go through all the displays and figure out if they need to be updated.
401         // Loops in reverse so that displays can be removed during the loop without affecting the
402         // rest of the loop.
403         for (int i = mLogicalDisplays.size() - 1; i >= 0; i--) {
404             final int displayId = mLogicalDisplays.keyAt(i);
405             LogicalDisplay display = mLogicalDisplays.valueAt(i);
406 
407             mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
408             display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
409 
410             display.updateLocked(mDisplayDeviceRepo);
411             final DisplayInfo newDisplayInfo = display.getDisplayInfoLocked();
412             final int updateState = mUpdatedLogicalDisplays.get(displayId, UPDATE_STATE_NEW);
413             final boolean wasPreviouslyUpdated = updateState != UPDATE_STATE_NEW;
414 
415             // The display is no longer valid and needs to be removed.
416             if (!display.isValidLocked()) {
417                 mUpdatedLogicalDisplays.delete(displayId);
418 
419                 // Remove from group
420                 final DisplayGroup displayGroup = getDisplayGroupLocked(
421                         getDisplayGroupIdFromDisplayIdLocked(displayId));
422                 if (displayGroup != null) {
423                     displayGroup.removeDisplayLocked(display);
424                 }
425 
426                 if (wasPreviouslyUpdated) {
427                     // The display isn't actually removed from our internal data structures until
428                     // after the notification is sent; see {@link #sendUpdatesForDisplaysLocked}.
429                     Slog.i(TAG, "Removing display: " + displayId);
430                     mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_REMOVED);
431                 } else {
432                     // This display never left this class, safe to remove without notification
433                     mLogicalDisplays.removeAt(i);
434                 }
435                 continue;
436 
437             // The display is new.
438             } else if (!wasPreviouslyUpdated) {
439                 Slog.i(TAG, "Adding new display: " + displayId + ": " + newDisplayInfo);
440                 assignDisplayGroupLocked(display);
441                 mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_ADDED);
442 
443             // Underlying displays device has changed to a different one.
444             } else if (!TextUtils.equals(mTempDisplayInfo.uniqueId, newDisplayInfo.uniqueId)) {
445                 // FLAG_OWN_DISPLAY_GROUP could have changed, recalculate just in case
446                 assignDisplayGroupLocked(display);
447                 mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_SWAPPED);
448 
449             // Something about the display device has changed.
450             } else if (!mTempDisplayInfo.equals(newDisplayInfo)) {
451                 // FLAG_OWN_DISPLAY_GROUP could have changed, recalculate just in case
452                 assignDisplayGroupLocked(display);
453                 mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
454 
455             // The display is involved in a display layout transition
456             } else if (updateState == UPDATE_STATE_TRANSITION) {
457                 mLogicalDisplaysToUpdate.put(displayId,
458                         LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION);
459 
460             // Display frame rate overrides changed.
461             } else if (!display.getPendingFrameRateOverrideUids().isEmpty()) {
462                 mLogicalDisplaysToUpdate.put(
463                         displayId, LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
464 
465             // Non-override display values changed.
466             } else {
467                 // While application shouldn't know nor care about the non-overridden info, we
468                 // still need to let WindowManager know so it can update its own internal state for
469                 // things like display cutouts.
470                 display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
471                 if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) {
472                     mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CHANGED);
473                 }
474             }
475 
476             mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_UPDATED);
477         }
478 
479         // Go through the groups and do the same thing. We do this after displays since group
480         // information can change in the previous loop.
481         // Loops in reverse so that groups can be removed during the loop without affecting the
482         // rest of the loop.
483         for (int i = mDisplayGroups.size() - 1; i >= 0; i--) {
484             final int groupId = mDisplayGroups.keyAt(i);
485             final DisplayGroup group = mDisplayGroups.valueAt(i);
486             final boolean wasPreviouslyUpdated = mUpdatedDisplayGroups.indexOfKey(groupId) > -1;
487             final int changeCount = group.getChangeCountLocked();
488 
489             if (group.isEmptyLocked()) {
490                 mUpdatedDisplayGroups.delete(groupId);
491                 if (wasPreviouslyUpdated) {
492                     mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_REMOVED);
493                 }
494                 continue;
495             } else if (!wasPreviouslyUpdated) {
496                 mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_ADDED);
497             } else if (mUpdatedDisplayGroups.get(groupId) != changeCount) {
498                 mDisplayGroupsToUpdate.put(groupId, DISPLAY_GROUP_EVENT_CHANGED);
499             }
500             mUpdatedDisplayGroups.put(groupId, changeCount);
501         }
502 
503         // Send the display and display group updates in order by message type. This is important
504         // to ensure that addition and removal notifications happen in the right order.
505         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION);
506         sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_ADDED);
507         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_REMOVED);
508         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_CHANGED);
509         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
510         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_SWAPPED);
511         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_ADDED);
512         sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_CHANGED);
513         sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_REMOVED);
514 
515         mLogicalDisplaysToUpdate.clear();
516         mDisplayGroupsToUpdate.clear();
517     }
518 
519     /**
520      * Send the specified message for all relevant displays in the specified display-to-message map.
521      */
sendUpdatesForDisplaysLocked(int msg)522     private void sendUpdatesForDisplaysLocked(int msg) {
523         for (int i = mLogicalDisplaysToUpdate.size() - 1; i >= 0; --i) {
524             final int currMsg = mLogicalDisplaysToUpdate.valueAt(i);
525             if (currMsg != msg) {
526                 continue;
527             }
528 
529             final int id = mLogicalDisplaysToUpdate.keyAt(i);
530             final LogicalDisplay display = getDisplayLocked(id);
531             if (DEBUG) {
532                 final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
533                 final String uniqueId = device == null ? "null" : device.getUniqueId();
534                 Slog.d(TAG, "Sending " + displayEventToString(msg) + " for display=" + id
535                         + " with device=" + uniqueId);
536             }
537             mListener.onLogicalDisplayEventLocked(display, msg);
538             if (msg == LOGICAL_DISPLAY_EVENT_REMOVED) {
539                 // We wait until we sent the EVENT_REMOVED event before actually removing the
540                 // display.
541                 mLogicalDisplays.delete(id);
542             }
543         }
544     }
545 
546     /**
547      * Send the specified message for all relevant display groups in the specified message map.
548      */
sendUpdatesForGroupsLocked(int msg)549     private void sendUpdatesForGroupsLocked(int msg) {
550         for (int i = mDisplayGroupsToUpdate.size() - 1; i >= 0; --i) {
551             final int currMsg = mDisplayGroupsToUpdate.valueAt(i);
552             if (currMsg != msg) {
553                 continue;
554             }
555 
556             final int id = mDisplayGroupsToUpdate.keyAt(i);
557             mListener.onDisplayGroupEventLocked(id, msg);
558             if (msg == DISPLAY_GROUP_EVENT_REMOVED) {
559                 // We wait until we sent the EVENT_REMOVED event before actually removing the
560                 // group.
561                 mDisplayGroups.delete(id);
562             }
563         }
564     }
565 
assignDisplayGroupLocked(LogicalDisplay display)566     private void assignDisplayGroupLocked(LogicalDisplay display) {
567         final int displayId = display.getDisplayIdLocked();
568 
569         // Get current display group data
570         int groupId = getDisplayGroupIdFromDisplayIdLocked(displayId);
571         final DisplayGroup oldGroup = getDisplayGroupLocked(groupId);
572 
573         // Get the new display group if a change is needed
574         final DisplayInfo info = display.getDisplayInfoLocked();
575         final boolean needsOwnDisplayGroup = (info.flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0;
576         final boolean hasOwnDisplayGroup = groupId != Display.DEFAULT_DISPLAY_GROUP;
577         if (groupId == Display.INVALID_DISPLAY_GROUP
578                 || hasOwnDisplayGroup != needsOwnDisplayGroup) {
579             groupId = assignDisplayGroupIdLocked(needsOwnDisplayGroup);
580         }
581 
582         // Create a new group if needed
583         DisplayGroup newGroup = getDisplayGroupLocked(groupId);
584         if (newGroup == null) {
585             newGroup = new DisplayGroup(groupId);
586             mDisplayGroups.append(groupId, newGroup);
587         }
588         if (oldGroup != newGroup) {
589             if (oldGroup != null) {
590                 oldGroup.removeDisplayLocked(display);
591             }
592             newGroup.addDisplayLocked(display);
593             display.updateDisplayGroupIdLocked(groupId);
594             Slog.i(TAG, "Setting new display group " + groupId + " for display "
595                     + displayId + ", from previous group: "
596                     + (oldGroup != null ? oldGroup.getGroupId() : "null"));
597         }
598     }
599 
600     /**
601      * Goes through all the displays used in the layouts for the specified {@code fromState} and
602      * {@code toState} and applies the specified {@code phase}. When a new layout is requested, we
603      * put the displays that will change into a transitional phase so that they can all be turned
604      * OFF. Once all are confirmed OFF, then this method gets called again to reset the phase to
605      * normal operation. This helps to ensure that all display-OFF requests are made before
606      * display-ON which in turn hides any resizing-jank windows might incur when switching displays.
607      *
608      * @param fromState The state we are switching from.
609      * @param toState The state we are switching to.
610      * @param phase The new phase to apply to the displays.
611      */
resetLayoutLocked(int fromState, int toState, @DisplayPhase int phase)612     private void resetLayoutLocked(int fromState, int toState, @DisplayPhase int phase) {
613         final Layout fromLayout = mDeviceStateToLayoutMap.get(fromState);
614         final Layout toLayout = mDeviceStateToLayoutMap.get(toState);
615 
616         final int count = mLogicalDisplays.size();
617         for (int i = 0; i < count; i++) {
618             final LogicalDisplay logicalDisplay = mLogicalDisplays.valueAt(i);
619             final int displayId = logicalDisplay.getDisplayIdLocked();
620             final DisplayDevice device = logicalDisplay.getPrimaryDisplayDeviceLocked();
621             if (device == null) {
622                 // If there's no device, then the logical display is due to be removed. Ignore it.
623                 continue;
624             }
625 
626             // Grab the display associations this display-device has in the old layout and the
627             // new layout.
628             final DisplayAddress address = device.getDisplayDeviceInfoLocked().address;
629 
630             // Virtual displays do not have addresses.
631             final Layout.Display fromDisplay =
632                     address != null ? fromLayout.getByAddress(address) : null;
633             final Layout.Display toDisplay =
634                     address != null ? toLayout.getByAddress(address) : null;
635 
636             // If a layout doesn't mention a display-device at all, then the display-device defaults
637             // to enabled. This is why we treat null as "enabled" in the code below.
638             final boolean wasEnabled = fromDisplay == null || fromDisplay.isEnabled();
639             final boolean willBeEnabled = toDisplay == null || toDisplay.isEnabled();
640 
641             final boolean deviceHasNewLogicalDisplayId = fromDisplay != null && toDisplay != null
642                     && fromDisplay.getLogicalDisplayId() != toDisplay.getLogicalDisplayId();
643 
644             // We consider a display-device as changing/transition if
645             // 1) It's already marked as transitioning
646             // 2) It's going from enabled to disabled, or vice versa
647             // 3) It's enabled, but it's mapped to a new logical display ID. To the user this
648             //    would look like apps moving from one screen to another since task-stacks stay
649             //    with the logical display [ID].
650             final boolean isTransitioning =
651                     (logicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION)
652                     || (wasEnabled != willBeEnabled)
653                     || deviceHasNewLogicalDisplayId;
654 
655             if (isTransitioning) {
656                 setDisplayPhase(logicalDisplay, phase);
657                 if (phase == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION) {
658                     mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_TRANSITION);
659                 }
660             }
661         }
662     }
663 
664     /**
665      * Apply (or reapply) the currently selected display layout.
666      */
applyLayoutLocked()667     private void applyLayoutLocked() {
668         final Layout oldLayout = mCurrentLayout;
669         mCurrentLayout = mDeviceStateToLayoutMap.get(mDeviceState);
670         Slog.i(TAG, "Applying layout: " + mCurrentLayout + ", Previous layout: " + oldLayout);
671 
672         // Go through each of the displays in the current layout set.
673         final int size = mCurrentLayout.size();
674         for (int i = 0; i < size; i++) {
675             final Layout.Display displayLayout = mCurrentLayout.getAt(i);
676 
677             // If the underlying display-device we want to use for this display
678             // doesn't exist, then skip it. This can happen at startup as display-devices
679             // trickle in one at a time. When the new display finally shows up, the layout is
680             // recalculated so that the display is properly added to the current layout.
681             final DisplayAddress address = displayLayout.getAddress();
682             final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(address);
683             if (device == null) {
684                 Slog.w(TAG, "The display device (" + address + "), is not available"
685                         + " for the display state " + mDeviceState);
686                 continue;
687             }
688 
689             // Now that we have a display-device, we need a LogicalDisplay to map it to. Find the
690             // right one, if it doesn't exist, create a new one.
691             final int logicalDisplayId = displayLayout.getLogicalDisplayId();
692             LogicalDisplay newDisplay = getDisplayLocked(logicalDisplayId);
693             if (newDisplay == null) {
694                 newDisplay = createNewLogicalDisplayLocked(
695                         null /*displayDevice*/, logicalDisplayId);
696             }
697 
698             // Now swap the underlying display devices between the old display and the new display
699             final LogicalDisplay oldDisplay = getDisplayLocked(device);
700             if (newDisplay != oldDisplay) {
701                 newDisplay.swapDisplaysLocked(oldDisplay);
702             }
703 
704             if (!displayLayout.isEnabled()) {
705                 setDisplayPhase(newDisplay, LogicalDisplay.DISPLAY_PHASE_DISABLED);
706             }
707         }
708 
709     }
710 
711 
712     /**
713      * Creates a new logical display for the specified device and display Id and adds it to the list
714      * of logical displays.
715      *
716      * @param device The device to associate with the LogicalDisplay.
717      * @param displayId The display ID to give the new display. If invalid, a new ID is assigned.
718      * @param isDefault Indicates if we are creating the default display.
719      * @return The new logical display if created, null otherwise.
720      */
createNewLogicalDisplayLocked(DisplayDevice device, int displayId)721     private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice device, int displayId) {
722         final int layerStack = assignLayerStackLocked(displayId);
723         final LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
724         display.updateLocked(mDisplayDeviceRepo);
725         mLogicalDisplays.put(displayId, display);
726         setDisplayPhase(display, LogicalDisplay.DISPLAY_PHASE_ENABLED);
727         return display;
728     }
729 
setDisplayPhase(LogicalDisplay display, @DisplayPhase int phase)730     private void setDisplayPhase(LogicalDisplay display, @DisplayPhase int phase) {
731         final int displayId = display.getDisplayIdLocked();
732         final DisplayInfo info = display.getDisplayInfoLocked();
733 
734         final boolean disallowSecondaryDisplay = mSingleDisplayDemoMode
735                 && (info.type != Display.TYPE_INTERNAL);
736         if (phase != LogicalDisplay.DISPLAY_PHASE_DISABLED && disallowSecondaryDisplay) {
737             Slog.i(TAG, "Not creating a logical display for a secondary display because single"
738                     + " display demo mode is enabled: " + display.getDisplayInfoLocked());
739             phase = LogicalDisplay.DISPLAY_PHASE_DISABLED;
740         }
741 
742         display.setPhase(phase);
743     }
744 
assignDisplayGroupIdLocked(boolean isOwnDisplayGroup)745     private int assignDisplayGroupIdLocked(boolean isOwnDisplayGroup) {
746         return isOwnDisplayGroup ? mNextNonDefaultGroupId++ : Display.DEFAULT_DISPLAY_GROUP;
747     }
748 
initializeInternalDisplayDeviceLocked(DisplayDevice device)749     private void initializeInternalDisplayDeviceLocked(DisplayDevice device) {
750         // We always want to make sure that our default layout creates a logical
751         // display for every internal display device that is found.
752         // To that end, when we are notified of a new internal display, we add it to
753         // the default layout definition if it is not already there.
754         final Layout layout = mDeviceStateToLayoutMap.get(DeviceStateToLayoutMap.STATE_DEFAULT);
755         final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
756         final boolean isDefault = (info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
757         final boolean isEnabled = isDefault || mSupportsConcurrentInternalDisplays;
758         layout.createDisplayLocked(info.address, isDefault, isEnabled);
759     }
760 
assignLayerStackLocked(int displayId)761     private int assignLayerStackLocked(int displayId) {
762         // Currently layer stacks and display ids are the same.
763         // This need not be the case.
764         return displayId;
765     }
766 
displayEventToString(int msg)767     private String displayEventToString(int msg) {
768         switch(msg) {
769             case LOGICAL_DISPLAY_EVENT_ADDED:
770                 return "added";
771             case LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION:
772                 return "transition";
773             case LOGICAL_DISPLAY_EVENT_CHANGED:
774                 return "changed";
775             case LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED:
776                 return "framerate_override";
777             case LOGICAL_DISPLAY_EVENT_SWAPPED:
778                 return "swapped";
779             case LOGICAL_DISPLAY_EVENT_REMOVED:
780                 return "removed";
781         }
782         return null;
783     }
784 
785     public interface Listener {
onLogicalDisplayEventLocked(LogicalDisplay display, int event)786         void onLogicalDisplayEventLocked(LogicalDisplay display, int event);
onDisplayGroupEventLocked(int groupId, int event)787         void onDisplayGroupEventLocked(int groupId, int event);
onTraversalRequested()788         void onTraversalRequested();
789     }
790 
791     private class LogicalDisplayMapperHandler extends Handler {
LogicalDisplayMapperHandler(Looper looper)792         LogicalDisplayMapperHandler(Looper looper) {
793             super(looper, null, true /*async*/);
794         }
795 
796         @Override
handleMessage(Message msg)797         public void handleMessage(Message msg) {
798             switch (msg.what) {
799                 case MSG_TRANSITION_TO_PENDING_DEVICE_STATE:
800                     synchronized (mSyncRoot) {
801                         finishStateTransitionLocked(true /*force*/);
802                     }
803                     break;
804             }
805         }
806     }
807 
808 }
809