• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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