Merge "Revert "Introduce SingleKeyGestureDetector to PhoneWindowManager"" into sc-dev
diff --git a/services/core/java/com/android/server/policy/KeyCombinationManager.java b/services/core/java/com/android/server/policy/KeyCombinationManager.java
index 7f55723..84ac124 100644
--- a/services/core/java/com/android/server/policy/KeyCombinationManager.java
+++ b/services/core/java/com/android/server/policy/KeyCombinationManager.java
@@ -102,11 +102,9 @@
     }
 
     /**
-     * Check if the key event could be intercepted by combination key rule before it is dispatched
-     * to a window.
-     * Return true if any active rule could be triggered by the key event, otherwise false.
+     * Check if the key event could be triggered by combine key rule before dispatching to a window.
      */
-    boolean interceptKey(KeyEvent event, boolean interactive) {
+    void interceptKey(KeyEvent event, boolean interactive) {
         final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
         final int keyCode = event.getKeyCode();
         final int count = mActiveRules.size();
@@ -119,9 +117,9 @@
                     // exceed time from first key down.
                     forAllRules(mActiveRules, (rule)-> rule.cancel());
                     mActiveRules.clear();
-                    return false;
+                    return;
                 } else if (count == 0) { // has some key down but no active rule exist.
-                    return false;
+                    return;
                 }
             }
 
@@ -129,7 +127,7 @@
                 mDownTimes.put(keyCode, eventTime);
             } else {
                 // ignore old key, maybe a repeat key.
-                return false;
+                return;
             }
 
             if (mDownTimes.size() == 1) {
@@ -143,7 +141,7 @@
             } else {
                 // Ignore if rule already triggered.
                 if (mTriggeredRule != null) {
-                    return true;
+                    return;
                 }
 
                 // check if second key can trigger rule, or remove the non-match rule.
@@ -158,7 +156,6 @@
                 mActiveRules.clear();
                 if (mTriggeredRule != null) {
                     mActiveRules.add(mTriggeredRule);
-                    return true;
                 }
             }
         } else {
@@ -171,7 +168,6 @@
                 }
             }
         }
-        return false;
     }
 
     /**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 1b192e4..c50efc7 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -73,8 +73,6 @@
 import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
-import static com.android.server.policy.SingleKeyGestureDetector.KEY_LONGPRESS;
-import static com.android.server.policy.SingleKeyGestureDetector.KEY_VERYLONGPRESS;
 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;
@@ -458,6 +456,7 @@
     volatile boolean mPowerKeyHandled;
     volatile boolean mBackKeyHandled;
     volatile boolean mBeganFromNonInteractive;
+    volatile int mPowerKeyPressCounter;
     volatile boolean mEndCallKeyHandled;
     volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
     volatile boolean mGoingToSleep;
@@ -498,6 +497,7 @@
     boolean mHasSoftInput = false;
     boolean mHapticTextHandleEnabled;
     boolean mUseTvRouting;
+    int mVeryLongPressTimeout;
     boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
     MetricsLogger mLogger;
     boolean mWakeOnDpadKeyPress;
@@ -595,13 +595,14 @@
     private final com.android.internal.policy.LogDecelerateInterpolator mLogDecelerateInterpolator
             = new LogDecelerateInterpolator(100, 0);
 
+    private final MutableBoolean mTmpBoolean = new MutableBoolean(false);
+
     private boolean mPerDisplayFocusEnabled = false;
     private volatile int mTopFocusedDisplayId = INVALID_DISPLAY;
 
     private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS;
 
     private KeyCombinationManager mKeyCombinationManager;
-    private SingleKeyGestureDetector mSingleKeyGestureDetector;
 
     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;
@@ -612,7 +613,10 @@
     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_POWER_DELAYED_PRESS = 13;
+    private static final int MSG_POWER_LONG_PRESS = 14;
     private static final int MSG_SHOW_PICTURE_IN_PICTURE_MENU = 15;
+    private static final int MSG_BACK_LONG_PRESS = 16;
     private static final int MSG_ACCESSIBILITY_SHORTCUT = 17;
     private static final int MSG_BUGREPORT_TV = 18;
     private static final int MSG_ACCESSIBILITY_TV = 19;
@@ -620,7 +624,8 @@
     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_POWER_VERY_LONG_PRESS = 25;
+    private static final int MSG_RINGER_TOGGLE_CHORD = 26;
 
     private class PolicyHandler extends Handler {
         @Override
@@ -661,9 +666,22 @@
                 case MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK:
                     launchVoiceAssistWithWakeLock();
                     break;
+                case MSG_POWER_DELAYED_PRESS:
+                    powerPress((Long) msg.obj, msg.arg1 != 0, msg.arg2);
+                    finishPowerKeyPress();
+                    break;
+                case MSG_POWER_LONG_PRESS:
+                    powerLongPress((Long) msg.obj /* eventTime */);
+                    break;
+                case MSG_POWER_VERY_LONG_PRESS:
+                    powerVeryLongPress();
+                    break;
                 case MSG_SHOW_PICTURE_IN_PICTURE_MENU:
                     showPictureInPictureMenuInternal();
                     break;
