diff options
author | 2024-09-04 23:44:55 +0000 | |
---|---|---|
committer | 2024-09-04 23:51:54 +0000 | |
commit | 900c31fd6c460f57d8c5ec4af4cac65c3bc656f6 (patch) | |
tree | 898bba2853ffb16a9441d1742a9bca7a1f758cf9 | |
parent | 67271f5bb119609a4f9400b505537de5449cea83 (diff) |
Sends ACTION_CANCEL before the very first touch exploration HOVER event.
This resets the InputDispatcher state in case the user had a pointer
down (and kept it down) before enabling TalkBack or while booting up
the device before TalkBack has started up.
Fix: 364408887
Test: atest TouchExplorerTest
Test: touch and hold on the screen;
start TalkBack with a shortcut or adb shell;
lift your finger off the screen;
touch the screen again, and observe that touch exploration
functions properly.
Flag: com.android.server.accessibility.reset_input_dispatcher_before_first_touch_exploration
Change-Id: I1c2bdb7b3d32cd124257e60e8c91c073c3040f50
4 files changed, 74 insertions, 5 deletions
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index 8e2e0ad76d15..3f604ca0016a 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -209,6 +209,16 @@ flag { } flag { + name: "reset_input_dispatcher_before_first_touch_exploration" + namespace: "accessibility" + description: "Resets InputDispatcher state by sending ACTION_CANCEL before the first TouchExploration hover events" + bug: "364408887" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "scan_packages_without_lock" namespace: "accessibility" description: "Scans packages for accessibility service/activity info without holding the A11yMS lock" @@ -221,6 +231,7 @@ flag { description: "Sends accessibility events in TouchExplorer#onAccessibilityEvent based on internal state to keep it consistent. This reduces test flakiness." bug: "295575684" } + flag { name: "send_hover_events_based_on_event_stream" namespace: "accessibility" diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index 04b42e49fad9..0ed239e442e7 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -1613,6 +1613,19 @@ public class TouchExplorer extends BaseEventStreamTransformation dispatchGesture(gestureEvent); } if (!mEvents.isEmpty() && !mRawEvents.isEmpty()) { + if (Flags.resetInputDispatcherBeforeFirstTouchExploration() + && !mState.hasResetInputDispatcherState()) { + // Cancel any possible ongoing touch gesture from before touch exploration + // started. This clears out the InputDispatcher event stream state so that it + // is ready to accept new injected HOVER events. + mDispatcher.sendMotionEvent( + mEvents.get(0), + ACTION_CANCEL, + mRawEvents.get(0), + mPointerIdBits, + mPolicyFlags); + setHasResetInputDispatcherState(true); + } // Deliver a down event. mDispatcher.sendMotionEvent( mEvents.get(0), @@ -1773,4 +1786,9 @@ public class TouchExplorer extends BaseEventStreamTransformation + ", mDraggingPointerId: " + mDraggingPointerId + " }"; } + + @VisibleForTesting + void setHasResetInputDispatcherState(boolean value) { + mState.setHasResetInputDispatcherState(value); + } } diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java index e13994e75690..f15b8eec3f6b 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java @@ -86,6 +86,7 @@ public class TouchState { private MotionEvent mLastInjectedHoverEvent; // The last injected hover event used for performing clicks. private MotionEvent mLastInjectedHoverEventForClick; + private boolean mHasResetInputDispatcherState; // The time of the last injected down. private long mLastInjectedDownEventTime; // Keep track of which pointers sent to the system are down. @@ -361,6 +362,14 @@ public class TouchState { return mLastInjectedDownEventTime; } + boolean hasResetInputDispatcherState() { + return mHasResetInputDispatcherState; + } + + void setHasResetInputDispatcherState(boolean value) { + mHasResetInputDispatcherState = value; + } + public int getLastTouchedWindowId() { return mLastTouchedWindowId; } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java index 1cd61e90126e..e5005d1beed4 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java @@ -44,6 +44,8 @@ import android.content.Context; import android.graphics.PointF; import android.os.Looper; import android.os.SystemClock; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.testing.DexmakerShareClassLoaderRule; import android.view.InputDevice; import android.view.MotionEvent; @@ -56,6 +58,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.accessibility.EventStreamTransformation; +import com.android.server.accessibility.Flags; import com.android.server.accessibility.utils.GestureLogParser; import com.android.server.testutils.OffsettableClock; @@ -119,6 +122,9 @@ public class TouchExplorerTest { public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = new DexmakerShareClassLoaderRule(); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + /** * {@link TouchExplorer#sendDownForAllNotInjectedPointers} injecting events with the same object * is resulting {@link ArgumentCaptor} to capture events with last state. Before implementation @@ -154,18 +160,43 @@ public class TouchExplorerTest { mHandler = new TestHandler(); mTouchExplorer = new TouchExplorer(mContext, mMockAms, null, mHandler); mTouchExplorer.setNext(mCaptor); + // Start TouchExplorer in the state where it has already reset InputDispatcher so that + // all tests do not start with an irrelevant ACTION_CANCEL. + mTouchExplorer.setHasResetInputDispatcherState(true); } @Test public void testOneFingerMove_shouldInjectHoverEvents() { - goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER); - // Wait for transiting to touch exploring state. + triggerTouchExplorationWithOneFingerDownMoveUp(); + assertCapturedEvents(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT); + assertState(STATE_TOUCH_EXPLORING); + } + + @Test + @EnableFlags(Flags.FLAG_RESET_INPUT_DISPATCHER_BEFORE_FIRST_TOUCH_EXPLORATION) + public void testStartTouchExploration_shouldResetInputDispatcherStateWithActionCancel() { + // Start TouchExplorer in the state where it has *not yet* reset InputDispatcher. + mTouchExplorer.setHasResetInputDispatcherState(false); + // Trigger touch exploration twice, with a handler fast-forward in between so TouchExplorer + // treats these as two separate interactions. + triggerTouchExplorationWithOneFingerDownMoveUp(); + mHandler.fastForward(2 * USER_INTENT_TIMEOUT); + triggerTouchExplorationWithOneFingerDownMoveUp(); + + assertCapturedEvents( + ACTION_CANCEL, // Only one ACTION_CANCEL before the first touch exploration + ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT, + ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT); + assertState(STATE_TOUCH_EXPLORING); + } + + private void triggerTouchExplorationWithOneFingerDownMoveUp() { + send(downEvent()); + // Fast forward so that TouchExplorer's timeouts transition us to the touch exploring state. mHandler.fastForward(2 * USER_INTENT_TIMEOUT); moveEachPointers(mLastEvent, p(10, 10)); send(mLastEvent); - goToStateClearFrom(STATE_TOUCH_EXPLORING_1FINGER); - assertCapturedEvents(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT); - assertState(STATE_TOUCH_EXPLORING); + send(upEvent()); } /** |