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