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