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