Revert "Revert "Allow focused privileged windows to capture the ..."

Revert submission 30698369-revert-30412668-HWHJRSWUQP

Reason for revert: Created a fix for the failing test

Reverted changes: /q/submissionid:30698369-revert-30412668-HWHJRSWUQP
Bug: b/382133936
Test: android.platform.test.scenario.sysui.power.PowerMenuTest#testPower_verifySystemPowerMenuAppears

Change-Id: I705b6371677ddbb71769a7c6cd21ddb0819ea301
diff --git a/core/java/com/android/internal/policy/KeyInterceptionInfo.java b/core/java/com/android/internal/policy/KeyInterceptionInfo.java
index b20f6d2..fed8fe3 100644
--- a/core/java/com/android/internal/policy/KeyInterceptionInfo.java
+++ b/core/java/com/android/internal/policy/KeyInterceptionInfo.java
@@ -27,11 +27,13 @@
     // Debug friendly name to help identify the window
     public final String windowTitle;
     public final int windowOwnerUid;
+    public final int inputFeaturesFlags;
 
-    public KeyInterceptionInfo(int type, int flags, String title, int uid) {
+    public KeyInterceptionInfo(int type, int flags, String title, int uid, int inputFeaturesFlags) {
         layoutParamsType = type;
         layoutParamsPrivateFlags = flags;
         windowTitle = title;
         windowOwnerUid = uid;
+        this.inputFeaturesFlags = inputFeaturesFlags;
     }
 }
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index ccc44a4..19bc8e3 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import static com.android.hardware.input.Flags.overridePowerKeyBehaviorInFocusedWindow;
 import static com.android.internal.R.integer.config_defaultMinEmergencyGestureTapDurationMillis;
 
 import android.app.ActivityManager;
@@ -103,7 +104,7 @@
     /**
      * Number of taps required to launch camera shortcut.
      */
-    private static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2;
+    public static final int CAMERA_POWER_TAP_COUNT_THRESHOLD = 2;
 
     /** The listener that receives the gesture event. */
     private final GestureEventListener mGestureListener = new GestureEventListener();
@@ -208,7 +209,9 @@
     }
 
     @VisibleForTesting
-    GestureLauncherService(Context context, MetricsLogger metricsLogger,
+    public GestureLauncherService(
+            Context context,
+            MetricsLogger metricsLogger,
             UiEventLogger uiEventLogger) {
         super(context);
         mContext = context;
@@ -501,14 +504,54 @@
     }
 
     /**
+     * Processes a power key event in GestureLauncherService without performing an action. This
+     * method is called on every KEYCODE_POWER ACTION_DOWN event and ensures that, even if
+     * KEYCODE_POWER events are passed to and handled by the app, the GestureLauncherService still
+     * keeps track of all running KEYCODE_POWER events for its gesture detection and relevant
+     * actions.
+     */
+    public void processPowerKeyDown(KeyEvent event) {
+        if (mEmergencyGestureEnabled && mEmergencyGesturePowerButtonCooldownPeriodMs >= 0
+                && event.getEventTime() - mLastEmergencyGestureTriggered
+                < mEmergencyGesturePowerButtonCooldownPeriodMs) {
+            return;
+        }
+        if (event.isLongPress()) {
+            return;
+        }
+
+        final long powerTapInterval;
+
+        synchronized (this) {
+            powerTapInterval = event.getEventTime() - mLastPowerDown;
+            mLastPowerDown = event.getEventTime();
+            if (powerTapInterval >= POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) {
+                // Tap too slow, reset consecutive tap counts.
+                mFirstPowerDown = event.getEventTime();
+                mPowerButtonConsecutiveTaps = 1;
+                mPowerButtonSlowConsecutiveTaps = 1;
+            } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
+                // Tap too slow for shortcuts
+                mFirstPowerDown = event.getEventTime();
+                mPowerButtonConsecutiveTaps = 1;
+                mPowerButtonSlowConsecutiveTaps++;
+            } else if (powerTapInterval > 0) {
+                // Fast consecutive tap
+                mPowerButtonConsecutiveTaps++;
+                mPowerButtonSlowConsecutiveTaps++;
+            }
+        }
+    }
+
+    /**
      * Attempts to intercept power key down event by detecting certain gesture patterns
      *
      * @param interactive true if the event's policy contains {@code FLAG_INTERACTIVE}
      * @param outLaunched true if some action is taken as part of the key intercept (eg, app launch)
      * @return true if the key down event is intercepted
      */
