• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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