Home | History | Annotate | Download | only in hardware
      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.hardware;
     18 
     19 import android.annotation.IntDef;
     20 import android.annotation.Nullable;
     21 import android.car.annotation.FutureFeature;
     22 import android.os.Parcel;
     23 import android.os.Parcelable;
     24 import android.util.SparseArray;
     25 import android.util.SparseIntArray;
     26 import java.lang.annotation.Retention;
     27 import java.lang.annotation.RetentionPolicy;
     28 import java.util.Objects;
     29 
     30 /**
     31  * A CarDiagnosticEvent object corresponds to a single diagnostic event frame coming from the car.
     32  *
     33  * @hide
     34  */
     35 @FutureFeature
     36 public class CarDiagnosticEvent implements Parcelable {
     37     /** Whether this frame represents a live or a freeze frame */
     38     public final int frameType;
     39 
     40     /**
     41      * When this data was acquired in car or received from car. It is elapsed real-time of data
     42      * reception from car in nanoseconds since system boot.
     43      */
     44     public final long timestamp;
     45 
     46     /**
     47      * Sparse array that contains the mapping of OBD2 diagnostic properties to their values for
     48      * integer valued properties
     49      */
     50     private final SparseIntArray intValues;
     51 
     52     /**
     53      * Sparse array that contains the mapping of OBD2 diagnostic properties to their values for
     54      * float valued properties
     55      */
     56     private final SparseArray<Float> floatValues;
     57 
     58     /**
     59      * Diagnostic Troubleshooting Code (DTC) that was detected and caused this frame to be stored
     60      * (if a freeze frame). Always null for a live frame.
     61      */
     62     public final String dtc;
     63 
     64     public CarDiagnosticEvent(Parcel in) {
     65         frameType = in.readInt();
     66         timestamp = in.readLong();
     67         int len = in.readInt();
     68         floatValues = new SparseArray<>(len);
     69         for (int i = 0; i < len; ++i) {
     70             int key = in.readInt();
     71             float value = in.readFloat();
     72             floatValues.put(key, value);
     73         }
     74         len = in.readInt();
     75         intValues = new SparseIntArray(len);
     76         for (int i = 0; i < len; ++i) {
     77             int key = in.readInt();
     78             int value = in.readInt();
     79             intValues.put(key, value);
     80         }
     81         dtc = (String)in.readValue(String.class.getClassLoader());
     82         // version 1 up to here
     83     }
     84 
     85     @Override
     86     public int describeContents() {
     87         return 0;
     88     }
     89 
     90     @Override
     91     public void writeToParcel(Parcel dest, int flags) {
     92         dest.writeInt(frameType);
     93         dest.writeLong(timestamp);
     94         dest.writeInt(floatValues.size());
     95         for (int i = 0; i < floatValues.size(); ++i) {
     96             int key = floatValues.keyAt(i);
     97             dest.writeInt(key);
     98             dest.writeFloat(floatValues.get(key));
     99         }
    100         dest.writeInt(intValues.size());
    101         for (int i = 0; i < intValues.size(); ++i) {
    102             int key = intValues.keyAt(i);
    103             dest.writeInt(key);
    104             dest.writeInt(intValues.get(key));
    105         }
    106         dest.writeValue(dtc);
    107     }
    108 
    109     public static final Parcelable.Creator<CarDiagnosticEvent> CREATOR =
    110             new Parcelable.Creator<CarDiagnosticEvent>() {
    111                 public CarDiagnosticEvent createFromParcel(Parcel in) {
    112                     return new CarDiagnosticEvent(in);
    113                 }
    114 
    115                 public CarDiagnosticEvent[] newArray(int size) {
    116                     return new CarDiagnosticEvent[size];
    117                 }
    118             };
    119 
    120     private CarDiagnosticEvent(
    121             int frameType,
    122             long timestamp,
    123             SparseArray<Float> floatValues,
    124             SparseIntArray intValues,
    125             String dtc) {
    126         this.frameType = frameType;
    127         this.timestamp = timestamp;
    128         this.floatValues = floatValues;
    129         this.intValues = intValues;
    130         this.dtc = dtc;
    131     }
    132 
    133     public static class Builder {
    134         private int mType = CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE;
    135         private long mTimestamp = 0;
    136         private SparseArray<Float> mFloatValues = new SparseArray<>();
    137         private SparseIntArray mIntValues = new SparseIntArray();
    138         private String mDtc = null;
    139 
    140         private Builder(int type) {
    141             mType = type;
    142         }
    143 
    144         public static Builder newLiveFrameBuilder() {
    145             return new Builder(CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE);
    146         }
    147 
    148         public static Builder newFreezeFrameBuilder() {
    149             return new Builder(CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE);
    150         }
    151 
    152         public Builder atTimestamp(long timestamp) {
    153             mTimestamp = timestamp;
    154             return this;
    155         }
    156 
    157         public Builder withIntValue(int key, int value) {
    158             mIntValues.put(key, value);
    159             return this;
    160         }
    161 
    162         public Builder withFloatValue(int key, float value) {
    163             mFloatValues.put(key, value);
    164             return this;
    165         }
    166 
    167         public Builder withDTC(String dtc) {
    168             mDtc = dtc;
    169             return this;
    170         }
    171 
    172         public CarDiagnosticEvent build() {
    173             return new CarDiagnosticEvent(mType, mTimestamp, mFloatValues, mIntValues, mDtc);
    174         }
    175     }
    176 
    177     /**
    178      * Returns a copy of this CarDiagnosticEvent with all vendor-specific sensors removed.
    179      * @hide
    180      */
    181     public CarDiagnosticEvent withVendorSensorsRemoved() {
    182         SparseIntArray newIntValues = intValues.clone();
    183         SparseArray<Float> newFloatValues = floatValues.clone();
    184         for(int i = 0; i < intValues.size(); ++i) {
    185             int key = intValues.keyAt(i);
    186             if (key >= CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.LAST_SYSTEM) {
    187                 newIntValues.delete(key);
    188             }
    189         }
    190         for(int i = 0; i < floatValues.size(); ++i) {
    191             int key = floatValues.keyAt(i);
    192             if (key >= CarDiagnosticSensorIndices.Obd2FloatSensorIndex.LAST_SYSTEM) {
    193                 newFloatValues.delete(key);
    194             }
    195         }
    196         return new CarDiagnosticEvent(frameType, timestamp, newFloatValues, newIntValues, dtc);
    197     }
    198 
    199     public boolean isLiveFrame() {
    200         return CarDiagnosticManager.FRAME_TYPE_FLAG_LIVE == frameType;
    201     }
    202 
    203     public boolean isFreezeFrame() {
    204         return CarDiagnosticManager.FRAME_TYPE_FLAG_FREEZE == frameType;
    205     }
    206 
    207     public boolean isEmptyFrame() {
    208         boolean empty = (0 == intValues.size());
    209         empty &= (0 == floatValues.size());
    210         if (isFreezeFrame()) empty &= dtc.isEmpty();
    211         return empty;
    212     }
    213 
    214     /** @hide */
    215     public CarDiagnosticEvent checkLiveFrame() {
    216         if (!isLiveFrame()) throw new IllegalStateException("frame is not a live frame");
    217         return this;
    218     }
    219 
    220     /** @hide */
    221     public CarDiagnosticEvent checkFreezeFrame() {
    222         if (!isFreezeFrame()) throw new IllegalStateException("frame is not a freeze frame");
    223         return this;
    224     }
    225 
    226     /** @hide */
    227     public boolean isEarlierThan(CarDiagnosticEvent otherEvent) {
    228         otherEvent = Objects.requireNonNull(otherEvent);
    229         return (timestamp < otherEvent.timestamp);
    230     }
    231 
    232     @Override
    233     public String toString() {
    234         return String.format(
    235                 "%s diagnostic frame {\n"
    236                         + "\ttimestamp: %d, "
    237                         + "DTC: %s\n"
    238                         + "\tintValues: %s\n"
    239                         + "\tfloatValues: %s\n}",
    240                 isLiveFrame() ? "live" : "freeze",
    241                 timestamp,
    242                 dtc,
    243                 intValues.toString(),
    244                 floatValues.toString());
    245     }
    246 
    247     public int getSystemIntegerSensor(@CarDiagnosticSensorIndices.IntegerSensorIndex int sensor, int defaultValue) {
    248         return intValues.get(sensor, defaultValue);
    249     }
    250 
    251     public float getSystemFloatSensor(@CarDiagnosticSensorIndices.FloatSensorIndex int sensor, float defaultValue) {
    252         return floatValues.get(sensor, defaultValue);
    253     }
    254 
    255     public int getVendorIntegerSensor(int sensor, int defaultValue) {
    256         return intValues.get(sensor, defaultValue);
    257     }
    258 
    259     public float getVendorFloatSensor(int sensor, float defaultValue) {
    260         return floatValues.get(sensor, defaultValue);
    261     }
    262 
    263     public @Nullable Integer getSystemIntegerSensor(@CarDiagnosticSensorIndices.IntegerSensorIndex int sensor) {
    264         int index = intValues.indexOfKey(sensor);
    265         if(index < 0) return null;
    266         return intValues.valueAt(index);
    267     }
    268 
    269     public @Nullable Float getSystemFloatSensor(@CarDiagnosticSensorIndices.FloatSensorIndex int sensor) {
    270         int index = floatValues.indexOfKey(sensor);
    271         if(index < 0) return null;
    272         return floatValues.valueAt(index);
    273     }
    274 
    275     public @Nullable Integer getVendorIntegerSensor(int sensor) {
    276         int index = intValues.indexOfKey(sensor);
    277         if(index < 0) return null;
    278         return intValues.valueAt(index);
    279     }
    280 
    281     public @Nullable Float getVendorFloatSensor(int sensor) {
    282         int index = floatValues.indexOfKey(sensor);
    283         if(index < 0) return null;
    284         return floatValues.valueAt(index);
    285     }
    286 
    287     /**
    288      * Represents possible states of the fuel system;
    289      * see {@link CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#FUEL_SYSTEM_STATUS}
    290      */
    291     public static final class FuelSystemStatus {
    292         private FuelSystemStatus() {}
    293 
    294         public static final int OPEN_INSUFFICIENT_ENGINE_TEMPERATURE = 1;
    295         public static final int CLOSED_LOOP = 2;
    296         public static final int OPEN_ENGINE_LOAD_OR_DECELERATION = 4;
    297         public static final int OPEN_SYSTEM_FAILURE = 8;
    298         public static final int CLOSED_LOOP_BUT_FEEDBACK_FAULT = 16;
    299 
    300         @Retention(RetentionPolicy.SOURCE)
    301         @IntDef({
    302             OPEN_INSUFFICIENT_ENGINE_TEMPERATURE,
    303             CLOSED_LOOP,
    304             OPEN_ENGINE_LOAD_OR_DECELERATION,
    305             OPEN_SYSTEM_FAILURE,
    306             CLOSED_LOOP_BUT_FEEDBACK_FAULT
    307         })
    308         public @interface Status {}
    309     }
    310 
    311     /**
    312      * Represents possible states of the secondary air system;
    313      * see {@link CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#COMMANDED_SECONDARY_AIR_STATUS}
    314      */
    315     public static final class SecondaryAirStatus {
    316         private SecondaryAirStatus() {}
    317 
    318         public static final int UPSTREAM = 1;
    319         public static final int DOWNSTREAM_OF_CATALYCIC_CONVERTER = 2;
    320         public static final int FROM_OUTSIDE_OR_OFF = 4;
    321         public static final int PUMP_ON_FOR_DIAGNOSTICS = 8;
    322 
    323         @Retention(RetentionPolicy.SOURCE)
    324         @IntDef({
    325             UPSTREAM,
    326             DOWNSTREAM_OF_CATALYCIC_CONVERTER,
    327             FROM_OUTSIDE_OR_OFF,
    328             PUMP_ON_FOR_DIAGNOSTICS
    329         })
    330         public @interface Status {}
    331     }
    332 
    333     /**
    334      * Represents possible types of fuel;
    335      * see {@link CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#FUEL_TYPE}
    336      */
    337     public static final class FuelType {
    338         private FuelType() {}
    339 
    340         public static final int NOT_AVAILABLE = 0;
    341         public static final int GASOLINE = 1;
    342         public static final int METHANOL = 2;
    343         public static final int ETHANOL = 3;
    344         public static final int DIESEL = 4;
    345         public static final int LPG = 5;
    346         public static final int CNG = 6;
    347         public static final int PROPANE = 7;
    348         public static final int ELECTRIC = 8;
    349         public static final int BIFUEL_RUNNING_GASOLINE = 9;
    350         public static final int BIFUEL_RUNNING_METHANOL = 10;
    351         public static final int BIFUEL_RUNNING_ETHANOL = 11;
    352         public static final int BIFUEL_RUNNING_LPG = 12;
    353         public static final int BIFUEL_RUNNING_CNG = 13;
    354         public static final int BIFUEL_RUNNING_PROPANE = 14;
    355         public static final int BIFUEL_RUNNING_ELECTRIC = 15;
    356         public static final int BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION = 16;
    357         public static final int HYBRID_GASOLINE = 17;
    358         public static final int HYBRID_ETHANOL = 18;
    359         public static final int HYBRID_DIESEL = 19;
    360         public static final int HYBRID_ELECTRIC = 20;
    361         public static final int HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION = 21;
    362         public static final int HYBRID_REGENERATIVE = 22;
    363         public static final int BIFUEL_RUNNING_DIESEL = 23;
    364 
    365         @Retention(RetentionPolicy.SOURCE)
    366         @IntDef({
    367             NOT_AVAILABLE,
    368             GASOLINE,
    369             METHANOL,
    370             ETHANOL,
    371             DIESEL,
    372             LPG,
    373             CNG,
    374             PROPANE,
    375             ELECTRIC,
    376             BIFUEL_RUNNING_GASOLINE,
    377             BIFUEL_RUNNING_METHANOL,
    378             BIFUEL_RUNNING_ETHANOL,
    379             BIFUEL_RUNNING_LPG,
    380             BIFUEL_RUNNING_CNG,
    381             BIFUEL_RUNNING_PROPANE,
    382             BIFUEL_RUNNING_ELECTRIC,
    383             BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION,
    384             HYBRID_GASOLINE,
    385             HYBRID_ETHANOL,
    386             HYBRID_DIESEL,
    387             HYBRID_ELECTRIC,
    388             HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION,
    389             HYBRID_REGENERATIVE,
    390             BIFUEL_RUNNING_DIESEL
    391         })
    392         public @interface Type {}
    393     }
    394 
    395     /**
    396      * Represents possible states of the ignition monitors on the vehicle;
    397      * see {@link CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#IGNITION_MONITORS_SUPPORTED}
    398      * see {@link CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#IGNITION_SPECIFIC_MONITORS}
    399      */
    400     public static final class IgnitionMonitors {
    401         public static final class IgnitionMonitor {
    402             public final boolean available;
    403             public final boolean incomplete;
    404 
    405             IgnitionMonitor(boolean available, boolean incomplete) {
    406                 this.available = available;
    407                 this.incomplete = incomplete;
    408             }
    409 
    410             public static final class Builder {
    411                 private int mAvailableBitmask;
    412                 private int mIncompleteBitmask;
    413 
    414                 Builder() {
    415                     mAvailableBitmask = 0;
    416                     mIncompleteBitmask = 0;
    417                 }
    418 
    419                 public Builder withAvailableBitmask(int bitmask) {
    420                     mAvailableBitmask = bitmask;
    421                     return this;
    422                 }
    423 
    424                 public Builder withIncompleteBitmask(int bitmask) {
    425                     mIncompleteBitmask = bitmask;
    426                     return this;
    427                 }
    428 
    429                 public IgnitionMonitor buildForValue(int value) {
    430                     boolean available = (0 != (value & mAvailableBitmask));
    431                     boolean incomplete = (0 != (value & mIncompleteBitmask));
    432 
    433                     return new IgnitionMonitor(available, incomplete);
    434                 }
    435             }
    436         }
    437 
    438         public static class CommonIgnitionMonitors {
    439             public final IgnitionMonitor components;
    440             public final IgnitionMonitor fuelSystem;
    441             public final IgnitionMonitor misfire;
    442 
    443             static final int COMPONENTS_AVAILABLE = 0x1 << 0;
    444             static final int COMPONENTS_INCOMPLETE = 0x1 << 1;
    445 
    446             static final int FUEL_SYSTEM_AVAILABLE = 0x1 << 2;
    447             static final int FUEL_SYSTEM_INCOMPLETE = 0x1 << 3;
    448 
    449             static final int MISFIRE_AVAILABLE = 0x1 << 4;
    450             static final int MISFIRE_INCOMPLETE = 0x1 << 5;
    451 
    452             static final IgnitionMonitor.Builder COMPONENTS_BUILDER =
    453                     new IgnitionMonitor.Builder()
    454                             .withAvailableBitmask(COMPONENTS_AVAILABLE)
    455                             .withIncompleteBitmask(COMPONENTS_INCOMPLETE);
    456 
    457             static final IgnitionMonitor.Builder FUEL_SYSTEM_BUILDER =
    458                     new IgnitionMonitor.Builder()
    459                             .withAvailableBitmask(FUEL_SYSTEM_AVAILABLE)
    460                             .withIncompleteBitmask(FUEL_SYSTEM_INCOMPLETE);
    461 
    462             static final IgnitionMonitor.Builder MISFIRE_BUILDER =
    463                     new IgnitionMonitor.Builder()
    464                             .withAvailableBitmask(MISFIRE_AVAILABLE)
    465                             .withIncompleteBitmask(MISFIRE_INCOMPLETE);
    466 
    467             CommonIgnitionMonitors(int bitmask) {
    468                 components = COMPONENTS_BUILDER.buildForValue(bitmask);
    469                 fuelSystem = FUEL_SYSTEM_BUILDER.buildForValue(bitmask);
    470                 misfire = MISFIRE_BUILDER.buildForValue(bitmask);
    471             }
    472 
    473             public @Nullable SparkIgnitionMonitors asSparkIgnitionMonitors() {
    474                 if (this instanceof SparkIgnitionMonitors)
    475                     return (SparkIgnitionMonitors)this;
    476                 return null;
    477             }
    478 
    479             public @Nullable CompressionIgnitionMonitors asCompressionIgnitionMonitors() {
    480                 if (this instanceof CompressionIgnitionMonitors)
    481                     return (CompressionIgnitionMonitors)this;
    482                 return null;
    483             }
    484         }
    485 
    486         public static final class SparkIgnitionMonitors extends CommonIgnitionMonitors {
    487             public final IgnitionMonitor EGR;
    488             public final IgnitionMonitor oxygenSensorHeater;
    489             public final IgnitionMonitor oxygenSensor;
    490             public final IgnitionMonitor ACRefrigerant;
    491             public final IgnitionMonitor secondaryAirSystem;
    492             public final IgnitionMonitor evaporativeSystem;
    493             public final IgnitionMonitor heatedCatalyst;
    494             public final IgnitionMonitor catalyst;
    495 
    496             static final int EGR_AVAILABLE = 0x1 << 6;
    497             static final int EGR_INCOMPLETE = 0x1 << 7;
    498 
    499             static final int OXYGEN_SENSOR_HEATER_AVAILABLE = 0x1 << 8;
    500             static final int OXYGEN_SENSOR_HEATER_INCOMPLETE = 0x1 << 9;
    501 
    502             static final int OXYGEN_SENSOR_AVAILABLE = 0x1 << 10;
    503             static final int OXYGEN_SENSOR_INCOMPLETE = 0x1 << 11;
    504 
    505             static final int AC_REFRIGERANT_AVAILABLE = 0x1 << 12;
    506             static final int AC_REFRIGERANT_INCOMPLETE = 0x1 << 13;
    507 
    508             static final int SECONDARY_AIR_SYSTEM_AVAILABLE = 0x1 << 14;
    509             static final int SECONDARY_AIR_SYSTEM_INCOMPLETE = 0x1 << 15;
    510 
    511             static final int EVAPORATIVE_SYSTEM_AVAILABLE = 0x1 << 16;
    512             static final int EVAPORATIVE_SYSTEM_INCOMPLETE = 0x1 << 17;
    513 
    514             static final int HEATED_CATALYST_AVAILABLE = 0x1 << 18;
    515             static final int HEATED_CATALYST_INCOMPLETE = 0x1 << 19;
    516 
    517             static final int CATALYST_AVAILABLE = 0x1 << 20;
    518             static final int CATALYST_INCOMPLETE = 0x1 << 21;
    519 
    520             static final IgnitionMonitor.Builder EGR_BUILDER =
    521                 new IgnitionMonitor.Builder()
    522                     .withAvailableBitmask(EGR_AVAILABLE)
    523                     .withIncompleteBitmask(EGR_INCOMPLETE);
    524 
    525             static final IgnitionMonitor.Builder OXYGEN_SENSOR_HEATER_BUILDER =
    526                 new IgnitionMonitor.Builder()
    527                     .withAvailableBitmask(OXYGEN_SENSOR_HEATER_AVAILABLE)
    528                     .withIncompleteBitmask(OXYGEN_SENSOR_HEATER_INCOMPLETE);
    529 
    530             static final IgnitionMonitor.Builder OXYGEN_SENSOR_BUILDER =
    531                 new IgnitionMonitor.Builder()
    532                     .withAvailableBitmask(OXYGEN_SENSOR_AVAILABLE)
    533                     .withIncompleteBitmask(OXYGEN_SENSOR_INCOMPLETE);
    534 
    535             static final IgnitionMonitor.Builder AC_REFRIGERANT_BUILDER =
    536                 new IgnitionMonitor.Builder()
    537                     .withAvailableBitmask(AC_REFRIGERANT_AVAILABLE)
    538                     .withIncompleteBitmask(AC_REFRIGERANT_INCOMPLETE);
    539 
    540             static final IgnitionMonitor.Builder SECONDARY_AIR_SYSTEM_BUILDER =
    541                 new IgnitionMonitor.Builder()
    542                     .withAvailableBitmask(SECONDARY_AIR_SYSTEM_AVAILABLE)
    543                     .withIncompleteBitmask(SECONDARY_AIR_SYSTEM_INCOMPLETE);
    544 
    545             static final IgnitionMonitor.Builder EVAPORATIVE_SYSTEM_BUILDER =
    546                 new IgnitionMonitor.Builder()
    547                     .withAvailableBitmask(EVAPORATIVE_SYSTEM_AVAILABLE)
    548                     .withIncompleteBitmask(EVAPORATIVE_SYSTEM_INCOMPLETE);
    549 
    550             static final IgnitionMonitor.Builder HEATED_CATALYST_BUILDER =
    551                 new IgnitionMonitor.Builder()
    552                     .withAvailableBitmask(HEATED_CATALYST_AVAILABLE)
    553                     .withIncompleteBitmask(HEATED_CATALYST_INCOMPLETE);
    554 
    555             static final IgnitionMonitor.Builder CATALYST_BUILDER =
    556                 new IgnitionMonitor.Builder()
    557                     .withAvailableBitmask(CATALYST_AVAILABLE)
    558                     .withIncompleteBitmask(CATALYST_INCOMPLETE);
    559 
    560             SparkIgnitionMonitors(int bitmask) {
    561                 super(bitmask);
    562                 EGR = EGR_BUILDER.buildForValue(bitmask);
    563                 oxygenSensorHeater = OXYGEN_SENSOR_HEATER_BUILDER.buildForValue(bitmask);
    564                 oxygenSensor = OXYGEN_SENSOR_BUILDER.buildForValue(bitmask);
    565                 ACRefrigerant = AC_REFRIGERANT_BUILDER.buildForValue(bitmask);
    566                 secondaryAirSystem = SECONDARY_AIR_SYSTEM_BUILDER.buildForValue(bitmask);
    567                 evaporativeSystem = EVAPORATIVE_SYSTEM_BUILDER.buildForValue(bitmask);
    568                 heatedCatalyst = HEATED_CATALYST_BUILDER.buildForValue(bitmask);
    569                 catalyst = CATALYST_BUILDER.buildForValue(bitmask);
    570             }
    571         }
    572 
    573         public static final class CompressionIgnitionMonitors extends CommonIgnitionMonitors {
    574             public final IgnitionMonitor EGROrVVT;
    575             public final IgnitionMonitor PMFilter;
    576             public final IgnitionMonitor exhaustGasSensor;
    577             public final IgnitionMonitor boostPressure;
    578             public final IgnitionMonitor NOxSCR;
    579             public final IgnitionMonitor NMHCCatalyst;
    580 
    581             static final int EGR_OR_VVT_AVAILABLE = 0x1 << 6;
    582             static final int EGR_OR_VVT_INCOMPLETE = 0x1 << 7;
    583 
    584             static final int PM_FILTER_AVAILABLE = 0x1 << 8;
    585             static final int PM_FILTER_INCOMPLETE = 0x1 << 9;
    586 
    587             static final int EXHAUST_GAS_SENSOR_AVAILABLE = 0x1 << 10;
    588             static final int EXHAUST_GAS_SENSOR_INCOMPLETE = 0x1 << 11;
    589 
    590             static final int BOOST_PRESSURE_AVAILABLE = 0x1 << 12;
    591             static final int BOOST_PRESSURE_INCOMPLETE = 0x1 << 13;
    592 
    593             static final int NOx_SCR_AVAILABLE = 0x1 << 14;
    594             static final int NOx_SCR_INCOMPLETE = 0x1 << 15;
    595 
    596             static final int NMHC_CATALYST_AVAILABLE = 0x1 << 16;
    597             static final int NMHC_CATALYST_INCOMPLETE = 0x1 << 17;
    598 
    599             static final IgnitionMonitor.Builder EGR_OR_VVT_BUILDER =
    600                 new IgnitionMonitor.Builder()
    601                     .withAvailableBitmask(EGR_OR_VVT_AVAILABLE)
    602                     .withIncompleteBitmask(EGR_OR_VVT_INCOMPLETE);
    603 
    604             static final IgnitionMonitor.Builder PM_FILTER_BUILDER =
    605                 new IgnitionMonitor.Builder()
    606                     .withAvailableBitmask(PM_FILTER_AVAILABLE)
    607                     .withIncompleteBitmask(PM_FILTER_INCOMPLETE);
    608 
    609             static final IgnitionMonitor.Builder EXHAUST_GAS_SENSOR_BUILDER =
    610                 new IgnitionMonitor.Builder()
    611                     .withAvailableBitmask(EXHAUST_GAS_SENSOR_AVAILABLE)
    612                     .withIncompleteBitmask(EXHAUST_GAS_SENSOR_INCOMPLETE);
    613 
    614             static final IgnitionMonitor.Builder BOOST_PRESSURE_BUILDER =
    615                 new IgnitionMonitor.Builder()
    616                     .withAvailableBitmask(BOOST_PRESSURE_AVAILABLE)
    617                     .withIncompleteBitmask(BOOST_PRESSURE_INCOMPLETE);
    618 
    619             static final IgnitionMonitor.Builder NOx_SCR_BUILDER =
    620                 new IgnitionMonitor.Builder()
    621                     .withAvailableBitmask(NOx_SCR_AVAILABLE)
    622                     .withIncompleteBitmask(NOx_SCR_INCOMPLETE);
    623 
    624             static final IgnitionMonitor.Builder NMHC_CATALYST_BUILDER =
    625                 new IgnitionMonitor.Builder()
    626                     .withAvailableBitmask(NMHC_CATALYST_AVAILABLE)
    627                     .withIncompleteBitmask(NMHC_CATALYST_INCOMPLETE);
    628 
    629             CompressionIgnitionMonitors(int bitmask) {
    630                 super(bitmask);
    631                 EGROrVVT = EGR_OR_VVT_BUILDER.buildForValue(bitmask);
    632                 PMFilter = PM_FILTER_BUILDER.buildForValue(bitmask);
    633                 exhaustGasSensor = EXHAUST_GAS_SENSOR_BUILDER.buildForValue(bitmask);
    634                 boostPressure = BOOST_PRESSURE_BUILDER.buildForValue(bitmask);
    635                 NOxSCR = NOx_SCR_BUILDER.buildForValue(bitmask);
    636                 NMHCCatalyst = NMHC_CATALYST_BUILDER.buildForValue(bitmask);
    637             }
    638         }
    639     }
    640 
    641     public @Nullable @FuelSystemStatus.Status Integer getFuelSystemStatus() {
    642         return getSystemIntegerSensor(CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.FUEL_SYSTEM_STATUS);
    643     }
    644 
    645     public @Nullable @SecondaryAirStatus.Status Integer getSecondaryAirStatus() {
    646         return getSystemIntegerSensor(CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS);
    647     }
    648 
    649     public @Nullable IgnitionMonitors.CommonIgnitionMonitors getIgnitionMonitors() {
    650         Integer ignitionMonitorsType = getSystemIntegerSensor(
    651             CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED);
    652         Integer ignitionMonitorsBitmask = getSystemIntegerSensor(
    653             CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS);
    654         if (null == ignitionMonitorsType) return null;
    655         if (null == ignitionMonitorsBitmask) return null;
    656         switch (ignitionMonitorsType) {
    657             case 0: return new IgnitionMonitors.SparkIgnitionMonitors(
    658                     ignitionMonitorsBitmask);
    659             case 1: return new IgnitionMonitors.CompressionIgnitionMonitors(
    660                     ignitionMonitorsBitmask);
    661             default: return null;
    662         }
    663     }
    664 
    665     public @Nullable @FuelType.Type Integer getFuelType() {
    666         return getSystemIntegerSensor(CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.FUEL_TYPE);
    667     }
    668 }
    669