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 mIntValues;
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> mFloatValues;
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         mFloatValues = new SparseArray<>(len);
72         for (int i = 0; i < len; ++i) {
73             int key = in.readInt();
74             float value = in.readFloat();
75             mFloatValues.put(key, value);
76         }
77         len = in.readInt();
78         mIntValues = new SparseIntArray(len);
79         for (int i = 0; i < len; ++i) {
80             int key = in.readInt();
81             int value = in.readInt();
82             mIntValues.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(mFloatValues.size());
98         for (int i = 0; i < mFloatValues.size(); ++i) {
99             int key = mFloatValues.keyAt(i);
100             dest.writeInt(key);
101             dest.writeFloat(mFloatValues.get(key));
102         }
103         dest.writeInt(mIntValues.size());
104         for (int i = 0; i < mIntValues.size(); ++i) {
105             int key = mIntValues.keyAt(i);
106             dest.writeInt(key);
107             dest.writeInt(mIntValues.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 < mIntValues.size(); ++i) {
145             jsonWriter.beginObject();
146             jsonWriter.name("id").value(mIntValues.keyAt(i));
147             jsonWriter.name("value").value(mIntValues.valueAt(i));
148             jsonWriter.endObject();
149         }
150         jsonWriter.endArray();
151 
152         jsonWriter.name("floatValues").beginArray();
153         for (int i = 0; i < mFloatValues.size(); ++i) {
154             jsonWriter.beginObject();
155             jsonWriter.name("id").value(mFloatValues.keyAt(i));
156             jsonWriter.name("value").value(mFloatValues.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         mFloatValues = floatValues;
188         mIntValues = 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 = mIntValues.clone();
312         SparseArray<Float> newFloatValues = mFloatValues.clone();
313         for (int i = 0; i < mIntValues.size(); ++i) {
314             int key = mIntValues.keyAt(i);
315             if (key >= android.car.diagnostic.IntegerSensorIndex.LAST_SYSTEM) {
316                 newIntValues.delete(key);
317             }
318         }
319         for (int i = 0; i < mFloatValues.size(); ++i) {
320             int key = mFloatValues.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 == mIntValues.size());
341         empty &= (0 == mFloatValues.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         }
379         if (otherEvent.timestamp != timestamp) {
380             return false;
381         }
382         if (otherEvent.mIntValues.size() != mIntValues.size()) {
383             return false;
384         }
385         if (otherEvent.mFloatValues.size() != mFloatValues.size()) {
386             return false;
387         }
388         if (!Objects.equals(dtc, otherEvent.dtc)) {
389             return false;
390         }
391         for (int i = 0; i < mIntValues.size(); ++i) {
392             int key = mIntValues.keyAt(i);
393             int otherKey = otherEvent.mIntValues.keyAt(i);
394             if (key != otherKey) {
395                 return false;
396             }
397             int value = mIntValues.valueAt(i);
398             int otherValue = otherEvent.mIntValues.valueAt(i);
399             if (value != otherValue) {
400                 return false;
401             }
402         }
403         for (int i = 0; i < mFloatValues.size(); ++i) {
404             int key = mFloatValues.keyAt(i);
405             int otherKey = otherEvent.mFloatValues.keyAt(i);
406             if (key != otherKey) {
407                 return false;
408             }
409             float value = mFloatValues.valueAt(i);
410             float otherValue = otherEvent.mFloatValues.valueAt(i);
411             if (value != otherValue) {
412                 return false;
413             }
414         }
415         return true;
416     }
417 
418     @Override
hashCode()419     public int hashCode() {
420         Integer[] intKeys = new Integer[mIntValues.size()];
421         Integer[] floatKeys = new Integer[mFloatValues.size()];
422         Integer[] intValues = new Integer[intKeys.length];
423         Float[] floatValues = new Float[floatKeys.length];
424         for (int i = 0; i < intKeys.length; ++i) {
425             intKeys[i] = mIntValues.keyAt(i);
426             intValues[i] = mIntValues.valueAt(i);
427         }
428         for (int i = 0; i < floatKeys.length; ++i) {
429             floatKeys[i] = mFloatValues.keyAt(i);
430             floatValues[i] = mFloatValues.valueAt(i);
431         }
432         int intKeysHash = Objects.hash((Object[]) intKeys);
433         int intValuesHash = Objects.hash((Object[]) intValues);
434         int floatKeysHash = Objects.hash((Object[]) floatKeys);
435         int floatValuesHash = Objects.hash((Object[]) floatValues);
436         return Objects.hash(frameType,
437                 timestamp,
438                 dtc,
439                 intKeysHash,
440                 intValuesHash,
441                 floatKeysHash,
442                 floatValuesHash);
443     }
444 
445     @Override
toString()446     public String toString() {
447         return String.format(
448                 "%s diagnostic frame {\n"
449                         + "\ttimestamp: %d, "
450                         + "DTC: %s\n"
451                         + "\tmIntValues: %s\n"
452                         + "\tmFloatValues: %s\n}",
453                 isLiveFrame() ? "live" : "freeze",
454                 timestamp,
455                 dtc,
456                 mIntValues.toString(),
457                 mFloatValues.toString());
458     }
459 
460     /**
461      * Returns the value of the given integer sensor, if present in this frame.
462      * Returns defaultValue otherwise.
463      */
getSystemIntegerSensor( @ndroid.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor, int defaultValue)464     public int getSystemIntegerSensor(
465             @android.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor, int defaultValue) {
466         return mIntValues.get(sensor, defaultValue);
467     }
468 
469     /**
470      * Returns the value of the given float sensor, if present in this frame.
471      * Returns defaultValue otherwise.
472      */
getSystemFloatSensor( @ndroid.car.diagnostic.FloatSensorIndex.SensorIndex int sensor, float defaultValue)473     public float getSystemFloatSensor(
474             @android.car.diagnostic.FloatSensorIndex.SensorIndex int sensor, float defaultValue) {
475         return mFloatValues.get(sensor, defaultValue);
476     }
477 
478     /**
479      * Returns the value of the given integer sensor, if present in this frame.
480      * Returns defaultValue otherwise.
481      */
getVendorIntegerSensor(int sensor, int defaultValue)482     public int getVendorIntegerSensor(int sensor, int defaultValue) {
483         return mIntValues.get(sensor, defaultValue);
484     }
485 
486     /**
487      * Returns the value of the given float sensor, if present in this frame.
488      * Returns defaultValue otherwise.
489      */
getVendorFloatSensor(int sensor, float defaultValue)490     public float getVendorFloatSensor(int sensor, float defaultValue) {
491         return mFloatValues.get(sensor, defaultValue);
492     }
493 
494     /**
495      * Returns the value of the given integer sensor, if present in this frame.
496      * Returns null otherwise.
497      */
getSystemIntegerSensor( @ndroid.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor)498     public @Nullable Integer getSystemIntegerSensor(
499             @android.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor) {
500         int index = mIntValues.indexOfKey(sensor);
501         if (index < 0) return null;
502         return mIntValues.valueAt(index);
503     }
504 
505     /**
506      * Returns the value of the given float sensor, if present in this frame.
507      * Returns null otherwise.
508      */
getSystemFloatSensor( @ndroid.car.diagnostic.FloatSensorIndex.SensorIndex int sensor)509     public @Nullable Float getSystemFloatSensor(
510             @android.car.diagnostic.FloatSensorIndex.SensorIndex int sensor) {
511         int index = mFloatValues.indexOfKey(sensor);
512         if (index < 0) return null;
513         return mFloatValues.valueAt(index);
514     }
515 
516     /**
517      * Returns the value of the given integer sensor, if present in this frame.
518      * Returns null otherwise.
519      */
getVendorIntegerSensor(int sensor)520     public @Nullable Integer getVendorIntegerSensor(int sensor) {
521         int index = mIntValues.indexOfKey(sensor);
522         if (index < 0) return null;
523         return mIntValues.valueAt(index);
524     }
525 
526     /**
527      * Returns the value of the given float sensor, if present in this frame.
528      * Returns null otherwise.
529      */
getVendorFloatSensor(int sensor)530     public @Nullable Float getVendorFloatSensor(int sensor) {
531         int index = mFloatValues.indexOfKey(sensor);
532         if (index < 0) return null;
533         return mFloatValues.valueAt(index);
534     }
535 
536     /**
537      * Represents possible states of the fuel system; see {@link
538      * android.car.diagnostic.IntegerSensorIndex#FUEL_SYSTEM_STATUS}
539      */
540     public static final class FuelSystemStatus {
FuelSystemStatus()541         private FuelSystemStatus() {}
542 
543         public static final int OPEN_INSUFFICIENT_ENGINE_TEMPERATURE = 1;
544         public static final int CLOSED_LOOP = 2;
545         public static final int OPEN_ENGINE_LOAD_OR_DECELERATION = 4;
546         public static final int OPEN_SYSTEM_FAILURE = 8;
547         public static final int CLOSED_LOOP_BUT_FEEDBACK_FAULT = 16;
548 
549         @Retention(RetentionPolicy.SOURCE)
550         @IntDef({
551             OPEN_INSUFFICIENT_ENGINE_TEMPERATURE,
552             CLOSED_LOOP,
553             OPEN_ENGINE_LOAD_OR_DECELERATION,
554             OPEN_SYSTEM_FAILURE,
555             CLOSED_LOOP_BUT_FEEDBACK_FAULT
556         })
557         public @interface Status {}
558     }
559 
560     /**
561      * Represents possible states of the secondary air system; see {@link
562      * android.car.diagnostic.IntegerSensorIndex#COMMANDED_SECONDARY_AIR_STATUS}
563      */
564     public static final class SecondaryAirStatus {
SecondaryAirStatus()565         private SecondaryAirStatus() {}
566 
567         public static final int UPSTREAM = 1;
568         public static final int DOWNSTREAM_OF_CATALYCIC_CONVERTER = 2;
569         public static final int FROM_OUTSIDE_OR_OFF = 4;
570         public static final int PUMP_ON_FOR_DIAGNOSTICS = 8;
571 
572         @Retention(RetentionPolicy.SOURCE)
573         @IntDef({
574             UPSTREAM,
575             DOWNSTREAM_OF_CATALYCIC_CONVERTER,
576             FROM_OUTSIDE_OR_OFF,
577             PUMP_ON_FOR_DIAGNOSTICS
578         })
579         public @interface Status {}
580     }
581 
582     /**
583      * Represents possible types of fuel; see {@link
584      * android.car.diagnostic.IntegerSensorIndex#FUEL_TYPE}
585      */
586     public static final class FuelType {
FuelType()587         private FuelType() {}
588 
589         public static final int NOT_AVAILABLE = 0;
590         public static final int GASOLINE = 1;
591         public static final int METHANOL = 2;
592         public static final int ETHANOL = 3;
593         public static final int DIESEL = 4;
594         public static final int LPG = 5;
595         public static final int CNG = 6;
596         public static final int PROPANE = 7;
597         public static final int ELECTRIC = 8;
598         public static final int BIFUEL_RUNNING_GASOLINE = 9;
599         public static final int BIFUEL_RUNNING_METHANOL = 10;
600         public static final int BIFUEL_RUNNING_ETHANOL = 11;
601         public static final int BIFUEL_RUNNING_LPG = 12;
602         public static final int BIFUEL_RUNNING_CNG = 13;
603         public static final int BIFUEL_RUNNING_PROPANE = 14;
604         public static final int BIFUEL_RUNNING_ELECTRIC = 15;
605         public static final int BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION = 16;
606         public static final int HYBRID_GASOLINE = 17;
607         public static final int HYBRID_ETHANOL = 18;
608         public static final int HYBRID_DIESEL = 19;
609         public static final int HYBRID_ELECTRIC = 20;
610         public static final int HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION = 21;
611         public static final int HYBRID_REGENERATIVE = 22;
612         public static final int BIFUEL_RUNNING_DIESEL = 23;
613 
614         @Retention(RetentionPolicy.SOURCE)
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         public @interface Type {}
642     }
643 
644     /**
645      * Represents the state of an ignition monitor on a vehicle.
646      */
647     public static final class IgnitionMonitor {
648         public final boolean available;
649         public final boolean incomplete;
650 
IgnitionMonitor(boolean available, boolean incomplete)651         IgnitionMonitor(boolean available, boolean incomplete) {
652             this.available = available;
653             this.incomplete = incomplete;
654         }
655 
656         /** @hide */
657         public static final class Decoder {
658             private final int mAvailableBitmask;
659             private final int mIncompleteBitmask;
660 
Decoder(int availableBitmask, int incompleteBitmask)661             Decoder(int availableBitmask, int incompleteBitmask) {
662                 mAvailableBitmask = availableBitmask;
663                 mIncompleteBitmask = incompleteBitmask;
664             }
665 
666             /**
667              * Returns the {@link IgnitionMonitor} associated with the value passed as parameter.
668              */
fromValue(int value)669             public IgnitionMonitor fromValue(int value) {
670                 boolean available = (0 != (value & mAvailableBitmask));
671                 boolean incomplete = (0 != (value & mIncompleteBitmask));
672 
673                 return new IgnitionMonitor(available, incomplete);
674             }
675         }
676     }
677 
678     /**
679      * Contains information about ignition monitors common to all vehicle types.
680      */
681     public static class CommonIgnitionMonitors {
682         public final IgnitionMonitor components;
683         public final IgnitionMonitor fuelSystem;
684         public final IgnitionMonitor misfire;
685 
686         /** @hide */
687         public static final int COMPONENTS_AVAILABLE = 0x1 << 0;
688         /** @hide */
689         public static final int COMPONENTS_INCOMPLETE = 0x1 << 1;
690 
691         /** @hide */
692         public static final int FUEL_SYSTEM_AVAILABLE = 0x1 << 2;
693         /** @hide */
694         public static final int FUEL_SYSTEM_INCOMPLETE = 0x1 << 3;
695 
696         /** @hide */
697         public static final int MISFIRE_AVAILABLE = 0x1 << 4;
698         /** @hide */
699         public static final int MISFIRE_INCOMPLETE = 0x1 << 5;
700 
701         static final IgnitionMonitor.Decoder COMPONENTS_DECODER =
702                 new IgnitionMonitor.Decoder(COMPONENTS_AVAILABLE, COMPONENTS_INCOMPLETE);
703 
704         static final IgnitionMonitor.Decoder FUEL_SYSTEM_DECODER =
705                 new IgnitionMonitor.Decoder(FUEL_SYSTEM_AVAILABLE, FUEL_SYSTEM_INCOMPLETE);
706 
707         static final IgnitionMonitor.Decoder MISFIRE_DECODER =
708                 new IgnitionMonitor.Decoder(MISFIRE_AVAILABLE, MISFIRE_INCOMPLETE);
709 
CommonIgnitionMonitors(int bitmask)710         CommonIgnitionMonitors(int bitmask) {
711             components = COMPONENTS_DECODER.fromValue(bitmask);
712             fuelSystem = FUEL_SYSTEM_DECODER.fromValue(bitmask);
713             misfire = MISFIRE_DECODER.fromValue(bitmask);
714         }
715 
716         /**
717          * Returns data about ignition monitors specific to spark vehicles, if this
718          * object represents ignition monitors for a spark vehicle.
719          * Returns null otherwise.
720          */
asSparkIgnitionMonitors()721         public @Nullable SparkIgnitionMonitors asSparkIgnitionMonitors() {
722             if (this instanceof SparkIgnitionMonitors) return (SparkIgnitionMonitors) this;
723             return null;
724         }
725 
726         /**
727          * Returns data about ignition monitors specific to compression vehicles, if this
728          * object represents ignition monitors for a compression vehicle.
729          * Returns null otherwise.
730          */
asCompressionIgnitionMonitors()731         public @Nullable CompressionIgnitionMonitors asCompressionIgnitionMonitors() {
732             if (this instanceof CompressionIgnitionMonitors) {
733                 return (CompressionIgnitionMonitors) this;
734             }
735             return null;
736         }
737     }
738 
739     /**
740      * Contains information about ignition monitors specific to spark vehicles.
741      */
742     public static final class SparkIgnitionMonitors extends CommonIgnitionMonitors {
743         public final IgnitionMonitor EGR;
744         public final IgnitionMonitor oxygenSensorHeater;
745         public final IgnitionMonitor oxygenSensor;
746         public final IgnitionMonitor ACRefrigerant;
747         public final IgnitionMonitor secondaryAirSystem;
748         public final IgnitionMonitor evaporativeSystem;
749         public final IgnitionMonitor heatedCatalyst;
750         public final IgnitionMonitor catalyst;
751 
752         /** @hide */
753         public static final int EGR_AVAILABLE = 0x1 << 6;
754         /** @hide */
755         public static final int EGR_INCOMPLETE = 0x1 << 7;
756 
757         /** @hide */
758         public static final int OXYGEN_SENSOR_HEATER_AVAILABLE = 0x1 << 8;
759         /** @hide */
760         public static final int OXYGEN_SENSOR_HEATER_INCOMPLETE = 0x1 << 9;
761 
762         /** @hide */
763         public static final int OXYGEN_SENSOR_AVAILABLE = 0x1 << 10;
764         /** @hide */
765         public static final int OXYGEN_SENSOR_INCOMPLETE = 0x1 << 11;
766 
767         /** @hide */
768         public static final int AC_REFRIGERANT_AVAILABLE = 0x1 << 12;
769         /** @hide */
770         public static final int AC_REFRIGERANT_INCOMPLETE = 0x1 << 13;
771 
772         /** @hide */
773         public static final int SECONDARY_AIR_SYSTEM_AVAILABLE = 0x1 << 14;
774         /** @hide */
775         public static final int SECONDARY_AIR_SYSTEM_INCOMPLETE = 0x1 << 15;
776 
777         /** @hide */
778         public static final int EVAPORATIVE_SYSTEM_AVAILABLE = 0x1 << 16;
779         /** @hide */
780         public static final int EVAPORATIVE_SYSTEM_INCOMPLETE = 0x1 << 17;
781 
782         /** @hide */
783         public static final int HEATED_CATALYST_AVAILABLE = 0x1 << 18;
784         /** @hide */
785         public static final int HEATED_CATALYST_INCOMPLETE = 0x1 << 19;
786 
787         /** @hide */
788         public static final int CATALYST_AVAILABLE = 0x1 << 20;
789         /** @hide */
790         public static final int CATALYST_INCOMPLETE = 0x1 << 21;
791 
792         static final IgnitionMonitor.Decoder EGR_DECODER =
793                 new IgnitionMonitor.Decoder(EGR_AVAILABLE, EGR_INCOMPLETE);
794 
795         static final IgnitionMonitor.Decoder OXYGEN_SENSOR_HEATER_DECODER =
796                 new IgnitionMonitor.Decoder(OXYGEN_SENSOR_HEATER_AVAILABLE,
797                         OXYGEN_SENSOR_HEATER_INCOMPLETE);
798 
799         static final IgnitionMonitor.Decoder OXYGEN_SENSOR_DECODER =
800                 new IgnitionMonitor.Decoder(OXYGEN_SENSOR_AVAILABLE, OXYGEN_SENSOR_INCOMPLETE);
801 
802         static final IgnitionMonitor.Decoder AC_REFRIGERANT_DECODER =
803                 new IgnitionMonitor.Decoder(AC_REFRIGERANT_AVAILABLE,
804                         AC_REFRIGERANT_INCOMPLETE);
805 
806         static final IgnitionMonitor.Decoder SECONDARY_AIR_SYSTEM_DECODER =
807                 new IgnitionMonitor.Decoder(SECONDARY_AIR_SYSTEM_AVAILABLE,
808                         SECONDARY_AIR_SYSTEM_INCOMPLETE);
809 
810         static final IgnitionMonitor.Decoder EVAPORATIVE_SYSTEM_DECODER =
811                 new IgnitionMonitor.Decoder(EVAPORATIVE_SYSTEM_AVAILABLE,
812                         EVAPORATIVE_SYSTEM_INCOMPLETE);
813 
814         static final IgnitionMonitor.Decoder HEATED_CATALYST_DECODER =
815                 new IgnitionMonitor.Decoder(HEATED_CATALYST_AVAILABLE,
816                         HEATED_CATALYST_INCOMPLETE);
817 
818         static final IgnitionMonitor.Decoder CATALYST_DECODER =
819                 new IgnitionMonitor.Decoder(CATALYST_AVAILABLE, CATALYST_INCOMPLETE);
820 
SparkIgnitionMonitors(int bitmask)821         SparkIgnitionMonitors(int bitmask) {
822             super(bitmask);
823             EGR = EGR_DECODER.fromValue(bitmask);
824             oxygenSensorHeater = OXYGEN_SENSOR_HEATER_DECODER.fromValue(bitmask);
825             oxygenSensor = OXYGEN_SENSOR_DECODER.fromValue(bitmask);
826             ACRefrigerant = AC_REFRIGERANT_DECODER.fromValue(bitmask);
827             secondaryAirSystem = SECONDARY_AIR_SYSTEM_DECODER.fromValue(bitmask);
828             evaporativeSystem = EVAPORATIVE_SYSTEM_DECODER.fromValue(bitmask);
829             heatedCatalyst = HEATED_CATALYST_DECODER.fromValue(bitmask);
830             catalyst = CATALYST_DECODER.fromValue(bitmask);
831         }
832     }
833 
834     /**
835      * Contains information about ignition monitors specific to compression vehicles.
836      */
837     public static final class CompressionIgnitionMonitors extends CommonIgnitionMonitors {
838         public final IgnitionMonitor EGROrVVT;
839         public final IgnitionMonitor PMFilter;
840         public final IgnitionMonitor exhaustGasSensor;
841         public final IgnitionMonitor boostPressure;
842         public final IgnitionMonitor NOxSCR;
843         public final IgnitionMonitor NMHCCatalyst;
844 
845         /** @hide */
846         public static final int EGR_OR_VVT_AVAILABLE = 0x1 << 6;
847         /** @hide */
848         public static final int EGR_OR_VVT_INCOMPLETE = 0x1 << 7;
849 
850         /** @hide */
851         public static final int PM_FILTER_AVAILABLE = 0x1 << 8;
852         /** @hide */
853         public static final int PM_FILTER_INCOMPLETE = 0x1 << 9;
854 
855         /** @hide */
856         public static final int EXHAUST_GAS_SENSOR_AVAILABLE = 0x1 << 10;
857         /** @hide */
858         public static final int EXHAUST_GAS_SENSOR_INCOMPLETE = 0x1 << 11;
859 
860         /** @hide */
861         public static final int BOOST_PRESSURE_AVAILABLE = 0x1 << 12;
862         /** @hide */
863         public static final int BOOST_PRESSURE_INCOMPLETE = 0x1 << 13;
864 
865         /** @hide */
866         public static final int NOx_SCR_AVAILABLE = 0x1 << 14;
867         /** @hide */
868         public static final int NOx_SCR_INCOMPLETE = 0x1 << 15;
869 
870         /** @hide */
871         public static final int NMHC_CATALYST_AVAILABLE = 0x1 << 16;
872         /** @hide */
873         public static final int NMHC_CATALYST_INCOMPLETE = 0x1 << 17;
874 
875         static final IgnitionMonitor.Decoder EGR_OR_VVT_DECODER =
876                 new IgnitionMonitor.Decoder(EGR_OR_VVT_AVAILABLE, EGR_OR_VVT_INCOMPLETE);
877 
878         static final IgnitionMonitor.Decoder PM_FILTER_DECODER =
879                 new IgnitionMonitor.Decoder(PM_FILTER_AVAILABLE, PM_FILTER_INCOMPLETE);
880 
881         static final IgnitionMonitor.Decoder EXHAUST_GAS_SENSOR_DECODER =
882                 new IgnitionMonitor.Decoder(EXHAUST_GAS_SENSOR_AVAILABLE,
883                         EXHAUST_GAS_SENSOR_INCOMPLETE);
884 
885         static final IgnitionMonitor.Decoder BOOST_PRESSURE_DECODER =
886                 new IgnitionMonitor.Decoder(BOOST_PRESSURE_AVAILABLE,
887                         BOOST_PRESSURE_INCOMPLETE);
888 
889         static final IgnitionMonitor.Decoder NOx_SCR_DECODER =
890                 new IgnitionMonitor.Decoder(NOx_SCR_AVAILABLE, NOx_SCR_INCOMPLETE);
891 
892         static final IgnitionMonitor.Decoder NMHC_CATALYST_DECODER =
893                 new IgnitionMonitor.Decoder(NMHC_CATALYST_AVAILABLE, NMHC_CATALYST_INCOMPLETE);
894 
CompressionIgnitionMonitors(int bitmask)895         CompressionIgnitionMonitors(int bitmask) {
896             super(bitmask);
897             EGROrVVT = EGR_OR_VVT_DECODER.fromValue(bitmask);
898             PMFilter = PM_FILTER_DECODER.fromValue(bitmask);
899             exhaustGasSensor = EXHAUST_GAS_SENSOR_DECODER.fromValue(bitmask);
900             boostPressure = BOOST_PRESSURE_DECODER.fromValue(bitmask);
901             NOxSCR = NOx_SCR_DECODER.fromValue(bitmask);
902             NMHCCatalyst = NMHC_CATALYST_DECODER.fromValue(bitmask);
903         }
904     }
905 
906     /**
907      * Returns the state of the fuel system, if present in this frame.
908      * Returns null otherwise.
909      */
getFuelSystemStatus()910     public @Nullable @FuelSystemStatus.Status Integer getFuelSystemStatus() {
911         return getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.FUEL_SYSTEM_STATUS);
912     }
913 
914     /**
915      * Returns the state of the secondary air system, if present in this frame.
916      * Returns null otherwise.
917      */
getSecondaryAirStatus()918     public @Nullable @SecondaryAirStatus.Status Integer getSecondaryAirStatus() {
919         return getSystemIntegerSensor(
920             android.car.diagnostic.IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS);
921     }
922 
923     /**
924      * Returns data about the ignition monitors, if present in this frame.
925      * Returns null otherwise.
926      */
getIgnitionMonitors()927     public @Nullable CommonIgnitionMonitors getIgnitionMonitors() {
928         Integer ignitionMonitorsType =
929                 getSystemIntegerSensor(
930                     android.car.diagnostic.IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED);
931         Integer ignitionMonitorsBitmask =
932                 getSystemIntegerSensor(
933                     android.car.diagnostic.IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS);
934         if (null == ignitionMonitorsType) return null;
935         if (null == ignitionMonitorsBitmask) return null;
936         switch (ignitionMonitorsType) {
937             case 0:
938                 return new SparkIgnitionMonitors(ignitionMonitorsBitmask);
939             case 1:
940                 return new CompressionIgnitionMonitors(ignitionMonitorsBitmask);
941             default:
942                 return null;
943         }
944     }
945 
946     /**
947      * Returns the fuel type, if present in this frame.
948      * Returns null otherwise.
949      */
getFuelType()950     public @Nullable @FuelType.Type Integer getFuelType() {
951         return getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.FUEL_TYPE);
952     }
953 }
954