Home | History | Annotate | Download | only in hal
      1 /*
      2  * Copyright (C) 2017 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 android.annotation.Nullable;
     20 import android.car.diagnostic.CarDiagnosticEvent;
     21 import android.car.diagnostic.CarDiagnosticManager;
     22 import android.car.hardware.CarSensorManager;
     23 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
     24 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
     25 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
     26 import android.hardware.automotive.vehicle.V2_0.DiagnosticFloatSensorIndex;
     27 import android.hardware.automotive.vehicle.V2_0.DiagnosticIntegerSensorIndex;
     28 import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
     29 import android.util.Log;
     30 import android.util.SparseArray;
     31 import com.android.car.CarLog;
     32 import com.android.car.CarServiceUtils;
     33 import com.android.car.vehiclehal.VehiclePropValueBuilder;
     34 import java.io.PrintWriter;
     35 import java.util.BitSet;
     36 import java.util.LinkedList;
     37 import java.util.List;
     38 import java.util.concurrent.CopyOnWriteArraySet;
     39 
     40 /**
     41  * Diagnostic HAL service supporting gathering diagnostic info from VHAL and translating it into
     42  * higher-level semantic information
     43  */
     44 public class DiagnosticHalService extends SensorHalServiceBase {
     45     public static class DiagnosticCapabilities {
     46         private final CopyOnWriteArraySet<Integer> mProperties = new CopyOnWriteArraySet<>();
     47 
     48         void setSupported(int propertyId) {
     49             mProperties.add(propertyId);
     50         }
     51 
     52         boolean isSupported(int propertyId) {
     53             return mProperties.contains(propertyId);
     54         }
     55 
     56         public boolean isLiveFrameSupported() {
     57             return isSupported(VehicleProperty.OBD2_LIVE_FRAME);
     58         }
     59 
     60         public boolean isFreezeFrameSupported() {
     61             return isSupported(VehicleProperty.OBD2_FREEZE_FRAME);
     62         }
     63 
     64         public boolean isFreezeFrameInfoSupported() {
     65             return isSupported(VehicleProperty.OBD2_FREEZE_FRAME_INFO);
     66         }
     67 
     68         public boolean isFreezeFrameClearSupported() {
     69             return isSupported(VehicleProperty.OBD2_FREEZE_FRAME_CLEAR);
     70         }
     71 
     72         void clear() {
     73             mProperties.clear();
     74         }
     75     }
     76 
     77     private final DiagnosticCapabilities mDiagnosticCapabilities = new DiagnosticCapabilities();
     78     private DiagnosticListener mDiagnosticListener;
     79     protected final SparseArray<VehiclePropConfig> mVehiclePropertyToConfig = new SparseArray<>();
     80 
     81     public DiagnosticHalService(VehicleHal hal) {
     82         super(hal);
     83     }
     84 
     85     @Override
     86     protected int getTokenForProperty(VehiclePropConfig propConfig) {
     87         switch (propConfig.prop) {
     88             case VehicleProperty.OBD2_LIVE_FRAME:
     89                 mDiagnosticCapabilities.setSupported(propConfig.prop);
     90                 mVehiclePropertyToConfig.put(propConfig.prop, propConfig);
     91                 Log.i(CarLog.TAG_DIAGNOSTIC, String.format("configArray for OBD2_LIVE_FRAME is %s",
     92                     propConfig.configArray));
     93                 return CarDiagnosticManager.FRAME_TYPE_LIVE;
     94             case VehicleProperty.OBD2_FREEZE_FRAME:
     95                 mDiagnosticCapabilities.setSupported(propConfig.prop);
     96                 mVehiclePropertyToConfig.put(propConfig.prop, propConfig);
     97                 Log.i(CarLog.TAG_DIAGNOSTIC, String.format("configArray for OBD2_FREEZE_FRAME is %s",
     98                     propConfig.configArray));
     99                 return CarDiagnosticManager.FRAME_TYPE_FREEZE;
    100             case VehicleProperty.OBD2_FREEZE_FRAME_INFO:
    101                 mDiagnosticCapabilities.setSupported(propConfig.prop);
    102                 return propConfig.prop;
    103             case VehicleProperty.OBD2_FREEZE_FRAME_CLEAR:
    104                 mDiagnosticCapabilities.setSupported(propConfig.prop);
    105                 return propConfig.prop;
    106             default:
    107                 return SENSOR_TYPE_INVALID;
    108         }
    109     }
    110 
    111     @Override
    112     public synchronized void release() {
    113         super.release();
    114         mDiagnosticCapabilities.clear();
    115     }
    116 
    117     private VehiclePropConfig getPropConfig(int halPropId) {
    118         return mVehiclePropertyToConfig.get(halPropId, null);
    119     }
    120 
    121     private List<Integer> getPropConfigArray(int halPropId) {
    122         VehiclePropConfig propConfig = getPropConfig(halPropId);
    123         return propConfig.configArray;
    124     }
    125 
    126     private int getNumIntegerSensors(int halPropId) {
    127         int count = DiagnosticIntegerSensorIndex.LAST_SYSTEM_INDEX + 1;
    128         List<Integer> configArray = getPropConfigArray(halPropId);
    129         if(configArray.size() < 2) {
    130             Log.e(CarLog.TAG_DIAGNOSTIC, String.format(
    131                     "property 0x%x does not specify the number of vendor-specific properties." +
    132                             "assuming 0.", halPropId));
    133         }
    134         else {
    135             count += configArray.get(0);
    136         }
    137         return count;
    138     }
    139 
    140     private int getNumFloatSensors(int halPropId) {
    141         int count = DiagnosticFloatSensorIndex.LAST_SYSTEM_INDEX + 1;
    142         List<Integer> configArray = getPropConfigArray(halPropId);
    143         if(configArray.size() < 2) {
    144             Log.e(CarLog.TAG_DIAGNOSTIC, String.format(
    145                 "property 0x%x does not specify the number of vendor-specific properties." +
    146                     "assuming 0.", halPropId));
    147         }
    148         else {
    149             count += configArray.get(1);
    150         }
    151         return count;
    152     }
    153 
    154     private CarDiagnosticEvent createCarDiagnosticEvent(VehiclePropValue value) {
    155         if (null == value)
    156             return null;
    157 
    158         final boolean isFreezeFrame = value.prop == VehicleProperty.OBD2_FREEZE_FRAME;
    159 
    160         CarDiagnosticEvent.Builder builder =
    161                 (isFreezeFrame
    162                                 ? CarDiagnosticEvent.Builder.newFreezeFrameBuilder()
    163                                 : CarDiagnosticEvent.Builder.newLiveFrameBuilder())
    164                         .atTimestamp(value.timestamp);
    165 
    166         BitSet bitset = BitSet.valueOf(CarServiceUtils.toByteArray(value.value.bytes));
    167 
    168         int numIntegerProperties = getNumIntegerSensors(value.prop);
    169         int numFloatProperties = getNumFloatSensors(value.prop);
    170 
    171         for (int i = 0; i < numIntegerProperties; ++i) {
    172             if (bitset.get(i)) {
    173                 builder.withIntValue(i, value.value.int32Values.get(i));
    174             }
    175         }
    176 
    177         for (int i = 0; i < numFloatProperties; ++i) {
    178             if (bitset.get(numIntegerProperties + i)) {
    179                 builder.withFloatValue(i, value.value.floatValues.get(i));
    180             }
    181         }
    182 
    183         builder.withDtc(value.value.stringValue);
    184 
    185         return builder.build();
    186     }
    187 
    188     /** Listener for monitoring diagnostic event. */
    189     public interface DiagnosticListener {
    190         /**
    191          * Diagnostic events are available.
    192          *
    193          * @param events
    194          */
    195         void onDiagnosticEvents(List<CarDiagnosticEvent> events);
    196     }
    197 
    198     // Should be used only inside handleHalEvents method.
    199     private final LinkedList<CarDiagnosticEvent> mEventsToDispatch = new LinkedList<>();
    200 
    201     @Override
    202     public void handleHalEvents(List<VehiclePropValue> values) {
    203         for (VehiclePropValue value : values) {
    204             CarDiagnosticEvent event = createCarDiagnosticEvent(value);
    205             if (event != null) {
    206                 mEventsToDispatch.add(event);
    207             }
    208         }
    209 
    210         DiagnosticListener listener = null;
    211         synchronized (this) {
    212             listener = mDiagnosticListener;
    213         }
    214         if (listener != null) {
    215             listener.onDiagnosticEvents(mEventsToDispatch);
    216         }
    217         mEventsToDispatch.clear();
    218     }
    219 
    220     public synchronized void setDiagnosticListener(DiagnosticListener listener) {
    221         mDiagnosticListener = listener;
    222     }
    223 
    224     public DiagnosticListener getDiagnosticListener() {
    225         return mDiagnosticListener;
    226     }
    227 
    228     @Override
    229     public void dump(PrintWriter writer) {
    230         writer.println("*Diagnostic HAL*");
    231     }
    232 
    233     @Override
    234     protected float fixSamplingRateForProperty(VehiclePropConfig prop, int carSensorManagerRate) {
    235         //TODO(egranata): tweak this for diagnostics
    236         switch (prop.changeMode) {
    237             case VehiclePropertyChangeMode.ON_CHANGE:
    238             case VehiclePropertyChangeMode.ON_SET:
    239                 return 0;
    240         }
    241         float rate = 1.0f;
    242         switch (carSensorManagerRate) {
    243             case CarSensorManager.SENSOR_RATE_FASTEST:
    244             case CarSensorManager.SENSOR_RATE_FAST:
    245                 rate = 10f;
    246                 break;
    247             case CarSensorManager.SENSOR_RATE_UI:
    248                 rate = 5f;
    249                 break;
    250             default: // fall back to default.
    251                 break;
    252         }
    253         if (rate > prop.maxSampleRate) {
    254             rate = prop.maxSampleRate;
    255         }
    256         if (rate < prop.minSampleRate) {
    257             rate = prop.minSampleRate;
    258         }
    259         return rate;
    260     }
    261 
    262     public DiagnosticCapabilities getDiagnosticCapabilities() {
    263         return mDiagnosticCapabilities;
    264     }
    265 
    266     @Nullable
    267     public CarDiagnosticEvent getCurrentLiveFrame() {
    268         try {
    269             VehiclePropValue value = mHal.get(VehicleProperty.OBD2_LIVE_FRAME);
    270             return createCarDiagnosticEvent(value);
    271         } catch (PropertyTimeoutException e) {
    272             Log.e(CarLog.TAG_DIAGNOSTIC, "timeout trying to read OBD2_LIVE_FRAME");
    273             return null;
    274         } catch (IllegalArgumentException e) {
    275             Log.e(CarLog.TAG_DIAGNOSTIC, "illegal argument trying to read OBD2_LIVE_FRAME", e);
    276             return null;
    277         }
    278     }
    279 
    280     @Nullable
    281     public long[] getFreezeFrameTimestamps() {
    282         try {
    283             VehiclePropValue value = mHal.get(VehicleProperty.OBD2_FREEZE_FRAME_INFO);
    284             long[] timestamps = new long[value.value.int64Values.size()];
    285             for (int i = 0; i < timestamps.length; ++i) {
    286                 timestamps[i] = value.value.int64Values.get(i);
    287             }
    288             return timestamps;
    289         } catch (PropertyTimeoutException e) {
    290             Log.e(CarLog.TAG_DIAGNOSTIC, "timeout trying to read OBD2_FREEZE_FRAME_INFO");
    291             return null;
    292         } catch (IllegalArgumentException e) {
    293             Log.e(CarLog.TAG_DIAGNOSTIC,
    294                     "illegal argument trying to read OBD2_FREEZE_FRAME_INFO", e);
    295             return null;
    296         }
    297     }
    298 
    299     @Nullable
    300     public CarDiagnosticEvent getFreezeFrame(long timestamp) {
    301         VehiclePropValueBuilder builder = VehiclePropValueBuilder.newBuilder(
    302             VehicleProperty.OBD2_FREEZE_FRAME);
    303         builder.setInt64Value(timestamp);
    304         try {
    305             VehiclePropValue value = mHal.get(builder.build());
    306             return createCarDiagnosticEvent(value);
    307         } catch (PropertyTimeoutException e) {
    308             Log.e(CarLog.TAG_DIAGNOSTIC, "timeout trying to read OBD2_FREEZE_FRAME");
    309             return null;
    310         } catch (IllegalArgumentException e) {
    311             Log.e(CarLog.TAG_DIAGNOSTIC,
    312                     "illegal argument trying to read OBD2_FREEZE_FRAME", e);
    313             return null;
    314         }
    315     }
    316 
    317     public void clearFreezeFrames(long... timestamps) {
    318         VehiclePropValueBuilder builder = VehiclePropValueBuilder.newBuilder(
    319             VehicleProperty.OBD2_FREEZE_FRAME_CLEAR);
    320         builder.setInt64Value(timestamps);
    321         try {
    322             mHal.set(builder.build());
    323         } catch (PropertyTimeoutException e) {
    324             Log.e(CarLog.TAG_DIAGNOSTIC, "timeout trying to write OBD2_FREEZE_FRAME_CLEAR");
    325         } catch (IllegalArgumentException e) {
    326             Log.e(CarLog.TAG_DIAGNOSTIC,
    327                 "illegal argument trying to write OBD2_FREEZE_FRAME_CLEAR", e);
    328         }
    329     }
    330 }
    331