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