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