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