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