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

Commit b57366c5 authored by Vaibhav Devmurari's avatar Vaibhav Devmurari
Browse files

(2/n) Remove usage of isKeyGestureSupported() from KeyGestureController

isKeyGestureSupported is synchronous API that makes system server
dependent on handler and can cause ANR if handlers misbehave.
It was added as a stop gap for multi-key gestures that require
some SysUI info like keyguard state, display state, etc, to decide
if the key needs to be consumed or not at the start of the gesture.

With this refactor:
- WM policy added an API to check isKeyguardLocked() and we will
use that for checking keyguard status
- Move A11yShortcutController to KGC to circumvent the need of the
isShortcutAvailable() API
- Since the only difference with TV and non TV event handling was
whether keyguard showing blocks the shortcut or not, we no longer
need to have separate handler logic for TV (we can cover it in
precondition check).

In subsequent CLs:
- Will remove usages of isKeyGestureSupported() API everywhere

Test: atest KeygestureControllerTests
Bug: 358569822
Bug: 383602794
Flag: EXEMPT refactor
Change-Id: Ib5d5c727fabb28623efa6247e817cf0c643a949c
parent 9daca6f9
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -108,7 +108,8 @@ public final class KeyGestureEvent {
    public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD = 55;
    public static final int KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD = 56;
    public static final int KEY_GESTURE_TYPE_GLOBAL_ACTIONS = 57;
    public static final int KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD = 58;
    @Deprecated
    public static final int DEPRECATED_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;
@@ -200,7 +201,6 @@ public final class KeyGestureEvent {
            KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
            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_ACCESSIBILITY_SHORTCUT,
            KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
@@ -777,8 +777,6 @@ public final class KeyGestureEvent {
                return "KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD";
            case KEY_GESTURE_TYPE_GLOBAL_ACTIONS:
                return "KEY_GESTURE_TYPE_GLOBAL_ACTIONS";
            case KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD:
                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:
+6 −0
Original line number Diff line number Diff line
@@ -568,6 +568,7 @@ public class InputManagerService extends IInputManager.Stub
        }
        mWindowManagerCallbacks = callbacks;
        registerLidSwitchCallbackInternal(mWindowManagerCallbacks);
        mKeyGestureController.setWindowManagerCallbacks(callbacks);
    }

    public void setWiredAccessoryCallbacks(WiredAccessoryCallbacks callbacks) {
@@ -3371,6 +3372,11 @@ public class InputManagerService extends IInputManager.Stub
         */
        @Nullable
        SurfaceControl createSurfaceForGestureMonitor(String name, int displayId);

        /**
         * Provide information on whether the keyguard is currently locked or not.
         */
        boolean isKeyguardLocked(int displayId);
    }

    /**
+94 −18
Original line number Diff line number Diff line
@@ -62,8 +62,10 @@ import android.view.Display;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.ViewConfiguration;

import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.IShortcutService;
@@ -104,6 +106,7 @@ final class KeyGestureController {
    private static final int MSG_NOTIFY_KEY_GESTURE_EVENT = 1;
    private static final int MSG_PERSIST_CUSTOM_GESTURES = 2;
    private static final int MSG_LOAD_CUSTOM_GESTURES = 3;
    private static final int MSG_ACCESSIBILITY_SHORTCUT = 4;

    // must match: config_settingsKeyBehavior in config.xml
    private static final int SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY = 0;
@@ -122,12 +125,15 @@ final class KeyGestureController {
    static final int POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS = 2;

    private final Context mContext;
    private InputManagerService.WindowManagerCallbacks mWindowManagerCallbacks;
    private final Handler mHandler;
    private final Handler mIoHandler;
    private final int mSystemPid;
    private final KeyCombinationManager mKeyCombinationManager;
    private final SettingsObserver mSettingsObserver;
    private final AppLaunchShortcutManager mAppLaunchShortcutManager;
    @VisibleForTesting
    final AccessibilityShortcutController mAccessibilityShortcutController;
    private final InputGestureManager mInputGestureManager;
    private final DisplayManager mDisplayManager;
    @GuardedBy("mInputDataStore")
@@ -175,8 +181,14 @@ final class KeyGestureController {

    private final boolean mVisibleBackgroundUsersEnabled = isVisibleBackgroundUsersEnabled();

    KeyGestureController(Context context, Looper looper, Looper ioLooper,
    public KeyGestureController(Context context, Looper looper, Looper ioLooper,
            InputDataStore inputDataStore) {
        this(context, looper, ioLooper, inputDataStore, new Injector());
    }

    @VisibleForTesting
    KeyGestureController(Context context, Looper looper, Looper ioLooper,
            InputDataStore inputDataStore, Injector injector) {
        mContext = context;
        mHandler = new Handler(looper, this::handleMessage);
        mIoHandler = new Handler(ioLooper, this::handleIoMessage);
@@ -197,6 +209,8 @@ final class KeyGestureController {
        mSettingsObserver = new SettingsObserver(mHandler);
        mAppLaunchShortcutManager = new AppLaunchShortcutManager(mContext);
        mInputGestureManager = new InputGestureManager(mContext);
        mAccessibilityShortcutController = injector.getAccessibilityShortcutController(mContext,
                mHandler);
        mDisplayManager = Objects.requireNonNull(mContext.getSystemService(DisplayManager.class));
        mInputDataStore = inputDataStore;
        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -295,8 +309,8 @@ final class KeyGestureController {
                        KeyEvent.KEYCODE_VOLUME_UP) {
                    @Override
                    public boolean preCondition() {
                        return isKeyGestureSupported(
                                KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD);
                        return mAccessibilityShortcutController.isAccessibilityShortcutAvailable(
                                mWindowManagerCallbacks.isKeyguardLocked(DEFAULT_DISPLAY));
                    }

                    @Override
@@ -376,15 +390,15 @@ final class KeyGestureController {
                            KeyEvent.KEYCODE_DPAD_DOWN) {
                        @Override
                        public boolean preCondition() {
                            return isKeyGestureSupported(
                                    KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD);
                            return mAccessibilityShortcutController
                                    .isAccessibilityShortcutAvailable(false);
                        }

                        @Override
                        public void execute() {
                            handleMultiKeyGesture(
                                    new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN},
                                    KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD,
                                    KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
                                    KeyGestureEvent.ACTION_GESTURE_START, 0);
                        }

@@ -392,7 +406,7 @@ final class KeyGestureController {
                        public void cancel() {
                            handleMultiKeyGesture(
                                    new int[]{KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN},
                                    KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD,
                                    KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
                                    KeyGestureEvent.ACTION_GESTURE_COMPLETE,
                                    KeyGestureEvent.FLAG_CANCELLED);
                        }
@@ -438,6 +452,7 @@ final class KeyGestureController {
        mSettingsObserver.observe();
        mAppLaunchShortcutManager.systemRunning();
        mInputGestureManager.systemRunning();
        initKeyGestures();

        int userId;
        synchronized (mUserLock) {
@@ -447,6 +462,41 @@ final class KeyGestureController {
        mIoHandler.obtainMessage(MSG_LOAD_CUSTOM_GESTURES, userId).sendToTarget();
    }

    @SuppressLint("MissingPermission")
    private void initKeyGestures() {
        InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
        im.registerKeyGestureEventHandler(new InputManager.KeyGestureEventHandler() {
            @Override
            public boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event,
                    @Nullable IBinder focusedToken) {
                switch (event.getKeyGestureType()) {
                    case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD:
                        if (event.getAction() == KeyGestureEvent.ACTION_GESTURE_START) {
                            mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
                            mHandler.sendMessageDelayed(
                                    mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT),
                                    getAccessibilityShortcutTimeout());
                        } else {
                            mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
                        }
                        return true;
                    default:
                        return false;
                }
            }

            @Override
            public boolean isKeyGestureSupported(int gestureType) {
                switch (gestureType) {
                    case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD:
                        return true;
                    default:
                        return false;
                }
            }
        });
    }

    public boolean interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        if (mVisibleBackgroundUsersEnabled && shouldIgnoreKeyEventForVisibleBackgroundUser(event)) {
            return false;
@@ -971,17 +1021,6 @@ final class KeyGestureController {
        return false;
    }

    private boolean isKeyGestureSupported(@KeyGestureEvent.KeyGestureType int gestureType) {
        synchronized (mKeyGestureHandlerRecords) {
            for (KeyGestureHandlerRecord handler : mKeyGestureHandlerRecords.values()) {
                if (handler.isKeyGestureSupported(gestureType)) {
                    return true;
                }
            }
        }
        return false;
    }

    public void notifyKeyGestureCompleted(int deviceId, int[] keycodes, int modifierState,
            @KeyGestureEvent.KeyGestureType int gestureType) {
        // TODO(b/358569822): Once we move the gesture detection logic to IMS, we ideally
@@ -1019,9 +1058,16 @@ final class KeyGestureController {
        synchronized (mUserLock) {
            mCurrentUserId = userId;
        }
        mAccessibilityShortcutController.setCurrentUser(userId);
        mIoHandler.obtainMessage(MSG_LOAD_CUSTOM_GESTURES, userId).sendToTarget();
    }


    public void setWindowManagerCallbacks(
            @NonNull InputManagerService.WindowManagerCallbacks callbacks) {
        mWindowManagerCallbacks = callbacks;
    }

    private boolean isDefaultDisplayOn() {
        Display defaultDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
        if (defaultDisplay == null) {
@@ -1068,6 +1114,9 @@ final class KeyGestureController {
                AidlKeyGestureEvent event = (AidlKeyGestureEvent) msg.obj;
                notifyKeyGestureEvent(event);
                break;
            case MSG_ACCESSIBILITY_SHORTCUT:
                mAccessibilityShortcutController.performAccessibilityShortcut();
                break;
        }
        return true;
    }
@@ -1413,6 +1462,25 @@ final class KeyGestureController {
        return event;
    }

    private long getAccessibilityShortcutTimeout() {
        synchronized (mUserLock) {
            final ViewConfiguration config = ViewConfiguration.get(mContext);
            final boolean hasDialogShown = Settings.Secure.getIntForUser(
                    mContext.getContentResolver(),
                    Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, mCurrentUserId) != 0;
            final boolean skipTimeoutRestriction =
                    Settings.Secure.getIntForUser(mContext.getContentResolver(),
                            Settings.Secure.SKIP_ACCESSIBILITY_SHORTCUT_DIALOG_TIMEOUT_RESTRICTION,
                            0, mCurrentUserId) != 0;

            // If users manually set the volume key shortcut for any accessibility service, the
            // system would bypass the timeout restriction of the shortcut dialog.
            return hasDialogShown || skipTimeoutRestriction
                    ? config.getAccessibilityShortcutKeyTimeoutAfterConfirmation()
                    : config.getAccessibilityShortcutKeyTimeout();
        }
    }

    public void dump(IndentingPrintWriter ipw) {
        ipw.println("KeyGestureController:");
        ipw.increaseIndent();
@@ -1459,4 +1527,12 @@ final class KeyGestureController {
        mAppLaunchShortcutManager.dump(ipw);
        mInputGestureManager.dump(ipw);
    }

    @VisibleForTesting
    static class Injector {
        AccessibilityShortcutController getAccessibilityShortcutController(Context context,
                Handler handler) {
            return new AccessibilityShortcutController(context, handler, UserHandle.USER_SYSTEM);
        }
    }
}
+0 −21
Original line number Diff line number Diff line
@@ -4290,12 +4290,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK:
                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS:
                        return true;
                    case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD:
                        return mAccessibilityShortcutController.isAccessibilityShortcutAvailable(
                                isKeyguardLocked());
                    case KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD:
                        return mAccessibilityShortcutController.isAccessibilityShortcutAvailable(
                                false);
                    default:
                        return false;
                }
@@ -4457,13 +4451,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    cancelPendingScreenshotChordAction();
                }
                return true;
            case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD:
                if (start) {
                    interceptAccessibilityShortcutChord();
                } else {
                    cancelPendingAccessibilityShortcutAction();
                }
                return true;
            case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD:
                if (start) {
                    interceptRingerToggleChord();
@@ -4481,14 +4468,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                    cancelGlobalActionsAction();
                }
                return true;
                // TODO (b/358569822): Consolidate TV and non-TV gestures into same KeyGestureEvent
            case KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD:
                if (start) {
                    interceptAccessibilityGestureTv();
                } else {
                    cancelAccessibilityGestureTv();
                }
                return true;
            case KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT:
                if (start) {
                    interceptBugreportGestureTv();
+7 −0
Original line number Diff line number Diff line
@@ -343,6 +343,13 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
        }
    }

    @Override
    public boolean isKeyguardLocked(int displayId) {
        synchronized (mService.mGlobalLock) {
            return mService.mAtmService.mKeyguardController.isKeyguardLocked(displayId);
        }
    }

    /** Waits until the built-in input devices have been configured. */
    public boolean waitForInputDevicesReady(long timeoutMillis) {
        synchronized (mInputDevicesReadyMonitor) {
Loading