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