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

Commit a76a87a8 authored by Winson Chung's avatar Winson Chung Committed by Android (Google) Code Review
Browse files

Merge "DO NOT MERGE. Improve screenshot chord debouncing. Bug: 5011907" into ics-mr0

parents f838b7c2 d5bb82d1
Loading
Loading
Loading
Loading
+11 −9
Original line number Diff line number Diff line
@@ -104,23 +104,23 @@ public interface WindowManagerPolicy {
     */
    public final static String EXTRA_HDMI_PLUGGED_STATE = "state";

    // flags for interceptKeyTq
    /**
     * Pass this event to the user / app.  To be returned from {@link #interceptKeyTq}.
     * Pass this event to the user / app.  To be returned from
     * {@link #interceptKeyBeforeQueueing}.
     */
    public final static int ACTION_PASS_TO_USER = 0x00000001;

    /**
     * This key event should extend the user activity timeout and turn the lights on.
     * To be returned from {@link #interceptKeyTq}. Do not return this and
     * {@link #ACTION_GO_TO_SLEEP} or {@link #ACTION_PASS_TO_USER}.
     * To be returned from {@link #interceptKeyBeforeQueueing}.
     * Do not return this and {@link #ACTION_GO_TO_SLEEP} or {@link #ACTION_PASS_TO_USER}.
     */
    public final static int ACTION_POKE_USER_ACTIVITY = 0x00000002;

    /**
     * This key event should put the device to sleep (and engage keyguard if necessary)
     * To be returned from {@link #interceptKeyTq}.  Do not return this and
     * {@link #ACTION_POKE_USER_ACTIVITY} or {@link #ACTION_PASS_TO_USER}.
     * To be returned from {@link #interceptKeyBeforeQueueing}.
     * Do not return this and {@link #ACTION_POKE_USER_ACTIVITY} or {@link #ACTION_PASS_TO_USER}.
     */
    public final static int ACTION_GO_TO_SLEEP = 0x00000004;

@@ -677,10 +677,12 @@ public interface WindowManagerPolicy {
     *            event will normally go.
     * @param event The key event.
     * @param policyFlags The policy flags associated with the key.
     * @return Returns true if the policy consumed the event and it should
     * not be further dispatched.
     * @return 0 if the key should be dispatched immediately, -1 if the key should
     * not be dispatched ever, or a positive value indicating the number of
     * milliseconds by which the key dispatch should be delayed before trying
     * again.
     */
    public boolean interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags);
    public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags);

    /**
     * Called from the input dispatcher thread when an application did not handle
+180 −130
Original line number Diff line number Diff line
@@ -267,7 +267,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    WindowState mKeyguard = null;
    KeyguardViewMediator mKeyguardMediator;
    GlobalActions mGlobalActions;
    volatile boolean mPowerKeyHandled;
    volatile boolean mPowerKeyHandled; // accessed from input reader and handler thread
    boolean mPendingPowerKeyUpCanceled;
    RecentApplicationsDialog mRecentAppsDialog;
    Handler mHandler;

@@ -403,8 +404,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    private int mLongPressOnHomeBehavior = -1;

    // Screenshot trigger states
    private boolean mVolumeDownTriggered;
    private boolean mPowerDownTriggered;
    // Time to volume and power must be pressed within this interval of each other.
    private static final long SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS = 150;
    private boolean mVolumeDownKeyTriggered;
    private long mVolumeDownKeyTime;
    private boolean mVolumeDownKeyConsumedByScreenshotChord;
    private boolean mVolumeUpKeyTriggered;
    private boolean mPowerKeyTriggered;
    private long mPowerKeyTime;

    ShortcutManager mShortcutManager;
    PowerManager.WakeLock mBroadcastWakeLock;
@@ -552,15 +559,37 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        if (!mPowerKeyHandled) {
            mHandler.removeCallbacks(mPowerLongPress);
            return !canceled;
        } else {
            mPowerKeyHandled = true;
        }
        return false;
    }

    private void cancelPendingPowerKeyAction() {
        if (!mPowerKeyHandled) {
            mHandler.removeCallbacks(mPowerLongPress);
        }
        mPendingPowerKeyUpCanceled = true;
    }

    private void interceptScreenshotChord() {
        if (mVolumeDownKeyTriggered && mPowerKeyTriggered && !mVolumeUpKeyTriggered) {
            final long now = SystemClock.uptimeMillis();
            if (now <= mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
                    && now <= mPowerKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
                mVolumeDownKeyConsumedByScreenshotChord = true;
                cancelPendingPowerKeyAction();

                mHandler.postDelayed(mScreenshotChordLongPress,
                        ViewConfiguration.getGlobalActionKeyTimeout());
            }
        }
    }

    private void cancelPendingScreenshotChordAction() {
        mHandler.removeCallbacks(mScreenshotChordLongPress);
    }

    private final Runnable mPowerLongPress = new Runnable() {
        public void run() {
            if (!mPowerKeyHandled) {
            // The context isn't read
            if (mLongPressOnPowerBehavior < 0) {
                mLongPressOnPowerBehavior = mContext.getResources().getInteger(
@@ -583,6 +612,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                break;
            }
        }
    };

    private final Runnable mScreenshotChordLongPress = new Runnable() {
        public void run() {
            takeScreenshot();
        }
    };

@@ -1381,11 +1415,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {

    /** {@inheritDoc} */
    @Override
    public boolean interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
    public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
        final boolean keyguardOn = keyguardOn();
        final int keyCode = event.getKeyCode();
        final int repeatCount = event.getRepeatCount();
        final int metaState = event.getMetaState();
        final int flags = event.getFlags();
        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
        final boolean canceled = event.isCanceled();

