1 /*
2  * Copyright (C) 2021 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;
18 
19 import static com.android.car.hal.property.HalPropertyDebugUtils.toAreaIdString;
20 import static com.android.car.hal.property.HalPropertyDebugUtils.toPropertyIdString;
21 import static com.android.car.hal.property.HalPropertyDebugUtils.toStatusString;
22 import static com.android.car.hal.property.HalPropertyDebugUtils.toValueString;
23 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
24 
25 import android.car.VehiclePropertyIds;
26 import android.car.builtin.util.Slogf;
27 import android.car.hardware.CarPropertyValue;
28 import android.hardware.automotive.vehicle.VehiclePropertyStatus;
29 import android.hardware.automotive.vehicle.VehiclePropertyType;
30 
31 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
32 import com.android.car.internal.property.CarPropertyHelper;
33 
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.List;
37 import java.util.StringJoiner;
38 
39 /**
40  * HalPropValue represents a vehicle property value.
41  *
42  * It could be used to convert between AIDL or HIDL VehiclePropValue used in vehicle HAL and
43  * {@link CarPropertyValue} used in CarPropertyManager.
44  */
45 public abstract class HalPropValue {
46     private static final String TAG = HalPropValue.class.getSimpleName();
47 
48     /**
49      * Gets the timestamp.
50      *
51      * @return The timestamp.
52      */
getTimestamp()53     public abstract long getTimestamp();
54 
55     /**
56      * Gets the area ID.
57      *
58      * @return The area ID.
59      */
getAreaId()60     public abstract int getAreaId();
61 
62     /**
63      * Gets the property ID.
64      *
65      * @return The property ID.
66      */
getPropId()67     public abstract int getPropId();
68 
69     /**
70      * Gets the property status.
71      *
72      * @return The property status.
73      */
getStatus()74     public abstract int getStatus();
75 
76     /**
77      * Get stored int32 values size.
78      *
79      * @return The size for the stored int32 values.
80      */
getInt32ValuesSize()81     public abstract int getInt32ValuesSize();
82 
83     /**
84      * Gets the int32 value at index.
85      *
86      * @param index The index.
87      * @return The int32 value at index.
88      */
getInt32Value(int index)89     public abstract int getInt32Value(int index);
90 
91     /**
92      * Dump all int32 values as a string. Used for debugging.
93      *
94      * @return A String representation of all int32 values.
95      */
dumpInt32Values()96     public abstract String dumpInt32Values();
97 
98     /**
99      * Get stored float values size.
100      *
101      * @return The size for the stored float values.
102      */
getFloatValuesSize()103     public abstract int getFloatValuesSize();
104 
105     /**
106      * Gets the float value at index.
107      *
108      * @param index The index.
109      * @return The float value at index.
110      */
getFloatValue(int index)111     public abstract float getFloatValue(int index);
112 
113     /**
114      * Dump all float values as a string. Used for debugging.
115      *
116      * @return A String representation of all float values.
117      */
dumpFloatValues()118     public abstract String dumpFloatValues();
119 
120     /**
121      * Get stored inn64 values size.
122      *
123      * @return The size for the stored inn64 values.
124      */
getInt64ValuesSize()125     public abstract int getInt64ValuesSize();
126 
127     /**
128      * Gets the int64 value at index.
129      *
130      * @param index The index.
131      * @return The int64 value at index.
132      */
getInt64Value(int index)133     public abstract long getInt64Value(int index);
134 
135     /**
136      * Dump all int64 values as a string. Used for debugging.
137      *
138      * @return A String representation of all int64 values.
139      */
dumpInt64Values()140     public abstract String dumpInt64Values();
141 
142     /**
143      * Get stored byte values size.
144      *
145      * @return The size for the stored byte values.
146      */
getByteValuesSize()147     public abstract int getByteValuesSize();
148 
149     /**
150      * Gets the byte value at index.
151      *
152      * @param index The index.
153      * @return The byte value at index.
154      */
getByteValue(int index)155     public abstract byte getByteValue(int index);
156 
157     /**
158      * Gets the byte values.
159      *
160      * @return The byte values.
161      */
getByteArray()162     public abstract byte[] getByteArray();
163 
164     /**
165      * Gets the string value.
166      *
167      * @return The stored string value.
168      */
getStringValue()169     public abstract String getStringValue();
170 
171     /**
172      * Converts to an AIDL/HIDL VehiclePropValue that could be used to sent to vehicle HAL.
173      *
174      * @return An AIDL or HIDL VehiclePropValue.
175      */
toVehiclePropValue()176     public abstract Object toVehiclePropValue();
177 
178     /**
179      * Turns this class to a {@link CarPropertyValue}.
180      *
181      * @param mgrPropId The property ID used in {@link android.car.VehiclePropertyIds}.
182      * @param config The config for the property.
183      * @return A CarPropertyValue that could be passed to upper layer
184      * @throws IllegalStateException If property has unsupported type
185      */
toCarPropertyValue(int mgrPropId, HalPropConfig config)186     public CarPropertyValue toCarPropertyValue(int mgrPropId, HalPropConfig config) {
187         if (isMixedTypeProperty(getPropId())) {
188             int[] configArray = config.getConfigArray();
189             boolean containStringType = configArray[0] == 1;
190             boolean containBooleanType = configArray[1] == 1;
191             return toMixedCarPropertyValue(mgrPropId, containBooleanType, containStringType);
192         }
193         return toCarPropertyValue(mgrPropId);
194     }
195 
196     /**
197      * Check whether this property is equal to another property.
198      *
199      * @param argument The property to compare.
200      * @return true if equal, false if not.
201      */
202     @Override
equals(Object argument)203     public boolean equals(Object argument) {
204         if (!(argument instanceof HalPropValue)) {
205             return false;
206         }
207 
208         HalPropValue other = (HalPropValue) argument;
209 
210         if (other.getPropId() != getPropId()) {
211             Slogf.i(TAG, "Property ID mismatch, got " + other.getPropId() + " want "
212                     + getPropId());
213             return false;
214         }
215         if (other.getAreaId() != getAreaId()) {
216             Slogf.i(TAG, "Area ID mismatch, got " + other.getAreaId() + " want " + getAreaId());
217             return false;
218         }
219         if (other.getStatus() != getStatus()) {
220             Slogf.i(TAG, "Status mismatch, got " + other.getStatus() + " want " + getStatus());
221             return false;
222         }
223         if (other.getTimestamp() != getTimestamp()) {
224             Slogf.i(TAG, "Timestamp mismatch, got " + other.getTimestamp() + " want "
225                     + getTimestamp());
226             return false;
227         }
228         if (!equalInt32Values(other)) {
229             Slogf.i(TAG, "Int32Values mismatch, got " + other.dumpInt32Values() + " want "
230                     + dumpInt32Values());
231             return false;
232         }
233         if (!equalFloatValues(other)) {
234             Slogf.i(TAG, "FloatValues mismatch, got " + other.dumpFloatValues() + " want "
235                     + dumpFloatValues());
236             return false;
237         }
238         if (!equalInt64Values(other)) {
239             Slogf.i(TAG, "Int64Values mismatch, got " + other.dumpInt64Values() + " want "
240                     + dumpInt64Values());
241             return false;
242         }
243         if (!Arrays.equals(other.getByteArray(), getByteArray())) {
244             Slogf.i(TAG, "ByteValues mismatch, got " + Arrays.toString(other.getByteArray())
245                     + " want " + Arrays.toString(getByteArray()));
246             return false;
247         }
248         if (!other.getStringValue().equals(getStringValue())) {
249             Slogf.i(TAG, "StringValue mismatch, got " + other.getStringValue() + " want "
250                     + getStringValue());
251             return false;
252         }
253         return true;
254     }
255 
256     @Override
257     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
toString()258     public String toString() {
259         StringJoiner debugStringJoiner = new StringJoiner(", ", "{", "}");
260         debugStringJoiner.add("Property ID: " + toPropertyIdString(getPropId()));
261         debugStringJoiner.add("Area ID: " + toAreaIdString(getPropId(), getAreaId()));
262         debugStringJoiner.add("ElapsedRealtimeNanos: " + getTimestamp());
263         debugStringJoiner.add("Status: " + toStatusString(getStatus()));
264         debugStringJoiner.add("Value: " + toValueString(this));
265         return "HalPropValue" + debugStringJoiner;
266     }
267 
268     /**
269      * Get the hashCode for this value.
270      */
271     @Override
hashCode()272     public abstract int hashCode();
273 
isMixedTypeProperty(int prop)274     protected static boolean isMixedTypeProperty(int prop) {
275         return (prop & VehiclePropertyType.MASK) == VehiclePropertyType.MIXED;
276     }
277 
getFloatContainerArray()278     protected abstract Float[] getFloatContainerArray();
279 
getInt32ContainerArray()280     protected abstract Integer[] getInt32ContainerArray();
281 
getInt64ContainerArray()282     protected abstract Long[] getInt64ContainerArray();
283 
toCarPropertyValue(int propertyId)284     private CarPropertyValue<?> toCarPropertyValue(int propertyId) {
285         Class<?> clazz = CarPropertyUtils.getJavaClass(getPropId() & VehiclePropertyType.MASK);
286         int areaId = getAreaId();
287         int status = vehiclePropertyStatusToCarPropertyStatus(getStatus());
288         long timestampNanos = getTimestamp();
289         Object value = null;
290 
291         if (Boolean.class == clazz) {
292             if (getInt32ValuesSize() > 0) {
293                 value = Boolean.valueOf(getInt32Value(0) == 1);
294             } else if (status == CarPropertyValue.STATUS_AVAILABLE) {
295                 status = CarPropertyValue.STATUS_ERROR;
296             }
297         } else if (Float.class == clazz) {
298             if (getFloatValuesSize() > 0) {
299                 value = Float.valueOf(getFloatValue(0));
300             } else if (status == CarPropertyValue.STATUS_AVAILABLE) {
301                 status = CarPropertyValue.STATUS_ERROR;
302             }
303         } else if (Integer.class == clazz) {
304             if (getInt32ValuesSize() > 0) {
305                 value = Integer.valueOf(getInt32Value(0));
306             } else if (status == CarPropertyValue.STATUS_AVAILABLE) {
307                 status = CarPropertyValue.STATUS_ERROR;
308             }
309         } else if (Long.class == clazz) {
310             if (getInt64ValuesSize() > 0) {
311                 value = Long.valueOf(getInt64Value(0));
312             } else if (status == CarPropertyValue.STATUS_AVAILABLE) {
313                 status = CarPropertyValue.STATUS_ERROR;
314             }
315         } else if (Float[].class == clazz) {
316             value = getFloatContainerArray();
317         } else if (Integer[].class == clazz) {
318             value = getInt32ContainerArray();
319         } else if (Long[].class == clazz) {
320             value = getInt64ContainerArray();
321         } else if (String.class == clazz) {
322             value = getStringValue();
323         } else if (byte[].class == clazz) {
324             value = getByteArray();
325         } else {
326             throw new IllegalStateException(
327                     "Unexpected type " + clazz + " - propertyId: " + VehiclePropertyIds.toString(
328                             propertyId));
329         }
330         if (value == null) {
331             value = CarPropertyHelper.getDefaultValue(clazz);
332         }
333         return new CarPropertyValue<>(propertyId, areaId, status, timestampNanos, value);
334     }
335 
toMixedCarPropertyValue( int propertyId, boolean containBoolean, boolean containString)336     private CarPropertyValue<?> toMixedCarPropertyValue(
337             int propertyId, boolean containBoolean, boolean containString) {
338         int areaId = getAreaId();
339         int status = vehiclePropertyStatusToCarPropertyStatus(getStatus());
340         long timestampNanos = getTimestamp();
341 
342         List<Object> valuesList = new ArrayList<>();
343         if (containString) {
344             valuesList.add(getStringValue());
345         }
346         if (containBoolean) {
347             if (getInt32ValuesSize() > 0) {
348                 boolean boolValue = getInt32Value(0) == 1;
349                 valuesList.add(boolValue);
350                 for (int i = 1; i < getInt32ValuesSize(); i++) {
351                     valuesList.add(getInt32Value(i));
352                 }
353             } else if (status == CarPropertyValue.STATUS_AVAILABLE) {
354                 status = CarPropertyValue.STATUS_ERROR;
355             }
356         } else {
357             for (int i = 0; i < getInt32ValuesSize(); i++) {
358                 valuesList.add(getInt32Value(i));
359             }
360         }
361         for (int i = 0; i < getInt64ValuesSize(); i++) {
362             valuesList.add(getInt64Value(i));
363         }
364         for (int i = 0; i < getFloatValuesSize(); i++) {
365             valuesList.add(getFloatValue(i));
366         }
367         for (int i = 0; i < getByteValuesSize(); i++) {
368             valuesList.add(getByteValue(i));
369         }
370         return new CarPropertyValue<>(propertyId, areaId, status, timestampNanos,
371                 valuesList.toArray());
372     }
373 
equalInt32Values(HalPropValue argument)374     private boolean equalInt32Values(HalPropValue argument) {
375         if (getInt32ValuesSize() != argument.getInt32ValuesSize()) {
376             return false;
377         }
378         for (int i = 0; i < getInt32ValuesSize(); i++) {
379             if (getInt32Value(i) != argument.getInt32Value(i)) {
380                 return false;
381             }
382         }
383         return true;
384     }
385 
equalFloatValues(HalPropValue argument)386     private boolean equalFloatValues(HalPropValue argument) {
387         if (getFloatValuesSize() != argument.getFloatValuesSize()) {
388             return false;
389         }
390         for (int i = 0; i < getFloatValuesSize(); i++) {
391             if (getFloatValue(i) != argument.getFloatValue(i)) {
392                 return false;
393             }
394         }
395         return true;
396     }
397 
equalInt64Values(HalPropValue argument)398     private boolean equalInt64Values(HalPropValue argument) {
399         if (getInt64ValuesSize() != argument.getInt64ValuesSize()) {
400             return false;
401         }
402         for (int i = 0; i < getInt64ValuesSize(); i++) {
403             if (getInt64Value(i) != argument.getInt64Value(i)) {
404                 return false;
405             }
406         }
407         return true;
408     }
409 
vehiclePropertyStatusToCarPropertyStatus( @ehiclePropertyStatus int status)410     private static @CarPropertyValue.PropertyStatus int vehiclePropertyStatusToCarPropertyStatus(
411             @VehiclePropertyStatus int status) {
412         switch (status) {
413             case VehiclePropertyStatus.AVAILABLE:
414                 return CarPropertyValue.STATUS_AVAILABLE;
415             case VehiclePropertyStatus.ERROR:
416                 return CarPropertyValue.STATUS_ERROR;
417             case VehiclePropertyStatus.UNAVAILABLE:
418                 return CarPropertyValue.STATUS_UNAVAILABLE;
419         }
420         return CarPropertyValue.STATUS_ERROR;
421     }
422 }
423