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