summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Daniel Norman <danielnorman@google.com> 2025-01-13 23:50:35 +0000
committer Daniel Norman <danielnorman@google.com> 2025-01-14 20:42:16 +0000
commit3192276a439484d1e7da50aef7ca1a180f78dcd6 (patch)
treef6421ab233d424565a51657b5de90307422840c6
parent28cef9b76c59eb2e8dd4b2614a70217608b978fb (diff)
Cleanup EventDispatcher#sendMotionEvent callers to provide raw event.
A few releases ago this codebase started tracking the raw (unmodified) and regular (possibly modified) event separately, but some older sendMotionEvent callers were not updated to provide the correct raw event. This should also help prevent a race condition NPE in TouchExplorer#sendHoverExitAndTouchExplorationGestureEndIfNeeded: That method was using the last received *regular* event instead of the raw event, and this regular event was already cleared from a previous call to clear() so EventDispatcher#sendMotionEvent was unable to access the event's attributes. (See linked bug) Bug: 385812366 Test: atest TouchExplorerTest Test: atest FrameworksServicesTests:com.android.server.accessibility Test: Use TalkBack touch exploration, observe no behavior changes Flag: com.android.server.accessibility.event_dispatcher_raw_event Change-Id: Ie60f8174de3b12da01987f9bad59669f45bbdfdf
-rw-r--r--services/accessibility/accessibility.aconfig10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java13
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java61
4 files changed, 89 insertions, 9 deletions
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 7f0bf0375b4a..722255404dd1 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -145,6 +145,16 @@ flag {
}
flag {
+ name: "event_dispatcher_raw_event"
+ namespace: "accessibility"
+ description: "Fixes EventDispatcher#sendMotionEvent callers to properly provide raw event"
+ bug: "385812366"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "fix_drag_pointer_when_ending_drag"
namespace: "accessibility"
description: "Send the correct pointer id when transitioning from dragging to delegating states."
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
index bf9202f1b266..5c0bbf4e01eb 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
@@ -31,6 +31,7 @@ import android.view.accessibility.AccessibilityManager;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.EventStreamTransformation;
+import com.android.server.accessibility.Flags;
import com.android.server.policy.WindowManagerPolicy;
/**
@@ -297,7 +298,8 @@ class EventDispatcher {
sendMotionEvent(
prototype,
action,
- mState.getLastReceivedEvent(),
+ Flags.eventDispatcherRawEvent() ? mState.getLastReceivedRawEvent() :
+ mState.getLastReceivedEvent(),
pointerIdBits,
policyFlags);
}
@@ -327,7 +329,8 @@ class EventDispatcher {
sendMotionEvent(
event,
action,
- mState.getLastReceivedEvent(),
+ Flags.eventDispatcherRawEvent() ? mState.getLastReceivedRawEvent() :
+ mState.getLastReceivedEvent(),
pointerIdBits,
policyFlags);
}
@@ -394,8 +397,10 @@ class EventDispatcher {
continue;
}
final int action = computeInjectionAction(MotionEvent.ACTION_POINTER_UP, i);
- sendMotionEvent(
- prototype, action, mState.getLastReceivedEvent(), pointerIdBits, policyFlags);
+ sendMotionEvent(prototype, action,
+ Flags.eventDispatcherRawEvent() ? mState.getLastReceivedRawEvent() :
+ mState.getLastReceivedEvent(),
+ pointerIdBits, policyFlags);
pointerIdBits &= ~(1 << pointerId);
}
}
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 0cbbf6da022b..59e55e5f4b1e 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -506,13 +506,14 @@ public class TouchExplorer extends BaseEventStreamTransformation
// We have just decided that the user is touch,
// exploring so start sending events.
- mSendHoverEnterAndMoveDelayed.addEvent(event, mState.getLastReceivedEvent());
+ mSendHoverEnterAndMoveDelayed.addEvent(event,
+ Flags.eventDispatcherRawEvent() ? rawEvent : mState.getLastReceivedEvent());
mSendHoverEnterAndMoveDelayed.forceSendAndRemove();
mSendHoverExitDelayed.cancel();
mDispatcher.sendMotionEvent(
event,
ACTION_HOVER_MOVE,
- event,
+ Flags.eventDispatcherRawEvent() ? rawEvent : event,
pointerIdBits,
policyFlags);
return true;
@@ -1108,7 +1109,8 @@ public class TouchExplorer extends BaseEventStreamTransformation
*
* @param policyFlags The policy flags associated with the event.
*/
- private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) {
+ @VisibleForTesting
+ void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) {
MotionEvent event = mState.getLastInjectedHoverEvent();
if (event != null && event.getActionMasked() != ACTION_HOVER_EXIT) {
final int pointerIdBits = event.getPointerIdBits();
@@ -1118,7 +1120,8 @@ public class TouchExplorer extends BaseEventStreamTransformation
mDispatcher.sendMotionEvent(
event,
ACTION_HOVER_EXIT,
- mState.getLastReceivedEvent(),
+ Flags.eventDispatcherRawEvent() ? mState.getLastReceivedRawEvent() :
+ mState.getLastReceivedEvent(),
pointerIdBits,
policyFlags);
}
@@ -1140,7 +1143,8 @@ public class TouchExplorer extends BaseEventStreamTransformation
mDispatcher.sendMotionEvent(
event,
ACTION_HOVER_ENTER,
- mState.getLastReceivedEvent(),
+ Flags.eventDispatcherRawEvent() ? mState.getLastReceivedRawEvent() :
+ mState.getLastReceivedEvent(),
pointerIdBits,
policyFlags);
}
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 e5005d1beed4..1af59daa9c78 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
@@ -33,6 +33,8 @@ import static com.android.server.accessibility.gestures.TouchState.STATE_DRAGGIN
import static com.android.server.accessibility.gestures.TouchState.STATE_GESTURE_DETECTING;
import static com.android.server.accessibility.gestures.TouchState.STATE_TOUCH_EXPLORING;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
@@ -132,10 +134,12 @@ public class TouchExplorerTest {
*/
private class EventCaptor implements EventStreamTransformation {
List<MotionEvent> mEvents = new ArrayList<>();
+ List<MotionEvent> mRawEvents = new ArrayList<>();
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
mEvents.add(0, event.copy());
+ mRawEvents.add(0, rawEvent.copy());
}
@Override
@@ -461,6 +465,45 @@ public class TouchExplorerTest {
AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN);
}
+ @Test
+ public void testSendHoverExitIfNeeded_lastSentHoverExit_noActionNeeded() {
+ // Prep TouchState so that the last injected hover event was a HOVER_EXIT
+ mTouchExplorer.getState().onInjectedMotionEvent(hoverExitEvent());
+
+ mTouchExplorer.sendHoverExitAndTouchExplorationGestureEndIfNeeded(/*policyFlags=*/0);
+
+ assertNoCapturedEvents();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_EVENT_DISPATCHER_RAW_EVENT)
+ public void testSendHoverExitIfNeeded_lastSentHoverEnter_sendsHoverExit_withCorrectRawEvent() {
+ final MotionEvent rawEvent = downEvent();
+ final MotionEvent modifiedEvent = hoverEnterEvent();
+ // Use different display IDs just so that we can differentiate between the raw event and
+ // the modified event later during test assertions.
+ final int rawDisplayId = 123;
+ final int modifiedDisplayId = 456;
+ rawEvent.setDisplayId(rawDisplayId);
+ modifiedEvent.setDisplayId(modifiedDisplayId);
+ // Prep TouchState to track the last received modified and raw events
+ mTouchExplorer.getState().onReceivedMotionEvent(modifiedEvent, rawEvent, /*policyFlags=*/0);
+ // Prep TouchState so that the last injected hover event was not a HOVER_EXIT
+ mTouchExplorer.getState().onInjectedMotionEvent(modifiedEvent);
+
+ mTouchExplorer.sendHoverExitAndTouchExplorationGestureEndIfNeeded(/*policyFlags=*/0);
+
+ assertThat(getCapturedEvents().size()).isEqualTo(1);
+ assertThat(getCapturedRawEvents().size()).isEqualTo(1);
+ MotionEvent sentEvent = getCapturedEvents().get(0);
+ MotionEvent sentRawEvent = getCapturedRawEvents().get(0);
+ // TouchExplorer should send ACTION_HOVER_EXIT built from the last injected hover event
+ assertThat(sentEvent.getAction()).isEqualTo(ACTION_HOVER_EXIT);
+ assertThat(sentEvent.getDisplayId()).isEqualTo(modifiedDisplayId);
+ // ... while passing along the original raw (unmodified) event
+ assertThat(sentRawEvent.getDisplayId()).isEqualTo(rawDisplayId);
+ }
+
/**
* Used to play back event data of a gesture by parsing the log into MotionEvents and sending
* them to TouchExplorer.
@@ -630,6 +673,10 @@ public class TouchExplorerTest {
return ((EventCaptor) mCaptor).mEvents;
}
+ private List<MotionEvent> getCapturedRawEvents() {
+ return ((EventCaptor) mCaptor).mRawEvents;
+ }
+
private MotionEvent cancelEvent() {
mLastDownTime = SystemClock.uptimeMillis();
return fromTouchscreen(
@@ -688,6 +735,20 @@ public class TouchExplorerTest {
return event;
}
+ private MotionEvent hoverEnterEvent() {
+ mLastDownTime = SystemClock.uptimeMillis();
+ return fromTouchscreen(
+ MotionEvent.obtain(
+ mLastDownTime, mLastDownTime, ACTION_HOVER_ENTER, DEFAULT_X, DEFAULT_Y, 0));
+ }
+
+ private MotionEvent hoverExitEvent() {
+ mLastDownTime = SystemClock.uptimeMillis();
+ return fromTouchscreen(
+ MotionEvent.obtain(
+ mLastDownTime, mLastDownTime, ACTION_HOVER_EXIT, DEFAULT_X, DEFAULT_Y, 0));
+ }
+
private void moveEachPointers(MotionEvent event, PointF... points) {
final float[] x = new float[points.length];
final float[] y = new float[points.length];