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 com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.ArgumentMatchers.any;
22 import static org.mockito.ArgumentMatchers.anyInt;
23 import static org.mockito.ArgumentMatchers.anyLong;
24 import static org.mockito.ArgumentMatchers.eq;
25 import static org.mockito.ArgumentMatchers.isNull;
26 import static org.mockito.Mockito.atLeast;
27 import static org.mockito.Mockito.clearInvocations;
28 import static org.mockito.Mockito.doNothing;
29 import static org.mockito.Mockito.doReturn;
30 import static org.mockito.Mockito.ignoreStubs;
31 import static org.mockito.Mockito.mock;
32 import static org.mockito.Mockito.never;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.verifyNoMoreInteractions;
35 import static org.mockito.Mockito.when;
36 
37 import android.car.CarProjectionManager;
38 import android.car.input.CarInputHandlingService.InputFilter;
39 import android.car.input.ICarInputListener;
40 import android.content.ComponentName;
41 import android.content.Context;
42 import android.content.Intent;
43 import android.net.Uri;
44 import android.os.Bundle;
45 import android.os.Handler;
46 import android.os.Looper;
47 import android.os.Message;
48 import android.os.RemoteException;
49 import android.os.SystemClock;
50 import android.os.UserHandle;
51 import android.service.voice.VoiceInteractionSession;
52 import android.telecom.TelecomManager;
53 import android.view.KeyEvent;
54 
55 import androidx.test.core.app.ApplicationProvider;
56 import androidx.test.runner.AndroidJUnit4;
57 
58 import com.android.car.hal.InputHalService;
59 import com.android.internal.app.AssistUtils;
60 
61 import com.google.common.collect.Range;
62 
63 import org.junit.Before;
64 import org.junit.Rule;
65 import org.junit.Test;
66 import org.junit.runner.RunWith;
67 import org.mockito.ArgumentCaptor;
68 import org.mockito.Mock;
69 import org.mockito.Spy;
70 import org.mockito.junit.MockitoJUnit;
71 import org.mockito.junit.MockitoRule;
72 
73 import java.util.BitSet;
74 import java.util.function.IntSupplier;
75 import java.util.function.Supplier;
76 
77 @RunWith(AndroidJUnit4.class)
78 public class CarInputServiceTest {
79     @Rule public MockitoRule rule = MockitoJUnit.rule();
80 
81     @Mock InputHalService mInputHalService;
82     @Mock TelecomManager mTelecomManager;
83     @Mock AssistUtils mAssistUtils;
84     @Mock CarInputService.KeyEventListener mDefaultMainListener;
85     @Mock Supplier<String> mLastCallSupplier;
86     @Mock IntSupplier mLongPressDelaySupplier;
87 
88     @Spy Context mContext = ApplicationProvider.getApplicationContext();
89     @Spy Handler mHandler = new Handler(Looper.getMainLooper());
90 
91     private CarInputService mCarInputService;
92 
93     @Before
setUp()94     public void setUp() {
95         mCarInputService = new CarInputService(mContext, mInputHalService, mHandler,
96                 mTelecomManager, mAssistUtils, mDefaultMainListener, mLastCallSupplier,
97                 /* customInputServiceComponent= */ null, mLongPressDelaySupplier);
98 
99         when(mInputHalService.isKeyInputSupported()).thenReturn(true);
100         mCarInputService.init();
101 
102         // Delay Handler callbacks until flushHandler() is called.
103         doReturn(true).when(mHandler).sendMessageAtTime(any(), anyLong());
104     }
105 
106     @Test
ordinaryEvents_onMainDisplay_routedToInputManager()107     public void ordinaryEvents_onMainDisplay_routedToInputManager() {
108         KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.MAIN);
109 
110         verify(mDefaultMainListener).onKeyEvent(event);
111     }
112 
113     @Test
ordinaryEvents_onInstrumentClusterDisplay_notRoutedToInputManager()114     public void ordinaryEvents_onInstrumentClusterDisplay_notRoutedToInputManager() {
115         send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.INSTRUMENT_CLUSTER);
116 
117         verify(mDefaultMainListener, never()).onKeyEvent(any());
118     }
119 
120     @Test
ordinaryEvents_onInstrumentClusterDisplay_routedToListener()121     public void ordinaryEvents_onInstrumentClusterDisplay_routedToListener() {
122         CarInputService.KeyEventListener listener = mock(CarInputService.KeyEventListener.class);
123         mCarInputService.setInstrumentClusterKeyListener(listener);
124 
125         KeyEvent event = send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.INSTRUMENT_CLUSTER);
126         verify(listener).onKeyEvent(event);
127     }
128 
129     @Test
customEventHandler_capturesRegisteredEvents_ignoresUnregisteredEvents()130     public void customEventHandler_capturesRegisteredEvents_ignoresUnregisteredEvents()
131             throws RemoteException {
132         KeyEvent event;
133         ICarInputListener listener = registerInputListener(
134                 new InputFilter(KeyEvent.KEYCODE_ENTER, InputHalService.DISPLAY_MAIN),
135                 new InputFilter(KeyEvent.KEYCODE_ENTER, InputHalService.DISPLAY_INSTRUMENT_CLUSTER),
136                 new InputFilter(KeyEvent.KEYCODE_MENU, InputHalService.DISPLAY_MAIN));
137 
138         CarInputService.KeyEventListener instrumentClusterListener =
139                 mock(CarInputService.KeyEventListener.class);
140         mCarInputService.setInstrumentClusterKeyListener(instrumentClusterListener);
141 
142         event = send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.MAIN);
143         verify(listener).onKeyEvent(event, InputHalService.DISPLAY_MAIN);
144         verify(mDefaultMainListener, never()).onKeyEvent(any());
145 
146         event = send(Key.DOWN, KeyEvent.KEYCODE_ENTER, Display.INSTRUMENT_CLUSTER);
147         verify(listener).onKeyEvent(event, InputHalService.DISPLAY_INSTRUMENT_CLUSTER);
148         verify(instrumentClusterListener, never()).onKeyEvent(any());
149 
150         event = send(Key.DOWN, KeyEvent.KEYCODE_MENU, Display.MAIN);
151         verify(listener).onKeyEvent(event, InputHalService.DISPLAY_MAIN);
152         verify(mDefaultMainListener, never()).onKeyEvent(any());
153 
154         event = send(Key.DOWN, KeyEvent.KEYCODE_MENU, Display.INSTRUMENT_CLUSTER);
155         verify(listener, never()).onKeyEvent(event, InputHalService.DISPLAY_INSTRUMENT_CLUSTER);
156         verify(instrumentClusterListener).onKeyEvent(event);
157     }
158 
159     @Test
voiceKey_shortPress_withRegisteredEventHandler_triggersEventHandler()160     public void voiceKey_shortPress_withRegisteredEventHandler_triggersEventHandler() {
161         CarProjectionManager.ProjectionKeyEventHandler eventHandler =
162                 registerProjectionKeyEventHandler(
163                         CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP);
164 
165         send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN);
166         send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN);
167 
168         verify(eventHandler)
169                 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP);
170     }
171 
172     @Test
voiceKey_longPress_withRegisteredEventHandler_triggersEventHandler()173     public void voiceKey_longPress_withRegisteredEventHandler_triggersEventHandler() {
174         CarProjectionManager.ProjectionKeyEventHandler eventHandler =
175                 registerProjectionKeyEventHandler(
176                         CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP,
177                         CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_DOWN);
178 
179         send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN);
180         verify(eventHandler, never()).onKeyEvent(anyInt());
181 
182         // Simulate the long-press timer expiring.
183         flushHandler();
184         verify(eventHandler)
185                 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_DOWN);
186 
187         // Ensure that the short-press handler is *not* called.
188         send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN);
189         flushHandler();
190         verify(eventHandler, never())
191                 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_SHORT_PRESS_KEY_UP);
192     }
193 
194     @Test
voiceKey_shortPress_withoutRegisteredEventHandler_triggersAssistUtils()195     public void voiceKey_shortPress_withoutRegisteredEventHandler_triggersAssistUtils() {
196         when(mAssistUtils.getAssistComponentForUser(anyInt()))
197                 .thenReturn(new ComponentName("pkg", "cls"));
198 
199         send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN);
200         send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN);
201 
202         ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
203         verify(mAssistUtils).showSessionForActiveService(
204                 bundleCaptor.capture(),
205                 eq(VoiceInteractionSession.SHOW_SOURCE_PUSH_TO_TALK),
206                 any(),
207                 isNull());
208         assertThat(bundleCaptor.getValue().getBoolean(CarInputService.EXTRA_CAR_PUSH_TO_TALK))
209                 .isTrue();
210     }
211 
212     @Test
voiceKey_longPress_withoutRegisteredEventHandler_triggersAssistUtils()213     public void voiceKey_longPress_withoutRegisteredEventHandler_triggersAssistUtils() {
214         when(mAssistUtils.getAssistComponentForUser(anyInt()))
215                 .thenReturn(new ComponentName("pkg", "cls"));
216 
217         send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN);
218         flushHandler();
219 
220         ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
221         verify(mAssistUtils).showSessionForActiveService(
222                 bundleCaptor.capture(),
223                 eq(VoiceInteractionSession.SHOW_SOURCE_PUSH_TO_TALK),
224                 any(),
225                 isNull());
226         assertThat(bundleCaptor.getValue().getBoolean(CarInputService.EXTRA_CAR_PUSH_TO_TALK))
227                 .isTrue();
228 
229         send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN);
230         verifyNoMoreInteractions(ignoreStubs(mAssistUtils));
231     }
232 
233     @Test
voiceKey_keyDown_withEventHandler_triggersEventHandler()234     public void voiceKey_keyDown_withEventHandler_triggersEventHandler() {
235         CarProjectionManager.ProjectionKeyEventHandler eventHandler =
236                 registerProjectionKeyEventHandler(
237                         CarProjectionManager.KEY_EVENT_VOICE_SEARCH_KEY_DOWN);
238 
239         send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN);
240 
241         verify(eventHandler).onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_KEY_DOWN);
242     }
243 
244     @Test
voiceKey_keyUp_afterLongPress_withEventHandler_triggersEventHandler()245     public void voiceKey_keyUp_afterLongPress_withEventHandler_triggersEventHandler() {
246         CarProjectionManager.ProjectionKeyEventHandler eventHandler =
247                 registerProjectionKeyEventHandler(
248                         CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_UP);
249 
250         send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN);
251         flushHandler();
252         verify(eventHandler, never())
253                 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_UP);
254 
255         send(Key.UP, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN);
256         verify(eventHandler)
257                 .onKeyEvent(CarProjectionManager.KEY_EVENT_VOICE_SEARCH_LONG_PRESS_KEY_UP);
258     }
259 
260     @Test
voiceKey_repeatedEvents_ignored()261     public void voiceKey_repeatedEvents_ignored() {
262         // Pressing a key starts the long-press timer.
263         send(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN);
264         verify(mHandler).sendMessageAtTime(any(), anyLong());
265         clearInvocations(mHandler);
266 
267         // Repeated KEY_DOWN events don't reset the timer.
268         sendWithRepeat(Key.DOWN, KeyEvent.KEYCODE_VOICE_ASSIST, Display.MAIN, 1);
269         verify(mHandler, never()).sendMessageAtTime(any(), anyLong());
270     }
271 
272     @Test
callKey_shortPress_withoutEventHandler_launchesDialer()273     public void callKey_shortPress_withoutEventHandler_launchesDialer() {
274         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
275 
276         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
277 
278         send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN);
279         send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN);
280 
281         verify(mContext).startActivityAsUser(
282                 intentCaptor.capture(), any(), eq(UserHandle.CURRENT_OR_SELF));
283         assertThat(intentCaptor.getValue().getAction()).isEqualTo(Intent.ACTION_DIAL);
284     }
285 
286     @Test
callKey_shortPress_withoutEventHandler_whenCallRinging_answersCall()287     public void callKey_shortPress_withoutEventHandler_whenCallRinging_answersCall() {
288         when(mTelecomManager.isRinging()).thenReturn(true);
289 
290         send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN);
291         send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN);
292 
293         verify(mTelecomManager).acceptRingingCall();
294         // Ensure default handler does not run.
295         verify(mContext, never()).startActivityAsUser(any(), any(), any());
296     }
297 
298     @Test
callKey_shortPress_withEventHandler_triggersEventHandler()299     public void callKey_shortPress_withEventHandler_triggersEventHandler() {
300         CarProjectionManager.ProjectionKeyEventHandler eventHandler =
301                 registerProjectionKeyEventHandler(
302                         CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP);
303 
304         send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN);
305         send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN);
306 
307         verify(eventHandler).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP);
308         // Ensure default handlers do not run.
309         verify(mTelecomManager, never()).acceptRingingCall();
310         verify(mContext, never()).startActivityAsUser(any(), any(), any());
311     }
312 
313     @Test
callKey_shortPress_withEventHandler_whenCallRinging_answersCall()314     public void callKey_shortPress_withEventHandler_whenCallRinging_answersCall() {
315         CarProjectionManager.ProjectionKeyEventHandler eventHandler =
316                 registerProjectionKeyEventHandler(
317                         CarProjectionManager.KEY_EVENT_CALL_SHORT_PRESS_KEY_UP);
318         when(mTelecomManager.isRinging()).thenReturn(true);
319 
320         send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN);
321         send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN);
322 
323         verify(mTelecomManager).acceptRingingCall();
324         verify(eventHandler, never()).onKeyEvent(anyInt());
325     }
326 
327     @Test
callKey_longPress_withoutEventHandler_redialsLastCall()328     public void callKey_longPress_withoutEventHandler_redialsLastCall() {
329         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
330 
331         when(mLastCallSupplier.get()).thenReturn("1234567890");
332         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
333 
334         send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN);
335         flushHandler();
336 
337         verify(mContext).startActivityAsUser(
338                 intentCaptor.capture(), any(), eq(UserHandle.CURRENT_OR_SELF));
339 
340         Intent intent = intentCaptor.getValue();
341         assertThat(intent.getAction()).isEqualTo(Intent.ACTION_CALL);
342         assertThat(intent.getData()).isEqualTo(Uri.parse("tel:1234567890"));
343 
344         clearInvocations(mContext);
345         send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN);
346         verify(mContext, never()).startActivityAsUser(any(), any(), any());
347     }
348 
349     @Test
callKey_longPress_withoutEventHandler_withNoLastCall_doesNothing()350     public void callKey_longPress_withoutEventHandler_withNoLastCall_doesNothing() {
351         when(mLastCallSupplier.get()).thenReturn("");
352 
353         send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN);
354         flushHandler();
355 
356         verify(mContext, never()).startActivityAsUser(any(), any(), any());
357     }
358 
359     @Test
callKey_longPress_withoutEventHandler_whenCallRinging_answersCall()360     public void callKey_longPress_withoutEventHandler_whenCallRinging_answersCall() {
361         when(mTelecomManager.isRinging()).thenReturn(true);
362 
363         send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN);
364         flushHandler();
365 
366         verify(mTelecomManager).acceptRingingCall();
367 
368         send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN);
369         // Ensure that default handler does not run, either after accepting ringing call,
370         // or as a result of key-up.
371         verify(mContext, never()).startActivityAsUser(any(), any(), any());
372     }
373 
374     @Test
callKey_longPress_withEventHandler_triggersEventHandler()375     public void callKey_longPress_withEventHandler_triggersEventHandler() {
376         CarProjectionManager.ProjectionKeyEventHandler eventHandler =
377                 registerProjectionKeyEventHandler(
378                         CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN);
379 
380         send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN);
381         flushHandler();
382 
383         verify(eventHandler).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN);
384         verify(mContext, never()).startActivityAsUser(any(), any(), any());
385     }
386 
387     @Test
callKey_longPress_withEventHandler_whenCallRinging_answersCall()388     public void callKey_longPress_withEventHandler_whenCallRinging_answersCall() {
389         CarProjectionManager.ProjectionKeyEventHandler eventHandler =
390                 registerProjectionKeyEventHandler(
391                         CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_DOWN);
392         when(mTelecomManager.isRinging()).thenReturn(true);
393 
394         send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN);
395         flushHandler();
396 
397         verify(mTelecomManager).acceptRingingCall();
398 
399         send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN);
400         // Ensure that event handler does not run, either after accepting ringing call,
401         // or as a result of key-up.
402         verify(eventHandler, never()).onKeyEvent(anyInt());
403     }
404 
405     @Test
callKey_keyDown_withEventHandler_triggersEventHandler()406     public void callKey_keyDown_withEventHandler_triggersEventHandler() {
407         CarProjectionManager.ProjectionKeyEventHandler eventHandler =
408                 registerProjectionKeyEventHandler(
409                         CarProjectionManager.KEY_EVENT_CALL_KEY_DOWN);
410 
411         send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN);
412 
413         verify(eventHandler).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_KEY_DOWN);
414     }
415 
416     @Test
callKey_keyUp_afterLongPress_withEventHandler_triggersEventHandler()417     public void callKey_keyUp_afterLongPress_withEventHandler_triggersEventHandler() {
418         CarProjectionManager.ProjectionKeyEventHandler eventHandler =
419                 registerProjectionKeyEventHandler(
420                         CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_UP);
421 
422         send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN);
423         flushHandler();
424         verify(eventHandler, never())
425                 .onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_UP);
426 
427         send(Key.UP, KeyEvent.KEYCODE_CALL, Display.MAIN);
428         verify(eventHandler).onKeyEvent(CarProjectionManager.KEY_EVENT_CALL_LONG_PRESS_KEY_UP);
429     }
430 
431     @Test
callKey_repeatedEvents_ignored()432     public void callKey_repeatedEvents_ignored() {
433         // Pressing a key starts the long-press timer.
434         send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN);
435         verify(mHandler).sendMessageAtTime(any(), anyLong());
436         clearInvocations(mHandler);
437 
438         // Repeated KEY_DOWN events don't reset the timer.
439         sendWithRepeat(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN, 1);
440         verify(mHandler, never()).sendMessageAtTime(any(), anyLong());
441     }
442 
443     @Test
longPressDelay_obeysValueFromSystem()444     public void longPressDelay_obeysValueFromSystem() {
445         final int systemDelay = 4242;
446 
447         when(mLongPressDelaySupplier.getAsInt()).thenReturn(systemDelay);
448         ArgumentCaptor<Long> timeCaptor = ArgumentCaptor.forClass(long.class);
449 
450         long then = SystemClock.uptimeMillis();
451         send(Key.DOWN, KeyEvent.KEYCODE_CALL, Display.MAIN);
452         long now = SystemClock.uptimeMillis();
453 
454         verify(mHandler).sendMessageAtTime(any(), timeCaptor.capture());
455 
456         // The message time must be the expected delay time (as provided by the supplier) after
457         // the time the message was sent to the handler. We don't know that exact time, but we
458         // can put a bound on it - it's no sooner than immediately before the call to send(), and no
459         // later than immediately afterwards. Check to make sure the actual observed message time is
460         // somewhere in the valid range.
461 
462         assertThat(timeCaptor.getValue()).isIn(Range.closed(then + systemDelay, now + systemDelay));
463     }
464 
465     private enum Key {DOWN, UP}
466 
467     private enum Display {MAIN, INSTRUMENT_CLUSTER}
468 
send(Key action, int keyCode, Display display)469     private KeyEvent send(Key action, int keyCode, Display display) {
470         return sendWithRepeat(action, keyCode, display, 0);
471     }
472 
sendWithRepeat(Key action, int keyCode, Display display, int repeatCount)473     private KeyEvent sendWithRepeat(Key action, int keyCode, Display display, int repeatCount) {
474         KeyEvent event = new KeyEvent(
475                 /* downTime= */ 0L,
476                 /* eventTime= */ 0L,
477                 action == Key.DOWN ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
478                 keyCode,
479                 repeatCount);
480         mCarInputService.onKeyEvent(
481                 event,
482                 display == Display.MAIN
483                         ? InputHalService.DISPLAY_MAIN
484                         : InputHalService.DISPLAY_INSTRUMENT_CLUSTER);
485         return event;
486     }
487 
registerInputListener(InputFilter... handledKeys)488     private ICarInputListener registerInputListener(InputFilter... handledKeys) {
489         ICarInputListener listener = mock(ICarInputListener.class);
490         mCarInputService.mCarInputListener = listener;
491         mCarInputService.setHandledKeys(handledKeys);
492         return listener;
493     }
494 
registerProjectionKeyEventHandler( int... events)495     private CarProjectionManager.ProjectionKeyEventHandler registerProjectionKeyEventHandler(
496             int... events) {
497         BitSet eventSet = new BitSet();
498         for (int event : events) {
499             eventSet.set(event);
500         }
501 
502         CarProjectionManager.ProjectionKeyEventHandler projectionKeyEventHandler =
503                 mock(CarProjectionManager.ProjectionKeyEventHandler.class);
504         mCarInputService.setProjectionKeyEventHandler(projectionKeyEventHandler, eventSet);
505         return projectionKeyEventHandler;
506     }
507 
flushHandler()508     private void flushHandler() {
509         ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
510 
511         verify(mHandler, atLeast(0)).sendMessageAtTime(messageCaptor.capture(), anyLong());
512 
513         for (Message message : messageCaptor.getAllValues()) {
514             mHandler.dispatchMessage(message);
515         }
516 
517         clearInvocations(mHandler);
518     }
519 }
520