1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.accessibility;
18 
19 import static android.view.KeyCharacterMap.VIRTUAL_KEYBOARD;
20 import static android.view.MotionEvent.ACTION_DOWN;
21 import static android.view.MotionEvent.ACTION_HOVER_MOVE;
22 import static android.view.MotionEvent.ACTION_UP;
23 import static android.view.WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY;
24 import static android.view.WindowManagerPolicyConstants.FLAG_PASS_TO_USER;
25 
26 import static org.hamcrest.CoreMatchers.allOf;
27 import static org.hamcrest.CoreMatchers.anyOf;
28 import static org.hamcrest.CoreMatchers.everyItem;
29 import static org.hamcrest.MatcherAssert.assertThat;
30 import static org.junit.Assert.assertEquals;
31 import static org.junit.Assert.assertFalse;
32 import static org.junit.Assert.assertTrue;
33 import static org.mockito.Matchers.anyBoolean;
34 import static org.mockito.Matchers.anyInt;
35 import static org.mockito.Matchers.eq;
36 import static org.mockito.Mockito.mock;
37 import static org.mockito.Mockito.reset;
38 import static org.mockito.Mockito.times;
39 import static org.mockito.Mockito.verify;
40 import static org.mockito.Mockito.verifyNoMoreInteractions;
41 import static org.mockito.Mockito.verifyZeroInteractions;
42 import static org.mockito.hamcrest.MockitoHamcrest.argThat;
43 
44 import android.accessibilityservice.GestureDescription.GestureStep;
45 import android.accessibilityservice.GestureDescription.TouchPoint;
46 import android.accessibilityservice.IAccessibilityServiceClient;
47 import android.graphics.Point;
48 import android.os.Handler;
49 import android.os.Message;
50 import android.os.RemoteException;
51 import android.view.Display;
52 import android.view.InputDevice;
53 import android.view.KeyEvent;
54 import android.view.MotionEvent;
55 import android.view.accessibility.AccessibilityEvent;
56 
57 import androidx.test.runner.AndroidJUnit4;
58 
59 import com.android.server.accessibility.test.MessageCapturingHandler;
60 import com.android.server.accessibility.utils.MotionEventMatcher;
61 
62 import org.hamcrest.Description;
63 import org.hamcrest.Matcher;
64 import org.hamcrest.TypeSafeMatcher;
65 import org.junit.After;
66 import org.junit.Before;
67 import org.junit.Test;
68 import org.junit.runner.RunWith;
69 import org.mockito.ArgumentCaptor;
70 
71 import java.util.ArrayList;
72 import java.util.Arrays;
73 import java.util.List;
74 
75 /**
76  * Tests for MotionEventInjector
77  */
78 @RunWith(AndroidJUnit4.class)
79 public class MotionEventInjectorTest {
80     private static final String LOG_TAG = "MotionEventInjectorTest";
81     private static final Matcher<MotionEvent> IS_ACTION_DOWN =
82             new MotionEventActionMatcher(ACTION_DOWN);
83     private static final Matcher<MotionEvent> IS_ACTION_POINTER_DOWN =
84             new MotionEventActionMatcher(MotionEvent.ACTION_POINTER_DOWN);
85     private static final Matcher<MotionEvent> IS_ACTION_UP =
86             new MotionEventActionMatcher(ACTION_UP);
87     private static final Matcher<MotionEvent> IS_ACTION_POINTER_UP =
88             new MotionEventActionMatcher(MotionEvent.ACTION_POINTER_UP);
89     private static final Matcher<MotionEvent> IS_ACTION_CANCEL =
90             new MotionEventActionMatcher(MotionEvent.ACTION_CANCEL);
91     private static final Matcher<MotionEvent> IS_ACTION_MOVE =
92             new MotionEventActionMatcher(MotionEvent.ACTION_MOVE);
93 
94     private static final Point LINE_START = new Point(100, 200);
95     private static final Point LINE_END = new Point(100, 300);
96     private static final int LINE_DURATION = 100;
97     private static final int LINE_SEQUENCE = 50;
98 
99     private static final Point CLICK_POINT = new Point(1000, 2000);
100     private static final int CLICK_DURATION = 10;
101     private static final int CLICK_SEQUENCE = 51;
102 
103     private static final int MOTION_EVENT_SOURCE = InputDevice.SOURCE_TOUCHSCREEN;
104     private static final int OTHER_EVENT_SOURCE = InputDevice.SOURCE_MOUSE;
105 
106     private static final Point CONTINUED_LINE_START = new Point(500, 300);
107     private static final Point CONTINUED_LINE_MID1 = new Point(500, 400);
108     private static final Point CONTINUED_LINE_MID2 = new Point(600, 300);
109     private static final Point CONTINUED_LINE_END = new Point(600, 400);
110     private static final int CONTINUED_LINE_STROKE_ID_1 = 100;
111     private static final int CONTINUED_LINE_STROKE_ID_2 = 101;
112     private static final int CONTINUED_LINE_INTERVAL = 100;
113     private static final int CONTINUED_LINE_SEQUENCE_1 = 52;
114     private static final int CONTINUED_LINE_SEQUENCE_2 = 53;
115 
116     private static final float PRESSURE = 1;
117     private static final float X_PRECISION = 1;
118     private static final float Y_PRECISION = 1;
119     private static final int EDGEFLAGS = 0;
120     private static final float POINTER_SIZE = 1;
121     private static final int METASTATE = 0;
122 
123     MotionEventInjector mMotionEventInjector;
124     IAccessibilityServiceClient mServiceInterface;
125     AccessibilityTraceManager mTrace;
126     List<GestureStep> mLineList = new ArrayList<>();
127     List<GestureStep> mClickList = new ArrayList<>();
128     List<GestureStep> mContinuedLineList1 = new ArrayList<>();
129     List<GestureStep> mContinuedLineList2 = new ArrayList<>();
130 
131     MotionEvent mClickDownEvent;
132     MotionEvent mClickUpEvent;
133     MotionEvent mHoverMoveEvent;
134 
135     ArgumentCaptor<MotionEvent> mCaptor1 = ArgumentCaptor.forClass(MotionEvent.class);
136     ArgumentCaptor<MotionEvent> mCaptor2 = ArgumentCaptor.forClass(MotionEvent.class);
137     MessageCapturingHandler mMessageCapturingHandler;
138     Matcher<MotionEvent> mIsLineStart;
139     Matcher<MotionEvent> mIsLineMiddle;
140     Matcher<MotionEvent> mIsLineEnd;
141     Matcher<MotionEvent> mIsClickDown;
142     Matcher<MotionEvent> mIsClickUp;
143 
144     @Before
setUp()145     public void setUp() {
146         mMessageCapturingHandler = new MessageCapturingHandler(new Handler.Callback() {
147             @Override
148             public boolean handleMessage(Message msg) {
149                 return mMotionEventInjector.handleMessage(msg);
150             }
151         });
152         mTrace = mock(AccessibilityTraceManager.class);
153         mMotionEventInjector = new MotionEventInjector(mMessageCapturingHandler, mTrace);
154         mServiceInterface = mock(IAccessibilityServiceClient.class);
155 
156         mLineList = createSimpleGestureFromPoints(0, 0, false, LINE_DURATION, LINE_START, LINE_END);
157         mClickList = createSimpleGestureFromPoints(
158                 0, 0, false, CLICK_DURATION, CLICK_POINT, CLICK_POINT);
159         mContinuedLineList1 = createSimpleGestureFromPoints(CONTINUED_LINE_STROKE_ID_1, 0, true,
160                 CONTINUED_LINE_INTERVAL, CONTINUED_LINE_START, CONTINUED_LINE_MID1);
161         mContinuedLineList2 = createSimpleGestureFromPoints(CONTINUED_LINE_STROKE_ID_2,
162                 CONTINUED_LINE_STROKE_ID_1, false, CONTINUED_LINE_INTERVAL, CONTINUED_LINE_MID1,
163                 CONTINUED_LINE_MID2, CONTINUED_LINE_END);
164 
165         mClickDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, CLICK_POINT.x, CLICK_POINT.y,
166                  PRESSURE, POINTER_SIZE, METASTATE, X_PRECISION, Y_PRECISION, VIRTUAL_KEYBOARD,
167                  EDGEFLAGS);
168         mClickDownEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
169         mClickUpEvent = MotionEvent.obtain(0, CLICK_DURATION, ACTION_UP, CLICK_POINT.x,
170                 CLICK_POINT.y, PRESSURE, POINTER_SIZE, METASTATE, X_PRECISION, Y_PRECISION,
171                 VIRTUAL_KEYBOARD, EDGEFLAGS);
172         mClickUpEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
173 
174         mHoverMoveEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, CLICK_POINT.x, CLICK_POINT.y,
175                 PRESSURE, POINTER_SIZE, METASTATE, X_PRECISION, Y_PRECISION, VIRTUAL_KEYBOARD,
176                 EDGEFLAGS);
177         mHoverMoveEvent.setSource(InputDevice.SOURCE_MOUSE);
178 
179         mIsLineStart = allOf(IS_ACTION_DOWN, isAtPoint(LINE_START), hasStandardInitialization(),
180                 hasTimeFromDown(0));
181         mIsLineMiddle = allOf(IS_ACTION_MOVE, isAtPoint(LINE_END), hasStandardInitialization(),
182                 hasTimeFromDown(LINE_DURATION));
183         mIsLineEnd = allOf(IS_ACTION_UP, isAtPoint(LINE_END), hasStandardInitialization(),
184                 hasTimeFromDown(LINE_DURATION));
185         mIsClickDown = allOf(IS_ACTION_DOWN, isAtPoint(CLICK_POINT), hasStandardInitialization(),
186                 hasTimeFromDown(0));
187         mIsClickUp = allOf(IS_ACTION_UP, isAtPoint(CLICK_POINT), hasStandardInitialization(),
188                 hasTimeFromDown(CLICK_DURATION));
189     }
190 
191     @After
tearDown()192     public void tearDown() {
193         mMessageCapturingHandler.removeAllMessages();
194     }
195 
196 
197     @Test
testInjectEvents_shouldEmergeInOrderWithCorrectTiming()198     public void testInjectEvents_shouldEmergeInOrderWithCorrectTiming() throws RemoteException {
199         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
200         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
201         verifyNoMoreInteractions(next);
202         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
203 
204         final int expectedFlags = FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY;
205         verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), eq(expectedFlags));
206         verify(next).onMotionEvent(argThat(mIsLineStart), argThat(mIsLineStart), eq(expectedFlags));
207         verifyNoMoreInteractions(next);
208         reset(next);
209 
210         Matcher<MotionEvent> hasRightDownTime = hasDownTime(mCaptor1.getValue().getDownTime());
211 
212         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
213         verify(next).onMotionEvent(argThat(allOf(mIsLineMiddle, hasRightDownTime)),
214                 argThat(allOf(mIsLineMiddle, hasRightDownTime)), eq(expectedFlags));
215         verifyNoMoreInteractions(next);
216         reset(next);
217 
218         verifyZeroInteractions(mServiceInterface);
219 
220         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
221         verify(next).onMotionEvent(argThat(allOf(mIsLineEnd, hasRightDownTime)),
222                 argThat(allOf(mIsLineEnd, hasRightDownTime)), eq(expectedFlags));
223         verifyNoMoreInteractions(next);
224 
225         verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, true);
226         verifyNoMoreInteractions(mServiceInterface);
227     }
228 
229     @Test
testInjectEvents_gestureWithTooManyPoints_shouldNotCrash()230     public void testInjectEvents_gestureWithTooManyPoints_shouldNotCrash() throws  Exception {
231         int tooManyPointsCount = 20;
232         TouchPoint[] startTouchPoints = new TouchPoint[tooManyPointsCount];
233         TouchPoint[] endTouchPoints = new TouchPoint[tooManyPointsCount];
234         for (int i = 0; i < tooManyPointsCount; i++) {
235             startTouchPoints[i] = new TouchPoint();
236             startTouchPoints[i].mIsStartOfPath = true;
237             startTouchPoints[i].mX = i;
238             startTouchPoints[i].mY = i;
239             endTouchPoints[i] = new TouchPoint();
240             endTouchPoints[i].mIsEndOfPath = true;
241             endTouchPoints[i].mX = i;
242             endTouchPoints[i].mY = i;
243         }
244         List<GestureStep> events = Arrays.asList(
245                 new GestureStep(0, tooManyPointsCount, startTouchPoints),
246                 new GestureStep(CLICK_DURATION, tooManyPointsCount, endTouchPoints));
247         attachMockNext(mMotionEventInjector);
248         injectEventsSync(events, mServiceInterface, CLICK_SEQUENCE);
249         mMessageCapturingHandler.sendAllMessages();
250         verify(mServiceInterface).onPerformGestureResult(eq(CLICK_SEQUENCE), anyBoolean());
251     }
252 
253     @Test
testRegularEvent_afterGestureComplete_shouldPassToNext()254     public void testRegularEvent_afterGestureComplete_shouldPassToNext() {
255         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
256         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
257         mMessageCapturingHandler.sendAllMessages(); // Send all motion events
258         reset(next);
259         mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0);
260         verify(next).onMotionEvent(argThat(mIsClickDown), argThat(mIsClickDown),
261                 eq(FLAG_INJECTED_FROM_ACCESSIBILITY));
262     }
263 
264     @Test
testInjectEvents_withRealGestureUnderway_shouldCancelRealAndPassInjected()265     public void testInjectEvents_withRealGestureUnderway_shouldCancelRealAndPassInjected() {
266         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
267         mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0);
268         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
269 
270         verify(next, times(2)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
271         assertThat(mCaptor1.getAllValues().get(0), mIsClickDown);
272         assertThat(mCaptor1.getAllValues().get(1), IS_ACTION_CANCEL);
273         reset(next);
274 
275         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
276         verify(next).onMotionEvent(
277                 argThat(mIsLineStart), argThat(mIsLineStart),
278                 eq(FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY));
279     }
280 
281     @Test
testInjectEvents_withRealMouseGestureUnderway_shouldContinueRealAndPassInjected()282     public void testInjectEvents_withRealMouseGestureUnderway_shouldContinueRealAndPassInjected() {
283         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
284         MotionEvent mouseEvent = MotionEvent.obtain(mClickDownEvent);
285         mouseEvent.setSource(InputDevice.SOURCE_MOUSE);
286         MotionEventMatcher isMouseEvent = new MotionEventMatcher(mouseEvent);
287         mMotionEventInjector.onMotionEvent(mouseEvent, mouseEvent, 0);
288         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
289 
290         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
291         verify(next, times(2)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
292         assertThat(mCaptor1.getAllValues().get(0), isMouseEvent);
293         assertThat(mCaptor1.getAllValues().get(1), mIsLineStart);
294     }
295 
296     @Test
testInjectEvents_withRealGestureFinished_shouldJustPassInjected()297     public void testInjectEvents_withRealGestureFinished_shouldJustPassInjected() {
298         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
299         mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0);
300         mMotionEventInjector.onMotionEvent(mClickUpEvent, mClickUpEvent, 0);
301 
302         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
303         verify(next, times(2)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
304         assertThat(mCaptor1.getAllValues().get(0), mIsClickDown);
305         assertThat(mCaptor1.getAllValues().get(1), mIsClickUp);
306         reset(next);
307 
308         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
309         verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(),
310                 eq(FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY));
311         verify(next).onMotionEvent(
312                 argThat(mIsLineStart), argThat(mIsLineStart),
313                 eq(FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY));
314     }
315 
316     @Test
testOnMotionEvents_openInjectedGestureInProgress_shouldCancelAndNotifyAndPassReal()317     public void testOnMotionEvents_openInjectedGestureInProgress_shouldCancelAndNotifyAndPassReal()
318             throws RemoteException {
319         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
320         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
321         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
322         mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0);
323 
324         verify(next, times(3)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
325         assertThat(mCaptor1.getAllValues().get(0), mIsLineStart);
326         assertThat(mCaptor1.getAllValues().get(1), IS_ACTION_CANCEL);
327         assertThat(mCaptor1.getAllValues().get(2), mIsClickDown);
328         verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, false);
329     }
330 
331     @Test
332     public void
testOnMotionEvents_fromMouseWithInjectedGestureInProgress_shouldNotCancelAndPassReal()333             testOnMotionEvents_fromMouseWithInjectedGestureInProgress_shouldNotCancelAndPassReal()
334             throws RemoteException {
335         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
336         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
337         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
338         mMotionEventInjector.onMotionEvent(mHoverMoveEvent, mHoverMoveEvent, 0);
339         mMessageCapturingHandler.sendAllMessages();
340 
341         verify(next, times(3)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
342         assertThat(mCaptor1.getAllValues().get(0), mIsLineStart);
343         assertThat(mCaptor1.getAllValues().get(1), mIsLineMiddle);
344         assertThat(mCaptor1.getAllValues().get(2), mIsLineEnd);
345         verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, true);
346     }
347 
348     @Test
testOnMotionEvents_closedInjectedGestureInProgress_shouldOnlyNotifyAndPassReal()349     public void testOnMotionEvents_closedInjectedGestureInProgress_shouldOnlyNotifyAndPassReal()
350             throws RemoteException {
351         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
352         // Tack a click down to the end of the line
353         TouchPoint clickTouchPoint = new TouchPoint();
354         clickTouchPoint.mIsStartOfPath = true;
355         clickTouchPoint.mX = CLICK_POINT.x;
356         clickTouchPoint.mY = CLICK_POINT.y;
357         mLineList.add(new GestureStep(0, 1, new TouchPoint[] {clickTouchPoint}));
358 
359         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
360 
361         // Send 3 motion events, leaving the extra down in the queue
362         mMessageCapturingHandler.sendOneMessage();
363         mMessageCapturingHandler.sendOneMessage();
364         mMessageCapturingHandler.sendOneMessage();
365 
366         mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0);
367 
368         verify(next, times(4)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
369         assertThat(mCaptor1.getAllValues().get(0), mIsLineStart);
370         assertThat(mCaptor1.getAllValues().get(1), mIsLineMiddle);
371         assertThat(mCaptor1.getAllValues().get(2), mIsLineEnd);
372         assertThat(mCaptor1.getAllValues().get(3), mIsClickDown);
373         verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, false);
374         assertFalse(mMessageCapturingHandler.hasMessages());
375     }
376 
377     @Test
testInjectEvents_openInjectedGestureInProgress_shouldCancelAndNotifyAndPassNew()378     public void testInjectEvents_openInjectedGestureInProgress_shouldCancelAndNotifyAndPassNew()
379             throws RemoteException {
380         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
381         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
382         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
383 
384         injectEventsSync(mClickList, mServiceInterface, CLICK_SEQUENCE);
385         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
386 
387         verify(mServiceInterface, times(1)).onPerformGestureResult(LINE_SEQUENCE, false);
388         verify(next, times(3)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
389         assertThat(mCaptor1.getAllValues().get(0), mIsLineStart);
390         assertThat(mCaptor1.getAllValues().get(1), IS_ACTION_CANCEL);
391         assertThat(mCaptor1.getAllValues().get(2), mIsClickDown);
392     }
393 
394     @Test
testInjectEvents_closedInjectedGestureInProgress_shouldOnlyNotifyAndPassNew()395     public void testInjectEvents_closedInjectedGestureInProgress_shouldOnlyNotifyAndPassNew()
396             throws RemoteException {
397         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
398         // Tack a click down to the end of the line
399         TouchPoint clickTouchPoint = new TouchPoint();
400         clickTouchPoint.mIsStartOfPath = true;
401         clickTouchPoint.mX = CLICK_POINT.x;
402         clickTouchPoint.mY = CLICK_POINT.y;
403         mLineList.add(new GestureStep(0, 1, new TouchPoint[] {clickTouchPoint}));
404         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
405 
406         // Send 3 motion events, leaving newEvent in the queue
407         mMessageCapturingHandler.sendOneMessage();
408         mMessageCapturingHandler.sendOneMessage();
409         mMessageCapturingHandler.sendOneMessage();
410 
411         injectEventsSync(mClickList, mServiceInterface, CLICK_SEQUENCE);
412         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
413 
414         verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, false);
415         verify(next, times(4)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
416         assertThat(mCaptor1.getAllValues().get(0), mIsLineStart);
417         assertThat(mCaptor1.getAllValues().get(1), mIsLineMiddle);
418         assertThat(mCaptor1.getAllValues().get(2), mIsLineEnd);
419         assertThat(mCaptor1.getAllValues().get(3), mIsClickDown);
420     }
421 
422     @Test
testContinuedGesture_continuationArrivesAfterDispatched_gestureCompletes()423     public void testContinuedGesture_continuationArrivesAfterDispatched_gestureCompletes()
424             throws Exception {
425         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
426         injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1);
427         mMessageCapturingHandler.sendAllMessages(); // Send all motion events
428         verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, true);
429         injectEventsSync(mContinuedLineList2, mServiceInterface, CONTINUED_LINE_SEQUENCE_2);
430         mMessageCapturingHandler.sendAllMessages(); // Send all motion events
431         verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_2, true);
432         verify(next, times(5)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
433         List<MotionEvent> events = mCaptor1.getAllValues();
434         long downTime = events.get(0).getDownTime();
435         assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN,
436                 hasEventTime(downTime)));
437         assertThat(events, everyItem(hasDownTime(downTime)));
438         assertThat(events.get(1), allOf(isAtPoint(CONTINUED_LINE_MID1), IS_ACTION_MOVE,
439                 hasEventTime(downTime + CONTINUED_LINE_INTERVAL)));
440         // Timing will restart when the gesture continues
441         long secondSequenceStart = events.get(2).getEventTime();
442         assertTrue(secondSequenceStart >= events.get(1).getEventTime());
443         assertThat(events.get(2), allOf(isAtPoint(CONTINUED_LINE_MID2), IS_ACTION_MOVE));
444         assertThat(events.get(3), allOf(isAtPoint(CONTINUED_LINE_END), IS_ACTION_MOVE,
445                 hasEventTime(secondSequenceStart + CONTINUED_LINE_INTERVAL)));
446         assertThat(events.get(4), allOf(isAtPoint(CONTINUED_LINE_END), IS_ACTION_UP,
447                 hasEventTime(secondSequenceStart + CONTINUED_LINE_INTERVAL)));
448     }
449 
450     @Test
testContinuedGesture_withTwoTouchPoints_gestureCompletes()451     public void testContinuedGesture_withTwoTouchPoints_gestureCompletes()
452             throws Exception {
453         // Run one point through the continued line backwards
454         int backLineId1 = 30;
455         int backLineId2 = 30;
456         List<GestureStep> continuedBackLineList1 = createSimpleGestureFromPoints(backLineId1, 0,
457                 true, CONTINUED_LINE_INTERVAL, CONTINUED_LINE_END, CONTINUED_LINE_MID2);
458         List<GestureStep> continuedBackLineList2 = createSimpleGestureFromPoints(backLineId2,
459                 backLineId1, false, CONTINUED_LINE_INTERVAL, CONTINUED_LINE_MID2,
460                 CONTINUED_LINE_MID1, CONTINUED_LINE_START);
461         List<GestureStep> combinedLines1 = combineGestureSteps(
462                 mContinuedLineList1, continuedBackLineList1);
463         List<GestureStep> combinedLines2 = combineGestureSteps(
464                 mContinuedLineList2, continuedBackLineList2);
465 
466         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
467         injectEventsSync(combinedLines1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1);
468         injectEventsSync(combinedLines2, mServiceInterface, CONTINUED_LINE_SEQUENCE_2);
469         mMessageCapturingHandler.sendAllMessages(); // Send all motion events
470         verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, true);
471         verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_2, true);
472         verify(next, times(7)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
473         List<MotionEvent> events = mCaptor1.getAllValues();
474         long downTime = events.get(0).getDownTime();
475         assertThat(events.get(0), allOf(
476                 anyOf(isAtPoint(CONTINUED_LINE_END), isAtPoint(CONTINUED_LINE_START)),
477                 IS_ACTION_DOWN, hasEventTime(downTime)));
478         assertThat(events, everyItem(hasDownTime(downTime)));
479         assertThat(events.get(1), allOf(containsPoints(CONTINUED_LINE_START, CONTINUED_LINE_END),
480                 IS_ACTION_POINTER_DOWN, hasEventTime(downTime)));
481         assertThat(events.get(2), allOf(containsPoints(CONTINUED_LINE_MID1, CONTINUED_LINE_MID2),
482                 IS_ACTION_MOVE, hasEventTime(downTime + CONTINUED_LINE_INTERVAL)));
483         assertThat(events.get(3), allOf(containsPoints(CONTINUED_LINE_MID1, CONTINUED_LINE_MID2),
484                 IS_ACTION_MOVE, hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 2)));
485         assertThat(events.get(4), allOf(containsPoints(CONTINUED_LINE_START, CONTINUED_LINE_END),
486                 IS_ACTION_MOVE, hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 3)));
487         assertThat(events.get(5), allOf(containsPoints(CONTINUED_LINE_START, CONTINUED_LINE_END),
488                 IS_ACTION_POINTER_UP, hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 3)));
489         assertThat(events.get(6), allOf(
490                 anyOf(isAtPoint(CONTINUED_LINE_END), isAtPoint(CONTINUED_LINE_START)),
491                 IS_ACTION_UP, hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 3)));
492     }
493 
494 
495     @Test
testContinuedGesture_continuationArrivesWhileDispatching_gestureCompletes()496     public void testContinuedGesture_continuationArrivesWhileDispatching_gestureCompletes()
497             throws Exception {
498         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
499         injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1);
500         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
501         injectEventsSync(mContinuedLineList2, mServiceInterface, CONTINUED_LINE_SEQUENCE_2);
502         mMessageCapturingHandler.sendAllMessages(); // Send all motion events
503         verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, true);
504         verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_2, true);
505         verify(next, times(5)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
506         List<MotionEvent> events = mCaptor1.getAllValues();
507         long downTime = events.get(0).getDownTime();
508         assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN,
509                 hasEventTime(downTime)));
510         assertThat(events, everyItem(hasDownTime(downTime)));
511         assertThat(events.get(1), allOf(isAtPoint(CONTINUED_LINE_MID1), IS_ACTION_MOVE,
512                 hasEventTime(downTime + CONTINUED_LINE_INTERVAL)));
513         assertThat(events.get(2), allOf(isAtPoint(CONTINUED_LINE_MID2), IS_ACTION_MOVE,
514                 hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 2)));
515         assertThat(events.get(3), allOf(isAtPoint(CONTINUED_LINE_END), IS_ACTION_MOVE,
516                 hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 3)));
517         assertThat(events.get(4), allOf(isAtPoint(CONTINUED_LINE_END), IS_ACTION_UP,
518                 hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 3)));
519     }
520 
521     @Test
testContinuedGesture_twoContinuationsArriveWhileDispatching_gestureCompletes()522     public void testContinuedGesture_twoContinuationsArriveWhileDispatching_gestureCompletes()
523             throws Exception {
524         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
525         injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1);
526         // Continue line again
527         List<GestureStep> continuedLineList2 = createSimpleGestureFromPoints(
528                 CONTINUED_LINE_STROKE_ID_2, CONTINUED_LINE_STROKE_ID_1, true,
529                 CONTINUED_LINE_INTERVAL, CONTINUED_LINE_MID1,
530                 CONTINUED_LINE_MID2, CONTINUED_LINE_END);
531         // Finish line by backtracking
532         int strokeId3 = CONTINUED_LINE_STROKE_ID_2 + 1;
533         int sequence3 = CONTINUED_LINE_SEQUENCE_2 + 1;
534         List<GestureStep> continuedLineList3 = createSimpleGestureFromPoints(strokeId3,
535                 CONTINUED_LINE_STROKE_ID_2, false, CONTINUED_LINE_INTERVAL, CONTINUED_LINE_END,
536                 CONTINUED_LINE_MID2);
537 
538         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
539         injectEventsSync(continuedLineList2, mServiceInterface, CONTINUED_LINE_SEQUENCE_2);
540         injectEventsSync(continuedLineList3, mServiceInterface, sequence3);
541         mMessageCapturingHandler.sendAllMessages(); // Send all motion events
542         verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, true);
543         verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_2, true);
544         verify(mServiceInterface).onPerformGestureResult(sequence3, true);
545         verify(next, times(6)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
546         List<MotionEvent> events = mCaptor1.getAllValues();
547         long downTime = events.get(0).getDownTime();
548         assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN,
549                 hasEventTime(downTime)));
550         assertThat(events, everyItem(hasDownTime(downTime)));
551         assertThat(events.get(1), allOf(isAtPoint(CONTINUED_LINE_MID1), IS_ACTION_MOVE,
552                 hasEventTime(downTime + CONTINUED_LINE_INTERVAL)));
553         assertThat(events.get(2), allOf(isAtPoint(CONTINUED_LINE_MID2), IS_ACTION_MOVE,
554                 hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 2)));
555         assertThat(events.get(3), allOf(isAtPoint(CONTINUED_LINE_END), IS_ACTION_MOVE,
556                 hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 3)));
557         assertThat(events.get(4), allOf(isAtPoint(CONTINUED_LINE_MID2), IS_ACTION_MOVE,
558                 hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 4)));
559         assertThat(events.get(5), allOf(isAtPoint(CONTINUED_LINE_MID2), IS_ACTION_UP,
560                 hasEventTime(downTime + CONTINUED_LINE_INTERVAL * 4)));
561     }
562 
563     @Test
testContinuedGesture_nonContinuingGestureArrivesDuringDispatch_gestureCanceled()564     public void testContinuedGesture_nonContinuingGestureArrivesDuringDispatch_gestureCanceled()
565             throws Exception {
566         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
567         injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1);
568         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
569         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
570         mMessageCapturingHandler.sendAllMessages(); // Send all motion events
571         verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, false);
572         verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, true);
573         verify(next, times(5)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
574         List<MotionEvent> events = mCaptor1.getAllValues();
575         assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN));
576         assertThat(events.get(1), IS_ACTION_CANCEL);
577         assertThat(events.get(2), allOf(isAtPoint(LINE_START), IS_ACTION_DOWN));
578         assertThat(events.get(3), allOf(isAtPoint(LINE_END), IS_ACTION_MOVE));
579         assertThat(events.get(4), allOf(isAtPoint(LINE_END), IS_ACTION_UP));
580     }
581 
582     @Test
testContinuedGesture_nonContinuingGestureArrivesAfterDispatch_gestureCanceled()583     public void testContinuedGesture_nonContinuingGestureArrivesAfterDispatch_gestureCanceled()
584             throws Exception {
585         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
586         injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1);
587         mMessageCapturingHandler.sendAllMessages(); // Send all motion events
588         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
589         mMessageCapturingHandler.sendAllMessages(); // Send all motion events
590         verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, true);
591         verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, true);
592         verify(next, times(6)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
593         List<MotionEvent> events = mCaptor1.getAllValues();
594         assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN));
595         assertThat(events.get(1), allOf(isAtPoint(CONTINUED_LINE_MID1), IS_ACTION_MOVE));
596         assertThat(events.get(2), IS_ACTION_CANCEL);
597         assertThat(events.get(3), allOf(isAtPoint(LINE_START), IS_ACTION_DOWN));
598         assertThat(events.get(4), allOf(isAtPoint(LINE_END), IS_ACTION_MOVE));
599         assertThat(events.get(5), allOf(isAtPoint(LINE_END), IS_ACTION_UP));
600     }
601 
602     @Test
testContinuedGesture_misMatchedContinuationArrives_bothGesturesCanceled()603     public void testContinuedGesture_misMatchedContinuationArrives_bothGesturesCanceled()
604             throws Exception {
605         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
606         injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1);
607         mMessageCapturingHandler.sendAllMessages(); // Send all motion events
608         verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, true);
609         List<GestureStep> discontinuousGesture = mContinuedLineList2
610                 .subList(1, mContinuedLineList2.size());
611         injectEventsSync(discontinuousGesture, mServiceInterface, CONTINUED_LINE_SEQUENCE_2);
612         mMessageCapturingHandler.sendAllMessages(); // Send all motion events
613         verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_2, false);
614         verify(next, times(3)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
615         List<MotionEvent> events = mCaptor1.getAllValues();
616         assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN));
617         assertThat(events.get(1), allOf(isAtPoint(CONTINUED_LINE_MID1), IS_ACTION_MOVE));
618         assertThat(events.get(2), allOf(isAtPoint(CONTINUED_LINE_MID1), IS_ACTION_CANCEL));
619     }
620 
621     @Test
testContinuedGesture_continuationArrivesFromOtherService_bothGesturesCanceled()622     public void testContinuedGesture_continuationArrivesFromOtherService_bothGesturesCanceled()
623             throws Exception {
624         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
625         IAccessibilityServiceClient otherService = mock(IAccessibilityServiceClient.class);
626         injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1);
627         mMessageCapturingHandler.sendOneMessage(); // Send a motion events
628         injectEventsSync(mContinuedLineList2, otherService, CONTINUED_LINE_SEQUENCE_2);
629         mMessageCapturingHandler.sendAllMessages(); // Send all motion events
630         verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, false);
631         verify(otherService).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_2, false);
632         verify(next, times(2)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
633         List<MotionEvent> events = mCaptor1.getAllValues();
634         assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN));
635         assertThat(events.get(1), IS_ACTION_CANCEL);
636     }
637 
638     @Test
testContinuedGesture_realGestureArrivesInBetween_getsCanceled()639     public void testContinuedGesture_realGestureArrivesInBetween_getsCanceled()
640             throws Exception {
641         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
642         injectEventsSync(mContinuedLineList1, mServiceInterface, CONTINUED_LINE_SEQUENCE_1);
643         mMessageCapturingHandler.sendAllMessages(); // Send all motion events
644         verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_1, true);
645 
646         mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0);
647 
648         injectEventsSync(mContinuedLineList2, mServiceInterface, CONTINUED_LINE_SEQUENCE_2);
649         mMessageCapturingHandler.sendAllMessages(); // Send all motion events
650         verify(mServiceInterface).onPerformGestureResult(CONTINUED_LINE_SEQUENCE_2, false);
651         verify(next, times(4)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
652         List<MotionEvent> events = mCaptor1.getAllValues();
653         assertThat(events.get(0), allOf(isAtPoint(CONTINUED_LINE_START), IS_ACTION_DOWN));
654         assertThat(events.get(1), allOf(isAtPoint(CONTINUED_LINE_MID1), IS_ACTION_MOVE));
655         assertThat(events.get(2), IS_ACTION_CANCEL);
656         assertThat(events.get(3), allOf(isAtPoint(CLICK_POINT), IS_ACTION_DOWN));
657     }
658 
659     @Test
testClearEvents_realGestureInProgress_shouldForgetAboutGesture()660     public void testClearEvents_realGestureInProgress_shouldForgetAboutGesture() {
661         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
662         mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0);
663         mMotionEventInjector.clearEvents(MOTION_EVENT_SOURCE);
664         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
665         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
666 
667         verify(next, times(2)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
668         assertThat(mCaptor1.getAllValues().get(0), mIsClickDown);
669         assertThat(mCaptor1.getAllValues().get(1), mIsLineStart);
670     }
671 
672     @Test
testClearEventsOnOtherSource_realGestureInProgress_shouldNotForgetAboutGesture()673     public void testClearEventsOnOtherSource_realGestureInProgress_shouldNotForgetAboutGesture() {
674         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
675         mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0);
676         mMotionEventInjector.clearEvents(OTHER_EVENT_SOURCE);
677         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
678         mMessageCapturingHandler.sendOneMessage(); // Send a motion event
679 
680         verify(next, times(3)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt());
681         assertThat(mCaptor1.getAllValues().get(0), mIsClickDown);
682         assertThat(mCaptor1.getAllValues().get(1), IS_ACTION_CANCEL);
683         assertThat(mCaptor1.getAllValues().get(2), mIsLineStart);
684     }
685 
686     @Test
testOnDestroy_shouldCancelGestures()687     public void testOnDestroy_shouldCancelGestures() throws RemoteException {
688         mMotionEventInjector.onDestroy();
689         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
690         verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, false);
691     }
692 
693     @Test
testInjectEvents_withNoNext_shouldCancel()694     public void testInjectEvents_withNoNext_shouldCancel() throws RemoteException {
695         injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
696         verify(mServiceInterface).onPerformGestureResult(LINE_SEQUENCE, false);
697     }
698 
699     @Test
testOnMotionEvent_withNoNext_shouldNotCrash()700     public void testOnMotionEvent_withNoNext_shouldNotCrash() {
701         mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0);
702     }
703 
704     @Test
testOnKeyEvent_shouldPassToNext()705     public void testOnKeyEvent_shouldPassToNext() {
706         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
707         KeyEvent event = new KeyEvent(0, 0);
708         mMotionEventInjector.onKeyEvent(event, 0);
709         verify(next).onKeyEvent(event, 0);
710     }
711 
712     @Test
testOnKeyEvent_withNoNext_shouldNotCrash()713     public void testOnKeyEvent_withNoNext_shouldNotCrash() {
714         KeyEvent event = new KeyEvent(0, 0);
715         mMotionEventInjector.onKeyEvent(event, 0);
716     }
717 
718     @Test
testOnAccessibilityEvent_shouldPassToNext()719     public void testOnAccessibilityEvent_shouldPassToNext() {
720         EventStreamTransformation next = attachMockNext(mMotionEventInjector);
721         AccessibilityEvent event = AccessibilityEvent.obtain();
722         mMotionEventInjector.onAccessibilityEvent(event);
723         verify(next).onAccessibilityEvent(event);
724     }
725 
726     @Test
testOnAccessibilityEvent_withNoNext_shouldNotCrash()727     public void testOnAccessibilityEvent_withNoNext_shouldNotCrash() {
728         AccessibilityEvent event = AccessibilityEvent.obtain();
729         mMotionEventInjector.onAccessibilityEvent(event);
730     }
731 
injectEventsSync(List<GestureStep> gestureSteps, IAccessibilityServiceClient serviceInterface, int sequence)732     private void injectEventsSync(List<GestureStep> gestureSteps,
733             IAccessibilityServiceClient serviceInterface, int sequence) {
734         mMotionEventInjector.injectEvents(gestureSteps, serviceInterface, sequence,
735                 Display.DEFAULT_DISPLAY);
736         // Dispatch the message sent by the injector. Our simple handler doesn't guarantee stuff
737         // happens in order.
738         mMessageCapturingHandler.sendLastMessage();
739     }
740 
createSimpleGestureFromPoints(int strokeId, int continuedStrokeId, boolean continued, long interval, Point... points)741     private List<GestureStep> createSimpleGestureFromPoints(int strokeId, int continuedStrokeId,
742             boolean continued, long interval, Point... points) {
743         List<GestureStep> gesture = new ArrayList<>(points.length);
744         TouchPoint[] touchPoints = new TouchPoint[1];
745         touchPoints[0] = new TouchPoint();
746         for (int i = 0; i < points.length; i++) {
747             touchPoints[0].mX = points[i].x;
748             touchPoints[0].mY = points[i].y;
749             touchPoints[0].mIsStartOfPath = ((i == 0) && (continuedStrokeId <= 0));
750             touchPoints[0].mContinuedStrokeId = continuedStrokeId;
751             touchPoints[0].mStrokeId = strokeId;
752             touchPoints[0].mIsEndOfPath = ((i == points.length - 1) && !continued);
753             gesture.add(new GestureStep(interval * i, 1, touchPoints));
754         }
755         return gesture;
756     }
757 
combineGestureSteps(List<GestureStep> list1, List<GestureStep> list2)758     List<GestureStep> combineGestureSteps(List<GestureStep> list1, List<GestureStep> list2) {
759         assertEquals(list1.size(), list2.size());
760         List<GestureStep> gesture = new ArrayList<>(list1.size());
761         for (int i = 0; i < list1.size(); i++) {
762             int numPoints1 = list1.get(i).numTouchPoints;
763             int numPoints2 = list2.get(i).numTouchPoints;
764             TouchPoint[] touchPoints = new TouchPoint[numPoints1 + numPoints2];
765             for (int j = 0; j < numPoints1; j++) {
766                 touchPoints[j] = new TouchPoint();
767                 touchPoints[j].copyFrom(list1.get(i).touchPoints[j]);
768             }
769             for (int j = 0; j < numPoints2; j++) {
770                 touchPoints[numPoints1 + j] = new TouchPoint();
771                 touchPoints[numPoints1 + j].copyFrom(list2.get(i).touchPoints[j]);
772             }
773             gesture.add(new GestureStep(list1.get(i).timeSinceGestureStart,
774                     numPoints1 + numPoints2, touchPoints));
775         }
776         return gesture;
777     }
778 
attachMockNext(MotionEventInjector motionEventInjector)779     private EventStreamTransformation attachMockNext(MotionEventInjector motionEventInjector) {
780         EventStreamTransformation next = mock(EventStreamTransformation.class);
781         motionEventInjector.setNext(next);
782         return next;
783     }
784 
785     private static class MotionEventActionMatcher extends TypeSafeMatcher<MotionEvent> {
786         int mAction;
787 
MotionEventActionMatcher(int action)788         MotionEventActionMatcher(int action) {
789             super();
790             mAction = action;
791         }
792 
793         @Override
matchesSafely(MotionEvent motionEvent)794         protected boolean matchesSafely(MotionEvent motionEvent) {
795             return motionEvent.getActionMasked() == mAction;
796         }
797 
798         @Override
describeTo(Description description)799         public void describeTo(Description description) {
800             description.appendText("Matching to action " + mAction);
801         }
802     }
803 
isAtPoint(final Point point)804     private static TypeSafeMatcher<MotionEvent> isAtPoint(final Point point) {
805         return new TypeSafeMatcher<MotionEvent>() {
806             @Override
807             protected boolean matchesSafely(MotionEvent event) {
808                 return ((event.getX() == point.x) && (event.getY() == point.y));
809             }
810 
811             @Override
812             public void describeTo(Description description) {
813                 description.appendText("Is at point " + point);
814             }
815         };
816     }
817 
818     private static TypeSafeMatcher<MotionEvent> containsPoints(final Point... points) {
819         return new TypeSafeMatcher<MotionEvent>() {
820             @Override
821             protected boolean matchesSafely(MotionEvent event) {
822                 MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
823                 for (int i = 0; i < points.length; i++) {
824                     boolean havePoint = false;
825                     for (int j = 0; j < points.length; j++) {
826                         event.getPointerCoords(j, coords);
827                         if ((points[i].x == coords.x) && (points[i].y == coords.y)) {
828                             havePoint = true;
829                         }
830                     }
831                     if (!havePoint) {
832                         return false;
833                     }
834                 }
835                 return true;
836             }
837 
838             @Override
839             public void describeTo(Description description) {
840                 description.appendText("Contains points " + Arrays.toString(points));
841             }
842         };
843     }
844 
845     private static TypeSafeMatcher<MotionEvent> hasDownTime(final long downTime) {
846         return new TypeSafeMatcher<MotionEvent>() {
847             @Override
848             protected boolean matchesSafely(MotionEvent event) {
849                 return event.getDownTime() == downTime;
850             }
851 
852             @Override
853             public void describeTo(Description description) {
854                 description.appendText("Down time = " + downTime);
855             }
856         };
857     }
858 
859     private static TypeSafeMatcher<MotionEvent> hasEventTime(final long eventTime) {
860         return new TypeSafeMatcher<MotionEvent>() {
861             @Override
862             protected boolean matchesSafely(MotionEvent event) {
863                 return event.getEventTime() == eventTime;
864             }
865 
866             @Override
867             public void describeTo(Description description) {
868                 description.appendText("Event time = " + eventTime);
869             }
870         };
871     }
872 
873     private static TypeSafeMatcher<MotionEvent> hasTimeFromDown(final long timeFromDown) {
874         return new TypeSafeMatcher<MotionEvent>() {
875             @Override
876             protected boolean matchesSafely(MotionEvent event) {
877                 return (event.getEventTime() - event.getDownTime()) == timeFromDown;
878             }
879 
880             @Override
881             public void describeTo(Description description) {
882                 description.appendText("Time from down to event times = " + timeFromDown);
883             }
884         };
885     }
886 
887     private static TypeSafeMatcher<MotionEvent> hasStandardInitialization() {
888         return new TypeSafeMatcher<MotionEvent>() {
889             @Override
890             protected boolean matchesSafely(MotionEvent event) {
891                 return (0 == event.getActionIndex()) && (VIRTUAL_KEYBOARD == event.getDeviceId())
892                         && (EDGEFLAGS == event.getEdgeFlags()) && (0 == event.getFlags())
893                         && (METASTATE == event.getMetaState()) && (0F == event.getOrientation())
894                         && (0F == event.getTouchMajor()) && (0F == event.getTouchMinor())
895                         && (X_PRECISION == event.getXPrecision())
896                         && (Y_PRECISION == event.getYPrecision())
897                         && (POINTER_SIZE == event.getSize())
898                         && (1 == event.getPointerCount()) && (PRESSURE == event.getPressure())
899                         && (InputDevice.SOURCE_TOUCHSCREEN == event.getSource());
900             }
901 
902             @Override
903             public void describeTo(Description description) {
904                 description.appendText("Has standard values for all parameters");
905             }
906         };
907     }
908 }
909