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