1 /*
2  * Copyright (C) 2011 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.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
20 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
21 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
22 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
23 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
24 
25 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
26 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
27 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
28 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
29 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
30 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
31 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
32 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
33 import static com.android.server.wm.WindowContainerChildProto.WINDOW_TOKEN;
34 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
35 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
36 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
37 import static com.android.server.wm.WindowTokenProto.HASH_CODE;
38 import static com.android.server.wm.WindowTokenProto.PAUSED;
39 import static com.android.server.wm.WindowTokenProto.WAITING_TO_SHOW;
40 import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER;
41 
42 import android.annotation.CallSuper;
43 import android.annotation.Nullable;
44 import android.app.servertransaction.FixedRotationAdjustmentsItem;
45 import android.content.res.Configuration;
46 import android.graphics.Rect;
47 import android.os.Bundle;
48 import android.os.Debug;
49 import android.os.IBinder;
50 import android.os.RemoteException;
51 import android.util.Slog;
52 import android.util.SparseArray;
53 import android.util.proto.ProtoOutputStream;
54 import android.view.DisplayAdjustments.FixedRotationAdjustments;
55 import android.view.DisplayInfo;
56 import android.view.InsetsState;
57 import android.view.SurfaceControl;
58 import android.view.WindowManager;
59 import android.view.WindowManager.LayoutParams.WindowType;
60 import android.window.WindowContext;
61 
62 import com.android.internal.protolog.common.ProtoLog;
63 import com.android.server.policy.WindowManagerPolicy;
64 
65 import java.io.PrintWriter;
66 import java.util.ArrayList;
67 import java.util.Comparator;
68 
69 /**
70  * Container of a set of related windows in the window manager. Often this is an AppWindowToken,
71  * which is the handle for an Activity that it uses to display windows. For nested windows, there is
72  * a WindowToken created for the parent window to manage its children.
73  */
74 class WindowToken extends WindowContainer<WindowState> {
75     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowToken" : TAG_WM;
76 
77     /** The actual token */
78     final IBinder token;
79 
80     /** The type of window this token is for, as per {@link WindowManager.LayoutParams} */
81     final int windowType;
82 
83     /**
84      * Options that will be used to determine which {@link RootDisplayArea} this window should be
85      * attached to.
86      */
87     @Nullable
88     final Bundle mOptions;
89 
90     /** {@code true} if this holds the rounded corner overlay */
91     final boolean mRoundedCornerOverlay;
92 
93     /**
94      * Set if this token was explicitly added by a client, so should persist (not be removed)
95      * when all windows are removed.
96      */
97     boolean mPersistOnEmpty;
98 
99     // For printing.
100     String stringName;
101 
102     // Is key dispatching paused for this token?
103     boolean paused = false;
104 
105     // Temporary for finding which tokens no longer have visible windows.
106     boolean hasVisible;
107 
108     // Set to true when this token is in a pending transaction where it
109     // will be shown.
110     boolean waitingToShow;
111 
112     /** The owner has {@link android.Manifest.permission#MANAGE_APP_TOKENS} */
113     final boolean mOwnerCanManageAppTokens;
114 
115     private FixedRotationTransformState mFixedRotationTransformState;
116 
117     /**
118      * When set to {@code true}, this window token is created from {@link WindowContext}
119      */
120     private final boolean mFromClientToken;
121 
122     /** Have we told the window clients to show themselves? */
123     private boolean mClientVisible;
124 
125     /**
126      * Used to fix the transform of the token to be rotated to a rotation different than it's
127      * display. The window frames and surfaces corresponding to this token will be layouted and
128      * rotated by the given rotated display info, frames and insets.
129      */
130     private static class FixedRotationTransformState {
131         final DisplayInfo mDisplayInfo;
132         final DisplayFrames mDisplayFrames;
133         final Configuration mRotatedOverrideConfiguration;
134         final SeamlessRotator mRotator;
135         /**
136          * The tokens that share the same transform. Their end time of transform are the same. The
137          * list should at least contain the token who creates this state.
138          */
139         final ArrayList<WindowToken> mAssociatedTokens = new ArrayList<>(3);
140         final ArrayList<WindowContainer<?>> mRotatedContainers = new ArrayList<>(3);
141         final SparseArray<Rect> mBarContentFrames = new SparseArray<>();
142         boolean mIsTransforming = true;
143 
FixedRotationTransformState(DisplayInfo rotatedDisplayInfo, DisplayFrames rotatedDisplayFrames, Configuration rotatedConfig, int currentRotation)144         FixedRotationTransformState(DisplayInfo rotatedDisplayInfo,
145                 DisplayFrames rotatedDisplayFrames, Configuration rotatedConfig,
146                 int currentRotation) {
147             mDisplayInfo = rotatedDisplayInfo;
148             mDisplayFrames = rotatedDisplayFrames;
149             mRotatedOverrideConfiguration = rotatedConfig;
150             // This will use unrotate as rotate, so the new and old rotation are inverted.
151             mRotator = new SeamlessRotator(rotatedDisplayInfo.rotation, currentRotation,
152                     rotatedDisplayInfo, true /* applyFixedTransformationHint */);
153         }
154 
155         /**
156          * Transforms the window container from the next rotation to the current rotation for
157          * showing the window in a display with different rotation.
158          */
transform(WindowContainer<?> container)159         void transform(WindowContainer<?> container) {
160             mRotator.unrotate(container.getPendingTransaction(), container);
161             if (!mRotatedContainers.contains(container)) {
162                 mRotatedContainers.add(container);
163             }
164         }
165 
166         /**
167          * Resets the transformation of the window containers which have been rotated. This should
168          * be called when the window has the same rotation as display.
169          */
resetTransform()170         void resetTransform() {
171             for (int i = mRotatedContainers.size() - 1; i >= 0; i--) {
172                 final WindowContainer<?> c = mRotatedContainers.get(i);
173                 // If the window is detached (no parent), its surface may have been released.
174                 if (c.getParent() != null) {
175                     mRotator.finish(c.getPendingTransaction(), c);
176                 }
177             }
178         }
179 
180         /** The state may not only be used by self. Make sure to leave the influence by others. */
disassociate(WindowToken token)181         void disassociate(WindowToken token) {
182             mAssociatedTokens.remove(token);
183             mRotatedContainers.remove(token);
184         }
185     }
186 
187     /**
188      * Compares two child window of this token and returns -1 if the first is lesser than the
189      * second in terms of z-order and 1 otherwise.
190      */
191     private final Comparator<WindowState> mWindowComparator =
192             (WindowState newWindow, WindowState existingWindow) -> {
193         final WindowToken token = WindowToken.this;
194         if (newWindow.mToken != token) {
195             throw new IllegalArgumentException("newWindow=" + newWindow
196                     + " is not a child of token=" + token);
197         }
198 
199         if (existingWindow.mToken != token) {
200             throw new IllegalArgumentException("existingWindow=" + existingWindow
201                     + " is not a child of token=" + token);
202         }
203 
204         return isFirstChildWindowGreaterThanSecond(newWindow, existingWindow) ? 1 : -1;
205     };
206 
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens)207     protected WindowToken(WindowManagerService service, IBinder _token, int type,
208             boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens) {
209         this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
210                 false /* roundedCornerOverlay */, false /* fromClientToken */, null /* options */);
211     }
212 
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options)213     protected WindowToken(WindowManagerService service, IBinder _token, int type,
214             boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens,
215             boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {
216         super(service);
217         token = _token;
218         windowType = type;
219         mOptions = options;
220         mPersistOnEmpty = persistOnEmpty;
221         mOwnerCanManageAppTokens = ownerCanManageAppTokens;
222         mRoundedCornerOverlay = roundedCornerOverlay;
223         mFromClientToken = fromClientToken;
224         if (dc != null) {
225             dc.addWindowToken(token, this);
226         }
227     }
228 
removeAllWindowsIfPossible()229     void removeAllWindowsIfPossible() {
230         for (int i = mChildren.size() - 1; i >= 0; --i) {
231             final WindowState win = mChildren.get(i);
232             ProtoLog.w(WM_DEBUG_WINDOW_MOVEMENT,
233                     "removeAllWindowsIfPossible: removing win=%s", win);
234             win.removeIfPossible();
235             if (i > mChildren.size()) {
236                 // It's possible for removeIfPossible to delete siblings (for example if it is a
237                 // starting window, it will perform operations on the ActivityRecord).
238                 i = mChildren.size();
239             }
240         }
241     }
242 
setExiting(boolean animateExit)243     void setExiting(boolean animateExit) {
244         if (isEmpty()) {
245             super.removeImmediately();
246             return;
247         }
248 
249         // This token is exiting, so allow it to be removed when it no longer contains any windows.
250         mPersistOnEmpty = false;
251 
252         if (!isVisible()) {
253             return;
254         }
255 
256         final int count = mChildren.size();
257         boolean changed = false;
258         final boolean delayed = isAnimating(TRANSITION | PARENTS)
259                 || (isAnimating(CHILDREN, ANIMATION_TYPE_WINDOW_ANIMATION) && animateExit);
260 
261         for (int i = 0; i < count; i++) {
262             final WindowState win = mChildren.get(i);
263             changed |= win.onSetAppExiting(animateExit);
264         }
265 
266         final ActivityRecord app = asActivityRecord();
267         if (app != null) {
268             app.setVisible(false);
269         }
270 
271         if (changed) {
272             mWmService.mWindowPlacerLocked.performSurfacePlacement();
273             mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /*updateInputWindows*/);
274         }
275 
276         if (delayed) {
277             mDisplayContent.mExitingTokens.add(this);
278         }
279     }
280 
281     /**
282      * @return The scale for applications running in compatibility mode. Multiply the size in the
283      *         application by this scale will be the size in the screen.
284      */
getSizeCompatScale()285     float getSizeCompatScale() {
286         return mDisplayContent.mCompatibleScreenScale;
287     }
288 
289     /**
290      * @return {@code true} if this window token has bounds for size compatibility mode.
291      */
hasSizeCompatBounds()292     boolean hasSizeCompatBounds() {
293         return false;
294     }
295 
296     /**
297      * Returns true if the new window is considered greater than the existing window in terms of
298      * z-order.
299      */
isFirstChildWindowGreaterThanSecond(WindowState newWindow, WindowState existingWindow)300     protected boolean isFirstChildWindowGreaterThanSecond(WindowState newWindow,
301             WindowState existingWindow) {
302         // New window is considered greater if it has a higher or equal base layer.
303         return newWindow.mBaseLayer >= existingWindow.mBaseLayer;
304     }
305 
addWindow(final WindowState win)306     void addWindow(final WindowState win) {
307         ProtoLog.d(WM_DEBUG_FOCUS,
308                 "addWindow: win=%s Callers=%s", win, Debug.getCallers(5));
309 
310         if (win.isChildWindow()) {
311             // Child windows are added to their parent windows.
312             return;
313         }
314         // This token is created from WindowContext and the client requests to addView now, create a
315         // surface for this token.
316         if (mSurfaceControl == null) {
317             createSurfaceControl(true /* force */);
318         }
319         if (!mChildren.contains(win)) {
320             ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", win, this);
321             addChild(win, mWindowComparator);
322             mWmService.mWindowsChanged = true;
323             // TODO: Should we also be setting layout needed here and other places?
324         }
325     }
326 
327     @Override
createSurfaceControl(boolean force)328     void createSurfaceControl(boolean force) {
329         if (!mFromClientToken || force) {
330             super.createSurfaceControl(force);
331         }
332     }
333 
334     /** Returns true if the token windows list is empty. */
isEmpty()335     boolean isEmpty() {
336         return mChildren.isEmpty();
337     }
338 
getReplacingWindow()339     WindowState getReplacingWindow() {
340         for (int i = mChildren.size() - 1; i >= 0; i--) {
341             final WindowState win = mChildren.get(i);
342             final WindowState replacing = win.getReplacingWindow();
343             if (replacing != null) {
344                 return replacing;
345             }
346         }
347         return null;
348     }
349 
350     /** Return true if this token has a window that wants the wallpaper displayed behind it. */
windowsCanBeWallpaperTarget()351     boolean windowsCanBeWallpaperTarget() {
352         for (int j = mChildren.size() - 1; j >= 0; j--) {
353             final WindowState w = mChildren.get(j);
354             if (w.hasWallpaper()) {
355                 return true;
356             }
357         }
358 
359         return false;
360     }
361 
362     @Override
removeImmediately()363     void removeImmediately() {
364         if (mDisplayContent != null) {
365             mDisplayContent.removeWindowToken(token, true /* animateExit */);
366         }
367         // Needs to occur after the token is removed from the display above to avoid attempt at
368         // duplicate removal of this window container from it's parent.
369         super.removeImmediately();
370     }
371 
372     @Override
onDisplayChanged(DisplayContent dc)373     void onDisplayChanged(DisplayContent dc) {
374         dc.reParentWindowToken(this);
375 
376         // TODO(b/36740756): One day this should perhaps be hooked
377         // up with goodToGo, so we don't move a window
378         // to another display before the window behind
379         // it is ready.
380         super.onDisplayChanged(dc);
381     }
382 
383     @Override
onConfigurationChanged(Configuration newParentConfig)384     public void onConfigurationChanged(Configuration newParentConfig) {
385         super.onConfigurationChanged(newParentConfig);
386     }
387 
388     @Override
assignLayer(SurfaceControl.Transaction t, int layer)389     void assignLayer(SurfaceControl.Transaction t, int layer) {
390         if (windowType == TYPE_DOCK_DIVIDER) {
391             // See {@link DisplayContent#mSplitScreenDividerAnchor}
392             super.assignRelativeLayer(t,
393                     mDisplayContent.getDefaultTaskDisplayArea().getSplitScreenDividerAnchor(), 1);
394         } else if (mRoundedCornerOverlay) {
395             super.assignLayer(t, WindowManagerPolicy.COLOR_FADE_LAYER + 1);
396         } else {
397             super.assignLayer(t, layer);
398         }
399     }
400 
401     @Override
makeSurface()402     SurfaceControl.Builder makeSurface() {
403         final SurfaceControl.Builder builder = super.makeSurface();
404         if (mRoundedCornerOverlay) {
405             builder.setParent(null);
406         }
407         return builder;
408     }
409 
isClientVisible()410     boolean isClientVisible() {
411         return mClientVisible;
412     }
413 
setClientVisible(boolean clientVisible)414     void setClientVisible(boolean clientVisible) {
415         if (mClientVisible == clientVisible) {
416             return;
417         }
418         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
419                 "setClientVisible: %s clientVisible=%b Callers=%s", this, clientVisible,
420                 Debug.getCallers(5));
421         mClientVisible = clientVisible;
422         sendAppVisibilityToClients();
423     }
424 
hasFixedRotationTransform()425     boolean hasFixedRotationTransform() {
426         return mFixedRotationTransformState != null;
427     }
428 
429     /** Returns {@code true} if the given token shares the same transform. */
hasFixedRotationTransform(WindowToken token)430     boolean hasFixedRotationTransform(WindowToken token) {
431         if (mFixedRotationTransformState == null || token == null) {
432             return false;
433         }
434         return this == token || mFixedRotationTransformState == token.mFixedRotationTransformState;
435     }
436 
isFinishingFixedRotationTransform()437     boolean isFinishingFixedRotationTransform() {
438         return mFixedRotationTransformState != null
439                 && !mFixedRotationTransformState.mIsTransforming;
440     }
441 
isFixedRotationTransforming()442     boolean isFixedRotationTransforming() {
443         return mFixedRotationTransformState != null
444                 && mFixedRotationTransformState.mIsTransforming;
445     }
446 
getFixedRotationTransformDisplayInfo()447     DisplayInfo getFixedRotationTransformDisplayInfo() {
448         return isFixedRotationTransforming() ? mFixedRotationTransformState.mDisplayInfo : null;
449     }
450 
getFixedRotationTransformDisplayFrames()451     DisplayFrames getFixedRotationTransformDisplayFrames() {
452         return isFixedRotationTransforming() ? mFixedRotationTransformState.mDisplayFrames : null;
453     }
454 
getFixedRotationTransformDisplayBounds()455     Rect getFixedRotationTransformDisplayBounds() {
456         return isFixedRotationTransforming()
457                 ? mFixedRotationTransformState.mRotatedOverrideConfiguration.windowConfiguration
458                         .getBounds()
459                 : null;
460     }
461 
getFixedRotationBarContentFrame(int windowType)462     Rect getFixedRotationBarContentFrame(int windowType) {
463         if (!isFixedRotationTransforming()) {
464             return null;
465         }
466         if (!INSETS_LAYOUT_GENERALIZATION) {
467             return mFixedRotationTransformState.mBarContentFrames.get(windowType);
468         }
469         final DisplayFrames displayFrames = mFixedRotationTransformState.mDisplayFrames;
470         final Rect tmpRect = new Rect();
471         if (windowType == TYPE_NAVIGATION_BAR) {
472             tmpRect.set(displayFrames.mInsetsState.getSource(InsetsState.ITYPE_NAVIGATION_BAR)
473                     .getFrame());
474         }
475         if (windowType == TYPE_STATUS_BAR) {
476             tmpRect.set(displayFrames.mInsetsState.getSource(InsetsState.ITYPE_STATUS_BAR)
477                     .getFrame());
478         }
479         tmpRect.intersect(displayFrames.mDisplayCutoutSafe);
480         return tmpRect;
481     }
482 
getFixedRotationTransformInsetsState()483     InsetsState getFixedRotationTransformInsetsState() {
484         return isFixedRotationTransforming()
485                 ? mFixedRotationTransformState.mDisplayFrames.mInsetsState
486                 : null;
487     }
488 
489     /** Applies the rotated layout environment to this token in the simulated rotated display. */
applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames, Configuration config)490     void applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames,
491             Configuration config) {
492         if (mFixedRotationTransformState != null) {
493             mFixedRotationTransformState.disassociate(this);
494         }
495         mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames,
496                 new Configuration(config), mDisplayContent.getRotation());
497         mFixedRotationTransformState.mAssociatedTokens.add(this);
498         mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames,
499                 mFixedRotationTransformState.mBarContentFrames);
500         onFixedRotationStatePrepared();
501     }
502 
503     /**
504      * Reuses the {@link FixedRotationTransformState} (if any) from the other WindowToken to this
505      * one. This takes the same effect as {@link #applyFixedRotationTransform}.
506      */
linkFixedRotationTransform(WindowToken other)507     void linkFixedRotationTransform(WindowToken other) {
508         final FixedRotationTransformState fixedRotationState = other.mFixedRotationTransformState;
509         if (fixedRotationState == null || mFixedRotationTransformState == fixedRotationState) {
510             return;
511         }
512         if (mFixedRotationTransformState != null) {
513             mFixedRotationTransformState.disassociate(this);
514         }
515         mFixedRotationTransformState = fixedRotationState;
516         fixedRotationState.mAssociatedTokens.add(this);
517         onFixedRotationStatePrepared();
518     }
519 
520     /**
521      * Makes the rotated states take effect for this window container and its client process.
522      * This should only be called when {@link #mFixedRotationTransformState} is non-null.
523      */
onFixedRotationStatePrepared()524     private void onFixedRotationStatePrepared() {
525         // Send the adjustment info first so when the client receives configuration change, it can
526         // get the rotated display metrics.
527         notifyFixedRotationTransform(true /* enabled */);
528         // Resolve the rotated configuration.
529         onConfigurationChanged(getParent().getConfiguration());
530         final ActivityRecord r = asActivityRecord();
531         if (r != null && r.hasProcess()) {
532             // The application needs to be configured as in a rotated environment for compatibility.
533             // This registration will send the rotated configuration to its process.
534             r.app.registerActivityConfigurationListener(r);
535         }
536     }
537 
538     /**
539      * Return {@code true} if one of the associated activity is still animating. Otherwise,
540      * return {@code false}.
541      */
hasAnimatingFixedRotationTransition()542     boolean hasAnimatingFixedRotationTransition() {
543         if (mFixedRotationTransformState == null) {
544             return false;
545         }
546 
547         for (int i = mFixedRotationTransformState.mAssociatedTokens.size() - 1; i >= 0; i--) {
548             final ActivityRecord r =
549                     mFixedRotationTransformState.mAssociatedTokens.get(i).asActivityRecord();
550             if (r != null && r.isAnimating(TRANSITION | PARENTS)) {
551                 return true;
552             }
553         }
554         return false;
555     }
556 
finishFixedRotationTransform()557     void finishFixedRotationTransform() {
558         finishFixedRotationTransform(null /* applyDisplayRotation */);
559     }
560 
561     /**
562      * Finishes the transform and apply display rotation if the action is given. If the display will
563      * not rotate, the transformed containers are restored to their original states.
564      */
finishFixedRotationTransform(Runnable applyDisplayRotation)565     void finishFixedRotationTransform(Runnable applyDisplayRotation) {
566         final FixedRotationTransformState state = mFixedRotationTransformState;
567         if (state == null) {
568             return;
569         }
570 
571         state.resetTransform();
572         // Clear the flag so if the display will be updated to the same orientation, the transform
573         // won't take effect.
574         state.mIsTransforming = false;
575         if (applyDisplayRotation != null) {
576             applyDisplayRotation.run();
577         }
578         // The state is cleared at the end, because it is used to indicate that other windows can
579         // use seamless rotation when applying rotation to display.
580         for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) {
581             final WindowToken token = state.mAssociatedTokens.get(i);
582             token.mFixedRotationTransformState = null;
583             token.notifyFixedRotationTransform(false /* enabled */);
584             if (applyDisplayRotation == null) {
585                 // Notify cancellation because the display does not change rotation.
586                 token.cancelFixedRotationTransform();
587             }
588         }
589     }
590 
591     /** Notifies application side to enable or disable the rotation adjustment of display info. */
notifyFixedRotationTransform(boolean enabled)592     void notifyFixedRotationTransform(boolean enabled) {
593         FixedRotationAdjustments adjustments = null;
594         // A token may contain windows of the same processes or different processes. The list is
595         // used to avoid sending the same adjustments to a process multiple times.
596         ArrayList<WindowProcessController> notifiedProcesses = null;
597         for (int i = mChildren.size() - 1; i >= 0; i--) {
598             final WindowState w = mChildren.get(i);
599             final WindowProcessController app;
600             if (w.mAttrs.type == TYPE_APPLICATION_STARTING) {
601                 // Use the host activity because starting window is controlled by window manager.
602                 final ActivityRecord r = asActivityRecord();
603                 if (r == null) {
604                     continue;
605                 }
606                 app = r.app;
607             } else {
608                 app = mWmService.mAtmService.mProcessMap.getProcess(w.mSession.mPid);
609             }
610             if (app == null || !app.hasThread()) {
611                 continue;
612             }
613             if (notifiedProcesses == null) {
614                 notifiedProcesses = new ArrayList<>(2);
615                 adjustments = enabled ? createFixedRotationAdjustmentsIfNeeded() : null;
616             } else if (notifiedProcesses.contains(app)) {
617                 continue;
618             }
619             notifiedProcesses.add(app);
620             try {
621                 mWmService.mAtmService.getLifecycleManager().scheduleTransaction(
622                         app.getThread(), FixedRotationAdjustmentsItem.obtain(token, adjustments));
623             } catch (RemoteException e) {
624                 Slog.w(TAG, "Failed to schedule DisplayAdjustmentsItem to " + app, e);
625             }
626         }
627     }
628 
629     /** Restores the changes that applies to this container. */
cancelFixedRotationTransform()630     private void cancelFixedRotationTransform() {
631         final WindowContainer<?> parent = getParent();
632         if (parent == null) {
633             // The window may be detached or detaching.
634             return;
635         }
636         final int originalRotation = getWindowConfiguration().getRotation();
637         onConfigurationChanged(parent.getConfiguration());
638         onCancelFixedRotationTransform(originalRotation);
639     }
640 
641     /**
642      * It is called when the window is using fixed rotation transform, and before display applies
643      * the same rotation, the rotation change for display is canceled, e.g. the orientation from
644      * sensor is updated to previous direction.
645      */
onCancelFixedRotationTransform(int originalDisplayRotation)646     void onCancelFixedRotationTransform(int originalDisplayRotation) {
647     }
648 
createFixedRotationAdjustmentsIfNeeded()649     FixedRotationAdjustments createFixedRotationAdjustmentsIfNeeded() {
650         if (!isFixedRotationTransforming()) {
651             return null;
652         }
653         final DisplayInfo displayInfo = mFixedRotationTransformState.mDisplayInfo;
654         return new FixedRotationAdjustments(displayInfo.rotation, displayInfo.appWidth,
655                 displayInfo.appHeight, displayInfo.displayCutout);
656     }
657 
658     @Override
resolveOverrideConfiguration(Configuration newParentConfig)659     void resolveOverrideConfiguration(Configuration newParentConfig) {
660         super.resolveOverrideConfiguration(newParentConfig);
661         if (isFixedRotationTransforming()) {
662             // Apply the rotated configuration to current resolved configuration, so the merged
663             // override configuration can update to the same state.
664             getResolvedOverrideConfiguration().updateFrom(
665                     mFixedRotationTransformState.mRotatedOverrideConfiguration);
666         }
667     }
668 
669     @Override
updateSurfacePosition(SurfaceControl.Transaction t)670     void updateSurfacePosition(SurfaceControl.Transaction t) {
671         super.updateSurfacePosition(t);
672         if (isFixedRotationTransforming()) {
673             final ActivityRecord r = asActivityRecord();
674             final Task rootTask = r != null ? r.getRootTask() : null;
675             // Don't transform the activity in PiP because the PiP task organizer will handle it.
676             if (rootTask == null || !rootTask.inPinnedWindowingMode()) {
677                 // The window is laid out in a simulated rotated display but the real display hasn't
678                 // rotated, so here transforms its surface to fit in the real display.
679                 mFixedRotationTransformState.transform(this);
680             }
681         }
682     }
683 
684     @Override
resetSurfacePositionForAnimationLeash(SurfaceControl.Transaction t)685     void resetSurfacePositionForAnimationLeash(SurfaceControl.Transaction t) {
686         // Keep the transformed position to animate because the surface will show in different
687         // rotation than the animator of leash.
688         if (!isFixedRotationTransforming()) {
689             super.resetSurfacePositionForAnimationLeash(t);
690         }
691     }
692 
693     /**
694      * Gives a chance to this {@link WindowToken} to adjust the {@link
695      * android.view.WindowManager.LayoutParams} of its windows.
696      */
adjustWindowParams(WindowState win, WindowManager.LayoutParams attrs)697     void adjustWindowParams(WindowState win, WindowManager.LayoutParams attrs) {
698     }
699 
700 
701     @CallSuper
702     @Override
dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel)703     public void dumpDebug(ProtoOutputStream proto, long fieldId,
704             @WindowTraceLogLevel int logLevel) {
705         if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
706             return;
707         }
708 
709         final long token = proto.start(fieldId);
710         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
711         proto.write(HASH_CODE, System.identityHashCode(this));
712         proto.write(WAITING_TO_SHOW, waitingToShow);
713         proto.write(PAUSED, paused);
714         proto.end(token);
715     }
716 
717     @Override
getProtoFieldId()718     long getProtoFieldId() {
719         return WINDOW_TOKEN;
720     }
721 
dump(PrintWriter pw, String prefix, boolean dumpAll)722     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
723         super.dump(pw, prefix, dumpAll);
724         pw.print(prefix); pw.print("windows="); pw.println(mChildren);
725         pw.print(prefix); pw.print("windowType="); pw.print(windowType);
726                 pw.print(" hasVisible="); pw.print(hasVisible);
727         if (waitingToShow) {
728             pw.print(" waitingToShow=true");
729         }
730         pw.println();
731         if (hasFixedRotationTransform()) {
732             pw.print(prefix);
733             pw.print("fixedRotationConfig=");
734             pw.println(mFixedRotationTransformState.mRotatedOverrideConfiguration);
735         }
736     }
737 
738     @Override
toString()739     public String toString() {
740         if (stringName == null) {
741             StringBuilder sb = new StringBuilder();
742             sb.append("WindowToken{");
743             sb.append(Integer.toHexString(System.identityHashCode(this)));
744             sb.append(" "); sb.append(token); sb.append('}');
745             stringName = sb.toString();
746         }
747         return stringName;
748     }
749 
750     @Override
getName()751     String getName() {
752         return toString();
753     }
754 
755     @Override
asWindowToken()756     WindowToken asWindowToken() {
757         return this;
758     }
759 
760     /**
761      * Return whether windows from this token can layer above the
762      * system bars, or in other words extend outside of the "Decor Frame"
763      */
canLayerAboveSystemBars()764     boolean canLayerAboveSystemBars() {
765         int layer = getWindowLayerFromType();
766         int navLayer = mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_NAVIGATION_BAR,
767                 mOwnerCanManageAppTokens);
768         return mOwnerCanManageAppTokens && (layer > navLayer);
769     }
770 
getWindowLayerFromType()771     int getWindowLayerFromType() {
772         return mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, mOwnerCanManageAppTokens,
773                 mRoundedCornerOverlay);
774     }
775 
isFromClient()776     boolean isFromClient() {
777         return mFromClientToken;
778     }
779 
780     /** @see WindowState#freezeInsetsState() */
setInsetsFrozen(boolean freeze)781     void setInsetsFrozen(boolean freeze) {
782         forAllWindows(w -> {
783             if (w.mToken == this) {
784                 if (freeze) {
785                     w.freezeInsetsState();
786                 } else {
787                     w.clearFrozenInsetsState();
788                 }
789             }
790         },  true /* traverseTopToBottom */);
791     }
792 
793     @Override
getWindowType()794     @WindowType int getWindowType() {
795         return windowType;
796     }
797 
798     static class Builder {
799         private final WindowManagerService mService;
800         private final IBinder mToken;
801         @WindowType
802         private final int mType;
803 
804         private boolean mPersistOnEmpty;
805         private DisplayContent mDisplayContent;
806         private boolean mOwnerCanManageAppTokens;
807         private boolean mRoundedCornerOverlay;
808         private boolean mFromClientToken;
809         @Nullable
810         private Bundle mOptions;
811 
Builder(WindowManagerService service, IBinder token, int type)812         Builder(WindowManagerService service, IBinder token, int type) {
813             mService = service;
814             mToken = token;
815             mType = type;
816         }
817 
818         /** @see WindowToken#mPersistOnEmpty */
setPersistOnEmpty(boolean persistOnEmpty)819         Builder setPersistOnEmpty(boolean persistOnEmpty) {
820             mPersistOnEmpty = persistOnEmpty;
821             return this;
822         }
823 
824         /** Sets the {@link DisplayContent} to be associated. */
setDisplayContent(DisplayContent dc)825         Builder setDisplayContent(DisplayContent dc) {
826             mDisplayContent = dc;
827             return this;
828         }
829 
830         /** @see WindowToken#mOwnerCanManageAppTokens */
setOwnerCanManageAppTokens(boolean ownerCanManageAppTokens)831         Builder setOwnerCanManageAppTokens(boolean ownerCanManageAppTokens) {
832             mOwnerCanManageAppTokens = ownerCanManageAppTokens;
833             return this;
834         }
835 
836         /** @see WindowToken#mRoundedCornerOverlay */
setRoundedCornerOverlay(boolean roundedCornerOverlay)837         Builder setRoundedCornerOverlay(boolean roundedCornerOverlay) {
838             mRoundedCornerOverlay = roundedCornerOverlay;
839             return this;
840         }
841 
842         /** @see WindowToken#mFromClientToken */
setFromClientToken(boolean fromClientToken)843         Builder setFromClientToken(boolean fromClientToken) {
844             mFromClientToken = fromClientToken;
845             return this;
846         }
847 
848         /** @see WindowToken#mOptions */
setOptions(Bundle options)849         Builder setOptions(Bundle options) {
850             mOptions = options;
851             return this;
852         }
853 
build()854         WindowToken build() {
855             return new WindowToken(mService, mToken, mType, mPersistOnEmpty, mDisplayContent,
856                     mOwnerCanManageAppTokens, mRoundedCornerOverlay, mFromClientToken, mOptions);
857         }
858     }
859 }
860