/*
 * Copyright (C) 2006 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.policy;

import static android.Manifest.permission.CREATE_VIRTUAL_DEVICE;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY;
import static android.app.AppOpsManager.OP_CREATE_ACCESSIBILITY_OVERLAY;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.AppOpsManager.OP_TOAST_WINDOW;
import static android.bluetooth.BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED;
import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
import static android.content.PermissionChecker.PID_UNKNOWN;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
import static android.content.pm.PackageManager.FEATURE_HDMI_CEC;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerPolicyProto.KEYGUARD_DELEGATE;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerPolicyProto.KEYGUARD_DRAW_COMPLETE;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerPolicyProto.KEYGUARD_OCCLUDED;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_CHANGED;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerPolicyProto.KEYGUARD_OCCLUDED_PENDING;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerPolicyProto.ORIENTATION;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerPolicyProto.ROTATION;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerPolicyProto.ROTATION_MODE;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerPolicyProto.SCREEN_ON_FULLY;
import static android.internal.perfetto.protos.Windowmanagerservice.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE;
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.O;
import static android.os.IInputConstants.INVALID_INPUT_DEVICE_ID;
import static android.os.UserManager.isVisibleBackgroundUsersEnabled;
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.STATE_OFF;
import static android.view.Display.TYPE_EXTERNAL;
import static android.view.KeyEvent.KEYCODE_BACK;
import static android.view.KeyEvent.KEYCODE_HOME;
import static android.view.KeyEvent.KEYCODE_POWER;
import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;
import static android.view.KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL;
import static android.view.KeyEvent.KEYCODE_UNKNOWN;
import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN;
import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
import static android.view.WindowManager.LayoutParams.TYPE_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_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled;

import static com.android.hardware.input.Flags.bluetoothWakeupStateCheck;
import static com.android.hardware.input.Flags.enableNew25q2Keycodes;
import static com.android.internal.policy.IKeyguardService.SCREEN_TURNING_ON_REASON_DISPLAY_SWITCH;
import static com.android.internal.policy.IKeyguardService.SCREEN_TURNING_ON_REASON_UNKNOWN;
import static com.android.server.policy.SingleKeyGestureEvent.ACTION_CANCEL;
import static com.android.server.policy.SingleKeyGestureEvent.ACTION_COMPLETE;
import static com.android.server.policy.SingleKeyGestureEvent.ACTION_START;
import static com.android.server.policy.SingleKeyGestureEvent.SINGLE_KEY_GESTURE_TYPE_LONG_PRESS;
import static com.android.server.policy.SingleKeyGestureEvent.SINGLE_KEY_GESTURE_TYPE_PRESS;
import static com.android.server.policy.SingleKeyGestureEvent.SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_BEHAVIOR_LOCK;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_BEHAVIOR_NONE;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_BEHAVIOR_SLEEP;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
import static com.android.systemui.shared.Flags.enableLppAssistInvocationEffect;
import static com.android.systemui.shared.Flags.enableLppAssistInvocationHapticEffect;

import android.accessibilityservice.AccessibilityService;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.IActivityManager;
import android.app.IUiModeManager;
import android.app.NotificationManager;
import android.app.ProgressDialog;
import android.app.SearchManager;
import android.app.UiModeManager;
import android.bluetooth.BluetoothProfile;
import android.content.ActivityNotFoundException;
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.PermissionChecker;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Rect;
import android.hardware.SensorPrivacyManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.hdmi.HdmiAudioSystemClient;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
import android.hardware.input.AppLaunchData;
import android.hardware.input.InputManager;
import android.hardware.input.KeyGestureEvent;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.session.MediaSessionLegacyHelper;
import android.os.Binder;
import android.os.Bundle;
import android.os.DeviceIdleManager;
import android.os.FactoryTest;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.WakeReason;
import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import com.android.secureline.ISecureLineManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UEventObserver;
import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.Vibrator;
import android.provider.MediaStore;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.service.SensorPrivacyToggleSourceProto;
import android.service.dreams.DreamManagerInternal;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.service.vr.IPersistentVrStateCallbacks;
import android.speech.RecognizerIntent;
import android.telecom.TelecomManager;
import android.util.Log;
import android.util.MathUtils;
import android.util.MutableBoolean;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.HapticFeedbackConstants;
import android.view.IDisplayFoldListener;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.autofill.AutofillManagerInternal;
import android.widget.Toast;
import android.window.DesktopExperienceFlags;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
import com.android.internal.display.BrightnessUtils;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.RoSystemProperties;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IKeyguardService;
import com.android.internal.policy.IShortcutService;
import com.android.internal.policy.KeyInterceptionInfo;
import com.android.internal.policy.LogDecelerateInterpolator;
import com.android.internal.policy.PhoneWindow;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.AccessibilityManagerInternal;
import com.android.server.DockObserverInternal;
import com.android.server.ExtconStateObserver;
import com.android.server.ExtconUEventObserver;
import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
import com.android.server.SystemServiceManager;
import com.android.server.UiThread;
import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.DisplayPolicy;
import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.AppTransitionListener;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;

/**
 * WindowManagerPolicy implementation for the Android phone UI.  This
 * introduces a new method suffix, Lp, for an internal lock of the
 * PhoneWindowManager.  This is used to protect some internal state, and
 * can be acquired with either the Lw and Li lock held, so has the restrictions
 * of both of those when held.
 */
public class PhoneWindowManager implements WindowManagerPolicy {
    static final String TAG = "WindowManager";
    static final boolean localLOGV = false;
    static final boolean DEBUG_INPUT = false;
    static final boolean DEBUG_KEYGUARD = false;
    static final boolean DEBUG_WAKEUP = false;
    static final boolean DEBUG_DREAMS = false;

    // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
    // No longer recommended for desk docks;
    static final boolean ENABLE_DESK_DOCK_HOME_CAPTURE = false;

    // Whether to allow devices placed in vr headset viewers to have an alternative Home intent.
    static final boolean ENABLE_VR_HEADSET_HOME_CAPTURE = true;

    // --------- Key behavior definitions below. ---------
    // NOTE: When updating the valid range of any of these behaviors, and if that behavior has a
    // Settings key associated to it for dynamic update, update its valid Settings value range in
    // android.provider.settings.validators.GlobalSettingsValidators.

    // must match: config_shortPressOnPowerBehavior in config.xml
    // The config value can be overridden using Settings.Global.POWER_BUTTON_SHORT_PRESS
    static final int SHORT_PRESS_POWER_NOTHING = 0;
    static final int SHORT_PRESS_POWER_GO_TO_SLEEP = 1;
    static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP = 2;
    static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME = 3;
    static final int SHORT_PRESS_POWER_GO_HOME = 4;
    static final int SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME = 5;
    static final int SHORT_PRESS_POWER_LOCK_OR_SLEEP = 6;
    static final int SHORT_PRESS_POWER_DREAM_OR_SLEEP = 7;
    static final int SHORT_PRESS_POWER_HUB_OR_DREAM_OR_SLEEP = 8;
    static final int SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP = 9;

    // must match: config_LongPressOnPowerBehavior in config.xml
    // The config value can be overridden using Settings.Global.POWER_BUTTON_LONG_PRESS
    static final int LONG_PRESS_POWER_NOTHING = 0;
    static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
    static final int LONG_PRESS_POWER_SHUT_OFF = 2;
    static final int LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM = 3;
    static final int LONG_PRESS_POWER_GO_TO_VOICE_ASSIST = 4;
    static final int LONG_PRESS_POWER_ASSISTANT = 5; // Settings.Secure.ASSISTANT
    static final int LONG_PRESS_POWER_GO_TO_SLEEP = 6;

    // must match: config_veryLongPresOnPowerBehavior in config.xml
    // The config value can be overridden using Settings.Global.POWER_BUTTON_VERY_LONG_PRESS
    static final int VERY_LONG_PRESS_POWER_NOTHING = 0;
    static final int VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;

    // must match: config_doublePressOnPowerBehavior in config.xml
    // The config value can be overridden using Settings.Global.POWER_BUTTON_DOUBLE_PRESS and/or
    // Settings.Global.POWER_BUTTON_TRIPLE_PRESS
    static final int MULTI_PRESS_POWER_NOTHING = 0;
    // Deprecated: static final int MULTI_PRESS_POWER_THEATER_MODE = 1;
    static final int MULTI_PRESS_POWER_BRIGHTNESS_BOOST = 2;
    static final int MULTI_PRESS_POWER_LAUNCH_TARGET_ACTIVITY = 3;

    // must match: config_longPressOnBackBehavior in config.xml
    static final int LONG_PRESS_BACK_NOTHING = 0;
    static final int LONG_PRESS_BACK_GO_TO_VOICE_ASSIST = 1;

    // must match: config_longPressOnHomeBehavior in config.xml
    static final int LONG_PRESS_HOME_NOTHING = 0;
    static final int LONG_PRESS_HOME_ALL_APPS = 1;
    static final int LONG_PRESS_HOME_ASSIST = 2;
    static final int LONG_PRESS_HOME_NOTIFICATION_PANEL = 3;
    static final int LAST_LONG_PRESS_HOME_BEHAVIOR = LONG_PRESS_HOME_NOTIFICATION_PANEL;

    // must match: config_doubleTapOnHomeBehavior in config.xml
    static final int DOUBLE_TAP_HOME_NOTHING = 0;
    static final int DOUBLE_TAP_HOME_RECENT_SYSTEM_UI = 1;
    static final int DOUBLE_TAP_HOME_PIP_MENU = 2;

    static final int SHORT_PRESS_WINDOW_NOTHING = 0;
    static final int SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE = 1;

    static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP = 0;
    static final int SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME = 1;

    static final int PENDING_KEY_NULL = -1;

    // Must match: config_shortPressOnStemPrimaryBehavior in config.xml
    // The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS
    static final int SHORT_PRESS_PRIMARY_NOTHING = 0;
    static final int SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS = 1;
    static final int SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY = 2;

    // Must match: config_longPressOnStemPrimaryBehavior in config.xml
    // The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS
    static final int LONG_PRESS_PRIMARY_NOTHING = 0;
    static final int LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT = 1;

    // Must match: config_doublePressOnStemPrimaryBehavior in config.xml
    //The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_DOUBLE_PRESS
    static final int DOUBLE_PRESS_PRIMARY_NOTHING = 0;
    static final int DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP = 1;
    static final int DOUBLE_PRESS_PRIMARY_LAUNCH_DEFAULT_FITNESS_APP = 2;

    // Must match: config_triplePressOnStemPrimaryBehavior in config.xml
    // The config value can be overridden using Settings.Global.STEM_PRIMARY_BUTTON_TRIPLE_PRESS
    static final int TRIPLE_PRESS_PRIMARY_NOTHING = 0;
    static final int TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY = 1;

    static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
    static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
    static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
    static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
    static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist";
    static public final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav";

    public static final String TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD = "waitForAllWindowsDrawn";

    /**
     * String extra key passed in the bundle of {@link IKeyguardService#doKeyguardTimeout(Bundle)}
     * if the value is {@code true}, indicates to keyguard that the device should show the
     * glanceable hub upon locking. If the hub is already visible, the device should go to sleep.
     */
    public static final String EXTRA_TRIGGER_HUB = "extra_trigger_hub";

    private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;

    // ===============================
    // SecureLine Panic Button
    // ===============================

    private static final int SECURELINE_PANIC_PRESS_COUNT = 5;
    private static final long SECURELINE_PANIC_WINDOW_MS = 3000;

    private int mSecureLinePanicCounter = 0;
    private long mSecureLineFirstPressTime = 0;

    private final Handler mSecureLineHandler =
            new Handler(Looper.getMainLooper());

    private void secureLineTriggerPanicAsync() {
        mSecureLineHandler.post(() -> {
            try {
                IBinder b = ServiceManager.getService("secureline");
                if (b == null) return;

                ISecureLineManager sl =
                        ISecureLineManager.Stub.asInterface(b);
                if (sl == null) return;

                if (!sl.isPanicProtectionEnabled()) {
                    return;
                }

                sl.triggerWipe(999); // panic reason

            } catch (Throwable t) {
                Slog.e("SecureLinePanic", "Panic wipe failed", t);
            }
        });
    }

    /**
     * Keyguard stuff
     */
    private boolean mKeyguardDrawnOnce;

    /** Amount of time (in milliseconds) to wait for windows drawn before powering on. */
    static final int WAITING_FOR_DRAWN_TIMEOUT = 1000;

    /**
      * Extra time for additional SystemUI animations.
      * <p>Since legacy apps can add Toast windows directly instead of using Toast APIs,
      * {@link DisplayPolicy} ensures that the window manager removes toast windows after
      * TOAST_WINDOW_TIMEOUT. We increase this timeout by TOAST_WINDOW_ANIM_BUFFER to account for
      * SystemUI's in/out toast animations, so that the toast text is still shown for a minimum
      * of 3.5 seconds and the animations are finished before window manager removes the window.
      */
    public static final int TOAST_WINDOW_ANIM_BUFFER = 600;

    /**
      * Amount of time (in milliseconds) a toast window can be shown before it's automatically
      * removed by window manager.
      */
    public static final int TOAST_WINDOW_TIMEOUT = 3500 + TOAST_WINDOW_ANIM_BUFFER;

    /**
     * Action for launching assistant in retail mode
     */
    private static final String ACTION_VOICE_ASSIST_RETAIL =
            "android.intent.action.VOICE_ASSIST_RETAIL";

    /**
     * Maximum amount of time in milliseconds between consecutive power onKeyDown events to be
     * considered a multi-press, only used for the power button.
     * Note: To maintain backwards compatibility for the power button, we are measuring the times
     * between consecutive down events instead of the first tap's up event and the second tap's
     * down event.
     */
    @VisibleForTesting public static final int POWER_MULTI_PRESS_TIMEOUT_MILLIS =
            ViewConfiguration.getMultiPressTimeout();

    /**
     * Lock protecting internal state.  Must not call out into window
     * manager with lock held.  (This lock will be acquired in places
     * where the window manager is calling in with its own lock held.)
     */
    private final Object mLock = new Object();

    /** List of {@link ScreenOnListener}s which do not belong to the default display. */
    private final SparseArray<ScreenOnListener> mScreenOnListeners = new SparseArray<>();

    Context mContext;
    Injector mInjector;
    WindowManagerFuncs mWindowManagerFuncs;
    WindowManagerInternal mWindowManagerInternal;
    PowerManager mPowerManager;
    ActivityManagerInternal mActivityManagerInternal;
    IActivityManager mActivityManagerService;
    ActivityTaskManagerInternal mActivityTaskManagerInternal;
    AutofillManagerInternal mAutofillManagerInternal;
    InputManager mInputManager;
    InputManagerInternal mInputManagerInternal;
    DreamManagerInternal mDreamManagerInternal;
    PowerManagerInternal mPowerManagerInternal;
    IStatusBarService mStatusBarService;
    StatusBarManagerInternal mStatusBarManagerInternal;
    AudioManagerInternal mAudioManagerInternal;
    SensorPrivacyManager mSensorPrivacyManager;
    DisplayManager mDisplayManager;
    DisplayManagerInternal mDisplayManagerInternal;
    UserManagerInternal mUserManagerInternal;
    DockObserverInternal mDockObserverInternal;

    private WallpaperManagerInternal mWallpaperManagerInternal;

    boolean mPreloadedRecentApps;
    final Object mServiceAcquireLock = new Object();
    Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
    AccessibilityManager mAccessibilityManager;
    AccessibilityManagerInternal mAccessibilityManagerInternal;
    BurnInProtectionHelper mBurnInProtectionHelper;
    private DisplayFoldController mDisplayFoldController;
    AppOpsManager mAppOpsManager;
    PackageManager mPackageManager;
    SideFpsEventHandler mSideFpsEventHandler;
    LockPatternUtils mLockPatternUtils;
    private boolean mHasFeatureAuto;
    private boolean mHasFeatureWatch;
    private boolean mHasFeatureLeanback;
    private boolean mHasFeatureHdmiCec;

    // Assigned on main thread, accessed on UI thread
    volatile VrManagerInternal mVrManagerInternal;

    private TalkbackShortcutController mTalkbackShortcutController;

    private WindowWakeUpPolicy mWindowWakeUpPolicy;

    boolean mSafeMode;

    // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
    // This is for car dock and this is updated from resource.
    private boolean mEnableCarDockHomeCapture = true;

    boolean mBootMessageNeedsHiding;
    volatile boolean mBootAnimationDismissable;
    @VisibleForTesting KeyguardServiceDelegate mKeyguardDelegate;
    private boolean mKeyguardBound;
    final DrawnListener mKeyguardDrawnCallback = new DrawnListener() {
        @Override
        public void onDrawn() {
            if (DEBUG_WAKEUP) Slog.d(TAG, "mKeyguardDelegate.ShowListener.onDrawn.");
            mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
        }
    };

    private Supplier<GlobalActions> mGlobalActionsFactory;
    private GlobalActions mGlobalActions;
    private Handler mHandler;

    // FIXME This state is shared between the input reader and handler thread.
    // Technically it's broken and buggy but it has been like this for many years
    // and we have not yet seen any problems.  Someday we'll rewrite this logic
    // so that only one thread is involved in handling input policy.  Unfortunately
    // it's on a critical path for power management so we can't just post the work to the
    // handler thread.  We'll need to resolve this someday by teaching the input dispatcher
    // to hold wakelocks during dispatch and eliminating the critical path.
    volatile boolean mPowerKeyHandled;
    volatile boolean mBackKeyHandled;
    volatile boolean mEndCallKeyHandled;
    volatile boolean mPowerButtonLaunchGestureTriggered;
    volatile boolean mPowerButtonLaunchGestureTriggeredDuringGoingToSleep;

    /**
     * {@code true} if the device is entering a low-power state; {@code false otherwise}.
     *
     * <p>This differs from {@link #mRequestedOrSleepingDefaultDisplay} which tracks the power state
     * of the {@link #mDefaultDisplay default display} versus the power state of the entire device.
     */
    volatile boolean mDeviceGoingToSleep;

    /**
     * {@code true} if the {@link #mDefaultDisplay default display} is entering or was requested to
     * enter a low-power state; {@code false otherwise}.
     *
     * <p>This differs from {@link #mDeviceGoingToSleep} which tracks the power state of the entire
     * device versus the power state of the {@link #mDefaultDisplay default display}.
     */
    // TODO(b/178103325): Track sleep/requested sleep for every display.
    volatile boolean mRequestedOrSleepingDefaultDisplay;

    /**
     * This is used to check whether to acquire screen-off sleep token when screen is
     * turned off. E.g. if it is false when screen is turned off and the display is swapping, it
     * is expected that the screen will be on in a short time. Then it is unnecessary to acquire
     * screen-off-sleep-token, so it can avoid intermediate visibility or lifecycle changes.
     */
    volatile boolean mIsGoingToSleep;

    // We are only tracking the current pending sleeping or waking group and not all the groups.
    // This is because these updates are coming from power thread via the notifier thread. As such,
    // we DON'T expect
    // 1. group 1 to start sleeping, and then group 2 to also start sleeping before group 1 could
    // finish sleeping
    // 2. group 1 to start waking, and then group 2 to also start waking before group 1 could
    // finish waking
    volatile int mPendingSleepingGroup;
    volatile int mPendingWakeupGroup;
    volatile boolean mRecentsVisible;
    volatile boolean mNavBarVirtualKeyHapticFeedbackEnabled = true;
    volatile boolean mPictureInPictureVisible;
    volatile private boolean mDismissImeOnBackKeyPressed;

    // Used to hold the last user key used to wake the device.  This helps us prevent up events
    // from being passed to the foregrounded app without a corresponding down event
    volatile int mPendingWakeKey = PENDING_KEY_NULL;

    int mCameraLensCoverState = CAMERA_LENS_COVER_ABSENT;
    boolean mHaveBuiltInKeyboard;

    boolean mSystemReady;
    boolean mSystemBooted;
    HdmiControl mHdmiControl;
    IUiModeManager mUiModeManager;
    int mUiMode;

    boolean mWakeGestureEnabledSetting;
    MyWakeGestureListener mWakeGestureListener;

    int mLidKeyboardAccessibility;
    int mLidNavigationAccessibility;
    int mShortPressOnPowerBehavior;
    private boolean mShouldEarlyShortPressOnPower;
    boolean mShouldEarlyShortPressOnStemPrimary;
    int mLongPressOnPowerBehavior;
    long mLongPressOnPowerAssistantTimeoutMs;
    int mVeryLongPressOnPowerBehavior;
    int mDoublePressOnPowerBehavior;
    ComponentName mPowerDoublePressTargetActivity;
    int mTriplePressOnPowerBehavior;
    int mLongPressOnBackBehavior;
    int mShortPressOnSleepBehavior;
    int mShortPressOnWindowBehavior;
    boolean mStylusButtonsEnabled = true;
    boolean mKidsModeEnabled;
    boolean mHasSoftInput = false;
    boolean mUseTvRouting;
    boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
    MetricsLogger mLogger;
    boolean mWakeOnDpadKeyPress;
    boolean mWakeOnAssistKeyPress;
    boolean mWakeOnBackKeyPress;
    boolean mSilenceRingerOnSleepKey;
    long mWakeUpToLastStateTimeout;
    long mTurnOffTvToastSuppressionDelay;
    long mTurnOffTvToastPostDelay;
    long mLastShortPressTurnOffTvHintToastTime = 0;
    ComponentName mSearchKeyTargetActivity;

    // Key Behavior - Stem Primary
    private int mShortPressOnStemPrimaryBehavior;
    private int mDoublePressOnStemPrimaryBehavior;
    private int mTriplePressOnStemPrimaryBehavior;
    private int mLongPressOnStemPrimaryBehavior;
    private RecentTaskInfo mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp;

    // The focused task at the time when the first STEM_PRIMARY key was released. This can only
    // be accessed from the looper thread.
    private RootTaskInfo mFocusedTaskInfoOnStemPrimarySingleKeyUp;

    private boolean mHandleVolumeKeysInWM;

    private boolean mPendingKeyguardOccluded;
    private boolean mKeyguardOccludedChanged;

    Intent mHomeIntent;
    Intent mCarDockIntent;
    Intent mDeskDockIntent;
    Intent mVrHeadsetHomeIntent;

    // support for activating the lock screen while the screen is on
    private HashSet<Integer> mAllowLockscreenWhenOnDisplays = new HashSet<>();
    int mLockScreenTimeout;
    boolean mLockScreenTimerActive;

    // Behavior of ENDCALL Button.  (See Settings.System.END_BUTTON_BEHAVIOR.)
    int mEndcallBehavior;

    // Behavior of POWER button while in-call and screen on.
    // (See Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR.)
    int mIncallPowerBehavior;

    // Behavior of Back button while in-call and screen on
    int mIncallBackBehavior;

    // Whether system navigation keys are enabled
    boolean mSystemNavigationKeysEnabled;

    // TODO(b/111361251): Remove default when the dependencies are multi-display ready.
    Display mDefaultDisplay;
    DisplayRotation mDefaultDisplayRotation;
    DisplayPolicy mDefaultDisplayPolicy;

    // What we do when the user long presses on home
    int mLongPressOnHomeBehavior;

    // What we do when the user double-taps on home
    int mDoubleTapOnHomeBehavior;

    // Must match config_primaryShortPressTargetActivity in config.xml
    ComponentName mPrimaryShortPressTargetActivity;

    // Whether to lock the device after the next dreaming transition has finished.
    private boolean mLockAfterDreamingTransitionFinished;

    // If true, the power button long press behavior will be invoked even if the default display is
    // non-interactive. If false, the power button long press behavior will be skipped if the
    // default display is non-interactive.
    private boolean mSupportLongPressPowerWhenNonInteractive;

    // If true, the power button short press behavior will be always invoked as long as the default
    // display is on, even if the display is not interactive. If false, the power button short press
    // behavior will be skipped if the default display is non-interactive.
    private boolean mSupportShortPressPowerWhenDefaultDisplayOn;

    // Ringer toggle should reuse timing and triggering from screenshot power and a11y vol up
    int mRingerToggleChord = VOLUME_HUSH_OFF;

    private static final long BUGREPORT_TV_GESTURE_TIMEOUT_MILLIS = 1000;

    /* The number of steps between min and max brightness */
    private static final int BRIGHTNESS_STEPS = 10;

    SettingsObserver mSettingsObserver;
    ModifierShortcutManager mModifierShortcutManager;
    /** Currently fully consumed key codes per device */
    private final SparseArray<Set<Integer>> mConsumedKeysForDevice = new SparseArray<>();
    PowerManager.WakeLock mBroadcastWakeLock;
    PowerManager.WakeLock mPowerKeyWakeLock;
    boolean mHavePendingMediaKeyRepeatWithWakeLock;

    private int mCurrentUserId;

    // Maps global key codes to the components that will handle them.
    private GlobalKeyManager mGlobalKeyManager;

    private final com.android.internal.policy.LogDecelerateInterpolator mLogDecelerateInterpolator
            = new LogDecelerateInterpolator(100, 0);
    private final DeferredKeyActionExecutor mDeferredKeyActionExecutor =
            new DeferredKeyActionExecutor();

    private volatile int mTopFocusedDisplayId = INVALID_DISPLAY;

    private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS;

    private SingleKeyGestureDetector mSingleKeyGestureDetector;
    private GestureLauncherService mGestureLauncherService;
    private ButtonOverridePermissionChecker mButtonOverridePermissionChecker;

    private boolean mLockNowPending = false;

    // Timeout for showing the keyguard after the screen is on, in case no "ready" is received.
    private int mKeyguardDrawnTimeout = 1000;

    private final boolean mVisibleBackgroundUsersEnabled = isVisibleBackgroundUsersEnabled();

    private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
    private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4;
    private static final int MSG_KEYGUARD_DRAWN_COMPLETE = 5;
    private static final int MSG_KEYGUARD_DRAWN_TIMEOUT = 6;
    private static final int MSG_WINDOW_MANAGER_DRAWN_COMPLETE = 7;
    private static final int MSG_DISPATCH_SHOW_RECENTS = 9;
    private static final int MSG_DISPATCH_SHOW_GLOBAL_ACTIONS = 10;
    private static final int MSG_HIDE_BOOT_MESSAGE = 11;
    private static final int MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK = 12;
    private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 15;
    private static final int MSG_BUGREPORT_TV = 18;
    private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 20;
    private static final int MSG_SYSTEM_KEY_PRESS = 21;
    private static final int MSG_HANDLE_ALL_APPS = 22;
    private static final int MSG_LAUNCH_ASSIST = 23;
    private static final int MSG_RINGER_TOGGLE_CHORD = 24;
    private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 25;
    private static final int MSG_SET_DEFERRED_KEY_ACTIONS_EXECUTABLE = 27;

    private class PolicyHandler extends Handler {

