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