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