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