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 CarDiagnosticEvent(Parcel in)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 describeContents()88 public int describeContents() { 89 return 0; 90 } 91 92 @Override writeToParcel(Parcel dest, int flags)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 */ writeToJson(JsonWriter jsonWriter)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 CarDiagnosticEvent( int frameType, long timestamp, SparseArray<Float> floatValues, SparseIntArray intValues, String dtc)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 Builder(int type)202 private Builder(int type) { 203 mType = type; 204 } 205 206 /** Returns a new Builder for a live frame */ newLiveFrameBuilder()207 public static Builder newLiveFrameBuilder() { 208 return new Builder(CarDiagnosticManager.FRAME_TYPE_LIVE); 209 } 210 211 /** Returns a new Builder for a freeze frame */ newFreezeFrameBuilder()212 public static Builder newFreezeFrameBuilder() { 213 return new Builder(CarDiagnosticManager.FRAME_TYPE_FREEZE); 214 } 215 216 /** Sets the timestamp for the frame being built */ atTimestamp(long timestamp)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 */ withIntValue(int key, int value)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 */ withFloatValue(int key, float value)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 */ withDtc(String dtc)235 public Builder withDtc(String dtc) { 236 mDtc = dtc; 237 return this; 238 } 239 240 /** Builds and returns the CarDiagnosticEvent */ build()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 */ withVendorSensorsRemoved()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 */ isLiveFrame()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 */ isFreezeFrame()275 public boolean isFreezeFrame() { 276 return CarDiagnosticManager.FRAME_TYPE_FREEZE == frameType; 277 } 278 279 /** @hide */ isEmptyFrame()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 */ checkLiveFrame()288 public CarDiagnosticEvent checkLiveFrame() { 289 if (!isLiveFrame()) throw new IllegalStateException("frame is not a live frame"); 290 return this; 291 } 292 293 /** @hide */ checkFreezeFrame()294 public CarDiagnosticEvent checkFreezeFrame() { 295 if (!isFreezeFrame()) throw new IllegalStateException("frame is not a freeze frame"); 296 return this; 297 } 298 299 /** @hide */ isEarlierThan(CarDiagnosticEvent otherEvent)300 public boolean isEarlierThan(CarDiagnosticEvent otherEvent) { 301 otherEvent = Objects.requireNonNull(otherEvent); 302 return (timestamp < otherEvent.timestamp); 303 } 304 305 @Override equals(Object otherObject)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 hashCode()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 toString()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 */ getSystemIntegerSensor( @ndroid.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor, int defaultValue)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 */ getSystemFloatSensor( @ndroid.car.diagnostic.FloatSensorIndex.SensorIndex int sensor, float defaultValue)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 */ getVendorIntegerSensor(int sensor, int defaultValue)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 */ getVendorFloatSensor(int sensor, float defaultValue)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 */ getSystemIntegerSensor( @ndroid.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor)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 */ getSystemFloatSensor( @ndroid.car.diagnostic.FloatSensorIndex.SensorIndex int sensor)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 */ getVendorIntegerSensor(int sensor)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 */ getVendorFloatSensor(int sensor)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 { 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 { 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 { 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 IgnitionMonitor(boolean available, boolean incomplete)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 Decoder(int availableBitmask, int incompleteBitmask)600 Decoder(int availableBitmask, int incompleteBitmask) { 601 mAvailableBitmask = availableBitmask; 602 mIncompleteBitmask = incompleteBitmask; 603 } 604 fromValue(int value)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 CommonIgnitionMonitors(int bitmask)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 */ asSparkIgnitionMonitors()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 */ asCompressionIgnitionMonitors()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 SparkIgnitionMonitors(int bitmask)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 CompressionIgnitionMonitors(int bitmask)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 */ getFuelSystemStatus()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 */ getSecondaryAirStatus()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 */ getIgnitionMonitors()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 */ getFuelType()882 public @Nullable @FuelType.Type Integer getFuelType() { 883 return getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.FUEL_TYPE); 884 } 885 } 886