/*
 * 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_TASK_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.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.graphics.GraphicsProtos.dumpPointProto;
import static android.internal.perfetto.protos.Animationadapter.AnimationSpecProto.MOVE;
import static android.internal.perfetto.protos.Animationadapter.MoveAnimationSpecProto.DURATION_MS;
import static android.internal.perfetto.protos.Animationadapter.MoveAnimationSpecProto.FROM;
import static android.internal.perfetto.protos.Animationadapter.MoveAnimationSpecProto.TO;
import static android.internal.perfetto.protos.Windowmanagerservice.IdentifierProto.HASH_CODE;
import static android.internal.perfetto.protos.Windowmanagerservice.IdentifierProto.TITLE;
import static android.internal.perfetto.protos.Windowmanagerservice.IdentifierProto.USER_ID;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowContainerChildProto.WINDOW;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.ANIMATING_EXIT;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.ANIMATOR;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.ATTRIBUTES;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.BUFFER_SEQ_ID;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.DESTROYING;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.DIM_BOUNDS;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.DISPLAY_ID;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.FORCE_SEAMLESS_ROTATION;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.GIVEN_CONTENT_INSETS;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.GLOBAL_SCALE;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.HAS_COMPAT_SCALE;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.HAS_SURFACE;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.IS_ON_SCREEN;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.IS_READY_FOR_DISPLAY;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.IS_VISIBLE;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.KEEP_CLEAR_AREAS;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.MERGED_LOCAL_INSETS_SOURCES;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.REMOVED;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.REMOVE_ON_EXIT;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.REQUESTED_HEIGHT;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.REQUESTED_VISIBLE_TYPES;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.REQUESTED_WIDTH;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.STACK_ID;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.SURFACE_INSETS;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.SURFACE_POSITION;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.SYNC_SEQ_ID;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.UNRESTRICTED_KEEP_CLEAR_AREAS;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.VIEW_VISIBILITY;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.WINDOW_CONTAINER;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowStateProto.WINDOW_FRAMES;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.PowerManager.DRAW_WAKE_LOCK;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.util.SequenceUtils.getNextSeq;
import static android.view.SurfaceControl.Transaction;
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.WindowInsets.Type.navigationBars;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowLayout.UNSPECIFIED_LENGTH;
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_BLUR_BEHIND;
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_NOT_FOCUSABLE;
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.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_INPUT_METHOD_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NOT_MAGNIFIABLE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPT_OUT_EDGE_TO_EDGE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
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.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
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_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_BOOT_PROGRESS;
import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY;
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_CONSUMER;
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_KEYGUARD_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_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
import static android.window.DesktopExperienceFlags.ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS;

import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ANIM;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_BACK_PREVIEW;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_DIMMER;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_FOCUS;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_FOCUS_LIGHT;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_RESIZE;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_STARTING_WINDOW;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_SYNC_ENGINE;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_INSETS;
import static com.android.internal.protolog.WmProtoLogGroups.WM_SHOW_TRANSACTIONS;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
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.DisplayContent.logsGestureExclusionRestrictions;
import static com.android.server.wm.StartingData.AFTER_TRANSITION_FINISH;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
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.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
import static com.android.server.wm.WindowManagerService.MY_PID;
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.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 android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyCache;
import android.app.servertransaction.WindowStateInsetsControlChangeItem;
import android.app.servertransaction.WindowStateResizeItem;
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.RectF;
import android.graphics.Region;
import android.gui.TouchOcclusionMode;
import android.os.Binder;
import android.os.Build;
import android.os.Debug;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
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.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.IWindow;
import android.view.IWindowFocusObserver;
import android.view.IWindowId;
import android.view.InputChannel;
import android.view.InputWindowHandle;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewTreeObserver;
import android.view.WindowInfo;
import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.inputmethod.ImeTracker;
import android.window.ActivityWindowInfo;
import android.window.ClientWindowFrames;
import android.window.OnBackInvokedCallbackInfo;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.KeyInterceptionInfo;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.protolog.common.LogLevel;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
import com.android.server.wm.RefreshRatePolicy.FrameRateVote;
import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.utils.RegionUtils;
import com.android.window.flags.Flags;

import dalvik.annotation.optimization.NeverCompile;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;

/** A window in the window manager. */
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,
        InsetsControlTarget, InputTarget {
    static final String TAG = TAG_WITH_CLASS_NAME ? "WindowState" : TAG_WM;

    // The minimal size of a window within the usable area of the freeform root task.
    // 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;
    /**
     * Requested userId, if this is not equals with the userId from mOwnerUid, then this window is
     * created for secondary user.
     * Use this member instead of get userId from mOwnerUid while query for visibility.
     */
    final int mShowUserId;
    /** The owner has {@link android.Manifest.permission#INTERNAL_SYSTEM_WINDOW} */
    final boolean mOwnerCanAddInternalSystemWindow;
    final WindowId mWindowId;
    @NonNull WindowToken mToken;
    // The same object as mToken if this is an app window and null for non-app windows.
    ActivityRecord mActivityRecord;
    /** Non-null if this is a starting window. */
    StartingData mStartingData;

    // 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();
    private boolean mIsChildWindow;
    final int mBaseLayer;
    final int mSubLayer;
    final boolean mLayoutAttached;
    final boolean mIsImWindow;
    final boolean mIsWallpaper;
    private final boolean mIsFloatingLayer;
    int mViewVisibility;

    /**
     * Flags to disable system UI functions. This can only be set by the one which has the
     * status bar permission.
     *
     * @see View.SystemUiVisibility
     */
    int mDisableFlags;

    /**
     * The visibility flag of the window based on policy like {@link WindowManagerPolicy}.
     * Normally set by calling {@link #show} and {@link #hide}.
     *
     * 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 mIsForceHiddenNonSystemOverlayWindow;
    boolean mHidden = true;    // Used to determine if to show child windows.
    private boolean mDragResizing;
    private boolean mDragResizingChangeReported = true;
    private boolean mRedrawForSyncReported = true;
    private long mCreateTime = System.currentTimeMillis();

    /**
     * Used to assosciate a given set of state changes sent from MSG_RESIZED
     * with a given call to finishDrawing (does this call contain or not contain
     * those state changes). We need to use it to handle cases like this:
     * 1. Server changes some state, calls applyWithNextDraw
     * 2. Client observes state change, begins drawing frame.
     * 3. Server makes another state change, and calls applyWithNextDraw again
     * 4. We receive finishDrawing, and it only contains the first frame
     *    but there was no way for us to know, because we no longer rely
     *    on a synchronous call to relayout before draw.
     * We track this by storing seqIds in each draw handler, and increment
     * this seqId every time we send MSG_RESIZED. The client sends it back
     * with finishDrawing, and this way we can know is the client replying to
     * the latest MSG_RESIZED or an earlier one. For a detailed discussion,
     * examine the git commit message introducing this comment and variable.2
     */
    int mSyncSeqId = 0;

    /**
     * The last syncId associated with a BLAST prepareSync or 0 when no BLAST sync is active.
     * This is only used when flag always_seq_id_layout is disabled.
     */
    int mPrepareSyncSeqId = 0;

    /**
     * The latest (or future) seqId requiring a buffer from the client. If this is > mSyncSeqId, it
     * means that a BLAST sync is pending but hasn't been sent to client yet.
     */
    int mBufferSeqId = 0;

    /**
     * This is {@code true} when a client resize-request was queued (in WMS.mResizingWindows)
     * while the associated SyncGroup was "open" (not ready). This is needed because actual dispatch
     * of resized messages is posted to surface-placement which runs after the associated SyncGroup
     * has been closed.
     */
    private boolean mPendingSyncResize = false;

    /**
     * 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;

    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 mRelayoutSeq = -1;
    int mLayoutSeq = -1;

    /**
     * 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();

    /**
     * Similar to {@link #mLastReportedConfiguration}, used to store last reported to client
     * ActivityWindowInfo. {@code null} if this is not an Activity window.
     */
    @Nullable
    final ActivityWindowInfo mLastReportedActivityWindowInfo;

    /** @see #isLastConfigReportedToClient() */
    private boolean mLastConfigReportedToClient;

    private final ClientWindowFrames mLastReportedFrames = new ClientWindowFrames();

    private final InsetsState mLastReportedInsetsState = new InsetsState();

    private final InsetsSourceControl.Array mLastReportedActiveControls =
            new InsetsSourceControl.Array();

    private final Configuration mTempConfiguration = new Configuration();

    /**
     * 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.
     * This is only applied to IME windows when corresponding process in DisplayPolicy executed.
     */
    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 = 1f;
    float mInvGlobalScale = 1f;
    float mCompatScale = 1f;
    final float mOverrideScale;
    float mHScale = 1f, mVScale = 1f;
    float mLastHScale = 1f, mLastVScale = 1f;

    // An offset in pixel of the surface contents from the window position. Used for Wallpaper
    // to provide the effect of scrolling within a large surface. We just use these values as
    // a cache.
    int mXOffset = 0;
    int mYOffset = 0;

    // A scale factor for the surface contents, that will be applied from the center of the visible
    // region.
    float mWallpaperScale = 1f;

    final Matrix mTmpMatrix = new Matrix();
    final float[] mTmpMatrixArray = new float[9];

    private final WindowFrames mWindowFrames = new WindowFrames();

    /**
     * List of rects where system gestures should be ignored.
     *
     * Coordinates are relative to the window's position.
     */
    private final List<Rect> mExclusionRects = new ArrayList<>();
    /**
     * List of rects which should ideally not be covered by floating windows like Pip.
     *
     * Coordinates are relative to the window's position.
     */
    private final List<Rect> mKeepClearAreas = new ArrayList<>();

    /**
     * Like mKeepClearAreas, but the unrestricted ones can be trusted to behave nicely.
     * Floating windows (like Pip) will be moved away from them without applying restrictions.
     */
    private final List<Rect> mUnrestrictedKeepClearAreas = 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: the requested zoom out for the
    // wallpaper; if a wallpaper window: the currently applied zoom.
    float mWallpaperZoomOut = -1;

    // If a wallpaper window: whether the wallpaper should be scaled when zoomed, if set
    // to false, mWallpaperZoom will be ignored here and just passed to the WallpaperService.
    boolean mShouldScaleWallpaper;

    // 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;

    /**
     * If the application is not currently visible but requires a layout,
     * then make sure we call performSurfacePlacement as well. This is set
     * in layout if mLayoutNeeded is set until surface placement is done.
     */
    boolean mSurfacePlacementNeeded;

    /** Currently running an exit animation? */
    boolean mAnimatingExit;

    /** Currently on the mDestroySurface list? */
    boolean mDestroying;

    /** Completely remove from window manager after exit animation? */
    boolean mRemoveOnExit;

    /** The time when the window was last requested to redraw for orientation change. */
    private long mOrientationChangeRedrawRequestTime;

    /** 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 InputWindowHandleWrapper mInputWindowHandle;

    /**
     * The token will be assigned to {@link InputWindowHandle#token} if this window can receive
     * input event. Note that the token of associated input window handle can be cleared if this
     * window becomes unable to receive input, but this field will remain until the input channel
     * is actually disposed.
     */
    IBinder mInputChannelToken;

    // Used to improve performance of toString()
    private String mStringNameCache;
    private CharSequence mLastTitle;
    private boolean mWasExiting;

    final WindowStateAnimator mWinAnimator;

    boolean mHasSurface = 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();
    private final Region mTmpRegion = new Region();

    private final Transaction mTmpTransaction;

    /**
     * Whether the surface position of window is paused to update. Currently it is only used for
     * {@link Task#setMainWindowSizeChangeTransaction(Transaction)} to synchronize position.
     */
    boolean mIsSurfacePositionPaused;

    /**
     * Whether the IME insets have been consumed. If {@code true}, this window won't be able to
     * receive visible IME insets; {@code false}, otherwise.
     */
    boolean mImeInsetsConsumed = false;

    /**
     * The insets state of sources provided by windows above the current window.
     */
    final InsetsState mAboveInsetsState = new InsetsState();

    /**
     * The insets state of sources provided by the overrides set on any parent up the hierarchy.
     */
    SparseArray<InsetsSource> mMergedLocalInsetsSources = null;

    /**
     * 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;

    /** The surface position relative to the parent container. */
    final Point mSurfacePosition = new Point();

    /**
     * A region inside of this window to be excluded from touch.
     */
    private final Region mTapExcludeRegion = new Region();

    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;

    @Nullable
    private InsetsSourceProvider mControllableInsetProvider;

    /**
     * The {@link InsetsSourceProvider}s provided by this window container.
     */
    @Nullable
    private SparseArray<InsetsSourceProvider> mInsetsSourceProviders = null;

    private @InsetsType int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible();

    private @InsetsType int mAnimatingTypes = 0;

    /**
     * Freeze the insets state in some cases that not necessarily keeps up-to-date to the client.
     * (e.g app exiting transition)
     */
    private InsetsState mFrozenInsetsState;

    private KeyInterceptionInfo mKeyInterceptionInfo;

    /**
     * This information is passed to SurfaceFlinger to decide which window should have a priority
     * when deciding about the refresh rate of the display. All windows have the lowest priority by
     * default. The variable is cached, so we do not send too many updates to SF.
     */
    int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET;


    /**
     * A value representing the importance of the window from the system perspective. A higher
     * priority value means the window will get preferred access to the limited resource in
     * rendering.
     */
    int mSystemContentPriority = 0;
    /**
     * A score contributing to the {@link mSystemContentPriority}. This score is calculated based on
     * the recent user interaction history. The newer interacted window will typically get a higher
     * score.
     */
    int mInteractionPriorityScore = 0;

    /**
     * This is the frame rate which is passed to SurfaceFlinger if the window set a
     * preferredDisplayModeId or is part of the high refresh rate deny list.
     * The variable is cached, so we do not send too many updates to SF.
     */
    FrameRateVote mFrameRateVote = new FrameRateVote();

    static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */

    static class DrawHandler {
        final Consumer<SurfaceControl.Transaction> mConsumer;
        final int mSeqId;

        DrawHandler(int seqId, Consumer<SurfaceControl.Transaction> consumer) {
            mSeqId = seqId;
            mConsumer = consumer;
        }
    }
    private final ArrayList<DrawHandler> mDrawHandlers = new ArrayList<>();

    /**
     * Indicates whether inset animations are currently running within the Window.
     * This value is used by (@link com.android.server.wm.RefreshRatePolicy.java)
     * to omit setting a frame rate on the WindowState. Insets Animation is unique in that
     * sense that an app might drive an insets animation for a Window owned by a different
     * app (such as IME). In that case, we need the app that drives the insets animation
     * to be able to vote for high refresh rate from VRI.
     */
    private boolean mInsetsAnimationRunning;

    private final Consumer<SurfaceControl.Transaction> mSetSurfacePositionConsumer = t -> {
        // Only apply the position to the surface when there's no leash created.
        if (mSurfaceControl != null && mSurfaceControl.isValid() && !mSurfaceAnimator.hasLeash()) {
            t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
        }
    };

    /**
     * @see #setOnBackInvokedCallbackInfo(OnBackInvokedCallbackInfo)
     */
    private OnBackInvokedCallbackInfo mOnBackInvokedCallbackInfo;
    @Override
    WindowState asWindowState() {
        return this;
    }

    /**
     * @see #setSurfaceTranslationY(int)
     */
    private int mSurfaceTranslationY;

    @Override
    public boolean isRequestedVisible(@InsetsType int types) {
        return (mRequestedVisibleTypes & types) != 0;
    }

    /**
     * Returns requested visible types of insets.
     *
     * @return an integer as the requested visible insets types.
     */
    @Override
    public @InsetsType int getRequestedVisibleTypes() {
        return mRequestedVisibleTypes;
    }

    /**
     * @return an integer as the changed requested visible insets types.
     * @see #getRequestedVisibleTypes()
     */
    @InsetsType int setRequestedVisibleTypes(@InsetsType int requestedVisibleTypes) {
        if (mRequestedVisibleTypes != requestedVisibleTypes) {
            final int changedTypes = mRequestedVisibleTypes ^ requestedVisibleTypes;
            mRequestedVisibleTypes = requestedVisibleTypes;
            return changedTypes;
        }
        return 0;
    }

    @VisibleForTesting
    @InsetsType int setRequestedVisibleTypes(
            @InsetsType int requestedVisibleTypes, @InsetsType int mask) {
        return setRequestedVisibleTypes(
                mRequestedVisibleTypes & ~mask | requestedVisibleTypes & mask);
    }

    @Override
    public @InsetsType int getAnimatingTypes() {
        return mAnimatingTypes;
    }

    @Override
    public void setAnimatingTypes(@InsetsType int animatingTypes,
            @Nullable ImeTracker.Token statsToken) {
        if (mAnimatingTypes != animatingTypes) {
            if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                Trace.instant(TRACE_TAG_WINDOW_MANAGER,
                        TextUtils.formatSimple("%s: setAnimatingTypes(%s)",
                                getName(),
                                animatingTypes));
            }
            mInsetsAnimationRunning = animatingTypes != 0;
            mWmService.scheduleAnimationLocked();

            getDisplayContent().getInsetsPolicy().onAnimatingTypesChanged(
                    this, mAnimatingTypes, animatingTypes);
            mAnimatingTypes = animatingTypes;

            if (android.view.inputmethod.Flags.reportAnimatingInsetsTypes()) {
                ImeTracker.forLogging().onProgress(statsToken,
                        ImeTracker.PHASE_WM_WINDOW_ANIMATING_TYPES_CHANGED);
                final InsetsStateController insetsStateController =
                        getDisplayContent().getInsetsStateController();
                insetsStateController.onAnimatingTypesChanged(this, statsToken);
            }
        } else {
            ImeTracker.forLogging().onFailed(statsToken,
                    ImeTracker.PHASE_WM_WINDOW_ANIMATING_TYPES_CHANGED);
        }
    }

    /**
     * Set a freeze state for the window to ignore dispatching its insets state to the client.
     *
     * Used to keep the insets state for some use cases. (e.g. app exiting transition)
     */
    void freezeInsetsState() {
        if (mFrozenInsetsState == null) {
            mFrozenInsetsState = new InsetsState(getInsetsState(), true /* copySources */);
        }
    }

    void clearFrozenInsetsState() {
        mFrozenInsetsState = null;
    }

    InsetsState getFrozenInsetsState() {
        return mFrozenInsetsState;
    }

    /**
     * Check if the insets state of the window is ready to dispatch to the client when invoking
     * {@link InsetsStateController#notifyInsetsChanged}.
     */
    boolean isReadyToDispatchInsetsState() {
        if (mStartingData != null) {
            // Starting window doesn't consider insets.
            return false;
        }
        final boolean visible = shouldCheckTokenVisibleRequested()
                ? isVisibleRequested() : isVisible();
        return visible && mFrozenInsetsState == null;
    }

    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 boolean stickyHideNav =
                mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
                        && !isRequestedVisible(navigationBars());
        return stickyHideNav && mWmService.mConstants.mSystemGestureExcludedByPreQStickyImmersive
                && mActivityRecord != null && mActivityRecord.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;
        }
    }

    /**
     * Collects all restricted and unrestricted keep-clear areas for this window.
     * Keep-clear areas are rects that should ideally not be covered by floating windows like Pip.
     * The system is more careful about restricted ones and may apply restrictions to them, while
     * the unrestricted ones are considered safe.
     *
     * @param outRestricted collection to add restricted keep-clear areas to
     * @param outUnrestricted collection to add unrestricted keep-clear areas to
     */
    void getKeepClearAreas(Collection<Rect> outRestricted, Collection<Rect> outUnrestricted) {
        final Matrix tmpMatrix = new Matrix();
        final float[] tmpFloat9 = new float[9];
        getKeepClearAreas(outRestricted, outUnrestricted, tmpMatrix, tmpFloat9);
    }

    /**
     * Collects all restricted and unrestricted keep-clear areas for this window.
     * Keep-clear areas are rects that should ideally not be covered by floating windows like Pip.
     * The system is more careful about restricted ones and may apply restrictions to them, while
     * the unrestricted ones are considered safe.
     *
     * @param outRestricted collection to add restricted keep-clear areas to
     * @param outUnrestricted collection to add unrestricted keep-clear areas to
     * @param tmpMatrix a temporary matrix to be used for transformations
     * @param float9 a temporary array of 9 floats
     */
    void getKeepClearAreas(Collection<Rect> outRestricted, Collection<Rect> outUnrestricted,
            Matrix tmpMatrix, float[] float9) {
        outRestricted.addAll(getRectsInScreenSpace(mKeepClearAreas, tmpMatrix, float9));
        outUnrestricted.addAll(
                getRectsInScreenSpace(mUnrestrictedKeepClearAreas, tmpMatrix, float9));
    }

    /**
     * Transforms the given rects from window coordinate space to screen space.
     */
    List<Rect> getRectsInScreenSpace(List<Rect> rects, Matrix tmpMatrix, float[] float9) {
        getTransformationMatrix(float9, tmpMatrix);

        final List<Rect> transformedRects = new ArrayList<Rect>();
        final RectF tmpRect = new RectF();
        Rect curr;
        for (Rect r : rects) {
            tmpRect.set(r);
            tmpMatrix.mapRect(tmpRect);
            curr = new Rect();
            tmpRect.roundOut(curr);
            transformedRects.add(curr);
        }
        return transformedRects;
    }

    /**
     * Sets the new keep-clear areas for this window. The rects should be defined in window
     * coordinate space.
     * Keep-clear areas can be restricted or unrestricted, depending on whether the app holds the
     * {@link android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS} system permission.
     * Restricted ones will be handled more carefully by the system. Restrictions may be applied.
     * Unrestricted ones are considered safe. The system should move floating windows away from them
     * without applying restrictions.
     *
     * @param restricted the new restricted keep-clear areas for this window
     * @param unrestricted the new unrestricted keep-clear areas for this window
     *
     * @return true if there is a change in the list of keep-clear areas; false otherwise
     */
    boolean setKeepClearAreas(List<Rect> restricted, List<Rect> unrestricted) {
        final boolean newRestrictedAreas = !mKeepClearAreas.equals(restricted);
        final boolean newUnrestrictedAreas = !mUnrestrictedKeepClearAreas.equals(unrestricted);
        if (!newRestrictedAreas && !newUnrestrictedAreas) {
            return false;
        }
        if (newRestrictedAreas) {
            mKeepClearAreas.clear();
            mKeepClearAreas.addAll(restricted);
        }

        if (newUnrestrictedAreas) {
            mUnrestrictedKeepClearAreas.clear();
            mUnrestrictedKeepClearAreas.addAll(unrestricted);
        }
        return true;
    }

    /**
     * Used by {@link android.window.WindowOnBackInvokedDispatcher} to set the callback to be
     * called when a back navigation action is initiated.
     * @see BackNavigationController
     */
    void setOnBackInvokedCallbackInfo(
            @Nullable OnBackInvokedCallbackInfo callbackInfo) {
        ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "%s: Setting back callback %s",
                this, callbackInfo);
        mOnBackInvokedCallbackInfo = callbackInfo;
    }

    @Nullable
    OnBackInvokedCallbackInfo getOnBackInvokedCallbackInfo() {
        return mOnBackInvokedCallbackInfo;
    }

    WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
            WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
            int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow) {
        super(service);
        mTmpTransaction = service.mTransactionFactory.get();
        mSession = s;
        mClient = c;
        mAppOp = appOp;
        mToken = token;
        mDisplayContent = token.mDisplayContent;
        mActivityRecord = mToken.asActivityRecord();
        mOwnerUid = ownerId;
        mShowUserId = showUserId;
        mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow;
        mWindowId = new WindowId(this);
        mAttrs.copyFrom(a);
        mLastSurfaceInsets.set(mAttrs.surfaceInsets);
        mViewVisibility = viewVisibility;
        mPolicy = mWmService.mPolicy;
        mContext = mWmService.mContext;
        mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
        mLastReportedActivityWindowInfo = mActivityRecord != null
                ? new ActivityWindowInfo()
                : null;
        mInputWindowHandle = new InputWindowHandleWrapper(new InputWindowHandle(
                mActivityRecord != null
                        ? mActivityRecord.getInputApplicationHandle(false /* update */) : null,
                getDisplayId()));
        mInputWindowHandle.setFocusable(false);
        mInputWindowHandle.setOwnerPid(s.mPid);
        mInputWindowHandle.setOwnerUid(s.mUid);
        mInputWindowHandle.setName(getName());
        mInputWindowHandle.setPackageName(mAttrs.packageName);
        mInputWindowHandle.setLayoutParamsType(mAttrs.type);
        if (DEBUG) {
            Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
                            + " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
        }

        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;

            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 (mActivityRecord != null && mActivityRecord.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 = UNSPECIFIED_LENGTH;
        mRequestedHeight = UNSPECIFIED_LENGTH;
        mLastRequestedWidth = UNSPECIFIED_LENGTH;
        mLastRequestedHeight = UNSPECIFIED_LENGTH;
        mLayer = 0;
        mOverrideScale = mWmService.mAtmService.mCompatModePackages.getCompatScale(
                mAttrs.packageName, s.mUid);
        updateGlobalScale();

        // Make sure we initial all fields before adding to parentWindow, to prevent exception
        // during onDisplayChanged.
        if (mIsChildWindow) {
            ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", this, parentWindow);
            parentWindow.addChild(this, sWindowSubLayerComparator);
        }

    }

    @Override
    void setInitialSurfaceControlProperties(SurfaceControl.Builder b) {
        super.setInitialSurfaceControlProperties(b);
        getPendingTransaction().setTrustedOverlay(mSurfaceControl, isWindowTrustedOverlay());
        getPendingTransaction().setSecure(mSurfaceControl, isSecureLocked());
        // All apps should be considered as occluding when computing TrustedPresentation Thresholds.
        final boolean canOccludePresentation = !mSession.mCanAddInternalSystemWindow;
        getPendingTransaction().setCanOccludePresentation(mSurfaceControl, canOccludePresentation);
    }

    void updateTrustedOverlay() {
        mInputWindowHandle.setTrustedOverlay(getPendingTransaction(), mSurfaceControl,
                isWindowTrustedOverlay());
        mInputWindowHandle.forceChange();
    }

    boolean isWindowTrustedOverlay() {
        if (InputMonitor.isTrustedOverlay(mAttrs.type)) {
            return true;
        }
        if (((mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
                && mSession.mCanAddInternalSystemWindow)) {
            return true;
        }
        if (((mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY) != 0
                && mSession.canCreateSystemApplicationOverlay())) {
            return true;
        }
        return false;
    }

    int getTouchOcclusionMode() {
        if (WindowManager.LayoutParams.isSystemAlertWindowType(mAttrs.type)) {
            return TouchOcclusionMode.USE_OPACITY;
        }
        if (isAnimating(PARENTS, ANIMATION_TYPE_ALL) || inTransition()) {
            return TouchOcclusionMode.USE_OPACITY;
        }
        return TouchOcclusionMode.BLOCK_UNTRUSTED;
    }

    void updateGlobalScale() {
        if (hasCompatScale()) {
            mCompatScale = (mOverrideScale == 1f || mToken.hasSizeCompatBounds())
                    ? mToken.getCompatScale()
                    : 1f;
            mGlobalScale = mCompatScale * mOverrideScale;
            mInvGlobalScale = 1f / mGlobalScale;
            return;
        }

        mGlobalScale = mInvGlobalScale = mCompatScale = 1f;
    }

    float getCompatScaleForClient() {
        // If this window in the size compat mode. The scaling is fully controlled at the server
        // side. The client doesn't need to take it into account.
        return mToken.hasSizeCompatBounds() ? 1f : mCompatScale;
    }

    /**
     * @return {@code true} if the application runs in size compatibility mode or has an app level
     * scaling override set.
     * @see CompatModePackages#getCompatScale
     * @see android.content.res.CompatibilityInfo#supportsScreen
     * @see ActivityRecord#hasSizeCompatBounds()
     */
    boolean hasCompatScale() {
        if (mAttrs.type == TYPE_APPLICATION_STARTING) {
            // Exclude starting window because it is not displayed by the application.
            return false;
        }
        if (mWmService.mAtmService.mCompatModePackages.useLegacyScreenCompatMode(
                mSession.mProcess.mInfo.packageName)) {
            return true;
        }
        return mActivityRecord != null && mActivityRecord.hasSizeCompatBounds()
                || mOverrideScale != 1f;
    }

    /**
     * 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(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
        super.onParentChanged(newParent, oldParent);
        setDrawnStateEvaluated(false /*evaluated*/);

        getDisplayContent().reapplyMagnificationSpec();
    }

    /** Returns the uid of the app that owns this window. */
    int getOwningUid() {
        return mOwnerUid;
    }

    @Override
    public String getOwningPackage() {
        return mAttrs.packageName;
    }

    @Override
    public boolean canAddInternalSystemWindow() {
        return mOwnerCanAddInternalSystemWindow;
    }

    boolean skipLayout() {
        // Skip layout of the window when in transition to pip mode.
        return mActivityRecord != null && (mActivityRecord.mWaitForEnteringPinnedMode
                || mActivityRecord.isConfigurationDispatchPaused());
    }

    void setFrames(ClientWindowFrames clientWindowFrames, int requestedWidth, int requestedHeight) {
        final WindowFrames windowFrames = mWindowFrames;
        mTmpRect.set(windowFrames.mParentFrame);

        windowFrames.mDisplayFrame.set(clientWindowFrames.displayFrame);
        windowFrames.mParentFrame.set(clientWindowFrames.parentFrame);
        windowFrames.mFrame.set(clientWindowFrames.frame);

        windowFrames.mCompatFrame.set(windowFrames.mFrame);
        if (mInvGlobalScale != 1f) {
            // Also, the scaled frame that we report to the app needs to be adjusted to be in
            // its coordinate space.
            windowFrames.mCompatFrame.scale(mInvGlobalScale);
        }
        windowFrames.setParentFrameWasClippedByDisplayCutout(
                clientWindowFrames.isParentFrameClippedByDisplayCutout);

        // Calculate relative frame
        windowFrames.mRelFrame.set(windowFrames.mFrame);
        WindowContainer<?> parent = getParent();
        int parentLeft = 0;
        int parentTop = 0;
        if (mIsChildWindow) {
            parentLeft = ((WindowState) parent).mWindowFrames.mFrame.left;
            parentTop = ((WindowState) parent).mWindowFrames.mFrame.top;
        } else if (parent != null) {
            final Rect parentBounds = parent.getBounds();
            parentLeft = parentBounds.left;
            parentTop = parentBounds.top;
        }
        windowFrames.mRelFrame.offsetTo(windowFrames.mFrame.left - parentLeft,
                windowFrames.mFrame.top - parentTop);

        if (requestedWidth != mLastRequestedWidth || requestedHeight != mLastRequestedHeight
                || !mTmpRect.equals(windowFrames.mParentFrame)) {
            mLastRequestedWidth = requestedWidth;
            mLastRequestedHeight = requestedHeight;
            windowFrames.setContentChanged(true);
        }

        if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)
                || !windowFrames.mRelFrame.equals(windowFrames.mLastRelFrame)) {
            mWmService.mFrameChangingWindows.add(this);
        }

        if (mAttrs.type == TYPE_DOCK_DIVIDER) {
            if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) {
                mMovedByResize = true;
            }
        }

        if (mIsWallpaper) {
            final Rect lastFrame = windowFrames.mLastFrame;
            final Rect frame = windowFrames.mFrame;
            if (lastFrame.width() != frame.width() || lastFrame.height() != frame.height()) {
                mDisplayContent.mWallpaperController.updateWallpaperOffset(this);
            }
        }

        updateSourceFrame(windowFrames.mFrame);

        if (mActivityRecord != null && !mIsChildWindow) {
            mActivityRecord.layoutLetterboxIfNeeded(this);
        }
        mSurfacePlacementNeeded = true;
        mHaveFrame = true;
    }

    void updateSourceFrame(Rect winFrame) {
        if (!hasInsetsSourceProvider()) {
            // This window doesn't provide any insets.
            return;
        }
        if (mGivenInsetsPending) {
            // The given insets are pending, and they are not reliable for now. The source frame
            // should be updated after the new given insets are sent to window manager.
            return;
        }
        if (!mRelayoutCalled) {
            // The window was not laid out yet. The source frame should be updated after the window
            // is laid out.
            return;
        }
        final SparseArray<InsetsSourceProvider> providers = getInsetsSourceProviders();
        for (int i = providers.size() - 1; i >= 0; i--) {
            providers.valueAt(i).updateSourceFrame(winFrame);
        }
    }

    @Override
    public Rect getBounds() {
        // The window bounds are used for layout in screen coordinates. If the token has bounds for
        // size compatibility mode, its configuration bounds are app based coordinates which should
        // not be used for layout.
        return mToken.hasSizeCompatBounds() ? mToken.getBounds() : super.getBounds();
    }

    /** Retrieves the current frame of the window that the application sees. */
    Rect getFrame() {
        return mWindowFrames.mFrame;
    }

    /** Accessor for testing */
    Rect getRelativeFrame() {
        return mWindowFrames.mRelFrame;
    }

    /**
     * Gets the frame that excludes the area of side insets according to the layout parameter from
     * {@link WindowManager.LayoutParams#setFitInsetsSides}.
     */
    Rect getDisplayFrame() {
        return mWindowFrames.mDisplayFrame;
    }

    Rect getParentFrame() {
        return mWindowFrames.mParentFrame;
    }

    /** Retrieves the flags used to disable system UI functions. */
    int getDisableFlags() {
        return mDisableFlags;
    }

    @Override
    public int getBaseType() {
        return getTopParentWindow().mAttrs.type;
    }

    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 boolean insetsChanged = mWindowFrames.hasInsetsChanged();
        if ((!mHasSurface || getDisplayContent().mLayoutSeq != mLayoutSeq || isGoneForLayout())
                && !insetsChanged) {
            return;
        }

        final WindowStateAnimator winAnimator = mWinAnimator;
        final boolean didFrameInsetsChange = setReportResizeHints();
        final boolean configChanged;
        if (mWmService.mAlwaysSeqId) {
            configChanged = !mLastConfigReportedToClient;
        } else {
            // The latest configuration will be returned by the out parameter of relayout, so it is
            // unnecessary to report resize if this window is running relayout.
            configChanged = !mInRelayout && !isLastConfigReportedToClient();
        }
        if (DEBUG_CONFIGURATION && configChanged) {
            Slog.v(TAG_WM, "Win " + this + " config changed: " + getConfiguration());
        }

        final boolean dragResizingChanged = !mDragResizingChangeReported && isDragResizeChanged();
        final boolean attachedFrameChanged = mLayoutAttached && getParentWindow().frameChanged();

        if (DEBUG) {
            Slog.v(TAG_WM, "Resizing " + this + ": configChanged=" + configChanged
                    + " last=" + mWindowFrames.mLastFrame + " frame=" + mWindowFrames.mFrame);
        }

        final boolean contentChanged = didFrameInsetsChange || configChanged
                || dragResizingChanged || attachedFrameChanged;

        if (!mWmService.mAlwaysSeqId) {
            // Cancel unchanged non-sync-buffer redraw request to avoid unnecessary reportResized().
            if (!contentChanged && !mRedrawForSyncReported && mPrepareSyncSeqId <= 0
                    && mDrawHandlers.isEmpty()) {
                mRedrawForSyncReported = true;
            }
        }

        // If a buffer is requested (BLAST) then we must redraw to produce that buffer.
        final boolean requireRedraw = mWmService.mAlwaysSeqId ? mBufferSeqId > mSyncSeqId
                : shouldSendRedrawForSync();

        if (contentChanged || insetsChanged || requireRedraw) {
            ProtoLog.v(WM_DEBUG_RESIZE,
                        "Resize reasons for w=%s:  %s configChanged=%b didFrameInsetsChange=%b",
                        this, mWindowFrames.getInsetsChangedInfo(),
                        configChanged, didFrameInsetsChange);

            consumeInsetsChange();
            onResizeHandled();

            // Reset the drawn state if the window need to redraw for the change, so the transition
            // can wait until it has finished drawing to start.
            if ((configChanged || dragResizingChanged) && isVisibleRequested()) {
                winAnimator.mDrawState = DRAW_PENDING;
                if (mActivityRecord != null) {
                    mActivityRecord.clearAllDrawn();
                    if (mAttrs.type == TYPE_APPLICATION_STARTING
                            && mActivityRecord.mStartingData != null) {
                        mActivityRecord.mStartingData.mIsDisplayed = false;
                    }
                }
            }
            if (!mWmService.mResizingWindows.contains(this)) {
                ProtoLog.v(WM_DEBUG_RESIZE, "Resizing window %s", this);
                mWmService.mResizingWindows.add(this);
                if (mWmService.mAlwaysSeqId) {
                    if (mSyncState != SYNC_STATE_NONE && !getSyncGroup().mReady) {
                        mPendingSyncResize = true;
                    }
                }
            }
        }
    }

    private boolean frameChanged() {
        return !mWindowFrames.mFrame.equals(mWindowFrames.mLastFrame);
    }

    @Override
    void onDisplayChanged(DisplayContent dc) {
        if (mDisplayContent != null && dc != mDisplayContent) {
            if (mInsetsSourceProviders != null && mInsetsSourceProviders.size() > 0) {
                boolean notifyInsetsChange = false;
                for (int i = mInsetsSourceProviders.size() - 1; i >= 0; i--) {
                    if (mInsetsSourceProviders.valueAt(i).isServerVisible()) {
                        notifyInsetsChange = true;
                        break;
                    }
                }
                mDisplayContent.getInsetsStateController().updateAboveInsetsState(
                        notifyInsetsChange);
            }
            if (dc != null) {
                dc.getInsetsStateController().updateAboveInsetsState(
                        // This window doesn't have a frame yet. Don't let this window cause the
                        // insets change.
                        false /* notifyInsetsChange */);

                if (mDisplayContent.getImeInputTarget() == this) {
                    dc.updateImeInputAndControlTarget(getImeInputTarget());
                    mDisplayContent.setImeInputTarget(null /* target */);
                }
            }
        }
        super.onDisplayChanged(dc);
        // Window was not laid out for this display yet, so make sure mLayoutSeq does not match.
        if (dc != null && mInputWindowHandle.getDisplayId() != dc.getDisplayId()) {
            mLayoutSeq = dc.mLayoutSeq - 1;
            mInputWindowHandle.setDisplayId(dc.getDisplayId());
        }
    }

    /** @return The display frames in use by this window. */
    DisplayFrames getDisplayFrames(DisplayFrames originalFrames) {
        final DisplayFrames displayFrames = mToken.getFixedRotationTransformDisplayFrames();
        if (displayFrames != null) {
            return displayFrames;
        }
        return originalFrames;
    }

    DisplayInfo getDisplayInfo() {
        final DisplayInfo displayInfo = mToken.getFixedRotationTransformDisplayInfo();
        if (displayInfo != null) {
            return displayInfo;
        }
        return getDisplayContent().getDisplayInfo();
    }

    @Override
    public Rect getMaxBounds() {
        final Rect maxBounds = mToken.getFixedRotationTransformMaxBounds();
        if (maxBounds != null) {
            return maxBounds;
        }
        return super.getMaxBounds();
    }

    /**
     * See {@link WindowState#getInsetsState(boolean)}
     */
    InsetsState getInsetsState() {
        return getInsetsState(false);
    }

    /**
     * Returns the insets state for the window. Its sources may be the copies with visibility
     * modification according to the state of transient bars.
     * This is to get the insets for a window layout on the screen. If the window is not there, use
     * the {@link InsetsPolicy#getInsetsForWindowMetrics} to get insets instead.
     * @param includeTransient whether or not the transient types should be included in the
     *                         insets state.
     */
    InsetsState getInsetsState(boolean includeTransient) {
        final InsetsState rotatedState = mToken.getFixedRotationTransformInsetsState();
        final InsetsPolicy insetsPolicy = getDisplayContent().getInsetsPolicy();
        if (rotatedState != null) {
            return insetsPolicy.adjustInsetsForWindow(this, rotatedState);
        }
        final InsetsState rawInsetsState =
                mFrozenInsetsState != null ? mFrozenInsetsState : getMergedInsetsState();
        final InsetsState insetsStateForWindow = insetsPolicy.enforceInsetsPolicyForTarget(this,
                rawInsetsState);
        return insetsPolicy.adjustInsetsForWindow(this, insetsStateForWindow,
                includeTransient);
    }

    private InsetsState getMergedInsetsState() {
        final InsetsState globalInsetsState = mAttrs.receiveInsetsIgnoringZOrder
                ? getDisplayContent().getInsetsStateController().getRawInsetsState()
                : mAboveInsetsState;
        if (mMergedLocalInsetsSources == null) {
            return globalInsetsState;
        }

        final InsetsState mergedInsetsState = new InsetsState(globalInsetsState);
        for (int i = 0; i < mMergedLocalInsetsSources.size(); i++) {
            mergedInsetsState.addSource(mMergedLocalInsetsSources.valueAt(i));
        }
        return mergedInsetsState;
    }

    /**
     * Returns the insets state for the client and scales the frames if the client is in the size
     * compatible mode.
     */
    InsetsState getCompatInsetsState() {
        InsetsState state = getInsetsState();
        if (mInvGlobalScale != 1f) {
            state = new InsetsState(state, true);
            state.scale(mInvGlobalScale);
        }
        return state;
    }

    /**
     * Returns the insets state for the window and applies the requested visibility.
     */
    InsetsState getInsetsStateWithVisibilityOverride() {
        final InsetsState state = new InsetsState(getInsetsState(), true /* copySources */);
        for (int i = state.sourceSize() - 1; i >= 0; i--) {
            final InsetsSource source = state.sourceAt(i);
            final boolean requestedVisible = isRequestedVisible(source.getType());
            if (source.isVisible() != requestedVisible) {
                source.setVisible(requestedVisible);
            }
        }
        return state;
    }

    @Override
    public int getDisplayId() {
        final DisplayContent displayContent = getDisplayContent();
        if (displayContent == null) {
            return Display.INVALID_DISPLAY;
        }
        return displayContent.getDisplayId();
    }

    @Override
    public WindowState getWindowState() {
        return this;
    }

    @Override
    public IBinder getWindowToken() {
        return mClient.asBinder();
    }

    @Override
    public int getPid() {
        return mSession.mPid;
    }

    @Override
    public int getUid() {
        return mSession.mUid;
    }

    Task getTask() {
        return mActivityRecord != null ? mActivityRecord.getTask() : null;
    }

    @Nullable TaskFragment getTaskFragment() {
        return mActivityRecord != null ? mActivityRecord.getTaskFragment() : 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 intersectWithRootTaskBounds = task != null && task.cropWindowsToRootTaskBounds();
        bounds.setEmpty();
        mTmpRect.setEmpty();
        if (intersectWithRootTaskBounds) {
            final Task rootTask = task.getRootTask();
            if (rootTask != null) {
                rootTask.getDimBounds(mTmpRect);
            } else {
                intersectWithRootTaskBounds = false;
            }
        }

        bounds.set(mWindowFrames.mFrame);
        bounds.inset(getInsetsStateWithVisibilityOverride().calculateVisibleInsets(
                bounds, bounds, mAttrs.type, getActivityType(), mAttrs.softInputMode, mAttrs.flags
        ));
        if (intersectWithRootTaskBounds) {
            bounds.intersect(mTmpRect);
        }
    }

    public long getInputDispatchingTimeoutMillis() {
        return mActivityRecord != null
                ? mActivityRecord.mInputDispatchingTimeoutMillis
                : DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
    }

    long getCreateTime() {
        return mCreateTime;
    }

    /**
     * Returns true if, at any point, the application token associated with this window has actually
     * displayed any windows. This is most useful with the "starting up" window to determine if any
     * windows were displayed when it is closed.
     *
     * @return {@code true} if one or more windows have been displayed, else false.
     */
    boolean hasAppShownWindows() {
        return mActivityRecord != null && (mActivityRecord.firstWindowDrawn
                || mActivityRecord.isStartingWindowDisplayed());
    }

    private boolean isVisibleByPolicyOrInsets() {
        return 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.
                && (mControllableInsetProvider == null
                        || mControllableInsetProvider.isClientVisible());
    }

    @Override
    boolean isVisible() {
        return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicyOrInsets();
    }

    @Override
    boolean isVisibleRequested() {
        final boolean localVisibleRequested =
                wouldBeVisibleRequestedIfPolicyIgnored() && isVisibleByPolicyOrInsets();
        if (localVisibleRequested && shouldCheckTokenVisibleRequested()) {
            return mToken.isVisibleRequested();
        }
        return localVisibleRequested;
    }

    /**
     * Returns {@code true} if {@link WindowToken#isVisibleRequested()} should be considered
     * before dispatching the latest configuration. Currently only {@link
     * ActivityRecord#isVisibleRequested()} and {@link WallpaperWindowToken#isVisibleRequested()}
     * implement explicit visible-requested.
     */
    boolean shouldCheckTokenVisibleRequested() {
        return mActivityRecord != null || mToken.asWallpaperToken() != null;
    }

    /**
     * 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;
    }

    boolean providesDisplayDecorInsets() {
        if (mInsetsSourceProviders == null) {
            return false;
        }
        for (int i = mInsetsSourceProviders.size() - 1; i >= 0; i--) {
            final InsetsSource source = mInsetsSourceProviders.valueAt(i).getSource();
            if ((source.getType() & DisplayPolicy.DecorInsets.OVERRIDE_CONFIG_TYPES) != 0) {
                return true;
            }
        }
        return false;
    }

    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() {
        if (!mHasSurface || isParentWindowHidden() || mAnimatingExit || mDestroying) {
            return false;
        }
        final boolean isWallpaper = mToken.asWallpaperToken() != null;
        return !isWallpaper || mToken.isVisible();
    }

    private boolean wouldBeVisibleRequestedIfPolicyIgnored() {
        final WindowState parent = getParentWindow();
        final boolean isParentHiddenRequested = parent != null && !parent.isVisibleRequested();
        if (isParentHiddenRequested || mAnimatingExit || mDestroying) {
            return false;
        }
        final boolean isWallpaper = mToken.asWallpaperToken() != null;
        return !isWallpaper || mToken.isVisibleRequested();
    }

    /**
     * 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.isVisible() || 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(boolean targetInterceptsGlobalDrag) {
        return (targetInterceptsGlobalDrag || isVisibleNow()) && !mRemoved
                && mInputChannelToken != null && mInputWindowHandle != null;
    }

    /**
     * Is this window capable of being visible (policy and content), in a visible part of the
     * hierarchy, and, if an activity window, the activity is visible-requested. Note, this means
     * if the activity is going-away, this will be {@code false} even when the window is visible.
     *
     * The 'adding' part refers to the period of time between IWindowSession.add() and the first
     * relayout() -- which, for activities, is the same as visibleRequested.
     *
     * TODO(b/206005136): This is very similar to isVisibleRequested(). Investigate merging them.
     */
    boolean isVisibleRequestedOrAdding() {
        final ActivityRecord atoken = mActivityRecord;
        return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
                && isVisibleByPolicy() && !isParentWindowHidden()
                && (atoken == null || atoken.isVisibleRequested())
                && !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 ActivityRecord atoken = mActivityRecord;
        if (atoken != null) {
            if (atoken.mStartingData != null && mAttrs.type != TYPE_APPLICATION_STARTING
                    && atoken.mStartingData.mRemoveAfterTransaction
                    == AFTER_TRANSITION_FINISH) {
                // Preventing app window from visible during un-occluding animation playing due to
                // alpha blending.
                return false;
            }
            final boolean isVisible = isStartingWindowAssociatedToTask()
                    ? mStartingData.mAssociatedTask.isVisible() : atoken.isVisible();
            return ((!isParentWindowHidden() && isVisible)
                    || isAnimationRunningSelfOrParent());
        }
        final WallpaperWindowToken wtoken = mToken.asWallpaperToken();
        if (wtoken != null) {
            return !isParentWindowHidden() && wtoken.isVisible();
        }
        return !isParentWindowHidden() || isAnimating(PARENTS, ANIMATION_TYPE_ALL);
    }

    boolean isDreamWindow() {
        return mActivityRecord != null
               && mActivityRecord.getActivityType() == ACTIVITY_TYPE_DREAM;
    }

    boolean isSecureLocked() {
        if (mWmService.getDisableSecureWindows()) {
            return false;
        }

        if ((mAttrs.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
            return true;
        }

        // block screen capture to protect sensitive notifications or content on the screen.
        if (mWmService.mSensitiveContentPackages.shouldBlockScreenCaptureForApp(
                getOwningPackage(), getOwningUid(), getWindowToken())) {
            return true;
        }

        return !DevicePolicyCache.getInstance().isScreenCaptureAllowed(mShowUserId);
    }

    /**
     * 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 mActivityRecord != null
                && 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() {
        final boolean parentAndClientVisible = !isParentWindowHidden()
                && mViewVisibility == View.VISIBLE;
        return mHasSurface && isVisibleByPolicy() && !mDestroying && mToken.isVisible()
                && (parentAndClientVisible || isAnimating(PARENTS, ANIMATION_TYPE_ALL));
    }

    boolean isFullyTransparent() {
        return mAttrs.alpha == 0f;
    }

    /**
     * @return Whether the window can affect SystemUI flags, meaning that SystemUI (system bars,
     *         for example) will be  affected by the flags specified in this window. This is the
     *         case when the surface is on screen but not exiting.
     */
    boolean canAffectSystemUiFlags() {
        if (isFullyTransparent()) {
            return false;
        }
        if (mActivityRecord == null) {
            final boolean shown = mWinAnimator.getShown();
            final boolean exiting = mAnimatingExit || mDestroying;
            return shown && !exiting;
        } else {
            return mActivityRecord.canAffectSystemUiFlags()
                    // Do not let snapshot window control the bar
                    && (mAttrs.type != TYPE_APPLICATION_STARTING
                            || !(mStartingData instanceof SnapshotStartingData));
        }
    }

    /**
     * Like isOnScreen, but returns false if the surface hasn't yet
     * been drawn.
     */
    boolean isDisplayed() {
        final ActivityRecord atoken = mActivityRecord;
        return isDrawn() && isVisibleByPolicy()
                && ((!isParentWindowHidden() && (atoken == null || atoken.isVisibleRequested()))
                        || isAnimationRunningSelfOrParent());
    }

    /**
     * Return true if this window or its app token is currently animating.
     */
    @Override
    public boolean isAnimatingLw() {
        return isAnimating(PARENTS, ANIMATION_TYPE_ALL);
    }

    /** Returns {@code true} if this window considered to be gone for purposes of layout. */
    boolean isGoneForLayout() {
        final ActivityRecord atoken = mActivityRecord;
        return mViewVisibility == View.GONE
                || !mRelayoutCalled
                // We can't check isVisible here because it will also check the client visibility
                // for WindowTokens. Even if the client is not visible, we still need to perform
                // a layout since they can request relayout when client visibility is false.
                // TODO (b/157682066) investigate if we can clean up isVisible
                || (atoken == null && !(wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicy()))
                || (atoken != null && !atoken.isVisibleRequested())
                || 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. Note that
     * this is different from {@link #hasDrawn()} in that it also returns true if the window is
     * READY_TO_SHOW, but was not yet promoted to HAS_DRAWN.
     */
    boolean isDrawn() {
        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.
        final boolean isWallpaper = mToken.asWallpaperToken() != null;
        return ((!isWallpaper && mAttrs.format == PixelFormat.OPAQUE)
                || (isWallpaper && mToken.isVisible()))
                && isDrawn() && !isAnimating(PARENTS, ANIMATION_TYPE_ALL);
    }

    /** @see WindowManagerInternal#waitForAllWindowsDrawn */
    void requestDrawIfNeeded(List<WindowState> outWaitingForDrawn) {
        if (!isVisible()) {
            return;
        }
        final WallpaperWindowToken wallpaperToken = mToken.asWallpaperToken();
        if (wallpaperToken != null) {
            if (wallpaperToken.hasVisibleNotDrawnWallpaper()) {
                outWaitingForDrawn.add(this);
            }
            return;
        }
        if (mActivityRecord != null) {
            if (!mActivityRecord.isVisibleRequested()) return;
            if (mActivityRecord.allDrawn) {
                // The allDrawn of activity is reset when the visibility is changed to visible, so
                // the content should be ready if allDrawn is set.
                return;
            }
            if (mAttrs.type == TYPE_APPLICATION_STARTING) {
                if (isDrawn()) {
                    // Unnecessary to redraw a drawn starting window.
                    return;
                }
            } else if (mActivityRecord.mStartingWindow != null) {
                // If the activity has an active starting window, there is no need to wait for the
                // main window.
                return;
            }
        } else if (!mPolicy.isKeyguardHostWindow(mAttrs)) {
            return;
            // Always invalidate keyguard host window to make sure it shows the latest content
            // because its visibility may not be changed.
        }

        mWinAnimator.mDrawState = DRAW_PENDING;
        // Force add to {@link WindowManagerService#mResizingWindows}.
        forceReportingResized();
        outWaitingForDrawn.add(this);
    }

    @Override
    void onMovedByResize() {
        ProtoLog.d(WM_DEBUG_RESIZE, "onMovedByResize: Moving %s", this);
        mMovedByResize = true;
        if (mControllableInsetProvider != null) {
            mControllableInsetProvider.onWindowBoundsChanged();
        }
        super.onMovedByResize();
    }

    void onAppCommitInvisible() {
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            mChildren.get(i).onAppCommitInvisible();
        }
        if (mAttrs.type != TYPE_APPLICATION_STARTING
                && mWmService.mAccessibilityController.hasCallbacks()
                // It is a change only if App visibility and WS visibility are different.
                && isVisible()) {
            mWmService.mAccessibilityController.onWindowTransition(this, TRANSIT_EXIT);
        }
    }

    boolean onSetAppExiting(boolean animateExit) {
        final DisplayContent displayContent = getDisplayContent();
        boolean changed = false;

        if (!animateExit) {
            // Hide the window permanently if no window exist animation is performed, so we can
            // avoid the window surface becoming visible again unexpectedly during the next
            // relayout.
            mPermanentlyHidden = true;
            hide(false /* doAnimation */, false /* requestAnim */);
        }
        if (isVisibleNow() && animateExit) {
            mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
            if (mWmService.mAccessibilityController.hasCallbacks()) {
                mWmService.mAccessibilityController.onWindowTransition(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(animateExit);
        }

        return changed;
    }

    @Override
    void onResize() {
        final ArrayList<WindowState> resizingWindows = mWmService.mResizingWindows;
        if (mHasSurface && !isGoneForLayout() && !resizingWindows.contains(this)) {
            ProtoLog.d(WM_DEBUG_RESIZE, "onResize: Resizing %s", this);
            resizingWindows.add(this);
            if (mWmService.mAlwaysSeqId) {
                if (mSyncState != SYNC_STATE_NONE && !getSyncGroup().mReady) {
                    mPendingSyncResize = true;
                }
            }
        }
        if (mControllableInsetProvider != null) {
            mControllableInsetProvider.onWindowBoundsChanged();
        }

        super.onResize();
    }

    /**
     * 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;

        if (canPlayMoveAnimation()) {
            startMoveAnimation(left, top);
        }

        if (mWmService.mAccessibilityController.hasCallbacks()) {
            mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId());
        }

        try {
            mClient.moved(left, top);
        } catch (RemoteException e) {
        }
        mMovedByResize = false;
    }

    private boolean canPlayMoveAnimation() {

        // During the transition from pip to fullscreen, the activity windowing mode is set to
        // fullscreen at the beginning while the task is kept in pinned mode. Skip the move
        // animation in such case since the transition is handled in SysUI.
        final boolean hasMovementAnimation = getTask() == null
                ? getWindowConfiguration().hasMovementAnimations()
                : getTask().getWindowConfiguration().hasMovementAnimations();
        return mToken.okToAnimate()
                && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
                && !isDragResizing()
                && hasMovementAnimation
                && !mWinAnimator.mLastHidden;
    }

    /**
     * Return whether this window has moved. (Only makes
     * sense to call from performLayoutAndPlaceSurfacesLockedInner().)
     */
    private boolean hasMoved() {
        return mHasSurface && (mWindowFrames.hasContentChanged() || mMovedByResize)
                && !mAnimatingExit
                && (mWindowFrames.mRelFrame.top != mWindowFrames.mLastRelFrame.top
                    || mWindowFrames.mRelFrame.left != mWindowFrames.mLastRelFrame.left)
                && (!mIsChildWindow || !getParentWindow().hasMoved())
                && !mTransitionController.isCollecting();
    }

    boolean isObscuringDisplay() {
        Task task = getTask();
        if (task != null && !task.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;
    }

    boolean matchesDisplayAreaBounds() {
        final Rect rotatedDisplayBounds = mToken.getFixedRotationTransformDisplayBounds();
        if (rotatedDisplayBounds != null) {
            // If the rotated display bounds are available, the window bounds are also rotated.
            return rotatedDisplayBounds.equals(getBounds());
        }
        final DisplayArea displayArea = getDisplayArea();
        if (displayArea == null) {
            return getDisplayContent().getBounds().equals(getBounds());
        }
        return displayArea.getBounds().equals(getBounds());
    }

    /**
     * @return {@code true} if last applied config was reported to the client already, {@code false}
     *         otherwise.
     */
    boolean isLastConfigReportedToClient() {
        return mLastConfigReportedToClient;
    }

    /**
     * Increments the seqId for cases where we are expecting/requiring the client to call
     * relayout (adding or making a window visible). This relies on the fact that relayout still
     * sends the latest configuration so we can "pretend" that we already sent the configuration
     * (to prevent additional resize messages).
     *
     * @return the seqId.
     */
    int incrementSeqForRelayout() {
        if (!mWmService.mAlwaysSeqId) return -1;
        ++mSyncSeqId;
        mLastConfigReportedToClient = true;
        return mSyncSeqId;
    }

    @Override
    public void onConfigurationChanged(Configuration newParentConfig) {
        // Get from super to avoid using the updated global config from the override method.
        final Configuration selfConfiguration = super.getConfiguration();
        mTempConfiguration.setTo(selfConfiguration);
        super.onConfigurationChanged(newParentConfig);
        final int diff = selfConfiguration.diff(mTempConfiguration);
        if (diff != 0) {
            mLastConfigReportedToClient = false;
        }

        if (getDisplayContent().getImeInputTarget() != this && !isImeLayeringTarget()) {
            return;
        }
        // When the window configuration changed, we need to update the IME control target in
        // case the app may lose the IME inets control when exiting from split-screen mode, or the
        // IME parent may failed to attach to the app during rotating the screen.
        // See DisplayContent#shouldImeAttachedToApp, DisplayContent#isImeControlledByApp
        if ((diff & CONFIG_WINDOW_CONFIGURATION) != 0) {
            // If the window was the IME layering target, updates the IME surface parent in case
            // the IME surface may be wrongly positioned when the window configuration affects the
            // IME surface association. (e.g. Attach IME surface on the display instead of the
            // app when the app bounds being letterboxed.)
            mDisplayContent.updateImeControlTarget(
                    isImeLayeringTarget() /* forceUpdateImeParent */);
            // Fix the starting window to task when Activity has changed.
            if (mStartingData != null && mStartingData.mAssociatedTask == null
                    && mTempConfiguration.windowConfiguration.getRotation()
                            == selfConfiguration.windowConfiguration.getRotation()
                    && !RegionUtils.sizeEquals(
                            mTempConfiguration.windowConfiguration.getBounds(), getBounds())) {
                mStartingData.mResizedFromTransfer = true;
                // Lock the starting window to task, so it won't resize from transfer anymore.
                mActivityRecord.associateStartingWindowWithTaskIfNeeded();
            }
        }
    }

    @Override
    void removeImmediately() {
        if (mRemoved) {
            // Nothing to do.
            ProtoLog.v(WM_DEBUG_ADD_REMOVE,
                    "WS.removeImmediately: %s Already removed...", this);
            return;
        }

        mRemoved = true;
        if (mWmService.mAlwaysSeqId) {
            mPendingSyncResize = false;
        }
        // Destroy surface before super call. The general pattern is that the children need
        // to be removed before the parent (so that the sync-engine tracking works). Since
        // WindowStateAnimator is a "virtual" child, we have to do it manually here.
        mWinAnimator.destroySurfaceLocked(getSyncTransaction());
        if (!mDrawHandlers.isEmpty()) {
            mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
        }
        super.removeImmediately();

        final DisplayContent dc = getDisplayContent();
        if (isImeLayeringTarget()) {
            // Remove the attached IME screenshot.
            dc.removeImeScreenshotByTarget(this);
            dc.computeImeLayeringTarget(true /* update */);
        }
        if (dc.getImeInputTarget() == this && !inRelaunchingActivity()) {
            mWmService.dispatchImeInputTargetVisibilityChanged(mClient.asBinder(),
                    false /* visible */, true /* removed */, dc.getDisplayId());
            dc.updateImeInputAndControlTarget(null /* target */);
        }

        // Check if window provides non decor insets before clearing its provided insets.
        final boolean windowProvidesDisplayDecorInsets = providesDisplayDecorInsets();

        dc.getDisplayPolicy().removeWindowLw(this);

        disposeInputChannel();
        mOnBackInvokedCallbackInfo = null;

        mSession.onWindowRemoved(this);
        mWmService.postWindowRemoveCleanupLocked(this);

        consumeInsetsChange();

        // Update the orientation when removing a window affecting the display orientation.
        // Also recompute configuration if it provides screen decor insets.
        boolean needToSendNewConfiguration = dc.getLastOrientationSource() == this
                && dc.updateOrientation();
        if (windowProvidesDisplayDecorInsets) {
            needToSendNewConfiguration |= dc.getDisplayPolicy().updateDecorInsetsInfo();
        }

        if (needToSendNewConfiguration) {
            dc.sendNewConfiguration();
        }
    }

    @Override
    void removeIfPossible() {
        mWindowRemovalAllowed = true;
        ProtoLog.v(WM_DEBUG_ADD_REMOVE,
                "removeIfPossible: %s callers=%s", this, Debug.getCallers(5));

        final boolean startingWindow = mStartingData != null;
        if (startingWindow) {
            ProtoLog.d(WM_DEBUG_STARTING_WINDOW, "Starting window removed %s", this);
            // Cancel the remove starting window animation on shell. The main window might changed
            // during animating, checking for all windows would be safer.
            if (mActivityRecord != null) {
                mActivityRecord.forAllWindows(w -> {
                    if (w.isSelfAnimating(0, ANIMATION_TYPE_STARTING_REVEAL)) {
                        w.cancelAnimation();
                        return true;
                    }
                    return false;
                }, true);
            }

            if (mStartingData.mTransitionId != 0) {
                mTransitionController.mTransitionTracer.logRemovingStartingWindow(mStartingData);
            }
        } else if (mAttrs.type == TYPE_BASE_APPLICATION
                && isSelfAnimating(0, ANIMATION_TYPE_STARTING_REVEAL)) {
            // Cancel the remove starting window animation in case the binder dead before remove
            // splash window.
            cancelAnimation();
        }

        ProtoLog.v(WM_DEBUG_FOCUS, "Remove client=%x, surfaceController=%s Callers=%s",
                    System.identityHashCode(mClient.asBinder()),
                    mWinAnimator.mSurfaceControl,
                    Debug.getCallers(5));

        final DisplayContent displayContent = getDisplayContent();
        final long origId = Binder.clearCallingIdentity();

        try {
            disposeInputChannel();
            mOnBackInvokedCallbackInfo = null;

            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                    "Remove %s: mSurfaceControl=%s mAnimatingExit=%b mRemoveOnExit=%b "
                            + "mHasSurface=%b surfaceShowing=%b animating=%b app-animation=%b "
                            + "callers=%s",
                    this, mWinAnimator.mSurfaceControl, mAnimatingExit, mRemoveOnExit,
                    mHasSurface, mWinAnimator.getShown(),
                    isAnimating(PARENTS, ANIMATION_TYPE_ALL),
                    mActivityRecord != null && mActivityRecord.inTransition(),
                    Debug.getCallers(6));

            if (mAttrs.type != TYPE_BASE_APPLICATION && mHasSurface
                    && mActivityRecord != null && !mActivityRecord.isVisibleRequested()
                    && mWinAnimator.getShown()) {
                // Only remove the activity snapshot, because the user might still want to see the
                // task snapshot during the recents animation.
                mWmService.mSnapshotController.mActivitySnapshotController
                        .invalidateSnapshot(mActivityRecord);
            }
            // 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()) {
                // Remove immediately if there is display transition because the animation is
                // usually unnoticeable (e.g. covered by rotation animation) and the animation
                // bounds could be inconsistent, such as depending on when the window applies
                // its draw transaction with new rotation.
                final boolean allowExitAnimation = !displayContent.inTransition()
                        // There will be a new window so the exit animation may not be visible or
                        // look weird if its orientation is changed.
                        && !inRelaunchingActivity();

                // If we are not currently running the exit animation,
                // we need to see about starting one
                if (isVisible() && isDisplayed()) {
                    final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;

                    // Try starting an animation.
                    if (allowExitAnimation && mWinAnimator.applyAnimationLocked(transit, false)) {
                        ProtoLog.v(WM_DEBUG_ANIM,
                                "Set animatingExit: reason=remove/applyAnimation win=%s", this);
                        if (startingWindow && mSurfaceAnimator.hasLeash()) {
                            // Keep starting window on top during fade-out animation.
                            getPendingTransaction().setLayer(mSurfaceAnimator.mLeash,
                                    Integer.MAX_VALUE);
                        }
                        mAnimatingExit = true;

                        // mAnimatingExit affects canAffectSystemUiFlags(). Run layout such that
                        // any change from that is performed immediately.
                        setDisplayLayoutNeeded();
                        mWmService.requestTraversal();
                    }
                    if (mWmService.mAccessibilityController.hasCallbacks()) {
                        mWmService.mAccessibilityController.onWindowTransition(this, transit);
                    }
                }
                final boolean isAnimating = allowExitAnimation
                        && (mAnimatingExit || isAnimationRunningSelfOrParent());
                final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null
                        && mActivityRecord.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() && !lastWindowIsStartingWindow && isAnimating) {
                    // Make isSelfOrAncestorWindowAnimatingExit return true so onExitAnimationDone
                    // can proceed to remove this window.
                    mAnimatingExit = true;
                    // The exit animation is running or should run... wait for it!
                    ProtoLog.v(WM_DEBUG_ADD_REMOVE,
                            "Not removing %s due to exit animation", this);
                    ProtoLog.v(WM_DEBUG_ANIM, "Set animatingExit: reason=remove/isAnimating win=%s",
                            this);
                    setupWindowForRemoveOnExit();
                    if (mActivityRecord != null) {
                        mActivityRecord.updateReportedVisibilityLocked();
                    }
                    return;
                }
            }

            // Only a presentation window needs a transition because its visibility affets the
            // lifecycle of apps below (b/390481865).
            if (ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS.isTrue() && isPresentation()) {
                final ActionChain chain =
                        mWmService.mAtmService.mChainTracker.startTransit("removeWin");
                final boolean wasTransitionOnDisplay = chain.isCollectingOnDisplay(displayContent);
                Transition newlyCreatedTransition = null;
                if (!chain.isCollecting()) {
                    chain.attachTransition(
                            mTransitionController.createAndStartCollecting(TRANSIT_CLOSE));
                    newlyCreatedTransition = chain.getTransition();
                }
                chain.collect(mToken);
                mAnimatingExit = true;
                mRemoveOnExit = true;
                mToken.setVisibleRequested(false);
                // A presentation hides all activities behind on the same display.
                mDisplayContent.ensureActivitiesVisible(/*starting=*/ null,
                        /*notifyClients=*/ true);
                if (!wasTransitionOnDisplay && mTransitionController
                        .isCollectingTransitionOnDisplay(displayContent)) {
                    // Set the display ready only when the display gets added to the collecting
                    // transition in this operation.
                    mTransitionController.setReady(mToken);
                }
                mWmService.mAtmService.mChainTracker.endPartial();
                if (newlyCreatedTransition != null) {
                    mTransitionController.requestStartTransition(newlyCreatedTransition, null,
                            null /* remoteTransition */, null /* displayChange */);
                }
            } else {
                removeImmediately();
                mWmService.updateFocusedWindowLocked(isFocused()
                                ? UPDATE_FOCUS_REMOVING_FOCUS
                                : UPDATE_FOCUS_NORMAL,
                        true /*updateInputWindows*/);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    private void setupWindowForRemoveOnExit() {
        mRemoveOnExit = true;
        setDisplayLayoutNeeded();
        getDisplayContent().getDisplayPolicy().removeWindowLw(this);
        // 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;
    }

    /** Check if the window can be the IME layering target. */
    boolean canBeImeLayeringTarget() {
        if (mIsImWindow) {
            // IME windows can't be IME layering targets as these must be below the IME
            // windows and that wouldn't be possible if the IME window is its own target...silly.
            return false;
        }

        if (inPinnedWindowingMode()) {
            return false;
        }

        if (mAttrs.type == TYPE_SCREENSHOT) {
            // Disallow screenshot windows from being IME layering targets
            return false;
        }

        final boolean windowsAreFocusable = mActivityRecord == null
                || mActivityRecord.windowsAreFocusable();
        if (!windowsAreFocusable) {
            // Can't be an IME layering target if the app's windows are not be focusable.
            return false;
        }

        final Task task = getTask();
        if (task != null && !task.isFocusable()) {
            // The task can be set as non-focusable, e.g. swapping split-screen sides.
            return false;
        }

        if (mAttrs.type == TYPE_APPLICATION_STARTING) {
            // Ignore mayUseInputMethod for starting window for now.
            // TODO(b/159911356): Remove this special casing (originally added in commit e75d872).
        } else {
            // TODO(b/145812508): Clean this up in S, may depend on b/141738570
            //  The current logic lets windows become the "ime layering target" even though they are
            //  not-focusable and can thus never actually start input.
            //  Ideally, this would reject windows where mayUseInputMethod() == false, but this
            //  also impacts Z-ordering of and delivery of IME insets to child windows, which means
            //  that simply disallowing non-focusable windows would break apps.
            //  See b/159438771, b/144619551.

            final int fl = mAttrs.flags & (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);

            // Can only be an IME layering 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)) {
                return false;
            }
        }

        // Don't allow transient-launch activities to take IME.
        if (task != null && mActivityRecord != null
                && mTransitionController.isTransientLaunch(mActivityRecord)) {
            return false;
        }

        if (DEBUG_INPUT_METHOD) {
            Slog.i(TAG_WM, "isVisibleRequestedOrAdding " + this + ": "
                    + isVisibleRequestedOrAdding() + " isVisible: " + (isVisible()
                    && mActivityRecord != null && mActivityRecord.isVisible()));
            if (!isVisibleRequestedOrAdding()) {
                Slog.i(TAG_WM, "  mSurfaceControl=" + mWinAnimator.mSurfaceControl
                        + " relayoutCalled=" + mRelayoutCalled
                        + " viewVis=" + mViewVisibility
                        + " policyVis=" + isVisibleByPolicy()
                        + " policyVisAfterAnim=" + mLegacyPolicyVisibilityAfterAnim
                        + " parentHidden=" + isParentWindowHidden()
                        + " exiting=" + mAnimatingExit + " destroying=" + mDestroying);
                if (mActivityRecord != null) {
                    Slog.i(TAG_WM, "  mActivityRecord.visibleRequested="
                            + mActivityRecord.isVisibleRequested());
                }
            }
        }
        return isVisibleRequestedOrAdding()
                || (isVisible() && mActivityRecord != null && mActivityRecord.isVisible());
    }

    void openInputChannel(@NonNull InputChannel outInputChannel) {
        if (mInputChannelToken != null) {
            throw new IllegalStateException("Window already has an input channel token.");
        }
        String name = getName();
        InputChannel channel = mWmService.mInputManager.createInputChannel(name);
        mInputChannelToken = channel.getToken();
        mInputWindowHandle.setToken(mInputChannelToken);
        mWmService.mInputToWindowMap.put(mInputChannelToken, this);
        channel.copyTo(outInputChannel);
        channel.dispose();
    }

    /**
     * Move the touch gesture from the currently touched window on this display to this window.
     *
     * @deprecated Use {@link
     *   com.android.server.input.InputManagerInternal#transferTouchGesture(IBinder, IBinder)}.
     */
    @Deprecated
    public boolean transferTouch() {
        return mWmService.mInputManager.transferTouch(mInputChannelToken, getDisplayId());
    }

    void disposeInputChannel() {
        if (mInputChannelToken != null) {
            // Unregister server channel first otherwise it complains about broken channel.
            mWmService.mInputManager.removeInputChannel(mInputChannelToken);
            mWmService.mKeyInterceptionInfoForToken.remove(mInputChannelToken);
            mWmService.mInputToWindowMap.remove(mInputChannelToken);
            mInputChannelToken = null;
        }

        mInputWindowHandle.setToken(null);
    }

    void setDisplayLayoutNeeded() {
        final DisplayContent dc = getDisplayContent();
        if (dc != null) {
            dc.setLayoutNeeded();
        }
    }

    @Override
    void switchUser(int userId) {
        super.switchUser(userId);

        if (showToCurrentUser()) {
            setPolicyVisibilityFlag(VISIBLE_FOR_USER);
        } else {
            if (DEBUG_VISIBILITY) Slog.w(TAG_WM, "user changing, hiding " + this
                    + ", attrs=" + mAttrs.type + ", belonging to " + mOwnerUid);
            clearPolicyVisibilityFlag(VISIBLE_FOR_USER);
        }
    }

    void getSurfaceTouchableRegion(Region region, WindowManager.LayoutParams attrs) {
        final boolean modal = attrs.isModal();
        if (modal) {
            if (mActivityRecord != null) {
                // Limit the outer touch to the activity root task region.
                updateRegionForModalActivityWindow(region);
            } else {
                // 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);
            }
            subtractTouchExcludeRegionIfNeeded(region);

        } else {
            // Not modal
            getTouchableRegion(region);
        }

        // Translate to surface based coordinates.
        final Rect frame = mWindowFrames.mFrame;
        if (frame.left != 0 || frame.top != 0) {
            region.translate(-frame.left, -frame.top);
        }
        if (modal && mTouchableInsets == TOUCHABLE_INSETS_REGION) {
            // The client gave us a touchable region and so first
            // we calculate the untouchable region, then punch that out of our
            // expanded modal region.
            mTmpRegion.set(0, 0, frame.width(), frame.height());
            mTmpRegion.op(mGivenTouchableRegion, Region.Op.DIFFERENCE);
            region.op(mTmpRegion, Region.Op.DIFFERENCE);
        }

        // TODO(b/139804591): sizecompat layout needs to be reworked. Currently mFrame is post-
        // scaling but the existing logic doesn't expect that. The result is that the already-
        // scaled region ends up getting sent to surfaceflinger which then applies the scale
        // (again). Until this is resolved, apply an inverse-scale here.
        if (mInvGlobalScale != 1.f) {
            region.scale(mInvGlobalScale);
        }
    }

    /**
     * Expands the given rectangle by the region of window resize handle for freeform window.
     * @param inOutRect The rectangle to update.
     */
    private void adjustRegionInFreeformWindowMode(Rect inOutRect) {
        if (!inFreeformWindowingMode()) {
            return;
        }

        // For freeform windows, we need 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);
        inOutRect.inset(-delta, -delta);
    }

    /**
     * Updates the region for a window in an Activity that was a touch modal. This will limit
     * the outer touch to the activity root task region.
     * @param outRegion The region to update.
     */
    private void updateRegionForModalActivityWindow(Region outRegion) {
        if (Flags.scrollingFromLetterbox()) {
            // Touchable region expands to the letterbox area to react to scrolls from letterbox.
            mTmpRect.setEmpty();
        } else {
            // If the activity is letterboxed and scrolling from letterbox is disabled, limit the
            // touchable region to the activity. This way, the letterbox area is exposed to react
            // to touch events, and the touch events can slip from the activity from letterbox.
            mActivityRecord.getLetterboxInnerBounds(mTmpRect);
        }

        if (mTmpRect.isEmpty()) {
            // TODO(b/371182877) If the app does not draw under cutout region, the touchable region
            // should not include cutout regions (if scrolling from letterbox feature is not desired
            // for this region).
            final Rect transformedBounds = mActivityRecord.getFixedRotationTransformDisplayBounds();
            if (transformedBounds != null) {
                // Task is in the same orientation as display, so the rotated bounds should be
                // chosen as the touchable region. Then when the surface layer transforms the
                // region to display space, the orientation will be consistent.
                mTmpRect.set(transformedBounds);
            } else {
                // 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 root task) will give us the good
                // bounds, as they would be used to display the dim layer.
                final TaskFragment taskFragment = mActivityRecord.getTaskFragment();
                if (taskFragment != null) {
                    taskFragment.getDimBounds(mTmpRect);
                }
            }
        }
        adjustRegionInFreeformWindowMode(mTmpRect);
        outRegion.set(mTmpRect);
        cropRegionToRootTaskBoundsIfNeeded(outRegion);
    }

    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(getPendingTransaction(), "checkPolicyVisibilityChange");
                if (isFocused()) {
                    ProtoLog.i(WM_DEBUG_FOCUS_LIGHT,
                            "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.
        final boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0
                || (mActivityRecord != null && mActivityRecord.canTurnScreenOn());

        // The screen will turn on if the following conditions are met
        // 1. The window has the flag FLAG_TURN_SCREEN_ON or ActivityRecord#canTurnScreenOn.
        // 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
        // currentLaunchCanTurnScreenOn 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 = mActivityRecord == null || mActivityRecord.currentLaunchCanTurnScreenOn();

            if (allowTheaterMode && canTurnScreenOn
                    && (mWmService.mAtmService.isDreaming()
                            || !mWmService.mPowerManager.isInteractive(getDisplayId()))) {
                if (DEBUG_VISIBILITY || DEBUG_POWER) {
                    Slog.v(TAG, "Relayout window turning screen on: " + this);
                }
                mWmService.mPowerManager.wakeUp(SystemClock.uptimeMillis(),
                        PowerManager.WAKE_REASON_APPLICATION, "android.server.wm:SCREEN_ON_FLAG",
                        getDisplayId());
            }

            if (mActivityRecord != null) {
                mActivityRecord.setCurrentLaunchCanTurnScreenOn(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 (isDrawn() && 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 Session session = parentWindow != null ? parentWindow.mSession : mSession;
        return session.mPid == MY_PID ? mWmService.mRoot.getConfiguration()
                : session.mProcess.getConfiguration();
    }

    private Configuration getLastReportedConfiguration() {
        return mLastReportedConfiguration.getMergedConfiguration();
    }

    void adjustStartingWindowFlags() {
        if (mAttrs.type == TYPE_BASE_APPLICATION && mActivityRecord != null
                && mActivityRecord.mStartingWindow != 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 = mActivityRecord.mStartingWindow.mAttrs;
            final boolean wasShowWhenLocked = (sa.flags & FLAG_SHOW_WHEN_LOCKED) != 0;
            final boolean removeShowWhenLocked = (mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) == 0;
            sa.flags = (sa.flags & ~mask) | (mAttrs.flags & mask);
            if (wasShowWhenLocked && removeShowWhenLocked) {
                // Trigger unoccluding animation if needed.
                mActivityRecord.checkKeyguardFlagsChanged();
                mActivityRecord.deferStartingWindowRemovalForKeyguardUnoccluding();
            }
        }
    }

    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;
        }
    }

    /** Returns {@code true} if this window desires key events. */
    boolean canReceiveKeys() {
        return canReceiveKeys(false /* fromUserTouch */);
    }

    public String canReceiveKeysReason(boolean fromUserTouch) {
        return "fromTouch= " + fromUserTouch
                + " isVisibleRequestedOrAdding=" + isVisibleRequestedOrAdding()
                + " mViewVisibility=" + mViewVisibility
                + " mRemoveOnExit=" + mRemoveOnExit
                + " flags=" + mAttrs.flags
                + " appWindowsAreFocusable="
                + (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch))
                + " canReceiveTouchInput=" + canReceiveTouchInput()
                + " displayIsOnTop=" + getDisplayContent().isOnTop()
                + " displayIsTrusted=" + getDisplayContent().isTrusted()
                + " transitShouldKeepFocus=" + (mActivityRecord != null
                        && mTransitionController.shouldKeepFocus(mActivityRecord));
    }

    public boolean canReceiveKeys(boolean fromUserTouch) {
        if (mActivityRecord != null && mTransitionController.shouldKeepFocus(mActivityRecord)) {
            // During transient launch, the transient-hide windows are not visibleRequested
            // or on-top but are kept focusable and thus can receive keys.
            return true;
        }
        final boolean canReceiveKeys = isVisibleRequestedOrAdding()
                && (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
                && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
                && (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch))
                // can it receive touches
                && (mActivityRecord == null || mActivityRecord.getTask() == null
                        || !mActivityRecord.getTask().getRootTask().shouldIgnoreInput());

        if (!canReceiveKeys) {
            return false;
        }
        // Do not allow untrusted virtual display to receive keys unless user intentionally
        // touches the display.
        return fromUserTouch || getDisplayContent().isOnTop()
                || getDisplayContent().isTrusted();
    }

    @Override
    public boolean canShowWhenLocked() {
        if (mActivityRecord != null) {
            // It will also check if its windows contain FLAG_SHOW_WHEN_LOCKED.
            return mActivityRecord.canShowWhenLocked();
        }
        return (mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0;
    }

    void applySizeOverride(Configuration newParentConfig, Configuration resolvedConfig) {
        applySizeOverrideIfNeeded(
                getDisplayContent(),
                mSession.mProcess.mInfo,
                newParentConfig,
                resolvedConfig,
                (mAttrs.privateFlags & PRIVATE_FLAG_OPT_OUT_EDGE_TO_EDGE) != 0,
                false /* hasFixedRotationTransform */,
                false /* hasCompatDisplayInsets */,
                null /* task */);
    }

    /**
     * @return {@code true} if this window can receive touches based on among other things,
     * windowing state and recents animation state.
     **/
    boolean canReceiveTouchInput() {
        if (mActivityRecord == null  || mActivityRecord.getTask() == null) {
            return true;
        }
        // During transient launch, the transient-hide windows are not visibleRequested
        // or on-top but are kept focusable and thus can receive touch input.
        if (mTransitionController.shouldKeepFocus(mActivityRecord)) {
            return true;
        }

        // Do not allow back predictive animation target to receive touch, app can trigger an
        // unexpected transition so basically unable to polish it.
        if (mWmService.mAtmService.mBackNavigationController.shouldPauseTouch(mActivityRecord)) {
            return false;
        }
        return !mActivityRecord.getTask().getRootTask().shouldIgnoreInput()
                && mActivityRecord.isVisibleRequested();
    }

    /**
     * Returns {@code true} if this window has been shown on screen at some time in the past.
     *
     * @deprecated Use {@link #isDrawn} or any of the other drawn/visibility methods.
     */
    @Deprecated
    boolean hasDrawn() {
        return mWinAnimator.mDrawState == WindowStateAnimator.HAS_DRAWN;
    }

    /**
     * Can be called to undo the effect of {@link #hide}, allowing a window to be shown as long
     * as the client would also like it to be shown.
     */
    boolean show(boolean doAnimation, boolean requestAnim) {
        if (isLegacyPolicyVisibility() && mLegacyPolicyVisibilityAfterAnim) {
            // Already showing.
            return false;
        }
        if (!showToCurrentUser()) {
            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 (mIsForceHiddenNonSystemOverlayWindow) {
            // 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(PARENTS, ANIMATION_TYPE_ALL));
            if (!mToken.okToAnimate()) {
                doAnimation = false;
            } else if (isLegacyPolicyVisibility() && !isAnimating(PARENTS, ANIMATION_TYPE_ALL)) {
                // 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 (mControllableInsetProvider != null) {
            mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
        }
        if (requestAnim) {
            mWmService.scheduleAnimationLocked();
        }
        if ((mAttrs.flags & FLAG_NOT_FOCUSABLE) == 0) {
            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
        }
        return true;
    }

    /** Forces the window to be hidden, regardless of whether the client like it shown. */
    boolean hide(boolean doAnimation, boolean requestAnim) {
        if (doAnimation) {
            if (!mToken.okToAnimate()) {
                doAnimation = false;
            }
        }
        boolean current =
                doAnimation ? mLegacyPolicyVisibilityAfterAnim : isLegacyPolicyVisibility();
        if (!current) {
            // Already hiding.
            return false;
        }
        if (doAnimation) {
            // If a hide animation is applied, then let onAnimationFinished
            // -> checkPolicyVisibilityChange hide the window. Otherwise make doAnimation false
            // to commit invisible immediately.
            if (!mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false /* isEntrance */)) {
                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) {
                ProtoLog.i(WM_DEBUG_FOCUS_LIGHT,
                        "WindowState.hideLw: setting mFocusMayChange true");
                mWmService.mFocusMayChange = true;
            }
        }
        if (mControllableInsetProvider != null) {
            mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
        }
        if (requestAnim) {
            mWmService.scheduleAnimationLocked();
        }
        if (isFocused) {
            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
        }
        return true;
    }

    boolean isForceHiddenNonSystemOverlayWindow() {
        return mIsForceHiddenNonSystemOverlayWindow;
    }

    void setForceHideNonSystemOverlayWindowIfNeeded(boolean forceHide) {
        final int baseType = getBaseType();
        if (mSession.mCanAddInternalSystemWindow
                || (!isSystemAlertWindowType(baseType) && baseType != TYPE_TOAST)) {
            return;
        }

        if (baseType == TYPE_APPLICATION_OVERLAY && mAttrs.isSystemApplicationOverlay()
                && mSession.canCreateSystemApplicationOverlay()) {
            return;
        }

        if (mIsForceHiddenNonSystemOverlayWindow == forceHide) {
            return;
        }
        mIsForceHiddenNonSystemOverlayWindow = forceHide;
        if (forceHide) {
            hide(true /* doAnimation */, true /* requestAnim */);
        } else {
            show(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) {
            hide(true /* doAnimation */, true /* requestAnim */);
        } else {
            show(true /* doAnimation */, true /* requestAnim */);
        }
    }

    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.
                show(true /* doAnimation */, true /* requestAnim */);
            } else {
                hide(true /* doAnimation */, true /* requestAnim */);
            }
        }
    }

    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 /* startIfModeDefault */, null /* featureId */,
                "init-default-visibility");
        if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
            setAppOpVisibilityLw(false);
        }
    }

    void resetAppOpsState() {
        if (mAppOp != OP_NONE && mAppOpVisibility) {
            mWmService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage(),
                    null /* featureId */);
        }
    }

    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, null /* featureId */);
                setAppOpVisibilityLw(false);
            }
        } else {
            final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, uid, packageName,
                    true /* startIfModeDefault */, null /* featureId */, "attempt-to-be-visible");
            if (mode == MODE_ALLOWED || mode == MODE_DEFAULT) {
                setAppOpVisibilityLw(true);
            }
        }
    }

    public void hidePermanentlyLw() {
        if (!mPermanentlyHidden) {
            mPermanentlyHidden = true;
            hide(true /* doAnimation */, true /* requestAnim */);
        }
    }

    public void pokeDrawLockLw(long timeout) {
        if (isVisibleRequestedOrAdding()) {
            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);
        }
    }

    /** Checks whether the process hosting this window is currently alive. */
    boolean isAlive() {
        return !mSession.isClientDead();
    }

    void sendAppVisibilityToClients() {
        super.sendAppVisibilityToClients();

        final boolean clientVisible = mToken.isClientVisible();
        // TODO(shell-transitions): This is currently only applicable to app windows, BUT we
        //                          want to extend the "starting" concept to other windows.
        if (mAttrs.type == TYPE_APPLICATION_STARTING && !clientVisible) {
            // Don't hide the starting window.
            return;
        }

        try {
            if (DEBUG_VISIBILITY) Slog.v(TAG,
                    "Setting visibility of " + this + ": " + clientVisible);
            final int seqId = clientVisible ? incrementSeqForRelayout() : -1;
            if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                Trace.instant(TRACE_TAG_WINDOW_MANAGER, "wm.sendAppVis_" + getWindowTag()
                        + " id=" + seqId + " vis=" + clientVisible + " surf=" + mHasSurface);
            }
            mClient.dispatchAppVisibility(clientVisible, seqId);
        } catch (RemoteException e) {
            // The remote client fails to process the visibility message. That means it is in a
            // wrong state. E.g. the binder buffer is running out or the binder threads are dead.
            // The window visibility is out-of-sync that may cause blank content or left over, so
            // just kill it. And if it is a window of foreground activity, the activity can be
            // restarted automatically if needed.
            Slog.w(TAG, "Exception thrown during dispatchAppVisibility " + this, e);
            if (android.os.Process.getUidForPid(mSession.mPid) == mSession.mUid
                    && android.os.Process.getThreadGroupLeader(mSession.mPid) == mSession.mPid) {
                android.os.Process.killProcess(mSession.mPid);
            }
        }

        // Because the client is notified to be invisible, it should no longer be considered as
        // drawn state. This prevent the app from showing incomplete content if the app is
        // requested to be visible in a short time (e.g. before activity stopped).
        if (!clientVisible && mActivityRecord != null && mWinAnimator.mDrawState == HAS_DRAWN) {
            mWinAnimator.resetDrawState();
            // Make sure the app can report drawn if it becomes visible again.
            forceReportingResized();
        }
    }

    boolean destroySurface(boolean cleanupOnResume, boolean appStopped) {
        boolean destroyedSomething = false;

        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) {
                final WindowState c = childWindows.get(i);
                destroyedSomething |= c.destroySurface(cleanupOnResume, appStopped);
            }
        }

        if (!(appStopped || mWindowRemovalAllowed || cleanupOnResume)) {
            return destroyedSomething;
        }

        if (mDestroying) {
            ProtoLog.e(WM_DEBUG_ADD_REMOVE, "win=%s"
                    + " destroySurfaces: appStopped=%b"
                    + " win.mWindowRemovalAllowed=%b"
                    + " win.mRemoveOnExit=%b", this, appStopped,
                    mWindowRemovalAllowed, mRemoveOnExit);
            if (!cleanupOnResume || mRemoveOnExit) {
                destroySurfaceUnchecked();
            }
            if (mRemoveOnExit) {
                removeImmediately();
            }
            if (cleanupOnResume) {
                requestUpdateWallpaperIfNeeded();
            }
            if (!mHasSurface) {
                mDestroying = false;
                destroyedSomething = true;
            }
        }

        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(mTmpTransaction);
        mTmpTransaction.apply();

        // 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;
        ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=destroySurface win=%s", this);

        if (syncNextBuffer()) {
            immediatelyNotifyBlastSync();
        }
    }

    void onSurfaceShownChanged(boolean shown) {
        if (mLastShownChangedReported == shown) {
            return;
        }
        mLastShownChangedReported = shown;

        if (shown) {
            initExclusionRestrictions();
        } else {
            logExclusionRestrictions(EXCLUSION_LEFT);
            logExclusionRestrictions(EXCLUSION_RIGHT);
            getDisplayContent().removeImeScreenshotByTarget(this);
        }
        // Exclude toast because legacy apps may show toast window by themselves, so the misused
        // apps won't always be considered as foreground state.
        // Exclude private presentations as they can only be shown on private virtual displays and
        // shouldn't be the cause of an app be considered foreground.
        // Exclude presentations on virtual displays as they are not actually visible.
        if (mAttrs.type >= FIRST_SYSTEM_WINDOW
                && mAttrs.type != TYPE_TOAST
                && mAttrs.type != TYPE_PRIVATE_PRESENTATION
                && !(mAttrs.type == TYPE_PRESENTATION && isOnVirtualDisplay())
        ) {
            mWmService.mAtmService.mActiveUids.onNonAppSurfaceVisibilityChanged(mOwnerUid,
                    mAttrs.type, shown);
        }
    }

    boolean isPublicPresentation() {
        return mAttrs.type == TYPE_PRESENTATION;
    }

    boolean isPresentation() {
        return isPublicPresentation() || mAttrs.type == TYPE_PRIVATE_PRESENTATION;
    }

    private boolean isOnVirtualDisplay() {
        return getDisplayContent().mDisplay.getType() == Display.TYPE_VIRTUAL;
    }

    private void logExclusionRestrictions(int side) {
        if (!logsGestureExclusionRestrictions(this)
                || SystemClock.uptimeMillis() < mLastExclusionLogUptimeMillis[side]
                + mWmService.mConstants.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];

        FrameworkStatsLog.write(FrameworkStatsLog.EXCLUSION_RECT_STATE_CHANGED,
                mAttrs.packageName, requested, requested - granted /* rejected */,
                side + 1 /* Sides are 1-indexed in atoms.proto */,
                (getConfiguration().orientation == ORIENTATION_LANDSCAPE),
                false /* (deprecated param) inSplitscreen */, (int) duration);
    }

    private void initExclusionRestrictions() {
        final long now = SystemClock.uptimeMillis();
        mLastExclusionLogUptimeMillis[EXCLUSION_LEFT] = now;
        mLastExclusionLogUptimeMillis[EXCLUSION_RIGHT] = now;
    }

    /** @return {@code true} if this window can be shown to all users. */
    boolean showForAllUsers() {

        // If this switch statement is modified, modify the comment in the declarations of
        // the type in {@link WindowManager.LayoutParams} as well.
        switch (mAttrs.type) {
            default:
                // These are the windows that by default are shown only to the user that created
                // them. If this needs to be overridden, set
                // {@link WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS} in
                // {@link WindowManager.LayoutParams}. Note that permission
                // {@link android.Manifest.permission.INTERNAL_SYSTEM_WINDOW} is required as well.
                if ((mAttrs.privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS) == 0) {
                    return false;
                }
                break;

            // These are the windows that by default are shown to all users. However, to
            // protect against spoofing, check permissions below.
            case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
            case TYPE_APPLICATION_STARTING:
            case TYPE_BOOT_PROGRESS:
            case TYPE_DISPLAY_OVERLAY:
            case TYPE_INPUT_CONSUMER:
            case TYPE_KEYGUARD_DIALOG:
            case TYPE_MAGNIFICATION_OVERLAY:
            case TYPE_NAVIGATION_BAR:
            case TYPE_NAVIGATION_BAR_PANEL:
            case TYPE_PHONE:
            case TYPE_POINTER:
            case TYPE_PRIORITY_PHONE:
            case TYPE_SEARCH_BAR:
            case TYPE_STATUS_BAR:
            case TYPE_NOTIFICATION_SHADE:
            case TYPE_STATUS_BAR_ADDITIONAL:
            case TYPE_STATUS_BAR_SUB_PANEL:
            case TYPE_SYSTEM_DIALOG:
            case TYPE_VOLUME_OVERLAY:
            case TYPE_PRESENTATION:
            case TYPE_PRIVATE_PRESENTATION:
            case TYPE_DOCK_DIVIDER:
                break;
        }

        // Only the system can show free windows to all users.
        return mOwnerCanAddInternalSystemWindow;

    }

    @Override
    boolean showToCurrentUser() {
        // Child windows are evaluated based on their parent window.
        final WindowState win = getTopParentWindow();
        if (win.mAttrs.type < FIRST_SYSTEM_WINDOW
                && win.mActivityRecord != null && win.mActivityRecord.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.getFrame().left <= win.getDisplayFrame().left
                    && win.getFrame().top <= win.getDisplayFrame().top
                    && win.getFrame().right >= win.getDisplayFrame().right
                    && win.getFrame().bottom >= win.getDisplayFrame().bottom) {
                // Is a fullscreen window, like the clock alarm. Show to everyone.
                return true;
            }
        }

        return win.showForAllUsers()
                || mWmService.isUserVisible(win.mShowUserId);
    }

    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);
                if (frame.left != 0 || frame.top != 0) {
                    outRegion.translate(frame.left, frame.top);
                }
                break;
            }
        }
        cropRegionToRootTaskBoundsIfNeeded(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 DisplayContent dc = getDisplayContent();

        if (mAttrs.isModal() && dc != null) {
            outRegion.set(dc.getBounds());
            cropRegionToRootTaskBoundsIfNeeded(outRegion);
            subtractTouchExcludeRegionIfNeeded(outRegion);
        } else {
            getTouchableRegion(outRegion);
        }
    }

    private void cropRegionToRootTaskBoundsIfNeeded(Region region) {
        final Task task = getTask();
        if (task == null || !task.cropWindowsToRootTaskBounds()) {
            return;
        }

        final Task rootTask = task.getRootTask();
        if (rootTask == null || rootTask.mCreatedByOrganizer) {
            return;
        }

        rootTask.getBounds(mTmpRect);
        adjustRegionInFreeformWindowMode(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 (mTapExcludeRegion.isEmpty()) {
            return;
        }
        final Region touchExcludeRegion = Region.obtain();
        getTapExcludeRegion(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) {
        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 the process has not registered to any display area to listen to the configuration
        // change, we can simply return the mFullConfiguration as default.
        if (!registeredForDisplayAreaConfigChanges()) {
            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 area as a config listener. */
    private boolean registeredForDisplayAreaConfigChanges() {
        final WindowState parentWindow = getParentWindow();
        final Session session = parentWindow != null ? parentWindow.mSession : mSession;
        if (session.mPid == MY_PID) {
            // System process cannot register to display area config change.
            return false;
        }
        return session.mProcess.registeredForDisplayAreaConfigChanges();
    }

    @NonNull
    WindowProcessController getProcess() {
        return mSession.mProcess;
    }

    /**
     * Fills the given window frames and merged configuration for the client.
     *
     * @param outFrames The frames that will be sent to the client.
     * @param outMergedConfiguration The configuration that will be sent to the client.
     * @param outActivityWindowInfo The ActivityWindowInfo that will be sent to the client if set.
     *                              {@code null} if this is not activity window.
     * @param useLatestConfig Whether to use the latest configuration.
     * @param relayoutVisible Whether to consider visibility to use the latest configuration.
     */
    void fillClientWindowFramesAndConfiguration(@NonNull ClientWindowFrames outFrames,
            @NonNull MergedConfiguration outMergedConfiguration,
            @Nullable ActivityWindowInfo outActivityWindowInfo,
            boolean useLatestConfig, boolean relayoutVisible) {
        outFrames.frame.set(mWindowFrames.mCompatFrame);
        outFrames.displayFrame.set(mWindowFrames.mDisplayFrame);
        if (mInvGlobalScale != 1f) {
            outFrames.displayFrame.scale(mInvGlobalScale);
        }
        if (mLayoutAttached) {
            if (outFrames.attachedFrame == null) {
                outFrames.attachedFrame = new Rect();
            }
            outFrames.attachedFrame.set(getParentWindow().getFrame());
            if (mInvGlobalScale != 1f) {
                outFrames.attachedFrame.scale(mInvGlobalScale);
            }
        }
        outFrames.compatScale = getCompatScaleForClient();
        outFrames.seq = getNextSeq(mLastReportedFrames.seq);
        if (mLastReportedFrames != outFrames) {
            mLastReportedFrames.setTo(outFrames);
        }

        // Note: in the cases where the window is tied to an activity, we should not send a
        // configuration update when the window has requested to be hidden. Doing so can lead to
        // the client erroneously accepting a configuration that would have otherwise caused an
        // activity restart. We instead hand back the last reported {@link MergedConfiguration}.
        if (useLatestConfig || (relayoutVisible && (mActivityRecord == null
                || mActivityRecord.isVisibleRequested()))) {
            final Configuration globalConfig = getProcessGlobalConfiguration();
            final Configuration overrideConfig = getMergedOverrideConfiguration();
            outMergedConfiguration.setConfiguration(globalConfig, overrideConfig);
            if (outMergedConfiguration != mLastReportedConfiguration) {
                mLastReportedConfiguration.setTo(outMergedConfiguration);
            }
            if (outActivityWindowInfo != null && mLastReportedActivityWindowInfo != null) {
                outActivityWindowInfo.set(mActivityRecord.getActivityWindowInfo());
                mLastReportedActivityWindowInfo.set(outActivityWindowInfo);
            }
        } else {
            outMergedConfiguration.setTo(mLastReportedConfiguration);
            if (outActivityWindowInfo != null && mLastReportedActivityWindowInfo != null) {
                outActivityWindowInfo.set(mLastReportedActivityWindowInfo);
            }
        }
        if (!mWmService.mAlwaysSeqId) {
            mLastConfigReportedToClient = true;
        }
    }

    void setLastConfigReportedToClientForTest(boolean reported) {
        mLastConfigReportedToClient = reported;
    }

    void fillInsetsState(@NonNull InsetsState outInsetsState, boolean copySources) {
        final int lastSeq = mLastReportedInsetsState.getSeq();
        outInsetsState.set(getCompatInsetsState(), copySources);
        outInsetsState.setSeq(getNextSeq(lastSeq));
        if (outInsetsState != mLastReportedInsetsState) {
            // No need to copy for the recorded.
            mLastReportedInsetsState.set(outInsetsState, false /* copySources */);
        }
    }

    void fillInsetsSourceControls(@NonNull InsetsSourceControl.Array outArray,
            boolean copyControls) {
        final int lastSeq = mLastReportedActiveControls.getSeq();
        final InsetsSourceControl[] controls =
                getDisplayContent().getInsetsStateController().getControlsForDispatch(this);
        outArray.set(controls, copyControls);
        outArray.setSeq(getNextSeq(lastSeq));
        if (outArray != mLastReportedActiveControls) {
            // No need to copy for the recorded.
            mLastReportedActiveControls.setTo(outArray, false /* copyControls */);
        }
    }

    void reportResized() {
        // If the activity is scheduled to relaunch, skip sending the resized to ViewRootImpl now
        // since it will be destroyed anyway. This also prevents the client from receiving
        // windowing mode change before it is destroyed.
        if (inRelaunchingActivity() && mAttrs.type != TYPE_APPLICATION_STARTING) {
            return;
        }
        // If this is an activity or wallpaper and is invisible or going invisible, don't report
        // either since it is going away. This is likely during a transition so we want to preserve
        // the original state.
        if (shouldCheckTokenVisibleRequested() && !mToken.isVisibleRequested()) {
            return;
        }

        if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
        }

        ProtoLog.v(WM_DEBUG_RESIZE, "Reporting new frame to %s: %s", this,
                mWindowFrames.mCompatFrame);
        final boolean drawPending = mWinAnimator.mDrawState == DRAW_PENDING;
        if (drawPending) {
            ProtoLog.i(WM_DEBUG_ORIENTATION, "Resizing %s WITH DRAW PENDING", this);
        }

        final int prevRotation = mLastReportedConfiguration
                .getMergedConfiguration().windowConfiguration.getRotation();
        fillClientWindowFramesAndConfiguration(mLastReportedFrames, mLastReportedConfiguration,
                mLastReportedActivityWindowInfo, true /* useLatestConfig */,
                false /* relayoutVisible */);
        fillInsetsState(mLastReportedInsetsState, false /* copySources */);
        final boolean syncWithBuffers;
        final boolean reportDraw;
        final int seqIdToSend;
        if (!mWmService.mAlwaysSeqId) {
            final boolean syncRedraw = shouldSendRedrawForSync();
            syncWithBuffers = syncRedraw && shouldSyncWithBuffers();
            reportDraw = syncRedraw || drawPending;
            seqIdToSend = syncWithBuffers ? mSyncSeqId : -1;
        } else {
            reportDraw = false;
            // If a sync is open (not-ready), or was open when this resize was queued, then we need
            // now wait for this new resize to report back.
            if (mSyncState == SYNC_STATE_READY && (mPendingSyncResize || !getSyncGroup().mReady)) {
                mSyncState = SYNC_STATE_WAITING_FOR_DRAW;
            }
            syncWithBuffers = mBufferSeqId > mSyncSeqId;
            if (syncWithBuffers) {
                if (mSyncState != SYNC_STATE_NONE) {
                    // We are about to request a new buffer, so drop the existing buffer (if it
                    // exists) since we won't use it and it occupies one of the available buffers
                    // in the buffer-queue which, if exhausted, will cause a deadlock.
                    ProtoLog.d(WM_DEBUG_SYNC_ENGINE, "Requesting a new buffer for a window that "
                            + "may already be holding one, so try dropping it. win=%s", this);
                    dropBufferFrom(mSyncTransaction);
                }
                mSyncSeqId = mBufferSeqId;
            } else if (!mLastConfigReportedToClient
                    || mWindowFrames.isForceReportingResized()
                    || mSyncState == SYNC_STATE_WAITING_FOR_DRAW) {
                ++mSyncSeqId;
            }
            mPendingSyncResize = false;
            seqIdToSend = mSyncSeqId;
            if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                Trace.instant(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag()
                        + " id=" + mSyncSeqId + " buf=" + syncWithBuffers);
            }
        }
        final boolean isDragResizeChanged = isDragResizeChanged();
        final boolean forceRelayout = syncWithBuffers || isDragResizeChanged;
        final DisplayContent displayContent = getDisplayContent();
        final int displayId = displayContent.getDisplayId();

        if (isDragResizeChanged) {
            setDragResizing();
        }
        final boolean isDragResizing = isDragResizing();

        mDragResizingChangeReported = true;
        mWindowFrames.clearReportResizeHints();
        if (!mWmService.mAlwaysSeqId) {
            markRedrawForSyncReported();
        }
        getProcess().scheduleClientTransactionItem(
                new WindowStateResizeItem(mClient, mLastReportedFrames, reportDraw,
                        mLastReportedConfiguration, mLastReportedInsetsState, forceRelayout,
                        displayId, seqIdToSend, syncWithBuffers, isDragResizing,
                        mLastReportedActivityWindowInfo));
        onResizePostDispatched(drawPending, prevRotation, displayId);
        if (mWmService.mAlwaysSeqId) {
            mLastConfigReportedToClient = true;
        }
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }

    private void onResizePostDispatched(boolean drawPending, int prevRotation, int displayId) {
        if (drawPending && prevRotation >= 0 && prevRotation != mLastReportedConfiguration
                .getMergedConfiguration().windowConfiguration.getRotation()) {
            mOrientationChangeRedrawRequestTime = SystemClock.elapsedRealtime();
            ProtoLog.v(WM_DEBUG_ORIENTATION,
                    "Requested redraw for orientation change: %s", this);
        }

        if (mWmService.mAccessibilityController.hasCallbacks()) {
            mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(displayId);
        }
    }

    boolean inRelaunchingActivity() {
        return mActivityRecord != null && mActivityRecord.isRelaunching();
    }

    boolean isClientLocal() {
        return mClient instanceof IWindow.Stub;
    }

    private void consumeInsetsChange() {
        if (mWindowFrames.hasInsetsChanged()) {
            mWindowFrames.setInsetsChanged(false);
            mWmService.mWindowsInsetsChanged--;
            if (mWmService.mWindowsInsetsChanged == 0) {
                mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED);
            }
        }
    }

    /**
     * Called when the insets state changed.
     */
    void notifyInsetsChanged() {
        ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "notifyInsetsChanged for %s ", this);
        if (!mWindowFrames.hasInsetsChanged()) {
            mWindowFrames.setInsetsChanged(true);
            mWmService.mWindowsInsetsChanged++;

            // If the new InsetsState won't be dispatched before releasing WM lock, the following
            // message will be executed.
            mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED);
            mWmService.mH.sendEmptyMessage(WindowManagerService.H.INSETS_CHANGED);
        }

        final WindowContainer p = getParent();
        if (p != null) {
            p.updateOverlayInsetsState(this);
        }
    }

    @Override
    public void notifyInsetsControlChanged(int displayId) {
        ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "notifyInsetsControlChanged for %s ", this);
        if (mRemoved) {
            return;
        }
        fillInsetsState(mLastReportedInsetsState, false /* copySources */);
        fillInsetsSourceControls(mLastReportedActiveControls, false /* copyControls */);
        getProcess().scheduleClientTransactionItem(new WindowStateInsetsControlChangeItem(
                mClient, mLastReportedInsetsState, mLastReportedActiveControls));
    }

    @Override
    public WindowState getWindow() {
        return this;
    }

    @Override
    public void showInsets(@InsetsType int types, @Nullable ImeTracker.Token statsToken) {
        try {
            ImeTracker.forLogging().onProgress(statsToken,
                    ImeTracker.PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_SHOW_INSETS);
            mClient.showInsets(types, statsToken);
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed to deliver showInsets", e);
            ImeTracker.forLogging().onFailed(statsToken,
                    ImeTracker.PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_SHOW_INSETS);
        }
    }

    @Override
    public void hideInsets(@InsetsType int types, @Nullable ImeTracker.Token statsToken) {
        try {
            ImeTracker.forLogging().onProgress(statsToken,
                    ImeTracker.PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_HIDE_INSETS);
            mClient.hideInsets(types, statsToken);
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed to deliver hideInsets", e);
            ImeTracker.forLogging().onFailed(statsToken,
                    ImeTracker.PHASE_WM_WINDOW_INSETS_CONTROL_TARGET_HIDE_INSETS);
        }
    }

    @Override
    public boolean canShowTransient() {
        return (mAttrs.insetsFlags.behavior & BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE) != 0;
    }

    boolean canBeHiddenByKeyguard() {
        // Keyguard visibility of window from activities are determined over activity visibility.
        if (mActivityRecord != null) {
            return false;
        }
        switch (mAttrs.type) {
            case TYPE_NOTIFICATION_SHADE:
            case TYPE_STATUS_BAR:
            case TYPE_STATUS_BAR_ADDITIONAL:
            case TYPE_NAVIGATION_BAR:
            case TYPE_WALLPAPER:
                return false;
            default:
                // Hide only windows below the keyguard host window.
                return mPolicy.getWindowLayerLw(this)
                        < mPolicy.getWindowLayerFromTypeLw(TYPE_NOTIFICATION_SHADE);
        }
    }

    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;
    }

    /**
     * Returns {@code true} if activity bounds are letterboxed or letterboxed for display cutout or
     * letterboxed for a safe region.
     *
     * <p>Note that letterbox UI may not be shown even when this returns {@code true}. See {@link
     * AppCompatLetterboxPolicy#shouldShowLetterboxUi} for more context.
     */
    boolean areAppWindowBoundsLetterboxed() {
        return mActivityRecord != null && !isStartingWindowAssociatedToTask()
                && (mActivityRecord.areBoundsLetterboxed() || isLetterboxedForDisplayCutout()
                || mActivityRecord.mAppCompatController
                .getSafeRegionPolicy().isLetterboxedForSafeRegionOnlyAllowed());
    }

    /** Returns {@code true} if the window is letterboxed for the display cutout. */
    boolean isLetterboxedForDisplayCutout() {
        if (mActivityRecord == null) {
            // Only windows with an ActivityRecord 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(mActivityRecord.getBounds());
        mTmpRect.intersectUnchecked(mWindowFrames.mFrame);
        return mActivityRecord.getBounds().equals(mTmpRect);
    }

    /**
     * @return {@code true} if bar shown within a given frame is allowed to be fully transparent
     *     when the current window is displayed.
     */
    boolean isFullyTransparentBarAllowed(Rect frame) {
        return mActivityRecord == null || mActivityRecord.isFullyTransparentBarAllowed(frame);
    }

    boolean isDragResizeChanged() {
        return mDragResizing != computeDragResizing();
    }

    /**
     * Resets the state whether we reported a drag resize change to the app.
     */
    @Override
    void resetDragResizingChangeReported() {
        mDragResizingChangeReported = false;
        super.resetDragResizingChangeReported();
    }

    private boolean computeDragResizing() {
        final Task task = getTask();
        if (task == null) {
            return false;
        }
        if (!inFreeformWindowingMode() && !task.getRootTask().mCreatedByOrganizer) {
            return false;
        }
        // TODO(157912944): formalize drag-resizing so that exceptions aren't hardcoded like this
        if (task.getActivityType() == ACTIVITY_TYPE_HOME) {
            // The current sys-ui implementations never live-resize home, so to prevent WSA from
            // creating/destroying surfaces (which messes up sync-transactions), skip HOME tasks.
            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;
        }

        return false;
    }

    void setDragResizing() {
        final boolean resizing = computeDragResizing();
        if (resizing == mDragResizing) {
            return;
        }
        mDragResizing = resizing;
    }

    boolean isDragResizing() {
        return mDragResizing;
    }

    @CallSuper
    @Override
    public void dumpDebug(ProtoOutputStream proto, long fieldId,
            @WindowTracingLogLevel int logLevel) {
        boolean isVisible = isVisible();
        if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible) {
            return;
        }

        final long token = proto.start(fieldId);
        super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
        proto.write(DISPLAY_ID, getDisplayId());
        int rootTaskId = INVALID_TASK_ID;
        final Task task = getTask();
        if (task != null) {
            rootTaskId = task.getRootTaskId();
        }
        proto.write(STACK_ID, rootTaskId);
        mAttrs.dumpDebug(proto, ATTRIBUTES);
        mGivenContentInsets.dumpDebug(proto, GIVEN_CONTENT_INSETS);
        mWindowFrames.dumpDebug(proto, WINDOW_FRAMES);
        mAttrs.surfaceInsets.dumpDebug(proto, SURFACE_INSETS);
        dumpPointProto(mSurfacePosition, proto, SURFACE_POSITION);
        mWinAnimator.dumpDebug(proto, ANIMATOR);
        proto.write(ANIMATING_EXIT, mAnimatingExit);
        proto.write(REQUESTED_WIDTH, mRequestedWidth);
        proto.write(REQUESTED_HEIGHT, mRequestedHeight);
        proto.write(VIEW_VISIBILITY, mViewVisibility);
        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(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
        proto.write(HAS_COMPAT_SCALE, hasCompatScale());
        proto.write(GLOBAL_SCALE, mGlobalScale);
        proto.write(REQUESTED_VISIBLE_TYPES, mRequestedVisibleTypes);
        for (Rect r : mKeepClearAreas) {
            r.dumpDebug(proto, KEEP_CLEAR_AREAS);
        }
        for (Rect r : mUnrestrictedKeepClearAreas) {
            r.dumpDebug(proto, UNRESTRICTED_KEEP_CLEAR_AREAS);
        }
        if (mMergedLocalInsetsSources != null) {
            for (int i = 0; i < mMergedLocalInsetsSources.size(); ++i) {
                mMergedLocalInsetsSources.valueAt(i).dumpDebug(proto, MERGED_LOCAL_INSETS_SOURCES);
            }
        }
        final var dimController = getDimController();
        if (dimController != null) {
            final Rect dimBounds = dimController.getDimBounds();
            if (dimBounds != null) {
                dimBounds.dumpDebug(proto, DIM_BOUNDS);
            }
        }
        proto.write(SYNC_SEQ_ID, mSyncSeqId);
        if (mWmService.mAlwaysSeqId) {
            proto.write(BUFFER_SEQ_ID, mBufferSeqId);
        } else {
            proto.write(BUFFER_SEQ_ID, mPrepareSyncSeqId);
        }
        proto.end(token);
    }

    @Override
    long getProtoFieldId() {
        return WINDOW;
    }

    @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, mShowUserId);
        final CharSequence title = getWindowTag();
        if (title != null) {
            proto.write(TITLE, title.toString());
        }
        proto.end(token);
    }

    @NeverCompile // Avoid size overhead of debugging code.
    @Override
    void dump(PrintWriter pw, String prefix, boolean dumpAll) {
        pw.print(prefix + "mDisplayId=" + getDisplayId());
        final Task task = getTask();
        if (task != null) {
            pw.print(" taskId=" + task.mTaskId);
        }
        pw.println(" mSession=" + mSession
                + " mClient=" + mClient.asBinder());
        pw.println(prefix + "mOwnerUid=" + mOwnerUid
                + " showForAllUsers=" + showForAllUsers()
                + " 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);
        }
        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 (mActivityRecord != null) {
                pw.println(prefix + "mActivityRecord=" + mActivityRecord);
                pw.print(prefix + "drawnStateEvaluated=" + getDrawnStateEvaluated());
                pw.println(prefix + "mightAffectAllDrawn=" + mightAffectAllDrawn());
            }
            pw.println(prefix + "mViewVisibility=0x" + Integer.toHexString(mViewVisibility)
                    + " mHaveFrame=" + mHaveFrame
                    + " mObscured=" + mObscured);
            if (mDisableFlags != 0) {
                pw.println(prefix + "mDisableFlags=" + ViewDebug.flagsToString(
                        View.class, "mSystemUiVisibility", mDisableFlags));
            }
        }
        if (!isVisibleByPolicy() || !mLegacyPolicyVisibilityAfterAnim || !mAppOpVisibility
                || isParentWindowHidden() || mPermanentlyHidden
                || mIsForceHiddenNonSystemOverlayWindow
                || mHiddenWhileSuspended) {
            pw.println(prefix + "mPolicyVisibility=" + isVisibleByPolicy()
                    + " mLegacyPolicyVisibilityAfterAnim=" + mLegacyPolicyVisibilityAfterAnim
                    + " mAppOpVisibility=" + mAppOpVisibility
                    + " parentHidden=" + isParentWindowHidden()
                    + " mPermanentlyHidden=" + mPermanentlyHidden
                    + " mHiddenWhileSuspended=" + mHiddenWhileSuspended
                    + " mIsForceHiddenNonSystemOverlayWindow="
                    + mIsForceHiddenNonSystemOverlayWindow);
        }
        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());
            if (mLastReportedActivityWindowInfo != null) {
                pw.println(prefix + "mLastReportedActivityWindowInfo="
                        + mLastReportedActivityWindowInfo);
            }
        }
        pw.println(prefix + "mHasSurface=" + mHasSurface
                + " isReadyForDisplay()=" + isReadyForDisplay()
                + " mWindowRemovalAllowed=" + mWindowRemovalAllowed);
        if (mIsSurfacePositionPaused) {
            pw.println(prefix + "mIsSurfacePositionPaused=true");
        }
        if (mInvGlobalScale != 1f) {
            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 (mXOffset != 0 || mYOffset != 0) {
            pw.println(prefix + "mXOffset=" + mXOffset + " mYOffset=" + mYOffset);
        }
        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 (mWallpaperZoomOut != -1) {
            pw.println(prefix + "mWallpaperZoomOut=" + mWallpaperZoomOut);
        }
        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());
        }
        if (mImeInsetsConsumed) {
            pw.println(prefix + "mImeInsetsConsumed=true");
        }
        pw.println(prefix + "isOnScreen=" + isOnScreen());
        pw.println(prefix + "isVisible=" + isVisible());
        pw.println(prefix + "keepClearAreas: restricted=" + mKeepClearAreas
                          + ", unrestricted=" + mUnrestrictedKeepClearAreas);
        if (dumpAll) {
            if (mRequestedVisibleTypes != WindowInsets.Type.defaultVisible()) {
                pw.println(prefix + "Requested non-default-visibility types: "
                        + WindowInsets.Type.toString(
                                mRequestedVisibleTypes ^ WindowInsets.Type.defaultVisible()));
            }
        }

        pw.println(prefix + "mPrepareSyncSeqId=" + mPrepareSyncSeqId);
        pw.println(prefix + "mBufferSeqId=" + mBufferSeqId);
    }

    @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" + mShowUserId
                    + " " + mLastTitle + (mAnimatingExit ? " EXITING}" : "}");
        }
        return mStringNameCache;
    }

    boolean isChildWindow() {
        return mIsChildWindow;
    }

    /**
     * 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.isGoneForLayout();
    }

    void requestUpdateWallpaperIfNeeded() {
        final DisplayContent dc = getDisplayContent();
        if (dc != null && ((mIsWallpaper && !mLastConfigReportedToClient) || hasWallpaper())) {
            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 (mGlobalScale != 1f) {
            winX *= mInvGlobalScale;
        }
        return winX;
    }

    float translateToWindowY(float y) {
        float winY = y - mWindowFrames.mFrame.top;
        if (mGlobalScale != 1f) {
            winY *= mInvGlobalScale;
        }
        return winY;
    }

    int getRotationAnimationHint() {
        if (mActivityRecord != null) {
            return mActivityRecord.mRotationAnimationHint;
        } else {
            return -1;
        }
    }

    /** Makes the surface of drawn window (COMMIT_DRAW_PENDING) to be visible. */
    void commitFinishDrawing(SurfaceControl.Transaction t) {
        if (mWinAnimator.commitFinishDrawingLocked()) {
            // Ensure that the visibility of buffer layer is set.
            mWinAnimator.prepareSurfaceLocked(t);
        }
        for (int i = mChildren.size() - 1; i >= 0; i--) {
            mChildren.get(i).commitFinishDrawing(t);
        }

        // When a new activity is showing, update dim in this transaction
        if (Flags.updateDimsWhenWindowShown()) {
            final Dimmer dimmer = getDimController();
            final WindowContainer<?> dimParent = getDimParent();
            if (dimmer != null && dimParent != null) {
                dimParent.adjustDims();
                dimmer.updateDims(t);
            }
        }

        // In case commitFinishDrawingLocked starts a window level animation, make sure the surface
        // operation (reparent to leash) is synced with the visibility by transition.
        if (getAnimationLeash() != null) {
            t.merge(getSyncTransaction());
        }
    }

    // This must be called while inside a transaction.
    boolean performShowLocked() {
        if (!showToCurrentUser()) {
            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) && mActivityRecord != null) {
            if (mAttrs.type != TYPE_APPLICATION_STARTING) {
                mActivityRecord.onFirstWindowDrawn(this);
            } else {
                mActivityRecord.onStartingWindowDrawn();
            }
        }

        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;
        ProtoLog.v(WM_DEBUG_ANIM, "performShowLocked: mDrawState=HAS_DRAWN in %s", 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.mSurfaceControl != 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();
                    }
                }
            }
        }

        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.visibleRequested="
                    + (mActivityRecord != null && mActivityRecord.isVisibleRequested())
                    + " tok.visible=" + (mActivityRecord != null && mActivityRecord.isVisible())
                    + " animating=" + isAnimating(PARENTS, ANIMATION_TYPE_ALL)
                    + " tok animating="
                    + (mActivityRecord != null && mActivityRecord.inTransition())
                    + " Callers=" + Debug.getCallers(4));
        }
    }

    WindowInfo getWindowInfo() {
        WindowInfo windowInfo = WindowInfo.obtain();
        windowInfo.displayId = getDisplayId();
        windowInfo.type = mAttrs.type;
        windowInfo.layer = mLayer;
        windowInfo.token = mClient.asBinder();
        if (mActivityRecord != null) {
            windowInfo.activityToken = mActivityRecord.token;
        }
        windowInfo.accessibilityIdOfAnchor = mAttrs.accessibilityIdOfAnchor;
        // TODO(b/378565144): Remove with Flags.useInputReportedFocusForAccessibility()
        if (!Flags.useInputReportedFocusForAccessibility()) {
            windowInfo.focused = isFocused();
        }
        Task task = getTask();
        windowInfo.inPictureInPicture = (task != null) && task.inPinnedWindowingMode();
        windowInfo.taskId = task == null ? ActivityTaskManager.INVALID_TASK_ID : task.mTaskId;
        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;
    }

    @Override
    void updateAboveInsetsState(InsetsState aboveInsetsState,
            SparseArray<InsetsSource> localInsetsSourcesFromParent,
            ArraySet<WindowState> insetsChangedWindows) {
        final SparseArray<InsetsSource> mergedLocalInsetsSources =
                createMergedSparseArray(localInsetsSourcesFromParent, mLocalInsetsSources);

        // ForAllWindows is the reliable way to visit the IME window and the windows within this
        // WindowState in the correct order. updateAboveInsetsState doesn't take the real order of
        // IME into account.
        forAllWindows(w -> {
            if (!w.mAboveInsetsState.equals(aboveInsetsState)) {
                w.mAboveInsetsState.set(aboveInsetsState);
                insetsChangedWindows.add(w);
            }

            if (!mergedLocalInsetsSources.contentEquals(w.mMergedLocalInsetsSources)) {
                // The traversal will reach IME if this window is an IME target window. However, we
                // should not copy the local insets to the IME window. The forAllWindow will reach
                // all IME containers by the logic in {@link #applyImeWindowsIfNeeded}.
                if (!w.mIsImWindow) {
                    w.mMergedLocalInsetsSources = mergedLocalInsetsSources;
                    insetsChangedWindows.add(w);
                }
            }

            final SparseArray<InsetsSourceProvider> providers = w.mInsetsSourceProviders;
            if (providers != null) {
                for (int i = providers.size() - 1; i >= 0; i--) {
                    aboveInsetsState.addSource(providers.valueAt(i).getSource());
                }
            }
        }, true /* traverseTopToBottom */);
    }

    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) {
        // No need to apply to IME window if the window is not the current IME layering target.
        if (!isImeLayeringTarget()) {
            return false;
        }
        return mDisplayContent.forAllImeWindows(callback, traverseTopToBottom);
    }

    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;
    }

    boolean isAnimationRunningSelfOrParent() {
        return inTransition()
                || isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
    }

    void onExitAnimationDone() {
        if (ProtoLog.isEnabled(WM_DEBUG_ANIM, LogLevel.VERBOSE)) {
            final AnimationAdapter animationAdapter = mSurfaceAnimator.getAnimation();
            StringWriter sw = new StringWriter();
            if (animationAdapter != null) {
                PrintWriter pw = new PrintWriter(sw);
                animationAdapter.dump(pw, "");
            }
            ProtoLog.v(WM_DEBUG_ANIM, "onExitAnimationDone in %s"
                            + ": exiting=%b remove=%b selfAnimating=%b anim=%s",
                    this, mAnimatingExit, mRemoveOnExit, isAnimating(), sw);
        }

        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 (mActivityRecord == null) {
                try {
                    mClient.dispatchWindowShown();
                } catch (RemoteException e) {
                }
            }
        }

        if (isAnimating()) {
            return;
        }

        if (!isSelfOrAncestorWindowAnimatingExit()) {
            return;
        }

        ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Exit animation finished in %s: remove=%b",
                this, 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 (mActivityRecord != null) {
            if (mAttrs.type == TYPE_BASE_APPLICATION) {
                mActivityRecord.destroySurfaces();
            } else {
                destroySurface(false /* cleanupOnResume */, mActivityRecord.mAppStopped);
            }
        } else {
            if (hasSurface) {
                mWmService.mDestroySurface.add(this);
            }
        }
        mAnimatingExit = false;
        ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=exitAnimationDone win=%s", this);
        getDisplayContent().mWallpaperController.hideWallpapers(this);
    }

    @Override
    boolean handleCompleteDeferredRemoval() {
        if (mRemoveOnExit && !isSelfAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION)) {
            mRemoveOnExit = false;
            removeImmediately();
        }
        return super.handleCompleteDeferredRemoval();
    }

    public boolean isRtl() {
        return getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
    }

    void updateReportedVisibility(UpdateReportedVisibilityResults results) {
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final WindowState c = mChildren.get(i);
            c.updateReportedVisibility(results);
        }

        if (mViewVisibility != View.VISIBLE
                || mAttrs.type == TYPE_APPLICATION_STARTING
                || mDestroying) {
            return;
        }
        if (DEBUG_VISIBILITY) {
            Slog.v(TAG, "Win " + this + ": isDrawn=" + isDrawn()
                    + ", animating=" + isAnimating(PARENTS, ANIMATION_TYPE_ALL));
            if (!isDrawn()) {
                Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceControl
                        + " pv=" + isVisibleByPolicy()
                        + " mDrawState=" + mWinAnimator.mDrawState
                        + " ph=" + isParentWindowHidden()
                        + " th=" + (mActivityRecord != null && mActivityRecord.isVisibleRequested())
                        + " a=" + isAnimating(PARENTS, ANIMATION_TYPE_ALL));
            }
        }

        results.numInteresting++;
        if (isDrawn()) {
            results.numDrawn++;
            if (!isAnimating(PARENTS, ANIMATION_TYPE_ALL)) {
                results.numVisible++;
            }
            results.nowGone = false;
        } else if (isAnimating(PARENTS, ANIMATION_TYPE_ALL)) {
            results.nowGone = false;
        }
    }

    boolean surfaceInsetsChanging() {
        return !mLastSurfaceInsets.equals(mAttrs.surfaceInsets);
    }

    int relayoutVisibleWindow(int result) {
        final boolean wasVisible = isVisible();

        result |= (!wasVisible || !isDrawn()) ? RELAYOUT_RES_FIRST_TIME : 0;

        if (mAnimatingExit) {
            Slog.d(TAG, "relayoutVisibleWindow: " + this + " mAnimatingExit=true, mRemoveOnExit="
                    + mRemoveOnExit + ", mDestroying=" + mDestroying);

            mAnimatingExit = false;
            // Cancel the existing exit animation for the next enter animation.
            if (isAnimating()) {
                cancelAnimation();
            }
            ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=relayoutVisibleWindow win=%s",
                    this);
        }
        if (mDestroying) {
            mDestroying = false;
            mWmService.mDestroySurface.remove(this);
        }
        if (!wasVisible) {
            mWinAnimator.mEnterAnimationPending = true;
        }

        mWinAnimator.mEnteringAnimation = true;

        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareToDisplay");
        try {
            prepareWindowToDisplayDuringRelayout(wasVisible);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }

        return result;
    }

    /**
     * @return True if this window has been laid out at least once; false otherwise.
     */
    boolean isLaidOut() {
        return mLayoutSeq != -1;
    }

    /** Updates the last frames and relative frames to the current ones. */
    void updateLastFrames() {
        mWindowFrames.mLastFrame.set(mWindowFrames.mFrame);
        mWindowFrames.mLastRelFrame.set(mWindowFrames.mRelFrame);
    }

    /**
     * Clears factors that would cause report-resize.
     */
    void onResizeHandled() {
        mWindowFrames.onResizeHandled();
    }

    @Override
    protected boolean isSelfAnimating(int flags, int typesToCheck) {
        if (mControllableInsetProvider != null) {
            return false;
        }
        return super.isSelfAnimating(flags, typesToCheck);
    }

    void startAnimation(Animation anim) {
        // If we are an inset provider, all our animations are driven by the inset client.
        if (mControllableInsetProvider != null) {
            return;
        }

        final DisplayInfo displayInfo = getDisplayInfo();
        anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
                displayInfo.appWidth, displayInfo.appHeight);
        anim.restrictDuration(MAX_ANIMATION_DURATION);
        anim.scaleCurrentDuration(mDisplayContent.getWindowAnimationScaleLocked());
        final Point position = new Point();
        transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
                position);
        final AnimationAdapter adapter = new LocalAnimationAdapter(
                new WindowAnimationSpec(anim, position, false /* canSkipFirstFrame */,
                        0 /* windowCornerRadius */),
                mWmService.mSurfaceAnimationRunner);
        final Transaction t = mActivityRecord != null && mActivityRecord.isVisibleRequested()
                ? getSyncTransaction() : getPendingTransaction();
        startAnimation(t, 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 (mControllableInsetProvider != null) {
            return;
        }

        ProtoLog.v(WM_DEBUG_ANIM, "Setting move animation on %s", 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, ANIMATION_TYPE_WINDOW_ANIMATION);
    }

    @Override
    protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
        super.onAnimationFinished(type, anim);
        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] = mGlobalScale;
        float9[Matrix.MSKEW_Y] = 0;
        float9[Matrix.MSKEW_X] = 0;
        float9[Matrix.MSCALE_Y] = mGlobalScale;
        transformSurfaceInsetsPosition(mTmpPoint, mAttrs.surfaceInsets);
        int x = mSurfacePosition.x + mTmpPoint.x;
        int y = mSurfacePosition.y + mTmpPoint.y;

        // 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 ActivityRecord needs to access without having
    // access to its windows children. Need to investigate re-writing
    // {@link ActivityRecord#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_ACCESSIBILITY_MAGNIFICATION_OVERLAY
                || mAttrs.type == TYPE_MAGNIFICATION_OVERLAY
                // 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;
        }
        if ((mAttrs.privateFlags & PRIVATE_FLAG_INPUT_METHOD_WINDOW) != 0
                || mAttrs.type == TYPE_INPUT_METHOD
                || mAttrs.type == TYPE_INPUT_METHOD_DIALOG) {
            return mWmService.isMagnifyImeEnabled();
        }
        if (mAttrs.type == TYPE_NAVIGATION_BAR) {
            return mWmService.isMagnifyNavBarEnabled();
        }
        if ((mAttrs.privateFlags & PRIVATE_FLAG_NOT_MAGNIFIABLE) != 0) {
            return false;
        }
        return true;
    }

    private boolean isStartingWindowAssociatedToTask() {
        return mStartingData != null && mStartingData.mAssociatedTask != null;
    }

    private void applyDims() {
        Task task = getTask();
        if (((mAttrs.flags & FLAG_DIM_BEHIND) != 0 || shouldDrawBlurBehind())
                && mWinAnimator.getShown()
                && !mHidden && mTransitionController.canApplyDim(task)) {
            // Only show the Dimmer when the following is satisfied:
            // 1. The window has the flag FLAG_DIM_BEHIND or blur behind is requested
            // 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.
            // 5. The window is not in a transition or is in a transition that allows to dim.
            mIsDimming = true;
            final float dimAmount = (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? mAttrs.dimAmount : 0;
            final int blurRadius = shouldDrawBlurBehind() ? mAttrs.getBlurBehindRadius() : 0;
            // If the window is visible from surface flinger perspective (mWinAnimator.getShown())
            // but not window manager visible (!isVisibleNow()), it can still be the parent of the
            // dim, but can not create a new surface or continue a dim alone.
            final Dimmer dimmer = getDimController();
            final WindowContainer<?> geometryParent = getDimParent();
            if (dimmer == null) {
                ProtoLog.e(WM_DEBUG_DIMMER, "WindowState %s does not have task or"
                        + " display area for dimming", this);
                return;
            }

            if (isVisibleNow()) {
                dimmer.adjustAppearance(this, dimAmount, blurRadius);
            }
            dimmer.adjustPosition(geometryParent, this /* relativeParent */);
        }
    }

    private Dimmer getDimController() {
        Task task = getTask();
        if (task != null) {
            return task.mDimmer;
        }
        RootDisplayArea displayArea = getRootDisplayArea();
        if (displayArea != null) {
            return displayArea.mDimmer;
        }
        return null;
    }

    private WindowContainer<?> getDimParent() {
        Task task = getTask();
        if (task != null && task.isSuitableForDimming()) {
            return task;
        }
        return getRootDisplayArea();
    }

    private boolean shouldDrawBlurBehind() {
        return (mAttrs.flags & FLAG_BLUR_BEHIND) != 0
            && mWmService.mBlurController.getBlurEnabled();
    }

    /**
     * Notifies SF about the priority of the window, if it changed. SF then uses this information
     * to decide which window's desired rendering rate should have a priority when deciding about
     * the refresh rate of the screen. Priority
     * {@link RefreshRatePolicy#LAYER_PRIORITY_FOCUSED_WITH_MODE} is considered the highest.
     */
    @VisibleForTesting
    void updateFrameRateSelectionPriorityIfNeeded() {
        RefreshRatePolicy refreshRatePolicy =
                getDisplayContent().getDisplayPolicy().getRefreshRatePolicy();
        final int priority = refreshRatePolicy.calculatePriority(this);
        if (mFrameRateSelectionPriority != priority) {
            mFrameRateSelectionPriority = priority;
            getPendingTransaction().setFrameRateSelectionPriority(mSurfaceControl,
                    mFrameRateSelectionPriority);
        }

        boolean voteChanged = refreshRatePolicy.updateFrameRateVote(this);
        if (voteChanged) {
            getPendingTransaction()
                    .setFrameRate(mSurfaceControl, mFrameRateVote.mRefreshRate,
                            mFrameRateVote.mCompatibility, Surface.CHANGE_FRAME_RATE_ALWAYS)
                    .setFrameRateSelectionStrategy(mSurfaceControl,
                            mFrameRateVote.mSelectionStrategy);
        }
    }

    private void updateScaleIfNeeded() {
        float globalScale = mGlobalScale;
        final WindowState parent = getParentWindow();
        if (parent != null) {
            // Undo parent's scale because the child surface has inherited scale from parent.
            globalScale *= parent.mInvGlobalScale;
        }
        final float newHScale = mHScale * globalScale * mWallpaperScale;
        final float newVScale = mVScale * globalScale * mWallpaperScale;
        if (mLastHScale != newHScale || mLastVScale != newVScale) {
            getSyncTransaction().setMatrix(mSurfaceControl, newHScale, 0, 0, newVScale);
            mLastHScale = newHScale;
            mLastVScale = newVScale;
        }
    }

    private void updateSystemContentPriorityIfNeeded() {
        int newPriority = mInteractionPriorityScore;
        if (newPriority != mSystemContentPriority) {
            getPendingTransaction().setSystemContentPriority(mSurfaceControl, newPriority);
            mSystemContentPriority = newPriority;
        }
    }

    @Override
    void prepareSurfaces() {
        mIsDimming = false;
        if (mHasSurface) {
            updateSurfacePositionNonOrganized();
            // Send information to SurfaceFlinger about the priority of the current window.
            updateFrameRateSelectionPriorityIfNeeded();
            updateSystemContentPriorityIfNeeded();
            if (isVisibleRequested()) {
                updateScaleIfNeeded();
            }
            mWinAnimator.prepareSurfaceLocked(getPendingTransaction());
            applyDims();
        }
        super.prepareSurfaces();
    }

    @Override
    void adjustDims() {
        applyDims();
        super.adjustDims();
    }

    void updateSurfacePositionIfNeeded() {
        if (mWindowFrames.mRelFrame.top == mWindowFrames.mLastRelFrame.top
                && mWindowFrames.mRelFrame.left == mWindowFrames.mLastRelFrame.left) {
            return;
        }
        updateSurfacePosition(getSyncTransaction());
    }

    @Override
    @VisibleForTesting
    void updateSurfacePosition(Transaction t) {
        if (mSurfaceControl == null || mIsSurfacePositionPaused) {
            return;
        }
        if (mActivityRecord != null && mActivityRecord.isConfigurationDispatchPaused()) {
            // Don't update surface-position while dispatch paused. This is calculated from
            // the server-side activity configuration so return early.
            return;
        }

        if ((mWmService.mWindowPlacerLocked.isLayoutDeferred() || isGoneForLayout())
                && !mSurfacePlacementNeeded) {
            // Since this relies on mWindowFrames, changes made while layout is deferred are
            // likely to be invalid. Similarly, if it's goneForLayout, mWindowFrames may not be
            // up-to-date and thus can't be relied on.
            return;
        }

        mSurfacePlacementNeeded = false;
        transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
                mSurfacePosition);

        if (mWallpaperScale != 1f) {
            final Rect bounds = getParentFrame();
            Matrix matrix = mTmpMatrix;
            matrix.setTranslate(mXOffset, mYOffset);
            matrix.postScale(mWallpaperScale, mWallpaperScale, bounds.exactCenterX(),
                    bounds.exactCenterY());
            matrix.getValues(mTmpMatrixArray);
            mSurfacePosition.offset(Math.round(mTmpMatrixArray[Matrix.MTRANS_X]),
                Math.round(mTmpMatrixArray[Matrix.MTRANS_Y]));
        } else {
            mSurfacePosition.offset(mXOffset, mYOffset);
        }

        final AsyncRotationController asyncRotationController =
                mDisplayContent.getAsyncRotationController();
        if (asyncRotationController != null
                && asyncRotationController.hasSeamlessOperation(mToken)) {
            // Freeze position while un-rotating the window, so its surface remains at the position
            // corresponding to the original rotation.
            return;
        }

        if (!mSurfaceAnimator.hasLeash() && !mLastSurfacePosition.equals(mSurfacePosition)) {
            final boolean frameSizeChanged = mWindowFrames.isFrameSizeChanged();
            final boolean surfaceInsetsChanged = surfaceInsetsChanging();
            final boolean surfaceSizeChanged = frameSizeChanged || surfaceInsetsChanged;
            mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
            if (surfaceInsetsChanged) {
                mLastSurfaceInsets.set(mAttrs.surfaceInsets);
            }
            final boolean surfaceResizedWithoutMoveAnimation = surfaceSizeChanged
                    && mWinAnimator.getShown() && !canPlayMoveAnimation() && okToDisplay()
                    && mSyncState == SYNC_STATE_NONE;
            final ActivityRecord activityRecord = getActivityRecord();
            // If this window belongs to an activity that is relaunching due to an orientation
            // change then delay the position update until it has redrawn to avoid any flickers.
            final boolean isLetterboxedAndRelaunching = activityRecord != null
                    && activityRecord.areBoundsLetterboxed()
                    && activityRecord.mAppCompatController.getOrientationOverrides()
                        .getIsRelaunchingAfterRequestedOrientationChanged();
            if (surfaceResizedWithoutMoveAnimation || isLetterboxedAndRelaunching) {
                applyWithNextDraw(mSetSurfacePositionConsumer);
            } else {
                mSetSurfacePositionConsumer.accept(t);
            }
        }
    }

    void transformFrameToSurfacePosition(int left, int top, Point outPoint) {
        outPoint.set(left, top);

        // If changed, also adjust getTransformationMatrix
        final WindowContainer parentWindowContainer = getParent();
        if (isChildWindow()) {
            final WindowState parent = getParentWindow();
            outPoint.offset(-parent.mWindowFrames.mFrame.left, -parent.mWindowFrames.mFrame.top);
            // Undo the scale of window position because the relative coordinates for child are
            // based on the scaled parent.
            if (mInvGlobalScale != 1f) {
                outPoint.x = (int) (outPoint.x * mInvGlobalScale + 0.5f);
                outPoint.y = (int) (outPoint.y * mInvGlobalScale + 0.5f);
            }
            // Since the parent was outset by its surface insets, we need to undo the outsetting
            // with insetting by the same amount.
            transformSurfaceInsetsPosition(mTmpPoint, parent.mAttrs.surfaceInsets);
            outPoint.offset(mTmpPoint.x, mTmpPoint.y);
        } else if (parentWindowContainer != null) {
            final Rect parentBounds = isStartingWindowAssociatedToTask()
                    ? mStartingData.mAssociatedTask.getBounds()
                    : parentWindowContainer.getBounds();
            outPoint.offset(-parentBounds.left, -parentBounds.top);
        }

        // The surface size is larger than the window if the window has positive surface insets.
        transformSurfaceInsetsPosition(mTmpPoint, mAttrs.surfaceInsets);
        outPoint.offset(-mTmpPoint.x, -mTmpPoint.y);

        outPoint.y += mSurfaceTranslationY;
    }

    /**
     * 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) {
        // Ignore the scale for child window because its insets have been scaled with the
        // parent surface.
        if (mGlobalScale == 1f || mIsChildWindow) {
            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);
    }

    /**
     * Whether the layer of this window should be relatively above IME.
     * @see DisplayContent#assignRelativeLayerForImeLayeringTargetChild
     */
    boolean needsRelativeLayeringToIme() {
        if (!mDisplayContent.getImeContainer().isVisible()) {
            return false;
        }
        // We use the relative layering when IME isn't attached to the app. Such as part of
        // elevating the IME and windows above it's target above the docked divider in
        // split-screen, or make the popupMenu to be above the IME when the parent window is the
        // IME layering target in bubble/freeform mode.
        if (mDisplayContent.shouldImeAttachedToApp()) {
            return false;
        }

        if (isChildWindow()) {
            // If we are a child of the IME layering target we need this promotion.
            if (getParentWindow().isImeLayeringTarget()) {
                return true;
            }
        } else if (mActivityRecord != null) {
            // Likewise if we share a token with the IME layering target and are ordered
            // above it but not necessarily a child (e.g. a Dialog) then we also need
            // this promotion.
            final WindowState imeLayeringTarget = getImeLayeringTarget();
            boolean inTokenWithAndAboveImeLayeringTarget = imeLayeringTarget != null
                    && imeLayeringTarget != this
                    && imeLayeringTarget.mToken == mToken
                    && mAttrs.type != TYPE_APPLICATION_STARTING
                    && getParent() != null
                    && imeLayeringTarget.compareTo(this) <= 0;
            return inTokenWithAndAboveImeLayeringTarget;
        }

        // The condition is for the system dialog not belonging to any Activity.
        // (^FLAG_NOT_FOCUSABLE & FLAG_ALT_FOCUSABLE_IM) means the dialog is still focusable but
        // should be placed above the IME window.
        if ((mAttrs.flags & (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM))
                == FLAG_ALT_FOCUSABLE_IM && isTrustedOverlay() && canAddInternalSystemWindow()) {
            // Check the current IME layering target so that it does not lift this window above the
            // IME if the Z-order of the current IME layering target is greater than it.
            final WindowState imeLayeringTarget = getImeLayeringTarget();
            return imeLayeringTarget != null && imeLayeringTarget != this
                    && imeLayeringTarget.compareTo(this) <= 0;
        }
        return false;
    }

    /**
     * Get IME target that should host IME.
     * Note: IME is never hosted by a display that doesn't support IME/system decorations.
     * When window calling
     * {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} is unknown,
     * use {@link DisplayContent#getImeControlTarget()} instead.
     *
     * @return {@link InsetsControlTarget} of host that controls the IME.
     *         When window is doesn't have a parent, it is returned as-is.
     */
    @Override
    public InsetsControlTarget getImeControlTarget() {
        return getDisplayContent().getImeHost(this);
    }

    @Override
    void assignLayer(Transaction t, int layer) {
        if (mStartingData != null) {
            if (mSyncState != SYNC_STATE_NONE) {
                // When this container needs to be synced, assign layer with its own sync
                // transaction to avoid out of ordering when merge.
                // Still use the passed-in transaction for non-sync case, such as building finish
                // transaction.
                t = getSyncTransaction();
            }
            // The starting window should cover the task.
            t.setLayer(mSurfaceControl, Integer.MAX_VALUE);
            return;
        }
        // See comment in assignRelativeLayerForImeLayeringTargetChild
        if (needsRelativeLayeringToIme()) {
            getDisplayContent().assignRelativeLayerForImeLayeringTargetChild(t, this);
            return;
        }
        super.assignLayer(t, layer);
    }

    boolean isDimming() {
        return mIsDimming;
    }

    @Override
    protected void reparentSurfaceControl(Transaction t, SurfaceControl newParent) {
        if (isStartingWindowAssociatedToTask()) {
            // Its surface is already put in task. Don't reparent when transferring starting window
            // across activities.
            return;
        }
        super.reparentSurfaceControl(t, newParent);
    }

    @Override
    public SurfaceControl getAnimationLeashParent() {
        if (mActivityRecord != null && !mActivityRecord.hasFixedRotationTransform()
                && isStartingWindowAssociatedToTask()) {
            return mStartingData.mAssociatedTask.mSurfaceControl;
        }
        return super.getAnimationLeashParent();
    }

    @Override
    public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
        super.onAnimationLeashCreated(t, leash);
        if (isStartingWindowAssociatedToTask()) {
            // Make sure the animation leash is still on top of the task.
            t.setLayer(leash, Integer.MAX_VALUE);
        }
    }

    @Override
    public void onAnimationLeashLost(Transaction t) {
        if (!mSurfacePlacementNeeded && mActivityRecord != null
                && !mActivityRecord.isVisibleRequested() && mActivityRecord.isVisible()
                && !mLastSurfacePosition.equals(mSurfacePosition)) {
            // The activity is closing but still visible. Make sure updateSurfacePosition() is not
            // skipped due to isGoneForLayout().
            mSurfacePlacementNeeded = true;
        }
        super.onAnimationLeashLost(t);
    }

    // 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) {
                if (mWinAnimator.hasSurface()) {
                    w.assignRelativeLayer(t, mWinAnimator.mSurfaceControl, -2);
                } else {
                    w.assignLayer(t, -2);
                }
            } else if (w.mAttrs.type == TYPE_APPLICATION_MEDIA_OVERLAY) {
                if (mWinAnimator.hasSurface()) {
                    w.assignRelativeLayer(t, mWinAnimator.mSurfaceControl, -1);
                } else {
                    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(Region region) {
        final DisplayContent currentDisplay = getDisplayContent();
        if (currentDisplay == null) {
            throw new IllegalStateException("Trying to update window not attached to any display.");
        }

        // Clear the tap excluded region if the region passed in is null or empty.
        if (region == null || region.isEmpty()) {
            mTapExcludeRegion.setEmpty();
        } else {
            mTapExcludeRegion.set(region);
        }

        // Trigger touchable region update for this window.
        currentDisplay.getInputMonitor().updateInputWindowsLw(true /* force */);
    }

    /**
     * Get the tap excluded region for this window in screen coordinates.
     *
     * @param outRegion The returned tap excluded region. It is on the screen coordinates.
     */
    void getTapExcludeRegion(Region outRegion) {
        mTmpRect.set(mWindowFrames.mFrame);
        mTmpRect.offsetTo(0, 0);

        outRegion.set(mTapExcludeRegion);
        outRegion.op(mTmpRect, Region.Op.INTERSECT);

        // The region is on the window coordinates, so it needs to  be translated into screen
        // coordinates. There's no need to scale since that will be done by native code.
        outRegion.translate(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top);
    }

    boolean isImeLayeringTarget() {
        return mDisplayContent.getImeLayeringTarget() == this;
    }

    /**
     * Whether the window is non-focusable IME overlay layering target.
     */
    boolean isImeOverlayLayeringTarget() {
        return isImeLayeringTarget()
                && (mAttrs.flags & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0;
    }

    @Nullable
    WindowState getImeLayeringTarget() {
        return mDisplayContent.getImeLayeringTarget();
    }

    @Nullable
    WindowState getImeInputTarget() {
        final InputTarget target = mDisplayContent.getImeInputTarget();
        return target != null ? target.getWindowState() : null;
    }

    void forceReportingResized() {
        mWindowFrames.forceReportingResized();
    }

    /** Returns the {@link WindowFrames} associated with this {@link WindowState}. */
    WindowFrames getWindowFrames() {
        return mWindowFrames;
    }

    void resetContentChanged() {
        mWindowFrames.setContentChanged(false);
    }

    /**
     * Sets an {@link InsetsSourceProvider} to be associated with this window, but only if the
     * provider itself is controllable, as one window can be the provider of more than one inset
     * type (i.e. gesture insets). If this window is controllable, all its animations must be
     * controlled by its control target, and the visibility of this window should be taken account
     * into the state of the control target.
     *
     * @param insetProvider the provider which should not be visible to the client.
     * @see WindowState#getInsetsState()
     */
    void setControllableInsetProvider(@Nullable InsetsSourceProvider insetProvider) {
        mControllableInsetProvider = insetProvider;
    }

    @Nullable
    InsetsSourceProvider getControllableInsetProvider() {
        return mControllableInsetProvider;
    }

    /**
     * Returns {@code true} if this node provides insets.
     */
    boolean hasInsetsSourceProvider() {
        return mInsetsSourceProviders != null;
    }

    /**
     * Returns {@link InsetsSourceProvider}s provided by this node.
     */
    @NonNull
    SparseArray<InsetsSourceProvider> getInsetsSourceProviders() {
        if (mInsetsSourceProviders == null) {
            mInsetsSourceProviders = new SparseArray<>();
        }
        return mInsetsSourceProviders;
    }

    private final class MoveAnimationSpec implements AnimationSpec {

        private final long mDuration;
        private final Interpolator mInterpolator;
        private final Point mFrom = new Point();
        private final 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() * mDisplayContent.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 = getFraction(currentPlayTime);
            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 dumpDebugInner(ProtoOutputStream proto) {
            final long token = proto.start(MOVE);
            dumpPointProto(mFrom, proto, FROM);
            dumpPointProto(mTo, proto, TO);
            proto.write(DURATION_MS, mDuration);
            proto.end(token);
        }
    }

    KeyInterceptionInfo getKeyInterceptionInfo() {
        if (mKeyInterceptionInfo == null
                || mKeyInterceptionInfo.layoutParamsPrivateFlags != mAttrs.privateFlags
                || mKeyInterceptionInfo.layoutParamsType != mAttrs.type
                || mKeyInterceptionInfo.windowTitle != getWindowTag()
                || mKeyInterceptionInfo.windowOwnerUid != getOwningUid()
                || mKeyInterceptionInfo.layoutParamsInputFeatures != mAttrs.inputFeatures) {
            mKeyInterceptionInfo = new KeyInterceptionInfo(mAttrs.type, mAttrs.privateFlags,
                    mAttrs.inputFeatures, getWindowTag().toString(), getOwningUid());
        }
        return mKeyInterceptionInfo;
    }

    void setViewVisibility(int viewVisibility) {
        mViewVisibility = viewVisibility;

        if (isPublicPresentation()
                && (viewVisibility == View.INVISIBLE || viewVisibility == View.GONE)) {
            mWmService.mPresentationController.removePresentation(getDisplayId(),
                    "setViewVisibility");
        }
    }

    SurfaceControl getClientViewRootSurface() {
        return mWinAnimator.mSurfaceControl;
    }

    /** Drops a buffer for this window's view-root from a transaction */
    private void dropBufferFrom(Transaction t) {
        SurfaceControl viewSurface = getClientViewRootSurface();
        if (viewSurface == null) return;
        t.unsetBuffer(viewSurface);
    }

    @Override
    protected boolean shouldUpdateSyncOnReparent() {
        // Keep the sync state in case the client is drawing for the latest conifguration or the
        // configuration is not changed after reparenting. This avoids a redundant redraw request.
        return mSyncState != SYNC_STATE_NONE && !mLastConfigReportedToClient;
    }

    @Override
    boolean prepareSync() {
        if (!mDrawHandlers.isEmpty()) {
            Slog.w(TAG, "prepareSync with mDrawHandlers, " + this + ", " + Debug.getCallers(8));
        }
        if (!super.prepareSync()) {
            return false;
        }
        if (mIsWallpaper) {
            // TODO(b/233286785): Add sync support to wallpaper.
            return true;
        }
        if (mActivityRecord != null && mViewVisibility != View.VISIBLE
                && mWinAnimator.mAttrType != TYPE_BASE_APPLICATION
                && mWinAnimator.mAttrType != TYPE_APPLICATION_STARTING) {
            // Skip sync for invisible app windows which are not managed by activity lifecycle.
            return false;
        }
        // In the WindowContainer implementation we immediately mark ready
        // since a generic WindowContainer only needs to wait for its
        // children to finish and is immediately ready from its own
        // perspective but at the WindowState level we need to wait for ourselves
        // to draw even if the children draw first or don't need to sync, so we start
        // in WAITING state rather than READY.
        mSyncState = SYNC_STATE_WAITING_FOR_DRAW;

        if (!mWmService.mAlwaysSeqId) {
            if (mPrepareSyncSeqId > 0) {
                // another prepareSync during existing sync (eg. reparented), so pre-emptively
                // drop buffer (if exists). If the buffer hasn't been received yet, it will be
                // dropped in finishDrawing.
                ProtoLog.d(WM_DEBUG_SYNC_ENGINE, "Preparing to sync a window that was already in "
                                + "the sync, so try dropping buffer. win=%s", this);
                dropBufferFrom(mSyncTransaction);
            }

            mSyncSeqId++;
            if (getSyncMethod() == BLASTSyncEngine.METHOD_BLAST) {
                mPrepareSyncSeqId = mSyncSeqId;
                requestRedrawForSync();
            } else if (mHasSurface && mWinAnimator.mDrawState != DRAW_PENDING) {
                // Only need to request redraw if the window has reported draw.
                requestRedrawForSync();
            }
        } else if (mBufferSeqId <= mSyncSeqId && getSyncMethod() == BLASTSyncEngine.METHOD_BLAST) {
            mBufferSeqId = mSyncSeqId + 1;
        }
        return true;
    }

    @Override
    boolean isSyncFinished(BLASTSyncEngine.SyncGroup group) {
        if (!isVisibleRequested() || isFullyTransparent()) {
            // Don't wait for invisible windows. However, we don't alter the state in case the
            // window becomes visible while the sync group is still active.
            return true;
        }
        if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mLastConfigReportedToClient && isDrawn()
                && (mWmService.mAlwaysSeqId
                        ? mBufferSeqId <= mSyncSeqId : mPrepareSyncSeqId <= 0)) {
            // Complete the sync state immediately for a drawn window that doesn't need to redraw.
            onSyncFinishedDrawing();
        }
        return super.isSyncFinished(group);
    }

    @Override
    void finishSync(Transaction outMergedTransaction, BLASTSyncEngine.SyncGroup group,
            boolean cancel) {
        if (isDifferentSyncGroup(group)) return;
        if (!mWmService.mAlwaysSeqId) {
            mPrepareSyncSeqId = 0;
        }
        if (cancel) {
            // This is leaving sync so any buffers left in the sync have a chance of
            // being applied out-of-order and can also block the buffer queue for this
            // window. To prevent this, drop the buffer.
            dropBufferFrom(mSyncTransaction);
        }
        super.finishSync(outMergedTransaction, group, cancel);
    }

    boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction, int syncSeqId) {
        if (mOrientationChangeRedrawRequestTime > 0) {
            final long duration =
                    SystemClock.elapsedRealtime() - mOrientationChangeRedrawRequestTime;
            Slog.i(TAG, "finishDrawing of orientation change: " + this + " " + duration + "ms");
            mOrientationChangeRedrawRequestTime = 0;
        } else if (mActivityRecord != null && mActivityRecord.mRelaunchStartTime != 0
                && mActivityRecord.findMainWindow(false /* includeStartingApp */) == this) {
            final long duration =
                    SystemClock.elapsedRealtime() - mActivityRecord.mRelaunchStartTime;
            Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms");
            mActivityRecord.finishOrAbortReplacingWindow();
        }
        if (mActivityRecord != null && mAttrs.type == TYPE_APPLICATION_STARTING) {
            mWmService.mAtmService.mTaskSupervisor.getActivityMetricsLogger()
                    .notifyStartingWindowDrawn(mActivityRecord);
        }

        final boolean syncActive;
        final boolean syncStillPending;
        if (mWmService.mAlwaysSeqId) {
            syncStillPending = false;
            syncActive = mSyncState == SYNC_STATE_WAITING_FOR_DRAW;
            final boolean syncBuffersPending = syncActive && mBufferSeqId > syncSeqId;
            if (syncBuffersPending && postDrawTransaction != null) {
                ProtoLog.d(WM_DEBUG_SYNC_ENGINE,
                        "Got a buffer for request id=%d but latest request is"
                                + " id=%d. Since the buffer is out-of-date, drop it. win=%s",
                        syncSeqId, mBufferSeqId, this);
                // sync is waiting for a newer seqId, so this buffer is obsolete and can be dropped
                // to free up the buffer queue.
                dropBufferFrom(postDrawTransaction);
            }
        } else {
            syncActive = mPrepareSyncSeqId > 0;
            syncStillPending = syncActive && mPrepareSyncSeqId > syncSeqId;
            if (syncStillPending && postDrawTransaction != null) {
                ProtoLog.d(WM_DEBUG_SYNC_ENGINE, "Got a buffer for request id=%d but latest request"
                                + " is id=%d. Since the buffer is out-of-date, drop it. win=%s",
                        syncSeqId, mPrepareSyncSeqId, this);
                // sync is waiting for a newer seqId, so this buffer is obsolete and can be dropped
                // to free up the buffer queue.
                dropBufferFrom(postDrawTransaction);
            }
        }

        final boolean hasSyncHandlers = executeDrawHandlers(postDrawTransaction, syncSeqId);

        boolean skipLayout = false;
        boolean layoutNeeded = false;
        // Control the timing to switch the appearance of window with different rotations.
        final AsyncRotationController asyncRotationController =
                mDisplayContent.getAsyncRotationController();
        if (asyncRotationController != null
                && asyncRotationController.handleFinishDrawing(this, postDrawTransaction)) {
            // Consume the transaction because the controller will apply it with fade animation.
            // Layout is not needed because the window will be hidden by the fade leash.
            postDrawTransaction = null;
            skipLayout = true;
        } else if (syncActive) {
            // Currently in a Sync.
            if (mWmService.mAlwaysSeqId ? syncSeqId >= mSyncSeqId : !syncStillPending) {
                layoutNeeded = onSyncFinishedDrawing();
            }
            if (postDrawTransaction != null
                    && (!mWmService.mAlwaysSeqId
                            || getSyncMethod() == BLASTSyncEngine.METHOD_BLAST)) {
                mSyncTransaction.merge(postDrawTransaction);
                // Consume the transaction because the sync group will merge it.
                postDrawTransaction = null;
            }
        } else if (!mWmService.mAlwaysSeqId && syncNextBuffer()) {
            // Sync that is not using BLAST
            layoutNeeded = onSyncFinishedDrawing();
        }

        layoutNeeded |= mWinAnimator.finishDrawingLocked(postDrawTransaction);
        // We always want to force a traversal after a finish draw for blast sync.
        return !skipLayout && (hasSyncHandlers || layoutNeeded);
    }

    void immediatelyNotifyBlastSync() {
        // We could be more subtle with Integer.MAX_VALUE and track a seqId in the timeout.
        finishDrawing(null, Integer.MAX_VALUE);
        mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
    }

    @Override
    boolean fillsParent() {
        return mAttrs.type == TYPE_APPLICATION_STARTING;
    }

    @Override
    boolean hasFillingContent() {
        return true;
    }

    @Override
    boolean showWallpaper() {
        if (!isVisibleRequested()
                // in multi-window mode, wallpaper is always visible at the back and not tied to
                // the app (there is no wallpaper target).
                || inMultiWindowMode()) {
            return false;
        }
        return hasWallpaper();
    }

    @Override
    boolean hasWallpaper() {
        return (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0 || hasWallpaperForLetterboxBackground();
    }

    boolean hasWallpaperForLetterboxBackground() {
        return mActivityRecord != null && mActivityRecord.hasWallpaperBackgroundForLetterbox();
    }

    /**
     * When using the two WindowOrganizer sync-primitives (BoundsChangeTransaction, BLASTSync)
     * it can be a little difficult to predict whether your change will actually trigger redrawing
     * on the client side. To ease the burden on shell developers, we force send MSG_RESIZED
     * for Windows involved in these Syncs
     */
    private boolean shouldSendRedrawForSync() {
        if (mRedrawForSyncReported) {
            return false;
        }
        if (mInRelayout && (mPrepareSyncSeqId > 0 || (mViewVisibility == View.VISIBLE
                && mWinAnimator.mDrawState == DRAW_PENDING))) {
            // The client will report draw if it gets the sync seq id from relayout or it is
            // drawing for being visible, then no need to request redraw.
            return false;
        }
        return syncNextBuffer();
    }

    int getSyncMethod() {
        final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
        if (syncGroup == null) return BLASTSyncEngine.METHOD_NONE;
        if (mSyncMethodOverride != BLASTSyncEngine.METHOD_UNDEFINED) return mSyncMethodOverride;
        return syncGroup.mSyncMethod;
    }

    void useBlastForNextSync() {
        if (mSyncMethodOverride == BLASTSyncEngine.METHOD_BLAST) return;
        mSyncMethodOverride = BLASTSyncEngine.METHOD_BLAST;
        final BLASTSyncEngine.SyncGroup syncGroup = getSyncGroup();
        // If not in sync, then the seqId will be incremented in prepareSync (when added)
        if (syncGroup == null || syncGroup.mSyncMethod == BLASTSyncEngine.METHOD_BLAST) {
            return;
        }
        mBufferSeqId = mSyncSeqId + 1;
    }

    boolean shouldSyncWithBuffers() {
        if (!mDrawHandlers.isEmpty()) return true;
        return getSyncMethod() == BLASTSyncEngine.METHOD_BLAST;
    }

    void requestRedrawForSync() {
        mRedrawForSyncReported = false;
    }

    /**
     * This method's name is outdated. It is used to check if we are currently waiting for the
     * results of a resize request.
     *
     * See {@link WindowState#mDrawHandlers}
     */
    boolean syncNextBuffer() {
        if (mWmService.mAlwaysSeqId) {
            return (mSyncState == SYNC_STATE_WAITING_FOR_DRAW) || !mDrawHandlers.isEmpty();
        }
        return mSyncState != SYNC_STATE_NONE || !mDrawHandlers.isEmpty();
    }

    /**
     * Apply the transaction with the next window redraw. A full relayout/finishDrawing
     * cycle must occur before completion. This means if you call the function while
     * "in relayout", the results may be undefined but at all other times the function
     * should sort of transparently work like this:
     *    1. Make changes to WM hierarchy (say change app configuration)
     *    2. Call applyWithNextDraw
     *    3. After finishDrawing, our consumer will be passed the Transaction
     *    containing the buffer, and we can merge in additional operations.
     * See {@link WindowState#mDrawHandlers}
     */
    void applyWithNextDraw(Consumer<SurfaceControl.Transaction> consumer) {
        if (mSyncState != SYNC_STATE_NONE) {
            Slog.w(TAG, "applyWithNextDraw with mSyncState=" + mSyncState + ", " + this
                    + ", " + Debug.getCallers(8));
        }
        if (mWmService.mAlwaysSeqId) {
            // Draw handlers require buffer.
            if (mBufferSeqId <= mSyncSeqId) {
                mBufferSeqId = mSyncSeqId + 1;
            }
            mDrawHandlers.add(new DrawHandler(mBufferSeqId, consumer));
        } else {
            mSyncSeqId++;
            mDrawHandlers.add(new DrawHandler(mSyncSeqId, consumer));

            requestRedrawForSync();
        }

        mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this,
            BLAST_TIMEOUT_DURATION);
    }

    /**
     * Drain the draw handlers, called from finishDrawing()
     */
    private boolean executeDrawHandlers(@Nullable SurfaceControl.Transaction t, int seqId) {
        final int numDrawHandlers = mDrawHandlers.size();
        if (numDrawHandlers == 0) {
            return false;
        }

        boolean applyHere = false;
        if (t == null) {
            t = mTmpTransaction;
            applyHere = true;
        }

        final ArrayList<DrawHandler> consumedHandlers = new ArrayList<>();
        // Iterate in the order the handlers were added.
        for (int i = 0; i < numDrawHandlers; i++) {
            final DrawHandler h = mDrawHandlers.get(i);
            if (h.mSeqId <= seqId) {
                h.mConsumer.accept(t);
                consumedHandlers.add(h);
            }
        }
        if (consumedHandlers.isEmpty()) {
            return false;
        }

        mDrawHandlers.removeAll(consumedHandlers);
        mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
        if (applyHere) {
            t.apply();
        }
        return true;
    }

    /**
     * Adds an additional translation offset to be applied when positioning the surface. Used to
     * correct offsets in specific reparenting situations, e.g. the navigation bar window attached
     * on the lower split-screen app.
     */
    void setSurfaceTranslationY(int translationY) {
        mSurfaceTranslationY = translationY;
    }

    @Override
    @WindowManager.LayoutParams.WindowType int getWindowType() {
        return mAttrs.type;
    }

    void markRedrawForSyncReported() {
       mRedrawForSyncReported = true;
    }

    boolean setWallpaperOffset(int dx, int dy, float scale) {
        if (mXOffset == dx && mYOffset == dy && Float.compare(mWallpaperScale, scale) == 0) {
            return false;
        }
        mXOffset = dx;
        mYOffset = dy;
        mWallpaperScale = scale;
        updateScaleIfNeeded();
        scheduleAnimation();
        return true;
    }

    boolean isTrustedOverlay() {
        WindowState parentWindow = getParentWindow();
        return isWindowTrustedOverlay() || (parentWindow != null
                && parentWindow.isWindowTrustedOverlay());
    }

    public boolean receiveFocusFromTapOutside() {
        return canReceiveKeys(true);
    }

    @Override
    public void handleTapOutsideFocusOutsideSelf() {
        // Nothing to do here since raising the other window will naturally take care of
        // us loosing focus
    }

    @Override
    public void handleTapOutsideFocusInsideSelf() {
        mWmService.moveDisplayToTopInternal(getDisplayId());
        mWmService.handleTaskFocusChange(getTask(), mActivityRecord);
    }

    void clearClientTouchableRegion() {
        mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
        mGivenTouchableRegion.setEmpty();
    }

    @Override
    public boolean shouldControlIme() {
        return !inMultiWindowMode();
    }

    @Override
    public boolean canScreenshotIme() {
        return !isSecureLocked();
    }

    @Override
    public ActivityRecord getActivityRecord() {
        return mActivityRecord;
    }

    @Override
    public boolean isInputMethodClientFocus(int uid, int pid) {
        return getDisplayContent().isInputMethodClientFocus(uid, pid);
    }

    @Override
    public void dumpProto(ProtoOutputStream proto, long fieldId,
                          @WindowTracingLogLevel int logLevel) {
        dumpDebug(proto, fieldId, logLevel);
    }

    public boolean cancelAndRedraw(int seqId) {
        // Cancel any draw requests during a sync.
        if (!mWmService.mAlwaysSeqId) {
            return mPrepareSyncSeqId > 0;
        }
        return Math.max(mSyncSeqId, mBufferSeqId) > seqId;
    }

    /**
     * Normally, if the client hasn't received the latest configuration yet, we can't assume that
     * the layout parameters are accurate (since they can depend on the configuration).
     *
     * However, there are specific situations where layout logic ignores configuration-dependent
     * layout params AND where we are confident that those layout params aren't, themselves,
     * configuration-dependent. We can use this information for certain optimizations.
     *
     * @return {@code true} if this window's client configuration is irrelevant to layout.
     */
    boolean layoutIgnoresClientConfig() {
        // We are only confident that fullscreen system-ui windows remain fullscreen regardless of
        // of configuration.
        return mActivityRecord == null && !mIsWallpaper
                && mAttrs.width == WindowManager.LayoutParams.MATCH_PARENT
                && mAttrs.height == WindowManager.LayoutParams.MATCH_PARENT;
    }

    public boolean isActivityWindow() {
        return mActivityRecord != null;
    }

    void setSecureLocked(boolean isSecure) {
        ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isSecure=%b: %s", isSecure, getName());
        if (mSurfaceControl == null) {
            return;
        }
        getPendingTransaction().setSecure(mSurfaceControl, isSecure);
        if (mDisplayContent != null) {
            mDisplayContent.refreshImeSecureFlag(getSyncTransaction());
        }
        mWmService.scheduleAnimationLocked();
    }

    boolean isInsetsAnimationRunning() {
        return mInsetsAnimationRunning;
    }
}
