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