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