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