@@ -1394,6 +1429,26 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    + repeatCount + " keyguardOn=" + keyguardOn + " mHomePressed=" + mHomePressed);
        }

        // If we think we might have a volume down & power key chord on the way
        // but we're not sure, then tell the dispatcher to wait a little while and
        // try again later before dispatching.
        if ((flags & KeyEvent.FLAG_FALLBACK) == 0) {
            if (mVolumeDownKeyTriggered && !mPowerKeyTriggered) {
                final long now = SystemClock.uptimeMillis();
                final long timeoutTime = mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS;
                if (now < timeoutTime) {
                    return timeoutTime - now;
                }
            }
            if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
                    && mVolumeDownKeyConsumedByScreenshotChord) {
                if (!down) {
                    mVolumeDownKeyConsumedByScreenshotChord = false;
                }
                return -1;
            }
        }

        // First we always handle the home key here, so applications
        // can never break it, although if keyguard is on, we do let
        // it handle it, because that gives us the correct 5 second
@@ -1425,7 +1480,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                } else {
                    Log.i(TAG, "Ignoring HOME; event canceled.");
                }
                return true;
                return -1;
            }

            // If a system window has focus, then it doesn't make sense
@@ -1436,13 +1491,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                if (type == WindowManager.LayoutParams.TYPE_KEYGUARD
                        || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
                    // the "app" is keyguard, so give it the key
                    return false;
                    return 0;
                }
                final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;
                for (int i=0; i<typeCount; i++) {
                    if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {
                        // don't do anything, but also don't pass it to the app
                        return true;
                        return -1;
                    }
                }
            }
@@ -1456,7 +1511,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    }
                }
            }
            return true;
            return -1;
        } else if (keyCode == KeyEvent.KEYCODE_MENU) {
            // Hijack modified menu keys for debugging features
            final int chordBug = KeyEvent.META_SHIFT_ON;
@@ -1465,7 +1520,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) {
                    Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
                    mContext.sendOrderedBroadcast(intent, null);
                    return true;
                    return -1;
                } else if (SHOW_PROCESSES_ON_ALT_MENU &&
                        (metaState & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) {
                    Intent service = new Intent();
@@ -1480,7 +1535,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    }
                    Settings.System.putInt(
                            res, Settings.System.SHOW_PROCESSES, shown ? 0 : 1);
                    return true;
                    return -1;
                }
            }
        } else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
@@ -1493,15 +1548,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                mShortcutKeyPressed = -1;
                if (mConsumeShortcutKeyUp) {
                    mConsumeShortcutKeyUp = false;
                    return true;
                    return -1;
                }
            }
            return false;
            return 0;
        } else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {
            if (down && repeatCount == 0) {
                showOrHideRecentAppsDialog(0, true /*dismissIfShown*/);
            }
            return true;
            return -1;
        }

        // Shortcuts are invoked through Search+key, so intercept those here
@@ -1531,11 +1586,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                                + "+" + KeyEvent.keyCodeToString(keyCode));
                    }
                }
                return true;
                return -1;
            }
        }

        return false;
        return 0;
    }

    /** {@inheritDoc} */
