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

Commit a9faca97 authored by Daniel Norman's avatar Daniel Norman
Browse files

fix(touchexplorer): Avoid conflicts with real and TouchExplorer events by using a virtual device id

InputDispatcher's touch streams track touch state consistency separately
for each device id. Using a virtual device id for TouchExplorer, like we
already do for MotionEventInjector, ensures that these events do not
clash with real touchscreen events that happened before the input filter
was enabled.

If a real pointer is on screen when TouchExplorer starts then
InputDispatcher will automatically clear that real deviceId's pointer
state when it sees an event from the new virtual deviceId on the same
display.

(Note: device id != display id. Real and injected events target the same
display, but use different device ids to indicate who made the event.)

Bug: 364408887
Test: manually use touch exploration and observe issue in bug is gone
Test: atest com.android.server.accessibility.gestures.TouchExplorerTest
Test: atest android.accessibilityservice.cts.TouchExplorerTest
Flag: com.android.server.accessibility.touch_explorer_use_virtual_device_id
Change-Id: I223d50bf9ce53e0ff366f43d2f913c7d119b789d
parent 975c5e65
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -352,3 +352,13 @@ flag {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "touch_explorer_use_virtual_device_id"
    namespace: "accessibility"
    description: "Use a VIRTUAL device id for injected TouchExplorer events to avoid temporary conflicts with real pointer touches still on the screen when TouchExplorer starts up."
    bug: "364408887"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
+3 −2
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.accessibility;

import static com.android.server.accessibility.gestures.EventDispatcher.VIRTUAL_TOUCHSCREEN_DEVICE_ID;

import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.GestureDescription.GestureStep;
@@ -30,7 +32,6 @@ import android.util.IntArray;
import android.util.Slog;
import android.util.SparseIntArray;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.MotionEvent;
import android.view.WindowManagerPolicyConstants;

@@ -526,7 +527,7 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
        }
        return MotionEvent.obtain(downTime, eventTime, action, touchPointsSize,
                sPointerProps, sPointerCoords, EVENT_META_STATE, EVENT_BUTTON_STATE,
                EVENT_X_PRECISION, EVENT_Y_PRECISION, KeyCharacterMap.VIRTUAL_KEYBOARD,
                EVENT_X_PRECISION, EVENT_Y_PRECISION, VIRTUAL_TOUCHSCREEN_DEVICE_ID,
                EVENT_EDGE_FLAGS, EVENT_SOURCE, EVENT_FLAGS);
    }

+14 −2
Original line number Diff line number Diff line
@@ -39,7 +39,16 @@ import com.android.server.policy.WindowManagerPolicy;
 * gesture dispatch. TouchExplorer is responsible for insuring that the receiver of motion events is
 * set correctly so that events go to the right place.
 */
class EventDispatcher {
public class EventDispatcher {

    /**
     * Device ID used for touchscreen events injected by touch exploration and gesture dispatch.
     *
     * <p>Using a virtual device ID that differs from a real touchscreen ID helps to prevent
     * conflicts in inputflinger/InputDispatcher between injected and real touch events.
     */
    public static final int VIRTUAL_TOUCHSCREEN_DEVICE_ID = -1;

    private static final String LOG_TAG = "EventDispatcher";
    private static final int CLICK_LOCATION_NONE = 0;
    private static final int CLICK_LOCATION_ACCESSIBILITY_FOCUS = 1;
@@ -125,10 +134,13 @@ class EventDispatcher {
            event.getPointerProperties(i, p);
            properties[i] = p;
        }
        final int deviceId = Flags.touchExplorerUseVirtualDeviceId()
                ? VIRTUAL_TOUCHSCREEN_DEVICE_ID
                : rawEvent.getDeviceId();
        event = MotionEvent.obtain(downTime, event.getEventTime(), event.getAction(),
                event.getPointerCount(), properties, coords,
                event.getMetaState(), event.getButtonState(),
                event.getXPrecision(), event.getYPrecision(), rawEvent.getDeviceId(),
                event.getXPrecision(), event.getYPrecision(), deviceId,
                event.getEdgeFlags(), rawEvent.getSource(), event.getDisplayId(), event.getFlags(),
                event.getClassification());
        // If the user is long pressing but the long pressing pointer
+15 −0
Original line number Diff line number Diff line
@@ -180,6 +180,21 @@ public class TouchExplorerTest {
        assertState(STATE_TOUCH_EXPLORING);
    }

    @Test
    @EnableFlags(Flags.FLAG_TOUCH_EXPLORER_USE_VIRTUAL_DEVICE_ID)
    public void testOneFingerMove_injectedEventsUseVirtualDeviceId() {
        goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
        // Wait for transiting to touch exploring state.
        mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
        goToStateClearFrom(STATE_TOUCH_EXPLORING_1FINGER);

        assertThat(getCapturedEvents()).hasSize(2);
        assertThat(getCapturedEvents().get(0).getDeviceId()).isEqualTo(
                EventDispatcher.VIRTUAL_TOUCHSCREEN_DEVICE_ID);
        assertThat(getCapturedEvents().get(1).getDeviceId()).isEqualTo(
                EventDispatcher.VIRTUAL_TOUCHSCREEN_DEVICE_ID);
    }

    /**
     * Test the case where ACTION_DOWN is followed by a number of ACTION_MOVE events that do not
     * change the coordinates.