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