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 static com.android.car.hal.property.HalPropertyDebugUtils.toAreaIdString; 20 import static com.android.car.hal.property.HalPropertyDebugUtils.toPropertyIdString; 21 import static com.android.car.hal.property.HalPropertyDebugUtils.toStatusString; 22 import static com.android.car.hal.property.HalPropertyDebugUtils.toValueString; 23 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 24 25 import android.annotation.Nullable; 26 import android.car.VehiclePropertyIds; 27 import android.car.builtin.util.Slogf; 28 import android.car.hardware.CarPropertyValue; 29 import android.hardware.automotive.vehicle.RawPropValues; 30 import android.hardware.automotive.vehicle.VehiclePropValue; 31 import android.hardware.automotive.vehicle.VehiclePropertyStatus; 32 import android.hardware.automotive.vehicle.VehiclePropertyType; 33 34 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 35 import com.android.car.internal.property.CarPropertyHelper; 36 import com.android.car.internal.property.RawPropertyValue; 37 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.List; 41 import java.util.StringJoiner; 42 43 /** 44 * HalPropValue represents a vehicle property value. 45 * 46 * It could be used to convert between AIDL or HIDL VehiclePropValue used in vehicle HAL and 47 * {@link CarPropertyValue} used in CarPropertyManager. 48 */ 49 public abstract class HalPropValue { 50 private static final String TAG = HalPropValue.class.getSimpleName(); 51 52 /** 53 * Gets the timestamp. 54 * 55 * @return The timestamp. 56 */ getTimestamp()57 public abstract long getTimestamp(); 58 59 /** 60 * Gets the area ID. 61 * 62 * @return The area ID. 63 */ getAreaId()64 public abstract int getAreaId(); 65 66 /** 67 * Gets the property ID. 68 * 69 * @return The property ID. 70 */ getPropId()71 public abstract int getPropId(); 72 73 /** 74 * Gets the property status. 75 * 76 * @return The property status. 77 */ getStatus()78 public abstract int getStatus(); 79 80 /** 81 * Get stored int32 values size. 82 * 83 * @return The size for the stored int32 values. 84 */ getInt32ValuesSize()85 public abstract int getInt32ValuesSize(); 86 87 /** 88 * Gets the int32 value at index. 89 * 90 * @param index The index. 91 * @return The int32 value at index. 92 */ getInt32Value(int index)93 public abstract int getInt32Value(int index); 94 95 /** 96 * Dump all int32 values as a string. Used for debugging. 97 * 98 * @return A String representation of all int32 values. 99 */ dumpInt32Values()100 public abstract String dumpInt32Values(); 101 102 /** 103 * Get stored float values size. 104 * 105 * @return The size for the stored float values. 106 */ getFloatValuesSize()107 public abstract int getFloatValuesSize(); 108 109 /** 110 * Gets the float value at index. 111 * 112 * @param index The index. 113 * @return The float value at index. 114 */ getFloatValue(int index)115 public abstract float getFloatValue(int index); 116 117 /** 118 * Dump all float values as a string. Used for debugging. 119 * 120 * @return A String representation of all float values. 121 */ dumpFloatValues()122 public abstract String dumpFloatValues(); 123 124 /** 125 * Get stored inn64 values size. 126 * 127 * @return The size for the stored inn64 values. 128 */ getInt64ValuesSize()129 public abstract int getInt64ValuesSize(); 130 131 /** 132 * Gets the int64 value at index. 133 * 134 * @param index The index. 135 * @return The int64 value at index. 136 */ getInt64Value(int index)137 public abstract long getInt64Value(int index); 138 139 /** 140 * Dump all int64 values as a string. Used for debugging. 141 * 142 * @return A String representation of all int64 values. 143 */ dumpInt64Values()144 public abstract String dumpInt64Values(); 145 146 /** 147 * Get stored byte values size. 148 * 149 * @return The size for the stored byte values. 150 */ getByteValuesSize()151 public abstract int getByteValuesSize(); 152 153 /** 154 * Gets the byte value at index. 155 * 156 * @param index The index. 157 * @return The byte value at index. 158 */ getByteValue(int index)159 public abstract byte getByteValue(int index); 160 161 /** 162 * Gets the byte values. 163 * 164 * @return The byte values. 165 */ getByteArray()166 public abstract byte[] getByteArray(); 167 168 /** 169 * Gets the string value. 170 * 171 * @return The stored string value. 172 */ getStringValue()173 public abstract String getStringValue(); 174 175 /** 176 * Converts to an AIDL/HIDL VehiclePropValue that could be used to sent to vehicle HAL. 177 * 178 * @return An AIDL or HIDL VehiclePropValue. 179 */ toVehiclePropValue()180 public abstract Object toVehiclePropValue(); 181 182 /** 183 * Converts a {@link RawPropValues} received from AIDL VHAL to a {@link RawPropertyValue}. 184 * 185 * This only works for AIDL types. 186 * 187 * This wraps the {@link RawPropValues} to a {@link HalPropValue}, converts it to a 188 * {@link CarPropertyValue} and gets the {@link RawPropertyValue} from it. 189 */ toRawPropertyValue(int propId, int areaId, int mgrPropId, RawPropValues rawPropValues, HalPropConfig config)190 public static @Nullable RawPropertyValue<?> toRawPropertyValue(int propId, int areaId, 191 int mgrPropId, RawPropValues rawPropValues, HalPropConfig config) { 192 VehiclePropValue aidlVehiclePropValue = new VehiclePropValue(); 193 aidlVehiclePropValue.prop = propId; 194 aidlVehiclePropValue.areaId = areaId; 195 aidlVehiclePropValue.value = rawPropValues; 196 HalPropValue aidlHalPropValue = new HalPropValueBuilder(/* isAidl= */ true) 197 .build(aidlVehiclePropValue); 198 RawPropertyValue<?> rawPropertyValue = aidlHalPropValue.toRawPropertyValue( 199 mgrPropId, config); 200 if (rawPropertyValue == null) { 201 Slogf.e(TAG, "Invalid RawPropValues: " + rawPropValues 202 + ", cannot convert to RawPropertyValue, return null"); 203 return null; 204 } 205 return rawPropertyValue; 206 } 207 208 /** 209 * Turns this class to a {@link CarPropertyValue}. 210 * 211 * @param mgrPropId The property ID used in {@link android.car.VehiclePropertyIds}. 212 * @param config The config for the property. 213 * @return A CarPropertyValue that could be passed to upper layer 214 * @throws IllegalStateException If property has unsupported type 215 */ toCarPropertyValue(int mgrPropId, HalPropConfig config)216 public CarPropertyValue toCarPropertyValue(int mgrPropId, HalPropConfig config) { 217 return toCarPropertyValue(mgrPropId, config, /* isVhalPropId= */ false); 218 } 219 220 /** 221 * Turns this class to a {@link CarPropertyValue}. 222 * 223 * @param mgrPropId The property ID used in {@link android.car.VehiclePropertyIds}. 224 * @param config The config for the property. 225 * @return A CarPropertyValue that could be passed to upper layer 226 * @throws IllegalStateException If property has unsupported type 227 */ toCarPropertyValue(int mgrPropId, HalPropConfig config, boolean isVhalPropId)228 public CarPropertyValue toCarPropertyValue(int mgrPropId, HalPropConfig config, 229 boolean isVhalPropId) { 230 Class<?> clazz = CarPropertyUtils.getJavaClass(getPropId() & VehiclePropertyType.MASK); 231 int areaId = getAreaId(); 232 int status = vehiclePropertyStatusToCarPropertyStatus(getStatus()); 233 long timestampNanos = getTimestamp(); 234 var rawPropertyValue = toRawPropertyValue(mgrPropId, config); 235 if (rawPropertyValue == null) { 236 // Cannot convert to a valid rawPropertyValue. If the property has available status, 237 // change it to error. 238 if (status == CarPropertyValue.STATUS_AVAILABLE) { 239 status = CarPropertyValue.STATUS_ERROR; 240 } 241 // Fill in the default value, rawPropertyValue must not be null. 242 rawPropertyValue = new RawPropertyValue(CarPropertyHelper.getDefaultValue(clazz)); 243 } 244 return new CarPropertyValue<>(mgrPropId, areaId, status, timestampNanos, 245 rawPropertyValue, isVhalPropId); 246 } 247 toRawPropertyValue(int mgrPropId, HalPropConfig config)248 private @Nullable RawPropertyValue<?> toRawPropertyValue(int mgrPropId, HalPropConfig config) { 249 if (isMixedTypeProperty(getPropId())) { 250 int[] configArray = config.getConfigArray(); 251 boolean containStringType = configArray[0] == 1; 252 boolean containBooleanType = configArray[1] == 1; 253 return toMixedRawPropertyValue(containBooleanType, containStringType); 254 } 255 return toRegularRawPropertyValue(mgrPropId); 256 } 257 258 /** 259 * Check whether this property is equal to another property. 260 * 261 * @param argument The property to compare. 262 * @return true if equal, false if not. 263 */ 264 @Override equals(Object argument)265 public boolean equals(Object argument) { 266 if (!(argument instanceof HalPropValue other)) { 267 return false; 268 } 269 270 if (!equalsExceptTimestamp(other)) { 271 return false; 272 } 273 274 if (other.getTimestamp() != getTimestamp()) { 275 Slogf.i(TAG, "Timestamp mismatch, got " + other.getTimestamp() + " want " 276 + getTimestamp()); 277 return false; 278 } 279 return true; 280 } 281 282 /** 283 * Check whether this property is equal to another property except timestamps. 284 * 285 * @param other The property to compare. 286 * @return true if equal, false if not. 287 */ equalsExceptTimestamp(HalPropValue other)288 public boolean equalsExceptTimestamp(HalPropValue other) { 289 if (other.getPropId() != getPropId()) { 290 Slogf.i(TAG, "Property ID mismatch, got " + other.getPropId() + " want " 291 + getPropId()); 292 return false; 293 } 294 if (other.getAreaId() != getAreaId()) { 295 Slogf.i(TAG, "Area ID mismatch, got " + other.getAreaId() + " want " + getAreaId()); 296 return false; 297 } 298 if (other.getStatus() != getStatus()) { 299 Slogf.i(TAG, "Status mismatch, got " + other.getStatus() + " want " + getStatus()); 300 return false; 301 } 302 if (!equalInt32Values(other)) { 303 Slogf.i(TAG, "Int32Values mismatch, got " + other.dumpInt32Values() + " want " 304 + dumpInt32Values()); 305 return false; 306 } 307 if (!equalFloatValues(other)) { 308 Slogf.i(TAG, "FloatValues mismatch, got " + other.dumpFloatValues() + " want " 309 + dumpFloatValues()); 310 return false; 311 } 312 if (!equalInt64Values(other)) { 313 Slogf.i(TAG, "Int64Values mismatch, got " + other.dumpInt64Values() + " want " 314 + dumpInt64Values()); 315 return false; 316 } 317 if (!Arrays.equals(other.getByteArray(), getByteArray())) { 318 Slogf.i(TAG, "ByteValues mismatch, got " + Arrays.toString(other.getByteArray()) 319 + " want " + Arrays.toString(getByteArray())); 320 return false; 321 } 322 if (!other.getStringValue().equals(getStringValue())) { 323 Slogf.i(TAG, "StringValue mismatch, got " + other.getStringValue() + " want " 324 + getStringValue()); 325 return false; 326 } 327 return true; 328 } 329 330 @Override 331 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) toString()332 public String toString() { 333 StringJoiner debugStringJoiner = new StringJoiner(", ", "{", "}"); 334 debugStringJoiner.add("Property ID: " + toPropertyIdString(getPropId())); 335 debugStringJoiner.add("Area ID: " + toAreaIdString(getPropId(), getAreaId())); 336 debugStringJoiner.add("ElapsedRealtimeNanos: " + getTimestamp()); 337 debugStringJoiner.add("Status: " + toStatusString(getStatus())); 338 debugStringJoiner.add("Value: " + toValueString(this)); 339 return "HalPropValue" + debugStringJoiner; 340 } 341 342 /** 343 * Get the hashCode for this value. 344 */ 345 @Override hashCode()346 public abstract int hashCode(); 347 isMixedTypeProperty(int prop)348 protected static boolean isMixedTypeProperty(int prop) { 349 return (prop & VehiclePropertyType.MASK) == VehiclePropertyType.MIXED; 350 } 351 getFloatContainerArray()352 protected abstract Float[] getFloatContainerArray(); 353 getInt32ContainerArray()354 protected abstract Integer[] getInt32ContainerArray(); 355 getInt64ContainerArray()356 protected abstract Long[] getInt64ContainerArray(); 357 toRegularRawPropertyValue(int mgrPropId)358 private @Nullable RawPropertyValue<?> toRegularRawPropertyValue(int mgrPropId) { 359 Class<?> clazz = CarPropertyUtils.getJavaClass(getPropId() & VehiclePropertyType.MASK); 360 Object value = null; 361 362 if (Boolean.class == clazz) { 363 if (getInt32ValuesSize() == 0) { 364 return null; 365 } 366 value = Boolean.valueOf(getInt32Value(0) == 1); 367 } else if (Float.class == clazz) { 368 if (getFloatValuesSize() == 0) { 369 return null; 370 } 371 value = Float.valueOf(getFloatValue(0)); 372 } else if (Integer.class == clazz) { 373 if (getInt32ValuesSize() == 0) { 374 return null; 375 } 376 value = Integer.valueOf(getInt32Value(0)); 377 } else if (Long.class == clazz) { 378 if (getInt64ValuesSize() == 0) { 379 return null; 380 } 381 value = Long.valueOf(getInt64Value(0)); 382 } else if (Float[].class == clazz) { 383 value = getFloatContainerArray(); 384 } else if (Integer[].class == clazz) { 385 value = getInt32ContainerArray(); 386 } else if (Long[].class == clazz) { 387 value = getInt64ContainerArray(); 388 } else if (String.class == clazz) { 389 value = getStringValue(); 390 } else if (byte[].class == clazz) { 391 value = getByteArray(); 392 } else { 393 throw new IllegalStateException( 394 "Unexpected type " + clazz + " - propertyId: " + VehiclePropertyIds.toString( 395 mgrPropId)); 396 } 397 return new RawPropertyValue(value); 398 } 399 toMixedRawPropertyValue( boolean containBoolean, boolean containString)400 private @Nullable RawPropertyValue<?> toMixedRawPropertyValue( 401 boolean containBoolean, boolean containString) { 402 List<Object> valuesList = new ArrayList<>(); 403 if (containString) { 404 valuesList.add(getStringValue()); 405 } 406 if (containBoolean) { 407 if (getInt32ValuesSize() == 0) { 408 return null; 409 } 410 boolean boolValue = getInt32Value(0) == 1; 411 valuesList.add(boolValue); 412 for (int i = 1; i < getInt32ValuesSize(); i++) { 413 valuesList.add(getInt32Value(i)); 414 } 415 } else { 416 for (int i = 0; i < getInt32ValuesSize(); i++) { 417 valuesList.add(getInt32Value(i)); 418 } 419 } 420 for (int i = 0; i < getInt64ValuesSize(); i++) { 421 valuesList.add(getInt64Value(i)); 422 } 423 for (int i = 0; i < getFloatValuesSize(); i++) { 424 valuesList.add(getFloatValue(i)); 425 } 426 for (int i = 0; i < getByteValuesSize(); i++) { 427 valuesList.add(getByteValue(i)); 428 } 429 return new RawPropertyValue(valuesList.toArray()); 430 } 431 equalInt32Values(HalPropValue argument)432 private boolean equalInt32Values(HalPropValue argument) { 433 if (getInt32ValuesSize() != argument.getInt32ValuesSize()) { 434 return false; 435 } 436 for (int i = 0; i < getInt32ValuesSize(); i++) { 437 if (getInt32Value(i) != argument.getInt32Value(i)) { 438 return false; 439 } 440 } 441 return true; 442 } 443 equalFloatValues(HalPropValue argument)444 private boolean equalFloatValues(HalPropValue argument) { 445 if (getFloatValuesSize() != argument.getFloatValuesSize()) { 446 return false; 447 } 448 for (int i = 0; i < getFloatValuesSize(); i++) { 449 if (getFloatValue(i) != argument.getFloatValue(i)) { 450 return false; 451 } 452 } 453 return true; 454 } 455 equalInt64Values(HalPropValue argument)456 private boolean equalInt64Values(HalPropValue argument) { 457 if (getInt64ValuesSize() != argument.getInt64ValuesSize()) { 458 return false; 459 } 460 for (int i = 0; i < getInt64ValuesSize(); i++) { 461 if (getInt64Value(i) != argument.getInt64Value(i)) { 462 return false; 463 } 464 } 465 return true; 466 } 467 vehiclePropertyStatusToCarPropertyStatus( @ehiclePropertyStatus int status)468 private static @CarPropertyValue.PropertyStatus int vehiclePropertyStatusToCarPropertyStatus( 469 @VehiclePropertyStatus int status) { 470 switch (status) { 471 case VehiclePropertyStatus.AVAILABLE: 472 return CarPropertyValue.STATUS_AVAILABLE; 473 case VehiclePropertyStatus.ERROR: 474 return CarPropertyValue.STATUS_ERROR; 475 case VehiclePropertyStatus.NOT_AVAILABLE_GENERAL: 476 return CarPropertyValue.STATUS_UNAVAILABLE; 477 // TODO(b/381298607): Map these to individual CarPropertyValue status. 478 case VehiclePropertyStatus.NOT_AVAILABLE_DISABLED: // Fallthrough 479 case VehiclePropertyStatus.NOT_AVAILABLE_SPEED_LOW: // Fallthrough 480 case VehiclePropertyStatus.NOT_AVAILABLE_SPEED_HIGH: // Fallthrough 481 case VehiclePropertyStatus.NOT_AVAILABLE_POOR_VISIBILITY: // Fallthrough 482 case VehiclePropertyStatus.NOT_AVAILABLE_SAFETY: // Fallthrough 483 case VehiclePropertyStatus.NOT_AVAILABLE_SUBSYSTEM_NOT_CONNECTED: 484 return CarPropertyValue.STATUS_UNAVAILABLE; 485 } 486 return CarPropertyValue.STATUS_ERROR; 487 } 488 }