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