-    public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
-            MutableBoolean outLaunched) {
+    public boolean interceptPowerKeyDown(
+            KeyEvent event, boolean interactive, MutableBoolean outLaunched) {
         if (mEmergencyGestureEnabled && mEmergencyGesturePowerButtonCooldownPeriodMs >= 0
                 && event.getEventTime() - mLastEmergencyGestureTriggered
                 < mEmergencyGesturePowerButtonCooldownPeriodMs) {
@@ -546,7 +589,7 @@
                 mFirstPowerDown  = event.getEventTime();
                 mPowerButtonConsecutiveTaps = 1;
                 mPowerButtonSlowConsecutiveTaps++;
-            } else {
+            } else if (!overridePowerKeyBehaviorInFocusedWindow() || powerTapInterval > 0) {
                 // Fast consecutive tap
                 mPowerButtonConsecutiveTaps++;
                 mPowerButtonSlowConsecutiveTaps++;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f1a4811..3fb371c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -88,10 +88,12 @@
 import static com.android.hardware.input.Flags.inputManagerLifecycleSupport;
 import static com.android.hardware.input.Flags.keyboardA11yShortcutControl;
 import static com.android.hardware.input.Flags.modifierShortcutDump;
+import static com.android.hardware.input.Flags.overridePowerKeyBehaviorInFocusedWindow;
 import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
+import static com.android.server.GestureLauncherService.CAMERA_POWER_TAP_COUNT_THRESHOLD;
 import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser;
 import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
 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;
@@ -432,6 +434,16 @@
             "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.)
@@ -492,6 +504,32 @@
 
     private WindowWakeUpPolicy mWindowWakeUpPolicy;
 
+    /**
+     * The three variables below are used for custom power key gesture detection in
+     * PhoneWindowManager. They are used to detect when the power button has been double pressed
+     * and, when it does happen, makes the behavior overrideable by the app.
+     *
+     * We cannot use the {@link PowerKeyRule} for this because multi-press power gesture detection
+     * and behaviors are handled by {@link com.android.server.GestureLauncherService}, and the
+     * {@link PowerKeyRule} only handles single and long-presses of the power button. As a result,
+     * overriding the double tap behavior requires custom gesture detection here that mimics the
+     * logic in {@link com.android.server.GestureLauncherService}.
+     *
+     * Long-term, it would be beneficial to move all power gesture detection to
+     * {@link PowerKeyRule} so that this custom logic isn't required.
+     */
+    // Time of last power down event.
+    private long mLastPowerDown;
+
+    // Number of power button events consecutively triggered (within a specific timeout threshold).
+    private int mPowerButtonConsecutiveTaps = 0;
+
+    // Whether a double tap of the power button has been detected.
+    volatile boolean mDoubleTapPowerDetected;
+
+    // Runnable that is queued on a delay when the first power keyDown event is sent to the app.
+    private Runnable mPowerKeyDelayedRunnable = null;
+
     boolean mSafeMode;
 
     // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
@@ -1097,6 +1135,11 @@
         mPowerKeyHandled = mPowerKeyHandled || hungUp
                 || handledByPowerManager || isKeyGestureTriggered
                 || mKeyCombinationManager.isPowerKeyIntercepted();
+
+        if (overridePowerKeyBehaviorInFocusedWindow()) {
+            mPowerKeyHandled |= mDoubleTapPowerDetected;
+        }
+
         if (!mPowerKeyHandled) {
             if (!interactive) {
                 wakeUpFromWakeKey(event);
@@ -2669,7 +2712,19 @@
             if (mShouldEarlyShortPressOnPower) {
                 return;
             }
-            powerPress(downTime, 1 /*count*/, displayId);
+            // TODO(b/380433365): Remove deferring single power press action when refactoring.
+            if (overridePowerKeyBehaviorInFocusedWindow()) {
+                mDeferredKeyActionExecutor.cancelQueuedAction(KEYCODE_POWER);
+                mDeferredKeyActionExecutor.queueKeyAction(
+                        KEYCODE_POWER,
+                        downTime,
+                        () -> {
+                            powerPress(downTime, 1 /*count*/, displayId);
+                        });
+            } else {
+                powerPress(downTime, 1 /*count*/, displayId);
+            }
+
         }
 
         @Override
@@ -2700,7 +2755,17 @@
 
         @Override
         void onMultiPress(long downTime, int count, int displayId) {
-            powerPress(downTime, count, displayId);
+            if (overridePowerKeyBehaviorInFocusedWindow()) {
+                mDeferredKeyActionExecutor.cancelQueuedAction(KEYCODE_POWER);
+                mDeferredKeyActionExecutor.queueKeyAction(
+                        KEYCODE_POWER,
+                        downTime,
+                        () -> {
+                            powerPress(downTime, count, displayId);
+                        });
+            } else {
+                powerPress(downTime, count, displayId);
+            }
         }
 
         @Override
@@ -3477,6 +3542,12 @@
             }
         }
 
+        if (overridePowerKeyBehaviorInFocusedWindow() && event.getKeyCode() == KEYCODE_POWER
+                && event.getAction() == KeyEvent.ACTION_UP
+                && mDoubleTapPowerDetected) {
+            mDoubleTapPowerDetected = false;
+        }
+
         return needToConsumeKey ? keyConsumed : keyNotConsumed;
     }
 
@@ -3992,6 +4063,8 @@
                     sendSystemKeyToStatusBarAsync(event);
                     return true;
                 }
+            case KeyEvent.KEYCODE_POWER:
+                return interceptPowerKeyBeforeDispatching(focusedToken, event);
             case KeyEvent.KEYCODE_SCREENSHOT:
                 if (firstDown) {
                     interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
@@ -4047,6 +4120,8 @@
                     sendSystemKeyToStatusBarAsync(event);
                     return true;
                 }
+            case KeyEvent.KEYCODE_POWER:
+                return interceptPowerKeyBeforeDispatching(focusedToken, event);
         }
         if (isValidGlobalKey(keyCode)
                 && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
@@ -4057,6 +4132,90 @@
         return (metaState & KeyEvent.META_META_ON) != 0;
     }
 
