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