1/*
2 * Copyright (C) 2015 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
17package com.android.car.hal;
18
19import static java.lang.Integer.toHexString;
20
21import android.annotation.Nullable;
22import android.car.hardware.CarSensorConfig;
23import android.car.hardware.CarSensorEvent;
24import android.car.hardware.CarSensorManager;
25import android.hardware.automotive.vehicle.V2_0.VehicleGear;
26import android.hardware.automotive.vehicle.V2_0.VehicleIgnitionState;
27import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
28import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
29import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
30import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
31import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
32import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
33import android.os.Bundle;
34import android.util.Log;
35import android.util.SparseIntArray;
36import com.android.car.CarLog;
37import com.android.car.CarSensorEventFactory;
38import java.io.PrintWriter;
39import java.util.ArrayList;
40import java.util.LinkedList;
41import java.util.List;
42
43/**
44 * Sensor HAL implementation for physical sensors in car.
45 */
46public class SensorHalService extends SensorHalServiceBase {
47    private static final String TAG = CarLog.concatTag(CarLog.TAG_SENSOR, SensorHalService.class);
48    private static final boolean DBG_EVENTS = false;
49
50    /**
51     * Listener for monitoring sensor event. Only sensor service will implement this.
52     */
53    public interface SensorListener {
54        /**
55         * Sensor events are available.
56         *
57         * @param events
58         */
59        void onSensorEvents(List<CarSensorEvent> events);
60    }
61
62    // Manager property Id to HAL property Id mapping.
63    private final static ManagerToHalPropIdMap mManagerToHalPropIdMap =
64        ManagerToHalPropIdMap.create(
65            CarSensorManager.SENSOR_TYPE_CAR_SPEED, VehicleProperty.PERF_VEHICLE_SPEED,
66            CarSensorManager.SENSOR_TYPE_RPM, VehicleProperty.ENGINE_RPM,
67            CarSensorManager.SENSOR_TYPE_ODOMETER, VehicleProperty.PERF_ODOMETER,
68            CarSensorManager.SENSOR_TYPE_GEAR, VehicleProperty.GEAR_SELECTION,
69            CarSensorManager.SENSOR_TYPE_NIGHT, VehicleProperty.NIGHT_MODE,
70            CarSensorManager.SENSOR_TYPE_PARKING_BRAKE, VehicleProperty.PARKING_BRAKE_ON,
71            CarSensorManager.SENSOR_TYPE_DRIVING_STATUS, VehicleProperty.DRIVING_STATUS,
72            CarSensorManager.SENSOR_TYPE_FUEL_LEVEL, VehicleProperty.FUEL_LEVEL_LOW,
73            CarSensorManager.SENSOR_TYPE_IGNITION_STATE, VehicleProperty.IGNITION_STATE,
74            CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE, VehicleProperty.WHEEL_TICK,
75            CarSensorManager.SENSOR_TYPE_ABS_ACTIVE, VehicleProperty.ABS_ACTIVE,
76            CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE,
77            VehicleProperty.TRACTION_CONTROL_ACTIVE
78        );
79
80    private final static SparseIntArray mMgrGearToHalMap = initSparseIntArray(
81        VehicleGear.GEAR_NEUTRAL, CarSensorEvent.GEAR_NEUTRAL,
82        VehicleGear.GEAR_REVERSE, CarSensorEvent.GEAR_REVERSE,
83        VehicleGear.GEAR_PARK, CarSensorEvent.GEAR_PARK,
84        VehicleGear.GEAR_DRIVE, CarSensorEvent.GEAR_DRIVE,
85        VehicleGear.GEAR_LOW, CarSensorEvent.GEAR_FIRST, // Also GEAR_1 - the value is the same.
86        VehicleGear.GEAR_2, CarSensorEvent.GEAR_SECOND,
87        VehicleGear.GEAR_3, CarSensorEvent.GEAR_THIRD,
88        VehicleGear.GEAR_4, CarSensorEvent.GEAR_FOURTH,
89        VehicleGear.GEAR_5, CarSensorEvent.GEAR_FIFTH,
90        VehicleGear.GEAR_6, CarSensorEvent.GEAR_SIXTH,
91        VehicleGear.GEAR_7, CarSensorEvent.GEAR_SEVENTH,
92        VehicleGear.GEAR_8, CarSensorEvent.GEAR_EIGHTH,
93        VehicleGear.GEAR_9, CarSensorEvent.GEAR_NINTH);
94
95    private final static SparseIntArray mMgrIgnitionStateToHalMap = initSparseIntArray(
96        VehicleIgnitionState.UNDEFINED, CarSensorEvent.IGNITION_STATE_UNDEFINED,
97        VehicleIgnitionState.LOCK, CarSensorEvent.IGNITION_STATE_LOCK,
98        VehicleIgnitionState.OFF, CarSensorEvent.IGNITION_STATE_OFF,
99        VehicleIgnitionState.ACC, CarSensorEvent.IGNITION_STATE_ACC,
100        VehicleIgnitionState.ON, CarSensorEvent.IGNITION_STATE_ON,
101        VehicleIgnitionState.START, CarSensorEvent.IGNITION_STATE_START);
102
103    private SensorListener mSensorListener;
104
105    private int[] mMicrometersPerWheelTick = {0, 0, 0, 0};
106
107    @Override
108    public void init() {
109        VehiclePropConfig config;
110        // Populate internal values if available
111        synchronized (this) {
112            config = mSensorToPropConfig.get(CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE);
113        }
114        if (config == null) {
115            Log.e(TAG, "init:  unable to get property config for SENSOR_TYPE_WHEEL_TICK_DISTANCE");
116        } else {
117            for (int i = 0; i < 4; i++) {
118                mMicrometersPerWheelTick[i] = config.configArray.get(i +
119                    INDEX_WHEEL_DISTANCE_FRONT_LEFT);
120            }
121        }
122        super.init();
123    }
124
125    public SensorHalService(VehicleHal hal) {
126        super(hal);
127    }
128
129    public synchronized void registerSensorListener(SensorListener listener) {
130        mSensorListener = listener;
131    }
132
133    @Override
134    protected int getTokenForProperty(VehiclePropConfig halProperty) {
135        int sensor = mManagerToHalPropIdMap.getManagerPropId(halProperty.prop);
136        if (sensor != SENSOR_TYPE_INVALID
137            && halProperty.changeMode != VehiclePropertyChangeMode.STATIC
138            && ((halProperty.access & VehiclePropertyAccess.READ) != 0)) {
139            return sensor;
140        }
141        return SENSOR_TYPE_INVALID;
142    }
143
144    // Should be used only inside handleHalEvents method.
145    private final LinkedList<CarSensorEvent> mEventsToDispatch = new LinkedList<>();
146
147    @Override
148    public void handleHalEvents(List<VehiclePropValue> values) {
149        for (VehiclePropValue v : values) {
150            CarSensorEvent event = createCarSensorEvent(v);
151            if (event != null) {
152                mEventsToDispatch.add(event);
153            }
154        }
155        SensorListener sensorListener;
156        synchronized (this) {
157            sensorListener = mSensorListener;
158        }
159        if (DBG_EVENTS) Log.d(TAG, "handleHalEvents, listener: " + sensorListener);
160        if (sensorListener != null) {
161            sensorListener.onSensorEvents(mEventsToDispatch);
162        }
163        mEventsToDispatch.clear();
164    }
165
166    @Nullable
167    private Integer mapHalEnumValueToMgr(int propId, int halValue) {
168        int mgrValue = halValue;
169
170        switch (propId) {
171            case VehicleProperty.GEAR_SELECTION:
172                mgrValue = mMgrGearToHalMap.get(halValue, -1);
173                break;
174            case VehicleProperty.IGNITION_STATE:
175                mgrValue = mMgrIgnitionStateToHalMap.get(halValue, -1);
176            default:
177                break; // Do nothing
178        }
179        return mgrValue == -1 ? null : mgrValue;
180    }
181
182    @Nullable
183    private CarSensorEvent createCarSensorEvent(VehiclePropValue v) {
184        int property = v.prop;
185        int sensorType = mManagerToHalPropIdMap.getManagerPropId(property);
186        if (sensorType == SENSOR_TYPE_INVALID) {
187            throw new RuntimeException("no sensor defined for property 0x" + toHexString(property));
188        }
189        // Handle the valid sensor
190        int dataType = property & VehiclePropertyType.MASK;
191        CarSensorEvent event = null;
192        switch (dataType) {
193            case VehiclePropertyType.BOOLEAN:
194                event = CarSensorEventFactory.createBooleanEvent(sensorType, v.timestamp,
195                    v.value.int32Values.get(0) == 1);
196                break;
197            case VehiclePropertyType.COMPLEX:
198                event = CarSensorEventFactory.createComplexEvent(sensorType, v.timestamp, v);
199                break;
200            case VehiclePropertyType.INT32:
201                Integer mgrVal = mapHalEnumValueToMgr(property, v.value.int32Values.get(0));
202                event = mgrVal == null ? null
203                    : CarSensorEventFactory.createIntEvent(sensorType, v.timestamp, mgrVal);
204                break;
205            case VehiclePropertyType.FLOAT:
206                event = CarSensorEventFactory.createFloatEvent(sensorType, v.timestamp,
207                    v.value.floatValues.get(0));
208                break;
209            default:
210                Log.w(TAG, "createCarSensorEvent: unsupported type: 0x" + toHexString(dataType));
211                break;
212        }
213        // Perform property specific actions
214        switch (property) {
215            case VehicleProperty.WHEEL_TICK:
216                // Apply the um/tick scaling factor, then divide by 1000 to generate mm
217                for (int i = 0; i < 4; i++) {
218                    // ResetCounts is at longValues[0]
219                    if (event.longValues[i + CarSensorEvent.INDEX_WHEEL_DISTANCE_FRONT_LEFT] !=
220                        Long.MAX_VALUE) {
221                        event.longValues[i + CarSensorEvent.INDEX_WHEEL_DISTANCE_FRONT_LEFT] *=
222                            mMicrometersPerWheelTick[i];
223                        event.longValues[i + CarSensorEvent.INDEX_WHEEL_DISTANCE_FRONT_LEFT] /=
224                            1000;
225                    }
226                }
227                break;
228        }
229        if (DBG_EVENTS) Log.i(TAG, "Sensor event created: " + event);
230        return event;
231    }
232
233    @Nullable
234    public CarSensorEvent getCurrentSensorValue(int sensorType) {
235        VehiclePropValue propValue = getCurrentSensorVehiclePropValue(sensorType);
236        return (null != propValue) ? createCarSensorEvent(propValue) : null;
237    }
238
239    @Override
240    protected float fixSamplingRateForProperty(VehiclePropConfig prop, int carSensorManagerRate) {
241        switch (prop.changeMode) {
242            case VehiclePropertyChangeMode.ON_CHANGE:
243            case VehiclePropertyChangeMode.ON_SET:
244                return 0;
245        }
246        float rate = 1.0f;
247        switch (carSensorManagerRate) {
248            case CarSensorManager.SENSOR_RATE_FASTEST:
249                rate = prop.maxSampleRate;
250                break;
251            case CarSensorManager.SENSOR_RATE_FAST:
252                rate = 10f;  // every 100ms
253                break;
254            case CarSensorManager.SENSOR_RATE_UI:
255                rate = 5f;   // every 200ms
256                break;
257            default: // fall back to default.
258                break;
259        }
260        if (rate > prop.maxSampleRate) {
261            rate = prop.maxSampleRate;
262        }
263        if (rate < prop.minSampleRate) {
264            rate = prop.minSampleRate;
265        }
266        return rate;
267    }
268
269    @Override
270    public void dump(PrintWriter writer) {
271        writer.println("*Sensor HAL*");
272        writer.println("**Supported properties**");
273        for (int i = 0; i < mSensorToPropConfig.size(); i++) {
274            writer.println(mSensorToPropConfig.valueAt(i).toString());
275        }
276        for (int i = 0; i < mMicrometersPerWheelTick.length; i++) {
277            writer.println("mMicrometersPerWheelTick[" + i + "] = " + mMicrometersPerWheelTick[i]);
278        }
279    }
280
281    private static SparseIntArray initSparseIntArray(int... keyValuePairs) {
282        int inputLength = keyValuePairs.length;
283        if (inputLength % 2 != 0) {
284            throw new IllegalArgumentException("Odd number of key-value elements");
285        }
286
287        SparseIntArray map = new SparseIntArray(inputLength / 2);
288        for (int i = 0; i < keyValuePairs.length; i += 2) {
289            map.put(keyValuePairs[i], keyValuePairs[i + 1]);
290        }
291        return map;
292    }
293
294    private static final int INDEX_WHEEL_DISTANCE_ENABLE_FLAG = 0;
295    private static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1;
296    private static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2;
297    private static final int INDEX_WHEEL_DISTANCE_REAR_RIGHT = 3;
298    private static final int INDEX_WHEEL_DISTANCE_REAR_LEFT = 4;
299    private static final int WHEEL_TICK_DISTANCE_BUNDLE_SIZE = 6;
300
301    private Bundle createWheelDistanceTickBundle(ArrayList<Integer> configArray) {
302        Bundle b = new Bundle(WHEEL_TICK_DISTANCE_BUNDLE_SIZE);
303        b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS,
304            configArray.get(INDEX_WHEEL_DISTANCE_ENABLE_FLAG));
305        b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK,
306            configArray.get(INDEX_WHEEL_DISTANCE_FRONT_LEFT));
307        b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK,
308            configArray.get(INDEX_WHEEL_DISTANCE_FRONT_RIGHT));
309        b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK,
310            configArray.get(INDEX_WHEEL_DISTANCE_REAR_RIGHT));
311        b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK,
312            configArray.get(INDEX_WHEEL_DISTANCE_REAR_LEFT));
313        return b;
314    }
315
316
317    public CarSensorConfig getSensorConfig(int sensorType) {
318        VehiclePropConfig cfg;
319        synchronized (this) {
320            cfg = mSensorToPropConfig.get(sensorType);
321        }
322        if (cfg == null) {
323            /* Invalid sensor type. */
324            throw new IllegalArgumentException("Unknown sensorType = " + sensorType);
325        } else {
326            Bundle b;
327            switch(sensorType) {
328                case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE:
329                    b = createWheelDistanceTickBundle(cfg.configArray);
330                    break;
331                default:
332                    /* Unhandled config.  Create empty bundle */
333                    b = Bundle.EMPTY;
334                    break;
335            }
336            return new CarSensorConfig(sensorType, b);
337        }
338    }
339}