+    /**
+     * Called by interceptKeyBeforeDispatching to handle interception logic for KEYCODE_POWER
+     * KeyEvents.
+     *
+     * @return true if intercepting the key, false if sending to app.
+     */
+    private boolean interceptPowerKeyBeforeDispatching(IBinder focusedToken, KeyEvent event) {
+        if (!overridePowerKeyBehaviorInFocusedWindow()) {
+            //Flag disabled: intercept the power key and do not send to app.
+            return true;
+        }
+        if (event.getKeyCode() != KEYCODE_POWER) {
+            Log.wtf(TAG, "interceptPowerKeyBeforeDispatching received a non-power KeyEvent "
+                    + "with key code: " + event.getKeyCode());
+            return false;
+        }
+
+        // Intercept keys (don't send to app) for 3x, 4x, 5x gestures)
+        if (mPowerButtonConsecutiveTaps > CAMERA_POWER_TAP_COUNT_THRESHOLD) {
+            setDeferredKeyActionsExecutableAsync(KEYCODE_POWER, event.getDownTime());
+            return true;
+        }
+
+        // UP key; just reuse the original decision.
+        if (event.getAction() == KeyEvent.ACTION_UP) {
+            final Set<Integer> consumedKeys = mConsumedKeysForDevice.get(event.getDeviceId());
+            return consumedKeys != null
+                    && consumedKeys.contains(event.getKeyCode());
+        }
+
+        KeyInterceptionInfo info =
+                mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
+
+        if (info == null || !mButtonOverridePermissionChecker.canWindowOverridePowerKey(mContext,
+                info.windowOwnerUid, info.inputFeaturesFlags)) {
+            // The focused window does not have the permission to override power key behavior.
+            if (DEBUG_INPUT) {
+                String interceptReason = "";
+                if (info == null) {
+                    interceptReason = "Window is null";
+                } else if (!mButtonOverridePermissionChecker.canAppOverrideSystemKey(mContext,
+                        info.windowOwnerUid)) {
+                    interceptReason = "Application does not have "
+                            + "OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW permission";
+                } else {
+                    interceptReason = "Window does not have inputFeatureFlag set";
+                }
+
+                Log.d(TAG, String.format("Intercepting KEYCODE_POWER event. action=%d, "
+                                + "eventTime=%d to window=%s. interceptReason=%s. "
+                                + "mDoubleTapPowerDetected=%b",
+                        event.getAction(), event.getEventTime(), (info != null)
+                                ? info.windowTitle : "null", interceptReason,
+                        mDoubleTapPowerDetected));
+            }
+            // Intercept the key (i.e. do not send to app)
+            setDeferredKeyActionsExecutableAsync(KEYCODE_POWER, event.getDownTime());
+            return true;
+        }
+
+        if (DEBUG_INPUT) {
+            Log.d(TAG, String.format("Sending KEYCODE_POWER to app. action=%d, "
+                            + "eventTime=%d to window=%s. mDoubleTapPowerDetected=%b",
+                    event.getAction(), event.getEventTime(), info.windowTitle,
+                    mDoubleTapPowerDetected));
+        }
+
+        if (!mDoubleTapPowerDetected) {
+            //Single press: post a delayed runnable for the single press power action that will be
+            // called if it's not cancelled by a double press.
+            final var downTime = event.getDownTime();
+            mPowerKeyDelayedRunnable = () ->
+                    setDeferredKeyActionsExecutableAsync(KEYCODE_POWER, downTime);
+            mHandler.postDelayed(mPowerKeyDelayedRunnable, POWER_MULTI_PRESS_TIMEOUT_MILLIS);
+        } else if (mPowerKeyDelayedRunnable != null) {
+            //Double press detected: cancel the single press runnable.
+            mHandler.removeCallbacks(mPowerKeyDelayedRunnable);
+            mPowerKeyDelayedRunnable = null;
+        }
+
+        // Focused window has permission. Send to app.
+        return false;
+    }
+
     @SuppressLint("MissingPermission")
     private void initKeyGestures() {
         if (!useKeyGestureEventHandler()) {
@@ -4633,6 +4792,11 @@
             return true;
         }
 
+        if (overridePowerKeyBehaviorInFocusedWindow() && keyCode == KEYCODE_POWER) {
+            handleUnhandledSystemKey(event);
+            return true;
+        }
+
         if (useKeyGestureEventHandler()) {
             return false;
         }
@@ -5467,8 +5631,13 @@
                         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;
+                if (overridePowerKeyBehaviorInFocusedWindow()) {
+                    result |= ACTION_PASS_TO_USER;
+                } else {
+                    // 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);
@@ -5730,6 +5899,32 @@
         }
 
         if (event.getKeyCode() == KEYCODE_POWER && event.getAction() == KeyEvent.ACTION_DOWN) {
+            if (overridePowerKeyBehaviorInFocusedWindow()) {
+                if (mGestureLauncherService != null) {
+                    mGestureLauncherService.processPowerKeyDown(event);
+                }
+
+                if (detectDoubleTapPower(event)) {
+                    mDoubleTapPowerDetected = true;
+
+                    // Copy of the event for handler in case the original event gets recycled.
+                    KeyEvent eventCopy = KeyEvent.obtain(event);
+                    mDeferredKeyActionExecutor.queueKeyAction(
+                            KeyEvent.KEYCODE_POWER,
+                            eventCopy.getEventTime(),
+                            () -> {
+                                if (!handleCameraGesture(eventCopy, interactive)) {
+                                    mSingleKeyGestureDetector.interceptKey(
+                                            eventCopy, interactive, defaultDisplayOn);
+                                } else {
+                                    mSingleKeyGestureDetector.reset();
+                                }
+                                eventCopy.recycle();
+                            });
+                    return;
+                }
+            }
+
             mPowerKeyHandled = handleCameraGesture(event, interactive);
             if (mPowerKeyHandled) {
                 // handled by camera gesture.
@@ -5741,6 +5936,27 @@
         mSingleKeyGestureDetector.interceptKey(event, interactive, defaultDisplayOn);
     }
 
+    private boolean detectDoubleTapPower(KeyEvent event) {
+        if (event.getKeyCode() != KEYCODE_POWER || event.getAction() != KeyEvent.ACTION_DOWN) {
+            return false;
+        }
+        if (event.isLongPress()) {
+            return false;
+        }
+
+        final long powerTapInterval = event.getEventTime() - mLastPowerDown;
+        mLastPowerDown = event.getEventTime();
+        if (powerTapInterval >= POWER_MULTI_PRESS_TIMEOUT_MILLIS) {
+            // Tap too slow for double press
+            mPowerButtonConsecutiveTaps = 1;
+        } else {
+            mPowerButtonConsecutiveTaps++;
+        }
+
+        return powerTapInterval < POWER_MULTI_PRESS_TIMEOUT_MILLIS
+                && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD;
+    }
+
     // The camera gesture will be detected by GestureLauncherService.
     private boolean handleCameraGesture(KeyEvent event, boolean interactive) {
         // camera gesture.
@@ -7597,6 +7813,12 @@
                     null)
                     == PERMISSION_GRANTED;
         }
+
+        boolean canWindowOverridePowerKey(Context context, int uid, int inputFeaturesFlags) {
+            return canAppOverrideSystemKey(context, uid)
+                    && (inputFeaturesFlags & WindowManager.LayoutParams
+                    .INPUT_FEATURE_RECEIVE_POWER_KEY_DOUBLE_PRESS) != 0;
+        }
     }
 
     private int getTargetDisplayIdForKeyEvent(KeyEvent event) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cebe790..447d443 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5750,9 +5750,10 @@
                 || mKeyInterceptionInfo.layoutParamsPrivateFlags != getAttrs().privateFlags
                 || mKeyInterceptionInfo.layoutParamsType != getAttrs().type
                 || mKeyInterceptionInfo.windowTitle != getWindowTag()
