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