1 /* 2 * Copyright (C) 2016 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 package com.android.car.hal; 17 18 import static android.car.CarOccupantZoneManager.DisplayTypeEnum; 19 import static android.hardware.automotive.vehicle.RotaryInputType.ROTARY_INPUT_TYPE_AUDIO_VOLUME; 20 import static android.hardware.automotive.vehicle.RotaryInputType.ROTARY_INPUT_TYPE_SYSTEM_NAVIGATION; 21 import static android.hardware.automotive.vehicle.VehicleProperty.HW_CUSTOM_INPUT; 22 import static android.hardware.automotive.vehicle.VehicleProperty.HW_KEY_INPUT; 23 import static android.hardware.automotive.vehicle.VehicleProperty.HW_KEY_INPUT_V2; 24 import static android.hardware.automotive.vehicle.VehicleProperty.HW_MOTION_INPUT; 25 import static android.hardware.automotive.vehicle.VehicleProperty.HW_ROTARY_INPUT; 26 27 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 28 29 import static java.util.concurrent.TimeUnit.NANOSECONDS; 30 31 import android.car.CarOccupantZoneManager; 32 import android.car.builtin.util.Slogf; 33 import android.car.input.CarInputManager; 34 import android.car.input.CustomInputEvent; 35 import android.car.input.RotaryEvent; 36 import android.hardware.automotive.vehicle.VehicleDisplay; 37 import android.hardware.automotive.vehicle.VehicleHwKeyInputAction; 38 import android.hardware.automotive.vehicle.VehicleHwMotionButtonStateFlag; 39 import android.hardware.automotive.vehicle.VehicleHwMotionInputAction; 40 import android.hardware.automotive.vehicle.VehicleHwMotionInputSource; 41 import android.hardware.automotive.vehicle.VehicleHwMotionToolType; 42 import android.os.SystemClock; 43 import android.util.Log; 44 import android.util.SparseArray; 45 import android.view.InputDevice; 46 import android.view.KeyEvent; 47 import android.view.MotionEvent; 48 import android.view.MotionEvent.PointerCoords; 49 import android.view.MotionEvent.PointerProperties; 50 51 import com.android.car.CarLog; 52 import com.android.car.CarServiceUtils; 53 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 54 import com.android.internal.annotations.GuardedBy; 55 import com.android.internal.annotations.VisibleForTesting; 56 57 import java.io.PrintWriter; 58 import java.util.ArrayDeque; 59 import java.util.Collection; 60 import java.util.List; 61 import java.util.Queue; 62 import java.util.concurrent.TimeUnit; 63 import java.util.function.LongSupplier; 64 65 /** 66 * Translates HAL input events to higher-level semantic information. 67 */ 68 public class InputHalService extends HalServiceBase { 69 70 private static final int MAX_EVENTS_TO_KEEP_AS_HISTORY = 10; 71 private static final String TAG = CarLog.TAG_INPUT; 72 private static final int[] SUPPORTED_PROPERTIES = new int[]{ 73 HW_KEY_INPUT, 74 HW_KEY_INPUT_V2, 75 HW_MOTION_INPUT, 76 HW_ROTARY_INPUT, 77 HW_CUSTOM_INPUT 78 }; 79 80 private final VehicleHal mHal; 81 82 @GuardedBy("mLock") 83 private final Queue<MotionEvent> mLastFewDispatchedMotionEvents = 84 new ArrayDeque<>(MAX_EVENTS_TO_KEEP_AS_HISTORY); 85 86 @GuardedBy("mLock") 87 private Queue<KeyEvent> mLastFewDispatchedV2KeyEvents = new ArrayDeque<>( 88 MAX_EVENTS_TO_KEEP_AS_HISTORY); 89 90 /** 91 * A function to retrieve the current system uptime in milliseconds - replaceable for testing. 92 */ 93 private final LongSupplier mUptimeSupplier; 94 95 /** 96 * Interface used to act upon HAL incoming key events. 97 */ 98 public interface InputListener { 99 /** Called for key event */ onKeyEvent(KeyEvent event, int targetDisplay)100 void onKeyEvent(KeyEvent event, int targetDisplay); 101 102 /** Called for key event per seat */ onKeyEvent(KeyEvent event, int targetDisplay, int seat)103 void onKeyEvent(KeyEvent event, int targetDisplay, int seat); 104 105 /** Called for motion event per seat */ onMotionEvent(MotionEvent event, int targetDisplay, int seat)106 void onMotionEvent(MotionEvent event, int targetDisplay, int seat); 107 108 /** Called for rotary event */ onRotaryEvent(RotaryEvent event, int targetDisplay)109 void onRotaryEvent(RotaryEvent event, int targetDisplay); 110 111 /** Called for OEM custom input event */ onCustomInputEvent(CustomInputEvent event)112 void onCustomInputEvent(CustomInputEvent event); 113 } 114 115 /** The current press state of a key. */ 116 private static class KeyState { 117 /** The timestamp (uptimeMillis) of the last ACTION_DOWN event for this key. */ 118 public long mLastKeyDownTimestamp = -1; 119 /** The number of ACTION_DOWN events that have been sent for this keypress. */ 120 public int mRepeatCount = 0; 121 } 122 123 private final Object mLock = new Object(); 124 125 @GuardedBy("mLock") 126 private boolean mKeyInputSupported; 127 128 @GuardedBy("mLock") 129 private boolean mKeyInputV2Supported; 130 131 @GuardedBy("mLock") 132 private boolean mMotionInputSupported; 133 134 @GuardedBy("mLock") 135 private boolean mRotaryInputSupported; 136 137 @GuardedBy("mLock") 138 private boolean mCustomInputSupported; 139 140 @GuardedBy("mLock") 141 private InputListener mListener; 142 143 @GuardedBy("mKeyStates") 144 private final SparseArray<KeyState> mKeyStates = new SparseArray<>(); 145 InputHalService(VehicleHal hal)146 public InputHalService(VehicleHal hal) { 147 this(hal, SystemClock::uptimeMillis); 148 } 149 150 @VisibleForTesting InputHalService(VehicleHal hal, LongSupplier uptimeSupplier)151 InputHalService(VehicleHal hal, LongSupplier uptimeSupplier) { 152 super(); 153 mHal = hal; 154 mUptimeSupplier = uptimeSupplier; 155 } 156 157 /** 158 * Sets the input event listener. 159 */ setInputListener(InputListener listener)160 public void setInputListener(InputListener listener) { 161 boolean keyInputSupported; 162 boolean keyInputV2Supported; 163 boolean motionInputSupported; 164 boolean rotaryInputSupported; 165 boolean customInputSupported; 166 synchronized (mLock) { 167 if (!mKeyInputSupported && !mRotaryInputSupported && !mCustomInputSupported) { 168 Slogf.w(TAG, "input listener set while rotary and key input not supported"); 169 return; 170 } 171 mListener = listener; 172 keyInputSupported = mKeyInputSupported; 173 keyInputV2Supported = mKeyInputV2Supported; 174 motionInputSupported = mMotionInputSupported; 175 rotaryInputSupported = mRotaryInputSupported; 176 customInputSupported = mCustomInputSupported; 177 } 178 if (keyInputSupported) { 179 mHal.subscribePropertySafe(this, HW_KEY_INPUT); 180 } 181 if (keyInputV2Supported) { 182 mHal.subscribePropertySafe(this, HW_KEY_INPUT_V2); 183 } 184 if (motionInputSupported) { 185 mHal.subscribePropertySafe(this, HW_MOTION_INPUT); 186 } 187 if (rotaryInputSupported) { 188 mHal.subscribePropertySafe(this, HW_ROTARY_INPUT); 189 } 190 if (customInputSupported) { 191 mHal.subscribePropertySafe(this, HW_CUSTOM_INPUT); 192 } 193 } 194 195 /** Returns whether {@code HW_KEY_INPUT} is supported. */ isKeyInputSupported()196 public boolean isKeyInputSupported() { 197 synchronized (mLock) { 198 return mKeyInputSupported; 199 } 200 } 201 202 /** Returns whether {@code HW_KEY_INPUT_V2} is supported. */ isKeyInputV2Supported()203 public boolean isKeyInputV2Supported() { 204 synchronized (mLock) { 205 return mKeyInputV2Supported; 206 } 207 } 208 209 /** Returns whether {@code HW_MOTION_INPUT} is supported. */ isMotionInputSupported()210 public boolean isMotionInputSupported() { 211 synchronized (mLock) { 212 return mMotionInputSupported; 213 } 214 } 215 216 /** Returns whether {@code HW_ROTARY_INPUT} is supported. */ isRotaryInputSupported()217 public boolean isRotaryInputSupported() { 218 synchronized (mLock) { 219 return mRotaryInputSupported; 220 } 221 } 222 223 /** Returns whether {@code HW_CUSTOM_INPUT} is supported. */ isCustomInputSupported()224 public boolean isCustomInputSupported() { 225 synchronized (mLock) { 226 return mCustomInputSupported; 227 } 228 } 229 230 @Override init()231 public void init() { 232 } 233 234 @Override release()235 public void release() { 236 synchronized (mLock) { 237 mListener = null; 238 mKeyInputSupported = false; 239 mKeyInputV2Supported = false; 240 mMotionInputSupported = false; 241 mRotaryInputSupported = false; 242 mCustomInputSupported = false; 243 } 244 } 245 246 @Override getAllSupportedProperties()247 int[] getAllSupportedProperties() { 248 return SUPPORTED_PROPERTIES; 249 } 250 251 @Override takeProperties(Collection<HalPropConfig> properties)252 public void takeProperties(Collection<HalPropConfig> properties) { 253 synchronized (mLock) { 254 for (HalPropConfig property : properties) { 255 switch (property.getPropId()) { 256 case HW_KEY_INPUT: 257 mKeyInputSupported = true; 258 break; 259 case HW_KEY_INPUT_V2: 260 mKeyInputV2Supported = true; 261 break; 262 case HW_MOTION_INPUT: 263 mMotionInputSupported = true; 264 break; 265 case HW_ROTARY_INPUT: 266 mRotaryInputSupported = true; 267 break; 268 case HW_CUSTOM_INPUT: 269 mCustomInputSupported = true; 270 break; 271 default: 272 break; 273 } 274 } 275 } 276 } 277 278 @Override onHalEvents(List<HalPropValue> values)279 public void onHalEvents(List<HalPropValue> values) { 280 InputListener listener; 281 synchronized (mLock) { 282 listener = mListener; 283 } 284 if (listener == null) { 285 Slogf.w(TAG, "Input event while listener is null"); 286 return; 287 } 288 for (int i = 0; i < values.size(); i++) { 289 HalPropValue value = values.get(i); 290 switch (value.getPropId()) { 291 case HW_KEY_INPUT: 292 dispatchKeyInput(listener, value); 293 break; 294 case HW_KEY_INPUT_V2: 295 dispatchKeyInputV2(listener, value); 296 break; 297 case HW_MOTION_INPUT: 298 dispatchMotionInput(listener, value); 299 break; 300 case HW_ROTARY_INPUT: 301 dispatchRotaryInput(listener, value); 302 break; 303 case HW_CUSTOM_INPUT: 304 dispatchCustomInput(listener, value); 305 break; 306 default: 307 Slogf.e(TAG, "Wrong event dispatched, prop:0x%x", value.getPropId()); 308 break; 309 } 310 } 311 } 312 dispatchKeyInput(InputListener listener, HalPropValue value)313 private void dispatchKeyInput(InputListener listener, HalPropValue value) { 314 int action; 315 int code; 316 int vehicleDisplay; 317 int indentsCount; 318 try { 319 action = (value.getInt32Value(0) == VehicleHwKeyInputAction.ACTION_DOWN) 320 ? KeyEvent.ACTION_DOWN 321 : KeyEvent.ACTION_UP; 322 code = value.getInt32Value(1); 323 vehicleDisplay = value.getInt32Value(2); 324 indentsCount = value.getInt32ValuesSize() < 4 ? 1 : value.getInt32Value(3); 325 Slogf.d(TAG, "hal event code: %d, action: %d, display: %d, number of indents: %d", 326 code, action, vehicleDisplay, indentsCount); 327 } catch (Exception e) { 328 Slogf.e(TAG, "Invalid hal key input event received, int32Values: " 329 + value.dumpInt32Values(), e); 330 return; 331 } 332 while (indentsCount > 0) { 333 indentsCount--; 334 dispatchKeyEvent(listener, action, code, convertDisplayType(vehicleDisplay)); 335 } 336 } 337 338 private void dispatchKeyInputV2(InputListener listener, HalPropValue value) { 339 final int int32ValuesSize = 4; 340 final int int64ValuesSize = 1; 341 int seat; 342 int vehicleDisplay; 343 int keyCode; 344 int action; 345 int repeatCount; 346 long elapsedDownTimeNanos; 347 long elapsedEventTimeNanos; 348 int convertedAction; 349 int convertedVehicleDisplay; 350 try { 351 seat = value.getAreaId(); 352 if (value.getInt32ValuesSize() < int32ValuesSize) { 353 Slogf.e(TAG, "Wrong int32 array size for key input v2 from vhal: %d", 354 value.getInt32ValuesSize()); 355 return; 356 } 357 vehicleDisplay = value.getInt32Value(0); 358 keyCode = value.getInt32Value(1); 359 action = value.getInt32Value(2); 360 repeatCount = value.getInt32Value(3); 361 362 if (value.getInt64ValuesSize() < int64ValuesSize) { 363 Slogf.e(TAG, "Wrong int64 array size for key input v2 from vhal: %d", 364 value.getInt64ValuesSize()); 365 return; 366 } 367 elapsedDownTimeNanos = value.getInt64Value(0); 368 if (Slogf.isLoggable(TAG, Log.DEBUG)) { 369 Slogf.d(TAG, "hal event keyCode: %d, action: %d, display: %d, repeatCount: %d" 370 + ", elapsedDownTimeNanos: %d", keyCode, action, vehicleDisplay, 371 repeatCount, elapsedDownTimeNanos); 372 } 373 convertedAction = convertToKeyEventAction(action); 374 convertedVehicleDisplay = convertDisplayType(vehicleDisplay); 375 } catch (Exception e) { 376 Slogf.e(TAG, "Invalid hal key input event received, int32Values: " 377 + value.dumpInt32Values() + ", int64Values: " + value.dumpInt64Values(), e); 378 return; 379 } 380 381 if (action == VehicleHwKeyInputAction.ACTION_DOWN) { 382 // For action down, the code should make sure that event time & down time are the same 383 // to maintain the invariant as defined in KeyEvent.java. 384 elapsedEventTimeNanos = elapsedDownTimeNanos; 385 } else { 386 elapsedEventTimeNanos = value.getTimestamp(); 387 } 388 389 dispatchKeyEventV2(listener, convertedAction, keyCode, convertedVehicleDisplay, 390 toUpTimeMillis(elapsedEventTimeNanos), toUpTimeMillis(elapsedDownTimeNanos), 391 repeatCount, seat); 392 } 393 394 private void dispatchMotionInput(InputListener listener, HalPropValue value) { 395 final int firstInt32ArrayOffset = 5; 396 final int int64ValuesSize = 1; 397 final int numInt32Arrays = 2; 398 final int numFloatArrays = 4; 399 int seat; 400 int vehicleDisplay; 401 int inputSource; 402 int action; 403 int buttonStateFlag; 404 int pointerCount; 405 int[] pointerIds; 406 int[] toolTypes; 407 float[] xData; 408 float[] yData; 409 float[] pressureData; 410 float[] sizeData; 411 long elapsedDownTimeNanos; 412 PointerProperties[] pointerProperties; 413 PointerCoords[] pointerCoords; 414 int convertedInputSource; 415 int convertedAction; 416 int convertedButtonStateFlag; 417 try { 418 seat = value.getAreaId(); 419 if (value.getInt32ValuesSize() < firstInt32ArrayOffset) { 420 Slogf.e(TAG, "Wrong int32 array size for key input v2 from vhal: %d", 421 value.getInt32ValuesSize()); 422 return; 423 } 424 vehicleDisplay = value.getInt32Value(0); 425 inputSource = value.getInt32Value(1); 426 action = value.getInt32Value(2); 427 buttonStateFlag = value.getInt32Value(3); 428 pointerCount = value.getInt32Value(4); 429 if (pointerCount < 1) { 430 Slogf.e(TAG, "Wrong pointerCount for key input v2 from vhal: %d", 431 pointerCount); 432 return; 433 } 434 pointerIds = new int[pointerCount]; 435 toolTypes = new int[pointerCount]; 436 xData = new float[pointerCount]; 437 yData = new float[pointerCount]; 438 pressureData = new float[pointerCount]; 439 sizeData = new float[pointerCount]; 440 if (value.getInt32ValuesSize() < firstInt32ArrayOffset 441 + pointerCount * numInt32Arrays) { 442 Slogf.e(TAG, "Wrong int32 array size for key input v2 from vhal: %d", 443 value.getInt32ValuesSize()); 444 return; 445 } 446 if (value.getFloatValuesSize() < pointerCount * numFloatArrays) { 447 Slogf.e(TAG, "Wrong int32 array size for key input v2 from vhal: %d", 448 value.getInt32ValuesSize()); 449 return; 450 } 451 for (int i = 0; i < pointerCount; i++) { 452 pointerIds[i] = value.getInt32Value(firstInt32ArrayOffset + i); 453 toolTypes[i] = value.getInt32Value(firstInt32ArrayOffset + pointerCount + i); 454 xData[i] = value.getFloatValue(i); 455 yData[i] = value.getFloatValue(pointerCount + i); 456 pressureData[i] = value.getFloatValue(2 * pointerCount + i); 457 sizeData[i] = value.getFloatValue(3 * pointerCount + i); 458 } 459 if (value.getInt64ValuesSize() < int64ValuesSize) { 460 Slogf.e(TAG, "Wrong int64 array size for key input v2 from vhal: %d", 461 value.getInt64ValuesSize()); 462 return; 463 } 464 elapsedDownTimeNanos = value.getInt64Value(0); 465 466 if (Slogf.isLoggable(TAG, Log.DEBUG)) { 467 Slogf.d(TAG, "hal motion event inputSource: %d, action: %d, display: %d" 468 + ", buttonStateFlag: %d, pointerCount: %d, elapsedDownTimeNanos: " 469 + "%d", inputSource, action, vehicleDisplay, buttonStateFlag, 470 pointerCount, elapsedDownTimeNanos); 471 } 472 pointerProperties = createPointerPropertiesArray(pointerCount); 473 pointerCoords = createPointerCoordsArray(pointerCount); 474 for (int i = 0; i < pointerCount; i++) { 475 pointerProperties[i].id = pointerIds[i]; 476 pointerProperties[i].toolType = convertToolType(toolTypes[i]); 477 pointerCoords[i].x = xData[i]; 478 pointerCoords[i].y = yData[i]; 479 pointerCoords[i].pressure = pressureData[i]; 480 pointerCoords[i].size = sizeData[i]; 481 } 482 483 convertedAction = convertMotionAction(action); 484 convertedButtonStateFlag = convertButtonStateFlag(buttonStateFlag); 485 convertedInputSource = convertInputSource(inputSource); 486 } catch (Exception e) { 487 Slogf.e(TAG, "Invalid hal key input event received, int32Values: " 488 + value.dumpInt32Values() + ", floatValues: " + value.dumpFloatValues() 489 + ", int64Values: " + value.dumpInt64Values(), e); 490 return; 491 } 492 MotionEvent event = MotionEvent.obtain(toUpTimeMillis(elapsedDownTimeNanos), 493 toUpTimeMillis(value.getTimestamp()) /* eventTime */, 494 convertedAction, 495 pointerCount, 496 pointerProperties, 497 pointerCoords, 498 0 /* metaState */, 499 convertedButtonStateFlag, 500 0f /* xPrecision */, 501 0f /* yPrecision */, 502 0 /* deviceId */, 503 0 /* edgeFlags */, 504 convertedInputSource, 505 0 /* flags */); 506 listener.onMotionEvent(event, convertDisplayType(vehicleDisplay), seat); 507 saveMotionEventInHistory(event); 508 } 509 510 private void saveV2KeyInputEventInHistory(KeyEvent keyEvent) { 511 synchronized (mLock) { 512 while (mLastFewDispatchedV2KeyEvents.size() >= MAX_EVENTS_TO_KEEP_AS_HISTORY) { 513 mLastFewDispatchedV2KeyEvents.remove(); 514 } 515 mLastFewDispatchedV2KeyEvents.add(keyEvent); 516 } 517 } 518 519 private void saveMotionEventInHistory(MotionEvent motionEvent) { 520 synchronized (mLock) { 521 while (mLastFewDispatchedMotionEvents.size() >= MAX_EVENTS_TO_KEEP_AS_HISTORY) { 522 mLastFewDispatchedMotionEvents.remove(); 523 } 524 mLastFewDispatchedMotionEvents.add(motionEvent); 525 } 526 } 527 528 private static long toUpTimeMillis(long elapsedEventTimeNanos) { 529 final byte maxTries = 5; 530 long timeSpentInSleep1 = 0; 531 long timeSpentInSleep2 = 0; 532 long smallestTimeSpentInSleep = Integer.MAX_VALUE; 533 int tryNum; 534 for (tryNum = 0; tryNum < maxTries; tryNum++) { 535 timeSpentInSleep1 = SystemClock.elapsedRealtime() - SystemClock.uptimeMillis(); 536 timeSpentInSleep2 = SystemClock.elapsedRealtime() - SystemClock.uptimeMillis(); 537 if (timeSpentInSleep1 < smallestTimeSpentInSleep) { 538 smallestTimeSpentInSleep = timeSpentInSleep1; 539 } 540 if (timeSpentInSleep2 < smallestTimeSpentInSleep) { 541 smallestTimeSpentInSleep = timeSpentInSleep2; 542 } 543 if (timeSpentInSleep1 == timeSpentInSleep2) { 544 break; 545 } 546 } 547 // If maxTries was reached, use the smallest of all calculated timeSpentInSleep. 548 long eventUpTimeMillis; 549 if (tryNum == maxTries) { 550 // Assuming no sleep after elapsedEventTimeNanos. 551 eventUpTimeMillis = NANOSECONDS.toMillis(elapsedEventTimeNanos) 552 - smallestTimeSpentInSleep; 553 } else { 554 // Assuming no sleep after elapsedEventTimeNanos. 555 eventUpTimeMillis = NANOSECONDS.toMillis(elapsedEventTimeNanos) - timeSpentInSleep1; 556 } 557 return eventUpTimeMillis; 558 } 559 560 private static PointerProperties[] createPointerPropertiesArray(int size) { 561 PointerProperties[] array = new PointerProperties[size]; 562 for (int i = 0; i < size; i++) { 563 array[i] = new PointerProperties(); 564 } 565 return array; 566 } 567 568 private static PointerCoords[] createPointerCoordsArray(int size) { 569 PointerCoords[] array = new PointerCoords[size]; 570 for (int i = 0; i < size; i++) { 571 array[i] = new PointerCoords(); 572 } 573 return array; 574 } 575 576 private int convertToKeyEventAction(int vehicleHwKeyAction) { 577 switch (vehicleHwKeyAction) { 578 case VehicleHwKeyInputAction.ACTION_DOWN: 579 return KeyEvent.ACTION_DOWN; 580 case VehicleHwKeyInputAction.ACTION_UP: 581 return KeyEvent.ACTION_UP; 582 default: 583 throw new IllegalArgumentException("Unexpected key event action: " 584 + vehicleHwKeyAction); 585 } 586 } 587 588 private int convertInputSource(int vehicleInputSource) { 589 switch (vehicleInputSource) { 590 case VehicleHwMotionInputSource.SOURCE_KEYBOARD: 591 return InputDevice.SOURCE_KEYBOARD; 592 case VehicleHwMotionInputSource.SOURCE_DPAD: 593 return InputDevice.SOURCE_DPAD; 594 case VehicleHwMotionInputSource.SOURCE_GAMEPAD: 595 return InputDevice.SOURCE_GAMEPAD; 596 case VehicleHwMotionInputSource.SOURCE_TOUCHSCREEN: 597 return InputDevice.SOURCE_TOUCHSCREEN; 598 case VehicleHwMotionInputSource.SOURCE_MOUSE: 599 return InputDevice.SOURCE_MOUSE; 600 case VehicleHwMotionInputSource.SOURCE_STYLUS: 601 return InputDevice.SOURCE_STYLUS; 602 case VehicleHwMotionInputSource.SOURCE_BLUETOOTH_STYLUS: 603 return InputDevice.SOURCE_BLUETOOTH_STYLUS; 604 case VehicleHwMotionInputSource.SOURCE_TRACKBALL: 605 return InputDevice.SOURCE_TRACKBALL; 606 case VehicleHwMotionInputSource.SOURCE_MOUSE_RELATIVE: 607 return InputDevice.SOURCE_MOUSE_RELATIVE; 608 case VehicleHwMotionInputSource.SOURCE_TOUCHPAD: 609 return InputDevice.SOURCE_TOUCHPAD; 610 case VehicleHwMotionInputSource.SOURCE_TOUCH_NAVIGATION: 611 return InputDevice.SOURCE_TOUCH_NAVIGATION; 612 case VehicleHwMotionInputSource.SOURCE_ROTARY_ENCODER: 613 return InputDevice.SOURCE_ROTARY_ENCODER; 614 case VehicleHwMotionInputSource.SOURCE_JOYSTICK: 615 return InputDevice.SOURCE_JOYSTICK; 616 case VehicleHwMotionInputSource.SOURCE_HDMI: 617 return InputDevice.SOURCE_HDMI; 618 case VehicleHwMotionInputSource.SOURCE_SENSOR: 619 return InputDevice.SOURCE_SENSOR; 620 default: 621 return InputDevice.SOURCE_UNKNOWN; 622 } 623 } 624 625 private int convertMotionAction(int vehicleAction) { 626 switch (vehicleAction) { 627 case VehicleHwMotionInputAction.ACTION_DOWN: 628 return MotionEvent.ACTION_DOWN; 629 case VehicleHwMotionInputAction.ACTION_UP: 630 return MotionEvent.ACTION_UP; 631 case VehicleHwMotionInputAction.ACTION_MOVE: 632 return MotionEvent.ACTION_MOVE; 633 case VehicleHwMotionInputAction.ACTION_CANCEL: 634 return MotionEvent.ACTION_CANCEL; 635 case VehicleHwMotionInputAction.ACTION_POINTER_DOWN: 636 return MotionEvent.ACTION_POINTER_DOWN; 637 case VehicleHwMotionInputAction.ACTION_POINTER_UP: 638 return MotionEvent.ACTION_POINTER_UP; 639 case VehicleHwMotionInputAction.ACTION_HOVER_MOVE: 640 return MotionEvent.ACTION_HOVER_MOVE; 641 case VehicleHwMotionInputAction.ACTION_SCROLL: 642 return MotionEvent.ACTION_SCROLL; 643 case VehicleHwMotionInputAction.ACTION_HOVER_ENTER: 644 return MotionEvent.ACTION_HOVER_ENTER; 645 case VehicleHwMotionInputAction.ACTION_HOVER_EXIT: 646 return MotionEvent.ACTION_HOVER_EXIT; 647 case VehicleHwMotionInputAction.ACTION_BUTTON_PRESS: 648 return MotionEvent.ACTION_BUTTON_PRESS; 649 case VehicleHwMotionInputAction.ACTION_BUTTON_RELEASE: 650 return MotionEvent.ACTION_BUTTON_RELEASE; 651 default: 652 throw new IllegalArgumentException("Unexpected motion action: " + vehicleAction); 653 } 654 } 655 656 private int convertButtonStateFlag(int buttonStateFlag) { 657 switch (buttonStateFlag) { 658 case VehicleHwMotionButtonStateFlag.BUTTON_PRIMARY: 659 return MotionEvent.BUTTON_PRIMARY; 660 case VehicleHwMotionButtonStateFlag.BUTTON_SECONDARY: 661 return MotionEvent.BUTTON_SECONDARY; 662 case VehicleHwMotionButtonStateFlag.BUTTON_TERTIARY: 663 return MotionEvent.BUTTON_TERTIARY; 664 case VehicleHwMotionButtonStateFlag.BUTTON_FORWARD: 665 return MotionEvent.BUTTON_FORWARD; 666 case VehicleHwMotionButtonStateFlag.BUTTON_BACK: 667 return MotionEvent.BUTTON_BACK; 668 case VehicleHwMotionButtonStateFlag.BUTTON_STYLUS_PRIMARY: 669 return MotionEvent.BUTTON_STYLUS_PRIMARY; 670 case VehicleHwMotionButtonStateFlag.BUTTON_STYLUS_SECONDARY: 671 return MotionEvent.BUTTON_STYLUS_SECONDARY; 672 default: 673 return 0; // No flag set. 674 } 675 } 676 677 private int convertToolType(int toolType) { 678 switch (toolType) { 679 case VehicleHwMotionToolType.TOOL_TYPE_FINGER: 680 return MotionEvent.TOOL_TYPE_FINGER; 681 case VehicleHwMotionToolType.TOOL_TYPE_STYLUS: 682 return MotionEvent.TOOL_TYPE_STYLUS; 683 case VehicleHwMotionToolType.TOOL_TYPE_MOUSE: 684 return MotionEvent.TOOL_TYPE_MOUSE; 685 case VehicleHwMotionToolType.TOOL_TYPE_ERASER: 686 return MotionEvent.TOOL_TYPE_ERASER; 687 default: 688 return MotionEvent.TOOL_TYPE_UNKNOWN; 689 } 690 } 691 692 private void dispatchRotaryInput(InputListener listener, HalPropValue value) { 693 int timeValuesIndex = 3; // remaining values are time deltas in nanoseconds 694 if (value.getInt32ValuesSize() < timeValuesIndex) { 695 Slogf.e(TAG, "Wrong int32 array size for RotaryInput from vhal: %d", 696 value.getInt32ValuesSize()); 697 return; 698 } 699 int rotaryInputType = value.getInt32Value(0); 700 int detentCount = value.getInt32Value(1); 701 int vehicleDisplay = value.getInt32Value(2); 702 long timestamp = value.getTimestamp(); // for first detent, uptime nanoseconds 703 Slogf.d(TAG, "hal rotary input type: %d, number of detents: %d, display: %d", 704 rotaryInputType, detentCount, vehicleDisplay); 705 boolean clockwise = detentCount > 0; 706 detentCount = Math.abs(detentCount); 707 if (detentCount == 0) { // at least there should be one event 708 Slogf.e(TAG, "Zero detentCount from vhal, ignore the event"); 709 return; 710 } 711 // If count is Integer.MIN_VALUE, Math.abs(count) < 0. 712 if (detentCount < 0 || detentCount > Integer.MAX_VALUE - detentCount + 1) { 713 Slogf.e(TAG, "Invalid detentCount from vhal: %d, ignore the event", detentCount); 714 } 715 if (vehicleDisplay != VehicleDisplay.MAIN 716 && vehicleDisplay != VehicleDisplay.INSTRUMENT_CLUSTER) { 717 Slogf.e(TAG, "Wrong display type for RotaryInput from vhal: %d", 718 vehicleDisplay); 719 return; 720 } 721 if (value.getInt32ValuesSize() != (timeValuesIndex + detentCount - 1)) { 722 Slogf.e(TAG, "Wrong int32 array size for RotaryInput from vhal: %d", 723 value.getInt32ValuesSize()); 724 return; 725 } 726 int carInputManagerType; 727 switch (rotaryInputType) { 728 case ROTARY_INPUT_TYPE_SYSTEM_NAVIGATION: 729 carInputManagerType = CarInputManager.INPUT_TYPE_ROTARY_NAVIGATION; 730 break; 731 case ROTARY_INPUT_TYPE_AUDIO_VOLUME: 732 carInputManagerType = CarInputManager.INPUT_TYPE_ROTARY_VOLUME; 733 break; 734 default: 735 Slogf.e(TAG, "Unknown rotary input type: %d", rotaryInputType); 736 return; 737 } 738 739 long[] timestamps = new long[detentCount]; 740 // vhal returns elapsed time while rotary event is using uptime to be in line with KeyEvent. 741 long uptimeToElapsedTimeDelta = CarServiceUtils.getUptimeToElapsedTimeDeltaInMillis(); 742 long startUptime = TimeUnit.NANOSECONDS.toMillis(timestamp) - uptimeToElapsedTimeDelta; 743 timestamps[0] = startUptime; 744 for (int i = 0; i < timestamps.length - 1; i++) { 745 timestamps[i + 1] = timestamps[i] + TimeUnit.NANOSECONDS.toMillis( 746 value.getInt32Value(timeValuesIndex + i)); 747 } 748 RotaryEvent event = new RotaryEvent(carInputManagerType, clockwise, timestamps); 749 listener.onRotaryEvent(event, convertDisplayType(vehicleDisplay)); 750 } 751 752 /** 753 * Dispatches a KeyEvent using {@link #mUptimeSupplier} for the event time. 754 * 755 * @param listener listener to dispatch the event to 756 * @param action action for the KeyEvent 757 * @param code keycode for the KeyEvent 758 * @param display target display the event is associated with 759 */ 760 private void dispatchKeyEvent(InputListener listener, int action, int code, 761 @DisplayTypeEnum int display) { 762 dispatchKeyEvent(listener, action, code, display, mUptimeSupplier.getAsLong()); 763 } 764 765 /** 766 * Dispatches a KeyEvent. 767 * 768 * @param listener listener to dispatch the event to 769 * @param action action for the KeyEvent 770 * @param code keycode for the KeyEvent 771 * @param display target display the event is associated with 772 * @param eventTime uptime in milliseconds when the event occurred 773 */ 774 private void dispatchKeyEvent(InputListener listener, int action, int code, 775 @DisplayTypeEnum int display, long eventTime) { 776 long downTime; 777 int repeat; 778 779 synchronized (mKeyStates) { 780 KeyState state = mKeyStates.get(code); 781 if (state == null) { 782 state = new KeyState(); 783 mKeyStates.put(code, state); 784 } 785 786 if (action == KeyEvent.ACTION_DOWN) { 787 downTime = eventTime; 788 repeat = state.mRepeatCount++; 789 state.mLastKeyDownTimestamp = eventTime; 790 } else { 791 // Handle key up events without any matching down event by setting the down time to 792 // the event time. This shouldn't happen in practice - keys should be pressed 793 // before they can be released! - but this protects us against HAL weirdness. 794 downTime = 795 (state.mLastKeyDownTimestamp == -1) 796 ? eventTime 797 : state.mLastKeyDownTimestamp; 798 repeat = 0; 799 state.mRepeatCount = 0; 800 } 801 } 802 803 KeyEvent event = new KeyEvent( 804 downTime, 805 eventTime, 806 action, 807 code, 808 repeat, 809 0 /* deviceId */, 810 0 /* scancode */, 811 0 /* flags */, 812 InputDevice.SOURCE_CLASS_BUTTON); 813 814 // event.displayId will be set in CarInputService#onKeyEvent 815 listener.onKeyEvent(event, display); 816 } 817 818 /** 819 * Dispatches a {@link KeyEvent} to the given {@code seat}. 820 * 821 * @param listener listener to dispatch the event to 822 * @param action action for the key event 823 * @param code keycode for the key event 824 * @param display target display the event is associated with 825 * @param eventTime uptime in milliseconds when the event occurred 826 * @param downTime time in milliseconds at which the key down was originally sent 827 * @param repeat A repeat count for down events For key down events, this is the repeat count 828 * with the first down starting at 0 and counting up from there. For key up 829 * events, this is always equal to 0. 830 * @param seat the area id this event is occurring from 831 */ 832 private void dispatchKeyEventV2(InputListener listener, int action, int code, 833 @DisplayTypeEnum int display, long eventTime, long downTime, int repeat, int seat) { 834 KeyEvent event = new KeyEvent( 835 downTime, 836 eventTime, 837 action, 838 code, 839 repeat, 840 0 /* metaState */, 841 0 /* deviceId */, 842 0 /* scancode */, 843 0 /* flags */, 844 InputDevice.SOURCE_CLASS_BUTTON); 845 846 // event.displayId will be set in CarInputService#onKeyEvent 847 listener.onKeyEvent(event, display, seat); 848 saveV2KeyInputEventInHistory(event); 849 } 850 851 private void dispatchCustomInput(InputListener listener, HalPropValue value) { 852 Slogf.d(TAG, "Dispatching CustomInputEvent for listener: %s and value: %s", 853 listener, value); 854 int inputCode; 855 int targetDisplayType; 856 int repeatCounter; 857 try { 858 inputCode = value.getInt32Value(0); 859 targetDisplayType = convertDisplayType(value.getInt32Value(1)); 860 repeatCounter = value.getInt32Value(2); 861 } catch (Exception e) { 862 Slogf.e(TAG, "Invalid hal custom input event received", e); 863 return; 864 } 865 CustomInputEvent event = new CustomInputEvent(inputCode, targetDisplayType, repeatCounter); 866 listener.onCustomInputEvent(event); 867 } 868 869 /** 870 * Converts the vehicle display type ({@link VehicleDisplay#MAIN} and 871 * {@link VehicleDisplay#INSTRUMENT_CLUSTER}) to their corresponding types in 872 * {@link CarOccupantZoneManager} ({@link CarOccupantZoneManager#DISPLAY_TYPE_MAIN} and 873 * {@link CarOccupantZoneManager#DISPLAY_TYPE_INSTRUMENT_CLUSTER}). 874 * 875 * @param vehicleDisplayType the vehicle display type 876 * @return the corresponding display type (defined in {@link CarOccupantZoneManager}) or 877 * {@link CarOccupantZoneManager#DISPLAY_TYPE_UNKNOWN} if the value passed as parameter doesn't 878 * correspond to a driver's display type 879 * @hide 880 */ 881 @DisplayTypeEnum 882 public static int convertDisplayType(int vehicleDisplayType) { 883 switch (vehicleDisplayType) { 884 case VehicleDisplay.MAIN: 885 return CarOccupantZoneManager.DISPLAY_TYPE_MAIN; 886 case VehicleDisplay.INSTRUMENT_CLUSTER: 887 return CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER; 888 case VehicleDisplay.HUD: 889 return CarOccupantZoneManager.DISPLAY_TYPE_HUD; 890 case VehicleDisplay.INPUT: 891 return CarOccupantZoneManager.DISPLAY_TYPE_INPUT; 892 case VehicleDisplay.AUXILIARY: 893 return CarOccupantZoneManager.DISPLAY_TYPE_AUXILIARY; 894 default: 895 return CarOccupantZoneManager.DISPLAY_TYPE_UNKNOWN; 896 } 897 } 898 899 @Override 900 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) 901 public void dump(PrintWriter writer) { 902 synchronized (mLock) { 903 writer.println("*Input HAL*"); 904 writer.println("mKeyInputSupported:" + mKeyInputSupported); 905 writer.println("mRotaryInputSupported:" + mRotaryInputSupported); 906 writer.println("mKeyInputV2Supported:" + mKeyInputV2Supported); 907 writer.println("mMotionInputSupported:" + mMotionInputSupported); 908 909 writer.println("mLastFewDispatchedV2KeyEvents:"); 910 KeyEvent[] keyEvents = new KeyEvent[mLastFewDispatchedV2KeyEvents.size()]; 911 mLastFewDispatchedV2KeyEvents.toArray(keyEvents); 912 for (int i = 0; i < keyEvents.length; i++) { 913 writer.println("Event [" + i + "]: " + keyEvents[i].toString()); 914 } 915 916 writer.println("mLastFewDispatchedMotionEvents:"); 917 MotionEvent[] motionEvents = new MotionEvent[mLastFewDispatchedMotionEvents.size()]; 918 mLastFewDispatchedMotionEvents.toArray(motionEvents); 919 for (int i = 0; i < motionEvents.length; i++) { 920 writer.println("Event [" + i + "]: " + motionEvents[i].toString()); 921 } 922 } 923 } 924 } 925