+                case MSG_BACK_LONG_PRESS:
+                    backLongPress();
+                    break;
                 case MSG_ACCESSIBILITY_SHORTCUT:
                     accessibilityShortcutActivated();
                     break;
@@ -774,6 +792,13 @@
         }
     };
 
+    private Runnable mPossibleVeryLongPressReboot = new Runnable() {
+        @Override
+        public void run() {
+            mActivityManagerInternal.prepareForPossibleShutdown();
+        }
+    };
+
     private void handleRingerChordGesture() {
         if (mRingerToggleChord == VOLUME_HUSH_OFF) {
             return;
@@ -813,13 +838,28 @@
         }
     }
 
+    private void interceptBackKeyDown() {
+        mLogger.count("key_back_down", 1);
+        // Reset back key state for long press
+        mBackKeyHandled = false;
+
+        if (hasLongPressOnBackBehavior()) {
+            Message msg = mHandler.obtainMessage(MSG_BACK_LONG_PRESS);
+            msg.setAsynchronous(true);
+            mHandler.sendMessageDelayed(msg,
+                    ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
+        }
+    }
 
     // returns true if the key was handled and should not be passed to the user
-    private boolean backKeyPress() {
-        mLogger.count("key_back_press", 1);
+    private boolean interceptBackKeyUp(KeyEvent event) {
+        mLogger.count("key_back_up", 1);
         // Cache handled state
         boolean handled = mBackKeyHandled;
 
+        // Reset back long press state
+        cancelPendingBackKeyAction();
+
         if (mHasFeatureWatch) {
             TelecomManager telecomManager = getTelecommService();
 
@@ -841,9 +881,10 @@
             }
         }
 
-        if (mAutofillManagerInternal != null) {
+        if (mAutofillManagerInternal != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
             mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_BACK_KEY_TO_AUTOFILL));
         }
+
         return handled;
     }
 
@@ -853,6 +894,11 @@
             mPowerKeyWakeLock.acquire();
         }
 
+        // Cancel multi-press detection timeout.
+        if (mPowerKeyPressCounter != 0) {
+            mHandler.removeMessages(MSG_POWER_DELAYED_PRESS);
+        }
+
         mWindowManagerFuncs.onPowerKeyDown(interactive);
 
         // Stop ringing or end call if configured to do so when power is pressed.
@@ -874,20 +920,71 @@
 
         final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);
 
+        GestureLauncherService gestureService = LocalServices.getService(
+                GestureLauncherService.class);
+        boolean gesturedServiceIntercepted = false;
+        if (gestureService != null) {
+            gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive,
+                    mTmpBoolean);
+            if (mTmpBoolean.value && mRequestedOrGoingToSleep) {
+                mCameraGestureTriggeredDuringGoingToSleep = true;
+            }
+        }
+
         // Inform the StatusBar; but do not allow it to consume the event.
         sendSystemKeyToStatusBarAsync(event.getKeyCode());
 
+        schedulePossibleVeryLongPressReboot();
+
         // 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
+        mPowerKeyHandled = hungUp || gesturedServiceIntercepted
                 || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
         if (!mPowerKeyHandled) {
-            if (!interactive) {
+            if (interactive) {
+                // When interactive, we're already awake.
+                // Wait for a long press or for the button to be released to decide what to do.
+                if (hasLongPressOnPowerBehavior()) {
+                    if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
+                        powerLongPress(event.getEventTime());
+                    } else {
+                        Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS,
+                                event.getEventTime());
+                        msg.setAsynchronous(true);
+                        mHandler.sendMessageDelayed(msg,
+                                ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
+
+                        if (hasVeryLongPressOnPowerBehavior()) {
+                            Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS);
+                            longMsg.setAsynchronous(true);
+                            mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout);
+                        }
+                    }
+                }
+            } else {
                 wakeUpFromPowerKey(event.getDownTime());
+
                 if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
+                    if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
+                        powerLongPress(event.getEventTime());
+                    } else {
+                        Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS,
+                                event.getEventTime());
+                        msg.setAsynchronous(true);
+                        mHandler.sendMessageDelayed(msg,
+                                ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
+
+                        if (hasVeryLongPressOnPowerBehavior()) {
+                            Message longMsg = mHandler.obtainMessage(MSG_POWER_VERY_LONG_PRESS);
+                            longMsg.setAsynchronous(true);
+                            mHandler.sendMessageDelayed(longMsg, mVeryLongPressTimeout);
+                        }
+                    }
+
                     mBeganFromNonInteractive = true;
                 } else {
                     final int maxCount = getMaxMultiPressPowerCount();
+
                     if (maxCount <= 1) {
                         mPowerKeyHandled = true;
                     } else {
@@ -895,38 +992,68 @@
                     }
                 }
             }
