Home | History | Annotate | Download | only in hal
      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 }