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