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

Commit 45f2a965 authored by Vaibhav Devmurari's avatar Vaibhav Devmurari
Browse files

Shift interceptUnhandledKey Shortcuts to IMS

Test: atest WmTests
Test: atest InputTests
Bug: 358569822
Flag: com.android.hardware.input.use_key_gesture_event_handler
Change-Id: Iaa1296fbd2f87984256c3afbda1e4e9a08cdaf20
parent e3861335
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -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);
        }
+74 −8
Original line number Diff line number Diff line
@@ -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;
@@ -2574,12 +2646,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) {
@@ -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();

+55 −0
Original line number Diff line number Diff line
@@ -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,
+27 −85
Original line number Diff line number Diff line
@@ -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,74 +4417,25 @@ 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;
                    + ", repeatCount=" + event.getRepeatCount());
        }

        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;
                }
        final int repeatCount = event.getRepeatCount();
        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
        final int metaState = event.getModifiers();

                if (initialDown) {
                    mFallbackActions.put(keyCode, fallbackAction);
                } else if (event.getAction() == KeyEvent.ACTION_UP) {
                    mFallbackActions.remove(keyCode);
                    fallbackAction.recycle();
                }
            }
        // TODO(b/358569822): Shift to KeyGestureEvent based handling
        if (keyCode == KeyEvent.KEYCODE_STEM_PRIMARY) {
            handleUnhandledSystemKey(event);
            sendSystemKeyToStatusBarAsync(event);
            return true;
        }

        if (DEBUG_INPUT) {
            if (fallbackEvent == null) {
                Slog.d(TAG, "No fallback.");
            } else {
                Slog.d(TAG, "Performing fallback: " + fallbackEvent);
            }
        }
        return fallbackEvent;
        if (useKeyGestureEventHandler()) {
            return false;
        }

    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) {
            case KeyEvent.KEYCODE_SPACE:
                if (down && repeatCount == 0) {
@@ -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;
+2 −4
Original line number Diff line number Diff line
@@ -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.
Loading