-                || mKeyInterceptionInfo.windowOwnerUid != getOwningUid()) {
+                || mKeyInterceptionInfo.windowOwnerUid != getOwningUid()
+                || mKeyInterceptionInfo.inputFeaturesFlags != getAttrs().inputFeatures) {
             mKeyInterceptionInfo = new KeyInterceptionInfo(getAttrs().type, getAttrs().privateFlags,
-                    getWindowTag().toString(), getOwningUid());
+                    getWindowTag().toString(), getOwningUid(), getAttrs().inputFeatures);
         }
         return mKeyInterceptionInfo;
     }
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 8024915..2349def 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -1409,6 +1409,47 @@
     }
 
     /**
+     * If processPowerKeyDown is called instead of interceptPowerKeyDown (meaning the double tap
+     * gesture isn't performed), the emergency gesture is still launched.
+     */
+    @Test
+    public void
+            testProcessPowerKeyDown_fiveInboundPresses_cameraDoesNotLaunch_emergencyGestureLaunches() {
+        enableCameraGesture();
+        enableEmergencyGesture();
+
+        // First event
+        long eventTime = INITIAL_EVENT_TIME_MILLIS;
+        sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
+
+        //Second event; call processPowerKeyDown without calling interceptPowerKeyDown
+        final long interval = CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        eventTime += interval;
+        KeyEvent keyEvent =
+                new KeyEvent(
+                        IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT);
+        mGestureLauncherService.processPowerKeyDown(keyEvent);
+
+        verify(mMetricsLogger, never())
+                .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+        verify(mUiEventLogger, never()).log(any());
+
+        // Presses 3 and 4 should not trigger any gesture
+        for (int i = 0; i < 2; i++) {
+            eventTime += interval;
+            sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, false);
+        }
+
+        // Fifth button press should still trigger the emergency flow
+        eventTime += interval;
+        sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
+
+        verify(mUiEventLogger, times(1))
+                .log(GestureLauncherService.GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
+        verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected();
+    }
+
+    /**
      * Helper method to trigger emergency gesture by pressing button for 5 times.
      *
      * @return last event time.
@@ -1510,4 +1551,32 @@
                 userSetupCompleteValue,
                 UserHandle.USER_CURRENT);
     }
+
+
+    private void enableEmergencyGesture() {
+        withEmergencyGestureEnabledConfigValue(true);
+        withEmergencyGestureEnabledSettingValue(true);
+        mGestureLauncherService.updateEmergencyGestureEnabled();
+        withUserSetupCompleteValue(true);
+    }
+
+    private void enableCameraGesture() {
+        withCameraDoubleTapPowerEnableConfigValue(true);
+        withCameraDoubleTapPowerDisableSettingValue(0);
+        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        withUserSetupCompleteValue(true);
+    }
+
+    private void sendPowerKeyDownToGestureLauncherServiceAndAssertValues(
+            long eventTime, boolean expectedIntercept, boolean expectedOutLaunchedValue) {
+        KeyEvent keyEvent =
+                new KeyEvent(
+                        IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, IGNORED_REPEAT);
+        boolean interactive = true;
+        MutableBoolean outLaunched = new MutableBoolean(true);
+        boolean intercepted =
+                mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, outLaunched);
+        assertEquals(intercepted, expectedIntercept);
+        assertEquals(outLaunched.value, expectedOutLaunchedValue);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
index 05a1482..be516e9 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java
@@ -18,15 +18,22 @@
 import static android.view.KeyEvent.KEYCODE_POWER;
 import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
 
+import static com.android.hardware.input.Flags.FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW;
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_ASSISTANT;
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_GLOBAL_ACTIONS;
+import static com.android.server.policy.PhoneWindowManager.POWER_MULTI_PRESS_TIMEOUT_MILLIS;
 import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_POWER_DREAM_OR_SLEEP;
 import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_POWER_GO_TO_SLEEP;
 
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.view.Display;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 
 /**
@@ -39,8 +46,12 @@
     @Before
     public void setUp() {
         setUpPhoneWindowManager();
+        mPhoneWindowManager.overrideStatusBarManagerInternal();
     }
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     /**
      * Power single press to turn screen on/off.
      */
