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