Shift Key gestures handled by InputManager to IMS from PWM
Test: atest InputTests
Bug: 358569822
Flag: com.android.hardware.input.use_key_gesture_event_handler
Change-Id: Ia104f506a22dacf6886bc86c9876c66bfd9751c6
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index edc35e5..65adaba 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -456,6 +456,14 @@
void registerLocalService(InputManagerInternal localService) {
LocalServices.addService(InputManagerInternal.class, localService);
}
+
+ KeyboardBacklightControllerInterface getKeyboardBacklightController(
+ NativeInputManagerService nativeService, PersistentDataStore dataStore) {
+ return InputFeatureFlagProvider.isKeyboardBacklightControlEnabled()
+ ? new KeyboardBacklightController(mContext, nativeService, dataStore,
+ mLooper, mUEventManager)
+ : new KeyboardBacklightControllerInterface() {};
+ }
}
public InputManagerService(Context context) {
@@ -479,10 +487,7 @@
injector.getLooper(), this) : null;
mBatteryController = new BatteryController(mContext, mNative, injector.getLooper(),
injector.getUEventManager());
- mKeyboardBacklightController = InputFeatureFlagProvider.isKeyboardBacklightControlEnabled()
- ? new KeyboardBacklightController(mContext, mNative, mDataStore,
- injector.getLooper(), injector.getUEventManager())
- : new KeyboardBacklightControllerInterface() {};
+ mKeyboardBacklightController = injector.getKeyboardBacklightController(mNative, mDataStore);
mStickyModifierStateController = new StickyModifierStateController();
mKeyGestureController = new KeyGestureController(mContext, injector.getLooper());
mKeyboardLedController = new KeyboardLedController(mContext, injector.getLooper(),
@@ -606,6 +611,8 @@
mKeyRemapper.systemRunning();
mPointerIconCache.systemRunning();
mKeyboardGlyphManager.systemRunning();
+
+ initKeyGestures();
}
private void reloadDeviceAliases() {
@@ -2504,6 +2511,59 @@
null, null, null) == PERMISSION_GRANTED;
}
+ private void initKeyGestures() {
+ if (!useKeyGestureEventHandler()) {
+ return;
+ }
+ InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
+ im.registerKeyGestureEventHandler(new InputManager.KeyGestureEventHandler() {
+ @Override
+ public boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event,
+ @Nullable IBinder focussedToken) {
+ int deviceId = event.getDeviceId();
+ boolean complete = event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ && !event.isCancelled();
+ switch (event.getKeyGestureType()) {
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
+ if (complete) {
+ mKeyboardBacklightController.incrementKeyboardBacklight(deviceId);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
+ if (complete) {
+ mKeyboardBacklightController.decrementKeyboardBacklight(deviceId);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
+ // TODO(b/367748270): Add functionality to turn keyboard backlight on/off.
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+ if (complete) {
+ mNative.toggleCapsLock(deviceId);
+ }
+ return true;
+ default:
+ return false;
+
+ }
+ }
+
+ @Override
+ public boolean isKeyGestureSupported(int gestureType) {
+ switch (gestureType) {
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+ return true;
+ default:
+ return false;
+
+ }
+ }
+ });
+ }
+
// Native callback.
@SuppressWarnings("unused")
private KeyEvent dispatchUnhandledKey(IBinder focus, KeyEvent event, int policyFlags) {
@@ -3207,6 +3267,8 @@
mKeyboardBacklightController.onInteractiveChanged(interactive);
}
+ // TODO(b/358569822): Remove this method from InputManagerInternal after key gesture
+ // handler refactoring complete
@Override
public void toggleCapsLock(int deviceId) {
mNative.toggleCapsLock(deviceId);
@@ -3294,11 +3356,15 @@
mKeyboardBacklightController.notifyUserActivity();
}
+ // TODO(b/358569822): Remove this method from InputManagerInternal after key gesture
+ // handler refactoring complete
@Override
public void incrementKeyboardBacklight(int deviceId) {
mKeyboardBacklightController.incrementKeyboardBacklight(deviceId);
}
+ // TODO(b/358569822): Remove this method from InputManagerInternal after key gesture
+ // handler refactoring complete
@Override
public void decrementKeyboardBacklight(int deviceId) {
mKeyboardBacklightController.decrementKeyboardBacklight(deviceId);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ef37464..02c02b0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3849,10 +3849,6 @@
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_KEYBOARD_BACKLIGHT_UP:
- case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
- case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
- case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
return true;
default:
return false;
@@ -4010,25 +4006,6 @@
sendSwitchKeyboardLayout(displayId, focusedToken, direction);
}
return true;
- // TODO (b/358569822): Move these input specific gesture handling to IMS since we
- // are calling into InputManager through internal API anyways
- case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
- if (complete) {
- mInputManagerInternal.incrementKeyboardBacklight(deviceId);
- }
- return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
- if (complete) {
- mInputManagerInternal.decrementKeyboardBacklight(deviceId);
- }
- return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
- return true;
- case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
- if (complete) {
- mInputManagerInternal.toggleCapsLock(deviceId);
- }
- return true;
}
return false;
}
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index c2e71f8..2a82d5f 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -21,6 +21,8 @@
import android.content.Context
import android.content.ContextWrapper
import android.content.PermissionChecker
+import android.content.pm.PackageManager
+import android.content.pm.PackageManagerInternal
import android.hardware.display.DisplayManager
import android.hardware.display.DisplayViewport
import android.hardware.display.VirtualDisplay
@@ -29,10 +31,13 @@
import android.os.InputEventInjectionSync
import android.os.SystemClock
import android.os.test.TestLooper
+import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.Presubmit
+import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings
import android.view.View.OnKeyListener
import android.view.InputDevice
+import android.view.KeyCharacterMap
import android.view.KeyEvent
import android.view.SurfaceHolder
import android.view.SurfaceView
@@ -94,6 +99,10 @@
ExtendedMockitoRule.Builder(this).mockStatic(LocalServices::class.java)
.mockStatic(PermissionChecker::class.java).build()!!
+ @JvmField
+ @Rule
+ val setFlagsRule = SetFlagsRule()
+
@get:Rule
val fakeSettingsProviderRule = FakeSettingsProvider.rule()!!
@@ -107,8 +116,14 @@
private lateinit var windowManagerInternal: WindowManagerInternal
@Mock
+ private lateinit var packageManagerInternal: PackageManagerInternal
+
+ @Mock
private lateinit var uEventManager: UEventManager
+ @Mock
+ private lateinit var kbdController: InputManagerService.KeyboardBacklightControllerInterface
+
private lateinit var service: InputManagerService
private lateinit var localService: InputManagerInternal
private lateinit var context: Context
@@ -135,15 +150,29 @@
override fun registerLocalService(service: InputManagerInternal?) {
localService = service!!
}
+
+ override fun getKeyboardBacklightController(
+ nativeService: NativeInputManagerService?,
+ dataStore: PersistentDataStore?
+ ): InputManagerService.KeyboardBacklightControllerInterface {
+ return kbdController
+ }
})
inputManagerGlobalSession = InputManagerGlobal.createTestSession(service)
val inputManager = InputManager(context)
whenever(context.getSystemService(InputManager::class.java)).thenReturn(inputManager)
whenever(context.getSystemService(Context.INPUT_SERVICE)).thenReturn(inputManager)
+ whenever(context.checkCallingOrSelfPermission(Manifest.permission.MANAGE_KEY_GESTURES))
+ .thenReturn(
+ PackageManager.PERMISSION_GRANTED
+ )
ExtendedMockito.doReturn(windowManagerInternal).`when` {
LocalServices.getService(eq(WindowManagerInternal::class.java))
}
+ ExtendedMockito.doReturn(packageManagerInternal).`when` {
+ LocalServices.getService(eq(PackageManagerInternal::class.java))
+ }
assertTrue("Local service must be registered", this::localService.isInitialized)
service.setWindowManagerCallbacks(wmCallbacks)
@@ -442,6 +471,37 @@
verify(mockOnKeyListener, never()).onKey(mockSurfaceView1, KeyEvent.KEYCODE_A, upEvent)
}
+ @Test
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+ fun handleKeyGestures_keyboardBacklight() {
+ service.systemRunning()
+
+ val backlightDownEvent = createKeyEvent(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN)
+ service.interceptKeyBeforeDispatching(null, backlightDownEvent, /* policyFlags = */0)
+ verify(kbdController).decrementKeyboardBacklight(anyInt())
+
+ val backlightUpEvent = createKeyEvent(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP)
+ service.interceptKeyBeforeDispatching(null, backlightUpEvent, /* policyFlags = */0)
+ verify(kbdController).incrementKeyboardBacklight(anyInt())
+ }
+
+ @Test
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+ fun handleKeyGestures_toggleCapsLock() {
+ service.systemRunning()
+
+ val metaDownEvent = createKeyEvent(KeyEvent.KEYCODE_META_LEFT)
+ service.interceptKeyBeforeDispatching(null, metaDownEvent, /* policyFlags = */0)
+ val altDownEvent =
+ createKeyEvent(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.META_META_ON, KeyEvent.ACTION_DOWN)
+ service.interceptKeyBeforeDispatching(null, altDownEvent, /* policyFlags = */0)
+ val altUpEvent =
+ createKeyEvent(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.META_META_ON, KeyEvent.ACTION_UP)
+ service.interceptKeyBeforeDispatching(null, altUpEvent, /* policyFlags = */0)
+
+ verify(native).toggleCapsLock(anyInt())
+ }
+
fun overrideSendActionKeyEventsToFocusedWindow(
hasPermission: Boolean,
hasPrivateFlag: Boolean
@@ -476,6 +536,25 @@
)
whenever(windowManagerInternal.getKeyInterceptionInfoFromToken(any())).thenReturn(info)
}
+
+ private fun createKeyEvent(
+ keycode: Int,
+ modifierState: Int = 0,
+ action: Int = KeyEvent.ACTION_DOWN
+ ): KeyEvent {
+ return KeyEvent(
+ /* downTime = */0,
+ /* eventTime = */0,
+ action,
+ keycode,
+ /* repeat = */0,
+ modifierState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD,
+ /* scancode = */0,
+ /* flags = */0,
+ InputDevice.SOURCE_KEYBOARD
+ )
+ }
}
private fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall)