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 17 package com.android.car.hal; 18 19 import static java.lang.Integer.toHexString; 20 21 import android.annotation.Nullable; 22 import android.car.hardware.CarSensorConfig; 23 import android.car.hardware.CarSensorEvent; 24 import android.car.hardware.CarSensorManager; 25 import android.hardware.automotive.vehicle.V2_0.VehicleGear; 26 import android.hardware.automotive.vehicle.V2_0.VehicleIgnitionState; 27 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig; 28 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 29 import android.hardware.automotive.vehicle.V2_0.VehicleProperty; 30 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess; 31 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode; 32 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType; 33 import android.os.Bundle; 34 import android.util.Log; 35 import android.util.SparseIntArray; 36 import com.android.car.CarLog; 37 import com.android.car.CarSensorEventFactory; 38 import java.io.PrintWriter; 39 import java.util.ArrayList; 40 import java.util.LinkedList; 41 import java.util.List; 42 43 /** 44 * Sensor HAL implementation for physical sensors in car. 45 */ 46 public 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 }