Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3192276a authored by Daniel Norman's avatar Daniel Norman
Browse files

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
parent 28cef9b7
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -144,6 +144,16 @@ flag {
    bug: "355487062"
}

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"
+9 −4
Original line number Diff line number Diff line
@@ -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,6 +298,7 @@ class EventDispatcher {
                sendMotionEvent(
                        prototype,
                        action,
                        Flags.eventDispatcherRawEvent() ? mState.getLastReceivedRawEvent() :
                                mState.getLastReceivedEvent(),
                        pointerIdBits,
                        policyFlags);
@@ -327,6 +329,7 @@ class EventDispatcher {
                sendMotionEvent(
                        event,
                        action,
                        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);
        }
    }
+9 −5
Original line number Diff line number Diff line
@@ -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,6 +1120,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
            mDispatcher.sendMotionEvent(
                    event,
                    ACTION_HOVER_EXIT,
                    Flags.eventDispatcherRawEvent() ? mState.getLastReceivedRawEvent() :
                            mState.getLastReceivedEvent(),
                    pointerIdBits,
                    policyFlags);
@@ -1140,6 +1143,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
            mDispatcher.sendMotionEvent(
                    event,
                    ACTION_HOVER_ENTER,
                    Flags.eventDispatcherRawEvent() ? mState.getLastReceivedRawEvent() :
                            mState.getLastReceivedEvent(),
                    pointerIdBits,
                    policyFlags);
+61 −0
Original line number Diff line number Diff line
@@ -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];