1 /*
2  * Copyright (C) 2016 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 package com.android.car.hal;
17 
18 import static com.android.car.CarServiceUtils.toByteArray;
19 
20 import static java.lang.Integer.toHexString;
21 
22 import android.car.VehicleAreaType;
23 import android.car.hardware.CarPropertyConfig;
24 import android.car.hardware.CarPropertyValue;
25 import android.hardware.automotive.vehicle.V2_0.VehicleArea;
26 import android.hardware.automotive.vehicle.V2_0.VehicleAreaConfig;
27 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
28 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
29 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
30 
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.List;
34 
35 /**
36  * Utility functions to work with {@link CarPropertyConfig} and {@link CarPropertyValue}
37  */
38 /*package*/ final class CarPropertyUtils {
39 
40     /* Utility class has no public constructor */
CarPropertyUtils()41     private CarPropertyUtils() {}
42 
43     private static final int[] DEFAULT_AREAIDS = {VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL};
44 
45     // configArray[0], 1 indicates the property has a String value
46     private static final int CONFIG_ARRAY_INDEX_STRING = 0;
47     // configArray[1], 1 indicates the property has a Boolean value .
48     private static final int CONFIG_ARRAY_INDEX_BOOLEAN = 1;
49     // configArray[2], 1 indicates the property has a Integer value.
50     private static final int CONFIG_ARRAY_INDEX_INT = 2;
51     // configArray[3], 1 indicates the property has a Integer[] value.
52     private static final int CONFIG_ARRAY_INDEX_INT_ARRAY = 3;
53     // configArray[4], 1 indicates the property has a Long value.
54     private static final int CONFIG_ARRAY_INDEX_LONG = 4;
55     // configArray[5], the number indicates the size of Long[]  in the property.
56     private static final int CONFIG_ARRAY_INDEX_LONG_ARRAY = 5;
57     // configArray[6], 1 indicates the property has a Float value.
58     private static final int CONFIG_ARRAY_INDEX_FLOAT = 6;
59     // configArray[7], the number indicates the size of Float[] in the property.
60     private static final int CONFIG_ARRAY_INDEX_FLOAT_ARRAY = 7;
61     // configArray[8], the number indicates the size of byte[] in the property.
62     private static final int CONFIG_ARRAY_INDEX_BYTES = 8;
63     // Length of mixed type properties' configArray should always be 9
64     private static final int CONFIG_ARRAY_LENGTH = 9;
65     /** Converts {@link VehiclePropValue} to {@link CarPropertyValue} */
toCarPropertyValue( VehiclePropValue halValue, int propertyId)66     static CarPropertyValue<?> toCarPropertyValue(
67             VehiclePropValue halValue, int propertyId) {
68         Class<?> clazz = getJavaClass(halValue.prop & VehiclePropertyType.MASK);
69         int areaId = halValue.areaId;
70         int status = halValue.status;
71         long timestamp = halValue.timestamp;
72         VehiclePropValue.RawValue v = halValue.value;
73 
74         // Handles each return value from {@link getJavaClass}.
75         if (Boolean.class == clazz) {
76             return new CarPropertyValue<>(propertyId, areaId, status, timestamp,
77                                           v.int32Values.get(0) == 1);
78         } else if (Float.class == clazz) {
79             return new CarPropertyValue<>(propertyId, areaId, status, timestamp,
80                                           v.floatValues.get(0));
81         } else if (Integer.class == clazz) {
82             return new CarPropertyValue<>(propertyId, areaId, status, timestamp,
83                                           v.int32Values.get(0));
84         } else if (Long.class == clazz) {
85             return new CarPropertyValue<>(propertyId, areaId, status, timestamp,
86                                           v.int64Values.get(0));
87         } else if (Float[].class == clazz) {
88             Float[] values = new Float[v.floatValues.size()];
89             for (int i = 0; i < values.length; i++) {
90                 values[i] = v.floatValues.get(i);
91             }
92             return new CarPropertyValue<>(propertyId, areaId, status, timestamp, values);
93         } else if (Integer[].class == clazz) {
94             Integer[] values = new Integer[v.int32Values.size()];
95             for (int i = 0; i < values.length; i++) {
96                 values[i] = v.int32Values.get(i);
97             }
98             return new CarPropertyValue<>(propertyId, areaId, status, timestamp, values);
99         } else if (Long[].class == clazz) {
100             Long[] values = new Long[v.int64Values.size()];
101             for (int i = 0; i < values.length; i++) {
102                 values[i] = v.int64Values.get(i);
103             }
104             return new CarPropertyValue<>(propertyId, areaId, status, timestamp, values);
105         } else if (String.class == clazz) {
106             return new CarPropertyValue<>(propertyId, areaId, status, timestamp, v.stringValue);
107         } else if (byte[].class == clazz) {
108             byte[] halData = toByteArray(v.bytes);
109             return new CarPropertyValue<>(propertyId, areaId, status, timestamp, halData);
110         } else {
111             throw new IllegalArgumentException("Unexpected type in: " + propertyId);
112         }
113     }
114 
115     /** Converts {@link VehiclePropValue} to {@link CarPropertyValue} for MIXED type properties*/
toMixedCarPropertyValue( VehiclePropValue halValue, int propertyId, boolean containBoolean, boolean containString)116     static CarPropertyValue<?> toMixedCarPropertyValue(
117             VehiclePropValue halValue, int propertyId, boolean containBoolean,
118             boolean containString) {
119         int areaId = halValue.areaId;
120         int status = halValue.status;
121         long timestamp = halValue.timestamp;
122         VehiclePropValue.RawValue value = halValue.value;
123 
124         List<Object> valuesList = new ArrayList<>();
125         if (containString) {
126             valuesList.add(value.stringValue);
127         }
128         if (containBoolean) {
129             boolean boolValue = value.int32Values.get(0) == 1;
130             valuesList.add(boolValue);
131             valuesList.addAll(value.int32Values.subList(1, value.int32Values.size()));
132         } else {
133             valuesList.addAll(value.int32Values);
134         }
135         valuesList.addAll(value.int64Values);
136         valuesList.addAll(value.floatValues);
137         valuesList.addAll(value.bytes);
138         return new CarPropertyValue<>(propertyId, areaId, status, timestamp, valuesList.toArray());
139     }
140 
141     /** Converts {@link CarPropertyValue} to {@link VehiclePropValue} */
toVehiclePropValue(CarPropertyValue carProp, int halPropId)142     static VehiclePropValue toVehiclePropValue(CarPropertyValue carProp, int halPropId) {
143         VehiclePropValue vehicleProp = new VehiclePropValue();
144         vehicleProp.prop = halPropId;
145         vehicleProp.areaId = carProp.getAreaId();
146         VehiclePropValue.RawValue v = vehicleProp.value;
147 
148         Object o = carProp.getValue();
149 
150         if (o instanceof Boolean) {
151             v.int32Values.add(((Boolean) o) ? 1 : 0);
152         } else if (o instanceof Integer) {
153             v.int32Values.add((Integer) o);
154         } else if (o instanceof Integer[]) {
155             Collections.addAll(v.int32Values, (Integer[]) o);
156         } else if (o instanceof Float) {
157             v.floatValues.add((Float) o);
158         } else if (o instanceof Float[]) {
159             Collections.addAll(v.floatValues, (Float[]) o);
160         } else if (o instanceof Long) {
161             v.int64Values.add((Long) o);
162         } else if (o instanceof Long[]) {
163             Collections.addAll(v.int64Values, (Long[]) o);
164         } else if (o instanceof String) {
165             v.stringValue = (String) o;
166         } else if (o instanceof byte[]) {
167             for (byte b : (byte[]) o) {
168                 v.bytes.add(b);
169             }
170         } else {
171             throw new IllegalArgumentException("Unexpected type in: " + carProp);
172         }
173 
174         return vehicleProp;
175     }
176 
177     /**
178      * Converts {@link CarPropertyValue} to {@link VehiclePropValue} for MIXED type properties
179      * configArray[0], 1 indicates the property has a String value
180      * configArray[1], 1 indicates the property has a Boolean value .
181      * configArray[2], 1 indicates the property has a Integer value.
182      * configArray[3], the number indicates the size of Integer[]  in the property.
183      * configArray[4], 1 indicates the property has a Long value .
184      * configArray[5], the number indicates the size of Long[]  in the property.
185      * configArray[6], 1 indicates the property has a Float value .
186      * configArray[7], the number indicates the size of Float[] in the property.
187      * configArray[8], the number indicates the size of byte[] in the property.
188      *
189      * For example:
190      * configArray = {1, 1, 1, 3, 0, 0, 0, 0, 0} indicates the property has a String value, a
191      * Boolean value, an Integer value, an Integer array with 3 enums.
192      */
toMixedVehiclePropValue(CarPropertyValue carProp, int halPropId, int[] configArray)193     static VehiclePropValue toMixedVehiclePropValue(CarPropertyValue carProp,
194             int halPropId, int[] configArray) {
195         if (configArray.length != CONFIG_ARRAY_LENGTH) {
196             throw new IllegalArgumentException("Unexpected configArray in:" + carProp);
197         }
198         VehiclePropValue vehicleProp = new VehiclePropValue();
199         vehicleProp.prop = halPropId;
200         vehicleProp.areaId = carProp.getAreaId();
201         VehiclePropValue.RawValue v = vehicleProp.value;
202 
203         Object[] values = (Object[]) carProp.getValue();
204         int indexOfValues = 0;
205         if (configArray[CONFIG_ARRAY_INDEX_STRING] != 0) {
206             // Add a string value
207             v.stringValue = (String) values[indexOfValues];
208             indexOfValues++;
209         }
210 
211         if (configArray[CONFIG_ARRAY_INDEX_BOOLEAN] != 0) {
212             // Add a boolean value
213             v.int32Values.add((Boolean) values[indexOfValues] ? 1 : 0); // in HAL, 1 indicates true
214             indexOfValues++;
215         }
216 
217         /*
218          * configArray[2], 1 indicates the property has a Integer value.
219          * configArray[3], the number indicates the size of Integer[]  in the property.
220          */
221         int integerSize = configArray[CONFIG_ARRAY_INDEX_INT]
222                 + configArray[CONFIG_ARRAY_INDEX_INT_ARRAY];
223         while (integerSize != 0) {
224             v.int32Values.add((Integer) values[indexOfValues]);
225             indexOfValues++;
226             integerSize--;
227         }
228         /* configArray[4], 1 indicates the property has a Long value .
229          * configArray[5], the number indicates the size of Long[]  in the property.
230          */
231         int longSize = configArray[CONFIG_ARRAY_INDEX_LONG]
232                 + configArray[CONFIG_ARRAY_INDEX_LONG_ARRAY];
233         while (longSize != 0) {
234             v.int64Values.add((Long) values[indexOfValues]);
235             indexOfValues++;
236             longSize--;
237         }
238         /* configArray[6], 1 indicates the property has a Float value .
239          * configArray[7], the number indicates the size of Float[] in the property.
240          */
241         int floatSize = configArray[CONFIG_ARRAY_INDEX_FLOAT]
242                 + configArray[CONFIG_ARRAY_INDEX_FLOAT_ARRAY];
243         while (floatSize != 0) {
244             v.floatValues.add((Float) values[indexOfValues]);
245             indexOfValues++;
246             floatSize--;
247         }
248 
249         /* configArray[8], the number indicates the size of byte[] in the property. */
250         if (configArray[CONFIG_ARRAY_INDEX_BYTES] != 0) {
251             Collections.addAll(v.bytes, (Byte[]) values[indexOfValues]);
252         }
253         return vehicleProp;
254     }
255 
256     /**
257      * Converts {@link VehiclePropConfig} to {@link CarPropertyConfig}.
258      */
toCarPropertyConfig(VehiclePropConfig p, int propertyId)259     static CarPropertyConfig<?> toCarPropertyConfig(VehiclePropConfig p, int propertyId) {
260         int areaType = getVehicleAreaType(p.prop & VehicleArea.MASK);
261 
262         Class<?> clazz = getJavaClass(p.prop & VehiclePropertyType.MASK);
263         float maxSampleRate = 0f;
264         float minSampleRate = 0f;
265         if (p.changeMode != CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_STATIC) {
266             maxSampleRate = p.maxSampleRate;
267             minSampleRate = p.minSampleRate;
268         }
269         if (p.areaConfigs.isEmpty()) {
270             return CarPropertyConfig
271                     .newBuilder(clazz, propertyId, areaType, /* capacity */ 1)
272                     .addAreas(DEFAULT_AREAIDS)
273                     .setAccess(p.access)
274                     .setChangeMode(p.changeMode)
275                     .setConfigArray(p.configArray)
276                     .setConfigString(p.configString)
277                     .setMaxSampleRate(maxSampleRate)
278                     .setMinSampleRate(minSampleRate)
279                     .build();
280         } else {
281             CarPropertyConfig.Builder builder = CarPropertyConfig
282                     .newBuilder(clazz, propertyId, areaType, /* capacity */ p.areaConfigs.size())
283                     .setAccess(p.access)
284                     .setChangeMode(p.changeMode)
285                     .setConfigArray(p.configArray)
286                     .setConfigString(p.configString)
287                     .setMaxSampleRate(maxSampleRate)
288                     .setMinSampleRate(minSampleRate);
289 
290             for (VehicleAreaConfig area : p.areaConfigs) {
291                 if (classMatched(Integer.class, clazz)) {
292                     builder.addAreaConfig(area.areaId, area.minInt32Value, area.maxInt32Value);
293                 } else if (classMatched(Float.class, clazz)) {
294                     builder.addAreaConfig(area.areaId, area.minFloatValue, area.maxFloatValue);
295                 } else if (classMatched(Long.class, clazz)) {
296                     builder.addAreaConfig(area.areaId, area.minInt64Value, area.maxInt64Value);
297                 } else if (classMatched(Boolean.class, clazz)
298                         || classMatched(Float[].class, clazz)
299                         || classMatched(Integer[].class, clazz)
300                         || classMatched(Long[].class, clazz)
301                         || classMatched(String.class, clazz)
302                         || classMatched(byte[].class, clazz)
303                         || classMatched(Object[].class, clazz)) {
304                     // These property types do not have min/max values
305                     builder.addArea(area.areaId);
306                 } else {
307                     throw new IllegalArgumentException("Unexpected type: " + clazz);
308                 }
309             }
310             return builder.build();
311         }
312     }
313 
getVehicleAreaType(int halArea)314     private static @VehicleAreaType.VehicleAreaTypeValue int getVehicleAreaType(int halArea) {
315         switch (halArea) {
316             case VehicleArea.GLOBAL:
317                 return VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
318             case VehicleArea.SEAT:
319                 return VehicleAreaType.VEHICLE_AREA_TYPE_SEAT;
320             case VehicleArea.DOOR:
321                 return VehicleAreaType.VEHICLE_AREA_TYPE_DOOR;
322             case VehicleArea.WINDOW:
323                 return VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW;
324             case VehicleArea.MIRROR:
325                 return VehicleAreaType.VEHICLE_AREA_TYPE_MIRROR;
326             case VehicleArea.WHEEL:
327                 return VehicleAreaType.VEHICLE_AREA_TYPE_WHEEL;
328             default:
329                 throw new RuntimeException("Unsupported area type " + halArea);
330         }
331     }
332 
getJavaClass(int halType)333     private static Class<?> getJavaClass(int halType) {
334         switch (halType) {
335             case VehiclePropertyType.BOOLEAN:
336                 return Boolean.class;
337             case VehiclePropertyType.FLOAT:
338                 return Float.class;
339             case VehiclePropertyType.INT32:
340                 return Integer.class;
341             case VehiclePropertyType.INT64:
342                 return Long.class;
343             case VehiclePropertyType.FLOAT_VEC:
344                 return Float[].class;
345             case VehiclePropertyType.INT32_VEC:
346                 return Integer[].class;
347             case VehiclePropertyType.INT64_VEC:
348                 return Long[].class;
349             case VehiclePropertyType.STRING:
350                 return String.class;
351             case VehiclePropertyType.BYTES:
352                 return byte[].class;
353             case VehiclePropertyType.MIXED:
354                 return Object[].class;
355             default:
356                 throw new IllegalArgumentException("Unexpected type: " + toHexString(halType));
357         }
358     }
359 
classMatched(Class<?> class1, Class<?> class2)360     private static boolean classMatched(Class<?> class1, Class<?> class2) {
361         return class1 == class2 || class1.getComponentType() == class2;
362     }
363 }
364