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.hardware.automotive.vehicle.V2_0.VehicleProperty.HW_KEY_INPUT;
19 
20 import android.hardware.automotive.vehicle.V2_0.VehicleDisplay;
21 import android.hardware.automotive.vehicle.V2_0.VehicleHwKeyInputAction;
22 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
23 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
24 import android.os.SystemClock;
25 import android.util.Log;
26 import android.util.SparseLongArray;
27 import android.view.InputDevice;
28 import android.view.KeyEvent;
29 
30 import com.android.car.CarLog;
31 
32 import java.io.PrintWriter;
33 import java.util.Collection;
34 import java.util.LinkedList;
35 import java.util.List;
36 
37 public class InputHalService extends HalServiceBase {
38 
39     public static final int DISPLAY_MAIN = VehicleDisplay.MAIN;
40     public static final int DISPLAY_INSTRUMENT_CLUSTER = VehicleDisplay.INSTRUMENT_CLUSTER;
41     private final VehicleHal mHal;
42 
43     public interface InputListener {
onKeyEvent(KeyEvent event, int targetDisplay)44         void onKeyEvent(KeyEvent event, int targetDisplay);
45     }
46 
47     private static final boolean DBG = false;
48 
49     private boolean mKeyInputSupported = false;
50     private InputListener mListener;
51     private final SparseLongArray mKeyDownTimes = new SparseLongArray();
52 
InputHalService(VehicleHal hal)53     public InputHalService(VehicleHal hal) {
54         mHal = hal;
55     }
56 
setInputListener(InputListener listener)57     public void setInputListener(InputListener listener) {
58         synchronized (this) {
59             if (!mKeyInputSupported) {
60                 Log.w(CarLog.TAG_INPUT, "input listener set while key input not supported");
61                 return;
62             }
63             mListener = listener;
64         }
65         mHal.subscribeProperty(this, HW_KEY_INPUT);
66     }
67 
isKeyInputSupported()68     public synchronized boolean isKeyInputSupported() {
69         return mKeyInputSupported;
70     }
71 
72     @Override
init()73     public void init() {
74     }
75 
76     @Override
release()77     public void release() {
78         synchronized (this) {
79             mListener = null;
80             mKeyInputSupported = false;
81         }
82     }
83 
84     @Override
takeSupportedProperties( Collection<VehiclePropConfig> allProperties)85     public Collection<VehiclePropConfig> takeSupportedProperties(
86             Collection<VehiclePropConfig> allProperties) {
87         List<VehiclePropConfig> supported = new LinkedList<>();
88         for (VehiclePropConfig p: allProperties) {
89             if (p.prop == HW_KEY_INPUT) {
90                 supported.add(p);
91                 synchronized (this) {
92                     mKeyInputSupported = true;
93                 }
94             }
95         }
96         return supported;
97     }
98 
99     @Override
handleHalEvents(List<VehiclePropValue> values)100     public void handleHalEvents(List<VehiclePropValue> values) {
101         InputListener listener;
102         synchronized (this) {
103             listener = mListener;
104         }
105         if (listener == null) {
106             Log.w(CarLog.TAG_INPUT, "Input event while listener is null");
107             return;
108         }
109         for (VehiclePropValue v : values) {
110             if (v.prop != HW_KEY_INPUT) {
111                 Log.e(CarLog.TAG_INPUT, "Wrong event dispatched, prop:0x" +
112                         Integer.toHexString(v.prop));
113                 continue;
114             }
115             int action = (v.value.int32Values.get(0) == VehicleHwKeyInputAction.ACTION_DOWN) ?
116                             KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP;
117             int code = v.value.int32Values.get(1);
118             int display = v.value.int32Values.get(2);
119             if (DBG) {
120                 Log.i(CarLog.TAG_INPUT, "hal event code:" + code + ", action:" + action +
121                         ", display:" + display);
122             }
123 
124             dispatchKeyEvent(listener, action, code, display);
125         }
126     }
127 
dispatchKeyEvent(InputListener listener, int action, int code, int display)128     private void dispatchKeyEvent(InputListener listener, int action, int code, int display) {
129         long eventTime = SystemClock.uptimeMillis();
130 
131         if (action == KeyEvent.ACTION_DOWN) {
132             mKeyDownTimes.put(code, eventTime);
133         }
134 
135         long downTime = action == KeyEvent.ACTION_UP
136                 ? mKeyDownTimes.get(code, eventTime) : eventTime;
137 
138         KeyEvent event = KeyEvent.obtain(
139                 downTime,
140                 eventTime,
141                 action,
142                 code,
143                 0 /* repeat */,
144                 0 /* meta state */,
145                 0 /* deviceId*/,
146                 0 /* scancode */,
147                 0 /* flags */,
148                 InputDevice.SOURCE_CLASS_BUTTON,
149                 null /* characters */);
150 
151         listener.onKeyEvent(event, display);
152         event.recycle();
153     }
154 
155     @Override
dump(PrintWriter writer)156     public void dump(PrintWriter writer) {
157         writer.println("*Input HAL*");
158         writer.println("mKeyInputSupported:" + mKeyInputSupported);
159     }
160 
161 }
162