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