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 com.android.car.hal.test; 18 19 import android.hardware.automotive.vehicle.DiagnosticFloatSensorIndex; 20 import android.hardware.automotive.vehicle.DiagnosticIntegerSensorIndex; 21 import android.hardware.automotive.vehicle.VehiclePropConfig; 22 import android.hardware.automotive.vehicle.VehiclePropValue; 23 import android.util.SparseArray; 24 25 import java.lang.reflect.Field; 26 import java.lang.reflect.Modifier; 27 import java.util.BitSet; 28 import java.util.Iterator; 29 30 /** 31 * A builder class for a VehiclePropValue that encapsulates a diagnostic event. This is the Java 32 * equivalent of Obd2SensorStore.cpp in the native layer. 33 * 34 * @hide 35 */ 36 public class DiagnosticEventBuilder { 37 /** 38 * An array-like container that knows to return a default value for any unwritten-to index. 39 * 40 * @param <T> the element type 41 */ 42 class DefaultedArray<T> implements Iterable<T> { 43 private final SparseArray<T> mElements = new SparseArray<>(); 44 private final int mSize; 45 private final T mDefaultValue; 46 DefaultedArray(int size, T defaultValue)47 DefaultedArray(int size, T defaultValue) { 48 mSize = size; 49 mDefaultValue = defaultValue; 50 } 51 checkIndex(int index)52 private int checkIndex(int index) { 53 if (index < 0 || index >= mSize) { 54 throw new IndexOutOfBoundsException(String.format( 55 "Index: %d, Size: %d", index, mSize)); 56 } 57 return index; 58 } 59 set(int index, T element)60 DefaultedArray<T> set(int index, T element) { 61 checkIndex(index); 62 mElements.put(index, element); 63 return this; 64 } 65 get(int index)66 T get(int index) { 67 checkIndex(index); 68 return mElements.get(index, mDefaultValue); 69 } 70 size()71 int size() { 72 return mSize; 73 } 74 clear()75 void clear() { 76 mElements.clear(); 77 } 78 79 @Override iterator()80 public Iterator<T> iterator() { 81 return new Iterator<T>() { 82 private int mIndex = 0; 83 84 @Override 85 public boolean hasNext() { 86 return (mIndex >= 0) && (mIndex < mSize); 87 } 88 89 @Override 90 public T next() { 91 int index = mIndex++; 92 return get(index); 93 } 94 }; 95 } 96 } 97 98 private final int mPropertyId; 99 private final int mNumIntSensors; 100 private final DefaultedArray<Integer> mIntValues; 101 private final DefaultedArray<Float> mFloatValues; 102 private final BitSet mBitmask; 103 private String mDtc = null; 104 DiagnosticEventBuilder(VehiclePropConfig propConfig)105 public DiagnosticEventBuilder(VehiclePropConfig propConfig) { 106 this(propConfig.prop, propConfig.configArray[0], propConfig.configArray[1]); 107 } 108 DiagnosticEventBuilder(int propertyId)109 public DiagnosticEventBuilder(int propertyId) { 110 this(propertyId, 0, 0); 111 } 112 DiagnosticEventBuilder( int propertyId, int numVendorIntSensors, int numVendorFloatSensors)113 public DiagnosticEventBuilder( 114 int propertyId, int numVendorIntSensors, int numVendorFloatSensors) { 115 mPropertyId = propertyId; 116 mNumIntSensors = getLastIndex(DiagnosticIntegerSensorIndex.class) + 1 + numVendorIntSensors; 117 final int numFloatSensors = 118 getLastIndex(DiagnosticFloatSensorIndex.class) + 1 + numVendorFloatSensors; 119 mBitmask = new BitSet(mNumIntSensors + numFloatSensors); 120 mIntValues = new DefaultedArray<>(mNumIntSensors, 0); 121 mFloatValues = new DefaultedArray<>(numFloatSensors, 0.0f); 122 } 123 clear()124 public DiagnosticEventBuilder clear() { 125 mIntValues.clear(); 126 mFloatValues.clear(); 127 mBitmask.clear(); 128 mDtc = null; 129 return this; 130 } 131 addIntSensor(int index, int value)132 public DiagnosticEventBuilder addIntSensor(int index, int value) { 133 mIntValues.set(index, value); 134 mBitmask.set(index); 135 return this; 136 } 137 addFloatSensor(int index, float value)138 public DiagnosticEventBuilder addFloatSensor(int index, float value) { 139 mFloatValues.set(index, value); 140 mBitmask.set(mNumIntSensors + index); 141 return this; 142 } 143 setDTC(String dtc)144 public DiagnosticEventBuilder setDTC(String dtc) { 145 mDtc = dtc; 146 return this; 147 } 148 build()149 public VehiclePropValue build() { 150 return build(0); 151 } 152 build(long timestamp)153 public VehiclePropValue build(long timestamp) { 154 AidlVehiclePropValueBuilder propValueBuilder = AidlVehiclePropValueBuilder.newBuilder( 155 mPropertyId); 156 if (0 == timestamp) { 157 propValueBuilder.setCurrentTimestamp(); 158 } else { 159 propValueBuilder.setTimestamp(timestamp); 160 } 161 mIntValues.forEach(propValueBuilder::addIntValues); 162 mFloatValues.forEach(propValueBuilder::addFloatValues); 163 propValueBuilder.addByteValues(mBitmask.toByteArray()); 164 if (mDtc != null) { 165 propValueBuilder.setStringValue(mDtc); 166 } 167 return propValueBuilder.build(); 168 } 169 170 /** 171 * Get the last index for an enum class. 172 */ getLastIndex(Class<?> clazz)173 public static int getLastIndex(Class<?> clazz) { 174 int lastIndex = 0; 175 for (Field field : clazz.getDeclaredFields()) { 176 int modifiers = field.getModifiers(); 177 try { 178 if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers) 179 && Modifier.isPublic(modifiers) && field.getType().equals(int.class)) { 180 int value = field.getInt(/* object= */ null); 181 if (value > lastIndex) { 182 lastIndex = value; 183 } 184 } 185 } catch (IllegalAccessException ignored) { 186 // Ignore the exception. 187 } 188 } 189 return lastIndex; 190 } 191 } 192