1 /* 2 * Copyright (C) 2019 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.car; 18 19 import static android.car.CarOccupantZoneManager.DisplayTypeEnum; 20 import static android.car.input.CustomInputEvent.INPUT_CODE_F1; 21 import static android.view.Display.DEFAULT_DISPLAY; 22 import static android.view.Display.INVALID_DISPLAY; 23 24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 27 28 import static com.google.common.truth.Truth.assertThat; 29 import static com.google.common.truth.Truth.assertWithMessage; 30 31 import static org.mockito.ArgumentMatchers.any; 32 import static org.mockito.ArgumentMatchers.anyBoolean; 33 import static org.mockito.ArgumentMatchers.anyInt; 34 import static org.mockito.ArgumentMatchers.anyLong; 35 import static org.mockito.ArgumentMatchers.eq; 36 import static org.mockito.ArgumentMatchers.same; 37 import static org.mockito.Mockito.atLeast; 38 import static org.mockito.Mockito.clearInvocations; 39 import static org.mockito.Mockito.doNothing; 40 import static org.mockito.Mockito.mock; 41 import static org.mockito.Mockito.never; 42 import static org.mockito.Mockito.times; 43 import static org.mockito.Mockito.when; 44 45 import android.car.CarOccupantZoneManager; 46 import android.car.CarOccupantZoneManager.OccupantZoneInfo; 47 import android.car.CarProjectionManager; 48 import android.car.VehicleAreaSeat; 49 import android.car.builtin.util.AssistUtilsHelper; 50 import android.car.builtin.util.AssistUtilsHelper.VoiceInteractionSessionShowCallbackHelper; 51 import android.car.input.CarInputManager; 52 import android.car.input.CustomInputEvent; 53 import android.car.input.ICarInputCallback; 54 import android.car.input.RotaryEvent; 55 import android.car.test.mocks.AbstractExtendedMockitoTestCase; 56 import android.content.Context; 57 import android.content.Intent; 58 import android.content.pm.PackageManager; 59 import android.content.res.Resources; 60 import android.net.Uri; 61 import android.os.Handler; 62 import android.os.Looper; 63 import android.os.Message; 64 import android.os.SystemClock; 65 import android.os.UserHandle; 66 import android.provider.Settings; 67 import android.telecom.TelecomManager; 68 import android.view.KeyEvent; 69 import android.view.MotionEvent; 70 71 import androidx.test.core.app.ApplicationProvider; 72 73 import com.android.car.audio.CarAudioService; 74 import com.android.car.bluetooth.CarBluetoothService; 75 import com.android.car.hal.InputHalService; 76 import com.android.car.power.CarPowerManagementService; 77 import com.android.car.systeminterface.SystemInterface; 78 import com.android.car.user.CarUserService; 79 80 import com.google.common.collect.Range; 81 82 import org.junit.After; 83 import org.junit.Assert; 84 import org.junit.Before; 85 import org.junit.Test; 86 import org.mockito.ArgumentCaptor; 87 import org.mockito.Mock; 88 import org.mockito.Spy; 89 90 import java.util.BitSet; 91 import java.util.Collections; 92 import java.util.List; 93 import java.util.function.BooleanSupplier; 94 import java.util.function.IntSupplier; 95 import java.util.function.Supplier; 96 97 public final class CarInputServiceTest extends AbstractExtendedMockitoTestCase { 98 99 @Mock InputHalService mInputHalService; 100 @Mock TelecomManager mTelecomManager; 101 @Mock CarInputService.KeyEventListener mDefaultKeyEventMainListener; 102 @Mock CarInputService.MotionEventListener mDefaultMotionEventMainListener; 103 @Mock CarInputService.KeyEventListener mInstrumentClusterKeyListener; 104 @Mock Supplier<String> mLastCallSupplier; 105 @Mock IntSupplier mLongPressDelaySupplier; 106 @Mock BooleanSupplier mShouldCallButtonEndOngoingCallSupplier; 107 @Mock InputCaptureClientController mCaptureController; 108 @Mock CarOccupantZoneService mCarOccupantZoneService; 109 @Mock CarBluetoothService mCarBluetoothService; 110 @Mock CarPowerManagementService mCarPowerManagementService; 111 @Mock SystemInterface mSystemInterface; 112 @Mock CarAudioService mCarAudioService; 113 @Mock CarMediaService mCarMediaService; 114 115 @Spy Context mContext = ApplicationProvider.getApplicationContext(); 116 @Mock private Resources mMockResources; 117 @Spy Handler mHandler = new Handler(Looper.getMainLooper()); 118 119 @Mock CarUserService mCarUserService; 120 private CarInputService mCarInputService; 121 122 private static final int DRIVER_USER_ID = 111; 123 private static final int PASSENGER_USER_ID = 112; 124 125 private static final int DRIVER_DISPLAY_ID = 121; 126 private static final int PASSENGER_DISPLAY_ID = 122; 127 128 private static final int DRIVER_ZONE_ID = 131; 129 private static final int PASSENGER_ZONE_ID = 132; 130 131 private static final int UNKNOWN_SEAT = VehicleAreaSeat.SEAT_UNKNOWN; 132 private static final int DRIVER_SEAT = VehicleAreaSeat.SEAT_ROW_1_LEFT; 133 private static final int PASSENGER_SEAT = VehicleAreaSeat.SEAT_ROW_1_RIGHT; 134 135 // CarInputService#sDefaultShowCallback() prints out some Slog message which can be conflicted 136 // with AbstractExtendedMockitoTestCase#interceptWtfCalls (b/294138315). 137 private static final VoiceInteractionSessionShowCallbackHelper sShowCallback = 138 new VoiceInteractionSessionShowCallbackHelper() { 139 @Override public void onFailed() { 140 // No op 141 } 142 @Override public void onShown() { 143 // No op 144 } 145 }; 146 CarInputServiceTest()147 public CarInputServiceTest() { 148 super(CarInputService.TAG); 149 } 150 151 @Before setUp()152 public void setUp() { 153 mCarInputService = new CarInputService(mContext, mInputHalService, mCarUserService, 154 mCarOccupantZoneService, mCarBluetoothService, mCarPowerManagementService, 155 mSystemInterface, mHandler, mTelecomManager, mDefaultKeyEventMainListener, 156 mDefaultMotionEventMainListener, mLastCallSupplier, mLongPressDelaySupplier, 157 mShouldCallButtonEndOngoingCallSupplier, mCaptureController, 158 sShowCallback); 159 160 mCarInputService.setInstrumentClusterKeyListener(mInstrumentClusterKeyListener); 161 162 when(mInputHalService.isKeyInputSupported()).thenReturn(true); 163 164 // Delay Handler callbacks until flushHandler() is called. 165 doReturn(true).when(mHandler).sendMessageAtTime(any(), anyLong()); 166 167 when(mShouldCallButtonEndOngoingCallSupplier.getAsBoolean()).thenReturn(false); 168 169 when(mContext.getResources()).thenReturn(mMockResources); 170 171 setUpCarOccupantZoneService(); 172 setUpService(); 173 mCarInputService.init(); 174 } 175 176 @Override onSessionBuilder(CustomMockitoSessionBuilder session)177 protected void onSessionBuilder(CustomMockitoSessionBuilder session) { 178 session.spyStatic(AssistUtilsHelper.class); 179 session.spyStatic(CarServiceUtils.class); 180 } 181 setUpCarOccupantZoneService()182 private void setUpCarOccupantZoneService() { 183 when(mCarOccupantZoneService.getDriverSeat()).thenReturn(DRIVER_SEAT); 184 185 when(mCarOccupantZoneService.getOccupantZoneIdForSeat(eq(DRIVER_SEAT))) 186 .thenReturn(DRIVER_ZONE_ID); 187 when(mCarOccupantZoneService.getOccupantZoneIdForSeat(eq(PASSENGER_SEAT))) 188 .thenReturn(PASSENGER_ZONE_ID); 189 190 when(mCarOccupantZoneService.getUserForOccupant(eq(DRIVER_ZONE_ID))) 191 .thenReturn(DRIVER_USER_ID); 192 when(mCarOccupantZoneService.getUserForOccupant(eq(PASSENGER_ZONE_ID))) 193 .thenReturn(PASSENGER_USER_ID); 194 195 when(mCarOccupantZoneService.getDisplayForOccupant(eq(DRIVER_ZONE_ID), 196 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN))).thenReturn(DRIVER_DISPLAY_ID); 197 when(mCarOccupantZoneService.getDisplayForOccupant(eq(PASSENGER_ZONE_ID), 198 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN))).thenReturn(PASSENGER_DISPLAY_ID); 199 } 200 setUpService()201 private void setUpService() { 202 CarLocalServices.removeServiceForTest(CarAudioService.class); 203 CarLocalServices.addService(CarAudioService.class, mCarAudioService); 204 CarLocalServices.removeServiceForTest(CarMediaService.class); 205 CarLocalServices.addService(CarMediaService.class, mCarMediaService); 206 } 207 208 @After tearDown()209 public void tearDown() { 210 if (mCarInputService != null) { 211 mCarInputService.release(); 212 } 213 214 CarLocalServices.removeServiceForTest(CarAudioService.class); 215 CarLocalServices.removeServiceForTest(CarMediaService.class); 216 } 217 218 @Test testConstantValueMatching()219 public void testConstantValueMatching() { 220 assertWithMessage( 221 "CarInputService.LONG_PRESS_TIMEOUT ('%s') must match the string defined in " 222 + "Settings.Secure.LONG_PRESS_TIMEOUT", 223 CarInputService.LONG_PRESS_TIMEOUT).that( 224 CarInputService.LONG_PRESS_TIMEOUT).isEqualTo( 225 Settings.Secure.LONG_PRESS_TIMEOUT); 226 } 227 228 @Test testOnRotaryEvent_injectingRotaryNavigationEvent()229 public void testOnRotaryEvent_injectingRotaryNavigationEvent() { 230 RotaryEvent event = new RotaryEvent( 231 /* inputType= */ CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION, 232 /* clockwise= */ true, 233 /* uptimeMillisForClicks= */ new long[]{1, 1}); 234 when(mCaptureController.onRotaryEvent( 235 same(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), same(event))).thenReturn(true); 236 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(true); 237 238 mCarInputService.onRotaryEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN); 239 240 // Since mCaptureController processed RotaryEvent, then no KeyEvent was generated or 241 // processed 242 verify(mCarOccupantZoneService, never()).getDisplayIdForDriver(anyInt()); 243 verify(mCaptureController, never()).onKeyEvent(anyInt(), any(KeyEvent.class)); 244 verify(mDefaultKeyEventMainListener, never()) 245 .onKeyEvent(any(KeyEvent.class), anyInt(), anyInt()); 246 } 247 248 @Test testOnRotaryEvent_injectingRotaryVolumeEvent()249 public void testOnRotaryEvent_injectingRotaryVolumeEvent() { 250 RotaryEvent event = new RotaryEvent( 251 /* inputType= */ CarInputManager.INPUT_TYPE_ROTARY_VOLUME, 252 /* clockwise= */ true, 253 /* uptimeMillisForClicks= */ new long[]{1, 1}); 254 when(mCaptureController.onRotaryEvent( 255 same(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), same(event))).thenReturn(false); 256 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(true); 257 258 mCarInputService.onRotaryEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN); 259 260 // Since mCaptureController processed RotaryEvent, then KeyEvent was generated or 261 // processed 262 int numberOfGeneratedKeyEvents = 4; 263 verify(mCarOccupantZoneService, times(numberOfGeneratedKeyEvents)).getDisplayIdForDriver( 264 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN)); 265 verify(mCaptureController, times(numberOfGeneratedKeyEvents)).onKeyEvent(anyInt(), 266 any(KeyEvent.class)); 267 verify(mDefaultKeyEventMainListener, never()) 268 .onKeyEvent(any(KeyEvent.class), anyInt(), anyInt()); 269 } 270 271 @Test testOnRotaryEvent_injectingRotaryNavigation_notConsumedByCaptureController()272 public void testOnRotaryEvent_injectingRotaryNavigation_notConsumedByCaptureController() { 273 RotaryEvent event = new RotaryEvent( 274 /* inputType= */ CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION, 275 /* clockwise= */ true, 276 /* uptimeMillisForClicks= */ new long[]{1, 1}); 277 when(mCaptureController.onRotaryEvent( 278 same(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), same(event))).thenReturn(false); 279 280 mCarInputService.onRotaryEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN); 281 282 // Since mCaptureController processed RotaryEvent, then KeyEvent was generated or 283 // processed 284 int numberOfGeneratedKeyEvents = 4; 285 verify(mCarOccupantZoneService, times(numberOfGeneratedKeyEvents)).getDisplayIdForDriver( 286 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN)); 287 verify(mCaptureController, times(numberOfGeneratedKeyEvents)).onKeyEvent(anyInt(), 288 any(KeyEvent.class)); 289 verify(mDefaultKeyEventMainListener, times(numberOfGeneratedKeyEvents)).onKeyEvent( 290 any(KeyEvent.class), eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), anyInt()); 291 } 292 293 @Test testRequestInputEventCapture_delegatesToCaptureController()294 public void testRequestInputEventCapture_delegatesToCaptureController() { 295 ICarInputCallback callback = mock(ICarInputCallback.class); 296 int[] inputTypes = new int[]{CarInputManager.INPUT_TYPE_CUSTOM_INPUT_EVENT}; 297 int requestFlags = CarInputManager.CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT; 298 mCarInputService.requestInputEventCapture(callback, 299 CarOccupantZoneManager.DISPLAY_TYPE_MAIN, inputTypes, requestFlags); 300 301 verify(mCaptureController).requestInputEventCapture(same(callback), 302 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), same(inputTypes), eq(requestFlags)); 303 } 304 305 @Test testOnCustomInputEvent_delegatesToCaptureController()306 public void testOnCustomInputEvent_delegatesToCaptureController() { 307 CustomInputEvent event = new CustomInputEvent(INPUT_CODE_F1, 308 CarOccupantZoneManager.DISPLAY_TYPE_MAIN, /* repeatCounter= */ 1); 309 310 mCarInputService.onCustomInputEvent(event); 311 312 verify(mCaptureController).onCustomInputEvent(same(event)); 313 } 314 315 @Test testReleaseInputEventCapture_delegatesToCaptureController()316 public void testReleaseInputEventCapture_delegatesToCaptureController() { 317 ICarInputCallback callback = mock(ICarInputCallback.class); 318 mCarInputService.releaseInputEventCapture(callback, 319 CarOccupantZoneManager.DISPLAY_TYPE_MAIN); 320 321 verify(mCaptureController).releaseInputEventCapture(same(callback), 322 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN)); 323 } 324 325 @Test ordinaryEvents_onMainDisplay_routedToInputManager()326 public void ordinaryEvents_onMainDisplay_routedToInputManager() { 327 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.MAIN); 328 329 verify(mDefaultKeyEventMainListener).onKeyEvent(same(event), 330 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), anyInt()); 331 } 332 333 @Test ordinaryEvents_onInstrumentClusterDisplay_notRoutedToInputManager()334 public void ordinaryEvents_onInstrumentClusterDisplay_notRoutedToInputManager() { 335 send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.INSTRUMENT_CLUSTER); 336 337 verify(mDefaultKeyEventMainListener, never()).onKeyEvent(any(), anyInt(), anyInt()); 338 } 339 340 @Test ordinaryEvents_onInstrumentClusterDisplay_routedToListener()341 public void ordinaryEvents_onInstrumentClusterDisplay_routedToListener() { 342 CarInputService.KeyEventListener listener = mock(CarInputService.KeyEventListener.class); 343 mCarInputService.setInstrumentClusterKeyListener(listener); 344 345 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.INSTRUMENT_CLUSTER); 346 verify(listener).onKeyEvent(event); 347 } 348 349 @Test registerKeyEventListener_separateListenersWithSameEventsOfInterest_fails()350 public void registerKeyEventListener_separateListenersWithSameEventsOfInterest_fails() { 351 CarInputService.KeyEventListener listener1 = mock(CarInputService.KeyEventListener.class); 352 CarInputService.KeyEventListener listener2 = mock(CarInputService.KeyEventListener.class); 353 List<Integer> interestedEvents = List.of(KeyEvent.KEYCODE_HOME, KeyEvent.KEYCODE_0); 354 mCarInputService.registerKeyEventListener(listener1, interestedEvents); 355 356 IllegalArgumentException thrown = Assert.assertThrows(IllegalArgumentException.class, 357 () -> mCarInputService.registerKeyEventListener(listener2, interestedEvents)); 358 359 assertWithMessage("Register key event listener exception") 360 .that(thrown).hasMessageThat() 361 .contains("Event " + KeyEvent.keyCodeToString(KeyEvent.KEYCODE_HOME) 362 + " already registered to another listener"); 363 } 364 365 @Test registerKeyEventListener_separateListenersWithOverlappingEventsOfInterest_fails()366 public void registerKeyEventListener_separateListenersWithOverlappingEventsOfInterest_fails() { 367 CarInputService.KeyEventListener listener1 = mock(CarInputService.KeyEventListener.class); 368 CarInputService.KeyEventListener listener2 = mock(CarInputService.KeyEventListener.class); 369 List<Integer> interestedEvents1 = List.of(KeyEvent.KEYCODE_HOME, KeyEvent.KEYCODE_0); 370 List<Integer> interestedEvents2 = List.of(KeyEvent.KEYCODE_1, KeyEvent.KEYCODE_HOME); 371 mCarInputService.registerKeyEventListener(listener1, interestedEvents1); 372 373 IllegalArgumentException thrown = Assert.assertThrows(IllegalArgumentException.class, 374 () -> mCarInputService.registerKeyEventListener(listener2, interestedEvents2)); 375 376 assertWithMessage("Register key event listener") 377 .that(thrown).hasMessageThat() 378 .contains("Event " + KeyEvent.keyCodeToString(KeyEvent.KEYCODE_HOME) 379 + " already registered to another listener"); 380 } 381 382 @Test registerKeyEventListener_withNullListener_fails()383 public void registerKeyEventListener_withNullListener_fails() { 384 var interestedEvents = List.of(KeyEvent.KEYCODE_HOME, KeyEvent.KEYCODE_0); 385 386 NullPointerException thrown = Assert.assertThrows(NullPointerException.class, 387 () -> mCarInputService.registerKeyEventListener(/* listener= */ null, 388 interestedEvents)); 389 390 assertWithMessage("Register null key event listener exception") 391 .that(thrown).hasMessageThat().contains("Key event listener"); 392 } 393 394 @Test registerKeyEventListener_withNullKeyEventList_fails()395 public void registerKeyEventListener_withNullKeyEventList_fails() { 396 CarInputService.KeyEventListener listener = mock(CarInputService.KeyEventListener.class); 397 398 NullPointerException thrown = Assert.assertThrows(NullPointerException.class, 399 () -> mCarInputService.registerKeyEventListener(listener, 400 /* keyCodesOfInterest= */ null)); 401 402 assertWithMessage("Register null key events of interest exception") 403 .that(thrown).hasMessageThat().contains("Key events of interest"); 404 } 405 406 @Test registerKeyEventListener_withEmptyKeyEventList_fails()407 public void registerKeyEventListener_withEmptyKeyEventList_fails() { 408 CarInputService.KeyEventListener listener = mock(CarInputService.KeyEventListener.class); 409 410 IllegalArgumentException thrown = Assert.assertThrows(IllegalArgumentException.class, 411 () -> mCarInputService.registerKeyEventListener(listener, 412 /* keyCodesOfInterest= */ Collections.emptyList())); 413 414 assertWithMessage("Register empty key events of interest exception") 415 .that(thrown).hasMessageThat().contains("Key events of interest"); 416 } 417 418 @Test unregisterKeyEventListener_withNullListener_fails()419 public void unregisterKeyEventListener_withNullListener_fails() { 420 NullPointerException thrown = Assert.assertThrows(NullPointerException.class, 421 () -> mCarInputService.unregisterKeyEventListener(/* listener= */ null)); 422 423 assertWithMessage("Unregister null key event listener exception") 424 .that(thrown).hasMessageThat().contains("Key event listener"); 425 } 426 427 @Test onKeyEvent_afterUnregistering_doesNotCallsListener()428 public void onKeyEvent_afterUnregistering_doesNotCallsListener() { 429 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HOME); 430 CarInputService.KeyEventListener listener = mock(CarInputService.KeyEventListener.class); 431 var interestedEvents = List.of(KeyEvent.KEYCODE_HOME, KeyEvent.KEYCODE_0); 432 mCarInputService.registerKeyEventListener(listener, interestedEvents); 433 mCarInputService.unregisterKeyEventListener(listener); 434 435 mCarInputService.onKeyEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN, 436 PASSENGER_SEAT); 437 438 verify(listener, never()).onKeyEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN, 439 PASSENGER_SEAT); 440 } 441 442 @Test onKeyEvent_withSingleListener_callsListener()443 public void onKeyEvent_withSingleListener_callsListener() { 444 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HOME); 445 CarInputService.KeyEventListener listener = mock(CarInputService.KeyEventListener.class); 446 List<Integer> interestedEvents = List.of(KeyEvent.KEYCODE_HOME, KeyEvent.KEYCODE_0); 447 mCarInputService.registerKeyEventListener(listener, interestedEvents); 448 449 mCarInputService.onKeyEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN, 450 PASSENGER_SEAT); 451 452 verify(listener).onKeyEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN, 453 PASSENGER_SEAT); 454 } 455 456 @Test onKeyEvent_withMultipleListeners_callToListener()457 public void onKeyEvent_withMultipleListeners_callToListener() { 458 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HOME); 459 CarInputService.KeyEventListener listener1 = mock(CarInputService.KeyEventListener.class); 460 CarInputService.KeyEventListener listener2 = mock(CarInputService.KeyEventListener.class); 461 List<Integer> interestedEvents1 = List.of(KeyEvent.KEYCODE_0); 462 List<Integer> interestedEvents2 = List.of(KeyEvent.KEYCODE_HOME); 463 mCarInputService.registerKeyEventListener(listener1, interestedEvents1); 464 mCarInputService.registerKeyEventListener(listener2, interestedEvents2); 465 466 mCarInputService.onKeyEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN, 467 PASSENGER_SEAT); 468 469 verify(listener1, never()).onKeyEvent(any(), anyInt(), anyInt()); 470 verify(listener2).onKeyEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN, 471 PASSENGER_SEAT); 472 } 473 474 @Test onKeyEvent_withMultipleListeners_noCallToListeners()475 public void onKeyEvent_withMultipleListeners_noCallToListeners() { 476 KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_1); 477 CarInputService.KeyEventListener listener1 = mock(CarInputService.KeyEventListener.class); 478 CarInputService.KeyEventListener listener2 = mock(CarInputService.KeyEventListener.class); 479 List<Integer> interestedEvents1 = List.of(KeyEvent.KEYCODE_0); 480 List<Integer> interestedEvents2 = List.of(KeyEvent.KEYCODE_HOME); 481 mCarInputService.registerKeyEventListener(listener1, interestedEvents1); 482 mCarInputService.registerKeyEventListener(listener2, interestedEvents2); 483 484 mCarInputService.onKeyEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN, 485 PASSENGER_SEAT); 486 487 verify(listener1, never()).onKeyEvent(any(), anyInt(), anyInt()); 488 verify(listener2, never()).onKeyEvent(any(), anyInt(), anyInt()); 489 } 490 491 @Test customEventHandler_capturesDisplayMainEvent_capturedByInputController()492 public void customEventHandler_capturesDisplayMainEvent_capturedByInputController() { 493 CarInputService.KeyEventListener instrumentClusterListener = 494 setupInstrumentClusterListener(); 495 496 // Assume mCaptureController will consume every event. 497 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(true); 498 499 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.MAIN); 500 verify(instrumentClusterListener, never()).onKeyEvent(any(KeyEvent.class)); 501 verify(mCaptureController).onKeyEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, event); 502 verify(mDefaultKeyEventMainListener, never()) 503 .onKeyEvent(any(KeyEvent.class), anyInt(), anyInt()); 504 } 505 506 @Test customEventHandler_capturesDisplayMainEvent_missedByInputController()507 public void customEventHandler_capturesDisplayMainEvent_missedByInputController() { 508 CarInputService.KeyEventListener instrumentClusterListener = 509 setupInstrumentClusterListener(); 510 511 // Assume mCaptureController will consume every event. 512 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(false); 513 514 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.MAIN); 515 verify(instrumentClusterListener, never()).onKeyEvent(any(KeyEvent.class)); 516 verify(mCaptureController).onKeyEvent(anyInt(), any(KeyEvent.class)); 517 verify(mDefaultKeyEventMainListener).onKeyEvent(same(event), 518 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), anyInt()); 519 } 520 521 @Test customEventHandler_capturesClusterEvents_capturedByInstrumentCluster()522 public void customEventHandler_capturesClusterEvents_capturedByInstrumentCluster() { 523 CarInputService.KeyEventListener instrumentClusterListener = 524 setupInstrumentClusterListener(); 525 526 // Assume mCaptureController will consume every event. 527 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(true); 528 529 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.INSTRUMENT_CLUSTER); 530 verify(instrumentClusterListener).onKeyEvent(event); 531 verify(mCaptureController, never()).onKeyEvent(anyInt(), any(KeyEvent.class)); 532 verify(mDefaultKeyEventMainListener, never()) 533 .onKeyEvent(any(KeyEvent.class), anyInt(), anyInt()); 534 } 535 setupInstrumentClusterListener()536 private CarInputService.KeyEventListener setupInstrumentClusterListener() { 537 CarInputService.KeyEventListener instrumentClusterListener = 538 mock(CarInputService.KeyEventListener.class); 539 mCarInputService.setInstrumentClusterKeyListener(instrumentClusterListener); 540 return instrumentClusterListener; 541 } 542 543 @Test voiceKey_shortPress_withRegisteredEventHandler_triggersEventHandler()544 public void voiceKey_shortPress_withRegisteredEventHandler_triggersEventHandler() { 545 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 546 registerProjectionKeyEventHandler( 547 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP); 548 549 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 550 send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 551 552 verify(eventHandler) 553 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP); 554 } 555 556 @Test voiceKey_longPress_withRegisteredEventHandler_triggersEventHandler()557 public void voiceKey_longPress_withRegisteredEventHandler_triggersEventHandler() { 558 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 559 registerProjectionKeyEventHandler( 560 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP, 561 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_DOWN); 562 563 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 564 verify(eventHandler, never()).onKeyEvent(anyInt()); 565 566 // Simulate the long-press timer expiring. 567 flushHandler(); 568 verify(eventHandler) 569 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_DOWN); 570 571 // Ensure that the short-press handler is *not* called. 572 send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 573 flushHandler(); 574 verify(eventHandler, never()) 575 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP); 576 } 577 578 @Test voiceKey_shortPress_withoutRegisteredEventHandler_triggersAssistUtils()579 public void voiceKey_shortPress_withoutRegisteredEventHandler_triggersAssistUtils() { 580 doReturn(true).when( 581 () -> AssistUtilsHelper.showPushToTalkSessionForActiveService(eq(mContext), any())); 582 583 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 584 send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 585 } 586 587 @Test voiceKey_longPress_withoutRegisteredEventHandler_triggersAssistUtils()588 public void voiceKey_longPress_withoutRegisteredEventHandler_triggersAssistUtils() { 589 doReturn(true).when( 590 () -> AssistUtilsHelper.showPushToTalkSessionForActiveService(eq(mContext), any())); 591 592 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 593 flushHandler(); 594 595 send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 596 597 // NOTE: extend mockito doesn't provide verifyNoMoreInteractions(), so we'd need to 598 // explicitly call verify() and verify(never()). But since AssistUtilHelper only has one 599 // static method, we don't need the latter. 600 verify(() -> AssistUtilsHelper.showPushToTalkSessionForActiveService(eq(mContext), any())); 601 } 602 603 /** 604 * Testing long press triggers Bluetooth voice recognition. 605 * 606 * Based on current implementation of {@link CarInputService#handleVoiceAssistLongPress}, 607 * long press of the button should trigger Bluetooth voice recognition if: 608 * (a) {@link CarProjectionManager.ProjectionKeyEventHandler} did not subscribe for the 609 * event, or if the key event handler does not exit; and 610 * (b) Bluetooth voice recognition is enabled. 611 * 612 * Preconditions: 613 * - Bluetooth voice recognition is enabled. 614 * - No {@link CarProjectionManager.ProjectionKeyEventHandler} registered for the key event. 615 * Action: 616 * - Long press the voice assistant key. 617 * Results: 618 * - Bluetooth voice recognition is invoked. 619 */ 620 @Test voiceKey_longPress_bluetoothVoiceRecognitionIsEnabled_triggersBluetoothAssist()621 public void voiceKey_longPress_bluetoothVoiceRecognitionIsEnabled_triggersBluetoothAssist() { 622 mockEnableLongPressBluetoothVoiceRecognitionProperty(true); 623 624 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 625 flushHandler(); 626 627 send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 628 629 verify(mCarBluetoothService, times(1)).startBluetoothVoiceRecognition(); 630 } 631 632 /** 633 * Testing short press does not trigger Bluetooth voice recognition. 634 * 635 * Based on current implementation of {@link CarInputService#handleVoiceAssistKey}, 636 * short press of the button should not trigger Bluetooth, and instead launch the default 637 * voice assistant handler. 638 * 639 * Preconditions: 640 * - Bluetooth voice recognition is enabled. 641 * Action: 642 * - Short press the voice assistant key. 643 * Results: 644 * - Bluetooth voice recognition is not invoked. 645 * - Default assistant handler is invoked instead. 646 */ 647 @Test voiceKey_shortPress_bluetoothVoiceRecognitionIsEnabled_triggersAssistUtils()648 public void voiceKey_shortPress_bluetoothVoiceRecognitionIsEnabled_triggersAssistUtils() { 649 mockEnableLongPressBluetoothVoiceRecognitionProperty(true); 650 651 doReturn(true).when( 652 () -> AssistUtilsHelper.showPushToTalkSessionForActiveService(eq(mContext), any())); 653 654 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 655 send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 656 657 verify(mCarBluetoothService, never()).startBluetoothVoiceRecognition(); 658 verify(() -> AssistUtilsHelper.showPushToTalkSessionForActiveService(eq(mContext), any())); 659 } 660 661 @Test voiceKey_keyDown_withEventHandler_triggersEventHandler()662 public void voiceKey_keyDown_withEventHandler_triggersEventHandler() { 663 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 664 registerProjectionKeyEventHandler( 665 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_KEY_DOWN); 666 667 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 668 669 verify(eventHandler).onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_KEY_DOWN); 670 } 671 672 @Test voiceKey_keyUp_afterLongPress_withEventHandler_triggersEventHandler()673 public void voiceKey_keyUp_afterLongPress_withEventHandler_triggersEventHandler() { 674 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 675 registerProjectionKeyEventHandler( 676 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_UP); 677 678 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 679 flushHandler(); 680 verify(eventHandler, never()) 681 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_UP); 682 683 send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 684 verify(eventHandler) 685 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_UP); 686 } 687 688 @Test voiceKey_repeatedEvents_ignored()689 public void voiceKey_repeatedEvents_ignored() { 690 // Pressing a key starts the long-press timer. 691 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 692 verify(mHandler).sendMessageAtTime(any(), anyLong()); 693 clearInvocations(mHandler); 694 695 // Repeated KEY_DOWN events don't reset the timer. 696 sendWithRepeat(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN, 1); 697 verify(mHandler, never()).sendMessageAtTime(any(), anyLong()); 698 } 699 700 @Test callKey_shortPress_withoutEventHandler_launchesDialer()701 public void callKey_shortPress_withoutEventHandler_launchesDialer() { 702 ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); 703 704 doNothing().when(mContext).startActivityAsUser(any(), any()); 705 706 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 707 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 708 709 verify(mContext).startActivityAsUser( 710 intentCaptor.capture(), eq(UserHandle.CURRENT)); 711 assertThat(intentCaptor.getValue().getAction()).isEqualTo(Intent.ACTION_DIAL); 712 } 713 714 @Test callKey_shortPress_withoutEventHandler_whenCallRinging_answersCall()715 public void callKey_shortPress_withoutEventHandler_whenCallRinging_answersCall() { 716 when(mTelecomManager.isRinging()).thenReturn(true); 717 718 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 719 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 720 721 verify(mTelecomManager).acceptRingingCall(); 722 // Ensure default handler does not run. 723 verify(mContext, never()).startActivityAsUser(any(), any()); 724 } 725 726 @Test callKey_shortPress_withEventHandler_triggersEventHandler()727 public void callKey_shortPress_withEventHandler_triggersEventHandler() { 728 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 729 registerProjectionKeyEventHandler( 730 CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP); 731 732 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 733 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 734 735 verify(eventHandler).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP); 736 // Ensure default handlers do not run. 737 verify(mTelecomManager, never()).acceptRingingCall(); 738 verify(mContext, never()).startActivityAsUser(any(), any()); 739 } 740 741 @Test callKey_shortPress_withEventHandler_whenCallRinging_answersCall()742 public void callKey_shortPress_withEventHandler_whenCallRinging_answersCall() { 743 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 744 registerProjectionKeyEventHandler( 745 CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP); 746 when(mTelecomManager.isRinging()).thenReturn(true); 747 748 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 749 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 750 751 verify(mTelecomManager).acceptRingingCall(); 752 verify(eventHandler, never()).onKeyEvent(anyInt()); 753 } 754 755 @Test callKey_shortPress_duringCall_endCallViaCallButtonOn_endsCall()756 public void callKey_shortPress_duringCall_endCallViaCallButtonOn_endsCall() { 757 when(mShouldCallButtonEndOngoingCallSupplier.getAsBoolean()).thenReturn(true); 758 when(mTelecomManager.isInCall()).thenReturn(true); 759 760 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 761 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 762 763 verify(mTelecomManager).endCall(); 764 } 765 766 @Test callKey_shortPress_duringCall_endCallViaCallButtonOff_doesNotEndCall()767 public void callKey_shortPress_duringCall_endCallViaCallButtonOff_doesNotEndCall() { 768 when(mTelecomManager.isInCall()).thenReturn(true); 769 770 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 771 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 772 773 verify(mTelecomManager, never()).endCall(); 774 } 775 776 @Test callKey_longPress_withoutEventHandler_redialsLastCall()777 public void callKey_longPress_withoutEventHandler_redialsLastCall() { 778 ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); 779 780 when(mLastCallSupplier.get()).thenReturn("1234567890"); 781 doNothing().when(mContext).startActivityAsUser(any(), any()); 782 783 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 784 flushHandler(); 785 786 verify(mContext).startActivityAsUser( 787 intentCaptor.capture(), eq(UserHandle.CURRENT)); 788 789 Intent intent = intentCaptor.getValue(); 790 assertThat(intent.getAction()).isEqualTo(Intent.ACTION_CALL); 791 assertThat(intent.getData()).isEqualTo(Uri.parse("tel:1234567890")); 792 793 clearInvocations(mContext); 794 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 795 verify(mContext, never()).startActivityAsUser(any(), any()); 796 } 797 798 @Test callKey_longPress_withoutEventHandler_withNoLastCall_doesNothing()799 public void callKey_longPress_withoutEventHandler_withNoLastCall_doesNothing() { 800 when(mLastCallSupplier.get()).thenReturn(""); 801 802 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 803 flushHandler(); 804 805 verify(mContext, never()).startActivityAsUser(any(), any()); 806 } 807 808 @Test callKey_longPress_withoutEventHandler_whenCallRinging_answersCall()809 public void callKey_longPress_withoutEventHandler_whenCallRinging_answersCall() { 810 when(mTelecomManager.isRinging()).thenReturn(true); 811 812 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 813 flushHandler(); 814 815 verify(mTelecomManager).acceptRingingCall(); 816 817 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 818 // Ensure that default handler does not run, either after accepting ringing call, 819 // or as a result of key-up. 820 verify(mContext, never()).startActivityAsUser(any(), any()); 821 } 822 823 @Test callKey_longPress_withEventHandler_triggersEventHandler()824 public void callKey_longPress_withEventHandler_triggersEventHandler() { 825 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 826 registerProjectionKeyEventHandler( 827 CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN); 828 829 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 830 flushHandler(); 831 832 verify(eventHandler).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN); 833 verify(mContext, never()).startActivityAsUser(any(), any()); 834 } 835 836 @Test callKey_longPress_withEventHandler_whenCallRinging_answersCall()837 public void callKey_longPress_withEventHandler_whenCallRinging_answersCall() { 838 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 839 registerProjectionKeyEventHandler( 840 CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN); 841 when(mTelecomManager.isRinging()).thenReturn(true); 842 843 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 844 flushHandler(); 845 846 verify(mTelecomManager).acceptRingingCall(); 847 848 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 849 // Ensure that event handler does not run, either after accepting ringing call, 850 // or as a result of key-up. 851 verify(eventHandler, never()).onKeyEvent(anyInt()); 852 } 853 854 @Test callKey_longPress_duringCall_endCallViaCallButtonOn_endsCall()855 public void callKey_longPress_duringCall_endCallViaCallButtonOn_endsCall() { 856 when(mShouldCallButtonEndOngoingCallSupplier.getAsBoolean()).thenReturn(true); 857 when(mTelecomManager.isInCall()).thenReturn(true); 858 859 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 860 flushHandler(); 861 862 verify(mTelecomManager).endCall(); 863 } 864 865 @Test callKey_longPress_duringCall_endCallViaCallButtonOff_doesNotEndCall()866 public void callKey_longPress_duringCall_endCallViaCallButtonOff_doesNotEndCall() { 867 when(mTelecomManager.isInCall()).thenReturn(true); 868 869 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 870 flushHandler(); 871 872 verify(mTelecomManager, never()).endCall(); 873 } 874 875 @Test callKey_keyDown_withEventHandler_triggersEventHandler()876 public void callKey_keyDown_withEventHandler_triggersEventHandler() { 877 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 878 registerProjectionKeyEventHandler( 879 CarProjectionManager.KEY_EVENT_CALL_KEY_DOWN); 880 881 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 882 883 verify(eventHandler).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_KEY_DOWN); 884 } 885 886 @Test callKey_keyUp_afterLongPress_withEventHandler_triggersEventHandler()887 public void callKey_keyUp_afterLongPress_withEventHandler_triggersEventHandler() { 888 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 889 registerProjectionKeyEventHandler( 890 CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_UP); 891 892 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 893 flushHandler(); 894 verify(eventHandler, never()) 895 .onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_UP); 896 897 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 898 verify(eventHandler).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_UP); 899 } 900 901 @Test callKey_repeatedEvents_ignored()902 public void callKey_repeatedEvents_ignored() { 903 // Pressing a key starts the long-press timer. 904 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 905 verify(mHandler).sendMessageAtTime(any(), anyLong()); 906 clearInvocations(mHandler); 907 908 // Repeated KEY_DOWN events don't reset the timer. 909 sendWithRepeat(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN, 1); 910 verify(mHandler, never()).sendMessageAtTime(any(), anyLong()); 911 } 912 913 @Test longPressDelay_obeysValueFromSystem()914 public void longPressDelay_obeysValueFromSystem() { 915 final int systemDelay = 4242; 916 917 when(mLongPressDelaySupplier.getAsInt()).thenReturn(systemDelay); 918 ArgumentCaptor<Long> timeCaptor = ArgumentCaptor.forClass(long.class); 919 920 long then = SystemClock.uptimeMillis(); 921 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 922 long now = SystemClock.uptimeMillis(); 923 924 verify(mHandler).sendMessageAtTime(any(), timeCaptor.capture()); 925 926 // The message time must be the expected delay time (as provided by the supplier) after 927 // the time the message was sent to the handler. We don't know that exact time, but we 928 // can put a bound on it - it's no sooner than immediately before the call to send(), and no 929 // later than immediately afterwards. Check to make sure the actual observed message time is 930 // somewhere in the valid range. 931 932 assertThat(timeCaptor.getValue()).isIn(Range.closed(then + systemDelay, now + systemDelay)); 933 } 934 935 @Test injectKeyEvent_throwsSecurityExceptionWithoutInjectEventsPermission()936 public void injectKeyEvent_throwsSecurityExceptionWithoutInjectEventsPermission() { 937 // Arrange 938 doReturn(PackageManager.PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission( 939 android.Manifest.permission.INJECT_EVENTS); 940 941 long currentTime = SystemClock.uptimeMillis(); 942 KeyEvent event = new KeyEvent(/* downTime= */ currentTime, 943 /* eventTime= */ currentTime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 944 /* repeat= */ 0); 945 946 // Act and assert 947 Assert.assertThrows(SecurityException.class, 948 () -> mCarInputService.injectKeyEvent(event, 949 CarOccupantZoneManager.DISPLAY_TYPE_MAIN)); 950 } 951 952 @Test injectKeyEvent_delegatesToOnKeyEvent()953 public void injectKeyEvent_delegatesToOnKeyEvent() { 954 long currentTime = SystemClock.uptimeMillis(); 955 KeyEvent event = new KeyEvent(/* downTime= */ currentTime, 956 /* eventTime= */ currentTime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 957 /* repeat= */ 0); 958 event.setDisplayId(INVALID_DISPLAY); 959 960 injectKeyEventAndVerify(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN); 961 962 verify(mDefaultKeyEventMainListener).onKeyEvent(same(event), 963 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), anyInt()); 964 verify(mInstrumentClusterKeyListener, never()).onKeyEvent(same(event)); 965 } 966 967 @Test injectKeyEvent_sendingKeyEventWithDefaultDisplayAgainstClusterDisplayType()968 public void injectKeyEvent_sendingKeyEventWithDefaultDisplayAgainstClusterDisplayType() { 969 long currentTime = SystemClock.uptimeMillis(); 970 KeyEvent event = new KeyEvent(/* downTime= */ currentTime, 971 /* eventTime= */ currentTime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 972 /* repeat= */ 0); 973 event.setDisplayId(INVALID_DISPLAY); 974 975 injectKeyEventAndVerify(event, CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER); 976 977 verify(mDefaultKeyEventMainListener, never()).onKeyEvent(same(event), anyInt(), anyInt()); 978 verify(mInstrumentClusterKeyListener).onKeyEvent(same(event)); 979 } 980 injectKeyEventAndVerify(KeyEvent event, @DisplayTypeEnum int displayType)981 private void injectKeyEventAndVerify(KeyEvent event, @DisplayTypeEnum int displayType) { 982 doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission( 983 android.Manifest.permission.INJECT_EVENTS); 984 int someDisplayId = Integer.MAX_VALUE; 985 when(mCarOccupantZoneService.getDisplayIdForDriver(anyInt())).thenReturn(someDisplayId); 986 assertThat(event.getDisplayId()).isNotEqualTo(someDisplayId); 987 988 mCarInputService.injectKeyEvent(event, displayType); 989 990 verify(mCarOccupantZoneService).getDisplayIdForDriver(displayType); 991 assertWithMessage("Event's display id not updated as expected").that( 992 event.getDisplayId()).isEqualTo(someDisplayId); 993 } 994 995 @Test onKey_assignDisplayId_mainDisplay()996 public void onKey_assignDisplayId_mainDisplay() { 997 // Act 998 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_HOME, Display.MAIN); 999 1000 // Arrange 1001 assertWithMessage("display id expected to be assigned with Display.DEFAULT_DISPLAY").that( 1002 event.getDisplayId()).isEqualTo(DEFAULT_DISPLAY); 1003 } 1004 1005 @Test onKey_assignDisplayId_clusterDisplay()1006 public void onKey_assignDisplayId_clusterDisplay() { 1007 // Act 1008 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_HOME, Display.INSTRUMENT_CLUSTER); 1009 1010 // Arrange 1011 assertWithMessage("display id expected to be assigned with Display.DEFAULT_DISPLAY").that( 1012 event.getDisplayId()).isEqualTo(DEFAULT_DISPLAY); 1013 } 1014 1015 @Test onKeyEvent_unknownSeat_throwsException()1016 public void onKeyEvent_unknownSeat_throwsException() { 1017 long currentTime = SystemClock.uptimeMillis(); 1018 KeyEvent event = new KeyEvent(/* downTime= */ currentTime, 1019 /* eventTime= */ currentTime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A, 1020 /* repeat= */ 0); 1021 1022 IllegalArgumentException thrown = Assert.assertThrows(IllegalArgumentException.class, 1023 () -> mCarInputService.onKeyEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN, 1024 UNKNOWN_SEAT)); 1025 1026 assertWithMessage("Sent key event with unknown seat exception") 1027 .that(thrown).hasMessageThat() 1028 .contains("Unknown seat"); 1029 } 1030 1031 @Test onKeyEvent_keyCodeAtoDriverSeat_triggersDefaultKeyEventMainListener()1032 public void onKeyEvent_keyCodeAtoDriverSeat_triggersDefaultKeyEventMainListener() { 1033 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(false); 1034 1035 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_A, Display.MAIN, DRIVER_SEAT); 1036 1037 verify(mCarPowerManagementService).notifyUserActivity(eq(DRIVER_DISPLAY_ID), anyLong()); 1038 verify(mDefaultKeyEventMainListener).onKeyEvent(same(event), 1039 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), eq(DRIVER_SEAT)); 1040 } 1041 1042 @Test onKeyEvent_keyCodeAtoPassengerSeat_triggersDefaultKeyEventMainListener()1043 public void onKeyEvent_keyCodeAtoPassengerSeat_triggersDefaultKeyEventMainListener() { 1044 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(false); 1045 1046 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_A, Display.MAIN, PASSENGER_SEAT); 1047 1048 verify(mCarPowerManagementService).notifyUserActivity(eq(PASSENGER_DISPLAY_ID), anyLong()); 1049 verify(mDefaultKeyEventMainListener).onKeyEvent(same(event), 1050 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), eq(PASSENGER_SEAT)); 1051 } 1052 1053 @Test onKeyEvent_homeKeyUpToDriverSeat_triggersDefaultKeyEventMainListener()1054 public void onKeyEvent_homeKeyUpToDriverSeat_triggersDefaultKeyEventMainListener() { 1055 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(false); 1056 1057 KeyEvent event = send(Key.UP, KeyEvent.KEYCODE_HOME, Display.MAIN, DRIVER_SEAT); 1058 1059 verify(mDefaultKeyEventMainListener).onKeyEvent(same(event), 1060 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), eq(DRIVER_SEAT)); 1061 verify(() -> CarServiceUtils.startHomeForUserAndDisplay(any(), anyInt(), anyInt()), 1062 never()); 1063 } 1064 1065 @Test onKeyEvent_homeKeyUpToInvalidSeat_doesNothing()1066 public void onKeyEvent_homeKeyUpToInvalidSeat_doesNothing() { 1067 int invalidSeat = -1; 1068 when(mCarOccupantZoneService.getOccupantZoneIdForSeat(eq(invalidSeat))) 1069 .thenReturn(OccupantZoneInfo.INVALID_ZONE_ID); 1070 1071 send(Key.UP, KeyEvent.KEYCODE_HOME, Display.MAIN, invalidSeat); 1072 1073 verify(() -> CarServiceUtils.startHomeForUserAndDisplay(any(), anyInt(), anyInt()), 1074 never()); 1075 } 1076 1077 @Test onKeyEvent_homeKeyDownToPassengerSeat_doesNothing()1078 public void onKeyEvent_homeKeyDownToPassengerSeat_doesNothing() { 1079 send(Key.DOWN, KeyEvent.KEYCODE_HOME, Display.MAIN, PASSENGER_SEAT); 1080 1081 verify(() -> CarServiceUtils.startHomeForUserAndDisplay(any(), anyInt(), anyInt()), 1082 never()); 1083 } 1084 1085 @Test onKeyEvent_homeKeyUpToPassengerSeat_triggersStartyHome()1086 public void onKeyEvent_homeKeyUpToPassengerSeat_triggersStartyHome() { 1087 doAnswer((invocation) -> null).when(() -> CarServiceUtils.startHomeForUserAndDisplay( 1088 any(Context.class), anyInt(), anyInt())); 1089 1090 send(Key.UP, KeyEvent.KEYCODE_HOME, Display.MAIN, PASSENGER_SEAT); 1091 1092 verify(() -> CarServiceUtils.startHomeForUserAndDisplay(mContext, PASSENGER_USER_ID, 1093 PASSENGER_DISPLAY_ID)); 1094 } 1095 1096 @Test onKeyEvent_powerKeyUpToDriverSeat_triggersDefaultKeyEventMainListener()1097 public void onKeyEvent_powerKeyUpToDriverSeat_triggersDefaultKeyEventMainListener() { 1098 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(false); 1099 1100 KeyEvent event = send(Key.UP, KeyEvent.KEYCODE_POWER, Display.MAIN, DRIVER_SEAT); 1101 1102 verify(mDefaultKeyEventMainListener).onKeyEvent(same(event), 1103 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), eq(DRIVER_SEAT)); 1104 verify(mCarPowerManagementService, never()).setDisplayPowerState(anyInt(), anyBoolean()); 1105 } 1106 1107 @Test onKeyEvent_powerKeyDownToPassengerSeatWhenDisplayOn_doesNothing()1108 public void onKeyEvent_powerKeyDownToPassengerSeatWhenDisplayOn_doesNothing() { 1109 injectPowerKeyEventToSeat(Key.DOWN, /* isOn= */ true, PASSENGER_DISPLAY_ID, PASSENGER_SEAT); 1110 1111 verify(mCarPowerManagementService, never()).setDisplayPowerState(anyInt(), anyBoolean()); 1112 } 1113 1114 @Test onKeyEvent_powerKeyDownToPassengerSeatWhenDisplayOff_setsDisplayPowerOn()1115 public void onKeyEvent_powerKeyDownToPassengerSeatWhenDisplayOff_setsDisplayPowerOn() { 1116 injectPowerKeyEventToSeat(Key.DOWN, /* isOn= */ false, PASSENGER_DISPLAY_ID, 1117 PASSENGER_SEAT); 1118 1119 boolean expectedDisplaySetStatus = true; 1120 verify(mCarPowerManagementService).setDisplayPowerState(eq(PASSENGER_DISPLAY_ID), 1121 eq(expectedDisplaySetStatus)); 1122 } 1123 1124 @Test onKeyEvent_powerKeyUpToPassengerSeatWhenDisplayOn_setsDisplayPowerOff()1125 public void onKeyEvent_powerKeyUpToPassengerSeatWhenDisplayOn_setsDisplayPowerOff() { 1126 injectPowerKeyEventToSeat(Key.UP, /* isOn= */ true, PASSENGER_DISPLAY_ID, PASSENGER_SEAT); 1127 1128 boolean expectedDisplaySetStatus = false; 1129 verify(mCarPowerManagementService).setDisplayPowerState(eq(PASSENGER_DISPLAY_ID), 1130 eq(expectedDisplaySetStatus)); 1131 } 1132 1133 @Test onKeyEvent_powerKeyUpToPassengerSeatWhenDisplayOff_doesNothing()1134 public void onKeyEvent_powerKeyUpToPassengerSeatWhenDisplayOff_doesNothing() { 1135 injectPowerKeyEventToSeat(Key.UP, /* isOn= */ false, PASSENGER_DISPLAY_ID, PASSENGER_SEAT); 1136 1137 verify(mCarPowerManagementService, never()).setDisplayPowerState(anyInt(), anyBoolean()); 1138 } 1139 1140 @Test onKeyEvent_powerKeyDownToPassengerSeatWhenInvalidDisplayId_doesNothing()1141 public void onKeyEvent_powerKeyDownToPassengerSeatWhenInvalidDisplayId_doesNothing() { 1142 when(mCarOccupantZoneService.getDisplayForOccupant(eq(PASSENGER_ZONE_ID), 1143 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN))) 1144 .thenReturn(INVALID_DISPLAY); 1145 1146 send(Key.DOWN, KeyEvent.KEYCODE_POWER, Display.MAIN, PASSENGER_SEAT); 1147 1148 verify(mCarPowerManagementService, never()).setDisplayPowerState(anyInt(), anyBoolean()); 1149 } 1150 injectPowerKeyEventToSeat(Key action, boolean isOn, int displayId, int seat)1151 private KeyEvent injectPowerKeyEventToSeat(Key action, boolean isOn, int displayId, int seat) { 1152 when(mSystemInterface.isDisplayEnabled(eq(displayId))).thenReturn(isOn); 1153 1154 return send(action, KeyEvent.KEYCODE_POWER, Display.MAIN, seat); 1155 } 1156 1157 @Test onMotionEvent_injectsMotionEventToDriverSeat()1158 public void onMotionEvent_injectsMotionEventToDriverSeat() { 1159 long currentTime = SystemClock.uptimeMillis(); 1160 MotionEvent event = MotionEvent.obtain( 1161 /* downTime= */ currentTime, 1162 /* eventTime= */ currentTime, 1163 MotionEvent.ACTION_DOWN, 1164 /* X coordinate= */ 0.0f, 1165 /* Y coordinate= */ 0.0f, 1166 /* metaState= */ 0); 1167 1168 mCarInputService.onMotionEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN, 1169 DRIVER_SEAT); 1170 1171 verify(mDefaultMotionEventMainListener).onMotionEvent(same(event)); 1172 verify(mCarPowerManagementService).notifyUserActivity(eq(DRIVER_DISPLAY_ID), 1173 eq(currentTime)); 1174 } 1175 1176 @Test onMotionEvent_injectsMotionEventToPassengerSeat()1177 public void onMotionEvent_injectsMotionEventToPassengerSeat() { 1178 long currentTime = SystemClock.uptimeMillis(); 1179 MotionEvent event = MotionEvent.obtain( 1180 /* downTime= */ currentTime, 1181 /* eventTime= */ currentTime, 1182 MotionEvent.ACTION_DOWN, 1183 /* X coordinate= */ 0.0f, 1184 /* Y coordinate= */ 0.0f, 1185 /* metaState= */ 0); 1186 1187 mCarInputService.onMotionEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN, 1188 PASSENGER_SEAT); 1189 1190 verify(mDefaultMotionEventMainListener).onMotionEvent(same(event)); 1191 verify(mCarPowerManagementService).notifyUserActivity(eq(PASSENGER_DISPLAY_ID), 1192 eq(currentTime)); 1193 } 1194 1195 @Test onMotionEvent_unknownSeat_throwsException()1196 public void onMotionEvent_unknownSeat_throwsException() { 1197 long currentTime = SystemClock.uptimeMillis(); 1198 MotionEvent event = MotionEvent.obtain( 1199 /* downTime= */ currentTime, 1200 /* eventTime= */ currentTime, 1201 MotionEvent.ACTION_DOWN, 1202 /* X coordinate= */ 0.0f, 1203 /* Y coordinate= */ 0.0f, 1204 /* metaState= */ 0); 1205 1206 IllegalArgumentException thrown = Assert.assertThrows(IllegalArgumentException.class, 1207 () -> mCarInputService.onMotionEvent(event, 1208 CarOccupantZoneManager.DISPLAY_TYPE_MAIN, UNKNOWN_SEAT)); 1209 1210 assertWithMessage("Sent motion event with unknown seat exception") 1211 .that(thrown).hasMessageThat() 1212 .contains("Unknown seat"); 1213 } 1214 1215 @Test injectKeyEventForDriver_delegatesToOnKeyEvent()1216 public void injectKeyEventForDriver_delegatesToOnKeyEvent() { 1217 doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission( 1218 android.Manifest.permission.INJECT_EVENTS); 1219 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(false); 1220 long currentTime = SystemClock.uptimeMillis(); 1221 KeyEvent event = new KeyEvent( 1222 /* downTime= */ currentTime, 1223 /* eventTime= */ currentTime, 1224 KeyEvent.ACTION_DOWN, 1225 KeyEvent.KEYCODE_ENTER, 1226 /* repeat= */ 0); 1227 1228 mCarInputService.injectKeyEventForSeat(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN, 1229 DRIVER_SEAT); 1230 1231 verify(mDefaultKeyEventMainListener).onKeyEvent(same(event), 1232 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), eq(DRIVER_SEAT)); 1233 } 1234 1235 @Test injectKeyEventForDriver_throwsSecurityExceptionWithoutInjectEventsPermission()1236 public void injectKeyEventForDriver_throwsSecurityExceptionWithoutInjectEventsPermission() { 1237 doReturn(PackageManager.PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission( 1238 android.Manifest.permission.INJECT_EVENTS); 1239 long currentTime = SystemClock.uptimeMillis(); 1240 KeyEvent event = new KeyEvent( 1241 /* downTime= */ currentTime, 1242 /* eventTime= */ currentTime, 1243 KeyEvent.ACTION_DOWN, 1244 KeyEvent.KEYCODE_ENTER, 1245 /* repeat= */ 0); 1246 1247 // Act and assert 1248 Assert.assertThrows(SecurityException.class, 1249 () -> mCarInputService.injectKeyEventForSeat(event, 1250 CarOccupantZoneManager.DISPLAY_TYPE_MAIN, 1251 DRIVER_SEAT)); 1252 } 1253 1254 @Test injectMotionEventForDriver_delegatesToOnKeyEvent()1255 public void injectMotionEventForDriver_delegatesToOnKeyEvent() { 1256 doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission( 1257 android.Manifest.permission.INJECT_EVENTS); 1258 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(false); 1259 long currentTime = SystemClock.uptimeMillis(); 1260 MotionEvent event = MotionEvent.obtain( 1261 /* downTime= */ currentTime, 1262 /* eventTime= */ currentTime, 1263 MotionEvent.ACTION_DOWN, 1264 /* X coordinate= */ 0.0f, 1265 /* Y coordinate= */ 0.0f, 1266 /* metaState= */ 0); 1267 1268 mCarInputService.injectMotionEventForSeat(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN, 1269 DRIVER_SEAT); 1270 1271 verify(mDefaultMotionEventMainListener).onMotionEvent(event); 1272 } 1273 1274 @Test injectMotionEventForDriver_throwsSecurityExceptionWithoutInjectEventsPermission()1275 public void injectMotionEventForDriver_throwsSecurityExceptionWithoutInjectEventsPermission() { 1276 doReturn(PackageManager.PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission( 1277 android.Manifest.permission.INJECT_EVENTS); 1278 1279 long currentTime = SystemClock.uptimeMillis(); 1280 MotionEvent event = MotionEvent.obtain( 1281 /* downTime= */ currentTime, 1282 /* eventTime= */ currentTime, 1283 MotionEvent.ACTION_DOWN, 1284 /* X coordinate= */ 0.0f, 1285 /* Y coordinate= */ 0.0f, 1286 /* metaState= */ 0); 1287 1288 Assert.assertThrows(SecurityException.class, 1289 () -> mCarInputService.injectMotionEventForSeat(event, 1290 CarOccupantZoneManager.DISPLAY_TYPE_MAIN, 1291 DRIVER_SEAT)); 1292 } 1293 1294 private enum Key {DOWN, UP} 1295 1296 private enum Display {MAIN, INSTRUMENT_CLUSTER} 1297 send(Key action, int keyCode, Display display)1298 private KeyEvent send(Key action, int keyCode, Display display) { 1299 return sendWithRepeat(action, keyCode, display, 0); 1300 } 1301 sendWithRepeat(Key action, int keyCode, Display display, int repeatCount)1302 private KeyEvent sendWithRepeat(Key action, int keyCode, Display display, int repeatCount) { 1303 KeyEvent event = new KeyEvent( 1304 /* downTime= */ 0L, 1305 /* eventTime= */ 0L, 1306 action == Key.DOWN ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP, 1307 keyCode, 1308 repeatCount); 1309 event.setDisplayId(INVALID_DISPLAY); 1310 mCarInputService.onKeyEvent( 1311 event, 1312 display == Display.MAIN 1313 ? CarOccupantZoneManager.DISPLAY_TYPE_MAIN 1314 : CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER); 1315 return event; 1316 } 1317 send(Key action, int keyCode, Display display, int seat)1318 private KeyEvent send(Key action, int keyCode, Display display, int seat) { 1319 return sendWithRepeatAndSeat(action, keyCode, display, 0, seat); 1320 } 1321 sendWithRepeatAndSeat(Key action, int keyCode, Display display, int repeatCount, int seat)1322 private KeyEvent sendWithRepeatAndSeat(Key action, int keyCode, Display display, 1323 int repeatCount, int seat) { 1324 KeyEvent event = new KeyEvent( 1325 /* downTime= */ 0L, 1326 /* eventTime= */ 0L, 1327 action == Key.DOWN ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP, 1328 keyCode, 1329 repeatCount); 1330 event.setDisplayId(INVALID_DISPLAY); 1331 mCarInputService.onKeyEvent( 1332 event, 1333 display == Display.MAIN 1334 ? CarOccupantZoneManager.DISPLAY_TYPE_MAIN 1335 : CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER, 1336 seat); 1337 return event; 1338 } 1339 registerProjectionKeyEventHandler( int... events)1340 private CarProjectionManager.ProjectionKeyEventHandler registerProjectionKeyEventHandler( 1341 int... events) { 1342 BitSet eventSet = new BitSet(); 1343 for (int event : events) { 1344 eventSet.set(event); 1345 } 1346 1347 CarProjectionManager.ProjectionKeyEventHandler projectionKeyEventHandler = 1348 mock(CarProjectionManager.ProjectionKeyEventHandler.class); 1349 mCarInputService.setProjectionKeyEventHandler(projectionKeyEventHandler, eventSet); 1350 return projectionKeyEventHandler; 1351 } 1352 flushHandler()1353 private void flushHandler() { 1354 ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); 1355 1356 verify(mHandler, atLeast(0)).sendMessageAtTime(messageCaptor.capture(), anyLong()); 1357 1358 for (Message message : messageCaptor.getAllValues()) { 1359 mHandler.dispatchMessage(message); 1360 } 1361 1362 clearInvocations(mHandler); 1363 } 1364 mockEnableLongPressBluetoothVoiceRecognitionProperty(boolean enabledOrNot)1365 private void mockEnableLongPressBluetoothVoiceRecognitionProperty(boolean enabledOrNot) { 1366 when(mMockResources.getBoolean(R.bool.enableLongPressBluetoothVoiceRecognition)) 1367 .thenReturn(enabledOrNot); 1368 } 1369 } 1370