MotionEventInjectorTest.java revision a7dcedc65fbdae3625f25e1a40756d251a5a586b
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 17package com.android.server.accessibility; 18 19import static org.junit.Assert.assertEquals; 20import static org.junit.Assert.assertFalse; 21import static org.junit.Assert.assertTrue; 22import static org.mockito.Matchers.anyInt; 23import static org.mockito.Matchers.argThat; 24import static org.mockito.Matchers.eq; 25import static org.mockito.Mockito.mock; 26import static org.mockito.Mockito.reset; 27import static org.mockito.Mockito.times; 28import static org.mockito.Mockito.verify; 29import static org.mockito.Mockito.verifyNoMoreInteractions; 30import static org.mockito.Mockito.verifyZeroInteractions; 31 32import android.accessibilityservice.IAccessibilityServiceClient; 33import android.os.Handler; 34import android.os.Looper; 35import android.os.Message; 36import android.os.RemoteException; 37import android.support.test.runner.AndroidJUnit4; 38import android.util.Log; 39import android.util.Pair; 40import android.view.InputDevice; 41import android.view.KeyEvent; 42import android.view.MotionEvent; 43import android.view.WindowManagerPolicy; 44import java.util.ArrayList; 45import java.util.List; 46 47import android.view.accessibility.AccessibilityEvent; 48import org.junit.Before; 49import org.junit.BeforeClass; 50import org.junit.Test; 51import org.junit.runner.RunWith; 52import org.mockito.ArgumentCaptor; 53import org.mockito.ArgumentMatcher; 54 55/** 56 * Tests for MotionEventInjector 57 */ 58@RunWith(AndroidJUnit4.class) 59public class MotionEventInjectorTest { 60 private static final String LOG_TAG = "MotionEventInjectorTest"; 61 private static final int CLICK_X = 100; 62 private static final int CLICK_Y_START = 200; 63 private static final int CLICK_Y_END = 201; 64 private static final int CLICK_DURATION = 10; 65 private static final int SEQUENCE = 50; 66 67 private static final int SECOND_CLICK_X = 1000; 68 private static final int SECOND_CLICK_Y = 2000; 69 private static final int SECOND_SEQUENCE = 51; 70 71 private static final int MOTION_EVENT_SOURCE = InputDevice.SOURCE_TOUCHSCREEN; 72 private static final int OTHER_EVENT_SOURCE = InputDevice.SOURCE_MOUSE; 73 74 MotionEventInjector mMotionEventInjector; 75 IAccessibilityServiceClient mServiceInterface; 76 List<MotionEvent> mClickList = new ArrayList<>(); 77 List<MotionEvent> mSecondClickList = new ArrayList<>(); 78 ArgumentCaptor<MotionEvent> mCaptor1 = ArgumentCaptor.forClass(MotionEvent.class); 79 ArgumentCaptor<MotionEvent> mCaptor2 = ArgumentCaptor.forClass(MotionEvent.class); 80 MessageCapturingHandler mMessageCapturingHandler; 81 MotionEventMatcher mClickEvent0Matcher; 82 MotionEventMatcher mClickEvent1Matcher; 83 MotionEventMatcher mClickEvent2Matcher; 84 MotionEventMatcher mSecondClickEvent0Matcher; 85 86 @BeforeClass 87 public static void oneTimeInitialization() { 88 if (Looper.myLooper() == null) { 89 Looper.prepare(); 90 } 91 } 92 93 @Before 94 public void setUp() { 95 mMessageCapturingHandler = new MessageCapturingHandler(); 96 mMotionEventInjector = new MotionEventInjector(mMessageCapturingHandler); 97 mClickList.add( 98 MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, CLICK_X, CLICK_Y_START, 0)); 99 mClickList.add(MotionEvent.obtain( 100 0, CLICK_DURATION, MotionEvent.ACTION_MOVE, CLICK_X, CLICK_Y_END, 0)); 101 mClickList.add(MotionEvent.obtain( 102 0, CLICK_DURATION, MotionEvent.ACTION_UP, CLICK_X, CLICK_Y_END, 0)); 103 for (int i = 0; i < mClickList.size(); i++) { 104 mClickList.get(i).setSource(MOTION_EVENT_SOURCE); 105 } 106 107 mClickEvent0Matcher = new MotionEventMatcher(mClickList.get(0)); 108 mClickEvent1Matcher = new MotionEventMatcher(mClickList.get(1)); 109 mClickEvent2Matcher = new MotionEventMatcher(mClickList.get(2)); 110 111 mSecondClickList.add(MotionEvent.obtain( 112 0, 0, MotionEvent.ACTION_DOWN, SECOND_CLICK_X, SECOND_CLICK_Y, 0)); 113 mSecondClickList.add(MotionEvent.obtain( 114 0, CLICK_DURATION, MotionEvent.ACTION_MOVE, SECOND_CLICK_X, CLICK_Y_END, 0)); 115 mSecondClickList.add(MotionEvent.obtain( 116 0, CLICK_DURATION, MotionEvent.ACTION_UP, SECOND_CLICK_X, CLICK_Y_END, 0)); 117 for (int i = 0; i < mSecondClickList.size(); i++) { 118 mSecondClickList.get(i).setSource(MOTION_EVENT_SOURCE); 119 } 120 121 mSecondClickEvent0Matcher = new MotionEventMatcher(mSecondClickList.get(0)); 122 123 mServiceInterface = mock(IAccessibilityServiceClient.class); 124 } 125 126 @Test 127 public void testInjectEvents_shouldEmergeInOrderWithCorrectTiming() throws RemoteException { 128 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 129 mMotionEventInjector.injectEvents(mClickList, mServiceInterface, SEQUENCE); 130 mMessageCapturingHandler.sendOneMessage(); // Process the event injection 131 verifyNoMoreInteractions(next); 132 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 133 134 verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), 135 eq(WindowManagerPolicy.FLAG_PASS_TO_USER)); 136 long gestureStart = mCaptor1.getValue().getDownTime(); 137 mClickEvent0Matcher.offsetTimesBy(gestureStart); 138 mClickEvent1Matcher.offsetTimesBy(gestureStart); 139 mClickEvent2Matcher.offsetTimesBy(gestureStart); 140 141 verify(next).onMotionEvent(argThat(mClickEvent0Matcher), argThat(mClickEvent0Matcher), 142 eq(WindowManagerPolicy.FLAG_PASS_TO_USER)); 143 verifyNoMoreInteractions(next); 144 reset(next); 145 146 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 147 verify(next).onMotionEvent(argThat(mClickEvent1Matcher), argThat(mClickEvent1Matcher), 148 eq(WindowManagerPolicy.FLAG_PASS_TO_USER)); 149 verifyNoMoreInteractions(next); 150 reset(next); 151 152 verifyZeroInteractions(mServiceInterface); 153 154 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 155 verify(next).onMotionEvent(argThat(mClickEvent2Matcher), argThat(mClickEvent2Matcher), 156 eq(WindowManagerPolicy.FLAG_PASS_TO_USER)); 157 verifyNoMoreInteractions(next); 158 reset(next); 159 160 verify(mServiceInterface).onPerformGestureResult(SEQUENCE, true); 161 verifyNoMoreInteractions(mServiceInterface); 162 } 163 164 @Test 165 public void testInjectEvents_eventWithManyPointers_shouldNotCrash() { 166 int manyPointersCount = 20; 167 MotionEvent.PointerCoords[] pointerCoords = 168 new MotionEvent.PointerCoords[manyPointersCount]; 169 MotionEvent.PointerProperties[] pointerProperties = 170 new MotionEvent.PointerProperties[manyPointersCount]; 171 for (int i = 0; i < manyPointersCount; i++) { 172 pointerProperties[i] = new MotionEvent.PointerProperties(); 173 pointerProperties[i].id = i; 174 pointerProperties[i].toolType = MotionEvent.TOOL_TYPE_UNKNOWN; 175 pointerCoords[i] = new MotionEvent.PointerCoords(); 176 pointerCoords[i].clear(); 177 pointerCoords[i].pressure = 1.0f; 178 pointerCoords[i].size = 1.0f; 179 pointerCoords[i].x = i; 180 pointerCoords[i].y = i; 181 } 182 List<MotionEvent> events = new ArrayList<>(); 183 events.add(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, manyPointersCount, 184 pointerProperties, pointerCoords, 0, 0, 185 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0)); 186 events.add(MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, manyPointersCount, 187 pointerProperties, pointerCoords, 0, 0, 188 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0)); 189 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 190 mMotionEventInjector.injectEvents(events, mServiceInterface, SEQUENCE); 191 mMessageCapturingHandler.sendAllMessages(); 192 verify(next, times(2)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 193 assertEquals(MotionEvent.ACTION_DOWN, mCaptor1.getAllValues().get(0).getActionMasked()); 194 assertEquals(MotionEvent.ACTION_UP, mCaptor1.getAllValues().get(1).getActionMasked()); 195 } 196 197 @Test 198 public void testRegularEvent_afterGestureComplete_shouldPassToNext() { 199 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 200 mMotionEventInjector.injectEvents(mClickList, mServiceInterface, SEQUENCE); 201 mMessageCapturingHandler.sendOneMessage(); // Process the event injection 202 mMessageCapturingHandler.sendAllMessages(); // Send all motion events 203 reset(next); 204 mMotionEventInjector.onMotionEvent(mSecondClickList.get(0), mClickList.get(0), 0); 205 verify(next).onMotionEvent(argThat(mSecondClickEvent0Matcher), 206 argThat(mClickEvent0Matcher), eq(0)); 207 } 208 209 @Test 210 public void testInjectEvents_withRealGestureUnderway_shouldCancelRealAndPassInjected() { 211 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 212 mMotionEventInjector.onMotionEvent(mClickList.get(0), mClickList.get(0), 0); 213 mMotionEventInjector.injectEvents(mSecondClickList, mServiceInterface, SEQUENCE); 214 mMessageCapturingHandler.sendOneMessage(); // Process the event injection 215 216 verify(next, times(2)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 217 assertTrue(mClickEvent0Matcher.matches(mCaptor1.getAllValues().get(0))); 218 assertEquals(MotionEvent.ACTION_CANCEL, mCaptor1.getAllValues().get(1).getActionMasked()); 219 reset(next); 220 221 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 222 verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), 223 eq(WindowManagerPolicy.FLAG_PASS_TO_USER)); 224 long gestureStart = mCaptor1.getValue().getDownTime(); 225 mSecondClickEvent0Matcher.offsetTimesBy(gestureStart); 226 227 verify(next).onMotionEvent(argThat(mSecondClickEvent0Matcher), 228 argThat(mSecondClickEvent0Matcher), eq(WindowManagerPolicy.FLAG_PASS_TO_USER)); 229 } 230 231 @Test 232 public void testInjectEvents_withRealMouseGestureUnderway_shouldContinueRealAndPassInjected() { 233 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 234 MotionEvent mouseEvent = MotionEvent.obtain(mClickList.get(0)); 235 mouseEvent.setSource(InputDevice.SOURCE_MOUSE); 236 MotionEventMatcher mouseEventMatcher = new MotionEventMatcher(mouseEvent); 237 mMotionEventInjector.onMotionEvent(mouseEvent, mouseEvent, 0); 238 mMotionEventInjector.injectEvents(mSecondClickList, mServiceInterface, SEQUENCE); 239 mMessageCapturingHandler.sendOneMessage(); // Process the event injection 240 241 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 242 verify(next, times(2)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 243 assertTrue(mouseEventMatcher.matches(mCaptor1.getAllValues().get(0))); 244 mSecondClickEvent0Matcher.offsetTimesBy(mCaptor1.getAllValues().get(1).getDownTime()); 245 assertTrue(mSecondClickEvent0Matcher.matches(mCaptor1.getAllValues().get(1))); 246 } 247 248 @Test 249 public void testInjectEvents_withRealGestureFinished_shouldJustPassInjected() { 250 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 251 mMotionEventInjector.onMotionEvent(mClickList.get(0), mClickList.get(0), 0); 252 mMotionEventInjector.onMotionEvent(mClickList.get(1), mClickList.get(1), 0); 253 mMotionEventInjector.onMotionEvent(mClickList.get(2), mClickList.get(2), 0); 254 255 mMotionEventInjector.injectEvents(mSecondClickList, mServiceInterface, SEQUENCE); 256 mMessageCapturingHandler.sendOneMessage(); // Process the event injection 257 verify(next, times(3)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 258 259 assertTrue(mClickEvent0Matcher.matches(mCaptor1.getAllValues().get(0))); 260 assertTrue(mClickEvent1Matcher.matches(mCaptor1.getAllValues().get(1))); 261 assertTrue(mClickEvent2Matcher.matches(mCaptor1.getAllValues().get(2))); 262 reset(next); 263 264 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 265 verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), 266 eq(WindowManagerPolicy.FLAG_PASS_TO_USER)); 267 mSecondClickEvent0Matcher.offsetTimesBy(mCaptor1.getValue().getDownTime()); 268 verify(next).onMotionEvent(argThat(mSecondClickEvent0Matcher), 269 argThat(mSecondClickEvent0Matcher), eq(WindowManagerPolicy.FLAG_PASS_TO_USER)); 270 } 271 272 @Test 273 public void testOnMotionEvents_openInjectedGestureInProgress_shouldCancelAndNotifyAndPassReal() 274 throws RemoteException { 275 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 276 mMotionEventInjector.injectEvents(mClickList, mServiceInterface, SEQUENCE); 277 mMessageCapturingHandler.sendOneMessage(); // Process the event injection 278 279 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 280 mMotionEventInjector.onMotionEvent(mSecondClickList.get(0), mSecondClickList.get(0), 0); 281 282 verify(next, times(3)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 283 mClickEvent0Matcher.offsetTimesBy(mCaptor1.getAllValues().get(0).getDownTime()); 284 assertTrue(mClickEvent0Matcher.matches(mCaptor1.getAllValues().get(0))); 285 assertEquals(MotionEvent.ACTION_CANCEL, mCaptor1.getAllValues().get(1).getActionMasked()); 286 assertTrue(mSecondClickEvent0Matcher.matches(mCaptor1.getAllValues().get(2))); 287 verify(mServiceInterface).onPerformGestureResult(SEQUENCE, false); 288 } 289 290 @Test 291 public void testOnMotionEvents_closedInjectedGestureInProgress_shouldOnlyNotifyAndPassReal() 292 throws RemoteException { 293 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 294 mClickList.add(MotionEvent.obtain(2 * CLICK_DURATION, 2 * CLICK_DURATION, 295 MotionEvent.ACTION_DOWN, CLICK_X, CLICK_Y_START, 0)); 296 mMotionEventInjector.injectEvents(mClickList, mServiceInterface, SEQUENCE); 297 mMessageCapturingHandler.sendOneMessage(); // Process the event injection 298 299 // Send 3 motion events, leaving the extra down in the queue 300 mMessageCapturingHandler.sendOneMessage(); 301 mMessageCapturingHandler.sendOneMessage(); 302 mMessageCapturingHandler.sendOneMessage(); 303 304 mMotionEventInjector.onMotionEvent(mSecondClickList.get(0), mClickList.get(0), 0); 305 306 verify(next, times(4)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 307 long gestureStart = mCaptor1.getAllValues().get(0).getDownTime(); 308 mClickEvent0Matcher.offsetTimesBy(gestureStart); 309 mClickEvent1Matcher.offsetTimesBy(gestureStart); 310 mClickEvent2Matcher.offsetTimesBy(gestureStart); 311 assertTrue(mClickEvent0Matcher.matches(mCaptor1.getAllValues().get(0))); 312 assertTrue(mClickEvent1Matcher.matches(mCaptor1.getAllValues().get(1))); 313 assertTrue(mClickEvent2Matcher.matches(mCaptor1.getAllValues().get(2))); 314 assertTrue(mSecondClickEvent0Matcher.matches(mCaptor1.getAllValues().get(3))); 315 316 verify(mServiceInterface).onPerformGestureResult(SEQUENCE, false); 317 assertFalse(mMessageCapturingHandler.hasMessages()); 318 } 319 320 @Test 321 public void testInjectEvents_openInjectedGestureInProgress_shouldCancelAndNotifyAndPassNew() 322 throws RemoteException { 323 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 324 mMotionEventInjector.injectEvents(mClickList, mServiceInterface, SEQUENCE); 325 mMessageCapturingHandler.sendOneMessage(); // Process the event injection 326 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 327 328 mMotionEventInjector.injectEvents(mSecondClickList, mServiceInterface, SECOND_SEQUENCE); 329 mMessageCapturingHandler.sendLastMessage(); // Process the second event injection 330 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 331 332 verify(mServiceInterface, times(1)).onPerformGestureResult(SEQUENCE, false); 333 verify(next, times(3)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 334 mClickEvent0Matcher.offsetTimesBy(mCaptor1.getAllValues().get(0).getDownTime()); 335 assertTrue(mClickEvent0Matcher.matches(mCaptor1.getAllValues().get(0))); 336 assertEquals(MotionEvent.ACTION_CANCEL, mCaptor1.getAllValues().get(1).getActionMasked()); 337 mSecondClickEvent0Matcher.offsetTimesBy(mCaptor1.getAllValues().get(2).getDownTime()); 338 assertTrue(mSecondClickEvent0Matcher.matches(mCaptor1.getAllValues().get(2))); 339 } 340 341 @Test 342 public void testInjectEvents_closedInjectedGestureInProgress_shouldOnlyNotifyAndPassNew() 343 throws RemoteException { 344 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 345 MotionEvent newEvent = MotionEvent.obtain(2 * CLICK_DURATION, 2 * CLICK_DURATION, 346 MotionEvent.ACTION_DOWN, CLICK_X, CLICK_Y_START, 0); 347 newEvent.setSource(mClickList.get(0).getSource()); 348 mClickList.add(newEvent); 349 mMotionEventInjector.injectEvents(mClickList, mServiceInterface, SEQUENCE); 350 mMessageCapturingHandler.sendOneMessage(); // Process the event injection 351 352 // Send 3 motion events, leaving newEvent in the queue 353 mMessageCapturingHandler.sendOneMessage(); 354 mMessageCapturingHandler.sendOneMessage(); 355 mMessageCapturingHandler.sendOneMessage(); 356 357 mMotionEventInjector.injectEvents(mSecondClickList, mServiceInterface, SECOND_SEQUENCE); 358 mMessageCapturingHandler.sendLastMessage(); // Process the event injection 359 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 360 361 verify(mServiceInterface).onPerformGestureResult(SEQUENCE, false); 362 verify(next, times(4)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 363 long gestureStart = mCaptor1.getAllValues().get(0).getDownTime(); 364 mClickEvent0Matcher.offsetTimesBy(gestureStart); 365 mClickEvent1Matcher.offsetTimesBy(gestureStart); 366 mClickEvent2Matcher.offsetTimesBy(gestureStart); 367 assertTrue(mClickEvent0Matcher.matches(mCaptor1.getAllValues().get(0))); 368 assertTrue(mClickEvent1Matcher.matches(mCaptor1.getAllValues().get(1))); 369 assertTrue(mClickEvent2Matcher.matches(mCaptor1.getAllValues().get(2))); 370 mSecondClickEvent0Matcher.offsetTimesBy(mCaptor1.getAllValues().get(3).getDownTime()); 371 assertTrue(mSecondClickEvent0Matcher.matches(mCaptor1.getAllValues().get(3))); 372 } 373 374 @Test 375 public void testClearEvents_realGestureInProgress_shouldForgetAboutGesture() { 376 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 377 mMotionEventInjector.onMotionEvent(mClickList.get(0), mClickList.get(0), 0); 378 mMotionEventInjector.clearEvents(MOTION_EVENT_SOURCE); 379 mMotionEventInjector.injectEvents(mSecondClickList, mServiceInterface, SEQUENCE); 380 mMessageCapturingHandler.sendOneMessage(); // Process the event injection 381 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 382 383 verify(next, times(2)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 384 assertTrue(mClickEvent0Matcher.matches(mCaptor1.getAllValues().get(0))); 385 mSecondClickEvent0Matcher.offsetTimesBy(mCaptor1.getAllValues().get(1).getDownTime()); 386 assertTrue(mSecondClickEvent0Matcher.matches(mCaptor1.getAllValues().get(1))); 387 } 388 389 @Test 390 public void testClearEventsOnOtherSource_realGestureInProgress_shouldNotForgetAboutGesture() { 391 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 392 mMotionEventInjector.onMotionEvent(mClickList.get(0), mClickList.get(0), 0); 393 mMotionEventInjector.clearEvents(OTHER_EVENT_SOURCE); 394 mMotionEventInjector.injectEvents(mSecondClickList, mServiceInterface, SECOND_SEQUENCE); 395 mMessageCapturingHandler.sendOneMessage(); // Process the event injection 396 mMessageCapturingHandler.sendOneMessage(); // Send a motion event 397 398 verify(next, times(3)).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), anyInt()); 399 assertTrue(mClickEvent0Matcher.matches(mCaptor1.getAllValues().get(0))); 400 assertEquals(MotionEvent.ACTION_CANCEL, mCaptor1.getAllValues().get(1).getActionMasked()); 401 mSecondClickEvent0Matcher.offsetTimesBy(mCaptor1.getAllValues().get(2).getDownTime()); 402 assertTrue(mSecondClickEvent0Matcher.matches(mCaptor1.getAllValues().get(2))); 403 } 404 405 @Test 406 public void testOnDestroy_shouldCancelGestures() throws RemoteException { 407 mMotionEventInjector.onDestroy(); 408 mMotionEventInjector.injectEvents(mClickList, mServiceInterface, SEQUENCE); 409 mMessageCapturingHandler.sendOneMessage(); // Process the event injection 410 verify(mServiceInterface).onPerformGestureResult(SEQUENCE, false); 411 } 412 413 @Test 414 public void testInjectEvents_withNoNext_shouldCancel() throws RemoteException { 415 mMotionEventInjector.injectEvents(mClickList, mServiceInterface, SEQUENCE); 416 mMessageCapturingHandler.sendOneMessage(); // Process the event injection 417 verify(mServiceInterface).onPerformGestureResult(SEQUENCE, false); 418 } 419 420 @Test 421 public void testOnMotionEvent_withNoNext_shouldNotCrash() { 422 mMotionEventInjector.onMotionEvent(mClickList.get(0), mClickList.get(0), 0); 423 } 424 425 @Test 426 public void testOnKeyEvent_shouldPassToNext() { 427 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 428 KeyEvent event = new KeyEvent(0, 0); 429 mMotionEventInjector.onKeyEvent(event, 0); 430 verify(next).onKeyEvent(event, 0); 431 } 432 433 @Test 434 public void testOnKeyEvent_withNoNext_shouldNotCrash() { 435 KeyEvent event = new KeyEvent(0, 0); 436 mMotionEventInjector.onKeyEvent(event, 0); 437 } 438 439 @Test 440 public void testOnAccessibilityEvent_shouldPassToNext() { 441 EventStreamTransformation next = attachMockNext(mMotionEventInjector); 442 AccessibilityEvent event = AccessibilityEvent.obtain(); 443 mMotionEventInjector.onAccessibilityEvent(event); 444 verify(next).onAccessibilityEvent(event); 445 } 446 447 @Test 448 public void testOnAccessibilityEvent_withNoNext_shouldNotCrash() { 449 AccessibilityEvent event = AccessibilityEvent.obtain(); 450 mMotionEventInjector.onAccessibilityEvent(event); 451 } 452 453 private EventStreamTransformation attachMockNext(MotionEventInjector motionEventInjector) { 454 EventStreamTransformation next = mock(EventStreamTransformation.class); 455 motionEventInjector.setNext(next); 456 return next; 457 } 458 459 static class MotionEventMatcher extends ArgumentMatcher<MotionEvent> { 460 long mDownTime; 461 long mEventTime; 462 long mActionMasked; 463 int mX; 464 int mY; 465 466 MotionEventMatcher(long downTime, long eventTime, int actionMasked, int x, int y) { 467 mDownTime = downTime; 468 mEventTime = eventTime; 469 mActionMasked = actionMasked; 470 mX = x; 471 mY = y; 472 } 473 474 MotionEventMatcher(MotionEvent event) { 475 this(event.getDownTime(), event.getEventTime(), event.getActionMasked(), 476 (int) event.getX(), (int) event.getY()); 477 } 478 479 void offsetTimesBy(long timeOffset) { 480 mDownTime += timeOffset; 481 mEventTime += timeOffset; 482 } 483 484 @Override 485 public boolean matches(Object o) { 486 MotionEvent event = (MotionEvent) o; 487 if ((event.getDownTime() == mDownTime) && (event.getEventTime() == mEventTime) 488 && (event.getActionMasked() == mActionMasked) && ((int) event.getX() == mX) 489 && ((int) event.getY() == mY)) { 490 return true; 491 } 492 Log.e(LOG_TAG, "MotionEvent match failed"); 493 Log.e(LOG_TAG, "event.getDownTime() = " + event.getDownTime() 494 + ", expected " + mDownTime); 495 Log.e(LOG_TAG, "event.getEventTime() = " + event.getEventTime() 496 + ", expected " + mEventTime); 497 Log.e(LOG_TAG, "event.getActionMasked() = " + event.getActionMasked() 498 + ", expected " + mActionMasked); 499 Log.e(LOG_TAG, "event.getX() = " + event.getX() + ", expected " + mX); 500 Log.e(LOG_TAG, "event.getY() = " + event.getY() + ", expected " + mY); 501 return false; 502 } 503 } 504 505 private class MessageCapturingHandler extends Handler { 506 List<Pair<Message, Long>> timedMessages = new ArrayList<>(); 507 508 @Override 509 public boolean sendMessageAtTime(Message message, long uptimeMillis) { 510 timedMessages.add(new Pair<>(Message.obtain(message), uptimeMillis)); 511 return super.sendMessageAtTime(message, uptimeMillis); 512 } 513 514 void sendOneMessage() { 515 Message message = timedMessages.remove(0).first; 516 removeMessages(message.what, message.obj); 517 mMotionEventInjector.handleMessage(message); 518 removeStaleMessages(); 519 } 520 521 void sendAllMessages() { 522 while (!timedMessages.isEmpty()) { 523 sendOneMessage(); 524 } 525 } 526 527 void sendLastMessage() { 528 Message message = timedMessages.remove(timedMessages.size() - 1).first; 529 removeMessages(message.what, message.obj); 530 mMotionEventInjector.handleMessage(message); 531 removeStaleMessages(); 532 } 533 534 boolean hasMessages() { 535 removeStaleMessages(); 536 return !timedMessages.isEmpty(); 537 } 538 539 private void removeStaleMessages() { 540 for (int i = 0; i < timedMessages.size(); i++) { 541 Message message = timedMessages.get(i).first; 542 if (!hasMessages(message.what, message.obj)) { 543 timedMessages.remove(i--); 544 } 545 } 546 } 547 } 548} 549