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