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