Loading core/java/android/view/WindowManagerPolicy.java +11 −9 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +180 −130 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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( Loading @@ -583,6 +612,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { break; } } }; private final Runnable mScreenshotChordLongPress = new Runnable() { public void run() { takeScreenshot(); } }; Loading Loading @@ -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(); Loading @@ -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 Loading Loading @@ -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 Loading @@ -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; } } } Loading @@ -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; Loading @@ -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(); Loading @@ -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) { Loading @@ -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 Loading Loading @@ -1531,11 +1586,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { + "+" + KeyEvent.keyCodeToString(keyCode)); } } return true; return -1; } } return false; return 0; } /** {@inheritDoc} */ Loading Loading @@ -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."); } Loading Loading @@ -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; Loading @@ -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); } } } }; Loading @@ -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 Loading Loading @@ -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) { Loading Loading @@ -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(); Loading @@ -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; } Loading services/input/InputDispatcher.cpp +24 −5 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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(); } Loading Loading @@ -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() { Loading @@ -4168,6 +4186,7 @@ void InputDispatcher::KeyEntry::recycle() { dispatchInProgress = false; syntheticRepeat = false; interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; interceptKeyWakeupTime = 0; } Loading services/input/InputDispatcher.h +3 −1 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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, Loading services/input/tests/InputDispatcher_test.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -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 Loading
core/java/android/view/WindowManagerPolicy.java +11 −9 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading
policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +180 −130 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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( Loading @@ -583,6 +612,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { break; } } }; private final Runnable mScreenshotChordLongPress = new Runnable() { public void run() { takeScreenshot(); } }; Loading Loading @@ -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(); Loading @@ -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 Loading Loading @@ -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 Loading @@ -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; } } } Loading @@ -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; Loading @@ -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(); Loading @@ -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) { Loading @@ -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 Loading Loading @@ -1531,11 +1586,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { + "+" + KeyEvent.keyCodeToString(keyCode)); } } return true; return -1; } } return false; return 0; } /** {@inheritDoc} */ Loading Loading @@ -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."); } Loading Loading @@ -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; Loading @@ -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); } } } }; Loading @@ -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 Loading Loading @@ -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) { Loading Loading @@ -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(); Loading @@ -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; } Loading
services/input/InputDispatcher.cpp +24 −5 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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(); } Loading Loading @@ -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() { Loading @@ -4168,6 +4186,7 @@ void InputDispatcher::KeyEntry::recycle() { dispatchInProgress = false; syntheticRepeat = false; interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; interceptKeyWakeupTime = 0; } Loading
services/input/InputDispatcher.h +3 −1 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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, Loading
services/input/tests/InputDispatcher_test.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -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