1 /* 2 * Copyright (C) 2023 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.wm; 18 19 import static android.view.WindowManager.TRANSIT_CHANGE; 20 21 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS_MIN; 22 import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY; 23 import static com.android.server.wm.utils.DisplayInfoOverrides.WM_OVERRIDE_FIELDS; 24 import static com.android.server.wm.utils.DisplayInfoOverrides.copyDisplayInfoFields; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.graphics.Rect; 29 import android.os.Message; 30 import android.os.Trace; 31 import android.util.Slog; 32 import android.view.DisplayInfo; 33 import android.window.DisplayAreaInfo; 34 import android.window.TransitionRequestInfo; 35 import android.window.WindowContainerTransaction; 36 37 import com.android.internal.annotations.VisibleForTesting; 38 import com.android.internal.display.BrightnessSynchronizer; 39 import com.android.internal.protolog.ProtoLog; 40 import com.android.server.wm.utils.DisplayInfoOverrides.DisplayInfoFieldsUpdater; 41 42 import java.util.Arrays; 43 import java.util.Objects; 44 45 /** 46 * A DisplayUpdater that could defer and queue display updates coming from DisplayManager to 47 * WindowManager. It allows to defer pending display updates if WindowManager is currently not 48 * ready to apply them. 49 * For example, this might happen if there is a Shell transition running and physical display 50 * changed. We can't immediately apply the display updates because we want to start a separate 51 * display change transition. In this case, we will queue all display updates until the current 52 * transition's collection finishes and then apply them afterwards. 53 */ 54 class DeferredDisplayUpdater { 55 56 /** 57 * List of fields that could be deferred before applying to DisplayContent. 58 * This should be kept in sync with {@link DeferredDisplayUpdater#calculateDisplayInfoDiff} 59 */ 60 @VisibleForTesting 61 static final DisplayInfoFieldsUpdater DEFERRABLE_FIELDS = (out, override) -> { 62 // Treat unique id, address, and canHostTasks change as WM-specific display change as we 63 // re-query display settings and parameters based on it which could cause window changes. 64 out.uniqueId = override.uniqueId; 65 out.address = override.address; 66 out.canHostTasks = override.canHostTasks; 67 68 // Also apply WM-override fields, since they might produce differences in window hierarchy 69 WM_OVERRIDE_FIELDS.setFields(out, override); 70 }; 71 72 private static final String TAG = "DeferredDisplayUpdater"; 73 74 private static final String TRACE_TAG_WAIT_FOR_TRANSITION = 75 "Screen unblock: wait for transition"; 76 private static final int WAIT_FOR_TRANSITION_TIMEOUT = 1000; 77 78 private final DisplayContent mDisplayContent; 79 80 @NonNull 81 private final DisplayInfo mNonOverrideDisplayInfo = new DisplayInfo(); 82 83 /** 84 * The last known display parameters from DisplayManager, some WM-specific fields in this object 85 * might not be applied to the DisplayContent yet 86 */ 87 @Nullable 88 private DisplayInfo mLastDisplayInfo; 89 90 /** 91 * The last DisplayInfo that was applied to DisplayContent, only WM-specific parameters must be 92 * used from this object. This object is used to store old values of DisplayInfo while these 93 * fields are pending to be applied to DisplayContent. 94 */ 95 @Nullable 96 private DisplayInfo mLastWmDisplayInfo; 97 98 @NonNull 99 private final DisplayInfo mOutputDisplayInfo = new DisplayInfo(); 100 101 /** Whether {@link #mScreenUnblocker} should wait for transition to be ready. */ 102 private boolean mShouldWaitForTransitionWhenScreenOn; 103 104 /** The message to notify PhoneWindowManager#finishWindowsDrawn. */ 105 @Nullable 106 private Message mScreenUnblocker; 107 108 private final Runnable mScreenUnblockTimeoutRunnable = () -> { 109 Slog.e(TAG, "Timeout waiting for the display switch transition to start"); 110 continueScreenUnblocking(); 111 }; 112 DeferredDisplayUpdater(@onNull DisplayContent displayContent)113 DeferredDisplayUpdater(@NonNull DisplayContent displayContent) { 114 mDisplayContent = displayContent; 115 mNonOverrideDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo()); 116 } 117 118 /** 119 * Reads the latest display parameters from the display manager and returns them in a callback. 120 * If there are pending display updates, it will wait for them to finish first and only then it 121 * will call the callback with the latest display parameters. 122 * 123 * @param finishCallback is called when all pending display updates are finished 124 */ updateDisplayInfo(@onNull Runnable finishCallback)125 void updateDisplayInfo(@NonNull Runnable finishCallback) { 126 // Get the latest display parameters from the DisplayManager 127 final DisplayInfo displayInfo = getCurrentDisplayInfo(); 128 129 final int displayInfoDiff = calculateDisplayInfoDiff(mLastDisplayInfo, displayInfo); 130 final boolean physicalDisplayUpdated = isPhysicalDisplayUpdated(mLastDisplayInfo, 131 displayInfo); 132 133 mLastDisplayInfo = displayInfo; 134 135 // Apply whole display info immediately as is if either: 136 // * it is the first display update 137 // * the display doesn't have visible content 138 // * shell transitions are disabled or temporary unavailable 139 if (displayInfoDiff == DIFF_EVERYTHING 140 || !mDisplayContent.getLastHasContent() 141 || !mDisplayContent.mTransitionController.isShellTransitionsEnabled()) { 142 ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS_MIN, 143 "DeferredDisplayUpdater: applying DisplayInfo(%d x %d) immediately", 144 displayInfo.logicalWidth, displayInfo.logicalHeight); 145 146 mLastWmDisplayInfo = displayInfo; 147 applyLatestDisplayInfo(); 148 finishCallback.run(); 149 return; 150 } 151 152 // If there are non WM-specific display info changes, apply only these fields immediately 153 if ((displayInfoDiff & DIFF_NOT_WM_DEFERRABLE) > 0) { 154 ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS_MIN, 155 "DeferredDisplayUpdater: partially applying DisplayInfo(%d x %d) immediately", 156 displayInfo.logicalWidth, displayInfo.logicalHeight); 157 applyLatestDisplayInfo(); 158 } 159 160 // If there are WM-specific display info changes, apply them through a Shell transition 161 if ((displayInfoDiff & DIFF_WM_DEFERRABLE) > 0) { 162 ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS_MIN, 163 "DeferredDisplayUpdater: deferring DisplayInfo(%d x %d) update", 164 displayInfo.logicalWidth, displayInfo.logicalHeight); 165 166 requestDisplayChangeTransition(physicalDisplayUpdated, () -> { 167 ProtoLog.d(WM_DEBUG_WINDOW_TRANSITIONS_MIN, 168 "DeferredDisplayUpdater: applying DisplayInfo(%d x %d) after deferring", 169 displayInfo.logicalWidth, displayInfo.logicalHeight); 170 171 // Apply deferrable fields to DisplayContent only when the transition 172 // starts collecting, non-deferrable fields are ignored in mLastWmDisplayInfo 173 mLastWmDisplayInfo = displayInfo; 174 applyLatestDisplayInfo(); 175 finishCallback.run(); 176 }); 177 } else { 178 // There are no WM-specific updates, so we can immediately notify that all display 179 // info changes are applied 180 finishCallback.run(); 181 } 182 } 183 184 /** 185 * Requests a display change Shell transition 186 * 187 * @param physicalDisplayUpdated if true also starts remote display change 188 * @param onStartCollect called when the Shell transition starts collecting 189 */ requestDisplayChangeTransition(boolean physicalDisplayUpdated, @NonNull Runnable onStartCollect)190 private void requestDisplayChangeTransition(boolean physicalDisplayUpdated, 191 @NonNull Runnable onStartCollect) { 192 193 final Transition transition = new Transition(TRANSIT_CHANGE, /* flags= */ 0, 194 mDisplayContent.mTransitionController, 195 mDisplayContent.mTransitionController.mSyncEngine); 196 197 mDisplayContent.mAtmService.startPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY); 198 199 mDisplayContent.mTransitionController.startCollectOrQueue(transition, deferred -> { 200 final Rect startBounds = new Rect(0, 0, mDisplayContent.mInitialDisplayWidth, 201 mDisplayContent.mInitialDisplayHeight); 202 final int fromRotation = mDisplayContent.getRotation(); 203 if (physicalDisplayUpdated) { 204 final WindowState notificationShade = 205 mDisplayContent.getDisplayPolicy().getNotificationShade(); 206 if (notificationShade != null && notificationShade.isVisible() 207 && mDisplayContent.mAtmService.mKeyguardController.isKeyguardOrAodShowing( 208 mDisplayContent.mDisplayId)) { 209 Slog.i(TAG, notificationShade + " uses blast for display switch"); 210 notificationShade.mSyncMethodOverride = BLASTSyncEngine.METHOD_BLAST; 211 } 212 } 213 214 mDisplayContent.mAtmService.deferWindowLayout(); 215 try { 216 onStartCollect.run(); 217 218 if (physicalDisplayUpdated) { 219 onDisplayUpdated(transition, fromRotation, startBounds); 220 } else { 221 final TransitionRequestInfo.DisplayChange displayChange = 222 getCurrentDisplayChange(fromRotation, startBounds); 223 mDisplayContent.mTransitionController.requestStartTransition(transition, 224 /* startTask= */ null, /* remoteTransition= */ null, displayChange); 225 } 226 } finally { 227 // Run surface placement after requestStartTransition, so shell side can receive 228 // the transition request before handling task info changes. 229 mDisplayContent.mAtmService.continueWindowLayout(); 230 } 231 }); 232 } 233 234 /** 235 * Applies current DisplayInfo to DisplayContent, DisplayContent is merged from two parts: 236 * - non-deferrable fields are set from the most recent values received from DisplayManager 237 * (uses {@link mLastDisplayInfo} field) 238 * - deferrable fields are set from the latest values that we could apply to WM 239 * (uses {@link mLastWmDisplayInfo} field) 240 */ applyLatestDisplayInfo()241 private void applyLatestDisplayInfo() { 242 copyDisplayInfoFields(mOutputDisplayInfo, /* base= */ mLastDisplayInfo, 243 /* override= */ mLastWmDisplayInfo, /* fields= */ DEFERRABLE_FIELDS); 244 mDisplayContent.onDisplayInfoUpdated(mOutputDisplayInfo); 245 } 246 247 @NonNull getCurrentDisplayInfo()248 private DisplayInfo getCurrentDisplayInfo() { 249 mDisplayContent.mWmService.mDisplayManagerInternal.getNonOverrideDisplayInfo( 250 mDisplayContent.mDisplayId, mNonOverrideDisplayInfo); 251 return new DisplayInfo(mNonOverrideDisplayInfo); 252 } 253 254 @NonNull getCurrentDisplayChange(int fromRotation, @NonNull Rect startBounds)255 private TransitionRequestInfo.DisplayChange getCurrentDisplayChange(int fromRotation, 256 @NonNull Rect startBounds) { 257 final Rect endBounds = new Rect(0, 0, mDisplayContent.mInitialDisplayWidth, 258 mDisplayContent.mInitialDisplayHeight); 259 final int toRotation = mDisplayContent.getRotation(); 260 261 final TransitionRequestInfo.DisplayChange displayChange = 262 new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId()); 263 displayChange.setStartAbsBounds(startBounds); 264 displayChange.setEndAbsBounds(endBounds); 265 displayChange.setStartRotation(fromRotation); 266 displayChange.setEndRotation(toRotation); 267 return displayChange; 268 } 269 270 /** 271 * Called when physical display is updated, this could happen e.g. on foldable 272 * devices when the physical underlying display is replaced. This method should be called 273 * when the new display info is already applied to the WM hierarchy. 274 * 275 * @param fromRotation rotation before the display change 276 * @param startBounds display bounds before the display change 277 */ onDisplayUpdated(@onNull Transition transition, int fromRotation, @NonNull Rect startBounds)278 private void onDisplayUpdated(@NonNull Transition transition, int fromRotation, 279 @NonNull Rect startBounds) { 280 final int toRotation = mDisplayContent.getRotation(); 281 282 final TransitionRequestInfo.DisplayChange displayChange = 283 getCurrentDisplayChange(fromRotation, startBounds); 284 displayChange.setPhysicalDisplayChanged(true); 285 286 transition.addTransactionCompletedListener(this::continueScreenUnblocking); 287 mDisplayContent.mTransitionController.requestStartTransition(transition, 288 /* startTask= */ null, /* remoteTransition= */ null, displayChange); 289 290 final DisplayAreaInfo newDisplayAreaInfo = mDisplayContent.getDisplayAreaInfo(); 291 292 final boolean startedRemoteChange = mDisplayContent.mRemoteDisplayChangeController 293 .performRemoteDisplayChange(fromRotation, toRotation, newDisplayAreaInfo, 294 transaction -> finishDisplayUpdate(transaction, transition)); 295 296 if (!startedRemoteChange) { 297 finishDisplayUpdate(/* wct= */ null, transition); 298 } 299 } 300 finishDisplayUpdate(@ullable WindowContainerTransaction wct, @NonNull Transition transition)301 private void finishDisplayUpdate(@Nullable WindowContainerTransaction wct, 302 @NonNull Transition transition) { 303 if (wct != null) { 304 mDisplayContent.mAtmService.mWindowOrganizerController.applyTransaction( 305 wct); 306 } 307 transition.setAllReady(); 308 } 309 isPhysicalDisplayUpdated(@ullable DisplayInfo first, @Nullable DisplayInfo second)310 private boolean isPhysicalDisplayUpdated(@Nullable DisplayInfo first, 311 @Nullable DisplayInfo second) { 312 if (first == null || second == null) return true; 313 return !Objects.equals(first.uniqueId, second.uniqueId); 314 } 315 316 /** 317 * Called after physical display has changed and after DisplayContent applied new display 318 * properties. 319 */ onDisplayContentDisplayPropertiesPostChanged()320 void onDisplayContentDisplayPropertiesPostChanged() { 321 // Unblock immediately in case there is no transition. This is unlikely to happen. 322 if (mScreenUnblocker != null && !mDisplayContent.mTransitionController.inTransition()) { 323 mScreenUnblocker.sendToTarget(); 324 mScreenUnblocker = null; 325 } 326 } 327 328 /** 329 * Called with {@code true} when physical display is going to switch. And {@code false} when 330 * the display is turned on or the device goes to sleep. 331 */ onDisplaySwitching(boolean switching)332 void onDisplaySwitching(boolean switching) { 333 mShouldWaitForTransitionWhenScreenOn = switching; 334 } 335 336 /** Returns {@code true} if the transition will control when to turn on the screen. */ waitForTransition(@onNull Message screenUnblocker)337 boolean waitForTransition(@NonNull Message screenUnblocker) { 338 if (!mShouldWaitForTransitionWhenScreenOn) { 339 return false; 340 } 341 mScreenUnblocker = screenUnblocker; 342 if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) { 343 Trace.beginAsyncSection(TRACE_TAG_WAIT_FOR_TRANSITION, screenUnblocker.hashCode()); 344 } 345 346 mDisplayContent.mWmService.mH.removeCallbacks(mScreenUnblockTimeoutRunnable); 347 mDisplayContent.mWmService.mH.postDelayed(mScreenUnblockTimeoutRunnable, 348 WAIT_FOR_TRANSITION_TIMEOUT); 349 return true; 350 } 351 352 /** 353 * Continues the screen unblocking flow, could be called either on a binder thread as 354 * a result of surface transaction completed listener or from {@link WindowManagerService#mH} 355 * handler in case of timeout 356 */ continueScreenUnblocking()357 private void continueScreenUnblocking() { 358 synchronized (mDisplayContent.mWmService.mGlobalLock) { 359 mShouldWaitForTransitionWhenScreenOn = false; 360 mDisplayContent.mWmService.mH.removeCallbacks(mScreenUnblockTimeoutRunnable); 361 if (mScreenUnblocker == null) { 362 return; 363 } 364 mScreenUnblocker.sendToTarget(); 365 if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) { 366 Trace.endAsyncSection(TRACE_TAG_WAIT_FOR_TRANSITION, mScreenUnblocker.hashCode()); 367 } 368 mScreenUnblocker = null; 369 } 370 } 371 372 /** 373 * Diff result: fields are the same 374 */ 375 static final int DIFF_NONE = 0; 376 377 /** 378 * Diff result: fields that could be deferred in WM are different 379 */ 380 static final int DIFF_WM_DEFERRABLE = 1 << 0; 381 382 /** 383 * Diff result: fields that could not be deferred in WM are different 384 */ 385 static final int DIFF_NOT_WM_DEFERRABLE = 1 << 1; 386 387 /** 388 * Diff result: everything is different 389 */ 390 static final int DIFF_EVERYTHING = 0XFFFFFFFF; 391 392 @VisibleForTesting calculateDisplayInfoDiff(@ullable DisplayInfo first, @Nullable DisplayInfo second)393 static int calculateDisplayInfoDiff(@Nullable DisplayInfo first, @Nullable DisplayInfo second) { 394 int diff = DIFF_NONE; 395 396 if (Objects.equals(first, second)) return diff; 397 if (first == null || second == null) return DIFF_EVERYTHING; 398 399 if (first.layerStack != second.layerStack 400 || first.flags != second.flags 401 || first.type != second.type 402 || first.displayId != second.displayId 403 || first.displayGroupId != second.displayGroupId 404 || !Objects.equals(first.deviceProductInfo, second.deviceProductInfo) 405 || first.modeId != second.modeId 406 || first.renderFrameRate != second.renderFrameRate 407 || first.hasArrSupport != second.hasArrSupport 408 || !Objects.equals(first.frameRateCategoryRate, second.frameRateCategoryRate) 409 || !Arrays.equals(first.supportedRefreshRates, second.supportedRefreshRates) 410 || first.defaultModeId != second.defaultModeId 411 || first.userPreferredModeId != second.userPreferredModeId 412 || !Arrays.equals(first.supportedModes, second.supportedModes) 413 || !Arrays.equals(first.appsSupportedModes, second.appsSupportedModes) 414 || first.colorMode != second.colorMode 415 || !Arrays.equals(first.supportedColorModes, second.supportedColorModes) 416 || !Objects.equals(first.hdrCapabilities, second.hdrCapabilities) 417 || !Arrays.equals(first.userDisabledHdrTypes, second.userDisabledHdrTypes) 418 || first.minimalPostProcessingSupported != second.minimalPostProcessingSupported 419 || first.appVsyncOffsetNanos != second.appVsyncOffsetNanos 420 || first.presentationDeadlineNanos != second.presentationDeadlineNanos 421 || first.state != second.state 422 || first.committedState != second.committedState 423 || first.ownerUid != second.ownerUid 424 || !Objects.equals(first.ownerPackageName, second.ownerPackageName) 425 || first.removeMode != second.removeMode 426 || first.getRefreshRate() != second.getRefreshRate() 427 || first.brightnessMinimum != second.brightnessMinimum 428 || first.brightnessMaximum != second.brightnessMaximum 429 || first.brightnessDefault != second.brightnessDefault 430 || first.brightnessDim != second.brightnessDim 431 || first.installOrientation != second.installOrientation 432 || first.isForceSdr != second.isForceSdr 433 || !Objects.equals(first.layoutLimitedRefreshRate, second.layoutLimitedRefreshRate) 434 || !BrightnessSynchronizer.floatEquals(first.hdrSdrRatio, second.hdrSdrRatio) 435 || !first.thermalRefreshRateThrottling.contentEquals( 436 second.thermalRefreshRateThrottling) 437 || !Objects.equals(first.thermalBrightnessThrottlingDataId, 438 second.thermalBrightnessThrottlingDataId) 439 ) { 440 diff |= DIFF_NOT_WM_DEFERRABLE; 441 } 442 443 if (first.appWidth != second.appWidth 444 || first.appHeight != second.appHeight 445 || first.smallestNominalAppWidth != second.smallestNominalAppWidth 446 || first.smallestNominalAppHeight != second.smallestNominalAppHeight 447 || first.largestNominalAppWidth != second.largestNominalAppWidth 448 || first.largestNominalAppHeight != second.largestNominalAppHeight 449 || first.logicalWidth != second.logicalWidth 450 || first.logicalHeight != second.logicalHeight 451 || first.physicalXDpi != second.physicalXDpi 452 || first.physicalYDpi != second.physicalYDpi 453 || first.rotation != second.rotation 454 || !Objects.equals(first.displayCutout, second.displayCutout) 455 || first.logicalDensityDpi != second.logicalDensityDpi 456 || !Objects.equals(first.roundedCorners, second.roundedCorners) 457 || !Objects.equals(first.displayShape, second.displayShape) 458 || !Objects.equals(first.uniqueId, second.uniqueId) 459 || !Objects.equals(first.address, second.address) 460 || first.canHostTasks != second.canHostTasks 461 ) { 462 diff |= DIFF_WM_DEFERRABLE; 463 } 464 465 return diff; 466 } 467 } 468