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