1 /* 2 * Copyright (C) 2023 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.property; 18 19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.PRIVATE_CONSTRUCTOR; 20 import static com.android.car.internal.util.ConstantDebugUtils.toName; 21 import static com.android.car.internal.util.DebugUtils.flagsToOptionalString; 22 23 import static java.lang.Integer.toHexString; 24 25 import android.annotation.Nullable; 26 import android.hardware.automotive.vehicle.EnumForVehicleProperty; 27 import android.hardware.automotive.vehicle.UnitsForVehicleProperty; 28 import android.hardware.automotive.vehicle.VehicleArea; 29 import android.hardware.automotive.vehicle.VehicleAreaDoor; 30 import android.hardware.automotive.vehicle.VehicleAreaMirror; 31 import android.hardware.automotive.vehicle.VehicleAreaSeat; 32 import android.hardware.automotive.vehicle.VehicleAreaWheel; 33 import android.hardware.automotive.vehicle.VehicleAreaWindow; 34 import android.hardware.automotive.vehicle.VehicleProperty; 35 import android.hardware.automotive.vehicle.VehiclePropertyAccess; 36 import android.hardware.automotive.vehicle.VehiclePropertyChangeMode; 37 import android.hardware.automotive.vehicle.VehiclePropertyGroup; 38 import android.hardware.automotive.vehicle.VehiclePropertyStatus; 39 import android.hardware.automotive.vehicle.VehiclePropertyType; 40 import android.hardware.automotive.vehicle.VehicleUnit; 41 import android.util.Slog; 42 43 import com.android.car.hal.HalPropValue; 44 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 45 import com.android.car.internal.property.CarPropertyHelper; 46 import com.android.car.internal.property.PropIdAreaId; 47 import com.android.car.internal.util.ConstantDebugUtils; 48 49 import java.util.Arrays; 50 import java.util.StringJoiner; 51 52 /** 53 * Utility class for converting {@link VehicleProperty} related information to human-readable names. 54 */ 55 public final class HalPropertyDebugUtils { 56 private static final String TAG = HalPropertyDebugUtils.class.getSimpleName(); 57 private static final int MAX_BYTE_SIZE = 20; 58 private static final String NO_VALUE = "NO_VALUE"; 59 60 /** 61 * HalPropertyDebugUtils only contains static fields and methods and must never be 62 * instantiated. 63 */ 64 @ExcludeFromCodeCoverageGeneratedReport(reason = PRIVATE_CONSTRUCTOR) HalPropertyDebugUtils()65 private HalPropertyDebugUtils() { 66 throw new UnsupportedOperationException("Must never be called"); 67 } 68 69 /** 70 * Gets a user-friendly representation string representation of a {@code propertyId}. 71 */ toPropertyIdString(int propertyId)72 public static String toPropertyIdString(int propertyId) { 73 String hexSuffix = "(0x" + toHexString(propertyId) + ")"; 74 if (isSystemPropertyId(propertyId)) { 75 return VehicleProperty.$.toString(propertyId) + hexSuffix; 76 } else if (CarPropertyHelper.isVendorProperty(propertyId)) { 77 return "VENDOR_PROPERTY" + hexSuffix; 78 } else if (CarPropertyHelper.isBackportedProperty(propertyId)) { 79 return "BACKPORTED_PROPERTY" + hexSuffix; 80 } 81 return "INVALID_PROPERTY_ID" + hexSuffix; 82 } 83 84 /** 85 * Gets the HAL property's ID based on the passed name. 86 */ 87 @Nullable toPropertyId(String propertyName)88 public static Integer toPropertyId(String propertyName) { 89 return ConstantDebugUtils.toValue(VehicleProperty.class, propertyName); 90 } 91 92 /** 93 * Gets a user-friendly string representation of an {@code areaId} for the given 94 * {@code propertyId}. 95 */ toAreaIdString(int propertyId, int areaId)96 public static String toAreaIdString(int propertyId, int areaId) { 97 switch (propertyId & VehicleArea.MASK) { 98 case VehicleArea.GLOBAL -> { 99 if (areaId == 0) { 100 return "GLOBAL(0x0)"; 101 } 102 return "INVALID_GLOBAL_AREA_ID(0x" + toHexString(areaId) + ")"; 103 } 104 case VehicleArea.DOOR -> { 105 return processOptionalFlagsString( 106 flagsToOptionalString(VehicleAreaDoor.class, areaId), 107 VehicleAreaDoor.class.getSimpleName(), areaId); 108 } 109 case VehicleArea.SEAT -> { 110 return processOptionalFlagsString( 111 flagsToOptionalString(VehicleAreaSeat.class, areaId), 112 VehicleAreaSeat.class.getSimpleName(), areaId); 113 } 114 case VehicleArea.MIRROR -> { 115 return processOptionalFlagsString( 116 flagsToOptionalString(VehicleAreaMirror.class, areaId), 117 VehicleAreaMirror.class.getSimpleName(), areaId); 118 } 119 case VehicleArea.WHEEL -> { 120 return processOptionalFlagsString( 121 flagsToOptionalString(VehicleAreaWheel.class, areaId), 122 VehicleAreaWheel.class.getSimpleName(), areaId); 123 } 124 case VehicleArea.WINDOW -> { 125 return processOptionalFlagsString( 126 flagsToOptionalString(VehicleAreaWindow.class, areaId), 127 VehicleAreaWindow.class.getSimpleName(), areaId); 128 } 129 default -> { 130 return "UNKNOWN_AREA_ID(0x" + toHexString(areaId) + ")"; 131 } 132 } 133 } 134 135 /** 136 * Gets a user-friendly representation string representation of the value of a 137 * {@link HalPropValue} instance. 138 */ toValueString(HalPropValue halPropValue)139 public static String toValueString(HalPropValue halPropValue) { 140 int propertyId = halPropValue.getPropId(); 141 int valueType = propertyId & VehiclePropertyType.MASK; 142 String propertyUnits = getUnitsIfSupported(propertyId); 143 StringJoiner stringJoiner = new StringJoiner(", ", "[", "]"); 144 switch (valueType) { 145 case VehiclePropertyType.BOOLEAN -> { 146 if (halPropValue.getInt32ValuesSize() != 1) { 147 return NO_VALUE; 148 } 149 return halPropValue.getInt32Value(0) == 0 ? "FALSE" : "TRUE"; 150 } 151 case VehiclePropertyType.INT32 -> { 152 if (halPropValue.getInt32ValuesSize() != 1) { 153 return NO_VALUE; 154 } 155 return getIntValueName(propertyId, halPropValue.getInt32Value(0), propertyUnits); 156 } 157 case VehiclePropertyType.INT32_VEC -> { 158 for (int i = 0; i < halPropValue.getInt32ValuesSize(); i++) { 159 stringJoiner.add(getIntValueName(propertyId, halPropValue.getInt32Value(i), 160 propertyUnits)); 161 } 162 return stringJoiner.toString(); 163 } 164 case VehiclePropertyType.FLOAT -> { 165 if (halPropValue.getFloatValuesSize() != 1) { 166 return NO_VALUE; 167 } 168 return halPropValue.getFloatValue(0) + propertyUnits; 169 } 170 case VehiclePropertyType.FLOAT_VEC -> { 171 for (int i = 0; i < halPropValue.getFloatValuesSize(); i++) { 172 stringJoiner.add(halPropValue.getFloatValue(i) + propertyUnits); 173 } 174 return stringJoiner.toString(); 175 } 176 case VehiclePropertyType.INT64 -> { 177 if (halPropValue.getInt64ValuesSize() != 1) { 178 return NO_VALUE; 179 } 180 return halPropValue.getInt64Value(0) + propertyUnits; 181 } 182 case VehiclePropertyType.INT64_VEC -> { 183 for (int i = 0; i < halPropValue.getInt64ValuesSize(); i++) { 184 stringJoiner.add(halPropValue.getInt64Value(i) + propertyUnits); 185 } 186 return stringJoiner.toString(); 187 } 188 case VehiclePropertyType.STRING -> { 189 return halPropValue.getStringValue(); 190 } 191 case VehiclePropertyType.BYTES -> { 192 String bytesString = ""; 193 byte[] byteValues = halPropValue.getByteArray(); 194 if (byteValues.length > MAX_BYTE_SIZE) { 195 byte[] bytes = Arrays.copyOf(byteValues, MAX_BYTE_SIZE); 196 bytesString = Arrays.toString(bytes); 197 } else { 198 bytesString = Arrays.toString(byteValues); 199 } 200 return bytesString; 201 } 202 } 203 String bytesString = ""; 204 byte[] byteValues = halPropValue.getByteArray(); 205 if (byteValues.length > MAX_BYTE_SIZE) { 206 byte[] bytes = Arrays.copyOf(byteValues, MAX_BYTE_SIZE); 207 bytesString = Arrays.toString(bytes); 208 } else { 209 bytesString = Arrays.toString(byteValues); 210 } 211 return "floatValues: " + halPropValue.dumpFloatValues() + ", int32Values: " 212 + halPropValue.dumpInt32Values() + ", int64Values: " 213 + halPropValue.dumpInt64Values() + ", bytes: " + bytesString + ", string: " 214 + halPropValue.getStringValue(); 215 } 216 getIntValueName(int propertyId, int value, String propertyUnits)217 private static String getIntValueName(int propertyId, int value, String propertyUnits) { 218 if (EnumForVehicleProperty.values.containsKey(propertyId)) { 219 for (int i = 0; i < EnumForVehicleProperty.values.get(propertyId).size(); i++) { 220 Class<?> enumClazz = EnumForVehicleProperty.values.get(propertyId).get(i); 221 String valueName = ConstantDebugUtils.toName(enumClazz, value); 222 if (valueName != null) { 223 return valueName + "(0x" + toHexString(value) + ")"; 224 } 225 } 226 Slog.w(TAG, 227 "Failed to find enum name for property ID: " + toPropertyIdString(propertyId) 228 + " value: " + value); 229 } 230 return value + propertyUnits; 231 } 232 getUnitsIfSupported(int propertyId)233 private static String getUnitsIfSupported(int propertyId) { 234 if (!UnitsForVehicleProperty.values.containsKey(propertyId)) { 235 return ""; 236 } 237 Integer units = UnitsForVehicleProperty.values.get(propertyId); 238 String unitsString = ConstantDebugUtils.toName(VehicleUnit.class, units); 239 if (unitsString == null) { 240 return ""; 241 } 242 return " " + unitsString; 243 } 244 245 /** 246 * Gets a user-friendly representation string representation of {@link VehicleArea} 247 * constant for the passed {@code propertyId}. 248 */ toAreaTypeString(int propertyId)249 public static String toAreaTypeString(int propertyId) { 250 int areaType = propertyId & VehicleArea.MASK; 251 return toDebugString(VehicleArea.class, areaType); 252 } 253 254 /** 255 * Gets a user-friendly representation string representation of {@link VehiclePropertyGroup} 256 * constant for the passed {@code propertyId}. 257 */ toGroupString(int propertyId)258 public static String toGroupString(int propertyId) { 259 int group = propertyId & VehiclePropertyGroup.MASK; 260 return toDebugString(VehiclePropertyGroup.class, group); 261 } 262 263 /** 264 * Gets a user-friendly representation string representation of {@link VehiclePropertyType} 265 * constant for the passed {@code propertyId}. 266 */ toValueTypeString(int propertyId)267 public static String toValueTypeString(int propertyId) { 268 int valueType = propertyId & VehiclePropertyType.MASK; 269 return toDebugString(VehiclePropertyType.class, valueType); 270 } 271 272 /** 273 * Gets a user-friendly representation string representation of 274 * {@link VehiclePropertyAccess} constant. 275 */ toAccessString(int access)276 public static String toAccessString(int access) { 277 return toDebugString(VehiclePropertyAccess.class, access); 278 } 279 280 /** 281 * Gets a user-friendly representation string representation of 282 * {@link VehiclePropertyChangeMode} constant. 283 */ toChangeModeString(int changeMode)284 public static String toChangeModeString(int changeMode) { 285 return toDebugString(VehiclePropertyChangeMode.class, changeMode); 286 } 287 288 /** 289 * Gets a user-friendly representation string representation of 290 * {@link VehiclePropertyStatus} constant. 291 */ toStatusString(int status)292 public static String toStatusString(int status) { 293 return toDebugString(VehiclePropertyStatus.class, status); 294 } 295 toDebugString(Class<?> clazz, int constantValue)296 private static String toDebugString(Class<?> clazz, int constantValue) { 297 String hexSuffix = "(0x" + toHexString(constantValue) + ")"; 298 if (toName(clazz, constantValue) == null) { 299 String invalidConstantValue = "INVALID_" + clazz.getSimpleName() + hexSuffix; 300 Slog.e(TAG, invalidConstantValue); 301 return invalidConstantValue; 302 } 303 return toName(clazz, constantValue) + hexSuffix; 304 } 305 306 /** 307 * Gets human-readable representation of a {@code PropIdAreaId} structure. 308 * 309 * Note that the property ID is the VHAL property ID, not the CarPropertyManager property ID. 310 */ toHalPropIdAreaIdString(PropIdAreaId propIdAreaId)311 public static String toHalPropIdAreaIdString(PropIdAreaId propIdAreaId) { 312 return "PropIdAreaId{propId=" + toPropertyIdString(propIdAreaId.propId) 313 + ", areaId=" + toAreaIdString(propIdAreaId.propId, propIdAreaId.areaId) + "}"; 314 } 315 316 /** 317 * Gets human-readable representation of a list of {@code PropIdAreaId}. 318 * 319 * Note that the property ID is the VHAL property ID, not the CarPropertyManager property ID. 320 */ toHalPropIdAreaIdsString(Iterable<PropIdAreaId> propIdAreaIds)321 public static String toHalPropIdAreaIdsString(Iterable<PropIdAreaId> propIdAreaIds) { 322 StringBuilder sb = new StringBuilder(); 323 sb.append("["); 324 boolean first = true; 325 for (PropIdAreaId propIdAreaId : propIdAreaIds) { 326 if (first) { 327 first = false; 328 } else { 329 sb.append(", "); 330 } 331 sb.append(toHalPropIdAreaIdString(propIdAreaId)); 332 } 333 sb.append("]"); 334 return sb.toString(); 335 } 336 337 /** 338 * Returns {@code true} if {@code propertyId} is defined in {@link VehicleProperty}. 339 * {@code false} otherwise. 340 */ isSystemPropertyId(int propertyId)341 private static boolean isSystemPropertyId(int propertyId) { 342 return (propertyId & VehiclePropertyGroup.MASK) == VehiclePropertyGroup.SYSTEM; 343 } 344 processOptionalFlagsString(@ullable String flagsString, String clazzName, int areaId)345 private static String processOptionalFlagsString(@Nullable String flagsString, String clazzName, 346 int areaId) { 347 StringBuilder stringBuilder = new StringBuilder(); 348 if (flagsString == null) { 349 stringBuilder.append("INVALID_").append(clazzName).append("_AREA_ID"); 350 } else { 351 stringBuilder.append(flagsString); 352 } 353 return stringBuilder.append("(0x").append(toHexString(areaId)).append(")").toString(); 354 } 355 } 356