diff options
12 files changed, 274 insertions, 134 deletions
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java index ef0bb98b5a26..5ee61bcd436a 100644 --- a/core/java/android/hardware/input/KeyGestureEvent.java +++ b/core/java/android/hardware/input/KeyGestureEvent.java @@ -97,6 +97,8 @@ public final class KeyGestureEvent { public static final int KEY_GESTURE_TYPE_GLOBAL_ACTIONS = 57; public static final int KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD = 58; public static final int KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT = 59; + public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT = 60; + public static final int KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS = 61; public static final int FLAG_CANCELLED = 1; @@ -170,7 +172,10 @@ public final class KeyGestureEvent { KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD, KEY_GESTURE_TYPE_GLOBAL_ACTIONS, KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD, - KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT + KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT, + KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT, + KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS, + }) @Retention(RetentionPolicy.SOURCE) public @interface KeyGestureType { @@ -583,6 +588,10 @@ public final class KeyGestureEvent { return "KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD"; case KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT: return "KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT"; + case KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT: + return "KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT"; + case KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS: + return "KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS"; default: return Integer.toHexString(value); } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 631663ac7dea..909c47bc9359 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -25,6 +25,7 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M import static com.android.hardware.input.Flags.touchpadVisualizer; import static com.android.hardware.input.Flags.useKeyGestureEventHandler; +import static com.android.server.policy.WindowManagerPolicy.ACTION_PASS_TO_USER; import android.Manifest; import android.annotation.EnforcePermission; @@ -326,6 +327,9 @@ public class InputManagerService extends IInputManager.Stub // Manages Sticky modifier state private final StickyModifierStateController mStickyModifierStateController; private final KeyGestureController mKeyGestureController; + /** Fallback actions by key code */ + private final SparseArray<KeyCharacterMap.FallbackAction> mFallbackActions = + new SparseArray<>(); // Manages Keyboard microphone mute led private final KeyboardLedController mKeyboardLedController; @@ -2521,6 +2525,74 @@ public class InputManagerService extends IInputManager.Stub null, null, null) == PERMISSION_GRANTED; } + // Native callback. + @SuppressWarnings("unused") + private KeyEvent dispatchUnhandledKey(IBinder focus, KeyEvent event, int policyFlags) { + if (interceptUnhandledKey(event, focus)) { + return null; + } + // TODO(b/358569822): Move fallback logic to KeyGestureController + if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) != 0) { + return null; + } + final KeyCharacterMap kcm = event.getKeyCharacterMap(); + final int keyCode = event.getKeyCode(); + final int metaState = event.getMetaState(); + final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN + && event.getRepeatCount() == 0; + + // Check for fallback actions specified by the key character map. + final KeyCharacterMap.FallbackAction fallbackAction; + if (initialDown) { + fallbackAction = kcm.getFallbackAction(keyCode, metaState); + } else { + fallbackAction = mFallbackActions.get(keyCode); + } + + if (fallbackAction == null) { + return null; + } + KeyEvent fallbackEvent = null; + final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK; + fallbackEvent = KeyEvent.obtain( + event.getDownTime(), event.getEventTime(), + event.getAction(), fallbackAction.keyCode, + event.getRepeatCount(), fallbackAction.metaState, + event.getDeviceId(), event.getScanCode(), + flags, event.getSource(), event.getDisplayId(), null); + + if (!interceptFallback(focus, fallbackEvent, policyFlags)) { + fallbackEvent.recycle(); + fallbackEvent = null; + } + + if (initialDown) { + mFallbackActions.put(keyCode, fallbackAction); + } else if (event.getAction() == KeyEvent.ACTION_UP) { + mFallbackActions.remove(keyCode); + fallbackAction.recycle(); + } + return fallbackEvent; + } + + private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent, + int policyFlags) { + int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags); + if ((actions & ACTION_PASS_TO_USER) == 0) { + return false; + } + long delayMillis = interceptKeyBeforeDispatching(focusedToken, fallbackEvent, policyFlags); + return delayMillis == 0 && !interceptUnhandledKey(fallbackEvent, focusedToken); + } + + private boolean interceptUnhandledKey(KeyEvent event, IBinder focus) { + if (useKeyGestureEventHandler() && mKeyGestureController.interceptUnhandledKey(event, + focus)) { + return true; + } + return mWindowManagerCallbacks.interceptUnhandledKey(event, focus); + } + private void initKeyGestures() { if (!useKeyGestureEventHandler()) { return; @@ -2576,12 +2648,6 @@ public class InputManagerService extends IInputManager.Stub // Native callback. @SuppressWarnings("unused") - private KeyEvent dispatchUnhandledKey(IBinder focus, KeyEvent event, int policyFlags) { - return mWindowManagerCallbacks.dispatchUnhandledKey(focus, event, policyFlags); - } - - // Native callback. - @SuppressWarnings("unused") private void onPointerDownOutsideFocus(IBinder touchedToken) { mWindowManagerCallbacks.onPointerDownOutsideFocus(touchedToken); } @@ -2996,9 +3062,9 @@ public class InputManagerService extends IInputManager.Stub long interceptKeyBeforeDispatching(IBinder token, KeyEvent event, int policyFlags); /** - * Dispatch unhandled key + * Intercept unhandled key */ - KeyEvent dispatchUnhandledKey(IBinder token, KeyEvent event, int policyFlags); + boolean interceptUnhandledKey(KeyEvent event, IBinder token); int getPointerLayer(); diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java index 5def0857bc17..b488db533d12 100644 --- a/services/core/java/com/android/server/input/KeyGestureController.java +++ b/services/core/java/com/android/server/input/KeyGestureController.java @@ -872,6 +872,61 @@ final class KeyGestureController { return false; } + boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken) { + final int keyCode = event.getKeyCode(); + final int repeatCount = event.getRepeatCount(); + final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; + final int metaState = event.getModifiers(); + final int deviceId = event.getDeviceId(); + final int displayId = event.getDisplayId(); + + switch(keyCode) { + case KeyEvent.KEYCODE_SPACE: + if (down && repeatCount == 0) { + // Handle keyboard layout switching. (CTRL + SPACE) + if (KeyEvent.metaStateHasModifiers(metaState & ~KeyEvent.META_SHIFT_MASK, + KeyEvent.META_CTRL_ON)) { + return handleKeyGesture(deviceId, new int[]{keyCode}, + KeyEvent.META_CTRL_ON | (event.isShiftPressed() + ? KeyEvent.META_SHIFT_ON : 0), + KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH, + KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, + focusedToken, /* flags = */0); + } + } + break; + case KeyEvent.KEYCODE_Z: + if (down && KeyEvent.metaStateHasModifiers(metaState, + KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON)) { + // Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users. + return handleKeyGesture(deviceId, new int[]{keyCode}, + KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT, + KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, + focusedToken, /* flags = */0); + } + break; + case KeyEvent.KEYCODE_SYSRQ: + if (down && repeatCount == 0) { + return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0, + KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT, + KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, + focusedToken, /* flags = */0); + } + break; + case KeyEvent.KEYCODE_ESCAPE: + if (down && KeyEvent.metaStateHasNoModifiers(metaState) && repeatCount == 0) { + return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0, + KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS, + KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, + focusedToken, /* flags = */0); + } + break; + } + + return false; + } + private boolean handleKeyGesture(int[] keycodes, @KeyGestureEvent.KeyGestureType int gestureType, int action, int flags) { return handleKeyGesture(KeyCharacterMap.VIRTUAL_KEYBOARD, keycodes, /* modifierState= */0, diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 29d00ca2b2e0..228405074dbb 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -199,7 +199,6 @@ import android.view.HapticFeedbackConstants; import android.view.IDisplayFoldListener; import android.view.InputDevice; import android.view.KeyCharacterMap; -import android.view.KeyCharacterMap.FallbackAction; import android.view.KeyEvent; import android.view.KeyboardShortcutGroup; import android.view.MotionEvent; @@ -699,10 +698,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Maps global key codes to the components that will handle them. private GlobalKeyManager mGlobalKeyManager; - // Fallback actions by key code. - private final SparseArray<KeyCharacterMap.FallbackAction> mFallbackActions = - new SparseArray<KeyCharacterMap.FallbackAction>(); - private final com.android.internal.policy.LogDecelerateInterpolator mLogDecelerateInterpolator = new LogDecelerateInterpolator(100, 0); private final DeferredKeyActionExecutor mDeferredKeyActionExecutor = @@ -3993,6 +3988,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS: case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH: case KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH: + case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT: + case KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS: return true; case KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD: case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD: @@ -4211,6 +4208,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { cancelBugreportGestureTv(); } return true; + case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT: + if (complete && mAccessibilityShortcutController.isAccessibilityShortcutAvailable( + isKeyguardLocked())) { + mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT)); + } + return true; + case KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS: + if (complete) { + mContext.closeSystemDialogs(); + } + return true; } return false; } @@ -4396,7 +4404,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // TODO(b/117479243): handle it in InputPolicy /** {@inheritDoc} */ @Override - public KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags) { + public boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken) { // Note: This method is only called if the initial down was unhandled. if (DEBUG_INPUT) { final KeyInterceptionInfo info = @@ -4409,75 +4417,26 @@ public class PhoneWindowManager implements WindowManagerPolicy { + ", keyCode=" + event.getKeyCode() + ", scanCode=" + event.getScanCode() + ", metaState=" + event.getMetaState() - + ", repeatCount=" + event.getRepeatCount() - + ", policyFlags=" + policyFlags); - } - - if (interceptUnhandledKey(event, focusedToken)) { - return null; - } - - KeyEvent fallbackEvent = null; - if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { - final KeyCharacterMap kcm = event.getKeyCharacterMap(); - final int keyCode = event.getKeyCode(); - final int metaState = event.getMetaState(); - final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN - && event.getRepeatCount() == 0; - - // Check for fallback actions specified by the key character map. - final FallbackAction fallbackAction; - if (initialDown) { - fallbackAction = kcm.getFallbackAction(keyCode, metaState); - } else { - fallbackAction = mFallbackActions.get(keyCode); - } - - if (fallbackAction != null) { - if (DEBUG_INPUT) { - Slog.d(TAG, "Fallback: keyCode=" + fallbackAction.keyCode - + " metaState=" + Integer.toHexString(fallbackAction.metaState)); - } - - final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK; - fallbackEvent = KeyEvent.obtain( - event.getDownTime(), event.getEventTime(), - event.getAction(), fallbackAction.keyCode, - event.getRepeatCount(), fallbackAction.metaState, - event.getDeviceId(), event.getScanCode(), - flags, event.getSource(), event.getDisplayId(), null); - - if (!interceptFallback(focusedToken, fallbackEvent, policyFlags)) { - fallbackEvent.recycle(); - fallbackEvent = null; - } - - if (initialDown) { - mFallbackActions.put(keyCode, fallbackAction); - } else if (event.getAction() == KeyEvent.ACTION_UP) { - mFallbackActions.remove(keyCode); - fallbackAction.recycle(); - } - } + + ", repeatCount=" + event.getRepeatCount()); } - if (DEBUG_INPUT) { - if (fallbackEvent == null) { - Slog.d(TAG, "No fallback."); - } else { - Slog.d(TAG, "Performing fallback: " + fallbackEvent); - } - } - return fallbackEvent; - } - - private boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken) { final int keyCode = event.getKeyCode(); final int repeatCount = event.getRepeatCount(); final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; final int metaState = event.getModifiers(); - switch(keyCode) { + // TODO(b/358569822): Shift to KeyGestureEvent based handling + if (keyCode == KeyEvent.KEYCODE_STEM_PRIMARY) { + handleUnhandledSystemKey(event); + sendSystemKeyToStatusBarAsync(event); + return true; + } + + if (useKeyGestureEventHandler()) { + return false; + } + + switch (keyCode) { case KeyEvent.KEYCODE_SPACE: if (down && repeatCount == 0) { // Handle keyboard layout switching. (CTRL + SPACE) @@ -4514,10 +4473,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { return true; } break; - case KeyEvent.KEYCODE_STEM_PRIMARY: - handleUnhandledSystemKey(event); - sendSystemKeyToStatusBarAsync(event); - return true; } return false; @@ -4556,19 +4511,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { targetWindowToken); } - private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent, - int policyFlags) { - int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags); - if ((actions & ACTION_PASS_TO_USER) != 0) { - long delayMillis = interceptKeyBeforeDispatching( - focusedToken, fallbackEvent, policyFlags); - if (delayMillis == 0 && !interceptUnhandledKey(fallbackEvent, focusedToken)) { - return true; - } - } - return false; - } - @Override public void setTopFocusedDisplay(int displayId) { mTopFocusedDisplayId = displayId; diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 892af6bec534..ad116578ae41 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -754,11 +754,9 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * @param focusedToken Client window token that currently has focus. This is where the key * event will normally go. * @param event The key event. - * @param policyFlags The policy flags associated with the key. - * @return Returns an alternate key event to redispatch as a fallback, or null to give up. - * The caller is responsible for recycling the key event. + * @return true if the unhandled key is intercepted by the policy. */ - KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags); + boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken); /** * Called when the top focused display is changed. diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index 232c3b62bcef..dcf031953610 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -188,9 +188,8 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal * the application did not handle. */ @Override - public KeyEvent dispatchUnhandledKey( - IBinder focusedToken, KeyEvent event, int policyFlags) { - return mService.mPolicy.dispatchUnhandledKey(focusedToken, event, policyFlags); + public boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken) { + return mService.mPolicy.interceptUnhandledKey(event, focusedToken); } /** Callback to get pointer layer. */ diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java index fbdc78b78810..cdb45423c11a 100644 --- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java @@ -684,4 +684,19 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase { sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT)); mPhoneWindowManager.assertBugReportNotTakenForTv(); } + + @Test + public void testKeyGestureAccessibilityShortcut() { + Assert.assertTrue( + sendKeyGestureEventComplete( + KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT)); + mPhoneWindowManager.assertAccessibilityKeychordCalled(); + } + + @Test + public void testKeyGestureCloseAllDialogs() { + Assert.assertTrue( + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS)); + mPhoneWindowManager.assertCloseAllDialogs(); + } } diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java index b54e74fb5188..c186a0355588 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java @@ -130,6 +130,7 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { * CTRL + SPACE to switch keyboard layout. */ @Test + @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) public void testCtrlSpace() { sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SPACE}, /* duration= */ 0, ANY_DISPLAY_ID); @@ -140,6 +141,7 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { * CTRL + SHIFT + SPACE to switch keyboard layout backwards. */ @Test + @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) public void testCtrlShiftSpace() { sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_SPACE}, /* duration= */ 0, ANY_DISPLAY_ID); @@ -150,6 +152,7 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { * CTRL + ALT + Z to enable accessibility service. */ @Test + @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) public void testCtrlAltZ() { sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_ALT_LEFT, KEYCODE_Z}, 0); mPhoneWindowManager.assertAccessibilityKeychordCalled(); 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 2aed30a5108c..9e47a008592c 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java +++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java @@ -283,7 +283,7 @@ class ShortcutKeyTestBase { if ((actions & ACTION_PASS_TO_USER) != 0) { if (0 == mPhoneWindowManager.interceptKeyBeforeDispatching(keyEvent)) { if (!mDispatchedKeyHandler.onKeyDispatched(keyEvent)) { - mPhoneWindowManager.dispatchUnhandledKey(keyEvent); + mPhoneWindowManager.interceptUnhandledKey(keyEvent); } } } 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 0d8c05903d2c..1aa908792c0e 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -418,8 +418,8 @@ class TestPhoneWindowManager { mKeyEventPolicyFlags); } - void dispatchUnhandledKey(KeyEvent event) { - mPhoneWindowManager.dispatchUnhandledKey(mInputToken, event, FLAG_INTERACTIVE); + void interceptUnhandledKey(KeyEvent event) { + mPhoneWindowManager.interceptUnhandledKey(event, mInputToken); } boolean sendKeyGestureEvent(KeyGestureEvent event) { @@ -683,6 +683,10 @@ class TestPhoneWindowManager { verify(mAccessibilityShortcutController, never()).performAccessibilityShortcut(); } + void assertCloseAllDialogs() { + verify(mContext).closeSystemDialogs(); + } + void assertDreamRequest() { mTestLooper.dispatchAll(); verify(mDreamManagerInternal).requestDream(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index eebb487d16cd..9e9874b32893 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -103,8 +103,8 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags) { - return null; + public boolean interceptUnhandledKey(KeyEvent event, IBinder focusedToken) { + return false; } @Override diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt index f4bebb566cba..4ae06a4f9812 100644 --- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt @@ -152,9 +152,11 @@ class KeyGestureControllerTests { } private fun notifyHomeGestureCompleted(keyGestureController: KeyGestureController) { - keyGestureController.notifyKeyGestureCompleted(DEVICE_ID, intArrayOf(KeyEvent.KEYCODE_H), + keyGestureController.notifyKeyGestureCompleted( + DEVICE_ID, intArrayOf(KeyEvent.KEYCODE_H), KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON, - KeyGestureEvent.KEY_GESTURE_TYPE_HOME) + KeyGestureEvent.KEY_GESTURE_TYPE_HOME + ) } @Test @@ -574,6 +576,54 @@ class KeyGestureControllerTests { KeyGestureEvent.ACTION_GESTURE_COMPLETE ) ), + TestData( + "CTRL + SPACE -> Switch Language Forward", + intArrayOf(KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_SPACE), + KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH, + intArrayOf(KeyEvent.KEYCODE_SPACE), + KeyEvent.META_CTRL_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "CTRL + SHIFT + SPACE -> Switch Language Backward", + intArrayOf( + KeyEvent.KEYCODE_CTRL_LEFT, + KeyEvent.KEYCODE_SHIFT_LEFT, + KeyEvent.KEYCODE_SPACE + ), + KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH, + intArrayOf(KeyEvent.KEYCODE_SPACE), + KeyEvent.META_CTRL_ON or KeyEvent.META_SHIFT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "CTRL + ALT + Z -> Accessibility Shortcut", + intArrayOf( + KeyEvent.KEYCODE_CTRL_LEFT, + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_Z + ), + KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT, + intArrayOf(KeyEvent.KEYCODE_Z), + KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "SYSRQ -> Take screenshot", + intArrayOf(KeyEvent.KEYCODE_SYSRQ), + KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT, + intArrayOf(KeyEvent.KEYCODE_SYSRQ), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "ESC -> Close All Dialogs", + intArrayOf(KeyEvent.KEYCODE_ESCAPE), + KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS, + intArrayOf(KeyEvent.KEYCODE_ESCAPE), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), ) } @@ -614,7 +664,7 @@ class KeyGestureControllerTests { keyGestureController.registerKeyGestureHandler(handler, 0) for (key in testKeys) { - sendKeys(keyGestureController, intArrayOf(key), assertAllConsumed = true) + sendKeys(keyGestureController, intArrayOf(key), assertNotSentToApps = true) } } @@ -895,7 +945,7 @@ class KeyGestureControllerTests { private fun sendKeys( keyGestureController: KeyGestureController, testKeys: IntArray, - assertAllConsumed: Boolean = false + assertNotSentToApps: Boolean = false ) { var metaState = 0 val now = SystemClock.uptimeMillis() @@ -905,18 +955,7 @@ class KeyGestureControllerTests { DEVICE_ID, 0 /*scancode*/, 0 /*flags*/, InputDevice.SOURCE_KEYBOARD ) - - keyGestureController.interceptKeyBeforeQueueing(downEvent, FLAG_INTERACTIVE) - testLooper.dispatchAll() - - val consumed = - keyGestureController.interceptKeyBeforeDispatching(null, downEvent, 0) == -1L - if (assertAllConsumed) { - assertTrue( - "interceptKeyBeforeDispatching should consume all events $downEvent", - consumed - ) - } + interceptKey(keyGestureController, downEvent, assertNotSentToApps) metaState = metaState or MODIFIER.getOrDefault(key, 0) downEvent.recycle() @@ -929,18 +968,7 @@ class KeyGestureControllerTests { DEVICE_ID, 0 /*scancode*/, 0 /*flags*/, InputDevice.SOURCE_KEYBOARD ) - - keyGestureController.interceptKeyBeforeQueueing(upEvent, FLAG_INTERACTIVE) - testLooper.dispatchAll() - - val consumed = - keyGestureController.interceptKeyBeforeDispatching(null, upEvent, 0) == -1L - if (assertAllConsumed) { - assertTrue( - "interceptKeyBeforeDispatching should consume all events $upEvent", - consumed - ) - } + interceptKey(keyGestureController, upEvent, assertNotSentToApps) metaState = metaState and MODIFIER.getOrDefault(key, 0).inv() upEvent.recycle() @@ -948,6 +976,27 @@ class KeyGestureControllerTests { } } + private fun interceptKey( + keyGestureController: KeyGestureController, + event: KeyEvent, + assertNotSentToApps: Boolean + ) { + keyGestureController.interceptKeyBeforeQueueing(event, FLAG_INTERACTIVE) + testLooper.dispatchAll() + + val consumed = + keyGestureController.interceptKeyBeforeDispatching(null, event, 0) == -1L + if (assertNotSentToApps) { + assertTrue( + "interceptKeyBeforeDispatching should consume all events $event", + consumed + ) + } + if (!consumed) { + keyGestureController.interceptUnhandledKey(event, null) + } + } + inner class KeyGestureEventListener : IKeyGestureEventListener.Stub() { override fun onKeyGestureEvent(event: AidlKeyGestureEvent) { events.add(KeyGestureEvent(event)) |