• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.input;
18 
19 import static android.car.CarOccupantZoneManager.DisplayTypeEnum;
20 import static android.hardware.automotive.vehicle.CustomInputType.CUSTOM_EVENT_F1;
21 
22 import static com.google.common.truth.Truth.assertThat;
23 import static com.google.common.truth.Truth.assertWithMessage;
24 
25 import static org.junit.Assert.assertThrows;
26 import static org.mockito.ArgumentMatchers.any;
27 import static org.mockito.Mockito.spy;
28 import static org.mockito.Mockito.verify;
29 
30 import android.annotation.NonNull;
31 import android.car.Car;
32 import android.car.CarOccupantZoneManager;
33 import android.car.input.CarInputManager;
34 import android.car.input.CustomInputEvent;
35 import android.car.input.RotaryEvent;
36 import android.hardware.automotive.vehicle.RotaryInputType;
37 import android.hardware.automotive.vehicle.VehicleDisplay;
38 import android.hardware.automotive.vehicle.VehicleProperty;
39 import android.os.SystemClock;
40 import android.util.Log;
41 import android.util.Pair;
42 import android.view.KeyEvent;
43 
44 import androidx.test.ext.junit.runners.AndroidJUnit4;
45 import androidx.test.filters.MediumTest;
46 
47 import com.android.car.CarServiceUtils;
48 import com.android.car.MockedCarTestBase;
49 import com.android.car.hal.test.AidlVehiclePropValueBuilder;
50 import com.android.internal.annotations.GuardedBy;
51 
52 import org.junit.Test;
53 import org.junit.runner.RunWith;
54 
55 import java.util.Arrays;
56 import java.util.Collections;
57 import java.util.LinkedList;
58 import java.util.List;
59 import java.util.concurrent.Executor;
60 import java.util.concurrent.Executors;
61 import java.util.concurrent.Semaphore;
62 import java.util.concurrent.TimeUnit;
63 
64 @RunWith(AndroidJUnit4.class)
65 @MediumTest
66 public final class CarInputManagerTest extends MockedCarTestBase {
67     private static final String TAG = CarInputManagerTest.class.getSimpleName();
68 
69     private static final int INVALID_DISPLAY_TYPE = -1;
70     private static final int INVALID_INPUT_TYPE = -1;
71 
72     private CarInputManager mCarInputManager;
73 
74     private final class CaptureCallback implements CarInputManager.CarInputCaptureCallback {
75 
76         private static final long EVENT_WAIT_TIME = 5_000;
77 
78         private final Object mLock = new Object();
79 
80         private final String mName;
81 
CaptureCallback(String name)82         private CaptureCallback(String name) {
83             mName = name;
84         }
85 
86         // Stores passed events. Last one in front
87         @GuardedBy("mLock")
88         private final LinkedList<Pair<Integer, List<KeyEvent>>> mKeyEvents = new LinkedList<>();
89 
90         // Stores passed events. Last one in front
91         @GuardedBy("mLock")
92         private final LinkedList<Pair<Integer, List<RotaryEvent>>> mRotaryEvents =
93                 new LinkedList<>();
94 
95         // Stores passed events. Last one in front
96         @GuardedBy("mLock")
97         private final LinkedList<Pair<Integer, List<CustomInputEvent>>> mCustomInputEvents =
98                 new LinkedList<>();
99 
100         // Stores passed state changes. Last one in front
101         @GuardedBy("mLock")
102         private final LinkedList<Pair<Integer, int[]>> mStateChanges = new LinkedList<>();
103 
104         private final Semaphore mKeyEventWait = new Semaphore(0);
105         private final Semaphore mRotaryEventWait = new Semaphore(0);
106         private final Semaphore mStateChangeWait = new Semaphore(0);
107         private final Semaphore mCustomInputEventWait = new Semaphore(0);
108 
109         @Override
onKeyEvents(@isplayTypeEnum int targetDisplayType, @NonNull List<KeyEvent> keyEvents)110         public void onKeyEvents(@DisplayTypeEnum int targetDisplayType,
111                 @NonNull List<KeyEvent> keyEvents) {
112             Log.i(TAG, "onKeyEvents event:" + keyEvents.get(0) + " this:" + this);
113             synchronized (mLock) {
114                 mKeyEvents.addFirst(new Pair<>(targetDisplayType, keyEvents));
115             }
116             mKeyEventWait.release();
117         }
118 
119         @Override
onRotaryEvents(@isplayTypeEnum int targetDisplayType, @NonNull List<RotaryEvent> events)120         public void onRotaryEvents(@DisplayTypeEnum int targetDisplayType,
121                 @NonNull List<RotaryEvent> events) {
122             Log.i(TAG, "onRotaryEvents event:" + events.get(0) + " this:" + this);
123             synchronized (mLock) {
124                 mRotaryEvents.addFirst(new Pair<>(targetDisplayType, events));
125             }
126             mRotaryEventWait.release();
127         }
128 
129         @Override
onCustomInputEvents(@isplayTypeEnum int targetDisplayType, @NonNull List<CustomInputEvent> events)130         public void onCustomInputEvents(@DisplayTypeEnum int targetDisplayType,
131                 @NonNull List<CustomInputEvent> events) {
132             Log.i(TAG, "onCustomInputEvents event:" + events.get(0) + " this:" + this);
133             synchronized (mLock) {
134                 mCustomInputEvents.addFirst(new Pair<>(targetDisplayType, events));
135             }
136             mCustomInputEventWait.release();
137         }
138 
139         @Override
onCaptureStateChanged(@isplayTypeEnum int targetDisplayType, @NonNull @CarInputManager.InputTypeEnum int[] activeInputTypes)140         public void onCaptureStateChanged(@DisplayTypeEnum int targetDisplayType,
141                 @NonNull @CarInputManager.InputTypeEnum int[] activeInputTypes) {
142             Log.i(TAG, "onCaptureStateChanged types:" + Arrays.toString(activeInputTypes)
143                     + " this:" + this);
144             synchronized (mLock) {
145                 mStateChanges.addFirst(new Pair<>(targetDisplayType, activeInputTypes));
146             }
147             mStateChangeWait.release();
148         }
149 
resetAllEventsWaiting()150         private void resetAllEventsWaiting() {
151             mStateChangeWait.drainPermits();
152             mKeyEventWait.drainPermits();
153             mRotaryEventWait.drainPermits();
154         }
155 
waitForStateChange()156         private void waitForStateChange() throws Exception {
157             assertWithMessage("Failed to acquire semaphore in %s ms", EVENT_WAIT_TIME).that(
158                     mStateChangeWait.tryAcquire(EVENT_WAIT_TIME, TimeUnit.MILLISECONDS)).isTrue();
159         }
160 
waitForKeyEvent()161         private void waitForKeyEvent() throws Exception {
162             assertWithMessage("Failed to acquire semaphore in %s ms", EVENT_WAIT_TIME).that(
163                     mKeyEventWait.tryAcquire(EVENT_WAIT_TIME, TimeUnit.MILLISECONDS)).isTrue();
164         }
165 
waitForRotaryEvent()166         private void waitForRotaryEvent() throws Exception {
167             assertWithMessage("Failed to acquire semaphore in %s ms", EVENT_WAIT_TIME).that(
168                     mRotaryEventWait.tryAcquire(EVENT_WAIT_TIME, TimeUnit.MILLISECONDS)).isTrue();
169         }
170 
waitForCustomInputEvent()171         private void waitForCustomInputEvent() throws Exception {
172             assertWithMessage("Failed to acquire semaphore in %s ms", EVENT_WAIT_TIME).that(
173                     mCustomInputEventWait.tryAcquire(
174                             EVENT_WAIT_TIME, TimeUnit.MILLISECONDS)).isTrue();
175         }
176 
getkeyEvents()177         private LinkedList<Pair<Integer, List<KeyEvent>>> getkeyEvents() {
178             synchronized (mLock) {
179                 LinkedList<Pair<Integer, List<KeyEvent>>> r =
180                         new LinkedList<>(mKeyEvents);
181                 Log.i(TAG, "getKeyEvents size:" + r.size() + ",this:" + this);
182                 return r;
183             }
184         }
185 
getRotaryEvents()186         private LinkedList<Pair<Integer, List<RotaryEvent>>> getRotaryEvents() {
187             synchronized (mLock) {
188                 LinkedList<Pair<Integer, List<RotaryEvent>>> r =
189                         new LinkedList<>(mRotaryEvents);
190                 Log.i(TAG, "getRotaryEvents size:" + r.size() + ",this:" + this);
191                 return r;
192             }
193         }
194 
getStateChanges()195         private LinkedList<Pair<Integer, int[]>> getStateChanges() {
196             synchronized (mLock) {
197                 return new LinkedList<>(mStateChanges);
198             }
199         }
200 
getCustomInputEvents()201         private LinkedList<Pair<Integer, List<CustomInputEvent>>> getCustomInputEvents() {
202             synchronized (mLock) {
203                 LinkedList<Pair<Integer, List<CustomInputEvent>>> r =
204                         new LinkedList<>(mCustomInputEvents);
205                 Log.i(TAG, "getCustomInputEvents size:" + r.size() + ",this:" + this);
206                 return r;
207             }
208         }
209 
210         @Override
toString()211         public String toString() {
212             return "CaptureCallback{mName='" + mName + "'}";
213         }
214     }
215 
216     private final CaptureCallback mCallback0 = new CaptureCallback("callback0");
217     private final CaptureCallback mCallback1 = new CaptureCallback("callback1");
218     private final CaptureCallback mCallback2 = new CaptureCallback("callback2");
219 
220     @Override
configureMockedHal()221     protected void configureMockedHal() {
222         addAidlProperty(VehicleProperty.HW_KEY_INPUT,
223                 AidlVehiclePropValueBuilder.newBuilder(VehicleProperty.HW_KEY_INPUT)
224                         .addIntValues(0, 0, 0)
225                         .build());
226         addAidlProperty(VehicleProperty.HW_ROTARY_INPUT,
227                 AidlVehiclePropValueBuilder.newBuilder(VehicleProperty.HW_ROTARY_INPUT)
228                         .addIntValues(0, 1, 0)
229                         .build());
230         addAidlProperty(VehicleProperty.HW_CUSTOM_INPUT,
231                 AidlVehiclePropValueBuilder.newBuilder(VehicleProperty.HW_CUSTOM_INPUT)
232                         .addIntValues(0)
233                         .build());
234     }
235 
236     @Override
configureResourceOverrides(MockResources resources)237     protected void configureResourceOverrides(MockResources resources) {
238         super.configureResourceOverrides(resources);
239         resources.overrideResource(com.android.car.R.string.config_clusterHomeActivity,
240                 getTestContext().getPackageName() + "/" + CarInputManagerTest.class.getName());
241     }
242 
243     @Override
setUp()244     public void setUp() throws Exception {
245         super.setUp();
246         mCarInputManager = (CarInputManager) getCar().getCarManager(Car.CAR_INPUT_SERVICE);
247         assertThat(mCarInputManager).isNotNull();
248     }
249 
250     @Test
testInvalidArgs()251     public void testInvalidArgs() {
252         // Invalid display
253         assertThrows(IllegalArgumentException.class,
254                 () -> mCarInputManager.requestInputEventCapture(INVALID_DISPLAY_TYPE,
255                         new int[]{CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION}, 0, mCallback0));
256 
257         // Invalid input types
258         assertThrows(IllegalArgumentException.class,
259                 () -> mCarInputManager.requestInputEventCapture(
260                         CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
261                         new int[]{CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION, INVALID_INPUT_TYPE},
262                         0, mCallback0));
263         assertThrows(IllegalArgumentException.class,
264                 () -> mCarInputManager.requestInputEventCapture(
265                         CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
266                         new int[]{CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION},
267                         CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY, mCallback0));
268         assertThrows(IllegalArgumentException.class,
269                 () -> mCarInputManager.requestInputEventCapture(
270                         CarOccupantZoneManager.DISPLAY_TYPE_MAIN, new int[]{INVALID_INPUT_TYPE},
271                         0, mCallback0));
272         assertThrows(IllegalArgumentException.class,
273                 () -> mCarInputManager.requestInputEventCapture(
274                         CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
275                         new int[]{CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION, INVALID_INPUT_TYPE},
276                         0, mCallback0));
277     }
278 
createAnotherCarInputManager()279     private CarInputManager createAnotherCarInputManager() {
280         return (CarInputManager) createNewCar().getCarManager(Car.CAR_INPUT_SERVICE);
281     }
282 
283     @Test
testInjectKeyEvent_mainDisplay()284     public void testInjectKeyEvent_mainDisplay() throws Exception {
285         int r = mCarInputManager.requestInputEventCapture(
286                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
287                 new int[]{CarInputManager.INPUT_TYPE_ALL_INPUTS},
288                 CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY, mCallback0);
289         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
290 
291         KeyEvent keyEvent = newKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER);
292 
293         mCarInputManager.injectKeyEvent(keyEvent, CarOccupantZoneManager.DISPLAY_TYPE_MAIN);
294 
295         mCallback0.waitForKeyEvent();
296         assertThat(mCallback0.getkeyEvents()).containsExactly(
297                 new Pair<>(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
298                         Collections.singletonList(keyEvent)));
299     }
300 
301     @Test
testInjectKeyEvent_instrumentClusterDisplay()302     public void testInjectKeyEvent_instrumentClusterDisplay() throws Exception {
303         int r = mCarInputManager.requestInputEventCapture(
304                 CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER,
305                 new int[]{CarInputManager.INPUT_TYPE_ALL_INPUTS},
306                 CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY, mCallback0);
307         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
308 
309         KeyEvent keyEvent = newKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER);
310 
311         mCarInputManager.injectKeyEvent(keyEvent,
312                 CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER);
313 
314         mCallback0.waitForKeyEvent();
315         assertThat(mCallback0.getkeyEvents()).containsExactly(
316                 new Pair<>(CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER,
317                         Collections.singletonList(keyEvent)));
318     }
319 
newKeyEvent(int action, int code)320     private static KeyEvent newKeyEvent(int action, int code) {
321         long currentTime = SystemClock.uptimeMillis();
322         return new KeyEvent(/* downTime= */ currentTime,
323                 /* eventTime= */ currentTime, action, code,
324                 /* repeat= */ 0);
325     }
326 
327     @Test
testFailWithFullCaptureHigherPriority()328     public void testFailWithFullCaptureHigherPriority() {
329         CarInputManager carInputManager0 = createAnotherCarInputManager();
330         int r = carInputManager0.requestInputEventCapture(
331                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
332                 new int[]{CarInputManager.INPUT_TYPE_ALL_INPUTS},
333                 CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY, mCallback0);
334         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
335 
336         //TODO(b/151225008) test event
337 
338         r = mCarInputManager.requestInputEventCapture(
339                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
340                 new int[]{CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION}, 0, mCallback1);
341         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_FAILED);
342 
343         carInputManager0.releaseInputEventCapture(CarOccupantZoneManager.DISPLAY_TYPE_MAIN);
344 
345         r = mCarInputManager.requestInputEventCapture(
346                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
347                 new int[]{CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION}, 0, mCallback1);
348         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
349 
350         //TODO(b/151225008) test event
351     }
352 
353     @Test
testDelayedGrantWithFullCapture()354     public void testDelayedGrantWithFullCapture() throws Exception {
355         mCallback1.resetAllEventsWaiting();
356         CarInputManager carInputManager0 = createAnotherCarInputManager();
357         int r = carInputManager0.requestInputEventCapture(
358                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
359                 new int[]{CarInputManager.INPUT_TYPE_ALL_INPUTS},
360                 CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY, mCallback0);
361         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
362 
363         injectKeyEvent(true, KeyEvent.KEYCODE_NAVIGATE_NEXT);
364         waitAndAssertLastKeyEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, true,
365                 KeyEvent.KEYCODE_NAVIGATE_NEXT, mCallback0);
366 
367         injectKeyEvent(true, KeyEvent.KEYCODE_DPAD_CENTER);
368         waitAndAssertLastKeyEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, true,
369                 KeyEvent.KEYCODE_DPAD_CENTER, mCallback0);
370 
371         int numClicks = 3;
372         injectRotaryNavigationEvent(VehicleDisplay.MAIN, numClicks);
373         waitAndAssertLastRotaryEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
374                 CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION, numClicks, mCallback0);
375 
376         r = mCarInputManager.requestInputEventCapture(
377                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
378                 new int[]{CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION},
379                 CarInputManager.CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT, mCallback1);
380         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_DELAYED);
381 
382         injectRotaryNavigationEvent(VehicleDisplay.MAIN, numClicks);
383         waitForDispatchToMain();
384         assertNumberOfOnRotaryEvents(0, mCallback1);
385 
386         carInputManager0.releaseInputEventCapture(CarOccupantZoneManager.DISPLAY_TYPE_MAIN);
387 
388         // Now capture should be granted back
389         waitAndAssertLastStateChange(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
390                 new int[]{
391                         CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION},
392                 mCallback1);
393         assertNoStateChange(mCallback0);
394 
395         injectRotaryNavigationEvent(VehicleDisplay.MAIN, numClicks);
396         waitAndAssertLastRotaryEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
397                 CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION, numClicks, mCallback1);
398     }
399 
400     @Test
testOneClientTransitionFromFullToNonFull()401     public void testOneClientTransitionFromFullToNonFull() throws Exception {
402         CarInputManager carInputManager0 = createAnotherCarInputManager();
403 
404         int r = carInputManager0.requestInputEventCapture(
405                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
406                 new int[]{CarInputManager.INPUT_TYPE_ALL_INPUTS},
407                 CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY, mCallback0);
408         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
409 
410         r = mCarInputManager.requestInputEventCapture(
411                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
412                 new int[]{
413                         CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION,
414                         CarInputManager.INPUT_TYPE_NAVIGATE_KEYS},
415                 CarInputManager.CAPTURE_REQ_FLAGS_ALLOW_DELAYED_GRANT, mCallback1);
416         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_DELAYED);
417 
418         r = carInputManager0.requestInputEventCapture(
419                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
420                 new int[]{
421                         CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION,
422                         CarInputManager.INPUT_TYPE_DPAD_KEYS}, 0, mCallback0);
423         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
424 
425         waitForDispatchToMain();
426         waitAndAssertLastStateChange(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
427                 new int[]{CarInputManager.INPUT_TYPE_NAVIGATE_KEYS},
428                 mCallback1);
429         assertNoStateChange(mCallback0);
430     }
431 
432     @Test
testSwitchFromFullCaptureToPerTypeCapture()433     public void testSwitchFromFullCaptureToPerTypeCapture() {
434         int r = mCarInputManager.requestInputEventCapture(
435                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
436                 new int[]{CarInputManager.INPUT_TYPE_ALL_INPUTS},
437                 CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY, mCallback0);
438         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
439 
440         r = mCarInputManager.requestInputEventCapture(
441                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
442                 new int[]{CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION}, 0, mCallback1);
443         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
444 
445         r = mCarInputManager.requestInputEventCapture(
446                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
447                 new int[]{CarInputManager.INPUT_TYPE_CUSTOM_INPUT_EVENT}, 0, mCallback2);
448         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
449     }
450 
451     @Test
testIndependentTwoCaptures()452     public void testIndependentTwoCaptures() throws Exception {
453         int r = createAnotherCarInputManager().requestInputEventCapture(
454                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
455                 new int[]{CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION}, 0, mCallback0);
456         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
457 
458         int numClicks = 3;
459         injectRotaryNavigationEvent(VehicleDisplay.MAIN, numClicks);
460         waitAndAssertLastRotaryEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
461                 CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION, numClicks, mCallback0);
462 
463         r = mCarInputManager.requestInputEventCapture(
464                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
465                 new int[]{CarInputManager.INPUT_TYPE_NAVIGATE_KEYS}, 0, mCallback1);
466         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
467 
468         injectKeyEvent(true, KeyEvent.KEYCODE_NAVIGATE_NEXT);
469         waitAndAssertLastKeyEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, true,
470                 KeyEvent.KEYCODE_NAVIGATE_NEXT, mCallback1);
471     }
472 
473     @Test
testTwoClientsOverwrap()474     public void testTwoClientsOverwrap() throws Exception {
475         CarInputManager carInputManager0 = createAnotherCarInputManager();
476         CarInputManager carInputManager1 = createAnotherCarInputManager();
477 
478         mCallback0.resetAllEventsWaiting();
479         int r = carInputManager0.requestInputEventCapture(
480                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
481                 new int[]{
482                         CarInputManager.INPUT_TYPE_DPAD_KEYS,
483                         CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION}, 0, mCallback0);
484         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
485 
486         injectKeyEvent(true, KeyEvent.KEYCODE_DPAD_CENTER);
487         waitAndAssertLastKeyEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, true,
488                 KeyEvent.KEYCODE_DPAD_CENTER, mCallback0);
489 
490         int numClicks = 3;
491         injectRotaryNavigationEvent(VehicleDisplay.MAIN, numClicks);
492         waitAndAssertLastRotaryEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
493                 CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION, numClicks, mCallback0);
494 
495         r = carInputManager1.requestInputEventCapture(
496                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
497                 new int[]{
498                         CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION,
499                         CarInputManager.INPUT_TYPE_NAVIGATE_KEYS}, 0, mCallback1);
500         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
501 
502         waitAndAssertLastStateChange(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
503                 new int[]{CarInputManager.INPUT_TYPE_DPAD_KEYS},
504                 mCallback0);
505         assertNoStateChange(mCallback1);
506 
507         injectKeyEvent(true, KeyEvent.KEYCODE_NAVIGATE_NEXT);
508         waitAndAssertLastKeyEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, true,
509                 KeyEvent.KEYCODE_NAVIGATE_NEXT, mCallback1);
510         assertNumberOfOnKeyEvents(1, mCallback0);
511 
512         injectKeyEvent(true, KeyEvent.KEYCODE_DPAD_CENTER);
513         waitAndAssertLastKeyEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, true,
514                 KeyEvent.KEYCODE_DPAD_CENTER, mCallback0);
515         assertNumberOfOnKeyEvents(2, mCallback0);
516 
517         injectRotaryNavigationEvent(VehicleDisplay.MAIN, numClicks);
518         waitAndAssertLastRotaryEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
519                 CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION, numClicks, mCallback1);
520         assertNumberOfOnRotaryEvents(1, mCallback0);
521 
522         mCallback0.resetAllEventsWaiting();
523         carInputManager1.releaseInputEventCapture(CarOccupantZoneManager.DISPLAY_TYPE_MAIN);
524 
525         waitAndAssertLastStateChange(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
526                 new int[]{CarInputManager.INPUT_TYPE_DPAD_KEYS,
527                         CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION},
528                 mCallback0);
529         assertNoStateChange(mCallback1);
530 
531         injectRotaryNavigationEvent(VehicleDisplay.MAIN, numClicks);
532         waitAndAssertLastRotaryEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
533                 CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION, numClicks, mCallback0);
534     }
535 
536     @Test
testInteractionWithFullCapturer()537     public void testInteractionWithFullCapturer() throws Exception {
538         CarInputManager carInputManager0 = createAnotherCarInputManager();
539         CarInputManager carInputManager1 = createAnotherCarInputManager();
540 
541         // Request rotary and dpad input event capture for display main (mCallback0)
542         int r = carInputManager0.requestInputEventCapture(
543                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
544                 new int[]{
545                         CarInputManager.INPUT_TYPE_DPAD_KEYS,
546                         CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION}, 0, mCallback0);
547         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
548 
549         // Request all input event capture for display main (mCallback1)
550         r = carInputManager1.requestInputEventCapture(
551                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
552                 new int[]{CarInputManager.INPUT_TYPE_ALL_INPUTS},
553                 CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY, mCallback1);
554         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
555 
556         waitForDispatchToMain();
557         waitAndAssertLastStateChange(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
558                 new int[0], mCallback0);
559         assertNoStateChange(mCallback1);
560 
561         // Release input event capture for main display
562         carInputManager1.releaseInputEventCapture(CarOccupantZoneManager.DISPLAY_TYPE_MAIN);
563         waitForDispatchToMain();
564         waitAndAssertLastStateChange(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
565                 new int[]{CarInputManager.INPUT_TYPE_DPAD_KEYS,
566                         CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION},
567                 mCallback0);
568         assertNoStateChange(mCallback1);
569     }
570 
571     @Test
testFullCapturerAcceptsNotMappedKey()572     public void testFullCapturerAcceptsNotMappedKey() throws Exception {
573         int r = mCarInputManager.requestInputEventCapture(
574                 CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER,
575                 new int[]{CarInputManager.INPUT_TYPE_ALL_INPUTS},
576                 CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY, mCallback0);
577         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
578 
579         injectKeyEvent(true, KeyEvent.KEYCODE_MENU, VehicleDisplay.INSTRUMENT_CLUSTER);
580         waitAndAssertLastKeyEvent(CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER, true,
581                 KeyEvent.KEYCODE_MENU, mCallback0);
582     }
583 
584     @Test
testSingleClientUpdates()585     public void testSingleClientUpdates() {
586         int r = mCarInputManager.requestInputEventCapture(
587                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
588                 new int[]{
589                         CarInputManager.INPUT_TYPE_DPAD_KEYS,
590                         CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION}, 0, mCallback0);
591         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
592 
593         r = mCarInputManager.requestInputEventCapture(
594                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
595                 new int[]{
596                         CarInputManager.INPUT_TYPE_DPAD_KEYS,
597                         CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION}, 0, mCallback0);
598         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
599 
600         waitForDispatchToMain();
601         assertNoStateChange(mCallback0);
602 
603         r = mCarInputManager.requestInputEventCapture(
604                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
605                 new int[]{
606                         CarInputManager.INPUT_TYPE_DPAD_KEYS,
607                         CarInputManager.INPUT_TYPE_NAVIGATE_KEYS}, 0, mCallback0);
608         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
609 
610         waitForDispatchToMain();
611         assertNoStateChange(mCallback0);
612 
613         r = mCarInputManager.requestInputEventCapture(
614                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
615                 new int[]{CarInputManager.INPUT_TYPE_ALL_INPUTS},
616                 CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY, mCallback0);
617         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
618 
619         waitForDispatchToMain();
620         assertNoStateChange(mCallback0);
621 
622         r = mCarInputManager.requestInputEventCapture(
623                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
624                 new int[]{CarInputManager.INPUT_TYPE_ALL_INPUTS},
625                 CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY, mCallback0);
626         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
627 
628         waitForDispatchToMain();
629         assertNoStateChange(mCallback0);
630     }
631 
632     @Test
testInjectingRotaryEventAndExecutor()633     public void testInjectingRotaryEventAndExecutor() throws Exception {
634         // Arrange executors to process events
635         Executor rotaryExecutor = spy(Executors.newSingleThreadExecutor());
636 
637         // Arrange: register callback
638         CarInputManager carInputManager = createAnotherCarInputManager();
639         int r = carInputManager.requestInputEventCapture(
640                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
641                 new int[]{CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION}, 0, rotaryExecutor,
642                 mCallback0);
643         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
644 
645         // Act: inject RotaryEvent
646         int numClicks = 3;
647         injectRotaryNavigationEvent(VehicleDisplay.MAIN, numClicks);
648 
649         // Assert: ensure RotaryEvent was delivered
650         waitAndAssertLastRotaryEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
651                 CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION, numClicks, mCallback0);
652 
653         // Assert: ensure that Rotary event was dispatched using the assigned executor
654         verify(rotaryExecutor).execute(any(Runnable.class));
655     }
656 
657     @Test
testInjectingKeyEventAndExecutor()658     public void testInjectingKeyEventAndExecutor() throws Exception {
659         // Arrange executors to process events
660         Executor keyEventExecutor = spy(Executors.newSingleThreadExecutor());
661 
662         // Arrange: register callback
663         CarInputManager carInputManager = createAnotherCarInputManager();
664         int r = carInputManager.requestInputEventCapture(
665                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
666                 new int[]{CarInputManager.INPUT_TYPE_NAVIGATE_KEYS}, 0, keyEventExecutor,
667                 mCallback0);
668         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
669 
670         // Act: inject KeyEvent
671         injectKeyEvent(true, KeyEvent.KEYCODE_NAVIGATE_NEXT);
672 
673         // Assert: ensure KeyEvent was delivered
674         waitAndAssertLastKeyEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, true,
675                 KeyEvent.KEYCODE_NAVIGATE_NEXT, mCallback0);
676 
677         // Assert: ensure that Key event was dispatched using the assigned executor
678         verify(keyEventExecutor).execute(any(Runnable.class));
679     }
680 
681     @Test
testInjectingCustomInputEventAndExecutor()682     public void testInjectingCustomInputEventAndExecutor() throws Exception {
683         // Arrange executors to process events
684         Executor customInputEventExecutor = spy(Executors.newSingleThreadExecutor());
685 
686         // Arrange: register callback
687         CarInputManager carInputManager = createAnotherCarInputManager();
688         int r = carInputManager.requestInputEventCapture(
689                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
690                 new int[]{CarInputManager.INPUT_TYPE_CUSTOM_INPUT_EVENT}, 0,
691                 customInputEventExecutor, mCallback0);
692         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
693 
694         // Act: inject CustomInputEvent
695         int repeatedCounter = 1;
696         injectCustomInputEvent(CUSTOM_EVENT_F1, VehicleDisplay.MAIN,
697                 /* repeatCounter= */ repeatedCounter);
698 
699         // Assert: ensure CustomInputEvent was delivered
700         waitAndAssertLastCustomInputEvent(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, CUSTOM_EVENT_F1,
701                 repeatedCounter, mCallback0);
702 
703         // Assert: ensure that CustomInputEvent was dispatched using the assigned executor
704         verify(customInputEventExecutor).execute(any(Runnable.class));
705     }
706 
707     @Test
testRotaryVolumeTypeIsNotSupportedYet()708     public void testRotaryVolumeTypeIsNotSupportedYet() {
709         assertThrows(IllegalArgumentException.class,
710                 () -> mCarInputManager.requestInputEventCapture(
711                         CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
712                         new int[]{CarInputManager.INPUT_TYPE_ROTARY_VOLUME}, 0, mCallback0));
713     }
714 
715     @Test
testCallbacksForDifferentDisplays_keyInputEvents_mainDisplay()716     public void testCallbacksForDifferentDisplays_keyInputEvents_mainDisplay() throws Exception {
717         registerCallbackForAllInputs(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, mCallback0);
718         registerCallbackForAllInputs(CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER,
719                 mCallback1);
720 
721         sendAndAssertKeyEvent(KeyEvent.KEYCODE_HOME, CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
722                 mCallback0);
723         assertNoKeyEventSent(mCallback1);
724     }
725 
726     @Test
testCallbacksForDifferentDisplays_keyInputEvents_clusterDisplay()727     public void testCallbacksForDifferentDisplays_keyInputEvents_clusterDisplay() throws Exception {
728         registerCallbackForAllInputs(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, mCallback0);
729         registerCallbackForAllInputs(CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER,
730                 mCallback1);
731 
732         sendAndAssertKeyEvent(KeyEvent.KEYCODE_HOME,
733                 CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER,
734                 mCallback1);
735         assertNoKeyEventSent(mCallback0);
736     }
737 
738     @Test
testCallbacksForDifferentDisplays_customInputEvents_mainDisplay()739     public void testCallbacksForDifferentDisplays_customInputEvents_mainDisplay() throws Exception {
740         registerCallbackForAllInputs(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, mCallback0);
741         registerCallbackForAllInputs(CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER,
742                 mCallback1);
743 
744         sendAndAssertCustomInputEvent(CUSTOM_EVENT_F1,
745                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN, /* repeatedCounter= */ 1,
746                 mCallback0);
747         assertNoCustomInputEventSent(mCallback1);
748     }
749 
750     @Test
testCallbacksForDifferentDisplays_customInputEvents_clusterDisplay()751     public void testCallbacksForDifferentDisplays_customInputEvents_clusterDisplay()
752             throws Exception {
753         registerCallbackForAllInputs(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, mCallback0);
754         registerCallbackForAllInputs(CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER,
755                 mCallback1);
756 
757         sendAndAssertCustomInputEvent(CUSTOM_EVENT_F1,
758                 CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER, /* repeatedCounter= */ 1,
759                 mCallback1);
760         assertNoCustomInputEventSent(mCallback0);
761     }
762 
763     @Test
testCallbacksForDifferentDisplays_rotaryEvents_mainDisplay()764     public void testCallbacksForDifferentDisplays_rotaryEvents_mainDisplay() throws Exception {
765         registerCallbackForAllInputs(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, mCallback0);
766         registerCallbackForAllInputs(CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER,
767                 mCallback1);
768 
769         sendAndAssertRotaryNavigationEvent(
770                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN, /* numClicks= */ 1,
771                 mCallback0);
772         assertNoRotaryEventSent(mCallback1);
773     }
774 
775     @Test
testCallbacksForDifferentDisplays_rotaryEvents_clusterDisplay()776     public void testCallbacksForDifferentDisplays_rotaryEvents_clusterDisplay() throws Exception {
777         registerCallbackForAllInputs(CarOccupantZoneManager.DISPLAY_TYPE_MAIN, mCallback0);
778         registerCallbackForAllInputs(CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER,
779                 mCallback1);
780 
781         sendAndAssertRotaryNavigationEvent(
782                 CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER, /* numClicks= */ 1,
783                 mCallback1);
784         assertNoRotaryEventSent(mCallback0);
785     }
786 
registerCallbackForAllInputs(int displayType, CaptureCallback callback)787     private void registerCallbackForAllInputs(int displayType, CaptureCallback callback) {
788         int respMain = mCarInputManager.requestInputEventCapture(
789                 displayType,
790                 new int[]{CarInputManager.INPUT_TYPE_ALL_INPUTS},
791                 CarInputManager.CAPTURE_REQ_FLAGS_TAKE_ALL_EVENTS_FOR_DISPLAY, callback);
792         assertThat(respMain).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
793     }
794 
795     @Test
testInputTypeSystemNavigateKeys()796     public void testInputTypeSystemNavigateKeys() throws Exception {
797         int r = mCarInputManager.requestInputEventCapture(
798                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN,
799                 new int[]{CarInputManager.INPUT_TYPE_SYSTEM_NAVIGATE_KEYS}, 0, mCallback0);
800         assertThat(r).isEqualTo(CarInputManager.INPUT_CAPTURE_RESPONSE_SUCCEEDED);
801 
802         sendAndAssertKeyEvent(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN,
803                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN, mCallback0);
804         sendAndAssertKeyEvent(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP,
805                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN, mCallback0);
806         sendAndAssertKeyEvent(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT,
807                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN, mCallback0);
808         sendAndAssertKeyEvent(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT,
809                 CarOccupantZoneManager.DISPLAY_TYPE_MAIN, mCallback0);
810     }
811 
sendAndAssertKeyEvent(int keyCode, int displayType, CaptureCallback callback)812     private void sendAndAssertKeyEvent(int keyCode, int displayType, CaptureCallback callback)
813             throws Exception {
814         KeyEvent keyEvent = newKeyEvent(KeyEvent.ACTION_DOWN, keyCode);
815 
816         mCarInputManager.injectKeyEvent(keyEvent, displayType);
817 
818         callback.waitForKeyEvent();
819         assertThat(callback.getkeyEvents()).contains(
820                 new Pair<>(displayType,
821                         Collections.singletonList(keyEvent)));
822     }
823 
sendAndAssertCustomInputEvent(int inputCode, int displayType, int repeatedCounter, CaptureCallback callback)824     private void sendAndAssertCustomInputEvent(int inputCode, int displayType, int repeatedCounter,
825             CaptureCallback callback)
826             throws Exception {
827         injectCustomInputEvent(inputCode,
828                 convertToVehicleDisplay(displayType), repeatedCounter);
829 
830         callback.waitForCustomInputEvent();
831         assertThat(callback.getCustomInputEvents()).hasSize(1);
832         Pair<Integer, List<CustomInputEvent>> customInputEvents =
833                 callback.getCustomInputEvents().get(0);
834         assertThat(customInputEvents.second).hasSize(1);
835         CustomInputEvent customInputEvent = customInputEvents.second.get(0);
836         assertThat(customInputEvent.getInputCode()).isEqualTo(inputCode);
837         assertThat(customInputEvent.getTargetDisplayType()).isEqualTo(displayType);
838         assertThat(customInputEvent.getRepeatCounter()).isEqualTo(repeatedCounter);
839     }
840 
841     /**
842      * Utility method to convert CarOccupantZoneManager display type to Vehicle HAL display type
843      */
convertToVehicleDisplay(int vehicleDisplayType)844     private static int convertToVehicleDisplay(int vehicleDisplayType) {
845         switch (vehicleDisplayType) {
846             case CarOccupantZoneManager.DISPLAY_TYPE_MAIN:
847                 return VehicleDisplay.MAIN;
848             case CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER:
849                 return VehicleDisplay.INSTRUMENT_CLUSTER;
850             default:
851                 throw new IllegalArgumentException(
852                         "CarOccupantZone display type {" + vehicleDisplayType
853                                 + "} has no equivalent in VehicleDisplay display type");
854         }
855     }
856 
sendAndAssertRotaryNavigationEvent(int displayType, int numClicks, CaptureCallback callback)857     private void sendAndAssertRotaryNavigationEvent(int displayType, int numClicks,
858             CaptureCallback callback)
859             throws Exception {
860         injectRotaryNavigationEvent(convertToVehicleDisplay(displayType),
861                 numClicks);
862 
863         callback.waitForRotaryEvent();
864         assertThat(callback.getRotaryEvents()).hasSize(1);
865         Pair<Integer, List<RotaryEvent>> capturedEvents = callback.getRotaryEvents().get(0);
866         assertThat(capturedEvents.second).hasSize(1);
867         RotaryEvent rotaryEvent = capturedEvents.second.get(0);
868         assertThat(rotaryEvent.getNumberOfClicks()).isEqualTo(numClicks);
869     }
870 
871     /**
872      * Events dispatched to main, so this should guarantee that all event dispatched are completed.
873      */
waitForDispatchToMain()874     private void waitForDispatchToMain() {
875         // Needs to be invoked twice as it is dispatched to main inside car service once, and it is
876         // dispatched to main inside CarInputManager once.
877         CarServiceUtils.runOnMainSync(() -> {});
878         CarServiceUtils.runOnMainSync(() -> {});
879     }
880 
waitAndAssertLastKeyEvent(int displayTarget, boolean down, int keyCode, CaptureCallback callback)881     private void waitAndAssertLastKeyEvent(int displayTarget, boolean down, int keyCode,
882             CaptureCallback callback) throws Exception {
883         // Wait for key event first.
884         callback.waitForKeyEvent();
885 
886         LinkedList<Pair<Integer, List<KeyEvent>>> events = callback.getkeyEvents();
887         assertThat(events).isNotEmpty();
888         Pair<Integer, List<KeyEvent>> lastEvent = events.getFirst();
889         assertThat(lastEvent.first).isEqualTo(displayTarget);
890         assertThat(lastEvent.second).hasSize(1);
891         KeyEvent keyEvent = lastEvent.second.get(0);
892         assertThat(keyEvent.getAction()).isEqualTo(
893                 down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP);
894         assertThat(keyEvent.getKeyCode()).isEqualTo(keyCode);
895     }
896 
assertNumberOfOnKeyEvents(int expectedNumber, CaptureCallback callback)897     private void assertNumberOfOnKeyEvents(int expectedNumber, CaptureCallback callback) {
898         LinkedList<Pair<Integer, List<KeyEvent>>> events = callback.getkeyEvents();
899         assertThat(events).hasSize(expectedNumber);
900     }
901 
waitAndAssertLastRotaryEvent(int displayTarget, int rotaryType, int numberOfClicks, CaptureCallback callback)902     private void waitAndAssertLastRotaryEvent(int displayTarget, int rotaryType, int numberOfClicks,
903             CaptureCallback callback) throws Exception {
904         // Wait for rotary event first.
905         callback.waitForRotaryEvent();
906 
907         LinkedList<Pair<Integer, List<RotaryEvent>>> rotaryEvents = callback.getRotaryEvents();
908         assertThat(rotaryEvents).isNotEmpty();
909         Pair<Integer, List<RotaryEvent>> lastEvent = rotaryEvents.getFirst();
910         assertThat(lastEvent.first).isEqualTo(displayTarget);
911         assertThat(lastEvent.second).hasSize(1);
912         RotaryEvent rotaryEvent = lastEvent.second.get(0);
913         assertThat(rotaryEvent.getInputType()).isEqualTo(rotaryType);
914         assertThat(rotaryEvent.getNumberOfClicks()).isEqualTo(numberOfClicks);
915         // TODO(b/151225008) Test timestamp
916     }
917 
assertNumberOfOnRotaryEvents(int expectedNumber, CaptureCallback callback)918     private void assertNumberOfOnRotaryEvents(int expectedNumber, CaptureCallback callback) {
919         LinkedList<Pair<Integer, List<RotaryEvent>>> rotaryEvents = callback.getRotaryEvents();
920         assertThat(rotaryEvents).hasSize(expectedNumber);
921     }
922 
waitAndAssertLastStateChange(int expectedTargetDisplayTarget, int[] expectedInputTypes, CaptureCallback callback)923     private void waitAndAssertLastStateChange(int expectedTargetDisplayTarget,
924             int[] expectedInputTypes, CaptureCallback callback) throws Exception {
925         // Wait for state change event first.
926         callback.waitForStateChange();
927 
928         LinkedList<Pair<Integer, int[]>> changes = callback.getStateChanges();
929         assertThat(changes).isNotEmpty();
930         Pair<Integer, int[]> lastChange = changes.getFirst();
931         assertStateChange(expectedTargetDisplayTarget, expectedInputTypes, lastChange);
932     }
933 
assertNoStateChange(CaptureCallback callback)934     private void assertNoStateChange(CaptureCallback callback) {
935         assertThat(callback.getStateChanges()).isEmpty();
936     }
937 
assertNoKeyEventSent(CaptureCallback callback)938     private void assertNoKeyEventSent(CaptureCallback callback) {
939         assertThat(callback.getkeyEvents()).isEmpty();
940     }
941 
assertNoCustomInputEventSent(CaptureCallback callback)942     private void assertNoCustomInputEventSent(CaptureCallback callback) {
943         assertThat(callback.getCustomInputEvents()).isEmpty();
944     }
945 
assertNoRotaryEventSent(CaptureCallback callback)946     private void assertNoRotaryEventSent(CaptureCallback callback) {
947         assertThat(callback.getRotaryEvents()).isEmpty();
948     }
949 
assertStateChange(int expectedTargetDisplayTarget, int[] expectedInputTypes, Pair<Integer, int[]> actual)950     private void assertStateChange(int expectedTargetDisplayTarget, int[] expectedInputTypes,
951             Pair<Integer, int[]> actual) {
952         Arrays.sort(expectedInputTypes);
953         assertThat(actual.first).isEqualTo(expectedTargetDisplayTarget);
954         assertThat(actual.second).isEqualTo(expectedInputTypes);
955     }
956 
waitAndAssertLastCustomInputEvent(int expectedDisplayType, int expectedCustomEventFunction, int expectedRepeatedCounter, CaptureCallback callback)957     private void waitAndAssertLastCustomInputEvent(int expectedDisplayType,
958             int expectedCustomEventFunction, int expectedRepeatedCounter,
959             CaptureCallback callback) throws Exception {
960         // Wait for custom input event first.
961         callback.waitForCustomInputEvent();
962 
963         LinkedList<Pair<Integer, List<CustomInputEvent>>> events = callback.getCustomInputEvents();
964         assertThat(events).isNotEmpty();
965 
966         Pair<Integer, List<CustomInputEvent>> lastEvent = events.getFirst();
967         assertThat(lastEvent.first).isEqualTo(expectedDisplayType);
968         assertThat(lastEvent.second).hasSize(1);
969 
970         CustomInputEvent event = lastEvent.second.get(0);
971         assertThat(event.getInputCode()).isEqualTo(expectedCustomEventFunction);
972         assertThat(event.getRepeatCounter()).isEqualTo(expectedRepeatedCounter);
973         assertThat(event.getTargetDisplayType()).isEqualTo(expectedDisplayType);
974     }
975 
injectKeyEvent(boolean down, int keyCode)976     private void injectKeyEvent(boolean down, int keyCode) {
977         injectKeyEvent(down, keyCode, VehicleDisplay.MAIN);
978     }
979 
injectKeyEvent(boolean down, int keyCode, int vehicleDisplayType)980     private void injectKeyEvent(boolean down, int keyCode, int vehicleDisplayType) {
981         getAidlMockedVehicleHal().injectEvent(
982                 AidlVehiclePropValueBuilder.newBuilder(VehicleProperty.HW_KEY_INPUT)
983                         .addIntValues(down ? 0 : 1, keyCode, vehicleDisplayType)
984                         .build());
985     }
986 
injectRotaryNavigationEvent(int displayTarget, int numClicks)987     private void injectRotaryNavigationEvent(int displayTarget, int numClicks) {
988         AidlVehiclePropValueBuilder builder = AidlVehiclePropValueBuilder.newBuilder(
989                 VehicleProperty.HW_ROTARY_INPUT)
990                 .addIntValues(RotaryInputType.ROTARY_INPUT_TYPE_SYSTEM_NAVIGATION, numClicks,
991                         displayTarget);
992         for (int i = 0; i < numClicks - 1; i++) {
993             builder.addIntValues(0);
994         }
995         getAidlMockedVehicleHal().injectEvent(builder.build());
996     }
997 
injectCustomInputEvent(int inputCode, int targetDisplayType, int repeatCounter)998     private void injectCustomInputEvent(int inputCode, int targetDisplayType, int repeatCounter) {
999         AidlVehiclePropValueBuilder builder = AidlVehiclePropValueBuilder.newBuilder(
1000                 VehicleProperty.HW_CUSTOM_INPUT)
1001                 .addIntValues(inputCode).addIntValues(targetDisplayType)
1002                 .addIntValues(repeatCounter);
1003         getAidlMockedVehicleHal().injectEvent(builder.build());
1004     }
1005 }
1006