• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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