diff options
author | 2025-02-21 18:27:14 +0000 | |
---|---|---|
committer | 2025-03-04 18:35:07 +0000 | |
commit | b57366c5021e018d8eeae62853128cf5fdb4f2dc (patch) | |
tree | c1de0406f9f71373a3513af690b27ab3ce8c97bb /services | |
parent | 9daca6f94ed29b4547c63206cbe2f334e400d250 (diff) |
(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
Diffstat (limited to 'services')
6 files changed, 107 insertions, 90 deletions
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index c2fecf283a34..7f00c96ecb8c 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -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); } /** diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java index ef5babf19d83..e9409e490f4d 100644 --- a/services/core/java/com/android/server/input/KeyGestureController.java +++ b/services/core/java/com/android/server/input/KeyGestureController.java @@ -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); + } + } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 46dc75817a36..85027033ca38 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -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(); diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index 7751ac3f9fc6..a4bc5cbcb5d3 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -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) { 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 3ca019728c2b..fcdf88f16550 100644 --- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java @@ -605,29 +605,6 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase { } @Test - public void testKeyGestureAccessibilityShortcutChord() { - Assert.assertTrue( - sendKeyGestureEventStart( - KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD)); - mPhoneWindowManager.moveTimeForward(5000); - Assert.assertTrue( - sendKeyGestureEventCancel( - KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD)); - mPhoneWindowManager.assertAccessibilityKeychordCalled(); - } - - @Test - public void testKeyGestureAccessibilityShortcutChordCancelled() { - Assert.assertTrue( - sendKeyGestureEventStart( - KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD)); - Assert.assertTrue( - sendKeyGestureEventCancel( - KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD)); - mPhoneWindowManager.assertAccessibilityKeychordNotCalled(); - } - - @Test public void testKeyGestureRingerToggleChord() { mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_MUTE); Assert.assertTrue( @@ -670,29 +647,6 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase { } @Test - public void testKeyGestureAccessibilityTvShortcutChord() { - Assert.assertTrue( - sendKeyGestureEventStart( - KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD)); - mPhoneWindowManager.moveTimeForward(5000); - Assert.assertTrue( - sendKeyGestureEventCancel( - KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD)); - mPhoneWindowManager.assertAccessibilityKeychordCalled(); - } - - @Test - public void testKeyGestureAccessibilityTvShortcutChordCancelled() { - Assert.assertTrue( - sendKeyGestureEventStart( - KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD)); - Assert.assertTrue( - sendKeyGestureEventCancel( - KeyGestureEvent.KEY_GESTURE_TYPE_TV_ACCESSIBILITY_SHORTCUT_CHORD)); - mPhoneWindowManager.assertAccessibilityKeychordNotCalled(); - } - - @Test public void testKeyGestureTvTriggerBugReport() { Assert.assertTrue( sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT)); 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 f88492477487..e56fd3c6272d 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -750,11 +750,6 @@ class TestPhoneWindowManager { verify(mAccessibilityShortcutController).performAccessibilityShortcut(); } - void assertAccessibilityKeychordNotCalled() { - mTestLooper.dispatchAll(); - verify(mAccessibilityShortcutController, never()).performAccessibilityShortcut(); - } - void assertCloseAllDialogs() { verify(mContext).closeSystemDialogs(); } |