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 mIntValues; 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> mFloatValues; 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 mFloatValues = new SparseArray<>(len); 72 for (int i = 0; i < len; ++i) { 73 int key = in.readInt(); 74 float value = in.readFloat(); 75 mFloatValues.put(key, value); 76 } 77 len = in.readInt(); 78 mIntValues = new SparseIntArray(len); 79 for (int i = 0; i < len; ++i) { 80 int key = in.readInt(); 81 int value = in.readInt(); 82 mIntValues.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(mFloatValues.size()); 98 for (int i = 0; i < mFloatValues.size(); ++i) { 99 int key = mFloatValues.keyAt(i); 100 dest.writeInt(key); 101 dest.writeFloat(mFloatValues.get(key)); 102 } 103 dest.writeInt(mIntValues.size()); 104 for (int i = 0; i < mIntValues.size(); ++i) { 105 int key = mIntValues.keyAt(i); 106 dest.writeInt(key); 107 dest.writeInt(mIntValues.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 < mIntValues.size(); ++i) { 145 jsonWriter.beginObject(); 146 jsonWriter.name("id").value(mIntValues.keyAt(i)); 147 jsonWriter.name("value").value(mIntValues.valueAt(i)); 148 jsonWriter.endObject(); 149 } 150 jsonWriter.endArray(); 151 152 jsonWriter.name("floatValues").beginArray(); 153 for (int i = 0; i < mFloatValues.size(); ++i) { 154 jsonWriter.beginObject(); 155 jsonWriter.name("id").value(mFloatValues.keyAt(i)); 156 jsonWriter.name("value").value(mFloatValues.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 mFloatValues = floatValues; 188 mIntValues = 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 = mIntValues.clone(); 312 SparseArray<Float> newFloatValues = mFloatValues.clone(); 313 for (int i = 0; i < mIntValues.size(); ++i) { 314 int key = mIntValues.keyAt(i); 315 if (key >= android.car.diagnostic.IntegerSensorIndex.LAST_SYSTEM) { 316 newIntValues.delete(key); 317 } 318 } 319 for (int i = 0; i < mFloatValues.size(); ++i) { 320 int key = mFloatValues.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 == mIntValues.size()); 341 empty &= (0 == mFloatValues.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 } 379 if (otherEvent.timestamp != timestamp) { 380 return false; 381 } 382 if (otherEvent.mIntValues.size() != mIntValues.size()) { 383 return false; 384 } 385 if (otherEvent.mFloatValues.size() != mFloatValues.size()) { 386 return false; 387 } 388 if (!Objects.equals(dtc, otherEvent.dtc)) { 389 return false; 390 } 391 for (int i = 0; i < mIntValues.size(); ++i) { 392 int key = mIntValues.keyAt(i); 393 int otherKey = otherEvent.mIntValues.keyAt(i); 394 if (key != otherKey) { 395 return false; 396 } 397 int value = mIntValues.valueAt(i); 398 int otherValue = otherEvent.mIntValues.valueAt(i); 399 if (value != otherValue) { 400 return false; 401 } 402 } 403 for (int i = 0; i < mFloatValues.size(); ++i) { 404 int key = mFloatValues.keyAt(i); 405 int otherKey = otherEvent.mFloatValues.keyAt(i); 406 if (key != otherKey) { 407 return false; 408 } 409 float value = mFloatValues.valueAt(i); 410 float otherValue = otherEvent.mFloatValues.valueAt(i); 411 if (value != otherValue) { 412 return false; 413 } 414 } 415 return true; 416 } 417 418 @Override hashCode()419 public int hashCode() { 420 Integer[] intKeys = new Integer[mIntValues.size()]; 421 Integer[] floatKeys = new Integer[mFloatValues.size()]; 422 Integer[] intValues = new Integer[intKeys.length]; 423 Float[] floatValues = new Float[floatKeys.length]; 424 for (int i = 0; i < intKeys.length; ++i) { 425 intKeys[i] = mIntValues.keyAt(i); 426 intValues[i] = mIntValues.valueAt(i); 427 } 428 for (int i = 0; i < floatKeys.length; ++i) { 429 floatKeys[i] = mFloatValues.keyAt(i); 430 floatValues[i] = mFloatValues.valueAt(i); 431 } 432 int intKeysHash = Objects.hash((Object[]) intKeys); 433 int intValuesHash = Objects.hash((Object[]) intValues); 434 int floatKeysHash = Objects.hash((Object[]) floatKeys); 435 int floatValuesHash = Objects.hash((Object[]) floatValues); 436 return Objects.hash(frameType, 437 timestamp, 438 dtc, 439 intKeysHash, 440 intValuesHash, 441 floatKeysHash, 442 floatValuesHash); 443 } 444 445 @Override toString()446 public String toString() { 447 return String.format( 448 "%s diagnostic frame {\n" 449 + "\ttimestamp: %d, " 450 + "DTC: %s\n" 451 + "\tmIntValues: %s\n" 452 + "\tmFloatValues: %s\n}", 453 isLiveFrame() ? "live" : "freeze", 454 timestamp, 455 dtc, 456 mIntValues.toString(), 457 mFloatValues.toString()); 458 } 459 460 /** 461 * Returns the value of the given integer sensor, if present in this frame. 462 * Returns defaultValue otherwise. 463 */ getSystemIntegerSensor( @ndroid.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor, int defaultValue)464 public int getSystemIntegerSensor( 465 @android.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor, int defaultValue) { 466 return mIntValues.get(sensor, defaultValue); 467 } 468 469 /** 470 * Returns the value of the given float sensor, if present in this frame. 471 * Returns defaultValue otherwise. 472 */ getSystemFloatSensor( @ndroid.car.diagnostic.FloatSensorIndex.SensorIndex int sensor, float defaultValue)473 public float getSystemFloatSensor( 474 @android.car.diagnostic.FloatSensorIndex.SensorIndex int sensor, float defaultValue) { 475 return mFloatValues.get(sensor, defaultValue); 476 } 477 478 /** 479 * Returns the value of the given integer sensor, if present in this frame. 480 * Returns defaultValue otherwise. 481 */ getVendorIntegerSensor(int sensor, int defaultValue)482 public int getVendorIntegerSensor(int sensor, int defaultValue) { 483 return mIntValues.get(sensor, defaultValue); 484 } 485 486 /** 487 * Returns the value of the given float sensor, if present in this frame. 488 * Returns defaultValue otherwise. 489 */ getVendorFloatSensor(int sensor, float defaultValue)490 public float getVendorFloatSensor(int sensor, float defaultValue) { 491 return mFloatValues.get(sensor, defaultValue); 492 } 493 494 /** 495 * Returns the value of the given integer sensor, if present in this frame. 496 * Returns null otherwise. 497 */ getSystemIntegerSensor( @ndroid.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor)498 public @Nullable Integer getSystemIntegerSensor( 499 @android.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor) { 500 int index = mIntValues.indexOfKey(sensor); 501 if (index < 0) return null; 502 return mIntValues.valueAt(index); 503 } 504 505 /** 506 * Returns the value of the given float sensor, if present in this frame. 507 * Returns null otherwise. 508 */ getSystemFloatSensor( @ndroid.car.diagnostic.FloatSensorIndex.SensorIndex int sensor)509 public @Nullable Float getSystemFloatSensor( 510 @android.car.diagnostic.FloatSensorIndex.SensorIndex int sensor) { 511 int index = mFloatValues.indexOfKey(sensor); 512 if (index < 0) return null; 513 return mFloatValues.valueAt(index); 514 } 515 516 /** 517 * Returns the value of the given integer sensor, if present in this frame. 518 * Returns null otherwise. 519 */ getVendorIntegerSensor(int sensor)520 public @Nullable Integer getVendorIntegerSensor(int sensor) { 521 int index = mIntValues.indexOfKey(sensor); 522 if (index < 0) return null; 523 return mIntValues.valueAt(index); 524 } 525 526 /** 527 * Returns the value of the given float sensor, if present in this frame. 528 * Returns null otherwise. 529 */ getVendorFloatSensor(int sensor)530 public @Nullable Float getVendorFloatSensor(int sensor) { 531 int index = mFloatValues.indexOfKey(sensor); 532 if (index < 0) return null; 533 return mFloatValues.valueAt(index); 534 } 535 536 /** 537 * Represents possible states of the fuel system; see {@link 538 * android.car.diagnostic.IntegerSensorIndex#FUEL_SYSTEM_STATUS} 539 */ 540 public static final class FuelSystemStatus { FuelSystemStatus()541 private FuelSystemStatus() {} 542 543 public static final int OPEN_INSUFFICIENT_ENGINE_TEMPERATURE = 1; 544 public static final int CLOSED_LOOP = 2; 545 public static final int OPEN_ENGINE_LOAD_OR_DECELERATION = 4; 546 public static final int OPEN_SYSTEM_FAILURE = 8; 547 public static final int CLOSED_LOOP_BUT_FEEDBACK_FAULT = 16; 548 549 @Retention(RetentionPolicy.SOURCE) 550 @IntDef({ 551 OPEN_INSUFFICIENT_ENGINE_TEMPERATURE, 552 CLOSED_LOOP, 553 OPEN_ENGINE_LOAD_OR_DECELERATION, 554 OPEN_SYSTEM_FAILURE, 555 CLOSED_LOOP_BUT_FEEDBACK_FAULT 556 }) 557 public @interface Status {} 558 } 559 560 /** 561 * Represents possible states of the secondary air system; see {@link 562 * android.car.diagnostic.IntegerSensorIndex#COMMANDED_SECONDARY_AIR_STATUS} 563 */ 564 public static final class SecondaryAirStatus { SecondaryAirStatus()565 private SecondaryAirStatus() {} 566 567 public static final int UPSTREAM = 1; 568 public static final int DOWNSTREAM_OF_CATALYCIC_CONVERTER = 2; 569 public static final int FROM_OUTSIDE_OR_OFF = 4; 570 public static final int PUMP_ON_FOR_DIAGNOSTICS = 8; 571 572 @Retention(RetentionPolicy.SOURCE) 573 @IntDef({ 574 UPSTREAM, 575 DOWNSTREAM_OF_CATALYCIC_CONVERTER, 576 FROM_OUTSIDE_OR_OFF, 577 PUMP_ON_FOR_DIAGNOSTICS 578 }) 579 public @interface Status {} 580 } 581 582 /** 583 * Represents possible types of fuel; see {@link 584 * android.car.diagnostic.IntegerSensorIndex#FUEL_TYPE} 585 */ 586 public static final class FuelType { FuelType()587 private FuelType() {} 588 589 public static final int NOT_AVAILABLE = 0; 590 public static final int GASOLINE = 1; 591 public static final int METHANOL = 2; 592 public static final int ETHANOL = 3; 593 public static final int DIESEL = 4; 594 public static final int LPG = 5; 595 public static final int CNG = 6; 596 public static final int PROPANE = 7; 597 public static final int ELECTRIC = 8; 598 public static final int BIFUEL_RUNNING_GASOLINE = 9; 599 public static final int BIFUEL_RUNNING_METHANOL = 10; 600 public static final int BIFUEL_RUNNING_ETHANOL = 11; 601 public static final int BIFUEL_RUNNING_LPG = 12; 602 public static final int BIFUEL_RUNNING_CNG = 13; 603 public static final int BIFUEL_RUNNING_PROPANE = 14; 604 public static final int BIFUEL_RUNNING_ELECTRIC = 15; 605 public static final int BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION = 16; 606 public static final int HYBRID_GASOLINE = 17; 607 public static final int HYBRID_ETHANOL = 18; 608 public static final int HYBRID_DIESEL = 19; 609 public static final int HYBRID_ELECTRIC = 20; 610 public static final int HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION = 21; 611 public static final int HYBRID_REGENERATIVE = 22; 612 public static final int BIFUEL_RUNNING_DIESEL = 23; 613 614 @Retention(RetentionPolicy.SOURCE) 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 public @interface Type {} 642 } 643 644 /** 645 * Represents the state of an ignition monitor on a vehicle. 646 */ 647 public static final class IgnitionMonitor { 648 public final boolean available; 649 public final boolean incomplete; 650 IgnitionMonitor(boolean available, boolean incomplete)651 IgnitionMonitor(boolean available, boolean incomplete) { 652 this.available = available; 653 this.incomplete = incomplete; 654 } 655 656 /** @hide */ 657 public static final class Decoder { 658 private final int mAvailableBitmask; 659 private final int mIncompleteBitmask; 660 Decoder(int availableBitmask, int incompleteBitmask)661 Decoder(int availableBitmask, int incompleteBitmask) { 662 mAvailableBitmask = availableBitmask; 663 mIncompleteBitmask = incompleteBitmask; 664 } 665 666 /** 667 * Returns the {@link IgnitionMonitor} associated with the value passed as parameter. 668 */ fromValue(int value)669 public IgnitionMonitor fromValue(int value) { 670 boolean available = (0 != (value & mAvailableBitmask)); 671 boolean incomplete = (0 != (value & mIncompleteBitmask)); 672 673 return new IgnitionMonitor(available, incomplete); 674 } 675 } 676 } 677 678 /** 679 * Contains information about ignition monitors common to all vehicle types. 680 */ 681 public static class CommonIgnitionMonitors { 682 public final IgnitionMonitor components; 683 public final IgnitionMonitor fuelSystem; 684 public final IgnitionMonitor misfire; 685 686 /** @hide */ 687 public static final int COMPONENTS_AVAILABLE = 0x1 << 0; 688 /** @hide */ 689 public static final int COMPONENTS_INCOMPLETE = 0x1 << 1; 690 691 /** @hide */ 692 public static final int FUEL_SYSTEM_AVAILABLE = 0x1 << 2; 693 /** @hide */ 694 public static final int FUEL_SYSTEM_INCOMPLETE = 0x1 << 3; 695 696 /** @hide */ 697 public static final int MISFIRE_AVAILABLE = 0x1 << 4; 698 /** @hide */ 699 public static final int MISFIRE_INCOMPLETE = 0x1 << 5; 700 701 static final IgnitionMonitor.Decoder COMPONENTS_DECODER = 702 new IgnitionMonitor.Decoder(COMPONENTS_AVAILABLE, COMPONENTS_INCOMPLETE); 703 704 static final IgnitionMonitor.Decoder FUEL_SYSTEM_DECODER = 705 new IgnitionMonitor.Decoder(FUEL_SYSTEM_AVAILABLE, FUEL_SYSTEM_INCOMPLETE); 706 707 static final IgnitionMonitor.Decoder MISFIRE_DECODER = 708 new IgnitionMonitor.Decoder(MISFIRE_AVAILABLE, MISFIRE_INCOMPLETE); 709 CommonIgnitionMonitors(int bitmask)710 CommonIgnitionMonitors(int bitmask) { 711 components = COMPONENTS_DECODER.fromValue(bitmask); 712 fuelSystem = FUEL_SYSTEM_DECODER.fromValue(bitmask); 713 misfire = MISFIRE_DECODER.fromValue(bitmask); 714 } 715 716 /** 717 * Returns data about ignition monitors specific to spark vehicles, if this 718 * object represents ignition monitors for a spark vehicle. 719 * Returns null otherwise. 720 */ asSparkIgnitionMonitors()721 public @Nullable SparkIgnitionMonitors asSparkIgnitionMonitors() { 722 if (this instanceof SparkIgnitionMonitors) return (SparkIgnitionMonitors) this; 723 return null; 724 } 725 726 /** 727 * Returns data about ignition monitors specific to compression vehicles, if this 728 * object represents ignition monitors for a compression vehicle. 729 * Returns null otherwise. 730 */ asCompressionIgnitionMonitors()731 public @Nullable CompressionIgnitionMonitors asCompressionIgnitionMonitors() { 732 if (this instanceof CompressionIgnitionMonitors) { 733 return (CompressionIgnitionMonitors) this; 734 } 735 return null; 736 } 737 } 738 739 /** 740 * Contains information about ignition monitors specific to spark vehicles. 741 */ 742 public static final class SparkIgnitionMonitors extends CommonIgnitionMonitors { 743 public final IgnitionMonitor EGR; 744 public final IgnitionMonitor oxygenSensorHeater; 745 public final IgnitionMonitor oxygenSensor; 746 public final IgnitionMonitor ACRefrigerant; 747 public final IgnitionMonitor secondaryAirSystem; 748 public final IgnitionMonitor evaporativeSystem; 749 public final IgnitionMonitor heatedCatalyst; 750 public final IgnitionMonitor catalyst; 751 752 /** @hide */ 753 public static final int EGR_AVAILABLE = 0x1 << 6; 754 /** @hide */ 755 public static final int EGR_INCOMPLETE = 0x1 << 7; 756 757 /** @hide */ 758 public static final int OXYGEN_SENSOR_HEATER_AVAILABLE = 0x1 << 8; 759 /** @hide */ 760 public static final int OXYGEN_SENSOR_HEATER_INCOMPLETE = 0x1 << 9; 761 762 /** @hide */ 763 public static final int OXYGEN_SENSOR_AVAILABLE = 0x1 << 10; 764 /** @hide */ 765 public static final int OXYGEN_SENSOR_INCOMPLETE = 0x1 << 11; 766 767 /** @hide */ 768 public static final int AC_REFRIGERANT_AVAILABLE = 0x1 << 12; 769 /** @hide */ 770 public static final int AC_REFRIGERANT_INCOMPLETE = 0x1 << 13; 771 772 /** @hide */ 773 public static final int SECONDARY_AIR_SYSTEM_AVAILABLE = 0x1 << 14; 774 /** @hide */ 775 public static final int SECONDARY_AIR_SYSTEM_INCOMPLETE = 0x1 << 15; 776 777 /** @hide */ 778 public static final int EVAPORATIVE_SYSTEM_AVAILABLE = 0x1 << 16; 779 /** @hide */ 780 public static final int EVAPORATIVE_SYSTEM_INCOMPLETE = 0x1 << 17; 781 782 /** @hide */ 783 public static final int HEATED_CATALYST_AVAILABLE = 0x1 << 18; 784 /** @hide */ 785 public static final int HEATED_CATALYST_INCOMPLETE = 0x1 << 19; 786 787 /** @hide */ 788 public static final int CATALYST_AVAILABLE = 0x1 << 20; 789 /** @hide */ 790 public static final int CATALYST_INCOMPLETE = 0x1 << 21; 791 792 static final IgnitionMonitor.Decoder EGR_DECODER = 793 new IgnitionMonitor.Decoder(EGR_AVAILABLE, EGR_INCOMPLETE); 794 795 static final IgnitionMonitor.Decoder OXYGEN_SENSOR_HEATER_DECODER = 796 new IgnitionMonitor.Decoder(OXYGEN_SENSOR_HEATER_AVAILABLE, 797 OXYGEN_SENSOR_HEATER_INCOMPLETE); 798 799 static final IgnitionMonitor.Decoder OXYGEN_SENSOR_DECODER = 800 new IgnitionMonitor.Decoder(OXYGEN_SENSOR_AVAILABLE, OXYGEN_SENSOR_INCOMPLETE); 801 802 static final IgnitionMonitor.Decoder AC_REFRIGERANT_DECODER = 803 new IgnitionMonitor.Decoder(AC_REFRIGERANT_AVAILABLE, 804 AC_REFRIGERANT_INCOMPLETE); 805 806 static final IgnitionMonitor.Decoder SECONDARY_AIR_SYSTEM_DECODER = 807 new IgnitionMonitor.Decoder(SECONDARY_AIR_SYSTEM_AVAILABLE, 808 SECONDARY_AIR_SYSTEM_INCOMPLETE); 809 810 static final IgnitionMonitor.Decoder EVAPORATIVE_SYSTEM_DECODER = 811 new IgnitionMonitor.Decoder(EVAPORATIVE_SYSTEM_AVAILABLE, 812 EVAPORATIVE_SYSTEM_INCOMPLETE); 813 814 static final IgnitionMonitor.Decoder HEATED_CATALYST_DECODER = 815 new IgnitionMonitor.Decoder(HEATED_CATALYST_AVAILABLE, 816 HEATED_CATALYST_INCOMPLETE); 817 818 static final IgnitionMonitor.Decoder CATALYST_DECODER = 819 new IgnitionMonitor.Decoder(CATALYST_AVAILABLE, CATALYST_INCOMPLETE); 820 SparkIgnitionMonitors(int bitmask)821 SparkIgnitionMonitors(int bitmask) { 822 super(bitmask); 823 EGR = EGR_DECODER.fromValue(bitmask); 824 oxygenSensorHeater = OXYGEN_SENSOR_HEATER_DECODER.fromValue(bitmask); 825 oxygenSensor = OXYGEN_SENSOR_DECODER.fromValue(bitmask); 826 ACRefrigerant = AC_REFRIGERANT_DECODER.fromValue(bitmask); 827 secondaryAirSystem = SECONDARY_AIR_SYSTEM_DECODER.fromValue(bitmask); 828 evaporativeSystem = EVAPORATIVE_SYSTEM_DECODER.fromValue(bitmask); 829 heatedCatalyst = HEATED_CATALYST_DECODER.fromValue(bitmask); 830 catalyst = CATALYST_DECODER.fromValue(bitmask); 831 } 832 } 833 834 /** 835 * Contains information about ignition monitors specific to compression vehicles. 836 */ 837 public static final class CompressionIgnitionMonitors extends CommonIgnitionMonitors { 838 public final IgnitionMonitor EGROrVVT; 839 public final IgnitionMonitor PMFilter; 840 public final IgnitionMonitor exhaustGasSensor; 841 public final IgnitionMonitor boostPressure; 842 public final IgnitionMonitor NOxSCR; 843 public final IgnitionMonitor NMHCCatalyst; 844 845 /** @hide */ 846 public static final int EGR_OR_VVT_AVAILABLE = 0x1 << 6; 847 /** @hide */ 848 public static final int EGR_OR_VVT_INCOMPLETE = 0x1 << 7; 849 850 /** @hide */ 851 public static final int PM_FILTER_AVAILABLE = 0x1 << 8; 852 /** @hide */ 853 public static final int PM_FILTER_INCOMPLETE = 0x1 << 9; 854 855 /** @hide */ 856 public static final int EXHAUST_GAS_SENSOR_AVAILABLE = 0x1 << 10; 857 /** @hide */ 858 public static final int EXHAUST_GAS_SENSOR_INCOMPLETE = 0x1 << 11; 859 860 /** @hide */ 861 public static final int BOOST_PRESSURE_AVAILABLE = 0x1 << 12; 862 /** @hide */ 863 public static final int BOOST_PRESSURE_INCOMPLETE = 0x1 << 13; 864 865 /** @hide */ 866 public static final int NOx_SCR_AVAILABLE = 0x1 << 14; 867 /** @hide */ 868 public static final int NOx_SCR_INCOMPLETE = 0x1 << 15; 869 870 /** @hide */ 871 public static final int NMHC_CATALYST_AVAILABLE = 0x1 << 16; 872 /** @hide */ 873 public static final int NMHC_CATALYST_INCOMPLETE = 0x1 << 17; 874 875 static final IgnitionMonitor.Decoder EGR_OR_VVT_DECODER = 876 new IgnitionMonitor.Decoder(EGR_OR_VVT_AVAILABLE, EGR_OR_VVT_INCOMPLETE); 877 878 static final IgnitionMonitor.Decoder PM_FILTER_DECODER = 879 new IgnitionMonitor.Decoder(PM_FILTER_AVAILABLE, PM_FILTER_INCOMPLETE); 880 881 static final IgnitionMonitor.Decoder EXHAUST_GAS_SENSOR_DECODER = 882 new IgnitionMonitor.Decoder(EXHAUST_GAS_SENSOR_AVAILABLE, 883 EXHAUST_GAS_SENSOR_INCOMPLETE); 884 885 static final IgnitionMonitor.Decoder BOOST_PRESSURE_DECODER = 886 new IgnitionMonitor.Decoder(BOOST_PRESSURE_AVAILABLE, 887 BOOST_PRESSURE_INCOMPLETE); 888 889 static final IgnitionMonitor.Decoder NOx_SCR_DECODER = 890 new IgnitionMonitor.Decoder(NOx_SCR_AVAILABLE, NOx_SCR_INCOMPLETE); 891 892 static final IgnitionMonitor.Decoder NMHC_CATALYST_DECODER = 893 new IgnitionMonitor.Decoder(NMHC_CATALYST_AVAILABLE, NMHC_CATALYST_INCOMPLETE); 894 CompressionIgnitionMonitors(int bitmask)895 CompressionIgnitionMonitors(int bitmask) { 896 super(bitmask); 897 EGROrVVT = EGR_OR_VVT_DECODER.fromValue(bitmask); 898 PMFilter = PM_FILTER_DECODER.fromValue(bitmask); 899 exhaustGasSensor = EXHAUST_GAS_SENSOR_DECODER.fromValue(bitmask); 900 boostPressure = BOOST_PRESSURE_DECODER.fromValue(bitmask); 901 NOxSCR = NOx_SCR_DECODER.fromValue(bitmask); 902 NMHCCatalyst = NMHC_CATALYST_DECODER.fromValue(bitmask); 903 } 904 } 905 906 /** 907 * Returns the state of the fuel system, if present in this frame. 908 * Returns null otherwise. 909 */ getFuelSystemStatus()910 public @Nullable @FuelSystemStatus.Status Integer getFuelSystemStatus() { 911 return getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.FUEL_SYSTEM_STATUS); 912 } 913 914 /** 915 * Returns the state of the secondary air system, if present in this frame. 916 * Returns null otherwise. 917 */ getSecondaryAirStatus()918 public @Nullable @SecondaryAirStatus.Status Integer getSecondaryAirStatus() { 919 return getSystemIntegerSensor( 920 android.car.diagnostic.IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS); 921 } 922 923 /** 924 * Returns data about the ignition monitors, if present in this frame. 925 * Returns null otherwise. 926 */ getIgnitionMonitors()927 public @Nullable CommonIgnitionMonitors getIgnitionMonitors() { 928 Integer ignitionMonitorsType = 929 getSystemIntegerSensor( 930 android.car.diagnostic.IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED); 931 Integer ignitionMonitorsBitmask = 932 getSystemIntegerSensor( 933 android.car.diagnostic.IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS); 934 if (null == ignitionMonitorsType) return null; 935 if (null == ignitionMonitorsBitmask) return null; 936 switch (ignitionMonitorsType) { 937 case 0: 938 return new SparkIgnitionMonitors(ignitionMonitorsBitmask); 939 case 1: 940 return new CompressionIgnitionMonitors(ignitionMonitorsBitmask); 941 default: 942 return null; 943 } 944 } 945 946 /** 947 * Returns the fuel type, if present in this frame. 948 * Returns null otherwise. 949 */ getFuelType()950 public @Nullable @FuelType.Type Integer getFuelType() { 951 return getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.FUEL_TYPE); 952 } 953 } 954