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