-        } else {
-            // handled by another power key policy.
-            if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {
-                mSingleKeyGestureDetector.reset();
-            }
         }
     }
 
     private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {
         final boolean handled = canceled || mPowerKeyHandled;
+        cancelPendingPowerKeyAction();
 
         if (!handled) {
             if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) == 0) {
                 // Abort possibly stuck animations only when power key up without long press case.
                 mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
             }
-        } else {
-            // handled by single key or another power key policy.
-            mSingleKeyGestureDetector.reset();
-            finishPowerKeyPress();
+
+            // Figure out how to handle the key now that it has been released.
+            mPowerKeyPressCounter += 1;
+
+            final int maxCount = getMaxMultiPressPowerCount();
+            final long eventTime = event.getDownTime();
+            if (mPowerKeyPressCounter < maxCount) {
+                // This could be a multi-press.  Wait a little bit longer to confirm.
+                // Continue holding the wake lock.
+                Message msg = mHandler.obtainMessage(MSG_POWER_DELAYED_PRESS,
+                        interactive ? 1 : 0, mPowerKeyPressCounter, eventTime);
+                msg.setAsynchronous(true);
+                mHandler.sendMessageDelayed(msg, ViewConfiguration.getMultiPressTimeout());
+                return;
+            }
+
+            // No other actions.  Handle it immediately.
+            powerPress(eventTime, interactive, mPowerKeyPressCounter);
         }
 
+        // Done.  Reset our state.
+        finishPowerKeyPress();
     }
 
     private void finishPowerKeyPress() {
         mBeganFromNonInteractive = false;
-        mPowerKeyHandled = false;
+        mPowerKeyPressCounter = 0;
         if (mPowerKeyWakeLock.isHeld()) {
             mPowerKeyWakeLock.release();
         }
     }
 
+    private void cancelPendingPowerKeyAction() {
+        if (!mPowerKeyHandled) {
+            mPowerKeyHandled = true;
+            mHandler.removeMessages(MSG_POWER_LONG_PRESS);
+        }
+        if (hasVeryLongPressOnPowerBehavior()) {
+            mHandler.removeMessages(MSG_POWER_VERY_LONG_PRESS);
+        }
+        cancelPossibleVeryLongPressReboot();
+    }
+
+    private void cancelPendingBackKeyAction() {
+        if (!mBackKeyHandled) {
+            mBackKeyHandled = true;
+            mHandler.removeMessages(MSG_BACK_LONG_PRESS);
+        }
+    }
+
     private void powerPress(long eventTime, boolean interactive, int count) {
         if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) {
             Slog.i(TAG, "Suppressed redundant power key press while "
@@ -1077,7 +1204,6 @@
 
     private void powerLongPress(long eventTime) {
         final int behavior = getResolvedLongPressOnPowerBehavior();
-
         switch (behavior) {
             case LONG_PRESS_POWER_NOTHING:
                 break;
@@ -1716,6 +1842,8 @@
                 com.android.internal.R.integer.config_triplePressOnPowerBehavior);
         mShortPressOnSleepBehavior = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_shortPressOnSleepBehavior);
+        mVeryLongPressTimeout = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_veryLongPressTimeout);
         mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup);
 
@@ -1809,7 +1937,6 @@
                     }
                 });
         initKeyCombinationRules();
-        initSingleKeyGestureRules();
     }
 
     private void initKeyCombinationRules() {
@@ -1822,7 +1949,7 @@
                     new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) {
                         @Override
                         void execute() {
-                            mPowerKeyHandled = true;
+                            cancelPendingPowerKeyAction();
                             interceptScreenshotChord();
                         }
                         @Override
@@ -1857,7 +1984,7 @@
                     }
                     @Override
                     void execute() {
-                        mPowerKeyHandled = true;
+                        cancelPendingPowerKeyAction();
                         interceptRingerToggleChord();
                     }
                     @Override
