• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.car.diagnostic;
18 
19 import android.annotation.IntDef;
20 import android.annotation.Nullable;
21 import android.annotation.SystemApi;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.util.JsonWriter;
25 import android.util.SparseArray;
26 import android.util.SparseIntArray;
27 import java.io.IOException;
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 import java.util.Objects;
31 
32 /**
33  * A CarDiagnosticEvent object corresponds to a single diagnostic event frame coming from the car.
34  *
35  * @hide
36  */
37 @SystemApi
38 public class CarDiagnosticEvent implements Parcelable {
39     /** Whether this frame represents a live or a freeze frame */
40     public final int frameType;
41 
42     /**
43      * When this data was acquired in car or received from car. It is elapsed real-time of data
44      * reception from car in nanoseconds since system boot.
45      */
46     public final long timestamp;
47 
48     /**
49      * Sparse array that contains the mapping of OBD2 diagnostic properties to their values for
50      * integer valued properties
51      */
52     private final SparseIntArray intValues;
53 
54     /**
55      * Sparse array that contains the mapping of OBD2 diagnostic properties to their values for
56      * float valued properties
57      */
58     private final SparseArray<Float> floatValues;
59 
60     /**
61      * Diagnostic Troubleshooting Code (DTC) that was detected and caused this frame to be stored
62      * (if a freeze frame). Always null for a live frame.
63      */
64     public final String dtc;
65 
CarDiagnosticEvent(Parcel in)66     public CarDiagnosticEvent(Parcel in) {
67         frameType = in.readInt();
68         timestamp = in.readLong();
69         int len = in.readInt();
70         floatValues = new SparseArray<>(len);
71         for (int i = 0; i < len; ++i) {
72             int key = in.readInt();
73             float value = in.readFloat();
74             floatValues.put(key, value);
75         }
76         len = in.readInt();
77         intValues = new SparseIntArray(len);
78         for (int i = 0; i < len; ++i) {
79             int key = in.readInt();
80             int value = in.readInt();
81             intValues.put(key, value);
82         }
83         dtc = (String) in.readValue(String.class.getClassLoader());
84         // version 1 up to here
85     }
86 
87     @Override
describeContents()88     public int describeContents() {
89         return 0;
90     }
91 
92     @Override
writeToParcel(Parcel dest, int flags)93     public void writeToParcel(Parcel dest, int flags) {
94         dest.writeInt(frameType);
95         dest.writeLong(timestamp);
96         dest.writeInt(floatValues.size());
97         for (int i = 0; i < floatValues.size(); ++i) {
98             int key = floatValues.keyAt(i);
99             dest.writeInt(key);
100             dest.writeFloat(floatValues.get(key));
101         }
102         dest.writeInt(intValues.size());
103         for (int i = 0; i < intValues.size(); ++i) {
104             int key = intValues.keyAt(i);
105             dest.writeInt(key);
106             dest.writeInt(intValues.get(key));
107         }
108         dest.writeValue(dtc);
109     }
110 
111     /**
112      * Store the contents of this diagnostic event in a JsonWriter.
113      *
114      * The data is stored as a JSON object, with these fields:
115      *  type: either "live" or "freeze" depending on the type of frame;
116      *  timestamp: the timestamp at which this frame was generated;
117      *  intValues: an array of objects each of which has two elements:
118      *    id: the integer identifier of the sensor;
119      *    value: the integer value of the sensor;
120      *  floatValues: an array of objects each of which has two elements:
121      *    id: the integer identifier of the sensor;
122      *    value: the floating-point value of the sensor;
123      *  stringValue: the DTC for a freeze frame, omitted for a live frame
124      */
writeToJson(JsonWriter jsonWriter)125     public void writeToJson(JsonWriter jsonWriter) throws IOException {
126         jsonWriter.beginObject();
127 
128         jsonWriter.name("type");
129         switch (frameType) {
130             case CarDiagnosticManager.FRAME_TYPE_LIVE:
131                 jsonWriter.value("live");
132                 break;
133             case CarDiagnosticManager.FRAME_TYPE_FREEZE:
134                 jsonWriter.value("freeze");
135                 break;
136             default:
137                 throw new IllegalStateException("unknown frameType " + frameType);
138         }
139 
140         jsonWriter.name("timestamp").value(timestamp);
141 
142         jsonWriter.name("intValues").beginArray();
143         for (int i = 0; i < intValues.size(); ++i) {
144             jsonWriter.beginObject();
145             jsonWriter.name("id").value(intValues.keyAt(i));
146             jsonWriter.name("value").value(intValues.valueAt(i));
147             jsonWriter.endObject();
148         }
149         jsonWriter.endArray();
150 
151         jsonWriter.name("floatValues").beginArray();
152         for (int i = 0; i < floatValues.size(); ++i) {
153             jsonWriter.beginObject();
154             jsonWriter.name("id").value(floatValues.keyAt(i));
155             jsonWriter.name("value").value(floatValues.valueAt(i));
156             jsonWriter.endObject();
157         }
158         jsonWriter.endArray();
159 
160         if (dtc != null) {
161             jsonWriter.name("stringValue").value(dtc);
162         }
163 
164         jsonWriter.endObject();
165     }
166 
167     public static final Parcelable.Creator<CarDiagnosticEvent> CREATOR =
168             new Parcelable.Creator<CarDiagnosticEvent>() {
169                 public CarDiagnosticEvent createFromParcel(Parcel in) {
170                     return new CarDiagnosticEvent(in);
171                 }
172 
173                 public CarDiagnosticEvent[] newArray(int size) {
174                     return new CarDiagnosticEvent[size];
175                 }
176             };
177 
CarDiagnosticEvent( int frameType, long timestamp, SparseArray<Float> floatValues, SparseIntArray intValues, String dtc)178     private CarDiagnosticEvent(
179             int frameType,
180             long timestamp,
181             SparseArray<Float> floatValues,
182             SparseIntArray intValues,
183             String dtc) {
184         this.frameType = frameType;
185         this.timestamp = timestamp;
186         this.floatValues = floatValues;
187         this.intValues = intValues;
188         this.dtc = dtc;
189     }
190 
191     /**
192      * This class can be used to incrementally construct a CarDiagnosticEvent.
193      * CarDiagnosticEvent instances are immutable once built.
194      */
195     public static class Builder {
196         private int mType = CarDiagnosticManager.FRAME_TYPE_LIVE;
197         private long mTimestamp = 0;
198         private SparseArray<Float> mFloatValues = new SparseArray<>();
199         private SparseIntArray mIntValues = new SparseIntArray();
200         private String mDtc = null;
201 
Builder(int type)202         private Builder(int type) {
203             mType = type;
204         }
205 
206         /** Returns a new Builder for a live frame */
newLiveFrameBuilder()207         public static Builder newLiveFrameBuilder() {
208             return new Builder(CarDiagnosticManager.FRAME_TYPE_LIVE);
209         }
210 
211         /** Returns a new Builder for a freeze frame */
newFreezeFrameBuilder()212         public static Builder newFreezeFrameBuilder() {
213             return new Builder(CarDiagnosticManager.FRAME_TYPE_FREEZE);
214         }
215 
216         /** Sets the timestamp for the frame being built */
atTimestamp(long timestamp)217         public Builder atTimestamp(long timestamp) {
218             mTimestamp = timestamp;
219             return this;
220         }
221 
222         /** Adds an integer-valued sensor to the frame being built */
withIntValue(int key, int value)223         public Builder withIntValue(int key, int value) {
224             mIntValues.put(key, value);
225             return this;
226         }
227 
228         /** Adds a float-valued sensor to the frame being built */
withFloatValue(int key, float value)229         public Builder withFloatValue(int key, float value) {
230             mFloatValues.put(key, value);
231             return this;
232         }
233 
234         /** Sets the DTC for the frame being built */
withDtc(String dtc)235         public Builder withDtc(String dtc) {
236             mDtc = dtc;
237             return this;
238         }
239 
240         /** Builds and returns the CarDiagnosticEvent */
build()241         public CarDiagnosticEvent build() {
242             return new CarDiagnosticEvent(mType, mTimestamp, mFloatValues, mIntValues, mDtc);
243         }
244     }
245 
246     /**
247      * Returns a copy of this CarDiagnosticEvent with all vendor-specific sensors removed.
248      *
249      * @hide
250      */
withVendorSensorsRemoved()251     public CarDiagnosticEvent withVendorSensorsRemoved() {
252         SparseIntArray newIntValues = intValues.clone();
253         SparseArray<Float> newFloatValues = floatValues.clone();
254         for (int i = 0; i < intValues.size(); ++i) {
255             int key = intValues.keyAt(i);
256             if (key >= android.car.diagnostic.IntegerSensorIndex.LAST_SYSTEM) {
257                 newIntValues.delete(key);
258             }
259         }
260         for (int i = 0; i < floatValues.size(); ++i) {
261             int key = floatValues.keyAt(i);
262             if (key >= android.car.diagnostic.FloatSensorIndex.LAST_SYSTEM) {
263                 newFloatValues.delete(key);
264             }
265         }
266         return new CarDiagnosticEvent(frameType, timestamp, newFloatValues, newIntValues, dtc);
267     }
268 
269     /** Returns true if this object is a live frame, false otherwise */
isLiveFrame()270     public boolean isLiveFrame() {
271         return CarDiagnosticManager.FRAME_TYPE_LIVE == frameType;
272     }
273 
274     /** Returns true if this object is a freeze frame, false otherwise */
isFreezeFrame()275     public boolean isFreezeFrame() {
276         return CarDiagnosticManager.FRAME_TYPE_FREEZE == frameType;
277     }
278 
279     /** @hide */
isEmptyFrame()280     public boolean isEmptyFrame() {
281         boolean empty = (0 == intValues.size());
282         empty &= (0 == floatValues.size());
283         if (isFreezeFrame()) empty &= dtc.isEmpty();
284         return empty;
285     }
286 
287     /** @hide */
checkLiveFrame()288     public CarDiagnosticEvent checkLiveFrame() {
289         if (!isLiveFrame()) throw new IllegalStateException("frame is not a live frame");
290         return this;
291     }
292 
293     /** @hide */
checkFreezeFrame()294     public CarDiagnosticEvent checkFreezeFrame() {
295         if (!isFreezeFrame()) throw new IllegalStateException("frame is not a freeze frame");
296         return this;
297     }
298 
299     /** @hide */
isEarlierThan(CarDiagnosticEvent otherEvent)300     public boolean isEarlierThan(CarDiagnosticEvent otherEvent) {
301         otherEvent = Objects.requireNonNull(otherEvent);
302         return (timestamp < otherEvent.timestamp);
303     }
304 
305     @Override
equals(Object otherObject)306     public boolean equals(Object otherObject) {
307         if (this == otherObject) {
308             return true;
309         }
310         if (null == otherObject) {
311             return false;
312         }
313         if (!(otherObject instanceof CarDiagnosticEvent)) {
314             return false;
315         }
316         CarDiagnosticEvent otherEvent = (CarDiagnosticEvent)otherObject;
317         if (otherEvent.frameType != frameType)
318             return false;
319         if (otherEvent.timestamp != timestamp)
320             return false;
321         if (otherEvent.intValues.size() != intValues.size())
322             return false;
323         if (otherEvent.floatValues.size() != floatValues.size())
324             return false;
325         if (!Objects.equals(dtc, otherEvent.dtc))
326             return false;
327         for (int i = 0; i < intValues.size(); ++i) {
328             int key = intValues.keyAt(i);
329             int otherKey = otherEvent.intValues.keyAt(i);
330             if (key != otherKey) {
331                 return false;
332             }
333             int value = intValues.valueAt(i);
334             int otherValue = otherEvent.intValues.valueAt(i);
335             if (value != otherValue) {
336                 return false;
337             }
338         }
339         for (int i = 0; i < floatValues.size(); ++i) {
340             int key = floatValues.keyAt(i);
341             int otherKey = otherEvent.floatValues.keyAt(i);
342             if (key != otherKey) {
343                 return false;
344             }
345             float value = floatValues.valueAt(i);
346             float otherValue = otherEvent.floatValues.valueAt(i);
347             if (value != otherValue) {
348                 return false;
349             }
350         }
351         return true;
352     }
353 
354     @Override
hashCode()355     public int hashCode() {
356         Integer[] intKeys = new Integer[intValues.size()];
357         Integer[] floatKeys = new Integer[floatValues.size()];
358         Integer[] intValues = new Integer[intKeys.length];
359         Float[] floatValues = new Float[floatKeys.length];
360         for (int i = 0; i < intKeys.length; ++i) {
361             intKeys[i] = this.intValues.keyAt(i);
362             intValues[i] = this.intValues.valueAt(i);
363         }
364         for (int i = 0; i < floatKeys.length; ++i) {
365             floatKeys[i] = this.floatValues.keyAt(i);
366             floatValues[i] = this.floatValues.valueAt(i);
367         }
368         int intKeysHash = Objects.hash((Object[])intKeys);
369         int intValuesHash = Objects.hash((Object[])intValues);
370         int floatKeysHash = Objects.hash((Object[])floatKeys);
371         int floatValuesHash = Objects.hash((Object[])floatValues);
372         return Objects.hash(frameType,
373                 timestamp,
374                 dtc,
375                 intKeysHash,
376                 intValuesHash,
377                 floatKeysHash,
378                 floatValuesHash);
379     }
380 
381     @Override
toString()382     public String toString() {
383         return String.format(
384                 "%s diagnostic frame {\n"
385                         + "\ttimestamp: %d, "
386                         + "DTC: %s\n"
387                         + "\tintValues: %s\n"
388                         + "\tfloatValues: %s\n}",
389                 isLiveFrame() ? "live" : "freeze",
390                 timestamp,
391                 dtc,
392                 intValues.toString(),
393                 floatValues.toString());
394     }
395 
396     /**
397      * Returns the value of the given integer sensor, if present in this frame.
398      * Returns defaultValue otherwise.
399      */
getSystemIntegerSensor( @ndroid.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor, int defaultValue)400     public int getSystemIntegerSensor(
401             @android.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor, int defaultValue) {
402         return intValues.get(sensor, defaultValue);
403     }
404 
405     /**
406      * Returns the value of the given float sensor, if present in this frame.
407      * Returns defaultValue otherwise.
408      */
getSystemFloatSensor( @ndroid.car.diagnostic.FloatSensorIndex.SensorIndex int sensor, float defaultValue)409     public float getSystemFloatSensor(
410             @android.car.diagnostic.FloatSensorIndex.SensorIndex int sensor, float defaultValue) {
411         return floatValues.get(sensor, defaultValue);
412     }
413 
414     /**
415      * Returns the value of the given integer sensor, if present in this frame.
416      * Returns defaultValue otherwise.
417      */
getVendorIntegerSensor(int sensor, int defaultValue)418     public int getVendorIntegerSensor(int sensor, int defaultValue) {
419         return intValues.get(sensor, defaultValue);
420     }
421 
422     /**
423      * Returns the value of the given float sensor, if present in this frame.
424      * Returns defaultValue otherwise.
425      */
getVendorFloatSensor(int sensor, float defaultValue)426     public float getVendorFloatSensor(int sensor, float defaultValue) {
427         return floatValues.get(sensor, defaultValue);
428     }
429 
430     /**
431      * Returns the value of the given integer sensor, if present in this frame.
432      * Returns null otherwise.
433      */
getSystemIntegerSensor( @ndroid.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor)434     public @Nullable Integer getSystemIntegerSensor(
435             @android.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor) {
436         int index = intValues.indexOfKey(sensor);
437         if (index < 0) return null;
438         return intValues.valueAt(index);
439     }
440 
441     /**
442      * Returns the value of the given float sensor, if present in this frame.
443      * Returns null otherwise.
444      */
getSystemFloatSensor( @ndroid.car.diagnostic.FloatSensorIndex.SensorIndex int sensor)445     public @Nullable Float getSystemFloatSensor(
446             @android.car.diagnostic.FloatSensorIndex.SensorIndex int sensor) {
447         int index = floatValues.indexOfKey(sensor);
448         if (index < 0) return null;
449         return floatValues.valueAt(index);
450     }
451 
452     /**
453      * Returns the value of the given integer sensor, if present in this frame.
454      * Returns null otherwise.
455      */
getVendorIntegerSensor(int sensor)456     public @Nullable Integer getVendorIntegerSensor(int sensor) {
457         int index = intValues.indexOfKey(sensor);
458         if (index < 0) return null;
459         return intValues.valueAt(index);
460     }
461 
462     /**
463      * Returns the value of the given float sensor, if present in this frame.
464      * Returns null otherwise.
465      */
getVendorFloatSensor(int sensor)466     public @Nullable Float getVendorFloatSensor(int sensor) {
467         int index = floatValues.indexOfKey(sensor);
468         if (index < 0) return null;
469         return floatValues.valueAt(index);
470     }
471 
472     /**
473      * Represents possible states of the fuel system; see {@link
474      * android.car.diagnostic.IntegerSensorIndex#FUEL_SYSTEM_STATUS}
475      */
476     public static final class FuelSystemStatus {
FuelSystemStatus()477         private FuelSystemStatus() {}
478 
479         public static final int OPEN_INSUFFICIENT_ENGINE_TEMPERATURE = 1;
480         public static final int CLOSED_LOOP = 2;
481         public static final int OPEN_ENGINE_LOAD_OR_DECELERATION = 4;
482         public static final int OPEN_SYSTEM_FAILURE = 8;
483         public static final int CLOSED_LOOP_BUT_FEEDBACK_FAULT = 16;
484 
485         @Retention(RetentionPolicy.SOURCE)
486         @IntDef({
487             OPEN_INSUFFICIENT_ENGINE_TEMPERATURE,
488             CLOSED_LOOP,
489             OPEN_ENGINE_LOAD_OR_DECELERATION,
490             OPEN_SYSTEM_FAILURE,
491             CLOSED_LOOP_BUT_FEEDBACK_FAULT
492         })
493         /** @hide */
494         public @interface Status {}
495     }
496 
497     /**
498      * Represents possible states of the secondary air system; see {@link
499      * android.car.diagnostic.IntegerSensorIndex#COMMANDED_SECONDARY_AIR_STATUS}
500      */
501     public static final class SecondaryAirStatus {
SecondaryAirStatus()502         private SecondaryAirStatus() {}
503 
504         public static final int UPSTREAM = 1;
505         public static final int DOWNSTREAM_OF_CATALYCIC_CONVERTER = 2;
506         public static final int FROM_OUTSIDE_OR_OFF = 4;
507         public static final int PUMP_ON_FOR_DIAGNOSTICS = 8;
508 
509         @Retention(RetentionPolicy.SOURCE)
510         @IntDef({
511             UPSTREAM,
512             DOWNSTREAM_OF_CATALYCIC_CONVERTER,
513             FROM_OUTSIDE_OR_OFF,
514             PUMP_ON_FOR_DIAGNOSTICS
515         })
516         /** @hide */
517         public @interface Status {}
518     }
519 
520     /**
521      * Represents possible types of fuel; see {@link
522      * android.car.diagnostic.IntegerSensorIndex#FUEL_TYPE}
523      */
524     public static final class FuelType {
FuelType()525         private FuelType() {}
526 
527         public static final int NOT_AVAILABLE = 0;
528         public static final int GASOLINE = 1;
529         public static final int METHANOL = 2;
530         public static final int ETHANOL = 3;
531         public static final int DIESEL = 4;
532         public static final int LPG = 5;
533         public static final int CNG = 6;
534         public static final int PROPANE = 7;
535         public static final int ELECTRIC = 8;
536         public static final int BIFUEL_RUNNING_GASOLINE = 9;
537         public static final int BIFUEL_RUNNING_METHANOL = 10;
538         public static final int BIFUEL_RUNNING_ETHANOL = 11;
539         public static final int BIFUEL_RUNNING_LPG = 12;
540         public static final int BIFUEL_RUNNING_CNG = 13;
541         public static final int BIFUEL_RUNNING_PROPANE = 14;
542         public static final int BIFUEL_RUNNING_ELECTRIC = 15;
543         public static final int BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION = 16;
544         public static final int HYBRID_GASOLINE = 17;
545         public static final int HYBRID_ETHANOL = 18;
546         public static final int HYBRID_DIESEL = 19;
547         public static final int HYBRID_ELECTRIC = 20;
548         public static final int HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION = 21;
549         public static final int HYBRID_REGENERATIVE = 22;
550         public static final int BIFUEL_RUNNING_DIESEL = 23;
551 
552         @Retention(RetentionPolicy.SOURCE)
553         @IntDef({
554             NOT_AVAILABLE,
555             GASOLINE,
556             METHANOL,
557             ETHANOL,
558             DIESEL,
559             LPG,
560             CNG,
561             PROPANE,
562             ELECTRIC,
563             BIFUEL_RUNNING_GASOLINE,
564             BIFUEL_RUNNING_METHANOL,
565             BIFUEL_RUNNING_ETHANOL,
566             BIFUEL_RUNNING_LPG,
567             BIFUEL_RUNNING_CNG,
568             BIFUEL_RUNNING_PROPANE,
569             BIFUEL_RUNNING_ELECTRIC,
570             BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION,
571             HYBRID_GASOLINE,
572             HYBRID_ETHANOL,
573             HYBRID_DIESEL,
574             HYBRID_ELECTRIC,
575             HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION,
576             HYBRID_REGENERATIVE,
577             BIFUEL_RUNNING_DIESEL
578         })
579         /** @hide */
580         public @interface Type {}
581     }
582 
583     /**
584      * Represents the state of an ignition monitor on a vehicle.
585      */
586     public static final class IgnitionMonitor {
587         public final boolean available;
588         public final boolean incomplete;
589 
IgnitionMonitor(boolean available, boolean incomplete)590         IgnitionMonitor(boolean available, boolean incomplete) {
591             this.available = available;
592             this.incomplete = incomplete;
593         }
594 
595         /** @hide */
596         public static final class Decoder {
597             private final int mAvailableBitmask;
598             private final int mIncompleteBitmask;
599 
Decoder(int availableBitmask, int incompleteBitmask)600             Decoder(int availableBitmask, int incompleteBitmask) {
601                 mAvailableBitmask = availableBitmask;
602                 mIncompleteBitmask = incompleteBitmask;
603             }
604 
fromValue(int value)605             public IgnitionMonitor fromValue(int value) {
606                 boolean available = (0 != (value & mAvailableBitmask));
607                 boolean incomplete = (0 != (value & mIncompleteBitmask));
608 
609                 return new IgnitionMonitor(available, incomplete);
610             }
611         }
612     }
613 
614     /**
615      * Contains information about ignition monitors common to all vehicle types.
616      */
617     public static class CommonIgnitionMonitors {
618         public final IgnitionMonitor components;
619         public final IgnitionMonitor fuelSystem;
620         public final IgnitionMonitor misfire;
621 
622         /** @hide */
623         public static final int COMPONENTS_AVAILABLE = 0x1 << 0;
624         /** @hide */
625         public static final int COMPONENTS_INCOMPLETE = 0x1 << 1;
626 
627         /** @hide */
628         public static final int FUEL_SYSTEM_AVAILABLE = 0x1 << 2;
629         /** @hide */
630         public static final int FUEL_SYSTEM_INCOMPLETE = 0x1 << 3;
631 
632         /** @hide */
633         public static final int MISFIRE_AVAILABLE = 0x1 << 4;
634         /** @hide */
635         public static final int MISFIRE_INCOMPLETE = 0x1 << 5;
636 
637         static final IgnitionMonitor.Decoder COMPONENTS_DECODER =
638                 new IgnitionMonitor.Decoder(COMPONENTS_AVAILABLE, COMPONENTS_INCOMPLETE);
639 
640         static final IgnitionMonitor.Decoder FUEL_SYSTEM_DECODER =
641                 new IgnitionMonitor.Decoder(FUEL_SYSTEM_AVAILABLE, FUEL_SYSTEM_INCOMPLETE);
642 
643         static final IgnitionMonitor.Decoder MISFIRE_DECODER =
644                 new IgnitionMonitor.Decoder(MISFIRE_AVAILABLE, MISFIRE_INCOMPLETE);
645 
CommonIgnitionMonitors(int bitmask)646         CommonIgnitionMonitors(int bitmask) {
647             components = COMPONENTS_DECODER.fromValue(bitmask);
648             fuelSystem = FUEL_SYSTEM_DECODER.fromValue(bitmask);
649             misfire = MISFIRE_DECODER.fromValue(bitmask);
650         }
651 
652         /**
653          * Returns data about ignition monitors specific to spark vehicles, if this
654          * object represents ignition monitors for a spark vehicle.
655          * Returns null otherwise.
656          */
asSparkIgnitionMonitors()657         public @Nullable SparkIgnitionMonitors asSparkIgnitionMonitors() {
658             if (this instanceof SparkIgnitionMonitors) return (SparkIgnitionMonitors) this;
659             return null;
660         }
661 
662         /**
663          * Returns data about ignition monitors specific to compression vehicles, if this
664          * object represents ignition monitors for a compression vehicle.
665          * Returns null otherwise.
666          */
asCompressionIgnitionMonitors()667         public @Nullable CompressionIgnitionMonitors asCompressionIgnitionMonitors() {
668             if (this instanceof CompressionIgnitionMonitors)
669                 return (CompressionIgnitionMonitors) this;
670             return null;
671         }
672     }
673 
674     /**
675      * Contains information about ignition monitors specific to spark vehicles.
676      */
677     public static final class SparkIgnitionMonitors extends CommonIgnitionMonitors {
678         public final IgnitionMonitor EGR;
679         public final IgnitionMonitor oxygenSensorHeater;
680         public final IgnitionMonitor oxygenSensor;
681         public final IgnitionMonitor ACRefrigerant;
682         public final IgnitionMonitor secondaryAirSystem;
683         public final IgnitionMonitor evaporativeSystem;
684         public final IgnitionMonitor heatedCatalyst;
685         public final IgnitionMonitor catalyst;
686 
687         /** @hide */
688         public static final int EGR_AVAILABLE = 0x1 << 6;
689         /** @hide */
690         public static final int EGR_INCOMPLETE = 0x1 << 7;
691 
692         /** @hide */
693         public static final int OXYGEN_SENSOR_HEATER_AVAILABLE = 0x1 << 8;
694         /** @hide */
695         public static final int OXYGEN_SENSOR_HEATER_INCOMPLETE = 0x1 << 9;
696 
697         /** @hide */
698         public static final int OXYGEN_SENSOR_AVAILABLE = 0x1 << 10;
699         /** @hide */
700         public static final int OXYGEN_SENSOR_INCOMPLETE = 0x1 << 11;
701 
702         /** @hide */
703         public static final int AC_REFRIGERANT_AVAILABLE = 0x1 << 12;
704         /** @hide */
705         public static final int AC_REFRIGERANT_INCOMPLETE = 0x1 << 13;
706 
707         /** @hide */
708         public static final int SECONDARY_AIR_SYSTEM_AVAILABLE = 0x1 << 14;
709         /** @hide */
710         public static final int SECONDARY_AIR_SYSTEM_INCOMPLETE = 0x1 << 15;
711 
712         /** @hide */
713         public static final int EVAPORATIVE_SYSTEM_AVAILABLE = 0x1 << 16;
714         /** @hide */
715         public static final int EVAPORATIVE_SYSTEM_INCOMPLETE = 0x1 << 17;
716 
717         /** @hide */
718         public static final int HEATED_CATALYST_AVAILABLE = 0x1 << 18;
719         /** @hide */
720         public static final int HEATED_CATALYST_INCOMPLETE = 0x1 << 19;
721 
722         /** @hide */
723         public static final int CATALYST_AVAILABLE = 0x1 << 20;
724         /** @hide */
725         public static final int CATALYST_INCOMPLETE = 0x1 << 21;
726 
727         static final IgnitionMonitor.Decoder EGR_DECODER =
728                 new IgnitionMonitor.Decoder(EGR_AVAILABLE, EGR_INCOMPLETE);
729 
730         static final IgnitionMonitor.Decoder OXYGEN_SENSOR_HEATER_DECODER =
731                 new IgnitionMonitor.Decoder(OXYGEN_SENSOR_HEATER_AVAILABLE,
732                         OXYGEN_SENSOR_HEATER_INCOMPLETE);
733 
734         static final IgnitionMonitor.Decoder OXYGEN_SENSOR_DECODER =
735                 new IgnitionMonitor.Decoder(OXYGEN_SENSOR_AVAILABLE, OXYGEN_SENSOR_INCOMPLETE);
736 
737         static final IgnitionMonitor.Decoder AC_REFRIGERANT_DECODER =
738                 new IgnitionMonitor.Decoder(AC_REFRIGERANT_AVAILABLE,
739                         AC_REFRIGERANT_INCOMPLETE);
740 
741         static final IgnitionMonitor.Decoder SECONDARY_AIR_SYSTEM_DECODER =
742                 new IgnitionMonitor.Decoder(SECONDARY_AIR_SYSTEM_AVAILABLE,
743                         SECONDARY_AIR_SYSTEM_INCOMPLETE);
744 
745         static final IgnitionMonitor.Decoder EVAPORATIVE_SYSTEM_DECODER =
746                 new IgnitionMonitor.Decoder(EVAPORATIVE_SYSTEM_AVAILABLE,
747                         EVAPORATIVE_SYSTEM_INCOMPLETE);
748 
749         static final IgnitionMonitor.Decoder HEATED_CATALYST_DECODER =
750                 new IgnitionMonitor.Decoder(HEATED_CATALYST_AVAILABLE,
751                         HEATED_CATALYST_INCOMPLETE);
752 
753         static final IgnitionMonitor.Decoder CATALYST_DECODER =
754                 new IgnitionMonitor.Decoder(CATALYST_AVAILABLE, CATALYST_INCOMPLETE);
755 
SparkIgnitionMonitors(int bitmask)756         SparkIgnitionMonitors(int bitmask) {
757             super(bitmask);
758             EGR = EGR_DECODER.fromValue(bitmask);
759             oxygenSensorHeater = OXYGEN_SENSOR_HEATER_DECODER.fromValue(bitmask);
760             oxygenSensor = OXYGEN_SENSOR_DECODER.fromValue(bitmask);
761             ACRefrigerant = AC_REFRIGERANT_DECODER.fromValue(bitmask);
762             secondaryAirSystem = SECONDARY_AIR_SYSTEM_DECODER.fromValue(bitmask);
763             evaporativeSystem = EVAPORATIVE_SYSTEM_DECODER.fromValue(bitmask);
764             heatedCatalyst = HEATED_CATALYST_DECODER.fromValue(bitmask);
765             catalyst = CATALYST_DECODER.fromValue(bitmask);
766         }
767     }
768 
769     /**
770      * Contains information about ignition monitors specific to compression vehicles.
771      */
772     public static final class CompressionIgnitionMonitors extends CommonIgnitionMonitors {
773         public final IgnitionMonitor EGROrVVT;
774         public final IgnitionMonitor PMFilter;
775         public final IgnitionMonitor exhaustGasSensor;
776         public final IgnitionMonitor boostPressure;
777         public final IgnitionMonitor NOxSCR;
778         public final IgnitionMonitor NMHCCatalyst;
779 
780         /** @hide */
781         public static final int EGR_OR_VVT_AVAILABLE = 0x1 << 6;
782         /** @hide */
783         public static final int EGR_OR_VVT_INCOMPLETE = 0x1 << 7;
784 
785         /** @hide */
786         public static final int PM_FILTER_AVAILABLE = 0x1 << 8;
787         /** @hide */
788         public static final int PM_FILTER_INCOMPLETE = 0x1 << 9;
789 
790         /** @hide */
791         public static final int EXHAUST_GAS_SENSOR_AVAILABLE = 0x1 << 10;
792         /** @hide */
793         public static final int EXHAUST_GAS_SENSOR_INCOMPLETE = 0x1 << 11;
794 
795         /** @hide */
796         public static final int BOOST_PRESSURE_AVAILABLE = 0x1 << 12;
797         /** @hide */
798         public static final int BOOST_PRESSURE_INCOMPLETE = 0x1 << 13;
799 
800         /** @hide */
801         public static final int NOx_SCR_AVAILABLE = 0x1 << 14;
802         /** @hide */
803         public static final int NOx_SCR_INCOMPLETE = 0x1 << 15;
804 
805         /** @hide */
806         public static final int NMHC_CATALYST_AVAILABLE = 0x1 << 16;
807         /** @hide */
808         public static final int NMHC_CATALYST_INCOMPLETE = 0x1 << 17;
809 
810         static final IgnitionMonitor.Decoder EGR_OR_VVT_DECODER =
811                 new IgnitionMonitor.Decoder(EGR_OR_VVT_AVAILABLE, EGR_OR_VVT_INCOMPLETE);
812 
813         static final IgnitionMonitor.Decoder PM_FILTER_DECODER =
814                 new IgnitionMonitor.Decoder(PM_FILTER_AVAILABLE, PM_FILTER_INCOMPLETE);
815 
816         static final IgnitionMonitor.Decoder EXHAUST_GAS_SENSOR_DECODER =
817                 new IgnitionMonitor.Decoder(EXHAUST_GAS_SENSOR_AVAILABLE,
818                         EXHAUST_GAS_SENSOR_INCOMPLETE);
819 
820         static final IgnitionMonitor.Decoder BOOST_PRESSURE_DECODER =
821                 new IgnitionMonitor.Decoder(BOOST_PRESSURE_AVAILABLE,
822                         BOOST_PRESSURE_INCOMPLETE);
823 
824         static final IgnitionMonitor.Decoder NOx_SCR_DECODER =
825                 new IgnitionMonitor.Decoder(NOx_SCR_AVAILABLE, NOx_SCR_INCOMPLETE);
826 
827         static final IgnitionMonitor.Decoder NMHC_CATALYST_DECODER =
828                 new IgnitionMonitor.Decoder(NMHC_CATALYST_AVAILABLE, NMHC_CATALYST_INCOMPLETE);
829 
CompressionIgnitionMonitors(int bitmask)830         CompressionIgnitionMonitors(int bitmask) {
831             super(bitmask);
832             EGROrVVT = EGR_OR_VVT_DECODER.fromValue(bitmask);
833             PMFilter = PM_FILTER_DECODER.fromValue(bitmask);
834             exhaustGasSensor = EXHAUST_GAS_SENSOR_DECODER.fromValue(bitmask);
835             boostPressure = BOOST_PRESSURE_DECODER.fromValue(bitmask);
836             NOxSCR = NOx_SCR_DECODER.fromValue(bitmask);
837             NMHCCatalyst = NMHC_CATALYST_DECODER.fromValue(bitmask);
838         }
839     }
840 
841     /**
842      * Returns the state of the fuel system, if present in this frame.
843      * Returns null otherwise.
844      */
getFuelSystemStatus()845     public @Nullable @FuelSystemStatus.Status Integer getFuelSystemStatus() {
846         return getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.FUEL_SYSTEM_STATUS);
847     }
848 
849     /**
850      * Returns the state of the secondary air system, if present in this frame.
851      * Returns null otherwise.
852      */
getSecondaryAirStatus()853     public @Nullable @SecondaryAirStatus.Status Integer getSecondaryAirStatus() {
854         return getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS);
855     }
856 
857     /**
858      * Returns data about the ignition monitors, if present in this frame.
859      * Returns null otherwise.
860      */
getIgnitionMonitors()861     public @Nullable CommonIgnitionMonitors getIgnitionMonitors() {
862         Integer ignitionMonitorsType =
863                 getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED);
864         Integer ignitionMonitorsBitmask =
865                 getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS);
866         if (null == ignitionMonitorsType) return null;
867         if (null == ignitionMonitorsBitmask) return null;
868         switch (ignitionMonitorsType) {
869             case 0:
870                 return new SparkIgnitionMonitors(ignitionMonitorsBitmask);
871             case 1:
872                 return new CompressionIgnitionMonitors(ignitionMonitorsBitmask);
873             default:
874                 return null;
875         }
876     }
877 
878     /**
879      * Returns the fuel type, if present in this frame.
880      * Returns null otherwise.
881      */
getFuelType()882     public @Nullable @FuelType.Type Integer getFuelType() {
883         return getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.FUEL_TYPE);
884     }
885 }
886