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