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.compatibility.common.util.SystemUtil.eventually; 23 24 import static com.google.common.truth.Truth.assertThat; 25 import static com.google.common.truth.Truth.assertWithMessage; 26 27 import static org.mockito.ArgumentMatchers.any; 28 import static org.mockito.ArgumentMatchers.anyInt; 29 import static org.mockito.ArgumentMatchers.anyLong; 30 import static org.mockito.ArgumentMatchers.eq; 31 import static org.mockito.ArgumentMatchers.isNull; 32 import static org.mockito.ArgumentMatchers.same; 33 import static org.mockito.Mockito.atLeast; 34 import static org.mockito.Mockito.clearInvocations; 35 import static org.mockito.Mockito.doNothing; 36 import static org.mockito.Mockito.doReturn; 37 import static org.mockito.Mockito.ignoreStubs; 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.verify; 42 import static org.mockito.Mockito.verifyNoMoreInteractions; 43 import static org.mockito.Mockito.when; 44 45 import android.bluetooth.BluetoothAdapter; 46 import android.bluetooth.BluetoothProfile; 47 import android.car.CarOccupantZoneManager; 48 import android.car.CarProjectionManager; 49 import android.car.input.CarInputManager; 50 import android.car.input.CustomInputEvent; 51 import android.car.input.ICarInputCallback; 52 import android.car.input.RotaryEvent; 53 import android.content.ComponentName; 54 import android.content.Context; 55 import android.content.Intent; 56 import android.content.pm.PackageManager; 57 import android.net.Uri; 58 import android.os.Bundle; 59 import android.os.Handler; 60 import android.os.Looper; 61 import android.os.Message; 62 import android.os.SystemClock; 63 import android.os.UserHandle; 64 import android.service.voice.VoiceInteractionSession; 65 import android.telecom.TelecomManager; 66 import android.view.KeyEvent; 67 68 import androidx.test.core.app.ApplicationProvider; 69 70 import com.android.car.hal.InputHalService; 71 import com.android.car.user.CarUserService; 72 import com.android.internal.app.AssistUtils; 73 74 import com.google.common.collect.Range; 75 76 import org.junit.After; 77 import org.junit.Before; 78 import org.junit.Test; 79 import org.junit.runner.RunWith; 80 import org.mockito.ArgumentCaptor; 81 import org.mockito.Mock; 82 import org.mockito.Spy; 83 import org.mockito.junit.MockitoJUnitRunner; 84 import org.testng.Assert; 85 86 import java.util.BitSet; 87 import java.util.function.BooleanSupplier; 88 import java.util.function.IntSupplier; 89 import java.util.function.Supplier; 90 91 @RunWith(MockitoJUnitRunner.class) 92 public class CarInputServiceTest { 93 94 @Mock InputHalService mInputHalService; 95 @Mock TelecomManager mTelecomManager; 96 @Mock AssistUtils mAssistUtils; 97 @Mock CarInputService.KeyEventListener mDefaultMainListener; 98 @Mock CarInputService.KeyEventListener mInstrumentClusterKeyListener; 99 @Mock Supplier<String> mLastCallSupplier; 100 @Mock IntSupplier mLongPressDelaySupplier; 101 @Mock BooleanSupplier mShouldCallButtonEndOngoingCallSupplier; 102 @Mock InputCaptureClientController mCaptureController; 103 @Mock CarOccupantZoneService mCarOccupantZoneService; 104 @Mock BluetoothAdapter mBluetoothAdapter; 105 106 @Spy Context mContext = ApplicationProvider.getApplicationContext(); 107 @Spy Handler mHandler = new Handler(Looper.getMainLooper()); 108 109 @Mock CarUserService mCarUserService; 110 private CarInputService mCarInputService; 111 112 @Before setUp()113 public void setUp() { 114 mCarInputService = new CarInputService(mContext, mInputHalService, mCarUserService, 115 mCarOccupantZoneService, mHandler, mTelecomManager, mAssistUtils, 116 mDefaultMainListener, mLastCallSupplier, mLongPressDelaySupplier, 117 mShouldCallButtonEndOngoingCallSupplier, mCaptureController, mBluetoothAdapter); 118 mCarInputService.setInstrumentClusterKeyListener(mInstrumentClusterKeyListener); 119 120 when(mInputHalService.isKeyInputSupported()).thenReturn(true); 121 mCarInputService.init(); 122 123 // Delay Handler callbacks until flushHandler() is called. 124 doReturn(true).when(mHandler).sendMessageAtTime(any(), anyLong()); 125 126 when(mShouldCallButtonEndOngoingCallSupplier.getAsBoolean()).thenReturn(false); 127 } 128 129 @After tearDown()130 public void tearDown() { 131 if (mCarInputService != null) { 132 mCarInputService.release(); 133 } 134 } 135 136 @Test testOnRotaryEvent_injectingRotaryNavigationEvent()137 public void testOnRotaryEvent_injectingRotaryNavigationEvent() { 138 RotaryEvent event = new RotaryEvent( 139 /* inputType= */ CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION, 140 /* clockwise= */ true, 141 /* uptimeMillisForClicks= */ new long[]{1, 1}); 142 when(mCaptureController.onRotaryEvent( 143 same(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), same(event))).thenReturn(true); 144 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(true); 145 146 mCarInputService.onRotaryEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN); 147 148 // Since mCaptureController processed RotaryEvent, then no KeyEvent was generated or 149 // processed 150 verify(mCarOccupantZoneService, never()).getDisplayIdForDriver(anyInt()); 151 verify(mCaptureController, never()).onKeyEvent(anyInt(), any(KeyEvent.class)); 152 verify(mDefaultMainListener, never()).onKeyEvent(any(KeyEvent.class)); 153 } 154 155 @Test testOnRotaryEvent_injectingRotaryVolumeEvent()156 public void testOnRotaryEvent_injectingRotaryVolumeEvent() { 157 RotaryEvent event = new RotaryEvent( 158 /* inputType= */ CarInputManager.INPUT_TYPE_ROTARY_VOLUME, 159 /* clockwise= */ true, 160 /* uptimeMillisForClicks= */ new long[]{1, 1}); 161 when(mCaptureController.onRotaryEvent( 162 same(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), same(event))).thenReturn(false); 163 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(true); 164 165 mCarInputService.onRotaryEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN); 166 167 // Since mCaptureController processed RotaryEvent, then KeyEvent was generated or 168 // processed 169 int numberOfGeneratedKeyEvents = 4; 170 verify(mCarOccupantZoneService, times(numberOfGeneratedKeyEvents)).getDisplayIdForDriver( 171 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN)); 172 verify(mCaptureController, times(numberOfGeneratedKeyEvents)).onKeyEvent(anyInt(), 173 any(KeyEvent.class)); 174 verify(mDefaultMainListener, never()).onKeyEvent(any(KeyEvent.class)); 175 } 176 177 @Test testOnRotaryEvent_injectingRotaryNavigation_notConsumedByCaptureController()178 public void testOnRotaryEvent_injectingRotaryNavigation_notConsumedByCaptureController() { 179 RotaryEvent event = new RotaryEvent( 180 /* inputType= */ CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION, 181 /* clockwise= */ true, 182 /* uptimeMillisForClicks= */ new long[]{1, 1}); 183 when(mCaptureController.onRotaryEvent( 184 same(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), same(event))).thenReturn(false); 185 186 mCarInputService.onRotaryEvent(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN); 187 188 // Since mCaptureController processed RotaryEvent, then KeyEvent was generated or 189 // processed 190 int numberOfGeneratedKeyEvents = 4; 191 verify(mCarOccupantZoneService, times(numberOfGeneratedKeyEvents)).getDisplayIdForDriver( 192 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN)); 193 verify(mCaptureController, times(numberOfGeneratedKeyEvents)).onKeyEvent(anyInt(), 194 any(KeyEvent.class)); 195 verify(mDefaultMainListener, times(numberOfGeneratedKeyEvents)).onKeyEvent( 196 any(KeyEvent.class)); 197 } 198 199 @Test testRequestInputEventCapture_delegatesToCaptureController()200 public void testRequestInputEventCapture_delegatesToCaptureController() { 201 ICarInputCallback callback = mock(ICarInputCallback.class); 202 int[] inputTypes = new int[]{CarInputManager.INPUT_TYPE_CUSTOM_INPUT_EVENT}; 203 int requestFlags = CarInputManager.CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT; 204 mCarInputService.requestInputEventCapture(callback, 205 CarOccupantZoneManager.DISPLAY_TYPE_MAIN, inputTypes, requestFlags); 206 207 verify(mCaptureController).requestInputEventCapture(same(callback), 208 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN), same(inputTypes), eq(requestFlags)); 209 } 210 211 @Test testOnCustomInputEvent_delegatesToCaptureController()212 public void testOnCustomInputEvent_delegatesToCaptureController() { 213 CustomInputEvent event = new CustomInputEvent(INPUT_CODE_F1, 214 CarOccupantZoneManager.DISPLAY_TYPE_MAIN, /* repeatCounter= */ 1); 215 216 mCarInputService.onCustomInputEvent(event); 217 218 verify(mCaptureController).onCustomInputEvent(same(event)); 219 } 220 221 @Test testReleaseInputEventCapture_delegatesToCaptureController()222 public void testReleaseInputEventCapture_delegatesToCaptureController() { 223 ICarInputCallback callback = mock(ICarInputCallback.class); 224 mCarInputService.releaseInputEventCapture(callback, 225 CarOccupantZoneManager.DISPLAY_TYPE_MAIN); 226 227 verify(mCaptureController).releaseInputEventCapture(same(callback), 228 eq(CarOccupantZoneManager.DISPLAY_TYPE_MAIN)); 229 } 230 231 @Test ensureBluetoothAdapterWasInitialized()232 public void ensureBluetoothAdapterWasInitialized() { 233 eventually(() -> verify(mBluetoothAdapter).getProfileProxy(same(mContext), 234 any(BluetoothProfile.ServiceListener.class), 235 same(BluetoothProfile.HEADSET_CLIENT))); 236 } 237 238 @Test ordinaryEvents_onMainDisplay_routedToInputManager()239 public void ordinaryEvents_onMainDisplay_routedToInputManager() { 240 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.MAIN); 241 242 verify(mDefaultMainListener).onKeyEvent(event); 243 } 244 245 @Test ordinaryEvents_onInstrumentClusterDisplay_notRoutedToInputManager()246 public void ordinaryEvents_onInstrumentClusterDisplay_notRoutedToInputManager() { 247 send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.INSTRUMENT_CLUSTER); 248 249 verify(mDefaultMainListener, never()).onKeyEvent(any()); 250 } 251 252 @Test ordinaryEvents_onInstrumentClusterDisplay_routedToListener()253 public void ordinaryEvents_onInstrumentClusterDisplay_routedToListener() { 254 CarInputService.KeyEventListener listener = mock(CarInputService.KeyEventListener.class); 255 mCarInputService.setInstrumentClusterKeyListener(listener); 256 257 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.INSTRUMENT_CLUSTER); 258 verify(listener).onKeyEvent(event); 259 } 260 261 @Test customEventHandler_capturesDisplayMainEvent_capturedByInputController()262 public void customEventHandler_capturesDisplayMainEvent_capturedByInputController() { 263 CarInputService.KeyEventListener instrumentClusterListener = 264 setupInstrumentClusterListener(); 265 266 // Assume mCaptureController will consume every event. 267 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(true); 268 269 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.MAIN); 270 verify(instrumentClusterListener, never()).onKeyEvent(any(KeyEvent.class)); 271 verify(mCaptureController).onKeyEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, event); 272 verify(mDefaultMainListener, never()).onKeyEvent(any(KeyEvent.class)); 273 } 274 275 @Test customEventHandler_capturesDisplayMainEvent_missedByInputController()276 public void customEventHandler_capturesDisplayMainEvent_missedByInputController() { 277 CarInputService.KeyEventListener instrumentClusterListener = 278 setupInstrumentClusterListener(); 279 280 // Assume mCaptureController will consume every event. 281 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(false); 282 283 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.MAIN); 284 verify(instrumentClusterListener, never()).onKeyEvent(any(KeyEvent.class)); 285 verify(mCaptureController).onKeyEvent(anyInt(), any(KeyEvent.class)); 286 verify(mDefaultMainListener).onKeyEvent(event); 287 } 288 289 @Test customEventHandler_capturesClusterEvents_capturedByInstrumentCluster()290 public void customEventHandler_capturesClusterEvents_capturedByInstrumentCluster() { 291 CarInputService.KeyEventListener instrumentClusterListener = 292 setupInstrumentClusterListener(); 293 294 // Assume mCaptureController will consume every event. 295 when(mCaptureController.onKeyEvent(anyInt(), any(KeyEvent.class))).thenReturn(true); 296 297 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.INSTRUMENT_CLUSTER); 298 verify(instrumentClusterListener).onKeyEvent(event); 299 verify(mCaptureController, never()).onKeyEvent(anyInt(), any(KeyEvent.class)); 300 verify(mDefaultMainListener, never()).onKeyEvent(any(KeyEvent.class)); 301 } 302 setupInstrumentClusterListener()303 private CarInputService.KeyEventListener setupInstrumentClusterListener() { 304 CarInputService.KeyEventListener instrumentClusterListener = 305 mock(CarInputService.KeyEventListener.class); 306 mCarInputService.setInstrumentClusterKeyListener(instrumentClusterListener); 307 return instrumentClusterListener; 308 } 309 310 @Test voiceKey_shortPress_withRegisteredEventHandler_triggersEventHandler()311 public void voiceKey_shortPress_withRegisteredEventHandler_triggersEventHandler() { 312 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 313 registerProjectionKeyEventHandler( 314 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP); 315 316 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 317 send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 318 319 verify(eventHandler) 320 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP); 321 } 322 323 @Test voiceKey_longPress_withRegisteredEventHandler_triggersEventHandler()324 public void voiceKey_longPress_withRegisteredEventHandler_triggersEventHandler() { 325 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 326 registerProjectionKeyEventHandler( 327 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP, 328 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_DOWN); 329 330 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 331 verify(eventHandler, never()).onKeyEvent(anyInt()); 332 333 // Simulate the long-press timer expiring. 334 flushHandler(); 335 verify(eventHandler) 336 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_DOWN); 337 338 // Ensure that the short-press handler is *not* called. 339 send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 340 flushHandler(); 341 verify(eventHandler, never()) 342 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP); 343 } 344 345 @Test voiceKey_shortPress_withoutRegisteredEventHandler_triggersAssistUtils()346 public void voiceKey_shortPress_withoutRegisteredEventHandler_triggersAssistUtils() { 347 when(mAssistUtils.getAssistComponentForUser(anyInt())) 348 .thenReturn(new ComponentName("pkg", "cls")); 349 350 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 351 send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 352 353 ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); 354 verify(mAssistUtils).showSessionForActiveService( 355 bundleCaptor.capture(), 356 eq(VoiceInteractionSession.SHOW_SOURCE_PUSH_TO_TALK), 357 any(), 358 isNull()); 359 assertThat(bundleCaptor.getValue().getBoolean(CarInputService.EXTRA_CAR_PUSH_TO_TALK)) 360 .isTrue(); 361 } 362 363 @Test voiceKey_longPress_withoutRegisteredEventHandler_triggersAssistUtils()364 public void voiceKey_longPress_withoutRegisteredEventHandler_triggersAssistUtils() { 365 when(mAssistUtils.getAssistComponentForUser(anyInt())) 366 .thenReturn(new ComponentName("pkg", "cls")); 367 368 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 369 flushHandler(); 370 371 ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); 372 verify(mAssistUtils).showSessionForActiveService( 373 bundleCaptor.capture(), 374 eq(VoiceInteractionSession.SHOW_SOURCE_PUSH_TO_TALK), 375 any(), 376 isNull()); 377 assertThat(bundleCaptor.getValue().getBoolean(CarInputService.EXTRA_CAR_PUSH_TO_TALK)) 378 .isTrue(); 379 380 send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 381 verifyNoMoreInteractions(ignoreStubs(mAssistUtils)); 382 } 383 384 @Test voiceKey_keyDown_withEventHandler_triggersEventHandler()385 public void voiceKey_keyDown_withEventHandler_triggersEventHandler() { 386 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 387 registerProjectionKeyEventHandler( 388 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_KEY_DOWN); 389 390 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 391 392 verify(eventHandler).onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_KEY_DOWN); 393 } 394 395 @Test voiceKey_keyUp_afterLongPress_withEventHandler_triggersEventHandler()396 public void voiceKey_keyUp_afterLongPress_withEventHandler_triggersEventHandler() { 397 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 398 registerProjectionKeyEventHandler( 399 CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_UP); 400 401 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 402 flushHandler(); 403 verify(eventHandler, never()) 404 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_UP); 405 406 send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 407 verify(eventHandler) 408 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_UP); 409 } 410 411 @Test voiceKey_repeatedEvents_ignored()412 public void voiceKey_repeatedEvents_ignored() { 413 // Pressing a key starts the long-press timer. 414 send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN); 415 verify(mHandler).sendMessageAtTime(any(), anyLong()); 416 clearInvocations(mHandler); 417 418 // Repeated KEY_DOWN events don't reset the timer. 419 sendWithRepeat(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN, 1); 420 verify(mHandler, never()).sendMessageAtTime(any(), anyLong()); 421 } 422 423 @Test callKey_shortPress_withoutEventHandler_launchesDialer()424 public void callKey_shortPress_withoutEventHandler_launchesDialer() { 425 ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); 426 427 doNothing().when(mContext).startActivityAsUser(any(), any(), any()); 428 429 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 430 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 431 432 verify(mContext).startActivityAsUser( 433 intentCaptor.capture(), any(), eq(UserHandle.CURRENT_OR_SELF)); 434 assertThat(intentCaptor.getValue().getAction()).isEqualTo(Intent.ACTION_DIAL); 435 } 436 437 @Test callKey_shortPress_withoutEventHandler_whenCallRinging_answersCall()438 public void callKey_shortPress_withoutEventHandler_whenCallRinging_answersCall() { 439 when(mTelecomManager.isRinging()).thenReturn(true); 440 441 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 442 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 443 444 verify(mTelecomManager).acceptRingingCall(); 445 // Ensure default handler does not run. 446 verify(mContext, never()).startActivityAsUser(any(), any(), any()); 447 } 448 449 @Test callKey_shortPress_withEventHandler_triggersEventHandler()450 public void callKey_shortPress_withEventHandler_triggersEventHandler() { 451 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 452 registerProjectionKeyEventHandler( 453 CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP); 454 455 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 456 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 457 458 verify(eventHandler).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP); 459 // Ensure default handlers do not run. 460 verify(mTelecomManager, never()).acceptRingingCall(); 461 verify(mContext, never()).startActivityAsUser(any(), any(), any()); 462 } 463 464 @Test callKey_shortPress_withEventHandler_whenCallRinging_answersCall()465 public void callKey_shortPress_withEventHandler_whenCallRinging_answersCall() { 466 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 467 registerProjectionKeyEventHandler( 468 CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP); 469 when(mTelecomManager.isRinging()).thenReturn(true); 470 471 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 472 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 473 474 verify(mTelecomManager).acceptRingingCall(); 475 verify(eventHandler, never()).onKeyEvent(anyInt()); 476 } 477 478 @Test callKey_shortPress_duringCall_endCallViaCallButtonOn_endsCall()479 public void callKey_shortPress_duringCall_endCallViaCallButtonOn_endsCall() { 480 when(mShouldCallButtonEndOngoingCallSupplier.getAsBoolean()).thenReturn(true); 481 when(mTelecomManager.isInCall()).thenReturn(true); 482 483 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 484 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 485 486 verify(mTelecomManager).endCall(); 487 } 488 489 @Test callKey_shortPress_duringCall_endCallViaCallButtonOff_doesNotEndCall()490 public void callKey_shortPress_duringCall_endCallViaCallButtonOff_doesNotEndCall() { 491 when(mTelecomManager.isInCall()).thenReturn(true); 492 493 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 494 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 495 496 verify(mTelecomManager, never()).endCall(); 497 } 498 499 @Test callKey_longPress_withoutEventHandler_redialsLastCall()500 public void callKey_longPress_withoutEventHandler_redialsLastCall() { 501 ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); 502 503 when(mLastCallSupplier.get()).thenReturn("1234567890"); 504 doNothing().when(mContext).startActivityAsUser(any(), any(), any()); 505 506 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 507 flushHandler(); 508 509 verify(mContext).startActivityAsUser( 510 intentCaptor.capture(), any(), eq(UserHandle.CURRENT_OR_SELF)); 511 512 Intent intent = intentCaptor.getValue(); 513 assertThat(intent.getAction()).isEqualTo(Intent.ACTION_CALL); 514 assertThat(intent.getData()).isEqualTo(Uri.parse("tel:1234567890")); 515 516 clearInvocations(mContext); 517 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 518 verify(mContext, never()).startActivityAsUser(any(), any(), any()); 519 } 520 521 @Test callKey_longPress_withoutEventHandler_withNoLastCall_doesNothing()522 public void callKey_longPress_withoutEventHandler_withNoLastCall_doesNothing() { 523 when(mLastCallSupplier.get()).thenReturn(""); 524 525 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 526 flushHandler(); 527 528 verify(mContext, never()).startActivityAsUser(any(), any(), any()); 529 } 530 531 @Test callKey_longPress_withoutEventHandler_whenCallRinging_answersCall()532 public void callKey_longPress_withoutEventHandler_whenCallRinging_answersCall() { 533 when(mTelecomManager.isRinging()).thenReturn(true); 534 535 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 536 flushHandler(); 537 538 verify(mTelecomManager).acceptRingingCall(); 539 540 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 541 // Ensure that default handler does not run, either after accepting ringing call, 542 // or as a result of key-up. 543 verify(mContext, never()).startActivityAsUser(any(), any(), any()); 544 } 545 546 @Test callKey_longPress_withEventHandler_triggersEventHandler()547 public void callKey_longPress_withEventHandler_triggersEventHandler() { 548 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 549 registerProjectionKeyEventHandler( 550 CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN); 551 552 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 553 flushHandler(); 554 555 verify(eventHandler).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN); 556 verify(mContext, never()).startActivityAsUser(any(), any(), any()); 557 } 558 559 @Test callKey_longPress_withEventHandler_whenCallRinging_answersCall()560 public void callKey_longPress_withEventHandler_whenCallRinging_answersCall() { 561 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 562 registerProjectionKeyEventHandler( 563 CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN); 564 when(mTelecomManager.isRinging()).thenReturn(true); 565 566 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 567 flushHandler(); 568 569 verify(mTelecomManager).acceptRingingCall(); 570 571 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 572 // Ensure that event handler does not run, either after accepting ringing call, 573 // or as a result of key-up. 574 verify(eventHandler, never()).onKeyEvent(anyInt()); 575 } 576 577 @Test callKey_longPress_duringCall_endCallViaCallButtonOn_endsCall()578 public void callKey_longPress_duringCall_endCallViaCallButtonOn_endsCall() { 579 when(mShouldCallButtonEndOngoingCallSupplier.getAsBoolean()).thenReturn(true); 580 when(mTelecomManager.isInCall()).thenReturn(true); 581 582 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 583 flushHandler(); 584 585 verify(mTelecomManager).endCall(); 586 } 587 588 @Test callKey_longPress_duringCall_endCallViaCallButtonOff_doesNotEndCall()589 public void callKey_longPress_duringCall_endCallViaCallButtonOff_doesNotEndCall() { 590 when(mTelecomManager.isInCall()).thenReturn(true); 591 592 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 593 flushHandler(); 594 595 verify(mTelecomManager, never()).endCall(); 596 } 597 598 @Test callKey_keyDown_withEventHandler_triggersEventHandler()599 public void callKey_keyDown_withEventHandler_triggersEventHandler() { 600 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 601 registerProjectionKeyEventHandler( 602 CarProjectionManager.KEY_EVENT_CALL_KEY_DOWN); 603 604 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 605 606 verify(eventHandler).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_KEY_DOWN); 607 } 608 609 @Test callKey_keyUp_afterLongPress_withEventHandler_triggersEventHandler()610 public void callKey_keyUp_afterLongPress_withEventHandler_triggersEventHandler() { 611 CarProjectionManager.ProjectionKeyEventHandler eventHandler = 612 registerProjectionKeyEventHandler( 613 CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_UP); 614 615 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 616 flushHandler(); 617 verify(eventHandler, never()) 618 .onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_UP); 619 620 send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN); 621 verify(eventHandler).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_UP); 622 } 623 624 @Test callKey_repeatedEvents_ignored()625 public void callKey_repeatedEvents_ignored() { 626 // Pressing a key starts the long-press timer. 627 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 628 verify(mHandler).sendMessageAtTime(any(), anyLong()); 629 clearInvocations(mHandler); 630 631 // Repeated KEY_DOWN events don't reset the timer. 632 sendWithRepeat(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN, 1); 633 verify(mHandler, never()).sendMessageAtTime(any(), anyLong()); 634 } 635 636 @Test longPressDelay_obeysValueFromSystem()637 public void longPressDelay_obeysValueFromSystem() { 638 final int systemDelay = 4242; 639 640 when(mLongPressDelaySupplier.getAsInt()).thenReturn(systemDelay); 641 ArgumentCaptor<Long> timeCaptor = ArgumentCaptor.forClass(long.class); 642 643 long then = SystemClock.uptimeMillis(); 644 send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN); 645 long now = SystemClock.uptimeMillis(); 646 647 verify(mHandler).sendMessageAtTime(any(), timeCaptor.capture()); 648 649 // The message time must be the expected delay time (as provided by the supplier) after 650 // the time the message was sent to the handler. We don't know that exact time, but we 651 // can put a bound on it - it's no sooner than immediately before the call to send(), and no 652 // later than immediately afterwards. Check to make sure the actual observed message time is 653 // somewhere in the valid range. 654 655 assertThat(timeCaptor.getValue()).isIn(Range.closed(then + systemDelay, now + systemDelay)); 656 } 657 658 @Test injectKeyEvent_throwsSecurityExceptionWithoutInjectEventsPermission()659 public void injectKeyEvent_throwsSecurityExceptionWithoutInjectEventsPermission() { 660 // Arrange 661 doReturn(PackageManager.PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission( 662 android.Manifest.permission.INJECT_EVENTS); 663 664 long currentTime = SystemClock.uptimeMillis(); 665 KeyEvent event = new KeyEvent(/* downTime= */ currentTime, 666 /* eventTime= */ currentTime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 667 /* repeat= */ 0); 668 669 // Act and assert 670 Assert.assertThrows(SecurityException.class, 671 () -> mCarInputService.injectKeyEvent(event, 672 CarOccupantZoneManager.DISPLAY_TYPE_MAIN)); 673 } 674 675 @Test injectKeyEvent_delegatesToOnKeyEvent()676 public void injectKeyEvent_delegatesToOnKeyEvent() { 677 long currentTime = SystemClock.uptimeMillis(); 678 KeyEvent event = new KeyEvent(/* downTime= */ currentTime, 679 /* eventTime= */ currentTime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 680 /* repeat= */ 0); 681 event.setDisplayId(android.view.Display.INVALID_DISPLAY); 682 683 injectKeyEventAndVerify(event, CarOccupantZoneManager.DISPLAY_TYPE_MAIN); 684 685 verify(mDefaultMainListener).onKeyEvent(event); 686 verify(mInstrumentClusterKeyListener, never()).onKeyEvent(event); 687 } 688 689 @Test injectKeyEvent_sendingKeyEventWithDefaultDisplayAgainstClusterDisplayType()690 public void injectKeyEvent_sendingKeyEventWithDefaultDisplayAgainstClusterDisplayType() { 691 long currentTime = SystemClock.uptimeMillis(); 692 KeyEvent event = new KeyEvent(/* downTime= */ currentTime, 693 /* eventTime= */ currentTime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 694 /* repeat= */ 0); 695 event.setDisplayId(android.view.Display.INVALID_DISPLAY); 696 697 injectKeyEventAndVerify(event, CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER); 698 699 verify(mDefaultMainListener, never()).onKeyEvent(event); 700 verify(mInstrumentClusterKeyListener).onKeyEvent(event); 701 } 702 injectKeyEventAndVerify(KeyEvent event, @DisplayTypeEnum int displayType)703 private void injectKeyEventAndVerify(KeyEvent event, @DisplayTypeEnum int displayType) { 704 doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission( 705 android.Manifest.permission.INJECT_EVENTS); 706 int someDisplayId = Integer.MAX_VALUE; 707 when(mCarOccupantZoneService.getDisplayIdForDriver(anyInt())).thenReturn(someDisplayId); 708 assertThat(event.getDisplayId()).isNotEqualTo(someDisplayId); 709 710 mCarInputService.injectKeyEvent(event, displayType); 711 712 verify(mCarOccupantZoneService).getDisplayIdForDriver(displayType); 713 assertWithMessage("Event's display id not updated as expected").that( 714 event.getDisplayId()).isEqualTo(someDisplayId); 715 } 716 717 @Test onKey_assignDisplayId_mainDisplay()718 public void onKey_assignDisplayId_mainDisplay() { 719 // Act 720 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_HOME, Display.MAIN); 721 722 // Arrange 723 assertWithMessage("display id expected to be assigned with Display.DEFAULT_DISPLAY").that( 724 event.getDisplayId()).isEqualTo(android.view.Display.DEFAULT_DISPLAY); 725 } 726 727 @Test onKey_assignDisplayId_clusterDisplay()728 public void onKey_assignDisplayId_clusterDisplay() { 729 // Act 730 KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_HOME, Display.INSTRUMENT_CLUSTER); 731 732 // Arrange 733 assertWithMessage("display id expected to be assigned with Display.DEFAULT_DISPLAY").that( 734 event.getDisplayId()).isEqualTo(android.view.Display.DEFAULT_DISPLAY); 735 } 736 737 private enum Key {DOWN, UP} 738 739 private enum Display {MAIN, INSTRUMENT_CLUSTER} 740 send(Key action, int keyCode, Display display)741 private KeyEvent send(Key action, int keyCode, Display display) { 742 return sendWithRepeat(action, keyCode, display, 0); 743 } 744 sendWithRepeat(Key action, int keyCode, Display display, int repeatCount)745 private KeyEvent sendWithRepeat(Key action, int keyCode, Display display, int repeatCount) { 746 KeyEvent event = new KeyEvent( 747 /* downTime= */ 0L, 748 /* eventTime= */ 0L, 749 action == Key.DOWN ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP, 750 keyCode, 751 repeatCount); 752 event.setDisplayId(android.view.Display.INVALID_DISPLAY); 753 mCarInputService.onKeyEvent( 754 event, 755 display == Display.MAIN 756 ? CarOccupantZoneManager.DISPLAY_TYPE_MAIN 757 : CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER); 758 return event; 759 } 760 registerProjectionKeyEventHandler( int... events)761 private CarProjectionManager.ProjectionKeyEventHandler registerProjectionKeyEventHandler( 762 int... events) { 763 BitSet eventSet = new BitSet(); 764 for (int event : events) { 765 eventSet.set(event); 766 } 767 768 CarProjectionManager.ProjectionKeyEventHandler projectionKeyEventHandler = 769 mock(CarProjectionManager.ProjectionKeyEventHandler.class); 770 mCarInputService.setProjectionKeyEventHandler(projectionKeyEventHandler, eventSet); 771 return projectionKeyEventHandler; 772 } 773 flushHandler()774 private void flushHandler() { 775 ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); 776 777 verify(mHandler, atLeast(0)).sendMessageAtTime(messageCaptor.capture(), anyLong()); 778 779 for (Message message : messageCaptor.getAllValues()) { 780 mHandler.dispatchMessage(message); 781 } 782 783 clearInvocations(mHandler); 784 } 785 } 786