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