/*
 * Copyright (C) 2007 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.Manifest.permission.ACCESS_SURFACE_FLINGER;
import static android.Manifest.permission.INPUT_CONSUMER;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.MANAGE_APP_TOKENS;
import static android.Manifest.permission.MODIFY_TOUCH_MODE_STATE;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.Manifest.permission.STATUS_BAR_SERVICE;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_PC;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerServiceDumpProto.BACK_NAVIGATION;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerServiceDumpProto.FOCUSED_APP;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerServiceDumpProto.FOCUSED_DISPLAY_ID;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerServiceDumpProto.FOCUSED_WINDOW;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerServiceDumpProto.HARD_KEYBOARD_AVAILABLE;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerServiceDumpProto.INPUT_METHOD_WINDOW;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerServiceDumpProto.POLICY;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerServiceDumpProto.WINDOW_FRAMES_VALID;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
import static android.os.Process.myUid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.permission.flags.Flags.sensitiveContentImprovements;
import static android.permission.flags.Flags.sensitiveContentMetricsBugfix;
import static android.permission.flags.Flags.sensitiveContentRecentsScreenshotBugfix;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH;
import static android.service.dreams.Flags.dreamHandlesConfirmKeys;
import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
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_SLIPPERY;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_DISPLAY_TOPOLOGY_AWARE;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SENSITIVE_FOR_PRIVACY;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
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.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
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_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
import static android.view.flags.Flags.sensitiveContentAppProtection;
import static android.window.DesktopExperienceFlags.ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS;
import static android.window.DesktopExperienceFlags.ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS;
import static android.window.ScreenCapture.ScreenCaptureParams.CAPTURE_MODE_REQUIRE_OPTIMIZED;
import static android.window.ScreenCapture.ScreenCaptureParams.PROTECTED_CONTENT_POLICY_THROW_EXCEPTION;
import static android.window.ScreenCapture.ScreenCaptureParams.SECURE_CONTENT_POLICY_THROW_EXCEPTION;
import static android.window.WindowProviderService.isWindowProviderService;

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_BOOT;
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_SCREEN_ON;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_STARTING_WINDOW;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_WINDOW_MOVEMENT;
import static com.android.internal.protolog.WmProtoLogGroups.WM_ERROR;
import static com.android.internal.protolog.WmProtoLogGroups.WM_SHOW_TRANSACTIONS;
import static com.android.internal.util.FrameworkStatsLog.SENSITIVE_NOTIFICATION_APP_PROTECTION_APPLIED;
import static com.android.server.LockGuard.INDEX_WINDOW;
import static com.android.server.LockGuard.installLock;
import static com.android.server.policy.PhoneWindowManager.TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityTaskManagerService.DEMOTE_TOP_REASON_EXPANDED_NOTIFICATION_SHADE;
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
import static com.android.server.wm.SensitiveContentPackages.PackageInfo;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.SYNC_STATE_WAITING_FOR_DRAW;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_VERBOSE_TRANSACTIONS;
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.WindowManagerInternal.OnWindowRemovedListener;
import static com.android.server.wm.WindowManagerInternal.WindowFocusChangeListener;
import static com.android.systemui.shared.Flags.enableLppAssistInvocationEffect;
import static com.android.window.flags.Flags.enableDeviceStateAutoRotateSettingRefactor;
import static com.android.window.flags.Flags.multiCrop;
import static com.android.window.flags.Flags.setScPropertiesInClient;

import android.Manifest;
import android.Manifest.permission;
import android.animation.ValueAnimator;
import android.annotation.EnforcePermission;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IApplicationThread;
import android.app.IAssistDataReceiver;
import android.app.WindowConfiguration;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.TestUtilityService;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.HardwareBuffer;
import android.hardware.configstore.V1_0.OptionalBool;
import android.hardware.configstore.V1_1.ISurfaceFlingerConfigs;
import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputSettings;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.InputConfig;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.SystemService;
import android.os.Trace;
import android.os.UserHandle;
import android.permission.PermissionManager;
import android.provider.DeviceConfigInterface;
import android.provider.Settings;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.sysprop.SurfaceFlingerProperties;
import android.text.format.DateUtils;
import android.tracing.TracingUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.AtomicFileOutputStream;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.IntArray;
import android.util.MergedConfiguration;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.util.proto.ProtoOutputStream;
import android.view.Choreographer;
import android.view.ContentRecordingSession;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.ICrossWindowBlurEnabledListener;
import android.view.IDecorViewGestureListener;
import android.view.IDisplayChangeWindowController;
import android.view.IDisplayFoldListener;
import android.view.IDisplayWindowInsetsController;
import android.view.IDisplayWindowListener;
import android.view.IInputFilter;
import android.view.IOnKeyguardExitResult;
import android.view.IPinnedTaskListener;
import android.view.IRotationWatcher;
import android.view.IScrollCaptureResponseListener;
import android.view.ISystemGestureExclusionListener;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindow;
import android.view.IWindowId;
import android.view.IWindowManager;
import android.view.IWindowSession;
import android.view.IWindowSessionCallback;
import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputWindowHandle;
import android.view.InsetsFrameProvider;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.MagnificationSpec;
import android.view.RemoteAnimationAdapter;
import android.view.ScrollCaptureResponse;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
import android.view.View.FocusDirection;
import android.view.ViewDebug;
import android.view.WindowContentFrameStats;
import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.RemoveContentMode;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import android.view.WindowRelayoutResult;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
import android.view.inputmethod.ImeTracker;
import android.widget.Toast;
import android.window.ActivityWindowInfo;
import android.window.AddToSurfaceSyncGroupResult;
import android.window.ClientWindowFrames;
import android.window.ConfigurationChangeSetting;
import android.window.DesktopExperienceFlags;
import android.window.DesktopModeFlags;
import android.window.IGlobalDragListener;
import android.window.IScreenCaptureCallback;
import android.window.IScreenRecordingCallback;
import android.window.ISurfaceSyncGroupCompletedListener;
import android.window.ITaskFpsCallback;
import android.window.ITrustedPresentationListener;
import android.window.InputTransferToken;
import android.window.ScreenCapture;
import android.window.ScreenCaptureInternal;
import android.window.ScreenCaptureInternal.ScreenshotHardwareBuffer;
import android.window.SystemPerformanceHinter;
import android.window.TaskSnapshot;
import android.window.TaskSnapshotManager;
import android.window.TransitionRequestInfo;
import android.window.TrustedPresentationThresholds;
import android.window.WindowContainerToken;
import android.window.WindowContextInfo;

import com.android.internal.R;
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.TransferPipe;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IKeyguardLockedStateListener;
import com.android.internal.policy.IShortcutService;
import com.android.internal.policy.KeyInterceptionInfo;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.protolog.WmProtoLogGroups;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.LatencyTracker;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.view.WindowManagerPolicyThread;
import com.android.server.AnimationThread;
import com.android.server.DisplayThread;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.am.UserState;
import com.android.server.input.InputManagerService;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.power.ShutdownThread;
import com.android.server.utils.PriorityDump;
import com.android.window.flags.Flags;

import dalvik.annotation.optimization.NeverCompile;

import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.Socket;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/** @hide */
public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowManagerService" : TAG_WM;
    private static final int TRACE_MAX_SECTION_NAME_LENGTH = 127;

    static final int LAYOUT_REPEAT_THRESHOLD = 4;

    /** The maximum length we will accept for a loaded animation duration:
     * this is 10 seconds.
     */
    static final int MAX_ANIMATION_DURATION = 10 * 1000;

    /** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
    static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000;

    /** Amount of time to allow a last ANR message to exist before freeing the memory. */
    static final int LAST_ANR_LIFETIME_DURATION_MSECS = 2 * 60 * 60 * 1000; // Two hours

    // Maximum number of milliseconds to wait for input devices to be enumerated before
    // proceding with safe mode detection.
    private static final int INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS = 1000;

    private static final int SYNC_INPUT_TRANSACTIONS_TIMEOUT_MS = 5000;

    // Poll interval in milliseconds for watching boot animation finished.
    // TODO(b/159045990) Migrate to SystemService.waitForState with dedicated thread.
    private static final int BOOT_ANIMATION_POLL_INTERVAL = 50;

    // The name of the boot animation service in init.rc.
    private static final String BOOT_ANIMATION_SERVICE = "bootanim";

    static final int UPDATE_FOCUS_NORMAL = 0;
    /** Caller will assign layers */
    static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;
    /** Caller is performing surface placement */
    static final int UPDATE_FOCUS_PLACING_SURFACES = 2;
    /** Caller will performSurfacePlacement */
    static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3;
    /** Indicates we are removing the focused window when updating the focus. */
    static final int UPDATE_FOCUS_REMOVING_FOCUS = 4;

    private static final String SYSTEM_SECURE = "ro.secure";
    private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";

    private static final String DENSITY_OVERRIDE = "ro.config.density_override";
    private static final String SIZE_OVERRIDE = "ro.config.size_override";

    private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.boot.emulator.circular";

    static final int MY_PID = myPid();
    static final int MY_UID = myUid();

    static final int LOGTAG_INPUT_FOCUS = 62001;

    /**
     * Allows a fullscreen windowing mode activity to launch in its desired orientation directly
     * when the display has different orientation.
     */
    static final boolean ENABLE_FIXED_ROTATION_TRANSFORM =
            SystemProperties.getBoolean("persist.wm.fixed_rotation_transform", true);

    // Enums for animation scale update types.
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE, ANIMATION_DURATION_SCALE})
    private @interface UpdateAnimationScaleMode {}

    @VisibleForTesting
    static final int WINDOW_ANIMATION_SCALE = 0;
    @VisibleForTesting
    static final int TRANSITION_ANIMATION_SCALE = 1;
    private static final int ANIMATION_DURATION_SCALE = 2;

    private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000;

    final WindowManagerConstants mConstants;

    final WindowTracing mWindowTracing;
    final TransitionTracer mTransitionTracer;

    private final DisplayAreaPolicy.Provider mDisplayAreaPolicyProvider;

    final private KeyguardDisableHandler mKeyguardDisableHandler;

    private final RemoteCallbackList<IKeyguardLockedStateListener> mKeyguardLockedStateListeners =
            new RemoteCallbackList<>();

    private final List<OnWindowRemovedListener> mOnWindowRemovedListeners = new ArrayList<>();

    /** Indicates whether the first keyguard locked state has been dispatched. */
    private boolean mFirstKeyguardLockedStateDispatched = false;

    /** The last dispatched keyguard locked state. */
    private boolean mDispatchedKeyguardLockedState = false;

    // VR Vr2d Display Id.
    int mVr2dDisplayId = INVALID_DISPLAY;
    boolean mVrModeEnabled = false;

    private static final int WINDOW_INTERACTION_HISTORY_SIZE = 3;

    WindowInteractionTracker mInteractionTracker = new WindowInteractionTracker(
            WINDOW_INTERACTION_HISTORY_SIZE);

    /**
     * Tracks a map of input tokens to info that is used to decide whether to intercept
     * a key event.
     */
    final Map<IBinder, KeyInterceptionInfo> mKeyInterceptionInfoForToken =
            Collections.synchronizedMap(new ArrayMap<>());

    final StartingSurfaceController mStartingSurfaceController;

    final PresentationController mPresentationController;

    private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
        @Override
        public void onVrStateChanged(boolean enabled) {
            synchronized (mGlobalLock) {
                mVrModeEnabled = enabled;
            }
        }
    };

    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            switch (intent.getAction()) {
                case ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED:
                    mKeyguardDisableHandler.updateKeyguardEnabled(getSendingUserId());
                    break;
            }
        }
    };
    final WindowSurfacePlacer mWindowPlacerLocked;

    private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() {
        @Override
        public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args,
                boolean asProto) {
            doDump(fd, pw, new String[] {"-a"}, asProto);
        }

        @Override
        public void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args,
                boolean asProto) {
            if (asProto) {
                return;
            }

            final long timeoutMs = 1000L;
            mAtmService.dumpActivity(fd, pw, /* name= */ "all", /* args= */ new String[]{},
                    /* opti= */ 0,
                    /* dumpAll= */ true,
                    /* dumpVisibleRootTasksOnly= */ true,
                    /* dumpFocusedRootTaskOnly= */ false, INVALID_DISPLAY, UserHandle.USER_ALL,
                    timeoutMs
            );
            dumpVisibleWindowClients(fd, pw, timeoutMs);
        }

        @Override
        public void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
            doDump(fd, pw, args, asProto);
        }
    };

    /**
     * Current user when multi-user is enabled. Don't show windows of non-current user.
     */
    @UserIdInt int mCurrentUserId;

    final Context mContext;

    final boolean mHasPermanentDpad;
    final long mDrawLockTimeoutMillis;
    final boolean mAllowAnimationsInLowPowerMode;
    final boolean mSupportsHighPerfTransitions;
    final boolean mAllowBootMessages;

    // Indicates whether the Assistant should show on top of the Dream (respectively, above
    // everything else on screen). Otherwise, it will be put under always-on-top stacks.
    final boolean mAssistantOnTopOfDream;

    /**
     * If true, don't relaunch the activity upon receiving a configuration change to transition to
     * or from the {@link UI_MODE_TYPE_DESK} uiMode, which is sent when docking. The configuration
     * change will still be sent regardless, only the relaunch is skipped. Apps with desk resources
     * are exempt from this and will behave like normal, since they may expect the relaunch upon the
     * desk uiMode change.
     */
    @VisibleForTesting
    boolean mSkipActivityRelaunchWhenDocking;

    final boolean mLimitedAlphaCompositing;
    final int mMaxUiWidth;

    @NonNull
    @VisibleForTesting
    WindowManagerPolicy mPolicy;

    final WindowManagerFlags mFlags;

    final IActivityManager mActivityManager;
    final ActivityManagerInternal mAmInternal;
    final UserManagerInternal mUmInternal;

    final PermissionManager mPermissionManager;
    final AppOpsManager mAppOps;
    final PackageManagerInternal mPmInternal;
    private final TestUtilityService mTestUtilityService;

    @NonNull
    final DisplayWindowSettingsProvider mDisplayWindowSettingsProvider;
    @NonNull
    final DisplayWindowSettings mDisplayWindowSettings;

    /** If the system should display notifications for apps displaying an alert window. */
    boolean mShowAlertWindowNotifications = true;

    /**
     * All currently active sessions with clients.
     */
    final ArraySet<Session> mSessions = new ArraySet<>();

    /** Mapping from an IWindow IBinder to the server's Window object. */
    final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();

    /** Mapping from an InputWindowHandle token to the server's Window object. */
    final HashMap<IBinder, WindowState> mInputToWindowMap = new HashMap<>();

    /** Global service lock used by the package that owns this service. */
    final WindowManagerGlobalLock mGlobalLock;

    /**
     * Windows that are being resized.  Used so we can tell the client about
     * the resize after closing the transaction in which we resized the
     * underlying surface.
     */
    final ArrayList<WindowState> mResizingWindows = new ArrayList<>();

    /**
     * Windows that their frames are being changed.  Used so we can clear the frame-changing states
     * after handling the moved or resized windows.
     */
    final ArrayList<WindowState> mFrameChangingWindows = new ArrayList<>();

    /**
     * Mapping of displayId to {@link DisplayImePolicy}.
     * Note that this can be accessed without holding the lock.
     */
    volatile Map<Integer, Integer> mDisplayImePolicyCache = Collections.unmodifiableMap(
            new ArrayMap<>());

    /**
     * Windows whose surface should be destroyed.
     */
    final ArrayList<WindowState> mDestroySurface = new ArrayList<>();

    /**
     * This is set when we have run out of memory, and will either be an empty
     * list or contain windows that need to be force removed.
     */
    final ArrayList<WindowState> mForceRemoves = new ArrayList<>();

    /**
     * The callbacks to make when the windows all have been drawn for a given
     * {@link WindowContainer}.
     */
    final ArrayMap<WindowContainer<?>, Message> mWaitingForDrawnCallbacks = new ArrayMap<>();

    /** List of window currently causing non-system overlay windows to be hidden. */
    private ArrayList<WindowState> mHidingNonSystemOverlayWindows = new ArrayList<>();

    /**
     * A map that tracks uid/count of windows that cause non-system overlay windows to be hidden.
     * The key is the window's uid and the value is the number of windows with that uid that are
     * requesting hiding non-system overlay
     */
    private final ArrayMap<Integer, Integer> mHidingNonSystemOverlayWindowsCountPerUid =
            new ArrayMap<>();

    /**
     * In some cases (e.g. when {@link R.bool.config_reverseDefaultRotation} has value
     * {@value true}) we need to map some orientation to others. This {@link SparseIntArray}
     * contains the relation between the source orientation and the one to use.
     */
    private final SparseIntArray mOrientationMapping = new SparseIntArray();

    final AccessibilityController mAccessibilityController;

    Watermark mWatermark;
    StrictModeFlash mStrictModeFlash;
    EmulatorDisplayOverlay mEmulatorDisplayOverlay;

    final Rect mTmpRect = new Rect();

    boolean mDisplayReady;
    boolean mSafeMode;
    boolean mDisplayEnabled = false;
    boolean mSystemBooted = false;
    boolean mForceDisplayEnabled = false;
    boolean mShowingBootMessages = false;
    boolean mSystemReady = false;
    boolean mBootAnimationStopped = false;
    long mBootWaitForWindowsStartTime = -1;

    // Cache whether to Magnify the IME.
    private boolean mMagnifyIme = false;
    private boolean mIsMouseOrKeyboardConnected = false;

    /** Dump of the windows and app tokens at the time of the last ANR. Cleared after
     * LAST_ANR_LIFETIME_DURATION_MSECS */
    String mLastANRState;

    // The root of the device window hierarchy.
    @NonNull
    final RootWindowContainer mRoot;

    final BLASTSyncEngine mSyncEngine;

    boolean mIsPc;
    /**
     * Flag that indicates that desktop mode is forced for public secondary screens.
     *
     * This includes several settings:
     * - Set freeform windowing mode on external screen if it's supported and enabled.
     * - Enable system decorations and IME on external screen.
     * - TODO: Show mouse pointer on external screen.
     */
    boolean mForceDesktopModeOnExternalDisplays;

    public boolean mAlwaysSeqId;

    boolean mDisableTransitionAnimation;

    final RotationWatcherController mRotationWatcherController;
    final WallpaperVisibilityListeners mWallpaperVisibilityListeners =
            new WallpaperVisibilityListeners();

    IDisplayChangeWindowController mDisplayChangeController = null;
    private final DeathRecipient mDisplayChangeControllerDeath = new DeathRecipient() {
        @Override
        public void binderDied() {
            synchronized (mGlobalLock) {
                mDisplayChangeController = null;
            }
        }
    };

    final DisplayWindowListenerController mDisplayNotificationController;
    final TaskSystemBarsListenerController mTaskSystemBarsListenerController;

    /** Amount of time (in milliseconds) to delay the pointer down outside focus handling */
    private static final int POINTER_DOWN_OUTSIDE_FOCUS_TIMEOUT_MS = 50;

    /** A runnable to handle pointer down outside focus event. */
    @Nullable
    private Runnable mPointerDownOutsideFocusRunnable;

    boolean mSwitchingUser = false;

    @VisibleForTesting
    boolean mPerDisplayFocusEnabled;

    // State while inside of layoutAndPlaceSurfacesLocked().
    boolean mFocusMayChange;

    // Number of windows whose insets state have been changed.
    int mWindowsInsetsChanged = 0;

    final TaskSnapshotController mTaskSnapshotController;
    final SnapshotController mSnapshotController;

    final BlurController mBlurController;
    final TaskFpsCallbackController mTaskFpsCallbackController;

    boolean mIsTouchDevice;
    boolean mIsFakeTouchDevice;

    final H mH = new H();

    /**
     * Handler for things to run that have direct impact on an animation, i.e. animation tick,
     * layout, starting window creation, whereas {@link H} runs things that are still important, but
     * not as critical.
     */
    final Handler mAnimationHandler = new Handler(AnimationThread.getHandler().getLooper());

    boolean mHardKeyboardAvailable;
    WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
    WindowManagerInternal.OnImeRequestedChangedListener mOnImeRequestedChangedListener;

    SettingsObserver mSettingsObserver;
    final EmbeddedWindowController mEmbeddedWindowController;
    final AnrController mAnrController;

    private final DisplayHashController mDisplayHashController;

    volatile float mMaximumObscuringOpacityForTouch =
            InputSettings.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH;

    final WindowContextListenerController mWindowContextListenerController =
            new WindowContextListenerController();

    // The currently focused input target (window or embedded window) as reported from input
    private InputTarget mFocusedInputTarget;

    @VisibleForTesting
    final ContentRecordingController mContentRecordingController = new ContentRecordingController();

    private final SurfaceSyncGroupController mSurfaceSyncGroupController =
            new SurfaceSyncGroupController();

    final TrustedPresentationListenerController mTrustedPresentationListenerController =
            new TrustedPresentationListenerController();

    @VisibleForTesting
    final class SettingsObserver extends ContentObserver {
        private final Uri mDisplayInversionEnabledUri =
                Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
        private final Uri mWindowAnimationScaleUri =
                Settings.Global.getUriFor(Settings.Global.WINDOW_ANIMATION_SCALE);
        private final Uri mTransitionAnimationScaleUri =
                Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE);
        private final Uri mAnimationDurationScaleUri =
                Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE);
        private final Uri mImmersiveModeConfirmationsUri =
                Settings.Secure.getUriFor(Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS);
        private final Uri mDisableSecureWindowsUri =
                Settings.Secure.getUriFor(Settings.Secure.DISABLE_SECURE_WINDOWS);
        private final Uri mMagnifyImeEnabledUri = Settings.Secure.getUriFor(
                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MAGNIFY_NAV_AND_IME);
        private final Uri mPolicyControlUri =
                Settings.Global.getUriFor(Settings.Global.POLICY_CONTROL);
        private final Uri mForceDesktopModeOnExternalDisplaysUri = Settings.Global.getUriFor(
                        Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS);
        private final Uri mFreeformWindowUri = Settings.Global.getUriFor(
                Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT);
        private final Uri mForceResizableUri = Settings.Global.getUriFor(
                DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES);
        private final Uri mDevEnableNonResizableMultiWindowUri = Settings.Global.getUriFor(
                DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW);
        private final Uri mDisplaySettingsPathUri = Settings.Global.getUriFor(
                DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH);
        private final Uri mMaximumObscuringOpacityForTouchUri = Settings.Global.getUriFor(
                Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
        private final Uri mDevelopmentOverrideDesktopExperienceUri = Settings.Global.getUriFor(
                Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_EXPERIENCE_FEATURES);

        public SettingsObserver() {
            super(new Handler());
            ContentResolver resolver = mContext.getContentResolver();
            resolver.registerContentObserver(mDisplayInversionEnabledUri, false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(mWindowAnimationScaleUri, false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(mTransitionAnimationScaleUri, false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(mAnimationDurationScaleUri, false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(mImmersiveModeConfirmationsUri, false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(mDisableSecureWindowsUri, false, this,
                    UserHandle.USER_ALL);
            if (com.android.server.accessibility.Flags.enableMagnificationMagnifyNavBarAndIme()) {
                resolver.registerContentObserver(mMagnifyImeEnabledUri, false, this,
                        UserHandle.USER_ALL);
            }
            resolver.registerContentObserver(mPolicyControlUri, false, this, UserHandle.USER_ALL);
            if (DesktopModeHelper.isDesktopExperienceDevOptionSupported(mContext)) {
                disableForceDesktopModeOnExternalDisplays();
            } else {
                resolver.registerContentObserver(mForceDesktopModeOnExternalDisplaysUri, false,
                        this,
                        UserHandle.USER_ALL);
            }
            resolver.registerContentObserver(mFreeformWindowUri, false, this, UserHandle.USER_ALL);
            resolver.registerContentObserver(mForceResizableUri, false, this, UserHandle.USER_ALL);
            resolver.registerContentObserver(mDevEnableNonResizableMultiWindowUri, false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(mDisplaySettingsPathUri, false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(mMaximumObscuringOpacityForTouchUri, false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(mDevelopmentOverrideDesktopExperienceUri, false, this,
                    UserHandle.USER_ALL);
        }

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            if (uri == null) {
                return;
            }

            if (mForceDesktopModeOnExternalDisplaysUri.equals(uri)) {
                updateForceDesktopModeOnExternalDisplays();
                return;
            }

            if (mFreeformWindowUri.equals(uri)) {
                updateFreeformWindowManagement();
                return;
            }

            if (mForceResizableUri.equals(uri)) {
                updateForceResizableTasks();
                return;
            }

            if (mDevEnableNonResizableMultiWindowUri.equals(uri)) {
                updateDevEnableNonResizableMultiWindow();
                return;
            }

            if (mDisplaySettingsPathUri.equals(uri)) {
                updateDisplaySettingsLocation();
                return;
            }

            if (mMaximumObscuringOpacityForTouchUri.equals(uri)) {
                updateMaximumObscuringOpacityForTouch();
                return;
            }

            if (mDisableSecureWindowsUri.equals(uri)) {
                updateDisableSecureWindows();
                return;
            }

            if (mMagnifyImeEnabledUri.equals(uri)) {
                updateMagnifyIme();
            }

            if (mDevelopmentOverrideDesktopExperienceUri.equals(uri)) {
                updateDevelopmentOverrideDesktopExperience();
                return;
            }

            @UpdateAnimationScaleMode
            final int mode;
            if (mWindowAnimationScaleUri.equals(uri)) {
                mode = WINDOW_ANIMATION_SCALE;
            } else if (mTransitionAnimationScaleUri.equals(uri)) {
                mode = TRANSITION_ANIMATION_SCALE;
            } else if (mAnimationDurationScaleUri.equals(uri)) {
                mode = ANIMATION_DURATION_SCALE;
            } else {
                // Ignoring unrecognized content changes
                return;
            }
            Message m = mH.obtainMessage(H.UPDATE_ANIMATION_SCALE, mode, 0);
            mH.sendMessage(m);
        }

        void loadSettings() {
            updateMaximumObscuringOpacityForTouch();
            updateDisableSecureWindows();
            updateMagnifyIme();
        }

        void updateMaximumObscuringOpacityForTouch() {
            ContentResolver resolver = mContext.getContentResolver();
            mMaximumObscuringOpacityForTouch = Settings.Global.getFloat(resolver,
                    Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
                    InputSettings.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
            if (mMaximumObscuringOpacityForTouch < 0.0f
                    || mMaximumObscuringOpacityForTouch > 1.0f) {
                mMaximumObscuringOpacityForTouch =
                        InputSettings.DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH;
            }
        }

        void disableForceDesktopModeOnExternalDisplays() {
            ContentResolver resolver = mContext.getContentResolver();
            Settings.Global.putInt(resolver,
                    DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0);
            if (mForceDesktopModeOnExternalDisplays) {
                setForceDesktopModeOnExternalDisplays(false);
            }
        }

        void updateForceDesktopModeOnExternalDisplays() {
            ContentResolver resolver = mContext.getContentResolver();
            final boolean enableForceDesktopMode = Settings.Global.getInt(resolver,
                    DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0;
            if (mForceDesktopModeOnExternalDisplays == enableForceDesktopMode) {
                return;
            }
            setForceDesktopModeOnExternalDisplays(enableForceDesktopMode);
        }

        void updateFreeformWindowManagement() {
            ContentResolver resolver = mContext.getContentResolver();
            final boolean freeformWindowManagement = mContext.getPackageManager().hasSystemFeature(
                    FEATURE_FREEFORM_WINDOW_MANAGEMENT) || Settings.Global.getInt(
                    resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;

            if (mAtmService.mSupportsFreeformWindowManagement != freeformWindowManagement) {
                mAtmService.mSupportsFreeformWindowManagement = freeformWindowManagement;
                synchronized (mGlobalLock) {
                    // Notify the root window container that the display settings value may change.
                    mRoot.onSettingsRetrieved();
                }
            }
        }

        void updateForceResizableTasks() {
            ContentResolver resolver = mContext.getContentResolver();
            final boolean forceResizable = Settings.Global.getInt(resolver,
                    DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;

            mAtmService.mForceResizableActivities = forceResizable;
        }

        void updateDevelopmentOverrideDesktopExperience() {
            ContentResolver resolver = mContext.getContentResolver();
            final int overrideDesktopMode = Settings.Global.getInt(resolver,
                    Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_EXPERIENCE_FEATURES,
                    DesktopModeFlags.ToggleOverride.OVERRIDE_UNSET.getSetting());

            SystemProperties.set(DesktopExperienceFlags.SYSTEM_PROPERTY_NAME,
                    Integer.toString(overrideDesktopMode));
        }

        void updateDevEnableNonResizableMultiWindow() {
            ContentResolver resolver = mContext.getContentResolver();
            final boolean devEnableNonResizableMultiWindow = Settings.Global.getInt(resolver,
                    DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, 0) != 0;

            mAtmService.mDevEnableNonResizableMultiWindow = devEnableNonResizableMultiWindow;
        }

        void updateDisplaySettingsLocation() {
            final ContentResolver resolver = mContext.getContentResolver();
            final String filePath = Settings.Global.getString(resolver,
                    DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH);
            synchronized (mGlobalLock) {
                mDisplayWindowSettingsProvider.setBaseSettingsFilePath(filePath);
                mRoot.forAllDisplays(display -> {
                    mDisplayWindowSettings.applySettingsToDisplayLocked(display);
                    display.reconfigureDisplayLocked();
                });
            }
        }

        void updateDisableSecureWindows() {
            if (!SystemProperties.getBoolean(SYSTEM_DEBUGGABLE, false)) {
                return;
            }

            boolean disableSecureWindows;
            try {
                disableSecureWindows = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                        Settings.Secure.DISABLE_SECURE_WINDOWS, 0) != 0;
            } catch (Settings.SettingNotFoundException e) {
                disableSecureWindows = false;
            }
            if (mDisableSecureWindows == disableSecureWindows) {
                return;
            }

            synchronized (mGlobalLock) {
                mDisableSecureWindows = disableSecureWindows;
                mRoot.refreshSecureSurfaceState();
            }
        }

        void updateMagnifyIme() {
            if (!com.android.server.accessibility.Flags.enableMagnificationMagnifyNavBarAndIme()) {
                mMagnifyIme = false;
                return;
            }

            boolean enabledMagnifyIme = Settings.Secure.getIntForUser(
                    mContext.getContentResolver(),
                    Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MAGNIFY_NAV_AND_IME,
                    AccessibilityUtils.getMagnificationMagnifyKeyboardDefaultValue(mContext),
                    mCurrentUserId) == 1;
            if (mMagnifyIme == enabledMagnifyIme) {
                return;
            }

            synchronized (mGlobalLock) {
                mMagnifyIme = enabledMagnifyIme;
            }
        }
    }

    PowerManager mPowerManager;
    PowerManagerInternal mPowerManagerInternal;

    private DeviceStateManager mDeviceStateManager;
    private DeviceStateCallback mDeviceStateCallback;
    private class DeviceStateCallback implements DeviceStateManager.DeviceStateCallback {
        private DeviceState mCurrentDeviceState;
        @Override
        public void onDeviceStateChanged(@NonNull DeviceState state) {
            mCurrentDeviceState = state;
        }

        boolean isInRearDisplayOuterDefaultState() {
            return mCurrentDeviceState != null && mCurrentDeviceState
                    .hasProperties(DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT);
        }
    }

    private static final float DEFAULT_ANIMATION_SCALE = 1.0f;

    @GuardedBy("mGlobalLock")
    private float mWindowAnimationScaleSetting = DEFAULT_ANIMATION_SCALE;
    @GuardedBy("mGlobalLock")
    private float mTransitionAnimationScaleSetting = DEFAULT_ANIMATION_SCALE;

    boolean mPointerLocationEnabled = false;

    private final SharedMemoryBackedCurrentAnimatorScale mAnimatorScale =
            new SharedMemoryBackedCurrentAnimatorScale();

    @NonNull
    final AppCompatConfiguration mAppCompatConfiguration;

    private boolean mIsIgnoreOrientationRequestDisabled;

    @NonNull
    final InputManagerService mInputManager;
    final DisplayManagerInternal mDisplayManagerInternal;
    final DisplayManager mDisplayManager;
    @NonNull
    final ActivityTaskManagerService mAtmService;

    /** Indicates whether this device supports wide color gamut / HDR rendering */
    private boolean mHasWideColorGamutSupport;
    private boolean mHasHdrSupport;

    /** Whether or not a layout can cause a wake up when theater mode is enabled. */
    boolean mAllowTheaterModeWakeFromLayout;

    final DragDropController mDragDropController;

    /** Skip repeated ActivityRecords initialization. Note that AppWindowsToken's version of this
     * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */
    int mTransactionSequence;

    final WindowAnimator mAnimator;
    SurfaceAnimationRunner mSurfaceAnimationRunner;

    /**
     * Keeps track of which animations got transferred to which animators. Entries will get cleaned
     * up when the animation finishes.
     */
    final ArrayMap<AnimationAdapter, SurfaceAnimator> mAnimationTransferMap = new ArrayMap<>();

    private WindowContentFrameStats mTempWindowRenderStats;

    final LatencyTracker mLatencyTracker;

    private ViewServer mViewServer;
    final ArrayList<WindowChangeListener> mWindowChangeListeners = new ArrayList<>();
    final ArrayList<WindowFocusChangeListener> mWindowFocusChangeListeners = new ArrayList<>();
    boolean mWindowsChanged = false;

    interface WindowChangeListener {
        /** Notify on windows changed */
        void windowsChanged();
    }

    final HighRefreshRateDenylist mHighRefreshRateDenylist;

    // Maintainer of a collection of all possible DisplayInfo for all configurations of the
    // logical displays.
    final PossibleDisplayInfoMapper mPossibleDisplayInfoMapper;

    static WindowManagerThreadPriorityBooster sThreadPriorityBooster =
            new WindowManagerThreadPriorityBooster();

    @NonNull
    Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
    @NonNull
    Supplier<SurfaceControl.Transaction> mTransactionFactory;

    private final SurfaceControl.Transaction mTransaction;

    static void boostPriorityForLockedSection() {
        sThreadPriorityBooster.boost();
    }

    static void resetPriorityAfterLockedSection() {
        sThreadPriorityBooster.reset();
    }

    SystemPerformanceHinter mSystemPerformanceHinter;

    @GuardedBy("mGlobalLock")
    private long mSensitiveContentProtectionSessionId = 0;

    @GuardedBy("mGlobalLock")
    final SensitiveContentPackages mSensitiveContentPackages = new SensitiveContentPackages();
    /**
     * UIDs for which a Toast has been shown to indicate
     * {@link LocalService#addBlockScreenCaptureForApps(ArraySet) screen capture blocking}. This is
     * used to ensure we don't keep re-showing the Toast every time the window becomes visible.
     * UIDs are removed when the app is removed from the block list.
     */
    @GuardedBy("mGlobalLock")
    private final IntArray mCaptureBlockedToastShownUids = new IntArray();

    /** Listener to notify activity manager about app transitions. */
    final WindowManagerInternal.AppTransitionListener mActivityManagerAppTransitionNotifier
            = new WindowManagerInternal.AppTransitionListener() {

        @Override
        public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled) {
        }

        @Override
        public void onAppTransitionFinishedLocked(IBinder token) {
            final ActivityRecord atoken = ActivityRecord.forTokenLocked(token);
            if (atoken == null) {
                return;
            }

            if (atoken.mLaunchTaskBehind) {
                mAtmService.mTaskSupervisor.scheduleLaunchTaskBehindComplete(atoken.token);
                atoken.mLaunchTaskBehind = false;
            } else {
                atoken.updateReportedVisibilityLocked();
                if (atoken.mEnteringAnimation) {
                    atoken.mEnteringAnimation = false;
                    if (atoken.attachedToProcess()) {
                        try {
                            atoken.app.getThread().scheduleEnterAnimationComplete(atoken.token);
                        } catch (RemoteException e) {
                        }
                    }
                }
            }
        }
    };

    private final ScreenRecordingCallbackController mScreenRecordingCallbackController;

    private volatile boolean mDisableSecureWindows = false;

    /** Creates an instance of the WindowManagerService for the system server. */
    public static WindowManagerService main(@NonNull final Context context,
            @NonNull final InputManagerService im, final boolean showBootMsgs,
            @NonNull final WindowManagerPolicy policy,
            @NonNull final ActivityTaskManagerService atm) {
        // Using SysUI context to have access to Material colors extracted from Wallpaper.
        final AppCompatConfiguration appCompat = new AppCompatConfiguration(
                ActivityThread.currentActivityThread().getSystemUiContext());

        final WindowManagerService wms = main(context, im, showBootMsgs, policy, atm,
                new DisplayWindowSettingsProvider(context), SurfaceControl.Transaction::new,
                SurfaceControl.Builder::new, appCompat);
        WindowManagerGlobal.setWindowManagerServiceForSystemProcess(wms);
        return wms;
    }

    /**
     * Creates and returns an instance of the WindowManagerService. This call allows the caller
     * to override factories that can be used to stub native calls during test. Tests should use
     * {@link WindowManagerServiceTestSupport} instead of calling this directly to ensure
     * proper initialization and cleanup of dependencies.
     */
    @VisibleForTesting(visibility = Visibility.PRIVATE)
    static WindowManagerService main(@NonNull final Context context,
            @NonNull final InputManagerService im, boolean showBootMsgs,
            @NonNull final WindowManagerPolicy policy,
            @NonNull final ActivityTaskManagerService atm,
            @NonNull final DisplayWindowSettingsProvider displayWindowSettingsProvider,
            @NonNull final Supplier<SurfaceControl.Transaction> transactionFactory,
            @NonNull final Supplier<SurfaceControl.Builder> surfaceControlFactory,
            @NonNull final AppCompatConfiguration appCompat) {

        final WindowManagerService[] wms = new WindowManagerService[1];
        DisplayThread.getHandler().runWithScissors(() ->
                wms[0] = new WindowManagerService(context, im, showBootMsgs, policy, atm,
                        displayWindowSettingsProvider, transactionFactory,
                        surfaceControlFactory, appCompat), 0);
        return wms[0];
    }

    private void initPolicy() {
        UiThread.getHandler().runWithScissors(new Runnable() {
            @Override
            public void run() {
                WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
                mPolicy.init(mContext, WindowManagerService.this);
            }
        }, 0);
    }

    @Override
    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
            String[] args, ShellCallback callback, ResultReceiver result) {
        new WindowManagerShellCommand(this).exec(this, in, out, err, args, callback, result);
    }

    private WindowManagerService(@NonNull Context context,
            @NonNull InputManagerService inputManager, boolean showBootMsgs,
            @NonNull WindowManagerPolicy policy, @NonNull ActivityTaskManagerService atm,
            @NonNull DisplayWindowSettingsProvider displayWindowSettingsProvider,
            @NonNull Supplier<SurfaceControl.Transaction> transactionFactory,
            @NonNull Supplier<SurfaceControl.Builder> surfaceControlFactory,
            @NonNull AppCompatConfiguration appCompat) {
        installLock(this, INDEX_WINDOW);
        mGlobalLock = atm.getGlobalLock();
        mAtmService = atm;
        mContext = context;
        mFlags = new WindowManagerFlags();
        mIsPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
        mAlwaysSeqId = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH)
                ? Flags.alwaysSeqIdLayoutWear() : Flags.alwaysSeqIdLayout();
        mAllowBootMessages = showBootMsgs;
        mLimitedAlphaCompositing = context.getResources().getBoolean(
                com.android.internal.R.bool.config_sf_limitedAlpha);
        mHasPermanentDpad = context.getResources().getBoolean(
                com.android.internal.R.bool.config_hasPermanentDpad);
        mDrawLockTimeoutMillis = context.getResources().getInteger(
                com.android.internal.R.integer.config_drawLockTimeoutMillis);
        mAllowAnimationsInLowPowerMode = context.getResources().getBoolean(
                com.android.internal.R.bool.config_allowAnimationsInLowPowerMode);
        mMaxUiWidth = context.getResources().getInteger(
                com.android.internal.R.integer.config_maxUiWidth);
        mSupportsHighPerfTransitions = context.getResources().getBoolean(
                com.android.internal.R.bool.config_deviceSupportsHighPerfTransitions);
        mDisableTransitionAnimation = context.getResources().getBoolean(
                com.android.internal.R.bool.config_disableTransitionAnimation);
        mPerDisplayFocusEnabled = context.getResources().getBoolean(
                com.android.internal.R.bool.config_perDisplayFocusEnabled);
        mAssistantOnTopOfDream = context.getResources().getBoolean(
                com.android.internal.R.bool.config_assistantOnTopOfDream);
        mSkipActivityRelaunchWhenDocking = context.getResources()
                .getBoolean(R.bool.config_skipActivityRelaunchWhenDocking);

        mAppCompatConfiguration = appCompat;

        mInputManager = inputManager; // Must be before createDisplayContentLocked.
        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
        mPossibleDisplayInfoMapper = new PossibleDisplayInfoMapper(mDisplayManagerInternal);

        mSurfaceControlFactory = surfaceControlFactory;
        mTransactionFactory = transactionFactory;
        mTransaction = mTransactionFactory.get();

        mPolicy = policy;
        mAnimator = new WindowAnimator(this);
        mRoot = new RootWindowContainer(this);

        final ContentResolver resolver = context.getContentResolver();

        mSyncEngine = new BLASTSyncEngine(this);

        mWindowPlacerLocked = new WindowSurfacePlacer(this);
        mSnapshotController = new SnapshotController(this);
        mTaskSnapshotController = mSnapshotController.mTaskSnapshotController;

        mWindowTracing = WindowTracing.createDefaultAndStartLooper(this,
                Choreographer.getInstance());

        mTransitionTracer = new PerfettoTransitionTracer();

        LocalServices.addService(WindowManagerPolicy.class, mPolicy);

        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);

        mKeyguardDisableHandler = KeyguardDisableHandler.create(mContext, mPolicy, mH);

        mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);

        mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
        mDeviceStateCallback = new DeviceStateCallback();
        mDeviceStateManager.registerCallback(new HandlerExecutor(mH), mDeviceStateCallback);

        if (mPowerManagerInternal != null) {
            mPowerManagerInternal.registerLowPowerModeObserver(
                    new PowerManagerInternal.LowPowerModeListener() {
                @Override
                public int getServiceType() {
                    return ServiceType.ANIMATION;
                }

                @Override
                public void onLowPowerModeChanged(PowerSaveState result) {
                    synchronized (mGlobalLock) {
                        final boolean enabled = result.batterySaverEnabled;
                        final boolean animationsDisabled = mAnimatorScale.isAnimationsDisabled();
                        if (animationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) {
                            mAnimatorScale.setAnimationsDisabled(enabled);
                            dispatchNewAnimatorScaleLocked(null);
                        }
                    }
                }
            });
            mAnimatorScale.setAnimationsDisabled(mPowerManagerInternal
                    .getLowPowerState(ServiceType.ANIMATION).batterySaverEnabled);
        }

        mRotationWatcherController = new RotationWatcherController(this);
        mDisplayNotificationController = new DisplayWindowListenerController(this);
        mTaskSystemBarsListenerController = new TaskSystemBarsListenerController();

        mActivityManager = ActivityManager.getService();
        mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
        mUmInternal = LocalServices.getService(UserManagerInternal.class);
        mPermissionManager = context.getSystemService(PermissionManager.class);
        mAppOps = context.getSystemService(AppOpsManager.class);
        mAppOps.startWatchingMode(OP_SYSTEM_ALERT_WINDOW, null,
                new AppOpsManager.OnOpChangedInternalListener() {
                    @Override
                    public void onOpChanged(int op, String packageName) {
                        updateAppOpsState();
                    }
                });
        mAppOps.startWatchingMode(AppOpsManager.OP_TOAST_WINDOW, null,
                new AppOpsManager.OnOpChangedInternalListener() {
                    @Override
                    public void onOpChanged(int op, String packageName) {
                        updateAppOpsState();
                    }
                });
        mAppOps.startWatchingMode(AppOpsManager.OPSTR_SYSTEM_APPLICATION_OVERLAY, null,
                new AppOpsManager.OnOpChangedInternalListener() {
                    @Override
                    public void onOpChanged(int op, String packageName) {
                        if (op == AppOpsManager.OP_SYSTEM_APPLICATION_OVERLAY) {
                            synchronized (mGlobalLock) {
                                for (Session session : mSessions) {
                                    session.updateCanCreateSystemApplicationOverlay(
                                            mPermissionManager);
                                }
                            }
                        }
                    }
                });

        mPmInternal = LocalServices.getService(PackageManagerInternal.class);
        mTestUtilityService = LocalServices.getService(TestUtilityService.class);
        final IntentFilter suspendPackagesFilter = new IntentFilter();
        suspendPackagesFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
        suspendPackagesFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
        context.registerReceiverAsUser(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                final String[] affectedPackages =
                        intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                final boolean suspended =
                        Intent.ACTION_PACKAGES_SUSPENDED.equals(intent.getAction());
                updateHiddenWhileSuspendedState(new ArraySet<>(Arrays.asList(affectedPackages)),
                        suspended);
            }
        }, UserHandle.ALL, suspendPackagesFilter, null, null);

        // Get persisted window scale setting
        mWindowAnimationScaleSetting = getWindowAnimationScaleSetting(DEFAULT_ANIMATION_SCALE);
        mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting();

        setAnimatorDurationScale(getAnimatorDurationScaleSetting());

        mForceDesktopModeOnExternalDisplays = Settings.Global.getInt(resolver,
                DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0;

        final String displaySettingsPath = Settings.Global.getString(resolver,
                DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH);
        mDisplayWindowSettingsProvider = displayWindowSettingsProvider;
        if (displaySettingsPath != null) {
            mDisplayWindowSettingsProvider.setBaseSettingsFilePath(displaySettingsPath);
        }
        mDisplayWindowSettings = new DisplayWindowSettings(this, mDisplayWindowSettingsProvider);

        IntentFilter filter = new IntentFilter();
        // Track changes to DevicePolicyManager state so we can enable/disable keyguard.
        filter.addAction(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
        mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);

        mLatencyTracker = LatencyTracker.getInstance(context);

        mSettingsObserver = new SettingsObserver();

        mSurfaceAnimationRunner = new SurfaceAnimationRunner(mTransactionFactory,
                mPowerManagerInternal);

        mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
                com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);

        mDragDropController = new DragDropController(this, mH.getLooper());

        mHighRefreshRateDenylist = HighRefreshRateDenylist.create(context.getResources());

        mConstants = new WindowManagerConstants(this, DeviceConfigInterface.REAL);
        mConstants.start(new HandlerExecutor(mH));

        LocalServices.addService(WindowManagerInternal.class, new LocalService());
        mEmbeddedWindowController = new EmbeddedWindowController(mAtmService, inputManager);

        mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
                mContext.getResources());

        mDisplayHashController = new DisplayHashController(mContext);
        setGlobalShadowSettings();
        mAnrController = new AnrController(this);
        mStartingSurfaceController = new StartingSurfaceController(this);
        mPresentationController = new PresentationController();

        mBlurController = new BlurController(mContext, mPowerManager);
        mTaskFpsCallbackController = new TaskFpsCallbackController();
        mAccessibilityController = new AccessibilityController(this);
        mScreenRecordingCallbackController = new ScreenRecordingCallbackController(this);
        mSystemPerformanceHinter = new SystemPerformanceHinter(mContext, displayId -> {
            synchronized (mGlobalLock) {
                DisplayContent dc = mRoot.getDisplayContent(displayId);
                return (dc == null) ? null : dc.getSurfaceControl();
            }
        }, mTransactionFactory);
        mSystemPerformanceHinter.mTraceTag = TRACE_TAG_WINDOW_MANAGER;

        LocalServices.addService(
                ConfigurationChangeSetting.ConfigurationChangeSettingInternal.class,
                new ConfigurationChangeSettingInternalImpl());
    }

    boolean isMagnifyImeEnabled() {
        return mMagnifyIme;
    }

    boolean isMagnifyNavBarEnabled() {
        return mMagnifyIme && mIsMouseOrKeyboardConnected;
    }

    DisplayAreaPolicy.Provider getDisplayAreaPolicyProvider() {
        return mDisplayAreaPolicyProvider;
    }

    private void setGlobalShadowSettings() {
        final TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
        float lightY = a.getDimension(R.styleable.Lighting_lightY, 0);
        float lightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
        float lightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
        float ambientShadowAlpha = a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0);
        float spotShadowAlpha = a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0);
        a.recycle();
        float[] ambientColor = {0.f, 0.f, 0.f, ambientShadowAlpha};
        float[] spotColor = {0.f, 0.f, 0.f, spotShadowAlpha};
        SurfaceControl.setGlobalShadowSettings(ambientColor, spotColor, lightY, lightZ,
                lightRadius);
    }

    private float getTransitionAnimationScaleSetting() {
        return fixScale(Settings.Global.getFloat(mContext.getContentResolver(),
                Settings.Global.TRANSITION_ANIMATION_SCALE, mContext.getResources().getFloat(
                                R.dimen.config_appTransitionAnimationDurationScaleDefault)));
    }

    private float getAnimatorDurationScaleSetting() {
        return fixScale(Settings.Global.getFloat(mContext.getContentResolver(),
                Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorScale.getCurrentScale()));
    }

    private float getWindowAnimationScaleSetting(float defaultValue) {
        return fixScale(Settings.Global.getFloat(mContext.getContentResolver(),
                Settings.Global.WINDOW_ANIMATION_SCALE, defaultValue));
    }

    /**
     * Called after all entities (such as the {@link ActivityManagerService}) have been set up and
     * associated with the {@link WindowManagerService}.
     */
    public void onInitReady() {
        initPolicy();

        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this);
        createWatermark();
        showEmulatorDisplayOverlayIfNeeded();
    }

    public InputManagerCallback getInputManagerCallback() {
        return mInputManagerCallback;
    }

    void onInputDevicesChanged() {
        boolean hasMouseOrKeyboard = false;
        for (final InputDevice device : mInputManager.getInputDevices()) {
            if (!device.isEnabled() || device.isVirtual()) {
                continue;
            }
            if (device.supportsSource(InputDevice.SOURCE_MOUSE) || device.isFullKeyboard()) {
                hasMouseOrKeyboard = true;
                break;
            }
        }
        synchronized (mGlobalLock) {
            if (mIsMouseOrKeyboardConnected != hasMouseOrKeyboard) {
                mIsMouseOrKeyboardConnected = hasMouseOrKeyboard;
                var t = mTransactionFactory.get();
                mRoot.forAllDisplays(dc -> dc.reapplyMagnificationSpec(t));
                t.apply();
            }
        }
    }

    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        try {
            return super.onTransact(code, data, reply, flags);
        } catch (RuntimeException e) {
            // The window manager only throws security exceptions, so let's
            // log all others.
            if (!(e instanceof SecurityException)) {
                ProtoLog.wtf(WM_ERROR, "Window Manager Crash %s", e);
            }
            throw e;
        }
    }

    public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
            int displayId, int requestUserId, @InsetsType int requestedVisibleTypes,
            InputChannel outInputChannel, WindowRelayoutResult result) {
        result.activeControls.set(null, false /* copyControls */);
        int[] appOp = new int[1];
        final boolean isRoundedCornerOverlay = (attrs.privateFlags
                & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
        int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
                appOp, displayId);
        if (res != ADD_OKAY) {
            return res;
        }

        WindowState parentWindow = null;
        final int callingUid = Binder.getCallingUid();
        final int callingPid = Binder.getCallingPid();
        final long origId = Binder.clearCallingIdentity();
        final int type = attrs.type;

        synchronized (mGlobalLock) {
            if (!mDisplayReady) {
                throw new IllegalStateException("Display has not been initialialized");
            }
            if (session.isClientDead()) {
                ProtoLog.w(WM_ERROR, "Attempted to add window with a client %s "
                        + "that is dead. Aborting.", session);
                return WindowManagerGlobal.ADD_APP_EXITING;
            }

            if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
                parentWindow = windowForClient(null /* session */, attrs.token);
                if (parentWindow == null) {
                    ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: "
                            + "%s.  Aborting.", attrs.token);
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
                if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
                        && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
                    ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: "
                            + "%s.  Aborting.", attrs.token);
                    return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
                }
            }
            // Update whether the session should be able to add system application overlays for any
            // attempt to add a system application overlay.
            if ((attrs.privateFlags & PRIVATE_FLAG_SYSTEM_APPLICATION_OVERLAY) != 0) {
                session.updateCanCreateSystemApplicationOverlay(mPermissionManager);
            }

            final DisplayContent displayContent = parentWindow != null
                    ? parentWindow.mDisplayContent
                    : getDisplayContentOrCreate(displayId, attrs.token);

            if (displayContent == null) {
                ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does "
                        + "not exist: %d. Aborting.", displayId);
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
            if (!displayContent.hasAccess(session.mUid)) {
                ProtoLog.w(WM_ERROR,
                        "Attempted to add window to a display for which the application "
                                + "does not have access: %d.  Aborting.",
                        displayContent.getDisplayId());
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }

            if (mWindowMap.containsKey(client.asBinder())) {
                ProtoLog.w(WM_ERROR, "Window %s is already added", client);
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            }

            if (type == TYPE_PRIVATE_PRESENTATION
                    && !mPresentationController.canPresent(null /*win*/, displayContent, type,
                    callingUid)) {
                ProtoLog.w(WM_ERROR,
                        "Attempted to add private presentation window to a non-private display.  "
                                + "Aborting.");
                return WindowManagerGlobal.ADD_PERMISSION_DENIED;
            }

            if (type == TYPE_PRESENTATION
                    && !mPresentationController.canPresent(null /*win*/, displayContent, type,
                    callingUid)) {
                ProtoLog.w(WM_ERROR,
                        "Attempted to add presentation window to a non-suitable display.  "
                                + "Aborting.");
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }

            int userId = UserHandle.getUserId(session.mUid);
            if (requestUserId != userId) {
                try {
                    mAmInternal.handleIncomingUser(callingPid, callingUid, requestUserId,
                            false /*allowAll*/, ALLOW_NON_FULL, null, null);
                } catch (Exception exp) {
                    ProtoLog.w(WM_ERROR, "Trying to add window with invalid user=%d",
                            requestUserId);
                    return WindowManagerGlobal.ADD_INVALID_USER;
                }
                // It's fine to use this userId
                userId = requestUserId;
            }

            ActivityRecord activity = null;
            final boolean hasParent = parentWindow != null;
            // Use existing parent window token for child windows since they go in the same token
            // as there parent window so we can apply the same policy on them.
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
            // If this is a child window, we want to apply the same type checking rules as the
            // parent window type.
            final int rootType = hasParent ? parentWindow.mAttrs.type : type;

            boolean addToastWindowRequiresToken = false;

            final IBinder windowContextToken = attrs.mWindowContextToken;

            if (token == null) {
                if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,
                        rootType, attrs.token, attrs.packageName)) {
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                if (hasParent) {
                    // Use existing parent window token for child windows.
                    token = parentWindow.mToken;
                } else if (mWindowContextListenerController.hasListener(windowContextToken)) {
                    // Respect the window context token if the user provided it.
                    final IBinder binder = attrs.token != null ? attrs.token : windowContextToken;
                    final Bundle options = mWindowContextListenerController
                            .getOptions(windowContextToken);
                    token = new WindowToken.Builder(this, binder, type)
                            .setDisplayContent(displayContent)
                            .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                            .setRoundedCornerOverlay(isRoundedCornerOverlay)
                            .setFromClientToken(true)
                            .setOptions(options)
                            .build();
                } else {
                    final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                    token = new WindowToken.Builder(this, binder, type)
                            .setDisplayContent(displayContent)
                            .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                            .setRoundedCornerOverlay(isRoundedCornerOverlay)
                            .build();
                }
            } else if (rootType >= FIRST_APPLICATION_WINDOW
                    && rootType <= LAST_APPLICATION_WINDOW) {
                activity = token.asActivityRecord();
                if (activity == null) {
                    ProtoLog.w(WM_ERROR, "Attempted to add window with non-application token "
                            + ".%s Aborting.", token);
                    return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
                } else if (activity.getParent() == null) {
                    ProtoLog.w(WM_ERROR, "Attempted to add window with exiting application token "
                            + ".%s Aborting.", token);
                    return WindowManagerGlobal.ADD_APP_EXITING;
                } else if (type == TYPE_APPLICATION_STARTING) {
                    if (activity.mStartingWindow != null) {
                        ProtoLog.w(WM_ERROR, "Attempted to add starting window to "
                                + "token with already existing starting window");
                        return WindowManagerGlobal.ADD_DUPLICATE_ADD;
                    }
                    if (activity.mStartingData == null) {
                        ProtoLog.w(WM_ERROR, "Attempted to add starting window to "
                                + "token but already cleaned");
                        return WindowManagerGlobal.ADD_DUPLICATE_ADD;
                    }
                }
            } else if (rootType == TYPE_INPUT_METHOD) {
                if (token.windowType != TYPE_INPUT_METHOD) {
                    ProtoLog.w(WM_ERROR, "Attempted to add input method window with bad token "
                            + "%s.  Aborting.", attrs.token);
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (rootType == TYPE_VOICE_INTERACTION) {
                if (token.windowType != TYPE_VOICE_INTERACTION) {
                    ProtoLog.w(WM_ERROR, "Attempted to add voice interaction window with bad token "
                            + "%s.  Aborting.", attrs.token);
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (rootType == TYPE_WALLPAPER) {
                if (token.windowType != TYPE_WALLPAPER) {
                    ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with bad token "
                            + "%s.  Aborting.", attrs.token);
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
                if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
                    ProtoLog.w(WM_ERROR,
                            "Attempted to add Accessibility overlay window with bad token "
                                    + "%s.  Aborting.", attrs.token);
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (type == TYPE_TOAST) {
                // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
                addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
                        callingUid, parentWindow);
                if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {
                    ProtoLog.w(WM_ERROR, "Attempted to add a toast window with bad token "
                            + "%s.  Aborting.", attrs.token);
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (type == TYPE_QS_DIALOG) {
                if (token.windowType != TYPE_QS_DIALOG) {
                    ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with bad token "
                            + "%s.  Aborting.", attrs.token);
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } else if (token.asActivityRecord() != null) {
                ProtoLog.w(WM_ERROR, "Non-null activity for system window of rootType=%d",
                        rootType);
                // It is not valid to use an app token with other system types; we will
                // instead make a new token for it (as if null had been passed in for the token).
                attrs.token = null;
                token = new WindowToken.Builder(this, client.asBinder(), type)
                        .setDisplayContent(displayContent)
                        .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                        .build();
            }

            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], attrs, viewVisibility, session.mUid, userId,
                    session.mCanAddInternalSystemWindow);
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
            displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
            attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), callingUid, callingPid);
            attrs.inputFeatures = sanitizeInputFeatures(attrs.inputFeatures, win.getName(),
                    callingUid, callingPid, win.isTrustedOverlay());
            win.setRequestedVisibleTypes(requestedVisibleTypes);

            res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
            if (res != ADD_OKAY) {
                return res;
            }

            final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
            if  (openInputChannels) {
                win.openInputChannel(outInputChannel);
            }

            // If adding a toast requires a token for this app we always schedule hiding
            // toast windows to make sure they don't stick around longer then necessary.
            // We hide instead of remove such windows as apps aren't prepared to handle
            // windows being removed under them.
            //
            // If the app is older it can add toasts without a token and hence overlay
            // other apps. To be maximally compatible with these apps we will hide the
            // window after the toast timeout only if the focused window is from another
            // UID, otherwise we allow unlimited duration. When a UID looses focus we
            // schedule hiding all of its toast windows.
            if (type == TYPE_TOAST) {
                if (!displayContent.canAddToastWindowForUid(callingUid)) {
                    ProtoLog.w(WM_ERROR, "Adding more than one toast window for UID at a time.");
                    return WindowManagerGlobal.ADD_DUPLICATE_ADD;
                }
                // Make sure this happens before we moved focus as one can make the
                // toast focusable to force it not being hidden after the timeout.
                // Focusable toasts are always timed out to prevent a focused app to
                // show a focusable toasts while it has focus which will be kept on
                // the screen after the activity goes away.
                if (addToastWindowRequiresToken
                        || (attrs.flags & FLAG_NOT_FOCUSABLE) == 0
                        || displayContent.mCurrentFocus == null
                        || displayContent.mCurrentFocus.mOwnerUid != callingUid) {
                    mH.sendMessageDelayed(
                            mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
                            win.mAttrs.hideTimeoutMilliseconds);
                }
            }

            // Switch to listen to the {@link WindowToken token}'s configuration changes when
            // adding a window to the window context. Filter sub window type here because the sub
            // window must be attached to the parent window, which is attached to the window context
            // created window token.
            if (!win.isChildWindow()
                    && mWindowContextListenerController.hasListener(windowContextToken)) {
                final int windowContextType = mWindowContextListenerController
                        .getWindowType(windowContextToken);
                final Bundle options = mWindowContextListenerController
                        .getOptions(windowContextToken);
                if (type != windowContextType) {
                    ProtoLog.w(WM_ERROR, "Window types in WindowContext and"
                            + " LayoutParams.type should match! Type from LayoutParams is %d,"
                            + " but type from WindowContext is %d", type, windowContextType);
                    // We allow WindowProviderService to add window other than windowContextType,
                    // but the WindowProviderService won't be associated with the window's
                    // WindowToken.
                    if (!isWindowProviderService(options)) {
                        return WindowManagerGlobal.ADD_INVALID_TYPE;
                    }
                } else {
                    mWindowContextListenerController.updateContainerForWindowContextListener(
                            windowContextToken, token);
                }
            }

            // From now on, no exceptions or errors allowed!
            if (displayContent.mCurrentFocus == null) {
                displayContent.mWinAddedSinceNullFocus.add(win);
            }

            win.mSession.onWindowAdded(win);
            mWindowMap.put(client.asBinder(), win);
            win.initAppOpsState();

            final boolean suspended = mPmInternal.isPackageSuspended(win.getOwningPackage(),
                    UserHandle.getUserId(win.getOwningUid()));
            win.setHiddenWhileSuspended(suspended);

            final boolean hideSystemAlertWindows = shouldHideNonSystemOverlayWindow(win);
            win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);

            // 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() && win.isPresentation()) {
                final ActionChain chain = mAtmService.mChainTracker.startTransit("addPresoWin");
                final boolean wasTransitionOnDisplay = chain.isCollectingOnDisplay(displayContent);
                Transition newlyCreatedTransition = null;
                if (!chain.isCollecting()) {
                    chain.attachTransition(
                            win.mTransitionController.createAndStartCollecting(TRANSIT_OPEN));
                    newlyCreatedTransition = chain.getTransition();
                }
                chain.collect(win.mToken);
                res |= addWindowInner(win, displayPolicy, activity, displayContent, client, attrs,
                        callingUid, result);
                // A presentation hides all activities behind on the same display.
                win.mDisplayContent.ensureActivitiesVisible(/*starting=*/ null,
                        /*notifyClients=*/ true);
                if (!wasTransitionOnDisplay && chain.isCollectingOnDisplay(displayContent)) {
                    // Set the display ready only when the display gets added to the collecting
                    // transition in this operation.
                    win.mTransitionController.setReady(win.mToken);
                }
                mAtmService.mChainTracker.end();
                if (newlyCreatedTransition != null) {
                    win.mTransitionController.requestStartTransition(newlyCreatedTransition, null,
                            null /* remoteTransition */, null /* displayChange */);
                }
            } else {
                res |= addWindowInner(win, displayPolicy, activity, displayContent, client, attrs,
                        callingUid, result);
            }
        }

        Binder.restoreCallingIdentity(origId);

        return res;
    }

    private int addWindowInner(@NonNull WindowState win, @NonNull DisplayPolicy displayPolicy,
            @NonNull ActivityRecord activity, @NonNull DisplayContent displayContent,
            @NonNull IWindow client, @NonNull LayoutParams attrs, int uid,
            @NonNull WindowRelayoutResult result) {
        int res = 0;
        final int type = attrs.type;
        boolean imMayMove = true;

        win.mToken.addWindow(win);
        displayPolicy.addWindowLw(win, attrs);
        displayPolicy.setDropInputModePolicy(win, win.mAttrs);
        if (type == TYPE_APPLICATION_STARTING && activity != null) {
            activity.attachStartingWindow(win);
            ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
                    activity, win);
        } else if (type == TYPE_INPUT_METHOD
                // IME window is always touchable.
                // Ignore non-touchable windows e.g. Stylus InkWindow.java.
                && (win.mAttrs.flags & FLAG_NOT_TOUCHABLE) == 0) {
            displayContent.setInputMethodWindowLocked(win);
            imMayMove = false;
        } else if (type == TYPE_INPUT_METHOD_DIALOG) {
            displayContent.computeImeLayeringTarget(true /* update */);
            imMayMove = false;
        } else {
            if (type == TYPE_WALLPAPER) {
                displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
                displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
            } else if (win.hasWallpaper()) {
                displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
            } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
                // If there is currently a wallpaper being shown, and
                // the base layer of the new window is below the current
                // layer of the target window, then adjust the wallpaper.
                // This is to avoid a new window being placed between the
                // wallpaper and its target.
                displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
            }
        }

        final WindowStateAnimator winAnimator = win.mWinAnimator;
        winAnimator.mEnterAnimationPending = true;
        winAnimator.mEnteringAnimation = true;

        if (displayContent.isInTouchMode()) {
            res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
        }
        if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) {
            res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
        }

        displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();

        boolean focusChanged = false;
        if (win.canReceiveKeys()) {
            focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                    false /*updateInputWindows*/);
            if (focusChanged) {
                imMayMove = false;
            }
        }

        if (imMayMove) {
            displayContent.computeImeLayeringTarget(true /* update */);
        }

        // Don't do layout here, the window must call
        // relayout to be displayed, so we'll do it there.
        if (win.mActivityRecord != null && win.mActivityRecord.isEmbedded()) {
            // Assign child layers from the parent Task if the Activity is embedded.
            win.getTask().assignChildLayers();
        } else {
            win.getParent().assignChildLayers();
        }

        if (focusChanged) {
            displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
                    false /*updateInputWindows*/);
        }
        displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);

        ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"
                + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));

        boolean needToSendNewConfiguration =
                win.isVisibleRequestedOrAdding() && displayContent.updateOrientation();
        if (win.providesDisplayDecorInsets()) {
            needToSendNewConfiguration |= displayPolicy.updateDecorInsetsInfo();
        }
        if (needToSendNewConfiguration) {
            displayContent.sendNewConfiguration();
        }

        // This window doesn't have a frame yet. Don't let this window cause the insets change.
        displayContent.getInsetsStateController().updateAboveInsetsState(
                false /* notifyInsetsChanged */);

        win.fillInsetsState(result.insetsState, true /* copySources */);
        getInsetsSourceControls(win, result.activeControls);

        if (win.mLayoutAttached) {
            result.frames.attachedFrame = new Rect(win.getParentWindow().getFrame());
            if (win.mInvGlobalScale != 1f) {
                result.frames.attachedFrame.scale(win.mInvGlobalScale);
            }
        } else {
            result.frames.attachedFrame = null;
        }
        result.frames.compatScale = win.getCompatScaleForClient();
        result.syncSeqId = win.incrementSeqForRelayout();
        if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
            Trace.instant(TRACE_TAG_WINDOW_MANAGER, "wm.addWindow_" + win.getWindowTag()
                    + " id=" + result.syncSeqId);
        }

        if (res >= ADD_OKAY && win.isPresentation()) {
            mPresentationController.onPresentationAdded(win, uid);
        }

        return res;
    }

    private boolean unprivilegedAppCanCreateTokenWith(WindowState parentWindow,
            int callingUid, int type, int rootType, IBinder tokenForLog, String packageName) {
        if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
            ProtoLog.w(WM_ERROR, "Attempted to add application window with unknown token "
                    + "%s.  Aborting.", tokenForLog);
            return false;
        }
        if (rootType == TYPE_INPUT_METHOD) {
            ProtoLog.w(WM_ERROR, "Attempted to add input method window with unknown token "
                    + "%s.  Aborting.", tokenForLog);
            return false;
        }
        if (rootType == TYPE_VOICE_INTERACTION) {
            ProtoLog.w(WM_ERROR,
                    "Attempted to add voice interaction window with unknown token "
                            + "%s.  Aborting.", tokenForLog);
            return false;
        }
        if (rootType == TYPE_WALLPAPER) {
            ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with unknown token "
                    + "%s.  Aborting.", tokenForLog);
            return false;
        }
        if (rootType == TYPE_QS_DIALOG) {
            ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with unknown token "
                    + "%s.  Aborting.", tokenForLog);
            return false;
        }
        if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
            ProtoLog.w(WM_ERROR,
                    "Attempted to add Accessibility overlay window with unknown token "
                            + "%s.  Aborting.", tokenForLog);
            return false;
        }
        if (type == TYPE_TOAST) {
            // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
            if (doesAddToastWindowRequireToken(packageName, callingUid, parentWindow)) {
                ProtoLog.w(WM_ERROR, "Attempted to add a toast window with unknown token "
                        + "%s.  Aborting.", tokenForLog);
                return false;
            }
        }
        return true;
    }

    /**
     * Get existing {@link DisplayContent} or create a new one if the display is registered in
     * DisplayManager.
     *
     * NOTE: This should only be used in cases when there is a chance that a {@link DisplayContent}
     * that corresponds to a display just added to DisplayManager has not yet been created. This
     * usually means that the call of this method was initiated from outside of Activity or Window
     * Manager. In most cases the regular getter should be used.
     * @param displayId The preferred display Id.
     * @param token The window token associated with the window we are trying to get display for.
     *              if not null then the display of the window token will be returned. Set to null
     *              is there isn't an a token associated with the request.
     * @see RootWindowContainer#getDisplayContent(int)
     */
    private DisplayContent getDisplayContentOrCreate(int displayId, IBinder token) {
        if (token != null) {
            final WindowToken wToken = mRoot.getWindowToken(token);
            if (wToken != null) {
                return wToken.getDisplayContent();
            }
        }

        return mRoot.getDisplayContentOrCreate(displayId);
    }

    private boolean doesAddToastWindowRequireToken(String packageName, int callingUid,
            WindowState attachedWindow) {
        // Try using the target SDK of the root window
        if (attachedWindow != null) {
            return attachedWindow.mActivityRecord != null
                    && attachedWindow.mActivityRecord.mTargetSdk >= Build.VERSION_CODES.O;
        } else {
            // Otherwise, look at the package
            final ApplicationInfo appInfo = mPmInternal.getApplicationInfo(
                    packageName, 0 /* flags */, SYSTEM_UID, UserHandle.getUserId(callingUid));
            if (appInfo == null
                    || !mPmInternal.isSameApp(
                            packageName, callingUid, UserHandle.getUserId(callingUid))) {
                throw new SecurityException("Package " + packageName + " not in UID "
                        + callingUid);
            }
            return appInfo.targetSdkVersion >= Build.VERSION_CODES.O;
        }
    }

    private boolean shouldHideNonSystemOverlayWindow(WindowState win) {
        if (mHidingNonSystemOverlayWindows.isEmpty()) {
            return false;
        }

        if (mHidingNonSystemOverlayWindowsCountPerUid.size() == 1
                && mHidingNonSystemOverlayWindowsCountPerUid.containsKey(win.getOwningUid())) {
            return false;
        }
        return true;
    }

    /**
     * Set whether screen capture is disabled for all windows of a specific user from
     * the device policy cache, or specific windows based on sensitive content protections.
     */
    @Override
    public void refreshScreenCaptureDisabled() {
        int callingUid = Binder.getCallingUid();
        // MY_UID (Process.myUid()) should always be SYSTEM_UID here, but using MY_UID for tests
        if (callingUid != MY_UID) {
            throw new SecurityException("Only system can call refreshScreenCaptureDisabled.");
        }

        synchronized (mGlobalLock) {
            // Refresh secure surface for all windows.
            mRoot.refreshSecureSurfaceState();
        }
    }

    void removeClientToken(Session session, IBinder client) {
        synchronized (mGlobalLock) {
            final WindowState win = windowForClient(session, client);
            if (win != null) {
                win.removeIfPossible();
                return;
            }

            // Remove embedded window map if the token belongs to an embedded window
            mEmbeddedWindowController.remove(client);
        }
    }

    /**
     * Performs some centralized bookkeeping clean-up on the window that is being removed.
     * NOTE: Should only be called from {@link WindowState#removeImmediately()}
     * TODO: Maybe better handled with a method {@link WindowContainer#removeChild} if we can
     * figure-out a good way to have all parents of a WindowState doing the same thing without
     * forgetting to add the wiring when a new parent of WindowState is added.
     */
    void postWindowRemoveCleanupLocked(WindowState win) {
        ProtoLog.v(WM_DEBUG_ADD_REMOVE, "postWindowRemoveCleanupLocked: %s", win);
        final IBinder client = win.mClient.asBinder();
        mWindowMap.remove(client);
        if (sensitiveContentAppProtection()) {
            notifyWindowRemovedListeners(client);
        }

        final DisplayContent dc = win.getDisplayContent();

        win.resetAppOpsState();

        if (dc.mCurrentFocus == null) {
            dc.mWinRemovedSinceNullFocus.add(win);
        }
        mEmbeddedWindowController.onWindowRemoved(win);
        mResizingWindows.remove(win);
        updateNonSystemOverlayWindowsVisibilityIfNeeded(win, false /* surfaceShown */);
        mWindowsChanged = true;
        ProtoLog.v(WM_DEBUG_WINDOW_MOVEMENT, "Final remove of window: %s", win);

        final DisplayContent displayContent = win.getDisplayContent();
        if (displayContent.mInputMethodWindow == win) {
            displayContent.setInputMethodWindowLocked(null);
        }

        final WindowToken token = win.mToken;
        ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Removing %s from %s", win, token);
        // Window will already be removed from token before this post clean-up method is called.
        if (token.isEmpty() && !token.mPersistOnEmpty) {
            token.removeIfPossible();
        }

        if (win.mActivityRecord != null) {
            win.mActivityRecord.postWindowRemoveStartingWindowCleanup(win);
        }

        if (win.mAttrs.type == TYPE_WALLPAPER) {
            dc.mWallpaperController.clearLastWallpaperTimeoutTime();
            dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
        } else if (dc.mWallpaperController.isWallpaperTarget(win)) {
            dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
        }

        if (dc != null && !mWindowPlacerLocked.isInLayout()) {
            dc.assignWindowLayers(true /* setLayoutNeeded */);
            mWindowPlacerLocked.performSurfacePlacement();
            if (win.mActivityRecord != null) {
                win.mActivityRecord.updateReportedVisibilityLocked();
            }
        }

        dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
    }

    private void updateHiddenWhileSuspendedState(ArraySet<String> packages, boolean suspended) {
        synchronized (mGlobalLock) {
            mRoot.updateHiddenWhileSuspendedState(packages, suspended);
        }
    }

    private void updateAppOpsState() {
        synchronized (mGlobalLock) {
            mRoot.updateAppOpsState();
        }
    }

    static void logSurface(WindowState w, String msg, boolean withStackTrace) {
        String str = "  SURFACE " + msg + ": " + w;
        if (withStackTrace) {
            logWithStack(TAG, str);
        } else {
            Slog.i(TAG_WM, str);
        }
    }

    static void logWithStack(String tag, String s) {
        RuntimeException e = null;
        if (SHOW_STACK_CRAWLS) {
            e = new RuntimeException();
        }
        Slog.i(tag, s, e);
    }

    void clearTouchableRegion(Session session, IWindow client) {
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final WindowState w = windowForClient(session, client);
                if (w != null) {
                    w.clearClientTouchableRegion();
                }
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    void setInsetsWindow(Session session, IWindow client, int touchableInsets, Rect contentInsets,
            Rect visibleInsets, Region touchableRegion) {
        int uid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final WindowState w = windowForClient(session, client);
                if (DEBUG_LAYOUT) Slog.d(TAG, "setInsetsWindow " + w
                        + ", contentInsets=" + w.mGivenContentInsets + " -> " + contentInsets
                        + ", visibleInsets=" + w.mGivenVisibleInsets + " -> " + visibleInsets
                        + ", touchableRegion=" + w.mGivenTouchableRegion + " -> " + touchableRegion
                        + ", touchableInsets " + w.mTouchableInsets + " -> " + touchableInsets);
                if (w != null) {
                    final boolean wasGivenInsetsPending = w.mGivenInsetsPending;
                    w.mGivenInsetsPending = false;
                    if ((!wasGivenInsetsPending || !w.hasInsetsSourceProvider())
                            && w.mTouchableInsets == touchableInsets
                            && w.mGivenContentInsets.equals(contentInsets)
                            && w.mGivenVisibleInsets.equals(visibleInsets)
                            && w.mGivenTouchableRegion.equals(touchableRegion)) {
                        return;
                    }
                    w.mGivenContentInsets.set(contentInsets);
                    w.mGivenVisibleInsets.set(visibleInsets);
                    w.mGivenTouchableRegion.set(touchableRegion);
                    w.mTouchableInsets = touchableInsets;
                    if (w.mGlobalScale != 1) {
                        w.mGivenContentInsets.scale(w.mGlobalScale);
                        w.mGivenVisibleInsets.scale(w.mGlobalScale);
                        w.mGivenTouchableRegion.scale(w.mGlobalScale);
                    }
                    w.setDisplayLayoutNeeded();
                    w.updateSourceFrame(w.getFrame());
                    mWindowPlacerLocked.performSurfacePlacement();
                    w.getDisplayContent().getInputMonitor().updateInputWindowsLw(true);

                    // We need to report touchable region changes to accessibility.
                    if (mAccessibilityController.hasCallbacks()) {
                        mAccessibilityController.onSomeWindowResizedOrMovedWithCallingUid(
                                uid, w.getDisplayContent().getDisplayId());
                    }
                }
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    /**
     * Called when a rectangle of a view is requested to be visible on the screen.
     */
    public void onRectangleOnScreenRequested(IBinder token, Rect rectangle,
            @View.RectangleOnScreenRequestSource int source) {
        final AccessibilityController.AccessibilityControllerInternalImpl a11yControllerInternal =
                AccessibilityController.getAccessibilityControllerInternal(this);
        synchronized (mGlobalLock) {
            if (a11yControllerInternal.hasWindowManagerEventDispatcher()) {
                WindowState window = mWindowMap.get(token);
                if (window != null) {
                    a11yControllerInternal.onRectangleOnScreenRequested(
                            window.getDisplayId(), rectangle, source);
                }
            }
        }
    }

    public IWindowId getWindowId(IBinder token) {
        synchronized (mGlobalLock) {
            WindowState window = mWindowMap.get(token);
            return window != null ? window.mWindowId : null;
        }
    }

    public void pokeDrawLock(Session session, IBinder token) {
        synchronized (mGlobalLock) {
            final WindowState window = windowForClient(session, token);
            if (window != null) {
                window.pokeDrawLockLw(mDrawLockTimeoutMillis);
            }
        }
    }

    private boolean hasStatusBarPermission(int pid, int uid) {
        return mContext.checkPermission(permission.STATUS_BAR, pid, uid)
                        == PackageManager.PERMISSION_GRANTED;
    }

    /**
     * Returns whether this window can proceed with drawing or needs to retry later.
     */
    public boolean cancelDraw(Session session, IWindow client, int seqId) {
        synchronized (mGlobalLock) {
            final WindowState win = windowForClient(session, client);
            if (win == null) {
                return false;
            }

            final boolean cancel = win.cancelAndRedraw(seqId);
            if (cancel && Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                Trace.instant(TRACE_TAG_WINDOW_MANAGER, "cancelDraw clientSeqId=" + seqId
                        + " serverSeqId=" + win.mSyncSeqId + " bufferSeqId=" + win.mBufferSeqId);
            }
            return cancel;
        }
    }

    /** Relayouts window. */
    public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
            int syncSeqId, WindowRelayoutResult outRelayoutResult,
            SurfaceControl outSurfaceControl) {
        final ClientWindowFrames outFrames;
        final MergedConfiguration outMergedConfiguration;
        final InsetsState outInsetsState;
        final InsetsSourceControl.Array outActiveControls;
        if (outRelayoutResult != null) {
            outFrames = outRelayoutResult.frames;
            outMergedConfiguration = outRelayoutResult.mergedConfiguration;
            outInsetsState = outRelayoutResult.insetsState;
            outActiveControls = outRelayoutResult.activeControls;
        } else {
            outFrames = null;
            outMergedConfiguration = null;
            outInsetsState = null;
            outActiveControls = null;
        }

        if (outActiveControls != null) {
            outActiveControls.set(null, false /* copyControls */);
        }
        int result = 0;
        boolean configChanged = false;
        final int pid = Binder.getCallingPid();
        final int uid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        synchronized (mGlobalLock) {
            final WindowState win = windowForClient(session, client);
            if (win == null) {
                return 0;
            }
            if (win.mRelayoutSeq < seq) {
                win.mRelayoutSeq = seq;
            } else if (win.mRelayoutSeq > seq) {
                return 0;
            }

            if (win.cancelAndRedraw(syncSeqId)
                    && (mAlwaysSeqId || (win.mPrepareSyncSeqId <= syncSeqId))) {
                // The client has reported the sync draw, but we haven't finished it yet.
                // Don't let the client perform a non-sync draw at this time.
                result |= RELAYOUT_RES_CANCEL_AND_REDRAW;
                if (mAlwaysSeqId && Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                    Trace.instant(TRACE_TAG_WINDOW_MANAGER, "earlyCancelDraw clientSeqId="
                            + syncSeqId + " serverSeqId=" + win.mSyncSeqId
                            + " bufferSeqId=" + win.mBufferSeqId);
                }
            }

            final DisplayContent displayContent = win.getDisplayContent();
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();

            WindowStateAnimator winAnimator = win.mWinAnimator;
            if (viewVisibility != View.GONE) {
                win.setRequestedSize(requestedWidth, requestedHeight);
            }

            int attrChanges = 0;
            int flagChanges = 0;
            int privateFlagChanges = 0;
            if (attrs != null) {
                displayPolicy.adjustWindowParamsLw(win, attrs);
                attrs.privateFlags = sanitizePrivateFlags(attrs.privateFlags,
                        win.mAttrs.privateFlags, win.getName(), uid, pid);
                attrs.flags = sanitizeFlagSlippery(attrs.flags, win.getName(), uid, pid);
                attrs.inputFeatures = sanitizeInputFeatures(attrs.inputFeatures, win.getName(), uid,
                        pid, win.isTrustedOverlay());
                int disableFlags =
                        (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility) & DISABLE_MASK;
                if (disableFlags != 0 && !hasStatusBarPermission(pid, uid)) {
                    disableFlags = 0;
                }
                win.mDisableFlags = disableFlags;
                if (win.mAttrs.type != attrs.type) {
                    throw new IllegalArgumentException(
                            "Window type can not be changed after the window is added.");
                }
                if (!(win.mAttrs.providedInsets == null && attrs.providedInsets == null)) {
                    if (win.mAttrs.providedInsets == null || attrs.providedInsets == null
                            || (win.mAttrs.providedInsets.length != attrs.providedInsets.length)) {
                        throw new IllegalArgumentException(
                                "Insets amount can not be changed after the window is added.");
                    } else {
                        final int insetsTypes = attrs.providedInsets.length;
                        for (int i = 0; i < insetsTypes; i++) {
                            if (!win.mAttrs.providedInsets[i].idEquals(attrs.providedInsets[i])) {
                                throw new IllegalArgumentException(
                                        "Insets ID can not be changed after the window is added.");
                            }
                            final InsetsFrameProvider.InsetsSizeOverride[] overrides =
                                    win.mAttrs.providedInsets[i].getInsetsSizeOverrides();
                            final InsetsFrameProvider.InsetsSizeOverride[] newOverrides =
                                    attrs.providedInsets[i].getInsetsSizeOverrides();
                            if (!(overrides == null && newOverrides == null)) {
                                if (overrides == null || newOverrides == null
                                        || (overrides.length != newOverrides.length)) {
                                    throw new IllegalArgumentException(
                                            "Insets override types can not be changed after the "
                                                    + "window is added.");
                                } else {
                                    final int overrideTypes = overrides.length;
                                    for (int j = 0; j < overrideTypes; j++) {
                                        if (overrides[j].getWindowType()
                                                != newOverrides[j].getWindowType()) {
                                            throw new IllegalArgumentException(
                                                    "Insets override types can not be changed after"
                                                            + " the window is added.");
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                final boolean wasTrustedOverlay = win.isWindowTrustedOverlay();
                flagChanges = win.mAttrs.flags ^ attrs.flags;
                privateFlagChanges = win.mAttrs.privateFlags ^ attrs.privateFlags;
                attrChanges = win.mAttrs.copyFrom(attrs);
                final boolean layoutChanged =
                        (attrChanges & WindowManager.LayoutParams.LAYOUT_CHANGED) != 0;
                if (layoutChanged || (attrChanges
                        & WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED) != 0) {
                    win.mLayoutNeeded = true;
                }
                if (layoutChanged && win.providesDisplayDecorInsets()) {
                    configChanged = displayPolicy.updateDecorInsetsInfo();
                }
                if (wasTrustedOverlay != win.isWindowTrustedOverlay()) {
                    win.updateTrustedOverlay();
                }
                if (win.mActivityRecord != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
                        || (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {
                    win.mActivityRecord.checkKeyguardFlagsChanged();
                }

                if ((privateFlagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
                    updateNonSystemOverlayWindowsVisibilityIfNeeded(
                            win, win.mWinAnimator.getShown());
                }
                if (!setScPropertiesInClient()) {
                    if ((attrChanges & (WindowManager.LayoutParams.PRIVATE_FLAGS_CHANGED)) != 0) {
                        winAnimator.setColorSpaceAgnosticLocked((win.mAttrs.privateFlags
                                & WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC)
                                != 0);
                    }
                }
                // See if the DisplayWindowPolicyController wants to keep the activity on the window
                if (displayContent.mDwpcHelper.hasController()
                        && win.mActivityRecord != null && (!win.mRelayoutCalled || flagChanges != 0
                        || privateFlagChanges != 0)) {
                    int newOrChangedFlags = !win.mRelayoutCalled ? win.mAttrs.flags : flagChanges;
                    int newOrChangedPrivateFlags =
                            !win.mRelayoutCalled ? win.mAttrs.privateFlags : privateFlagChanges;

                    if (!displayContent.mDwpcHelper.keepActivityOnWindowFlagsChanged(
                            win.mActivityRecord.info, newOrChangedFlags, newOrChangedPrivateFlags,
                            win.mAttrs.flags,
                            win.mAttrs.privateFlags)) {
                        mH.sendMessage(mH.obtainMessage(H.REPARENT_TASK_TO_DEFAULT_DISPLAY,
                                win.mActivityRecord.getTask()));
                        Slog.w(TAG_WM, "Activity " + win.mActivityRecord + " window flag changed,"
                                + " can't remain on display " + displayContent.getDisplayId());
                        return 0;
                    }
                }
            }

            if (DEBUG_LAYOUT) {
                Slog.v(
                        TAG_WM,
                        "Relayout "
                                + win
                                + ": viewVisibility="
                                + viewVisibility
                                + " req="
                                + requestedWidth
                                + "x"
                                + requestedHeight
                                + " "
                                + win.mAttrs);
            }
            if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
                winAnimator.mAlpha = attrs.alpha;
            }
            if ((attrChanges & WindowManager.LayoutParams.TITLE_CHANGED) != 0) {
                win.mInputWindowHandle.setName(win.getName());
            }
            win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);

            if (win.mAttrs.surfaceInsets.left != 0
                    || win.mAttrs.surfaceInsets.top != 0
                    || win.mAttrs.surfaceInsets.right != 0
                    || win.mAttrs.surfaceInsets.bottom != 0) {
                winAnimator.setOpaqueLocked(false);
            }

            final int oldVisibility = win.mViewVisibility;

            // If the window is becoming visible, visibleOrAdding may change which may in turn
            // change the IME layering target.
            final boolean becameVisible =
                    (oldVisibility == View.INVISIBLE || oldVisibility == View.GONE)
                            && viewVisibility == View.VISIBLE;
            boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
                    || becameVisible;
            boolean focusMayChange = win.mViewVisibility != viewVisibility
                    || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
                    || (!win.mRelayoutCalled);

            boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
                    && win.hasWallpaper();
            wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
            if ((flagChanges & FLAG_SECURE) != 0) {
                win.setSecureLocked(win.isSecureLocked());
            }

            final boolean wasVisible = win.isVisible();

            win.mRelayoutCalled = true;
            win.mInRelayout = true;

            win.setViewVisibility(viewVisibility);
            ProtoLog.i(WM_DEBUG_SCREEN_ON, "Relayout %s: oldVis=%d newVis=%d", win, oldVisibility,
                    viewVisibility);
            if (becameVisible) {
                onWindowVisible(win);
            }

            win.setDisplayLayoutNeeded();
            win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;

            // We should only relayout if the view is visible, it is a starting window, or the
            // associated appToken is not hidden.
            final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                    (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                            || win.mActivityRecord.isClientVisible());

            // If we are not currently running the exit animation, we need to see about starting
            // one.
            // This must be called before the call to performSurfacePlacement.
            if (!shouldRelayout && winAnimator.hasSurface() && !win.mAnimatingExit) {
                if (DEBUG_VISIBILITY) {
                    Slog.i(TAG_WM,
                            "Relayout invis " + win + ": mAnimatingExit=" + win.mAnimatingExit);
                }
                result |= RELAYOUT_RES_SURFACE_CHANGED;
                // When FLAG_SHOW_WALLPAPER flag is removed from a window, we usually set a flag
                // in DC#pendingLayoutChanges and update the wallpaper target later.
                // However it's possible that FLAG_SHOW_WALLPAPER flag is removed from a window
                // when the window is about to exit, so we update the wallpaper target
                // immediately here. Otherwise this window will be stuck in exiting and its
                // surface remains on the screen.
                // TODO(b/189856716): Allow destroying surface even if it belongs to the
                //  keyguard target.
                if (wallpaperMayMove) {
                    displayContent.mWallpaperController.adjustWallpaperWindows();
                }
                tryStartExitingAnimation(win, winAnimator);
            }

            // Create surfaceControl before surface placement otherwise layout will be skipped
            // (because WS.isGoneForLayout() is true when there is no surface.
            if (shouldRelayout && outSurfaceControl != null) {
                try {
                    result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
                } catch (Exception e) {
                    displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);

                    ProtoLog.w(WM_ERROR,
                            "Exception thrown when creating surface for client %s (%s). %s",
                            client, win.mAttrs.getTitle(), e);
                    Binder.restoreCallingIdentity(origId);
                    return 0;
                }
            }

            // We may be deferring layout passes at the moment, but since the client is interested
            // in the new out values right now we need to force a layout.
            mWindowPlacerLocked.performSurfacePlacement(true /* force */);

            if (!win.mHaveFrame && displayContent.mWaitingForConfig) {
                // We just forcibly triggered the layout, but this could still be intercepted by
                // mWaitingForConfig. Here, we are forcefully marking a value for mLayoutSeq to
                // ensure that the resize can occur properly later. Otherwise, the window's frame
                // will remain empty forever.
                win.mLayoutSeq = displayContent.mLayoutSeq;
            }

            if (shouldRelayout) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");

                result = win.relayoutVisibleWindow(result);

                if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                    focusMayChange = true;
                }
                if (win.mAttrs.type == TYPE_INPUT_METHOD
                        && displayContent.mInputMethodWindow == null) {
                    displayContent.setInputMethodWindowLocked(win);
                    imMayMove = true;
                }
                win.adjustStartingWindowFlags();
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            } else {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_2");

                winAnimator.mEnterAnimationPending = false;
                winAnimator.mEnteringAnimation = false;

                if (outSurfaceControl != null) {
                    if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) {
                        // We already told the client to go invisible, but the message may not be
                        // handled yet, or it might want to draw a last frame. If we already have a
                        // surface, let the client use that, but don't create new surface at this
                        // point.
                        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface");
                        winAnimator.getSurfaceControl(outSurfaceControl);
                        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                    } else {
                        if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);

                        try {
                            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_"
                                    + win.mAttrs.getTitle());
                            outSurfaceControl.release();
                        } finally {
                            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                        }
                    }
                }

                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            }

            if (focusMayChange) {
                if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
                    imMayMove = false;
                }
            }

            // updateFocusedWindowLocked() already assigned layers so we only need to
            // reassign them at this point if the IM window state gets shuffled
            boolean toBeDisplayed = (result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
            if (imMayMove) {
                displayContent.computeImeLayeringTarget(true /* update */);
                if (toBeDisplayed) {
                    // Little hack here -- we -should- be able to rely on the function to return
                    // true if the IME has moved and needs its layer recomputed. However, if the IME
                    // was hidden and isn't actually moved in the list, its layer may be out of data
                    // so we make sure to recompute it.
                    displayContent.assignWindowLayers(false /* setLayoutNeeded */);
                }
            }

            if (wallpaperMayMove) {
                displayContent.pendingLayoutChanges |=
                        WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
            }

            if (win.mActivityRecord != null) {
                displayContent.mUnknownAppVisibilityController.notifyRelayouted(win.mActivityRecord);
            }

            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: updateOrientation");
            configChanged |= displayContent.updateOrientation();
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);

            if (toBeDisplayed && win.mIsWallpaper) {
                displayContent.mWallpaperController.updateWallpaperOffset(win);
            }
            if (win.mActivityRecord != null) {
                win.mActivityRecord.updateReportedVisibilityLocked();
            }

            if (outRelayoutResult != null) {
                final boolean shouldReportActivityWindowInfo =
                        win.mLastReportedActivityWindowInfo != null;
                final ActivityWindowInfo outActivityWindowInfo = shouldReportActivityWindowInfo
                        ? new ActivityWindowInfo()
                        : null;

                win.fillClientWindowFramesAndConfiguration(outFrames, outMergedConfiguration,
                        outActivityWindowInfo, false /* useLatestConfig */, shouldRelayout);

                if (shouldReportActivityWindowInfo) {
                    outRelayoutResult.activityWindowInfo = outActivityWindowInfo;
                }

                // Set resize-handled here because the values are sent back to the client.
                win.onResizeHandled();
            }

            if (outInsetsState != null) {
                win.fillInsetsState(outInsetsState, true /* copySources */);
            }

            ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b",
                    win, focusMayChange);

            if (DEBUG_LAYOUT) {
                Slog.v(TAG_WM, "Relayout complete " + win + ": outFrames=" + outFrames);
            }
            win.mInRelayout = false;

            final boolean winVisibleChanged = win.isVisible() != wasVisible;
            if (win.isImeOverlayLayeringTarget() && winVisibleChanged) {
                dispatchImeOverlayLayeringTargetVisibilityChanged(client.asBinder(),
                        win.mAttrs.type, win.isVisible(), false /* removed */, win.getDisplayId());
            }
            // Notify listeners about IME input target window visibility change.
            final boolean isImeInputTarget = win.getDisplayContent().getImeInputTarget() == win;
            if (isImeInputTarget && winVisibleChanged) {
                dispatchImeInputTargetVisibilityChanged(win.mClient.asBinder(),
                        win.isVisible() /* visible */, false /* removed */, win.getDisplayId());
            }

            if (outRelayoutResult != null) {
                if (!mAlwaysSeqId
                        && win.syncNextBuffer() && viewVisibility == View.VISIBLE
                        && win.mSyncSeqId > syncSeqId && !displayContent.mWaitingForConfig) {
                    outRelayoutResult.syncSeqId = win.shouldSyncWithBuffers()
                            ? win.mSyncSeqId
                            : -1;
                    win.markRedrawForSyncReported();
                } else {
                    outRelayoutResult.syncSeqId = -1;
                    if (mAlwaysSeqId && (result & RELAYOUT_RES_CANCEL_AND_REDRAW) == 0
                            && win.cancelAndRedraw(syncSeqId)) {
                        // Surface-placement has resulted in a new configuration or a new sync,
                        // so this current layout is invalid until subsequent reportResized.

                        // However, make a targeted optimization to let the client draw early if the
                        // relayout result won't change even after the client receives the new
                        // configuration. If there is an explicit sync, though, the user-perceived
                        // latency will be worse due to the client drawing content that won't be
                        // presented; so, don't "optimize" in that case.
                        final boolean inExplicitSync = syncSeqId <= win.mBufferSeqId
                                || win.mSyncState == SYNC_STATE_WAITING_FOR_DRAW;
                        if (!inExplicitSync && win.layoutIgnoresClientConfig()) {
                            // Returning a seqId indicates, to the client, that it can use this
                            // result even though it's configuration is out-dated.
                            outRelayoutResult.syncSeqId = win.mSyncSeqId;
                            if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                                Trace.instant(TRACE_TAG_WINDOW_MANAGER, "ignoreCancelDraw seqId="
                                        + win.mSyncSeqId);
                            }
                        } else {
                            result |= RELAYOUT_RES_CANCEL_AND_REDRAW;
                            if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                                Trace.instant(TRACE_TAG_WINDOW_MANAGER, "lateCancelDraw "
                                        + " clientSeqId=" + syncSeqId
                                        + " serverSeqId=" + win.mSyncSeqId
                                        + " bufferSeqId=" + win.mBufferSeqId);
                            }
                        }
                    }
                }
            }

            if (configChanged) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                        "relayoutWindow: postNewConfigurationToHandler");
                displayContent.sendNewConfiguration();
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            }
            if (outActiveControls != null) {
                getInsetsSourceControls(win, outActiveControls);
            }
        }

        Binder.restoreCallingIdentity(origId);
        return result;
    }

    private void getInsetsSourceControls(WindowState win, InsetsSourceControl.Array outArray) {
        // We will leave the critical section before returning the leash to the client,
        // so we need to copy the leash to prevent others release the one that we are
        // about to return.
        win.fillInsetsSourceControls(outArray, true /* copyControls */);
        // This source control is an extra copy if the client is not local. By setting
        // PARCELABLE_WRITE_RETURN_VALUE, the leash will be released at the end of
        // SurfaceControl.writeToParcel.
        outArray.setParcelableFlags(PARCELABLE_WRITE_RETURN_VALUE);
    }

    private void tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator) {
        // Try starting an animation; if there isn't one, we
        // can destroy the surface right away.
        int transit = WindowManagerPolicy.TRANSIT_EXIT;
        if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
            transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
        }

        if (win.isVisible() && win.isDisplayed() && win.mDisplayContent.okToAnimate()) {
            String reason = null;
            if (winAnimator.applyAnimationLocked(transit, false)) {
                // This is a WMCore-driven window animation.
                reason = "applyAnimation";
            } else if (win.isSelfAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION)) {
                // This is already animating via a WMCore-driven window animation.
                reason = "selfAnimating";
            } else if (win.mActivityRecord != null && win.mActivityRecord.inTransition()) {
                // Already animating as part of a shell-transition. Currently this only handles
                // activity window because other types should be WMCore-driven.
                win.mTransitionController.mAnimatingExitWindows.add(win);
                reason = "inTransition";
            }
            if (reason != null) {
                win.mAnimatingExit = true;
                ProtoLog.d(WM_DEBUG_ANIM,
                        "Set animatingExit: reason=startExitingAnimation/%s win=%s", reason, win);
            }
        }
        if (!win.mAnimatingExit) {
            boolean stopped = win.mActivityRecord == null || win.mActivityRecord.mAppStopped;
            // We set mDestroying=true so ActivityRecord#notifyAppStopped in-to destroy surfaces
            // will later actually destroy the surface if we do not do so here. Normally we leave
            // this to the exit animation.
            win.mDestroying = true;
            win.destroySurface(false, stopped);
        }
        if (mAccessibilityController.hasCallbacks()) {
            mAccessibilityController.onWindowTransition(win, transit);
        }
    }

    private int createSurfaceControl(SurfaceControl outSurfaceControl, int result,
            WindowState win, WindowStateAnimator winAnimator) {
        if (!win.mHasSurface) {
            result |= RELAYOUT_RES_SURFACE_CHANGED;
        }

        SurfaceControl surfaceControl;
        try {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
            surfaceControl = winAnimator.createSurfaceLocked();
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
        if (surfaceControl != null) {
            winAnimator.getSurfaceControl(outSurfaceControl);
            ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl);

        } else {
            // For some reason there isn't a surface.  Clear the
            // caller's object so they see the same state.
            ProtoLog.w(WM_ERROR, "Failed to create surface control for %s", win);
            outSurfaceControl.release();
        }

        return result;
    }

    public boolean outOfMemoryWindow(Session session, IWindow client) {
        final long origId = Binder.clearCallingIdentity();

        try {
            synchronized (mGlobalLock) {
                final WindowState win = windowForClient(session, client);
                if (win == null) {
                    return false;
                }
                return mRoot.reclaimSomeSurfaceMemory(win.mWinAnimator, "from-client", false);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    void finishDrawingWindow(Session session, IWindow client,
            @Nullable SurfaceControl.Transaction postDrawTransaction, int seqId) {
        if (postDrawTransaction != null) {
            postDrawTransaction.sanitize(Binder.getCallingPid(), Binder.getCallingUid());
        }

        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final WindowState win = windowForClient(session, client);
                ProtoLog.d(WM_DEBUG_ADD_REMOVE, "finishDrawingWindow: %s mDrawState=%s",
                        win, (win != null ? win.mWinAnimator.drawStateToString() : "null"));
                if (win != null && win.finishDrawing(postDrawTransaction, seqId)) {
                    if (win.hasWallpaper()) {
                        win.getDisplayContent().pendingLayoutChanges |=
                                WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                    }
                    win.setDisplayLayoutNeeded();
                    mWindowPlacerLocked.requestTraversal();
                }
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    boolean checkCallingPermission(String permission, String func) {
        return checkCallingPermission(permission, func, true /* printLog */);
    }

    boolean checkCallingPermission(String permission, String func, boolean printLog) {
        if (Binder.getCallingPid() == MY_PID) {
            return true;
        }

        if (mContext.checkCallingPermission(permission)
                == PackageManager.PERMISSION_GRANTED) {
            return true;
        }
        if (printLog) {
            ProtoLog.w(WM_ERROR, "Permission Denial: %s from pid=%d, uid=%d requires %s",
                    func, Binder.getCallingPid(), Binder.getCallingUid(), permission);
        }
        return false;
    }

    @Override
    public void addWindowToken(@NonNull IBinder binder, int type, int displayId,
            @Nullable Bundle options) {
        if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) {
            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
        }

        synchronized (mGlobalLock) {
            final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
            if (dc == null) {
                ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s"
                        + " for non-exiting displayId=%d", binder, displayId);
                return;
            }

            WindowToken token = dc.getWindowToken(binder);
            if (token != null) {
                ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s"
                        + " for already created window token: %s"
                        + " displayId=%d", binder, token, displayId);
                return;
            }
            if (type == TYPE_WALLPAPER) {
                new WallpaperWindowToken(this, binder, true, dc,
                        true /* ownerCanManageAppTokens */, options);
            } else {
                new WindowToken.Builder(this, binder, type)
                        .setDisplayContent(dc)
                        .setPersistOnEmpty(true)
                        .setOwnerCanManageAppTokens(true)
                        .setOptions(options)
                        .build();
            }
        }
    }

    @Nullable
    @Override
    public WindowContextInfo attachWindowContextToDisplayArea(@NonNull IApplicationThread appThread,
            @NonNull IBinder clientToken, @LayoutParams.WindowType int type, int displayId,
            @Nullable Bundle options) {
        Objects.requireNonNull(appThread);
        Objects.requireNonNull(clientToken);
        final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
                "attachWindowContextToDisplayArea", false /* printLog */);
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final WindowProcessController wpc = mAtmService.getProcessController(appThread);
                if (wpc == null) {
                    ProtoLog.w(WM_ERROR, "attachWindowContextToDisplayArea: calling from"
                            + " non-existing process pid=%d uid=%d", callingPid, callingUid);
                    return null;
                }
                final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
                if (dc == null) {
                    ProtoLog.w(WM_ERROR, "attachWindowContextToDisplayArea: trying to attach"
                            + " to a non-existing display:%d", displayId);
                    return null;
                }
                // TODO(b/155340867): Investigate if we still need roundedCornerOverlay after
                // the feature b/155340867 is completed.
                final DisplayArea<?> da = dc.findAreaForWindowType(type, options,
                        callerCanManageAppTokens, false /* roundedCornerOverlay */);
                mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
                        da, type, callerCanManageAppTokens, options,
                        false /* shouldDispatchConfigWhenRegistering */);
                return new WindowContextInfo(da.getConfiguration(), displayId);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @Nullable
    @Override
    public WindowContextInfo attachWindowContextToDisplayContent(
            @NonNull IApplicationThread appThread, @NonNull IBinder clientToken, int displayId) {
        Objects.requireNonNull(appThread);
        Objects.requireNonNull(clientToken);
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final WindowProcessController wpc = mAtmService.getProcessController(appThread);
                if (wpc == null) {
                    ProtoLog.w(WM_ERROR, "attachWindowContextToDisplayContent: calling from"
                            + " non-existing process pid=%d uid=%d", callingPid, callingUid);
                    return null;
                }
                // We use "getDisplayContent" instead of "getDisplayContentOrCreate" because
                // this method may be called in DisplayPolicy's constructor and may cause
                // infinite loop. In this scenario, we early return here and switch to do the
                // registration in DisplayContent#onParentChanged at DisplayContent initialization.
                final DisplayContent dc = mRoot.getDisplayContent(displayId);
                if (dc == null) {
                    ProtoLog.w(WM_ERROR,
                            "attachWindowContextToDisplayContent: "
                                    + "trying to attach to a non-existing display:%d",
                            displayId);
                    return null;
                }
                mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
                        dc, INVALID_WINDOW_TYPE, false /* callerCanManageAppTokens */,
                        null /* options */, false /* shouldDispatchConfigWhenRegistering */);
                return new WindowContextInfo(dc.getConfiguration(), displayId);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @Nullable
    @Override
    public WindowContextInfo attachWindowContextToWindowToken(@NonNull IApplicationThread appThread,
            @NonNull IBinder clientToken, @NonNull IBinder token) {
        Objects.requireNonNull(appThread);
        Objects.requireNonNull(clientToken);
        Objects.requireNonNull(token);
        final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
                "attachWindowContextToWindowToken", false /* printLog */);
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final WindowProcessController wpc = mAtmService.getProcessController(appThread);
                if (wpc == null) {
                    ProtoLog.w(WM_ERROR, "attachWindowContextToWindowToken: calling from"
                            + " non-existing process pid=%d uid=%d", callingPid, callingUid);
                    return null;
                }
                final WindowToken windowToken = mRoot.getWindowToken(token);
                if (windowToken == null) {
                    ProtoLog.w(WM_ERROR, "Then token:%s is invalid. It might be "
                            + "removed", token);
                    return null;
                }
                final int type = mWindowContextListenerController.getWindowType(clientToken);
                if (type == INVALID_WINDOW_TYPE) {
                    throw new IllegalArgumentException("The clientToken:" + clientToken
                            + " should have been attached.");
                }
                if (type != windowToken.windowType) {
                    throw new IllegalArgumentException("The WindowToken's type should match"
                            + " the created WindowContext's type. WindowToken's type is "
                            + windowToken.windowType + ", while WindowContext's is " + type);
                }
                if (!mWindowContextListenerController.assertCallerCanModifyListener(clientToken,
                        callerCanManageAppTokens, callingUid)) {
                    return null;
                }
                mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
                        windowToken, windowToken.windowType, callerCanManageAppTokens,
                        windowToken.mOptions, false /* shouldDispatchConfigWhenRegistering */);
                return new WindowContextInfo(windowToken.getConfiguration(),
                        windowToken.getDisplayContent().getDisplayId());
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @Override
    public void detachWindowContext(@NonNull IBinder clientToken) {
        Objects.requireNonNull(clientToken);
        final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
                "detachWindowContext", false /* printLog */);
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                if (!mWindowContextListenerController.assertCallerCanModifyListener(clientToken,
                        callerCanManageAppTokens, callingUid)) {
                    return;
                }
                final WindowContainer wc = mWindowContextListenerController
                        .getContainer(clientToken);

                mWindowContextListenerController.unregisterWindowContainerListener(clientToken);

                final WindowToken token = wc != null ? wc.asWindowToken() : null;
                if (token != null && token.isFromClient() && token.getDisplayContent() != null) {
                    removeWindowToken(token.token, token.getDisplayContent().getDisplayId());
                }
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @Override
    public boolean reparentWindowContextToDisplayArea(
            @NonNull IApplicationThread appThread, @NonNull IBinder clientToken, int displayId) {
        if (!Flags.reparentWindowTokenApi()) {
            return false;
        }
        Objects.requireNonNull(appThread);
        Objects.requireNonNull(clientToken);
        final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
                "reparentWindowContextToDisplayArea", false /* printLog */);
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final WindowProcessController wpc = mAtmService.getProcessController(appThread);
                if (wpc == null) {
                    ProtoLog.w(WM_ERROR, "reparentWindowContextToDisplayArea: calling from"
                            + " non-existing process pid=%d uid=%d", callingPid, callingUid);
                    return false;
                }
                final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
                if (dc == null) {
                    ProtoLog.w(WM_ERROR, "reparentWindowContextToDisplayArea: trying to attach"
                            + " to a non-existing display:%d", displayId);
                    return false;
                }

                if (!mWindowContextListenerController.assertCallerCanReparentListener(clientToken,
                        callerCanManageAppTokens, callingUid, displayId)) {
                    return false;
                }
                return reparentWindowContextToDisplay(clientToken, dc, callingPid, callingUid);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    /**
     * Reparents the {@link android.window.WindowContext} specified with {@code clientCode} to
     * the {@link WindowContainer} on {@code display}.
     *
     * @param clientToken the {@link android.window.WindowContext}'s token
     * @param display the {@link DisplayContent} to reparent
     * @return {@code true} if the reparent operation is successful. Otherwise, {@code false}
     */
    boolean reparentWindowContextToDisplay(
            @NonNull IBinder clientToken,
            @NonNull DisplayContent display,
            int callingPid,
            int callingUid) {
        if (!Flags.reparentWindowTokenApi()) {
            return false;
        }
        final WindowContainer<?> container = mWindowContextListenerController.getContainer(
                clientToken);

        // If the WindowContext associate a WindowToken created by the WindowContext, reparent the
        // WindowToken as well.
        final WindowToken token = container != null ? container.asWindowToken() : null;
        // TODO(b/404532651): Checks if we should reparent system managed WindowToken as well.
        if (token != null && token.isFromClient()) {
            final int displayId = display.getDisplayId();
            ProtoLog.d(WM_DEBUG_ADD_REMOVE, "Reparenting to displayId=%d", displayId);

            // TODO b/404532651 - check the destination display content doesn't have a window
            //  with that type already.

            // Reparent the window created for this window context.
            display.reParentWindowToken(token);
            hideUntilNextDraw(token);
            // Prevent a race condition where VRI temporarily reverts the context display ID
            // before the onDisplayMoved callback arrives. This caused incorrect display IDs
            // during configuration changes, breaking SysUI layouts dependent on it.
            // Forcing a resize report ensures VRI has the correct ID before the update.
            forceReportResizing(token);
            // This makes sure there is a traversal scheduled that will eventually report
            // the window resize to the client.
            display.setLayoutNeeded();
            requestTraversal();
            return true;
        }
        return mWindowContextListenerController.reparentToDisplayArea(clientToken, display);
    }

    private void forceReportResizing(@NonNull WindowContainer<?> wc) {
        wc.forAllWindows(w -> {
            if (!mResizingWindows.contains(w)) {
                mResizingWindows.add(w);
            }
        }, true /* traverseTopToBottom */);
    }

    private void hideUntilNextDraw(@NonNull WindowToken token) {
        final WindowState topChild = token.getTopChild();
        if (topChild != null) {
            mTransactionFactory.get().hide(token.mSurfaceControl).apply();
            topChild.applyWithNextDraw(t -> {
                if (token.mSurfaceControl != null) {
                    t.show(token.mSurfaceControl);
                }
            });
        }
    }

    /** Returns {@code true} if this binder is a registered window token. */
    @Override
    public boolean isWindowToken(IBinder binder) {
        synchronized (mGlobalLock) {
            return mRoot.getWindowToken(binder) != null;
        }

    }

    void removeWindowToken(IBinder binder, boolean removeWindows, boolean animateExit,
            int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent dc = mRoot.getDisplayContent(displayId);

            if (dc == null) {
                ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"
                        + " for non-exiting displayId=%d", binder, displayId);
                return;
            }
            final WindowToken token = dc.removeWindowToken(binder, animateExit);
            if (token == null) {
                ProtoLog.w(WM_ERROR,
                        "removeWindowToken: Attempted to remove non-existing token: %s",
                        binder);
                return;
            }

            if (removeWindows) {
                token.removeAllWindowsIfPossible();
            }
            dc.getInputMonitor().updateInputWindowsLw(true /* force */);
        }
    }

    @Override
    public void removeWindowToken(IBinder binder, int displayId) {
        if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) {
            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
        }
        final long origId = Binder.clearCallingIdentity();
        try {
            removeWindowToken(binder, false /* removeWindows */, true /* animateExit */, displayId);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    /** @see WindowManagerInternal#moveWindowTokenToDisplay(IBinder, int)  */
    public void moveWindowTokenToDisplay(IBinder binder, int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
            if (dc == null) {
                ProtoLog.w(WM_ERROR, "moveWindowTokenToDisplay: Attempted to move token: %s"
                        + " to non-exiting displayId=%d", binder, displayId);
                return;
            }
            final WindowToken token = mRoot.getWindowToken(binder);
            if (token == null) {
                ProtoLog.w(WM_ERROR,
                        "moveWindowTokenToDisplay: Attempted to move non-existing token: %s",
                        binder);
                return;
            }
            if (token.getDisplayContent() == dc) {
                ProtoLog.w(WM_ERROR,
                        "moveWindowTokenToDisplay: Cannot move to the original display "
                                + "for token: %s", binder);
                return;
            }
            dc.reParentWindowToken(token);
        }
    }

    @Override
    public void overridePendingAppTransitionMultiThumbFuture(
            IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
            boolean scaleUp, int displayId) {
        // TODO(b/365884835): remove this method and callers.
    }

    @Override
    public void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter,
            int displayId) {
        // TODO(b/365884835): remove this method and callers.
    }

    @Override
    public void endProlongedAnimations() {
        // TODO: Remove once clients are updated.
    }

    // TODO(multi-display): remove when no default display use case.
    public void executeAppTransition() {
        if (!checkCallingPermission(MANAGE_APP_TOKENS, "executeAppTransition()")) {
            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
        }
        getDefaultDisplayContentLocked().executeAppTransition();
    }

    boolean isValidPictureInPictureAspectRatio(DisplayContent displayContent, float aspectRatio) {
        return displayContent.getPinnedTaskController().isValidPictureInPictureAspectRatio(
                aspectRatio);
    }

    boolean isValidExpandedPictureInPictureAspectRatio(DisplayContent displayContent,
            float aspectRatio) {
        return displayContent.getPinnedTaskController().isValidExpandedPictureInPictureAspectRatio(
                aspectRatio);
    }

    @Override
    public void notifyKeyguardTrustedChanged() {
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                if (mAtmService.mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
                    mRoot.ensureActivitiesVisible();
                }
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @Override
    public void screenTurningOff(int displayId, ScreenOffListener listener) {
        mTaskSnapshotController.screenTurningOff(displayId, listener);
    }

    @Override
    public void onKeyguardShowingAndNotOccludedChanged() {
        mH.sendEmptyMessage(H.RECOMPUTE_FOCUS);
        dispatchKeyguardLockedState();
    }

    @Override
    public void onPowerKeyDown(boolean isScreenOn) {
        mRoot.forAllDisplayPolicies(p -> p.onPowerKeyDown(isScreenOn));
    }

    @Override
    public void onUserSwitched() {
        synchronized (mGlobalLock) {
            // force a re-application of focused window sysui visibility on each display.
            mRoot.forAllDisplayPolicies(DisplayPolicy::resetSystemBarAttributes);
        }
    }

    @Override
    public void moveDisplayToTopIfAllowed(int displayId) {
        final boolean moved = moveDisplayToTopInternal(displayId);
        if (moved) {
            syncInputTransactions(true /* waitForAnimations */);
        }
    }

    /**
     * Moves the given display to the top. If it cannot be moved to the top this method does
     * nothing (e.g. if the display has the flag FLAG_STEAL_TOP_FOCUS_DISABLED set).
     * @param displayId The display to move to the top.
     *
     * @return whether the move actually occurred.
     */
    boolean moveDisplayToTopInternal(int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent != null && mRoot.getTopChild() != displayContent) {
                // Check whether anything prevents us from moving the display to the top.
                if (!displayContent.canStealTopFocus()) {
                    ProtoLog.i(WM_DEBUG_FOCUS_LIGHT,
                            "Not moving display (displayId=%d) to top. Top focused displayId=%d. "
                                    + "Reason: FLAG_STEAL_TOP_FOCUS_DISABLED",
                            displayId, mRoot.getTopFocusedDisplayContent().getDisplayId());
                    return false;
                }

                final ActionChain chain = mAtmService.mChainTracker.startTransit("dispToTop");
                Transition transition = null;
                boolean transitionNewlyCreated = false;
                if (ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS.isTrue()) {
                    transition = mAtmService.getTransitionController().requestTransitionIfNeeded(
                                    TRANSIT_TO_FRONT, 0 /* flags */, null /* trigger */,
                                    displayContent, chain);
                    if (transition != null) {
                        transitionNewlyCreated = true;
                    } else {
                        transition =
                                mAtmService.getTransitionController().getCollectingTransition();
                    }
                    if (transition != null) {
                        transition.recordTaskOrder(displayContent);
                    }
                }
                // Nothing prevented us from moving the display to the top. Let's do it!
                displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP,
                        displayContent, true /* includingParents */);
                mAtmService.mChainTracker.end();
                if (transitionNewlyCreated) {
                    transition.setReady(displayContent, true /* ready */);
                }
            }
            return true;
        }
    }

    @Override
    public void disableKeyguard(IBinder token, String tag, int userId) {
        userId = mAmInternal.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                userId, false /* allowAll */, ALLOW_FULL_ONLY, "disableKeyguard", null);
        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
            != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Requires DISABLE_KEYGUARD permission");
        }
        final int callingUid = Binder.getCallingUid();
        final long origIdentity = Binder.clearCallingIdentity();
        try {
            mKeyguardDisableHandler.disableKeyguard(token, tag, callingUid, userId);
        } finally {
            Binder.restoreCallingIdentity(origIdentity);
        }
    }

    @Override
    public void reenableKeyguard(IBinder token, int userId) {
        userId = mAmInternal.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                userId, false /* allowAll */, ALLOW_FULL_ONLY, "reenableKeyguard", null);
        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
            != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Requires DISABLE_KEYGUARD permission");
        }
        Objects.requireNonNull(token, "token is null");
        final int callingUid = Binder.getCallingUid();
        final long origIdentity = Binder.clearCallingIdentity();
        try {
            mKeyguardDisableHandler.reenableKeyguard(token, callingUid, userId);
        } finally {
            Binder.restoreCallingIdentity(origIdentity);
        }
    }

    @EnforcePermission(android.Manifest.permission.DISABLE_KEYGUARD)
    /**
     * @see android.app.KeyguardManager#exitKeyguardSecurely
     */
    @Override
    public void exitKeyguardSecurely(final IOnKeyguardExitResult callback) {
        exitKeyguardSecurely_enforcePermission();

        if (callback == null) {
            throw new IllegalArgumentException("callback == null");
        }

        mPolicy.exitKeyguardSecurely(new WindowManagerPolicy.OnKeyguardExitResult() {
            @Override
            public void onKeyguardExitResult(boolean success) {
                try {
                    callback.onKeyguardExitResult(success);
                } catch (RemoteException e) {
                    // Client has died, we don't care.
                }
            }
        });
    }

    @Override
    public boolean isKeyguardLocked() {
        return mPolicy.isKeyguardLocked();
    }

    public boolean isKeyguardShowingAndNotOccluded() {
        return mPolicy.isKeyguardShowingAndNotOccluded();
    }

    @Override
    public boolean isKeyguardSecure(int userId) {
        if (userId != UserHandle.getCallingUserId()
                && !checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS,
                "isKeyguardSecure")) {
            throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
        }

        final long origId = Binder.clearCallingIdentity();
        try {
            return mPolicy.isKeyguardSecure(userId);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @Override
    public void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message) {
        if (!checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard")) {
            throw new SecurityException("Requires CONTROL_KEYGUARD permission");
        }
        synchronized (mGlobalLock) {
            final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
            if (!dreamHandlesConfirmKeys()
                    && defaultDisplayContent.getDisplayPolicy().isShowingDreamLw()) {
                mAtmService.mTaskSupervisor.wakeUp(
                        defaultDisplayContent.getDisplayId(), "leaveDream");
            }
            mPolicy.dismissKeyguardLw(callback, message);
        }
    }

    @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
    @Override
    public void addKeyguardLockedStateListener(IKeyguardLockedStateListener listener) {
        enforceSubscribeToKeyguardLockedStatePermission();
        boolean registered = mKeyguardLockedStateListeners.register(listener);
        if (!registered) {
            Slog.w(TAG, "Failed to register listener: " + listener);
        }
    }

    @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
    @Override
    public void removeKeyguardLockedStateListener(IKeyguardLockedStateListener listener) {
        enforceSubscribeToKeyguardLockedStatePermission();
        mKeyguardLockedStateListeners.unregister(listener);
    }

    private void enforceSubscribeToKeyguardLockedStatePermission() {
        mContext.enforceCallingOrSelfPermission(
                Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE,
                Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE
                        + " permission required to subscribe to keyguard locked state changes");
    }

    private void dispatchKeyguardLockedState() {
        mH.post(() -> {
            final boolean isKeyguardLocked = mPolicy.isKeyguardShowing();
            // Ensure we don't skip the call for the first dispatch
            if (mFirstKeyguardLockedStateDispatched
                    && mDispatchedKeyguardLockedState == isKeyguardLocked) {
                return;
            }
            final int n = mKeyguardLockedStateListeners.beginBroadcast();
            for (int i = 0; i < n; i++) {
                try {
                    mKeyguardLockedStateListeners.getBroadcastItem(i).onKeyguardLockedStateChanged(
                            isKeyguardLocked);
                } catch (RemoteException e) {
                    // Handled by the RemoteCallbackList.
                }
            }
            mKeyguardLockedStateListeners.finishBroadcast();
            mDispatchedKeyguardLockedState = isKeyguardLocked;
            mFirstKeyguardLockedStateDispatched = true;
        });
    }

    void dispatchImeOverlayLayeringTargetVisibilityChanged(@Nullable IBinder token,
            @WindowManager.LayoutParams.WindowType int windowType, boolean visible,
            boolean removed, int displayId) {
        if (DEBUG_INPUT_METHOD) {
            Slog.d(TAG, "setHasVisibleImeLayeringOverlay, win=" + mWindowMap.get(token)
                    + ", type=" + ViewDebug.intToString(WindowManager.LayoutParams.class,
                    "type", windowType) + "visible=" + visible + ", removed=" + removed
                    + ", displayId=" + displayId);
        }
        // Ignoring the starting window since it's ok to cover the IME layering target
        // window temporarily without affecting the IME visibility.
        final boolean hasOverlay = visible && !removed && windowType != TYPE_APPLICATION_STARTING;
        mH.post(() -> InputMethodManagerInternal.get().setHasVisibleImeLayeringOverlay(hasOverlay,
                displayId));
    }

    void dispatchImeInputTargetVisibilityChanged(@NonNull IBinder token, boolean visible,
            boolean removed, int displayId) {
        if (DEBUG_INPUT_METHOD) {
            Slog.d(TAG, "onImeInputTargetVisibilityChanged, win=" + mWindowMap.get(token)
                    + "visible=" + visible + ", removed=" + removed + ", displayId=" + displayId);
        }
        final boolean visibleAndNotRemoved = visible && !removed;
        mH.post(() -> InputMethodManagerInternal.get().onImeInputTargetVisibilityChanged(token,
                visibleAndNotRemoved, displayId));
    }

    @Override
    public void setSwitchingUser(boolean switching) {
        if (!checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                "setSwitchingUser()")) {
            throw new SecurityException("Requires INTERACT_ACROSS_USERS_FULL permission");
        }
        mPolicy.setSwitchingUser(switching);
        synchronized (mGlobalLock) {
            mSwitchingUser = switching;
        }
    }

    @RequiresPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
    @Override
    public void showGlobalActions() {
        if (!checkCallingPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW,
                "showGlobalActions()")) {
            throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
        }
        mPolicy.showGlobalActions();
    }

    @Override
    public void closeSystemDialogs(String reason) {
        int callingPid = Binder.getCallingPid();
        int callingUid = Binder.getCallingUid();
        if (!mAtmService.checkCanCloseSystemDialogs(callingPid, callingUid, null)) {
            return;
        }
        synchronized (mGlobalLock) {
            mRoot.closeSystemDialogs(reason);
        }
    }

    @Override
    public void setAnimationScale(@UpdateAnimationScaleMode int which, float scale) {
        if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE,
                "setAnimationScale()")) {
            throw new SecurityException("Requires SET_ANIMATION_SCALE permission");
        }

        scale = fixScale(scale);
        switch (which) {
            case WINDOW_ANIMATION_SCALE:
                synchronized (mGlobalLock) {
                    mWindowAnimationScaleSetting = scale;
                }
                break;
            case TRANSITION_ANIMATION_SCALE:
                synchronized (mGlobalLock) {
                    mTransitionAnimationScaleSetting = scale;
                }
                break;
            case ANIMATION_DURATION_SCALE:
                synchronized (mGlobalLock) {
                    mAnimatorScale.setCurrentScale(scale);
                }
                break;
        }

        // Persist setting
        mH.sendEmptyMessage(H.PERSIST_ANIMATION_SCALE);
    }

    @Override
    public void setAnimationScales(float[] scales) {
        if (!checkCallingPermission(android.Manifest.permission.SET_ANIMATION_SCALE,
                "setAnimationScale()")) {
            throw new SecurityException("Requires SET_ANIMATION_SCALE permission");
        }

        if (scales != null) {
            if (scales.length >= 1) {
                synchronized (mGlobalLock) {
                    mWindowAnimationScaleSetting = fixScale(scales[0]);
                }
            }
            if (scales.length >= 2) {
                synchronized (mGlobalLock) {
                    mTransitionAnimationScaleSetting = fixScale(scales[1]);
                }
            }
            if (scales.length >= 3) {
                synchronized (mGlobalLock) {
                    mAnimatorScale.setCurrentScale(fixScale(scales[2]));
                }
                dispatchNewAnimatorScaleLocked(null);
            }
        }

        // Persist setting
        mH.sendEmptyMessage(H.PERSIST_ANIMATION_SCALE);
    }

    @VisibleForTesting
    void setAnimationsDisabledForDisplay(int displayId, boolean disabled) {
        if (!android.companion.virtualdevice.flags.Flags.enableAnimationsPerDisplay()) {
            Slog.e(TAG, "Required feature flag is disabled");
            return;
        }

        synchronized (mGlobalLock) {
            DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
            displayContent.setAnimationsDisabledLocked(disabled);
        }
    }

    private void setAnimatorDurationScale(float scale) {
        mAnimatorScale.setCurrentScale(scale);
        ValueAnimator.setDurationScale(scale);
    }

    @Override
    public float getAnimationScale(@UpdateAnimationScaleMode int which) {
        synchronized (mGlobalLock) {
            return getAnimationScaleLocked(which);
        }
    }

    float getAnimationScaleLocked(@UpdateAnimationScaleMode int which) {
        return switch (which) {
            case WINDOW_ANIMATION_SCALE -> mWindowAnimationScaleSetting;
            case TRANSITION_ANIMATION_SCALE -> mTransitionAnimationScaleSetting;
            case ANIMATION_DURATION_SCALE -> mAnimatorScale.getCurrentScale();
            default -> 0;
        };
    }

    @Override
    public float[] getAnimationScales() {
        float windowAnimationScale;
        float transitionAnimationScale;
        float animatorDurationScale;
        synchronized (mGlobalLock) {
            windowAnimationScale = mWindowAnimationScaleSetting;
            transitionAnimationScale = mTransitionAnimationScaleSetting;
            animatorDurationScale = mAnimatorScale.getCurrentScale();
        }
        return new float[] { windowAnimationScale, transitionAnimationScale,
                animatorDurationScale };
    }

    @Override
    public float getCurrentAnimatorScale() {
        synchronized (mGlobalLock) {
            return mAnimatorScale.getCurrentScale();
        }
    }

    boolean isAnimationsDisabledLocked() {
        return mAnimatorScale.isAnimationsDisabled();
    }

    void dispatchNewAnimatorScaleLocked(Session session) {
        mH.obtainMessage(H.NEW_ANIMATOR_SCALE, session).sendToTarget();
    }

    @Override
    public void registerPointerEventListener(PointerEventListener listener, int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent != null) {
                displayContent.registerPointerEventListener(listener);
            }
        }
    }

    @Override
    public void unregisterPointerEventListener(PointerEventListener listener, int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent != null) {
                displayContent.unregisterPointerEventListener(listener);
            }
        }
    }

    // Called by window manager policy. Not exposed externally.
    @Override
    public int getLidState() {
        int sw = mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY,
                InputManagerService.SW_LID);
        if (sw > 0) {
            // Switch state: AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL.
            return LID_CLOSED;
        } else if (sw == 0) {
            // Switch state: AKEY_STATE_UP.
            return LID_OPEN;
        } else {
            // Switch state: AKEY_STATE_UNKNOWN.
            return LID_ABSENT;
        }
    }

    // Called by window manager policy. Not exposed externally.
    @Override
    public void lockDeviceNow() {
        lockNow(null);
    }

    // Called by window manager policy. Not exposed externally.
    @Override
    public int getCameraLensCoverState() {
        int sw = mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY,
                InputManagerService.SW_CAMERA_LENS_COVER);
        if (sw > 0) {
            // Switch state: AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL.
            return CAMERA_LENS_COVERED;
        } else if (sw == 0) {
            // Switch state: AKEY_STATE_UP.
            return CAMERA_LENS_UNCOVERED;
        } else {
            // Switch state: AKEY_STATE_UNKNOWN.
            return CAMERA_LENS_COVER_ABSENT;
        }
    }

    // Called by window manager policy.  Not exposed externally.
    @Override
    public void shutdown(boolean confirm) {
        // Pass in the UI context, since ShutdownThread requires it (to show UI).
        ShutdownThread.shutdown(ActivityThread.currentActivityThread().getSystemUiContext(),
                PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
    }

    // Called by window manager policy.  Not exposed externally.
    @Override
    public void reboot(boolean confirm) {
        // Pass in the UI context, since ShutdownThread requires it (to show UI).
        ShutdownThread.reboot(ActivityThread.currentActivityThread().getSystemUiContext(),
                PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
    }

    // Called by window manager policy.  Not exposed externally.
    @Override
    public void rebootSafeMode(boolean confirm) {
        // Pass in the UI context, since ShutdownThread requires it (to show UI).
        ShutdownThread.rebootSafeMode(ActivityThread.currentActivityThread().getSystemUiContext(),
                confirm);
    }

    /** Update the current user. */
    public void setCurrentUser(@UserIdInt int newUserId) {
        if (DesktopExperienceFlags.ENABLE_APPLY_DESK_ACTIVATION_ON_USER_SWITCH.isTrue()) return;
        synchronized (mGlobalLock) {
            final TransitionController controller = mAtmService.getTransitionController();
            final ActionChain chain = mAtmService.mChainTracker.startTransit("setUser");
            if (!chain.isCollecting() && controller.isShellTransitionsEnabled()) {
                chain.attachTransition(controller.createTransition(TRANSIT_OPEN));
                controller.requestStartTransition(chain.getTransition(),
                        null /* trigger */, null /* remote */, null /* disp */);
            }
            prepareUserStart(newUserId);
            switchUserInternal(newUserId);
            mAtmService.mChainTracker.end();
        }
    }

    /**
     * Called when a new user is about to start.
     */
    public void prepareUserStart(@UserIdInt int newUserId) {
        synchronized (mGlobalLock) {
            mCurrentUserId = newUserId;
            mDisplayWindowSettingsProvider.setOverrideSettingsForUser(newUserId);
            mPolicy.setCurrentUserLw(newUserId);
            mKeyguardDisableHandler.setCurrentUser(newUserId);
            // This call is crucial on user switch to ensure the Magnify IME state
            // is correctly re-evaluated and applied for the new user.
            mSettingsObserver.updateMagnifyIme();
        }
    }

    /**
     * Starts a user-switch transition to the given user.
     */
    public void startUserSwitchTransition(@UserIdInt int oldUserId, @UserIdInt int newUserId,
            UserState uss) {
        synchronized (mGlobalLock) {
            mRoot.mTaskSupervisor.mStartingUsers.add(uss);
            final Runnable switchUserRunnable = () -> {
                switchUserInternal(newUserId);
                moveUserToForeground(newUserId, uss, "startUserSwitchTransition");
            };

            final TransitionController controller = mAtmService.getTransitionController();
            if (!controller.isShellTransitionsEnabled()) {
                switchUserRunnable.run();
                return;
            }
            final Transition transition = new Transition(TRANSIT_OPEN, 0 /* flags */,
                    controller, mAtmService.mWindowManager.mSyncEngine);
            controller.startCollectOrQueue(transition, (deferred) -> {
                final ActionChain chain = mAtmService.mChainTracker.start("setUser", transition);
                final TransitionRequestInfo.UserChange userChange =
                           new TransitionRequestInfo.UserChange(oldUserId, newUserId);
                controller.requestStartUserTransition(chain.getTransition(), userChange);
                switchUserRunnable.run();
                mAtmService.mChainTracker.end();
            });
        }
    }

    private void switchUserInternal(@UserIdInt int newUserId) {
        synchronized (mGlobalLock) {
            // Hide windows that should not be seen by the new user.
            mRoot.switchUser(newUserId);
            mWindowPlacerLocked.performSurfacePlacement();

            // Notify whether the root docked task exists for the current user
            final DisplayContent displayContent = getDefaultDisplayContentLocked();

            if (mDisplayReady) {
                // If the display is already prepared, update the density.
                // Otherwise, we'll update it when it's prepared.
                final int forcedDensity = getForcedDisplayDensityForUserLocked(newUserId);
                final int targetDensity = forcedDensity != 0
                        ? forcedDensity : displayContent.getInitialDisplayDensity();
                displayContent.setForcedDensity(targetDensity, UserHandle.USER_CURRENT);

                // Because DisplayWindowSettingsProvider.mOverrideSettings has been reset for
                // the new user, we need to update DisplayWindowSettings.mShouldShowSystemDecors
                // to ensure it reflects the latest value.
                if (DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue()) {
                    final int displayCount = mRoot.mChildren.size();
                    for (int i = 0; i < displayCount; ++i) {
                        final DisplayContent dc = mRoot.mChildren.get(i);
                        dc.updateShouldShowSystemDecorations();
                    }
                }
            }
        }
    }

    /**
     * Moves a user to the foreground, either by starting its home activity or restoring its
     * previous tasks.
     */
    public void moveUserToForeground(@UserIdInt int newUserId, UserState uss, String reason) {
        boolean homeInFront = mAtmService.mInternal.switchUser(newUserId, uss);
        if (homeInFront) {
            mAtmService.mInternal.startHomeActivity(newUserId, reason);
        } else {
            mAtmService.mInternal.resumeTopActivities(false /* scheduleIdle */);
        }
    }

    /* Called by WindowState */
    boolean isUserVisible(@UserIdInt int userId) {
        return mUmInternal.isUserVisible(userId);
    }

    @UserIdInt int getUserAssignedToDisplay(int displayId) {
        return mUmInternal.getUserAssignedToDisplay(displayId);
    }

    boolean shouldPlacePrimaryHomeOnDisplay(int displayId) {
        int userId = mUmInternal.getUserAssignedToDisplay(displayId);
        return shouldPlacePrimaryHomeOnDisplay(displayId, userId);
    }

    boolean shouldPlacePrimaryHomeOnDisplay(int displayId, int userId) {
        return mUmInternal.getMainDisplayAssignedToUser(userId) == displayId;
    }

    public void enableScreenAfterBoot() {
        synchronized (mGlobalLock) {
            ProtoLog.i(WM_DEBUG_BOOT, "enableScreenAfterBoot: mDisplayEnabled=%b "
                            + "mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b",
                    mDisplayEnabled, mForceDisplayEnabled, mShowingBootMessages, mSystemBooted);
            if (mSystemBooted) {
                return;
            }
            mSystemBooted = true;
            hideBootMessagesLocked();
            // If the screen still doesn't come up after 30 seconds, give
            // up and turn it on.
            mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000 * Build.HW_TIMEOUT_MULTIPLIER);
        }

        mPolicy.systemBooted();

        performEnableScreen();
    }

    @Override
    public void enableScreenIfNeeded() {
        synchronized (mGlobalLock) {
            enableScreenIfNeededLocked();
        }
    }

    void enableScreenIfNeededLocked() {
        ProtoLog.i(WM_DEBUG_BOOT, "enableScreenIfNeededLocked: mDisplayEnabled=%b "
                        + "mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b",
                mDisplayEnabled, mForceDisplayEnabled, mShowingBootMessages, mSystemBooted);
        if (mDisplayEnabled) {
            return;
        }
        if (!mSystemBooted && !mShowingBootMessages) {
            return;
        }
        mH.sendEmptyMessage(H.ENABLE_SCREEN);
    }

    public void performBootTimeout() {
        synchronized (mGlobalLock) {
            if (mDisplayEnabled) {
                return;
            }
            ProtoLog.w(WM_ERROR, "***** BOOT TIMEOUT: forcing display enabled");
            mForceDisplayEnabled = true;
        }
        performEnableScreen();
    }

    /**
     * Called when System UI has been started.
     */
    public void onSystemUiStarted() {
        mPolicy.onSystemUiStarted();
    }

    private void performEnableScreen() {
        synchronized (mGlobalLock) {
            ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: mDisplayEnabled=%b"
                            + " mForceDisplayEnabled=%b" + " mShowingBootMessages=%b"
                            + " mSystemBooted=%b", mDisplayEnabled, mForceDisplayEnabled,
                    mShowingBootMessages, mSystemBooted);
            if (mDisplayEnabled) {
                return;
            }
            if (!mSystemBooted && !mShowingBootMessages) {
                return;
            }

            if (!mShowingBootMessages && !mPolicy.canDismissBootAnimation()) {
                return;
            }

            // Don't enable the screen until all existing windows have been drawn.
            if (!mForceDisplayEnabled) {
                if (mBootWaitForWindowsStartTime < 0) {
                    // First time we will start waiting for all windows to be drawn.
                    mBootWaitForWindowsStartTime = SystemClock.elapsedRealtime();
                }
                for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
                    if (mRoot.getChildAt(i).shouldWaitForSystemDecorWindowsOnBoot()) {
                        return;
                    }
                }
                long waitTime = SystemClock.elapsedRealtime() - mBootWaitForWindowsStartTime;
                mBootWaitForWindowsStartTime = -1;
                if (waitTime > 10) {
                    ProtoLog.i(WM_DEBUG_BOOT,
                            "performEnableScreen: Waited %dms for all windows to be drawn",
                            waitTime);
                }
            }

            if (!mBootAnimationStopped) {
                Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
                // stop boot animation
                // formerly we would just kill the process, but we now ask it to exit so it
                // can choose where to stop the animation.
                SystemProperties.set("service.bootanim.exit", "1");
                mBootAnimationStopped = true;
            }

            if (!mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) {
                ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: Waiting for anim complete");
                return;
            }

            if (!SurfaceControl.bootFinished()) {
                ProtoLog.w(WM_ERROR, "performEnableScreen: bootFinished() failed.");
                return;
            }

            EventLogTags.writeWmBootAnimationDone(SystemClock.uptimeMillis());
            Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
            mDisplayEnabled = true;
            ProtoLog.i(WM_DEBUG_SCREEN_ON, "******************** ENABLING SCREEN!");

            // Enable input dispatch.
            mInputManagerCallback.setEventDispatchingLw(mEventDispatchingEnabled);
        }

        try {
            mActivityManager.bootAnimationComplete();
        } catch (RemoteException e) {
        }

        mPolicy.enableScreenAfterBoot();

        // Make sure the last requested orientation has been applied.
        updateRotationUnchecked(false, false);

        synchronized (mGlobalLock) {
            mAtmService.getTransitionController().mIsWaitingForDisplayEnabled = false;
            ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
                    "Notified TransitionController that the display is ready.");
        }
    }

    private boolean checkBootAnimationCompleteLocked() {
        if (SystemService.isRunning(BOOT_ANIMATION_SERVICE)) {
            mH.removeMessages(H.CHECK_IF_BOOT_ANIMATION_FINISHED);
            mH.sendEmptyMessageDelayed(H.CHECK_IF_BOOT_ANIMATION_FINISHED,
                    BOOT_ANIMATION_POLL_INTERVAL);
            ProtoLog.i(WM_DEBUG_BOOT, "checkBootAnimationComplete: Waiting for anim complete");
            return false;
        }
        ProtoLog.i(WM_DEBUG_BOOT, "checkBootAnimationComplete: Animation complete!");
        return true;
    }

    public void showBootMessage(final CharSequence msg, final boolean always) {
        boolean first = false;
        synchronized (mGlobalLock) {
            ProtoLog.i(WM_DEBUG_BOOT, "showBootMessage: msg=%s always=%b"
                            + " mAllowBootMessages=%b mShowingBootMessages=%b"
                            + " mSystemBooted=%b", msg, always, mAllowBootMessages,
                    mShowingBootMessages, mSystemBooted);
            if (!mAllowBootMessages) {
                return;
            }
            if (!mShowingBootMessages) {
                if (!always) {
                    return;
                }
                first = true;
            }
            if (mSystemBooted) {
                return;
            }
            mShowingBootMessages = true;
            mPolicy.showBootMessage(msg, always);
        }
        if (first) {
            performEnableScreen();
        }
    }

    public void hideBootMessagesLocked() {
        ProtoLog.i(WM_DEBUG_BOOT, "hideBootMessagesLocked: mDisplayEnabled=%b"
                        + " mForceDisplayEnabled=%b mShowingBootMessages=%b"
                        + " mSystemBooted=%b", mDisplayEnabled, mForceDisplayEnabled,
                mShowingBootMessages, mSystemBooted);
        if (mShowingBootMessages) {
            mShowingBootMessages = false;
            mPolicy.hideBootMessages();
        }
    }

    /**
     * Sets the touch mode state.
     *
     * If {@code com.android.internal.R.bool.config_perDisplayFocusEnabled} is set to true, then
     * only the display represented by the {@code displayId} parameter will be requested to switch
     * the touch mode state. Otherwise all displays that do not maintain their own focus and touch
     * mode will be requested to switch their touch mode state (disregarding {@code displayId}
     * parameter).
     *
     * To be able to change touch mode state, the caller must either own the focused window, or must
     * have the {@link android.Manifest.permission#MODIFY_TOUCH_MODE_STATE} permission. Instrumented
     * process, sourced with {@link android.Manifest.permission#MODIFY_TOUCH_MODE_STATE}, may switch
     * touch mode at any time.
     *
     * @param inTouch   the touch mode to set
     * @param displayId the target display id
     */
    @Override // Binder call
    public void setInTouchMode(boolean inTouch, int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (mPerDisplayFocusEnabled && (displayContent == null
                    || displayContent.isInTouchMode() == inTouch)) {
                return;
            }
            final boolean displayHasOwnTouchMode =
                    displayContent != null && displayContent.hasOwnFocus();
            if (displayHasOwnTouchMode && displayContent.isInTouchMode() == inTouch) {
                return;
            }
            final int pid = Binder.getCallingPid();
            final int uid = Binder.getCallingUid();
            final boolean hasPermission = hasTouchModePermission(pid);
            final long token = Binder.clearCallingIdentity();
            try {
                // If mPerDisplayFocusEnabled is set or the display maintains its own touch mode,
                // then just update the display pointed by displayId
                if (mPerDisplayFocusEnabled || displayHasOwnTouchMode) {
                    if (mInputManager.setInTouchMode(inTouch, pid, uid, hasPermission, displayId)) {
                        displayContent.setInTouchMode(inTouch);
                    }
                } else {  // Otherwise update all displays that do not maintain their own touch mode
                    final int displayCount = mRoot.mChildren.size();
                    for (int i = 0; i < displayCount; ++i) {
                        DisplayContent dc = mRoot.mChildren.get(i);
                        if (dc.isInTouchMode() == inTouch || dc.hasOwnFocus()) {
                            continue;
                        }
                        if (mInputManager.setInTouchMode(inTouch, pid, uid, hasPermission,
                                dc.mDisplayId)) {
                            dc.setInTouchMode(inTouch);
                        }
                    }
                }
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }
    }

    /**
     * Sets the touch mode state forcibly on all displays (disregarding both the value of
     * {@code com.android.internal.R.bool.config_perDisplayFocusEnabled} and whether the display
     * maintains its own focus and touch mode).
     *
     * @param inTouch the touch mode to set
     */
    @Override // Binder call
    public void setInTouchModeOnAllDisplays(boolean inTouch) {
        final int pid = Binder.getCallingPid();
        final int uid = Binder.getCallingUid();
        final boolean hasPermission = hasTouchModePermission(pid);
        final long token = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                for (int i = 0; i < mRoot.mChildren.size(); ++i) {
                    DisplayContent dc = mRoot.mChildren.get(i);
                    if (dc.isInTouchMode() != inTouch
                            && mInputManager.setInTouchMode(inTouch, pid, uid, hasPermission,
                            dc.mDisplayId)) {
                        dc.setInTouchMode(inTouch);
                    }
                }
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    private boolean hasTouchModePermission(int pid) {
        return mAtmService.instrumentationSourceHasPermission(pid, MODIFY_TOUCH_MODE_STATE)
                || checkCallingPermission(MODIFY_TOUCH_MODE_STATE, "setInTouchMode()",
                /* printlog= */ false);
    }

    /**
     * Returns the touch mode state for the display id passed as argument.
     *
     * This method will return the default touch mode state (represented by
     * {@code com.android.internal.R.bool.config_defaultInTouchMode}) if the display passed as
     * argument is no longer registered in {@RootWindowContainer}).
     */
    @Override  // Binder call
    public boolean isInTouchMode(int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                return mContext.getResources().getBoolean(R.bool.config_defaultInTouchMode);
            }
            return displayContent.isInTouchMode();
        }
    }

    @VisibleForTesting
    void showEmulatorDisplayOverlayIfNeeded() {
        if (shouldShowEmulatorDisplayOverlay()) {
            mH.sendMessage(mH.obtainMessage(H.SHOW_EMULATOR_DISPLAY_OVERLAY));
        }
    }

    private boolean shouldShowEmulatorDisplayOverlay() {
        return enableCircularEmulatorDisplayOverlay()
                || enableScreenBrightnessEmulatorDisplayOverlay();
    }

    @VisibleForTesting
    boolean enableCircularEmulatorDisplayOverlay() {
        return Build.IS_EMULATOR
                && mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_windowEnableCircularEmulatorDisplayOverlay)
                && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false);
    }

    @VisibleForTesting
    boolean enableScreenBrightnessEmulatorDisplayOverlay() {
        return Build.IS_EMULATOR
                && mContext.getResources().getBoolean(
                R.bool.config_windowEnableScreenBrightnessEmulatorDisplayOverlay);
    }

    @VisibleForTesting
    void showEmulatorDisplayOverlay() {
        synchronized (mGlobalLock) {

            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG_WM, ">>> showEmulatorDisplayOverlay");
            if (mEmulatorDisplayOverlay == null) {
                mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(mContext,
                        this,
                        getDefaultDisplayContentLocked(),
                        mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER)
                                * TYPE_LAYER_MULTIPLIER + 10,
                        mTransaction,
                        enableCircularEmulatorDisplayOverlay(),
                        enableScreenBrightnessEmulatorDisplayOverlay());
            }
            mEmulatorDisplayOverlay.setVisibility(true, mTransaction);
            mTransaction.apply();
        }
    }

    // TODO: more accounting of which pid(s) turned it on, keep count,
    // only allow disables from pids which have count on, etc.
    @Override
    public void showStrictModeViolation(boolean on) {
        final int pid = Binder.getCallingPid();
        if (on) {
            // Show the visualization, and enqueue a second message to tear it
            // down if we don't hear back from the app.
            mH.sendMessage(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, 1, pid));
            mH.sendMessageDelayed(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, 0, pid),
                    DateUtils.SECOND_IN_MILLIS);
        } else {
            mH.sendMessage(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, 0, pid));
        }
    }

    private void showStrictModeViolation(int arg, int pid) {
        final boolean on = arg != 0;
        synchronized (mGlobalLock) {
            // Ignoring requests to enable the red border from clients which aren't on screen.
            // (e.g. Broadcast Receivers in the background..)
            if (on && !mRoot.canShowStrictModeViolation(pid)) {
                return;
            }

            if (SHOW_VERBOSE_TRANSACTIONS) Slog.i(TAG_WM, ">>> showStrictModeViolation");
            // TODO: Modify this to use the surface trace once it is not going baffling.
            // b/31532461
            // TODO(multi-display): support multiple displays
            if (mStrictModeFlash == null) {
                mStrictModeFlash = new StrictModeFlash(getDefaultDisplayContentLocked(),
                        mTransaction);
            }
            mStrictModeFlash.setVisibility(on, mTransaction);
            mTransaction.apply();
        }
    }

    @Override
    public void setStrictModeVisualIndicatorPreference(String value) {
        SystemProperties.set(StrictMode.VISUAL_PROPERTY, value);
    }

    @Override
    public Bitmap screenshotWallpaper() {
        if (!checkCallingPermission(READ_FRAME_BUFFER, "screenshotWallpaper()")) {
            throw new SecurityException("Requires READ_FRAME_BUFFER permission");
        }
        try {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
            synchronized (mGlobalLock) {
                // TODO(b/115486823) Screenshot at secondary displays if needed.
                final DisplayContent dc = mRoot.getDisplayContent(DEFAULT_DISPLAY);
                return dc.mWallpaperController.screenshotWallpaperLocked();
            }
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

    @Override
    public SurfaceControl mirrorWallpaperSurface(int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent dc = mRoot.getDisplayContent(displayId);
            return dc.mWallpaperController.mirrorWallpaperSurface();
        }
    }

    private void requestAssistScreenshotInternal(final IAssistDataReceiver receiver,
            int displayId) {
        final ScreenshotHardwareBuffer shb = takeAssistScreenshot(/* predicate= */ null,
                displayId);
        final Bitmap bm = shb != null ? shb.asBitmap() : null;
        FgThread.getHandler().post(() -> {
            try {
                receiver.onHandleAssistScreenshot(bm);
            } catch (RemoteException e) {
            }
        });
    }

    @Nullable
    private ScreenshotHardwareBuffer takeAssistScreenshot(
            @Nullable ToBooleanFunction<WindowState> predicate, int displayId) {
        if (!checkCallingPermission(READ_FRAME_BUFFER, "requestAssistScreenshot()")) {
            throw new SecurityException("Requires READ_FRAME_BUFFER permission");
        }

        ScreenCaptureInternal.LayerCaptureArgs captureArgs;
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                if (DEBUG_SCREENSHOT) {
                    Slog.i(TAG_WM, "Screenshot returning null. No Display for displayId="
                            + displayId);
                }
                captureArgs = null;
            } else {
                if (DEBUG_SCREENSHOT) {
                    Slog.i(TAG_WM, "Taking assist screenshot for displayId=" + displayId
                            + " display state="
                            + Display.stateToString(displayContent.getDisplayInfo().state));
                }
                captureArgs = displayContent.getLayerCaptureArgs(predicate,
                        /*useWindowingLayerAsScreenshotRoot*/ enableLppAssistInvocationEffect());
            }
        }

        final ScreenshotHardwareBuffer screenshotBuffer;
        if (captureArgs != null) {
            ScreenCaptureInternal.SynchronousScreenCaptureListener syncScreenCapture =
                    ScreenCaptureInternal.createSyncCaptureListener();

            ScreenCaptureInternal.captureLayers(captureArgs, syncScreenCapture);

            screenshotBuffer = syncScreenCapture.getBuffer();
        } else {
            screenshotBuffer = null;
        }

        if (screenshotBuffer == null) {
            Slog.w(TAG_WM, "Failed to take screenshot for displayId=" + displayId);
        }

        return screenshotBuffer;
    }

    /**
     * Requests a screenshot to be taken for Assist purposes.
     *
     * This method initiates the process of capturing the current screen content and delivering it
     * to the provided {@link IAssistDataReceiver}.
     *
     * @param receiver The {@link IAssistDataReceiver} that will receive the screenshot bitmap. Must
     * not be null.
     */
    @Override
    public void requestAssistScreenshot(final IAssistDataReceiver receiver) {
        requestAssistScreenshotInternal(receiver, DEFAULT_DISPLAY);
    }

    /**
     * Generates and returns an up-to-date {@link Bitmap} for the specified taskId.
     *
     * @param taskId The task ID of the task for which a Bitmap is requested.
     * @param layerCaptureArgsBuilder A {@link ScreenCaptureInternal.LayerCaptureArgs.Builder} with
     *     arguments for how to capture the Bitmap. The caller can specify any arguments, but this
     *     method will ensure that the specified task's SurfaceControl is used and the crop is set
     *     to the bounds of that task.
     * @return The Bitmap, or null if no task with the specified ID can be found or the bitmap could
     *     not be generated.
     */
    @Nullable
    public Bitmap captureTaskBitmap(
            int taskId,
            @NonNull ScreenCaptureInternal.LayerCaptureArgs.Builder layerCaptureArgsBuilder) {
        if (mTaskSnapshotController.shouldDisableSnapshots()) {
            return null;
        }

        synchronized (mGlobalLock) {
            final Task task = mRoot.anyTaskForId(taskId);
            if (task == null) {
                return null;
            }

            // The bounds returned by the task represent the task's position on the screen. However,
            // we need to specify a crop relative to the task's surface control. Therefore, shift
            // the task's bounds to 0,0 so that we have the correct size and position within the
            // task's surface control.
            task.getBounds(mTmpRect);
            mTmpRect.offsetTo(0, 0);

            final SurfaceControl sc = task.getSurfaceControl();
            final ScreenshotHardwareBuffer buffer =
                    ScreenCaptureInternal.captureLayers(
                            layerCaptureArgsBuilder.setLayer(sc).setSourceCrop(mTmpRect).build());
            if (buffer == null) {
                Slog.w(TAG, "Could not get screenshot buffer for taskId: " + taskId);
                return null;
            }

            return buffer.asBitmap();
        }
    }

    /**
     * In case a task write/delete operation was lost because the system crashed, this makes sure to
     * clean up the directory to remove obsolete files.
     *
     * @param persistentTaskIds A set of task ids that exist in our in-memory model.
     * @param runningUserIds The ids of the list of users that have tasks loaded in our in-memory
     *                       model.
     */
    void removeObsoleteTaskFiles(ArraySet<Integer> persistentTaskIds, int[] runningUserIds) {
        mTaskSnapshotController.removeObsoleteTaskFiles(persistentTaskIds, runningUserIds);
    }

    @Override
    public void setFixedToUserRotation(int displayId, int fixedToUserRotation) {
        if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
                "setFixedToUserRotation()")) {
            throw new SecurityException("Requires SET_ORIENTATION permission");
        }
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent display = mRoot.getDisplayContent(displayId);
                if (display == null) {
                    Slog.w(TAG, "Trying to set fixed to user rotation for a missing display.");
                    return;
                }
                display.getDisplayRotation().setFixedToUserRotation(fixedToUserRotation);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    int getFixedToUserRotation(int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent display = mRoot.getDisplayContent(displayId);
            if (display == null) {
                Slog.w(TAG, "Trying to get fixed to user rotation for a missing display.");
                return -1;
            }
            return display.getDisplayRotation().getFixedToUserRotationMode();
        }
    }

    @Override
    public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) {
        if (!checkCallingPermission(
                android.Manifest.permission.SET_ORIENTATION, "setIgnoreOrientationRequest()")) {
            throw new SecurityException("Requires SET_ORIENTATION permission");
        }

        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent display = mRoot.getDisplayContent(displayId);
                if (display == null) {
                    Slog.w(TAG, "Trying to setIgnoreOrientationRequest() for a missing display.");
                    return;
                }
                display.setIgnoreOrientationRequest(ignoreOrientationRequest);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @Override
    public boolean getIgnoreOrientationRequest(int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent display = mRoot.getDisplayContent(displayId);
            if (display == null) {
                Slog.w(TAG, "Trying to getIgnoreOrientationRequest() for a missing display.");
                return false;
            }
            return display.getIgnoreOrientationRequest();
        }
    }

    @Nullable
    Boolean resetIgnoreOrientationRequest(int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent display = mRoot.getDisplayContent(displayId);
            if (display == null) {
                return null;
            }
            display.mHasSetIgnoreOrientationRequest = false;
            // Clear existing override settings.
            mDisplayWindowSettings.setIgnoreOrientationRequest(display,
                    null /* ignoreOrientationRequest */);
            // Reload from settings in case there is built-in config.
            mDisplayWindowSettings.applyRotationSettingsToDisplayLocked(display);
            return display.getIgnoreOrientationRequest();
        }
    }

    /**
     * Controls whether ignore orientation request logic in {@link DisplayArea} is disabled
     * at runtime and how to optionally map some requested orientations to others.
     *
     * <p>Note: this assumes that {@link #mGlobalLock} is held by the caller.
     *
     * @param isIgnoreOrientationRequestDisabled when {@code true}, the system always ignores the
     *                   value of {@link DisplayArea#getIgnoreOrientationRequest} and app requested
     *                   orientation is respected.
     * @param fromOrientations The orientations we want to map to the correspondent orientations
     *                        in toOrientation.
     * @param toOrientations The orientations we map to the ones in fromOrientations at  the same
     *                       index
     */
    void setOrientationRequestPolicy(boolean isIgnoreOrientationRequestDisabled,
            @Nullable int[] fromOrientations, @Nullable int[] toOrientations) {
        mOrientationMapping.clear();
        if (fromOrientations != null && toOrientations != null
                && fromOrientations.length == toOrientations.length) {
            for (int i = 0; i < fromOrientations.length; i++) {
                mOrientationMapping.put(fromOrientations[i], toOrientations[i]);
            }
        }
        if (isIgnoreOrientationRequestDisabled == mIsIgnoreOrientationRequestDisabled) {
            return;
        }
        mIsIgnoreOrientationRequestDisabled = isIgnoreOrientationRequestDisabled;
        for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
            mRoot.getChildAt(i).onIsIgnoreOrientationRequestDisabledChanged();
        }
    }

    /**
     * When {@link mIsIgnoreOrientationRequestDisabled} is {@value true} this method returns the
     * orientation to use in place of the one in input. It returns the same requestedOrientation in
     * input otherwise.
     *
     * @param requestedOrientation The orientation that can be mapped.
     * @return The orientation to use in place of requestedOrientation.
     */
    int mapOrientationRequest(int requestedOrientation) {
        if (!mIsIgnoreOrientationRequestDisabled) {
            return requestedOrientation;
        }
        return mOrientationMapping.get(requestedOrientation, requestedOrientation);
    }

    /**
     * Whether the system ignores the value of {@link DisplayArea#getIgnoreOrientationRequest} and
     * app requested orientation is respected.
     *
     * <p>Note: this assumes that {@link #mGlobalLock} is held by the caller.
     */
    boolean isIgnoreOrientationRequestDisabled() {
        return mIsIgnoreOrientationRequestDisabled
                || !mAppCompatConfiguration.isIgnoreOrientationRequestAllowed();
    }

    @Override
    public void setDeviceStateAutoRotateSetting(int deviceState, boolean autoRotate) {
        if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
                "setDeviceStateAutoRotateSetting()")) {
            throw new SecurityException("Requires SET_ORIENTATION permission");
        }
        if (!enableDeviceStateAutoRotateSettingRefactor()) {
            throw new UnsupportedOperationException(
                    "API setDeviceStateAutoRotateSetting should not be used when "
                            + "enableDeviceStateAutoRotateSettingRefactor is disabled");
        }
        synchronized (mGlobalLock) {
            final DisplayContent display = mRoot.getDefaultDisplay();
            display.getDisplayRotation().requestDeviceStateAutoRotateSettingChange(deviceState,
                    autoRotate);
        }
    }

    @Override
    public void freezeRotation(int rotation, String caller) {
        freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation, caller);
    }

    /**
     * Freeze rotation changes.  (Enable "rotation lock".)
     * Persists across reboots.
     * @param displayId The ID of the display to freeze.
     * @param rotation The desired rotation to freeze to, or -1 to use the current rotation.
     */
    @Override
    public void freezeDisplayRotation(int displayId, int rotation, String caller) {
        // TODO(multi-display): Track which display is rotated.
        if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
                "freezeRotation()")) {
            throw new SecurityException("Requires SET_ORIENTATION permission");
        }
        if (rotation < -1 || rotation > Surface.ROTATION_270) {
            throw new IllegalArgumentException("Rotation argument must be -1 or a valid "
                    + "rotation constant.");
        }
        ProtoLog.v(WM_DEBUG_ORIENTATION,
                "freezeDisplayRotation: current rotation=%d, new rotation=%d, caller=%s",
                getDefaultDisplayRotation(), rotation, caller);

        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent display = mRoot.getDisplayContent(displayId);
                if (display == null) {
                    Slog.w(TAG, "Trying to freeze rotation for a missing display.");
                    return;
                }
                display.getDisplayRotation().freezeRotation(rotation, caller);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }

        updateRotationUnchecked(false, false);
    }

    @Override
    public void thawRotation(String caller) {
        thawDisplayRotation(Display.DEFAULT_DISPLAY, caller);
    }

    /**
     * Thaw rotation changes.  (Disable "rotation lock".)
     * Persists across reboots.
     */
    @Override
    public void thawDisplayRotation(int displayId, String caller) {
        if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
                "thawRotation()")) {
            throw new SecurityException("Requires SET_ORIENTATION permission");
        }

        ProtoLog.v(WM_DEBUG_ORIENTATION, "thawRotation: mRotation=%d, caller=%s",
                getDefaultDisplayRotation(), caller);

        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent display = mRoot.getDisplayContent(displayId);
                if (display == null) {
                    Slog.w(TAG, "Trying to thaw rotation for a missing display.");
                    return;
                }
                display.getDisplayRotation().thawRotation(caller);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }

        updateRotationUnchecked(false, false);
    }

    @Override
    public boolean isRotationFrozen() {
        return isDisplayRotationFrozen(Display.DEFAULT_DISPLAY);
    }

    @Override
    public boolean isDisplayRotationFrozen(int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent display = mRoot.getDisplayContent(displayId);
            if (display == null) {
                Slog.w(TAG, "Trying to check if rotation is frozen on a missing display.");
                return false;
            }
            return display.getDisplayRotation().isRotationFrozen();
        }
    }

    @Override
    public void setRotationAtAngleIfAllowed(int rotation, String caller) {
        if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,
                "setRotationAtAngleIfAllowed()")) {
            throw new SecurityException("Requires SET_ORIENTATION permission");
        }
        if (!enableDeviceStateAutoRotateSettingRefactor()) {
            throw new UnsupportedOperationException(
                    "API setRotationAtAngleIfAllowed should not be used when "
                            + "enableDeviceStateAutoRotateSettingRefactor is disabled");
        }
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent display = mRoot.getDefaultDisplay();
                display.getDisplayRotation().setRotationAtAngleIfAllowed(rotation, caller);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @Override
    public int getDisplayUserRotation(int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent display = mRoot.getDisplayContent(displayId);
            if (display == null) {
                Slog.w(TAG, "Trying to get user rotation of a missing display.");
                return -1;
            }
            return display.getDisplayRotation().getUserRotation();
        }
    }

    /**
     * Recalculate the current rotation.
     *
     * Called by the window manager policy whenever the state of the system changes
     * such that the current rotation might need to be updated, such as when the
     * device is docked or rotated into a new posture.
     */
    @Override
    public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {
        updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
    }

    private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
        ProtoLog.v(WM_DEBUG_ORIENTATION, "updateRotationUnchecked:"
                        + " alwaysSendConfiguration=%b forceRelayout=%b",
                alwaysSendConfiguration, forceRelayout);

        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation");

        final long origId = Binder.clearCallingIdentity();

        try {
            synchronized (mGlobalLock) {
                boolean layoutNeeded = false;
                final int displayCount = mRoot.mChildren.size();
                for (int i = 0; i < displayCount; ++i) {
                    final DisplayContent displayContent = mRoot.mChildren.get(i);
                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
                    final boolean rotationChanged = displayContent.updateRotationUnchecked();
                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);

                    if (rotationChanged) {
                        mAtmService.getTaskChangeNotificationController()
                                .notifyOnActivityRotation(displayContent.mDisplayId);
                    }

                    final boolean pendingRemoteDisplayChange = rotationChanged
                            && (displayContent.mRemoteDisplayChangeController
                                    .isWaitingForRemoteDisplayChange()
                            || displayContent.mTransitionController.isCollecting());
                    // Even if alwaysSend, we are waiting for a transition or remote to provide
                    // updated configuration, so we can't update configuration yet.
                    if (!pendingRemoteDisplayChange) {
                        // The layout-needed flag will be set if there is a rotation change, so
                        // only set it if the caller requests to force relayout.
                        if (forceRelayout) {
                            displayContent.setLayoutNeeded();
                            layoutNeeded = true;
                        }
                        if (rotationChanged || alwaysSendConfiguration) {
                            displayContent.sendNewConfiguration();
                        }
                    }
                }

                if (layoutNeeded) {
                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                            "updateRotation: performSurfacePlacement");
                    mWindowPlacerLocked.performSurfacePlacement();
                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

    @Override
    public int getDefaultDisplayRotation() {
        synchronized (mGlobalLock) {
            return getDefaultDisplayContentLocked().getRotation();
        }
    }

    @Override
    public void setDisplayChangeWindowController(IDisplayChangeWindowController controller) {
        mAtmService.enforceTaskPermission("setDisplayWindowRotationController");
        try {
            synchronized (mGlobalLock) {
                if (mDisplayChangeController != null) {
                    mDisplayChangeController.asBinder().unlinkToDeath(
                            mDisplayChangeControllerDeath, 0);
                    mDisplayChangeController = null;
                }
                controller.asBinder().linkToDeath(mDisplayChangeControllerDeath, 0);
                mDisplayChangeController = controller;
            }
        } catch (RemoteException e) {
            throw new RuntimeException("Unable to set rotation controller", e);
        }
    }

    @EnforcePermission(android.Manifest.permission.MANAGE_APP_TOKENS)
    @Override
    public SurfaceControl addShellRoot(int displayId, IWindow client,
            @WindowManager.ShellRootLayer int shellRootLayer) {
        addShellRoot_enforcePermission();
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent dc = mRoot.getDisplayContent(displayId);
                if (dc == null) {
                    return null;
                }
                return dc.addShellRoot(client, shellRootLayer);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @EnforcePermission(android.Manifest.permission.MANAGE_APP_TOKENS)
    @Override
    public void setShellRootAccessibilityWindow(int displayId,
            @WindowManager.ShellRootLayer int shellRootLayer, IWindow target) {
        setShellRootAccessibilityWindow_enforcePermission();
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent dc = mRoot.getDisplayContent(displayId);
                if (dc == null) {
                    return;
                }
                ShellRoot root = dc.mShellRoots.get(shellRootLayer);
                if (root == null) {
                    return;
                }
                root.setAccessibilityWindow(target);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @EnforcePermission(android.Manifest.permission.MANAGE_APP_TOKENS)
    @Override
    public void setDisplayWindowInsetsController(
            int displayId, IDisplayWindowInsetsController insetsController) {
        setDisplayWindowInsetsController_enforcePermission();
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent dc = mRoot.getDisplayContent(displayId);
                if (dc == null) {
                    return;
                }
                dc.setRemoteInsetsController(insetsController);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @EnforcePermission(android.Manifest.permission.MANAGE_APP_TOKENS)
    @Override
    public void updateDisplayWindowRequestedVisibleTypes(int displayId,
            @InsetsType int visibleTypes, @InsetsType int mask,
            @Nullable ImeTracker.Token statsToken) {
        updateDisplayWindowRequestedVisibleTypes_enforcePermission();
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent dc = mRoot.getDisplayContent(displayId);
                if (dc == null || dc.mRemoteInsetsControlTarget == null) {
                    ImeTracker.forLogging().onFailed(statsToken,
                            ImeTracker.PHASE_WM_UPDATE_DISPLAY_WINDOW_REQUESTED_VISIBLE_TYPES);
                    return;
                }
                ImeTracker.forLogging().onProgress(statsToken,
                        ImeTracker.PHASE_WM_UPDATE_DISPLAY_WINDOW_REQUESTED_VISIBLE_TYPES);
                final @InsetsType int changedTypes =
                        dc.mRemoteInsetsControlTarget.updateRequestedVisibleTypes(
                                visibleTypes, mask);
                // TODO(b/353463205) the statsToken shouldn't be null as it is used later in the
                //  IME provider. Check if we have to create a new request here, if null.
                dc.getInsetsStateController().onRequestedVisibleTypesChanged(
                        dc.mRemoteInsetsControlTarget, changedTypes, statsToken);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @EnforcePermission(android.Manifest.permission.MANAGE_APP_TOKENS)
    @Override
    public void updateDisplayWindowAnimatingTypes(int displayId, @InsetsType int animatingTypes,
            @Nullable ImeTracker.Token statsToken) {
        updateDisplayWindowAnimatingTypes_enforcePermission();
        if (android.view.inputmethod.Flags.reportAnimatingInsetsTypes()) {
            final long origId = Binder.clearCallingIdentity();
            try {
                synchronized (mGlobalLock) {
                    final DisplayContent dc = mRoot.getDisplayContent(displayId);
                    if (dc == null || dc.mRemoteInsetsControlTarget == null) {
                        ImeTracker.forLogging().onFailed(statsToken,
                                ImeTracker.PHASE_WM_UPDATE_DISPLAY_WINDOW_ANIMATING_TYPES);
                        return;
                    }
                    ImeTracker.forLogging().onProgress(statsToken,
                            ImeTracker.PHASE_WM_UPDATE_DISPLAY_WINDOW_ANIMATING_TYPES);
                    dc.mRemoteInsetsControlTarget.setAnimatingTypes(animatingTypes, statsToken);
                }
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
        }
    }

    @Override
    public int watchRotation(IRotationWatcher watcher, int displayId) {
        final DisplayContent displayContent;
        synchronized (mGlobalLock) {
            displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                throw new IllegalArgumentException("Trying to register rotation event "
                        + "for invalid display: " + displayId);
            }
            mRotationWatcherController.registerDisplayRotationWatcher(watcher, displayId);
            return displayContent.getRotation();
        }
    }

    @Override
    public void removeRotationWatcher(IRotationWatcher watcher) {
        synchronized (mGlobalLock) {
            mRotationWatcherController.removeRotationWatcher(watcher);
        }
    }

    @Surface.Rotation
    @Override
    public int registerProposedRotationListener(IBinder contextToken, IRotationWatcher listener) {
        synchronized (mGlobalLock) {
            final WindowContainer<?> wc =
                    mRotationWatcherController.getAssociatedWindowContainer(contextToken);
            if (wc == null) {
                Slog.w(TAG, "Register rotation listener from non-existing token, uid="
                        + Binder.getCallingUid());
                return Surface.ROTATION_0;
            }
            mRotationWatcherController.registerProposedRotationListener(listener, contextToken);
            final WindowOrientationListener orientationListener =
                    wc.mDisplayContent.getDisplayRotation().getOrientationListener();
            if (orientationListener != null) {
                // It may be -1 if sensor is disabled.
                final int rotation = orientationListener.getProposedRotation();
                if (rotation >= Surface.ROTATION_0) {
                    return rotation;
                }
            }
            return wc.getWindowConfiguration().getRotation();
        }
    }

    @Override
    public boolean registerWallpaperVisibilityListener(IWallpaperVisibilityListener listener,
            int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                throw new IllegalArgumentException("Trying to register visibility event "
                        + "for invalid display: " + displayId);
            }
            mWallpaperVisibilityListeners.registerWallpaperVisibilityListener(listener, displayId);
            return displayContent.mWallpaperController.isWallpaperVisible();
        }
    }

    @Override
    public void unregisterWallpaperVisibilityListener(IWallpaperVisibilityListener listener,
            int displayId) {
        synchronized (mGlobalLock) {
            mWallpaperVisibilityListeners
                    .unregisterWallpaperVisibilityListener(listener, displayId);
        }
    }

    @Override
    public void registerSystemGestureExclusionListener(ISystemGestureExclusionListener listener,
            int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                throw new IllegalArgumentException(
                        "Trying to register system gesture exclusion event for invalid display: "
                                + displayId);
            }
            displayContent.registerSystemGestureExclusionListener(listener);
        }
    }

    @Override
    public void unregisterSystemGestureExclusionListener(ISystemGestureExclusionListener listener,
            int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                throw new IllegalArgumentException(
                        "Trying to unregister system gesture exclusion event for invalid display: "
                                + displayId);
            }
            displayContent.unregisterSystemGestureExclusionListener(listener);
        }
    }

    @Override
    public void registerDecorViewGestureListener(
            IDecorViewGestureListener listener, int displayId) {
        if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT,
                "registerDecorViewGestureListener()")) {
            throw new SecurityException("Requires MONITOR_INPUT permission");
        }
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                throw new IllegalArgumentException(
                        "Trying to register DecorView gesture event listener"
                                + "for invalid display: "
                                + displayId);
            }
            displayContent.registerDecorViewGestureListener(listener);
        }
    }

    @Override
    public void unregisterDecorViewGestureListener(
            IDecorViewGestureListener listener, int displayId) {
        if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT,
                "unregisterSystemGestureExclusionListener()")) {
            throw new SecurityException("Requires MONITOR_INPUT permission");
        }
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                throw new IllegalArgumentException(
                        "Trying to unregister DecorView gesture event listener"
                                + "for invalid display: "
                                + displayId);
            }
            displayContent.unregisterDecorViewGestureListener(listener);
        }
    }

    void reportDecorViewGestureChanged(Session session, IWindow window, boolean intercepted) {
        synchronized (mGlobalLock) {
            final WindowState win = windowForClient(session, window);
            if (win == null) {
                return;
            }
            win.getDisplayContent()
                    .updateDecorViewGestureIntercepted(win.mToken.token, intercepted);
        }
    }

    void reportSystemGestureExclusionChanged(Session session, IWindow window,
            List<Rect> exclusionRects) {
        synchronized (mGlobalLock) {
            final WindowState win = windowForClient(session, window);
            if (win == null) {
                Slog.i(TAG_WM,
                        "reportSystemGestureExclusionChanged(): No window state for package:"
                                + session.mPackageName);
                return;
            }
            if (win.setSystemGestureExclusion(exclusionRects)) {
                win.getDisplayContent().updateSystemGestureExclusion();
            }
        }
    }

    void reportKeepClearAreasChanged(Session session, IWindow window,
            List<Rect> restricted, List<Rect> unrestricted) {
        synchronized (mGlobalLock) {
            final WindowState win = windowForClient(session, window);
            if (win == null) {
                Slog.i(TAG_WM,
                        "reportKeepClearAreasChanged(): No window state for package:"
                                + session.mPackageName);
                return;
            }
            if (win.setKeepClearAreas(restricted, unrestricted)) {
                win.getDisplayContent().updateKeepClearAreas();
            }
        }
    }

    @Override
    public void registerDisplayFoldListener(IDisplayFoldListener listener) {
        mPolicy.registerDisplayFoldListener(listener);
    }

    @Override
    public void unregisterDisplayFoldListener(IDisplayFoldListener listener) {
        mPolicy.unregisterDisplayFoldListener(listener);
    }

    /**
     * Overrides the folded area.
     *
     * @param area the overriding folded area or an empty {@code Rect} to clear the override.
     */
    void setOverrideFoldedArea(@NonNull Rect area) {
        if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
        }

        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                mPolicy.setOverrideFoldedArea(area);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    /**
     * Get the display folded area.
     */
    @NonNull Rect getFoldedArea() {
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                return mPolicy.getFoldedArea();
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    /**
     * Registers a hierarchy listener that gets callbacks when the hierarchy changes. The listener's
     * onDisplayAdded() will not be called for the displays returned.
     *
     * @return the displayIds for the existing displays
     */
    @Override
    public int[] registerDisplayWindowListener(IDisplayWindowListener listener) {
        mAtmService.enforceTaskPermission("registerDisplayWindowListener");
        final long ident = Binder.clearCallingIdentity();
        try {
            return mDisplayNotificationController.registerListener(listener);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    /** Unregister a hierarchy listener so that it stops receiving callbacks. */
    @Override
    public void unregisterDisplayWindowListener(IDisplayWindowListener listener) {
        mAtmService.enforceTaskPermission("unregisterDisplayWindowListener");
        mDisplayNotificationController.unregisterListener(listener);
    }

    @Override
    public int getPreferredOptionsPanelGravity(int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                return Gravity.CENTER | Gravity.BOTTOM;
            }
            return displayContent.getPreferredOptionsPanelGravity();
        }
    }

    /**
     * Starts the view server on the specified port.
     *
     * @param port The port to listener to.
     *
     * @return True if the server was successfully started, false otherwise.
     *
     * @see com.android.server.wm.ViewServer
     * @see com.android.server.wm.ViewServer#VIEW_SERVER_DEFAULT_PORT
     */
    @Override
    public boolean startViewServer(int port) {
        if (isSystemSecure()) {
            return false;
        }

        if (!checkCallingPermission(Manifest.permission.DUMP, "startViewServer")) {
            return false;
        }

        if (port < 1024) {
            return false;
        }

        if (mViewServer != null) {
            if (!mViewServer.isRunning()) {
                try {
                    return mViewServer.start();
                } catch (IOException e) {
                    ProtoLog.w(WM_ERROR, "View server did not start");
                }
            }
            return false;
        }

        try {
            mViewServer = new ViewServer(this, port);
            return mViewServer.start();
        } catch (IOException e) {
            ProtoLog.w(WM_ERROR, "View server did not start");
        }
        return false;
    }

    private boolean isSystemSecure() {
        return "1".equals(SystemProperties.get(SYSTEM_SECURE, "1")) &&
                "0".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
    }

    /**
     * Stops the view server if it exists.
     *
     * @return True if the server stopped, false if it wasn't started or
     *         couldn't be stopped.
     *
     * @see com.android.server.wm.ViewServer
     */
    @Override
    public boolean stopViewServer() {
        if (isSystemSecure()) {
            return false;
        }

        if (!checkCallingPermission(Manifest.permission.DUMP, "stopViewServer")) {
            return false;
        }

        if (mViewServer != null) {
            return mViewServer.stop();
        }
        return false;
    }

    /**
     * Indicates whether the view server is running.
     *
     * @return True if the server is running, false otherwise.
     *
     * @see com.android.server.wm.ViewServer
     */
    @Override
    public boolean isViewServerRunning() {
        if (isSystemSecure()) {
            return false;
        }

        if (!checkCallingPermission(Manifest.permission.DUMP, "isViewServerRunning")) {
            return false;
        }

        return mViewServer != null && mViewServer.isRunning();
    }

    /**
     * Lists all available windows in the system. The listing is written in the specified Socket's
     * output stream with the following syntax: windowHashCodeInHexadecimal windowName
     * Each line of the output represents a different window.
     *
     * @param client The remote client to send the listing to.
     * @return false if an error occurred, true otherwise.
     */
    boolean viewServerListWindows(Socket client) {
        if (isSystemSecure()) {
            return false;
        }

        boolean result = true;

        final ArrayList<WindowState> windows = new ArrayList();
        synchronized (mGlobalLock) {
            mRoot.forAllWindows(w -> {
                windows.add(w);
            }, false /* traverseTopToBottom */);
        }

        BufferedWriter out = null;

        // Any uncaught exception will crash the system process
        try {
            OutputStream clientStream = client.getOutputStream();
            out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);

            final int count = windows.size();
            for (int i = 0; i < count; i++) {
                final WindowState w = windows.get(i);
                out.write(Integer.toHexString(System.identityHashCode(w)));
                out.write(' ');
                out.append(w.mAttrs.getTitle());
                out.write('\n');
            }

            out.write("DONE.\n");
            out.flush();
        } catch (Exception e) {
            result = false;
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    result = false;
                }
            }
        }

        return result;
    }

    // TODO(multidisplay): Extend to multiple displays.
    /**
     * Returns the focused window in the following format:
     * windowHashCodeInHexadecimal windowName
     *
     * @param client The remote client to send the listing to.
     * @return False if an error occurred, true otherwise.
     */
    boolean viewServerGetFocusedWindow(Socket client) {
        if (isSystemSecure()) {
            return false;
        }

        boolean result = true;

        WindowState focusedWindow = getFocusedWindow();

        BufferedWriter out = null;

        // Any uncaught exception will crash the system process
        try {
            OutputStream clientStream = client.getOutputStream();
            out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);

            if(focusedWindow != null) {
                out.write(Integer.toHexString(System.identityHashCode(focusedWindow)));
                out.write(' ');
                out.append(focusedWindow.mAttrs.getTitle());
            }
            out.write('\n');
            out.flush();
        } catch (Exception e) {
            result = false;
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    result = false;
                }
            }
        }

        return result;
    }

    /**
     * Sends a command to a target window. The result of the command, if any, will be
     * written in the output stream of the specified socket.
     *
     * The parameters must follow this syntax:
     * windowHashcode extra
     *
     * Where XX is the length in characeters of the windowTitle.
     *
     * The first parameter is the target window. The window with the specified hashcode
     * will be the target. If no target can be found, nothing happens. The extra parameters
     * will be delivered to the target window and as parameters to the command itself.
     *
     * @param client The remote client to sent the result, if any, to.
     * @param command The command to execute.
     * @param parameters The command parameters.
     *
     * @return True if the command was successfully delivered, false otherwise. This does
     *         not indicate whether the command itself was successful.
     */
    boolean viewServerWindowCommand(Socket client, String command, String parameters) {
        if (isSystemSecure()) {
            return false;
        }

        boolean success = true;
        Parcel data = null;
        Parcel reply = null;

        BufferedWriter out = null;

        // Any uncaught exception will crash the system process
        try {
            // Find the hashcode of the window
            int index = parameters.indexOf(' ');
            if (index == -1) {
                index = parameters.length();
            }
            final String code = parameters.substring(0, index);
            int hashCode = (int) Long.parseLong(code, 16);

            // Extract the command's parameter after the window description
            if (index < parameters.length()) {
                parameters = parameters.substring(index + 1);
            } else {
                parameters = "";
            }

            final WindowState window = findWindow(hashCode);
            if (window == null) {
                return false;
            }

            data = Parcel.obtain();
            data.writeInterfaceToken("android.view.IWindow");
            data.writeString(command);
            data.writeString(parameters);
            data.writeInt(1);
            ParcelFileDescriptor.fromSocket(client).writeToParcel(data, 0);

            reply = Parcel.obtain();

            final IBinder binder = window.mClient.asBinder();
            // TODO: GET THE TRANSACTION CODE IN A SAFER MANNER
            binder.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);

            reply.readException();

            if (!client.isOutputShutdown()) {
                out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
                out.write("DONE\n");
                out.flush();
            }

        } catch (Exception e) {
            ProtoLog.w(WM_ERROR, "Could not send command %s with parameters %s. %s", command,
                    parameters, e);
            success = false;
        } finally {
            if (data != null) {
                data.recycle();
            }
            if (reply != null) {
                reply.recycle();
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {

                }
            }
        }

        return success;
    }

    void addWindowChangeListener(WindowChangeListener listener) {
        synchronized (mGlobalLock) {
            mWindowChangeListeners.add(listener);
        }
    }

    void removeWindowChangeListener(WindowChangeListener listener) {
        synchronized (mGlobalLock) {
            mWindowChangeListeners.remove(listener);
        }
    }

    void addWindowFocusChangeListener(WindowFocusChangeListener listener) {
        synchronized (mGlobalLock) {
            mWindowFocusChangeListeners.add(listener);
        }
    }

    void removeWindowFocusChangeListener(WindowFocusChangeListener listener) {
        synchronized (mGlobalLock) {
            mWindowFocusChangeListeners.remove(listener);
        }
    }

    private void notifyWindowRemovedListeners(IBinder client) {
        OnWindowRemovedListener[] windowRemovedListeners;
        synchronized (mGlobalLock) {
            if (mOnWindowRemovedListeners.isEmpty()) {
                return;
            }
            windowRemovedListeners = new OnWindowRemovedListener[mOnWindowRemovedListeners.size()];
            mOnWindowRemovedListeners.toArray(windowRemovedListeners);
        }
        mH.post(() -> {
            int size = windowRemovedListeners.length;
            for (int i = 0; i < size; i++) {
                windowRemovedListeners[i].onWindowRemoved(client);
            }
        });
    }

    private void notifyWindowsChanged() {
        WindowChangeListener[] windowChangeListeners;
        synchronized (mGlobalLock) {
            if(mWindowChangeListeners.isEmpty()) {
                return;
            }
            windowChangeListeners = new WindowChangeListener[mWindowChangeListeners.size()];
            windowChangeListeners = mWindowChangeListeners.toArray(windowChangeListeners);
        }
        int N = windowChangeListeners.length;
        for(int i = 0; i < N; i++) {
            windowChangeListeners[i].windowsChanged();
        }
    }

    private void notifyFocusChanged(IBinder focusedWindowToken) {
        WindowFocusChangeListener[] windowFocusChangeListeners;
        synchronized (mGlobalLock) {
            if(mWindowFocusChangeListeners.isEmpty()) {
                return;
            }
            windowFocusChangeListeners =
                    new WindowFocusChangeListener[mWindowFocusChangeListeners.size()];
            mWindowFocusChangeListeners.toArray(windowFocusChangeListeners);
        }
        int N = windowFocusChangeListeners.length;
        for(int i = 0; i < N; i++) {
            windowFocusChangeListeners[i].focusChanged(focusedWindowToken);
        }
    }

    private WindowState findWindow(int hashCode) {
        if (hashCode == -1) {
            // TODO(multidisplay): Extend to multiple displays.
            return getFocusedWindow();
        }

        synchronized (mGlobalLock) {
            return mRoot.getWindow((w) -> System.identityHashCode(w) == hashCode);
        }
    }

    public Configuration computeNewConfiguration(int displayId) {
        synchronized (mGlobalLock) {
            return computeNewConfigurationLocked(displayId);
        }
    }

    private Configuration computeNewConfigurationLocked(int displayId) {
        if (!mDisplayReady) {
            return null;
        }
        final Configuration config = new Configuration();
        final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
        displayContent.computeScreenConfiguration(config);
        return config;
    }

    void notifyHardKeyboardStatusChange() {
        final boolean available;
        final WindowManagerInternal.OnHardKeyboardStatusChangeListener listener;
        synchronized (mGlobalLock) {
            listener = mHardKeyboardStatusChangeListener;
            available = mHardKeyboardAvailable;
        }
        if (listener != null) {
            listener.onHardKeyboardStatusChange(available);
        }
    }

    // -------------------------------------------------------------
    // Input Events and Focus Management
    // -------------------------------------------------------------

    final InputManagerCallback mInputManagerCallback = new InputManagerCallback(this);
    private boolean mEventDispatchingEnabled;

    @Override
    public void setEventDispatching(boolean enabled) {
        if (!checkCallingPermission(MANAGE_APP_TOKENS, "setEventDispatching()")) {
            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
        }

        synchronized (mGlobalLock) {
            mEventDispatchingEnabled = enabled;
            if (mDisplayEnabled) {
                mInputManagerCallback.setEventDispatchingLw(enabled);
            }
        }
    }

    private WindowState getFocusedWindow() {
        synchronized (mGlobalLock) {
            return getFocusedWindowLocked();
        }
    }

    WindowState getFocusedWindowLocked() {
        // Return the focused window in the focused display.
        return mRoot.getTopFocusedDisplayContent().mCurrentFocus;
    }

    Task getImeFocusRootTaskLocked() {
        // Don't use mCurrentFocus.getStack() because it returns home stack for system windows.
        // Also don't use mInputMethodTarget's stack, because some window with FLAG_NOT_FOCUSABLE
        // and FLAG_ALT_FOCUSABLE_IM flags both set might be set to IME layering target so they're
        // moved to make room for IME, but the window is not the focused window that's taking input.
        // TODO (b/111080190): Consider the case of multiple IMEs on multi-display.
        final DisplayContent topFocusedDisplay = mRoot.getTopFocusedDisplayContent();
        final ActivityRecord focusedApp = topFocusedDisplay.mFocusedApp;
        return (focusedApp != null && focusedApp.getTask() != null)
                ? focusedApp.getTask().getRootTask() : null;
    }

    public boolean detectSafeMode() {
        if (!mInputManagerCallback.waitForInputDevicesReady(
                INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS)) {
            ProtoLog.w(WM_ERROR, "Devices still not ready after waiting %d"
                            + " milliseconds before attempting to detect safe mode.",
                    INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS);
        }

        if (Settings.Global.getInt(
                mContext.getContentResolver(), Settings.Global.SAFE_BOOT_DISALLOWED, 0) != 0) {
            return false;
        }

        int menuState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY,
                KeyEvent.KEYCODE_MENU);
        int sState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, KeyEvent.KEYCODE_S);
        int dpadState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_DPAD,
                KeyEvent.KEYCODE_DPAD_CENTER);
        int trackballState = mInputManager.getScanCodeState(-1, InputDevice.SOURCE_TRACKBALL,
                InputManagerService.BTN_MOUSE);
        int volumeDownState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY,
                KeyEvent.KEYCODE_VOLUME_DOWN);
        mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0
                || volumeDownState > 0;
        try {
            if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0
                    || SystemProperties.getInt(ShutdownThread.RO_SAFEMODE_PROPERTY, 0) != 0) {
                mSafeMode = true;
                SystemProperties.set(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, "");
            }
        } catch (IllegalArgumentException e) {
        }
        if (mSafeMode) {
            ProtoLog.i(WM_ERROR, "SAFE MODE ENABLED (menu=%d s=%d dpad=%d"
                    + " trackball=%d)", menuState, sState, dpadState, trackballState);
            // May already be set if (for instance) this process has crashed
            if (SystemProperties.getInt(ShutdownThread.RO_SAFEMODE_PROPERTY, 0) == 0) {
                SystemProperties.set(ShutdownThread.RO_SAFEMODE_PROPERTY, "1");
            }
        } else {
            ProtoLog.i(WM_ERROR, "SAFE MODE not enabled");
        }
        mPolicy.setSafeMode(mSafeMode);
        return mSafeMode;
    }

    public void displayReady() {
        synchronized (mGlobalLock) {
            if (mMaxUiWidth > 0) {
                mRoot.forAllDisplays(dc -> {
                    if (dc.mDisplay.getType() == Display.TYPE_INTERNAL) {
                        dc.setMaxUiWidth(mMaxUiWidth);
                    }
                });
            }
            applyForcedPropertiesForDefaultDisplay();
            mAnimator.ready();
            mDisplayReady = true;
            mHasWideColorGamutSupport = queryWideColorGamutSupport();
            mHasHdrSupport = queryHdrSupport();
            mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
                    PackageManager.FEATURE_TOUCHSCREEN);
            mIsFakeTouchDevice = mContext.getPackageManager().hasSystemFeature(
                    PackageManager.FEATURE_FAKETOUCH);
            // Reconfigure all displays to make sure that the forced properties and
            // DisplayWindowSettings are applied. In addition, wide-color/hdr/isTouchDevice also
            // affect the Configuration.
            mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked);
        }
    }

    public void systemReady() {
        mSystemReady = true;
        mPolicy.systemReady();
        mRoot.forAllDisplayPolicies(DisplayPolicy::systemReady);
        mSnapshotController.systemReady();
        UiThread.getHandler().post(mSettingsObserver::loadSettings);
        IVrManager vrManager = IVrManager.Stub.asInterface(
                ServiceManager.getService(Context.VR_SERVICE));
        if (vrManager != null) {
            try {
                final boolean vrModeEnabled = vrManager.getVrModeState();
                synchronized (mGlobalLock) {
                    vrManager.registerListener(mVrStateCallbacks);
                    if (vrModeEnabled) {
                        mVrModeEnabled = vrModeEnabled;
                        mVrStateCallbacks.onVrStateChanged(vrModeEnabled);
                    }
                }
            } catch (RemoteException e) {
                // Ignore, we cannot do anything if we failed to register VR mode listener
            }
        }
    }


    // Keep logic in sync with SurfaceFlingerProperties.cpp
    // Consider exposing properties via ISurfaceComposer instead.
    private static boolean queryWideColorGamutSupport() {
        boolean defaultValue = false;
        Optional<Boolean> hasWideColorProp = SurfaceFlingerProperties.has_wide_color_display();
        if (hasWideColorProp.isPresent()) {
            return hasWideColorProp.get();
        }
        try {
            ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService();
            OptionalBool hasWideColor = surfaceFlinger.hasWideColorDisplay();
            if (hasWideColor != null) {
                return hasWideColor.value;
            }
        } catch (RemoteException e) {
            // Ignore, we're in big trouble if we can't talk to SurfaceFlinger's config store
        } catch (NoSuchElementException e) {
            return defaultValue;
        }
        return false;
    }

    private static boolean queryHdrSupport() {
        boolean defaultValue = false;
        Optional<Boolean> hasHdrProp = SurfaceFlingerProperties.has_HDR_display();
        if (hasHdrProp.isPresent()) {
            return hasHdrProp.get();
        }
        try {
            ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService();
            OptionalBool hasHdr = surfaceFlinger.hasHDRDisplay();
            if (hasHdr != null) {
                return hasHdr.value;
            }
        } catch (RemoteException e) {
            // Ignore, we're in big trouble if we can't talk to SurfaceFlinger's config store
        } catch (NoSuchElementException e) {
            return defaultValue;
        }
        return false;
    }

    // Returns an input target which is mapped to the given input token. This can be a WindowState
    // or an embedded window.
    @Nullable InputTarget getInputTargetFromToken(IBinder inputToken) {
        WindowState windowState = mInputToWindowMap.get(inputToken);
        if (windowState != null) {
            return windowState;
        }

        EmbeddedWindowController.EmbeddedWindow embeddedWindow =
                mEmbeddedWindowController.get(inputToken);
        if (embeddedWindow != null) {
            return embeddedWindow;
        }

        return null;
    }

    @Nullable
    InputTarget getInputTargetFromWindowTokenLocked(IBinder windowToken) {
        final InputTarget window = mWindowMap.get(windowToken);
        if (window != null) {
            return window;
        }
        return mEmbeddedWindowController.getByWindowToken(windowToken);
    }

    void reportFocusChanged(IBinder oldToken, IBinder newToken) {
        InputTarget lastTarget;
        InputTarget newTarget;
        synchronized (mGlobalLock) {
            lastTarget = getInputTargetFromToken(oldToken);
            newTarget = getInputTargetFromToken(newToken);
            if (newTarget == null && lastTarget == null) {
                Slog.v(TAG_WM, "Unknown focus tokens, dropping reportFocusChanged");
                return;
            }
            mFocusedInputTarget = newTarget;

            mAccessibilityController.onFocusChanged(lastTarget, newTarget);
            ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus changing: %s -> %s", lastTarget, newTarget);
        }

        // Call WindowState focus change observers
        WindowState newFocusedWindow = newTarget != null ? newTarget.getWindowState() : null;
        if (newFocusedWindow != null && newFocusedWindow.mInputChannelToken == newToken) {
            mAnrController.onFocusChanged(newFocusedWindow);
            newFocusedWindow.reportFocusChangedSerialized(true);
            notifyFocusChanged(newTarget.getWindowToken());
        }

        WindowState lastFocusedWindow = lastTarget != null ? lastTarget.getWindowState() : null;
        if (lastFocusedWindow != null && lastFocusedWindow.mInputChannelToken == oldToken) {
            lastFocusedWindow.reportFocusChangedSerialized(false);
        }
    }

    // -------------------------------------------------------------
    // Async Handler
    // -------------------------------------------------------------

    final class H extends android.os.Handler {
        public static final int PERSIST_ANIMATION_SCALE = 14;
        public static final int ENABLE_SCREEN = 16;
        public static final int REPORT_WINDOWS_CHANGE = 19;

        public static final int REPORT_HARD_KEYBOARD_STATUS_CHANGE = 22;
        public static final int BOOT_TIMEOUT = 23;
        public static final int WAITING_FOR_DRAWN_TIMEOUT = 24;
        public static final int SHOW_STRICT_MODE_VIOLATION = 25;

        public static final int NOTIFY_ACTIVITY_DRAWN = 32;

        public static final int NEW_ANIMATOR_SCALE = 34;

        public static final int SHOW_EMULATOR_DISPLAY_OVERLAY = 36;

        public static final int CHECK_IF_BOOT_ANIMATION_FINISHED = 37;
        public static final int RESET_ANR_MESSAGE = 38;
        public static final int WALLPAPER_DRAW_PENDING_TIMEOUT = 39;

        public static final int UPDATE_MULTI_WINDOW_STACKS = 41;

        public static final int UPDATE_ANIMATION_SCALE = 51;
        public static final int WINDOW_HIDE_TIMEOUT = 52;
        public static final int SET_HAS_OVERLAY_UI = 58;
        public static final int RECOMPUTE_FOCUS = 61;
        public static final int ON_POINTER_DOWN_OUTSIDE_FOCUS = 62;
        public static final int WINDOW_STATE_BLAST_SYNC_TIMEOUT = 64;
        public static final int REPARENT_TASK_TO_DEFAULT_DISPLAY = 65;
        public static final int INSETS_CHANGED = 66;

        /**
         * Used to denote that an integer field in a message will not be used.
         */
        public static final int UNUSED = 0;

        @Override
        public void handleMessage(Message msg) {
            if (DEBUG_WINDOW_TRACE) {
                Slog.v(TAG_WM, "handleMessage: entry what=" + msg.what);
            }
            switch (msg.what) {
                case PERSIST_ANIMATION_SCALE: {
                    float windowAnimationScale;
                    float transitionAnimationScale;
                    synchronized (mGlobalLock) {
                        windowAnimationScale = mWindowAnimationScaleSetting;
                        transitionAnimationScale = mTransitionAnimationScaleSetting;
                    }
                    Settings.Global.putFloat(mContext.getContentResolver(),
                            Settings.Global.WINDOW_ANIMATION_SCALE, windowAnimationScale);
                    Settings.Global.putFloat(mContext.getContentResolver(),
                            Settings.Global.TRANSITION_ANIMATION_SCALE, transitionAnimationScale);
                    Settings.Global.putFloat(mContext.getContentResolver(),
                            Settings.Global.ANIMATOR_DURATION_SCALE,
                            mAnimatorScale.getCurrentScale());
                    break;
                }

                case UPDATE_ANIMATION_SCALE: {
                    @UpdateAnimationScaleMode
                    final int mode = msg.arg1;
                    switch (mode) {
                        case WINDOW_ANIMATION_SCALE: {
                            float windowAnimationScale;
                            synchronized (mGlobalLock) {
                                windowAnimationScale = mWindowAnimationScaleSetting;
                            }
                            windowAnimationScale = getWindowAnimationScaleSetting(
                                    windowAnimationScale);
                            synchronized (mGlobalLock) {
                                mWindowAnimationScaleSetting = windowAnimationScale;
                            }
                            break;
                        }
                        case TRANSITION_ANIMATION_SCALE: {
                            float transitionAnimationScale = getTransitionAnimationScaleSetting();
                            synchronized (mGlobalLock) {
                                mTransitionAnimationScaleSetting = transitionAnimationScale;
                            }
                            break;
                        }
                        case ANIMATION_DURATION_SCALE: {
                            mAnimatorScale.setCurrentScale(getAnimatorDurationScaleSetting());
                            dispatchNewAnimatorScaleLocked(null);
                            break;
                        }
                    }
                    break;
                }

                case ENABLE_SCREEN: {
                    performEnableScreen();
                    break;
                }

                case REPORT_WINDOWS_CHANGE: {
                    if (mWindowsChanged) {
                        synchronized (mGlobalLock) {
                            mWindowsChanged = false;
                        }
                        notifyWindowsChanged();
                    }
                    break;
                }

                case REPORT_HARD_KEYBOARD_STATUS_CHANGE: {
                    notifyHardKeyboardStatusChange();
                    break;
                }

                case BOOT_TIMEOUT: {
                    performBootTimeout();
                    break;
                }

                case WAITING_FOR_DRAWN_TIMEOUT: {
                    final Message callback;
                    final WindowContainer<?> container = (WindowContainer<?>) msg.obj;
                    synchronized (mGlobalLock) {
                        ProtoLog.w(WM_ERROR, "Timeout waiting for drawn: undrawn=%s",
                                container.mWaitingForDrawn);
                        if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                            for (int i = 0; i < container.mWaitingForDrawn.size(); i++) {
                                traceEndWaitingForWindowDrawn(container.mWaitingForDrawn.get(i));
                            }
                        }
                        container.mWaitingForDrawn.clear();
                        callback = mWaitingForDrawnCallbacks.remove(container);
                    }
                    if (callback != null) {
                        callback.sendToTarget();
                    }
                    break;
                }

                case SHOW_STRICT_MODE_VIOLATION: {
                    showStrictModeViolation(msg.arg1, msg.arg2);
                    break;
                }

                case SHOW_EMULATOR_DISPLAY_OVERLAY: {
                    showEmulatorDisplayOverlay();
                    break;
                }

                case NOTIFY_ACTIVITY_DRAWN: {
                    final ActivityRecord activity = (ActivityRecord) msg.obj;
                    synchronized (mGlobalLock) {
                        if (activity.isAttached()) {
                            activity.getRootTask().notifyActivityDrawnLocked(activity);
                        }
                    }
                    break;
                }
                case NEW_ANIMATOR_SCALE: {
                    float scale = getCurrentAnimatorScale();
                    ValueAnimator.setDurationScale(scale);
                    Session session = (Session)msg.obj;
                    if (session != null) {
                        try {
                            session.mCallback.onAnimatorScaleChanged(scale);
                        } catch (RemoteException e) {
                        }
                    } else {
                        ArrayList<IWindowSessionCallback> callbacks
                                = new ArrayList<IWindowSessionCallback>();
                        synchronized (mGlobalLock) {
                            for (int i=0; i<mSessions.size(); i++) {
                                callbacks.add(mSessions.valueAt(i).mCallback);
                            }

                        }
                        for (int i=0; i<callbacks.size(); i++) {
                            try {
                                callbacks.get(i).onAnimatorScaleChanged(scale);
                            } catch (RemoteException e) {
                            }
                        }
                    }
                    break;
                }
                case CHECK_IF_BOOT_ANIMATION_FINISHED: {
                    final boolean bootAnimationComplete;
                    synchronized (mGlobalLock) {
                        ProtoLog.i(WM_DEBUG_BOOT, "CHECK_IF_BOOT_ANIMATION_FINISHED:");
                        bootAnimationComplete = checkBootAnimationCompleteLocked();
                    }
                    if (bootAnimationComplete) {
                        performEnableScreen();
                    }
                    break;
                }
                case RESET_ANR_MESSAGE: {
                    synchronized (mGlobalLock) {
                        mLastANRState = null;
                        mAtmService.mLastANRState = null;
                    }
                    break;
                }
                case WALLPAPER_DRAW_PENDING_TIMEOUT: {
                    synchronized (mGlobalLock) {
                        final WallpaperController wallpaperController =
                                (WallpaperController) msg.obj;
                        if (wallpaperController != null
                                && wallpaperController.processWallpaperDrawPendingTimeout()) {
                            mWindowPlacerLocked.performSurfacePlacement();
                        }
                    }
                    break;
                }
                case UPDATE_MULTI_WINDOW_STACKS: {
                    synchronized (mGlobalLock) {
                        final DisplayContent displayContent = (DisplayContent) msg.obj;
                        if (displayContent != null) {
                            displayContent.adjustForImeIfNeeded();
                        }
                    }
                    break;
                }
                case WINDOW_HIDE_TIMEOUT: {
                    final WindowState window = (WindowState) msg.obj;
                    synchronized (mGlobalLock) {
                        // TODO: This is all about fixing b/21693547
                        // where partially initialized Toasts get stuck
                        // around and keep the screen on. We'd like
                        // to just remove the toast...but this can cause clients
                        // who miss the timeout due to normal circumstances (e.g.
                        // running under debugger) to crash (b/29105388). The windows will
                        // eventually be removed when the client process finishes.
                        // The best we can do for now is remove the FLAG_KEEP_SCREEN_ON
                        // and prevent the symptoms of b/21693547. Since apps don't
                        // support windows being removed under them we hide the window
                        // and it will be removed when the app dies.
                        window.mAttrs.flags &= ~FLAG_KEEP_SCREEN_ON;
                        window.hidePermanentlyLw();
                        window.setDisplayLayoutNeeded();
                        mWindowPlacerLocked.performSurfacePlacement();
                    }
                    break;
                }
                case SET_HAS_OVERLAY_UI: {
                    mAmInternal.setHasOverlayUi(msg.arg1, msg.arg2 == 1);
                    break;
                }
                case RECOMPUTE_FOCUS: {
                    synchronized (mGlobalLock) {
                        updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
                                true /* updateInputWindows */);
                    }
                    break;
                }
                case ON_POINTER_DOWN_OUTSIDE_FOCUS: {
                    synchronized (mGlobalLock) {
                        final IBinder touchedToken = (IBinder) msg.obj;
                        onPointerDownOutsideFocusLocked(getInputTargetFromToken(touchedToken),
                                true /* fromHandler */);
                    }
                    break;
                }
                case WINDOW_STATE_BLAST_SYNC_TIMEOUT: {
                    synchronized (mGlobalLock) {
                        final WindowState ws = (WindowState) msg.obj;
                        Slog.i(TAG, "Blast sync timeout: " + ws);
                        ws.immediatelyNotifyBlastSync();
                    }
                    break;
                }
                case REPARENT_TASK_TO_DEFAULT_DISPLAY: {
                    synchronized (mGlobalLock) {
                        Task task = (Task) msg.obj;
                        task.reparent(mRoot.getDefaultTaskDisplayArea(), true /* onTop */);
                        // Resume focusable root task after reparenting to another display area.
                        task.resumeNextFocusAfterReparent();
                    }
                    break;
                }
                case INSETS_CHANGED: {
                    synchronized (mGlobalLock) {
                        if (mWindowsInsetsChanged > 0) {
                            // We need to update resizing windows and dispatch the new insets state
                            // to them.
                            mWindowPlacerLocked.performSurfacePlacement();
                        }
                    }
                    break;
                }
            }
            if (DEBUG_WINDOW_TRACE) {
                Slog.v(TAG_WM, "handleMessage: exit");
            }
        }

        /** Remove the previous messages with the same 'what' and 'obj' then send the new one. */
        void sendNewMessageDelayed(int what, Object obj, long delayMillis) {
            removeMessages(what, obj);
            sendMessageDelayed(obtainMessage(what, obj), delayMillis);
        }
    }

    // -------------------------------------------------------------
    // IWindowManager API
    // -------------------------------------------------------------

    @Override
    public IWindowSession openSession(IWindowSessionCallback callback) {
        return new Session(this, callback);
    }

    @Override
    public void getInitialDisplaySize(int displayId, Point size) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
                size.x = displayContent.mInitialDisplayWidth;
                size.y = displayContent.mInitialDisplayHeight;
            }
        }
    }

    @Override
    public void getBaseDisplaySize(int displayId, Point size) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
                size.x = displayContent.mBaseDisplayWidth;
                size.y = displayContent.mBaseDisplayHeight;
            }
        }
    }

    @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
    @Override
    public void setForcedDisplaySize(int displayId, int width, int height) {
        setForcedDisplaySize_enforcePermission();

        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                if (displayContent != null) {
                    displayContent.setForcedSize(width, height);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
    @Override
    public void setForcedDisplayScalingMode(int displayId, int mode) {
        setForcedDisplayScalingMode_enforcePermission();

        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                if (displayContent != null) {
                    displayContent.setForcedScalingMode(mode);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    void setSandboxDisplayApis(int displayId, boolean sandboxDisplayApis) {
        if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
        }

        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                if (displayContent != null) {
                    displayContent.setSandboxDisplayApis(sandboxDisplayApis);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    /** The global settings only apply to default display. */
    private boolean applyForcedPropertiesForDefaultDisplay() {
        boolean changed = false;
        final DisplayContent displayContent = getDefaultDisplayContentLocked();
        // Display size.
        String sizeStr = Settings.Global.getString(mContext.getContentResolver(),
                Settings.Global.DISPLAY_SIZE_FORCED);
        if (sizeStr == null || sizeStr.length() == 0) {
            sizeStr = SystemProperties.get(SIZE_OVERRIDE, null);
        }
        if (sizeStr != null && sizeStr.length() > 0) {
            final int pos = sizeStr.indexOf(',');
            if (pos > 0 && sizeStr.lastIndexOf(',') == pos) {
                try {
                    final Point size = displayContent.getValidForcedSize(
                            Integer.parseInt(sizeStr.substring(0, pos)),
                            Integer.parseInt(sizeStr.substring(pos + 1)));
                    final int width = size.x;
                    final int height = size.y;
                    if (displayContent.mBaseDisplayWidth != width
                            || displayContent.mBaseDisplayHeight != height) {
                        ProtoLog.i(WM_ERROR, "FORCED DISPLAY SIZE: %dx%d", width, height);
                        displayContent.updateBaseDisplayMetrics(width, height,
                                displayContent.mBaseDisplayDensity,
                                displayContent.mBaseDisplayPhysicalXDpi,
                                displayContent.mBaseDisplayPhysicalYDpi);
                        changed = true;
                    }
                } catch (NumberFormatException ex) {
                }
            }
        }

        // Display density.
        final int density = getForcedDisplayDensityForUserLocked(mCurrentUserId);
        if (density != 0 && density != displayContent.mBaseDisplayDensity) {
            displayContent.mBaseDisplayDensity = density;
            changed = true;
        }

        // Display scaling mode.
        int mode = Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.DISPLAY_SCALING_FORCE, 0);
        if (displayContent.mDisplayScalingDisabled != (mode != 0)) {
            ProtoLog.i(WM_ERROR, "FORCED DISPLAY SCALING DISABLED");
            displayContent.mDisplayScalingDisabled = true;
            changed = true;
        }
        return changed;
    }

    @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
    @Override
    public void clearForcedDisplaySize(int displayId) {
        clearForcedDisplaySize_enforcePermission();

        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                if (displayContent != null) {
                    displayContent.setForcedSize(displayContent.mInitialDisplayWidth,
                            displayContent.mInitialDisplayHeight,
                            displayContent.mInitialPhysicalXDpi,
                            displayContent.mInitialPhysicalYDpi);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    @Override
    public int getInitialDisplayDensity(int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
                return displayContent.getInitialDisplayDensity();
            }

            DisplayInfo info = mDisplayManagerInternal.getDisplayInfo(displayId);
            if (info != null && info.hasAccess(Binder.getCallingUid())) {
                return info.logicalDensityDpi;
            }
        }
        return -1;
    }

    @Override
    public int getBaseDisplayDensity(int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
                return displayContent.mBaseDisplayDensity;
            }
        }
        return -1;
    }

    /**
     * Return the display Id that has the given uniqueId. Unique ID is defined in
     * {@link DisplayInfo#uniqueId}.
     */
    @Override
    public int getDisplayIdByUniqueId(String uniqueId) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(uniqueId);
            if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
                return displayContent.mDisplayId;
            }
        }
        return -1;
    }

    @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
    @Override
    public void setForcedDisplayDensityForUser(int displayId, int density, int userId) {
        setForcedDisplayDensityForUser_enforcePermission();

        final int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                Binder.getCallingUid(), userId, false, true, "setForcedDisplayDensityForUser",
                null);
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                setForcedDensityLockedInternal(displayId, density, targetUserId);
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    @GuardedBy("mGlobalLock")
    private void setForcedDensityLockedInternal(final int displayId, final int density,
            @UserIdInt final int targetUserId) {
        final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
        if (displayContent != null) {
            displayContent.setForcedDensity(density, targetUserId);
            displayContent.clearForcedDensityRatio();
            return;
        }

        final DisplayInfo info = mDisplayManagerInternal.getDisplayInfo(displayId);
        if (info == null) {
            ProtoLog.e(WM_ERROR, "Failed to get information about logical display %d. "
                    + "Skip setting forced display density.", displayId);
            return;
        }

        mDisplayWindowSettings.setForcedDensity(info, density, targetUserId);
        mDisplayWindowSettings.setForcedDensityRatio(info, 0.0f);
    }

    @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
    @Override
    public void clearForcedDisplayDensityForUser(int displayId, int userId) {
        clearForcedDisplayDensityForUser_enforcePermission();

        final int callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                Binder.getCallingUid(), userId, false, true, "clearForcedDisplayDensityForUser",
                null);
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                // Clear forced display density ratio
                setForcedDisplayDensityRatioInternal(displayId, 0.0f, userId);

                // Clear forced display density
                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                if (displayContent != null) {
                    displayContent.setForcedDensity(displayContent.getInitialDisplayDensity(),
                            callingUserId);
                    return;
                }

                final DisplayInfo info = mDisplayManagerInternal.getDisplayInfo(displayId);
                if (info == null) {
                    ProtoLog.e(WM_ERROR, "Failed to get information about logical display %d. "
                            + "Skip clearing forced display density.", displayId);
                    return;
                }

                mDisplayWindowSettings.setForcedDensity(info, info.logicalDensityDpi,
                        userId);
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
    @Override
    public void setForcedDisplayDensityRatio(int displayId, float ratio, int userId) {
        setForcedDisplayDensityRatio_enforcePermission();
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                setForcedDisplayDensityRatioInternal(displayId, ratio, userId);
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    private void setForcedDisplayDensityRatioInternal(
            int displayId, float ratio, int userId) {
        final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
        if (displayContent != null) {
            displayContent.setForcedDensityRatio(ratio, userId);
            return;
        }

        final DisplayInfo info = mDisplayManagerInternal.getDisplayInfo(displayId);
        if (info == null) {
            ProtoLog.e(WM_ERROR, "Failed to get information about logical display %d. "
                    + "Skip setting forced display density.", displayId);
            return;
        }
        mDisplayWindowSettings.setForcedDensityRatio(info, ratio);
    }

    @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
    @Override
    public void setConfigurationChangeSettingsForUser(
            @NonNull List<ConfigurationChangeSetting> settings, int userId) {
        setConfigurationChangeSettingsForUser_enforcePermission();

        final int callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                Binder.getCallingUid(), userId, false, true,
                "setConfigurationChangeSettingsForUser",
                null);
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                mAtmService.deferWindowLayout();
                try {
                    // Apply the settings.
                    for (int i = 0; i < settings.size(); i++) {
                        settings.get(i).apply(callingUserId);
                    }
                } finally {
                    mAtmService.continueWindowLayout();
                }
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    /**
     * @param userId the ID of the user
     * @return the forced display density for the specified user, if set, or
     *         {@code 0} if not set
     */
    private int getForcedDisplayDensityForUserLocked(int userId) {
        String densityStr = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                Settings.Secure.DISPLAY_DENSITY_FORCED, userId);
        if (densityStr == null || densityStr.length() == 0) {
            densityStr = SystemProperties.get(DENSITY_OVERRIDE, null);
        }
        if (densityStr != null && densityStr.length() > 0) {
            try {
                return Integer.parseInt(densityStr);
            } catch (NumberFormatException ex) {
            }
        }
        return 0;
    }

    @Override
    public void startWindowTrace(){
        mWindowTracing.startTrace(null /* printwriter */);
    }

    @Override
    public void stopWindowTrace(){
        mWindowTracing.stopTrace(null /* printwriter */);
    }

    @Override
    public void saveWindowTraceToFile() {
        mWindowTracing.saveForBugreport(null /* printwriter */);
    }

    @Override
    public boolean isWindowTraceEnabled() {
        return mWindowTracing.isEnabled();
    }

    @Override
    public void startTransitionTrace() {
        mTransitionTracer.startTrace(null /* printwriter */);
    }

    @Override
    public void stopTransitionTrace() {
        mTransitionTracer.stopTrace(null /* printwriter */);
    }

    @Override
    public boolean isTransitionTraceEnabled() {
        return mTransitionTracer.isTracing();
    }

    @Override
    public boolean registerCrossWindowBlurEnabledListener(
                ICrossWindowBlurEnabledListener listener) {
        return mBlurController.registerCrossWindowBlurEnabledListener(listener);
    }

    @Override
    public void unregisterCrossWindowBlurEnabledListener(
                ICrossWindowBlurEnabledListener listener) {
        mBlurController.unregisterCrossWindowBlurEnabledListener(listener);
    }

    // -------------------------------------------------------------
    // Internals
    // -------------------------------------------------------------

    @Nullable
    @GuardedBy("mGlobalLock")
    final WindowState windowForClient(@Nullable Session session, @NonNull IWindow client) {
        return windowForClient(session, client.asBinder());
    }

    @Nullable
    @GuardedBy("mGlobalLock")
    final WindowState windowForClient(@Nullable Session session, @NonNull IBinder client) {
        WindowState win = mWindowMap.get(client);
        if (DEBUG) Slog.v(TAG_WM, "Looking up client " + client + ": " + win);
        if (win == null) {
            ProtoLog.w(WM_ERROR, "Failed looking up window session=%s callers=%s", session,
                    Debug.getCallers(3));
            return null;
        }
        if (session != null && win.mSession != session) {
            ProtoLog.w(WM_ERROR, "Failed looking up window session=%s callers=%s", session,
                    Debug.getCallers(3));
            return null;
        }

        return win;
    }

    void checkDrawnWindowsLocked() {
        if (mWaitingForDrawnCallbacks.isEmpty()) {
            return;
        }
        for (int i = mWaitingForDrawnCallbacks.size() - 1; i >= 0; i--) {
            final WindowContainer<?> container = mWaitingForDrawnCallbacks.keyAt(i);
            for (int j = container.mWaitingForDrawn.size() - 1; j >= 0; j--) {
                final WindowState win = (WindowState) container.mWaitingForDrawn.get(j);
                ProtoLog.i(WM_DEBUG_SCREEN_ON,
                        "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d",
                        win, win.mRemoved, win.isVisible(), win.mHasSurface,
                        win.mWinAnimator.mDrawState);
                if (win.mRemoved || !win.mHasSurface || !win.isVisibleByPolicy()) {
                    // Window has been removed or hidden; no draw will now happen, so stop waiting.
                    ProtoLog.w(WM_DEBUG_SCREEN_ON, "Aborted waiting for drawn: %s", win);
                    container.mWaitingForDrawn.remove(win);
                    if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                        traceEndWaitingForWindowDrawn(win);
                    }
                } else if (win.hasDrawn()) {
                    // Window is now drawn (and shown).
                    ProtoLog.d(WM_DEBUG_SCREEN_ON, "Window drawn win=%s", win);
                    container.mWaitingForDrawn.remove(win);
                    if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                        traceEndWaitingForWindowDrawn(win);
                    }
                }
            }
            if (container.mWaitingForDrawn.isEmpty()) {
                ProtoLog.d(WM_DEBUG_SCREEN_ON, "All windows drawn!");
                mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
                mWaitingForDrawnCallbacks.removeAt(i).sendToTarget();
            }
        }
    }

    private void traceStartWaitingForWindowDrawn(WindowState window) {
        final String traceName = TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD + "#"
                + window.getWindowTag();
        final String shortenedTraceName = traceName.substring(0, Math.min(
                TRACE_MAX_SECTION_NAME_LENGTH, traceName.length()));
        Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, shortenedTraceName, /* cookie= */ 0);
    }

    private void traceEndWaitingForWindowDrawn(WindowState window) {
        final String traceName = TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD + "#"
                + window.getWindowTag();
        final String shortenedTraceName = traceName.substring(0, Math.min(
                TRACE_MAX_SECTION_NAME_LENGTH, traceName.length()));
        Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, shortenedTraceName, /* cookie= */ 0);
    }

    void requestTraversal() {
        mWindowPlacerLocked.requestTraversal();
    }

    /** Note that Locked in this case is on mLayoutToAnim */
    void scheduleAnimationLocked() {
        mAnimator.scheduleAnimation();
    }

    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
        boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);
        if (changed && Flags.systemContentPriority()) {
            DisplayContent dc = mRoot.getTopFocusedDisplayContent();
            if (dc != null && updateWindowInteractionHistoryByFocus(dc.mCurrentFocus)) {
                scheduleAnimationLocked();
            }
        }
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        return changed;
    }

    static int getPropertyInt(String[] tokens, int index, int defUnits, int defDps,
            DisplayMetrics dm) {
        if (index < tokens.length) {
            String str = tokens[index];
            if (str != null && str.length() > 0) {
                try {
                    int val = Integer.parseInt(str);
                    return val;
                } catch (Exception e) {
                }
            }
        }
        if (defUnits == TypedValue.COMPLEX_UNIT_PX) {
            return defDps;
        }
        int val = (int)TypedValue.applyDimension(defUnits, defDps, dm);
        return val;
    }

    void createWatermark() {
        if (mWatermark != null) {
            return;
        }

        File file = new File("/system/etc/setup.conf");
        FileInputStream in = null;
        DataInputStream ind = null;
        try {
            in = new FileInputStream(file);
            ind = new DataInputStream(in);
            String line = ind.readLine();
            if (line != null) {
                String[] toks = line.split("%");
                if (toks != null && toks.length > 0) {
                    // TODO(multi-display): Show watermarks on secondary displays.
                    final DisplayContent displayContent = getDefaultDisplayContentLocked();
                    mWatermark = new Watermark(displayContent, displayContent.mRealDisplayMetrics,
                            toks, mTransaction);
                    mTransaction.apply();
                }
            }
        } catch (FileNotFoundException e) {
        } catch (IOException e) {
        } finally {
            if (ind != null) {
                try {
                    ind.close();
                } catch (IOException e) {
                }
            } else if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                }
            }
        }
    }

    @Override
    public void setRecentsVisibility(boolean visible) {
        if (!checkCallingPermission(
                android.Manifest.permission.STATUS_BAR, "setRecentsVisibility()")) {
            throw new SecurityException("Requires STATUS_BAR permission");
        }
        synchronized (mGlobalLock) {
            mPolicy.setRecentsVisibilityLw(visible);
        }
    }

    @Override
    public void hideTransientBars(int displayId) {
        if (!checkCallingPermission(
                android.Manifest.permission.STATUS_BAR, "hideTransientBars()")) {
            throw new SecurityException("Requires STATUS_BAR permission");
        }

        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent != null) {
                displayContent.getInsetsPolicy().hideTransient();
            } else {
                Slog.w(TAG, "hideTransientBars with invalid displayId=" + displayId);
            }
        }
    }

    @Override
    public void updateStaticPrivacyIndicatorBounds(int displayId,
            Rect[] staticBounds) {
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent != null) {
                displayContent.updatePrivacyIndicatorBounds(staticBounds);
            } else {
                Slog.w(TAG, "updateStaticPrivacyIndicatorBounds with invalid displayId="
                        + displayId);
            }
        }
    }

    @EnforcePermission(android.Manifest.permission.STATUS_BAR)
    public void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) {
        setNavBarVirtualKeyHapticFeedbackEnabled_enforcePermission();

        synchronized (mGlobalLock) {
            mPolicy.setNavBarVirtualKeyHapticFeedbackEnabledLw(enabled);
        }
    }

    @Override
    public void createInputConsumer(IBinder token, String name, int displayId,
            InputChannel inputChannel) {
        if (!mAtmService.isCallerRecents(Binder.getCallingUid())
                && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) {
            throw new SecurityException("createInputConsumer requires INPUT_CONSUMER permission");
        }

        synchronized (mGlobalLock) {
            DisplayContent display = mRoot.getDisplayContent(displayId);
            if (display != null) {
                display.getInputMonitor().createInputConsumer(token, name, inputChannel,
                        Binder.getCallingPid(), Binder.getCallingUserHandle());
            }
        }
    }

    @Override
    public boolean destroyInputConsumer(IBinder token, int displayId) {
        if (!mAtmService.isCallerRecents(Binder.getCallingUid())
                && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) {
            throw new SecurityException("destroyInputConsumer requires INPUT_CONSUMER permission");
        }

        synchronized (mGlobalLock) {
            DisplayContent display = mRoot.getDisplayContent(displayId);
            if (display != null) {
                return display.getInputMonitor().destroyInputConsumer(token);
            }
            return false;
        }
    }

    @EnforcePermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
    @Override
    public Region getCurrentImeTouchRegion() {
        getCurrentImeTouchRegion_enforcePermission();
        synchronized (mGlobalLock) {
            final Region r = new Region();
            // TODO(b/111080190): this method is only return the recent focused IME touch region,
            // For Multi-Session IME, will need to add API for given display Id to
            // get the right IME touch region.
            for (int i = mRoot.mChildren.size() - 1; i >= 0; --i) {
                final DisplayContent displayContent = mRoot.mChildren.get(i);
                if (displayContent.mInputMethodWindow != null) {
                    displayContent.mInputMethodWindow.getTouchableRegion(r);
                    return r;
                }
            }
            return r;
        }
    }

    @Override
    public boolean hasNavigationBar(int displayId) {
        synchronized (mGlobalLock) {
            final DisplayContent dc = mRoot.getDisplayContent(displayId);
            if (dc == null) {
                return false;
            }
            return dc.getDisplayPolicy().hasNavigationBar();
        }
    }

    @Override
    public void lockNow(Bundle options) {
        mPolicy.lockNow(options);
    }

    public void showRecentApps() {
        mPolicy.showRecentApps();
    }

    @Override
    public boolean isSafeModeEnabled() {
        return mSafeMode;
    }

    @Override
    public boolean clearWindowContentFrameStats(IBinder token) {
        if (!checkCallingPermission(Manifest.permission.FRAME_STATS,
                "clearWindowContentFrameStats()")) {
            throw new SecurityException("Requires FRAME_STATS permission");
        }
        synchronized (mGlobalLock) {
            WindowState windowState = mWindowMap.get(token);
            if (windowState == null) {
                return false;
            }
            final SurfaceControl surfaceControl = windowState.mWinAnimator.mSurfaceControl;
            if (surfaceControl == null) {
                return false;
            }
            return surfaceControl.clearContentFrameStats();
        }
    }

    @Override
    public WindowContentFrameStats getWindowContentFrameStats(IBinder token) {
        if (!checkCallingPermission(Manifest.permission.FRAME_STATS,
                "getWindowContentFrameStats()")) {
            throw new SecurityException("Requires FRAME_STATS permission");
        }
        synchronized (mGlobalLock) {
            WindowState windowState = mWindowMap.get(token);
            if (windowState == null) {
                return null;
            }
            final SurfaceControl surfaceControl = windowState.mWinAnimator.mSurfaceControl;
            if (surfaceControl == null) {
                return null;
            }
            if (mTempWindowRenderStats == null) {
                mTempWindowRenderStats = new WindowContentFrameStats();
            }
            WindowContentFrameStats stats = mTempWindowRenderStats;
            if (!surfaceControl.getContentFrameStats(stats)) {
                return null;
            }
            return stats;
        }
    }

    private void dumpPolicyLocked(PrintWriter pw, String[] args) {
        pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)");
        mPolicy.dump("    ", pw, args);
    }

    private void dumpAnimatorLocked(PrintWriter pw, boolean dumpAll) {
        pw.println("WINDOW MANAGER ANIMATOR STATE (dumpsys window animator)");
        mAnimator.dumpLocked(pw, "    ", dumpAll);
    }

    private void dumpTokensLocked(PrintWriter pw, boolean dumpAll) {
        pw.println("WINDOW MANAGER TOKENS (dumpsys window tokens)");
        mRoot.dumpTokens(pw, dumpAll);
    }


    private void dumpHighRefreshRateBlacklist(PrintWriter pw) {
        pw.println("WINDOW MANAGER HIGH REFRESH RATE BLACKLIST (dumpsys window refresh)");
        mHighRefreshRateDenylist.dump(pw);
    }

    private void dumpTraceStatus(PrintWriter pw) {
        pw.println("WINDOW MANAGER TRACE (dumpsys window trace)");
        pw.print(mWindowTracing.getStatus() + "\n");
    }

    private void dumpLogStatus(PrintWriter pw) {
        pw.println("Deprecated legacy command. Use Perfetto commands instead.");
        return;
    }

    private void dumpSessionsLocked(PrintWriter pw) {
        pw.println("WINDOW MANAGER SESSIONS (dumpsys window sessions)");
        for (int i=0; i<mSessions.size(); i++) {
            Session s = mSessions.valueAt(i);
            pw.print("  Session "); pw.print(s); pw.println(':');
            s.dump(pw, "    ");
        }
    }

    /**
     * Write to a protocol buffer output stream. Protocol buffer message definition is at
     * {@link android.internal.perfetto.protos.Windowmanagerservice.WindowManagerServiceDumpProto}.
     *
     * @param proto     Stream to write the WindowContainer object to.
     * @param logLevel  Determines the amount of data to be written to the Protobuf.
     */
    void dumpDebugLocked(ProtoOutputStream proto, @WindowTracingLogLevel int logLevel) {
        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
                TracingUtils.uiTracingSliceName("Window::dumpDebugLocked"));
        try {
            mPolicy.dumpDebug(proto, POLICY);
            mRoot.dumpDebug(proto, ROOT_WINDOW_CONTAINER, logLevel);
            final DisplayContent topFocusedDisplayContent = mRoot.getTopFocusedDisplayContent();
            if (topFocusedDisplayContent.mCurrentFocus != null) {
                topFocusedDisplayContent.mCurrentFocus
                        .writeIdentifierToProto(proto, FOCUSED_WINDOW);
            }
            if (topFocusedDisplayContent.mFocusedApp != null) {
                topFocusedDisplayContent.mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
            }
            final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
            if (imeWindow != null) {
                imeWindow.writeIdentifierToProto(proto, INPUT_METHOD_WINDOW);
            }
            proto.write(FOCUSED_DISPLAY_ID, topFocusedDisplayContent.getDisplayId());
            proto.write(HARD_KEYBOARD_AVAILABLE, mHardKeyboardAvailable);

            // This is always true for now since we still update the window frames at the server
            // side. Once we move the window layout to the client side, this can be false when we
            // are waiting for the frames.
            proto.write(WINDOW_FRAMES_VALID, true);

            // Write the BackNavigationController's state into the protocol buffer
            mAtmService.mBackNavigationController.dumpDebug(proto, BACK_NAVIGATION);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
        }
    }

    private void dumpWindowsLocked(PrintWriter pw, boolean dumpAll,
            ArrayList<WindowState> windows) {
        pw.println("WINDOW MANAGER WINDOWS (dumpsys window windows)");
        mRoot.dumpWindowsNoHeader(pw, dumpAll, windows);

        if (!mHidingNonSystemOverlayWindows.isEmpty()) {
            pw.println();
            pw.println("  Hiding System Alert Windows:");
            for (int i = mHidingNonSystemOverlayWindows.size() - 1; i >= 0; i--) {
                final WindowState w = mHidingNonSystemOverlayWindows.get(i);
                pw.print("  #"); pw.print(i); pw.print(' ');
                pw.print(w);
                if (dumpAll) {
                    pw.println(":");
                    w.dump(pw, "    ", true);
                } else {
                    pw.println();
                }
            }
        }
        if (mForceRemoves != null && !mForceRemoves.isEmpty()) {
            pw.println();
            pw.println("  Windows force removing:");
            for (int i=mForceRemoves.size()-1; i>=0; i--) {
                WindowState w = mForceRemoves.get(i);
                pw.print("  Removing #"); pw.print(i); pw.print(' ');
                        pw.print(w);
                if (dumpAll) {
                    pw.println(":");
                    w.dump(pw, "    ", true);
                } else {
                    pw.println();
                }
            }
        }
        if (!mDestroySurface.isEmpty()) {
            pw.println();
            pw.println("  Windows waiting to destroy their surface:");
            for (int i=mDestroySurface.size()-1; i>=0; i--) {
                WindowState w = mDestroySurface.get(i);
                if (windows == null || windows.contains(w)) {
                    pw.print("  Destroy #"); pw.print(i); pw.print(' ');
                            pw.print(w);
                    if (dumpAll) {
                        pw.println(":");
                        w.dump(pw, "    ", true);
                    } else {
                        pw.println();
                    }
                }
            }
        }
        if (!mResizingWindows.isEmpty()) {
            pw.println();
            pw.println("  Windows waiting to resize:");
            for (int i=mResizingWindows.size()-1; i>=0; i--) {
                WindowState w = mResizingWindows.get(i);
                if (windows == null || windows.contains(w)) {
                    pw.print("  Resizing #"); pw.print(i); pw.print(' ');
                            pw.print(w);
                    if (dumpAll) {
                        pw.println(":");
                        w.dump(pw, "    ", true);
                    } else {
                        pw.println();
                    }
                }
            }
        }
        if (!mWaitingForDrawnCallbacks.isEmpty()) {
            pw.println();
            pw.println("  Clients waiting for these windows to be drawn:");
            mWaitingForDrawnCallbacks.forEach((wc, callback) -> {
                pw.print("  WindowContainer ");
                pw.println(wc.getName());
                for (int i = wc.mWaitingForDrawn.size() - 1; i >= 0; i--) {
                    final WindowState win = (WindowState) wc.mWaitingForDrawn.get(i);
                    pw.print("  Waiting #"); pw.print(i); pw.print(' '); pw.print(win);
                }
            });

        }
        pw.println();
        pw.print("  mGlobalConfiguration="); pw.println(mRoot.getConfiguration());
        pw.print("  mHasPermanentDpad="); pw.println(mHasPermanentDpad);
        mRoot.dumpTopFocusedDisplayId(pw);
        mRoot.forAllDisplays(dc -> {
            final int displayId = dc.getDisplayId();
            pw.print("  Minimum task size of display#"); pw.print(displayId);
            pw.print(' '); pw.println(dc.mMinSizeOfResizeableTaskDp);
        });
        pw.print("  mBlurEnabled="); pw.println(mBlurController.getBlurEnabled());
        pw.print("  mDisableSecureWindows="); pw.println(mDisableSecureWindows);

        mInputManagerCallback.dump(pw, "  ");
        mSnapshotController.dump(pw, " ");

        dumpAccessibilityController(pw, /* force= */ false);

        if (dumpAll) {
            final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
            if (imeWindow != null) {
                pw.print("  mInputMethodWindow="); pw.println(imeWindow);
            }
            mWindowPlacerLocked.dump(pw, "  ");
            pw.print("  mSystemBooted="); pw.print(mSystemBooted);
                    pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);

            mRoot.dumpLayoutNeededDisplayIds(pw);

            pw.print("  mTransactionSequence="); pw.println(mTransactionSequence);
            final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
            pw.print("  mRotation="); pw.println(defaultDisplayContent.getRotation());
            pw.print("  mLastOrientation=");
                    pw.println(defaultDisplayContent.getLastOrientation());
            pw.print("  mWaitingForConfig=");
                    pw.println(defaultDisplayContent.mWaitingForConfig);
            pw.print("  mWindowsInsetsChanged="); pw.println(mWindowsInsetsChanged);
            mRotationWatcherController.dump(pw);

            pw.print("  Animation settings: disabled=");
                    pw.print(mAnimatorScale.isAnimationsDisabled());
                    pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
                    pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
                    pw.print(" animator="); pw.println(mAnimatorScale.getCurrentScale());
        }
    }

    private void dumpAccessibilityController(PrintWriter pw, boolean force) {
        boolean hasCallbacks = mAccessibilityController.hasCallbacks();
        if (!hasCallbacks && !force) {
            return;
        }
        if (!hasCallbacks) {
            pw.println("AccessibilityController doesn't have callbacks, but printing it anways:");
        } else {
            pw.println("AccessibilityController:");
        }
        mAccessibilityController.dump(pw, "  ");
    }

    private void dumpAccessibilityLocked(PrintWriter pw) {
        dumpAccessibilityController(pw, /* force= */ true);
    }

    private boolean dumpWindows(PrintWriter pw, String name, boolean dumpAll) {
        final ArrayList<WindowState> windows = new ArrayList();
        if ("apps".equals(name) || "visible".equals(name) || "visible-apps".equals(name)) {
            final boolean appsOnly = name.contains("apps");
            final boolean visibleOnly = name.contains("visible");
            synchronized (mGlobalLock) {
                if (appsOnly) {
                    mRoot.dumpDisplayContents(pw);
                }

                mRoot.forAllWindows((w) -> {
                    if ((!visibleOnly || w.isVisible())
                            && (!appsOnly || w.mActivityRecord != null)) {
                        windows.add(w);
                    }
                }, true /* traverseTopToBottom */);
            }
        } else {
            synchronized (mGlobalLock) {
                mRoot.getWindowsByName(windows, name);
            }
        }

        if (windows.isEmpty()) {
            return false;
        }

        synchronized (mGlobalLock) {
            dumpWindowsLocked(pw, dumpAll, windows);
        }
        return true;
    }

    private void dumpLastANRLocked(PrintWriter pw) {
        pw.println("WINDOW MANAGER LAST ANR (dumpsys window lastanr)");
        if (mLastANRState == null) {
            pw.println("  <no ANR has occurred since boot>");
        } else {
            pw.println(mLastANRState);
        }
    }

    /**
     * Saves information about the state of the window manager at
     * the time an ANR occurred before anything else in the system changes
     * in response.
     *
     * @param activity The application that ANR'd, may be null.
     * @param windowState The window that ANR'd, may be null.
     * @param reason The reason for the ANR, may be null.
     */
    void saveANRStateLocked(ActivityRecord activity, WindowState windowState, String reason) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new FastPrintWriter(sw, false, 1024);
        pw.println("  ANR time: " + DateFormat.getDateTimeInstance().format(new Date()));
        if (activity != null) {
            pw.println("  Application at fault: " + activity.stringName);
        }
        if (windowState != null) {
            pw.println("  Window at fault: " + windowState.mAttrs.getTitle());
        }
        if (reason != null) {
            pw.println("  Reason: " + reason);
        }
        pw.println();
        final ArrayList<WindowState> relatedWindows = new ArrayList<>();
        for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
            final DisplayContent dc = mRoot.getChildAt(i);
            final int displayId = dc.getDisplayId();
            final WindowState currentFocus = dc.mCurrentFocus;
            final ActivityRecord focusedApp = dc.mFocusedApp;
            pw.println("  Display #" + displayId + " currentFocus=" + currentFocus
                    + " focusedApp=" + focusedApp);
            if (!dc.mWinAddedSinceNullFocus.isEmpty()) {
                pw.println("  Windows added in display #" + displayId + " since null focus: "
                        + dc.mWinAddedSinceNullFocus);
            }
            if (!dc.mWinRemovedSinceNullFocus.isEmpty()) {
                pw.println("  Windows removed in display #" + displayId + " since null focus: "
                        + dc.mWinRemovedSinceNullFocus);
            }
            pw.println("  Tasks in top down Z order:");
            dc.forAllTaskDisplayAreas(tda -> {
                tda.dump(pw, "    ", false /* dumpAll */);
            });
            dc.getInputMonitor().dump(pw, "  ");
            pw.println();
            dc.forAllWindows(w -> {
                if ((currentFocus != null && Objects.equals(w.mAttrs.packageName,
                        currentFocus.mAttrs.packageName)) || (focusedApp != null
                        && Objects.equals(w.mAttrs.packageName, focusedApp.packageName))) {
                    relatedWindows.add(w);
                }
            }, true /* traverseTopToBottom */);
        }
        if (windowState != null && !relatedWindows.contains(windowState)) {
            relatedWindows.add(windowState);
        }
        mRoot.dumpWindowsNoHeader(pw, true /* dumpAll */, relatedWindows);
        pw.println();
        pw.close();
        mLastANRState = sw.toString();

        mH.removeMessages(H.RESET_ANR_MESSAGE);
        mH.sendEmptyMessageDelayed(H.RESET_ANR_MESSAGE, LAST_ANR_LIFETIME_DURATION_MSECS);
    }

    @Override
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
        PriorityDump.dump(mPriorityDumper, fd, pw, args);
    }

    @NeverCompile // Avoid size overhead of debugging code.
    private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) {
        boolean dumpAll = false;

        int opti = 0;
        while (opti < args.length) {
            String opt = args[opti];
            if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
                break;
            }
            opti++;
            if ("-a".equals(opt)) {
                dumpAll = true;
            } else if ("-h".equals(opt)) {
                pw.println("Window manager dump options:");
                pw.println("  [-a] [-h] [cmd] ...");
                pw.println("  cmd may be one of:");
                pw.println("    l[astanr]: last ANR information");
                pw.println("    p[policy]: policy state");
                pw.println("    a[animator]: animator state");
                pw.println("    s[essions]: active sessions");
                pw.println("    surfaces: active surfaces (debugging enabled only)");
                pw.println("    d[isplays]: active display contents");
                pw.println("    t[okens]: token list");
                pw.println("    w[indows]: window list");
                pw.println("    a11y[accessibility]: accessibility-related state");
                pw.println("    package-config: installed packages having app-specific config");
                pw.println("    trace: print trace status and write Winscope trace to file");
                pw.println("  cmd may also be a NAME to dump windows.  NAME may");
                pw.println("    be a partial substring in a window name, a");
                pw.println("    Window hex object identifier, or");
                pw.println("    \"all\" for all windows, or");
                pw.println("    \"visible\" for the visible windows.");
                pw.println("    \"visible-apps\" for the visible app windows.");
                pw.println("  -a: include all available server state.");
                pw.println("  --proto: output dump in protocol buffer format.");
                return;
            } else {
                pw.println("Unknown argument: " + opt + "; use -h for help");
            }
        }

        if (useProto) {
            final ProtoOutputStream proto = new ProtoOutputStream(fd);
            synchronized (mGlobalLock) {
                dumpDebugLocked(proto, WindowTracingLogLevel.ALL);
            }
            proto.flush();
            return;
        }
        // Is the caller requesting to dump a particular piece of data?
        if (opti < args.length) {
            String cmd = args[opti];
            opti++;
            if ("lastanr".equals(cmd) || "l".equals(cmd)) {
                synchronized (mGlobalLock) {
                    dumpLastANRLocked(pw);
                }
                return;
            } else if ("policy".equals(cmd) || "p".equals(cmd)) {
                synchronized (mGlobalLock) {
                    dumpPolicyLocked(pw, args);
                }
                return;
            } else if ("animator".equals(cmd) || "a".equals(cmd)) {
                synchronized (mGlobalLock) {
                    dumpAnimatorLocked(pw, true);
                }
                return;
            } else if ("sessions".equals(cmd) || "s".equals(cmd)) {
                synchronized (mGlobalLock) {
                    dumpSessionsLocked(pw);
                }
                return;
            } else if ("displays".equals(cmd) || "d".equals(cmd)) {
                synchronized (mGlobalLock) {
                    mRoot.dumpDisplayContents(pw);
                }
                return;
            } else if ("tokens".equals(cmd) || "t".equals(cmd)) {
                synchronized (mGlobalLock) {
                    dumpTokensLocked(pw, true);
                }
                return;
            } else if ("windows".equals(cmd) || "w".equals(cmd)) {
                synchronized (mGlobalLock) {
                    dumpWindowsLocked(pw, true, null);
                }
                return;
            } else if ("accessibility".equals(cmd) || "a11y".equals(cmd)) {
                synchronized (mGlobalLock) {
                    dumpAccessibilityLocked(pw);
                }
                return;
            } else if ("all".equals(cmd)) {
                synchronized (mGlobalLock) {
                    dumpWindowsLocked(pw, true, null);
                }
                return;
            } else if ("containers".equals(cmd)) {
                synchronized (mGlobalLock) {
                    mRoot.dumpChildrenNames(pw, "");
                    pw.println(" ");
                    mRoot.forAllWindows(w -> {pw.println(w);}, true /* traverseTopToBottom */);
                }
                return;
            } else if ("trace".equals(cmd)) {
                dumpTraceStatus(pw);
                return;
            } else if ("logging".equals(cmd)) {
                dumpLogStatus(pw);
                return;
            } else if ("refresh".equals(cmd)) {
                dumpHighRefreshRateBlacklist(pw);
                return;
            } else if ("constants".equals(cmd)) {
                mConstants.dump(pw);
                return;
            } else if ("package-config".equals(cmd)) {
                mAtmService.dumpInstalledPackagesConfig(pw);
                return;
            } else {
                // Dumping a single name?
                if (!dumpWindows(pw, cmd, dumpAll)) {
                    pw.println("Bad window command, or no windows match: " + cmd);
                    pw.println("Use -h for help.");
                }
                return;
            }
        }

        synchronized (mGlobalLock) {
            pw.println();
            final String separator = "---------------------------------------------------------"
                    + "----------------------";
            if (dumpAll) {
                pw.println(separator);
            }
            dumpLastANRLocked(pw);
            pw.println();
            if (dumpAll) {
                pw.println(separator);
            }
            dumpPolicyLocked(pw, args);
            pw.println();
            if (dumpAll) {
                pw.println(separator);
            }
            dumpAnimatorLocked(pw, dumpAll);
            pw.println();
            if (dumpAll) {
                pw.println(separator);
            }
            dumpSessionsLocked(pw);
            pw.println();
            if (dumpAll) {
                pw.println(separator);
            }
            if (dumpAll) {
                pw.println(separator);
            }
            mRoot.dumpDisplayContents(pw);
            pw.println();
            if (dumpAll) {
                pw.println(separator);
            }
            dumpTokensLocked(pw, dumpAll);
            pw.println();
            if (dumpAll) {
                pw.println(separator);
            }
            dumpWindowsLocked(pw, dumpAll, null);
            if (dumpAll) {
                pw.println(separator);
            }
            dumpTraceStatus(pw);
            if (dumpAll) {
                pw.println(separator);
            }
            dumpLogStatus(pw);
            if (dumpAll) {
                pw.println(separator);
            }
            dumpHighRefreshRateBlacklist(pw);
            if (dumpAll) {
                pw.println(separator);
            }
            mAtmService.dumpInstalledPackagesConfig(pw);
            if (dumpAll) {
                pw.println(separator);
            }
            mConstants.dump(pw);
            if (dumpAll) {
                pw.println(separator);
            }
            mSystemPerformanceHinter.dump(pw, "");
            mTrustedPresentationListenerController.dump(pw);
            mSensitiveContentPackages.dump(pw);
            mScreenRecordingCallbackController.dump(pw);
        }
    }

    // Called by the heartbeat to ensure locks are not held indefnitely (for deadlock detection).
    @Override
    public void monitor() {
        synchronized (mGlobalLock) { }
    }

    // There is an inherent assumption that this will never return null.
    // TODO(multi-display): Inspect all the call-points of this method to see if they make sense to
    // support non-default display.
    DisplayContent getDefaultDisplayContentLocked() {
        return mRoot.getDisplayContent(DEFAULT_DISPLAY);
    }

    /**
     * Returns the main display content for the user passed as parameter.
     *
     * <p>Visible background users may have their own designated main display, distinct from the
     * system default display (DEFAULT_DISPLAY). Visible background users operate independently
     * with their own main displays. These secondary user main displays host the secondary home
     * activities.
     */
    @Nullable
    DisplayContent getUserMainDisplayContentLocked(@UserIdInt int userId) {
        final int userMainDisplayId = mUmInternal.getMainDisplayAssignedToUser(userId);
        if (userMainDisplayId == -1) {
            return null;
        }
        return mRoot.getDisplayContent(userMainDisplayId);
    }

    public void onOverlayChanged() {
        // Post to display thread so it can get the latest display info.
        mH.post(() -> {
            synchronized (mGlobalLock) {
                mAtmService.deferWindowLayout();
                mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().onOverlayChanged());
                mAtmService.continueWindowLayout();
            }
        });
    }

    @Override
    public Object getWindowManagerLock() {
        return mGlobalLock;
    }

    @Override
    public int getDockedStackSide() {
        return 0;
    }

    void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) {
        synchronized (mGlobalLock) {
            mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays;
            mRoot.updateDisplayImePolicyCache();
        }
    }

    @VisibleForTesting
    void setIsPc(boolean isPc) {
        synchronized (mGlobalLock) {
            mIsPc = isPc;
        }
    }

    static int dipToPixel(int dip, DisplayMetrics displayMetrics) {
        return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
    }

    public void registerPinnedTaskListener(int displayId, IPinnedTaskListener listener) {
        if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS,
                "registerPinnedTaskListener()")) {
            return;
        }
        if (!mAtmService.mSupportsPictureInPicture) {
            return;
        }
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            displayContent.getPinnedTaskController().registerPinnedTaskListener(listener);
        }
    }

    @Override
    public KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId) {
        long token = Binder.clearCallingIdentity();
        try {
            return mPolicy.getApplicationLaunchKeyboardShortcuts(deviceId);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    @Override
    public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
        enforceRegisterWindowManagerListenersPermission("requestAppKeyboardShortcuts");

        WindowState focusedWindow = getFocusedWindow();
        if (focusedWindow == null || focusedWindow.mClient == null) {
            notifyReceiverWithEmptyBundle(receiver);
            return;
        }
        try {
            focusedWindow.mClient.requestAppKeyboardShortcuts(receiver, deviceId);
        } catch (RemoteException e) {
            notifyReceiverWithEmptyBundle(receiver);
        }
    }

    @Override
    public void requestImeKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
        enforceRegisterWindowManagerListenersPermission("requestImeKeyboardShortcuts");

        WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
        if (imeWindow == null || imeWindow.mClient == null) {
            notifyReceiverWithEmptyBundle(receiver);
            return;
        }
        try {
            imeWindow.mClient.requestAppKeyboardShortcuts(receiver, deviceId);
        } catch (RemoteException e) {
            notifyReceiverWithEmptyBundle(receiver);
        }
    }

    private void enforceRegisterWindowManagerListenersPermission(String message) {
        mContext.enforceCallingOrSelfPermission(REGISTER_WINDOW_MANAGER_LISTENERS, message);
    }

    private static void notifyReceiverWithEmptyBundle(IResultReceiver receiver) {
        try {
            receiver.send(0, Bundle.EMPTY);
        } catch (RemoteException e) {
            ProtoLog.e(WM_ERROR, "unable to call receiver for empty keyboard shortcuts");
        }
    }

    @Override
    public void getStableInsets(int displayId, Rect outInsets) throws RemoteException {
        synchronized (mGlobalLock) {
            getStableInsetsLocked(displayId, outInsets);
        }
    }

    /** This is used when there's no app info available and shall return the system default.*/
    void getStableInsetsLocked(int displayId, Rect outInsets) {
        outInsets.setEmpty();
        final DisplayContent dc = mRoot.getDisplayContent(displayId);
        if (dc != null) {
            final DisplayInfo di = dc.getDisplayInfo();
            outInsets.set(dc.getDisplayPolicy().getDecorInsetsInfo(
                    di.rotation, di.logicalWidth, di.logicalHeight).mConfigInsets);
        }
    }

    /**
     * Update a tap exclude region in the window identified by the provided id. Touches down on this
     * region will not:
     * <ol>
     * <li>Switch focus to this window.</li>
     * <li>Move the display of this window to top.</li>
     * <li>Send the touch events to this window.</li>
     * </ol>
     * Passing an invalid region will remove the area from the exclude region of this window.
     */
    void updateTapExcludeRegion(IWindow client, Region region) {
        synchronized (mGlobalLock) {
            final WindowState callingWin = windowForClient(null /* session */, client);
            if (callingWin == null) {
                ProtoLog.w(WM_ERROR, "Bad requesting window %s", client);
                return;
            }
            callingWin.updateTapExcludeRegion(region);
        }
    }

    /**
     * Forwards a scroll capture request to the appropriate window, if available.
     *
     * @param displayId the display for the request
     * @param behindClient token for a window, used to filter the search to windows behind it
     * @param taskId specifies the id of a task the result must belong to or -1 to match any task
     * @param listener to receive the response
     */
    public void requestScrollCapture(int displayId, @Nullable IBinder behindClient, int taskId,
            IScrollCaptureResponseListener listener) {
        if (!checkCallingPermission(READ_FRAME_BUFFER, "requestScrollCapture()")) {
            throw new SecurityException("Requires READ_FRAME_BUFFER permission");
        }
        final long token = Binder.clearCallingIdentity();
        try {
            ScrollCaptureResponse.Builder responseBuilder = new ScrollCaptureResponse.Builder();
            synchronized (mGlobalLock) {
                DisplayContent dc = mRoot.getDisplayContent(displayId);
                if (dc == null) {
                    ProtoLog.e(WM_ERROR,
                            "Invalid displayId for requestScrollCapture: %d", displayId);
                    responseBuilder.setDescription(String.format("bad displayId: %d", displayId));
                    listener.onScrollCaptureResponse(responseBuilder.build());
                    return;
                }
                WindowState topWindow = null;
                if (behindClient != null) {
                    topWindow = windowForClient(null /* session */, behindClient);
                }
                WindowState targetWindow = dc.findScrollCaptureTargetWindow(topWindow, taskId);
                if (targetWindow == null) {
                    responseBuilder.setDescription("findScrollCaptureTargetWindow returned null");
                    listener.onScrollCaptureResponse(responseBuilder.build());
                    return;
                }
                try {
                    // Forward to the window for handling, which will respond using the callback.
                    targetWindow.mClient.requestScrollCapture(listener);
                } catch (RemoteException e) {
                    ProtoLog.w(WM_ERROR,
                            "requestScrollCapture: caught exception dispatching to window."
                                    + "token=%s", targetWindow.mClient.asBinder());
                    responseBuilder.setWindowTitle(targetWindow.getName());
                    responseBuilder.setPackageName(targetWindow.getOwningPackage());
                    responseBuilder.setDescription(String.format("caught exception: %s", e));
                    listener.onScrollCaptureResponse(responseBuilder.build());
                }
            }
        } catch (RemoteException e) {
            ProtoLog.w(WM_ERROR,
                    "requestScrollCapture: caught exception dispatching callback: %s", e);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    @Override
    public int getWindowingMode(int displayId) {
        if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "getWindowingMode()")) {
            throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
        }

        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                ProtoLog.w(WM_ERROR,
                        "Attempted to get windowing mode of a display that does not exist: %d",
                        displayId);
                return WindowConfiguration.WINDOWING_MODE_UNDEFINED;
            }
            return mDisplayWindowSettings.getWindowingModeLocked(displayContent);
        }
    }

    @Override
    public void setWindowingMode(int displayId, int mode) {
        if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setWindowingMode()")) {
            throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
        }

        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
                if (displayContent == null) {
                    ProtoLog.w(WM_ERROR,
                            "Attempted to set windowing mode to a display that does not exist: %d",
                            displayId);
                    return;
                }

                int lastWindowingMode = displayContent.getWindowingMode();
                mDisplayWindowSettings.setWindowingModeLocked(displayContent, mode);

                displayContent.reconfigureDisplayLocked();

                if (lastWindowingMode != displayContent.getWindowingMode()) {
                    // reconfigure won't detect this change in isolation because the windowing mode
                    // is already set on the display, so fire off a new config now.
                    displayContent.sendNewConfiguration();
                    // Now that all configurations are updated, execute pending transitions.
                    displayContent.executeAppTransition();
                }
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @Override
    public @RemoveContentMode int getRemoveContentMode(int displayId) {
        if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "getRemoveContentMode()")) {
            throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
        }

        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                ProtoLog.w(WM_ERROR,
                        "Attempted to get remove mode of a display that does not exist: %d",
                        displayId);
                return REMOVE_CONTENT_MODE_UNDEFINED;
            }
            return displayContent.getRemoveContentMode();
        }
    }

    @Override
    public void setRemoveContentMode(int displayId, @RemoveContentMode int mode) {
        if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setRemoveContentMode()")) {
            throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
        }

        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
                if (displayContent == null) {
                    ProtoLog.w(WM_ERROR,
                            "Attempted to set remove mode to a display that does not exist: %d",
                            displayId);
                    return;
                }

                mDisplayWindowSettings.setRemoveContentModeLocked(displayContent, mode);
                displayContent.reconfigureDisplayLocked();
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @Override
    public boolean shouldShowWithInsecureKeyguard(int displayId) {
        if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "shouldShowWithInsecureKeyguard()")) {
            throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
        }

        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                ProtoLog.w(WM_ERROR, "Attempted to get flag of a display that does not exist: %d",
                        displayId);
                return false;
            }
            return mDisplayWindowSettings.shouldShowWithInsecureKeyguardLocked(displayContent);
        }
    }

    @Override
    public void setShouldShowWithInsecureKeyguard(int displayId, boolean shouldShow) {
        if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW,
                "setShouldShowWithInsecureKeyguard()")) {
            throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
        }
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
                if (displayContent == null) {
                    ProtoLog.w(WM_ERROR, "Attempted to set flag to a display that does not exist: "
                            + "%d", displayId);
                    return;
                }

                mDisplayWindowSettings.setShouldShowWithInsecureKeyguardLocked(displayContent,
                        shouldShow);

                displayContent.reconfigureDisplayLocked();
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @Override
    public boolean shouldShowSystemDecors(int displayId) {
        if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "shouldShowSystemDecors()")) {
            throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
        }

        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                ProtoLog.w(WM_ERROR, "Attempted to get system decors flag of a display that does "
                        + "not exist: %d", displayId);
                return false;
            }
            return displayContent.isSystemDecorationsSupported();
        }
    }

    @Override
    public boolean isEligibleForDesktopMode(int displayId) {
        if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "isEligibleForDesktopMode()")) {
            throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
        }

        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                ProtoLog.e(WM_ERROR, "Attempted to check isEligibleForDesktopMode() "
                        + "for a display that does not exist: %d", displayId);
                return false;
            }
            if (!displayContent.isSystemDecorationsSupported()) {
                return false;
            }
            if (!displayContent.isWindowingModeSupported(WINDOWING_MODE_FREEFORM)) {
                return false;
            }
            return displayContent.isDefaultDisplay || displayContent.allowContentModeSwitch();
        }
    }

    @Override
    public @DisplayImePolicy int getDisplayImePolicy(int displayId) {
        if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "getDisplayImePolicy()")) {
            throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
        }
        final Map<Integer, Integer> displayImePolicyCache = mDisplayImePolicyCache;
        if (!displayImePolicyCache.containsKey(displayId)) {
            ProtoLog.w(WM_ERROR,
                    "Attempted to get IME policy of a display that does not exist: %d",
                    displayId);
            return DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
        }
        return displayImePolicyCache.get(displayId);
    }

    @Override
    public void setDisplayImePolicy(int displayId, @DisplayImePolicy int imePolicy) {
        if (!checkCallingPermission(INTERNAL_SYSTEM_WINDOW, "setDisplayImePolicy()")) {
            throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
        }
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
                if (displayContent == null) {
                    ProtoLog.w(WM_ERROR, "Attempted to set IME policy to a display"
                            + " that does not exist: %d", displayId);
                    return;
                }
                if (!displayContent.isTrusted()) {
                    throw new SecurityException("Attempted to set IME policy to an untrusted "
                            + "virtual display: " + displayId);
                }

                mDisplayWindowSettings.setDisplayImePolicy(displayContent, imePolicy);

                displayContent.reconfigureDisplayLocked();
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @Override
    public void onNotificationShadeExpanded(IBinder token, boolean expanded) {
        synchronized (mGlobalLock) {
            final WindowState w = mWindowMap.get(token);
            if (w == null || w != w.mDisplayContent.getDisplayPolicy().getNotificationShade()) {
                return;
            }
            final WindowProcessController topApp = mAtmService.mTopApp;
            // Demotes the priority of top app if notification shade is expanded to occlude the app.
            // So the notification shade may have more capacity to draw and animate.
            final int demoteTopAppReasons = mAtmService.mDemoteTopAppReasons;
            if (expanded && mAtmService.mTopProcessState == ActivityManager.PROCESS_STATE_TOP
                    && (demoteTopAppReasons & DEMOTE_TOP_REASON_EXPANDED_NOTIFICATION_SHADE) == 0) {
                mAtmService.mDemoteTopAppReasons =
                        demoteTopAppReasons | DEMOTE_TOP_REASON_EXPANDED_NOTIFICATION_SHADE;
                mAtmService.mActivityStateUpdater.setExpandedNotificationShadeAsync(true);
                Trace.instant(TRACE_TAG_WINDOW_MANAGER, "demote-top-for-ns");
                if (topApp != null) {
                    topApp.scheduleUpdateOomAdj();
                }
            } else if (!expanded
                    && (demoteTopAppReasons & DEMOTE_TOP_REASON_EXPANDED_NOTIFICATION_SHADE) != 0) {
                mAtmService.mDemoteTopAppReasons =
                        demoteTopAppReasons & ~DEMOTE_TOP_REASON_EXPANDED_NOTIFICATION_SHADE;
                mAtmService.mActivityStateUpdater.setExpandedNotificationShadeAsync(false);
                Trace.instant(TRACE_TAG_WINDOW_MANAGER, "cancel-demote-top-for-ns");
                if (topApp != null) {
                    topApp.scheduleUpdateOomAdj();
                }
            }
        }
    }

    @Override
    public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver)
            throws RemoteException {
        if (!checkCallingPermission(REGISTER_WINDOW_MANAGER_LISTENERS, "registerShortcutKey")) {
            throw new SecurityException(
                    "Requires REGISTER_WINDOW_MANAGER_LISTENERS permission");
        }
        mPolicy.registerShortcutKey(shortcutCode, shortcutKeyReceiver);
    }

    private final class LocalService extends WindowManagerInternal {

        @Override
        public AccessibilityControllerInternal getAccessibilityController() {
            return AccessibilityController.getAccessibilityControllerInternal(
                    WindowManagerService.this);
        }

        @Override
        public void clearSnapshotCache() {
            synchronized (mGlobalLock) {
                mTaskSnapshotController.clearSnapshotCache();
            }
        }

        @Override
        public void requestTraversalFromDisplayManager() {
            synchronized (mGlobalLock) {
                requestTraversal();
            }
        }

        @Override
        public void onDisplayManagerReceivedDeviceState(int deviceState) {
            mH.post(() -> {
                synchronized (mGlobalLock) {
                    mRoot.onDisplayManagerReceivedDeviceState(deviceState);
                }
            });
        }

        @Override
        public void setMagnificationSpec(int displayId, MagnificationSpec spec) {
            synchronized (mGlobalLock) {
                if (mAccessibilityController.hasCallbacks()) {
                    mAccessibilityController.setMagnificationSpec(displayId, spec);
                } else {
                    throw new IllegalStateException("Magnification callbacks not set!");
                }
            }
        }

        @Override
        public void setFullscreenMagnificationActivated(int displayId, boolean activated) {
            synchronized (mGlobalLock) {
                if (mAccessibilityController.hasCallbacks()) {
                    mAccessibilityController
                            .setFullscreenMagnificationActivated(displayId, activated);
                } else {
                    throw new IllegalStateException("Magnification callbacks not set!");
                }
            }
        }

        @Override
        public void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion) {
            synchronized (mGlobalLock) {
                if (mAccessibilityController.hasCallbacks()) {
                    mAccessibilityController.getMagnificationRegion(displayId, magnificationRegion);
                } else {
                    throw new IllegalStateException("Magnification callbacks not set!");
                }
            }
        }

        @Override
        public boolean setMagnificationCallbacks(int displayId,
                @Nullable MagnificationCallbacks callbacks) {
            synchronized (mGlobalLock) {
                return mAccessibilityController.setMagnificationCallbacks(displayId, callbacks);
            }
        }

        @Override
        public void setWindowsForAccessibilityCallback(int displayId,
                WindowsForAccessibilityCallback callback) {
            synchronized (mGlobalLock) {
                mAccessibilityController.setWindowsForAccessibilityCallback(displayId, callback);
            }
        }

        @Override
        public void setInputFilter(IInputFilter filter) {
            mInputManager.setInputFilter(filter);
        }

        @Override
        public IBinder getFocusedWindowToken() {
            synchronized (mGlobalLock) {
                return mAccessibilityController.getFocusedWindowToken();
            }
        }

        // TODO (b/229837707): Delete this method after changing the solution.
        @Override
        public IBinder getFocusedWindowTokenFromWindowStates() {
            synchronized (mGlobalLock) {
                final WindowState windowState = getFocusedWindowLocked();
                if (windowState != null) {
                    return windowState.mClient.asBinder();
                }
                return null;
            }
        }

        @Override
        public void moveDisplayToTopIfAllowed(int displayId) {
            WindowManagerService.this.moveDisplayToTopIfAllowed(displayId);
        }

        @Override
        public void requestWindowFocus(IBinder windowToken) {
            synchronized (mGlobalLock) {
                final InputTarget inputTarget =
                        WindowManagerService.this.getInputTargetFromWindowTokenLocked(windowToken);
                WindowManagerService.this.onPointerDownOutsideFocusLocked(inputTarget,
                        false /* fromHandler */);
            }
        }

        @Override
        public boolean isKeyguardLocked() {
            return WindowManagerService.this.isKeyguardLocked();
        }

        @Override
        public boolean isKeyguardShowingAndNotOccluded() {
            return WindowManagerService.this.isKeyguardShowingAndNotOccluded();
        }

        @Override
        public boolean isKeyguardShowing() {
            return mPolicy.isKeyguardShowing();
        }

        @Override
        public boolean isKeyguardSecure(@UserIdInt int userId) {
            return mPolicy.isKeyguardSecure(userId);
        }

        @Override
        public void showGlobalActions() {
            WindowManagerService.this.showGlobalActions();
        }

        @Override
        public void getWindowFrame(IBinder token, Rect outBounds) {
            synchronized (mGlobalLock) {
                WindowState windowState = mWindowMap.get(token);
                if (windowState != null) {
                    outBounds.set(windowState.getFrame());
                } else {
                    outBounds.setEmpty();
                }
            }
        }

        @Override
        public Pair<Matrix, MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
                IBinder token) {
            return mAccessibilityController
                    .getWindowTransformationMatrixAndMagnificationSpec(token);
        }

        @Override
        public void waitForAllWindowsDrawn(Message message, long timeout, int displayId) {
            Objects.requireNonNull(message.getTarget());
            boolean allWindowsDrawn = false;
            synchronized (mGlobalLock) {
                allWindowsDrawn = waitForAllWindowsDrawnLocked(message, timeout, displayId);
            }
            if (allWindowsDrawn) {
                message.sendToTarget();
            }
        }

        /** Return {@code true} if all windows have been drawn. */
        private boolean waitForAllWindowsDrawnLocked(Message message, long timeout, int displayId) {
            final WindowContainer<?> container = displayId == INVALID_DISPLAY
                    ? mRoot : mRoot.getDisplayContent(displayId);
            if (container == null) {
                // The waiting container doesn't exist, no need to wait. Treat as drawn.
                return true;
            }
            if (displayId == INVALID_DISPLAY
                    && mRoot.getDefaultDisplay().mDisplayUpdater.waitForTransition(message)) {
                // Use the ready-to-play of transition as the signal.
                return false;
            }
            container.waitForAllWindowsDrawn();
            mWindowPlacerLocked.requestTraversal();
            mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
            if (container.mWaitingForDrawn.isEmpty()) {
                return true;
            }
            if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                for (int i = 0; i < container.mWaitingForDrawn.size(); i++) {
                    traceStartWaitingForWindowDrawn(container.mWaitingForDrawn.get(i));
                }
            }

            mWaitingForDrawnCallbacks.put(container, message);
            mH.sendNewMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, container, timeout);
            checkDrawnWindowsLocked();
            return false;
        }

        @Override
        public void setForcedDisplaySize(int displayId, int width, int height) {
            WindowManagerService.this.setForcedDisplaySize(displayId, width, height);
        }

        @Override
        public void clearForcedDisplaySize(int displayId) {
            WindowManagerService.this.clearForcedDisplaySize(displayId);
        }

        @Override
        public void addWindowToken(IBinder token, int type, int displayId,
                @Nullable Bundle options) {
            WindowManagerService.this.addWindowToken(token, type, displayId, options);
        }

        @Override
        public void removeWindowToken(IBinder binder, boolean removeWindows, boolean animateExit,
                int displayId) {
            WindowManagerService.this.removeWindowToken(binder, removeWindows, animateExit,
                    displayId);
        }

        @Override
        public void moveWindowTokenToDisplay(IBinder binder, int displayId) {
            WindowManagerService.this.moveWindowTokenToDisplay(binder, displayId);
        }

        // TODO(multi-display): currently only used by PWM to notify keyguard transitions as well
        // forwarding it to SystemUI for synchronizing status and navigation bar animations.
        @Override
        public void registerAppTransitionListener(AppTransitionListener listener) {
            synchronized (mGlobalLock) {
                mAtmService.getTransitionController().registerLegacyListener(listener);
            }
        }

        @Override
        public void registerTaskSystemBarsListener(TaskSystemBarsListener listener) {
            synchronized (mGlobalLock) {
                mTaskSystemBarsListenerController.registerListener(listener);
            }
        }

        @Override
        public void unregisterTaskSystemBarsListener(TaskSystemBarsListener listener) {
            synchronized (mGlobalLock) {
                mTaskSystemBarsListenerController.unregisterListener(listener);
            }
        }

        @Override
        public void reportPasswordChanged(int userId) {
            mKeyguardDisableHandler.updateKeyguardEnabled(userId);
        }

        @Override
        public int getInputMethodWindowVisibleHeight(int displayId) {
            synchronized (mGlobalLock) {
                final DisplayContent dc = mRoot.getDisplayContent(displayId);
                return dc.getInputMethodWindowVisibleHeight();
            }
        }

        @Override
        public void setDismissImeOnBackKeyPressed(boolean dismissImeOnBackKeyPressed) {
            mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
        }

        @Override
        public void updateImeTargetWindow(@NonNull IBinder windowToken) {
            // TODO (b/34628091): Use this method to address the window animation issue.
            if (DEBUG_INPUT_METHOD) {
                Slog.w(TAG_WM, "updateImeTargetWindow windowToken: " + windowToken);
            }
            synchronized (mGlobalLock) {
                final InputTarget imeInputTarget = getInputTargetFromWindowTokenLocked(windowToken);
                if (imeInputTarget != null) {
                    imeInputTarget.getDisplayContent()
                            .updateImeInputAndControlTarget(imeInputTarget);

                    // In case of a virtual display that may not show the IME, reset the
                    // inputTarget of all other displays
                    WindowState imeInputTargetWindow = imeInputTarget.getWindowState();
                    if (imeInputTargetWindow != null) {
                        final InsetsControlTarget fallback = imeInputTarget.getDisplayContent()
                                .getImeHost(imeInputTargetWindow);
                        if (imeInputTargetWindow != fallback) {
                            // fallback should be the RemoteInsetsControlTarget of the
                            // default display
                            final int currentDisplayId = imeInputTarget.getDisplayContent()
                                    .getDisplayId();
                            mRoot.forAllDisplays(display -> {
                                if (display.getDisplayId() != currentDisplayId) {
                                    display.setImeInputTarget(null /* target */);
                                }
                            });
                        }
                    }
                }
            }
        }

        @Override
        public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId) {
            synchronized (mGlobalLock) {
                final WindowState imeTargetWindow = mWindowMap.get(imeTarget);
                if (imeTargetWindow == null) {
                    return false;
                }
                final DisplayContent dc = mRoot.getDisplayContent(displayId);
                if (dc == null) {
                    Slog.w(TAG, "Invalid displayId:" + displayId + ", fail to show IME screenshot");
                    return false;
                }

                dc.showImeScreenshot(imeTargetWindow);
                return true;
            }
        }

        @Override
        public boolean removeImeScreenshot(int displayId) {
            synchronized (mGlobalLock) {
                final DisplayContent dc = mRoot.getDisplayContent(displayId);
                if (dc == null) {
                    Slog.w(TAG, "Invalid displayId:" + displayId
                            + ", fail to remove IME screenshot");
                    return false;
                }
                dc.removeImeScreenshotImmediately();
                return true;
            }
        }

        @Override
        public void setAnimationsDisabledForDisplay(int displayId, boolean disabled) {
            WindowManagerService.this.setAnimationsDisabledForDisplay(displayId, disabled);
        }

        @Override
        public boolean isHardKeyboardAvailable() {
            synchronized (mGlobalLock) {
                return mHardKeyboardAvailable;
            }
        }

        @Override
        public void setOnHardKeyboardStatusChangeListener(
                OnHardKeyboardStatusChangeListener listener) {
            synchronized (mGlobalLock) {
                mHardKeyboardStatusChangeListener = listener;
            }
        }

        @Override
        public void setOnImeRequestedChangedListener(
                OnImeRequestedChangedListener listener) {
            synchronized (mGlobalLock) {
                mOnImeRequestedChangedListener = listener;
            }
        }

        @Override
        public void computeWindowsForAccessibility(int displayId) {
            mAccessibilityController.performComputeChangedWindowsNot(displayId, true);
        }

        @Override
        public void setVr2dDisplayId(int vr2dDisplayId) {
            if (DEBUG_DISPLAY) {
                Slog.d(TAG, "setVr2dDisplayId called for: " + vr2dDisplayId);
            }
            synchronized (mGlobalLock) {
                mVr2dDisplayId = vr2dDisplayId;
            }
        }

        @Override
        public void registerDragDropControllerCallback(IDragDropCallback callback) {
            mDragDropController.registerCallback(callback);
        }

        @Override
        public void lockNow() {
            WindowManagerService.this.lockNow(null);
        }

        @Override
        public int getWindowOwnerUserId(IBinder token) {
            synchronized (mGlobalLock) {
                WindowState window = mWindowMap.get(token);
                if (window != null) {
                    return window.mShowUserId;
                }
                return UserHandle.USER_NULL;
            }
        }

        @Override
        public void setWallpaperShowWhenLocked(IBinder binder, boolean showWhenLocked) {
            synchronized (mGlobalLock) {
                final WindowToken token = mRoot.getWindowToken(binder);
                if (token == null || token.asWallpaperToken() == null) {
                    ProtoLog.w(WM_ERROR,
                            "setWallpaperShowWhenLocked: non-existent wallpaper token: %s", binder);
                    return;
                }
                token.asWallpaperToken().setShowWhenLocked(showWhenLocked);
            }
        }

        @Override
        public void setWallpaperCropHints(IBinder binder, SparseArray<Rect> cropHints) {
            synchronized (mGlobalLock) {
                final WindowToken token = mRoot.getWindowToken(binder);
                if (token == null || token.asWallpaperToken() == null) {
                    ProtoLog.w(WM_ERROR,
                            "setWallpaperCropHints: non-existent wallpaper token: %s", binder);
                    return;
                }
                token.asWallpaperToken().setCropHints(cropHints);
            }
        }

        @Override
        public boolean isUidFocused(int uid) {
            synchronized (mGlobalLock) {
                for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
                    final DisplayContent displayContent = mRoot.getChildAt(i);
                    if (displayContent.mCurrentFocus != null
                            && uid == displayContent.mCurrentFocus.getOwningUid()) {
                        return true;
                    }
                }
                return false;
            }
        }


        @Override
        public byte[] backupDisplayWindowSettings(int userId) {
            final AtomicFile file =
                    DisplayWindowSettingsProvider.getOverrideSettingsFileForUser(userId);
            final byte[] payload;
            try (InputStream inputStream = file.openRead()) {
                payload = DisplayWindowSettingsXmlHelper.readAndFilterSettings(inputStream);
            } catch (FileNotFoundException e) {
                Slog.w(TAG, "No display settings file found; skipping backup.");
                return null; // Nothing to back up
            } catch (IOException e) {
                if (DEBUG) Slog.d(TAG, "Skip display window settings backup", e);
                return null;
            }
            return payload;
        }

        @Override
        public void restoreDisplayWindowSettings(int userId, byte[] payload) {
            final AtomicFile file =
                    DisplayWindowSettingsProvider.getOverrideSettingsFileForUser(userId);
            try (var oStream = new AtomicFileOutputStream(file)) {
                oStream.write(payload);
                oStream.markSuccess();
            } catch (IOException e) {
                Slog.e(TAG, "restoreDisplayWindowSettings failed", e);
                throw new RuntimeException(e);
            }
            synchronized (mGlobalLock) {
                mDisplayWindowSettingsProvider.setOverrideSettingsForUser(userId);
            }
        }

        @Override
        @ImeClientFocusResult
        public int hasInputMethodClientFocus(IBinder windowToken, int uid, int pid, int displayId) {
            if (displayId == Display.INVALID_DISPLAY) {
                return ImeClientFocusResult.INVALID_DISPLAY_ID;
            }
            synchronized (mGlobalLock) {
                final DisplayContent displayContent = mRoot.getTopFocusedDisplayContent();
                InputTarget target = getInputTargetFromWindowTokenLocked(windowToken);
                if (target == null) {
                    return ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
                }
                final int tokenDisplayId = target.getDisplayContent().getDisplayId();
                if (tokenDisplayId != displayId) {
                    Slog.e(TAG, "isInputMethodClientFocus: display ID mismatch."
                            + " from client: " + displayId
                            + " from window: " + tokenDisplayId);
                    return ImeClientFocusResult.DISPLAY_ID_MISMATCH;
                }
                if (displayContent == null
                        || displayContent.getDisplayId() != displayId
                        || !displayContent.hasAccess(uid)) {
                    return ImeClientFocusResult.INVALID_DISPLAY_ID;
                }

                if (target.isInputMethodClientFocus(uid, pid)) {
                    return ImeClientFocusResult.HAS_IME_FOCUS;
                }
                // Okay, how about this...  what is the current focus?
                // It seems in some cases we may not have moved the IME layering
                // target window, such as when it was in a pop-up window,
                // so let's also look at the current focus.  (An example:
                // go to Gmail, start searching so the keyboard goes up,
                // press home.  Sometimes the IME won't go down.)
                // Would be nice to fix this more correctly, but it's
                // way at the end of a release, and this should be good enough.
                final WindowState currentFocus = displayContent.mCurrentFocus;
                if (currentFocus != null && currentFocus.mSession.mUid == uid
                        && currentFocus.mSession.mPid == pid) {
                    return currentFocus.canBeImeLayeringTarget()
                            ? ImeClientFocusResult.HAS_IME_FOCUS
                            : ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
                }
            }
            return ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
        }

        @Override
        public boolean isUidAllowedOnDisplay(int displayId, int uid) {
            if (displayId == Display.DEFAULT_DISPLAY) {
                return true;
            }
            if (displayId == Display.INVALID_DISPLAY) {
                return false;
            }
            synchronized (mGlobalLock) {
                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                return displayContent != null && displayContent.hasAccess(uid);
            }
        }

        @Override
        public int getDisplayIdForWindow(IBinder windowToken) {
            synchronized (mGlobalLock) {
                final WindowState window = mWindowMap.get(windowToken);
                if (window != null) {
                    return window.getDisplayContent().getDisplayId();
                }
                return Display.INVALID_DISPLAY;
            }
        }

        @Override
        public int getTopFocusedDisplayId() {
            synchronized (mGlobalLock) {
                return mRoot.getTopFocusedDisplayContent().getDisplayId();
            }
        }

        @Override
        public Context getTopFocusedDisplayUiContext() {
            synchronized (mGlobalLock) {
                return mRoot.getTopFocusedDisplayContent().getDisplayUiContext();
            }
        }

        @Override
        public Context getDisplayUiContext(int displayId) {
            synchronized (mGlobalLock) {
                return mRoot.getDisplayUiContext(displayId);
            }
        }

        @Override
        @Nullable
        public DisplayPolicy getDisplayPolicy(int displayId) {
            synchronized (mGlobalLock) {
                final DisplayContent dc = mRoot.getDisplayContent(displayId);
                return dc == null ? null : dc.getDisplayPolicy();
            }
        }

        @Override
        public void setNonDefaultDisplayRotation(int displayId, @Surface.Rotation int rotation,
                @NonNull String caller) {
            if (displayId == Display.DEFAULT_DISPLAY || displayId == Display.INVALID_DISPLAY) {
                Slog.w(TAG, "Cannot set rotation for display with id: " + displayId);
                return;
            }
            synchronized (mGlobalLock) {
                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                if (displayContent == null) {
                    Slog.w(TAG, "Cannot set rotation for display " + displayId
                            + " due to missing DisplayContent");
                    return;
                }
                displayContent.getDisplayRotation().setUserRotation(
                        displayContent.getDisplayRotation().getUserRotationMode(), rotation,
                        caller);
            }
        }

        @Override
        public void setHomeSupportedOnDisplay(String displayUniqueId, int displayType,
                boolean supported) {
            final long origId = Binder.clearCallingIdentity();
            try {
                synchronized (mGlobalLock) {
                    mDisplayWindowSettings.setHomeSupportedOnDisplayLocked(
                            displayUniqueId, displayType, supported);
                }
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
        }

        @Override
        public boolean isHomeSupportedOnDisplay(int displayId) {
            synchronized (mGlobalLock) {
                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                if (displayContent == null) {
                    ProtoLog.w(WM_ERROR, "Attempted to get home support flag of a display that "
                            + "does not exist: %d", displayId);
                    return false;
                }
                return displayContent.isHomeSupported();
            }
        }

        @Override
        public void setIgnoreActivitySizeRestrictionsOnDisplay(@NonNull String displayUniqueId,
                int displayType, boolean enabled) {
            final long origId = Binder.clearCallingIdentity();
            try {
                synchronized (mGlobalLock) {
                    mDisplayWindowSettings.setIgnoreActivitySizeRestrictionsOnDisplayLocked(
                            displayUniqueId, displayType, enabled);
                }
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
        }

        @Override
        public void clearDisplaySettings(String displayUniqueId, int displayType) {
            final long origId = Binder.clearCallingIdentity();
            try {
                synchronized (mGlobalLock) {
                    mDisplayWindowSettings.clearDisplaySettings(displayUniqueId, displayType);
                }
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
        }

        @Override
        public @DisplayImePolicy int getDisplayImePolicy(int displayId) {
            return WindowManagerService.this.getDisplayImePolicy(displayId);
        }

        @Override
        public void onDisplayUiModeChanged(int displayId) {
            synchronized (mGlobalLock) {
                final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
                if (displayContent == null) {
                    ProtoLog.w(WM_ERROR,
                            "Received UI mode change on a display that does not exist: %d",
                            displayId);
                    return;
                }

                displayContent.getDisplayPolicy().onDisplayUiModeChanged();
                // Trigger a configuration change.
                displayContent.reconfigureDisplayLocked();
            }
        }

        @Override
        public void addRefreshRateRangeForPackage(@NonNull String packageName,
                float minRefreshRate, float maxRefreshRate) {
            synchronized (mGlobalLock) {
                mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().getRefreshRatePolicy()
                        .addRefreshRateRangeForPackage(
                                packageName, minRefreshRate, maxRefreshRate));
            }
        }

        @Override
        public void removeRefreshRateRangeForPackage(@NonNull String packageName) {
            synchronized (mGlobalLock) {
                mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().getRefreshRatePolicy()
                        .removeRefreshRateRangeForPackage(packageName));
            }
        }

        @Override
        public boolean isTouchOrFaketouchDevice() {
            synchronized (mGlobalLock) {
                if (mIsTouchDevice && !mIsFakeTouchDevice) {
                    throw new IllegalStateException(
                            "touchscreen supported device must report faketouch.");
                }
                return mIsFakeTouchDevice;
            }
        }

        @Override
        public @Nullable KeyInterceptionInfo getKeyInterceptionInfoFromToken(IBinder inputToken) {
            return mKeyInterceptionInfoForToken.get(inputToken);
        }

        @Override
        public void setAccessibilityIdToSurfaceMetadata(
                IBinder windowToken, int accessibilityWindowId) {
            synchronized (mGlobalLock) {
                final WindowState state = mWindowMap.get(windowToken);
                if (state == null) {
                    Slog.w(TAG, "Cannot find window which accessibility connection is added to");
                    return;
                }
                mTransaction.setMetadata(state.mSurfaceControl,
                        SurfaceControl.METADATA_ACCESSIBILITY_ID, accessibilityWindowId).apply();
            }
        }

        @Override
        public String getWindowName(@NonNull IBinder binder) {
            synchronized (mGlobalLock) {
                final WindowState w = mWindowMap.get(binder);
                return w != null ? w.getName() : null;
            }
        }

        @NonNull
        @Override
        public ImeTargetInfo onToggleImeRequested(boolean show, @NonNull IBinder focusedToken,
                @NonNull IBinder requestToken, int displayId) {
            final String focusedWindowName;
            final String requestWindowName;
            final String imeLayeringTargetName;
            final String imeInputTargetName;
            final String imeControlTargetName;
            final String imeSurfaceParentName;
            synchronized (mGlobalLock) {
                focusedWindowName = String.valueOf(mWindowMap.get(focusedToken));
                requestWindowName = String.valueOf(mWindowMap.get(requestToken));
                final DisplayContent dc = mRoot.getDisplayContent(displayId);
                if (dc != null) {
                    imeLayeringTargetName = String.valueOf(dc.getImeLayeringTarget());
                    imeInputTargetName =  String.valueOf(dc.getImeInputTarget());
                    imeControlTargetName = String.valueOf(dc.getImeControlTarget());
                    imeSurfaceParentName = String.valueOf(dc.mInputMethodSurfaceParent);
                    if (show) {
                        dc.onShowImeRequested();
                    }
                } else {
                    imeLayeringTargetName = "no-display";
                    imeInputTargetName = "no-display";
                    imeControlTargetName = "no-display";
                    imeSurfaceParentName = "no-display";
                }
            }
            return new ImeTargetInfo(focusedWindowName, requestWindowName, imeLayeringTargetName,
                    imeInputTargetName, imeControlTargetName, imeSurfaceParentName);
        }

        @Override
        public boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
            return WindowManagerService.this.shouldRestoreImeVisibility(imeTargetWindowToken);
       }

        @Override
        public void addTrustedTaskOverlay(int taskId,
                SurfaceControlViewHost.SurfacePackage overlay) {
            if (overlay == null) {
                throw new IllegalArgumentException("Invalid overlay passed in for task=" + taskId);
            }
            synchronized (mGlobalLock) {
                if (overlay.getSurfaceControl() == null
                    || !overlay.getSurfaceControl().isValid()) {
                    throw new IllegalArgumentException(
                            "Invalid overlay surfacecontrol passed in for task=" + taskId);
                }
                final Task task = mRoot.getRootTask(taskId);
                if (task == null) {
                    throw new IllegalArgumentException("no task with taskId" + taskId);
                }
                task.addTrustedOverlay(overlay, task.getTopVisibleAppMainWindow());
            }
        }

        @Override
        public void removeTrustedTaskOverlay(int taskId,
                SurfaceControlViewHost.SurfacePackage overlay) {
            if (overlay == null) {
                throw new IllegalArgumentException("Invalid overlay passed in for task=" + taskId);
            }
            synchronized (mGlobalLock) {
                if (overlay.getSurfaceControl() == null
                        || !overlay.getSurfaceControl().isValid()) {
                    throw new IllegalArgumentException(
                            "Invalid overlay surfacecontrol passed in for task=" + taskId);
                }
                final Task task = mRoot.getRootTask(taskId);
                if (task == null) {
                    throw new IllegalArgumentException("no task with taskId" + taskId);
                }
                task.removeTrustedOverlay(overlay);
            }
        }

        @Override
        public SurfaceControl getHandwritingSurfaceForDisplay(int displayId) {
            synchronized (mGlobalLock) {
                final DisplayContent dc = mRoot.getDisplayContent(displayId);
                if (dc == null) {
                    Slog.e(TAG, "Failed to create a handwriting surface on display: "
                            + displayId + " - DisplayContent not found.");
                    return null;
                }
                final SurfaceControl inputOverlay = dc.getInputOverlayLayer();
                if (inputOverlay == null) {
                    Slog.e(TAG, "Failed to create a gesture monitor on display: " + displayId
                            + " - Input overlay layer is not initialized.");
                    return null;
                }
                // TODO(b/210039666): Use a method like add/removeDisplayOverlay if available.
                return makeSurfaceBuilder()
                        .setContainerLayer()
                        .setName("IME Handwriting Surface")
                        .setCallsite("getHandwritingSurfaceForDisplay")
                        .setParent(inputOverlay)
                        .build();
            }
        }

        @Override
        public boolean isPointInsideWindow(@NonNull IBinder windowToken, int displayId,
                float displayX, float displayY) {
            synchronized (mGlobalLock) {
                final WindowState w = mWindowMap.get(windowToken);
                if (w == null || w.getDisplayId() != displayId) {
                    return false;
                }

                return w.getBounds().contains((int) displayX, (int) displayY);
            }
        }

        @Override
        public boolean setContentRecordingSession(
                @Nullable ContentRecordingSession incomingSession) {
            synchronized (mGlobalLock) {
                // Allow the controller to handle teardown of a non-task session.
                if (incomingSession == null
                        || incomingSession.getContentToRecord() != RECORD_CONTENT_TASK) {
                    mContentRecordingController.setContentRecordingSessionLocked(incomingSession,
                            WindowManagerService.this);
                    return true;
                }
                // For a task session, find the activity identified by the launch cookie.
                final WindowContainerInfo wci =
                        getTaskWindowContainerInfoForRecordingSession(incomingSession);
                if (wci == null) {
                    Slog.w(TAG, "Handling a new recording session; unable to find the "
                            + "WindowContainerToken");
                    return false;
                }
                // Replace the launch cookie in the session details with the task's
                // WindowContainerToken.
                incomingSession.setTokenToRecord(wci.getToken().asBinder());
                // Also replace the UNKNOWN target UID with the actual UID.
                incomingSession.setTargetUid(wci.getUid());
                mContentRecordingController.setContentRecordingSessionLocked(incomingSession,
                        WindowManagerService.this);
                return true;
            }
        }

        @Override
        public SurfaceControl getA11yOverlayLayer(int displayId) {
            synchronized (mGlobalLock) {
                DisplayContent dc = mRoot.getDisplayContent(displayId);
                if (dc != null) {
                    return dc.getA11yOverlayLayer();
                }
            }
            return null;
        }

        @Override
        public void captureDisplay(
                int displayId,
                @Nullable ScreenCaptureInternal.CaptureArgs captureArgs,
                ScreenCaptureInternal.ScreenCaptureListener listener) {
            WindowManagerService.this.captureDisplay(displayId, captureArgs, listener);
        }

        @Override
        public boolean hasNavigationBar(int displayId) {
            return WindowManagerService.this.hasNavigationBar(displayId);
        }

        @Override
        public void setOrientationRequestPolicy(boolean respected,
                int[] fromOrientations, int[] toOrientations) {
            synchronized (mGlobalLock) {
                WindowManagerService.this.setOrientationRequestPolicy(respected,
                        fromOrientations, toOrientations);
            }
        }

        @Override
        public @Nullable IBinder getTargetWindowTokenFromInputToken(IBinder inputToken) {
            synchronized (mGlobalLock) {
                InputTarget inputTarget =
                        WindowManagerService.this.getInputTargetFromToken(inputToken);
                return inputTarget == null ? null : inputTarget.getWindowToken();
            }
        }

        @Override
        public void setBlockScreenCaptureForAppsSessionId(long sessionId) {
            synchronized (mGlobalLock) {
                if (sensitiveContentMetricsBugfix()
                        && mSensitiveContentProtectionSessionId != sessionId) {
                    mSensitiveContentProtectionSessionId = sessionId;
                }
            }
        }

        @Override
        public void addBlockScreenCaptureForApps(ArraySet<PackageInfo> packageInfos) {
            synchronized (mGlobalLock) {
                boolean modified =
                        mSensitiveContentPackages.addBlockScreenCaptureForApps(packageInfos);
                if (modified) {
                    WindowManagerService.this.refreshScreenCaptureDisabled();
                    if (sensitiveContentImprovements()) {
                        // TODO(b/331842561): Combine this traversal with the one inside
                        // refreshScreenCaptureDisabled above.
                        mRoot.forAllWindows((w) -> {
                            if (w.isVisible()) {
                                WindowManagerService.this.showToastIfBlockingScreenCapture(w);
                            } else if (sensitiveContentRecentsScreenshotBugfix()
                                    && shouldInvalidateSnapshot(w)) {
                                final Task task = w.getTask();
                                // preventing from showing up in starting window.
                                mTaskSnapshotController.removeAndDeleteSnapshot(
                                        task.mTaskId, task.mUserId);
                                // Refresh TaskThumbnailCache
                                task.onSnapshotInvalidated();
                            }
                        }, /* traverseTopToBottom= */ true);
                    }
                }
            }
        }

        private boolean shouldInvalidateSnapshot(WindowState w) {
            return w.getTask() != null
                    && mSensitiveContentPackages.shouldBlockScreenCaptureForApp(
                    w.getOwningPackage(), w.getOwningUid(), w.getWindowToken());
        }

        @Override
        public void removeBlockScreenCaptureForApps(ArraySet<PackageInfo> packageInfos) {
            synchronized (mGlobalLock) {
                boolean modified =
                        mSensitiveContentPackages.removeBlockScreenCaptureForApps(packageInfos);
                if (modified) {
                    WindowManagerService.this.refreshScreenCaptureDisabled();
                }
                if (sensitiveContentImprovements()) {
                    for (int i = 0; i < packageInfos.size(); i++) {
                        int uid = packageInfos.valueAt(i).getUid();
                        if (mCaptureBlockedToastShownUids.contains(uid)) {
                            mCaptureBlockedToastShownUids.remove(
                                    mCaptureBlockedToastShownUids.indexOf(uid));
                        }
                    }
                }
            }
        }

        @Override
        public void clearBlockedApps() {
            synchronized (mGlobalLock) {
                boolean modified = mSensitiveContentPackages.clearBlockedApps();
                if (modified) {
                    WindowManagerService.this.refreshScreenCaptureDisabled();
                }
                if (sensitiveContentImprovements()) {
                    mCaptureBlockedToastShownUids.clear();
                }
            }
        }

        @Override
        public void registerWindowFocusChangeListener(WindowFocusChangeListener listener) {
            WindowManagerService.this.addWindowFocusChangeListener(listener);
        }

        @Override
        public void unregisterWindowFocusChangeListener(WindowFocusChangeListener listener) {
            WindowManagerService.this.removeWindowFocusChangeListener(listener);
        }

        @Override
        public void registerOnWindowRemovedListener(OnWindowRemovedListener listener) {
            synchronized (mGlobalLock) {
                mOnWindowRemovedListeners.add(listener);
            }
        }

        @Override
        public void unregisterOnWindowRemovedListener(OnWindowRemovedListener listener) {
            synchronized (mGlobalLock) {
                mOnWindowRemovedListeners.remove(listener);
            }
        }

        @Override
        public boolean moveFocusToAdjacentEmbeddedActivityIfNeeded() {
            boolean focusMoved = false;
            synchronized (mGlobalLock) {
                final WindowState focusedWindow = getFocusedWindow();
                if (focusedWindow == null) {
                    return false;
                }

                if (moveFocusToAdjacentEmbeddedWindow(focusedWindow)) {
                    // Sync the input transactions to ensure the input focus updates as well.
                    focusMoved = true;
                }
            }

            if (focusMoved) {
                syncInputTransactions(false);
            }
            return focusMoved;
        }

        @Override
        public ScreenshotHardwareBuffer takeAssistScreenshot() {
            // WMS.takeAssistScreenshot takes care of the locking.
            return WindowManagerService.this.takeAssistScreenshot(/* predicate */ null,
                    DEFAULT_DISPLAY);
        }

        @Override
        public ScreenshotHardwareBuffer takeContextualSearchScreenshot(int uid, int displayId) {
            // WMS.takeAssistScreenshot takes care of the locking.
            return WindowManagerService.this.takeAssistScreenshot(win -> {
                switch (win.getWindowType()) {
                    case LayoutParams.TYPE_STATUS_BAR:
                    case LayoutParams.TYPE_NAVIGATION_BAR:
                    case LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
                    case LayoutParams.TYPE_POINTER:
                        return false;
                    case LayoutParams.TYPE_APPLICATION_OVERLAY:
                        return uid != win.getOwningUid();
                    default:
                        return true;
                }
            }, displayId);
        }

        @Override
        public void requestAssistScreenshot(IAssistDataReceiver receiver, IBinder activityToken) {
            int displayId = DEFAULT_DISPLAY;
            synchronized (mGlobalLock) {
                final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken);
                if (r != null && r.isAttached()) {
                    displayId = r.getDisplayId();
                } else {
                    Slog.e(TAG, "Failed to get displayId for activityToken: " + activityToken);
                }
            }
            WindowManagerService.this.requestAssistScreenshotInternal(receiver, displayId);
        }
    }

    /** Called to inform window manager if non-Vr UI shoul be disabled or not. */
    public void disableNonVrUi(boolean disable) {
        synchronized (mGlobalLock) {
            // Allow alert window notifications to be shown if non-vr UI is enabled.
            final boolean showAlertWindowNotifications = !disable;
            if (showAlertWindowNotifications == mShowAlertWindowNotifications) {
                return;
            }
            mShowAlertWindowNotifications = showAlertWindowNotifications;

            for (int i = mSessions.size() - 1; i >= 0; --i) {
                final Session s = mSessions.valueAt(i);
                s.setShowingAlertWindowNotificationAllowed(mShowAlertWindowNotifications);
            }
        }
    }

    boolean hasWideColorGamutSupport() {
        return mHasWideColorGamutSupport &&
                SystemProperties.getInt("persist.sys.sf.native_mode", 0) != 1;
    }

    boolean hasHdrSupport() {
        return mHasHdrSupport && hasWideColorGamutSupport();
    }

    void updateNonSystemOverlayWindowsVisibilityIfNeeded(WindowState win, boolean surfaceShown) {
        final boolean effective = (surfaceShown && win.hideNonSystemOverlayWindowsWhenVisible());
        if (effective == mHidingNonSystemOverlayWindows.contains(win)) {
            return;
        }

        if (effective) {
            mHidingNonSystemOverlayWindows.add(win);
        } else {
            mHidingNonSystemOverlayWindows.remove(win);
        }

        final boolean changed;
        final int uid = win.getOwningUid();
        final int numUIDsPreUpdate = mHidingNonSystemOverlayWindowsCountPerUid.size();
        final int newCount = mHidingNonSystemOverlayWindowsCountPerUid.getOrDefault(uid, 0)
                + (effective ? +1 : -1);
        if (newCount <= 0) {
            mHidingNonSystemOverlayWindowsCountPerUid.remove(uid);
        } else {
            mHidingNonSystemOverlayWindowsCountPerUid.put(uid, newCount);
        }
        final int numUIDsPostUpdate = mHidingNonSystemOverlayWindowsCountPerUid.size();
        // The visibility of SAWs needs to be refreshed when the number of uids that
        // request hiding SAWs changes between "0", "1", or "2+".
        changed = (numUIDsPostUpdate != numUIDsPreUpdate)
                && (numUIDsPostUpdate <= 1 || numUIDsPreUpdate <= 1);

        if (changed) {
            mRoot.forAllWindows((w) -> {
                w.setForceHideNonSystemOverlayWindowIfNeeded(shouldHideNonSystemOverlayWindow(w));
            }, false /* traverseTopToBottom */);
        }
    }

    /** Called from Accessibility Controller to apply magnification spec */
    public void applyMagnificationSpecLocked(int displayId, MagnificationSpec spec) {
        final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
        if (displayContent != null) {
            displayContent.applyMagnificationSpec(spec);
        }
    }

    SurfaceControl.Builder makeSurfaceBuilder() {
        return mSurfaceControlFactory.get();
    }

    @Override
    public void syncInputTransactions(boolean waitForAnimations) {
        final long token = Binder.clearCallingIdentity();
        try {
            if (waitForAnimations) {
                waitForAnimationsToComplete();
            }

            // Collect all input transactions from all displays to make sure we could sync all input
            // windows at same time.
            final SurfaceControl.Transaction t = mTransactionFactory.get();
            synchronized (mGlobalLock) {
                mWindowPlacerLocked.performSurfacePlacementIfScheduled();
                mRoot.forAllDisplays(displayContent ->
                        displayContent.getInputMonitor().updateInputWindowsImmediately(t));
            }

            CountDownLatch countDownLatch = new CountDownLatch(1);
            t.addWindowInfosReportedListener(countDownLatch::countDown).apply();
            countDownLatch.await(SYNC_INPUT_TRANSACTIONS_TIMEOUT_MS, TimeUnit.MILLISECONDS);
        } catch (InterruptedException exception) {
            Slog.e(TAG_WM, "Exception thrown while waiting for window infos to be reported",
                    exception);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    /**
     * Wait until all container animations and surface operations behalf of WindowManagerService
     * complete.
     */
    private void waitForAnimationsToComplete() {
        synchronized (mGlobalLock) {
            long timeoutRemaining = ANIMATION_COMPLETED_TIMEOUT_MS;
            // This could prevent if there is no container animation, we still have to apply the
            // pending transaction and exit waiting.
            mAnimator.mNotifyWhenNoAnimation = true;
            boolean animateStarting = false;
            while (timeoutRemaining > 0) {
                // Waiting until all starting windows has finished animating.
                animateStarting = !mAtmService.getTransitionController().isShellTransitionsEnabled()
                        && mRoot.forAllActivities(ActivityRecord::hasStartingWindow);
                boolean isAnimating = mAnimator.isAnimationScheduled()
                        || mRoot.isAnimating(CHILDREN, ANIMATION_TYPE_ALL)
                        || animateStarting;
                if (!isAnimating) {
                    // isAnimating is a legacy transition query and will be removed, so also add
                    // a check for whether this is in a shell-transition when not using legacy.
                    if (!mAtmService.getTransitionController().inTransition()) {
                        break;
                    }
                }
                long startTime = System.currentTimeMillis();
                try {
                    mGlobalLock.wait(timeoutRemaining);
                } catch (InterruptedException e) {
                }
                timeoutRemaining -= (System.currentTimeMillis() - startTime);
            }
            mAnimator.mNotifyWhenNoAnimation = false;

            WindowContainer animatingContainer;
            animatingContainer = mRoot.getAnimatingContainer(CHILDREN, ANIMATION_TYPE_ALL);
            if (mAnimator.isAnimationScheduled() || animatingContainer != null || animateStarting) {
                Slog.w(TAG, "Timed out waiting for animations to complete,"
                        + " animatingContainer=" + animatingContainer
                        + " animationType=" + SurfaceAnimator.animationTypeToString(
                        animatingContainer != null
                                ? animatingContainer.mSurfaceAnimator.getAnimationType()
                                : SurfaceAnimator.ANIMATION_TYPE_NONE)
                        + " animateStarting=" + animateStarting);
            }
        }
    }

    void onAnimationFinished() {
        synchronized (mGlobalLock) {
            mGlobalLock.notifyAll();
        }
    }

    void clearPointerDownOutsideFocusRunnable() {
        if (mPointerDownOutsideFocusRunnable == null) return;

        mH.removeCallbacks(mPointerDownOutsideFocusRunnable);
        mPointerDownOutsideFocusRunnable = null;
    }

    private void onPointerDownOutsideFocusLocked(InputTarget t, boolean fromHandler) {
        if (t == null || !t.receiveFocusFromTapOutside()) {
            // If the window that received the input event cannot receive keys, don't move the
            // display it's on to the top since that window won't be able to get focus anyway.
            return;
        }

        final ActivityRecord touchedApp = t.getActivityRecord();
        if (touchedApp != null && touchedApp.getTask() != null) {
            final ActivityRecord top = touchedApp.getTask().topRunningActivity();
            if (top != null && top != touchedApp && top.getTaskFragment().getBounds().contains(
                    touchedApp.getTaskFragment().getBounds())) {
                // This is a special case where the pointer-down-outside focus on an Activity that's
                // entirely occluded by the task top running activity, this is possible if the
                // pointer-down-outside-focus event is delayed (after new activity started on top).
                // In that case, drop the event to prevent changing focus to a background activity.
                Slog.w(TAG, "onPointerDownOutsideFocusLocked, drop event because " + touchedApp
                        + " is occluded and should not be focused.");
                return;
            }
        }

        if (mDeviceStateCallback.isInRearDisplayOuterDefaultState()) {
            final Display[] rearDisplays = mDisplayManager
                    .getDisplays(DisplayManager.DISPLAY_CATEGORY_REAR);
            if (rearDisplays.length > 0 && rearDisplays[0].getDisplayId() == t.getDisplayId()) {
                // Do not change display focus to the inner display if we're in this mode. Note that
                // in this mode, the inner display is configured as a rear display.
                Slog.w(TAG, "Ignoring focus change because device is in RDM.");
                return;
            }
        }

        clearPointerDownOutsideFocusRunnable();

        final InputTarget focusedInputTarget = mFocusedInputTarget;
        if (shouldDelayTouchOutside(t)) {
            mPointerDownOutsideFocusRunnable =
                    () -> handlePointerDownOutsideFocus(t, focusedInputTarget);
            mH.postDelayed(mPointerDownOutsideFocusRunnable, POINTER_DOWN_OUTSIDE_FOCUS_TIMEOUT_MS);
        } else if (!fromHandler) {
            // Still post the runnable to handler thread in case there is already a runnable
            // in execution, but still waiting to hold the wm lock.
            mPointerDownOutsideFocusRunnable =
                    () -> handlePointerDownOutsideFocus(t, focusedInputTarget);
            mH.post(mPointerDownOutsideFocusRunnable);
        } else {
            handlePointerDownOutsideFocus(t, focusedInputTarget);
        }
    }

    private boolean shouldDelayTouchOutside(InputTarget t) {
        final ActivityRecord activity = t.getActivityRecord();
        final Task task = activity != null ? activity.getTask() : null;

        final boolean isInputTargetNotFocused =
                mFocusedInputTarget != t && mFocusedInputTarget != null;
        final boolean isTouchOnFocusedDisplay = mFocusedInputTarget != null
                && t.getDisplayId() == mFocusedInputTarget.getDisplayId();
        if (!(isInputTargetNotFocused && isTouchOnFocusedDisplay)) {
            return false;
        }

        // For embedded activity that is showing side-by-side with another activity, delay
        // handling the touch-outside event to prevent focus rapid changes back-n-forth.
        final boolean shouldDelayTouchForEmbeddedActivity = activity != null
                && activity.isEmbedded()
                && activity.getTaskFragment().hasAdjacentTaskFragment();

        // For cases when there are multiple freeform windows where non-top windows are blocking
        // the gesture zones, delay handling the touch-outside event to prevent refocusing the
        // the non-top windows during the gesture.
        final boolean shouldDelayTouchForFreeform =
                task != null && task.getWindowingMode() == WINDOWING_MODE_FREEFORM;

        // If non of the above cases are true, handle the touch-outside event directly.
        return shouldDelayTouchForEmbeddedActivity || shouldDelayTouchForFreeform;
    }

    private void handlePointerDownOutsideFocus(InputTarget t, InputTarget focusedInputTarget) {
        synchronized (mGlobalLock) {
            final WindowState w = t.getWindowState();
            // Skip if the mFocusedInputTarget is already changed or the touched Activity is no
            // longer visible. This is possible if the pointer-down-outside-focus event is
            // delayed to be handled.
            if (mFocusedInputTarget != focusedInputTarget || (w != null
                    && w.getActivityRecord() != null
                    && !w.getActivityRecord().isVisibleRequested())) {
                ProtoLog.i(WM_DEBUG_FOCUS_LIGHT,
                        "Skip onPointerDownOutsideFocusLocked due to input target changed %s", t);
                return;
            }
            if (mPointerDownOutsideFocusRunnable != null
                    && mH.hasCallbacks(mPointerDownOutsideFocusRunnable)) {
                // Skip if there's another pending pointer-down-outside-focus event.
                return;
            }
            clearPointerDownOutsideFocusRunnable();

            if (w != null) {
                final Task task = w.getTask();
                if (task != null && w.mTransitionController.isTransientHide(task)) {
                    // Don't disturb transient animation by accident touch.
                    return;
                }
            }

            ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "onPointerDownOutsideFocusLocked called on %s", t);
            if (mFocusedInputTarget != t && mFocusedInputTarget != null) {
                mFocusedInputTarget.handleTapOutsideFocusOutsideSelf();
            }
            // Trigger Activity#onUserLeaveHint() if the order change of task pauses any activities.
            mAtmService.mTaskSupervisor.mUserLeaving = true;
            t.handleTapOutsideFocusInsideSelf();
            mAtmService.mTaskSupervisor.mUserLeaving = false;
        }
    }

    @VisibleForTesting
    void handleTaskFocusChange(Task task, ActivityRecord touchedActivity) {
        if (task == null) {
            return;
        }

        // We ignore root home task since we don't want root home task to move to front when
        // touched. Specifically, in freeform we don't want tapping on home to cause the freeform
        // apps to go behind home. See b/117376413
        if (task.isActivityTypeHome()) {
            // Only ignore root home task if the requested focus home Task is in the same
            // TaskDisplayArea as the current focus Task.
            TaskDisplayArea homeTda = task.getDisplayArea();
            WindowState curFocusedWindow = getFocusedWindow();
            if (curFocusedWindow != null && homeTda != null
                    && curFocusedWindow.isDescendantOf(homeTda)) {
                return;
            }
        }

        mAtmService.setFocusedTask(task.mTaskId, touchedActivity);
    }

    @VisibleForTesting
    static class WindowContainerInfo {
        private final int mUid;
        @NonNull private final WindowContainerToken mToken;

        private WindowContainerInfo(int uid, @NonNull WindowContainerToken token) {
            this.mUid = uid;
            this.mToken = token;
        }

        public int getUid() {
            return mUid;
        }

        @NonNull
        public WindowContainerToken getToken() {
            return mToken;
        }
    }

    /**
     * Retrieve the {@link WindowContainerInfo} of the task that was launched for MediaProjection.
     *
     * @param session the {@link ContentRecordingSession} containing the launch cookie and/or
     *     task id of the Task started for capture.
     * @return a token representing the task containing the activity started with the given launch
     *     cookie, or {@code null} if the token couldn't be found.
     */
    @VisibleForTesting
    @Nullable
    WindowContainerInfo getTaskWindowContainerInfoForRecordingSession(
            @NonNull ContentRecordingSession session) {
        WindowContainerToken taskWindowContainerToken = null;
        ActivityRecord targetActivity = null;
        Task targetTask = null;

        // First attempt to find the launched task by looking for the launched activity with the
        // matching launch cookie.
        if (session.getTokenToRecord() != null) {
            IBinder launchCookie = session.getTokenToRecord();
            targetActivity = mRoot.getActivity(activity -> activity.mLaunchCookie == launchCookie);
            if (targetActivity == null) {
                Slog.w(TAG, "Unable to find the activity for this launch cookie");
            } else {
                if (targetActivity.getTask() == null) {
                    Slog.w(TAG, "Unable to find the task for this launch cookie");
                } else {
                    targetTask = targetActivity.getTask();
                    taskWindowContainerToken = targetTask.mRemoteToken.toWindowContainerToken();
                }
            }
        }

        // In the case we can't find an activity with a matching launch cookie, it could be due to
        // the launched activity being closed, but the launched task is still open, so now attempt
        // to look for the task directly.
        if (taskWindowContainerToken == null && session.getTaskId() != -1) {
            int targetTaskId = session.getTaskId();
            targetTask = mRoot.getTask(task -> task.isTaskId(targetTaskId));
            if (targetTask == null) {
                Slog.w(TAG, "Unable to find the task for this projection");
            } else {
                taskWindowContainerToken = targetTask.mRemoteToken.toWindowContainerToken();
            }
        }

        // If we were unable to find the launched task in either fashion, then something must have
        // wrong (i.e. the task was closed before capture started).
        if (taskWindowContainerToken == null) {
            Slog.w(TAG, "Unable to find the WindowContainerToken for ContentRecordingSession");
            return null;
        }
        return new WindowContainerInfo(targetTask.effectiveUid, taskWindowContainerToken);
    }

    /**
     * Update the interaction history based on focus changes if needed. It assumes that the new
     * focused window is recently interacted by the user. It returns {@code true} if the history is
     * updated, otherwise returns {@code false}.
     */
    private boolean updateWindowInteractionHistoryByFocus(WindowState focusedWindow) {
        if (focusedWindow == null) {
            return false;
        }
        WindowState lastInteractedWindow = mInteractionTracker.peek();
        if (lastInteractedWindow == null || lastInteractedWindow != focusedWindow) {
            mInteractionTracker.add(focusedWindow);
            RenderingPrioritizationPolicy.updatePriorityByInteraction(
                    mInteractionTracker.getRecentlyInteractedWindows());
            return true;
        }
        return false;
    }

    /**
     * You need ALLOW_SLIPPERY_TOUCHES permission to be able to set FLAG_SLIPPERY.
     */
    private int sanitizeFlagSlippery(int flags, String windowName, int callingUid, int callingPid) {
        if ((flags & FLAG_SLIPPERY) == 0) {
            return flags;
        }
        final int permissionResult = mContext.checkPermission(
                    android.Manifest.permission.ALLOW_SLIPPERY_TOUCHES, callingPid, callingUid);
        if (permissionResult != PackageManager.PERMISSION_GRANTED) {
            Slog.w(TAG, "Removing FLAG_SLIPPERY from '" + windowName
                    + "' because it doesn't have ALLOW_SLIPPERY_TOUCHES permission");
            return flags & ~FLAG_SLIPPERY;
        }
        return flags;
    }

    /**
     * Ensure the caller has the right permissions to be able to set the requested input features.
     */
    private int sanitizeInputFeatures(int inputFeatures, String windowName, int callingUid,
            int callingPid, boolean isTrustedOverlay) {
        // You need MONITOR_INPUT permission to be able to set INPUT_FEATURE_SPY.
        if ((inputFeatures & INPUT_FEATURE_SPY) != 0) {
            final int permissionResult = mContext.checkPermission(
                    permission.MONITOR_INPUT, callingPid, callingUid);
            if (permissionResult != PackageManager.PERMISSION_GRANTED) {
                throw new SecurityException(
                        "Cannot use INPUT_FEATURE_SPY from '" + windowName
                                + "' because it doesn't the have MONITOR_INPUT permission");
            }
        }

        // You can only use INPUT_FEATURE_SENSITIVE_FOR_PRIVACY on a trusted overlay.
        if ((inputFeatures & INPUT_FEATURE_SENSITIVE_FOR_PRIVACY) != 0 && !isTrustedOverlay) {
            throw new SecurityException("Cannot use INPUT_FEATURE_SENSITIVE_FOR_PRIVACY from '"
                    + windowName + "' because it isn't a trusted overlay");
        }

        if ((inputFeatures & INPUT_FEATURE_DISPLAY_TOPOLOGY_AWARE) != 0) {
            final int permissionResult = mContext.checkPermission(
                    permission.MANAGE_DISPLAYS, callingPid, callingUid);
            if (permissionResult != PackageManager.PERMISSION_GRANTED) {
                throw new SecurityException(
                        "Cannot use INPUT_FEATURE_DISPLAY_TOPOLOGY_AWARE from '" + windowName
                                + "' because it doesn't the have MANAGE_DISPLAYS permission");
            }
        }
        return inputFeatures;
    }

    private boolean hasFlags(int flags, int mask) {
        return (flags & mask) != 0;
    }

    private boolean hasPermission(String permission, int callingPid, int callingUid) {
        return mContext.checkPermission(permission, callingPid, callingUid)
                == PackageManager.PERMISSION_GRANTED;
    }

    /**
     * Ensure the caller has the right permissions to be able to set the requested private flags.
     */
    private int sanitizePrivateFlags(int newPrivateFlags, int oldPrivateFlags, String windowName,
            int callingUid, int callingPid) {
        final int addedPrivateFlags = ~oldPrivateFlags & newPrivateFlags;
        int sanitizedFlags = newPrivateFlags;
        if (hasFlags(addedPrivateFlags,
                (PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY| PRIVATE_FLAG_TRUSTED_OVERLAY))
                && !hasPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW,
                    callingUid, callingPid)) {
            Slog.w(TAG, "Removing PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY or"
                    + " PRIVATE_FLAG_TRUSTED_OVERLAY from '" + windowName
                    + "' because it doesn't have INTERNAL_SYSTEM_WINDOW permission");
            sanitizedFlags &=
                    ~(PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY | PRIVATE_FLAG_TRUSTED_OVERLAY);
        }
        if (hasFlags(addedPrivateFlags, LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP)
                && !hasPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS, callingUid,
                    callingPid)) {
            Slog.w(TAG, "Removing PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP from '"
                + windowName + "' because it doesn't have MANAGE_ACTIVITY_TASKS permission");
            sanitizedFlags &= ~LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
        }
        return sanitizedFlags;
    }

    /**
     * Assigns an InputChannel to a SurfaceControl and configures it to receive
     * touch input according to it's on-screen geometry.
     *
     * Used by WindowlessWindowManager to enable input on SurfaceControl embedded
     * views.
     */
    InputChannel grantInputChannel(Session session, int callingUid, int callingPid, int displayId,
            SurfaceControl surface, IBinder clientToken,
            @Nullable InputTransferToken hostInputTransferToken, int flags, int privateFlags,
            int inputFeatures, int type, IBinder windowToken, InputTransferToken inputTransferToken,
            String inputHandleName) {
        final int sanitizedType = sanitizeWindowType(session, displayId, windowToken, type);
        final InputApplicationHandle applicationHandle;
        final String name;
        InputChannel inputChannel = new InputChannel();
        Objects.requireNonNull(inputTransferToken);

        synchronized (mGlobalLock) {
            WindowState hostWindowState = hostInputTransferToken != null
                    ? mInputToWindowMap.get(hostInputTransferToken.getToken()) : null;
            EmbeddedWindowController.EmbeddedWindow win =
                    new EmbeddedWindowController.EmbeddedWindow(session, this, clientToken,
                            hostWindowState, callingUid, callingPid, sanitizedType, displayId,
                            inputTransferToken, inputHandleName, (flags & FLAG_NOT_FOCUSABLE) == 0);
            win.openInputChannel(inputChannel);
            mEmbeddedWindowController.add(inputChannel.getToken(), win);
            applicationHandle = win.getApplicationHandle();
            name = win.toString();
        }

        updateInputChannel(inputChannel.getToken(), callingUid, callingPid, displayId, surface,
                name, applicationHandle, flags, privateFlags, inputFeatures, sanitizedType,
                null /* region */, clientToken);
        return inputChannel;
    }

    @Override
    public boolean transferTouchGesture(@NonNull InputTransferToken transferFromToken,
            @NonNull InputTransferToken transferToToken) {
        Objects.requireNonNull(transferFromToken);
        Objects.requireNonNull(transferToToken);

        final int callingUid = Binder.getCallingUid();
        final long identity = Binder.clearCallingIdentity();
        boolean didTransfer;
        try {
            synchronized (mGlobalLock) {
                // If the transferToToken exists in the input to window map, it means the request
                // is to transfer from embedded to host. Otherwise, the transferToToken
                // represents an embedded window so transfer from host to embedded.
                WindowState windowStateTo = mInputToWindowMap.get(transferToToken.getToken());
                if (windowStateTo != null) {
                    didTransfer = mEmbeddedWindowController.transferToHost(callingUid,
                            transferFromToken,
                            windowStateTo);
                } else {
                    WindowState windowStateFrom = mInputToWindowMap.get(
                            transferFromToken.getToken());
                    didTransfer = mEmbeddedWindowController.transferToEmbedded(callingUid,
                            windowStateFrom,
                            transferToToken);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
        return didTransfer;
    }

    private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
            int displayId, SurfaceControl surface, String name,
            InputApplicationHandle applicationHandle, int flags,
            int privateFlags, int inputFeatures, int type, Region region, IBinder clientToken) {
        final InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId);
        h.token = channelToken;
        h.setWindowToken(clientToken);
        h.name = name;

        final boolean isTrustedOverlay = (privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0;
        flags = sanitizeFlagSlippery(flags, name, callingUid, callingPid);
        inputFeatures = sanitizeInputFeatures(inputFeatures, name, callingUid, callingPid,
                isTrustedOverlay);

        final int sanitizedLpFlags =
                (flags & (FLAG_NOT_TOUCHABLE | FLAG_SLIPPERY | LayoutParams.FLAG_NOT_FOCUSABLE
                    | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH))
                | LayoutParams.FLAG_NOT_TOUCH_MODAL;
        h.layoutParamsType = type;
        h.layoutParamsFlags = sanitizedLpFlags;

        // Do not allow any input features to be set without sanitizing them first.
        h.inputConfig = InputConfigAdapter.getInputConfigFromWindowParams(
                        type, sanitizedLpFlags, inputFeatures);


        if ((flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
            h.inputConfig |= InputConfig.NOT_FOCUSABLE;
        }

        h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
        h.ownerUid = callingUid;
        h.ownerPid = callingPid;

        if (region == null) {
            h.replaceTouchableRegionWithCrop(null);
        } else {
            h.touchableRegion.set(region);
            h.replaceTouchableRegionWithCrop = false;

            // Task managers may need to receive input events around task layers to resize tasks.
            final int permissionResult = mContext.checkPermission(
                    permission.MANAGE_ACTIVITY_TASKS, callingPid, callingUid);
            if (permissionResult != PackageManager.PERMISSION_GRANTED) {
                h.setTouchableRegionCrop(surface);
            }
        }

        final SurfaceControl.Transaction t = mTransactionFactory.get();
        //  Check private trusted overlay flag to set trustedOverlay field of input window handle.
        h.setTrustedOverlay(t, surface, isTrustedOverlay);
        t.setInputWindowInfo(surface, h);
        t.apply();
        t.close();
        surface.release();
    }

    /**
     * Updates the flags on an existing surface's input channel. This assumes the surface provided
     * is the one associated with the provided input-channel. If this isn't the case, behavior is
     * undefined.
     */
    void updateInputChannel(IBinder channelToken,
            @Nullable InputTransferToken hostInputTransferToken, int displayId,
            SurfaceControl surface,
            int flags, int privateFlags, int inputFeatures, Region region) {
        final InputApplicationHandle applicationHandle;
        final String name;
        final EmbeddedWindowController.EmbeddedWindow win;
        synchronized (mGlobalLock) {
            win = mEmbeddedWindowController.get(channelToken);
            if (win == null) {
                Slog.e(TAG, "Couldn't find window for provided channelToken.");
                return;
            }
            name = win.toString();
            applicationHandle = win.getApplicationHandle();
            win.setIsFocusable((flags & FLAG_NOT_FOCUSABLE) == 0);
            WindowState hostWindowState = hostInputTransferToken != null
                    ? mInputToWindowMap.get(hostInputTransferToken.getToken()) : null;
            win.updateHost(hostWindowState);
        }

        updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name,
                applicationHandle, flags, privateFlags, inputFeatures, win.mWindowType, region,
                win.mClient);
    }

    /**
     * Returns the Activity that has the most recently created window in the adjacent activities
     * if any.
     */
    @NonNull
    ActivityRecord getMostRecentActivityInAdjacent(@NonNull ActivityRecord focusedActivity) {
        final TaskFragment taskFragment = focusedActivity.getTaskFragment();
        if (taskFragment == null) {
            // Return if activity no attached.
            return focusedActivity;
        }

        if (!focusedActivity.isEmbedded()) {
            // Return if the focused activity is not embedded.
            return focusedActivity;
        }

        if (!taskFragment.hasAdjacentTaskFragment()) {
            return focusedActivity;
        }

        // Find the adjacent activity with more recently active window.
        final ActivityRecord[] mostRecentActiveActivity = { focusedActivity };
        final long[] mostRecentActiveTime = { focusedActivity.getLastWindowCreateTime() };
        taskFragment.forOtherAdjacentTaskFragments(adjacentTaskFragment -> {
            final ActivityRecord adjacentTopActivity = adjacentTaskFragment.topRunningActivity();
            if (adjacentTopActivity != null) {
                final long lastWindowCreateTime = adjacentTopActivity.getLastWindowCreateTime();
                if (lastWindowCreateTime > mostRecentActiveTime[0]) {
                    mostRecentActiveTime[0] = lastWindowCreateTime;
                    mostRecentActiveActivity[0] = adjacentTopActivity;
                }
            }
        });
        return mostRecentActiveActivity[0];
    }

    @NonNull
    WindowState getMostRecentUsedEmbeddedWindowForBack(@NonNull WindowState focusedWindow) {
        final ActivityRecord focusedActivity = focusedWindow.getActivityRecord();
        if (focusedActivity == null) {
            // Not an Activity.
            return focusedWindow;
        }

        final ActivityRecord mostRecentActivityInAdjacent = getMostRecentActivityInAdjacent(
                focusedActivity);
        if (mostRecentActivityInAdjacent == focusedActivity) {
            // Already be the most recent window.
            return focusedWindow;
        }

        // Looks for a candidate focused window on the adjacent Activity for the back event.
        final WindowState candidate =
                mostRecentActivityInAdjacent.getDisplayContent().findFocusedWindow(
                        mostRecentActivityInAdjacent);
        return candidate != null ? candidate : focusedWindow;
    }

    /**
     * Move focus to the adjacent embedded activity if the adjacent activity is more recently
     * created or has a window more recently added.
     * <p>
     * Returns {@code true} if the focused window is changed. Otherwise, returns {@code false}.
     */
    boolean moveFocusToAdjacentEmbeddedWindow(@NonNull WindowState focusedWindow) {
        final ActivityRecord activity = focusedWindow.getActivityRecord();
        if (activity == null || !activity.isVisible()) {
            return false;
        }

        final ActivityRecord mostRecentActivityInAdjacent = getMostRecentActivityInAdjacent(
                activity);

        moveFocusToActivity(mostRecentActivityInAdjacent);
        return !focusedWindow.isFocused();
    }

    boolean moveFocusToAdjacentWindow(@NonNull WindowState fromWin, @FocusDirection int direction) {
        if (!fromWin.isFocused()) {
            return false;
        }
        final TaskFragment fromFragment = fromWin.getTaskFragment();
        if (fromFragment == null || fromFragment.asTask() != null) {
            // Don't move the focus to another task.
            return false;
        }
        if (!fromFragment.hasAdjacentTaskFragment()) {
            // No adjacent window.
            return false;
        }
        if (fromFragment.getAdjacentTaskFragments().size() > 2) {
            throw new IllegalStateException("Not yet support 3+ adjacent for non-Task TFs");
        }
        final TaskFragment[] tmpAdjacent = new TaskFragment[1];
        fromFragment.forOtherAdjacentTaskFragments(adjacentTF -> {
            tmpAdjacent[0] = adjacentTF;
            return true;
        });
        final TaskFragment adjacentFragment = tmpAdjacent[0];
        if (adjacentFragment.isIsolatedNav()) {
            // Don't move the focus if the adjacent TF is isolated navigation.
            return false;
        }
        final Rect fromBounds = fromFragment.getBounds();
        final Rect adjacentBounds = adjacentFragment.getBounds();
        switch (direction) {
            case View.FOCUS_LEFT:
                if (adjacentBounds.left >= fromBounds.left) {
                    return false;
                }
                break;
            case View.FOCUS_UP:
                if (adjacentBounds.top >= fromBounds.top) {
                    return false;
                }
                break;
            case View.FOCUS_RIGHT:
                if (adjacentBounds.right <= fromBounds.right) {
                    return false;
                }
                break;
            case View.FOCUS_DOWN:
                if (adjacentBounds.bottom <= fromBounds.bottom) {
                    return false;
                }
                break;
            case View.FOCUS_BACKWARD:
            case View.FOCUS_FORWARD:
                // These are not absolute directions. Skip checking the bounds.
                break;
            default:
                return false;
        }
        final ActivityRecord topRunningActivity = adjacentFragment.topRunningActivity(
                true /* focusableOnly */);
        if (topRunningActivity == null) {
            return false;
        }
        moveFocusToActivity(topRunningActivity);
        return !fromWin.isFocused();
    }

    @VisibleForTesting
    void moveFocusToActivity(@NonNull ActivityRecord activity) {
        moveDisplayToTopInternal(activity.getDisplayId());
        handleTaskFocusChange(activity.getTask(), activity);
    }

    /** Return whether layer tracing is enabled */
    public boolean isLayerTracing() {
        if (!checkCallingPermission(
                android.Manifest.permission.DUMP, "isLayerTracing()")) {
            throw new SecurityException("Requires DUMP permission");
        }

        final long token = Binder.clearCallingIdentity();
        try {
            Parcel data = null;
            Parcel reply = null;
            try {
                IBinder sf = ServiceManager.getService("SurfaceFlinger");
                if (sf != null) {
                    reply = Parcel.obtain();
                    data = Parcel.obtain();
                    data.writeInterfaceToken("android.ui.ISurfaceComposer");
                    sf.transact(/* LAYER_TRACE_STATUS_CODE */ 1026, data, reply, 0 /* flags */);
                    return reply.readBoolean();
                }
            } catch (RemoteException e) {
                Slog.e(TAG, "Failed to get layer tracing");
            } finally {
                if (data != null) {
                    data.recycle();
                }
                if (reply != null) {
                    reply.recycle();
                }
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
        return false;
    }

    /** Enable or disable layer tracing */
    public void setLayerTracing(boolean enabled) {
        if (!checkCallingPermission(
                android.Manifest.permission.DUMP, "setLayerTracing()")) {
            throw new SecurityException("Requires DUMP permission");
        }

        final long token = Binder.clearCallingIdentity();
        try {
            Parcel data = null;
            try {
                IBinder sf = ServiceManager.getService("SurfaceFlinger");
                if (sf != null) {
                    data = Parcel.obtain();
                    data.writeInterfaceToken("android.ui.ISurfaceComposer");
                    data.writeInt(enabled ? 1 : 0);
                    sf.transact(/* LAYER_TRACE_CONTROL_CODE */ 1025, data, null, 0 /* flags */);
                }
            } catch (RemoteException e) {
                Slog.e(TAG, "Failed to set layer tracing");
            } finally {
                if (data != null) {
                    data.recycle();
                }
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    /** Set layer tracing flags. */
    public void setLayerTracingFlags(int flags) {
        if (!checkCallingPermission(
                android.Manifest.permission.DUMP, "setLayerTracingFlags")) {
            throw new SecurityException("Requires DUMP permission");
        }

        final long token = Binder.clearCallingIdentity();
        try {
            Parcel data = null;
            try {
                IBinder sf = ServiceManager.getService("SurfaceFlinger");
                if (sf != null) {
                    data = Parcel.obtain();
                    data.writeInterfaceToken("android.ui.ISurfaceComposer");
                    data.writeInt(flags);
                    sf.transact(1033 /* LAYER_TRACE_FLAGS_CODE */, data, null, 0 /* flags */);
                }
            } catch (RemoteException e) {
                Slog.e(TAG, "Failed to set layer tracing flags");
            } finally {
                if (data != null) {
                    data.recycle();
                }
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    /**
     * Toggle active transaction tracing.
     * Setting to true increases the buffer size for active debugging.
     * Setting to false resets the buffer size and dumps the trace to file.
     */
    public void setActiveTransactionTracing(boolean active) {
        if (!checkCallingPermission(
                android.Manifest.permission.DUMP, "setActiveTransactionTracing()")) {
            throw new SecurityException("Requires DUMP permission");
        }

        final long token = Binder.clearCallingIdentity();
        try {
            Parcel data = null;
            try {
                IBinder sf = ServiceManager.getService("SurfaceFlinger");
                if (sf != null) {
                    data = Parcel.obtain();
                    data.writeInterfaceToken("android.ui.ISurfaceComposer");
                    data.writeInt(active ? 1 : 0);
                    sf.transact(/* TRANSACTION_TRACE_CONTROL_CODE */ 1041, data,
                            null, 0 /* flags */);
                }
            } catch (RemoteException e) {
                Slog.e(TAG, "Failed to set transaction tracing");
            } finally {
                if (data != null) {
                    data.recycle();
                }
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    @Override
    public boolean mirrorDisplay(int displayId, SurfaceControl outSurfaceControl) {
        if (!checkCallingPermission(READ_FRAME_BUFFER, "mirrorDisplay()")) {
            throw new SecurityException("Requires READ_FRAME_BUFFER permission");
        }

        final SurfaceControl displaySc;
        synchronized (mGlobalLock) {
            DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                Slog.e(TAG, "Invalid displayId " + displayId + " for mirrorDisplay");
                return false;
            }

            displaySc = displayContent.getWindowingLayer();
        }

        final SurfaceControl mirror = SurfaceControl.mirrorSurface(displaySc);
        outSurfaceControl.copyFrom(mirror, "WMS.mirrorDisplay");
        mirror.release();
        return true;
    }

    @Override
    public void getWindowInsets(int displayId, IBinder token, InsetsState outInsetsState) {
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final DisplayContent dc = getDisplayContentOrCreate(displayId, token);
                if (dc == null) {
                    throw new WindowManager.InvalidDisplayException("Display#" + displayId
                            + "could not be found!");
                }
                final WindowToken winToken = dc.getWindowToken(token);
                dc.getInsetsPolicy().getInsetsForWindowMetrics(winToken, outInsetsState);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    @Override
    public List<DisplayInfo> getPossibleDisplayInfo(int displayId) {
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                if (!mAtmService.isCallerRecents(callingUid)
                        && (!multiCrop() || callingUid != SYSTEM_UID)) {
                    Slog.e(TAG, "Unable to verify uid for getPossibleDisplayInfo"
                            + " on uid " + callingUid);
                    return new ArrayList<>();
                }

                // Retrieve the DisplayInfo across all possible display layouts.
                return mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId);
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    List<DisplayInfo> getPossibleDisplayInfoLocked(int displayId) {
        // Retrieve the DisplayInfo for all possible rotations across all possible display
        // layouts.
        return mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId);
    }

    void grantEmbeddedWindowFocus(Session session, InputTransferToken inputTransferToken,
            boolean grantFocus) {
        synchronized (mGlobalLock) {
            final EmbeddedWindowController.EmbeddedWindow embeddedWindow =
                    mEmbeddedWindowController.getByInputTransferToken(inputTransferToken);
            if (embeddedWindow == null) {
                Slog.e(TAG, "Embedded window not found");
                return;
            }
            if (embeddedWindow.mSession != session) {
                Slog.e(TAG, "Window not in session:" + session);
                return;
            }
            IBinder inputToken = embeddedWindow.getInputChannelToken();
            if (inputToken == null) {
                Slog.e(TAG, "Focus token found but input channel token not found");
                return;
            }
            SurfaceControl.Transaction t = mTransactionFactory.get();
            final int displayId = embeddedWindow.mDisplayId;
            if (grantFocus) {
                t.setFocusedWindow(inputToken, embeddedWindow.toString(), displayId).apply();
                EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
                        "Focus request " + embeddedWindow, "reason=grantEmbeddedWindowFocus(true)");
            } else {
                // Search for a new focus target
                DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                WindowState newFocusTarget =  displayContent == null
                        ? null : displayContent.findFocusedWindow();
                if (newFocusTarget == null) {
                    t.setFocusedWindow(null, null, displayId).apply();
                    ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s"
                                    + " dropped focus so setting focus to null since no candidate"
                                    + " was found",
                            embeddedWindow);
                    return;
                }
                t.setFocusedWindow(newFocusTarget.mInputChannelToken, newFocusTarget.getName(),
                        displayId).apply();

                EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
                        "Focus request " + newFocusTarget,
                        "reason=grantEmbeddedWindowFocus(false)");
            }
            ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s",
                    embeddedWindow, grantFocus);
        }
    }

    void grantEmbeddedWindowFocus(Session session, IWindow callingWindow,
            InputTransferToken inputTransferToken, boolean grantFocus) {
        synchronized (mGlobalLock) {
            final WindowState hostWindow = windowForClient(session, callingWindow);
            if (hostWindow == null) {
                Slog.e(TAG, "Host window not found");
                return;
            }
            if (hostWindow.mInputChannelToken == null) {
                Slog.e(TAG, "Host window does not have an input channel");
                return;
            }
            final EmbeddedWindowController.EmbeddedWindow embeddedWindow =
                    mEmbeddedWindowController.getByInputTransferToken(inputTransferToken);
            if (embeddedWindow == null) {
                Slog.e(TAG, "Embedded window not found");
                return;
            }
            if (embeddedWindow.mHostWindowState != hostWindow) {
                Slog.e(TAG, "Embedded window does not belong to the host");
                return;
            }
            if (grantFocus) {
                hostWindow.mInputWindowHandle.setFocusTransferTarget(
                        embeddedWindow.getInputChannelToken());
                EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
                        "Transfer focus request " + embeddedWindow,
                        "reason=grantEmbeddedWindowFocus(true)");
            } else {
                hostWindow.mInputWindowHandle.setFocusTransferTarget(null);
                EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
                        "Transfer focus request " + hostWindow,
                        "reason=grantEmbeddedWindowFocus(false)");
            }
            DisplayContent dc = mRoot.getDisplayContent(hostWindow.getDisplayId());
            if (dc != null) {
                dc.getInputMonitor().updateInputWindowsLw(true);
            }

            ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s",
                    embeddedWindow, grantFocus);
        }
    }

    @Override
    public void holdLock(IBinder token, int durationMs) {
        mTestUtilityService.verifyHoldLockToken(token);

        synchronized (mGlobalLock) {
            SystemClock.sleep(durationMs);
        }
    }

    @Override
    public String[] getSupportedDisplayHashAlgorithms() {
        return mDisplayHashController.getSupportedHashAlgorithms();
    }

    @Override
    public VerifiedDisplayHash verifyDisplayHash(DisplayHash displayHash) {
        return mDisplayHashController.verifyDisplayHash(displayHash);
    }

    @Override
    public void setDisplayHashThrottlingEnabled(boolean enable) {
        if (!checkCallingPermission(READ_FRAME_BUFFER, "setDisplayHashThrottle()")) {
            throw new SecurityException("Requires READ_FRAME_BUFFER permission");
        }
        mDisplayHashController.setDisplayHashThrottlingEnabled(enable);
    }

    @Override
    public boolean isTaskSnapshotSupported() {
        synchronized (mGlobalLock) {
            return !mTaskSnapshotController.shouldDisableSnapshots();
        }
    }

    void generateDisplayHash(Session session, IWindow window, Rect boundsInWindow,
            String hashAlgorithm, RemoteCallback callback) {
        final SurfaceControl displaySurfaceControl;
        final Rect boundsInDisplay = new Rect(boundsInWindow);
        synchronized (mGlobalLock) {
            final WindowState win = windowForClient(session, window);
            if (win == null) {
                Slog.w(TAG, "Failed to generate DisplayHash. Invalid window");
                mDisplayHashController.sendDisplayHashError(callback,
                        DISPLAY_HASH_ERROR_MISSING_WINDOW);
                return;
            }

            if (win.mActivityRecord == null || !win.mActivityRecord.isState(
                    ActivityRecord.State.RESUMED)) {
                mDisplayHashController.sendDisplayHashError(callback,
                        DISPLAY_HASH_ERROR_MISSING_WINDOW);
                return;
            }

            DisplayContent displayContent = win.getDisplayContent();
            if (displayContent == null) {
                Slog.w(TAG, "Failed to generate DisplayHash. Window is not on a display");
                mDisplayHashController.sendDisplayHashError(callback,
                        DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
                return;
            }

            displaySurfaceControl = displayContent.getSurfaceControl();
            mDisplayHashController.calculateDisplayHashBoundsLocked(win, boundsInWindow,
                    boundsInDisplay);

            if (boundsInDisplay.isEmpty()) {
                Slog.w(TAG, "Failed to generate DisplayHash. Bounds are not on screen");
                mDisplayHashController.sendDisplayHashError(callback,
                        DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
                return;
            }
        }

        // A screenshot of the entire display is taken rather than just the window. This is
        // because if we take a screenshot of the window, it will not include content that might
        // be covering it with the same uid. We want to make sure we include content that's
        // covering to ensure we get as close as possible to what the user sees
        final int uid = session.mUid;
        ScreenCaptureInternal.LayerCaptureArgs.Builder args =
                new ScreenCaptureInternal.LayerCaptureArgs.Builder(displaySurfaceControl)
                        .setUid(uid)
                        .setSourceCrop(boundsInDisplay);

        mDisplayHashController.generateDisplayHash(args, boundsInWindow, hashAlgorithm, uid,
                callback);
    }

    boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
        final Task imeTargetWindowTask;
        synchronized (mGlobalLock) {
            final WindowState imeTargetWindow = mWindowMap.get(imeTargetWindowToken);
            if (imeTargetWindow == null) {
                return false;
            }
            imeTargetWindowTask = imeTargetWindow.getTask();
            if (imeTargetWindowTask == null) {
                return false;
            }
            if (android.view.inputmethod.Flags.disableImeRestoreOnActivityCreate()) {
                if (imeTargetWindow.isRequestedVisible(WindowInsets.Type.ime())) {
                    return true;
                }
            } else {
                if (imeTargetWindow.mActivityRecord != null
                        && imeTargetWindow.mActivityRecord.mLastImeShown) {
                    return true;
                }
            }
            final TaskSnapshot snapshot;
            if (Flags.reduceTaskSnapshotMemoryUsage()) {
                snapshot = mTaskSnapshotController.getSnapshot(imeTargetWindowTask.mTaskId,
                        TaskSnapshotManager.RESOLUTION_ANY);
            } else {
                snapshot = mTaskSnapshotController.getSnapshot(imeTargetWindowTask.mTaskId,
                        false /* isLowResolution */);
            }
            return snapshot != null && snapshot.hasImeSurface();
        }
    }

    @Override
    public int getImeDisplayId() {
        // TODO(b/189805422): Add a toast to notify users that IMS may get extra
        //  onConfigurationChanged callback when perDisplayFocus is enabled.
        //  Enabling perDisplayFocus means that we track focus on each display, so we don't have
        //  the "top focus" display and getTopFocusedDisplayContent returns the default display
        //  as the fallback. It leads to InputMethodService receives an extra onConfiguration
        //  callback when InputMethodService move from a secondary display to another display
        //  with the same display metrics because InputMethodService will always associate with
        //  the ImeContainer on the default display in onCreate and receive a configuration update
        //  to match default display ImeContainer and then receive another configuration update
        //  from attachToWindowToken.
        synchronized (mGlobalLock) {
            final DisplayContent dc = mRoot.getTopFocusedDisplayContent();
            return dc.getImePolicy() == DISPLAY_IME_POLICY_LOCAL ? dc.getDisplayId()
                    : DEFAULT_DISPLAY;
        }
    }

    @Override
    public void setTaskSnapshotEnabled(boolean enabled) {
        mTaskSnapshotController.setSnapshotEnabled(enabled);
    }

    @Override
    @RequiresPermission(Manifest.permission.ACCESS_FPS_COUNTER)
    public void registerTaskFpsCallback(@IntRange(from = 0) int taskId,
            ITaskFpsCallback callback) {
        if (mContext.checkCallingOrSelfPermission(Manifest.permission.ACCESS_FPS_COUNTER)
                != PackageManager.PERMISSION_GRANTED) {
            final int pid = Binder.getCallingPid();
            throw new SecurityException("Access denied to process: " + pid
                    + ", must have permission " + Manifest.permission.ACCESS_FPS_COUNTER);
        }
        synchronized (mGlobalLock) {
            if (mRoot.anyTaskForId(taskId) == null) {
                throw new IllegalArgumentException("no task with taskId: " + taskId);
            }
        }

        mTaskFpsCallbackController.registerListener(taskId, callback);
    }

    @Override
    @RequiresPermission(Manifest.permission.ACCESS_FPS_COUNTER)
    public void unregisterTaskFpsCallback(ITaskFpsCallback callback) {
        if (mContext.checkCallingOrSelfPermission(Manifest.permission.ACCESS_FPS_COUNTER)
                != PackageManager.PERMISSION_GRANTED) {
            final int pid = Binder.getCallingPid();
            throw new SecurityException("Access denied to process: " + pid
                    + ", must have permission " + Manifest.permission.ACCESS_FPS_COUNTER);
        }

        mTaskFpsCallbackController.unregisterListener(callback);
    }

    @Override
    public Bitmap snapshotTaskForRecents(int taskId) {
        if (!checkCallingPermission(READ_FRAME_BUFFER, "snapshotTaskForRecents()")) {
            throw new SecurityException("Requires READ_FRAME_BUFFER permission");
        }

        TaskSnapshot taskSnapshot;
        final long token = Binder.clearCallingIdentity();
        try {
            final Supplier<TaskSnapshot> supplier;
            synchronized (mGlobalLock) {
                Task task = mRoot.anyTaskForId(taskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
                if (task == null) {
                    throw new IllegalArgumentException(
                            "Failed to find matching task for taskId=" + taskId);
                }
                supplier = mTaskSnapshotController.captureSnapshot(task, true /* allowAppTheme */);
            }
            taskSnapshot = supplier != null ? supplier.get() : null;
        } finally {
            Binder.restoreCallingIdentity(token);
        }

        if (taskSnapshot == null) {
            return null;
        }
        if (Flags.reduceTaskSnapshotMemoryUsage()) {
            return taskSnapshot.wrapToBitmap();
        } else {
            if (taskSnapshot.getHardwareBuffer() == null) {
                return null;
            }
            return Bitmap.wrapHardwareBuffer(taskSnapshot.getHardwareBuffer(),
                    taskSnapshot.getColorSpace());
        }
    }

    @Override
    public void setRecentsAppBehindSystemBars(boolean behindSystemBars) {
        if (!checkCallingPermission(START_TASKS_FROM_RECENTS, "setRecentsAppBehindSystemBars()")) {
            throw new SecurityException("Requires START_TASKS_FROM_RECENTS permission");
        }
        final long token = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final Task recentsApp = mRoot.getTask(task -> task.isActivityTypeHomeOrRecents()
                        && task.getTopVisibleActivity() != null);
                if (recentsApp != null) {
                    recentsApp.getTask().setCanAffectSystemUiFlags(behindSystemBars);
                    mWindowPlacerLocked.requestTraversal();
                }
                InputMethodManagerInternal.get().maybeFinishStylusHandwriting();
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    /**
     * Gets the background color of the letterbox. Considered invalid if the background has
     * multiple colors {@link #isLetterboxBackgroundMultiColored}
     */
    @Override
    public int getLetterboxBackgroundColorInArgb() {
        return mAppCompatConfiguration.getLetterboxBackgroundColor().toArgb();
    }

    /**
     *  Whether the outer area of the letterbox has multiple colors (e.g. blurred background).
     */
    @Override
    public boolean isLetterboxBackgroundMultiColored() {
        @AppCompatConfiguration.LetterboxBackgroundType int letterboxBackgroundType =
                mAppCompatConfiguration.getLetterboxBackgroundType();
        switch (letterboxBackgroundType) {
            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
            case LETTERBOX_BACKGROUND_WALLPAPER:
                return true;
            case LETTERBOX_BACKGROUND_SOLID_COLOR:
                return false;
            default:
                throw new AssertionError(
                        "Unexpected letterbox background type: " + letterboxBackgroundType);
        }
    }

    @Override
    public void captureDisplay(
            int displayId,
            @Nullable ScreenCaptureInternal.CaptureArgs captureArgs,
            ScreenCaptureInternal.ScreenCaptureListener listener) {
        Slog.d(TAG, "captureDisplay");
        if (!checkCallingPermission(READ_FRAME_BUFFER, "captureDisplay()")) {
            throw new SecurityException("Requires READ_FRAME_BUFFER permission");
        }

        ScreenCaptureInternal.LayerCaptureArgs layerCaptureArgs =
                getCaptureArgs(displayId, captureArgs);
        ScreenCaptureInternal.captureLayers(layerCaptureArgs, listener);

        if (Binder.getCallingUid() != SYSTEM_UID) {
            // Release the SurfaceControl objects only if the caller is not in system server as no
            // parcelling occurs in this case.
            layerCaptureArgs.release();
        }
    }

    @Override
    public void screenCapture(
            @NonNull ScreenCapture.ScreenCaptureParams params,
            @NonNull IScreenCaptureCallback callback) {
        if (!checkCallingPermission(READ_FRAME_BUFFER, "screenCapture()")) {
            try {
                callback.onFailure(ScreenCapture.SCREEN_CAPTURE_ERROR_MISSING_PERMISSIONS);
            } catch (RemoteException remoteException) {
                Slog.e(
                        TAG,
                        "Failed to deliver screenshot permission error to client",
                        remoteException);
            }
            return;
        }

        // Translate ScreenCaptureParams to DisplayCaptureArgs.
        ScreenCaptureInternal.DisplayCaptureArgs.Builder argsBuilder =
                new ScreenCaptureInternal.DisplayCaptureArgs.Builder()
                        .setPixelFormat(params.getPixelFormat())
                        .setSecureContentPolicy(params.getSecureContentPolicy())
                        .setProtectedContentPolicy(params.getProtectedContentPolicy())
                        .setCaptureMode(params.getCaptureMode())
                        .setPreserveDisplayColors(params.isPreserveDisplayColors())
                        .setUseDisplayInstallationOrientation(
                                params.isUseDisplayInstallationOrientation())
                        .setIncludeSystemOverlays(params.isIncludeSystemOverlays());

        // Capture mode REQUIRE_OPTIMIZED only support a fixed set of params.
        if (params.getCaptureMode() == CAPTURE_MODE_REQUIRE_OPTIMIZED) {
            // Must error out if there is protected or secure content.
            // Must preserve the content appears on the display.
            argsBuilder = argsBuilder.setSecureContentPolicy(SECURE_CONTENT_POLICY_THROW_EXCEPTION)
                    .setProtectedContentPolicy(PROTECTED_CONTENT_POLICY_THROW_EXCEPTION)
                    .setPreserveDisplayColors(true)
                    .setUseDisplayInstallationOrientation(true)
                    .setIncludeSystemOverlays(true);
        }

        ScreenCaptureInternal.ScreenCaptureListener listener =
                new ScreenCaptureInternal.ScreenCaptureListener(
                        (screenshotHardwareBuffer, status) -> {
                            try {
                                if (screenshotHardwareBuffer == null) {
                                    Slog.w(TAG, "Screenshot failed. error: " + status);
                                    callback.onFailure(status);
                                } else {
                                    callback.onSuccess(
                                            new ScreenCapture.ScreenCaptureResult(
                                                    screenshotHardwareBuffer.getColorSpace(),
                                                    screenshotHardwareBuffer.getHardwareBuffer()));
                                }
                            } catch (RemoteException remoteException) {
                                Slog.e(TAG, "Failed to deliver screenshot result to client",
                                        remoteException);
                                // clean up hardware buffer
                                if (screenshotHardwareBuffer != null) {
                                    HardwareBuffer hwBuffer =
                                            screenshotHardwareBuffer.getHardwareBuffer();
                                    if (hwBuffer != null) {
                                        hwBuffer.close();
                                    }
                                }
                            }
                        });
        mDisplayManagerInternal.systemScreenshot(params.getDisplayId(), argsBuilder, listener);
    }

    @VisibleForTesting
    ScreenCaptureInternal.LayerCaptureArgs getCaptureArgs(
            int displayId, @Nullable ScreenCaptureInternal.CaptureArgs captureArgs) {
        final SurfaceControl displaySurfaceControl;
        synchronized (mGlobalLock) {
            DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                throw new IllegalArgumentException("Trying to screenshot and invalid display: "
                        + displayId);
            }

            displaySurfaceControl = displayContent.getSurfaceControl();

            if (captureArgs == null) {
                captureArgs = new ScreenCaptureInternal.CaptureArgs.Builder<>().build();
            }

            if (captureArgs.mSourceCrop.isEmpty()) {
                displayContent.getBounds(mTmpRect);
                mTmpRect.offsetTo(0, 0);
            } else {
                mTmpRect.set(captureArgs.mSourceCrop);
            }
        }

        return new ScreenCaptureInternal.LayerCaptureArgs.Builder(
                        displaySurfaceControl, captureArgs)
                .setSourceCrop(mTmpRect)
                .build();
    }

    @Override
    public boolean isGlobalKey(int keyCode) {
        return mPolicy.isGlobalKey(keyCode);
    }

    private int sanitizeWindowType(Session session, int displayId, IBinder windowToken, int type) {
        // Determine whether this window type is valid for this process.
        final boolean isTypeValid;
        if (type == TYPE_ACCESSIBILITY_OVERLAY && windowToken != null) {
            // Only accessibility services can add accessibility overlays.
            // Accessibility services will have a WindowToken with type
            // TYPE_ACCESSIBILITY_OVERLAY.
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            final WindowToken token = displayContent.getWindowToken(windowToken);
            if (token == null) {
                isTypeValid = false;
            } else if (type == token.getWindowType()) {
                isTypeValid = true;
            } else {
                isTypeValid = false;
            }
        } else if (!session.mCanAddInternalSystemWindow && type != 0) {
            Slog.w(
                    TAG_WM,
                    "Requires INTERNAL_SYSTEM_WINDOW permission if assign type to"
                            + " input. New type will be 0.");
            isTypeValid = false;
        } else {
            isTypeValid = true;
        }

        if (!isTypeValid) {
            return 0;
        }
        return type;
    }
    @Override
    public boolean addToSurfaceSyncGroup(IBinder syncGroupToken, boolean parentSyncGroupMerge,
            @Nullable ISurfaceSyncGroupCompletedListener completedListener,
            AddToSurfaceSyncGroupResult outAddToSyncGroupResult) {
        return mSurfaceSyncGroupController.addToSyncGroup(syncGroupToken, parentSyncGroupMerge,
                completedListener, outAddToSyncGroupResult);
    }

    @Override
    public void markSurfaceSyncGroupReady(IBinder syncGroupToken) {
        mSurfaceSyncGroupController.markSyncGroupReady(syncGroupToken);
    }


    /**
     * Must be called when a screenshot is taken via hardware chord.
     *
     * Notifies all registered visible activities that have registered for screencapture callback,
     * Returns a list of visible apps component names.
     */
    @Override
    public List<ComponentName> notifyScreenshotListeners(int displayId) {
        // make sure caller is SysUI.
        if (!checkCallingPermission(STATUS_BAR_SERVICE,
                "notifyScreenshotListeners()")) {
            throw new SecurityException("Requires STATUS_BAR_SERVICE permission");
        }
        synchronized (mGlobalLock) {
            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
            if (displayContent == null) {
                return new ArrayList<>();
            }
            ArraySet<ComponentName> notifiedApps = new ArraySet<>();
            displayContent.forAllActivities(
                    (ar) -> {
                        if (!notifiedApps.contains(ar.mActivityComponent) && ar.isVisible()
                                && ar.isRegisteredForScreenCaptureCallback()) {
                            ar.reportScreenCaptured();
                            notifiedApps.add(ar.mActivityComponent);
                        }
                    },
                    true /* traverseTopToBottom */);
            return List.copyOf(notifiedApps);
        }
    }

    /**
     * Returns whether the given UID is the owner of a virtual device, which the given display
     * belongs to.
     */
    @Override
    public boolean isCallerVirtualDeviceOwner(int displayId, int callingUid) {
        if (!android.companion.virtualdevice.flags.Flags.statusBarAndInsets()) {
            return false;
        }
        final long identity = Binder.clearCallingIdentity();
        try {
            return mAtmService.mTaskSupervisor.isDeviceOwnerUid(displayId, callingUid);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    /**
     * Returns whether the display with the given ID is trusted.
     */
    @Override
    public boolean isDisplayTrusted(int displayId) {
        final long identity = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                DisplayContent dc = mRoot.getDisplayContent(displayId);
                return dc != null && dc.isTrusted();
            }
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    @RequiresPermission(ACCESS_SURFACE_FLINGER)
    @Override
    public boolean replaceContentOnDisplay(int displayId, SurfaceControl sc) {
        if (!checkCallingPermission(ACCESS_SURFACE_FLINGER,
                "replaceDisplayContent()")) {
            throw new SecurityException("Requires ACCESS_SURFACE_FLINGER permission");
        }

        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
                if (dc == null) {
                    return false;
                }
                dc.replaceContent(sc);
                return true;
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

    /**
     * Resets the spatial ordering of recents for testing purposes.
     */
    void resetFreezeRecentTaskListReordering() {
        if (!checkCallingPermission(permission.MANAGE_ACTIVITY_TASKS,
                "resetFreezeRecentTaskListReordering()")) {
            throw new SecurityException("Requires MANAGE_ACTIVITY_TASKS permission");
        }
        mAtmService.getRecentTasks().resetFreezeTaskListReorderingOnTimeout();
    }

    @Override
    public void registerTrustedPresentationListener(IBinder window,
            ITrustedPresentationListener listener,
            TrustedPresentationThresholds thresholds, int id) {
        mTrustedPresentationListenerController.registerListener(window, listener, thresholds, id);
    }

    @Override
    public void unregisterTrustedPresentationListener(ITrustedPresentationListener listener,
            int id) {
        mTrustedPresentationListenerController.unregisterListener(listener, id);
    }

    @EnforcePermission(android.Manifest.permission.DETECT_SCREEN_RECORDING)
    @Override
    public boolean registerScreenRecordingCallback(IScreenRecordingCallback callback) {
        registerScreenRecordingCallback_enforcePermission();
        return mScreenRecordingCallbackController.register(callback);
    }

    @EnforcePermission(android.Manifest.permission.DETECT_SCREEN_RECORDING)
    @Override
    public void unregisterScreenRecordingCallback(IScreenRecordingCallback callback) {
        unregisterScreenRecordingCallback_enforcePermission();
        mScreenRecordingCallbackController.unregister(callback);
    }

    void onProcessActivityVisibilityChanged(int uid, boolean visible) {
        mScreenRecordingCallbackController.onProcessActivityVisibilityChanged(uid, visible);
    }

    /**
     * Sets the listener to be called back when a cross-window drag and drop operation happens.
     */
    @Override
    public void setGlobalDragListener(IGlobalDragListener listener) throws RemoteException {
        mAtmService.enforceTaskPermission("setUnhandledDragListener");
        synchronized (mGlobalLock) {
            mDragDropController.setGlobalDragListener(listener);
        }
    }

    boolean getDisableSecureWindows() {
        return mDisableSecureWindows;
    }

    /**
     * Called to notify WMS that the specified window has become visible. This shows a Toast if the
     * window is deemed to hold sensitive content.
     */
    private void onWindowVisible(@NonNull WindowState w) {
        showToastIfBlockingScreenCapture(w);
    }

    /**
     * Shows a Toast if the specified window is
     * {@link LocalService#addBlockScreenCaptureForApps(ArraySet) blocked} from screen capture based
     * on sensitive content protections.
     */
    private void showToastIfBlockingScreenCapture(@NonNull WindowState w) {
        int uid = w.getOwningUid();
        if (mCaptureBlockedToastShownUids.contains(uid)) {
            return;
        }
        if (mSensitiveContentPackages.shouldBlockScreenCaptureForApp(w.getOwningPackage(), uid,
                w.getWindowToken())) {
            mCaptureBlockedToastShownUids.add(uid);
            mH.post(() -> {
                Toast.makeText(mContext, Looper.getMainLooper(),
                                mContext.getString(R.string.screen_not_shared_sensitive_content),
                                Toast.LENGTH_LONG)
                        .show();
            });
            // If blocked due to notification protection (null window token) log protection applied
            if (sensitiveContentMetricsBugfix()
                    && mSensitiveContentPackages
                    .shouldBlockScreenCaptureForApp(w.getOwningPackage(), uid, null)) {
                FrameworkStatsLog.write(
                        SENSITIVE_NOTIFICATION_APP_PROTECTION_APPLIED,
                        mSensitiveContentProtectionSessionId,
                        uid);
            }
        }
    }

    /**
     * Dump ViewRootImpl for visible non-activity windows.
     */
    private void dumpVisibleWindowClients(FileDescriptor fd, PrintWriter pw, long timeout) {
        final ArrayList<WindowState> systemWindows = new ArrayList<>();
        synchronized (mGlobalLock) {
            mRoot.forAllWindows(w -> {
                if (!w.isActivityWindow() && w.isVisibleNow()) {
                    systemWindows.add(w);
                }
            }, false /* traverseTopToBottom */);
        }

        systemWindows.forEach(w -> {
            pw.println("---------------------------------");
            pw.println(w.toString());
            pw.flush();
            try (TransferPipe tp = new TransferPipe()) {
                w.mClient.dumpWindow(tp.getWriteFd());
                tp.go(fd, timeout);
            } catch (IOException e) {
                pw.println("Failure while dumping the window: " + e);
            } catch (RemoteException e) {
                pw.println("Got a RemoteException while dumping the window");
            }
        });
    }

    private final class ConfigurationChangeSettingInternalImpl implements
            ConfigurationChangeSetting.ConfigurationChangeSettingInternal {
        @NonNull
        @Override
        public ConfigurationChangeSetting createImplFromParcel(
                @ConfigurationChangeSetting.SettingType int settingType, @NonNull Parcel in) {
            switch (settingType) {
                case ConfigurationChangeSetting.SETTING_TYPE_DISPLAY_DENSITY:
                    return new DensitySettingImpl(in);
                case ConfigurationChangeSetting.SETTING_TYPE_FONT_SCALE:
                    return new FontScaleSettingImpl(in);
                default:
                    throw new IllegalArgumentException("Unknown setting type " + settingType);
            }
        }

        private final class DensitySettingImpl extends ConfigurationChangeSetting.DensitySetting {
            private DensitySettingImpl(Parcel in) {
                super(in);
            }

            @Override
            @GuardedBy("mGlobalLock")
            public void apply(@UserIdInt int userId) {
                setForcedDensityLockedInternal(mDisplayId, mDensity, userId);
            }
        }

        private final class FontScaleSettingImpl extends
                ConfigurationChangeSetting.FontScaleSetting {
            private FontScaleSettingImpl(Parcel in) {
                super(in);
            }

            @Override
            @GuardedBy("mGlobalLock")
            public void apply(@UserIdInt int userId) {
                Settings.System.putFloat(mContext.getContentResolver(),
                        Settings.System.FONT_SCALE, mFontScaleFactor);
                mAtmService.updateFontScaleIfNeeded(userId);
            }
        }
    }
}
