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.test;
18
19import static java.lang.Integer.toHexString;
20
21import android.car.Car;
22import android.car.hardware.CarDiagnosticEvent;
23import android.car.hardware.CarDiagnosticEvent.FuelSystemStatus;
24import android.car.hardware.CarDiagnosticEvent.FuelType;
25import android.car.hardware.CarDiagnosticEvent.IgnitionMonitors.CommonIgnitionMonitors;
26import android.car.hardware.CarDiagnosticEvent.IgnitionMonitors.CompressionIgnitionMonitors;
27import android.car.hardware.CarDiagnosticEvent.IgnitionMonitors.SparkIgnitionMonitors;
28import android.car.hardware.CarDiagnosticEvent.SecondaryAirStatus;
29import android.car.hardware.CarDiagnosticManager;
30import android.car.hardware.CarDiagnosticSensorIndices.Obd2FloatSensorIndex;
31import android.car.hardware.CarDiagnosticSensorIndices.Obd2IntegerSensorIndex;
32import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
33import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
34import android.os.SystemClock;
35import android.test.suitebuilder.annotation.MediumTest;
36import android.util.Log;
37import com.android.car.internal.FeatureConfiguration;
38import com.android.car.vehiclehal.DiagnosticEventBuilder;
39import com.android.car.vehiclehal.VehiclePropValueBuilder;
40import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
41import java.util.Arrays;
42import java.util.HashMap;
43import java.util.HashSet;
44import java.util.Set;
45
46/** Test the public entry points for the CarDiagnosticManager */
47@MediumTest
48public class CarDiagnosticManagerTest extends MockedCarTestBase {
49    private static final String TAG = CarDiagnosticManagerTest.class.getSimpleName();
50
51    private final DiagnosticEventBuilder mLiveFrameEventBuilder =
52            new DiagnosticEventBuilder(VehicleProperty.OBD2_LIVE_FRAME);
53    private final DiagnosticEventBuilder mFreezeFrameEventBuilder =
54            new DiagnosticEventBuilder(VehicleProperty.OBD2_FREEZE_FRAME);
55    private final FreezeFrameProperties mFreezeFrameProperties = new FreezeFrameProperties();
56
57    private CarDiagnosticManager mCarDiagnosticManager;
58
59    private static final String DTC = "P1010";
60
61    /**
62     * This class is a central repository for freeze frame data. It ensures that timestamps and
63     * events are kept in sync and provides a consistent access model for diagnostic properties.
64     */
65    class FreezeFrameProperties {
66        private final HashMap<Long, VehiclePropValue> mEvents = new HashMap<>();
67
68        public final VehicleHalPropertyHandler mFreezeFrameInfoHandler =
69                new FreezeFrameInfoHandler();
70        public final VehicleHalPropertyHandler mFreezeFrameHandler = new FreezeFrameHandler();
71        public final VehicleHalPropertyHandler mFreezeFrameClearHandler =
72                new FreezeFrameClearHandler();
73
74        synchronized VehiclePropValue addNewEvent(DiagnosticEventBuilder builder) {
75            long timestamp = SystemClock.elapsedRealtimeNanos();
76            return addNewEvent(builder, timestamp);
77        }
78
79        synchronized VehiclePropValue addNewEvent(DiagnosticEventBuilder builder, long timestamp) {
80            VehiclePropValue newEvent = builder.build(timestamp);
81            mEvents.put(timestamp, newEvent);
82            return newEvent;
83        }
84
85        synchronized VehiclePropValue removeEvent(long timestamp) {
86            return mEvents.remove(timestamp);
87        }
88
89        synchronized void removeEvents() {
90            mEvents.clear();
91        }
92
93        synchronized long[] getTimestamps() {
94            return mEvents.keySet().stream().mapToLong(Long::longValue).toArray();
95        }
96
97        synchronized VehiclePropValue getEvent(long timestamp) {
98            return mEvents.get(timestamp);
99        }
100
101        class FreezeFramePropertyHandler implements VehicleHalPropertyHandler {
102            private boolean mSubscribed = false;
103
104            protected final int VEHICLE_PROPERTY;
105
106            protected FreezeFramePropertyHandler(int propertyId) {
107                VEHICLE_PROPERTY = propertyId;
108            }
109
110            @Override
111            public synchronized void onPropertySet(VehiclePropValue value) {
112                assertEquals(VEHICLE_PROPERTY, value.prop);
113            }
114
115            @Override
116            public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
117                assertEquals(VEHICLE_PROPERTY, value.prop);
118                return null;
119            }
120
121            @Override
122            public synchronized void onPropertySubscribe(
123                    int property, int zones, float sampleRate) {
124                assertEquals(VEHICLE_PROPERTY, property);
125                mSubscribed = true;
126            }
127
128            @Override
129            public synchronized void onPropertyUnsubscribe(int property) {
130                assertEquals(VEHICLE_PROPERTY, property);
131                if (!mSubscribed) {
132                    throw new IllegalArgumentException(
133                            "Property was not subscribed 0x" + toHexString(property));
134                }
135                mSubscribed = false;
136            }
137        }
138
139        class FreezeFrameInfoHandler extends FreezeFramePropertyHandler {
140            FreezeFrameInfoHandler() {
141                super(VehicleProperty.OBD2_FREEZE_FRAME_INFO);
142            }
143
144            @Override
145            public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
146                super.onPropertyGet(value);
147                VehiclePropValueBuilder builder =
148                        VehiclePropValueBuilder.newBuilder(VEHICLE_PROPERTY);
149                builder.setInt64Value(getTimestamps());
150                return builder.build();
151            }
152        }
153
154        class FreezeFrameHandler extends FreezeFramePropertyHandler {
155            FreezeFrameHandler() {
156                super(VehicleProperty.OBD2_FREEZE_FRAME);
157            }
158
159            @Override
160            public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
161                super.onPropertyGet(value);
162                long timestamp = value.value.int64Values.get(0);
163                return getEvent(timestamp);
164            }
165        }
166
167        class FreezeFrameClearHandler extends FreezeFramePropertyHandler {
168            FreezeFrameClearHandler() {
169                super(VehicleProperty.OBD2_FREEZE_FRAME_CLEAR);
170            }
171
172            @Override
173            public synchronized void onPropertySet(VehiclePropValue value) {
174                super.onPropertySet(value);
175                if (0 == value.value.int64Values.size()) {
176                    removeEvents();
177                } else {
178                    for (long timestamp : value.value.int64Values) {
179                        removeEvent(timestamp);
180                    }
181                }
182            }
183        }
184    }
185
186    @Override
187    protected synchronized void configureMockedHal() {
188        java.util.Collection<Integer> numVendorSensors = Arrays.asList(0, 0);
189        addProperty(VehicleProperty.OBD2_LIVE_FRAME, mLiveFrameEventBuilder.build())
190                .setConfigArray(numVendorSensors);
191        addProperty(
192                VehicleProperty.OBD2_FREEZE_FRAME_INFO,
193                mFreezeFrameProperties.mFreezeFrameInfoHandler);
194        addProperty(VehicleProperty.OBD2_FREEZE_FRAME, mFreezeFrameProperties.mFreezeFrameHandler)
195                .setConfigArray(numVendorSensors);
196        addProperty(
197                VehicleProperty.OBD2_FREEZE_FRAME_CLEAR,
198                mFreezeFrameProperties.mFreezeFrameClearHandler);
199    }
200
201    private boolean isFeatureEnabled() {
202        return FeatureConfiguration.ENABLE_DIAGNOSTIC;
203    }
204
205    @Override
206    protected void setUp() throws Exception {
207        mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE, 30);
208        mLiveFrameEventBuilder.addIntSensor(
209                Obd2IntegerSensorIndex.FUEL_SYSTEM_STATUS,
210                FuelSystemStatus.OPEN_ENGINE_LOAD_OR_DECELERATION);
211        mLiveFrameEventBuilder.addIntSensor(
212                Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START, 5000);
213        mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.CONTROL_MODULE_VOLTAGE, 2);
214        mLiveFrameEventBuilder.addFloatSensor(Obd2FloatSensorIndex.CALCULATED_ENGINE_LOAD, 0.125f);
215        mLiveFrameEventBuilder.addFloatSensor(Obd2FloatSensorIndex.VEHICLE_SPEED, 12.5f);
216
217        mFreezeFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE, 30);
218        mFreezeFrameEventBuilder.addIntSensor(
219                Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START, 5000);
220        mFreezeFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.CONTROL_MODULE_VOLTAGE, 2);
221        mFreezeFrameEventBuilder.addFloatSensor(
222                Obd2FloatSensorIndex.CALCULATED_ENGINE_LOAD, 0.125f);
223        mFreezeFrameEventBuilder.addFloatSensor(Obd2FloatSensorIndex.VEHICLE_SPEED, 12.5f);
224        mFreezeFrameEventBuilder.setDTC(DTC);
225
226        super.setUp();
227
228        if (isFeatureEnabled()) {
229            Log.i(TAG, "attempting to get DIAGNOSTIC_SERVICE");
230            mCarDiagnosticManager =
231                    (CarDiagnosticManager) getCar().getCarManager(Car.DIAGNOSTIC_SERVICE);
232        } else {
233            Log.i(TAG, "skipping diagnostic tests as ENABLE_DIAGNOSTIC flag is false");
234        }
235    }
236
237    public void testLiveFrameRead() throws Exception {
238        if (!isFeatureEnabled()) {
239            Log.i(TAG, "skipping testLiveFrameRead as diagnostics API is not enabled");
240            return;
241        }
242
243        CarDiagnosticEvent liveFrame = mCarDiagnosticManager.getLatestLiveFrame();
244
245        assertNotNull(liveFrame);
246        assertTrue(liveFrame.isLiveFrame());
247        assertFalse(liveFrame.isFreezeFrame());
248        assertFalse(liveFrame.isEmptyFrame());
249
250        assertEquals(
251                5000,
252                liveFrame
253                        .getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
254                        .intValue());
255        assertEquals(
256                30,
257                liveFrame
258                        .getSystemIntegerSensor(Obd2IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE)
259                        .intValue());
260        assertEquals(
261                2,
262                liveFrame
263                        .getSystemIntegerSensor(Obd2IntegerSensorIndex.CONTROL_MODULE_VOLTAGE)
264                        .intValue());
265        assertEquals(
266                0.125f,
267                liveFrame
268                        .getSystemFloatSensor(Obd2FloatSensorIndex.CALCULATED_ENGINE_LOAD)
269                        .floatValue());
270        assertEquals(
271                12.5f,
272                liveFrame.getSystemFloatSensor(Obd2FloatSensorIndex.VEHICLE_SPEED).floatValue());
273    }
274
275    public void testLiveFrameEvent() throws Exception {
276        if (!isFeatureEnabled()) {
277            Log.i(TAG, "skipping testLiveFrameEvent as diagnostics API is not enabled");
278            return;
279        }
280
281        Listener listener = new Listener();
282        mCarDiagnosticManager.registerListener(
283                listener,
284                CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
285                android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
286
287        listener.reset();
288        long time = SystemClock.elapsedRealtimeNanos();
289        mLiveFrameEventBuilder.addIntSensor(
290                Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START, 5100);
291
292        getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(time));
293        assertTrue(listener.waitForEvent(time));
294
295        CarDiagnosticEvent liveFrame = listener.getLastEvent();
296
297        assertEquals(
298                5100,
299                liveFrame
300                        .getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
301                        .intValue());
302    }
303
304    public void testMissingSensorRead() throws Exception {
305        if (!isFeatureEnabled()) {
306            Log.i(TAG, "skipping testMissingSensorRead as diagnostics API is not enabled");
307            return;
308        }
309
310        Listener listener = new Listener();
311        mCarDiagnosticManager.registerListener(
312                listener,
313                CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
314                android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
315
316        getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build());
317        assertTrue(listener.waitForEvent());
318
319        CarDiagnosticEvent liveFrame = listener.getLastEvent();
320        assertNotNull(liveFrame);
321
322        assertNull(
323                liveFrame.getSystemIntegerSensor(
324                        Obd2IntegerSensorIndex.DRIVER_DEMAND_PERCENT_TORQUE));
325        assertEquals(
326                -1,
327                liveFrame.getSystemIntegerSensor(
328                        Obd2IntegerSensorIndex.DRIVER_DEMAND_PERCENT_TORQUE, -1));
329
330        assertNull(liveFrame.getSystemFloatSensor(Obd2FloatSensorIndex.OXYGEN_SENSOR6_VOLTAGE));
331        assertEquals(
332                0.25f,
333                liveFrame.getSystemFloatSensor(Obd2FloatSensorIndex.OXYGEN_SENSOR5_VOLTAGE, 0.25f));
334
335        assertNull(liveFrame.getVendorIntegerSensor(Obd2IntegerSensorIndex.VENDOR_START));
336        assertEquals(-1, liveFrame.getVendorIntegerSensor(Obd2IntegerSensorIndex.VENDOR_START, -1));
337
338        assertNull(liveFrame.getVendorFloatSensor(Obd2FloatSensorIndex.VENDOR_START));
339        assertEquals(
340                0.25f, liveFrame.getVendorFloatSensor(Obd2FloatSensorIndex.VENDOR_START, 0.25f));
341    }
342
343    public void testFuelSystemStatus() throws Exception {
344        if (!isFeatureEnabled()) {
345            Log.i(TAG, "skipping testFuelSystemStatus as diagnostics API is not enabled");
346            return;
347        }
348
349        Listener listener = new Listener();
350        mCarDiagnosticManager.registerListener(
351                listener,
352                CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
353                android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
354
355        getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build());
356        assertTrue(listener.waitForEvent());
357
358        CarDiagnosticEvent liveFrame = listener.getLastEvent();
359        assertNotNull(liveFrame);
360
361        assertEquals(
362                FuelSystemStatus.OPEN_ENGINE_LOAD_OR_DECELERATION,
363                liveFrame
364                        .getSystemIntegerSensor(Obd2IntegerSensorIndex.FUEL_SYSTEM_STATUS)
365                        .intValue());
366        assertEquals(
367                FuelSystemStatus.OPEN_ENGINE_LOAD_OR_DECELERATION,
368                liveFrame.getFuelSystemStatus().intValue());
369    }
370
371    public void testSecondaryAirStatus() throws Exception {
372        if (!isFeatureEnabled()) {
373            Log.i(TAG, "skipping testSecondaryAirStatus as diagnostics API is not enabled");
374            return;
375        }
376
377        Listener listener = new Listener();
378        mCarDiagnosticManager.registerListener(
379                listener,
380                CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
381                android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
382
383        mLiveFrameEventBuilder.addIntSensor(
384                Obd2IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS,
385                SecondaryAirStatus.FROM_OUTSIDE_OR_OFF);
386        long timestamp = SystemClock.elapsedRealtimeNanos();
387        getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(timestamp));
388
389        assertTrue(listener.waitForEvent(timestamp));
390
391        CarDiagnosticEvent liveFrame = listener.getLastEvent();
392        assertNotNull(liveFrame);
393
394        assertEquals(
395                SecondaryAirStatus.FROM_OUTSIDE_OR_OFF,
396                liveFrame
397                        .getSystemIntegerSensor(
398                                Obd2IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS)
399                        .intValue());
400        assertEquals(
401                SecondaryAirStatus.FROM_OUTSIDE_OR_OFF,
402                liveFrame.getSecondaryAirStatus().intValue());
403    }
404
405    public void testIgnitionMonitors() throws Exception {
406        if (!isFeatureEnabled()) {
407            Log.i(TAG, "skipping testIgnitionMonitors as diagnostics API is not enabled");
408            return;
409        }
410
411        Listener listener = new Listener();
412        mCarDiagnosticManager.registerListener(
413                listener,
414                CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
415                android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
416
417        // cfr. CarDiagnosticEvent for the meaning of the several bits
418        final int sparkMonitorsValue =
419                0x1 | (0x1 << 2) | (0x1 << 3) | (0x1 << 6) | (0x1 << 10) | (0x1 << 11);
420
421        final int compressionMonitorsValue =
422                (0x1 << 2) | (0x1 << 3) | (0x1 << 6) | (0x1 << 12) | (0x1 << 13);
423
424        mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED, 0);
425        mLiveFrameEventBuilder.addIntSensor(
426                Obd2IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS, sparkMonitorsValue);
427
428        long timestamp = SystemClock.elapsedRealtimeNanos();
429        getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(timestamp));
430
431        assertTrue(listener.waitForEvent(timestamp));
432
433        CarDiagnosticEvent liveFrame = listener.getLastEvent();
434        assertNotNull(liveFrame);
435
436        CommonIgnitionMonitors commonIgnitionMonitors = liveFrame.getIgnitionMonitors();
437        assertNotNull(commonIgnitionMonitors);
438        assertTrue(commonIgnitionMonitors.components.available);
439        assertFalse(commonIgnitionMonitors.components.incomplete);
440        assertTrue(commonIgnitionMonitors.fuelSystem.available);
441        assertTrue(commonIgnitionMonitors.fuelSystem.incomplete);
442        assertFalse(commonIgnitionMonitors.misfire.available);
443        assertFalse(commonIgnitionMonitors.misfire.incomplete);
444
445        SparkIgnitionMonitors sparkIgnitionMonitors =
446                commonIgnitionMonitors.asSparkIgnitionMonitors();
447        assertNotNull(sparkIgnitionMonitors);
448        assertNull(commonIgnitionMonitors.asCompressionIgnitionMonitors());
449
450        assertTrue(sparkIgnitionMonitors.EGR.available);
451        assertFalse(sparkIgnitionMonitors.EGR.incomplete);
452        assertFalse(sparkIgnitionMonitors.oxygenSensorHeater.available);
453        assertFalse(sparkIgnitionMonitors.oxygenSensorHeater.incomplete);
454        assertTrue(sparkIgnitionMonitors.oxygenSensor.available);
455        assertTrue(sparkIgnitionMonitors.oxygenSensor.incomplete);
456        assertFalse(sparkIgnitionMonitors.ACRefrigerant.available);
457        assertFalse(sparkIgnitionMonitors.ACRefrigerant.incomplete);
458        assertFalse(sparkIgnitionMonitors.secondaryAirSystem.available);
459        assertFalse(sparkIgnitionMonitors.secondaryAirSystem.incomplete);
460        assertFalse(sparkIgnitionMonitors.evaporativeSystem.available);
461        assertFalse(sparkIgnitionMonitors.evaporativeSystem.incomplete);
462        assertFalse(sparkIgnitionMonitors.heatedCatalyst.available);
463        assertFalse(sparkIgnitionMonitors.heatedCatalyst.incomplete);
464        assertFalse(sparkIgnitionMonitors.catalyst.available);
465        assertFalse(sparkIgnitionMonitors.catalyst.incomplete);
466
467        mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED, 1);
468        mLiveFrameEventBuilder.addIntSensor(
469                Obd2IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS, compressionMonitorsValue);
470
471        timestamp += 1000;
472        getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(timestamp));
473
474        assertTrue(listener.waitForEvent(timestamp));
475
476        liveFrame = listener.getLastEvent();
477        assertNotNull(liveFrame);
478        assertEquals(timestamp, liveFrame.timestamp);
479
480        commonIgnitionMonitors = liveFrame.getIgnitionMonitors();
481        assertNotNull(commonIgnitionMonitors);
482        assertFalse(commonIgnitionMonitors.components.available);
483        assertFalse(commonIgnitionMonitors.components.incomplete);
484        assertTrue(commonIgnitionMonitors.fuelSystem.available);
485        assertTrue(commonIgnitionMonitors.fuelSystem.incomplete);
486        assertFalse(commonIgnitionMonitors.misfire.available);
487        assertFalse(commonIgnitionMonitors.misfire.incomplete);
488        CompressionIgnitionMonitors compressionIgnitionMonitors =
489                commonIgnitionMonitors.asCompressionIgnitionMonitors();
490        assertNull(commonIgnitionMonitors.asSparkIgnitionMonitors());
491        assertNotNull(compressionIgnitionMonitors);
492
493        assertTrue(compressionIgnitionMonitors.EGROrVVT.available);
494        assertFalse(compressionIgnitionMonitors.EGROrVVT.incomplete);
495        assertFalse(compressionIgnitionMonitors.PMFilter.available);
496        assertFalse(compressionIgnitionMonitors.PMFilter.incomplete);
497        assertFalse(compressionIgnitionMonitors.exhaustGasSensor.available);
498        assertFalse(compressionIgnitionMonitors.exhaustGasSensor.incomplete);
499        assertTrue(compressionIgnitionMonitors.boostPressure.available);
500        assertTrue(compressionIgnitionMonitors.boostPressure.incomplete);
501        assertFalse(compressionIgnitionMonitors.NOxSCR.available);
502        assertFalse(compressionIgnitionMonitors.NOxSCR.incomplete);
503        assertFalse(compressionIgnitionMonitors.NMHCCatalyst.available);
504        assertFalse(compressionIgnitionMonitors.NMHCCatalyst.incomplete);
505    }
506
507    public void testFuelType() throws Exception {
508        if (!isFeatureEnabled()) {
509            Log.i(TAG, "skipping testFuelType as diagnostics API is not enabled");
510            return;
511        }
512
513        Listener listener = new Listener();
514        mCarDiagnosticManager.registerListener(
515                listener,
516                CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
517                android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
518
519        mLiveFrameEventBuilder.addIntSensor(
520                Obd2IntegerSensorIndex.FUEL_TYPE, FuelType.BIFUEL_RUNNING_LPG);
521        long timestamp = SystemClock.elapsedRealtimeNanos();
522        getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(timestamp));
523
524        assertTrue(listener.waitForEvent(timestamp));
525
526        CarDiagnosticEvent liveFrame = listener.getLastEvent();
527        assertNotNull(liveFrame);
528
529        assertEquals(
530                FuelType.BIFUEL_RUNNING_LPG,
531                liveFrame.getSystemIntegerSensor(Obd2IntegerSensorIndex.FUEL_TYPE).intValue());
532        assertEquals(FuelType.BIFUEL_RUNNING_LPG, liveFrame.getFuelType().intValue());
533    }
534
535    public void testMultipleListeners() throws Exception {
536        if (!isFeatureEnabled()) {
537            Log.i(TAG, "skipping testMultipleListeners as diagnostics API is not enabled");
538            return;
539        }
540
541        Listener listener1 = new Listener();
542        Listener listener2 = new Listener();
543
544        mCarDiagnosticManager.registerListener(
545                listener1,
546                CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
547                android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
548        mCarDiagnosticManager.registerListener(
549                listener2,
550                CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE,
551                android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
552
553        listener1.reset();
554        listener2.reset();
555
556        long time = SystemClock.elapsedRealtimeNanos();
557        getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(time));
558        assertTrue(listener1.waitForEvent(time));
559        assertTrue(listener2.waitForEvent(time));
560
561        CarDiagnosticEvent event1 = listener1.getLastEvent();
562        CarDiagnosticEvent event2 = listener2.getLastEvent();
563        assertEquals(
564                5000,
565                event1.getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
566                        .intValue());
567        assertEquals(
568                5000,
569                event2.getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
570                        .intValue());
571
572        listener1.reset();
573        listener2.reset();
574
575        mCarDiagnosticManager.unregisterListener(listener1);
576
577        time += 1000;
578        getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(time));
579        assertFalse(listener1.waitForEvent(time));
580        assertTrue(listener2.waitForEvent(time));
581
582        assertNull(listener1.getLastEvent());
583        event2 = listener2.getLastEvent();
584
585        assertTrue(event1.isEarlierThan(event2));
586
587        assertEquals(
588                5000,
589                event2.getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
590                        .intValue());
591    }
592
593    public void testFreezeFrameEvent() throws Exception {
594        if (!isFeatureEnabled()) {
595            Log.i(TAG, "skipping testFreezeFrameEvent as diagnostics API is not enabled");
596            return;
597        }
598
599        Listener listener = new Listener();
600        mCarDiagnosticManager.registerListener(
601                listener,
602                CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE,
603                android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
604
605        listener.reset();
606        VehiclePropValue injectedEvent =
607                mFreezeFrameProperties.addNewEvent(mFreezeFrameEventBuilder);
608        getMockedVehicleHal().injectEvent(injectedEvent);
609        assertTrue(listener.waitForEvent(injectedEvent.timestamp));
610
611        CarDiagnosticEvent freezeFrame = listener.getLastEvent();
612
613        assertEquals(DTC, freezeFrame.dtc);
614
615        mFreezeFrameEventBuilder.addIntSensor(
616                Obd2IntegerSensorIndex.ABSOLUTE_BAROMETRIC_PRESSURE, 22);
617        injectedEvent = mFreezeFrameProperties.addNewEvent(mFreezeFrameEventBuilder);
618        getMockedVehicleHal().injectEvent(injectedEvent);
619        assertTrue(listener.waitForEvent(injectedEvent.timestamp));
620
621        freezeFrame = listener.getLastEvent();
622
623        assertNotNull(freezeFrame);
624        assertFalse(freezeFrame.isLiveFrame());
625        assertTrue(freezeFrame.isFreezeFrame());
626        assertFalse(freezeFrame.isEmptyFrame());
627
628        assertEquals(DTC, freezeFrame.dtc);
629        assertEquals(
630                22,
631                freezeFrame
632                        .getSystemIntegerSensor(Obd2IntegerSensorIndex.ABSOLUTE_BAROMETRIC_PRESSURE)
633                        .intValue());
634    }
635
636    public void testFreezeFrameTimestamps() throws Exception {
637        if (!isFeatureEnabled()) {
638            Log.i(TAG, "skipping testFreezeFrameTimestamps as diagnostics API is not enabled");
639            return;
640        }
641
642        Listener listener = new Listener();
643        mCarDiagnosticManager.registerListener(
644                listener,
645                CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE,
646                android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
647
648        Set<Long> generatedTimestamps = new HashSet<>();
649
650        VehiclePropValue injectedEvent =
651                mFreezeFrameProperties.addNewEvent(mFreezeFrameEventBuilder);
652        getMockedVehicleHal().injectEvent(injectedEvent);
653        generatedTimestamps.add(injectedEvent.timestamp);
654        assertTrue(listener.waitForEvent(injectedEvent.timestamp));
655
656        injectedEvent =
657                mFreezeFrameProperties.addNewEvent(
658                        mFreezeFrameEventBuilder, injectedEvent.timestamp + 1000);
659        getMockedVehicleHal().injectEvent(injectedEvent);
660        generatedTimestamps.add(injectedEvent.timestamp);
661        assertTrue(listener.waitForEvent(injectedEvent.timestamp));
662
663        long[] acquiredTimestamps = mCarDiagnosticManager.getFreezeFrameTimestamps();
664        assertEquals(generatedTimestamps.size(), acquiredTimestamps.length);
665        for (long acquiredTimestamp : acquiredTimestamps) {
666            assertTrue(generatedTimestamps.contains(acquiredTimestamp));
667        }
668    }
669
670    public void testClearFreezeFrameTimestamps() throws Exception {
671        if (!isFeatureEnabled()) {
672            Log.i(TAG, "skipping testClearFreezeFrameTimestamps as diagnostics API is not enabled");
673            return;
674        }
675
676        Listener listener = new Listener();
677        mCarDiagnosticManager.registerListener(
678                listener,
679                CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE,
680                android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
681
682        VehiclePropValue injectedEvent =
683                mFreezeFrameProperties.addNewEvent(mFreezeFrameEventBuilder);
684        getMockedVehicleHal().injectEvent(injectedEvent);
685        assertTrue(listener.waitForEvent(injectedEvent.timestamp));
686
687        assertNotNull(mCarDiagnosticManager.getFreezeFrame(injectedEvent.timestamp));
688        mCarDiagnosticManager.clearFreezeFrames(injectedEvent.timestamp);
689        assertNull(mCarDiagnosticManager.getFreezeFrame(injectedEvent.timestamp));
690    }
691
692    class Listener implements CarDiagnosticManager.OnDiagnosticEventListener {
693        private final Object mSync = new Object();
694
695        private CarDiagnosticEvent mLastEvent = null;
696
697        CarDiagnosticEvent getLastEvent() {
698            return mLastEvent;
699        }
700
701        void reset() {
702            synchronized (mSync) {
703                mLastEvent = null;
704            }
705        }
706
707        boolean waitForEvent() throws InterruptedException {
708            return waitForEvent(0);
709        }
710
711        boolean waitForEvent(long eventTimeStamp) throws InterruptedException {
712            long start = SystemClock.elapsedRealtime();
713            boolean matchTimeStamp = eventTimeStamp != 0;
714            synchronized (mSync) {
715                while ((mLastEvent == null
716                                || (matchTimeStamp && mLastEvent.timestamp != eventTimeStamp))
717                        && (start + SHORT_WAIT_TIMEOUT_MS > SystemClock.elapsedRealtime())) {
718                    mSync.wait(10L);
719                }
720                return mLastEvent != null
721                        && (!matchTimeStamp || mLastEvent.timestamp == eventTimeStamp);
722            }
723        }
724
725        @Override
726        public void onDiagnosticEvent(CarDiagnosticEvent event) {
727            synchronized (mSync) {
728                // We're going to hold a reference to this object
729                mLastEvent = event;
730                mSync.notify();
731            }
732        }
733    }
734}
735