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