Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 29f27e8d authored by arthurhung's avatar arthurhung
Browse files

Introduce SingleKeyGestureDetector to PhoneWindowManager

The SingleKeyGestureDetector is used to detect the single key gesture
such as short press, long press or multi presses.

If the key has been handled by other policy, it should call 'cancel'
to reset all gesture states.

This patch refactor the power key and back key (long press behavior
enabled), it can detect below gestures:
- single press
- long press
- very long press
- multiple press ( <= 3)

Bug: 169058831
Bug: 127687575
Test: atest KeyEventTest SingleKeyGestureTests
Test: manual (short press power, long press power, double tap power)
Change-Id: I1c042e693e9844e30ce568eded7681fa4101daf9
parent f6ed5fc5
Loading
Loading
Loading
Loading
+10 −6
Original line number Diff line number Diff line
@@ -102,9 +102,11 @@ public class KeyCombinationManager {
    }

    /**
     * Check if the key event could be triggered by combine key rule before dispatching to a window.
     * 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.
     */
    void interceptKey(KeyEvent event, boolean interactive) {
    boolean interceptKey(KeyEvent event, boolean interactive) {
        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
        final int keyCode = event.getKeyCode();
        final int count = mActiveRules.size();
@@ -117,9 +119,9 @@ public class KeyCombinationManager {
                    // exceed time from first key down.
                    forAllRules(mActiveRules, (rule)-> rule.cancel());
                    mActiveRules.clear();
                    return;
                    return false;
                } else if (count == 0) { // has some key down but no active rule exist.
                    return;
                    return false;
                }
            }

@@ -127,7 +129,7 @@ public class KeyCombinationManager {
                mDownTimes.put(keyCode, eventTime);
            } else {
                // ignore old key, maybe a repeat key.
                return;
                return false;
            }

            if (mDownTimes.size() == 1) {
@@ -141,7 +143,7 @@ public class KeyCombinationManager {
            } else {
                // Ignore if rule already triggered.
                if (mTriggeredRule != null) {
                    return;
                    return true;
                }

                // check if second key can trigger rule, or remove the non-match rule.
@@ -156,6 +158,7 @@ public class KeyCombinationManager {
                mActiveRules.clear();
                if (mTriggeredRule != null) {
                    mActiveRules.add(mTriggeredRule);
                    return true;
                }
            }
        } else {
@@ -168,6 +171,7 @@ public class KeyCombinationManager {
                }
            }
        }
        return false;
    }

    /**
+163 −186
Original line number Diff line number Diff line
@@ -73,6 +73,8 @@ import static android.view.WindowManagerGlobal.ADD_OKAY;
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;
@@ -456,7 +458,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    volatile boolean mPowerKeyHandled;
    volatile boolean mBackKeyHandled;
    volatile boolean mBeganFromNonInteractive;
    volatile int mPowerKeyPressCounter;
    volatile boolean mEndCallKeyHandled;
    volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
    volatile boolean mGoingToSleep;
@@ -497,7 +498,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    boolean mHasSoftInput = false;
    boolean mHapticTextHandleEnabled;
    boolean mUseTvRouting;
    int mVeryLongPressTimeout;
    boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
    MetricsLogger mLogger;
    boolean mWakeOnDpadKeyPress;
@@ -597,14 +597,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    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;
@@ -615,10 +614,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    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;
@@ -626,8 +622,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    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_POWER_VERY_LONG_PRESS = 25;
    private static final int MSG_RINGER_TOGGLE_CHORD = 26;
    private static final int MSG_RINGER_TOGGLE_CHORD = 24;

    private class PolicyHandler extends Handler {
        @Override
@@ -668,22 +663,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                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;
@@ -794,13 +776,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }
    };

    private Runnable mPossibleVeryLongPressReboot = new Runnable() {
        @Override
        public void run() {
            mActivityManagerInternal.prepareForPossibleShutdown();
        }
    };

    private void handleRingerChordGesture() {
        if (mRingerToggleChord == VOLUME_HUSH_OFF) {
            return;
@@ -840,28 +815,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }
    }

    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 interceptBackKeyUp(KeyEvent event) {
        mLogger.count("key_back_up", 1);
    private boolean backKeyPress() {
        mLogger.count("key_back_press", 1);
        // Cache handled state
        boolean handled = mBackKeyHandled;

        // Reset back long press state
        cancelPendingBackKeyAction();

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

@@ -883,10 +843,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            }
        }

        if (mAutofillManagerInternal != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
        if (mAutofillManagerInternal != null) {
            mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_BACK_KEY_TO_AUTOFILL));
        }

        return handled;
    }

@@ -896,11 +855,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            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.
@@ -922,71 +876,20 @@ public class PhoneWindowManager implements WindowManagerPolicy {

        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 = hungUp || gesturedServiceIntercepted
        mPowerKeyHandled = mPowerKeyHandled || hungUp
                || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
        if (!mPowerKeyHandled) {
            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 {
            if (!interactive) {
                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 {
@@ -994,68 +897,38 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    }
                }
            }
        } 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);
            }

            // 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);
        } else {
            // handled by single key or another power key policy.
            mSingleKeyGestureDetector.reset();
            finishPowerKeyPress();
        }

        // Done.  Reset our state.
        finishPowerKeyPress();
    }

    private void finishPowerKeyPress() {
        mBeganFromNonInteractive = false;
        mPowerKeyPressCounter = 0;
        mPowerKeyHandled = false;
        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 "
@@ -1206,6 +1079,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {

    private void powerLongPress(long eventTime) {
        final int behavior = getResolvedLongPressOnPowerBehavior();

        switch (behavior) {
            case LONG_PRESS_POWER_NOTHING:
                break;
@@ -1844,8 +1718,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                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);

@@ -1939,6 +1811,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    }
                });
        initKeyCombinationRules();
        initSingleKeyGestureRules();
    }

    private void initKeyCombinationRules() {
@@ -1951,7 +1824,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) {
                        @Override
                        void execute() {
                            cancelPendingPowerKeyAction();
                            mPowerKeyHandled = true;
                            interceptScreenshotChord();
                        }
                        @Override
@@ -1986,7 +1859,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    }
                    @Override
                    void execute() {
                        cancelPendingPowerKeyAction();
                        mPowerKeyHandled = true;
                        interceptRingerToggleChord();
                    }
                    @Override
@@ -2000,7 +1873,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    new TwoKeysCombinationRule(KEYCODE_BACK, KEYCODE_DPAD_DOWN) {
                        @Override
                        void execute() {
                            cancelPendingBackKeyAction();
                            mBackKeyHandled = true;
                            interceptAccessibilityGestureTv();
                        }

@@ -2014,7 +1887,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    new TwoKeysCombinationRule(KEYCODE_DPAD_CENTER, KEYCODE_BACK) {
                        @Override
                        void execute() {
                            cancelPendingBackKeyAction();
                            mBackKeyHandled = true;
                            interceptBugreportGestureTv();
                        }

@@ -2026,6 +1899,84 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }
    }

    /**
     * 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.
@@ -3550,8 +3501,21 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            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) {
            mKeyCombinationManager.interceptKey(event, interactive);
            handleKeyGesture(event, interactive);
        }

        // Enable haptics if down and virtual key without multiple repetitions. If this is a hard
@@ -3566,12 +3530,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        switch (keyCode) {
            case KeyEvent.KEYCODE_BACK: {
                if (down) {
                    interceptBackKeyDown();
                    mBackKeyHandled = false;
                } else {
                    boolean handled = interceptBackKeyUp(event);

                    if (!hasLongPressOnBackBehavior()) {
                        mBackKeyHandled |= backKeyPress();
                    }
                    // Don't pass back press to app if we've already handled it via long press
                    if (handled) {
                    if (mBackKeyHandled) {
                        result &= ~ACTION_PASS_TO_USER;
                    }
                }
@@ -3683,33 +3648,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            case KeyEvent.KEYCODE_TV_POWER: {
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                HdmiControlManager hdmiControlManager = getHdmiControlManager();
                if (hdmiControlManager != null && hdmiControlManager.shouldHandleTvPowerKey()) {
                    if (down) {
                if (down && hdmiControlManager != null) {
                    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, mPowerKeyPressCounter);
                        mPowerKeyHandled ? 1 : 0,
                        mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));
                // Any activity on the power button stops the accessibility shortcut
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
@@ -3890,6 +3839,43 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        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
@@ -4889,15 +4875,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }
    }

    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) {