content: /*
   * Copyright (C) 2011 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.server.wm;
  
  import static android.app.ActivityTaskManager.INVALID_STACK_ID;
  import static android.app.AppOpsManager.MODE_ALLOWED;
  import static android.app.AppOpsManager.MODE_DEFAULT;
  import static android.app.AppOpsManager.OP_NONE;
  import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
  import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
  import static android.os.PowerManager.DRAW_WAKE_LOCK;
  import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
  import static android.view.Display.DEFAULT_DISPLAY;
  import static android.view.SurfaceControl.Transaction;
  import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
  import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
  import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
  import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
  import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
  import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
  import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
  import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
  import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
  import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
  import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
  import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
  import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
  import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
  import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
  import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
  import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
  import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
  import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
  import static android.view.WindowManager.LayoutParams.FORMAT_CHANGED;
  import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
  import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
  import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
  import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
  import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
  import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
  import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
  import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
  import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
  import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
  import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
  import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
  import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
  import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
  import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
  import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
  import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
  import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
  import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
  import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
  import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
  import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
  import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
  import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
  import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
  import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED;
  import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM;
  import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
  import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
  
  import static com.android.server.am.ActivityManagerService.MY_PID;
  import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
  import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
  import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
  import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
  import static com.android.server.wm.AnimationSpecProto.MOVE;
  import static com.android.server.wm.DisplayContent.logsGestureExclusionRestrictions;
  import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
  import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
  import static com.android.server.wm.IdentifierProto.HASH_CODE;
  import static com.android.server.wm.IdentifierProto.TITLE;
  import static com.android.server.wm.IdentifierProto.USER_ID;
  import static com.android.server.wm.MoveAnimationSpecProto.DURATION_MS;
  import static com.android.server.wm.MoveAnimationSpecProto.FROM;
  import static com.android.server.wm.MoveAnimationSpecProto.TO;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
  import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
  import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
  import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
  import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
  import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
  import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
  import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
  import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
  import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
  import static com.android.server.wm.WindowManagerService.localLOGV;
  import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
  import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
  import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
  import static com.android.server.wm.WindowStateAnimator.PRESERVED_SURFACE_LAYER;
  import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
  import static com.android.server.wm.WindowStateProto.ANIMATING_EXIT;
  import static com.android.server.wm.WindowStateProto.ANIMATOR;
  import static com.android.server.wm.WindowStateProto.ATTRIBUTES;
  import static com.android.server.wm.WindowStateProto.CHILD_WINDOWS;
  import static com.android.server.wm.WindowStateProto.DESTROYING;
  import static com.android.server.wm.WindowStateProto.DISPLAY_ID;
  import static com.android.server.wm.WindowStateProto.FINISHED_SEAMLESS_ROTATION_FRAME;
  import static com.android.server.wm.WindowStateProto.FORCE_SEAMLESS_ROTATION;
  import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS;
  import static com.android.server.wm.WindowStateProto.HAS_SURFACE;
  import static com.android.server.wm.WindowStateProto.IDENTIFIER;
  import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN;
  import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
  import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
  import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION;
  import static com.android.server.wm.WindowStateProto.REMOVED;
  import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
  import static com.android.server.wm.WindowStateProto.REQUESTED_HEIGHT;
  import static com.android.server.wm.WindowStateProto.REQUESTED_WIDTH;
  import static com.android.server.wm.WindowStateProto.STACK_ID;
  import static com.android.server.wm.WindowStateProto.SURFACE_INSETS;
  import static com.android.server.wm.WindowStateProto.SURFACE_POSITION;
  import static com.android.server.wm.WindowStateProto.SYSTEM_UI_VISIBILITY;
  import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
  import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
  import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
  
  import android.annotation.CallSuper;
  import android.annotation.Nullable;
  import android.app.AppOpsManager;
  import android.content.Context;
  import android.content.res.Configuration;
  import android.graphics.Matrix;
  import android.graphics.PixelFormat;
  import android.graphics.Point;
  import android.graphics.Rect;
  import android.graphics.Region;
  import android.os.Binder;
  import android.os.Build;
  import android.os.Debug;
  import android.os.IBinder;
  import android.os.PowerManager;
  import android.os.PowerManager.WakeReason;
  import android.os.RemoteCallbackList;
  import android.os.RemoteException;
  import android.os.SystemClock;
  import android.os.Trace;
  import android.os.UserHandle;
  import android.os.WorkSource;
  import android.provider.Settings;
  import android.text.TextUtils;
  import android.util.ArraySet;
  import android.util.DisplayMetrics;
  import android.util.MergedConfiguration;
  import android.util.Slog;
  import android.util.StatsLog;
  import android.util.TimeUtils;
  import android.util.proto.ProtoOutputStream;
  import android.view.Display;
  import android.view.DisplayCutout;
  import android.view.DisplayInfo;
  import android.view.Gravity;
  import android.view.IApplicationToken;
  import android.view.IWindow;
  import android.view.IWindowFocusObserver;
  import android.view.IWindowId;
  import android.view.InputChannel;
  import android.view.InputEvent;
  import android.view.InputEventReceiver;
  import android.view.InputWindowHandle;
  import android.view.Surface.Rotation;
  import android.view.SurfaceControl;
  import android.view.SurfaceSession;
  import android.view.View;
  import android.view.ViewTreeObserver;
  import android.view.WindowInfo;
  import android.view.WindowManager;
  import android.view.animation.Animation;
  import android.view.animation.AnimationUtils;
  import android.view.animation.Interpolator;
  
  import com.android.internal.annotations.VisibleForTesting;
  import com.android.internal.util.ToBooleanFunction;
  import com.android.server.policy.WindowManagerPolicy;
  import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
  import com.android.server.wm.utils.InsetUtils;
  import com.android.server.wm.utils.WmDisplayCutout;
  
  import java.io.PrintWriter;
  import java.lang.ref.WeakReference;
  import java.util.ArrayList;
  import java.util.Comparator;
  import java.util.List;
  import java.util.function.Predicate;
  
  /** A window in the window manager. */
  class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {
      static final String TAG = TAG_WITH_CLASS_NAME ? "WindowState" : TAG_WM;
  
      // The minimal size of a window within the usable area of the freeform stack.
      // TODO(multi-window): fix the min sizes when we have minimum width/height support,
      //                     use hard-coded min sizes for now.
      static final int MINIMUM_VISIBLE_WIDTH_IN_DP = 48;
      static final int MINIMUM_VISIBLE_HEIGHT_IN_DP = 32;
  
      // The thickness of a window resize handle outside the window bounds on the free form workspace
      // to capture touch events in that area.
      static final int RESIZE_HANDLE_WIDTH_IN_DP = 30;
  
      static final int EXCLUSION_LEFT = 0;
      static final int EXCLUSION_RIGHT = 1;
  
      final WindowManagerPolicy mPolicy;
      final Context mContext;
      final Session mSession;
      final IWindow mClient;
      final int mAppOp;
      // UserId and appId of the owner. Don't display windows of non-current user.
      final int mOwnerUid;
      /** The owner has {@link android.Manifest.permission#INTERNAL_SYSTEM_WINDOW} */
      final boolean mOwnerCanAddInternalSystemWindow;
      final WindowId mWindowId;
      WindowToken mToken;
      // The same object as mToken if this is an app window and null for non-app windows.
      AppWindowToken mAppToken;
  
      // mAttrs.flags is tested in animation without being locked. If the bits tested are ever
      // modified they will need to be locked.
      final WindowManager.LayoutParams mAttrs = new WindowManager.LayoutParams();
      final DeathRecipient mDeathRecipient;
      private boolean mIsChildWindow;
      final int mBaseLayer;
      final int mSubLayer;
      final boolean mLayoutAttached;
      final boolean mIsImWindow;
      final boolean mIsWallpaper;
      private final boolean mIsFloatingLayer;
      int mSeq;
      int mViewVisibility;
      int mSystemUiVisibility;
  
      /**
       * The visibility flag of the window based on policy like {@link WindowManagerPolicy}.
       * Normally set by calling {@link #showLw} and {@link #hideLw}.
       *
       * TODO: b/131253938 This will eventually be split into individual visibility policy flags.
       */
      static final int LEGACY_POLICY_VISIBILITY = 1;
      /**
       * The visibility flag that determines whether this window is visible for the current user.
       */
      private static final int VISIBLE_FOR_USER = 1 << 1;
      private static final int POLICY_VISIBILITY_ALL = VISIBLE_FOR_USER | LEGACY_POLICY_VISIBILITY;
      /**
       * The Bitwise-or of flags that contribute to visibility of the WindowState
       */
      private int mPolicyVisibility = POLICY_VISIBILITY_ALL;
  
      /**
       * Whether {@link #LEGACY_POLICY_VISIBILITY} flag should be set after a transition animation.
       * For example, {@link #LEGACY_POLICY_VISIBILITY} might be set during an exit animation to hide
       * it and then unset when the value of {@link #mLegacyPolicyVisibilityAfterAnim} is false
       * after the exit animation is done.
       *
       * TODO: b/131253938 Determine whether this can be changed to use a visibility flag instead.
       */
      boolean mLegacyPolicyVisibilityAfterAnim = true;
      // overlay window is hidden because the owning app is suspended
      private boolean mHiddenWhileSuspended;
      private boolean mAppOpVisibility = true;
      boolean mPermanentlyHidden; // the window should never be shown again
      // This is a non-system overlay window that is currently force hidden.
      private boolean mForceHideNonSystemOverlayWindow;
      boolean mAppFreezing;
      boolean mHidden = true;    // Used to determine if to show child windows.
      boolean mWallpaperVisible;  // for wallpaper, what was last vis report?
      private boolean mDragResizing;
      private boolean mDragResizingChangeReported = true;
      private int mResizeMode;
      /**
       * Special mode that is intended only for the rounded corner overlay: during rotation
       * transition, we un-rotate the window token such that the window appears as it did before the
       * rotation.
       */
      final boolean mForceSeamlesslyRotate;
      SeamlessRotator mPendingSeamlessRotate;
      long mFinishSeamlessRotateFrameNumber;
  
      private RemoteCallbackList<IWindowFocusObserver> mFocusCallbacks;
  
      /**
       * The window size that was requested by the application.  These are in
       * the application's coordinate space (without compatibility scale applied).
       */
      int mRequestedWidth;
      int mRequestedHeight;
      private int mLastRequestedWidth;
      private int mLastRequestedHeight;
  
      int mLayer;
      boolean mHaveFrame;
      boolean mObscured;
  
      int mLayoutSeq = -1;
  
      /** @see #addEmbeddedDisplayContent(DisplayContent dc) */
      private final ArraySet<DisplayContent> mEmbeddedDisplayContents = new ArraySet<>();
  
      /**
       * Used to store last reported to client configuration and check if we have newer available.
       * We'll send configuration to client only if it is different from the last applied one and
       * client won't perform unnecessary updates.
       */
      private final MergedConfiguration mLastReportedConfiguration = new MergedConfiguration();
  
      /** @see #isLastConfigReportedToClient() */
      private boolean mLastConfigReportedToClient;
  
      private final Configuration mTempConfiguration = new Configuration();
  
      /**
       * The last content insets returned to the client in relayout. We use
       * these in the bounds animation to ensure we only observe inset changes
       * at the same time that a client resizes it's surface so that we may use
       * the geometryAppliesWithResize synchronization mechanism to keep
       * the contents in place.
       */
      final Rect mLastRelayoutContentInsets = new Rect();
  
      /**
       * Set to true if we are waiting for this window to receive its
       * given internal insets before laying out other windows based on it.
       */
      boolean mGivenInsetsPending;
  
      /**
       * These are the content insets that were given during layout for
       * this window, to be applied to windows behind it.
       */
      final Rect mGivenContentInsets = new Rect();
  
      /**
       * These are the visible insets that were given during layout for
       * this window, to be applied to windows behind it.
       */
      final Rect mGivenVisibleInsets = new Rect();
  
      /**
       * This is the given touchable area relative to the window frame, or null if none.
       */
      final Region mGivenTouchableRegion = new Region();
  
      /**
       * Flag indicating whether the touchable region should be adjusted by
       * the visible insets; if false the area outside the visible insets is
       * NOT touchable, so we must use those to adjust the frame during hit
       * tests.
       */
      int mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
  
      // Current transformation being applied.
      float mGlobalScale=1;
      float mInvGlobalScale=1;
      float mHScale=1, mVScale=1;
      float mLastHScale=1, mLastVScale=1;
      final Matrix mTmpMatrix = new Matrix();
  
      private final WindowFrames mWindowFrames = new WindowFrames();
  
      /**
       * Usually empty. Set to the task's tempInsetFrame. See
       *{@link android.app.IActivityTaskManager#resizeDockedStack}.
       */
      private final Rect mInsetFrame = new Rect();
  
      /**
       * List of rects where system gestures should be ignored.
       *
       * Coordinates are relative to the window's position.
       */
      private final List<Rect> mExclusionRects = new ArrayList<>();
  
      // 0 = left, 1 = right
      private final int[] mLastRequestedExclusionHeight = {0, 0};
      private final int[] mLastGrantedExclusionHeight = {0, 0};
      private final long[] mLastExclusionLogUptimeMillis = {0, 0};
  
      private boolean mLastShownChangedReported;
  
      // If a window showing a wallpaper: the requested offset for the
      // wallpaper; if a wallpaper window: the currently applied offset.
      float mWallpaperX = -1;
      float mWallpaperY = -1;
  
      // If a window showing a wallpaper: what fraction of the offset
      // range corresponds to a full virtual screen.
      float mWallpaperXStep = -1;
      float mWallpaperYStep = -1;
  
      // If a window showing a wallpaper: a raw pixel offset to forcibly apply
      // to its window; if a wallpaper window: not used.
      int mWallpaperDisplayOffsetX = Integer.MIN_VALUE;
      int mWallpaperDisplayOffsetY = Integer.MIN_VALUE;
  
      /**
       * This is set after IWindowSession.relayout() has been called at
       * least once for the window.  It allows us to detect the situation
       * where we don't yet have a surface, but should have one soon, so
       * we can give the window focus before waiting for the relayout.
       */
      boolean mRelayoutCalled;
  
      boolean mInRelayout;
  
      /**
       * If the application has called relayout() with changes that can
       * impact its window's size, we need to perform a layout pass on it
       * even if it is not currently visible for layout.  This is set
       * when in that case until the layout is done.
       */
      boolean mLayoutNeeded;
  
      /** Currently running an exit animation? */
      boolean mAnimatingExit;
  
      /** Currently on the mDestroySurface list? */
      boolean mDestroying;
  
      /** Completely remove from window manager after exit animation? */
      boolean mRemoveOnExit;
  
      /**
       * Whether the app died while it was visible, if true we might need
       * to continue to show it until it's restarted.
       */
      boolean mAppDied;
  
      /**
       * Set when the orientation is changing and this window has not yet
       * been updated for the new orientation.
       */
      private boolean mOrientationChanging;
  
      /**
       * Sometimes in addition to the mOrientationChanging
       * flag we report that the orientation is changing
       * due to a mismatch in current and reported configuration.
       *
       * In the case of timeout we still need to make sure we
       * leave the orientation changing state though, so we
       * use this as a special time out escape hatch.
       */
      private boolean mOrientationChangeTimedOut;
  
      /**
       * The orientation during the last visible call to relayout. If our
       * current orientation is different, the window can't be ready
       * to be shown.
       */
      int mLastVisibleLayoutRotation = -1;
  
      /**
       * Set when we need to report the orientation change to client to trigger a relayout.
       */
      boolean mReportOrientationChanged;
  
      /**
       * How long we last kept the screen frozen.
       */
      int mLastFreezeDuration;
  
      /** Is this window now (or just being) removed? */
      boolean mRemoved;
  
      /**
       * It is save to remove the window and destroy the surface because the client requested removal
       * or some other higher level component said so (e.g. activity manager).
       * TODO: We should either have different booleans for the removal reason or use a bit-field.
       */
      boolean mWindowRemovalAllowed;
  
      // Input channel and input window handle used by the input dispatcher.
      final InputWindowHandle mInputWindowHandle;
      InputChannel mInputChannel;
      private InputChannel mClientChannel;
  
      // Used to improve performance of toString()
      private String mStringNameCache;
      private CharSequence mLastTitle;
      private boolean mWasExiting;
  
      final WindowStateAnimator mWinAnimator;
  
      boolean mHasSurface = false;
  
      /** When true this window can be displayed on screens owther than mOwnerUid's */
      private boolean mShowToOwnerOnly;
  
      // Whether the window was visible when we set the app to invisible last time. WM uses
      // this as a hint to restore the surface (if available) for early animation next time
      // the app is brought visible.
      private boolean mWasVisibleBeforeClientHidden;
  
      // This window will be replaced due to relaunch. This allows window manager
      // to differentiate between simple removal of a window and replacement. In the latter case it
      // will preserve the old window until the new one is drawn.
      boolean mWillReplaceWindow = false;
      // If true, the replaced window was already requested to be removed.
      private boolean mReplacingRemoveRequested = false;
      // Whether the replacement of the window should trigger app transition animation.
      private boolean mAnimateReplacingWindow = false;
      // If not null, the window that will be used to replace the old one. This is being set when
      // the window is added and unset when this window reports its first draw.
      private WindowState mReplacementWindow = null;
      // For the new window in the replacement transition, if we have
      // requested to replace without animation, then we should
      // make sure we also don't apply an enter animation for
      // the new window.
      boolean mSkipEnterAnimationForSeamlessReplacement = false;
      // Whether this window is being moved via the resize API
      private boolean mMovedByResize;
  
      /**
       * Wake lock for drawing.
       * Even though it's slightly more expensive to do so, we will use a separate wake lock
       * for each app that is requesting to draw while dozing so that we can accurately track
       * who is preventing the system from suspending.
       * This lock is only acquired on first use.
       */
      private PowerManager.WakeLock mDrawLock;
  
      private final Rect mTmpRect = new Rect();
      private final Point mTmpPoint = new Point();
  
      /**
       * If a window is on a display which has been re-parented to a view in another window,
       * use this offset to indicate the correct location.
       */
      private final Point mLastReportedDisplayOffset = new Point();
  
      /**
       * Whether the window was resized by us while it was gone for layout.
       */
      boolean mResizedWhileGone = false;
  
      /**
       * During seamless rotation we have two phases, first the old window contents
       * are rotated to look as if they didn't move in the new coordinate system. Then we
       * have to freeze updates to this layer (to preserve the transformation) until
       * the resize actually occurs. This is true from when the transformation is set
       * and false until the transaction to resize is sent.
       */
      boolean mSeamlesslyRotated = false;
  
      /**
       * Surface insets from the previous call to relayout(), used to track
       * if we are changing the Surface insets.
       */
      final Rect mLastSurfaceInsets = new Rect();
  
      /**
       * A flag set by the {@link WindowState} parent to indicate that the parent has examined this
       * {@link WindowState} in its overall drawing context. This book-keeping allows the parent to
       * make sure all children have been considered.
       */
      private boolean mDrawnStateEvaluated;
  
      private final Point mSurfacePosition = new Point();
  
      /**
       * A region inside of this window to be excluded from touch.
       */
      private TapExcludeRegionHolder mTapExcludeRegionHolder;
  
      /**
       * Used for testing because the real PowerManager is final.
       */
      private PowerManagerWrapper mPowerManagerWrapper;
  
      /**
       * A frame number in which changes requested in this layout will be rendered.
       */
      private long mFrameNumber = -1;
  
      private static final StringBuilder sTmpSB = new StringBuilder();
  
      /**
       * Compares two window sub-layers and returns -1 if the first is lesser than the second in terms
       * of z-order and 1 otherwise.
       */
      private static final Comparator<WindowState> sWindowSubLayerComparator =
              new Comparator<WindowState>() {
                  @Override
                  public int compare(WindowState w1, WindowState w2) {
                      final int layer1 = w1.mSubLayer;
                      final int layer2 = w2.mSubLayer;
                      if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) {
                          // We insert the child window into the list ordered by
                          // the sub-layer.  For same sub-layers, the negative one
                          // should go below others; the positive one should go
                          // above others.
                          return -1;
                      }
                      return 1;
                  };
              };
  
      /**
       * Indicates whether we have requested a Dim (in the sense of {@link Dimmer}) from our host
       * container.
       */
      private boolean mIsDimming = false;
  
      private @Nullable InsetsSourceProvider mInsetProvider;
  
      private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
  
      void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
              @Rotation int rotation, boolean requested) {
          // Invisible windows and the wallpaper do not participate in the seamless rotation animation
          if (!isVisibleNow() || mIsWallpaper) {
              return;
          }
  
          if (mPendingSeamlessRotate != null) {
              oldRotation = mPendingSeamlessRotate.getOldRotation();
          }
  
          if (mForceSeamlesslyRotate || requested) {
              mPendingSeamlessRotate = new SeamlessRotator(oldRotation, rotation, getDisplayInfo());
              mPendingSeamlessRotate.unrotate(transaction, this);
              mWmService.markForSeamlessRotation(this, true);
          }
      }
  
      void finishSeamlessRotation(boolean timeout) {
          if (mPendingSeamlessRotate != null) {
              mPendingSeamlessRotate.finish(this, timeout);
              mFinishSeamlessRotateFrameNumber = getFrameNumber();
              mPendingSeamlessRotate = null;
              mWmService.markForSeamlessRotation(this, false);
          }
      }
  
      List<Rect> getSystemGestureExclusion() {
          return mExclusionRects;
      }
  
      /**
       * Sets the system gesture exclusion rects.
       *
       * @return {@code true} if anything changed
       */
      boolean setSystemGestureExclusion(List<Rect> exclusionRects) {
          if (mExclusionRects.equals(exclusionRects)) {
              return false;
          }
          mExclusionRects.clear();
          mExclusionRects.addAll(exclusionRects);
          return true;
      }
  
      boolean isImplicitlyExcludingAllSystemGestures() {
          final int immersiveStickyFlags =
                  SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
          final boolean immersiveSticky =
                  (mSystemUiVisibility & immersiveStickyFlags) == immersiveStickyFlags;
          return immersiveSticky && mWmService.mSystemGestureExcludedByPreQStickyImmersive
                  && mAppToken != null && mAppToken.mTargetSdk < Build.VERSION_CODES.Q;
      }
  
      void setLastExclusionHeights(int side, int requested, int granted) {
          boolean changed = mLastGrantedExclusionHeight[side] != granted
                  || mLastRequestedExclusionHeight[side] != requested;
  
          if (changed) {
              if (mLastShownChangedReported) {
                  logExclusionRestrictions(side);
              }
  
              mLastGrantedExclusionHeight[side] = granted;
              mLastRequestedExclusionHeight[side] = requested;
          }
      }
  
      interface PowerManagerWrapper {
          void wakeUp(long time, @WakeReason int reason, String details);
  
          boolean isInteractive();
  
      }
  
      WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
              WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
              int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) {
          this(service, s, c, token, parentWindow, appOp, seq, a, viewVisibility, ownerId,
                  ownerCanAddInternalSystemWindow, new PowerManagerWrapper() {
                      @Override
                      public void wakeUp(long time, @WakeReason int reason, String details) {
                          service.mPowerManager.wakeUp(time, reason, details);
                      }
  
                      @Override
                      public boolean isInteractive() {
                          return service.mPowerManager.isInteractive();
                      }
                  });
      }
  
      WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
              WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
              int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow,
              PowerManagerWrapper powerManagerWrapper) {
          super(service);
          mSession = s;
          mClient = c;
          mAppOp = appOp;
          mToken = token;
          mAppToken = mToken.asAppWindowToken();
          mOwnerUid = ownerId;
          mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow;
          mWindowId = new WindowId(this);
          mAttrs.copyFrom(a);
          mLastSurfaceInsets.set(mAttrs.surfaceInsets);
          mViewVisibility = viewVisibility;
          mPolicy = mWmService.mPolicy;
          mContext = mWmService.mContext;
          DeathRecipient deathRecipient = new DeathRecipient();
          mSeq = seq;
          mPowerManagerWrapper = powerManagerWrapper;
          mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
          if (localLOGV) Slog.v(
              TAG, "Window " + this + " client=" + c.asBinder()
              + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
          try {
              c.asBinder().linkToDeath(deathRecipient, 0);
          } catch (RemoteException e) {
              mDeathRecipient = null;
              mIsChildWindow = false;
              mLayoutAttached = false;
              mIsImWindow = false;
              mIsWallpaper = false;
              mIsFloatingLayer = false;
              mBaseLayer = 0;
              mSubLayer = 0;
              mInputWindowHandle = null;
              mWinAnimator = null;
              return;
          }
          mDeathRecipient = deathRecipient;
  
          if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
              // The multiplier here is to reserve space for multiple
              // windows in the same type layer.
              mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
                      * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
              mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
              mIsChildWindow = true;
  
              if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to " + parentWindow);
              parentWindow.addChild(this, sWindowSubLayerComparator);
  
              mLayoutAttached = mAttrs.type !=
                      WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
              mIsImWindow = parentWindow.mAttrs.type == TYPE_INPUT_METHOD
                      || parentWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
              mIsWallpaper = parentWindow.mAttrs.type == TYPE_WALLPAPER;
          } else {
              // The multiplier here is to reserve space for multiple
              // windows in the same type layer.
              mBaseLayer = mPolicy.getWindowLayerLw(this)
                      * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
              mSubLayer = 0;
              mIsChildWindow = false;
              mLayoutAttached = false;
              mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
                      || mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
              mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;
          }
          mIsFloatingLayer = mIsImWindow || mIsWallpaper;
  
          if (mAppToken != null && mAppToken.mShowForAllUsers) {
              // Windows for apps that can show for all users should also show when the device is
              // locked.
              mAttrs.flags |= FLAG_SHOW_WHEN_LOCKED;
          }
  
          mWinAnimator = new WindowStateAnimator(this);
          mWinAnimator.mAlpha = a.alpha;
  
          mRequestedWidth = 0;
          mRequestedHeight = 0;
          mLastRequestedWidth = 0;
          mLastRequestedHeight = 0;
          mLayer = 0;
          mInputWindowHandle = new InputWindowHandle(
                  mAppToken != null ? mAppToken.mInputApplicationHandle : null, c,
                      getDisplayId());
      }
  
      void attach() {
          if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
          mSession.windowAddedLocked(mAttrs.packageName);
      }
  
      /**
       * @return {@code true} if the application runs in size compatibility mode.
       * @see android.content.res.CompatibilityInfo#supportsScreen
       * @see ActivityRecord#inSizeCompatMode
       */
      boolean inSizeCompatMode() {
          return (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
                  || (mAppToken != null && mAppToken.inSizeCompatMode()
                          // Exclude starting window because it is not displayed by the application.
                          && mAttrs.type != TYPE_APPLICATION_STARTING);
      }
  
      /**
       * Returns whether this {@link WindowState} has been considered for drawing by its parent.
       */
      boolean getDrawnStateEvaluated() {
          return mDrawnStateEvaluated;
      }
  
      /**
       * Sets whether this {@link WindowState} has been considered for drawing by its parent. Should
       * be cleared when detached from parent.
       */
      void setDrawnStateEvaluated(boolean evaluated) {
          mDrawnStateEvaluated = evaluated;
      }
  
      @Override
      void onParentChanged() {
          super.onParentChanged();
          setDrawnStateEvaluated(false /*evaluated*/);
  
          getDisplayContent().reapplyMagnificationSpec();
      }
  
      @Override
      public int getOwningUid() {
          return mOwnerUid;
      }
  
      @Override
      public String getOwningPackage() {
          return mAttrs.packageName;
      }
  
      @Override
      public boolean canAddInternalSystemWindow() {
          return mOwnerCanAddInternalSystemWindow;
      }
  
      @Override
      public boolean canAcquireSleepToken() {
          return mSession.mCanAcquireSleepToken;
      }
  
      /**
       * Subtracts the insets calculated by intersecting {@param layoutFrame} with {@param insetFrame}
       * from {@param frame}. In other words, it applies the insets that would result if
       * {@param frame} would be shifted to {@param layoutFrame} and then applying the insets from
       * {@param insetFrame}. Also it respects {@param displayFrame} in case window has minimum
       * width/height applied and insets should be overridden.
       */
      private void subtractInsets(Rect frame, Rect layoutFrame, Rect insetFrame, Rect displayFrame) {
          final int left = Math.max(0, insetFrame.left - Math.max(layoutFrame.left, displayFrame.left));
          final int top = Math.max(0, insetFrame.top - Math.max(layoutFrame.top, displayFrame.top));
          final int right = Math.max(0, Math.min(layoutFrame.right, displayFrame.right) - insetFrame.right);
          final int bottom = Math.max(0, Math.min(layoutFrame.bottom, displayFrame.bottom) - insetFrame.bottom);
          frame.inset(left, top, right, bottom);
      }
  
      @Override
      public Rect getDisplayedBounds() {
          final Task task = getTask();
          if (task != null) {
              Rect bounds = task.getOverrideDisplayedBounds();
              if (!bounds.isEmpty()) {
                  return bounds;
              }
          }
          return super.getDisplayedBounds();
      }
  
      @Override
      public void computeFrameLw() {
          if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
              // This window is being replaced and either already got information that it's being
              // removed or we are still waiting for some information. Because of this we don't
              // want to apply any more changes to it, so it remains in this state until new window
              // appears.
              return;
          }
          mHaveFrame = true;
  
          final Task task = getTask();
          final boolean isFullscreenAndFillsDisplay = !inMultiWindowMode() && matchesDisplayBounds();
          final boolean windowsAreFloating = task != null && task.isFloating();
          final DisplayContent dc = getDisplayContent();
  
          mInsetFrame.set(getBounds());
  
          // Denotes the actual frame used to calculate the insets and to perform the layout. When
          // resizing in docked mode, we'd like to freeze the layout, so we also need to freeze the
          // insets temporarily. By the notion of a task having a different layout frame, we can
          // achieve that while still moving the task around.
          final Rect layoutContainingFrame;
          final Rect layoutDisplayFrame;
  
          // The offset from the layout containing frame to the actual containing frame.
          final int layoutXDiff;
          final int layoutYDiff;
          final WindowState imeWin = mWmService.mRoot.getCurrentInputMethodWindow();
          final boolean isImeTarget =
                  imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget();
          if (isFullscreenAndFillsDisplay || layoutInParentFrame()) {
              // We use the parent frame as the containing frame for fullscreen and child windows
              mWindowFrames.mContainingFrame.set(mWindowFrames.mParentFrame);
              layoutDisplayFrame = mWindowFrames.mDisplayFrame;
              layoutContainingFrame = mWindowFrames.mParentFrame;
              layoutXDiff = 0;
              layoutYDiff = 0;
          } else {
              mWindowFrames.mContainingFrame.set(getDisplayedBounds());
              if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) {
  
                  // If the bounds are frozen, we still want to translate the window freely and only
                  // freeze the size.
                  Rect frozen = mAppToken.mFrozenBounds.peek();
                  mWindowFrames.mContainingFrame.right =
                          mWindowFrames.mContainingFrame.left + frozen.width();
                  mWindowFrames.mContainingFrame.bottom =
                          mWindowFrames.mContainingFrame.top + frozen.height();
              }
              // IME is up and obscuring this window. Adjust the window position so it is visible.
              if (isImeTarget) {
                  if (inFreeformWindowingMode()) {
                      // Push the freeform window up to make room for the IME. However, don't push
                      // it up past the top of the screen.
                      final int bottomOverlap = mWindowFrames.mContainingFrame.bottom
                              - mWindowFrames.mVisibleFrame.bottom;
                      if (bottomOverlap > 0) {
                          final int distanceToTop = Math.max(mWindowFrames.mContainingFrame.top
                                  - mWindowFrames.mDisplayFrame.top, 0);
                          int offs = Math.min(bottomOverlap, distanceToTop);
                          mWindowFrames.mContainingFrame.top -= offs;
                      }
                  } else if (!inPinnedWindowingMode() && mWindowFrames.mContainingFrame.bottom
                          > mWindowFrames.mParentFrame.bottom) {
                      // But in docked we want to behave like fullscreen and behave as if the task
                      // were given smaller bounds for the purposes of layout. Skip adjustments for
                      // the pinned stack, they are handled separately in the PinnedStackController.
                      mWindowFrames.mContainingFrame.bottom = mWindowFrames.mParentFrame.bottom;
                  }
              }
  
              if (windowsAreFloating) {
                  // In floating modes (e.g. freeform, pinned) we have only to set the rectangle
                  // if it wasn't set already. No need to intersect it with the (visible)
                  // "content frame" since it is allowed to be outside the visible desktop.
                  if (mWindowFrames.mContainingFrame.isEmpty()) {
                      mWindowFrames.mContainingFrame.set(mWindowFrames.mContentFrame);
                  }
              }
  
              final TaskStack stack = getStack();
              if (inPinnedWindowingMode() && stack != null
                      && stack.lastAnimatingBoundsWasToFullscreen()) {
                  // PIP edge case: When going from pinned to fullscreen, we apply a
                  // tempInsetFrame for the full task - but we're still at the start of the animation.
                  // To prevent a jump if there's a letterbox, restrict to the parent frame.
                  mInsetFrame.intersectUnchecked(mWindowFrames.mParentFrame);
                  mWindowFrames.mContainingFrame.intersectUnchecked(mWindowFrames.mParentFrame);
              }
  
              layoutDisplayFrame = new Rect(mWindowFrames.mDisplayFrame);
              mWindowFrames.mDisplayFrame.set(mWindowFrames.mContainingFrame);
              layoutXDiff = mInsetFrame.left - mWindowFrames.mContainingFrame.left;
              layoutYDiff = mInsetFrame.top - mWindowFrames.mContainingFrame.top;
              layoutContainingFrame = mInsetFrame;
              mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight);
              subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame,
                      mTmpRect);
              if (!layoutInParentFrame()) {
                  subtractInsets(mWindowFrames.mContainingFrame, layoutContainingFrame,
                          mWindowFrames.mParentFrame, mTmpRect);
                  subtractInsets(mInsetFrame, layoutContainingFrame, mWindowFrames.mParentFrame,
                          mTmpRect);
              }
              layoutDisplayFrame.intersect(layoutContainingFrame);
          }
  
          final int pw = mWindowFrames.mContainingFrame.width();
          final int ph = mWindowFrames.mContainingFrame.height();
  
          if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
              mLastRequestedWidth = mRequestedWidth;
              mLastRequestedHeight = mRequestedHeight;
              mWindowFrames.setContentChanged(true);
          }
  
          final int fw = mWindowFrames.mFrame.width();
          final int fh = mWindowFrames.mFrame.height();
  
          applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
  
          // Calculate the outsets before the content frame gets shrinked to the window frame.
          mWindowFrames.calculateOutsets();
  
          // Make sure the content and visible frames are inside of the
          // final window frame.
          if (windowsAreFloating && !mWindowFrames.mFrame.isEmpty()) {
              final int visBottom = mWindowFrames.mVisibleFrame.bottom;
              final int contentBottom = mWindowFrames.mContentFrame.bottom;
              mWindowFrames.mContentFrame.set(mWindowFrames.mFrame);
              mWindowFrames.mVisibleFrame.set(mWindowFrames.mContentFrame);
              mWindowFrames.mStableFrame.set(mWindowFrames.mContentFrame);
              if (isImeTarget && inFreeformWindowingMode()) {
                  // After displacing a freeform window to make room for the ime, any part of
                  // the window still covered by IME should be inset.
                  if (contentBottom + layoutYDiff < mWindowFrames.mContentFrame.bottom) {
                      mWindowFrames.mContentFrame.bottom = contentBottom + layoutYDiff;
                  }
                  if (visBottom + layoutYDiff < mWindowFrames.mVisibleFrame.bottom) {
                      mWindowFrames.mVisibleFrame.bottom = visBottom + layoutYDiff;
                  }
              }
          } else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
              dc.getDockedDividerController().positionDockedStackedDivider(mWindowFrames.mFrame);
              mWindowFrames.mContentFrame.set(mWindowFrames.mFrame);
              if (!mWindowFrames.mFrame.equals(mWindowFrames.mLastFrame)) {
                  mMovedByResize = true;
              }
          } else {
              mWindowFrames.mContentFrame.set(
                      Math.max(mWindowFrames.mContentFrame.left, mWindowFrames.mFrame.left),
                      Math.max(mWindowFrames.mContentFrame.top, mWindowFrames.mFrame.top),
                      Math.min(mWindowFrames.mContentFrame.right, mWindowFrames.mFrame.right),
                      Math.min(mWindowFrames.mContentFrame.bottom, mWindowFrames.mFrame.bottom));
  
              mWindowFrames.mVisibleFrame.set(
                      Math.max(mWindowFrames.mVisibleFrame.left, mWindowFrames.mFrame.left),
                      Math.max(mWindowFrames.mVisibleFrame.top, mWindowFrames.mFrame.top),
                      Math.min(mWindowFrames.mVisibleFrame.right, mWindowFrames.mFrame.right),
                      Math.min(mWindowFrames.mVisibleFrame.bottom, mWindowFrames.mFrame.bottom));
  
              mWindowFrames.mStableFrame.set(
                      Math.max(mWindowFrames.mStableFrame.left, mWindowFrames.mFrame.left),
                      Math.max(mWindowFrames.mStableFrame.top, mWindowFrames.mFrame.top),
                      Math.min(mWindowFrames.mStableFrame.right, mWindowFrames.mFrame.right),
                      Math.min(mWindowFrames.mStableFrame.bottom, mWindowFrames.mFrame.bottom));
          }
  
          if (isFullscreenAndFillsDisplay && !windowsAreFloating) {
              // Windows that are not fullscreen can be positioned outside of the display frame,
              // but that is not a reason to provide them with overscan insets.
              InsetUtils.insetsBetweenFrames(layoutContainingFrame, mWindowFrames.mOverscanFrame,
                      mWindowFrames.mOverscanInsets);
          }
  
          if (mAttrs.type == TYPE_DOCK_DIVIDER) {
              final WmDisplayCutout c = mWindowFrames.mDisplayCutout.calculateRelativeTo(
                      mWindowFrames.mDisplayFrame);
              mWindowFrames.calculateDockedDividerInsets(c.getDisplayCutout().getSafeInsets());
          } else {
              getDisplayContent().getBounds(mTmpRect);
              mWindowFrames.calculateInsets(
                      windowsAreFloating, isFullscreenAndFillsDisplay, mTmpRect);
          }
  
          mWindowFrames.setDisplayCutout(
                  mWindowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame));
  
          // Offset the actual frame by the amount layout frame is off.
          mWindowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
  
          mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame);
          if (inSizeCompatMode()) {
              // If there is a size compatibility scale being applied to the
              // window, we need to apply this to its insets so that they are
              // reported to the app in its coordinate space.
              mWindowFrames.scaleInsets(mInvGlobalScale);
  
              // Also the scaled frame that we report to the app needs to be
              // adjusted to be in its coordinate space.
              mWindowFrames.mCompatFrame.scale(mInvGlobalScale);
          }
  
          if (mIsWallpaper && (fw != mWindowFrames.mFrame.width()
                  || fh != mWindowFrames.mFrame.height())) {
              final DisplayContent displayContent = getDisplayContent();
              if (displayContent != null) {
                  final DisplayInfo displayInfo = displayContent.getDisplayInfo();
                  getDisplayContent().mWallpaperController.updateWallpaperOffset(this,
                          displayInfo.logicalWidth, displayInfo.logicalHeight, false);
              }
          }
  
          if (DEBUG_LAYOUT || localLOGV) Slog.v(TAG,
                  "Resolving (mRequestedWidth="
                  + mRequestedWidth + ", mRequestedheight="
                  + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
                  + "): frame=" + mWindowFrames.mFrame.toShortString()
                  + " " + mWindowFrames.getInsetsInfo()
                  + " " + mAttrs.getTitle());
      }
  
      // TODO: Look into whether this override is still necessary.
      @Override
      public Rect getBounds() {
          if (mAppToken != null) {
              return mAppToken.getBounds();
          } else {
              return super.getBounds();
          }
      }
  
      @Override
      public Rect getFrameLw() {
          return mWindowFrames.mFrame;
      }
  
      @Override
      public Rect getDisplayFrameLw() {
          return mWindowFrames.mDisplayFrame;
      }
  
      @Override
      public Rect getOverscanFrameLw() {
          return mWindowFrames.mOverscanFrame;
      }
  
      @Override
      public Rect getContentFrameLw() {
          return mWindowFrames.mContentFrame;
      }
  
      @Override
      public Rect getVisibleFrameLw() {
          return mWindowFrames.mVisibleFrame;
      }
  
      Rect getStableFrameLw() {
          return mWindowFrames.mStableFrame;
      }
  
      Rect getDecorFrame() {
          return mWindowFrames.mDecorFrame;
      }
  
      Rect getParentFrame() {
          return mWindowFrames.mParentFrame;
      }
  
      Rect getContainingFrame() {
          return mWindowFrames.mContainingFrame;
      }
  
      WmDisplayCutout getWmDisplayCutout() {
          return mWindowFrames.mDisplayCutout;
      }
  
      void getCompatFrame(Rect outFrame) {
          outFrame.set(mWindowFrames.mCompatFrame);
      }
  
      void getCompatFrameSize(Rect outFrame) {
          outFrame.set(0, 0, mWindowFrames.mCompatFrame.width(), mWindowFrames.mCompatFrame.height());
      }
  
      @Override
      public boolean getGivenInsetsPendingLw() {
          return mGivenInsetsPending;
      }
  
      @Override
      public Rect getGivenContentInsetsLw() {
          return mGivenContentInsets;
      }
  
      @Override
      public Rect getGivenVisibleInsetsLw() {
          return mGivenVisibleInsets;
      }
  
      @Override
      public WindowManager.LayoutParams getAttrs() {
          return mAttrs;
      }
  
      @Override
      public boolean getNeedsMenuLw(WindowManagerPolicy.WindowState bottom) {
          return getDisplayContent().getNeedsMenu(this, bottom);
      }
  
      @Override
      public int getSystemUiVisibility() {
          return mSystemUiVisibility;
      }
  
      @Override
      public int getSurfaceLayer() {
          return mLayer;
      }
  
      @Override
      public int getBaseType() {
          return getTopParentWindow().mAttrs.type;
      }
  
      @Override
      public IApplicationToken getAppToken() {
          return mAppToken != null ? mAppToken.appToken : null;
      }
  
      @Override
      public boolean isVoiceInteraction() {
          return mAppToken != null && mAppToken.mVoiceInteraction;
      }
  
      boolean setReportResizeHints() {
          return mWindowFrames.setReportResizeHints();
      }
  
      /**
       * Adds the window to the resizing list if any of the parameters we use to track the window
       * dimensions or insets have changed.
       */
      void updateResizingWindowIfNeeded() {
          final WindowStateAnimator winAnimator = mWinAnimator;
          if (!mHasSurface || getDisplayContent().mLayoutSeq != mLayoutSeq || isGoneForLayoutLw()) {
              return;
          }
  
          final Task task = getTask();
          // In the case of stack bound animations, the window frames will update (unlike other
          // animations which just modify various transformation properties). We don't want to
          // notify the client of frame changes in this case. Not only is it a lot of churn, but
          // the frame may not correspond to the surface size or the onscreen area at various
          // phases in the animation, and the client will become sad and confused.
          if (task != null && task.mStack.isAnimatingBounds()) {
              return;
          }
  
          boolean didFrameInsetsChange = setReportResizeHints();
          boolean configChanged = !isLastConfigReportedToClient();
          if (DEBUG_CONFIGURATION && configChanged) {
              Slog.v(TAG_WM, "Win " + this + " config changed: " + getConfiguration());
          }
  
          final boolean dragResizingChanged = isDragResizeChanged()
                  && !isDragResizingChangeReported();
  
          if (localLOGV) Slog.v(TAG_WM, "Resizing " + this + ": configChanged=" + configChanged
                  + " dragResizingChanged=" + dragResizingChanged
                  + " last=" + mWindowFrames.mLastFrame + " frame=" + mWindowFrames.mFrame);
  
          // We update mLastFrame always rather than in the conditional with the last inset
          // variables, because mFrameSizeChanged only tracks the width and height changing.
          mWindowFrames.mLastFrame.set(mWindowFrames.mFrame);
  
          if (didFrameInsetsChange
                  || winAnimator.mSurfaceResized
                  || configChanged
                  || dragResizingChanged
                  || mReportOrientationChanged) {
              if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
                  Slog.v(TAG_WM, "Resize reasons for w=" + this + ": "
                          + " " + mWindowFrames.getInsetsChangedInfo()
                          + " surfaceResized=" + winAnimator.mSurfaceResized
                          + " configChanged=" + configChanged
                          + " dragResizingChanged=" + dragResizingChanged
                          + " reportOrientationChanged=" + mReportOrientationChanged);
              }
  
              // If it's a dead window left on screen, and the configuration changed, there is nothing
              // we can do about it. Remove the window now.
              if (mAppToken != null && mAppDied) {
                  mAppToken.removeDeadWindows();
                  return;
              }
  
              updateLastInsetValues();
              mWmService.makeWindowFreezingScreenIfNeededLocked(this);
  
              // If the orientation is changing, or we're starting or ending a drag resizing action,
              // then we need to hold off on unfreezing the display until this window has been
              // redrawn; to do that, we need to go through the process of getting informed by the
              // application when it has finished drawing.
              if (getOrientationChanging() || dragResizingChanged) {
                  if (DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
                      Slog.v(TAG_WM, "Orientation or resize start waiting for draw"
                              + ", mDrawState=DRAW_PENDING in " + this
                              + ", surfaceController " + winAnimator.mSurfaceController);
                  }
                  winAnimator.mDrawState = DRAW_PENDING;
                  if (mAppToken != null) {
                      mAppToken.clearAllDrawn();
                  }
              }
              if (!mWmService.mResizingWindows.contains(this)) {
                  if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG_WM, "Resizing window " + this);
                  mWmService.mResizingWindows.add(this);
              }
          } else if (getOrientationChanging()) {
              if (isDrawnLw()) {
                  if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Orientation not waiting for draw in "
                          + this + ", surfaceController " + winAnimator.mSurfaceController);
                  setOrientationChanging(false);
                  mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
                          - mWmService.mDisplayFreezeTime);
              }
          }
      }
  
      boolean getOrientationChanging() {
          // In addition to the local state flag, we must also consider the difference in the last
          // reported configuration vs. the current state. If the client code has not been informed of
          // the change, logic dependent on having finished processing the orientation, such as
          // unfreezing, could be improperly triggered.
          // TODO(b/62846907): Checking against {@link mLastReportedConfiguration} could be flaky as
          //                   this is not necessarily what the client has processed yet. Find a
          //                   better indicator consistent with the client.
          return (mOrientationChanging || (isVisible()
                  && getConfiguration().orientation != getLastReportedConfiguration().orientation))
                  && !mSeamlesslyRotated
                  && !mOrientationChangeTimedOut;
      }
  
      void setOrientationChanging(boolean changing) {
          mOrientationChanging = changing;
          mOrientationChangeTimedOut = false;
      }
  
      void orientationChangeTimedOut() {
          mOrientationChangeTimedOut = true;
      }
  
      @Override
      DisplayContent getDisplayContent() {
          return mToken.getDisplayContent();
      }
  
      @Override
      void onDisplayChanged(DisplayContent dc) {
          super.onDisplayChanged(dc);
          // Window was not laid out for this display yet, so make sure mLayoutSeq does not match.
          if (dc != null && mInputWindowHandle.displayId != dc.getDisplayId()) {
              mLayoutSeq = dc.mLayoutSeq - 1;
              mInputWindowHandle.displayId = dc.getDisplayId();
          }
      }
  
      DisplayInfo getDisplayInfo() {
          final DisplayContent displayContent = getDisplayContent();
          return displayContent != null ? displayContent.getDisplayInfo() : null;
      }
  
      @Override
      public int getDisplayId() {
          final DisplayContent displayContent = getDisplayContent();
          if (displayContent == null) {
              return Display.INVALID_DISPLAY;
          }
          return displayContent.getDisplayId();
      }
  
      Task getTask() {
          return mAppToken != null ? mAppToken.getTask() : null;
      }
  
      TaskStack getStack() {
          Task task = getTask();
          if (task != null) {
              if (task.mStack != null) {
                  return task.mStack;
              }
          }
          // Some system windows (e.g. "Power off" dialog) don't have a task, but we would still
          // associate them with some stack to enable dimming.
          final DisplayContent dc = getDisplayContent();
          return mAttrs.type >= FIRST_SYSTEM_WINDOW && dc != null ? dc.getHomeStack() : null;
      }
  
      /**
       * Retrieves the visible bounds of the window.
       * @param bounds The rect which gets the bounds.
       */
      void getVisibleBounds(Rect bounds) {
          final Task task = getTask();
          boolean intersectWithStackBounds = task != null && task.cropWindowsToStackBounds();
          bounds.setEmpty();
          mTmpRect.setEmpty();
          if (intersectWithStackBounds) {
              final TaskStack stack = task.mStack;
              if (stack != null) {
                  stack.getDimBounds(mTmpRect);
              } else {
                  intersectWithStackBounds = false;
              }
          }
  
          bounds.set(mWindowFrames.mVisibleFrame);
          if (intersectWithStackBounds) {
              bounds.intersect(mTmpRect);
          }
  
          if (bounds.isEmpty()) {
              bounds.set(mWindowFrames.mFrame);
              if (intersectWithStackBounds) {
                  bounds.intersect(mTmpRect);
              }
              return;
          }
      }
  
      public long getInputDispatchingTimeoutNanos() {
          return mAppToken != null
                  ? mAppToken.mInputDispatchingTimeoutNanos
                  : WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
      }
  
      @Override
      public boolean hasAppShownWindows() {
          return mAppToken != null && (mAppToken.firstWindowDrawn || mAppToken.startingDisplayed);
      }
  
      boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
          if (dsdx < .99999f || dsdx > 1.00001f) return false;
          if (dtdy < .99999f || dtdy > 1.00001f) return false;
          if (dtdx < -.000001f || dtdx > .000001f) return false;
          if (dsdy < -.000001f || dsdy > .000001f) return false;
          return true;
      }
  
      void prelayout() {
          if (inSizeCompatMode()) {
              mGlobalScale = mToken.getSizeCompatScale();
              mInvGlobalScale = 1 / mGlobalScale;
          } else {
              mGlobalScale = mInvGlobalScale = 1;
          }
      }
  
      @Override
      boolean hasContentToDisplay() {
          if (!mAppFreezing && isDrawnLw() && (mViewVisibility == View.VISIBLE
                  || (isAnimating() && !getDisplayContent().mAppTransition.isTransitionSet()))) {
              return true;
          }
  
          return super.hasContentToDisplay();
      }
  
      @Override
      boolean isVisible() {
          return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicy()
                  // If we don't have a provider, this window isn't used as a window generating
                  // insets, so nobody can hide it over the inset APIs.
                  && (mInsetProvider == null || mInsetProvider.isClientVisible());
      }
  
      /**
       * Ensures that all the policy visibility bits are set.
       * @return {@code true} if all flags about visiblity are set
       */
      boolean isVisibleByPolicy() {
          return (mPolicyVisibility & POLICY_VISIBILITY_ALL) == POLICY_VISIBILITY_ALL;
      }
  
      void clearPolicyVisibilityFlag(int policyVisibilityFlag) {
          mPolicyVisibility &= ~policyVisibilityFlag;
          mWmService.scheduleAnimationLocked();
      }
  
      void setPolicyVisibilityFlag(int policyVisibilityFlag) {
          mPolicyVisibility |= policyVisibilityFlag;
          mWmService.scheduleAnimationLocked();
      }
  
      private boolean isLegacyPolicyVisibility() {
          return (mPolicyVisibility & LEGACY_POLICY_VISIBILITY) != 0;
      }
  
      /**
       * @return {@code true} if the window would be visible if we'd ignore policy visibility,
       *         {@code false} otherwise.
       */
      boolean wouldBeVisibleIfPolicyIgnored() {
          return mHasSurface && !isParentWindowHidden()
                  && !mAnimatingExit && !mDestroying && (!mIsWallpaper || mWallpaperVisible);
      }
  
      @Override
      public boolean isVisibleLw() {
          return isVisible();
      }
  
      /**
       * Is this window visible, ignoring its app token? It is not visible if there is no surface,
       * or we are in the process of running an exit animation that will remove the surface.
       */
      // TODO: Can we consolidate this with #isVisible() or have a more appropriate name for this?
      boolean isWinVisibleLw() {
          return (mAppToken == null || !mAppToken.hiddenRequested || mAppToken.isSelfAnimating())
                  && isVisible();
      }
  
      /**
       * The same as isVisible(), but follows the current hidden state of the associated app token,
       * not the pending requested hidden state.
       */
      boolean isVisibleNow() {
          return (!mToken.isHidden() || mAttrs.type == TYPE_APPLICATION_STARTING)
                  && isVisible();
      }
  
      /**
       * Can this window possibly be a drag/drop target?  The test here is
       * a combination of the above "visible now" with the check that the
       * Input Manager uses when discarding windows from input consideration.
       */
      boolean isPotentialDragTarget() {
          return isVisibleNow() && !mRemoved
                  && mInputChannel != null && mInputWindowHandle != null;
      }
  
      /**
       * Same as isVisible(), but we also count it as visible between the
       * call to IWindowSession.add() and the first relayout().
       */
      boolean isVisibleOrAdding() {
          final AppWindowToken atoken = mAppToken;
          return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
                  && isVisibleByPolicy() && !isParentWindowHidden()
                  && (atoken == null || !atoken.hiddenRequested)
                  && !mAnimatingExit && !mDestroying;
      }
  
      /**
       * Is this window currently on-screen?  It is on-screen either if it
       * is visible or it is currently running an animation before no longer
       * being visible.
       */
      boolean isOnScreen() {
          if (!mHasSurface || mDestroying || !isVisibleByPolicy()) {
              return false;
          }
          final AppWindowToken atoken = mAppToken;
          if (atoken != null) {
              return ((!isParentWindowHidden() && !atoken.hiddenRequested)
                      || isAnimating());
          }
          return !isParentWindowHidden() || isAnimating();
      }
  
      /**
       * Whether this window's drawn state might affect the drawn states of the app token.
       *
       * @return true if the window should be considered while evaluating allDrawn flags.
       */
      boolean mightAffectAllDrawn() {
          final boolean isAppType = mWinAnimator.mAttrType == TYPE_BASE_APPLICATION
                  || mWinAnimator.mAttrType == TYPE_DRAWN_APPLICATION;
          return (isOnScreen() || isAppType) && !mAnimatingExit && !mDestroying;
      }
  
      /**
       * Whether this window is "interesting" when evaluating allDrawn. If it's interesting,
       * it must be drawn before allDrawn can become true.
       */
      boolean isInteresting() {
          return mAppToken != null && !mAppDied
                  && (!mAppToken.isFreezingScreen() || !mAppFreezing)
                  && mViewVisibility == View.VISIBLE;
      }
  
      /**
       * Like isOnScreen(), but we don't return true if the window is part
       * of a transition that has not yet been started.
       */
      boolean isReadyForDisplay() {
          if (mToken.waitingToShow && getDisplayContent().mAppTransition.isTransitionSet()) {
              return false;
          }
          final boolean parentAndClientVisible = !isParentWindowHidden()
                  && mViewVisibility == View.VISIBLE && !mToken.isHidden();
          return mHasSurface && isVisibleByPolicy() && !mDestroying
                  && (parentAndClientVisible || isAnimating());
      }
  
      // TODO: Another visibility method that was added late in the release to minimize risk.
      @Override
      public boolean canAffectSystemUiFlags() {
          final boolean translucent = mAttrs.alpha == 0.0f;
          if (translucent) {
              return false;
          }
          if (mAppToken == null) {
              final boolean shown = mWinAnimator.getShown();
              final boolean exiting = mAnimatingExit || mDestroying;
              return shown && !exiting;
          } else {
              final Task task = getTask();
              final boolean canFromTask = task != null && task.canAffectSystemUiFlags();
              return canFromTask && !mAppToken.isHidden();
          }
      }
  
      /**
       * Like isOnScreen, but returns false if the surface hasn't yet
       * been drawn.
       */
      @Override
      public boolean isDisplayedLw() {
          final AppWindowToken atoken = mAppToken;
          return isDrawnLw() && isVisibleByPolicy()
                  && ((!isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested))
                          || isAnimating());
      }
  
      /**
       * Return true if this window or its app token is currently animating.
       */
      @Override
      public boolean isAnimatingLw() {
          return isAnimating();
      }
  
      @Override
      public boolean isGoneForLayoutLw() {
          final AppWindowToken atoken = mAppToken;
          return mViewVisibility == View.GONE
                  || !mRelayoutCalled
                  || (atoken == null && mToken.isHidden())
                  || (atoken != null && atoken.hiddenRequested)
                  || isParentWindowGoneForLayout()
                  || (mAnimatingExit && !isAnimatingLw())
                  || mDestroying;
      }
  
      /**
       * Returns true if the window has a surface that it has drawn a
       * complete UI in to.
       */
      public boolean isDrawFinishedLw() {
          return mHasSurface && !mDestroying &&
                  (mWinAnimator.mDrawState == COMMIT_DRAW_PENDING
                  || mWinAnimator.mDrawState == READY_TO_SHOW
                  || mWinAnimator.mDrawState == HAS_DRAWN);
      }
  
      /**
       * Returns true if the window has a surface that it has drawn a
       * complete UI in to.
       */
      @Override
      public boolean isDrawnLw() {
          return mHasSurface && !mDestroying &&
                  (mWinAnimator.mDrawState == READY_TO_SHOW || mWinAnimator.mDrawState == HAS_DRAWN);
      }
  
      /**
       * Return true if the window is opaque and fully drawn.  This indicates
       * it may obscure windows behind it.
       */
      private boolean isOpaqueDrawn() {
          // When there is keyguard, wallpaper could be placed over the secure app
          // window but invisible. We need to check wallpaper visibility explicitly
          // to determine if it's occluding apps.
          return ((!mIsWallpaper && mAttrs.format == PixelFormat.OPAQUE)
                  || (mIsWallpaper && mWallpaperVisible))
                  && isDrawnLw() && !isAnimating();
      }
  
      @Override
      void onMovedByResize() {
          if (DEBUG_RESIZE) Slog.d(TAG, "onMovedByResize: Moving " + this);
          mMovedByResize = true;
          super.onMovedByResize();
      }
  
      boolean onAppVisibilityChanged(boolean visible, boolean runningAppAnimation) {
          boolean changed = false;
  
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowState c = mChildren.get(i);
              changed |= c.onAppVisibilityChanged(visible, runningAppAnimation);
          }
  
          if (mAttrs.type == TYPE_APPLICATION_STARTING) {
              // Starting window that's exiting will be removed when the animation finishes.
              // Mark all relevant flags for that onExitAnimationDone will proceed all the way
              // to actually remove it.
              if (!visible && isVisibleNow() && mAppToken.isSelfAnimating()) {
                  mAnimatingExit = true;
                  mRemoveOnExit = true;
                  mWindowRemovalAllowed = true;
              }
              return changed;
          }
  
          final boolean isVisibleNow = isVisibleNow();
          if (visible != isVisibleNow) {
              // Run exit animation if:
              // 1. App visibility and WS visibility are different
              // 2. App is not running an animation
              // 3. WS is currently visible
              if (!runningAppAnimation && isVisibleNow) {
                  final AccessibilityController accessibilityController =
                          mWmService.mAccessibilityController;
                  final int winTransit = TRANSIT_EXIT;
                  mWinAnimator.applyAnimationLocked(winTransit, false /* isEntrance */);
                  if (accessibilityController != null) {
                      accessibilityController.onWindowTransitionLocked(this, winTransit);
                  }
              }
              changed = true;
              setDisplayLayoutNeeded();
          }
  
          return changed;
      }
  
      boolean onSetAppExiting() {
          final DisplayContent displayContent = getDisplayContent();
          boolean changed = false;
  
          if (isVisibleNow()) {
              mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
              if (mWmService.mAccessibilityController != null) {
                  mWmService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT);
              }
              changed = true;
              if (displayContent != null) {
                  displayContent.setLayoutNeeded();
              }
          }
  
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowState c = mChildren.get(i);
              changed |= c.onSetAppExiting();
          }
  
          return changed;
      }
  
      @Override
      void onResize() {
          final ArrayList<WindowState> resizingWindows = mWmService.mResizingWindows;
          if (mHasSurface && !isGoneForLayoutLw() && !resizingWindows.contains(this)) {
              if (DEBUG_RESIZE) Slog.d(TAG, "onResize: Resizing " + this);
              resizingWindows.add(this);
          }
          if (isGoneForLayoutLw()) {
              mResizedWhileGone = true;
          }
  
          super.onResize();
      }
  
      void onUnfreezeBounds() {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowState c = mChildren.get(i);
              c.onUnfreezeBounds();
          }
  
          if (!mHasSurface) {
              return;
          }
  
          mLayoutNeeded = true;
          setDisplayLayoutNeeded();
          if (!mWmService.mResizingWindows.contains(this)) {
              mWmService.mResizingWindows.add(this);
          }
      }
  
      /**
       * If the window has moved due to its containing content frame changing, then notify the
       * listeners and optionally animate it. Simply checking a change of position is not enough,
       * because being move due to dock divider is not a trigger for animation.
       */
      void handleWindowMovedIfNeeded() {
          if (!hasMoved()) {
              return;
          }
  
          // Frame has moved, containing content frame has also moved, and we're not currently
          // animating... let's do something.
          final int left = mWindowFrames.mFrame.left;
          final int top = mWindowFrames.mFrame.top;
          final Task task = getTask();
          final boolean adjustedForMinimizedDockOrIme = task != null
                  && (task.mStack.isAdjustedForMinimizedDockedStack()
                  || task.mStack.isAdjustedForIme());
          if (mToken.okToAnimate()
                  && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
                  && !isDragResizing() && !adjustedForMinimizedDockOrIme
                  && getWindowConfiguration().hasMovementAnimations()
                  && !mWinAnimator.mLastHidden
                  && !mSeamlesslyRotated) {
              startMoveAnimation(left, top);
          }
  
          // TODO (multidisplay): Accessibility supported only for the default display and
          // embedded displays
          if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY
                  || getDisplayContent().getParentWindow() != null)) {
              mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
          }
          updateLocationInParentDisplayIfNeeded();
  
          try {
              mClient.moved(left, top);
          } catch (RemoteException e) {
          }
          mMovedByResize = false;
      }
  
      /**
       * Return whether this window has moved. (Only makes
       * sense to call from performLayoutAndPlaceSurfacesLockedInner().)
       */
      private boolean hasMoved() {
          return mHasSurface && (mWindowFrames.hasContentChanged() || mMovedByResize)
                  && !mAnimatingExit
                  && (mWindowFrames.mFrame.top != mWindowFrames.mLastFrame.top
                      || mWindowFrames.mFrame.left != mWindowFrames.mLastFrame.left)
                  && (!mIsChildWindow || !getParentWindow().hasMoved());
      }
  
      boolean isObscuringDisplay() {
          Task task = getTask();
          if (task != null && task.mStack != null && !task.mStack.fillsParent()) {
              return false;
          }
          return isOpaqueDrawn() && fillsDisplay();
      }
  
      boolean fillsDisplay() {
          final DisplayInfo displayInfo = getDisplayInfo();
          return mWindowFrames.mFrame.left <= 0 && mWindowFrames.mFrame.top <= 0
                  && mWindowFrames.mFrame.right >= displayInfo.appWidth
                  && mWindowFrames.mFrame.bottom >= displayInfo.appHeight;
      }
  
      private boolean matchesDisplayBounds() {
          return getDisplayContent().getBounds().equals(getBounds());
      }
  
      /**
       * @return {@code true} if last applied config was reported to the client already, {@code false}
       *         otherwise.
       */
      boolean isLastConfigReportedToClient() {
          return mLastConfigReportedToClient;
      }
  
      @Override
      void onMergedOverrideConfigurationChanged() {
          super.onMergedOverrideConfigurationChanged();
          mLastConfigReportedToClient = false;
      }
  
      void onWindowReplacementTimeout() {
          if (mWillReplaceWindow) {
              // Since the window already timed out, remove it immediately now.
              // Use WindowState#removeImmediately() instead of WindowState#removeIfPossible(), as the latter
              // delays removal on certain conditions, which will leave the stale window in the
              // stack and marked mWillReplaceWindow=false, so the window will never be removed.
              //
              // Also removes child windows.
              removeImmediately();
          } else {
              for (int i = mChildren.size() - 1; i >= 0; --i) {
                  final WindowState c = mChildren.get(i);
                  c.onWindowReplacementTimeout();
              }
          }
      }
  
      @Override
      void forceWindowsScaleableInTransaction(boolean force) {
          if (mWinAnimator != null && mWinAnimator.hasSurface()) {
              mWinAnimator.mSurfaceController.forceScaleableInTransaction(force);
          }
  
          super.forceWindowsScaleableInTransaction(force);
      }
  
      @Override
      void removeImmediately() {
          super.removeImmediately();
  
          if (mRemoved) {
              // Nothing to do.
              if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
                      "WS.removeImmediately: " + this + " Already removed...");
              return;
          }
  
          mRemoved = true;
  
          mWillReplaceWindow = false;
          if (mReplacementWindow != null) {
              mReplacementWindow.mSkipEnterAnimationForSeamlessReplacement = false;
          }
  
          final DisplayContent dc = getDisplayContent();
          if (isInputMethodTarget()) {
              dc.computeImeTarget(true /* updateImeTarget */);
          }
  
          final int type = mAttrs.type;
          if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) {
              dc.mTapExcludedWindows.remove(this);
          }
          if (mTapExcludeRegionHolder != null) {
              // If a tap exclude region container was initialized for this window, then it should've
              // also been registered in display.
              dc.mTapExcludeProvidingWindows.remove(this);
          }
          dc.getDisplayPolicy().removeWindowLw(this);
  
          disposeInputChannel();
  
          mWinAnimator.destroyDeferredSurfaceLocked();
          mWinAnimator.destroySurfaceLocked();
          mSession.windowRemovedLocked();
          try {
              mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
          } catch (RuntimeException e) {
              // Ignore if it has already been removed (usually because
              // we are doing this as part of processing a death note.)
          }
  
          mWmService.postWindowRemoveCleanupLocked(this);
      }
  
      @Override
      void removeIfPossible() {
          super.removeIfPossible();
          removeIfPossible(false /*keepVisibleDeadWindow*/);
      }
  
      private void removeIfPossible(boolean keepVisibleDeadWindow) {
          mWindowRemovalAllowed = true;
          if (DEBUG_ADD_REMOVE) Slog.v(TAG,
                  "removeIfPossible: " + this + " callers=" + Debug.getCallers(5));
  
          final boolean startingWindow = mAttrs.type == TYPE_APPLICATION_STARTING;
          if (startingWindow && DEBUG_STARTING_WINDOW) Slog.d(TAG_WM,
                  "Starting window removed " + this);
  
          if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && isFocused())
              Slog.v(TAG_WM, "Remove " + this + " client="
                          + Integer.toHexString(System.identityHashCode(mClient.asBinder()))
                          + ", surfaceController=" + mWinAnimator.mSurfaceController + " Callers="
                          + Debug.getCallers(5));
  
          final long origId = Binder.clearCallingIdentity();
  
          try {
              disposeInputChannel();
  
              if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Remove " + this
                      + ": mSurfaceController=" + mWinAnimator.mSurfaceController
                      + " mAnimatingExit=" + mAnimatingExit
                      + " mRemoveOnExit=" + mRemoveOnExit
                      + " mHasSurface=" + mHasSurface
                      + " surfaceShowing=" + mWinAnimator.getShown()
                      + " animating=" + isAnimating()
                      + " app-animation="
                      + (mAppToken != null ? mAppToken.isSelfAnimating() : "false")
                      + " mWillReplaceWindow=" + mWillReplaceWindow
                      + " inPendingTransaction="
                      + (mAppToken != null ? mAppToken.inPendingTransaction : false)
                      + " mDisplayFrozen=" + mWmService.mDisplayFrozen
                      + " callers=" + Debug.getCallers(6));
  
              // Visibility of the removed window. Will be used later to update orientation later on.
              boolean wasVisible = false;
  
              final int displayId = getDisplayId();
  
              // First, see if we need to run an animation. If we do, we have to hold off on removing the
              // window until the animation is done. If the display is frozen, just remove immediately,
              // since the animation wouldn't be seen.
              if (mHasSurface && mToken.okToAnimate()) {
                  if (mWillReplaceWindow) {
                      // This window is going to be replaced. We need to keep it around until the new one
                      // gets added, then we will get rid of this one.
                      if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
                              "Preserving " + this + " until the new one is " + "added");
                      // TODO: We are overloading mAnimatingExit flag to prevent the window state from
                      // been removed. We probably need another flag to indicate that window removal
                      // should be deffered vs. overloading the flag that says we are playing an exit
                      // animation.
                      mAnimatingExit = true;
                      mReplacingRemoveRequested = true;
                      return;
                  }
  
                  // If we are not currently running the exit animation, we need to see about starting one
                  wasVisible = isWinVisibleLw();
  
                  if (keepVisibleDeadWindow) {
                      if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
                              "Not removing " + this + " because app died while it's visible");
  
                      mAppDied = true;
                      setDisplayLayoutNeeded();
                      mWmService.mWindowPlacerLocked.performSurfacePlacement();
  
                      // Set up a replacement input channel since the app is now dead.
                      // We need to catch tapping on the dead window to restart the app.
                      openInputChannel(null);
                      getDisplayContent().getInputMonitor().updateInputWindowsLw(true /*force*/);
                      return;
                  }
  
                  if (wasVisible) {
                      final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
  
                      // Try starting an animation.
                      if (mWinAnimator.applyAnimationLocked(transit, false)) {
                          mAnimatingExit = true;
  
                          // mAnimatingExit affects canAffectSystemUiFlags(). Run layout such that
                          // any change from that is performed immediately.
                          setDisplayLayoutNeeded();
                          mWmService.requestTraversal();
                      }
                      if (mWmService.mAccessibilityController != null) {
                          mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit);
                      }
                  }
                  final boolean isAnimating = isAnimating()
                          && (mAppToken == null || !mAppToken.isWaitingForTransitionStart());
                  final boolean lastWindowIsStartingWindow = startingWindow && mAppToken != null
                          && mAppToken.isLastWindow(this);
                  // We delay the removal of a window if it has a showing surface that can be used to run
                  // exit animation and it is marked as exiting.
                  // Also, If isn't the an animating starting window that is the last window in the app.
                  // We allow the removal of the non-animating starting window now as there is no
                  // additional window or animation that will trigger its removal.
                  if (mWinAnimator.getShown() && mAnimatingExit
                          && (!lastWindowIsStartingWindow || isAnimating)) {
                      // The exit animation is running or should run... wait for it!
                      if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
                              "Not removing " + this + " due to exit animation ");
                      setupWindowForRemoveOnExit();
                      if (mAppToken != null) {
                          mAppToken.updateReportedVisibilityLocked();
                      }
                      return;
                  }
              }
  
              removeImmediately();
              // Removing a visible window will effect the computed orientation
              // So just update orientation if needed.
              if (wasVisible) {
                  final DisplayContent displayContent = getDisplayContent();
                  if (displayContent.updateOrientationFromAppTokens()) {
                      displayContent.sendNewConfiguration();
                  }
              }
              mWmService.updateFocusedWindowLocked(isFocused()
                              ? UPDATE_FOCUS_REMOVING_FOCUS
                              : UPDATE_FOCUS_NORMAL,
                      true /*updateInputWindows*/);
          } finally {
              Binder.restoreCallingIdentity(origId);
          }
      }
  
      private void setupWindowForRemoveOnExit() {
          mRemoveOnExit = true;
          setDisplayLayoutNeeded();
          // Request a focus update as this window's input channel is already gone. Otherwise
          // we could have no focused window in input manager.
          final boolean focusChanged = mWmService.updateFocusedWindowLocked(
                  UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
          mWmService.mWindowPlacerLocked.performSurfacePlacement();
          if (focusChanged) {
              getDisplayContent().getInputMonitor().updateInputWindowsLw(false /*force*/);
          }
      }
  
      void setHasSurface(boolean hasSurface) {
          mHasSurface = hasSurface;
      }
  
      boolean canBeImeTarget() {
          if (mIsImWindow) {
              // IME windows can't be IME targets. IME targets are required to be below the IME
              // windows and that wouldn't be possible if the IME window is its own target...silly.
              return false;
          }
  
          final boolean windowsAreFocusable = mAppToken == null || mAppToken.windowsAreFocusable();
          if (!windowsAreFocusable) {
              // This window can't be an IME target if the app's windows should not be focusable.
              return false;
          }
  
          final int fl = mAttrs.flags & (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
          final int type = mAttrs.type;
  
          // Can only be an IME target if both FLAG_NOT_FOCUSABLE and FLAG_ALT_FOCUSABLE_IM are set or
          // both are cleared...and not a starting window.
          if (fl != 0 && fl != (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM)
                  && type != TYPE_APPLICATION_STARTING) {
              return false;
          }
  
          if (DEBUG_INPUT_METHOD) {
              Slog.i(TAG_WM, "isVisibleOrAdding " + this + ": " + isVisibleOrAdding());
              if (!isVisibleOrAdding()) {
                  Slog.i(TAG_WM, "  mSurfaceController=" + mWinAnimator.mSurfaceController
                          + " relayoutCalled=" + mRelayoutCalled
                          + " viewVis=" + mViewVisibility
                          + " policyVis=" + isVisibleByPolicy()
                          + " policyVisAfterAnim=" + mLegacyPolicyVisibilityAfterAnim
                          + " parentHidden=" + isParentWindowHidden()
                          + " exiting=" + mAnimatingExit + " destroying=" + mDestroying);
                  if (mAppToken != null) {
                      Slog.i(TAG_WM, "  mAppToken.hiddenRequested=" + mAppToken.hiddenRequested);
                  }
              }
          }
          return isVisibleOrAdding();
      }
  
      private final class DeadWindowEventReceiver extends InputEventReceiver {
          DeadWindowEventReceiver(InputChannel inputChannel) {
              super(inputChannel, mWmService.mH.getLooper());
          }
          @Override
          public void onInputEvent(InputEvent event) {
              finishInputEvent(event, true);
          }
      }
      /**
       *  Dummy event receiver for windows that died visible.
       */
      private DeadWindowEventReceiver mDeadWindowEventReceiver;
  
      void openInputChannel(InputChannel outInputChannel) {
          if (mInputChannel != null) {
              throw new IllegalStateException("Window already has an input channel.");
          }
          String name = getName();
          InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
          mInputChannel = inputChannels[0];
          mClientChannel = inputChannels[1];
          mInputWindowHandle.token = mClient.asBinder();
          if (outInputChannel != null) {
              mClientChannel.transferTo(outInputChannel);
              mClientChannel.dispose();
              mClientChannel = null;
          } else {
              // If the window died visible, we setup a dummy input channel, so that taps
              // can still detected by input monitor channel, and we can relaunch the app.
              // Create dummy event receiver that simply reports all events as handled.
              mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
          }
          mWmService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
      }
  
      void disposeInputChannel() {
          if (mDeadWindowEventReceiver != null) {
              mDeadWindowEventReceiver.dispose();
              mDeadWindowEventReceiver = null;
          }
  
          // unregister server channel first otherwise it complains about broken channel
          if (mInputChannel != null) {
              mWmService.mInputManager.unregisterInputChannel(mInputChannel);
  
              mInputChannel.dispose();
              mInputChannel = null;
          }
          if (mClientChannel != null) {
              mClientChannel.dispose();
              mClientChannel = null;
          }
          mInputWindowHandle.token = null;
      }
  
      /** Returns true if the replacement window was removed. */
      boolean removeReplacedWindowIfNeeded(WindowState replacement) {
          if (mWillReplaceWindow && mReplacementWindow == replacement && replacement.hasDrawnLw()) {
              replacement.mSkipEnterAnimationForSeamlessReplacement = false;
              removeReplacedWindow();
              return true;
          }
  
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowState c = mChildren.get(i);
              if (c.removeReplacedWindowIfNeeded(replacement)) {
                  return true;
              }
          }
          return false;
      }
  
      private void removeReplacedWindow() {
          if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + this);
          mWillReplaceWindow = false;
          mAnimateReplacingWindow = false;
          mReplacingRemoveRequested = false;
          mReplacementWindow = null;
          if (mAnimatingExit || !mAnimateReplacingWindow) {
              removeImmediately();
          }
      }
  
      boolean setReplacementWindowIfNeeded(WindowState replacementCandidate) {
          boolean replacementSet = false;
  
          if (mWillReplaceWindow && mReplacementWindow == null
                  && getWindowTag().toString().equals(replacementCandidate.getWindowTag().toString())) {
  
              mReplacementWindow = replacementCandidate;
              replacementCandidate.mSkipEnterAnimationForSeamlessReplacement = !mAnimateReplacingWindow;
              replacementSet = true;
          }
  
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowState c = mChildren.get(i);
              replacementSet |= c.setReplacementWindowIfNeeded(replacementCandidate);
          }
  
          return replacementSet;
      }
  
      void setDisplayLayoutNeeded() {
          final DisplayContent dc = getDisplayContent();
          if (dc != null) {
              dc.setLayoutNeeded();
          }
      }
  
      void applyAdjustForImeIfNeeded() {
          final Task task = getTask();
          if (task != null && task.mStack != null && task.mStack.isAdjustedForIme()) {
              task.mStack.applyAdjustForImeIfNeeded(task);
          }
      }
  
      @Override
      void switchUser() {
          super.switchUser();
          if (isHiddenFromUserLocked()) {
              if (DEBUG_VISIBILITY) Slog.w(TAG_WM, "user changing, hiding " + this
                      + ", attrs=" + mAttrs.type + ", belonging to " + mOwnerUid);
              clearPolicyVisibilityFlag(VISIBLE_FOR_USER);
          } else {
              setPolicyVisibilityFlag(VISIBLE_FOR_USER);
          }
      }
  
      int getSurfaceTouchableRegion(InputWindowHandle inputWindowHandle, int flags) {
          final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
          final Region region = inputWindowHandle.touchableRegion;
          setTouchableRegionCropIfNeeded(inputWindowHandle);
  
          final Rect appOverrideBounds = mAppToken != null
                  ? mAppToken.getResolvedOverrideBounds() : null;
          if (appOverrideBounds != null && !appOverrideBounds.isEmpty()) {
              // There may have touchable letterboxes around the activity, so in order to let the
              // letterboxes are able to receive touch event and slip to activity, the activity with
              // compatibility bounds cannot occupy full screen touchable region.
              if (modal) {
                  // A modal window uses the whole compatibility bounds.
                  flags |= FLAG_NOT_TOUCH_MODAL;
                  mTmpRect.set(0, 0, appOverrideBounds.width(), appOverrideBounds.height());
              } else {
                  // Non-modal uses the application based frame.
                  mTmpRect.set(mWindowFrames.mCompatFrame);
              }
              // The offset of compatibility bounds is applied to surface of {@link #AppWindowToken}
              // and frame, so it is unnecessary to translate twice in surface based coordinates.
              final int surfaceOffsetX = mAppToken.inSizeCompatMode()
                      ? mAppToken.getBounds().left : 0;
              mTmpRect.offset(surfaceOffsetX - mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
              region.set(mTmpRect);
              return flags;
          }
  
          if (modal && mAppToken != null) {
              // Limit the outer touch to the activity stack region.
              flags |= FLAG_NOT_TOUCH_MODAL;
              // If the inner bounds of letterbox is available, then it will be used as the touchable
              // region so it won't cover the touchable letterbox and the touch events can slip to
              // activity from letterbox.
              mAppToken.getLetterboxInnerBounds(mTmpRect);
              if (mTmpRect.isEmpty()) {
                  // If this is a modal window we need to dismiss it if it's not full screen and the
                  // touch happens outside of the frame that displays the content. This means we need
                  // to intercept touches outside of that window. The dim layer user associated with
                  // the window (task or stack) will give us the good bounds, as they would be used to
                  // display the dim layer.
                  final Task task = getTask();
                  if (task != null) {
                      task.getDimBounds(mTmpRect);
                  } else {
                      getStack().getDimBounds(mTmpRect);
                  }
              }
              if (inFreeformWindowingMode()) {
                  // For freeform windows we the touch region to include the whole surface for the
                  // shadows.
                  final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
                  final int delta = WindowManagerService.dipToPixel(
                          RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
                  mTmpRect.inset(-delta, -delta);
              }
              region.set(mTmpRect);
              cropRegionToStackBoundsIfNeeded(region);
              subtractTouchExcludeRegionIfNeeded(region);
          } else if (modal && mTapExcludeRegionHolder != null) {
              final Region touchExcludeRegion = Region.obtain();
              amendTapExcludeRegion(touchExcludeRegion);
              if (!touchExcludeRegion.isEmpty()) {
                  // Remove touch modal because there are some areas that cannot be touched.
                  flags |= FLAG_NOT_TOUCH_MODAL;
                  // Give it a large touchable region at first because it was touch modal. The window
                  // might be moved on the display, so the touchable region should be large enough to
                  // ensure it covers the whole display, no matter where it is moved.
                  getDisplayContent().getBounds(mTmpRect);
                  final int dw = mTmpRect.width();
                  final int dh = mTmpRect.height();
                  region.set(-dw, -dh, dw + dw, dh + dh);
                  // Subtract the area that cannot be touched.
                  region.op(touchExcludeRegion, Region.Op.DIFFERENCE);
                  inputWindowHandle.setTouchableRegionCrop(null);
              }
              touchExcludeRegion.recycle();
          } else {
              // Not modal or full screen modal
              getTouchableRegion(region);
          }
          // Translate to surface based coordinates.
          region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
  
          return flags;
      }
  
      void checkPolicyVisibilityChange() {
          if (isLegacyPolicyVisibility() != mLegacyPolicyVisibilityAfterAnim) {
              if (DEBUG_VISIBILITY) {
                  Slog.v(TAG, "Policy visibility changing after anim in " +
                          mWinAnimator + ": " + mLegacyPolicyVisibilityAfterAnim);
              }
              if (mLegacyPolicyVisibilityAfterAnim) {
                  setPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
              } else {
                  clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
              }
              if (!isVisibleByPolicy()) {
                  mWinAnimator.hide("checkPolicyVisibilityChange");
                  if (isFocused()) {
                      if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
                              "setAnimationLocked: setting mFocusMayChange true");
                      mWmService.mFocusMayChange = true;
                  }
                  setDisplayLayoutNeeded();
                  // Window is no longer visible -- make sure if we were waiting
                  // for it to be displayed before enabling the display, that
                  // we allow the display to be enabled now.
                  mWmService.enableScreenIfNeededLocked();
              }
          }
      }
  
      void setRequestedSize(int requestedWidth, int requestedHeight) {
          if ((mRequestedWidth != requestedWidth || mRequestedHeight != requestedHeight)) {
              mLayoutNeeded = true;
              mRequestedWidth = requestedWidth;
              mRequestedHeight = requestedHeight;
          }
      }
  
      void prepareWindowToDisplayDuringRelayout(boolean wasVisible) {
          // We need to turn on screen regardless of visibility.
          boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0;
  
          // The screen will turn on if the following conditions are met
          // 1. The window has the flag FLAG_TURN_SCREEN_ON
          // 2. The WMS allows theater mode.
          // 3. No AWT or the AWT allows the screen to be turned on. This should only be true once
          // per resume to prevent the screen getting getting turned on for each relayout. Set
          // canTurnScreenOn will be set to false so the window doesn't turn the screen on again
          // during this resume.
          // 4. When the screen is not interactive. This is because when the screen is already
          // interactive, the value may persist until the next animation, which could potentially
          // be occurring while turning off the screen. This would lead to the screen incorrectly
          // turning back on.
          if (hasTurnScreenOnFlag) {
              boolean allowTheaterMode = mWmService.mAllowTheaterModeWakeFromLayout
                      || Settings.Global.getInt(mWmService.mContext.getContentResolver(),
                              Settings.Global.THEATER_MODE_ON, 0) == 0;
              boolean canTurnScreenOn = mAppToken == null || mAppToken.canTurnScreenOn();
  
              if (allowTheaterMode && canTurnScreenOn && !mPowerManagerWrapper.isInteractive()) {
                  if (DEBUG_VISIBILITY || DEBUG_POWER) {
                      Slog.v(TAG, "Relayout window turning screen on: " + this);
                  }
                  mPowerManagerWrapper.wakeUp(SystemClock.uptimeMillis(),
                          PowerManager.WAKE_REASON_APPLICATION, "android.server.wm:SCREEN_ON_FLAG");
              }
  
              if (mAppToken != null) {
                  mAppToken.setCanTurnScreenOn(false);
              }
          }
  
          // If we were already visible, skip rest of preparation.
          if (wasVisible) {
              if (DEBUG_VISIBILITY) Slog.v(TAG,
                      "Already visible and does not turn on screen, skip preparing: " + this);
              return;
          }
  
          if ((mAttrs.softInputMode & SOFT_INPUT_MASK_ADJUST)
                  == SOFT_INPUT_ADJUST_RESIZE) {
              mLayoutNeeded = true;
          }
  
          if (isDrawnLw() && mToken.okToAnimate()) {
              mWinAnimator.applyEnterAnimationLocked();
          }
      }
  
      private Configuration getProcessGlobalConfiguration() {
          // For child windows we want to use the pid for the parent window in case the the child
          // window was added from another process.
          final WindowState parentWindow = getParentWindow();
          final int pid = parentWindow != null ? parentWindow.mSession.mPid : mSession.mPid;
          final Configuration processConfig =
                  mWmService.mAtmService.getGlobalConfigurationForPid(pid);
          return processConfig;
      }
  
      void getMergedConfiguration(MergedConfiguration outConfiguration) {
          final Configuration globalConfig = getProcessGlobalConfiguration();
          final Configuration overrideConfig = getMergedOverrideConfiguration();
          outConfiguration.setConfiguration(globalConfig, overrideConfig);
      }
  
      void setLastReportedMergedConfiguration(MergedConfiguration config) {
          mLastReportedConfiguration.setTo(config);
          mLastConfigReportedToClient = true;
      }
  
      void getLastReportedMergedConfiguration(MergedConfiguration config) {
          config.setTo(mLastReportedConfiguration);
      }
  
      private Configuration getLastReportedConfiguration() {
          return mLastReportedConfiguration.getMergedConfiguration();
      }
  
      void adjustStartingWindowFlags() {
          if (mAttrs.type == TYPE_BASE_APPLICATION && mAppToken != null
                  && mAppToken.startingWindow != null) {
              // Special handling of starting window over the base
              // window of the app: propagate lock screen flags to it,
              // to provide the correct semantics while starting.
              final int mask = FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD
                      | FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
              WindowManager.LayoutParams sa = mAppToken.startingWindow.mAttrs;
              sa.flags = (sa.flags & ~mask) | (mAttrs.flags & mask);
          }
      }
  
      void setWindowScale(int requestedWidth, int requestedHeight) {
          final boolean scaledWindow = (mAttrs.flags & FLAG_SCALED) != 0;
  
          if (scaledWindow) {
              // requested{Width|Height} Surface's physical size
              // attrs.{width|height} Size on screen
              // TODO: We don't check if attrs != null here. Is it implicitly checked?
              mHScale = (mAttrs.width  != requestedWidth)  ?
                      (mAttrs.width  / (float)requestedWidth) : 1.0f;
              mVScale = (mAttrs.height != requestedHeight) ?
                      (mAttrs.height / (float)requestedHeight) : 1.0f;
          } else {
              mHScale = mVScale = 1;
          }
      }
  
      private class DeathRecipient implements IBinder.DeathRecipient {
          @Override
          public void binderDied() {
              try {
                  boolean resetSplitScreenResizing = false;
                  synchronized (mWmService.mGlobalLock) {
                      final WindowState win = mWmService
                              .windowForClientLocked(mSession, mClient, false);
                      Slog.i(TAG, "WIN DEATH: " + win);
                      if (win != null) {
                          final DisplayContent dc = getDisplayContent();
                          if (win.mAppToken != null && win.mAppToken.findMainWindow() == win) {
                              mWmService.mTaskSnapshotController.onAppDied(win.mAppToken);
                          }
                          win.removeIfPossible(shouldKeepVisibleDeadAppWindow());
                          if (win.mAttrs.type == TYPE_DOCK_DIVIDER) {
                              // The owner of the docked divider died :( We reset the docked stack,
                              // just in case they have the divider at an unstable position. Better
                              // also reset drag resizing state, because the owner can't do it
                              // anymore.
                              final TaskStack stack =
                                      dc.getSplitScreenPrimaryStackIgnoringVisibility();
                              if (stack != null) {
                                  stack.resetDockedStackToMiddle();
                              }
                              resetSplitScreenResizing = true;
                          }
                      } else if (mHasSurface) {
                          Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
                          WindowState.this.removeIfPossible();
                      }
                  }
                  if (resetSplitScreenResizing) {
                      try {
                          // Note: this calls into ActivityManager, so we must *not* hold the window
                          // manager lock while calling this.
                          mWmService.mActivityTaskManager.setSplitScreenResizing(false);
                      } catch (RemoteException e) {
                          // Local call, shouldn't return RemoteException.
                          throw e.rethrowAsRuntimeException();
                      }
                  }
              } catch (IllegalArgumentException ex) {
                  // This will happen if the window has already been removed.
              }
          }
      }
  
      /**
       * Returns true if this window is visible and belongs to a dead app and shouldn't be removed,
       * because we want to preserve its location on screen to be re-activated later when the user
       * interacts with it.
       */
      private boolean shouldKeepVisibleDeadAppWindow() {
          if (!isWinVisibleLw() || mAppToken == null || mAppToken.isClientHidden()) {
              // Not a visible app window or the app isn't dead.
              return false;
          }
  
          if (mAttrs.token != mClient.asBinder()) {
              // The window was add by a client using another client's app token. We don't want to
              // keep the dead window around for this case since this is meant for 'real' apps.
              return false;
          }
  
          if (mAttrs.type == TYPE_APPLICATION_STARTING) {
              // We don't keep starting windows since they were added by the window manager before
              // the app even launched.
              return false;
          }
  
          return getWindowConfiguration().keepVisibleDeadAppWindowOnScreen();
      }
  
      @Override
      public boolean canReceiveKeys() {
          return isVisibleOrAdding()
                  && (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
                  && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
                  && (mAppToken == null || mAppToken.windowsAreFocusable())
                  && !cantReceiveTouchInput();
      }
  
      @Override
      public boolean canShowWhenLocked() {
          final boolean showBecauseOfActivity =
                  mAppToken != null && mAppToken.mActivityRecord.canShowWhenLocked();
          final boolean showBecauseOfWindow = (getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0;
          return showBecauseOfActivity || showBecauseOfWindow;
      }
  
      /** @return false if this window desires touch events. */
      boolean cantReceiveTouchInput() {
          return mAppToken != null && mAppToken.getTask() != null
                  && (mAppToken.getTask().mStack.shouldIgnoreInput() || mAppToken.hiddenRequested);
      }
  
      @Override
      public boolean hasDrawnLw() {
          return mWinAnimator.mDrawState == WindowStateAnimator.HAS_DRAWN;
      }
  
      @Override
      public boolean showLw(boolean doAnimation) {
          return showLw(doAnimation, true);
      }
  
      boolean showLw(boolean doAnimation, boolean requestAnim) {
          if (isLegacyPolicyVisibility() && mLegacyPolicyVisibilityAfterAnim) {
              // Already showing.
              return false;
          }
          if (isHiddenFromUserLocked()) {
              return false;
          }
          if (!mAppOpVisibility) {
              // Being hidden due to app op request.
              return false;
          }
          if (mPermanentlyHidden) {
              // Permanently hidden until the app exists as apps aren't prepared
              // to handle their windows being removed from under them.
              return false;
          }
          if (mHiddenWhileSuspended) {
              // Being hidden due to owner package being suspended.
              return false;
          }
          if (mForceHideNonSystemOverlayWindow) {
              // This is an alert window that is currently force hidden.
              return false;
          }
          if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this);
          if (doAnimation) {
              if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility="
                      + isLegacyPolicyVisibility() + " animating=" + isAnimating());
              if (!mToken.okToAnimate()) {
                  doAnimation = false;
              } else if (isLegacyPolicyVisibility() && !isAnimating()) {
                  // Check for the case where we are currently visible and
                  // not animating; we do not want to do animation at such a
                  // point to become visible when we already are.
                  doAnimation = false;
              }
          }
          setPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
          mLegacyPolicyVisibilityAfterAnim = true;
          if (doAnimation) {
              mWinAnimator.applyAnimationLocked(TRANSIT_ENTER, true);
          }
          if (requestAnim) {
              mWmService.scheduleAnimationLocked();
          }
          if ((mAttrs.flags & FLAG_NOT_FOCUSABLE) == 0) {
              mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
          }
          return true;
      }
  
      @Override
      public boolean hideLw(boolean doAnimation) {
          return hideLw(doAnimation, true);
      }
  
      boolean hideLw(boolean doAnimation, boolean requestAnim) {
          if (doAnimation) {
              if (!mToken.okToAnimate()) {
                  doAnimation = false;
              }
          }
          boolean current =
                  doAnimation ? mLegacyPolicyVisibilityAfterAnim : isLegacyPolicyVisibility();
          if (!current) {
              // Already hiding.
              return false;
          }
          if (doAnimation) {
              mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
              if (!isAnimating()) {
                  doAnimation = false;
              }
          }
          mLegacyPolicyVisibilityAfterAnim = false;
          final boolean isFocused = isFocused();
          if (!doAnimation) {
              if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this);
              clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
              // Window is no longer visible -- make sure if we were waiting
              // for it to be displayed before enabling the display, that
              // we allow the display to be enabled now.
              mWmService.enableScreenIfNeededLocked();
              if (isFocused) {
                  if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
                          "WindowState.hideLw: setting mFocusMayChange true");
                  mWmService.mFocusMayChange = true;
              }
          }
          if (requestAnim) {
              mWmService.scheduleAnimationLocked();
          }
          if (isFocused) {
              mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
          }
          return true;
      }
  
      void setForceHideNonSystemOverlayWindowIfNeeded(boolean forceHide) {
          if (mOwnerCanAddInternalSystemWindow
                  || (!isSystemAlertWindowType(mAttrs.type) && mAttrs.type != TYPE_TOAST)) {
              return;
          }
          if (mForceHideNonSystemOverlayWindow == forceHide) {
              return;
          }
          mForceHideNonSystemOverlayWindow = forceHide;
          if (forceHide) {
              hideLw(true /* doAnimation */, true /* requestAnim */);
          } else {
              showLw(true /* doAnimation */, true /* requestAnim */);
          }
      }
  
      void setHiddenWhileSuspended(boolean hide) {
          if (mOwnerCanAddInternalSystemWindow
                  || (!isSystemAlertWindowType(mAttrs.type) && mAttrs.type != TYPE_TOAST)) {
              return;
          }
          if (mHiddenWhileSuspended == hide) {
              return;
          }
          mHiddenWhileSuspended = hide;
          if (hide) {
              hideLw(true, true);
          } else {
              showLw(true, true);
          }
      }
  
      private void setAppOpVisibilityLw(boolean state) {
          if (mAppOpVisibility != state) {
              mAppOpVisibility = state;
              if (state) {
                  // If the policy visibility had last been to hide, then this
                  // will incorrectly show at this point since we lost that
                  // information.  Not a big deal -- for the windows that have app
                  // ops modifies they should only be hidden by policy due to the
                  // lock screen, and the user won't be changing this if locked.
                  // Plus it will quickly be fixed the next time we do a layout.
                  showLw(true, true);
              } else {
                  hideLw(true, true);
              }
          }
      }
  
      void initAppOpsState() {
          if (mAppOp == OP_NONE || !mAppOpVisibility) {
              return;
          }
          // If the app op was MODE_DEFAULT we would have checked the permission
          // and add the window only if the permission was granted. Therefore, if
          // the mode is MODE_DEFAULT we want the op to succeed as the window is
          // shown.
          final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp,
                  getOwningUid(), getOwningPackage(), true);
          if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
              setAppOpVisibilityLw(false);
          }
      }
  
      void resetAppOpsState() {
          if (mAppOp != OP_NONE && mAppOpVisibility) {
              mWmService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage());
          }
      }
  
      void updateAppOpsState() {
          if (mAppOp == OP_NONE) {
              return;
          }
          final int uid = getOwningUid();
          final String packageName = getOwningPackage();
          if (mAppOpVisibility) {
              // There is a race between the check and the finish calls but this is fine
              // as this would mean we will get another change callback and will reconcile.
              int mode = mWmService.mAppOps.checkOpNoThrow(mAppOp, uid, packageName);
              if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
                  mWmService.mAppOps.finishOp(mAppOp, uid, packageName);
                  setAppOpVisibilityLw(false);
              }
          } else {
              final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, uid, packageName, true);
              if (mode == MODE_ALLOWED || mode == MODE_DEFAULT) {
                  setAppOpVisibilityLw(true);
              }
          }
      }
  
      public void hidePermanentlyLw() {
          if (!mPermanentlyHidden) {
              mPermanentlyHidden = true;
              hideLw(true, true);
          }
      }
  
      public void pokeDrawLockLw(long timeout) {
          if (isVisibleOrAdding()) {
              if (mDrawLock == null) {
                  // We want the tag name to be somewhat stable so that it is easier to correlate
                  // in wake lock statistics.  So in particular, we don't want to include the
                  // window's hash code as in toString().
                  final CharSequence tag = getWindowTag();
                  mDrawLock = mWmService.mPowerManager.newWakeLock(DRAW_WAKE_LOCK, "Window:" + tag);
                  mDrawLock.setReferenceCounted(false);
                  mDrawLock.setWorkSource(new WorkSource(mOwnerUid, mAttrs.packageName));
              }
              // Each call to acquire resets the timeout.
              if (DEBUG_POWER) {
                  Slog.d(TAG, "pokeDrawLock: poking draw lock on behalf of visible window owned by "
                          + mAttrs.packageName);
              }
              mDrawLock.acquire(timeout);
          } else if (DEBUG_POWER) {
              Slog.d(TAG, "pokeDrawLock: suppressed draw lock request for invisible window "
                      + "owned by " + mAttrs.packageName);
          }
      }
  
      @Override
      public boolean isAlive() {
          return mClient.asBinder().isBinderAlive();
      }
  
      boolean isClosing() {
          return mAnimatingExit || (mAppToken != null && mAppToken.isClosingOrEnteringPip());
      }
  
      void addWinAnimatorToList(ArrayList<WindowStateAnimator> animators) {
          animators.add(mWinAnimator);
  
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowState c = mChildren.get(i);
              c.addWinAnimatorToList(animators);
          }
      }
  
      void sendAppVisibilityToClients() {
          super.sendAppVisibilityToClients();
  
          final boolean clientHidden = mAppToken.isClientHidden();
          if (mAttrs.type == TYPE_APPLICATION_STARTING && clientHidden) {
              // Don't hide the starting window.
              return;
          }
  
          if (clientHidden) {
              // Once we are notifying the client that it's visibility has changed, we need to prevent
              // it from destroying child surfaces until the animation has finished. We do this by
              // detaching any surface control the client added from the client.
              for (int i = mChildren.size() - 1; i >= 0; --i) {
                  final WindowState c = mChildren.get(i);
                  c.mWinAnimator.detachChildren();
              }
  
              mWinAnimator.detachChildren();
          }
  
          try {
              if (DEBUG_VISIBILITY) Slog.v(TAG,
                      "Setting visibility of " + this + ": " + (!clientHidden));
              mClient.dispatchAppVisibility(!clientHidden);
          } catch (RemoteException e) {
          }
      }
  
      void onStartFreezingScreen() {
          mAppFreezing = true;
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowState c = mChildren.get(i);
              c.onStartFreezingScreen();
          }
      }
  
      boolean onStopFreezingScreen() {
          boolean unfrozeWindows = false;
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowState c = mChildren.get(i);
              unfrozeWindows |= c.onStopFreezingScreen();
          }
  
          if (!mAppFreezing) {
              return unfrozeWindows;
          }
  
          mAppFreezing = false;
  
          if (mHasSurface && !getOrientationChanging()
                  && mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
              if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "set mOrientationChanging of " + this);
              setOrientationChanging(true);
              mWmService.mRoot.mOrientationChangeComplete = false;
          }
          mLastFreezeDuration = 0;
          setDisplayLayoutNeeded();
          return true;
      }
  
      boolean destroySurface(boolean cleanupOnResume, boolean appStopped) {
          boolean destroyedSomething = false;
  
          // Copying to a different list as multiple children can be removed.
          final ArrayList<WindowState> childWindows = new ArrayList<>(mChildren);
          for (int i = childWindows.size() - 1; i >= 0; --i) {
              final WindowState c = childWindows.get(i);
              destroyedSomething |= c.destroySurface(cleanupOnResume, appStopped);
          }
  
          if (!(appStopped || mWindowRemovalAllowed || cleanupOnResume)) {
              return destroyedSomething;
          }
  
          if (appStopped || mWindowRemovalAllowed) {
              mWinAnimator.destroyPreservedSurfaceLocked();
          }
  
          if (mDestroying) {
              if (DEBUG_ADD_REMOVE) Slog.e(TAG_WM, "win=" + this
                      + " destroySurfaces: appStopped=" + appStopped
                      + " win.mWindowRemovalAllowed=" + mWindowRemovalAllowed
                      + " win.mRemoveOnExit=" + mRemoveOnExit);
              if (!cleanupOnResume || mRemoveOnExit) {
                  destroySurfaceUnchecked();
              }
              if (mRemoveOnExit) {
                  removeImmediately();
              }
              if (cleanupOnResume) {
                  requestUpdateWallpaperIfNeeded();
              }
              mDestroying = false;
              destroyedSomething = true;
  
              // Since mDestroying will affect AppWindowToken#allDrawn, we need to perform another
              // traversal in case we are waiting on this window to start the transition.
              if (getDisplayContent().mAppTransition.isTransitionSet()
                      && getDisplayContent().mOpeningApps.contains(mAppToken)) {
                  mWmService.mWindowPlacerLocked.requestTraversal();
              }
          }
  
          return destroyedSomething;
      }
  
      // Destroy or save the application surface without checking
      // various indicators of whether the client has released the surface.
      // This is in general unsafe, and most callers should use {@link #destroySurface}
      void destroySurfaceUnchecked() {
          mWinAnimator.destroySurfaceLocked();
  
          // Clear animating flags now, since the surface is now gone. (Note this is true even
          // if the surface is saved, to outside world the surface is still NO_SURFACE.)
          mAnimatingExit = false;
      }
  
      void onSurfaceShownChanged(boolean shown) {
          if (mLastShownChangedReported == shown) {
              return;
          }
          mLastShownChangedReported = shown;
  
          if (shown) {
              initExclusionRestrictions();
          } else {
              logExclusionRestrictions(EXCLUSION_LEFT);
              logExclusionRestrictions(EXCLUSION_RIGHT);
          }
      }
  
      private void logExclusionRestrictions(int side) {
          if (!logsGestureExclusionRestrictions(this)
                  || SystemClock.uptimeMillis() < mLastExclusionLogUptimeMillis[side]
                  + mWmService.mSystemGestureExclusionLogDebounceTimeoutMillis) {
              // Drop the log if we have just logged; this is okay, because what we would have logged
              // was true only for a short duration.
              return;
          }
  
          final long now = SystemClock.uptimeMillis();
          final long duration = now - mLastExclusionLogUptimeMillis[side];
          mLastExclusionLogUptimeMillis[side] = now;
  
          final int requested = mLastRequestedExclusionHeight[side];
          final int granted = mLastGrantedExclusionHeight[side];
  
          StatsLog.write(StatsLog.EXCLUSION_RECT_STATE_CHANGED,
                  mAttrs.packageName, requested, requested - granted /* rejected */,
                  side + 1 /* Sides are 1-indexed in atoms.proto */,
                  (getConfiguration().orientation == ORIENTATION_LANDSCAPE),
                  isSplitScreenWindowingMode(getWindowingMode()), (int) duration);
      }
  
      private void initExclusionRestrictions() {
          final long now = SystemClock.uptimeMillis();
          mLastExclusionLogUptimeMillis[EXCLUSION_LEFT] = now;
          mLastExclusionLogUptimeMillis[EXCLUSION_RIGHT] = now;
      }
  
      @Override
      public boolean isDefaultDisplay() {
          final DisplayContent displayContent = getDisplayContent();
          if (displayContent == null) {
              // Only a window that was on a non-default display can be detached from it.
              return false;
          }
          return displayContent.isDefaultDisplay;
      }
  
      void setShowToOwnerOnlyLocked(boolean showToOwnerOnly) {
          mShowToOwnerOnly = showToOwnerOnly;
      }
  
      private boolean isHiddenFromUserLocked() {
          // Child windows are evaluated based on their parent window.
          final WindowState win = getTopParentWindow();
          if (win.mAttrs.type < FIRST_SYSTEM_WINDOW
                  && win.mAppToken != null && win.mAppToken.mShowForAllUsers) {
  
              // All window frames that are fullscreen extend above status bar, but some don't extend
              // below navigation bar. Thus, check for display frame for top/left and stable frame for
              // bottom right.
              if (win.getFrameLw().left <= win.getDisplayFrameLw().left
                      && win.getFrameLw().top <= win.getDisplayFrameLw().top
                      && win.getFrameLw().right >= win.getStableFrameLw().right
                      && win.getFrameLw().bottom >= win.getStableFrameLw().bottom) {
                  // Is a fullscreen window, like the clock alarm. Show to everyone.
                  return false;
              }
          }
  
          return win.mShowToOwnerOnly
                  && !mWmService.isCurrentProfileLocked(UserHandle.getUserId(win.mOwnerUid));
      }
  
      private static void applyInsets(Region outRegion, Rect frame, Rect inset) {
          outRegion.set(
                  frame.left + inset.left, frame.top + inset.top,
                  frame.right - inset.right, frame.bottom - inset.bottom);
      }
  
      /** Get the touchable region in global coordinates. */
      void getTouchableRegion(Region outRegion) {
          final Rect frame = mWindowFrames.mFrame;
          switch (mTouchableInsets) {
              default:
              case TOUCHABLE_INSETS_FRAME:
                  outRegion.set(frame);
                  break;
              case TOUCHABLE_INSETS_CONTENT:
                  applyInsets(outRegion, frame, mGivenContentInsets);
                  break;
              case TOUCHABLE_INSETS_VISIBLE:
                  applyInsets(outRegion, frame, mGivenVisibleInsets);
                  break;
              case TOUCHABLE_INSETS_REGION: {
                  outRegion.set(mGivenTouchableRegion);
                  outRegion.translate(frame.left, frame.top);
                  break;
              }
          }
          cropRegionToStackBoundsIfNeeded(outRegion);
          subtractTouchExcludeRegionIfNeeded(outRegion);
      }
  
      /**
       * Get the effective touchable region in global coordinates.
       *
       * In contrast to {@link #getTouchableRegion}, this takes into account
       * {@link WindowManager.LayoutParams#FLAG_NOT_TOUCH_MODAL touch modality.}
       */
      void getEffectiveTouchableRegion(Region outRegion) {
          final boolean modal = (mAttrs.flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
          final DisplayContent dc = getDisplayContent();
  
          if (modal && dc != null) {
              outRegion.set(dc.getBounds());
              cropRegionToStackBoundsIfNeeded(outRegion);
              subtractTouchExcludeRegionIfNeeded(outRegion);
          } else {
              getTouchableRegion(outRegion);
          }
      }
  
      private void setTouchableRegionCropIfNeeded(InputWindowHandle handle) {
          final Task task = getTask();
          if (task == null || !task.cropWindowsToStackBounds()) {
              return;
          }
  
          final TaskStack stack = task.mStack;
          if (stack == null) {
              return;
          }
  
          handle.setTouchableRegionCrop(stack.getSurfaceControl());
      }
  
      private void cropRegionToStackBoundsIfNeeded(Region region) {
          final Task task = getTask();
          if (task == null || !task.cropWindowsToStackBounds()) {
              return;
          }
  
          final TaskStack stack = task.mStack;
          if (stack == null) {
              return;
          }
  
          stack.getDimBounds(mTmpRect);
          region.op(mTmpRect, Region.Op.INTERSECT);
      }
  
      /**
       * If this window has areas that cannot be touched, we subtract those areas from its touchable
       * region.
       */
      private void subtractTouchExcludeRegionIfNeeded(Region touchableRegion) {
          if (mTapExcludeRegionHolder == null) {
              return;
          }
          final Region touchExcludeRegion = Region.obtain();
          amendTapExcludeRegion(touchExcludeRegion);
          if (!touchExcludeRegion.isEmpty()) {
              touchableRegion.op(touchExcludeRegion, Region.Op.DIFFERENCE);
          }
          touchExcludeRegion.recycle();
      }
  
      /**
       * Report a focus change.  Must be called with no locks held, and consistently
       * from the same serialized thread (such as dispatched from a handler).
       */
      void reportFocusChangedSerialized(boolean focused, boolean inTouchMode) {
          try {
              mClient.windowFocusChanged(focused, inTouchMode);
          } catch (RemoteException e) {
          }
          if (mFocusCallbacks != null) {
              final int N = mFocusCallbacks.beginBroadcast();
              for (int i=0; i<N; i++) {
                  IWindowFocusObserver obs = mFocusCallbacks.getBroadcastItem(i);
                  try {
                      if (focused) {
                          obs.focusGained(mWindowId.asBinder());
                      } else {
                          obs.focusLost(mWindowId.asBinder());
                      }
                  } catch (RemoteException e) {
                  }
              }
              mFocusCallbacks.finishBroadcast();
          }
      }
  
      @Override
      public Configuration getConfiguration() {
          if (mAppToken != null && mAppToken.mFrozenMergedConfig.size() > 0) {
              return mAppToken.mFrozenMergedConfig.peek();
          }
  
          // If the process has not registered to any display to listen to the configuration change,
          // we can simply return the mFullConfiguration as default.
          if (!registeredForDisplayConfigChanges()) {
              return super.getConfiguration();
          }
  
          // We use the process config this window is associated with as the based global config since
          // the process can override its config, but isn't part of the window hierarchy.
          mTempConfiguration.setTo(getProcessGlobalConfiguration());
          mTempConfiguration.updateFrom(getMergedOverrideConfiguration());
          return mTempConfiguration;
      }
  
      /** @return {@code true} if the process registered to a display as a config listener. */
      private boolean registeredForDisplayConfigChanges() {
          final WindowState parentWindow = getParentWindow();
          final Session session = parentWindow != null ? parentWindow.mSession : mSession;
          // System process or invalid process cannot register to display config change.
          if (session.mPid == MY_PID || session.mPid < 0) return false;
          WindowProcessController app =
                  mWmService.mAtmService.getProcessController(session.mPid, session.mUid);
          if (app == null || !app.registeredForDisplayConfigChanges()) return false;
          return true;
      }
  
      void reportResized() {
          Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
          try {
              if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
                      + ": " + mWindowFrames.mCompatFrame);
              final MergedConfiguration mergedConfiguration =
                      new MergedConfiguration(mWmService.mRoot.getConfiguration(),
                      getMergedOverrideConfiguration());
  
              setLastReportedMergedConfiguration(mergedConfiguration);
  
              if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == DRAW_PENDING)
                  Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
  
              final Rect frame = mWindowFrames.mCompatFrame;
              final Rect overscanInsets = mWindowFrames.mLastOverscanInsets;
              final Rect contentInsets = mWindowFrames.mLastContentInsets;
              final Rect visibleInsets = mWindowFrames.mLastVisibleInsets;
              final Rect stableInsets = mWindowFrames.mLastStableInsets;
              final Rect outsets = mWindowFrames.mLastOutsets;
              final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING;
              final boolean reportOrientation = mReportOrientationChanged;
              final int displayId = getDisplayId();
              final DisplayCutout displayCutout = getWmDisplayCutout().getDisplayCutout();
              if (mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
                      && mClient instanceof IWindow.Stub) {
                  // To prevent deadlock simulate one-way call if win.mClient is a local object.
                  mWmService.mH.post(new Runnable() {
                      @Override
                      public void run() {
                          try {
                              dispatchResized(frame, overscanInsets, contentInsets, visibleInsets,
                                      stableInsets, outsets, reportDraw, mergedConfiguration,
                                      reportOrientation, displayId, displayCutout);
                          } catch (RemoteException e) {
                              // Not a remote call, RemoteException won't be raised.
                          }
                      }
                  });
              } else {
                  dispatchResized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
                          outsets, reportDraw, mergedConfiguration, reportOrientation, displayId,
                          displayCutout);
              }
  
              // TODO (multidisplay): Accessibility supported only for the default display and
              // embedded displays
              if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY
                      || getDisplayContent().getParentWindow() != null)) {
                  mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
              }
              updateLocationInParentDisplayIfNeeded();
  
              mWindowFrames.resetInsetsChanged();
              mWinAnimator.mSurfaceResized = false;
              mReportOrientationChanged = false;
          } catch (RemoteException e) {
              setOrientationChanging(false);
              mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
                      - mWmService.mDisplayFreezeTime);
              // We are assuming the hosting process is dead or in a zombie state.
              Slog.w(TAG, "Failed to report 'resized' to the client of " + this
                      + ", removing this window.");
              mWmService.mPendingRemove.add(this);
              mWmService.mWindowPlacerLocked.requestTraversal();
          }
          Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
      }
  
      void updateLocationInParentDisplayIfNeeded() {
          final int embeddedDisplayContentsSize = mEmbeddedDisplayContents.size();
          // If there is any embedded display which is re-parented to this window, we need to
          // notify all windows in the embedded display about the location change.
          if (embeddedDisplayContentsSize != 0) {
              for (int i = embeddedDisplayContentsSize - 1; i >= 0; i--) {
                  final DisplayContent edc = mEmbeddedDisplayContents.valueAt(i);
                  edc.notifyLocationInParentDisplayChanged();
              }
          }
          // If this window is in a embedded display which is re-parented to another window,
          // we may need to update its correct on-screen location.
          final DisplayContent dc = getDisplayContent();
          if (dc.getParentWindow() == null) {
              return;
          }
  
          final Point offset = dc.getLocationInParentDisplay();
          if (mLastReportedDisplayOffset.equals(offset)) {
              return;
          }
  
          mLastReportedDisplayOffset.set(offset.x, offset.y);
          try {
              mClient.locationInParentDisplayChanged(mLastReportedDisplayOffset);
          } catch (RemoteException e) {
              Slog.e(TAG, "Failed to update offset from DisplayContent", e);
          }
      }
  
      /**
       * Called when the insets state changed.
       */
      void notifyInsetsChanged() {
          try {
              mClient.insetsChanged(
                      getDisplayContent().getInsetsStateController().getInsetsForDispatch(this));
          } catch (RemoteException e) {
              Slog.w(TAG, "Failed to deliver inset state change", e);
          }
      }
  
      void notifyInsetsControlChanged() {
          final InsetsStateController stateController =
                  getDisplayContent().getInsetsStateController();
          try {
              mClient.insetsControlChanged(stateController.getInsetsForDispatch(this),
                      stateController.getControlsForDispatch(this));
          } catch (RemoteException e) {
              Slog.w(TAG, "Failed to deliver inset state change", e);
          }
      }
  
      Rect getBackdropFrame(Rect frame) {
          // When the task is docked, we send fullscreen sized backDropFrame as soon as resizing
          // start even if we haven't received the relayout window, so that the client requests
          // the relayout sooner. When dragging stops, backDropFrame needs to stay fullscreen
          // until the window to small size, otherwise the multithread renderer will shift last
          // one or more frame to wrong offset. So here we send fullscreen backdrop if either
          // isDragResizing() or isDragResizeChanged() is true.
          boolean resizing = isDragResizing() || isDragResizeChanged();
          if (getWindowConfiguration().useWindowFrameForBackdrop() || !resizing) {
              // Surface position is now inherited from parent, and BackdropFrameRenderer uses
              // backdrop frame to position content. Thus we just keep the size of backdrop frame, and
              // remove the offset to avoid double offset from display origin.
              mTmpRect.set(frame);
              mTmpRect.offsetTo(0, 0);
              return mTmpRect;
          }
          final DisplayInfo displayInfo = getDisplayInfo();
          mTmpRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
          return mTmpRect;
      }
  
      private int getStackId() {
          final TaskStack stack = getStack();
          if (stack == null) {
              return INVALID_STACK_ID;
          }
          return stack.mStackId;
      }
  
      private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
              Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
              MergedConfiguration mergedConfiguration, boolean reportOrientation, int displayId,
              DisplayCutout displayCutout)
              throws RemoteException {
          final boolean forceRelayout = isDragResizeChanged() || reportOrientation;
  
          mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets,
                  reportDraw, mergedConfiguration, getBackdropFrame(frame), forceRelayout,
                  getDisplayContent().getDisplayPolicy().areSystemBarsForcedShownLw(this), displayId,
                  new DisplayCutout.ParcelableWrapper(displayCutout));
          mDragResizingChangeReported = true;
      }
  
      public void registerFocusObserver(IWindowFocusObserver observer) {
          synchronized (mWmService.mGlobalLock) {
              if (mFocusCallbacks == null) {
                  mFocusCallbacks = new RemoteCallbackList<IWindowFocusObserver>();
              }
              mFocusCallbacks.register(observer);
          }
      }
  
      public void unregisterFocusObserver(IWindowFocusObserver observer) {
          synchronized (mWmService.mGlobalLock) {
              if (mFocusCallbacks != null) {
                  mFocusCallbacks.unregister(observer);
              }
          }
      }
  
      boolean isFocused() {
          return getDisplayContent().mCurrentFocus == this;
      }
  
  
      /** Is this window in a container that takes up the entire screen space? */
      private boolean inAppWindowThatMatchesParentBounds() {
          return mAppToken == null || (mAppToken.matchParentBounds() && !inMultiWindowMode());
      }
  
      /** @return true when the window is in fullscreen mode, but has non-fullscreen bounds set, or
       *          is transitioning into/out-of fullscreen. */
      boolean isLetterboxedAppWindow() {
          return !inMultiWindowMode() && !matchesDisplayBounds()
                  || isLetterboxedForDisplayCutoutLw();
      }
  
      @Override
      public boolean isLetterboxedForDisplayCutoutLw() {
          if (mAppToken == null) {
              // Only windows with an AppWindowToken are letterboxed.
              return false;
          }
          if (!mWindowFrames.parentFrameWasClippedByDisplayCutout()) {
              // Cutout didn't make a difference, no letterbox
              return false;
          }
          if (mAttrs.layoutInDisplayCutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
              // Layout in cutout, no letterbox.
              return false;
          }
          if (!mAttrs.isFullscreen()) {
              // Not filling the parent frame, no letterbox
              return false;
          }
          // Otherwise we need a letterbox if the layout was smaller than the app window token allowed
          // it to be.
          return !frameCoversEntireAppTokenBounds();
      }
  
      /**
       * @return true if this window covers the entire bounds of its app window token
       * @throws NullPointerException if there is no app window token for this window
       */
      private boolean frameCoversEntireAppTokenBounds() {
          mTmpRect.set(mAppToken.getBounds());
          mTmpRect.intersectUnchecked(mWindowFrames.mFrame);
          return mAppToken.getBounds().equals(mTmpRect);
      }
  
      @Override
      public boolean isLetterboxedOverlappingWith(Rect rect) {
          return mAppToken != null && mAppToken.isLetterboxOverlappingWith(rect);
      }
  
      boolean isDragResizeChanged() {
          return mDragResizing != computeDragResizing();
      }
  
      @Override
      void setWaitingForDrawnIfResizingChanged() {
          if (isDragResizeChanged()) {
              mWmService.mWaitingForDrawn.add(this);
          }
          super.setWaitingForDrawnIfResizingChanged();
      }
  
      /**
       * @return Whether we reported a drag resize change to the application or not already.
       */
      private boolean isDragResizingChangeReported() {
          return mDragResizingChangeReported;
      }
  
      /**
       * Resets the state whether we reported a drag resize change to the app.
       */
      @Override
      void resetDragResizingChangeReported() {
          mDragResizingChangeReported = false;
          super.resetDragResizingChangeReported();
      }
  
      int getResizeMode() {
          return mResizeMode;
      }
  
      private boolean computeDragResizing() {
          final Task task = getTask();
          if (task == null) {
              return false;
          }
          if (!inSplitScreenWindowingMode() && !inFreeformWindowingMode()) {
              return false;
          }
          if (mAttrs.width != MATCH_PARENT || mAttrs.height != MATCH_PARENT) {
              // Floating windows never enter drag resize mode.
              return false;
          }
          if (task.isDragResizing()) {
              return true;
          }
  
          // If the bounds are currently frozen, it means that the layout size that the app sees
          // and the bounds we clip this window to might be different. In order to avoid holes, we
          // simulate that we are still resizing so the app fills the hole with the resizing
          // background.
          return (getDisplayContent().mDividerControllerLocked.isResizing()
                          || mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) &&
                  !task.inFreeformWindowingMode() && !isGoneForLayoutLw();
  
      }
  
      void setDragResizing() {
          final boolean resizing = computeDragResizing();
          if (resizing == mDragResizing) {
              return;
          }
          mDragResizing = resizing;
          final Task task = getTask();
          if (task != null && task.isDragResizing()) {
              mResizeMode = task.getDragResizeMode();
          } else {
              mResizeMode = mDragResizing && getDisplayContent().mDividerControllerLocked.isResizing()
                      ? DRAG_RESIZE_MODE_DOCKED_DIVIDER
                      : DRAG_RESIZE_MODE_FREEFORM;
          }
      }
  
      boolean isDragResizing() {
          return mDragResizing;
      }
  
      boolean isDockedResizing() {
          return (mDragResizing && getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER)
                  || (isChildWindow() && getParentWindow().isDockedResizing());
      }
  
      @CallSuper
      @Override
      public void writeToProto(ProtoOutputStream proto, long fieldId,
              @WindowTraceLogLevel int logLevel) {
          boolean isVisible = isVisible();
          if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible) {
              return;
          }
  
          final long token = proto.start(fieldId);
          super.writeToProto(proto, WINDOW_CONTAINER, logLevel);
          writeIdentifierToProto(proto, IDENTIFIER);
          proto.write(DISPLAY_ID, getDisplayId());
          proto.write(STACK_ID, getStackId());
          mAttrs.writeToProto(proto, ATTRIBUTES);
          mGivenContentInsets.writeToProto(proto, GIVEN_CONTENT_INSETS);
          mWindowFrames.writeToProto(proto, WINDOW_FRAMES);
          mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS);
          mSurfacePosition.writeToProto(proto, SURFACE_POSITION);
          mWinAnimator.writeToProto(proto, ANIMATOR);
          proto.write(ANIMATING_EXIT, mAnimatingExit);
          for (int i = 0; i < mChildren.size(); i++) {
              mChildren.get(i).writeToProto(proto, CHILD_WINDOWS, logLevel);
          }
          proto.write(REQUESTED_WIDTH, mRequestedWidth);
          proto.write(REQUESTED_HEIGHT, mRequestedHeight);
          proto.write(VIEW_VISIBILITY, mViewVisibility);
          proto.write(SYSTEM_UI_VISIBILITY, mSystemUiVisibility);
          proto.write(HAS_SURFACE, mHasSurface);
          proto.write(IS_READY_FOR_DISPLAY, isReadyForDisplay());
          proto.write(REMOVE_ON_EXIT, mRemoveOnExit);
          proto.write(DESTROYING, mDestroying);
          proto.write(REMOVED, mRemoved);
          proto.write(IS_ON_SCREEN, isOnScreen());
          proto.write(IS_VISIBLE, isVisible);
          proto.write(PENDING_SEAMLESS_ROTATION, mPendingSeamlessRotate != null);
          proto.write(FINISHED_SEAMLESS_ROTATION_FRAME, mFinishSeamlessRotateFrameNumber);
          proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
          proto.end(token);
      }
  
      @Override
      public void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
          final long token = proto.start(fieldId);
          proto.write(HASH_CODE, System.identityHashCode(this));
          proto.write(USER_ID, UserHandle.getUserId(mOwnerUid));
          final CharSequence title = getWindowTag();
          if (title != null) {
              proto.write(TITLE, title.toString());
          }
          proto.end(token);
      }
  
      @Override
      void dump(PrintWriter pw, String prefix, boolean dumpAll) {
          final TaskStack stack = getStack();
          pw.print(prefix + "mDisplayId=" + getDisplayId());
          if (stack != null) {
              pw.print(" stackId=" + stack.mStackId);
          }
          pw.println(" mSession=" + mSession
                  + " mClient=" + mClient.asBinder());
          pw.println(prefix + "mOwnerUid=" + mOwnerUid
                  + " mShowToOwnerOnly=" + mShowToOwnerOnly
                  + " package=" + mAttrs.packageName
                  + " appop=" + AppOpsManager.opToName(mAppOp));
          pw.println(prefix + "mAttrs=" + mAttrs.toString(prefix));
          pw.println(prefix + "Requested w=" + mRequestedWidth
                  + " h=" + mRequestedHeight
                  + " mLayoutSeq=" + mLayoutSeq);
          if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
              pw.println(prefix + "LastRequested w=" + mLastRequestedWidth
                      + " h=" + mLastRequestedHeight);
          }
          if (mIsChildWindow || mLayoutAttached) {
              pw.println(prefix + "mParentWindow=" + getParentWindow()
                      + " mLayoutAttached=" + mLayoutAttached);
          }
          if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) {
              pw.println(prefix + "mIsImWindow=" + mIsImWindow
                      + " mIsWallpaper=" + mIsWallpaper
                      + " mIsFloatingLayer=" + mIsFloatingLayer
                      + " mWallpaperVisible=" + mWallpaperVisible);
          }
          if (dumpAll) {
              pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
                      pw.print(" mSubLayer="); pw.print(mSubLayer);
          }
          if (dumpAll) {
              pw.println(prefix + "mToken=" + mToken);
              if (mAppToken != null) {
                  pw.println(prefix + "mAppToken=" + mAppToken);
                  pw.print(prefix + "mAppDied=" + mAppDied);
                  pw.print(prefix + "drawnStateEvaluated=" + getDrawnStateEvaluated());
                  pw.println(prefix + "mightAffectAllDrawn=" + mightAffectAllDrawn());
              }
              pw.println(prefix + "mViewVisibility=0x" + Integer.toHexString(mViewVisibility)
                      + " mHaveFrame=" + mHaveFrame
                      + " mObscured=" + mObscured);
              pw.println(prefix + "mSeq=" + mSeq
                      + " mSystemUiVisibility=0x" + Integer.toHexString(mSystemUiVisibility));
          }
          if (!isVisibleByPolicy() || !mLegacyPolicyVisibilityAfterAnim || !mAppOpVisibility
                  || isParentWindowHidden() || mPermanentlyHidden || mForceHideNonSystemOverlayWindow
                  || mHiddenWhileSuspended) {
              pw.println(prefix + "mPolicyVisibility=" + isVisibleByPolicy()
                      + " mLegacyPolicyVisibilityAfterAnim=" + mLegacyPolicyVisibilityAfterAnim
                      + " mAppOpVisibility=" + mAppOpVisibility
                      + " parentHidden=" + isParentWindowHidden()
                      + " mPermanentlyHidden=" + mPermanentlyHidden
                      + " mHiddenWhileSuspended=" + mHiddenWhileSuspended
                      + " mForceHideNonSystemOverlayWindow=" + mForceHideNonSystemOverlayWindow);
          }
          if (!mRelayoutCalled || mLayoutNeeded) {
              pw.println(prefix + "mRelayoutCalled=" + mRelayoutCalled
                      + " mLayoutNeeded=" + mLayoutNeeded);
          }
          if (dumpAll) {
              pw.println(prefix + "mGivenContentInsets=" + mGivenContentInsets.toShortString(sTmpSB)
                      + " mGivenVisibleInsets=" + mGivenVisibleInsets.toShortString(sTmpSB));
              if (mTouchableInsets != 0 || mGivenInsetsPending) {
                  pw.println(prefix + "mTouchableInsets=" + mTouchableInsets
                          + " mGivenInsetsPending=" + mGivenInsetsPending);
                  Region region = new Region();
                  getTouchableRegion(region);
                  pw.println(prefix + "touchable region=" + region);
              }
              pw.println(prefix + "mFullConfiguration=" + getConfiguration());
              pw.println(prefix + "mLastReportedConfiguration=" + getLastReportedConfiguration());
          }
          pw.println(prefix + "mHasSurface=" + mHasSurface
                  + " isReadyForDisplay()=" + isReadyForDisplay()
                  + " mWindowRemovalAllowed=" + mWindowRemovalAllowed);
          if (inSizeCompatMode()) {
              pw.println(prefix + "mCompatFrame=" + mWindowFrames.mCompatFrame.toShortString(sTmpSB));
          }
          if (dumpAll) {
              mWindowFrames.dump(pw, prefix);
              pw.println(prefix + " surface=" + mAttrs.surfaceInsets.toShortString(sTmpSB));
          }
          super.dump(pw, prefix, dumpAll);
          pw.println(prefix + mWinAnimator + ":");
          mWinAnimator.dump(pw, prefix + "  ", dumpAll);
          if (mAnimatingExit || mRemoveOnExit || mDestroying || mRemoved) {
              pw.println(prefix + "mAnimatingExit=" + mAnimatingExit
                      + " mRemoveOnExit=" + mRemoveOnExit
                      + " mDestroying=" + mDestroying
                      + " mRemoved=" + mRemoved);
          }
          if (getOrientationChanging() || mAppFreezing || mReportOrientationChanged) {
              pw.println(prefix + "mOrientationChanging=" + mOrientationChanging
                      + " configOrientationChanging="
                      + (getLastReportedConfiguration().orientation != getConfiguration().orientation)
                      + " mAppFreezing=" + mAppFreezing
                      + " mReportOrientationChanged=" + mReportOrientationChanged);
          }
          if (mLastFreezeDuration != 0) {
              pw.print(prefix + "mLastFreezeDuration=");
              TimeUtils.formatDuration(mLastFreezeDuration, pw);
              pw.println();
          }
          pw.print(prefix + "mForceSeamlesslyRotate=" + mForceSeamlesslyRotate
                  + " seamlesslyRotate: pending=");
          if (mPendingSeamlessRotate != null) {
              mPendingSeamlessRotate.dump(pw);
          } else {
              pw.print("null");
          }
          pw.println(" finishedFrameNumber=" + mFinishSeamlessRotateFrameNumber);
  
          if (mHScale != 1 || mVScale != 1) {
              pw.println(prefix + "mHScale=" + mHScale
                      + " mVScale=" + mVScale);
          }
          if (mWallpaperX != -1 || mWallpaperY != -1) {
              pw.println(prefix + "mWallpaperX=" + mWallpaperX
                      + " mWallpaperY=" + mWallpaperY);
          }
          if (mWallpaperXStep != -1 || mWallpaperYStep != -1) {
              pw.println(prefix + "mWallpaperXStep=" + mWallpaperXStep
                      + " mWallpaperYStep=" + mWallpaperYStep);
          }
          if (mWallpaperDisplayOffsetX != Integer.MIN_VALUE
                  || mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
              pw.println(prefix + "mWallpaperDisplayOffsetX=" + mWallpaperDisplayOffsetX
                      + " mWallpaperDisplayOffsetY=" + mWallpaperDisplayOffsetY);
          }
          if (mDrawLock != null) {
              pw.println(prefix + "mDrawLock=" + mDrawLock);
          }
          if (isDragResizing()) {
              pw.println(prefix + "isDragResizing=" + isDragResizing());
          }
          if (computeDragResizing()) {
              pw.println(prefix + "computeDragResizing=" + computeDragResizing());
          }
          pw.println(prefix + "isOnScreen=" + isOnScreen());
          pw.println(prefix + "isVisible=" + isVisible());
          pw.println(prefix + "mEmbeddedDisplayContents=" + mEmbeddedDisplayContents);
      }
  
      @Override
      String getName() {
          return Integer.toHexString(System.identityHashCode(this))
                  + " " + getWindowTag();
      }
  
      CharSequence getWindowTag() {
          CharSequence tag = mAttrs.getTitle();
          if (tag == null || tag.length() <= 0) {
              tag = mAttrs.packageName;
          }
          return tag;
      }
  
      @Override
      public String toString() {
          final CharSequence title = getWindowTag();
          if (mStringNameCache == null || mLastTitle != title || mWasExiting != mAnimatingExit) {
              mLastTitle = title;
              mWasExiting = mAnimatingExit;
              mStringNameCache = "Window{" + Integer.toHexString(System.identityHashCode(this))
                      + " u" + UserHandle.getUserId(mOwnerUid)
                      + " " + mLastTitle + (mAnimatingExit ? " EXITING}" : "}");
          }
          return mStringNameCache;
      }
  
      void transformClipRectFromScreenToSurfaceSpace(Rect clipRect) {
          if (mHScale == 1 && mVScale == 1) {
              return;
          }
          if (mHScale >= 0) {
              clipRect.left = (int) (clipRect.left / mHScale);
              clipRect.right = (int) Math.ceil(clipRect.right / mHScale);
          }
          if (mVScale >= 0) {
              clipRect.top = (int) (clipRect.top / mVScale);
              clipRect.bottom = (int) Math.ceil(clipRect.bottom / mVScale);
          }
      }
  
      private void applyGravityAndUpdateFrame(Rect containingFrame, Rect displayFrame) {
          final int pw = containingFrame.width();
          final int ph = containingFrame.height();
          final Task task = getTask();
          final boolean inNonFullscreenContainer = !inAppWindowThatMatchesParentBounds();
          final boolean noLimits = (mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
  
          // We need to fit it to the display if either
          // a) The window is in a fullscreen container, or we don't have a task (we assume fullscreen
          // for the taskless windows)
          // b) If it's a secondary app window, we also need to fit it to the display unless
          // FLAG_LAYOUT_NO_LIMITS is set. This is so we place Popups, dialogs, and similar windows on
          // screen, but SurfaceViews want to be always at a specific location so we don't fit it to
          // the display.
          final boolean fitToDisplay = (task == null || !inNonFullscreenContainer)
                  || ((mAttrs.type != TYPE_BASE_APPLICATION) && !noLimits);
          float x, y;
          int w,h;
  
          final boolean inSizeCompatMode = inSizeCompatMode();
          if ((mAttrs.flags & FLAG_SCALED) != 0) {
              if (mAttrs.width < 0) {
                  w = pw;
              } else if (inSizeCompatMode) {
                  w = (int)(mAttrs.width * mGlobalScale + .5f);
              } else {
                  w = mAttrs.width;
              }
              if (mAttrs.height < 0) {
                  h = ph;
              } else if (inSizeCompatMode) {
                  h = (int)(mAttrs.height * mGlobalScale + .5f);
              } else {
                  h = mAttrs.height;
              }
          } else {
              if (mAttrs.width == MATCH_PARENT) {
                  w = pw;
              } else if (inSizeCompatMode) {
                  w = (int)(mRequestedWidth * mGlobalScale + .5f);
              } else {
                  w = mRequestedWidth;
              }
              if (mAttrs.height == MATCH_PARENT) {
                  h = ph;
              } else if (inSizeCompatMode) {
                  h = (int)(mRequestedHeight * mGlobalScale + .5f);
              } else {
                  h = mRequestedHeight;
              }
          }
  
          if (inSizeCompatMode) {
              x = mAttrs.x * mGlobalScale;
              y = mAttrs.y * mGlobalScale;
          } else {
              x = mAttrs.x;
              y = mAttrs.y;
          }
  
          if (inNonFullscreenContainer && !layoutInParentFrame()) {
              // Make sure window fits in containing frame since it is in a non-fullscreen task as
              // required by {@link Gravity#apply} call.
              w = Math.min(w, pw);
              h = Math.min(h, ph);
          }
  
          // Set mFrame
          Gravity.apply(mAttrs.gravity, w, h, containingFrame,
                  (int) (x + mAttrs.horizontalMargin * pw),
                  (int) (y + mAttrs.verticalMargin * ph), mWindowFrames.mFrame);
  
          // Now make sure the window fits in the overall display frame.
          if (fitToDisplay) {
              Gravity.applyDisplay(mAttrs.gravity, displayFrame, mWindowFrames.mFrame);
          }
  
          // We need to make sure we update the CompatFrame as it is used for
          // cropping decisions, etc, on systems where we lack a decor layer.
          mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame);
          if (inSizeCompatMode) {
              // See comparable block in computeFrameLw.
              mWindowFrames.mCompatFrame.scale(mInvGlobalScale);
          }
      }
  
      boolean isChildWindow() {
          return mIsChildWindow;
      }
  
      boolean layoutInParentFrame() {
          return mIsChildWindow
                  && (mAttrs.privateFlags & PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME) != 0;
      }
  
      /**
       * Returns true if any window added by an application process that if of type
       * {@link android.view.WindowManager.LayoutParams#TYPE_TOAST} or that requires that requires
       * {@link android.app.AppOpsManager#OP_SYSTEM_ALERT_WINDOW} permission should be hidden when
       * this window is visible.
       */
      boolean hideNonSystemOverlayWindowsWhenVisible() {
          return (mAttrs.privateFlags & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0
                  && mSession.mCanHideNonSystemOverlayWindows;
      }
  
      /** Returns the parent window if this is a child of another window, else null. */
      WindowState getParentWindow() {
          // NOTE: We are not calling getParent() directly as the WindowState might be a child of a
          // WindowContainer that isn't a WindowState.
          return (mIsChildWindow) ? ((WindowState) super.getParent()) : null;
      }
  
      /** Returns the topmost parent window if this is a child of another window, else this. */
      WindowState getTopParentWindow() {
          WindowState current = this;
          WindowState topParent = current;
          while (current != null && current.mIsChildWindow) {
              current = current.getParentWindow();
              // Parent window can be null if the child is detached from it's parent already, but
              // someone still has a reference to access it. So, we return the top parent value we
              // already have instead of null.
              if (current != null) {
                  topParent = current;
              }
          }
          return topParent;
      }
  
      boolean isParentWindowHidden() {
          final WindowState parent = getParentWindow();
          return parent != null && parent.mHidden;
      }
  
      private boolean isParentWindowGoneForLayout() {
          final WindowState parent = getParentWindow();
          return parent != null && parent.isGoneForLayoutLw();
      }
  
      void setWillReplaceWindow(boolean animate) {
          for (int i = mChildren.size() - 1; i >= 0; i--) {
              final WindowState c = mChildren.get(i);
              c.setWillReplaceWindow(animate);
          }
  
          if ((mAttrs.privateFlags & PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH) != 0
                  || mAttrs.type == TYPE_APPLICATION_STARTING) {
              // We don't set replacing on starting windows since they are added by window manager and
              // not the client so won't be replaced by the client.
              return;
          }
  
          mWillReplaceWindow = true;
          mReplacementWindow = null;
          mAnimateReplacingWindow = animate;
      }
  
      void clearWillReplaceWindow() {
          mWillReplaceWindow = false;
          mReplacementWindow = null;
          mAnimateReplacingWindow = false;
  
          for (int i = mChildren.size() - 1; i >= 0; i--) {
              final WindowState c = mChildren.get(i);
              c.clearWillReplaceWindow();
          }
      }
  
      boolean waitingForReplacement() {
          if (mWillReplaceWindow) {
              return true;
          }
  
          for (int i = mChildren.size() - 1; i >= 0; i--) {
              final WindowState c = mChildren.get(i);
              if (c.waitingForReplacement()) {
                  return true;
              }
          }
          return false;
      }
  
      void requestUpdateWallpaperIfNeeded() {
          final DisplayContent dc = getDisplayContent();
          if (dc != null && (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
              dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
              dc.setLayoutNeeded();
              mWmService.mWindowPlacerLocked.requestTraversal();
          }
  
          for (int i = mChildren.size() - 1; i >= 0; i--) {
              final WindowState c = mChildren.get(i);
              c.requestUpdateWallpaperIfNeeded();
          }
      }
  
      float translateToWindowX(float x) {
          float winX = x - mWindowFrames.mFrame.left;
          if (inSizeCompatMode()) {
              winX *= mGlobalScale;
          }
          return winX;
      }
  
      float translateToWindowY(float y) {
          float winY = y - mWindowFrames.mFrame.top;
          if (inSizeCompatMode()) {
              winY *= mGlobalScale;
          }
          return winY;
      }
  
      // During activity relaunch due to resize, we sometimes use window replacement
      // for only child windows (as the main window is handled by window preservation)
      // and the big surface.
      //
      // Though windows of TYPE_APPLICATION or TYPE_DRAWN_APPLICATION (as opposed to
      // TYPE_BASE_APPLICATION) are not children in the sense of an attached window,
      // we also want to replace them at such phases, as they won't be covered by window
      // preservation, and in general we expect them to return following relaunch.
      boolean shouldBeReplacedWithChildren() {
          return mIsChildWindow || mAttrs.type == TYPE_APPLICATION
                  || mAttrs.type == TYPE_DRAWN_APPLICATION;
      }
  
      void setWillReplaceChildWindows() {
          if (shouldBeReplacedWithChildren()) {
              setWillReplaceWindow(false /* animate */);
          }
          for (int i = mChildren.size() - 1; i >= 0; i--) {
              final WindowState c = mChildren.get(i);
              c.setWillReplaceChildWindows();
          }
      }
  
      WindowState getReplacingWindow() {
          if (mAnimatingExit && mWillReplaceWindow && mAnimateReplacingWindow) {
              return this;
          }
          for (int i = mChildren.size() - 1; i >= 0; i--) {
              final WindowState c = mChildren.get(i);
              final WindowState replacing = c.getReplacingWindow();
              if (replacing != null) {
                  return replacing;
              }
          }
          return null;
      }
  
      @Override
      public int getRotationAnimationHint() {
          if (mAppToken != null) {
              return mAppToken.mRotationAnimationHint;
          } else {
              return -1;
          }
      }
  
      @Override
      public boolean isInputMethodWindow() {
          return mIsImWindow;
      }
  
      // This must be called while inside a transaction.
      boolean performShowLocked() {
          if (isHiddenFromUserLocked()) {
              if (DEBUG_VISIBILITY) Slog.w(TAG, "hiding " + this + ", belonging to " + mOwnerUid);
              clearPolicyVisibilityFlag(VISIBLE_FOR_USER);
              return false;
          }
  
          logPerformShow("performShow on ");
  
          final int drawState = mWinAnimator.mDrawState;
          if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW)
                  && mAttrs.type != TYPE_APPLICATION_STARTING && mAppToken != null) {
              mAppToken.onFirstWindowDrawn(this, mWinAnimator);
          }
  
          if (mWinAnimator.mDrawState != READY_TO_SHOW || !isReadyForDisplay()) {
              return false;
          }
  
          logPerformShow("Showing ");
  
          mWmService.enableScreenIfNeededLocked();
          mWinAnimator.applyEnterAnimationLocked();
  
          // Force the show in the next prepareSurfaceLocked() call.
          mWinAnimator.mLastAlpha = -1;
          if (DEBUG_ANIM) Slog.v(TAG,
                  "performShowLocked: mDrawState=HAS_DRAWN in " + this);
          mWinAnimator.mDrawState = HAS_DRAWN;
          mWmService.scheduleAnimationLocked();
  
          if (mHidden) {
              mHidden = false;
              final DisplayContent displayContent = getDisplayContent();
  
              for (int i = mChildren.size() - 1; i >= 0; --i) {
                  final WindowState c = mChildren.get(i);
                  if (c.mWinAnimator.mSurfaceController != null) {
                      c.performShowLocked();
                      // It hadn't been shown, which means layout not performed on it, so now we
                      // want to make sure to do a layout.  If called from within the transaction
                      // loop, this will cause it to restart with a new layout.
                      if (displayContent != null) {
                          displayContent.setLayoutNeeded();
                      }
                  }
              }
          }
  
          if (mAttrs.type == TYPE_INPUT_METHOD) {
              getDisplayContent().mDividerControllerLocked.resetImeHideRequested();
          }
  
          return true;
      }
  
      private void logPerformShow(String prefix) {
          if (DEBUG_VISIBILITY
                  || (DEBUG_STARTING_WINDOW_VERBOSE && mAttrs.type == TYPE_APPLICATION_STARTING)) {
              Slog.v(TAG, prefix + this
                      + ": mDrawState=" + mWinAnimator.drawStateToString()
                      + " readyForDisplay=" + isReadyForDisplay()
                      + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING)
                      + " during animation: policyVis=" + isVisibleByPolicy()
                      + " parentHidden=" + isParentWindowHidden()
                      + " tok.hiddenRequested="
                      + (mAppToken != null && mAppToken.hiddenRequested)
                      + " tok.hidden=" + (mAppToken != null && mAppToken.isHidden())
                      + " animating=" + isAnimating()
                      + " tok animating="
                      + (mAppToken != null && mAppToken.isSelfAnimating())
                      + " Callers=" + Debug.getCallers(4));
          }
      }
  
      WindowInfo getWindowInfo() {
          WindowInfo windowInfo = WindowInfo.obtain();
          windowInfo.type = mAttrs.type;
          windowInfo.layer = mLayer;
          windowInfo.token = mClient.asBinder();
          if (mAppToken != null) {
              windowInfo.activityToken = mAppToken.appToken.asBinder();
          }
          windowInfo.title = mAttrs.accessibilityTitle;
          // Panel windows have no public way to set the a11y title directly. Use the
          // regular title as a fallback.
          final boolean isPanelWindow = (mAttrs.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW)
                  && (mAttrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW);
          // Accessibility overlays should have titles that work for accessibility, and can't set
          // the a11y title themselves.
          final boolean isAccessibilityOverlay =
                  windowInfo.type == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
          if (TextUtils.isEmpty(windowInfo.title) && (isPanelWindow || isAccessibilityOverlay)) {
              final CharSequence title = mAttrs.getTitle();
              windowInfo.title = TextUtils.isEmpty(title) ? null : title;
          }
          windowInfo.accessibilityIdOfAnchor = mAttrs.accessibilityIdOfAnchor;
          windowInfo.focused = isFocused();
          Task task = getTask();
          windowInfo.inPictureInPicture = (task != null) && task.inPinnedWindowingMode();
          windowInfo.hasFlagWatchOutsideTouch =
                  (mAttrs.flags & WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) != 0;
  
          if (mIsChildWindow) {
              windowInfo.parentToken = getParentWindow().mClient.asBinder();
          }
  
          final int childCount = mChildren.size();
          if (childCount > 0) {
              if (windowInfo.childTokens == null) {
                  windowInfo.childTokens = new ArrayList(childCount);
              }
              for (int j = 0; j < childCount; j++) {
                  final WindowState child = mChildren.get(j);
                  windowInfo.childTokens.add(child.mClient.asBinder());
              }
          }
          return windowInfo;
      }
  
      @Override
      boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
          if (mChildren.isEmpty()) {
              // The window has no children so we just return it.
              return applyInOrderWithImeWindows(callback, traverseTopToBottom);
          }
  
          if (traverseTopToBottom) {
              return forAllWindowTopToBottom(callback);
          } else {
              return forAllWindowBottomToTop(callback);
          }
      }
  
      private boolean forAllWindowBottomToTop(ToBooleanFunction<WindowState> callback) {
          // We want to consume the negative sublayer children first because they need to appear
          // below the parent, then this window (the parent), and then the positive sublayer children
          // because they need to appear above the parent.
          int i = 0;
          final int count = mChildren.size();
          WindowState child = mChildren.get(i);
  
          while (i < count && child.mSubLayer < 0) {
              if (child.applyInOrderWithImeWindows(callback, false /* traverseTopToBottom */)) {
                  return true;
              }
              i++;
              if (i >= count) {
                  break;
              }
              child = mChildren.get(i);
          }
  
          if (applyInOrderWithImeWindows(callback, false /* traverseTopToBottom */)) {
              return true;
          }
  
          while (i < count) {
              if (child.applyInOrderWithImeWindows(callback, false /* traverseTopToBottom */)) {
                  return true;
              }
              i++;
              if (i >= count) {
                  break;
              }
              child = mChildren.get(i);
          }
  
          return false;
      }
  
      private boolean forAllWindowTopToBottom(ToBooleanFunction<WindowState> callback) {
          // We want to consume the positive sublayer children first because they need to appear
          // above the parent, then this window (the parent), and then the negative sublayer children
          // because they need to appear above the parent.
          int i = mChildren.size() - 1;
          WindowState child = mChildren.get(i);
  
          while (i >= 0 && child.mSubLayer >= 0) {
              if (child.applyInOrderWithImeWindows(callback, true /* traverseTopToBottom */)) {
                  return true;
              }
              --i;
              if (i < 0) {
                  break;
              }
              child = mChildren.get(i);
          }
  
          if (applyInOrderWithImeWindows(callback, true /* traverseTopToBottom */)) {
              return true;
          }
  
          while (i >= 0) {
              if (child.applyInOrderWithImeWindows(callback, true /* traverseTopToBottom */)) {
                  return true;
              }
              --i;
              if (i < 0) {
                  break;
              }
              child = mChildren.get(i);
          }
  
          return false;
      }
  
      private boolean applyImeWindowsIfNeeded(ToBooleanFunction<WindowState> callback,
              boolean traverseTopToBottom) {
          // If this window is the current IME target, so we need to process the IME windows
          // directly above it. The exception is if we are in split screen
          // in which case we process the IME at the DisplayContent level to
          // ensure it is above the docked divider.
          if (isInputMethodTarget() && !inSplitScreenWindowingMode()) {
              if (getDisplayContent().forAllImeWindows(callback, traverseTopToBottom)) {
                  return true;
              }
          }
          return false;
      }
  
      private boolean applyInOrderWithImeWindows(ToBooleanFunction<WindowState> callback,
              boolean traverseTopToBottom) {
          if (traverseTopToBottom) {
              if (applyImeWindowsIfNeeded(callback, traverseTopToBottom)
                      || callback.apply(this)) {
                  return true;
              }
          } else {
              if (callback.apply(this)
                      || applyImeWindowsIfNeeded(callback, traverseTopToBottom)) {
                  return true;
              }
          }
          return false;
      }
  
      WindowState getWindow(Predicate<WindowState> callback) {
          if (mChildren.isEmpty()) {
              return callback.test(this) ? this : null;
          }
  
          // We want to consume the positive sublayer children first because they need to appear
          // above the parent, then this window (the parent), and then the negative sublayer children
          // because they need to appear above the parent.
          int i = mChildren.size() - 1;
          WindowState child = mChildren.get(i);
  
          while (i >= 0 && child.mSubLayer >= 0) {
              if (callback.test(child)) {
                  return child;
              }
              --i;
              if (i < 0) {
                  break;
              }
              child = mChildren.get(i);
          }
  
          if (callback.test(this)) {
              return this;
          }
  
          while (i >= 0) {
              if (callback.test(child)) {
                  return child;
              }
              --i;
              if (i < 0) {
                  break;
              }
              child = mChildren.get(i);
          }
  
          return null;
      }
  
      /**
       * @return True if we our one of our ancestors has {@link #mAnimatingExit} set to true, false
       *         otherwise.
       */
      @VisibleForTesting
      boolean isSelfOrAncestorWindowAnimatingExit() {
          WindowState window = this;
          do {
              if (window.mAnimatingExit) {
                  return true;
              }
              window = window.getParentWindow();
          } while (window != null);
          return false;
      }
  
      void onExitAnimationDone() {
          if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
                  + ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
                  + " selfAnimating=" + isSelfAnimating());
  
          if (!mChildren.isEmpty()) {
              // Copying to a different list as multiple children can be removed.
              final ArrayList<WindowState> childWindows = new ArrayList<>(mChildren);
              for (int i = childWindows.size() - 1; i >= 0; i--) {
                  childWindows.get(i).onExitAnimationDone();
              }
          }
  
          if (mWinAnimator.mEnteringAnimation) {
              mWinAnimator.mEnteringAnimation = false;
              mWmService.requestTraversal();
              // System windows don't have an activity and an app token as a result, but need a way
              // to be informed about their entrance animation end.
              if (mAppToken == null) {
                  try {
                      mClient.dispatchWindowShown();
                  } catch (RemoteException e) {
                  }
              }
          }
  
          if (isSelfAnimating()) {
              return;
          }
  
          // TODO (multidisplay): Accessibility supported only for the default display and
          // embedded displays
          if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY
                  || getDisplayContent().getParentWindow() != null)) {
              mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
          }
  
          if (!isSelfOrAncestorWindowAnimatingExit()) {
              return;
          }
  
          if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG,
                  "Exit animation finished in " + this + ": remove=" + mRemoveOnExit);
  
          mDestroying = true;
  
          final boolean hasSurface = mWinAnimator.hasSurface();
  
          // Use pendingTransaction here so hide is done the same transaction as the other
          // animations when exiting
          mWinAnimator.hide(getPendingTransaction(), "onExitAnimationDone");
  
          // If we have an app token, we ask it to destroy the surface for us, so that it can take
          // care to ensure the activity has actually stopped and the surface is not still in use.
          // Otherwise we add the service to mDestroySurface and allow it to be processed in our next
          // transaction.
          if (mAppToken != null) {
              mAppToken.destroySurfaces();
          } else {
              if (hasSurface) {
                  mWmService.mDestroySurface.add(this);
              }
              if (mRemoveOnExit) {
                  mWmService.mPendingRemove.add(this);
                  mRemoveOnExit = false;
              }
          }
          mAnimatingExit = false;
          getDisplayContent().mWallpaperController.hideWallpapers(this);
      }
  
      boolean clearAnimatingFlags() {
          boolean didSomething = false;
          // We don't want to clear it out for windows that get replaced, because the
          // animation depends on the flag to remove the replaced window.
          //
          // We also don't clear the mAnimatingExit flag for windows which have the
          // mRemoveOnExit flag. This indicates an explicit remove request has been issued
          // by the client. We should let animation proceed and not clear this flag or
          // they won't eventually be removed by WindowStateAnimator#finishExit.
          if (!mWillReplaceWindow && !mRemoveOnExit) {
              // Clear mAnimating flag together with mAnimatingExit. When animation
              // changes from exiting to entering, we need to clear this flag until the
              // new animation gets applied, so that isAnimationStarting() becomes true
              // until then.
              // Otherwise applySurfaceChangesTransaction will fail to skip surface
              // placement for this window during this period, one or more frame will
              // show up with wrong position or scale.
              if (mAnimatingExit) {
                  mAnimatingExit = false;
                  didSomething = true;
              }
              if (mDestroying) {
                  mDestroying = false;
                  mWmService.mDestroySurface.remove(this);
                  didSomething = true;
              }
          }
  
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              didSomething |= (mChildren.get(i)).clearAnimatingFlags();
          }
  
          return didSomething;
      }
  
      public boolean isRtl() {
          return getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
      }
  
      void hideWallpaperWindow(boolean wasDeferred, String reason) {
          for (int j = mChildren.size() - 1; j >= 0; --j) {
              final WindowState c = mChildren.get(j);
              c.hideWallpaperWindow(wasDeferred, reason);
          }
          if (!mWinAnimator.mLastHidden || wasDeferred) {
              mWinAnimator.hide(reason);
              getDisplayContent().mWallpaperController.mDeferredHideWallpaper = null;
              dispatchWallpaperVisibility(false);
              final DisplayContent displayContent = getDisplayContent();
              if (displayContent != null) {
                  displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                  if (DEBUG_LAYOUT_REPEATS) {
                      mWmService.mWindowPlacerLocked.debugLayoutRepeats("hideWallpaperWindow " + this,
                              displayContent.pendingLayoutChanges);
                  }
              }
          }
      }
  
      /**
       * Check wallpaper window for visibility change and notify window if so.
       * @param visible Current visibility.
       */
      void dispatchWallpaperVisibility(final boolean visible) {
          final boolean hideAllowed =
                  getDisplayContent().mWallpaperController.mDeferredHideWallpaper == null;
  
          // Only send notification if the visibility actually changed and we are not trying to hide
          // the wallpaper when we are deferring hiding of the wallpaper.
          if (mWallpaperVisible != visible && (hideAllowed || visible)) {
              mWallpaperVisible = visible;
              try {
                  if (DEBUG_VISIBILITY || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
                          "Updating vis of wallpaper " + this
                                  + ": " + visible + " from:\n" + Debug.getCallers(4, "  "));
                  mClient.dispatchAppVisibility(visible);
              } catch (RemoteException e) {
              }
          }
      }
  
      boolean hasVisibleNotDrawnWallpaper() {
          if (mWallpaperVisible && !isDrawnLw()) {
              return true;
          }
          for (int j = mChildren.size() - 1; j >= 0; --j) {
              final WindowState c = mChildren.get(j);
              if (c.hasVisibleNotDrawnWallpaper()) {
                  return true;
              }
          }
          return false;
      }
  
      void updateReportedVisibility(UpdateReportedVisibilityResults results) {
          for (int i = mChildren.size() - 1; i >= 0; --i) {
              final WindowState c = mChildren.get(i);
              c.updateReportedVisibility(results);
          }
  
          if (mAppFreezing || mViewVisibility != View.VISIBLE
                  || mAttrs.type == TYPE_APPLICATION_STARTING
                  || mDestroying) {
              return;
          }
          if (DEBUG_VISIBILITY) {
              Slog.v(TAG, "Win " + this + ": isDrawn=" + isDrawnLw()
                      + ", animating=" + isAnimating());
              if (!isDrawnLw()) {
                  Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceController
                          + " pv=" + isVisibleByPolicy()
                          + " mDrawState=" + mWinAnimator.mDrawState
                          + " ph=" + isParentWindowHidden()
                          + " th=" + (mAppToken != null ? mAppToken.hiddenRequested : false)
                          + " a=" + isAnimating());
              }
          }
  
          results.numInteresting++;
          if (isDrawnLw()) {
              results.numDrawn++;
              if (!isAnimating()) {
                  results.numVisible++;
              }
              results.nowGone = false;
          } else if (isAnimating()) {
              results.nowGone = false;
          }
      }
  
      private boolean skipDecorCrop() {
          // The decor frame is used to specify the region not covered by the system
          // decorations (nav bar, status bar). In case this is empty, for example with
          // FLAG_TRANSLUCENT_NAVIGATION, we don't need to do any cropping.
          if (mWindowFrames.mDecorFrame.isEmpty()) {
              return true;
          }
  
          // But if we have a frame, and are an application window, then we must be cropped.
          if (mAppToken != null) {
              return false;
          }
  
          // For non application windows, we may be allowed to extend over the decor bars
          // depending on our type and permissions assosciated with our token.
          return mToken.canLayerAboveSystemBars();
      }
  
      /**
       * Calculate the window crop according to system decor policy. In general this is
       * the system decor rect (see #calculateSystemDecorRect), but we also have some
       * special cases. This rectangle is in screen space.
       */
      void calculatePolicyCrop(Rect policyCrop) {
          final DisplayContent displayContent = getDisplayContent();
  
          if (!displayContent.isDefaultDisplay && !displayContent.supportsSystemDecorations()) {
              // On a different display there is no system decor. Crop the window
              // by the screen boundaries.
              final DisplayInfo displayInfo = displayContent.getDisplayInfo();
              policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(),
                      mWindowFrames.mCompatFrame.height());
              policyCrop.intersect(-mWindowFrames.mCompatFrame.left, -mWindowFrames.mCompatFrame.top,
                      displayInfo.logicalWidth - mWindowFrames.mCompatFrame.left,
                      displayInfo.logicalHeight - mWindowFrames.mCompatFrame.top);
          } else if (skipDecorCrop()) {
              // Windows without policy decor aren't cropped.
              policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(),
                      mWindowFrames.mCompatFrame.height());
          } else {
              // Crop to the system decor specified by policy.
              calculateSystemDecorRect(policyCrop);
          }
      }
  
      /**
       * The system decor rect is the region of the window which is not covered
       * by system decorations.
       */
      private void calculateSystemDecorRect(Rect systemDecorRect) {
          final Rect decorRect = mWindowFrames.mDecorFrame;
          final int width = mWindowFrames.mFrame.width();
          final int height = mWindowFrames.mFrame.height();
  
          final int left = mWindowFrames.mFrame.left;
          final int top = mWindowFrames.mFrame.top;
  
          // Initialize the decor rect to the entire frame.
          if (isDockedResizing()) {
              // If we are resizing with the divider, the task bounds might be smaller than the
              // stack bounds. The system decor is used to clip to the task bounds, which we don't
              // want in this case in order to avoid holes.
              //
              // We take care to not shrink the width, for surfaces which are larger than
              // the display region. Of course this area will not eventually be visible
              // but if we truncate the width now, we will calculate incorrectly
              // when adjusting to the stack bounds.
              final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
              systemDecorRect.set(0, 0,
                      Math.max(width, displayInfo.logicalWidth),
                      Math.max(height, displayInfo.logicalHeight));
          } else {
              systemDecorRect.set(0, 0, width, height);
          }
  
          // If a freeform window is animating from a position where it would be cutoff, it would be
          // cutoff during the animation. We don't want that, so for the duration of the animation
          // we ignore the decor cropping and depend on layering to position windows correctly.
  
          // We also ignore cropping when the window is currently being drag resized in split screen
          // to prevent issues with the crop for screenshot.
          final boolean cropToDecor =
                  !(inFreeformWindowingMode() && isAnimatingLw()) && !isDockedResizing();
          if (cropToDecor) {
              // Intersect with the decor rect, offsetted by window position.
              systemDecorRect.intersect(decorRect.left - left, decorRect.top - top,
                      decorRect.right - left, decorRect.bottom - top);
          }
  
          // If size compatibility is being applied to the window, the
          // surface is scaled relative to the screen.  Also apply this
          // scaling to the crop rect.  We aren't using the standard rect
          // scale function because we want to round things to make the crop
          // always round to a larger rect to ensure we don't crop too
          // much and hide part of the window that should be seen.
          if (mInvGlobalScale != 1.0f && inSizeCompatMode()) {
              final float scale = mInvGlobalScale;
              systemDecorRect.left = (int) (systemDecorRect.left * scale - 0.5f);
              systemDecorRect.top = (int) (systemDecorRect.top * scale - 0.5f);
              systemDecorRect.right = (int) ((systemDecorRect.right + 1) * scale - 0.5f);
              systemDecorRect.bottom = (int) ((systemDecorRect.bottom + 1) * scale - 0.5f);
          }
  
      }
  
      /**
       * Expand the given rectangle by this windows surface insets. This
       * takes you from the 'window size' to the 'surface size'.
       * The surface insets are positive in each direction, so we inset by
       * the inverse.
       */
      void expandForSurfaceInsets(Rect r) {
          r.inset(-mAttrs.surfaceInsets.left,
                  -mAttrs.surfaceInsets.top,
                  -mAttrs.surfaceInsets.right,
                  -mAttrs.surfaceInsets.bottom);
      }
  
      boolean surfaceInsetsChanging() {
          return !mLastSurfaceInsets.equals(mAttrs.surfaceInsets);
      }
  
      int relayoutVisibleWindow(int result, int attrChanges) {
          final boolean wasVisible = isVisibleLw();
  
          result |= (!wasVisible || !isDrawnLw()) ? RELAYOUT_RES_FIRST_TIME : 0;
  
          if (mAnimatingExit) {
              Slog.d(TAG, "relayoutVisibleWindow: " + this + " mAnimatingExit=true, mRemoveOnExit="
                      + mRemoveOnExit + ", mDestroying=" + mDestroying);
  
              // Cancel the existing exit animation for the next enter animation.
              if (isSelfAnimating()) {
                  cancelAnimation();
                  destroySurfaceUnchecked();
              }
              mAnimatingExit = false;
          }
          if (mDestroying) {
              mDestroying = false;
              mWmService.mDestroySurface.remove(this);
          }
          if (!wasVisible) {
              mWinAnimator.mEnterAnimationPending = true;
          }
  
          mLastVisibleLayoutRotation = getDisplayContent().getRotation();
  
          mWinAnimator.mEnteringAnimation = true;
  
          Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareToDisplay");
          try {
              prepareWindowToDisplayDuringRelayout(wasVisible);
          } finally {
              Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
          }
  
          if ((attrChanges & FORMAT_CHANGED) != 0) {
              // If the format can't be changed in place, preserve the old surface until the app draws
              // on the new one. This prevents blinking when we change elevation of freeform and
              // pinned windows.
              if (!mWinAnimator.tryChangeFormatInPlaceLocked()) {
                  mWinAnimator.preserveSurfaceLocked();
                  result |= RELAYOUT_RES_SURFACE_CHANGED
                          | RELAYOUT_RES_FIRST_TIME;
              }
          }
  
          // When we change the Surface size, in scenarios which may require changing
          // the surface position in sync with the resize, we use a preserved surface
          // so we can freeze it while waiting for the client to report draw on the newly
          // sized surface. At the moment this logic is only in place for switching
          // in and out of the big surface for split screen resize.
          if (isDragResizeChanged()) {
              setDragResizing();
              // We can only change top level windows to the full-screen surface when
              // resizing (as we only have one full-screen surface). So there is no need
              // to preserve and destroy windows which are attached to another, they
              // will keep their surface and its size may change over time.
              if (mHasSurface && !isChildWindow()) {
                  mWinAnimator.preserveSurfaceLocked();
                  result |= RELAYOUT_RES_SURFACE_CHANGED |
                      RELAYOUT_RES_FIRST_TIME;
              }
          }
          final boolean freeformResizing = isDragResizing()
                  && getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
          final boolean dockedResizing = isDragResizing()
                  && getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER;
          result |= freeformResizing ? RELAYOUT_RES_DRAG_RESIZING_FREEFORM : 0;
          result |= dockedResizing ? RELAYOUT_RES_DRAG_RESIZING_DOCKED : 0;
          return result;
      }
  
      /**
       * @return True if this window has been laid out at least once; false otherwise.
       */
      boolean isLaidOut() {
          return mLayoutSeq != -1;
      }
  
      /**
       * Add the DisplayContent of the embedded display which is re-parented to this window to
       * the list of embedded displays.
       *
       * @param dc DisplayContent of the re-parented embedded display.
       * @return {@code true} if the giving DisplayContent is added, {@code false} otherwise.
       */
      boolean addEmbeddedDisplayContent(DisplayContent dc) {
          return mEmbeddedDisplayContents.add(dc);
      }
  
      /**
       * Remove the DisplayContent of the embedded display which is re-parented to this window from
       * the list of embedded displays.
       *
       * @param dc DisplayContent of the re-parented embedded display.
       * @return {@code true} if the giving DisplayContent is removed, {@code false} otherwise.
       */
      boolean removeEmbeddedDisplayContent(DisplayContent dc) {
          return mEmbeddedDisplayContents.remove(dc);
      }
  
      /**
       * Updates the last inset values to the current ones.
       */
      void updateLastInsetValues() {
          mWindowFrames.updateLastInsetValues();
      }
  
      void startAnimation(Animation anim) {
  
          // If we are an inset provider, all our animations are driven by the inset client.
          if (mInsetProvider != null && mInsetProvider.isControllable()) {
              return;
          }
  
          final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
          anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
                  displayInfo.appWidth, displayInfo.appHeight);
          anim.restrictDuration(MAX_ANIMATION_DURATION);
          anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
          final AnimationAdapter adapter = new LocalAnimationAdapter(
                  new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,
                          0 /* windowCornerRadius */),
                  mWmService.mSurfaceAnimationRunner);
          startAnimation(getPendingTransaction(), adapter);
          commitPendingTransaction();
      }
  
      private void startMoveAnimation(int left, int top) {
  
          // If we are an inset provider, all our animations are driven by the inset client.
          if (mInsetProvider != null && mInsetProvider.isControllable()) {
              return;
          }
  
          if (DEBUG_ANIM) Slog.v(TAG, "Setting move animation on " + this);
          final Point oldPosition = new Point();
          final Point newPosition = new Point();
          transformFrameToSurfacePosition(mWindowFrames.mLastFrame.left, mWindowFrames.mLastFrame.top,
                  oldPosition);
          transformFrameToSurfacePosition(left, top, newPosition);
          final AnimationAdapter adapter = new LocalAnimationAdapter(
                  new MoveAnimationSpec(oldPosition.x, oldPosition.y, newPosition.x, newPosition.y),
                  mWmService.mSurfaceAnimationRunner);
          startAnimation(getPendingTransaction(), adapter);
      }
  
      private void startAnimation(Transaction t, AnimationAdapter adapter) {
          startAnimation(t, adapter, mWinAnimator.mLastHidden);
      }
  
      @Override
      protected void onAnimationFinished() {
          super.onAnimationFinished();
          mWinAnimator.onAnimationFinished();
      }
  
      /**
       * Retrieves the current transformation matrix of the window, relative to the display.
       *
       * @param float9 A temporary array of 9 floats.
       * @param outMatrix Matrix to fill in the transformation.
       */
      void getTransformationMatrix(float[] float9, Matrix outMatrix) {
          float9[Matrix.MSCALE_X] = mWinAnimator.mDsDx;
          float9[Matrix.MSKEW_Y] = mWinAnimator.mDtDx;
          float9[Matrix.MSKEW_X] = mWinAnimator.mDtDy;
          float9[Matrix.MSCALE_Y] = mWinAnimator.mDsDy;
          int x = mSurfacePosition.x;
          int y = mSurfacePosition.y;
  
          // We might be on a display which has been re-parented to a view in another window, so here
          // computes the global location of our display.
          DisplayContent dc = getDisplayContent();
          while (dc != null && dc.getParentWindow() != null) {
              final WindowState displayParent = dc.getParentWindow();
              x += displayParent.mWindowFrames.mFrame.left - displayParent.mAttrs.surfaceInsets.left
                      + (dc.getLocationInParentWindow().x * displayParent.mGlobalScale + 0.5f);
              y += displayParent.mWindowFrames.mFrame.top - displayParent.mAttrs.surfaceInsets.top
                      + (dc.getLocationInParentWindow().y * displayParent.mGlobalScale + 0.5f);
              dc = displayParent.getDisplayContent();
          }
  
          // If changed, also adjust transformFrameToSurfacePosition
          final WindowContainer parent = getParent();
          if (isChildWindow()) {
              final WindowState parentWindow = getParentWindow();
              x += parentWindow.mWindowFrames.mFrame.left - parentWindow.mAttrs.surfaceInsets.left;
              y += parentWindow.mWindowFrames.mFrame.top - parentWindow.mAttrs.surfaceInsets.top;
          } else if (parent != null) {
              final Rect parentBounds = parent.getBounds();
              x += parentBounds.left;
              y += parentBounds.top;
          }
          float9[Matrix.MTRANS_X] = x;
          float9[Matrix.MTRANS_Y] = y;
          float9[Matrix.MPERSP_0] = 0;
          float9[Matrix.MPERSP_1] = 0;
          float9[Matrix.MPERSP_2] = 1;
          outMatrix.setValues(float9);
      }
  
      // TODO: Hack to work around the number of states AppWindowToken needs to access without having
      // access to its windows children. Need to investigate re-writing
      // {@link AppWindowToken#updateReportedVisibilityLocked} so this can be removed.
      static final class UpdateReportedVisibilityResults {
          int numInteresting;
          int numVisible;
          int numDrawn;
          boolean nowGone = true;
  
          void reset() {
              numInteresting = 0;
              numVisible = 0;
              numDrawn = 0;
              nowGone = true;
          }
      }
  
      private static final class WindowId extends IWindowId.Stub {
          private final WeakReference<WindowState> mOuter;
  
          private WindowId(WindowState outer) {
  
              // Use a weak reference for the outer class. This is important to prevent the following
              // leak: Since we send this class to the client process, binder will keep it alive as
              // long as the client keeps it alive. Now, if the window is removed, we need to clear
              // out our reference so even though this class is kept alive we don't leak WindowState,
              // which can keep a whole lot of classes alive.
              mOuter = new WeakReference<>(outer);
          }
  
          @Override
          public void registerFocusObserver(IWindowFocusObserver observer) {
              final WindowState outer = mOuter.get();
              if (outer != null) {
                  outer.registerFocusObserver(observer);
              }
          }
          @Override
          public void unregisterFocusObserver(IWindowFocusObserver observer) {
              final WindowState outer = mOuter.get();
              if (outer != null) {
                  outer.unregisterFocusObserver(observer);
              }
          }
          @Override
          public boolean isFocused() {
              final WindowState outer = mOuter.get();
              if (outer != null) {
                  synchronized (outer.mWmService.mGlobalLock) {
                      return outer.isFocused();
                  }
              }
              return false;
          }
      }
  
  
      @Override
      boolean shouldMagnify() {
          if (mAttrs.type == TYPE_INPUT_METHOD ||
                  mAttrs.type == TYPE_INPUT_METHOD_DIALOG ||
                  mAttrs.type == TYPE_MAGNIFICATION_OVERLAY ||
                  mAttrs.type == TYPE_NAVIGATION_BAR ||
                  // It's tempting to wonder: Have we forgotten the rounded corners overlay?
                  // worry not: it's a fake TYPE_NAVIGATION_BAR_PANEL
                  mAttrs.type == TYPE_NAVIGATION_BAR_PANEL) {
              return false;
          }
          return true;
      }
  
      @Override
      SurfaceSession getSession() {
          if (mSession.mSurfaceSession != null) {
              return mSession.mSurfaceSession;
          } else {
              return getParent().getSession();
          }
      }
  
      @Override
      boolean needsZBoost() {
          final WindowState inputMethodTarget = getDisplayContent().mInputMethodTarget;
          if (mIsImWindow && inputMethodTarget != null) {
              final AppWindowToken appToken = inputMethodTarget.mAppToken;
              if (appToken != null) {
                  return appToken.needsZBoost();
              }
          }
          return mWillReplaceWindow;
      }
  
      private void applyDims(Dimmer dimmer) {
          if (!mAnimatingExit && mAppDied) {
              mIsDimming = true;
              dimmer.dimAbove(getPendingTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
          } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0 && isVisibleNow() && !mHidden) {
              // Only show a dim behind when the following is satisfied:
              // 1. The window has the flag FLAG_DIM_BEHIND
              // 2. The WindowToken is not hidden so dims aren't shown when the window is exiting.
              // 3. The WS is considered visible according to the isVisible() method
              // 4. The WS is not hidden.
              mIsDimming = true;
              dimmer.dimBelow(getPendingTransaction(), this, mAttrs.dimAmount);
          }
      }
  
      @Override
      void prepareSurfaces() {
          final Dimmer dimmer = getDimmer();
          mIsDimming = false;
          if (dimmer != null) {
              applyDims(dimmer);
          }
          updateSurfacePosition();
  
          mWinAnimator.prepareSurfaceLocked(true);
          super.prepareSurfaces();
      }
  
      @Override
      public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
          super.onAnimationLeashCreated(t, leash);
  
          // Leash is now responsible for position, so set our position to 0.
          t.setPosition(mSurfaceControl, 0, 0);
          mLastSurfacePosition.set(0, 0);
      }
  
      @Override
      public void onAnimationLeashLost(Transaction t) {
          super.onAnimationLeashLost(t);
          updateSurfacePosition(t);
      }
  
      @Override
      void updateSurfacePosition() {
          updateSurfacePosition(getPendingTransaction());
      }
  
      @VisibleForTesting
      void updateSurfacePosition(Transaction t) {
          if (mSurfaceControl == null) {
              return;
          }
  
          transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
                  mSurfacePosition);
  
          // Freeze position while we're unrotated, so the surface remains at the position it was
          // prior to the rotation.
          if (!mSurfaceAnimator.hasLeash() && mPendingSeamlessRotate == null
                  && !mLastSurfacePosition.equals(mSurfacePosition)) {
              t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
              mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
              if (surfaceInsetsChanging() && mWinAnimator.hasSurface()) {
                  mLastSurfaceInsets.set(mAttrs.surfaceInsets);
                  t.deferTransactionUntil(mSurfaceControl,
                          mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
                          getFrameNumber());
              }
          }
      }
  
      private void transformFrameToSurfacePosition(int left, int top, Point outPoint) {
          outPoint.set(left, top);
  
          // If changed, also adjust getTransformationMatrix
          final WindowContainer parentWindowContainer = getParent();
          if (isChildWindow()) {
              // TODO: This probably falls apart at some point and we should
              // actually compute relative coordinates.
  
              // Since the parent was outset by its surface insets, we need to undo the outsetting
              // with insetting by the same amount.
              final WindowState parent = getParentWindow();
              transformSurfaceInsetsPosition(mTmpPoint, parent.mAttrs.surfaceInsets);
              outPoint.offset(-parent.mWindowFrames.mFrame.left + mTmpPoint.x,
                      -parent.mWindowFrames.mFrame.top + mTmpPoint.y);
          } else if (parentWindowContainer != null) {
              final Rect parentBounds = parentWindowContainer.getDisplayedBounds();
              outPoint.offset(-parentBounds.left, -parentBounds.top);
          }
  
          TaskStack stack = getStack();
  
          // If we have stack outsets, that means the top-left
          // will be outset, and we need to inset ourselves
          // to account for it. If we actually have shadows we will
          // then un-inset ourselves by the surfaceInsets.
          if (stack != null) {
              final int outset = stack.getStackOutset();
              outPoint.offset(outset, outset);
          }
  
          // Expand for surface insets. See WindowState.expandForSurfaceInsets.
          transformSurfaceInsetsPosition(mTmpPoint, mAttrs.surfaceInsets);
          outPoint.offset(-mTmpPoint.x, -mTmpPoint.y);
      }
  
      /**
       * The surface insets from layout parameter are in application coordinate. If the window is
       * scaled, the insets also need to be scaled for surface position in global coordinate.
       */
      private void transformSurfaceInsetsPosition(Point outPos, Rect surfaceInsets) {
          if (!inSizeCompatMode()) {
              outPos.x = surfaceInsets.left;
              outPos.y = surfaceInsets.top;
              return;
          }
          outPos.x = (int) (surfaceInsets.left * mGlobalScale + 0.5f);
          outPos.y = (int) (surfaceInsets.top * mGlobalScale + 0.5f);
      }
  
      boolean needsRelativeLayeringToIme() {
          // We only use the relative layering mode in split screen, as part of elevating the IME
          // and windows above it's target above the docked divider.
          if (!inSplitScreenWindowingMode()) {
              return false;
          }
  
          if (isChildWindow()) {
              // If we are a child of the input method target we need this promotion.
              if (getParentWindow().isInputMethodTarget()) {
                  return true;
              }
          } else if (mAppToken != null) {
              // Likewise if we share a token with the Input method target and are ordered
              // above it but not necessarily a child (e.g. a Dialog) then we also need
              // this promotion.
              final WindowState imeTarget = getDisplayContent().mInputMethodTarget;
              boolean inTokenWithAndAboveImeTarget = imeTarget != null && imeTarget != this
                      && imeTarget.mToken == mToken && imeTarget.compareTo(this) <= 0;
              return inTokenWithAndAboveImeTarget;
          }
          return false;
      }
  
      @Override
      void assignLayer(Transaction t, int layer) {
          // See comment in assignRelativeLayerForImeTargetChild
          if (needsRelativeLayeringToIme()) {
              getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
              return;
          }
          super.assignLayer(t, layer);
      }
  
      @Override
      public boolean isDimming() {
          return mIsDimming;
      }
  
      // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA
      // then we can drop all negative layering on the windowing side and simply inherit
      // the default implementation here.
      public void assignChildLayers(Transaction t) {
          // The surface of the main window might be preserved. So the child window on top of the main
          // window should be also on top of the preserved surface.
          int layer = PRESERVED_SURFACE_LAYER + 1;
          for (int i = 0; i < mChildren.size(); i++) {
              final WindowState w = mChildren.get(i);
  
              // APPLICATION_MEDIA_OVERLAY needs to go above APPLICATION_MEDIA
              // while they both need to go below the main window. However the
              // relative layering of multiple APPLICATION_MEDIA/OVERLAY has never
              // been defined and so we can use static layers and leave it that way.
              if (w.mAttrs.type == TYPE_APPLICATION_MEDIA) {
                  w.assignLayer(t, -2);
              } else if (w.mAttrs.type == TYPE_APPLICATION_MEDIA_OVERLAY) {
                  w.assignLayer(t, -1);
              } else {
                  w.assignLayer(t, layer);
              }
              w.assignChildLayers(t);
              layer++;
          }
      }
  
      /**
       * Update a tap exclude region identified by provided id. The requested area will be clipped to
       * the window bounds.
       */
      void updateTapExcludeRegion(int regionId, Region region) {
          final DisplayContent currentDisplay = getDisplayContent();
          if (currentDisplay == null) {
              throw new IllegalStateException("Trying to update window not attached to any display.");
          }
  
          if (mTapExcludeRegionHolder == null) {
              mTapExcludeRegionHolder = new TapExcludeRegionHolder();
  
              // Make sure that this window is registered as one that provides a tap exclude region
              // for its containing display.
              currentDisplay.mTapExcludeProvidingWindows.add(this);
          }
  
          mTapExcludeRegionHolder.updateRegion(regionId, region);
          // Trigger touch exclude region update on current display.
          currentDisplay.updateTouchExcludeRegion();
          // Trigger touchable region update for this window.
          currentDisplay.getInputMonitor().updateInputWindowsLw(true /* force */);
      }
  
      /**
       * Union the region with current tap exclude region that this window provides.
       *
       * @param region The region to be amended. It is on the screen coordinates.
       */
      void amendTapExcludeRegion(Region region) {
          final Region tempRegion = Region.obtain();
          mTmpRect.set(mWindowFrames.mFrame);
          mTmpRect.offsetTo(0, 0);
          mTapExcludeRegionHolder.amendRegion(tempRegion, mTmpRect);
          // The region held by the holder is on the window coordinates. We need to translate it to
          // the screen coordinates.
          tempRegion.translate(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top);
          region.op(tempRegion, Region.Op.UNION);
          tempRegion.recycle();
      }
  
      boolean hasTapExcludeRegion() {
          return mTapExcludeRegionHolder != null && !mTapExcludeRegionHolder.isEmpty();
      }
  
      @Override
      public boolean isInputMethodTarget() {
          return getDisplayContent().mInputMethodTarget == this;
      }
  
      long getFrameNumber() {
          return mFrameNumber;
      }
  
      void setFrameNumber(long frameNumber) {
          mFrameNumber = frameNumber;
      }
  
      public void getMaxVisibleBounds(Rect out) {
          if (out.isEmpty()) {
              out.set(mWindowFrames.mVisibleFrame);
              return;
          }
  
          if (mWindowFrames.mVisibleFrame.left < out.left) {
              out.left = mWindowFrames.mVisibleFrame.left;
          }
          if (mWindowFrames.mVisibleFrame.top < out.top) {
              out.top = mWindowFrames.mVisibleFrame.top;
          }
          if (mWindowFrames.mVisibleFrame.right > out.right) {
              out.right = mWindowFrames.mVisibleFrame.right;
          }
          if (mWindowFrames.mVisibleFrame.bottom > out.bottom) {
              out.bottom = mWindowFrames.mVisibleFrame.bottom;
          }
      }
  
      /**
       * Copy the inset values over so they can be sent back to the client when a relayout occurs.
       */
      void getInsetsForRelayout(Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
              Rect outStableInsets, Rect outOutsets) {
          outOverscanInsets.set(mWindowFrames.mOverscanInsets);
          outContentInsets.set(mWindowFrames.mContentInsets);
          outVisibleInsets.set(mWindowFrames.mVisibleInsets);
          outStableInsets.set(mWindowFrames.mStableInsets);
          outOutsets.set(mWindowFrames.mOutsets);
  
          mLastRelayoutContentInsets.set(mWindowFrames.mContentInsets);
      }
  
      void getContentInsets(Rect outContentInsets) {
          outContentInsets.set(mWindowFrames.mContentInsets);
      }
  
      Rect getContentInsets() {
          return mWindowFrames.mContentInsets;
      }
  
      void getStableInsets(Rect outStableInsets) {
          outStableInsets.set(mWindowFrames.mStableInsets);
      }
  
      Rect getStableInsets() {
          return mWindowFrames.mStableInsets;
      }
  
      void resetLastContentInsets() {
          mWindowFrames.resetLastContentInsets();
      }
  
      Rect getVisibleInsets() {
          return mWindowFrames.mVisibleInsets;
      }
  
      @Override
      public WindowFrames getWindowFrames() {
          return mWindowFrames;
      }
  
      void resetContentChanged() {
          mWindowFrames.setContentChanged(false);
      }
  
      void setInsetProvider(InsetsSourceProvider insetProvider) {
          mInsetProvider = insetProvider;
      }
  
      InsetsSourceProvider getInsetProvider() {
          return mInsetProvider;
      }
  
      private final class MoveAnimationSpec implements AnimationSpec {
  
          private final long mDuration;
          private Interpolator mInterpolator;
          private Point mFrom = new Point();
          private Point mTo = new Point();
  
          private MoveAnimationSpec(int fromX, int fromY, int toX, int toY) {
              final Animation anim = AnimationUtils.loadAnimation(mContext,
                      com.android.internal.R.anim.window_move_from_decor);
              mDuration = (long)
                      (anim.computeDurationHint() * mWmService.getWindowAnimationScaleLocked());
              mInterpolator = anim.getInterpolator();
              mFrom.set(fromX, fromY);
              mTo.set(toX, toY);
          }
  
          @Override
          public long getDuration() {
              return mDuration;
          }
  
          @Override
          public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
              final float fraction = (float) currentPlayTime / getDuration();
              final float v = mInterpolator.getInterpolation(fraction);
              t.setPosition(leash, mFrom.x + (mTo.x - mFrom.x) * v,
                      mFrom.y + (mTo.y - mFrom.y) * v);
          }
  
          @Override
          public void dump(PrintWriter pw, String prefix) {
              pw.println(prefix + "from=" + mFrom
                      + " to=" + mTo
                      + " duration=" + mDuration);
          }
  
          @Override
          public void writeToProtoInner(ProtoOutputStream proto) {
              final long token = proto.start(MOVE);
              mFrom.writeToProto(proto, FROM);
              mTo.writeToProto(proto, TO);
              proto.write(DURATION_MS, mDuration);
              proto.end(token);
          }
      }
  }
  