@@ -1606,7 +1661,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                        flags, event.getSource(), null);
                int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags, true);
                if ((actions & ACTION_PASS_TO_USER) != 0) {
                    if (!interceptKeyBeforeDispatching(win, fallbackEvent, policyFlags)) {
                    long delayMillis = interceptKeyBeforeDispatching(
                            win, fallbackEvent, policyFlags);
                    if (delayMillis == 0) {
                        if (DEBUG_FALLBACK) {
                            Slog.d(TAG, "Performing fallback.");
                        }
@@ -2472,23 +2529,20 @@ public class PhoneWindowManager implements WindowManagerPolicy {

    final Object mScreenshotLock = new Object();
    ServiceConnection mScreenshotConnection = null;
    Runnable mScreenshotTimeout = null;

    void finishScreenshotLSS(ServiceConnection conn) {
        if (mScreenshotConnection == conn) {
            mContext.unbindService(conn);
    final Runnable mScreenshotTimeout = new Runnable() {
        @Override public void run() {
            synchronized (mScreenshotLock) {
                if (mScreenshotConnection != null) {
                    mContext.unbindService(mScreenshotConnection);
                    mScreenshotConnection = null;
            if (mScreenshotTimeout != null) {
                mHandler.removeCallbacks(mScreenshotTimeout);
                mScreenshotTimeout = null;
                }
            }
        }
    };

    // Assume this is called from the Handler thread.
    private void takeScreenshot() {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
        synchronized (mScreenshotLock) {
            if (mScreenshotConnection != null) {
                return;
@@ -2511,7 +2565,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                            @Override
                            public void handleMessage(Message msg) {
                                synchronized (mScreenshotLock) {
                                            finishScreenshotLSS(myConn);
                                    if (mScreenshotConnection == myConn) {
                                        mContext.unbindService(mScreenshotConnection);
                                        mScreenshotConnection = null;
                                        mHandler.removeCallbacks(mScreenshotTimeout);
                                    }
                                }
                            }
                        };
@@ -2527,22 +2585,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            };
            if (mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)) {
                mScreenshotConnection = conn;
                        mScreenshotTimeout = new Runnable() {
                            @Override public void run() {
                                synchronized (mScreenshotLock) {
                                    if (mScreenshotConnection != null) {
                                        finishScreenshotLSS(mScreenshotConnection);
                                    }
                                }
                            }
    
                        };
                mHandler.postDelayed(mScreenshotTimeout, 10000);
            }
        }
    }
        });
    }

    /** {@inheritDoc} */
    @Override
@@ -2609,28 +2655,35 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        // Handle special keys.
        switch (keyCode) {
            case KeyEvent.KEYCODE_VOLUME_DOWN:
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_VOLUME_MUTE: {
                if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
                    if (down) {
                    if (isScreenOn) {
                        // If the power key down was already triggered, take the screenshot
                        if (mPowerDownTriggered) {
                            // Dismiss the power-key longpress
                            mHandler.removeCallbacks(mPowerLongPress);
                            mPowerKeyHandled = true;

                            // Take the screenshot
                            takeScreenshot();

                            // Prevent the event from being passed through to the current activity
                            result &= ~ACTION_PASS_TO_USER;
                            break;
                        if (isScreenOn && !mVolumeDownKeyTriggered
                                && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                            mVolumeDownKeyTriggered = true;
                            mVolumeDownKeyTime = event.getDownTime();
                            mVolumeDownKeyConsumedByScreenshotChord = false;
                            cancelPendingPowerKeyAction();
                            interceptScreenshotChord();
                        }
                    } else {
                        mVolumeDownKeyTriggered = false;
                        cancelPendingScreenshotChordAction();
                    }
                        mVolumeDownTriggered = true;
                } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
                    if (down) {
                        if (isScreenOn && !mVolumeUpKeyTriggered
                                && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                            mVolumeUpKeyTriggered = true;
                            cancelPendingPowerKeyAction();
                            cancelPendingScreenshotChordAction();
                        }
                    } else {
                    mVolumeDownTriggered = false;
                        mVolumeUpKeyTriggered = false;
                        cancelPendingScreenshotChordAction();
                    }
                }
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_VOLUME_MUTE: {
                if (down) {
                    ITelephony telephonyService = getTelephonyService();
                    if (telephonyService != null) {
@@ -2709,17 +2762,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            case KeyEvent.KEYCODE_POWER: {
                result &= ~ACTION_PASS_TO_USER;
                if (down) {
                    if (isScreenOn) {
                        // If the volume down key has been triggered, then just take the screenshot
                        if (mVolumeDownTriggered) {
                            // Take the screenshot
                            takeScreenshot();
                            mPowerKeyHandled = true;

                            // Prevent the event from being passed through to the current activity
                            break;
                        }
                        mPowerDownTriggered = true;
                    if (isScreenOn && !mPowerKeyTriggered
                            && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                        mPowerKeyTriggered = true;
                        mPowerKeyTime = event.getDownTime();
                        interceptScreenshotChord();
                    }

                    ITelephony telephonyService = getTelephonyService();
@@ -2741,12 +2788,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                            Log.w(TAG, "ITelephony threw RemoteException", ex);
                        }
                    }
                    interceptPowerKeyDown(!isScreenOn || hungUp);
                    interceptPowerKeyDown(!isScreenOn || hungUp
                            || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);
                } else {
                    mPowerDownTriggered = false;
                    if (interceptPowerKeyUp(canceled)) {
                    mPowerKeyTriggered = false;
                    cancelPendingScreenshotChordAction();
                    if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) {
                        result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP;
                    }
                    mPendingPowerKeyUpCanceled = false;
                }
                break;
            }
+24 −5
Original line number Diff line number Diff line
@@ -804,6 +804,18 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        logOutboundKeyDetailsLocked("dispatchKey - ", entry);
    }

    // Handle case where the policy asked us to try again later last time.
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER) {
        if (currentTime < entry->interceptKeyWakeupTime) {
            if (entry->interceptKeyWakeupTime < *nextWakeupTime) {
                *nextWakeupTime = entry->interceptKeyWakeupTime;
            }
            return false; // wait until next wakeup
        }
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
        entry->interceptKeyWakeupTime = 0;
    }

    // Give the policy a chance to intercept the key.
    if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
        if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
@@ -3827,14 +3839,19 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(

    mLock.unlock();

    bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
    nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle,
            &event, entry->policyFlags);

    mLock.lock();

    entry->interceptKeyResult = consumed
            ? KeyEntry::INTERCEPT_KEY_RESULT_SKIP
            : KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
    if (delay < 0) {
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
    } else if (!delay) {
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
    } else {
        entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
        entry->interceptKeyWakeupTime = now() + delay;
    }
    entry->release();
}

@@ -4156,7 +4173,8 @@ InputDispatcher::KeyEntry::KeyEntry(nsecs_t eventTime,
        deviceId(deviceId), source(source), action(action), flags(flags),
        keyCode(keyCode), scanCode(scanCode), metaState(metaState),
        repeatCount(repeatCount), downTime(downTime),
        syntheticRepeat(false), interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
        syntheticRepeat(false), interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN),
        interceptKeyWakeupTime(0) {
}

InputDispatcher::KeyEntry::~KeyEntry() {
@@ -4168,6 +4186,7 @@ void InputDispatcher::KeyEntry::recycle() {
    dispatchInProgress = false;
    syntheticRepeat = false;
    interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN;
    interceptKeyWakeupTime = 0;
}


+3 −1
Original line number Diff line number Diff line
@@ -242,7 +242,7 @@ public:
    virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) = 0;

    /* Allows the policy a chance to intercept a key before dispatching. */
    virtual bool interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
    virtual nsecs_t interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
            const KeyEvent* keyEvent, uint32_t policyFlags) = 0;

    /* Allows the policy a chance to perform default processing for an unhandled key.
@@ -481,8 +481,10 @@ private:
            INTERCEPT_KEY_RESULT_UNKNOWN,
            INTERCEPT_KEY_RESULT_SKIP,
            INTERCEPT_KEY_RESULT_CONTINUE,
            INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER,
        };
        InterceptKeyResult interceptKeyResult; // set based on the interception result
        nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER

        KeyEntry(nsecs_t eventTime,
                int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action,
+2 −2
Original line number Diff line number Diff line
@@ -75,9 +75,9 @@ private:
    virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
    }

    virtual bool interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
    virtual nsecs_t interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle,
            const KeyEvent* keyEvent, uint32_t policyFlags) {
        return false;
        return 0;
    }

    virtual bool dispatchUnhandledKey(const sp<InputWindowHandle>& inputWindowHandle,
Loading