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