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