From 0a5dd6e8a7d507c9b8b5adcbc40fc1863f83e7fd Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Mon, 26 Feb 2024 16:00:12 -0800 Subject: A11yInputFilter: only multiplex pointer events For events that are focus-dispatched, like joystick events, and also for ACTION_SCROLL events (since they are always treated independently), we don't need to turn on the multiplexer. The multiplexer should only be used on pointer events. The AccessibilityDisplayProxyTest was tested on tangor by first removing all of the test assumptions locally (otherwise, the tests were skipped due to assumption failure). Bug: 326686780 Test: atest AccessibilityInputFilterInputTest Test: atest android.accessibilityservice.cts.AccessibilityEndToEndTest#testOnMotionEvent_interceptsEventFromRequestedSource_SetAndUnset Test: atest AccessibilityDisplayProxyTest Change-Id: I40a1b42aa8a169138c96972a39ebed5d16483d11 --- .../accessibility/AccessibilityInputFilter.java | 10 ++++++++- .../AccessibilityInputFilterInputTest.kt | 25 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 16119d11ee1e..54e545d6d73a 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -16,6 +16,8 @@ package com.android.server.accessibility; +import static android.view.InputDevice.SOURCE_CLASS_POINTER; +import static android.view.MotionEvent.ACTION_SCROLL; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; @@ -425,6 +427,12 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo boolean shouldProcessMultiDeviceEvent(InputEvent event, int policyFlags) { if (event instanceof MotionEvent motion) { + if (!motion.isFromSource(SOURCE_CLASS_POINTER) || motion.getAction() == ACTION_SCROLL) { + // Non-pointer events are focus-dispatched and don't require special logic. + // Scroll events are stand-alone and therefore can be considered to not be part of + // a stream. + return true; + } // Only allow 1 device to be sending motion events at a time // If the event is from an active device, let it through. // If the event is not from an active device, only let it through if it starts a new @@ -481,7 +489,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } private void processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags) { - if (!state.shouldProcessScroll() && event.getActionMasked() == MotionEvent.ACTION_SCROLL) { + if (!state.shouldProcessScroll() && event.getActionMasked() == ACTION_SCROLL) { super.onInputEvent(event, policyFlags); return; } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt index 5c8c6bb69ef8..e6c94c51d1b1 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt @@ -25,6 +25,7 @@ import android.view.Display.DEFAULT_DISPLAY import android.view.DisplayAdjustments import android.view.DisplayInfo import android.view.IInputFilterHost +import android.view.InputDevice.SOURCE_JOYSTICK import android.view.InputDevice.SOURCE_STYLUS import android.view.InputDevice.SOURCE_TOUCHSCREEN import android.view.InputEvent @@ -130,6 +131,8 @@ class AccessibilityInputFilterInputTest { private val fromTouchScreen = allOf(withDeviceId(touchDeviceId), withSource(SOURCE_TOUCHSCREEN)) private val stylusDeviceId = 2 private val fromStylus = allOf(withDeviceId(stylusDeviceId), withSource(STYLUS_SOURCE)) + private val joystickDeviceId = 3 + private val fromJoystick = allOf(withDeviceId(joystickDeviceId), withSource(SOURCE_JOYSTICK)) @Before fun setUp() { @@ -457,6 +460,23 @@ class AccessibilityInputFilterInputTest { verifier.assertNoEvents() } + /** + * Send some joystick events and ensure they pass through normally. + */ + @Test + fun testJoystickEvents() { + enableFeatures(ALL_A11Y_FEATURES) + + sendJoystickEvent() + verifier.assertReceivedMotion(fromJoystick) + + sendJoystickEvent() + verifier.assertReceivedMotion(fromJoystick) + + sendJoystickEvent() + verifier.assertReceivedMotion(fromJoystick) + } + private fun createStubDisplay(displayId: Int, displayInfo: DisplayInfo): Display { val display = Display(DisplayManagerGlobal.getInstance(), displayId, displayInfo, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS) @@ -477,6 +497,11 @@ class AccessibilityInputFilterInputTest { send(createMotionEvent(action, downTime, eventTime, STYLUS_SOURCE, stylusDeviceId)) } + private fun sendJoystickEvent() { + val time = SystemClock.uptimeMillis() + send(createMotionEvent(ACTION_MOVE, time, time, SOURCE_JOYSTICK, joystickDeviceId)) + } + private fun send(event: InputEvent) { // We need to make a copy of the event before sending it to the filter, because the filter // will recycle it, but the caller of this function might want to still be able to use -- cgit v1.2.3-59-g8ed1b