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