@@ -50,6 +61,8 @@
         sendKey(KEYCODE_POWER);
         mPhoneWindowManager.assertPowerSleep();
 
+        mPhoneWindowManager.moveTimeForward(POWER_MULTI_PRESS_TIMEOUT_MILLIS);
+
         // turn screen on when begin from non-interactive.
         mPhoneWindowManager.overrideDisplayState(Display.STATE_OFF);
         sendKey(KEYCODE_POWER);
@@ -90,7 +103,7 @@
         mPhoneWindowManager.overrideCanStartDreaming(false);
         sendKey(KEYCODE_POWER);
         sendKey(KEYCODE_POWER);
-        mPhoneWindowManager.assertCameraLaunch();
+        mPhoneWindowManager.assertDoublePowerLaunch();
         mPhoneWindowManager.assertDidNotLockAfterAppTransitionFinished();
     }
 
@@ -101,7 +114,7 @@
     public void testPowerDoublePress() {
         sendKey(KEYCODE_POWER);
         sendKey(KEYCODE_POWER);
-        mPhoneWindowManager.assertCameraLaunch();
+        mPhoneWindowManager.assertDoublePowerLaunch();
     }
 
     /**
@@ -114,6 +127,8 @@
         sendKey(KEYCODE_POWER, true);
         mPhoneWindowManager.assertSearchManagerLaunchAssist();
 
+        mPhoneWindowManager.moveTimeForward(POWER_MULTI_PRESS_TIMEOUT_MILLIS);
+
         // Show global actions.
         mPhoneWindowManager.overrideLongPressOnPower(LONG_PRESS_POWER_GLOBAL_ACTIONS);
         sendKey(KEYCODE_POWER, true);
@@ -141,4 +156,139 @@
         sendKey(KEYCODE_POWER);
         mPhoneWindowManager.assertNoPowerSleep();
     }
+
+    /**
+     * Double press of power when the window handles the power key events. The
+     * system double power gesture launch should not be performed.
+     */
+    @Test
+    @EnableFlags(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
+    public void testPowerDoublePress_windowHasOverridePermissionAndKeysHandled() {
+        mPhoneWindowManager.overrideCanWindowOverridePowerKey(true);
+        setDispatchedKeyHandler(keyEvent -> true);
+
+        sendKey(KEYCODE_POWER);
+        sendKey(KEYCODE_POWER);
+
+        mPhoneWindowManager.assertDidNotLockAfterAppTransitionFinished();
+
+        mPhoneWindowManager.assertNoDoublePowerLaunch();
+    }
+
+    /**
+     * Double press of power when the window doesn't handle the power key events.
+     * The system default gesture launch should be performed and the app should receive both events.
+     */
+    @Test
+    @EnableFlags(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
+    public void testPowerDoublePress_windowHasOverridePermissionAndKeysUnHandled() {
+        mPhoneWindowManager.overrideCanWindowOverridePowerKey(true);
+        setDispatchedKeyHandler(keyEvent -> false);
+
+        sendKey(KEYCODE_POWER);
+        sendKey(KEYCODE_POWER);
+
+        mPhoneWindowManager.assertDidNotLockAfterAppTransitionFinished();
+        mPhoneWindowManager.assertDoublePowerLaunch();
+        assertEquals(getDownKeysDispatched(), 2);
+        assertEquals(getUpKeysDispatched(), 2);
+    }
+
+    /**
+     * Triple press of power when the window handles the power key double press gesture.
+     * The system default gesture launch should not be performed, and the app only receives the
+     * first two presses.
+     */
+    @Test
+    @EnableFlags(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
+    public void testPowerTriplePress_windowHasOverridePermissionAndKeysHandled() {
+        mPhoneWindowManager.overrideCanWindowOverridePowerKey(true);
+        setDispatchedKeyHandler(keyEvent -> true);
+
+        sendKey(KEYCODE_POWER);
+        sendKey(KEYCODE_POWER);
+        sendKey(KEYCODE_POWER);
+
+        mPhoneWindowManager.assertDidNotLockAfterAppTransitionFinished();
+        mPhoneWindowManager.assertNoDoublePowerLaunch();
+        assertEquals(getDownKeysDispatched(), 2);
+        assertEquals(getUpKeysDispatched(), 2);
+    }
+
+    /**
+     * Tests a single press, followed by a double press when the window can handle the power key.
+     * The app should receive all 3 events.
+     */
+    @Test
+    @EnableFlags(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
+    public void testPowerTriplePressWithDelay_windowHasOverridePermissionAndKeysHandled() {
+        mPhoneWindowManager.overrideCanWindowOverridePowerKey(true);
+        setDispatchedKeyHandler(keyEvent -> true);
+
+        sendKey(KEYCODE_POWER);
+        mPhoneWindowManager.moveTimeForward(POWER_MULTI_PRESS_TIMEOUT_MILLIS);
+        sendKey(KEYCODE_POWER);
+        sendKey(KEYCODE_POWER);
+
+        mPhoneWindowManager.assertNoDoublePowerLaunch();
+        assertEquals(getDownKeysDispatched(), 3);
+        assertEquals(getUpKeysDispatched(), 3);
+    }
+
+    /**
+     * Tests single press when window doesn't handle the power key. Phone should go to sleep.
+     */
+    @Test
+    @EnableFlags(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
+    public void testPowerSinglePress_windowHasOverridePermissionAndKeyUnhandledByApp() {
+        mPhoneWindowManager.overrideCanWindowOverridePowerKey(true);
+        setDispatchedKeyHandler(keyEvent -> false);
+        mPhoneWindowManager.overrideShortPressOnPower(SHORT_PRESS_POWER_GO_TO_SLEEP);
+
+        sendKey(KEYCODE_POWER);
+
+        mPhoneWindowManager.assertPowerSleep();
+    }
+
+    /**
+     * Tests single press when the window handles the power key. Phone should go to sleep after a
+     * delay of {POWER_MULTI_PRESS_TIMEOUT_MILLIS}
+     */
+    @Test
+    @EnableFlags(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
+    public void testPowerSinglePress_windowHasOverridePermissionAndKeyHandledByApp() {
+        mPhoneWindowManager.overrideCanWindowOverridePowerKey(true);
+        setDispatchedKeyHandler(keyEvent -> true);
+        mPhoneWindowManager.overrideDisplayState(Display.STATE_ON);
+        mPhoneWindowManager.overrideShortPressOnPower(SHORT_PRESS_POWER_GO_TO_SLEEP);
+
+        sendKey(KEYCODE_POWER);
+
+        mPhoneWindowManager.moveTimeForward(POWER_MULTI_PRESS_TIMEOUT_MILLIS);
+
+        mPhoneWindowManager.assertPowerSleep();
+    }
+
+
+    /**
+     * Tests 5x press when the window handles the power key. Emergency gesture should still be
+     * launched.
+     */
+    @Test
+    @EnableFlags(FLAG_OVERRIDE_POWER_KEY_BEHAVIOR_IN_FOCUSED_WINDOW)
+    public void testPowerFiveTimesPress_windowHasOverridePermissionAndKeyHandledByApp() {
+        mPhoneWindowManager.overrideCanWindowOverridePowerKey(true);
+        setDispatchedKeyHandler(keyEvent -> true);
+        mPhoneWindowManager.overrideDisplayState(Display.STATE_ON);
+        mPhoneWindowManager.overrideShortPressOnPower(SHORT_PRESS_POWER_GO_TO_SLEEP);
+
+        for (int i = 0; i < 5; ++i) {
+            sendKey(KEYCODE_POWER);
+            mPhoneWindowManager.moveTimeForward(100);
+        }
+
+        mPhoneWindowManager.assertEmergencyLaunch();
+        assertEquals(getDownKeysDispatched(), 2);
+        assertEquals(getUpKeysDispatched(), 2);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 9e47a00..5919501 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -85,7 +85,11 @@
     private Resources mResources;
     private PackageManager mPackageManager;
     TestPhoneWindowManager mPhoneWindowManager;
-    DispatchedKeyHandler mDispatchedKeyHandler = event -> false;
+
+    DispatchedKeyHandler mDispatchedKeyHandler;
+    private int mDownKeysDispatched;
+    private int mUpKeysDispatched;
+
     Context mContext;
 
     /** Modifier key to meta state */
@@ -116,6 +120,9 @@
         XmlResourceParser testBookmarks = mResources.getXml(
                 com.android.frameworks.wmtests.R.xml.bookmarks);
         doReturn(testBookmarks).when(mResources).getXml(com.android.internal.R.xml.bookmarks);
+        mDispatchedKeyHandler = event -> false;
+        mDownKeysDispatched = 0;
+        mUpKeysDispatched = 0;
 
         try {
             // Keep packageName / className in sync with
@@ -278,6 +285,14 @@
         doReturn(expectedBehavior).when(mResources).getInteger(eq(resId));
     }
 
+    int getDownKeysDispatched() {
+        return mDownKeysDispatched;
+    }
+
+    int getUpKeysDispatched() {
+        return mUpKeysDispatched;
+    }
+
     private void interceptKey(KeyEvent keyEvent) {
         int actions = mPhoneWindowManager.interceptKeyBeforeQueueing(keyEvent);
         if ((actions & ACTION_PASS_TO_USER) != 0) {
@@ -285,6 +300,11 @@
                 if (!mDispatchedKeyHandler.onKeyDispatched(keyEvent)) {
                     mPhoneWindowManager.interceptUnhandledKey(keyEvent);
                 }
+                if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+                    ++mDownKeysDispatched;
+                } else {
+                    ++mUpKeysDispatched;
+                }
             }
         }
         mPhoneWindowManager.dispatchAllPendingEvents();
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 9db76d4..18ecfa1 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -22,6 +22,7 @@
 import static android.view.Display.STATE_ON;
 import static android.view.WindowManagerPolicyConstants.FLAG_INTERACTIVE;
 
+import static com.android.hardware.input.Flags.overridePowerKeyBehaviorInFocusedWindow;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
@@ -45,10 +46,14 @@
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
 import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_MUTE;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.CALLS_REAL_METHODS;
 import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atMost;
 import static org.mockito.Mockito.description;
 import static org.mockito.Mockito.mockingDetails;
 import static org.mockito.Mockito.timeout;
@@ -85,7 +90,9 @@
 import android.os.test.TestLooper;
 import android.provider.Settings;
 import android.service.dreams.DreamManagerInternal;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
 import android.telecom.TelecomManager;
+import android.util.MutableBoolean;
 import android.view.Display;
 import android.view.InputEvent;
 import android.view.KeyCharacterMap;
@@ -95,9 +102,12 @@
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
 import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.policy.KeyInterceptionInfo;
 import com.android.server.GestureLauncherService;
 import com.android.server.LocalServices;
+import com.android.server.SystemService;
 import com.android.server.input.InputManagerInternal;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.pm.UserManagerInternal;
@@ -120,6 +130,7 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
 
 import java.util.List;
 import java.util.function.Supplier;
@@ -132,6 +143,8 @@
 
     private PhoneWindowManager mPhoneWindowManager;
     private Context mContext;
+    private GestureLauncherService mGestureLauncherService;
+
 
     @Mock private WindowManagerInternal mWindowManagerInternal;
     @Mock private ActivityManagerInternal mActivityManagerInternal;
@@ -163,7 +176,9 @@
     @Mock private DisplayRotation mDisplayRotation;
     @Mock private DisplayPolicy mDisplayPolicy;
     @Mock private WindowManagerPolicy.ScreenOnListener mScreenOnListener;
-    @Mock private GestureLauncherService mGestureLauncherService;
+    @Mock private QuickAccessWalletClient mQuickAccessWalletClient;
+    @Mock private MetricsLogger mMetricsLogger;
+    @Mock private UiEventLogger mUiEventLogger;
     @Mock private GlobalActions mGlobalActions;
     @Mock private AccessibilityShortcutController mAccessibilityShortcutController;
 
@@ -192,6 +207,8 @@
 
     private int mKeyEventPolicyFlags = FLAG_INTERACTIVE;
 
+    private int mProcessPowerKeyDownCount = 0;
+
     private class TestTalkbackShortcutController extends TalkbackShortcutController {
         TestTalkbackShortcutController(Context context) {
             super(context);
@@ -260,6 +277,8 @@
         MockitoAnnotations.initMocks(this);
         mHandler = new Handler(mTestLooper.getLooper());
         mContext = mockingDetails(context).isSpy() ? context : spy(context);
+        mGestureLauncherService = spy(new GestureLauncherService(mContext, mMetricsLogger,
+                mUiEventLogger));
         setUp(supportSettingsUpdate);
         mTestLooper.dispatchAll();
     }
@@ -272,6 +291,7 @@
         mMockitoSession = mockitoSession()
                 .mockStatic(LocalServices.class, spyStubOnly)
                 .mockStatic(KeyCharacterMap.class)
+                .mockStatic(GestureLauncherService.class)
                 .strictness(Strictness.LENIENT)
                 .startMocking();
 
@@ -294,6 +314,16 @@
                 () -> LocalServices.getService(eq(PowerManagerInternal.class)));
         doReturn(mDisplayManagerInternal).when(
                 () -> LocalServices.getService(eq(DisplayManagerInternal.class)));
+        doReturn(true).when(
+                () -> GestureLauncherService.isCameraDoubleTapPowerSettingEnabled(any(), anyInt())
+        );
+        doReturn(true).when(
+                () -> GestureLauncherService.isEmergencyGestureSettingEnabled(any(), anyInt())
+        );
+        doReturn(true).when(
+                () -> GestureLauncherService.isGestureLauncherEnabled(any())
+        );
+        mGestureLauncherService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
         doReturn(mGestureLauncherService).when(
                 () -> LocalServices.getService(eq(GestureLauncherService.class)));
         doReturn(mUserManagerInternal).when(
@@ -375,7 +405,8 @@
         doNothing().when(mContext).startActivityAsUser(any(), any());
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
 
-        KeyInterceptionInfo interceptionInfo = new KeyInterceptionInfo(0, 0, null, 0);
+        KeyInterceptionInfo interceptionInfo = new KeyInterceptionInfo(0, 0, null, 0,
+                /* inputFeatureFlags = */ 0);
         doReturn(interceptionInfo)
                 .when(mWindowManagerInternal).getKeyInterceptionInfoFromToken(any());
 
@@ -393,6 +424,8 @@
                 eq(TEST_BROWSER_ROLE_PACKAGE_NAME));
         doReturn(mSmsIntent).when(mPackageManager).getLaunchIntentForPackage(
                 eq(TEST_SMS_ROLE_PACKAGE_NAME));
+        mProcessPowerKeyDownCount = 0;
+        captureProcessPowerKeyDownCount();
 
         Mockito.reset(mContext);
     }
@@ -638,6 +671,12 @@
                 .when(mButtonOverridePermissionChecker).canAppOverrideSystemKey(any(), anyInt());
     }
 
+    void overrideCanWindowOverridePowerKey(boolean granted) {
+        doReturn(granted)
+                .when(mButtonOverridePermissionChecker).canWindowOverridePowerKey(any(), anyInt(),
+                        anyInt());
+    }
+
     void overrideKeyEventPolicyFlags(int flags) {
         mKeyEventPolicyFlags = flags;
     }
@@ -713,13 +752,59 @@
         verify(mPowerManager, never()).goToSleep(anyLong(), anyInt(), anyInt());
     }
 
-    void assertCameraLaunch() {
+    void assertDoublePowerLaunch() {
+        ArgumentCaptor<MutableBoolean> valueCaptor = ArgumentCaptor.forClass(MutableBoolean.class);
+
         mTestLooper.dispatchAll();
-        // GestureLauncherService should receive interceptPowerKeyDown twice.
-        verify(mGestureLauncherService, times(2))
-                .interceptPowerKeyDown(any(), anyBoolean(), any());
+        verify(mGestureLauncherService, atLeast(2))
+                .interceptPowerKeyDown(any(), anyBoolean(), valueCaptor.capture());
+        verify(mGestureLauncherService, atMost(4))
+                .interceptPowerKeyDown(any(), anyBoolean(), valueCaptor.capture());
+
+        if (overridePowerKeyBehaviorInFocusedWindow()) {
+            assertTrue(mProcessPowerKeyDownCount >= 2 && mProcessPowerKeyDownCount <= 4);
+        }
+
+        List<Boolean> capturedValues = valueCaptor.getAllValues().stream()
+                .map(mutableBoolean -> mutableBoolean.value)
+                .toList();
+
+        assertTrue(capturedValues.contains(true));
     }
 
+    void assertNoDoublePowerLaunch() {
+        ArgumentCaptor<MutableBoolean> valueCaptor = ArgumentCaptor.forClass(MutableBoolean.class);
+
+        mTestLooper.dispatchAll();
+        verify(mGestureLauncherService, atLeast(0))
+                .interceptPowerKeyDown(any(), anyBoolean(), valueCaptor.capture());
+
+        List<Boolean> capturedValues = valueCaptor.getAllValues().stream()
+                .map(mutableBoolean -> mutableBoolean.value)
+                .toList();
+
+        assertTrue(capturedValues.stream().noneMatch(value -> value));
+    }
+
+    void assertEmergencyLaunch() {
+        ArgumentCaptor<MutableBoolean> valueCaptor = ArgumentCaptor.forClass(MutableBoolean.class);
+
+        mTestLooper.dispatchAll();
+        verify(mGestureLauncherService, atLeast(1))
+                .interceptPowerKeyDown(any(), anyBoolean(), valueCaptor.capture());
+
+        if (overridePowerKeyBehaviorInFocusedWindow()) {
+            assertEquals(mProcessPowerKeyDownCount, 5);
+        }
+
+        List<Boolean> capturedValues = valueCaptor.getAllValues().stream()
+                .map(mutableBoolean -> mutableBoolean.value)
+                .toList();
+
+        assertTrue(capturedValues.getLast());
+    }
+
+
     void assertSearchManagerLaunchAssist() {
         mTestLooper.dispatchAll();
         verify(mSearchManager).launchAssist(any());
@@ -929,4 +1014,12 @@
         verify(mInputManagerInternal)
                 .handleKeyGestureInKeyGestureController(anyInt(), any(), anyInt(), eq(gestureType));
     }
+
+    private void captureProcessPowerKeyDownCount() {
+        doAnswer((Answer<Void>) invocation -> {
+            invocation.callRealMethod();
+            mProcessPowerKeyDownCount++;
+            return null;
+        }).when(mGestureLauncherService).processPowerKeyDown(any());
+    }
 }
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 43844f6..038c6d7 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -541,7 +541,8 @@
                 0
             },
             "title",
-            /* uid = */0
+            /* uid = */0,
+            /* inputFeatureFlags = */ 0
         )
         whenever(windowManagerInternal.getKeyInterceptionInfoFromToken(any())).thenReturn(info)
     }