        private PolicyHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK:
                    dispatchMediaKeyWithWakeLock((KeyEvent)msg.obj);
                    break;
                case MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK:
                    dispatchMediaKeyRepeatWithWakeLock((KeyEvent)msg.obj);
                    break;
                case MSG_DISPATCH_SHOW_RECENTS:
                    showRecentApps(false);
                    break;
                case MSG_DISPATCH_SHOW_GLOBAL_ACTIONS:
                    showGlobalActionsInternal();
                    break;
                case MSG_KEYGUARD_DRAWN_COMPLETE:
                    if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mKeyguardDrawComplete");
                    finishKeyguardDrawn();
                    break;
                case MSG_KEYGUARD_DRAWN_TIMEOUT:
                    Slog.w(TAG, "Keyguard drawn timeout. Setting mKeyguardDrawComplete");
                    finishKeyguardDrawn();
                    break;
                case MSG_WINDOW_MANAGER_DRAWN_COMPLETE:
                    final int displayId = msg.arg1;
                    if (DEBUG_WAKEUP) Slog.w(TAG, "All windows drawn on display " + displayId);
                    Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER,
                            TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, displayId /* cookie */);
                    finishWindowsDrawn(displayId);
                    break;
                case MSG_HIDE_BOOT_MESSAGE:
                    handleHideBootMessage();
                    break;
                case MSG_LAUNCH_ASSIST:
                    final int deviceId = msg.arg1;
                    final Long eventTime = (Long) msg.obj;
                    launchAssistAction(null /* hint */, deviceId, eventTime,
                            AssistUtils.INVOCATION_TYPE_ASSIST_BUTTON);
                    break;
                case MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK:
                    launchVoiceAssistWithWakeLock();
                    break;
                case MSG_SHOW_PICTURE_IN_PICTURE_MENU:
                    showPictureInPictureMenuInternal();
                    break;
                case MSG_BUGREPORT_TV:
                    requestBugreportForTv();
                    break;
                case MSG_DISPATCH_BACK_KEY_TO_AUTOFILL:
                    mAutofillManagerInternal.onBackKeyPressed();
                    break;
                case MSG_SYSTEM_KEY_PRESS:
                    KeyEvent event = (KeyEvent) msg.obj;
                    sendSystemKeyToStatusBar(event);
                    event.recycle();
                    break;
                case MSG_HANDLE_ALL_APPS:
                    KeyEvent keyEvent = (KeyEvent) msg.obj;
                    if (isKeyEventForCurrentUser(keyEvent.getDisplayId(), keyEvent.getKeyCode(),
                            "launchAllAppsViaA11y")) {
                        launchAllAppsAction();
                    }
                    break;
                case MSG_RINGER_TOGGLE_CHORD:
                    handleRingerChordGesture();
                    break;
                case MSG_SWITCH_KEYBOARD_LAYOUT:
                    SwitchKeyboardLayoutMessageObject object =
                            (SwitchKeyboardLayoutMessageObject) msg.obj;
                    handleSwitchKeyboardLayout(object.displayId, object.direction,
                            object.focusedToken);
                    break;
                case MSG_SET_DEFERRED_KEY_ACTIONS_EXECUTABLE:
                    final int keyCode = msg.arg1;
                    final long downTime = (Long) msg.obj;
                    mDeferredKeyActionExecutor.setActionsExecutable(keyCode, downTime);
                    break;
            }
        }
    }

    private UEventObserver mHDMIObserver = new UEventObserver() {
        @Override
        public void onUEvent(UEventObserver.UEvent event) {
            mDefaultDisplayPolicy.setHdmiPlugged("1".equals(event.get("SWITCH_STATE")));
        }
    };

    class SettingsObserver extends ContentObserver {
        SettingsObserver(Handler handler) {
            super(handler);
        }

        void observe() {
            // Observe all users' changes
            ContentResolver resolver = mContext.getContentResolver();
            resolver.registerContentObserver(Settings.System.getUriFor(
                    Settings.System.END_BUTTON_BEHAVIOR), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Secure.getUriFor(
                    Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Secure.getUriFor(
                    Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Secure.getUriFor(
                    Settings.Secure.WAKE_GESTURE_ENABLED), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.System.getUriFor(
                    Settings.System.SCREEN_OFF_TIMEOUT), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Secure.getUriFor(
                    Settings.Secure.DEFAULT_INPUT_METHOD), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Secure.getUriFor(
                    Settings.Secure.VOLUME_HUSH_GESTURE), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Secure.getUriFor(
                    Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.POWER_BUTTON_SHORT_PRESS), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.POWER_BUTTON_DOUBLE_PRESS), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.POWER_BUTTON_TRIPLE_PRESS), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.POWER_BUTTON_LONG_PRESS), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.POWER_BUTTON_VERY_LONG_PRESS), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.STEM_PRIMARY_BUTTON_DOUBLE_PRESS), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.STEM_PRIMARY_BUTTON_TRIPLE_PRESS), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.KEY_CHORD_POWER_VOLUME_UP), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Secure.getUriFor(
                    Settings.Secure.STYLUS_BUTTONS_ENABLED), false, this,
                    UserHandle.USER_ALL);
            resolver.registerContentObserver(Settings.Secure.getUriFor(
                    Settings.Secure.NAV_BAR_KIDS_MODE), false, this,
                    UserHandle.USER_ALL);
            updateSettings();
        }

        @Override public void onChange(boolean selfChange) {
            updateSettings();
        }
    }

    class MyWakeGestureListener extends WakeGestureListener {
        MyWakeGestureListener(Context context, Handler handler) {
            super(context, handler);
        }

        @Override
        public void onWakeUp() {
            synchronized (mLock) {
                if (shouldEnableWakeGestureLp()) {
                    performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, "Wake Up");
                    mWindowWakeUpPolicy.wakeUpFromWakeGesture();
                }
            }
        }
    }

    private record SwitchKeyboardLayoutMessageObject(int displayId, IBinder focusedToken,
                                                     int direction) {
    }

    final IPersistentVrStateCallbacks mPersistentVrModeListener =
            new IPersistentVrStateCallbacks.Stub() {
        @Override
        public void onPersistentVrStateChanged(boolean enabled) {
            mDefaultDisplayPolicy.setPersistentVrModeEnabled(enabled);
        }
    };

    private void handleRingerChordGesture() {
        if (mRingerToggleChord == VOLUME_HUSH_OFF) {
            return;
        }
        getAudioManagerInternal();
        mAudioManagerInternal.silenceRingerModeInternal("volume_hush");
        Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.HUSH_GESTURE_USED, 1);
        mLogger.action(MetricsProto.MetricsEvent.ACTION_HUSH_GESTURE, mRingerToggleChord);
    }

    IStatusBarService getStatusBarService() {
        synchronized (mServiceAcquireLock) {
            if (mStatusBarService == null) {
                mStatusBarService = IStatusBarService.Stub.asInterface(
                        ServiceManager.getService("statusbar"));
            }
            return mStatusBarService;
        }
    }

    StatusBarManagerInternal getStatusBarManagerInternal() {
        synchronized (mServiceAcquireLock) {
            if (mStatusBarManagerInternal == null) {
                mStatusBarManagerInternal =
                        LocalServices.getService(StatusBarManagerInternal.class);
            }
            return mStatusBarManagerInternal;
        }
    }

    AudioManagerInternal getAudioManagerInternal() {
        synchronized (mServiceAcquireLock) {
            if (mAudioManagerInternal == null) {
                mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
            }
            return mAudioManagerInternal;
        }
    }

    AccessibilityManagerInternal getAccessibilityManagerInternal() {
        synchronized (mServiceAcquireLock) {
            if (mAccessibilityManagerInternal == null) {
                mAccessibilityManagerInternal =
                        LocalServices.getService(AccessibilityManagerInternal.class);
            }
            return mAccessibilityManagerInternal;
        }
    }

    // returns true if the key was handled and should not be passed to the user
    private boolean backKeyPress() {
        mLogger.count("key_back_press", 1);
        // Cache handled state
        boolean handled = mBackKeyHandled;

        if (mHasFeatureWatch) {
            TelecomManager telecomManager = getTelecommService();

            if (telecomManager != null) {
                if (telecomManager.isRinging()) {
                    // Pressing back while there's a ringing incoming
                    // call should silence the ringer.
                    telecomManager.silenceRinger();

                    // It should not prevent navigating away
                    return false;
                } else if (
                    (mIncallBackBehavior & Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR_HANGUP) != 0
                        && telecomManager.isInCall()) {
                    // Otherwise, if "Back button ends call" is enabled,
                    // the Back button will hang up any current active call.
                    return telecomManager.endCall();
                }
            }
        }

        if (mAutofillManagerInternal != null) {
            mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_BACK_KEY_TO_AUTOFILL));
        }
        return handled;
    }

    private void interceptPowerKeyDown(KeyEvent event, boolean interactive,
            boolean isKeyGestureTriggered) {
        // Hold a wake lock until the power key is released.
        if (!mPowerKeyWakeLock.isHeld()) {
            mPowerKeyWakeLock.acquire();
        }

        mWindowManagerFuncs.onPowerKeyDown(interactive);

        // Stop ringing or end call if configured to do so when power is pressed.
        TelecomManager telecomManager = getTelecommService();
        boolean hungUp = false;
        if (telecomManager != null) {
            if (telecomManager.isRinging()) {
                // Pressing Power while there's a ringing incoming
                // call should silence the ringer.
                telecomManager.silenceRinger();
            } else if ((mIncallPowerBehavior
                    & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
                    && telecomManager.isInCall() && interactive) {
                // Otherwise, if "Power button ends call" is enabled,
                // the Power button will hang up any current active call.
                hungUp = telecomManager.endCall();
            }
        }

        final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);

        // Inform the StatusBar; but do not allow it to consume the event.
        sendSystemKeyToStatusBarAsync(event);

        // If the power key has still not yet been handled, then detect short
        // press, long press, or multi press and decide what to do.
        mPowerKeyHandled = mPowerKeyHandled || hungUp
                || handledByPowerManager || isKeyGestureTriggered;

        if (!mPowerKeyHandled) {
            if (!interactive) {
                wakeUpFromWakeKey(event);
            }
        } else {
            // handled by another power key policy.
            if (mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {
                Slog.d(TAG, "Skip power key gesture for other policy has handled it.");
                mSingleKeyGestureDetector.reset();
            }
        }
    }

    private void interceptPowerKeyUp(KeyEvent event, boolean canceled) {
        // Inform the StatusBar; but do not allow it to consume the event.
        sendSystemKeyToStatusBarAsync(event);
        finishPowerKeyPress();
    }

    private void finishPowerKeyPress() {
        mPowerKeyHandled = false;
        if (mPowerKeyWakeLock.isHeld()) {
            mPowerKeyWakeLock.release();
        }
    }

    @VisibleForTesting
    void powerPress(long eventTime, int count, int displayId) {
        // SideFPS still needs to know about suppressed power buttons, in case it needs to block
        // an auth attempt.
        if (count == 1) {
            mSideFpsEventHandler.notifyPowerPressed();
        }
        if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) {
            Slog.i(TAG, "Suppressed redundant power key press while "
                    + "already in the process of turning the screen on.");
            return;
        }

        final boolean interactive = mDefaultDisplayPolicy.isAwake();

        Slog.d(
                TAG,
                "powerPress: eventTime="
                        + eventTime
                        + " interactive="
                        + interactive
                        + " count="
                        + count
                        + " mShortPressOnPowerBehavior="
                        + mShortPressOnPowerBehavior);

        if (count == 2) {
            powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
        } else if (count == 3) {
            powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
        } else if (count > 3 && count <= getMaxMultiPressPowerCount()) {
            Slog.d(TAG, "No behavior defined for power press count " + count);
        } else if (count == 1 && shouldHandleShortPressPowerAction(interactive, eventTime)) {
            switch (mShortPressOnPowerBehavior) {
                case SHORT_PRESS_POWER_NOTHING:
                    break;
                case SHORT_PRESS_POWER_GO_TO_SLEEP:
                    sleepDefaultDisplayFromPowerButton(eventTime, 0);
                    break;
                case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
                    sleepDefaultDisplayFromPowerButton(eventTime,
                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                    break;
                case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
                    if (sleepDefaultDisplayFromPowerButton(eventTime,
                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE)) {
                        launchHomeFromHotKey(DEFAULT_DISPLAY);
                    }
                    break;
                case SHORT_PRESS_POWER_GO_HOME:
                    shortPressPowerGoHome();
                    break;
                case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: {
                    if (mDismissImeOnBackKeyPressed) {
                        InputMethodManagerInternal.get().hideInputMethod(
                                SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME, displayId);
                    } else {
                        shortPressPowerGoHome();
                    }
                    break;
                }
                case SHORT_PRESS_POWER_LOCK_OR_SLEEP: {
                    if (mKeyguardDelegate == null || !mKeyguardDelegate.hasKeyguard()
                            || !mKeyguardDelegate.isSecure(mCurrentUserId) || keyguardOn()) {
                        sleepDefaultDisplayFromPowerButton(eventTime, 0);
                    } else {
                        lockNow(null /*options*/);
                    }
                    break;
                }
                case SHORT_PRESS_POWER_DREAM_OR_SLEEP: {
                    attemptToDreamOrAwakeFromShortPowerButtonPress(
                            /* isScreenOn */ true,
                            /* awakeWhenDream */ false,
                            /* noDreamAction */
                            () -> sleepDefaultDisplayFromPowerButton(eventTime, 0));
                    break;
                }
                case SHORT_PRESS_POWER_HUB_OR_DREAM_OR_SLEEP: {
                    // With this power button behavior, the following behavior is expected from each
                    // system space on a power button short press:
                    // - Unlocked: go to hub if available, dream if not, screen off if neither
                    // - Lock screen, hub, or dream: go to screen off
                    // - Screen off: go to hub if available, dream if not, lock screen if enabled,
                    //               unlocked if lockscreen is disabled
                    // TODO(b/394657933): consolidate policy into SysUI
                    final boolean hubEnabled = Settings.Secure.getIntForUser(
                            mContext.getContentResolver(), Settings.Secure.GLANCEABLE_HUB_ENABLED,
                            1, mCurrentUserId) == 1;

                    final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal();
                    if (dreamManagerInternal == null) {
                        break;
                    }

                    if (dreamManagerInternal.isDreaming() || isKeyguardShowing()) {
                        // If the device is already dreaming or on keyguard, go to sleep.
                        sleepDefaultDisplayFromPowerButton(eventTime, 0);
                        break;
                    }

                    // Check isLockScreenDisabled to exclude NONE lock screen option, which cannot
                    // show hub.
                    boolean keyguardAvailable = !mLockPatternUtils.isLockScreenDisabled(
                            mCurrentUserId);
                    if (mUserManagerInternal.isUserUnlocked(mCurrentUserId) && hubEnabled
                            && keyguardAvailable && dreamManagerInternal.dreamConditionActive()) {
                        // If the hub can be launched, send a message to keyguard.
                        Bundle options = new Bundle();
                        options.putBoolean(EXTRA_TRIGGER_HUB, true);
                        lockNow(options);
                    } else {
                        // If the hub cannot be run, attempt to dream instead.
                        attemptToDreamOrAwakeFromShortPowerButtonPress(
                                /* isScreenOn */ true,
                                /* awakeWhenDream */ false,
                                /* noDreamAction */
                                () -> sleepDefaultDisplayFromPowerButton(eventTime, 0));
                    }
                    break;
                }
                case SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP: {
                    attemptToDreamOrAwakeFromShortPowerButtonPress(
                            /* isScreenOn */ true,
                            /* awakeWhenDream */ true,
                            /* noDreamAction */
                            () -> sleepDefaultDisplayFromPowerButton(eventTime, 0));
                    break;
                }
            }
        }
    }

    private boolean shouldHandleShortPressPowerAction(boolean interactive, long eventTime) {
        if (mSupportShortPressPowerWhenDefaultDisplayOn) {
            final boolean defaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
            final boolean beganFromDefaultDisplayOn =
                    mSingleKeyGestureDetector.beganFromDefaultDisplayOn();
            if (!defaultDisplayOn || !beganFromDefaultDisplayOn) {
                Slog.v(
                        TAG,
                        "Ignoring short press of power button because the default display is not"
                                + " on. defaultDisplayOn="
                                + defaultDisplayOn
                                + ", beganFromDefaultDisplayOn="
                                + beganFromDefaultDisplayOn);
                return false;
            }
            return true;
        }
        final boolean beganFromNonInteractive = mSingleKeyGestureDetector.beganFromNonInteractive();
        if (!interactive || beganFromNonInteractive) {
            Slog.v(
                    TAG,
                    "Ignoring short press of power button because the device is not interactive."
                            + " interactive="
                            + interactive
                            + ", beganFromNonInteractive="
                            + beganFromNonInteractive);
            return false;
        }
        if (mSideFpsEventHandler.shouldConsumeSinglePress(eventTime)) {
            Slog.i(
                    TAG,
                    "Suppressing power key because the user is interacting with the "
                            + "fingerprint sensor");
            return false;
        }
        return true;
    }

    /**
     * Attempt to dream, awake or sleep from a power button press.
     *
     * @param isScreenOn Whether the screen is currently on.
     * @param awakeWhenDream When it's set to {@code true}, awake the device from dreaming.
     *        Otherwise, go to sleep.
     * @param noDreamAction The action to perform if dreaming is not possible.
     */
    private void attemptToDreamOrAwakeFromShortPowerButtonPress(
            boolean isScreenOn, boolean awakeWhenDream, Runnable noDreamAction) {
        if (mShortPressOnPowerBehavior != SHORT_PRESS_POWER_DREAM_OR_SLEEP
                && mShortPressOnPowerBehavior != SHORT_PRESS_POWER_HUB_OR_DREAM_OR_SLEEP
                && mShortPressOnPowerBehavior != SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP) {
            // If the power button behavior isn't one that should be able to trigger the dream, give
            // up.
            noDreamAction.run();
            return;
        }

        final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal();
        if (dreamManagerInternal == null) {
            Slog.d(TAG,
                    "Can't access dream manager dreaming when attempting to start or stop dream "
                    + "from short power press (isScreenOn="
                            + isScreenOn + ", awakeWhenDream=" + awakeWhenDream + ")");
            noDreamAction.run();
            return;
        }

        if (!dreamManagerInternal.canStartDreaming(isScreenOn)) {
            if (awakeWhenDream && dreamManagerInternal.isDreaming()) {
                dreamManagerInternal.stopDream(false /*immediate*/, "short press power" /*reason*/);
                return;
            }
            if (DEBUG_DREAMS) {
                Slog.d(TAG,
                        "Can't start dreaming and the device is not dreaming when attempting to "
                                + "start or stop dream from short power press (isScreenOn="
                                + isScreenOn + ", awakeWhenDream=" + awakeWhenDream + ")");
            }
            noDreamAction.run();
            return;
        }

        synchronized (mLock) {
            // If the setting to lock instantly on power button press is true, then set the flag to
            // lock after the dream transition has finished.
            mLockAfterDreamingTransitionFinished =
                    mLockPatternUtils.getPowerButtonInstantlyLocks(mCurrentUserId);
        }

        dreamManagerInternal.requestDream();
        if (mHasFeatureLeanback
                && getResolvedLongPressOnPowerBehavior() == LONG_PRESS_POWER_GO_TO_SLEEP) {
            Runnable toastRunnable = () -> {
                final long now = SystemClock.uptimeMillis();
                if (mLastShortPressTurnOffTvHintToastTime == 0L
                        || now - mLastShortPressTurnOffTvHintToastTime
                                >= mTurnOffTvToastSuppressionDelay) {
                    mLastShortPressTurnOffTvHintToastTime = now;
                    Toast.makeText(
                            mContext,
                            UiThread.get().getLooper(),
                            mContext.getString(R.string.long_press_power_to_turn_off_tv_toast),
                            Toast.LENGTH_LONG)
                            .show();
                }
            };
            mHandler.postDelayed(toastRunnable, mTurnOffTvToastPostDelay);
        }
    }

    /**
     * Sends the default display to sleep as a result of a power button press.
     *
     * @return {@code true} if the device was sent to sleep, {@code false} if the device did not
     * sleep.
     */
    private boolean sleepDefaultDisplayFromPowerButton(long eventTime, int flags) {
        // Before we actually go to sleep, we check the last wakeup reason.
        // If the device very recently woke up from a gesture (like user lifting their device)
        // then ignore the sleep instruction. This is because users have developed
        // a tendency to hit the power button immediately when they pick up their device, and we
        // don't want to put the device back to sleep in those cases.
        final PowerManager.WakeData lastWakeUp = mPowerManagerInternal.getLastWakeup();
        if (lastWakeUp != null && (lastWakeUp.wakeReason == PowerManager.WAKE_REASON_GESTURE
                || lastWakeUp.wakeReason == PowerManager.WAKE_REASON_LIFT
                || lastWakeUp.wakeReason == PowerManager.WAKE_REASON_BIOMETRIC
                || lastWakeUp.wakeReason == PowerManager.WAKE_REASON_TAP)) {
            final long now = mInjector.getUptimeMillis();
            if (mPowerButtonSuppressionDelayMillis > 0
                    && (now < lastWakeUp.wakeTime + mPowerButtonSuppressionDelayMillis)) {
                Slog.i(TAG, "Sleep from power button suppressed. Time since gesture: "
                        + (now - lastWakeUp.wakeTime) + "ms. Gesture: "
                        + PowerManager.wakeReasonToString(lastWakeUp.wakeReason));
                return false;
            }
        }

        goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
        return true;
    }

    private void goToSleep(long eventTime, int reason, int flags) {
        mRequestedOrSleepingDefaultDisplay = true;
        mPowerManager.goToSleep(eventTime, reason, flags);
    }

    private void shortPressPowerGoHome() {
        KeyGestureEvent keyGestureEvent =
                new KeyGestureEvent.Builder()
                        .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
                        .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
                        .setDisplayId(mDefaultDisplay.getDisplayId())
                        .setKeycodes(new int[] {KEYCODE_POWER})
                        .setModifierState(/* metaState= */ 0)
                        .build();
        mInputManagerInternal.handleKeyGestureInKeyGestureController(keyGestureEvent);
        if (isKeyguardShowingAndNotOccluded()) {
            // Notify keyguard so it can do any special handling for the power button since the
            // device will not power off and only launch home.
            mKeyguardDelegate.onShortPowerPressedGoHome();
        }
    }

    private void powerMultiPressAction(long eventTime, boolean interactive, int behavior) {
        switch (behavior) {
            case MULTI_PRESS_POWER_NOTHING:
                break;
            case MULTI_PRESS_POWER_BRIGHTNESS_BOOST:
                Slog.i(TAG, "Starting brightness boost.");
                if (!interactive) {
                    wakeUpFromWakeKey(eventTime, KEYCODE_POWER, /* isDown= */ false);
                }
                mPowerManager.boostScreenBrightness(eventTime);
                break;
            case MULTI_PRESS_POWER_LAUNCH_TARGET_ACTIVITY:
                launchTargetActivityOnMultiPressPower();
                break;
        }
    }

    private void launchTargetActivityOnMultiPressPower() {
        if (DEBUG_INPUT) {
            Slog.d(TAG, "Executing the double press power action.");
        }
        if (mPowerDoublePressTargetActivity != null) {
            Intent intent = new Intent();
            intent.setComponent(mPowerDoublePressTargetActivity);
            ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
                    intent, /* flags= */0);
            if (resolveInfo != null) {
                final boolean keyguardActive =
                        mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
                if (!keyguardActive) {
                    startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
                } else {
                    mKeyguardDelegate.dismissKeyguardToLaunch(intent);
                }
            } else {
                Slog.e(TAG, "Could not resolve activity with : "
                        + mPowerDoublePressTargetActivity.flattenToString()
                        + " name.");
            }
        }
    }

    private int getLidBehavior() {
        return Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.LID_BEHAVIOR, LID_BEHAVIOR_NONE);
    }

    private int getMaxMultiPressPowerCount() {
        // The actual max power button press count is 5
        // (EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD), which is coming from
        // GestureLauncherService.
        // To speed up the handling of single-press of power button inside SingleKeyGestureDetector,
        // however, we limit the max count to the number of button presses actually handled by the
        // SingleKeyGestureDetector except for wearable devices, where we want to de-dup the double
        // press gesture with the emergency gesture.
        if (mHasFeatureWatch
                && GestureLauncherService.isEmergencyGestureSettingEnabled(
                        mContext, ActivityManager.getCurrentUser())) {
            return 5;
        }
        if (mTriplePressOnPowerBehavior != MULTI_PRESS_POWER_NOTHING) {
            return 3;
        }
        if (mDoublePressOnPowerBehavior != MULTI_PRESS_POWER_NOTHING) {
            return 2;
        }
        return 1;
    }

    private void powerLongPress(long eventTime) {
        final int behavior = getResolvedLongPressOnPowerBehavior();
        Slog.d(TAG, "powerLongPress: eventTime=" + eventTime
                + " mLongPressOnPowerBehavior=" + mLongPressOnPowerBehavior);
        switch (behavior) {
            case LONG_PRESS_POWER_NOTHING:
                break;
            case LONG_PRESS_POWER_GLOBAL_ACTIONS:
                mPowerKeyHandled = true;
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON,
                        "Power - Long Press - Global Actions");
                showGlobalActions();
                break;
            case LONG_PRESS_POWER_SHUT_OFF:
            case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
                mPowerKeyHandled = true;
                // don't actually trigger the shutdown if we are running stability
                // tests via monkey
                if (ActivityManager.isUserAMonkey()) {
                    break;
                }
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON,
                        "Power - Long Press - Shut Off");
                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
                mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
                break;
            case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
                mPowerKeyHandled = true;
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON,
                        "Power - Long Press - Go To Voice Assist");
                // Some devices allow the voice assistant intent during setup (and use that intent
                // to launch something else, like Settings). So we explicitly allow that via the
                // config_allowStartActivityForLongPressOnPowerInSetup resource in config.xml.
                launchVoiceAssist(mAllowStartActivityForLongPressOnPowerDuringSetup);
                break;
            case LONG_PRESS_POWER_ASSISTANT:
                mPowerKeyHandled = true;
                if (!enableLppAssistInvocationEffect()) {
                    performHapticFeedback(HapticFeedbackConstants.ASSISTANT_BUTTON,
                            "Power - Long Press - Go To Assistant");
                }
                final int powerKeyDeviceId = INVALID_INPUT_DEVICE_ID;
                launchAssistAction(null, powerKeyDeviceId, eventTime,
                        AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS);
                break;
            case LONG_PRESS_POWER_GO_TO_SLEEP:
                mPowerKeyHandled = true;
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON,
                        "Power - Long Press - Go To Sleep (Doze)");
                sleepDefaultDisplayFromPowerButton(eventTime, 0);
                break;
        }
    }

    private void powerVeryLongPress() {
        Slog.d(TAG, "powerVeryLongPress: "  + mVeryLongPressOnPowerBehavior);
        switch (mVeryLongPressOnPowerBehavior) {
            case VERY_LONG_PRESS_POWER_NOTHING:
                break;
            case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS:
                mPowerKeyHandled = true;
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON,
                        "Power - Very Long Press - Show Global Actions");
                showGlobalActions();
                break;
        }
    }

    private void backLongPress() {
        mBackKeyHandled = true;

        switch (mLongPressOnBackBehavior) {
            case LONG_PRESS_BACK_NOTHING:
                break;
            case LONG_PRESS_BACK_GO_TO_VOICE_ASSIST:
                launchVoiceAssist(false /* allowDuringSetup */);
                break;
        }
    }

    private void sleepPress() {
        if (mShortPressOnSleepBehavior == SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME) {
            launchHomeFromHotKey(DEFAULT_DISPLAY, false /* awakenDreams */,
                    true /*respectKeyguard*/);
        }
    }

    private void sleepRelease(long eventTime) {
        if (mSilenceRingerOnSleepKey) {
            TelecomManager telecomManager = getTelecommService();
            if (telecomManager != null && telecomManager.isRinging()) {
                telecomManager.silenceRinger();
                Slog.i(TAG, "sleepRelease() silence ringer");
                return;
            }
        }

        switch (mShortPressOnSleepBehavior) {
            case SHORT_PRESS_SLEEP_GO_TO_SLEEP:
            case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME:
                Slog.i(TAG, "sleepRelease() calling goToSleep(GO_TO_SLEEP_REASON_SLEEP_BUTTON)");
                goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
                break;
        }
    }

    private int getResolvedLongPressOnPowerBehavior() {
        if (FactoryTest.isLongPressOnPowerOffEnabled()) {
            return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
        }

        // If the config indicates the assistant behavior but the device isn't yet provisioned, show
        // global actions instead.
        if (mLongPressOnPowerBehavior == LONG_PRESS_POWER_ASSISTANT && !isDeviceProvisioned()) {
            return LONG_PRESS_POWER_GLOBAL_ACTIONS;
        }

        // If long press to launch assistant is disabled in settings, do nothing.
        if (mLongPressOnPowerBehavior == LONG_PRESS_POWER_GO_TO_VOICE_ASSIST
                && !isLongPressToAssistantEnabled(mContext)) {
            return LONG_PRESS_POWER_NOTHING;
        }

        return mLongPressOnPowerBehavior;
    }

    private void stemPrimaryPress(int count) {
        Slog.d(TAG, "stemPrimaryPress: " + count);
        if (count == 3) {
            stemPrimaryTriplePressAction(mTriplePressOnStemPrimaryBehavior);
        } else if (count == 2) {
            stemPrimaryDoublePressAction(mDoublePressOnStemPrimaryBehavior);
        } else if (count == 1) {
            stemPrimarySinglePressAction(mShortPressOnStemPrimaryBehavior);
        }
    }

    private void stemPrimarySinglePressAction(int behavior) {
        Slog.d(TAG, "stemPrimarySinglePressAction: behavior=" + behavior);
        if (behavior == SHORT_PRESS_PRIMARY_NOTHING) return;

        final boolean keyguardActive = mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
        if (keyguardActive) {
            // If keyguarded then notify the keyguard.
            mKeyguardDelegate.onSystemKeyPressed(KeyEvent.KEYCODE_STEM_PRIMARY);
            Slog.d(TAG, "stemPrimarySinglePressAction: skip due to keyguard");
            return;
        }
        switch (behavior) {
            case SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS:
                Intent allAppsIntent = new Intent(Intent.ACTION_ALL_APPS);
                allAppsIntent.addFlags(
                        Intent.FLAG_ACTIVITY_NEW_TASK
                                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
                startActivityAsUser(allAppsIntent, UserHandle.CURRENT_OR_SELF);
                break;
            case SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY:
                if (mPrimaryShortPressTargetActivity != null) {
                    Intent targetActivityIntent = new Intent();
                    targetActivityIntent.setComponent(mPrimaryShortPressTargetActivity);
                    ResolveInfo resolveInfo =
                            mContext.getPackageManager()
                                    .resolveActivity(targetActivityIntent, /* flags= */ 0);
                    if (resolveInfo != null) {
                        targetActivityIntent.addFlags(
                                Intent.FLAG_ACTIVITY_NEW_TASK
                                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
                                        | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
                        startActivityAsUser(targetActivityIntent, UserHandle.CURRENT_OR_SELF);
                    } else {
                        Slog.wtf(
                                TAG,
                                "Could not resolve activity with : "
                                        + mPrimaryShortPressTargetActivity.flattenToString()
                                        + " name.");
                    }
                } else {
                    Slog.wtf(
                            TAG,
                            "mPrimaryShortPressTargetActivity must not be null and correctly"
                                + " specified");
                }
                break;
        }
    }

    private void stemPrimaryDoublePressAction(int behavior) {
        Slog.d(TAG, "stemPrimaryDoublePressAction: " + behavior);
        switch (behavior) {
            case DOUBLE_PRESS_PRIMARY_NOTHING:
                break;
            case DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP:
                final boolean keyguardActive = mKeyguardDelegate == null
                        ? false
                        : mKeyguardDelegate.isShowing();
                if (!keyguardActive) {
                    performStemPrimaryDoublePressSwitchToRecentTask();
                }
                break;
            case DOUBLE_PRESS_PRIMARY_LAUNCH_DEFAULT_FITNESS_APP:
                mInputManagerInternal.handleKeyGestureInKeyGestureController(
                        new KeyGestureEvent.Builder()
                                .setKeyGestureType(
                                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS)
                                .setDeviceId(INVALID_INPUT_DEVICE_ID)
                                .setKeycodes(new int[]{KEYCODE_STEM_PRIMARY})
                                .setModifierState(/* metaState= */0)
                                .build());
                break;
        }
    }

    private void stemPrimaryTriplePressAction(int behavior) {
        Slog.d(TAG, "stemPrimaryTriplePressAction: " + behavior);
        switch (behavior) {
            case TRIPLE_PRESS_PRIMARY_NOTHING:
                break;
            case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY:
                mTalkbackShortcutController.toggleTalkback(mCurrentUserId);
                if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) {
                    performHapticFeedback(HapticFeedbackConstants.CONFIRM,
                            "Stem primary - Triple Press - Toggle Accessibility");
                }
                break;
        }
    }

    private void stemPrimaryLongPress(long eventTime) {
        Slog.d(TAG, "stemPrimaryLongPress: "  + mLongPressOnStemPrimaryBehavior);

        switch (mLongPressOnStemPrimaryBehavior) {
            case LONG_PRESS_PRIMARY_NOTHING:
                break;
            case LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT:
                final int stemPrimaryKeyDeviceId = INVALID_INPUT_DEVICE_ID;
                launchAssistAction(
                        null,
                        stemPrimaryKeyDeviceId,
                        eventTime,
                        AssistUtils.INVOCATION_TYPE_UNKNOWN);
                break;
        }
    }

    /**
     * Load most recent task (expect current task) and bring it to the front.
     */
    void performStemPrimaryDoublePressSwitchToRecentTask() {
        RecentTaskInfo targetTask = mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp;
        if (targetTask == null) {
            if (DEBUG_INPUT) {
                Slog.w(TAG, "No recent task available! Show wallpaper.");
            }
            goHome();
            return;
        }

        if (DEBUG_INPUT) {
            Slog.d(
                    TAG,
                    "Starting task from recents. id="
                            + targetTask.id
                            + ", persistentId="
                            + targetTask.persistentId
                            + ", topActivity="
                            + targetTask.topActivity
                            + ", baseIntent="
                            + targetTask.baseIntent);
        }
        try {
            mActivityManagerService.startActivityFromRecents(targetTask.persistentId, null);
        } catch (RemoteException | IllegalArgumentException e) {
            Slog.e(TAG, "Failed to start task " + targetTask.persistentId + " from recents", e);
        }
    }

    private int getMaxMultiPressStemPrimaryCount() {
        switch (mTriplePressOnStemPrimaryBehavior) {
            case TRIPLE_PRESS_PRIMARY_NOTHING:
                break;
            case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY:
                if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) {
                    return 3;
                }
                break;
        }
        if (mDoublePressOnStemPrimaryBehavior != DOUBLE_PRESS_PRIMARY_NOTHING) {
            return 2;
        }
        return 1;
    }

    private boolean hasLongPressOnPowerBehavior() {
        return getResolvedLongPressOnPowerBehavior() != LONG_PRESS_POWER_NOTHING;
    }

    private boolean hasVeryLongPressOnPowerBehavior() {
        return mVeryLongPressOnPowerBehavior != VERY_LONG_PRESS_POWER_NOTHING;
    }

    private boolean hasLongPressOnBackBehavior() {
        return mLongPressOnBackBehavior != LONG_PRESS_BACK_NOTHING;
    }

    private boolean hasLongPressOnStemPrimaryBehavior() {
        return mLongPressOnStemPrimaryBehavior != LONG_PRESS_PRIMARY_NOTHING;
    }

    /** Determine whether the device has any stem primary behaviors. */
    private boolean hasStemPrimaryBehavior() {
        // Read the default stem behaviors from the XML config to determine whether stem primary
        // behaviors are supported in this build. If they are supported, then the behaviors may be
        // overridden at runtime through their respective Settings overrides. If they are not
        // supported, the Settings overrides will not apply.
        final int defaultShortPressOnStemPrimaryBehavior = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior);
        final int defaultLongPressOnStemPrimaryBehavior = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_longPressOnStemPrimaryBehavior);
        return getMaxMultiPressStemPrimaryCount() > 1
                || defaultLongPressOnStemPrimaryBehavior != LONG_PRESS_PRIMARY_NOTHING
                || defaultShortPressOnStemPrimaryBehavior != SHORT_PRESS_PRIMARY_NOTHING;
    }

    private void interceptRingerToggleChord() {
        mHandler.removeMessages(MSG_RINGER_TOGGLE_CHORD);
        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RINGER_TOGGLE_CHORD),
                getRingerToggleChordDelay());
    }

    private long getRingerToggleChordDelay() {
        // Always timeout like a tap
        return ViewConfiguration.getTapTimeout();
    }

    private void cancelPendingRingerToggleChordAction() {
        mHandler.removeMessages(MSG_RINGER_TOGGLE_CHORD);
    }

    private final Runnable mEndCallLongPress = new Runnable() {
        @Override
        public void run() {
            mEndCallKeyHandled = true;
            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
                    "End Call - Long Press - Show Global Actions");
            showGlobalActionsInternal();
        }
    };

    @Override
    public void showGlobalActions() {
        mHandler.removeMessages(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS);
        mHandler.sendEmptyMessage(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS);
    }

    void showGlobalActionsInternal() {

        boolean securelineLocked = false;

        try {

            com.android.secureline.ISecureLineManager svc =
                    com.android.secureline.ISecureLineManager.Stub.asInterface(
                            android.os.ServiceManager.getService("secureline"));

            if (svc != null) {

                android.app.KeyguardManager km =
                        (android.app.KeyguardManager)
                                mContext.getSystemService(Context.KEYGUARD_SERVICE);

                if (km != null && km.isDeviceLocked()) {
                    securelineLocked = true;
                }
            }

        } catch (Throwable ignored) {}

        // Block power menu if device locked
        if (securelineLocked) {

            Slog.i(TAG, "SecureLine: Power menu blocked while device locked");

            return;
        }

        if (mGlobalActions == null) {
            mGlobalActions = mGlobalActionsFactory.get();
        }

        final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();

        mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());

        mPowerManager.userActivity(mInjector.getUptimeMillis(), false);
    }

    private void cancelGlobalActionsAction() {
        mHandler.removeMessages(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS);
    }

    boolean isDeviceProvisioned() {
        return Settings.Global.getInt(
                mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0;
    }

    @Override
    public boolean isUserSetupComplete() {
        boolean isSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
        if (mHasFeatureLeanback) {
            isSetupComplete &= isTvUserSetupComplete();
        } else if (mHasFeatureAuto) {
            isSetupComplete &= isAutoUserSetupComplete();
        }
        return isSetupComplete;
    }

    private boolean isAutoUserSetupComplete() {
        return Settings.Secure.getIntForUser(mContext.getContentResolver(),
                "android.car.SETUP_WIZARD_IN_PROGRESS", 0, UserHandle.USER_CURRENT) == 0;
    }

    private boolean isTvUserSetupComplete() {
        return Settings.Secure.getIntForUser(mContext.getContentResolver(),
                Settings.Secure.TV_USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
    }

    private void handleShortPressOnHome(int displayId) {
        // Turn on the connected TV and switch HDMI input if we're a HDMI playback device.
        final HdmiControl hdmiControl = getHdmiControl();
        if (hdmiControl != null) {
            hdmiControl.turnOnTv();
        }

        // If there's a dream running then use home to escape the dream
        // but don't actually go home.
        final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal();
        if (dreamManagerInternal != null && dreamManagerInternal.isDreaming()) {
            dreamManagerInternal.stopDream(false /*immediate*/, "short press on home" /*reason*/);
            if (mHasFeatureLeanback) {
                if (localLOGV) Log.v(TAG, "TV will launch home after stopping dream");
            } else {
                return;
            }

        }

        // Go home!
        launchHomeFromHotKey(displayId);
    }

    /**
     * Creates an accessor to HDMI control service that performs the operation of
     * turning on TV (optional) and switching input to us. If HDMI control service
     * is not available or we're not a HDMI playback device, the operation is no-op.
     * @return {@link HdmiControl} instance if available, null otherwise.
     */
    private HdmiControl getHdmiControl() {
        if (null == mHdmiControl) {
            if (!mHasFeatureHdmiCec) {
                return null;
            }
            HdmiControlManager manager = (HdmiControlManager) mContext.getSystemService(
                        Context.HDMI_CONTROL_SERVICE);
            HdmiPlaybackClient client = null;
            if (manager != null) {
                client = manager.getPlaybackClient();
            }
            mHdmiControl = new HdmiControl(client);
        }
        return mHdmiControl;
    }

    private static class HdmiControl {
        private final HdmiPlaybackClient mClient;

        private HdmiControl(HdmiPlaybackClient client) {
            mClient = client;
        }

        public void turnOnTv() {
            if (mClient == null) {
                return;
            }
            mClient.oneTouchPlay(new OneTouchPlayCallback() {
                @Override
                public void onComplete(int result) {
                    if (result != HdmiControlManager.RESULT_SUCCESS) {
                        Log.w(TAG, "One touch play failed: " + result);
                    }
                }
            });
        }
    }

    private void launchAllAppsAction() {
        if (mHasFeatureLeanback || mHasFeatureWatch) {
            // TV and watch support the all apps intent
            Intent intent = new Intent(Intent.ACTION_ALL_APPS);
            if (mHasFeatureLeanback) {
                Intent intentLauncher = new Intent(Intent.ACTION_MAIN);
                intentLauncher.addCategory(Intent.CATEGORY_HOME);
                ResolveInfo resolveInfo = mPackageManager.resolveActivityAsUser(intentLauncher,
                        PackageManager.MATCH_SYSTEM_ONLY,
                        mCurrentUserId);
                if (resolveInfo != null) {
                    intent.setPackage(resolveInfo.activityInfo.packageName);
                }
            }
            startActivityAsUser(intent, UserHandle.CURRENT);
        } else {
            AccessibilityManagerInternal accessibilityManager = getAccessibilityManagerInternal();
            if (accessibilityManager != null) {
                accessibilityManager.performSystemAction(
                        AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
            }
        }
        dismissKeyboardShortcutsMenu();
    }

    private void toggleNotificationPanel() {
        IStatusBarService statusBarService = getStatusBarService();
        if (isUserSetupComplete() && statusBarService != null) {
            try {
                statusBarService.togglePanel();
            } catch (RemoteException e) {
                // do nothing.
            }
        }
    }

    private void showSystemSettings(int displayId) {
        startActivityAsUser(new Intent(android.provider.Settings.ACTION_SETTINGS),
                UserHandle.CURRENT_OR_SELF, displayId);
    }

    private void showPictureInPictureMenu(KeyEvent event) {
        if (DEBUG_INPUT) Log.d(TAG, "showPictureInPictureMenu event=" + event);
        mHandler.removeMessages(MSG_SHOW_PICTURE_IN_PICTURE_MENU);
        Message msg = mHandler.obtainMessage(MSG_SHOW_PICTURE_IN_PICTURE_MENU);
        msg.setAsynchronous(true);
        msg.sendToTarget();
    }

    private void showPictureInPictureMenuInternal() {
        StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
        if (statusbar != null) {
            statusbar.showPictureInPictureMenu();
        }
    }

    /** A handler to handle home keys per display */
    private class DisplayHomeButtonHandler {

        private final int mDisplayId;
        private boolean mHomePressed;
        private boolean mHomeConsumed;
        private KeyEvent mPendingHomeKeyEvent;
        private final Runnable mHomeDoubleTapTimeoutRunnable = new Runnable() {
            @Override
            public void run() {
                if (mPendingHomeKeyEvent != null) {
                    notifyKeyGestureCompleted(mPendingHomeKeyEvent,
                            KeyGestureEvent.KEY_GESTURE_TYPE_HOME);
                    handleShortPressOnHome(mPendingHomeKeyEvent.getDisplayId());
                    mPendingHomeKeyEvent = null;
                }
            }
        };

        DisplayHomeButtonHandler(int displayId) {
            mDisplayId = displayId;
        }

        boolean handleHomeButton(IBinder focusedToken, KeyEvent event) {
            final boolean keyguardOn = keyguardOn();
            final int repeatCount = event.getRepeatCount();
            final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
            final boolean canceled = event.isCanceled();

            if (DEBUG_INPUT) {
                Log.d(TAG, String.format("handleHomeButton in display#%d mHomePressed = %b",
                        mDisplayId, mHomePressed));
            }

            // If we have released the home key, and didn't do anything else
            // while it was pressed, then it is time to go home!
            if (!down) {
                if (mDisplayId == DEFAULT_DISPLAY) {
                    cancelPreloadRecentApps();
                }

                mHomePressed = false;
                if (mHomeConsumed) {
                    mHomeConsumed = false;
                    return true;
                }

                if (canceled) {
                    Log.i(TAG, "Ignoring HOME; event canceled.");
                    return true;
                }

                // Delay handling home if a double-tap is possible.
                if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
                    // For the picture-in-picture menu, only add the delay if a pip is there.
                    if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_PIP_MENU
                            || mPictureInPictureVisible) {
                        mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
                        mPendingHomeKeyEvent = event;
                        mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
                                ViewConfiguration.getDoubleTapTimeout());
                        return true;
                    }
                }

                // Post to main thread to avoid blocking input pipeline.
                mHandler.post(() -> {
                    notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_HOME);
                    handleShortPressOnHome(event.getDisplayId());
                });
                return true;
            }

            final KeyInterceptionInfo info =
                    mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
            if (info != null) {
                // If a system window has focus, then it doesn't make sense
                // right now to interact with applications.
                if (info.layoutParamsType == TYPE_KEYGUARD_DIALOG
                        || (info.layoutParamsType == TYPE_NOTIFICATION_SHADE
                        && isKeyguardShowing())) {
                    // the "app" is keyguard, so give it the key
                    return false;
                }
                for (int t : WINDOW_TYPES_WHERE_HOME_DOESNT_WORK) {
                    if (info.layoutParamsType == t) {
                        // don't do anything, but also don't pass it to the app
                        return true;
                    }
                }
            }

            // Remember that home is pressed and handle special actions.
            if (repeatCount == 0) {
                mHomePressed = true;
                if (mPendingHomeKeyEvent != null) {
                    mPendingHomeKeyEvent = null;
                    mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
                    mHandler.post(() -> handleDoubleTapOnHome(event));
                // TODO(multi-display): Remove display id check once we support recents on
                // multi-display
                } else if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI
                        && mDisplayId == DEFAULT_DISPLAY) {
                    preloadRecentApps();
                }
            } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
                if (!keyguardOn) {
                    // Post to main thread to avoid blocking input pipeline.
                    mHandler.post(() -> handleLongPressOnHome(event));
                }
            }
            return true;
        }

        private void handleDoubleTapOnHome(KeyEvent event) {
            if (mHomeConsumed) {
                return;
            }
            switch (mDoubleTapOnHomeBehavior) {
                case DOUBLE_TAP_HOME_RECENT_SYSTEM_UI:
                    if (!isKeyEventForCurrentUser(
                            event.getDisplayId(), event.getKeyCode(), "toggleRecentApps")) {
                        break;
                    }
                    notifyKeyGestureCompleted(event,
                            KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH);
                    mHomeConsumed = true;
                    toggleRecentApps();
                    break;
                case DOUBLE_TAP_HOME_PIP_MENU:
                    if (!isKeyEventForCurrentUser(
                            event.getDisplayId(), event.getKeyCode(),
                            "showPictureInPictureMenu")) {
                        break;
                    }
                    mHomeConsumed = true;
                    showPictureInPictureMenuInternal();
                    break;
                default:
                    Log.w(TAG, "No action or undefined behavior for double tap home: "
                            + mDoubleTapOnHomeBehavior);
                    break;
            }
        }

        private void handleLongPressOnHome(KeyEvent event) {
            if (mHomeConsumed) {
                return;
            }
            if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_NOTHING) {
                return;
            }
            mHomeConsumed = true;
            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, "Home - Long Press");
            switch (mLongPressOnHomeBehavior) {
                case LONG_PRESS_HOME_ALL_APPS:
                    notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
                    if (isKeyEventForCurrentUser(event.getDisplayId(), event.getKeyCode(),
                            "launchAllAppsViaA11y")) {
                        launchAllAppsAction();
                    }
                    break;
                case LONG_PRESS_HOME_ASSIST:
                    if (!isKeyEventForCurrentUser(
                            event.getDisplayId(), event.getKeyCode(), "launchAssistAction")) {
                        break;
                    }
                    notifyKeyGestureCompleted(event,
                            KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT);
                    launchAssistAction(null, event.getDeviceId(), event.getDisplayId(),
                            event.getEventTime(),
                            AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
                    break;
                case LONG_PRESS_HOME_NOTIFICATION_PANEL:
                    if (!isKeyEventForCurrentUser(
                            event.getDisplayId(), event.getKeyCode(), "toggleNotificationPanel")) {
                        break;
                    }
                    notifyKeyGestureCompleted(event,
                            KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL);
                    toggleNotificationPanel();
                    break;
                default:
                    Log.w(TAG, "Undefined long press on home behavior: "
                            + mLongPressOnHomeBehavior);
                    break;
            }
        }

        @Override
        public String toString() {
            return String.format("mDisplayId = %d, mHomePressed = %b", mDisplayId, mHomePressed);
        }
    }

    /** A DisplayHomeButtonHandler map indexed by display id */
    private final SparseArray<DisplayHomeButtonHandler> mDisplayHomeButtonHandlers =
            new SparseArray<>();

    private boolean isRoundWindow() {
        return mContext.getResources().getConfiguration().isScreenRound();
    }

    @Override
    public void setDefaultDisplay(DisplayContentInfo displayContentInfo) {
        mDefaultDisplay = displayContentInfo.getDisplay();
        mDefaultDisplayRotation = displayContentInfo.getDisplayRotation();
        mDefaultDisplayPolicy = mDefaultDisplayRotation.getDisplayPolicy();
    }

    /** Point of injection for test dependencies. */
    @VisibleForTesting
    static class Injector {
        private final Context mContext;
        private final WindowManagerFuncs mWindowManagerFuncs;

        Injector(Context context, WindowManagerFuncs funcs) {
            mContext = context;
            mWindowManagerFuncs = funcs;
        }

        Context getContext() {
            return mContext;
        }

        WindowManagerFuncs getWindowManagerFuncs() {
            return mWindowManagerFuncs;
        }

        Looper getLooper() {
            return Looper.myLooper();
        }

        Supplier<GlobalActions> getGlobalActionsFactory() {
            return () -> new GlobalActions(mContext, mWindowManagerFuncs);
        }

        KeyguardServiceDelegate getKeyguardServiceDelegate() {
            return new KeyguardServiceDelegate(mContext,
                    new StateCallback() {
                        @Override
                        public void onTrustedChanged() {
                            mWindowManagerFuncs.notifyKeyguardTrustedChanged();
                        }

                        @Override
                        public void onShowingChanged() {
                            mWindowManagerFuncs.onKeyguardShowingAndNotOccludedChanged();
                        }
                    });
        }

        IActivityManager getActivityManagerService() {
            return ActivityManager.getService();
        }

        LockPatternUtils getLockPatternUtils() {
            return new LockPatternUtils(mContext);
        }

        ButtonOverridePermissionChecker getButtonOverridePermissionChecker() {
            return new ButtonOverridePermissionChecker();
        }

        TalkbackShortcutController getTalkbackShortcutController() {
            return new TalkbackShortcutController(mContext);
        }

        WindowWakeUpPolicy getWindowWakeUpPolicy() {
            return new WindowWakeUpPolicy(mContext);
        }

        long getUptimeMillis() {
            return SystemClock.uptimeMillis();
        }
    }

    /** {@inheritDoc} */
    @Override
    public void init(Context context, WindowManagerFuncs funcs) {
        init(new Injector(context, funcs));
    }

    @VisibleForTesting
    void init(Injector injector) {
        mInjector = injector;
        mContext = injector.getContext();
        mWindowManagerFuncs = injector.getWindowManagerFuncs();
        mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
        mActivityManagerService = injector.getActivityManagerService();
        mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
        mInputManager = mContext.getSystemService(InputManager.class);
        mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
        mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
        mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
        mSensorPrivacyManager = mContext.getSystemService(SensorPrivacyManager.class);
        mDisplayManager = mContext.getSystemService(DisplayManager.class);
        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
        mPackageManager = mContext.getPackageManager();
        mHasFeatureWatch = mPackageManager.hasSystemFeature(FEATURE_WATCH);
        mHasFeatureLeanback = mPackageManager.hasSystemFeature(FEATURE_LEANBACK);
        mHasFeatureAuto = mPackageManager.hasSystemFeature(FEATURE_AUTOMOTIVE);
        mHasFeatureHdmiCec = mPackageManager.hasSystemFeature(FEATURE_HDMI_CEC);
        mGlobalActionsFactory = injector.getGlobalActionsFactory();
        mLockPatternUtils = injector.getLockPatternUtils();
        mLogger = new MetricsLogger();

        Resources res = mContext.getResources();
        mWakeOnDpadKeyPress =
                res.getBoolean(com.android.internal.R.bool.config_wakeOnDpadKeyPress);
        mWakeOnAssistKeyPress =
                res.getBoolean(com.android.internal.R.bool.config_wakeOnAssistKeyPress);
        mWakeOnBackKeyPress =
                res.getBoolean(com.android.internal.R.bool.config_wakeOnBackKeyPress);

        // Init display burn-in protection
        boolean burnInProtectionEnabled = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_enableBurnInProtection);
        // Allow a system property to override this. Used by developer settings.
        boolean burnInProtectionDevMode =
                SystemProperties.getBoolean("persist.debug.force_burn_in", false);
        if (burnInProtectionEnabled || burnInProtectionDevMode) {
            final int minHorizontal;
            final int maxHorizontal;
            final int minVertical;
            final int maxVertical;
            final int maxRadius;
            if (burnInProtectionDevMode) {
                minHorizontal = -8;
                maxHorizontal = 8;
                minVertical = -8;
                maxVertical = -4;
                maxRadius = (isRoundWindow()) ? 6 : -1;
            } else {
                Resources resources = mContext.getResources();
                minHorizontal = resources.getInteger(
                        com.android.internal.R.integer.config_burnInProtectionMinHorizontalOffset);
                maxHorizontal = resources.getInteger(
                        com.android.internal.R.integer.config_burnInProtectionMaxHorizontalOffset);
                minVertical = resources.getInteger(
                        com.android.internal.R.integer.config_burnInProtectionMinVerticalOffset);
                maxVertical = resources.getInteger(
                        com.android.internal.R.integer.config_burnInProtectionMaxVerticalOffset);
                maxRadius = resources.getInteger(
                        com.android.internal.R.integer.config_burnInProtectionMaxRadius);
            }
            mBurnInProtectionHelper = new BurnInProtectionHelper(
                    mContext, minHorizontal, maxHorizontal, minVertical, maxVertical, maxRadius);
        }

        mHandler = new PolicyHandler(injector.getLooper());
        mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
        mSettingsObserver = new SettingsObserver(mHandler);
        mSettingsObserver.observe();
        mModifierShortcutManager = new ModifierShortcutManager(mContext,
                UserHandle.of(mCurrentUserId));
        mUiMode = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_defaultUiModeType);
        mHomeIntent =  new Intent(Intent.ACTION_MAIN, null);
        mHomeIntent.addCategory(Intent.CATEGORY_HOME);
        mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
        mEnableCarDockHomeCapture = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_enableCarDockHomeLaunch);
        mCarDockIntent =  new Intent(Intent.ACTION_MAIN, null);
        mCarDockIntent.addCategory(Intent.CATEGORY_CAR_DOCK);
        mCarDockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
        mDeskDockIntent =  new Intent(Intent.ACTION_MAIN, null);
        mDeskDockIntent.addCategory(Intent.CATEGORY_DESK_DOCK);
        mDeskDockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
        mVrHeadsetHomeIntent =  new Intent(Intent.ACTION_MAIN, null);
        mVrHeadsetHomeIntent.addCategory(Intent.CATEGORY_VR_HOME);
        mVrHeadsetHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);

        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        mBroadcastWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                "PhoneWindowManager.mBroadcastWakeLock");
        mPowerKeyWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                "PhoneWindowManager.mPowerKeyWakeLock");
        mLidKeyboardAccessibility = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_lidKeyboardAccessibility);
        mLidNavigationAccessibility = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_lidNavigationAccessibility);

        mSupportLongPressPowerWhenNonInteractive = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_supportLongPressPowerWhenNonInteractive);
        mSupportShortPressPowerWhenDefaultDisplayOn =
                mContext.getResources()
                        .getBoolean(
                                com.android.internal.R.bool
                                        .config_supportShortPressPowerWhenDefaultDisplayOn);

        mLongPressOnBackBehavior = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_longPressOnBackBehavior);

        mLongPressOnPowerBehavior = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_longPressOnPowerBehavior);
        mLongPressOnPowerAssistantTimeoutMs = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_longPressOnPowerDurationMs);
        mVeryLongPressOnPowerBehavior = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_veryLongPressOnPowerBehavior);
        mPowerDoublePressTargetActivity = ComponentName.unflattenFromString(
            mContext.getResources().getString(
                com.android.internal.R.string.config_doublePressOnPowerTargetActivity));
        mPrimaryShortPressTargetActivity = ComponentName.unflattenFromString(
            mContext.getResources().getString(
                com.android.internal.R.string.config_primaryShortPressTargetActivity));
        mShortPressOnSleepBehavior = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_shortPressOnSleepBehavior);
        mSilenceRingerOnSleepKey = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_silenceRingerOnSleepKey);
        mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup);

        mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION;

        mHandleVolumeKeysInWM = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_handleVolumeKeysInWindowManager);

        mWakeUpToLastStateTimeout = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_wakeUpToLastStateTimeoutMillis);

        mSearchKeyTargetActivity = ComponentName.unflattenFromString(
            mContext.getResources().getString(
                com.android.internal.R.string.config_searchKeyTargetActivity));
        readConfigurationDependentBehaviors();

        mTurnOffTvToastSuppressionDelay = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_turnOffTvToastSuppressionDelayMs);
        mTurnOffTvToastPostDelay = mContext.getResources().getInteger(
                    com.android.internal.R.integer.config_turnOffTvToastPostDelayMs);

        mDisplayFoldController = DisplayFoldController.create(mContext, DEFAULT_DISPLAY);

        mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);

        // register for dock events
        IntentFilter filter = new IntentFilter();
        filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
        filter.addAction(UiModeManager.ACTION_EXIT_CAR_MODE);
        filter.addAction(UiModeManager.ACTION_ENTER_DESK_MODE);
        filter.addAction(UiModeManager.ACTION_EXIT_DESK_MODE);
        filter.addAction(Intent.ACTION_DOCK_EVENT);
        mContext.registerReceiver(mDockReceiver, filter);

        // register for multiuser-relevant broadcasts
        filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
        mContext.registerReceiver(mMultiuserReceiver, filter);

        // register for Bluetooth HID profile broadcasts.
        filter = new IntentFilter(ACTION_CONNECTION_STATE_CHANGED);
        mContext.registerReceiver(mBluetoothHidReceiver, filter);

        mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);

        mGlobalKeyManager = new GlobalKeyManager(mContext);

        // Controls rotation and the like.
        initializeHdmiState();

        // Match current screen state.
        if (!mPowerManager.isInteractive()) {
            startedGoingToSleep(Display.DEFAULT_DISPLAY_GROUP,
                    PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
            finishedGoingToSleep(Display.DEFAULT_DISPLAY_GROUP,
                    PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
        }

        final var transitionListener = new AppTransitionListener(DEFAULT_DISPLAY) {
            @Override
            public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
                    long statusBarAnimationDuration) {
                return handleTransitionForKeyguardLw(false /* startKeyguardExitAnimation */,
                        false /* notifyOccluded */);
            }

            @Override
            public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled) {
                // When KEYGUARD_GOING_AWAY app transition is canceled, we need to trigger relevant
                // IKeyguardService calls to sync keyguard status in WindowManagerService and SysUI.
                handleTransitionForKeyguardLw(
                        keyguardGoingAwayCancelled /* startKeyguardExitAnimation */,
                        true /* notifyOccluded */);

                synchronized (mLock) {
                    mLockAfterDreamingTransitionFinished = false;
                }
            }

            @Override
            public void onAppTransitionFinishedLocked(IBinder token) {
                synchronized (mLock) {
                    final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal();
                    // check both isDreaming and mLockAfterDreamingTransitionFinished before lockNow
                    // so it won't relock after dreaming has stopped
                    if (dreamManagerInternal != null && dreamManagerInternal.isDreaming()
                            && mLockAfterDreamingTransitionFinished) {
                        lockNow(null);
                    }
                    mLockAfterDreamingTransitionFinished = false;
                }
            }
        };
        mWindowManagerInternal.registerAppTransitionListener(transitionListener);

        mKeyguardDrawnTimeout = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_keyguardDrawnTimeout);
        mKeyguardDelegate = injector.getKeyguardServiceDelegate();
        mTalkbackShortcutController = injector.getTalkbackShortcutController();
        mWindowWakeUpPolicy = injector.getWindowWakeUpPolicy();
        initSingleKeyGestureRules(injector.getLooper());
        initKeyGestures();
        mButtonOverridePermissionChecker = injector.getButtonOverridePermissionChecker();
        mSideFpsEventHandler = new SideFpsEventHandler(mContext, mHandler, mPowerManager);
    }

    /**
     * Rule for single power key gesture.
     */
    private final class PowerKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
        PowerKeyRule() {
            super(KEYCODE_POWER);
        }

        @Override
        boolean supportLongPress() {
            return hasLongPressOnPowerBehavior();
        }

        @Override
        boolean supportVeryLongPress() {
            return hasVeryLongPressOnPowerBehavior();
        }


        @Override
        int getMaxMultiPressCount() {
            return getMaxMultiPressPowerCount();
        }

        @Override
        void onKeyGesture(@NonNull SingleKeyGestureEvent event) {
            final long startTime = event.getStartTime();
            final int displayId = event.getDisplayId();
            final int pressCount = event.getPressCount();
            final int action = event.getAction();
            switch (event.getType()) {
                case SINGLE_KEY_GESTURE_TYPE_PRESS:
                    if (action != ACTION_COMPLETE) {
                        return;
                    }
                    if (event.getPressCount() > 1) {
                        onMultiPress(startTime, pressCount, displayId);
                    } else {
                        onPress(startTime, displayId);
                    }
                    break;
                case SINGLE_KEY_GESTURE_TYPE_LONG_PRESS:
                    onLongPress(event);
                    break;
                case SINGLE_KEY_GESTURE_TYPE_VERY_LONG_PRESS:
                    if (action != ACTION_COMPLETE) {
                        return;
                    }
                    onVeryLongPress();
                    break;
            }
        }

        private void onPress(long downTime, int displayId) {
            if (mShouldEarlyShortPressOnPower) {
                return;
            }
            powerPress(downTime, 1 /*count*/, displayId);
        }

        @Override
        long getLongPressTimeoutMs() {
            if (getResolvedLongPressOnPowerBehavior() == LONG_PRESS_POWER_ASSISTANT) {
                return mLongPressOnPowerAssistantTimeoutMs;
            } else {
                return super.getLongPressTimeoutMs();
            }
        }

        private void onLongPress(@NonNull SingleKeyGestureEvent event) {
            if (mSingleKeyGestureDetector.beganFromNonInteractive()
                    && !mSupportLongPressPowerWhenNonInteractive) {
                Slog.v(TAG, "Not support long press power when device is not interactive.");
                return;
            }
            // If Assistant mapped to long press, we send start, complete and cancel gesture
            // This is done to allow Assistant launch animation in SysUI. Will extend
            // this to all single key gestures after moving Single key gestures to
            // KeyGestureController.
            if (enableLppAssistInvocationEffect()) {
                if (getResolvedLongPressOnPowerBehavior() == LONG_PRESS_POWER_ASSISTANT) {
                    handleSingleKeyGestureInKeyGestureController(
                            KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT, event);
                    if (!enableLppAssistInvocationHapticEffect()
                            && event.getAction() == ACTION_COMPLETE) {
                        // The invocation effect will not play haptics so we must play the
                        // assistant effect here
                        performHapticFeedback(HapticFeedbackConstants.ASSISTANT_BUTTON,
                                "Power - Long Press - Go To Assistant");
                    }
                    return;
                }
            }
            if (event.getAction() == ACTION_COMPLETE) {
                powerLongPress(event.getStartTime());
            }
        }

        private void onVeryLongPress() {
            mActivityManagerInternal.prepareForPossibleShutdown();
            powerVeryLongPress();
        }

        private void onMultiPress(long downTime, int count, int displayId) {
            powerPress(downTime, count, displayId);
        }

        @Override
        void onKeyUp(int count, KeyEvent event) {
            if (mShouldEarlyShortPressOnPower && count == 1) {
                powerPress(event.getDownTime(), 1 /*pressCount*/, event.getDisplayId());
            }
        }
    }

    /**
     * Rule for single back key gesture.
     */
    private final class BackKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
        BackKeyRule() {
            super(KEYCODE_BACK);
        }

        @Override
        boolean supportLongPress() {
            return hasLongPressOnBackBehavior();
        }

        @Override
        void onKeyGesture(@NonNull SingleKeyGestureEvent event) {
            if (event.getAction() != ACTION_COMPLETE) {
                return;
            }
            switch (event.getType()) {
                case SINGLE_KEY_GESTURE_TYPE_PRESS:
                    if (event.getPressCount() == 1) {
                        mBackKeyHandled |= backKeyPress();
                    }
                    break;
                case SINGLE_KEY_GESTURE_TYPE_LONG_PRESS:
                    backLongPress();
                    break;
            }
        }
    }

    /**
     * Rule for single stem primary key gesture.
     */
    private final class StemPrimaryKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
        StemPrimaryKeyRule() {
            super(KeyEvent.KEYCODE_STEM_PRIMARY);
        }

        @Override
        boolean supportLongPress() {
            return hasLongPressOnStemPrimaryBehavior();
        }

        @Override
        int getMaxMultiPressCount() {
            return getMaxMultiPressStemPrimaryCount();
        }

        @Override
        void onKeyGesture(@NonNull SingleKeyGestureEvent event) {
            final long startTime = event.getStartTime();
            final int pressCount = event.getPressCount();
            if (event.getAction() != ACTION_COMPLETE) {
                return;
            }
            switch (event.getType()) {
                case SINGLE_KEY_GESTURE_TYPE_PRESS:
                    if (event.getPressCount() > 1) {
                        onMultiPress(startTime, pressCount);
                    } else {
                        onPress(startTime);
                    }
                    break;
                case SINGLE_KEY_GESTURE_TYPE_LONG_PRESS:
                    onLongPress(startTime);
                    break;
            }
        }

        private void onPress(long downTime) {
            if (shouldHandleStemPrimaryEarlyShortPress()) {
                return;
            }
            // Short-press should be triggered only if app doesn't handle it.
            mDeferredKeyActionExecutor.queueKeyAction(
                    KeyEvent.KEYCODE_STEM_PRIMARY, downTime, () -> stemPrimaryPress(1 /*count*/));
        }

        private void onLongPress(long downTime) {
            if (mLongPressOnStemPrimaryBehavior == LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT) {
                // Long-press to assistant gesture is not overridable by apps.
                stemPrimaryLongPress(downTime);
            } else {
                // Other long-press actions should be triggered only if app doesn't handle it.
                mDeferredKeyActionExecutor.queueKeyAction(
                        KeyEvent.KEYCODE_STEM_PRIMARY,
                        downTime,
                        () -> stemPrimaryLongPress(downTime));
            }
        }

        private void onMultiPress(long downTime, int count) {
            // Triple-press stem to toggle accessibility gesture should always be triggered
            // regardless of if app handles it.
            if (count == 3
                    && mTriplePressOnStemPrimaryBehavior
                    == TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY) {
                // Cancel any queued actions for current key code to prevent them from being
                // launched after a11y layer enabled. If the action happens early,
                // undoEarlySinglePress will make sure the correct task is on top.
                mDeferredKeyActionExecutor.cancelQueuedAction(KeyEvent.KEYCODE_STEM_PRIMARY);
                undoEarlySinglePress();
                stemPrimaryPress(count);
            } else {
                // Other multi-press gestures should be triggered only if app doesn't handle it.
                mDeferredKeyActionExecutor.queueKeyAction(
                        KeyEvent.KEYCODE_STEM_PRIMARY, downTime, () -> stemPrimaryPress(count));
            }
        }

        /**
         * This method undo the previously launched early-single-press action by bringing the
         * focused task before launching early-single-press back to top.
         */
        private void undoEarlySinglePress() {
            if (shouldHandleStemPrimaryEarlyShortPress()
                    && mFocusedTaskInfoOnStemPrimarySingleKeyUp != null) {
                try {
                    mActivityManagerService.startActivityFromRecents(
                            mFocusedTaskInfoOnStemPrimarySingleKeyUp.taskId, null);
                } catch (RemoteException | IllegalArgumentException e) {
                    Slog.e(
                            TAG,
                            "Failed to start task "
                                    + mFocusedTaskInfoOnStemPrimarySingleKeyUp.taskId
                                    + " from recents",
                            e);
                }
            }
        }

        @Override
        void onKeyUp(int count, KeyEvent event) {
            if (count == 1) {
                // Save info about the most recent task on the first press of the stem key. This
                // may be used later to switch to the most recent app using double press gesture.
                // It is possible that we may navigate away from this task before the double
                // press is detected, as a result of the first press, so we save the  current
                // most recent task before that happens.
                // TODO(b/311497918): guard this with DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP
                mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp =
                        mActivityTaskManagerInternal.getMostRecentTaskFromBackground();

                mFocusedTaskInfoOnStemPrimarySingleKeyUp = null;

                if (shouldHandleStemPrimaryEarlyShortPress()) {
                    // Key-up gesture should be triggered only if app doesn't handle it.
                    mDeferredKeyActionExecutor.queueKeyAction(
                            KeyEvent.KEYCODE_STEM_PRIMARY,
                            event.getDownTime(),
                            () -> {
                                Slog.d(TAG, "StemPrimaryKeyRule: executing deferred onKeyUp");
                                // Save the info of the focused task on screen. This may be used
                                // later to bring the current focused task back to top. For
                                // example, stem primary triple press enables the A11y interface
                                // on top of the current focused task. When early single press is
                                // enabled for stem primary, the focused task could change to
                                // something else upon first key up event. In that case, we will
                                // bring the task recorded by this variable back to top. Then, start
                                // A11y interface.
                                try {
                                    mFocusedTaskInfoOnStemPrimarySingleKeyUp =
                                            mActivityManagerService.getFocusedRootTaskInfo();
                                } catch (RemoteException e) {
                                    Slog.e(
                                            TAG,
                                            "StemPrimaryKeyRule: onKeyUp: error while getting "
                                                    + "focused task "
                                                    + "info.",
                                            e);
                                }

                                stemPrimaryPress(1);
                            });
                }
            }
        }

        // TODO(b/311497918): make a shouldHandlePowerEarlyShortPress for power button.
        private boolean shouldHandleStemPrimaryEarlyShortPress() {
            return mShouldEarlyShortPressOnStemPrimary
                    && mShortPressOnStemPrimaryBehavior == SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS;
        }
    }

    // TODO(b/358569822): Move to KeyGestureController.
    private final class StylusTailButtonRule extends SingleKeyGestureDetector.SingleKeyRule {
        StylusTailButtonRule() {
            super(KEYCODE_STYLUS_BUTTON_TAIL);
        }

        @Override
        int getMaxMultiPressCount() {
            return 2;
        }

        @Override
        void onKeyUp(int pressCount, KeyEvent event) {
            if (pressCount != 1) {
                return;
            }
            // Single press on tail button triggers the open notes gesture.
            mInputManagerInternal.handleKeyGestureInKeyGestureController(
                    new KeyGestureEvent.Builder()
                            .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES)
                            .setDeviceId(event.getDeviceId())
                            .setKeycodes(new int[]{KEYCODE_STYLUS_BUTTON_TAIL})
                            .setModifierState(event.getMetaState())
                            .build());
        }
    }

    // TODO(b/358569822): This is temporarily added to allow single key gestures to be processed
    //  through key gesture infra but keep the detection logic in PWM.
    private void handleSingleKeyGestureInKeyGestureController(
            @KeyGestureEvent.KeyGestureType int keyGestureType,
            @NonNull SingleKeyGestureEvent event) {
        int flags = 0;
        if (event.getType() == SINGLE_KEY_GESTURE_TYPE_LONG_PRESS) {
            flags |= KeyGestureEvent.FLAG_LONG_PRESS;
        } else {
            // Currently not supporting non-long press gestures
            return;
        }
        if (event.getAction() == ACTION_CANCEL) {
            flags |= KeyGestureEvent.FLAG_CANCELLED;
        }
        mInputManagerInternal.handleKeyGestureInKeyGestureController(
                new KeyGestureEvent.Builder()
                        .setKeycodes(new int[]{event.getKeyCode()})
                        .setDeviceId(event.getDeviceId())
                        .setKeyGestureType(keyGestureType)
                        .setFlags(flags)
                        .setAction(event.getAction() == ACTION_START
                                ? KeyGestureEvent.ACTION_GESTURE_START
                                : KeyGestureEvent.ACTION_GESTURE_COMPLETE)
                        .setDisplayId(event.getDisplayId())
                        .build());
    }

    private void initSingleKeyGestureRules(Looper looper) {
        mSingleKeyGestureDetector = SingleKeyGestureDetector.get(mContext, looper);
        mSingleKeyGestureDetector.addRule(new PowerKeyRule());
        if (hasLongPressOnBackBehavior()) {
            mSingleKeyGestureDetector.addRule(new BackKeyRule());
        }
        if (hasStemPrimaryBehavior()) {
            mSingleKeyGestureDetector.addRule(new StemPrimaryKeyRule());
        }
        mSingleKeyGestureDetector.addRule(new StylusTailButtonRule());
    }

    /**
     * Read values from config.xml that may be overridden depending on
     * the configuration of the device.
     * eg. Disable long press on home goes to recents on sw600dp.
     */
    private void readConfigurationDependentBehaviors() {
        final Resources res = mContext.getResources();

        mLongPressOnHomeBehavior = res.getInteger(
                com.android.internal.R.integer.config_longPressOnHomeBehavior);
        if (mLongPressOnHomeBehavior < LONG_PRESS_HOME_NOTHING ||
                mLongPressOnHomeBehavior > LAST_LONG_PRESS_HOME_BEHAVIOR) {
            mLongPressOnHomeBehavior = LONG_PRESS_HOME_NOTHING;
        }

        mDoubleTapOnHomeBehavior = res.getInteger(
                com.android.internal.R.integer.config_doubleTapOnHomeBehavior);
        if (mDoubleTapOnHomeBehavior < DOUBLE_TAP_HOME_NOTHING ||
                mDoubleTapOnHomeBehavior > DOUBLE_TAP_HOME_PIP_MENU) {
            mDoubleTapOnHomeBehavior = DOUBLE_TAP_HOME_NOTHING;
        }

        mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_NOTHING;
        if (mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
            mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
        }
    }

    private void updateSettings() {
        updateSettings(null);
    }

    /**
     * Update provider Setting values on a given {@code handler}, or synchronously if {@code null}
     * is passed for handler.
     */
    void updateSettings(Handler handler) {
        if (handler != null) {
            handler.post(() -> updateSettings(null));
            return;
        }
        ContentResolver resolver = mContext.getContentResolver();
        boolean updateRotation = false;
        boolean updateKidsModeSettings = false;
        final boolean kidsModeEnabled;
        synchronized (mLock) {
            mEndcallBehavior = Settings.System.getIntForUser(resolver,
                    Settings.System.END_BUTTON_BEHAVIOR,
                    Settings.System.END_BUTTON_BEHAVIOR_DEFAULT,
                    UserHandle.USER_CURRENT);
            mIncallPowerBehavior = Settings.Secure.getIntForUser(resolver,
                    Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
                    Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT,
                    UserHandle.USER_CURRENT);
            mIncallBackBehavior = Settings.Secure.getIntForUser(resolver,
                    Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR,
                    Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR_DEFAULT,
                    UserHandle.USER_CURRENT);
            mSystemNavigationKeysEnabled = Settings.Secure.getIntForUser(resolver,
                    Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
                    0, UserHandle.USER_CURRENT) == 1;
            mRingerToggleChord = Settings.Secure.getIntForUser(resolver,
                    Settings.Secure.VOLUME_HUSH_GESTURE, VOLUME_HUSH_OFF,
                    UserHandle.USER_CURRENT);
            mPowerButtonSuppressionDelayMillis = Settings.Global.getInt(resolver,
                    Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE,
                    POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS);
            if (!mContext.getResources()
                    .getBoolean(com.android.internal.R.bool.config_volumeHushGestureEnabled)) {
                mRingerToggleChord = VOLUME_HUSH_OFF;
            }

            // Configure wake gesture.
            boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver,
                    Settings.Secure.WAKE_GESTURE_ENABLED, 0,
                    UserHandle.USER_CURRENT) != 0;
            if (mWakeGestureEnabledSetting != wakeGestureEnabledSetting) {
                mWakeGestureEnabledSetting = wakeGestureEnabledSetting;
                updateWakeGestureListenerLp();
            }

            // use screen off timeout setting as the timeout for the lockscreen
            mLockScreenTimeout = Settings.System.getIntForUser(resolver,
                    Settings.System.SCREEN_OFF_TIMEOUT, 0, UserHandle.USER_CURRENT);
            String imId = Settings.Secure.getStringForUser(resolver,
                    Settings.Secure.DEFAULT_INPUT_METHOD, UserHandle.USER_CURRENT);
            boolean hasSoftInput = imId != null && imId.length() > 0;
            if (mHasSoftInput != hasSoftInput) {
                mHasSoftInput = hasSoftInput;
                updateRotation = true;
            }

            mShortPressOnPowerBehavior = Settings.Global.getInt(resolver,
                    Settings.Global.POWER_BUTTON_SHORT_PRESS,
                    mContext.getResources().getInteger(
                            com.android.internal.R.integer.config_shortPressOnPowerBehavior));
            mDoublePressOnPowerBehavior = Settings.Global.getInt(resolver,
                    Settings.Global.POWER_BUTTON_DOUBLE_PRESS,
                    mContext.getResources().getInteger(
                            com.android.internal.R.integer.config_doublePressOnPowerBehavior));
            mTriplePressOnPowerBehavior = Settings.Global.getInt(resolver,
                    Settings.Global.POWER_BUTTON_TRIPLE_PRESS,
                    mContext.getResources().getInteger(
                            com.android.internal.R.integer.config_triplePressOnPowerBehavior));

            final int longPressOnPowerBehavior = Settings.Global.getInt(resolver,
                    Settings.Global.POWER_BUTTON_LONG_PRESS,
                    mContext.getResources().getInteger(
                            com.android.internal.R.integer.config_longPressOnPowerBehavior));
            final int veryLongPressOnPowerBehavior = Settings.Global.getInt(resolver,
                    Settings.Global.POWER_BUTTON_VERY_LONG_PRESS,
                    mContext.getResources().getInteger(
                            com.android.internal.R.integer.config_veryLongPressOnPowerBehavior));
            if (mLongPressOnPowerBehavior != longPressOnPowerBehavior
                    || mVeryLongPressOnPowerBehavior != veryLongPressOnPowerBehavior) {
                mLongPressOnPowerBehavior = longPressOnPowerBehavior;
                mVeryLongPressOnPowerBehavior = veryLongPressOnPowerBehavior;
            }

            mLongPressOnPowerAssistantTimeoutMs = Settings.Global.getLong(
                    mContext.getContentResolver(),
                    Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS,
                    mContext.getResources().getInteger(
                            com.android.internal.R.integer.config_longPressOnPowerDurationMs));

            mShortPressOnStemPrimaryBehavior = Settings.Global.getInt(resolver,
                    Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS,
                    mContext.getResources().getInteger(
                            com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior));
            mDoublePressOnStemPrimaryBehavior = Settings.Global.getInt(resolver,
                    Settings.Global.STEM_PRIMARY_BUTTON_DOUBLE_PRESS,
                    mContext.getResources().getInteger(
                            com.android.internal.R.integer
                                    .config_doublePressOnStemPrimaryBehavior));
            mTriplePressOnStemPrimaryBehavior = Settings.Global.getInt(resolver,
                    Settings.Global.STEM_PRIMARY_BUTTON_TRIPLE_PRESS,
                    mContext.getResources().getInteger(
                            com.android.internal.R.integer
                                    .config_triplePressOnStemPrimaryBehavior));
            mLongPressOnStemPrimaryBehavior = Settings.Global.getInt(resolver,
                    Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS,
                    mContext.getResources().getInteger(
                            com.android.internal.R.integer.config_longPressOnStemPrimaryBehavior));
            mShouldEarlyShortPressOnPower =
                    mContext.getResources()
                            .getBoolean(com.android.internal.R.bool.config_shortPressEarlyOnPower);
            mShouldEarlyShortPressOnStemPrimary = mContext.getResources().getBoolean(
                    com.android.internal.R.bool.config_shortPressEarlyOnStemPrimary);

            mStylusButtonsEnabled = Settings.Secure.getIntForUser(resolver,
                    Secure.STYLUS_BUTTONS_ENABLED, 1, UserHandle.USER_CURRENT) == 1;
            mInputManagerInternal.setStylusButtonMotionEventsEnabled(mStylusButtonsEnabled);

            kidsModeEnabled = Settings.Secure.getIntForUser(resolver,
                    Settings.Secure.NAV_BAR_KIDS_MODE, 0, UserHandle.USER_CURRENT) == 1;
            if (mKidsModeEnabled != kidsModeEnabled) {
                mKidsModeEnabled = kidsModeEnabled;
                updateKidsModeSettings = true;
            }
        }
        if (updateKidsModeSettings) {
            updateKidsModeSettings(kidsModeEnabled);
        }
        if (updateRotation) {
            updateRotation(true);
        }
    }

    private void updateKidsModeSettings(boolean kidsModeEnabled) {
        if (kidsModeEnabled) {
            // Needed since many Kids apps aren't optimised to support both orientations and it
            // will be hard for kids to understand the app compat mode.
            // TODO(229961548): Remove ignoreOrientationRequest exception for Kids Mode once
            //                  possible.
            if (mContext.getResources().getBoolean(R.bool.config_reverseDefaultRotation)) {
                mWindowManagerInternal.setOrientationRequestPolicy(
                        true /* isIgnoreOrientationRequestDisabled */,
                        new int[]{SCREEN_ORIENTATION_LANDSCAPE,
                                SCREEN_ORIENTATION_REVERSE_LANDSCAPE},
                        new int[]{SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
                                SCREEN_ORIENTATION_SENSOR_LANDSCAPE});
            } else {
                mWindowManagerInternal.setOrientationRequestPolicy(
                        true /* isIgnoreOrientationRequestDisabled */,
                        null /* fromOrientations */, null /* toOrientations */);
            }
        } else {
            mWindowManagerInternal.setOrientationRequestPolicy(
                    false /* isIgnoreOrientationRequestDisabled */,
                    null /* fromOrientations */, null /* toOrientations */);
        }
    }

    private DreamManagerInternal getDreamManagerInternal() {
        if (mDreamManagerInternal == null) {
            // If mDreamManagerInternal is null, attempt to re-fetch it.
            mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
        }

        return mDreamManagerInternal;
    }

    private void updateWakeGestureListenerLp() {
        if (shouldEnableWakeGestureLp()) {
            mWakeGestureListener.requestWakeUpTrigger();
        } else {
            mWakeGestureListener.cancelWakeUpTrigger();
        }
    }

    private boolean shouldEnableWakeGestureLp() {
        return mWakeGestureEnabledSetting && !mDefaultDisplayPolicy.isAwake()
                && (getLidBehavior() != LID_BEHAVIOR_SLEEP
                || mDefaultDisplayPolicy.getLidState() != LID_CLOSED)
                && mWakeGestureListener.isSupported();
    }

    /** {@inheritDoc} */
    @Override
    public int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
            int[] outAppOp, int displayId) {
        if (isRoundedCornerOverlay && mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
                != PERMISSION_GRANTED) {
            return ADD_PERMISSION_DENIED;
        }

        outAppOp[0] = AppOpsManager.OP_NONE;

        if (!((type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW)
                || (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW)
                || (type >= FIRST_SYSTEM_WINDOW && type <= LAST_SYSTEM_WINDOW))) {
            return WindowManagerGlobal.ADD_INVALID_TYPE;
        }

        if (type < FIRST_SYSTEM_WINDOW || type > LAST_SYSTEM_WINDOW) {
            // Window manager will make sure these are okay.
            return ADD_OKAY;
        }

        if (!isSystemAlertWindowType(type)) {
            switch (type) {
                case TYPE_TOAST:
                    // Only apps that target older than O SDK can add window without a token, after
                    // that we require a token so apps cannot add toasts directly as the token is
                    // added by the notification system.
                    // Window manager does the checking for this.
                    outAppOp[0] = OP_TOAST_WINDOW;
                    return ADD_OKAY;
                case TYPE_ACCESSIBILITY_OVERLAY:
                    if (createAccessibilityOverlayAppOpEnabled()) {
                        outAppOp[0] = OP_CREATE_ACCESSIBILITY_OVERLAY;
                        return ADD_OKAY;
                    }
                case TYPE_INPUT_METHOD:
                case TYPE_WALLPAPER:
                case TYPE_PRESENTATION:
                case TYPE_PRIVATE_PRESENTATION:
                case TYPE_VOICE_INTERACTION:
                case TYPE_QS_DIALOG:
                case TYPE_NAVIGATION_BAR_PANEL:
                case TYPE_STATUS_BAR:
                case TYPE_NOTIFICATION_SHADE:
                case TYPE_NAVIGATION_BAR:
                case TYPE_STATUS_BAR_ADDITIONAL:
                case TYPE_STATUS_BAR_SUB_PANEL:
                case TYPE_VOICE_INTERACTION_STARTING:
                    // The window manager will check these.
                    return ADD_OKAY;
            }

            return (mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
                    == PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
        }

        // Things get a little more interesting for alert windows...
        outAppOp[0] = OP_SYSTEM_ALERT_WINDOW;

        final int callingUid = Binder.getCallingUid();
        // system processes will be automatically granted privilege to draw
        if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
            return ADD_OKAY;
        }

        ApplicationInfo appInfo;
        try {
            appInfo = mPackageManager.getApplicationInfoAsUser(
                            packageName,
                            0 /* flags */,
                            UserHandle.getUserId(callingUid));
        } catch (PackageManager.NameNotFoundException e) {
            appInfo = null;
        }

        if (appInfo == null || (type != TYPE_APPLICATION_OVERLAY && appInfo.targetSdkVersion >= O)) {
            /**
             * Apps targeting >= {@link Build.VERSION_CODES#O} are required to hold
             * {@link android.Manifest.permission#INTERNAL_SYSTEM_WINDOW} (system signature apps)
             * permission to add alert windows that aren't
             * {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY}.
             */
            return (mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
                    == PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
        }

        if (mContext.checkCallingOrSelfPermission(SYSTEM_APPLICATION_OVERLAY)
                == PERMISSION_GRANTED) {
            return ADD_OKAY;
        }

        // Allow virtual device owners to add overlays on the trusted displays they own.
        if (mWindowManagerFuncs.isCallerVirtualDeviceOwner(displayId, callingUid)
                && mWindowManagerFuncs.isDisplayTrusted(displayId)
                && mContext.checkCallingOrSelfPermission(CREATE_VIRTUAL_DEVICE)
                == PERMISSION_GRANTED) {
            return ADD_OKAY;
        }

        // check if user has enabled this operation. SecurityException will be thrown if this app
        // has not been allowed by the user. The reason to use "noteOp" (instead of checkOp) is to
        // make sure the usage is logged.
        final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, packageName,
                null /* featureId */, "check-add");
        switch (mode) {
            case AppOpsManager.MODE_ALLOWED:
            case AppOpsManager.MODE_IGNORED:
                // although we return ADD_OKAY for MODE_IGNORED, the added window will
                // actually be hidden in WindowManagerService
                return ADD_OKAY;
            case AppOpsManager.MODE_ERRORED:
                // Don't crash legacy apps
                if (appInfo.targetSdkVersion < M) {
                    return ADD_OKAY;
                }
                return ADD_PERMISSION_DENIED;
            default:
                // in the default mode, we will make a decision here based on
                // checkCallingPermission()
                return (mContext.checkCallingOrSelfPermission(SYSTEM_ALERT_WINDOW)
                        == PERMISSION_GRANTED) ? ADD_OKAY : ADD_PERMISSION_DENIED;
        }
    }

    void readLidState() {
        mDefaultDisplayPolicy.setLidState(mWindowManagerFuncs.getLidState());
    }

    private void readCameraLensCoverState() {
        mCameraLensCoverState = mWindowManagerFuncs.getCameraLensCoverState();
    }

    private boolean isHidden(int accessibilityMode) {
        final int lidState = mDefaultDisplayPolicy.getLidState();
        switch (accessibilityMode) {
            case 1:
                return lidState == LID_CLOSED;
            case 2:
                return lidState == LID_OPEN;
            default:
                return false;
        }
    }

    /** {@inheritDoc} */
    @Override
    public void adjustConfigurationLw(Configuration config, int keyboardPresence,
            int navigationPresence) {
        mHaveBuiltInKeyboard = (keyboardPresence & PRESENCE_INTERNAL) != 0;

        readConfigurationDependentBehaviors();
        readLidState();

        if (config.keyboard == Configuration.KEYBOARD_NOKEYS
                || (keyboardPresence == PRESENCE_INTERNAL
                        && isHidden(mLidKeyboardAccessibility))) {
            config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_YES;
            if (!mHasSoftInput) {
                config.keyboardHidden = Configuration.KEYBOARDHIDDEN_YES;
            }
        }

        if (config.navigation == Configuration.NAVIGATION_NONAV
                || (navigationPresence == PRESENCE_INTERNAL
                        && isHidden(mLidNavigationAccessibility))) {
            config.navigationHidden = Configuration.NAVIGATIONHIDDEN_YES;
        }
    }

    @Override
    public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) {
        return attrs.type == TYPE_NOTIFICATION_SHADE;
    }

    @Override
    public Animation createHiddenByKeyguardExit(boolean onWallpaper,
            boolean goingToNotificationShade, boolean subtleAnimation) {
        return TransitionAnimation.createHiddenByKeyguardExit(mContext,
                mLogDecelerateInterpolator, onWallpaper, goingToNotificationShade, subtleAnimation);
    }


    @Override
    public Animation createKeyguardWallpaperExit(boolean goingToNotificationShade) {
        if (goingToNotificationShade) {
            return null;
        } else {
            return AnimationUtils.loadAnimation(mContext, R.anim.lock_screen_wallpaper_exit);
        }
    }

    private static void awakenDreams() {
        IDreamManager dreamManager = getDreamManager();
        if (dreamManager != null) {
            try {
                dreamManager.awaken();
            } catch (RemoteException e) {
                // fine, stay asleep then
            }
        }
    }

    static IDreamManager getDreamManager() {
        return IDreamManager.Stub.asInterface(
                ServiceManager.checkService(DreamService.DREAM_SERVICE));
    }

    TelecomManager getTelecommService() {
        return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
    }

    NotificationManager getNotificationService() {
        return mContext.getSystemService(NotificationManager.class);
    }

    static IAudioService getAudioService() {
        IAudioService audioService = IAudioService.Stub.asInterface(
                ServiceManager.checkService(Context.AUDIO_SERVICE));
        if (audioService == null) {
            Log.w(TAG, "Unable to find IAudioService interface.");
        }
        return audioService;
    }

    boolean keyguardOn() {
        return isKeyguardShowingAndNotOccluded() || inKeyguardRestrictedKeyInputMode();
    }

    private static final int[] WINDOW_TYPES_WHERE_HOME_DOESNT_WORK = {
            WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
            WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
        };

    private void notifyKeyGestureCompletedOnActionUp(KeyEvent event,
            @KeyGestureEvent.KeyGestureType int gestureType) {
        if (event.getAction() != KeyEvent.ACTION_UP) {
            return;
        }
        notifyKeyGestureCompleted(event, gestureType);
    }

    private void notifyKeyGestureCompletedOnActionDown(KeyEvent event,
            @KeyGestureEvent.KeyGestureType int gestureType) {
        if (event.getAction() != KeyEvent.ACTION_DOWN) {
            return;
        }
        notifyKeyGestureCompleted(event, gestureType);
    }

    private void notifyKeyGestureCompleted(KeyEvent event,
            @KeyGestureEvent.KeyGestureType int gestureType) {
        if (gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
            return;
        }
        mInputManagerInternal.notifyKeyGestureCompleted(event.getDeviceId(),
                new int[]{event.getKeyCode()}, event.getMetaState(), gestureType);
    }

    @Override
    public KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId) {
        return mModifierShortcutManager.getApplicationLaunchKeyboardShortcuts(deviceId,
                mInputManager.getAppLaunchBookmarks());
    }

    // TODO(b/117479243): handle it in InputPolicy
    // TODO (b/283241997): Add the remaining keyboard shortcut logging after refactoring
    /** {@inheritDoc} */
    @Override
    public boolean interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event) {
        final int keyCode = event.getKeyCode();
        final int deviceId = event.getDeviceId();

        if (DEBUG_INPUT) {
            Log.d(TAG,
                    "interceptKeyTi keyCode=" + keyCode + " action=" + event.getAction()
                            + " repeatCount=" + event.getRepeatCount() + " keyguardOn="
                            + keyguardOn() + " canceled=" + event.isCanceled());
        }

        Set<Integer> consumedKeys = mConsumedKeysForDevice.get(deviceId);
        if (consumedKeys == null) {
            consumedKeys = new HashSet<>();
            mConsumedKeysForDevice.put(deviceId, consumedKeys);
        }

        if (interceptSystemKeysAndShortcuts(focusedToken, event)
                && event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
            consumedKeys.add(keyCode);
            return true;
        }

        boolean needToConsumeKey = consumedKeys.contains(keyCode);
        if (event.getAction() == KeyEvent.ACTION_UP || event.isCanceled()) {
            consumedKeys.remove(keyCode);
            if (consumedKeys.isEmpty()) {
                mConsumedKeysForDevice.remove(deviceId);
            }
        }

        return needToConsumeKey;
    }

    // You can only start consuming the key gesture if ACTION_DOWN and repeat count
    // is 0. If you start intercepting the key halfway, then key will not be consumed
    // and will be sent to apps for processing too.
    // e.g. If a certain combination is only handled on ACTION_UP (i.e.
    // interceptShortcut() returns true only for ACTION_UP), then since we already
    // sent the ACTION_DOWN events to the application, we MUST also send the
    // ACTION_UP to the application.
    // So, to ensure that your intercept logic works properly, and we don't send any
    // conflicting events to application, make sure to consume the event on
    // ACTION_DOWN even if you want to do something on ACTION_UP. This is essential
    // to maintain event parity and to not have incomplete key gestures.
    //
    // NOTE: Please try not to add new Shortcut combinations here and instead use KeyGestureEvents.
    // Add shortcut trigger logic in {@code KeyGestureController} and add handling logic in
    // {@link handleKeyGesture()}
    private boolean interceptSystemKeysAndShortcuts(IBinder focusedToken, KeyEvent event) {
        final int keyCode = event.getKeyCode();

        switch (keyCode) {
            case KeyEvent.KEYCODE_HOME:
                return handleHomeShortcuts(focusedToken, event);
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_VOLUME_DOWN:
            case KeyEvent.KEYCODE_VOLUME_MUTE:
                if (mUseTvRouting || mHandleVolumeKeysInWM) {
                    // On TVs or when the configuration is enabled, volume keys never
                    // go to the foreground app.
                    dispatchDirectAudioEvent(event);
                    return true;
                }

                // If the device is in VR mode and keys are "internal" (e.g. on the side of the
                // device), then drop the volume keys and don't forward it to the
                // application/dispatch the audio event.
                if (mDefaultDisplayPolicy.isPersistentVrModeEnabled()) {
                    final InputDevice d = event.getDevice();
                    if (d != null && !d.isExternal()) {
                        return true;
                    }
                }
                break;
            case KeyEvent.KEYCODE_STEM_PRIMARY:
                if (prepareToSendSystemKeyToApplication(focusedToken, event)) {
                    // Send to app.
                    return false;
                } else {
                    // Intercepted.
                    sendSystemKeyToStatusBarAsync(event);
                    return true;
                }
        }
        if (isValidGlobalKey(keyCode)
                && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
            return true;
        }
        return false;
    }

    @SuppressLint("MissingPermission")
    private void initKeyGestures() {
        List<Integer> supportedGestures = new ArrayList<>(List.of(
                KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT,
                KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
                KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN,
                KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
                KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
                KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
                KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
                KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
                KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
                KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP,
                KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
                KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
                KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB,
                KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD,
                KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS,
                KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT
        ));
        if (!com.android.window.flags.Flags.grantManageKeyGesturesToRecents()) {
            // When grantManageKeyGesturesToRecents is enabled, the event is handled in the
            // recents app.
            supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
            supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS);
            supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER);
        }
        if (!com.android.window.flags.Flags.enableKeyGestureHandlerForSysui()) {
            supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL);
        }
        mInputManager.registerKeyGestureEventHandler(supportedGestures,
                PhoneWindowManager.this::handleKeyGestureEvent);
    }

    @VisibleForTesting
    void handleKeyGestureEvent(KeyGestureEvent event, IBinder focusedToken) {
        boolean start = event.getAction() == KeyGestureEvent.ACTION_GESTURE_START;
        boolean complete = event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
                && !event.isCancelled();
        int deviceId = event.getDeviceId();
        int gestureType = event.getKeyGestureType();
        int displayId = event.getDisplayId();
        int modifierState = event.getModifierState();
        boolean keyguardOn = keyguardOn();
        boolean canLaunchApp = isUserSetupComplete() && !keyguardOn;
        boolean isPowerKeyPressed = Arrays.stream(event.getKeycodes()).anyMatch(
                (keycode) -> keycode == KeyEvent.KEYCODE_POWER);
        if (complete && isPowerKeyPressed) {
            mPowerKeyHandled = true;
        }
        switch (gestureType) {
            case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS:
                if (complete) {
                    showRecentApps(false);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH:
                if (!keyguardOn) {
                    if (start) {
                        preloadRecentApps();
                    } else if (complete) {
                        toggleRecentApps();
                    }
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
            case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT:
                boolean isPowerLongPress = event.isLongPress() && isPowerKeyPressed;
                boolean shouldLaunchAssist = complete && (canLaunchApp || isPowerLongPress);
                if (shouldLaunchAssist) {
                    launchAssistAction(
                            isPowerLongPress ? null : Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
                            deviceId, event.getDisplayId(), mInjector.getUptimeMillis(),
                            isPowerLongPress
                                    ? AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS
                                    : AssistUtils.INVOCATION_TYPE_UNKNOWN);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_HOME:
                if (complete) {
                    // Post to main thread to avoid blocking input pipeline.
                    mHandler.post(() -> handleShortPressOnHome(displayId));
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
                if (complete && canLaunchApp) {
                    showSystemSettings(displayId);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN:
                if (complete) {
                    lockNow(null /* options */);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL:
                if (complete) {
                    toggleNotificationPanel();
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
                if (complete) {
                    try {
                        if (mActivityManagerService.launchBugReportHandlerApp()) {
                            return;
                        }
                        // Take bug report only for debuggable builds as a fallback when there is
                        // no bug handler or feedback app on the system image.
                        if ("1".equals(SystemProperties.get("ro.debuggable"))) {
                            mActivityManagerService.requestInteractiveBugReport();
                        }
                        return;
                    } catch (RemoteException e) {
                        Slog.d(TAG, "Error taking bugreport", e);
                    }
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
                if (complete) {
                    StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
                    if (statusbar != null) {
                        statusbar.moveFocusedTaskToFullscreen(
                                getTargetDisplayIdForKeyGestureEvent(event));
                    }
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE:
                if (complete) {
                    StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
                    if (statusbar != null) {
                        statusbar.moveFocusedTaskToDesktop(
                                getTargetDisplayIdForKeyGestureEvent(event));
                    }
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT:
                if (complete) {
                    moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyGestureEvent(event),
                            true /* leftOrTop */);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT:
                if (complete) {
                    moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyGestureEvent(event),
                            false /* leftOrTop */);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
                if (complete) {
                    toggleKeyboardShortcutsMenu(deviceId);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP:
            case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN:
                if (complete) {
                    int direction =
                            gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP ? 1 : -1;
                    changeDisplayBrightnessValue(displayId, direction);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER:
                if (start) {
                    showRecentApps(true);
                } else {
                    hideRecentApps(true, false);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS:
                if (complete && isKeyEventForCurrentUser(event.getDisplayId(),
                        event.getKeycodes()[0], "launchAllAppsViaA11y")) {
                    launchAllAppsAction();
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH:
                if (complete && canLaunchApp) {
                    launchTargetSearchActivity(displayId);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
                if (complete) {
                    int direction = (modifierState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
                    sendSwitchKeyboardLayout(displayId, focusedToken, direction);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD:
                if (start) {
                    interceptRingerToggleChord();
                } else {
                    cancelPendingRingerToggleChordAction();
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS:
                if (start) {
                    performHapticFeedback(
                            HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON,
                            "KEY_GESTURE_TYPE_GLOBAL_ACTIONS - Global Actions");
                    showGlobalActions();
                } else {
                    cancelGlobalActionsAction();
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT:
                if (start) {
                    interceptBugreportGestureTv();
                } else {
                    cancelBugreportGestureTv();
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS:
                if (complete) {
                    closeSystemDialogsAsUser(UserHandle.CURRENT_OR_SELF);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
                AppLaunchData data = event.getAppLaunchData();
                if (complete && canLaunchApp && data != null) {
                    startActivityAsUser(mModifierShortcutManager.getIntentFromAppLaunchData(data),
                            UserHandle.of(mCurrentUserId), displayId);
                }
                break;
            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB:
                NotificationManager nm = getNotificationService();
                if (nm != null) {
                    boolean isEnabled = nm.getZenMode() != Settings.Global.ZEN_MODE_OFF;
                    nm.setZenMode(isEnabled ? Settings.Global.ZEN_MODE_OFF
                                    : Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
                            "Key gesture DND", true);
                }
                break;
            default:
                Log.w(TAG, "Received a key gesture " + event
                        + " that was not registered by this handler");
                break;
        }
    }

    private void changeDisplayBrightnessValue(int displayId, int direction) {
        int screenDisplayId = displayId < 0 ? DEFAULT_DISPLAY : displayId;

        float minLinearBrightness = mPowerManager.getBrightnessConstraint(
                screenDisplayId, PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
        float maxLinearBrightness = mPowerManager.getBrightnessConstraint(
                screenDisplayId, PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
        float linearBrightness = mDisplayManager.getBrightness(screenDisplayId);

        float gammaBrightness = BrightnessUtils.convertLinearToGamma(linearBrightness);
        float adjustedGammaBrightness = gammaBrightness + 1f / BRIGHTNESS_STEPS * direction;
        adjustedGammaBrightness = MathUtils.constrain(adjustedGammaBrightness, 0f, 1f);
        float adjustedLinearBrightness = BrightnessUtils.convertGammaToLinear(
                adjustedGammaBrightness);
        adjustedLinearBrightness = MathUtils.constrain(adjustedLinearBrightness,
                minLinearBrightness, maxLinearBrightness);
        mDisplayManager.setBrightness(screenDisplayId, adjustedLinearBrightness);

        Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG);
        intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION
                | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
        intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true);
        startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
    }

    /**
     * In this function, we check whether a system key should be sent to the application. We also
     * detect the key gesture on this key, even if the key will be sent to the app. The gesture
     * action, if any, will not be executed immediately. It will be queued and execute only after
     * the application tells us that it didn't handle this key.
     *
     * @return true if this key should be sent to the application. This also means that the target
     *     application has the necessary permissions to receive this key. Return false otherwise.
     */
    private boolean prepareToSendSystemKeyToApplication(IBinder focusedToken, KeyEvent event) {
        final int keyCode = event.getKeyCode();
        if (!event.isSystem()) {
            Log.wtf(
                    TAG,
                    "Illegal keycode provided to prepareToSendSystemKeyToApplication: "
                            + KeyEvent.keyCodeToString(keyCode));
            return false;
        }
        final boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN;
        if (isDown && event.getRepeatCount() == 0) {
            // This happens at the initial DOWN event. Check focused window permission now.
            final KeyInterceptionInfo info =
                    mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
            if (info != null
                    && mButtonOverridePermissionChecker.canAppOverrideSystemKey(
                            mContext, info.windowOwnerUid)) {
                // Focused window has the permission. Pass the event to it.
                return true;
            } else {
                // Focused window doesn't have the permission. Intercept the event.
                // If the initial DOWN event is intercepted, follow-up events will be intercepted
                // too. So we know the gesture won't be handled by app, and can handle the gesture
                // in system.
                setDeferredKeyActionsExecutableAsync(keyCode, event.getDownTime());
                return false;
            }
        } else {
            // This happens after the initial DOWN event. We will just reuse the initial decision.
            // I.e., if the initial DOWN event was dispatched, follow-up events should be
            // dispatched. Otherwise, follow-up events should be consumed.
            final Set<Integer> consumedKeys = mConsumedKeysForDevice.get(event.getDeviceId());
            final boolean wasConsumed = consumedKeys != null && consumedKeys.contains(keyCode);
            return !wasConsumed;
        }
    }

    private void setDeferredKeyActionsExecutableAsync(int keyCode, long downTime) {
        Message msg = Message.obtain(mHandler, MSG_SET_DEFERRED_KEY_ACTIONS_EXECUTABLE);
        msg.arg1 = keyCode;
        msg.obj = downTime;
        msg.setAsynchronous(true);
        msg.sendToTarget();
    }

    private boolean handleHomeShortcuts(IBinder focusedToken, KeyEvent event) {
        // First we always handle the home key here, so applications
        // can never break it, although if keyguard is on, we do let
        // it handle it, because that gives us the correct 5 second
        // timeout.
        DisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(event.getDisplayId());
        if (handler == null) {
            handler = new DisplayHomeButtonHandler(event.getDisplayId());
            mDisplayHomeButtonHandlers.put(event.getDisplayId(), handler);
        }
        return handler.handleHomeButton(focusedToken, event);
    }

    private void toggleMicrophoneMuteFromKey() {
        if (mSensorPrivacyManager.supportsSensorToggle(
                SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE,
                SensorPrivacyManager.Sensors.MICROPHONE)) {
            boolean isEnabled = mSensorPrivacyManager.isSensorPrivacyEnabled(
                    SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE,
                    SensorPrivacyManager.Sensors.MICROPHONE);

            mSensorPrivacyManager.setSensorPrivacy(SensorPrivacyToggleSourceProto.OTHER,
                    SensorPrivacyManager.Sensors.MICROPHONE, !isEnabled, mCurrentUserId);

            int toastTextResId;
            if (isEnabled) {
                toastTextResId = R.string.mic_access_on_toast;
            } else {
                toastTextResId = R.string.mic_access_off_toast;
            }

            Toast.makeText(
                mContext,
                UiThread.get().getLooper(),
                mContext.getString(toastTextResId),
                Toast.LENGTH_SHORT)
                .show();
        }
    }

    /**
     * TV only: recognizes a remote control gesture for capturing a bug report.
     */
    private void interceptBugreportGestureTv() {
        mHandler.removeMessages(MSG_BUGREPORT_TV);
        // The bugreport capture chord is a long press on DPAD CENTER and BACK simultaneously.
        Message msg = Message.obtain(mHandler, MSG_BUGREPORT_TV);
        msg.setAsynchronous(true);
        mHandler.sendMessageDelayed(msg, BUGREPORT_TV_GESTURE_TIMEOUT_MILLIS);
    }

    private void cancelBugreportGestureTv() {
        mHandler.removeMessages(MSG_BUGREPORT_TV);
    }

    @VisibleForTesting
    void requestBugreportForTv() {
        try {
            if (!ActivityManager.getService().launchBugReportHandlerApp()) {
                ActivityManager.getService().requestInteractiveBugReport();
            }
        } catch (RemoteException e) {
            Slog.e(TAG, "Error taking bugreport", e);
        }
    }

    // TODO(b/117479243): handle it in InputPolicy
    /** {@inheritDoc} */
    @Override
    public boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken) {
        // Note: This method is only called if the initial down was unhandled.
        if (DEBUG_INPUT) {
            final KeyInterceptionInfo info =
                    mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
            final String title = info == null ? "<unknown>" : info.windowTitle;
            Slog.d(TAG, "Unhandled key: inputToken=" + focusedToken
                    + ", title=" + title
                    + ", action=" + event.getAction()
                    + ", flags=" + event.getFlags()
                    + ", keyCode=" + event.getKeyCode()
                    + ", scanCode=" + event.getScanCode()
                    + ", metaState=" + event.getMetaState()
                    + ", repeatCount=" + event.getRepeatCount());
        }

        final int keyCode = event.getKeyCode();
        // TODO(b/358569822): Shift to KeyGestureEvent based handling
        if (keyCode == KeyEvent.KEYCODE_STEM_PRIMARY) {
            handleUnhandledSystemKey(event);
            sendSystemKeyToStatusBarAsync(event);
            return true;
        }
        return false;
    }

    /**
     * Called when a system key was sent to application and was unhandled. We will execute any
     * queued actions associated with this key code at this point.
     */
    private void handleUnhandledSystemKey(KeyEvent event) {
        if (!event.isSystem()) {
            Log.wtf(
                    TAG,
                    "Illegal keycode provided to handleUnhandledSystemKey: "
                            + KeyEvent.keyCodeToString(event.getKeyCode()));
            return;
        }
        if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
            // If the initial DOWN event is unhandled by app, follow-up events will also be
            // unhandled by app. So we can handle the key event in system.
            setDeferredKeyActionsExecutableAsync(event.getKeyCode(), event.getDownTime());
        }
    }

    private void sendSwitchKeyboardLayout(int displayId, @Nullable IBinder focusedToken,
            int direction) {
        SwitchKeyboardLayoutMessageObject object =
                new SwitchKeyboardLayoutMessageObject(displayId, focusedToken, direction);
        mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, object).sendToTarget();
    }

    private void handleSwitchKeyboardLayout(int displayId, int direction, IBinder focusedToken) {
        IBinder targetWindowToken =
                mWindowManagerInternal.getTargetWindowTokenFromInputToken(focusedToken);
        InputMethodManagerInternal.get().onSwitchKeyboardLayoutShortcut(direction, displayId,
                targetWindowToken);
    }

    @Override
    public void setTopFocusedDisplay(int displayId) {
        mTopFocusedDisplayId = displayId;
    }

    @Override
    public void registerDisplayFoldListener(IDisplayFoldListener listener) {
        if (mDisplayFoldController != null) {
            mDisplayFoldController.registerDisplayFoldListener(listener);
        }
    }

    @Override
    public void unregisterDisplayFoldListener(IDisplayFoldListener listener) {
        if (mDisplayFoldController != null) {
            mDisplayFoldController.unregisterDisplayFoldListener(listener);
        }
    }

    @Override
    public void setOverrideFoldedArea(Rect area) {
        if (mDisplayFoldController != null) {
            mDisplayFoldController.setOverrideFoldedArea(area);
        }
    }

    @Override
    public Rect getFoldedArea() {
        if (mDisplayFoldController != null) {
            return mDisplayFoldController.getFoldedArea();
        }
        return new Rect();
    }

    @Override
    public void onDefaultDisplayFocusChangedLw(WindowState newFocus) {
        if (mDisplayFoldController != null) {
            mDisplayFoldController.onDefaultDisplayFocusChanged(
                    newFocus != null ? newFocus.getOwningPackage() : null);
        }
    }

    @Override
    public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService)
            throws RemoteException {
        synchronized (mLock) {
            mInputManagerInternal.registerShortcutKey(shortcutCode, shortcutService);
        }
    }

    @Override
    public void onKeyguardOccludedChangedLw(boolean occluded) {
        if (mKeyguardDelegate != null) {
            mPendingKeyguardOccluded = occluded;
            mKeyguardOccludedChanged = true;
        }
    }

    @Override
    public int applyKeyguardOcclusionChange() {
        if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded commit occluded="
                + mPendingKeyguardOccluded + " changed=" + mKeyguardOccludedChanged);

        // TODO(b/276433230): Explicitly save before/after for occlude state in each
        // Transition so we don't need to update SysUI every time.
        if (setKeyguardOccludedLw(mPendingKeyguardOccluded)) {
            return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
        } else {
            return 0;
        }
    }

    /**
     * Called when keyguard related app transition starts, or cancelled.
     *
     * @param startKeyguardExitAnimation Trigger IKeyguardService#startKeyguardExitAnimation to
     *                                  start keyguard exit animation.
     * @param notifyOccluded Trigger IKeyguardService#setOccluded binder call to notify whether
     *                      the top activity can occlude the keyguard or not.
     *
     * @return Whether the flags have changed and we have to redo the layout.
     */
    private int handleTransitionForKeyguardLw(boolean startKeyguardExitAnimation,
            boolean notifyOccluded) {
        int redoLayout = 0;
        if (notifyOccluded) {
            redoLayout = applyKeyguardOcclusionChange();
        }
        if (startKeyguardExitAnimation) {
            if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
            startKeyguardExitAnimation(mInjector.getUptimeMillis());
        }
        return redoLayout;
    }

    /**
     * Shows the keyguard without immediately locking the device.
     */
    @Override
    public void showDismissibleKeyguard() {
        mKeyguardDelegate.showDismissibleKeyguard();
    }

    // There are several different flavors of "assistant" that can be launched from
    // various parts of the UI.

    /**
     * Asks the status bar to startAssist(), usually a full "assistant" interface
     * @deprecated Use {@link #launchAssistAction(String, int, int, long, int)} instead.
     */
    private void launchAssistAction(String hint, int deviceId, long eventTime,
            int invocationType) {
        launchAssistAction(hint, deviceId, INVALID_DISPLAY, eventTime, invocationType);
    }

    private void launchAssistAction(String hint, int deviceId, int displayId, long eventTime,
            int invocationType) {
        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST);
        if (!isUserSetupComplete()) {
            // Disable opening assist window during setup
            return;
        }

        // Add Intent Extra data.
        Bundle args = null;
        args = new Bundle();
        if (deviceId != INVALID_INPUT_DEVICE_ID) {
            args.putInt(Intent.EXTRA_ASSIST_INPUT_DEVICE_ID, deviceId);
        }
        if (hint != null) {
            args.putBoolean(hint, true);
        }
        args.putLong(Intent.EXTRA_TIME, eventTime);
        args.putInt(AssistUtils.INVOCATION_TYPE_KEY, invocationType);
        if (com.android.window.flags.Flags.supportGeminiOnMultiDisplay()) {
            if (invocationType == AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS) {
                args.putInt(Intent.EXTRA_ASSIST_DISPLAY_ID,
                        displayId >= 0 ? displayId : DEFAULT_DISPLAY);
            } else {
                args.putInt(Intent.EXTRA_ASSIST_DISPLAY_ID, mTopFocusedDisplayId);
            }
        }

        SearchManager searchManager = mContext.getSystemService(SearchManager.class);
        if (searchManager != null) {
            searchManager.launchAssist(args);
        } else {
            // Fallback to status bar if search manager doesn't exist (e.g. on wear).
            StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
            if (statusBar != null) {
                statusBar.startAssist(args);
            }
        }
    }

    /**
     * Launches ACTION_VOICE_ASSIST_RETAIL if in retail mode, or ACTION_VOICE_ASSIST otherwise
     * Does nothing on keyguard except for watches. Delegates it to keyguard if present on watch.
     */
    private void launchVoiceAssist(boolean allowDuringSetup) {
        final boolean keyguardActive =
                mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
        if (!keyguardActive) {
            startActivityAsUser(
                    new Intent(Intent.ACTION_VOICE_ASSIST),
                    UserHandle.CURRENT_OR_SELF,
                    allowDuringSetup);
        } else {
            mKeyguardDelegate.dismissKeyguardToLaunch(new Intent(Intent.ACTION_VOICE_ASSIST));
        }
    }

    private boolean isInRetailMode() {
        return Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.DEVICE_DEMO_MODE, 0) == 1;
    }

    private void startActivityAsUser(Intent intent, UserHandle handle) {
        startActivityAsUser(intent, handle, INVALID_DISPLAY);
    }

    private void startActivityAsUser(Intent intent, UserHandle handle, int displayId) {
        startActivityAsUser(intent, handle, displayId, false /* allowDuringSetup */);
    }

    private void startActivityAsUser(Intent intent, UserHandle handle, boolean allowDuringSetup) {
        startActivityAsUser(intent, handle, INVALID_DISPLAY, allowDuringSetup);
    }

    private void startActivityAsUser(Intent intent, UserHandle handle, int displayId,
            boolean allowDuringSetup) {
        try {
            if (allowDuringSetup || isUserSetupComplete()) {
                ActivityOptions options = ActivityOptions.makeBasic();
                options.setLaunchDisplayId(displayId);
                mContext.startActivityAsUser(intent, options.toBundle(), handle);
                dismissKeyboardShortcutsMenu();
            } else {
                Slog.i(TAG, "Not starting activity because user setup is in progress: " + intent);
            }
        } catch (ActivityNotFoundException ex) {
            Slog.w(TAG, "Not launching app because "
                    + "the activity to launch intent: " + intent + " was not found");
        }
    }

    @SuppressLint("MissingPermission")
    private void closeSystemDialogsAsUser(UserHandle handle) {
        final Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        final Bundle options = BroadcastOptions.makeBasic()
                .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
                .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
                .toBundle();
        mContext.sendBroadcastAsUser(
                intent,
                handle,
                null /* receiverPermission */,
                options);
    }

    private void preloadRecentApps() {
        mPreloadedRecentApps = true;
        StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
        if (statusbar != null) {
            statusbar.preloadRecentApps();
        }
    }

    private void cancelPreloadRecentApps() {
        if (mPreloadedRecentApps) {
            mPreloadedRecentApps = false;
            StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
            if (statusbar != null) {
                statusbar.cancelPreloadRecentApps();
            }
        }
    }

    private void toggleTaskbar() {
        StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
        if (statusbar != null) {
            statusbar.toggleTaskbar();
        }
    }

    private void toggleRecentApps() {
        mPreloadedRecentApps = false; // preloading no longer needs to be canceled
        StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
        if (statusbar != null) {
            statusbar.toggleRecentApps();
        }
    }

    @Override
    public void showRecentApps() {
        mHandler.removeMessages(MSG_DISPATCH_SHOW_RECENTS);
        mHandler.obtainMessage(MSG_DISPATCH_SHOW_RECENTS).sendToTarget();
    }

    private void showRecentApps(boolean triggeredFromAltTab) {
        mPreloadedRecentApps = false; // preloading no longer needs to be canceled
        StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
        if (statusbar != null) {
            statusbar.showRecentApps(triggeredFromAltTab);
        }
        dismissKeyboardShortcutsMenu();
    }

    private void toggleKeyboardShortcutsMenu(int deviceId) {
        StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
        if (statusbar != null) {
            statusbar.toggleKeyboardShortcutsMenu(deviceId);
        }
    }

    private void dismissKeyboardShortcutsMenu() {
        StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
        if (statusbar != null) {
            statusbar.dismissKeyboardShortcutsMenu();
        }
    }

    private void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHome) {
        mPreloadedRecentApps = false; // preloading no longer needs to be canceled
        StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
        if (statusbar != null) {
            statusbar.hideRecentApps(triggeredFromAltTab, triggeredFromHome);
        }
    }

    private void moveFocusedTaskToStageSplit(int displayId, boolean leftOrTop) {
        StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
        if (statusbar != null) {
            statusbar.moveFocusedTaskToStageSplit(displayId, leftOrTop);
        }
    }

    void launchHomeFromHotKey(int displayId) {
        launchHomeFromHotKey(displayId, true /* awakenFromDreams */, true /*respectKeyguard*/);
    }

    /**
     * A home key -> launch home action was detected.  Take the appropriate action
     * given the situation with the keyguard.
     */
    void launchHomeFromHotKey(int displayId, final boolean awakenFromDreams,
            final boolean respectKeyguard) {
        if (respectKeyguard) {
            if (isKeyguardShowingAndNotOccluded()) {
                // don't launch home if keyguard showing
                return;
            }

            if (!isKeyguardOccluded() && mKeyguardDelegate.isInputRestricted()) {
                // when in keyguard restricted mode, must first verify unlock
                // before launching home
                mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {
                    @Override
                    public void onKeyguardExitResult(boolean success) {
                        if (success) {
                            final long origId = Binder.clearCallingIdentity();
                            try {
                                startDockOrHome(displayId, true /*fromHomeKey*/, awakenFromDreams);
                            } finally {
                                Binder.restoreCallingIdentity(origId);
                            }
                        }
                    }
                });
                return;
            }
        }

        // no keyguard stuff to worry about, just launch home!
        // If Recents is visible and the action is not from visible background users,
        // hide Recents and notify it to launch Home.
        if (mRecentsVisible
                && (!mVisibleBackgroundUsersEnabled || displayId == DEFAULT_DISPLAY)) {
            try {
                ActivityManager.getService().stopAppSwitches();
            } catch (RemoteException e) {}

            // Hide Recents and notify it to launch Home
            if (awakenFromDreams) {
                awakenDreams();
            }
            hideRecentApps(false, true);
        } else {
            // Otherwise, just launch Home
            startDockOrHome(displayId, true /*fromHomeKey*/, awakenFromDreams);
        }
    }

    @Override
    public void setRecentsVisibilityLw(boolean visible) {
        mRecentsVisible = visible;
    }

    @Override
    public void setPipVisibilityLw(boolean visible) {
        mPictureInPictureVisible = visible;
    }

    @Override
    public void setNavBarVirtualKeyHapticFeedbackEnabledLw(boolean enabled) {
        mNavBarVirtualKeyHapticFeedbackEnabled = enabled;
    }

    /**
     * Updates the occluded state of the Keyguard immediately via {@link IKeyguardService}.
     *
     * @param isOccluded Whether the Keyguard is occluded by another window.
     * @return Whether the flags have changed and we have to redo the layout.
     */
    private boolean setKeyguardOccludedLw(boolean isOccluded) {
        if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
        mKeyguardOccludedChanged = false;
        mPendingKeyguardOccluded = isOccluded;
        mKeyguardDelegate.setOccluded(isOccluded, true /* notify */);
        return mKeyguardDelegate.isShowing();
    }

    /** {@inheritDoc} */
    @Override
    public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
        // lid changed state
        final int newLidState = lidOpen ? LID_OPEN : LID_CLOSED;
        if (newLidState == mDefaultDisplayPolicy.getLidState()) {
            return;
        }

        mDefaultDisplayPolicy.setLidState(newLidState);
        applyLidSwitchState();
        updateRotation(true);

        if (lidOpen) {
            mWindowWakeUpPolicy.wakeUpFromLid();
        } else if (getLidBehavior() != LID_BEHAVIOR_SLEEP) {
            mPowerManager.userActivity(mInjector.getUptimeMillis(), false);
        }
    }

    @Override
    public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) {
        int lensCoverState = lensCovered ? CAMERA_LENS_COVERED : CAMERA_LENS_UNCOVERED;
        if (mCameraLensCoverState == lensCoverState) {
            return;
        }
        if (!mContext.getResources().getBoolean(
                R.bool.config_launchCameraOnCameraLensCoverToggle)) {
            return;
        }
        if (mCameraLensCoverState == CAMERA_LENS_COVERED &&
                lensCoverState == CAMERA_LENS_UNCOVERED) {
            Intent intent;
            final boolean keyguardActive = mKeyguardDelegate == null ? false :
                    mKeyguardDelegate.isShowing();
            if (keyguardActive) {
                intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
            } else {
                intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
            }
            mWindowWakeUpPolicy.wakeUpFromCameraCover(whenNanos / 1000000);
            startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
        }
        mCameraLensCoverState = lensCoverState;
    }

    void initializeHdmiState() {
        final int oldMask = StrictMode.allowThreadDiskReadsMask();
        try {
            initializeHdmiStateInternal();
        } finally {
            StrictMode.setThreadPolicyMask(oldMask);
        }
    }

    void initializeHdmiStateInternal() {
        boolean plugged = false;
        // watch for HDMI plug messages if the hdmi switch exists
        if (new File("/sys/devices/virtual/switch/hdmi/state").exists()) {
            mHDMIObserver.startObserving("DEVPATH=/devices/virtual/switch/hdmi");

            final String filename = "/sys/class/switch/hdmi/state";
            FileReader reader = null;
            try {
                reader = new FileReader(filename);
                char[] buf = new char[15];
                int n = reader.read(buf);
                if (n > 1) {
                    plugged = 0 != Integer.parseInt(new String(buf, 0, n - 1));
                }
            } catch (IOException ex) {
                Slog.w(TAG, "Couldn't read hdmi state from " + filename + ": " + ex);
            } catch (NumberFormatException ex) {
                Slog.w(TAG, "Couldn't read hdmi state from " + filename + ": " + ex);
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException ex) {
                    }
                }
            }
        } else {
            final List<ExtconUEventObserver.ExtconInfo> extcons =
                    ExtconUEventObserver.ExtconInfo.getExtconInfoForTypes(
                            new String[] {ExtconUEventObserver.ExtconInfo.EXTCON_HDMI});
            if (!extcons.isEmpty()) {
                // TODO: handle more than one HDMI
                HdmiVideoExtconUEventObserver observer = new HdmiVideoExtconUEventObserver();
                plugged = observer.init(extcons.get(0));
                mHDMIObserver = observer;
            } else if (localLOGV) {
                Slog.v(TAG, "Not observing HDMI plug state because HDMI was not found.");
            }
        }

        // This dance forces the code in setHdmiPlugged to run.
        // Always do this so the sticky intent is stuck (to false) if there is no hdmi.
        mDefaultDisplayPolicy.setHdmiPlugged(plugged, true /* force */);
    }

    // TODO(b/117479243): handle it in InputPolicy
    /** {@inheritDoc} */
    @Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        final int keyCode = event.getKeyCode();
        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
        boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
                || event.isWakeKey();
        boolean isKeyGestureTriggered = (policyFlags & FLAG_KEY_GESTURE_TRIGGERED) != 0;

        // There are key events that perform the operation as the current user,
        // and these should be ignored for visible background users.
        if (mVisibleBackgroundUsersEnabled
                && !KeyEvent.isVisibleBackgroundUserAllowedKey(keyCode)
                && !isKeyEventForCurrentUser(event.getDisplayId(), keyCode, null)) {
            return 0;
        }

        if (!mSystemBooted) {
            // If we have not yet booted, don't let key events do anything.
            // Exception: Wake and power key events are forwarded to PowerManager to allow it to
            // wake from quiescent mode during boot. On these key events we also explicitly turn on
            // the connected TV and switch HDMI input if we're a HDMI playback device.
            boolean shouldTurnOnTv = false;
            if (down && (keyCode == KeyEvent.KEYCODE_POWER
                    || keyCode == KeyEvent.KEYCODE_TV_POWER)) {
                wakeUpFromWakeKey(event);
                shouldTurnOnTv = true;
            } else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP)
                    && isWakeKeyWhenScreenOff(keyCode)) {
                wakeUpFromWakeKey(event);
                shouldTurnOnTv = true;
            }
            if (shouldTurnOnTv) {
                final HdmiControl hdmiControl = getHdmiControl();
                if (hdmiControl != null) {
                    hdmiControl.turnOnTv();
                }
            }
            return 0;
        }

	if (keyCode == KeyEvent.KEYCODE_POWER
 	       && down
 	       && event.getRepeatCount() == 0) {

 	   long now = SystemClock.uptimeMillis();

 	   if (mSecureLinePanicCounter == 0 ||
 	       (now - mSecureLineFirstPressTime) > SECURELINE_PANIC_WINDOW_MS) {

	        mSecureLineFirstPressTime = now;
	        mSecureLinePanicCounter = 1;

	    } else {

	        mSecureLinePanicCounter++;

	        if (mSecureLinePanicCounter >= SECURELINE_PANIC_PRESS_COUNT) {

	            // Reset BEFORE wipe
	            mSecureLinePanicCounter = 0;
	            mSecureLineFirstPressTime = 0;

	            Slog.e("SecureLinePanic", "PANIC BUTTON TRIGGERED");

	            secureLineTriggerPanicAsync();
	        }
	    }
	}

        final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
        final boolean canceled = event.isCanceled();
        final int displayId = event.getDisplayId();
        final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;

        if (DEBUG_INPUT) {
            // If screen is off then we treat the case where the keyguard is open but hidden
            // the same as if it were open and in front.
            // This will prevent any keys other than the power button from waking the screen
            // when the keyguard is hidden by another activity.
            final boolean keyguardActive = (mKeyguardDelegate != null
                    && (interactive ? isKeyguardShowingAndNotOccluded() :
                    mKeyguardDelegate.isShowing()));
            Log.d(TAG, "interceptKeyTq keycode=" + keyCode
                    + " interactive=" + interactive + " keyguardActive=" + keyguardActive
                    + " policyFlags=" + Integer.toHexString(policyFlags));
        }

        // Basic policy based on interactive state.
        int result;
        if (interactive || (isInjected && !isWakeKey)) {
            // When the device is interactive or the key is injected pass the
            // key to the application.
            result = ACTION_PASS_TO_USER;
            isWakeKey = false;

            if (interactive) {
                // If the screen is awake, but the button pressed was the one that woke the device
                // then don't pass it to the application
                if (keyCode == mPendingWakeKey && !down) {
                    result = 0;
                }
                // Reset the pending key
                mPendingWakeKey = PENDING_KEY_NULL;
            }
        } else if (shouldDispatchInputWhenNonInteractive(displayId, keyCode)) {
            // If we're currently dozing with the screen on and the keyguard showing, pass the key
            // to the application but preserve its wake key status to make sure we still move
            // from dozing to fully interactive if we would normally go from off to fully
            // interactive.
            result = ACTION_PASS_TO_USER;
            // Since we're dispatching the input, reset the pending key
            mPendingWakeKey = PENDING_KEY_NULL;
        } else {
            // When the screen is off and the key is not injected, determine whether
            // to wake the device but don't pass the key to the application.
            result = 0;
            if (isWakeKey && (!down || !isWakeKeyWhenScreenOff(keyCode))) {
                isWakeKey = false;
            }
            // Cache the wake key on down event so we can also avoid sending the up event to the app
            if (isWakeKey && down) {
                mPendingWakeKey = keyCode;
            }
        }

        // If the key would be handled globally, just return the result, don't worry about special
        // key processing.
        if (isValidGlobalKey(keyCode)
                && mGlobalKeyManager.shouldHandleGlobalKey(keyCode)) {
            // Dispatch if global key defined dispatchWhenNonInteractive.
            if (!interactive && isWakeKey && down
                    && mGlobalKeyManager.shouldDispatchFromNonInteractive(keyCode)) {
                mGlobalKeyManager.setBeganFromNonInteractive();
                result = ACTION_PASS_TO_USER;
                // Since we're dispatching the input, reset the pending key
                mPendingWakeKey = PENDING_KEY_NULL;
            }

            if (isWakeKey) {
                wakeUpFromWakeKey(event);
            }
            return result;
        }

        // Alternate TV power to power key for Android TV device.
        final HdmiControlManager hdmiControlManager = getHdmiControlManager();
        if (keyCode == KeyEvent.KEYCODE_TV_POWER && mHasFeatureLeanback
                && (hdmiControlManager == null || !hdmiControlManager.shouldHandleTvPowerKey())) {
            event = KeyEvent.obtain(
                    event.getDownTime(), event.getEventTime(),
                    event.getAction(), KeyEvent.KEYCODE_POWER,
                    event.getRepeatCount(), event.getMetaState(),
                    event.getDeviceId(), event.getScanCode(),
                    event.getFlags(), event.getSource(), event.getDisplayId(), null);
            return interceptKeyBeforeQueueing(event, policyFlags);
        }

        // This could prevent some wrong state in multi-displays environment,
        // the default display may turned off but interactive is true.
        final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
        final boolean isDefaultDisplayAwake = mDefaultDisplayPolicy.isAwake();
        final boolean interactiveAndAwake = interactive && isDefaultDisplayAwake;
        if (isKeyGestureTriggered) {
            // If key gesture is triggered outside policy, reset gesture handlers here
            mSingleKeyGestureDetector.reset();
        } else {
            if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                handleKeyGesture(event, interactiveAndAwake, isDefaultDisplayOn);
            }
        }

        // Enable haptics if down and virtual key without multiple repetitions. If this is a hard
        // virtual key such as a navigation bar button, only vibrate if flag is enabled.
        final boolean isNavBarVirtKey = ((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0);
        boolean useHapticFeedback = down
                && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0
                && (!isNavBarVirtKey || mNavBarVirtualKeyHapticFeedbackEnabled)
                && event.getRepeatCount() == 0;

        // Handle special keys.
        switch (keyCode) {
            case KeyEvent.KEYCODE_BACK: {
                notifyKeyGestureCompletedOnActionUp(event,
                        KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
                if (down) {
                    // There may have other embedded activities on the same Task. Try to move the
                    // focus before processing the back event.
                    mWindowManagerInternal.moveFocusToAdjacentEmbeddedActivityIfNeeded();
                    mBackKeyHandled = false;
                } else {
                    if (!hasLongPressOnBackBehavior()) {
                        mBackKeyHandled |= backKeyPress();
                    }
                    // Don't pass back press to app if we've already handled it via long press
                    if (mBackKeyHandled) {
                        result &= ~ACTION_PASS_TO_USER;
                    }
                }
                break;
            }

            case KeyEvent.KEYCODE_VOLUME_DOWN:
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_VOLUME_MUTE: {
                int gestureType = keyCode == KEYCODE_VOLUME_DOWN
                        ? KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_DOWN
                        : keyCode == KEYCODE_VOLUME_UP
                                ? KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_UP
                                : KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_MUTE;
                notifyKeyGestureCompletedOnActionDown(event, gestureType);
                if (down) {
                    sendSystemKeyToStatusBarAsync(event);

                    NotificationManager nm = getNotificationService();
                    if (nm != null && !mHandleVolumeKeysInWM) {
                        nm.silenceNotificationSound();
                    }

                    TelecomManager telecomManager = getTelecommService();
                    if (telecomManager != null && !mHandleVolumeKeysInWM) {
                        // When {@link #mHandleVolumeKeysInWM} is set, volume key events
                        // should be dispatched to WM.
                        if (telecomManager.isRinging()) {
                            // If an incoming call is ringing, either VOLUME key means
                            // "silence ringer".  We handle these keys here, rather than
                            // in the InCallScreen, to make sure we'll respond to them
                            // even if the InCallScreen hasn't come to the foreground yet.
                            // Look for the DOWN event here, to agree with the "fallback"
                            // behavior in the InCallScreen.
                            Log.i(TAG, "interceptKeyBeforeQueueing:"
                                  + " VOLUME key-down while ringing: Silence ringer!");

                            // Silence the ringer.  (It's safe to call this
                            // even if the ringer has already been silenced.)
                            telecomManager.silenceRinger();

                            // And *don't* pass this key thru to the current activity
                            // (which is probably the InCallScreen.)
                            result &= ~ACTION_PASS_TO_USER;
                            break;
                        }
                    }
                    int audioMode = AudioManager.MODE_NORMAL;
                    try {
                        audioMode = getAudioService().getMode();
                    } catch (Exception e) {
                        Log.e(TAG, "Error getting AudioService in interceptKeyBeforeQueueing.", e);
                    }
                    boolean isInCall = (telecomManager != null && telecomManager.isInCall()) ||
                            audioMode == AudioManager.MODE_IN_COMMUNICATION;
                    if (isInCall && (result & ACTION_PASS_TO_USER) == 0) {
                        // If we are in call but we decided not to pass the key to
                        // the application, just pass it to the session service.
                        MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
                                event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
                        break;
                    }
                }
                if (mUseTvRouting || mHandleVolumeKeysInWM) {
                    // Defer special key handlings to
                    // {@link interceptKeyBeforeDispatching()}.
                    result |= ACTION_PASS_TO_USER;
                } else if ((result & ACTION_PASS_TO_USER) == 0) {
                    // If we aren't passing to the user and no one else
                    // handled it send it to the session manager to
                    // figure out.
                    MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
                            event, AudioManager.USE_DEFAULT_STREAM_TYPE, true);
                }
                break;
            }

            case KeyEvent.KEYCODE_ENDCALL: {
                result &= ~ACTION_PASS_TO_USER;
                if (down) {
                    TelecomManager telecomManager = getTelecommService();
                    boolean hungUp = false;
                    if (telecomManager != null) {
                        hungUp = telecomManager.endCall();
                    }
                    if (interactive && !hungUp) {
                        mEndCallKeyHandled = false;
                        mHandler.postDelayed(mEndCallLongPress,
                                ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
                    } else {
                        mEndCallKeyHandled = true;
                    }
                } else {
                    if (!mEndCallKeyHandled) {
                        mHandler.removeCallbacks(mEndCallLongPress);
                        if (!canceled) {
                            if ((mEndcallBehavior
                                    & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) {
                                if (goHome()) {
                                    break;
                                }
                            }
                            if ((mEndcallBehavior
                                    & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
                                goToSleep(event.getEventTime(),
                                        PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                                isWakeKey = false;
                            }
                        }
                    }
                }
                break;
            }

            case KeyEvent.KEYCODE_TV_POWER: {
                notifyKeyGestureCompletedOnActionUp(event,
                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_POWER);
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down && hdmiControlManager != null) {
                    hdmiControlManager.toggleAndFollowTvPower();
                }
                break;
            }

            case KeyEvent.KEYCODE_POWER: {
                notifyKeyGestureCompletedOnActionUp(event,
                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_POWER);
                EventLogTags.writeInterceptPower(
                        KeyEvent.actionToString(event.getAction()),
                        mPowerKeyHandled ? 1 : 0,
                        mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));
                // Any activity on the power button stops the accessibility shortcut
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down) {
                    interceptPowerKeyDown(event, interactiveAndAwake, isKeyGestureTriggered);
                } else {
                    interceptPowerKeyUp(event, canceled);
                }
                break;
            }

            case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN:
                // fall through
            case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP:
                // fall through
            case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT:
                // fall through
            case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT: {
                notifyKeyGestureCompletedOnActionUp(event,
                        KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_NAVIGATION);
                result &= ~ACTION_PASS_TO_USER;
                interceptSystemNavigationKey(event);
                break;
            }

            case KeyEvent.KEYCODE_SLEEP: {
                notifyKeyGestureCompletedOnActionUp(event,
                        KeyGestureEvent.KEY_GESTURE_TYPE_SLEEP);
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false;
                if (!mPowerManager.isInteractive()) {
                    useHapticFeedback = false; // suppress feedback if already non-interactive
                }
                if (down) {
                    sleepPress();
                } else {
                    sleepRelease(event.getEventTime());
                }
                sendSystemKeyToStatusBarAsync(event);
                break;
            }

            case KeyEvent.KEYCODE_SOFT_SLEEP: {
                notifyKeyGestureCompletedOnActionUp(event,
                        KeyGestureEvent.KEY_GESTURE_TYPE_SLEEP);
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false;
                if (!down) {
                    mPowerManagerInternal.setUserInactiveOverrideFromWindowManager();
                }
                sendSystemKeyToStatusBarAsync(event);
                break;
            }

            case KeyEvent.KEYCODE_WAKEUP: {
                notifyKeyGestureCompletedOnActionUp(event,
                        KeyGestureEvent.KEY_GESTURE_TYPE_WAKEUP);
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = true;
                break;
            }

            case KeyEvent.KEYCODE_MUTE:
                result &= ~ACTION_PASS_TO_USER;
                if (down && event.getRepeatCount() == 0) {
                    notifyKeyGestureCompleted(event,
                            KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_MUTE);
                    toggleMicrophoneMuteFromKey();
                }
                break;
            case KeyEvent.KEYCODE_MEDIA_PLAY:
            case KeyEvent.KEYCODE_MEDIA_PAUSE:
            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
            case KeyEvent.KEYCODE_HEADSETHOOK:
            case KeyEvent.KEYCODE_MEDIA_STOP:
            case KeyEvent.KEYCODE_MEDIA_NEXT:
            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
            case KeyEvent.KEYCODE_MEDIA_REWIND:
            case KeyEvent.KEYCODE_MEDIA_RECORD:
            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
            case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
                notifyKeyGestureCompletedOnActionUp(event,
                        KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY);
                if (MediaSessionLegacyHelper.getHelper(mContext).isGlobalPriorityActive()) {
                    // If the global session is active pass all media keys to it
                    // instead of the active window.
                    result &= ~ACTION_PASS_TO_USER;
                }
                if ((result & ACTION_PASS_TO_USER) == 0) {
                    // Only do this if we would otherwise not pass it to the user. In that
                    // case, the PhoneWindow class will do the same thing, except it will
                    // only do it if the showing app doesn't process the key on its own.
                    // Note that we need to make a copy of the key event here because the
                    // original key event will be recycled when we return.
                    mBroadcastWakeLock.acquire();
                    Message msg = mHandler.obtainMessage(MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK,
                            new KeyEvent(event));
                    msg.setAsynchronous(true);
                    msg.sendToTarget();
                }
                break;
            }

            case KeyEvent.KEYCODE_CALL: {
                if (down) {
                    TelecomManager telecomManager = getTelecommService();
                    if (telecomManager != null) {
                        if (telecomManager.isRinging()) {
                            Log.i(TAG, "interceptKeyBeforeQueueing:"
                                  + " CALL key-down while ringing: Answer the call!");
                            telecomManager.acceptRingingCall();

                            // And *don't* pass this key thru to the current activity
                            // (which is presumably the InCallScreen.)
                            result &= ~ACTION_PASS_TO_USER;
                        }
                    }
                }
                break;
            }
            case KeyEvent.KEYCODE_ASSIST: {
                final boolean longPressed = event.getRepeatCount() > 0;
                if (down && !longPressed) {
                    Message msg = mHandler.obtainMessage(MSG_LAUNCH_ASSIST, event.getDeviceId(),
                            0 /* unused */, event.getEventTime() /* eventTime */);
                    msg.setAsynchronous(true);
                    msg.sendToTarget();
                    notifyKeyGestureCompleted(event,
                            KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT);
                }
                result &= ~ACTION_PASS_TO_USER;
                break;
            }
            case KeyEvent.KEYCODE_VOICE_ASSIST: {
                if (!down) {
                    mBroadcastWakeLock.acquire();
                    Message msg = mHandler.obtainMessage(MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK);
                    msg.setAsynchronous(true);
                    msg.sendToTarget();
                    notifyKeyGestureCompleted(event,
                            KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT);
                }
                result &= ~ACTION_PASS_TO_USER;
                break;
            }
            case KeyEvent.KEYCODE_WINDOW: {
                if (mShortPressOnWindowBehavior == SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE) {
                    if (mPictureInPictureVisible) {
                        // Consumes the key only if picture-in-picture is visible to show
                        // picture-in-picture control menu. This gives a chance to the foreground
                        // activity to customize PIP key behavior.
                        if (!down) {
                            showPictureInPictureMenu(event);
                        }
                        result &= ~ACTION_PASS_TO_USER;
                    }
                }
                break;
            }
            case KeyEvent.KEYCODE_STEM_PRIMARY: {
                if (down && event.getRepeatCount() == 0 && (result & ACTION_PASS_TO_USER) == 0) {
                    // We've decided not to pass key to user at queueing stage. Make the gesture
                    // executable.
                    setDeferredKeyActionsExecutableAsync(keyCode, event.getDownTime());
                }
                break;
            }
            case KeyEvent.KEYCODE_VIDEO_APP_1:
            case KeyEvent.KEYCODE_VIDEO_APP_2:
            case KeyEvent.KEYCODE_VIDEO_APP_3:
            case KeyEvent.KEYCODE_VIDEO_APP_4:
            case KeyEvent.KEYCODE_VIDEO_APP_5:
            case KeyEvent.KEYCODE_VIDEO_APP_6:
            case KeyEvent.KEYCODE_VIDEO_APP_7:
            case KeyEvent.KEYCODE_VIDEO_APP_8:
            case KeyEvent.KEYCODE_FEATURED_APP_1:
            case KeyEvent.KEYCODE_FEATURED_APP_2:
            case KeyEvent.KEYCODE_FEATURED_APP_3:
            case KeyEvent.KEYCODE_FEATURED_APP_4:
            case KeyEvent.KEYCODE_DEMO_APP_1:
            case KeyEvent.KEYCODE_DEMO_APP_2:
            case KeyEvent.KEYCODE_DEMO_APP_3:
            case KeyEvent.KEYCODE_DEMO_APP_4: {
                // Just drop if keys are not intercepted for direct key.
                result &= ~ACTION_PASS_TO_USER;
                break;
            }
            case KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY:
            case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
            case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
            case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL: {
                Slog.i(TAG, "Stylus buttons event: " + keyCode + " received. Should handle event? "
                        + mStylusButtonsEnabled);
                if (mStylusButtonsEnabled) {
                    sendSystemKeyToStatusBarAsync(event);
                }
                result &= ~ACTION_PASS_TO_USER;
                break;
            }
            case KeyEvent.KEYCODE_MACRO_1:
            case KeyEvent.KEYCODE_MACRO_2:
            case KeyEvent.KEYCODE_MACRO_3:
            case KeyEvent.KEYCODE_MACRO_4:
                result &= ~ACTION_PASS_TO_USER;
                break;
            case KeyEvent.KEYCODE_DICTATE:
            case KeyEvent.KEYCODE_NEW:
            case KeyEvent.KEYCODE_CLOSE:
            case KeyEvent.KEYCODE_PRINT:
            case KeyEvent.KEYCODE_F13:
            case KeyEvent.KEYCODE_F14:
            case KeyEvent.KEYCODE_F15:
            case KeyEvent.KEYCODE_F16:
            case KeyEvent.KEYCODE_F17:
            case KeyEvent.KEYCODE_F18:
            case KeyEvent.KEYCODE_F19:
            case KeyEvent.KEYCODE_F20:
            case KeyEvent.KEYCODE_F21:
            case KeyEvent.KEYCODE_F22:
            case KeyEvent.KEYCODE_F23:
            case KeyEvent.KEYCODE_F24:
                if (!enableNew25q2Keycodes()) {
                    result &= ~ACTION_PASS_TO_USER;
                }
                break;
        }

        if (useHapticFeedback) {
            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, "Virtual Key - Press");
        }

        if (isWakeKey) {
            wakeUpFromWakeKey(event);
        }

        // If the key event is targeted to a specific display, then the user is interacting with
        // that display. Therefore, try to give focus to the display that the user is interacting
        // with.
        if ((result & ACTION_PASS_TO_USER) != 0 && displayId != INVALID_DISPLAY
                && displayId != mTopFocusedDisplayId) {
            // An event is targeting a non-focused display. Move the display to top so that
            // it can become the focused display to interact with the user.
            // This should be done asynchronously, once the focus logic is fully moved to input
            // from windowmanager. Currently, we need to ensure the setInputWindows completes,
            // which would force the focus event to be queued before the current key event.
            // TODO(b/70668286): post call to 'moveDisplayToTop' to mHandler instead
            Log.i(TAG, "Attempting to move non-focused display " + displayId + " to top "
                    + "because a key is targeting it");
            mWindowManagerFuncs.moveDisplayToTopIfAllowed(displayId);
        }

        return result;
    }

    private void handleKeyGesture(KeyEvent event, boolean interactive, boolean defaultDisplayOn) {
        if (event.getKeyCode() == KEYCODE_POWER && event.getAction() == KeyEvent.ACTION_DOWN) {
            mPowerKeyHandled = handleCameraGesture(event, interactive);
            if (mPowerKeyHandled) {
                // handled by camera gesture.
                mSingleKeyGestureDetector.reset();
                return;
            }
        }

        mSingleKeyGestureDetector.interceptKey(event, interactive, defaultDisplayOn);
    }

    // The camera gesture will be detected by GestureLauncherService.
    private boolean handleCameraGesture(KeyEvent event, boolean interactive) {
        // camera gesture.
        if (mGestureLauncherService == null) {
            return false;
        }
        mPowerButtonLaunchGestureTriggered = false;
        final MutableBoolean outLaunched = new MutableBoolean(false);
        final boolean intercept =
                mGestureLauncherService.interceptPowerKeyDown(event, interactive, outLaunched);
        if (!outLaunched.value) {
            // If GestureLauncherService intercepted the power key, but didn't launch camera app,
            // we should still return the intercept result. This prevents the single key gesture
            // detector from processing the power key later on.
            return intercept;
        }
        mPowerButtonLaunchGestureTriggered = true;
        if (mRequestedOrSleepingDefaultDisplay) {
            mPowerButtonLaunchGestureTriggeredDuringGoingToSleep = true;
            // Wake device up early to prevent display doing redundant turning off/on stuff.
            mWindowWakeUpPolicy.wakeUpFromPowerKeyCameraGesture();
        }
        return true;
    }

    /**
     * Handle statusbar expansion events.
     * @param event
     */
    private void interceptSystemNavigationKey(KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_UP) {
            if (!mAccessibilityManager.isEnabled()
                    || !mAccessibilityManager.sendFingerprintGesture(event.getKeyCode())) {
                if (mSystemNavigationKeysEnabled) {
                    sendSystemKeyToStatusBarAsync(event);
                }
            }
        }
    }

    /**
     * Notify the StatusBar that a system key was pressed.
     */
    private void sendSystemKeyToStatusBar(KeyEvent key) {
        if (!isKeyEventForCurrentUser(key.getDisplayId(), key.getKeyCode(), "handleSystemKey")) {
            return;
        }
        IStatusBarService statusBar = getStatusBarService();
        if (statusBar != null) {
            try {
                statusBar.handleSystemKey(key);
            } catch (RemoteException e) {
                // Oh well.
            }
        }
    }

    /**
     * Notify the StatusBar that a system key was pressed without blocking the current thread.
     */
    private void sendSystemKeyToStatusBarAsync(KeyEvent keyEvent) {
        // Make a copy because the event may be recycled.
        Message message = mHandler.obtainMessage(MSG_SYSTEM_KEY_PRESS, KeyEvent.obtain(keyEvent));
        message.setAsynchronous(true);
        mHandler.sendMessage(message);
    }

    /**
     * Returns true if the key can have global actions attached to it.
     * We reserve all power management keys for the system since they require
     * very careful handling.
     */
    private static boolean isValidGlobalKey(int keyCode) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_POWER:
            case KeyEvent.KEYCODE_WAKEUP:
            case KeyEvent.KEYCODE_SLEEP:
                return false;
            default:
                return true;
        }
    }

    /**
     * When the screen is off we ignore some keys that might otherwise typically
     * be considered wake keys.  We filter them out here.
     *
     * {@link KeyEvent#KEYCODE_POWER} is notably absent from this list because it
     * is always considered a wake key.
     */
    private boolean isWakeKeyWhenScreenOff(int keyCode) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_DPAD_UP:
            case KeyEvent.KEYCODE_DPAD_DOWN:
            case KeyEvent.KEYCODE_DPAD_LEFT:
            case KeyEvent.KEYCODE_DPAD_RIGHT:
            case KeyEvent.KEYCODE_DPAD_CENTER:
                return mWakeOnDpadKeyPress;

            case KeyEvent.KEYCODE_ASSIST:
                return mWakeOnAssistKeyPress;

            case KeyEvent.KEYCODE_BACK:
                return mWakeOnBackKeyPress;

            case KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY:
            case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
            case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
            case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL:
                return mStylusButtonsEnabled;
        }

        return true;
    }

    // TODO(b/117479243): handle it in InputPolicy
    /** {@inheritDoc} */
    @Override
    public int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action,
            long whenNanos, int policyFlags) {
        if ((policyFlags & FLAG_WAKE) != 0) {
            if (mWindowWakeUpPolicy.wakeUpFromMotion(displayId, whenNanos / 1000000, source,
                    action == MotionEvent.ACTION_DOWN, mDeviceGoingToSleep)) {
                // Woke up. Pass motion events to user.
                return ACTION_PASS_TO_USER;
            }
        }

        if (shouldDispatchInputWhenNonInteractive(displayId, KEYCODE_UNKNOWN)) {
            return ACTION_PASS_TO_USER;
        }

        return 0;
    }

    private boolean shouldDispatchInputWhenNonInteractive(int displayId, int keyCode) {
        // Apply the default display policy to unknown displays as well.
        final boolean isDefaultDisplay = displayId == DEFAULT_DISPLAY
                || displayId == INVALID_DISPLAY;
        final Display display = isDefaultDisplay
                ? mDefaultDisplay
                : mDisplayManager.getDisplay(displayId);
        final boolean displayOff = (display == null
                || display.getState() == STATE_OFF);

        if (displayOff) {
            return false;
        }

        // Send events to keyguard while the screen is on and it's showing.
        if (isKeyguardShowingAndNotOccluded()) {
            return true;
        }

        // TODO(b/123372519): Refine when dream can support multi display.
        if (isDefaultDisplay) {
            // Send events to a dozing dream since the dream is in control of the state of the
            // screen.
            IDreamManager dreamManager = getDreamManager();

            try {
                if (dreamManager != null && dreamManager.isDreaming()) {
                    return true;
                }
            } catch (RemoteException e) {
                Slog.e(TAG, "RemoteException when checking if dreaming", e);
            }
        }

        // Otherwise, consume events since the user can't see what is being
        // interacted with.
        return false;
    }

    // pre-condition: event.getKeyCode() is one of KeyEvent.KEYCODE_VOLUME_UP,
    //                                   KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_MUTE
    private void dispatchDirectAudioEvent(KeyEvent event) {
        // When System Audio Mode is off, volume keys received by AVR can be either consumed by AVR
        // or forwarded to the TV. It's up to Amplifier manufacturer’s implementation.
        HdmiControlManager hdmiControlManager = getHdmiControlManager();
        if (null != hdmiControlManager
                && !hdmiControlManager.getSystemAudioMode()
                && shouldCecAudioDeviceForwardVolumeKeysSystemAudioModeOff()) {
            HdmiAudioSystemClient audioSystemClient = hdmiControlManager.getAudioSystemClient();
            if (audioSystemClient != null) {
                audioSystemClient.sendKeyEvent(
                        event.getKeyCode(), event.getAction() == KeyEvent.ACTION_DOWN);
                return;
            }
        }
        try {
            getAudioService().handleVolumeKey(event, mUseTvRouting,
                    mContext.getOpPackageName(), TAG);
        } catch (Exception e) {
            Log.e(TAG, "Error dispatching volume key in handleVolumeKey for event:"
                    + event, e);
        }
    }

    @Nullable
    private HdmiControlManager getHdmiControlManager() {
        if (!mHasFeatureHdmiCec) {
            return null;
        }
        return (HdmiControlManager) mContext.getSystemService(HdmiControlManager.class);
    }

    private boolean shouldCecAudioDeviceForwardVolumeKeysSystemAudioModeOff() {
        return RoSystemProperties.CEC_AUDIO_DEVICE_FORWARD_VOLUME_KEYS_SYSTEM_AUDIO_MODE_OFF;
    }

    void dispatchMediaKeyWithWakeLock(KeyEvent event) {
        if (DEBUG_INPUT) {
            Slog.d(TAG, "dispatchMediaKeyWithWakeLock: " + event);
        }

        if (mHavePendingMediaKeyRepeatWithWakeLock) {
            if (DEBUG_INPUT) {
                Slog.d(TAG, "dispatchMediaKeyWithWakeLock: canceled repeat");
            }

            mHandler.removeMessages(MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK);
            mHavePendingMediaKeyRepeatWithWakeLock = false;
            mBroadcastWakeLock.release(); // pending repeat was holding onto the wake lock
        }

        dispatchMediaKeyWithWakeLockToAudioService(event);

        if (event.getAction() == KeyEvent.ACTION_DOWN
                && event.getRepeatCount() == 0) {
            mHavePendingMediaKeyRepeatWithWakeLock = true;

            Message msg = mHandler.obtainMessage(
                    MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK, event);
            msg.setAsynchronous(true);
            mHandler.sendMessageDelayed(msg, ViewConfiguration.getKeyRepeatTimeout());
        } else {
            mBroadcastWakeLock.release();
        }
    }

    void dispatchMediaKeyRepeatWithWakeLock(KeyEvent event) {
        mHavePendingMediaKeyRepeatWithWakeLock = false;

        KeyEvent repeatEvent = KeyEvent.changeTimeRepeat(event,
                mInjector.getUptimeMillis(), 1, event.getFlags() | KeyEvent.FLAG_LONG_PRESS);
        if (DEBUG_INPUT) {
            Slog.d(TAG, "dispatchMediaKeyRepeatWithWakeLock: " + repeatEvent);
        }

        dispatchMediaKeyWithWakeLockToAudioService(repeatEvent);
        mBroadcastWakeLock.release();
    }

    void dispatchMediaKeyWithWakeLockToAudioService(KeyEvent event) {
        if (mActivityManagerInternal.isSystemReady()) {
            MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(event, true);
        }
    }

    void launchVoiceAssistWithWakeLock() {
        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST);

        final Intent voiceIntent;
        if (!keyguardOn()) {
            voiceIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
        } else {
            DeviceIdleManager dim = mContext.getSystemService(DeviceIdleManager.class);
            if (dim != null) {
                dim.endIdle("voice-search");
            }
            voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
            voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, true);
        }
        startActivityAsUser(voiceIntent, UserHandle.CURRENT_OR_SELF);
        mBroadcastWakeLock.release();
    }

    BroadcastReceiver mDockReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())) {
                mDefaultDisplayPolicy.setDockMode(intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
                        Intent.EXTRA_DOCK_STATE_UNDOCKED));
            } else {
                try {
                    IUiModeManager uiModeService = IUiModeManager.Stub.asInterface(
                            ServiceManager.getService(Context.UI_MODE_SERVICE));
                    mUiMode = uiModeService.getCurrentModeType(DEFAULT_DISPLAY);
                } catch (RemoteException e) {
                }
            }
            updateRotation(true);
            mDefaultDisplayRotation.updateOrientationListener();
        }
    };

    BroadcastReceiver mMultiuserReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
                // tickle the settings observer: this first ensures that we're
                // observing the relevant settings for the newly-active user,
                // and then updates our own bookkeeping based on the now-
                // current user.
                mSettingsObserver.onChange(false);
                mDefaultDisplayRotation.onUserSwitch();
                mWindowManagerFuncs.onUserSwitched();
            }
        }
    };

    BroadcastReceiver mBluetoothHidReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (bluetoothWakeupStateCheck() && !SystemProperties.getBoolean(
                    "bluetooth.power.suspend.hid_wake_up.enabled", false)) {
                Slog.d(TAG, "Bluetooth HID wake up disabled.");
                return;
            }
            if (ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
                Integer newState = (Integer) intent.getExtra(BluetoothProfile.EXTRA_STATE);
                Integer prevState = (Integer) intent.getExtra(
                        BluetoothProfile.EXTRA_PREVIOUS_STATE);
                final boolean interactive = mDefaultDisplayPolicy.isAwake();
                if (bluetoothWakeupStateCheck()
                        && (newState == null || prevState == null || prevState.equals(newState))) {
                    if (DEBUG_WAKEUP) {
                        Slog.w(TAG, "Bluetooth connection state does not change: " + intent);
                    }
                    return;
                }
                if (newState != null && !interactive && newState.equals(STATE_CONNECTED)) {
                    mWindowWakeUpPolicy.wakeUpFromBluetooth();
                }
            }
        }
    };

    @Override
    public void startedWakingUpGlobal(@WakeReason int reason) {

    }

    @Override
    public void finishedWakingUpGlobal(@WakeReason int reason) {

        try {

            final IBinder b = ServiceManager.getService("secureline");

            if (b != null) {

                final ISecureLineManager sl =
                        ISecureLineManager.Stub.asInterface(b);

                sl.notifyDeviceUnlocked();
            }

        } catch (Throwable t) {
            Slog.e("SecureLine", "Inactivity unlock hook failed", t);
        }
    }


    @Override
    public void startedGoingToSleepGlobal(@PowerManager.GoToSleepReason int reason) {
        mDeviceGoingToSleep = true;

        // ===============================
        // SecureLine Inactivity Hook
        // ===============================

        try {

            final IBinder b = ServiceManager.getService("secureline");

            if (b != null) {

                final ISecureLineManager sl =
                        ISecureLineManager.Stub.asInterface(b);

                sl.notifyDeviceLocked();
            }

        } catch (Throwable t) {
            Slog.e("SecureLine", "Inactivity lock hook failed", t);
        }
    }

    @Override
    public void finishedGoingToSleepGlobal(@PowerManager.GoToSleepReason int reason) {
        mDeviceGoingToSleep = false;
    }

    // Both the default and default adjacent groups should be non interactive
    private boolean isReadyToSignalSleep(int displayGroupId) {
        if (!com.android.server.display.feature.flags.Flags.separateTimeouts()) {
            return displayGroupId == Display.DEFAULT_DISPLAY_GROUP;
        }

        // We only care about default and default-adjacent groups
        if (displayGroupId != Display.DEFAULT_DISPLAY_GROUP
                && !mPowerManagerInternal.isDefaultGroupAdjacent(displayGroupId)) {
            if (com.android.server.power.feature.flags.Flags.extraLoggingSeparateTimeout()) {
                Slog.i(TAG, "Not signalling isReadyToSignalSleep because it's a non default "
                        + "adjacent group " + displayGroupId);
            }
            return false;
        }

        boolean areAllDefaultAdjacentGroupsNonInteractive =
                !mPowerManagerInternal.isAnyDefaultAdjacentGroupInteractive();
        boolean isDefaultGroupNonInteractive =
                !mPowerManagerInternal.isGroupInteractive(Display.DEFAULT_DISPLAY_GROUP);
        if (com.android.server.power.feature.flags.Flags.extraLoggingSeparateTimeout()) {
            Slog.i(TAG, "Started going to sleep check status for group " + displayGroupId
                    + " : "
                    + (areAllDefaultAdjacentGroupsNonInteractive && isDefaultGroupNonInteractive)
                    + " areAllDefaultAdjacentGroupsNonInteractive "
                    + areAllDefaultAdjacentGroupsNonInteractive
                    + " isDefaultGroupNonInteractive "
                    + isDefaultGroupNonInteractive);
        }
        return areAllDefaultAdjacentGroupsNonInteractive && isDefaultGroupNonInteractive;
    }

    private boolean shouldFinishSleeping(int displayGroupId) {
        if (!com.android.server.display.feature.flags.Flags.separateTimeouts()) {
            return displayGroupId == Display.DEFAULT_DISPLAY_GROUP;
        }

        return mPendingSleepingGroup == displayGroupId;
    }

    private boolean shouldFinishWakingUp(int displayGroupId) {
        if (!com.android.server.display.feature.flags.Flags.separateTimeouts()) {
            return displayGroupId == Display.DEFAULT_DISPLAY_GROUP;
        }

        return mPendingWakeupGroup == displayGroupId;
    }

    private void setPendingSleepingGroup(int displayGroupId) {
        if (displayGroupId != Display.INVALID_DISPLAY_GROUP
                && mPendingSleepingGroup != Display.INVALID_DISPLAY_GROUP) {
            Log.wtf(TAG, "Unexpected pending sleeping group. Requested Group "
                    + displayGroupId + " Current Sleeping group " + mPendingSleepingGroup);
        }
        mPendingSleepingGroup = displayGroupId;
    }

    private void setPendingWakingUpGroup(int displayGroupId) {
        if (displayGroupId != Display.INVALID_DISPLAY_GROUP
                && mPendingWakeupGroup != Display.INVALID_DISPLAY_GROUP) {
            Log.wtf(TAG, "Unexpected pending wakeup group. Requested Group "
                    + displayGroupId + " Current Waking up group " + mPendingWakeupGroup);
        }
        mPendingWakeupGroup = displayGroupId;
    }

    // Either of the default or default adjacent groups should be interactive
    private boolean isReadyToSignalWakeup(int displayGroupId) {
        if (!com.android.server.display.feature.flags.Flags.separateTimeouts()) {
            return displayGroupId == Display.DEFAULT_DISPLAY_GROUP;
        }

        // We only care about default and default-adjacent groups
        if (displayGroupId != Display.DEFAULT_DISPLAY_GROUP
                && !mPowerManagerInternal.isDefaultGroupAdjacent(displayGroupId)) {
            return false;
        }

        boolean isAnyDefaultAdjacentGroupInteractive =
                mPowerManagerInternal.isAnyDefaultAdjacentGroupInteractive();
        boolean isDefaultGroupInteractive = mPowerManagerInternal
                .isGroupInteractive(DEFAULT_DISPLAY);
        return isAnyDefaultAdjacentGroupInteractive || isDefaultGroupInteractive;
    }

    // Called on the PowerManager's Notifier thread.
    @Override
    public void startedGoingToSleep(int displayGroupId,
            @PowerManager.GoToSleepReason int pmSleepReason) {
        if (!isReadyToSignalSleep(displayGroupId)) {
            return;
        }

        if (DEBUG_WAKEUP) {
            Slog.i(TAG, "Started going to sleep... (groupId=" + displayGroupId + " why="
                    + WindowManagerPolicyConstants.offReasonToString(
                            WindowManagerPolicyConstants.translateSleepReasonToOffReason(
                                    pmSleepReason)) + ")");
        }

        mRequestedOrSleepingDefaultDisplay = true;
        mIsGoingToSleep = true;
        setPendingSleepingGroup(displayGroupId);

        if (mKeyguardDelegate != null) {
            if (com.android.server.power.feature.flags.Flags.extraLoggingSeparateTimeout()) {
                Slog.i(TAG, "Notifying keyguard about onGoingToSleep displayGroupId "
                        + displayGroupId);
            }
            mKeyguardDelegate.onStartedGoingToSleep(pmSleepReason);
        } else {
            if (com.android.server.power.feature.flags.Flags.extraLoggingSeparateTimeout()) {
                Slog.i(TAG, "Not notifying keyguard about onGoingToSleep displayGroupId "
                        + displayGroupId);
            }
        }
    }

    // Called on the PowerManager's Notifier thread.
    @Override
    public void finishedGoingToSleep(int displayGroupId,
            @PowerManager.GoToSleepReason int pmSleepReason) {
        if (!shouldFinishSleeping(displayGroupId)) {
            return;
        }

        EventLogTags.writeScreenToggled(0);
        if (DEBUG_WAKEUP) {
            Slog.i(TAG, "Finished going to sleep... (groupId=" + displayGroupId + " why="
                    + WindowManagerPolicyConstants.offReasonToString(
                            WindowManagerPolicyConstants.translateSleepReasonToOffReason(
                                    pmSleepReason)) + ")");
        }
        MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000);

        mRequestedOrSleepingDefaultDisplay = false;
        mIsGoingToSleep = false;
        setPendingSleepingGroup(Display.INVALID_DISPLAY_GROUP);

        mDefaultDisplayPolicy.setAwake(false);

        // We must get this work done here because the power manager will drop
        // the wake lock and let the system suspend once this function returns.
        synchronized (mLock) {
            updateWakeGestureListenerLp();
            updateLockScreenTimeout();
        }
        mDefaultDisplayRotation.updateOrientationListener();

        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.onFinishedGoingToSleep(pmSleepReason,
                    mPowerButtonLaunchGestureTriggeredDuringGoingToSleep);
        }
        if (mDisplayFoldController != null) {
            mDisplayFoldController.finishedGoingToSleep();
        }
        mPowerButtonLaunchGestureTriggeredDuringGoingToSleep = false;
        mPowerButtonLaunchGestureTriggered = false;
    }

    // Called on the PowerManager's Notifier thread.
    @Override
    public void startedWakingUp(int displayGroupId, @WakeReason int pmWakeReason) {
        if (DEBUG_WAKEUP) {
            Slog.i(TAG, "Started waking up... (groupId=" + displayGroupId + " why="
                    + WindowManagerPolicyConstants.onReasonToString(
                    WindowManagerPolicyConstants.translateWakeReasonToOnReason(
                            pmWakeReason)) + ")");
        }

        if (!isReadyToSignalWakeup(displayGroupId)) {
            return;
        }

        EventLogTags.writeScreenToggled(1);

        mIsGoingToSleep = false;
        setPendingWakingUpGroup(displayGroupId);
        mDefaultDisplayPolicy.setAwake(true);

        // Since goToSleep performs these functions synchronously, we must
        // do the same here.  We cannot post this work to a handler because
        // that might cause it to become reordered with respect to what
        // may happen in a future call to goToSleep.
        synchronized (mLock) {
            updateWakeGestureListenerLp();
            updateLockScreenTimeout();
        }
        mDefaultDisplayRotation.updateOrientationListener();

        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.onStartedWakingUp(pmWakeReason, mPowerButtonLaunchGestureTriggered);
        }

        mPowerButtonLaunchGestureTriggered = false;
    }

    // Called on the PowerManager's Notifier thread.
    @Override
    public void finishedWakingUp(int displayGroupId, @WakeReason int pmWakeReason) {
        if (DEBUG_WAKEUP) {
            Slog.i(TAG, "Finished waking up... (groupId=" + displayGroupId + " why="
                    + WindowManagerPolicyConstants.onReasonToString(
                            WindowManagerPolicyConstants.translateWakeReasonToOnReason(
                                    pmWakeReason)) + ")");
        }

        if (!shouldFinishWakingUp(displayGroupId)) {
            return;
        }

        setPendingWakingUpGroup(Display.INVALID_DISPLAY_GROUP);
        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.onFinishedWakingUp();
        }
        if (mDisplayFoldController != null) {
            mDisplayFoldController.finishedWakingUp();
        }
    }

    private boolean shouldWakeUpWithHomeIntent() {
        if (mWakeUpToLastStateTimeout <= 0) {
            return false;
        }

        final long sleepDurationRealtime =
                mPowerManagerInternal.getLastWakeup().sleepDurationRealtime;
        if (DEBUG_WAKEUP) {
            Log.i(TAG, "shouldWakeUpWithHomeIntent: sleepDurationRealtime= " + sleepDurationRealtime
                    + " mWakeUpToLastStateTimeout= " + mWakeUpToLastStateTimeout);
        }
        return sleepDurationRealtime > mWakeUpToLastStateTimeout;
    }

    private void wakeUpFromWakeKey(KeyEvent event) {
        if (!isKeyEventForCurrentUser(
                event.getDisplayId(), event.getKeyCode(), "wakeUpFromWakeKey")) {
            return;
        }
        wakeUpFromWakeKey(
                event.getEventTime(),
                event.getKeyCode(),
                event.getAction() == KeyEvent.ACTION_DOWN);
    }

    private void wakeUpFromWakeKey(long eventTime, int keyCode, boolean isDown) {
        if (mWindowWakeUpPolicy.wakeUpFromKey(DEFAULT_DISPLAY, eventTime, keyCode, isDown)) {
            final boolean keyCanLaunchHome = keyCode == KEYCODE_HOME || keyCode == KEYCODE_POWER;
            // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
            if (shouldWakeUpWithHomeIntent() &&  keyCanLaunchHome) {
                startDockOrHome(
                        DEFAULT_DISPLAY,
                        /*fromHomeKey*/ keyCode == KEYCODE_HOME,
                        /*wakenFromDreams*/ true,
                        "Wake from " + KeyEvent. keyCodeToString(keyCode));
            }
        }
    }

    private void finishKeyguardDrawn() {
        if (!mDefaultDisplayPolicy.finishKeyguardDrawn()) {
            return;
        }

        synchronized (mLock) {
            if (mKeyguardDelegate != null) {
                mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
            }
        }

        // ... eventually calls finishWindowsDrawn which will finalize our screen turn on
        // as well as enabling the orientation change logic/sensor.
        Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
                TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, INVALID_DISPLAY /* cookie */);
        mWindowManagerInternal.waitForAllWindowsDrawn(mHandler.obtainMessage(
                MSG_WINDOW_MANAGER_DRAWN_COMPLETE, INVALID_DISPLAY, 0),
                WAITING_FOR_DRAWN_TIMEOUT, INVALID_DISPLAY);
    }

    // Called on the DisplayManager's DisplayPowerController thread.
    @Override
    public void screenTurnedOff(int displayId, boolean isSwappingDisplay) {
        if (DEBUG_WAKEUP) Slog.i(TAG, "Display" + displayId + " turned off...");

        if (displayId == DEFAULT_DISPLAY) {
            SystemProperties.set(
                    android.ext.settings.AltTouchscreenMode.STATE_OF_DEFAULT_DISPLAY_PROP, "off");

            final boolean acquireSleepToken = !isSwappingDisplay || mIsGoingToSleep;
            mRequestedOrSleepingDefaultDisplay = false;
            mDefaultDisplayPolicy.screenTurnedOff(acquireSleepToken);
            synchronized (mLock) {
                if (mKeyguardDelegate != null) {
                    mKeyguardDelegate.onScreenTurnedOff();
                }
            }
            mDefaultDisplayRotation.updateOrientationListener();
            reportScreenStateToVrManager(false);
        }
    }

    @Override
    public void onDisplaySwitchStart(int displayId) {
        if (displayId == DEFAULT_DISPLAY) {
            mDefaultDisplayPolicy.onDisplaySwitchStart();
        }
    }

    private long getKeyguardDrawnTimeout() {
        final boolean bootCompleted =
                LocalServices.getService(SystemServiceManager.class).isBootCompleted();
        // Set longer timeout if it has not booted yet to prevent showing empty window.
        return bootCompleted ? mKeyguardDrawnTimeout : 5000;
    }

    @Nullable
    private WallpaperManagerInternal getWallpaperManagerInternal() {
        if (mWallpaperManagerInternal == null) {
            mWallpaperManagerInternal = LocalServices.getService(WallpaperManagerInternal.class);
        }
        return mWallpaperManagerInternal;
    }

    private void reportScreenTurningOnToWallpaper(int displayId) {
        WallpaperManagerInternal wallpaperManagerInternal = getWallpaperManagerInternal();
        if (wallpaperManagerInternal != null) {
            wallpaperManagerInternal.onScreenTurningOn(displayId);
        }
    }

    private void reportScreenTurnedOnToWallpaper(int displayId) {
        WallpaperManagerInternal wallpaperManagerInternal = getWallpaperManagerInternal();
        if (wallpaperManagerInternal != null) {
            wallpaperManagerInternal.onScreenTurnedOn(displayId);
        }
    }

    // Called on the DisplayManager's DisplayPowerController thread.
    @Override
    public void screenTurningOn(int displayId, final ScreenOnListener screenOnListener) {
        if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turning on...");

        reportScreenTurningOnToWallpaper(displayId);
        if (displayId == DEFAULT_DISPLAY) {
            Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn",
                    0 /* cookie */);
            mDefaultDisplayPolicy.screenTurningOn(screenOnListener);
            mBootAnimationDismissable = false;

            synchronized (mLock) {
                if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) {
                    mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
                    mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT,
                            getKeyguardDrawnTimeout());
                    final int reason = mDefaultDisplayPolicy.isDisplaySwitching()
                            ? SCREEN_TURNING_ON_REASON_DISPLAY_SWITCH
                            : SCREEN_TURNING_ON_REASON_UNKNOWN;
                    mKeyguardDelegate.onScreenTurningOn(reason, mKeyguardDrawnCallback);
                } else {
                    if (DEBUG_WAKEUP) Slog.d(TAG,
                            "null mKeyguardDelegate: setting mKeyguardDrawComplete.");
                    mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
                }
            }
        } else {
            mScreenOnListeners.put(displayId, screenOnListener);

            Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
                    TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, displayId /* cookie */);
            mWindowManagerInternal.waitForAllWindowsDrawn(mHandler.obtainMessage(
                    MSG_WINDOW_MANAGER_DRAWN_COMPLETE, displayId, 0),
                    WAITING_FOR_DRAWN_TIMEOUT, displayId);
        }
    }

    // Called on the DisplayManager's DisplayPowerController thread.
    @Override
    public void screenTurnedOn(int displayId) {
        if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turned on...");

        if (displayId == DEFAULT_DISPLAY) {
            SystemProperties.set(
                    android.ext.settings.AltTouchscreenMode.STATE_OF_DEFAULT_DISPLAY_PROP, "on");
        }

        reportScreenTurnedOnToWallpaper(displayId);

        if (displayId != DEFAULT_DISPLAY) {
            return;
        }

        synchronized (mLock) {
            if (mKeyguardDelegate != null) {
                mKeyguardDelegate.onScreenTurnedOn();
            }
        }
        mDefaultDisplayPolicy.screenTurnedOn();
        reportScreenStateToVrManager(true);
    }

    @Override
    public void screenTurningOff(int displayId, ScreenOffListener screenOffListener) {
        mWindowManagerFuncs.screenTurningOff(displayId, screenOffListener);
        if (displayId != DEFAULT_DISPLAY) {
            return;
        }

        mRequestedOrSleepingDefaultDisplay = true;
        synchronized (mLock) {
            if (mKeyguardDelegate != null) {
                mKeyguardDelegate.onScreenTurningOff();
            }
        }
    }

    private void reportScreenStateToVrManager(boolean isScreenOn) {
        if (mVrManagerInternal == null) {
            return;
        }
        mVrManagerInternal.onScreenStateChanged(isScreenOn);
    }

    private void finishWindowsDrawn(int displayId) {
        if (displayId != DEFAULT_DISPLAY && displayId != INVALID_DISPLAY) {
            final ScreenOnListener screenOnListener = mScreenOnListeners.removeReturnOld(displayId);
            if (screenOnListener != null) {
                screenOnListener.onScreenOn();
            }
            return;
        }

        if (!mDefaultDisplayPolicy.finishWindowsDrawn()) {
            return;
        }

        finishScreenTurningOn();
    }

    private void finishScreenTurningOn() {
        // We have just finished drawing screen content. Since the orientation listener
        // gets only installed when all windows are drawn, we try to install it again.
        mDefaultDisplayRotation.updateOrientationListener();

        final ScreenOnListener listener = mDefaultDisplayPolicy.getScreenOnListener();
        if (!mDefaultDisplayPolicy.finishScreenTurningOn()) {
            return; // Spurious or not ready yet.
        }
        Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn", 0 /* cookie */);

        enableScreen(listener, true /* report */);
    }

    private void enableScreen(ScreenOnListener listener, boolean report) {
        final boolean enableScreen;
        final boolean awake = mDefaultDisplayPolicy.isAwake();
        synchronized (mLock) {
            // Remember the first time we draw the keyguard so we know when we're done with
            // the main part of booting and can enable the screen and hide boot messages.
            if (!mKeyguardDrawnOnce && awake) {
                mKeyguardDrawnOnce = true;
                enableScreen = true;
                if (mBootMessageNeedsHiding) {
                    mBootMessageNeedsHiding = false;
                    hideBootMessages();
                }
            } else {
                enableScreen = false;
            }
        }

        if (report) {
            if (listener != null) {
                listener.onScreenOn();
            }
        }

        if (enableScreen) {
            mWindowManagerFuncs.enableScreenIfNeeded();
        }
    }

    private void handleHideBootMessage() {
        synchronized (mLock) {
            if (!mKeyguardDrawnOnce) {
                mBootMessageNeedsHiding = true;
                return; // keyguard hasn't drawn the first time yet, not done booting
            }
        }

        if (mBootMsgDialog != null) {
            if (DEBUG_WAKEUP) Slog.d(TAG, "handleHideBootMessage: dismissing");
            mBootMsgDialog.dismiss();
            mBootMsgDialog = null;
        }
    }

    @Override
    public boolean isScreenOn(int displayId) {
        final DisplayPolicy policy = getDisplayPolicy(displayId);
        return policy != null && policy.isScreenOnEarly();
    }

    @Nullable
    private DisplayPolicy getDisplayPolicy(int displayId) {
        if (displayId == DEFAULT_DISPLAY) {
            return mDefaultDisplayPolicy;
        }
        return mWindowManagerInternal.getDisplayPolicy(displayId);
    }

    @Override
    public boolean okToAnimate(boolean ignoreScreenOn) {
        return (ignoreScreenOn || isScreenOn()) && !mDeviceGoingToSleep;
    }

    /** {@inheritDoc} */
    @Override
    public void enableKeyguard(boolean enabled) {
        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.setKeyguardEnabled(enabled);
        }
    }

    /** {@inheritDoc} */
    @Override
    public void exitKeyguardSecurely(OnKeyguardExitResult callback) {
        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.verifyUnlock(callback);
        }
    }

    @Override
    public boolean isKeyguardShowing() {
        if (mKeyguardDelegate == null) return false;
        return mKeyguardDelegate.isShowing();
    }

    @Override
    public boolean isKeyguardShowingAndNotOccluded() {
        if (mKeyguardDelegate == null) return false;
        return mKeyguardDelegate.isShowing() && !isKeyguardOccluded();
    }

    @Override
    public boolean isKeyguardTrustedLw() {
        if (mKeyguardDelegate == null) return false;
        return mKeyguardDelegate.isTrusted();
    }

    /** {@inheritDoc} */
    @Override
    public boolean isKeyguardLocked() {
        return keyguardOn();
    }

    /** {@inheritDoc} */
    @Override
    public boolean isKeyguardSecure(int userId) {
        if (mKeyguardDelegate == null) return false;
        return mKeyguardDelegate.isSecure(userId);
    }

    /** {@inheritDoc} */
    @Override
    public boolean isKeyguardOccluded() {
        if (mKeyguardDelegate == null) return false;
        return mKeyguardDelegate.isOccluded();
    }

    /** {@inheritDoc} */
    @Override
    public boolean inKeyguardRestrictedKeyInputMode() {
        if (mKeyguardDelegate == null) return false;
        return mKeyguardDelegate.isInputRestricted();
    }

    @Override
    public void dismissKeyguardLw(IKeyguardDismissCallback callback, CharSequence message) {
        if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
            if (DEBUG_KEYGUARD) Slog.d(TAG, "PWM.dismissKeyguardLw");

            // ask the keyguard to prompt the user to authenticate if necessary
            mKeyguardDelegate.dismiss(callback, message);
        } else if (callback != null) {
            try {
                callback.onDismissError();
            } catch (RemoteException e) {
                Slog.w(TAG, "Failed to call callback", e);
            }
        }
    }

    @Override
    public boolean isKeyguardDrawnLw() {
        synchronized (mLock) {
            return mKeyguardDrawnOnce;
        }
    }

    @Override
    public void startKeyguardExitAnimation(long startTime) {
        if (mKeyguardDelegate != null) {
            if (DEBUG_KEYGUARD) Slog.d(TAG, "PWM.startKeyguardExitAnimation");
            mKeyguardDelegate.startKeyguardExitAnimation(startTime);
        }
    }

    void sendCloseSystemWindows() {
        PhoneWindow.sendCloseSystemWindows(mContext, null);
    }

    void sendCloseSystemWindows(String reason) {
        PhoneWindow.sendCloseSystemWindows(mContext, reason);
    }

    @Override
    public void setSafeMode(boolean safeMode) {
        mSafeMode = safeMode;
        if (safeMode) {
            performHapticFeedback(
                    HapticFeedbackConstants.SAFE_MODE_ENABLED,
                    "Safe Mode Enabled" /* reason */,
                    HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
        }
    }

    private void bindKeyguard() {
        synchronized (mLock) {
            if (mKeyguardBound) {
                return;
            }
            mKeyguardBound = true;
        }
        mKeyguardDelegate.bindService(mContext);
    }

    @Override
    public void onSystemUiStarted() {
        bindKeyguard();
    }

    /** {@inheritDoc} */
    @Override
    public void systemReady() {
        // In normal flow, systemReady is called before other system services are ready.
        // So it is better not to bind keyguard here.
        mKeyguardDelegate.onSystemReady();

        mVrManagerInternal = LocalServices.getService(VrManagerInternal.class);
        if (mVrManagerInternal != null) {
            mVrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
        }

        mDockObserverInternal = LocalServices.getService(DockObserverInternal.class);
        if (mDockObserverInternal != null) {
            // Get initial state from DockObserverInternal, DockObserver starts after WM.
            int dockMode = mDockObserverInternal.getActualDockState();
            mDefaultDisplayPolicy.setDockMode(dockMode);
        }

        readCameraLensCoverState();
        updateUiMode();
        mDefaultDisplayRotation.updateOrientationListener();
        synchronized (mLock) {
            mSystemReady = true;
            updateSettings(mHandler);
            // If this happens, for whatever reason, systemReady came later than systemBooted.
            // And keyguard should be already bound from systemBooted
            if (mSystemBooted) {
                mKeyguardDelegate.onBootCompleted();
            }
        }

        mAutofillManagerInternal = LocalServices.getService(AutofillManagerInternal.class);
        mGestureLauncherService = LocalServices.getService(GestureLauncherService.class);
    }

    /** {@inheritDoc} */
    @Override
    public void systemBooted() {
        bindKeyguard();
        synchronized (mLock) {
            mSystemBooted = true;
            if (mSystemReady) {
                mKeyguardDelegate.onBootCompleted();
            }
        }
        mSideFpsEventHandler.onFingerprintSensorReady();
        startedWakingUp(Display.DEFAULT_DISPLAY_GROUP, PowerManager.WAKE_REASON_UNKNOWN);
        finishedWakingUp(Display.DEFAULT_DISPLAY_GROUP, PowerManager.WAKE_REASON_UNKNOWN);

        int defaultDisplayState = mDisplayManager.getDisplay(DEFAULT_DISPLAY).getState();
        boolean defaultDisplayOn = defaultDisplayState == Display.STATE_ON;
        boolean defaultScreenTurningOn = mDefaultDisplayPolicy.getScreenOnListener() != null;
        if (defaultDisplayOn || defaultScreenTurningOn) {
            // Now that system is booted, wait for keyguard and windows to be drawn before
            // updating the orientation listener, stopping the boot animation and enabling screen.
            screenTurningOn(DEFAULT_DISPLAY, mDefaultDisplayPolicy.getScreenOnListener());
            screenTurnedOn(DEFAULT_DISPLAY);
        } else {
            // We're not turning the screen on, so don't wait for keyguard to be drawn
            // to dismiss the boot animation and finish booting
            mBootAnimationDismissable = true;
            enableScreen(null, false /* report */);
        }
    }

    @Override
    public boolean canDismissBootAnimation() {
        // Allow to dismiss the boot animation if the keyguard has finished drawing,
        // or mBootAnimationDismissable has been set
        return mDefaultDisplayPolicy.isKeyguardDrawComplete() || mBootAnimationDismissable;
    }

    ProgressDialog mBootMsgDialog = null;

    /** {@inheritDoc} */
    @Override
    public void showBootMessage(final CharSequence msg, final boolean always) {
        mHandler.post(new Runnable() {
            @Override public void run() {
                if (mBootMsgDialog == null) {
                    int theme;
                    if (mPackageManager.hasSystemFeature(FEATURE_LEANBACK)) {
                        theme = com.android.internal.R.style.Theme_Leanback_Dialog_Alert;
                    } else {
                        theme = 0;
                    }

                    mBootMsgDialog = new ProgressDialog(mContext, theme) {
                        // This dialog will consume all events coming in to
                        // it, to avoid it trying to do things too early in boot.
                        @Override public boolean dispatchKeyEvent(KeyEvent event) {
                            return true;
                        }
                        @Override public boolean dispatchKeyShortcutEvent(KeyEvent event) {
                            return true;
                        }
                        @Override public boolean dispatchTouchEvent(MotionEvent ev) {
                            return true;
                        }
                        @Override public boolean dispatchTrackballEvent(MotionEvent ev) {
                            return true;
                        }
                        @Override public boolean dispatchGenericMotionEvent(MotionEvent ev) {
                            return true;
                        }
                        @Override public boolean dispatchPopulateAccessibilityEvent(
                                AccessibilityEvent event) {
                            return true;
                        }
                    };
                    if (mPackageManager.isDeviceUpgrading()) {
                        mBootMsgDialog.setTitle(R.string.android_upgrading_title);
                    } else {
                        mBootMsgDialog.setTitle(R.string.android_start_title);
                    }
                    mBootMsgDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
                    mBootMsgDialog.setIndeterminate(true);
                    mBootMsgDialog.getWindow().setType(
                            WindowManager.LayoutParams.TYPE_BOOT_PROGRESS);
                    mBootMsgDialog.getWindow().addFlags(
                            WindowManager.LayoutParams.FLAG_DIM_BEHIND
                            | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
                    mBootMsgDialog.getWindow().setDimAmount(1);
                    WindowManager.LayoutParams lp = mBootMsgDialog.getWindow().getAttributes();
                    lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
                    lp.setFitInsetsTypes(0 /* types */);
                    mBootMsgDialog.getWindow().setAttributes(lp);
                    mBootMsgDialog.setCancelable(false);
                    mBootMsgDialog.show();
                }
                mBootMsgDialog.setMessage(msg);
            }
        });
    }

    /** {@inheritDoc} */
    @Override
    public void hideBootMessages() {
        mHandler.sendEmptyMessage(MSG_HIDE_BOOT_MESSAGE);
    }

    /** {@inheritDoc} */
    @Override
    public void userActivity(int displayGroupId, int event) {
        if (displayGroupId == DEFAULT_DISPLAY && event == PowerManager.USER_ACTIVITY_EVENT_TOUCH) {
            mDefaultDisplayPolicy.onUserActivityEventTouch();
        }
        synchronized (mScreenLockTimeout) {
            if (mLockScreenTimerActive) {
                // reset the timer
                mHandler.removeCallbacks(mScreenLockTimeout);
                mHandler.postDelayed(mScreenLockTimeout, mLockScreenTimeout);
            }
        }
    }

    class ScreenLockTimeout implements Runnable {
        Bundle options;

        @Override
        public void run() {
            synchronized (this) {
                if (localLOGV) Log.v(TAG, "mScreenLockTimeout activating keyguard");
                if (mKeyguardDelegate != null) {
                    mKeyguardDelegate.doKeyguardTimeout(options);
                }
                mLockScreenTimerActive = false;
                mLockNowPending = false;
                options = null;
            }
        }

        public void setLockOptions(Bundle options) {
            this.options = options;
        }
    }

    final ScreenLockTimeout mScreenLockTimeout = new ScreenLockTimeout();

    @Override
    public void lockNow(Bundle options) {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
        mHandler.removeCallbacks(mScreenLockTimeout);
        if (options != null) {
            // In case multiple calls are made to lockNow, we don't wipe out the options
            // until the runnable actually executes.
            mScreenLockTimeout.setLockOptions(options);
        }
        mHandler.post(mScreenLockTimeout);
        synchronized (mScreenLockTimeout) {
            mLockNowPending = true;
        }
    }

    // TODO (b/113840485): Move this logic to DisplayPolicy when lockscreen supports multi-display.
    @Override
    public void setAllowLockscreenWhenOn(int displayId, boolean allow) {
        // We should ignore this operation for visible background users
        // until lockscreen supports multi-display.
        if (mVisibleBackgroundUsersEnabled
                && mUserManagerInternal.getUserAssignedToDisplay(displayId) != mCurrentUserId) {
            return;
        }
        if (allow) {
            mAllowLockscreenWhenOnDisplays.add(displayId);
        } else {
            mAllowLockscreenWhenOnDisplays.remove(displayId);
        }
        updateLockScreenTimeout();
    }

    private void updateLockScreenTimeout() {
        synchronized (mScreenLockTimeout) {
            if (mLockNowPending) {
                Log.w(TAG, "lockNow pending, ignore updating lockscreen timeout");
                return;
            }
            final boolean enable = !mAllowLockscreenWhenOnDisplays.isEmpty()
                    && mDefaultDisplayPolicy.isAwake()
                    && mKeyguardDelegate != null && mKeyguardDelegate.isSecure(mCurrentUserId);
            if (mLockScreenTimerActive != enable) {
                if (enable) {
                    if (localLOGV) Log.v(TAG, "setting lockscreen timer");
                    mHandler.removeCallbacks(mScreenLockTimeout); // remove any pending requests
                    mHandler.postDelayed(mScreenLockTimeout, mLockScreenTimeout);
                } else {
                    if (localLOGV) Log.v(TAG, "clearing lockscreen timer");
                    mHandler.removeCallbacks(mScreenLockTimeout);
                }
                mLockScreenTimerActive = enable;
            }
        }
    }

    /** {@inheritDoc} */
    @Override
    public void enableScreenAfterBoot() {
        readLidState();
        applyLidSwitchState();
        updateRotation(true);
    }

    private void applyLidSwitchState() {
        final int lidState = mDefaultDisplayPolicy.getLidState();
        if (lidState == LID_CLOSED) {
            int lidBehavior = getLidBehavior();
            switch (lidBehavior) {
                case LID_BEHAVIOR_LOCK:
                    mWindowManagerFuncs.lockDeviceNow();
                    break;
                case LID_BEHAVIOR_SLEEP:
                    goToSleep(mInjector.getUptimeMillis(),
                            PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                    break;
                case LID_BEHAVIOR_NONE:
                    // fall through
                default:
                    break;
            }
        }

        synchronized (mLock) {
            updateWakeGestureListenerLp();
        }
    }

    void updateUiMode() {
        if (mUiModeManager == null) {
            mUiModeManager = IUiModeManager.Stub.asInterface(
                    ServiceManager.getService(Context.UI_MODE_SERVICE));
        }
        try {
            // The uiMode on DEFAULT_DISPLAY is equivalent to the device-level uiMode.
            // Per-display overrides of uiMode are disallowed on DEFAULT_DISPLAY.
            mUiMode = mUiModeManager.getCurrentModeType(DEFAULT_DISPLAY);
        } catch (RemoteException e) {
        }
    }

    @Override
    public int getUiMode() {
        return mUiMode;
    }

    void updateRotation(boolean alwaysSendConfiguration) {
        mWindowManagerFuncs.updateRotation(alwaysSendConfiguration, false /* forceRelayout */);
    }

    /**
     * Return an Intent to launch the currently active dock app as home.  Returns
     * null if the standard home should be launched, which is the case if any of the following is
     * true:
     * <ul>
     *  <li>The device is not in either car mode or desk mode
     *  <li>The device is in car mode but mEnableCarDockHomeCapture is false
     *  <li>The device is in desk mode but ENABLE_DESK_DOCK_HOME_CAPTURE is false
     *  <li>The device is in car mode but there's no CAR_DOCK app with METADATA_DOCK_HOME
     *  <li>The device is in desk mode but there's no DESK_DOCK app with METADATA_DOCK_HOME
     * </ul>
     * @return A dock intent.
     */
    Intent createHomeDockIntent() {
        Intent intent = null;

        // What home does is based on the mode, not the dock state.  That
        // is, when in car mode you should be taken to car home regardless
        // of whether we are actually in a car dock.
        if (mUiMode == Configuration.UI_MODE_TYPE_CAR) {
            if (mEnableCarDockHomeCapture) {
                intent = mCarDockIntent;
            }
        } else if (mUiMode == Configuration.UI_MODE_TYPE_DESK) {
            if (ENABLE_DESK_DOCK_HOME_CAPTURE) {
                intent = mDeskDockIntent;
            }
        } else if (mUiMode == Configuration.UI_MODE_TYPE_WATCH) {
            final int dockMode = mDefaultDisplayPolicy.getDockMode();
            if (dockMode == Intent.EXTRA_DOCK_STATE_DESK
                    || dockMode == Intent.EXTRA_DOCK_STATE_HE_DESK
                    || dockMode == Intent.EXTRA_DOCK_STATE_LE_DESK) {
                // Always launch dock home from home when watch is docked, if it exists.
                intent = mDeskDockIntent;
            }
        } else if (mUiMode == Configuration.UI_MODE_TYPE_VR_HEADSET) {
            if (ENABLE_VR_HEADSET_HOME_CAPTURE) {
                intent = mVrHeadsetHomeIntent;
            }
        }

        if (intent == null) {
            return null;
        }

        ActivityInfo ai = null;
        ResolveInfo info = mPackageManager.resolveActivityAsUser(
                intent,
                PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
                mCurrentUserId);
        if (info != null) {
            ai = info.activityInfo;
        }
        if (ai != null
                && ai.metaData != null
                && ai.metaData.getBoolean(Intent.METADATA_DOCK_HOME)) {
            intent = new Intent(intent);
            intent.setClassName(ai.packageName, ai.name);
            return intent;
        }

        return null;
    }

    void startDockOrHome(int displayId, boolean fromHomeKey, boolean awakenFromDreams,
            String startReason) {
        try {
            ActivityManager.getService().stopAppSwitches();
        } catch (RemoteException e) {}
        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);

        if (awakenFromDreams) {
            awakenDreams();
        }

        if (!mHasFeatureAuto && !isUserSetupComplete()) {
            Slog.i(TAG, "Not going home because user setup is in progress.");
            return;
        }

        // Start dock.
        Intent dock = createHomeDockIntent();
        if (dock != null) {
            try {
                if (fromHomeKey) {
                    dock.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, fromHomeKey);
                }
                startActivityAsUser(dock, UserHandle.CURRENT, displayId);
                return;
            } catch (ActivityNotFoundException e) {
            }
        }

        if (DEBUG_WAKEUP) {
            Log.d(TAG, "startDockOrHome: startReason= " + startReason);
        }

        DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(displayId);
        boolean isDisplayExternal = displayInfo != null && displayInfo.type == TYPE_EXTERNAL;
        if (DesktopExperienceFlags.ENABLE_LAUNCHER_HANDLE_GO_HOME_KEYBOARD_SHORTCUT.isTrue()
                && isDisplayExternal) {
            // TODO(b/441952247): Clean up using home gesture handling in WM Core
            mInputManagerInternal.handleKeyGestureInKeyGestureController(
                    new KeyGestureEvent.Builder()
                            .setKeyGestureType(
                                 KeyGestureEvent.KEY_GESTURE_TYPE_REJECT_HOME_ON_EXTERNAL_DISPLAY)
                            .setKeycodes(new int[]{KEYCODE_HOME})
                            .setDisplayId(displayId)
                            .setAction(ACTION_COMPLETE)
                            .build());
        } else {
            int userId = mUserManagerInternal.getUserAssignedToDisplay(displayId);
            // Start home.
            mActivityTaskManagerInternal.startHomeOnDisplay(userId, startReason,
                    displayId, true /* allowInstrumenting */, fromHomeKey);
        }
    }

    void startDockOrHome(int displayId, boolean fromHomeKey, boolean awakenFromDreams) {
        startDockOrHome(displayId, fromHomeKey, awakenFromDreams, /*startReason*/
                "startDockOrHome");
    }

    /**
     * goes to the home screen
     * @return whether it did anything
     */
    boolean goHome() {
        if (!isUserSetupComplete()) {
            Slog.i(TAG, "Not going home because user setup is in progress.");
            return false;
        }
        if (false) {
            // This code always brings home to the front.
            startDockOrHome(DEFAULT_DISPLAY, false /*fromHomeKey*/, true /* awakenFromDreams */);
        } else {
            // This code brings home to the front or, if it is already
            // at the front, puts the device to sleep.
            try {
                if (SystemProperties.getInt("persist.sys.uts-test-mode", 0) == 1) {
                    /// Roll back EndcallBehavior as the cupcake design to pass P1 lab entry.
                    Log.d(TAG, "UTS-TEST-MODE");
                } else {
                    ActivityManager.getService().stopAppSwitches();
                    sendCloseSystemWindows();
                    final Intent dock = createHomeDockIntent();
                    if (dock != null) {
                        int result = ActivityTaskManager.getService()
                                .startActivityAsUser(null, mContext.getOpPackageName(),
                                        mContext.getAttributionTag(), dock,
                                        dock.resolveTypeIfNeeded(mContext.getContentResolver()),
                                        null, null, 0,
                                        ActivityManager.START_FLAG_ONLY_IF_NEEDED,
                                        null, null, UserHandle.USER_CURRENT);
                        if (result == ActivityManager.START_RETURN_INTENT_TO_CALLER) {
                            return false;
                        }
                    }
                }
                int result = ActivityTaskManager.getService()
                        .startActivityAsUser(null, mContext.getOpPackageName(),
                                mContext.getAttributionTag(), mHomeIntent,
                                mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
                                null, null, 0,
                                ActivityManager.START_FLAG_ONLY_IF_NEEDED,
                                null, null, UserHandle.USER_CURRENT);
                if (result == ActivityManager.START_RETURN_INTENT_TO_CALLER) {
                    return false;
                }
            } catch (RemoteException ex) {
                // bummer, the activity manager, which is in this process, is dead
            }
        }
        return true;
    }

    private void performHapticFeedback(int effectId, String reason) {
        performHapticFeedback(effectId, reason, 0 /* flags */);
    }

    private void performHapticFeedback(
            int effectId, String reason, @HapticFeedbackConstants.Flags int flags) {
        mVibrator.performHapticFeedback(effectId, VibrationAttributes.USAGE_UNKNOWN, reason, flags,
                0 /* privFlags */);
    }

    @Override
    public boolean isGlobalKey(int keyCode) {
        return mGlobalKeyManager.shouldHandleGlobalKey(keyCode);
    }


    @Override
    public void keepScreenOnStartedLw() {
    }

    @Override
    public void keepScreenOnStoppedLw() {
        if (isKeyguardShowingAndNotOccluded()) {
            mPowerManager.userActivity(mInjector.getUptimeMillis(), false);
        }
    }

    // Use this instead of checking config_showNavigationBar so that it can be consistently
    // overridden by qemu.hw.mainkeys in the emulator.
    @Override
    public boolean hasNavigationBar() {
        return mDefaultDisplayPolicy.hasNavigationBar();
    }

    @Override
    public void setDismissImeOnBackKeyPressed(boolean newValue) {
        mDismissImeOnBackKeyPressed = newValue;
    }

    @Override
    public void setCurrentUserLw(int newUserId) {
        mCurrentUserId = newUserId;
        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.setCurrentUser(newUserId);
        }
        StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
        if (statusBar != null) {
            statusBar.setCurrentUser(newUserId);
        }
        mModifierShortcutManager.setCurrentUser(UserHandle.of(newUserId));
    }

    @Override
    public void setSwitchingUser(boolean switching) {
        mKeyguardDelegate.setSwitchingUser(switching);
        if (switching) {
            dismissKeyboardShortcutsMenu();
        }
    }

    @Override
    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
        final long token = proto.start(fieldId);
        proto.write(ROTATION_MODE, mDefaultDisplayRotation.getUserRotationMode());
        proto.write(ROTATION, mDefaultDisplayRotation.getUserRotation());
        proto.write(ORIENTATION, mDefaultDisplayRotation.getCurrentAppOrientation());
        proto.write(SCREEN_ON_FULLY, mDefaultDisplayPolicy.isScreenOnFully());
        proto.write(KEYGUARD_DRAW_COMPLETE, mDefaultDisplayPolicy.isKeyguardDrawComplete());
        proto.write(WINDOW_MANAGER_DRAW_COMPLETE,
                mDefaultDisplayPolicy.isWindowManagerDrawComplete());
        proto.write(KEYGUARD_OCCLUDED, isKeyguardOccluded());
        proto.write(KEYGUARD_OCCLUDED_CHANGED, mKeyguardOccludedChanged);
        proto.write(KEYGUARD_OCCLUDED_PENDING, mPendingKeyguardOccluded);
        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.dumpDebug(proto, KEYGUARD_DELEGATE);
        }
        proto.end(token);
    }

    @Override
    public void dump(String prefix, PrintWriter pw, String[] args) {
        pw.print(prefix); pw.print("mSafeMode="); pw.print(mSafeMode);
                pw.print(" mSystemReady="); pw.print(mSystemReady);
                pw.print(" mSystemBooted="); pw.println(mSystemBooted);
        pw.print(prefix); pw.print("mCameraLensCoverState=");
                pw.println(WindowManagerFuncs.cameraLensStateToString(mCameraLensCoverState));
        pw.print(prefix); pw.print("mWakeGestureEnabledSetting=");
                pw.println(mWakeGestureEnabledSetting);

        pw.print(prefix);
                pw.print("mUiMode=");
                pw.print(Configuration.uiModeToString(mUiMode));
                pw.print("mEnableCarDockHomeCapture="); pw.println(mEnableCarDockHomeCapture);
        pw.print(prefix); pw.print("mLidKeyboardAccessibility=");
                pw.print(mLidKeyboardAccessibility);
                pw.print(" mLidNavigationAccessibility="); pw.print(mLidNavigationAccessibility);
                pw.print(" getLidBehavior="); pw.println(lidBehaviorToString(getLidBehavior()));
        pw.print(prefix);
                pw.print("mLongPressOnBackBehavior=");
                pw.println(longPressOnBackBehaviorToString(mLongPressOnBackBehavior));
        pw.print(prefix);
                pw.print("mLongPressOnHomeBehavior=");
                pw.println(longPressOnHomeBehaviorToString(mLongPressOnHomeBehavior));
        pw.print(prefix);
                pw.print("mDoubleTapOnHomeBehavior=");
                pw.println(doubleTapOnHomeBehaviorToString(mDoubleTapOnHomeBehavior));
        pw.print(prefix);
                pw.print("mShortPressOnPowerBehavior=");
                pw.println(shortPressOnPowerBehaviorToString(mShortPressOnPowerBehavior));
        pw.print(prefix);
                pw.print("mLongPressOnPowerBehavior=");
                pw.println(longPressOnPowerBehaviorToString(mLongPressOnPowerBehavior));
        pw.print(prefix);
        pw.print("mLongPressOnPowerAssistantTimeoutMs=");
        pw.println(mLongPressOnPowerAssistantTimeoutMs);
        pw.print(prefix);
                pw.print("mVeryLongPressOnPowerBehavior=");
                pw.println(veryLongPressOnPowerBehaviorToString(mVeryLongPressOnPowerBehavior));
        pw.print(prefix);
                pw.print("mDoublePressOnPowerBehavior=");
                pw.println(multiPressOnPowerBehaviorToString(mDoublePressOnPowerBehavior));
        pw.print(prefix);
                pw.print("mTriplePressOnPowerBehavior=");
                pw.println(multiPressOnPowerBehaviorToString(mTriplePressOnPowerBehavior));
        pw.print(prefix);
                pw.print("mSupportShortPressPowerWhenDefaultDisplayOn=");
                pw.println(mSupportShortPressPowerWhenDefaultDisplayOn);
        pw.print(prefix);
        pw.print(prefix);
                pw.print("mShortPressOnSleepBehavior=");
                pw.println(shortPressOnSleepBehaviorToString(mShortPressOnSleepBehavior));
        pw.print(prefix);
                pw.print("mShortPressOnWindowBehavior=");
                pw.println(shortPressOnWindowBehaviorToString(mShortPressOnWindowBehavior));
        pw.print(prefix);
                pw.print("mShortPressOnStemPrimaryBehavior=");
                pw.println(shortPressOnStemPrimaryBehaviorToString(
                    mShortPressOnStemPrimaryBehavior));
        pw.print(prefix);
                pw.print("mDoublePressOnStemPrimaryBehavior=");
                pw.println(doublePressOnStemPrimaryBehaviorToString(
                    mDoublePressOnStemPrimaryBehavior));
        pw.print(prefix);
                pw.print("mTriplePressOnStemPrimaryBehavior=");
                pw.println(triplePressOnStemPrimaryBehaviorToString(
                    mTriplePressOnStemPrimaryBehavior));
        pw.print(prefix);
                pw.print("mLongPressOnStemPrimaryBehavior=");
                pw.println(longPressOnStemPrimaryBehaviorToString(
                    mLongPressOnStemPrimaryBehavior));
        pw.print(prefix);
                pw.print("mAllowStartActivityForLongPressOnPowerDuringSetup=");
                pw.println(mAllowStartActivityForLongPressOnPowerDuringSetup);
        pw.print(prefix);
                pw.print("mHasSoftInput="); pw.println(mHasSoftInput);
        pw.print(prefix);
                pw.print("mDismissImeOnBackKeyPressed="); pw.print(mDismissImeOnBackKeyPressed);
                pw.print(" mIncallPowerBehavior=");
                pw.println(incallPowerBehaviorToString(mIncallPowerBehavior));
        pw.print(prefix);
                pw.print("mIncallBackBehavior=");
                pw.print(incallBackBehaviorToString(mIncallBackBehavior));
                pw.print(" mEndcallBehavior=");
                pw.println(endcallBehaviorToString(mEndcallBehavior));
        pw.print(prefix);
        // TODO(b/117479243): handle it in InputPolicy
        pw.println("mDisplayHomeButtonHandlers=");
        for (int i = 0; i < mDisplayHomeButtonHandlers.size(); i++) {
            final int key = mDisplayHomeButtonHandlers.keyAt(i);
            pw.print(prefix); pw.print("  "); pw.println(mDisplayHomeButtonHandlers.get(key));
        }
        pw.print(prefix); pw.print("mKeyguardOccluded="); pw.print(isKeyguardOccluded());
                pw.print(" mKeyguardOccludedChanged="); pw.print(mKeyguardOccludedChanged);
                pw.print(" mPendingKeyguardOccluded="); pw.println(mPendingKeyguardOccluded);
        pw.print(prefix); pw.print("mAllowLockscreenWhenOnDisplays=");
                pw.print(!mAllowLockscreenWhenOnDisplays.isEmpty());
                pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
                pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
        pw.print(prefix); pw.print("mKidsModeEnabled="); pw.println(mKidsModeEnabled);

        mGlobalKeyManager.dump(prefix, pw);
        mSingleKeyGestureDetector.dump(prefix, pw);
        mDeferredKeyActionExecutor.dump(prefix, pw);

        if (mWakeGestureListener != null) {
            mWakeGestureListener.dump(pw, prefix);
        }
        if (mBurnInProtectionHelper != null) {
            mBurnInProtectionHelper.dump(prefix, pw);
        }
        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.dump(prefix, pw);
        }

        pw.print(prefix); pw.println("Looper state:");
        mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + "  ");
    }

    private static String endcallBehaviorToString(int behavior) {
        StringBuilder sb = new StringBuilder();
        if ((behavior & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0 ) {
            sb.append("home|");
        }
        if ((behavior & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
            sb.append("sleep|");
        }

        final int N = sb.length();
        if (N == 0) {
            return "<nothing>";
        } else {
            // Chop off the trailing '|'
            return sb.substring(0, N - 1);
        }
    }

    private static String incallPowerBehaviorToString(int behavior) {
        if ((behavior & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0) {
            return "hangup";
        } else {
            return "sleep";
        }
    }

    private static String incallBackBehaviorToString(int behavior) {
        if ((behavior & Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR_HANGUP) != 0) {
            return "hangup";
        } else {
            return "<nothing>";
        }
    }

    private static String longPressOnBackBehaviorToString(int behavior) {
        switch (behavior) {
            case LONG_PRESS_BACK_NOTHING:
                return "LONG_PRESS_BACK_NOTHING";
            case LONG_PRESS_BACK_GO_TO_VOICE_ASSIST:
                return "LONG_PRESS_BACK_GO_TO_VOICE_ASSIST";
            default:
                return Integer.toString(behavior);
        }
    }

    private static String longPressOnHomeBehaviorToString(int behavior) {
        switch (behavior) {
            case LONG_PRESS_HOME_NOTHING:
                return "LONG_PRESS_HOME_NOTHING";
            case LONG_PRESS_HOME_ALL_APPS:
                return "LONG_PRESS_HOME_ALL_APPS";
            case LONG_PRESS_HOME_ASSIST:
                return "LONG_PRESS_HOME_ASSIST";
            case LONG_PRESS_HOME_NOTIFICATION_PANEL:
                return "LONG_PRESS_HOME_NOTIFICATION_PANEL";
            default:
                return Integer.toString(behavior);
        }
    }

    private static String doubleTapOnHomeBehaviorToString(int behavior) {
        switch (behavior) {
            case DOUBLE_TAP_HOME_NOTHING:
                return "DOUBLE_TAP_HOME_NOTHING";
            case DOUBLE_TAP_HOME_RECENT_SYSTEM_UI:
                return "DOUBLE_TAP_HOME_RECENT_SYSTEM_UI";
            case DOUBLE_TAP_HOME_PIP_MENU:
                return "DOUBLE_TAP_HOME_PIP_MENU";
            default:
                return Integer.toString(behavior);
        }
    }

    private static String shortPressOnPowerBehaviorToString(int behavior) {
        switch (behavior) {
            case SHORT_PRESS_POWER_NOTHING:
                return "SHORT_PRESS_POWER_NOTHING";
            case SHORT_PRESS_POWER_GO_TO_SLEEP:
                return "SHORT_PRESS_POWER_GO_TO_SLEEP";
            case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
                return "SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP";
            case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
                return "SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME";
            case SHORT_PRESS_POWER_GO_HOME:
                return "SHORT_PRESS_POWER_GO_HOME";
            case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME:
                return "SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME";
            default:
                return Integer.toString(behavior);
        }
    }

    private static String longPressOnPowerBehaviorToString(int behavior) {
        switch (behavior) {
            case LONG_PRESS_POWER_NOTHING:
                return "LONG_PRESS_POWER_NOTHING";
            case LONG_PRESS_POWER_GLOBAL_ACTIONS:
                return "LONG_PRESS_POWER_GLOBAL_ACTIONS";
            case LONG_PRESS_POWER_SHUT_OFF:
                return "LONG_PRESS_POWER_SHUT_OFF";
            case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
                return "LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM";
            case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
                return "LONG_PRESS_POWER_GO_TO_VOICE_ASSIST";
            case LONG_PRESS_POWER_ASSISTANT:
                return "LONG_PRESS_POWER_ASSISTANT";
            case LONG_PRESS_POWER_GO_TO_SLEEP:
                return "LONG_PRESS_POWER_GO_TO_SLEEP";
            default:
                return Integer.toString(behavior);
        }
    }

    private static String veryLongPressOnPowerBehaviorToString(int behavior) {
        switch (behavior) {
            case VERY_LONG_PRESS_POWER_NOTHING:
                return "VERY_LONG_PRESS_POWER_NOTHING";
            case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS:
                return "VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS";
            default:
                return Integer.toString(behavior);
        }
    }

    private static String multiPressOnPowerBehaviorToString(int behavior) {
        switch (behavior) {
            case MULTI_PRESS_POWER_NOTHING:
                return "MULTI_PRESS_POWER_NOTHING";
            case MULTI_PRESS_POWER_BRIGHTNESS_BOOST:
                return "MULTI_PRESS_POWER_BRIGHTNESS_BOOST";
            case MULTI_PRESS_POWER_LAUNCH_TARGET_ACTIVITY:
                return "MULTI_PRESS_POWER_LAUNCH_TARGET_ACTIVITY";
            default:
                return Integer.toString(behavior);
        }
    }

    private static String shortPressOnSleepBehaviorToString(int behavior) {
        switch (behavior) {
            case SHORT_PRESS_SLEEP_GO_TO_SLEEP:
                return "SHORT_PRESS_SLEEP_GO_TO_SLEEP";
            case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME:
                return "SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME";
            default:
                return Integer.toString(behavior);
        }
    }

    private static String shortPressOnWindowBehaviorToString(int behavior) {
        switch (behavior) {
            case SHORT_PRESS_WINDOW_NOTHING:
                return "SHORT_PRESS_WINDOW_NOTHING";
            case SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE:
                return "SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE";
            default:
                return Integer.toString(behavior);
        }
    }

    private static String shortPressOnStemPrimaryBehaviorToString(int behavior) {
        switch (behavior) {
            case SHORT_PRESS_PRIMARY_NOTHING:
                return "SHORT_PRESS_PRIMARY_NOTHING";
            case SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS:
                return "SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS";
            case SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY:
                return "SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY";
            default:
                return Integer.toString(behavior);
        }
    }

    private static String doublePressOnStemPrimaryBehaviorToString(int behavior) {
        switch (behavior) {
            case DOUBLE_PRESS_PRIMARY_NOTHING:
                return "DOUBLE_PRESS_PRIMARY_NOTHING";
            case DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP:
                return "DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP";
            case DOUBLE_PRESS_PRIMARY_LAUNCH_DEFAULT_FITNESS_APP:
                return "DOUBLE_PRESS_PRIMARY_LAUNCH_DEFAULT_FITNESS_APP";
            default:
                return Integer.toString(behavior);
        }
    }

    private static String triplePressOnStemPrimaryBehaviorToString(int behavior) {
        switch (behavior) {
            case TRIPLE_PRESS_PRIMARY_NOTHING:
                return "TRIPLE_PRESS_PRIMARY_NOTHING";
            case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY:
                return "TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY";
            default:
                return Integer.toString(behavior);
        }
    }

    private static String longPressOnStemPrimaryBehaviorToString(int behavior) {
        switch (behavior) {
            case LONG_PRESS_PRIMARY_NOTHING:
                return "LONG_PRESS_PRIMARY_NOTHING";
            case LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT:
                return "LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT";
            default:
                return Integer.toString(behavior);
        }
    }

    private static String lidBehaviorToString(int behavior) {
        switch (behavior) {
            case LID_BEHAVIOR_LOCK:
                return "LID_BEHAVIOR_LOCK";
            case LID_BEHAVIOR_SLEEP:
                return "LID_BEHAVIOR_SLEEP";
            case LID_BEHAVIOR_NONE:
                return "LID_BEHAVIOR_NONE";
            default:
                return Integer.toString(behavior);
        }
    }

    public static boolean isLongPressToAssistantEnabled(Context context) {
        ContentResolver resolver = context.getContentResolver();
        int longPressToAssistant = Settings.System.getIntForUser(resolver,
                Settings.Global.Wearable.CLOCKWORK_LONG_PRESS_TO_ASSISTANT_ENABLED,
                /* def= */ 1,
                UserHandle.USER_CURRENT);
        if(Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "longPressToAssistant = " + longPressToAssistant);
        }
        return (longPressToAssistant == 1);
    }

    private class HdmiVideoExtconUEventObserver extends ExtconStateObserver<Boolean> {
        private static final String HDMI_EXIST = "HDMI=1";
        private static final String DP_EXIST = "DP=1";
        private static final String NAME = "hdmi";

        private boolean init(ExtconInfo hdmi) {
            boolean plugged = false;
            try {
                plugged = parseStateFromFile(hdmi);
            } catch (FileNotFoundException e) {
                Slog.w(TAG,
                        hdmi.getStatePath()
                                + " not found while attempting to determine initial state",
                        e);
            } catch (IOException e) {
                Slog.e(TAG,
                        "Error reading " + hdmi.getStatePath()
                                + " while attempting to determine initial state",
                        e);
            }
            startObserving(hdmi);
            return plugged;
        }

        @Override
        public void updateState(ExtconInfo extconInfo, String eventName, Boolean state) {
            mDefaultDisplayPolicy.setHdmiPlugged(state);
        }

        @Override
        public Boolean parseState(ExtconInfo extconIfno, String state) {
            // extcon event state changes from kernel4.9
            // new state will be like STATE=HDMI=1
            // or like STATE=DP=1 for newer kernel
            return state.contains(HDMI_EXIST) || state.contains(DP_EXIST);
        }
    }

    private void launchTargetSearchActivity(int displayId) {
        Intent intent;
        if (mSearchKeyTargetActivity != null) {
            intent = new Intent();
            intent.setComponent(mSearchKeyTargetActivity);
        } else {
            intent = new Intent(Intent.ACTION_WEB_SEARCH);
        }
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
        try {
            startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF, displayId);
        } catch (ActivityNotFoundException ignore) {
            Slog.e(TAG, "Could not resolve activity with : "
                    + intent.getComponent().flattenToString()
                    + " name.");
        }
    }

    /** A helper class to check button override permission. */
    static class ButtonOverridePermissionChecker {
        boolean canAppOverrideSystemKey(Context context, int uid) {
            return PermissionChecker.checkPermissionForDataDelivery(
                    context,
                    OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW,
                    PID_UNKNOWN,
                    uid,
                    null,
                    null,
                    null)
                    == PERMISSION_GRANTED;
        }
    }

    private int getTargetDisplayIdForKeyEvent(KeyEvent event) {
        if (event.getDisplayId() != INVALID_DISPLAY) {
            return event.getDisplayId();
        }
        if (mTopFocusedDisplayId != INVALID_DISPLAY) {
            return mTopFocusedDisplayId;
        }
        return DEFAULT_DISPLAY;
    }

    private int getTargetDisplayIdForKeyGestureEvent(KeyGestureEvent event) {
        if (event.getDisplayId() != INVALID_DISPLAY) {
            return event.getDisplayId();
        }
        if (mTopFocusedDisplayId != INVALID_DISPLAY) {
            return mTopFocusedDisplayId;
        }
        return DEFAULT_DISPLAY;
    }

    /**
     * This method is intended to prevent key events for visible background users
     * from interfering with the current user's experience in MUMD environment.
     *
     * @param displayId the displayId of the key event.
     * @param keyCode the key code of the event.
     *
     * @return false if the key event is for a visible background user.
     */
    private boolean isKeyEventForCurrentUser(int displayId, int keyCode, @Nullable String purpose) {
        if (!mVisibleBackgroundUsersEnabled) {
            return true;
        }
        int assignedUser = mUserManagerInternal.getUserAssignedToDisplay(displayId);
        if (assignedUser == mCurrentUserId) {
            return true;
        }
        if (DEBUG_INPUT) {
            Slog.w(TAG, "Cannot handle " + KeyEvent.keyCodeToString(keyCode)
                    + (purpose != null ? " to " + purpose : "")
                    + " for visible background user(u" + assignedUser + ")");
        }
        return false;
    }
}
