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