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