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