@@ -1871,7 +1998,7 @@
                     new TwoKeysCombinationRule(KEYCODE_BACK, KEYCODE_DPAD_DOWN) {
                         @Override
                         void execute() {
-                            mBackKeyHandled = true;
+                            cancelPendingBackKeyAction();
                             interceptAccessibilityGestureTv();
                         }
 
@@ -1885,7 +2012,7 @@
                     new TwoKeysCombinationRule(KEYCODE_DPAD_CENTER, KEYCODE_BACK) {
                         @Override
                         void execute() {
-                            mBackKeyHandled = true;
+                            cancelPendingBackKeyAction();
                             interceptBugreportGestureTv();
                         }
 
@@ -1898,84 +2025,6 @@
     }
 
     /**
-     * Rule for single power key gesture.
-     */
-    private final class PowerKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
-        PowerKeyRule(int gestures) {
-            super(KEYCODE_POWER, gestures);
-        }
-
-        @Override
-        int getMaxMultiPressCount() {
-            return getMaxMultiPressPowerCount();
-        }
-
-        @Override
-        void onPress(long downTime) {
-            powerPress(downTime, true, 1 /*count*/);
-            finishPowerKeyPress();
-        }
-
-        @Override
-        void onLongPress(long downTime) {
-            powerLongPress(downTime);
-        }
-
-        @Override
-        void onVeryLongPress(long downTime) {
-            mActivityManagerInternal.prepareForPossibleShutdown();
-            powerVeryLongPress();
-        }
-
-        @Override
-        void onMultiPress(long downTime, int count) {
-            powerPress(downTime, true, count);
-            finishPowerKeyPress();
-        }
-    }
-
-    /**
-     * Rule for single back key gesture.
-     */
-    private final class BackKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
-        BackKeyRule(int gestures) {
-            super(KEYCODE_BACK, gestures);
-        }
-
-        @Override
-        int getMaxMultiPressCount() {
-            return 1;
-        }
-
-        @Override
-        void onPress(long downTime) {
-            mBackKeyHandled |= backKeyPress();
-        }
-
-        @Override
-        void onLongPress(long downTime) {
-            backLongPress();
-        }
-    }
-
-    private void initSingleKeyGestureRules() {
-        mSingleKeyGestureDetector = new SingleKeyGestureDetector(mContext);
-
-        int powerKeyGestures = 0;
-        if (hasVeryLongPressOnPowerBehavior()) {
-            powerKeyGestures |= KEY_VERYLONGPRESS;
-        }
-        if (hasLongPressOnPowerBehavior()) {
-            powerKeyGestures |= KEY_LONGPRESS;
-        }
-        mSingleKeyGestureDetector.addRule(new PowerKeyRule(powerKeyGestures));
-
-        if (hasLongPressOnBackBehavior()) {
-            mSingleKeyGestureDetector.addRule(new BackKeyRule(KEY_LONGPRESS));
-        }
-    }
-
-    /**
      * 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.
@@ -3519,21 +3568,8 @@
             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);
-        }
-
         if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
-            handleKeyGesture(event, interactive);
+            mKeyCombinationManager.interceptKey(event, interactive);
         }
 
         // Enable haptics if down and virtual key without multiple repetitions. If this is a hard
@@ -3548,13 +3584,12 @@
         switch (keyCode) {
             case KeyEvent.KEYCODE_BACK: {
                 if (down) {
-                    mBackKeyHandled = false;
+                    interceptBackKeyDown();
                 } else {
-                    if (!hasLongPressOnBackBehavior()) {
-                        mBackKeyHandled |= backKeyPress();
-                    }
+                    boolean handled = interceptBackKeyUp(event);
+
                     // Don't pass back press to app if we've already handled it via long press
-                    if (mBackKeyHandled) {
+                    if (handled) {
                         result &= ~ACTION_PASS_TO_USER;
                     }
                 }
@@ -3666,17 +3701,33 @@
             case KeyEvent.KEYCODE_TV_POWER: {
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false; // wake-up will be handled separately
-                if (down && hdmiControlManager != null) {
-                    hdmiControlManager.toggleAndFollowTvPower();
+                HdmiControlManager hdmiControlManager = getHdmiControlManager();
+                if (hdmiControlManager != null && hdmiControlManager.shouldHandleTvPowerKey()) {
+                    if (down) {
+                        hdmiControlManager.toggleAndFollowTvPower();
+                    }
+                } else if (mHasFeatureLeanback) {
+                    KeyEvent fallbackEvent = 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);
+                    if (down) {
+                        interceptPowerKeyDown(fallbackEvent, interactive);
+                    } else {
+                        interceptPowerKeyUp(fallbackEvent, interactive, canceled);
+                    }
                 }
+                // Ignore this key for any device that is not connected to a TV via HDMI and
+                // not an Android TV device.
                 break;
             }
 
             case KeyEvent.KEYCODE_POWER: {
                 EventLogTags.writeInterceptPower(
                         KeyEvent.actionToString(event.getAction()),
-                        mPowerKeyHandled ? 1 : 0,
-                        mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));
+                        mPowerKeyHandled ? 1 : 0, mPowerKeyPressCounter);
                 // Any activity on the power button stops the accessibility shortcut
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false; // wake-up will be handled separately
@@ -3857,43 +3908,6 @@
         return result;
     }
 
-    private void handleKeyGesture(KeyEvent event, boolean interactive) {
-        if (mKeyCombinationManager.interceptKey(event, interactive)) {
-            // handled by combo keys manager.
-            mSingleKeyGestureDetector.reset();
-            return;
-        }
-
-        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);
-    }
-
-    // The camera gesture will be detected by GestureLauncherService.
-    private boolean handleCameraGesture(KeyEvent event, boolean interactive) {
-        // camera gesture.
-        GestureLauncherService gestureService = LocalServices.getService(
-                GestureLauncherService.class);
-        if (gestureService == null) {
-            return false;
-        }
-
-        final MutableBoolean outLaunched = new MutableBoolean(false);
-        final boolean gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event,
-                interactive, outLaunched);
-        if (outLaunched.value && mRequestedOrGoingToSleep) {
-            mCameraGestureTriggeredDuringGoingToSleep = true;
-        }
-        return gesturedServiceIntercepted;
-    }
-
     /**
      * Handle statusbar expansion events.
      * @param event
@@ -4893,6 +4907,15 @@
         }
     }
 
+    private void schedulePossibleVeryLongPressReboot() {
+        mHandler.removeCallbacks(mPossibleVeryLongPressReboot);
+        mHandler.postDelayed(mPossibleVeryLongPressReboot, mVeryLongPressTimeout);
+    }
+
+    private void cancelPossibleVeryLongPressReboot() {
+        mHandler.removeCallbacks(mPossibleVeryLongPressReboot);
+    }
+
     // TODO (multidisplay): Support multiple displays in WindowManagerPolicy.
     private void updateScreenOffSleepToken(boolean acquire) {
         if (acquire) {
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
deleted file mode 100644
index 3dafb0ce..0000000
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (C) 2021 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 android.annotation.IntDef;
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.ViewConfiguration;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-
-/**
- * Detect single key gesture: press, long press, very long press and multi press.
- *
- * Call {@link #reset} if current {@link KeyEvent} has been handled by another policy
- */
-
-public final class SingleKeyGestureDetector {
-    private static final String TAG = "SingleKeyGesture";
-    private static final boolean DEBUG = false;
-
-    private static final int MSG_KEY_LONG_PRESS = 0;
-    private static final int MSG_KEY_VERY_LONG_PRESS = 1;
-    private static final int MSG_KEY_DELAYED_PRESS = 2;
-
-    private final long mLongPressTimeout;
-    private final long mVeryLongPressTimeout;
-
-    private volatile int mKeyPressCounter;
-
-    private final ArrayList<SingleKeyRule> mRules = new ArrayList();
-    private SingleKeyRule mActiveRule = null;
-
-    // Key code of current key down event, reset when key up.
-    private int mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN;
-    private volatile boolean mHandledByLongPress = false;
-    private final Handler mHandler;
-    private static final long MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout();
-
-
-    /** Supported gesture flags */
-    public static final int KEY_LONGPRESS = 1 << 1;
-    public static final int KEY_VERYLONGPRESS = 1 << 2;
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = { "KEY_" }, value = {
-            KEY_LONGPRESS,
-            KEY_VERYLONGPRESS,
-    })
-    public @interface KeyGestureFlag {}
-
-    /**
-     *  Rule definition for single keys gesture.
-     *  E.g : define power key.
-     *  <pre class="prettyprint">
-     *  SingleKeyRule rule =
-     *      new SingleKeyRule(KEYCODE_POWER, KEY_LONGPRESS|KEY_VERYLONGPRESS) {
-     *           int getMaxMultiPressCount() { // maximum multi press count. }
-     *           void onPress(long downTime) { // short press behavior. }
-     *           void onLongPress() { // long press behavior. }
-     *           void onVeryLongPress() { // very long press behavior. }
-     *           void onMultiPress(long downTime, int count) { // multi press behavior.  }
-     *       };
-     *  </pre>
-     */
-    abstract static class SingleKeyRule {
-        private final int mKeyCode;
-        private final int mSupportedGestures;
-
-        SingleKeyRule(int keyCode, @KeyGestureFlag int supportedGestures) {
-            mKeyCode = keyCode;
-            mSupportedGestures = supportedGestures;
-        }
-
-        /**
-         *  True if the rule could intercept the key.
-         */
-        private boolean shouldInterceptKey(int keyCode) {
-            return keyCode == mKeyCode;
-        }
-
-        /**
-         *  True if the rule support long press.
-         */
-        private boolean supportLongPress() {
-            return (mSupportedGestures & KEY_LONGPRESS) != 0;
-        }
-
-        /**
-         *  True if the rule support very long press.
-         */
-        private boolean supportVeryLongPress() {
-            return (mSupportedGestures & KEY_VERYLONGPRESS) != 0;
-        }
-
-        /**
-         *  Maximum count of multi presses.
-         *  Return 1 will trigger onPress immediately when {@link KeyEvent.ACTION_UP}.
-         *  Otherwise trigger onMultiPress immediately when reach max count when
-         *  {@link KeyEvent.ACTION_DOWN}.
-         */
-        int getMaxMultiPressCount() {
-            return 1;
-        }
-
-        /**
-         *  Called when short press has been detected.
-         */
-        abstract void onPress(long downTime);
-        /**
-         *  Callback when multi press (>= 2) has been detected.
-         */
-        void onMultiPress(long downTime, int count) {}
-        /**
-         *  Callback when long press has been detected.
-         */
-        void onLongPress(long downTime) {}
-        /**
-         *  Callback when very long press has been detected.
-         */
-        void onVeryLongPress(long downTime) {}
-
-        @Override
-        public String toString() {
-            return "KeyCode = " + KeyEvent.keyCodeToString(mKeyCode)
-                    + ", long press : " + supportLongPress()
-                    + ", very Long press : " + supportVeryLongPress()
-                    + ", max multi press count : " + getMaxMultiPressCount();
-        }
-    }
-
-    public SingleKeyGestureDetector(Context context) {
-        mLongPressTimeout = ViewConfiguration.get(context).getDeviceGlobalActionKeyTimeout();
-        mVeryLongPressTimeout = context.getResources().getInteger(
-                com.android.internal.R.integer.config_veryLongPressTimeout);
-        mHandler = new KeyHandler();
-    }
-
-    void addRule(SingleKeyRule rule) {
-        mRules.add(rule);
-    }
-
-    void interceptKey(KeyEvent event) {
-        if (event.getAction() == KeyEvent.ACTION_DOWN) {
-            interceptKeyDown(event);
-        } else {
-            interceptKeyUp(event);
-        }
-    }
-
-    private void interceptKeyDown(KeyEvent event) {
-        final int keyCode = event.getKeyCode();
-        // same key down.
-        if (mDownKeyCode == keyCode) {
-            if (mActiveRule != null && (event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0
-                    && !mHandledByLongPress) {
-                if (DEBUG) {
-                    Log.i(TAG, "Long press Key " + KeyEvent.keyCodeToString(keyCode));
-                }
-                mHandledByLongPress = true;
-                mHandler.removeMessages(MSG_KEY_LONG_PRESS);
-                mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
-                mActiveRule.onLongPress(event.getEventTime());
-            }
-            return;
-        }
-
-        // When a different key is pressed, stop processing gestures for the currently active key.
-        if (mDownKeyCode != KeyEvent.KEYCODE_UNKNOWN
-                || (mActiveRule != null && !mActiveRule.shouldInterceptKey(keyCode))) {
-            if (DEBUG) {
-                Log.i(TAG, "Press another key " + KeyEvent.keyCodeToString(keyCode));
-            }
-
-            reset();
-        }
-        mDownKeyCode = keyCode;
-
-        // Picks a new rule, return if no rule picked.
-        if (mActiveRule == null) {
-            final int count = mRules.size();
-            for (int index = 0; index < count; index++) {
-                final SingleKeyRule rule = mRules.get(index);
-                if (rule.shouldInterceptKey(keyCode)) {
-                    mActiveRule = rule;
-                    break;
-                }
-            }
-        }
-        if (mActiveRule == null) {
-            return;
-        }
-
-        final long eventTime = event.getEventTime();
-        if (mKeyPressCounter == 0) {
-            if (mActiveRule.supportLongPress()) {
-                final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, keyCode, 0,
-                        eventTime);
-                msg.setAsynchronous(true);
-                mHandler.sendMessageDelayed(msg, mLongPressTimeout);
-            }
-
-            if (mActiveRule.supportVeryLongPress()) {
-                final Message msg = mHandler.obtainMessage(MSG_KEY_VERY_LONG_PRESS, keyCode, 0,
-                        eventTime);
-                msg.setAsynchronous(true);
-                mHandler.sendMessageDelayed(msg, mVeryLongPressTimeout);
-            }
-        } else {
-            mHandler.removeMessages(MSG_KEY_LONG_PRESS);
-            mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
-            mHandler.removeMessages(MSG_KEY_DELAYED_PRESS);
-
-            // Trigger multi press immediately when reach max count.( > 1)
-            if (mKeyPressCounter == mActiveRule.getMaxMultiPressCount() - 1) {
-                if (DEBUG) {
-                    Log.i(TAG, "Trigger multi press " + mActiveRule.toString() + " for it"
-                            + " reach the max count " + mKeyPressCounter);
-                }
-                mActiveRule.onMultiPress(eventTime, mKeyPressCounter + 1);
-                mKeyPressCounter = 0;
-            }
-        }
-    }
-
-    private boolean interceptKeyUp(KeyEvent event) {
-        mHandler.removeMessages(MSG_KEY_LONG_PRESS);
-        mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
-        mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN;
-        if (mActiveRule == null) {
-            return false;
-        }
-
-        if (mHandledByLongPress) {
-            mHandledByLongPress = false;
-            return true;
-        }
-
-        final long downTime = event.getDownTime();
-        if (event.getKeyCode() == mActiveRule.mKeyCode) {
-            // Directly trigger short press when max count is 1.
-            if (mActiveRule.getMaxMultiPressCount() == 1) {
-                mActiveRule.onPress(downTime);
-                return true;
-            }
-
-            // This could be a multi-press.  Wait a little bit longer to confirm.
-            mKeyPressCounter++;
-            Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode,
-                    mKeyPressCounter, downTime);
-            msg.setAsynchronous(true);
-            mHandler.sendMessageDelayed(msg, MULTI_PRESS_TIMEOUT);
-            return true;
-        }
-        reset();
-        return false;
-    }
-
-    int getKeyPressCounter(int keyCode) {
-        if (mActiveRule != null && mActiveRule.mKeyCode == keyCode) {
-            return mKeyPressCounter;
-        } else {
-            return 0;
-        }
-    }
-
-    void reset() {
-        if (mActiveRule != null) {
-            if (mDownKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
-                mHandler.removeMessages(MSG_KEY_LONG_PRESS);
-                mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
-            }
-
-            if (mKeyPressCounter > 0) {
-                mHandler.removeMessages(MSG_KEY_DELAYED_PRESS);
-                mKeyPressCounter = 0;
-            }
-            mActiveRule = null;
-        }
-
-        mHandledByLongPress = false;
-        mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN;
-    }
-
-    boolean isKeyIntercepted(int keyCode) {
-        if (mActiveRule != null && mActiveRule.shouldInterceptKey(keyCode)) {
-            return mHandledByLongPress;
-        }
-        return false;
-    }
-
-    private class KeyHandler extends Handler {
-        KeyHandler() {
-            super(Looper.getMainLooper());
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            if (mActiveRule == null) {
-                return;
-            }
-            final int keyCode = msg.arg1;
-            final long eventTime = (long) msg.obj;
-            switch(msg.what) {
-                case MSG_KEY_LONG_PRESS:
-                    if (DEBUG) {
-                        Log.i(TAG, "Detect long press " + KeyEvent.keyCodeToString(keyCode));
-                    }
-                    mHandledByLongPress = true;
-                    mActiveRule.onLongPress(eventTime);
-                    break;
-                case MSG_KEY_VERY_LONG_PRESS:
-                    if (DEBUG) {
-                        Log.i(TAG, "Detect very long press "
-                                + KeyEvent.keyCodeToString(keyCode));
-                    }
-                    mHandledByLongPress = true;
-                    mActiveRule.onVeryLongPress(eventTime);
-                    break;
-                case MSG_KEY_DELAYED_PRESS:
-                    if (DEBUG) {
-                        Log.i(TAG, "Detect press " + KeyEvent.keyCodeToString(keyCode)
-                                + ", count " + mKeyPressCounter);
-                    }
-                    if (mKeyPressCounter == 1) {
-                        mActiveRule.onPress(eventTime);
-                    } else {
-                        mActiveRule.onMultiPress(eventTime, mKeyPressCounter);
-                    }
-                    mKeyPressCounter = 0;
-                    break;
-            }
-        }
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
deleted file mode 100644
index 3025a95..0000000
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2021 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.view.KeyEvent.ACTION_DOWN;
-import static android.view.KeyEvent.ACTION_UP;
-import static android.view.KeyEvent.KEYCODE_POWER;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static com.android.server.policy.SingleKeyGestureDetector.KEY_LONGPRESS;
-import static com.android.server.policy.SingleKeyGestureDetector.KEY_VERYLONGPRESS;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.app.Instrumentation;
-import android.content.Context;
-import android.os.SystemClock;
-import android.view.KeyEvent;
-import android.view.ViewConfiguration;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Test class for {@link SingleKeyGestureDetector}.
- *
- * Build/Install/Run:
- *  atest WmTests:SingleKeyGestureTests
- */
-public class SingleKeyGestureTests {
-    private SingleKeyGestureDetector mDetector;
-
-    private int mMaxMultiPressPowerCount = 2;
-
-    private CountDownLatch mShortPressed = new CountDownLatch(1);
-    private CountDownLatch mLongPressed = new CountDownLatch(1);
-    private CountDownLatch mVeryLongPressed = new CountDownLatch(1);
-    private CountDownLatch mMultiPressed = new CountDownLatch(1);
-
-    private final Instrumentation mInstrumentation = getInstrumentation();
-    private final Context mContext = mInstrumentation.getTargetContext();
-    private long mWaitTimeout;
-    private long mLongPressTime;
-    private long mVeryLongPressTime;
-
-    @Before
-    public void setUp() {
-        mDetector = new SingleKeyGestureDetector(mContext);
-        initSingleKeyGestureRules();
-        mWaitTimeout = ViewConfiguration.getMultiPressTimeout() + 50;
-        mLongPressTime = ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout() + 50;
-        mVeryLongPressTime = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_veryLongPressTimeout) + 50;
-    }
-
-    private void initSingleKeyGestureRules() {
-        mDetector.addRule(new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_POWER,
-                KEY_LONGPRESS | KEY_VERYLONGPRESS) {
-            @Override
-            int getMaxMultiPressCount() {
-                return mMaxMultiPressPowerCount;
-            }
-            @Override
-            public void onPress(long downTime) {
-                mShortPressed.countDown();
-            }
-
-            @Override
-            void onLongPress(long downTime) {
-                mLongPressed.countDown();
-            }
-
-            @Override
-            void onVeryLongPress(long downTime) {
-                mVeryLongPressed.countDown();
-            }
-
-            @Override
-            void onMultiPress(long downTime, int count) {
-                mMultiPressed.countDown();
-                assertEquals(mMaxMultiPressPowerCount, count);
-            }
-        });
-    }
-
-    private void pressKey(long eventTime, int keyCode, long pressTime) {
-        final KeyEvent keyDown = new KeyEvent(eventTime, eventTime, ACTION_DOWN,
-                keyCode, 0 /* repeat */, 0 /* metaState */);
-        mDetector.interceptKey(keyDown);
-
-        // keep press down.
-        try {
-            Thread.sleep(pressTime);
-        } catch (InterruptedException e) {
-            e.printStackTrace();
-        }
-
-        eventTime += pressTime;
-        final KeyEvent keyUp = new KeyEvent(eventTime, eventTime, ACTION_UP,
-                keyCode, 0 /* repeat */, 0 /* metaState */);
-
-        mDetector.interceptKey(keyUp);
-    }
-
-    @Test
-    public void testShortPress() throws InterruptedException {
-        final long eventTime = SystemClock.uptimeMillis();
-        pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
-        assertTrue(mShortPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
-    }
-
-    @Test
-    public void testLongPress() throws InterruptedException {
-        final long eventTime = SystemClock.uptimeMillis();
-        pressKey(eventTime, KEYCODE_POWER, mLongPressTime);
-        assertTrue(mLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
-    }
-
-    @Test
-    public void testVeryLongPress() throws InterruptedException {
-        final long eventTime = SystemClock.uptimeMillis();
-        pressKey(eventTime, KEYCODE_POWER, mVeryLongPressTime);
-        assertTrue(mVeryLongPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
-    }
-
-    @Test
-    public void testMultiPress() throws InterruptedException {
-        final long eventTime = SystemClock.uptimeMillis();
-        pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
-        pressKey(eventTime, KEYCODE_POWER, 0 /* pressTime */);
-        assertTrue(mMultiPressed.await(mWaitTimeout, TimeUnit.MILLISECONDS));
-    }
-}