Add delay to power-button power-off after gesture wakeup.

Adds a small delay to avoid turning off the device if the user
hits the power button right after the device wakes up via a gesture.

Bug: 126560003
Test: Manual
Change-Id: I3116ce98f244e5660573d5fa764a77083aee7fc5
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 7d61bf6..2fff595 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -555,6 +555,18 @@
     }
 
     /**
+     * @hide
+     */
+    public static class WakeData {
+        public WakeData(long wakeTime, @WakeReason int wakeReason) {
+            this.wakeTime = wakeTime;
+            this.wakeReason = wakeReason;
+        }
+        public long wakeTime;
+        public @WakeReason int wakeReason;
+    }
+
+    /**
      * The value to pass as the 'reason' argument to reboot() to reboot into
      * recovery mode for tasks other than applying system updates, such as
      * doing factory resets.
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index d55489a..9661a08 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -203,4 +203,7 @@
 
     /** Returns whether there hasn't been a user activity event for the given number of ms. */
     public abstract boolean wasDeviceIdleFor(long ms);
+
+    /** Returns information about the last wakeup event. */
+    public abstract PowerManager.WakeData getLastWakeup();
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ac59101..58b70a7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14712,6 +14712,18 @@
         public static final String TEXT_CLASSIFIER_ACTION_MODEL_PARAMS =
                 "text_classifier_action_model_params";
 
+        /**
+         * The amount of time to suppress "power-off" from the power button after the device has
+         * woken due to a gesture (lifting the phone).  Since users have learned to hit the power
+         * button immediately when lifting their device, it can cause the device to turn off if a
+         * gesture has just woken the device. This value tells us the milliseconds to wait after
+         * a gesture before "power-off" via power-button is functional again. A value of 0 is no
+         * delay, and reverts to the old behavior.
+         *
+         * @hide
+         */
+        public static final String POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE =
+                "power_button_suppression_delay_after_gesture_wake";
     }
 
     /**
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index a853121..f4d3c81 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -392,6 +392,7 @@
                     Settings.Global.PDP_WATCHDOG_POLL_INTERVAL_MS,
                     Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT,
                     Settings.Global.POLICY_CONTROL,
+                    Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE,
                     Settings.Global.POWER_MANAGER_CONSTANTS,
                     Settings.Global.PREFERRED_NETWORK_MODE,
                     Settings.Global.PRIVATE_DNS_DEFAULT_MODE,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 9b4293d48..3eb7f95 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -310,6 +310,7 @@
     static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist";
     static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
 
+    private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;
     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
             .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
@@ -615,6 +616,8 @@
     private boolean mPerDisplayFocusEnabled = false;
     private volatile int mTopFocusedDisplayId = INVALID_DISPLAY;
 
+    private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS;
+
     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;
@@ -782,6 +785,9 @@
             resolver.registerContentObserver(Settings.Global.getUriFor(
                     Settings.Global.POWER_BUTTON_VERY_LONG_PRESS), false, this,
                     UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE), false, this,
+                    UserHandle.USER_ALL);
             updateSettings();
         }
 
@@ -1105,16 +1111,16 @@
                 case SHORT_PRESS_POWER_NOTHING:
                     break;
                 case SHORT_PRESS_POWER_GO_TO_SLEEP:
-                    goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
+                    goToSleepFromPowerButton(eventTime, 0);
                     break;
                 case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
-                    goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
-                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+                    goToSleepFromPowerButton(eventTime, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                     break;
                 case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
-                    goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
-                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
-                    launchHomeFromHotKey(DEFAULT_DISPLAY);
+                    if (goToSleepFromPowerButton(eventTime,
+                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE)) {
+                        launchHomeFromHotKey(DEFAULT_DISPLAY);
+                    }
                     break;
                 case SHORT_PRESS_POWER_GO_HOME:
                     shortPressPowerGoHome();
@@ -1137,6 +1143,35 @@
         }
     }
 
+    /**
+     * Sends the device to sleep as a result of a power button press.
+     *
+     * @return True if the was device was sent to sleep, false if sleep was suppressed.
+     */
+    private boolean goToSleepFromPowerButton(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) {
+            final int gestureDelayMillis = Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE,
+                    POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS);
+            final long now = SystemClock.uptimeMillis();
+            if (mPowerButtonSuppressionDelayMillis > 0
+                    && (now < lastWakeUp.wakeTime + mPowerButtonSuppressionDelayMillis)) {
+                Slog.i(TAG, "Sleep from power button suppressed. Time since gesture: "
+                        + (now - lastWakeUp.wakeTime) + "ms");
+                return false;
+            }
+        }
+
+        goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
+        return true;
+    }
+
     private void goToSleep(long eventTime, int reason, int flags) {
         mRequestedOrGoingToSleep = true;
         mPowerManager.goToSleep(eventTime, reason, flags);
@@ -1981,6 +2016,9 @@
             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 = Settings.Secure.VOLUME_HUSH_OFF;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index cfe11bf..e2bbb2d 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -52,6 +52,7 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
+import android.os.PowerManager.WakeData;
 import android.os.PowerManager.WakeReason;
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
@@ -4850,6 +4851,12 @@
         }
     }
 
+    private PowerManager.WakeData getLastWakeupInternal() {
+        synchronized (mLock) {
+            return new WakeData(mLastWakeTime, mLastWakeReason);
+        }
+    }
+
     private final class LocalService extends PowerManagerInternal {
         @Override
         public void setScreenBrightnessOverrideFromWindowManager(int screenBrightness) {
@@ -4971,5 +4978,10 @@
         public boolean wasDeviceIdleFor(long ms) {
             return wasDeviceIdleForInternal(ms);
         }
+
+        @Override
+        public WakeData getLastWakeup() {
+            return getLastWakeupInternal();
+        }
     }
 }