1 /* 2 * Copyright (C) 2022 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.internal.property; 18 19 import android.annotation.SuppressLint; 20 import android.car.VehiclePropertyIds; 21 import android.car.hardware.CarPropertyValue; 22 import android.car.hardware.property.VehicleHalStatusCode; 23 import android.car.hardware.property.VehicleHalStatusCode.VehicleHalStatusCodeInt; 24 import android.util.Log; 25 import android.util.SparseArray; 26 27 import java.lang.reflect.Field; 28 import java.lang.reflect.Modifier; 29 import java.util.Collection; 30 import java.util.concurrent.atomic.AtomicReference; 31 32 /** 33 * Helper class for CarPropertyService/CarPropertyManager. 34 * 35 * @hide 36 */ 37 public final class CarPropertyHelper { 38 private static final String TAG = CarPropertyHelper.class.getSimpleName(); 39 40 /** 41 * Status indicating no error. 42 * 43 * <p>This is not exposed to the client as this will be used only for deciding 44 * {@link GetPropertyCallback#onSuccess} or {@link GetPropertyCallback#onFailure} is called. 45 */ 46 public static final int STATUS_OK = 0; 47 48 /** 49 * Error indicating that too many sync operation is ongoing, caller should try again after 50 * some time. 51 */ 52 public static final int SYNC_OP_LIMIT_TRY_AGAIN = -1; 53 54 // These are the same values as defined in VHAL interface. 55 private static final int VEHICLE_PROPERTY_GROUP_MASK = 0xf0000000; 56 private static final int VEHICLE_PROPERTY_GROUP_VENDOR = 0x20000000; 57 58 private static final int SYSTEM_ERROR_CODE_MASK = 0xffff; 59 private static final int VENDOR_ERROR_CODE_SHIFT = 16; 60 61 /* 62 * Used to cache the mapping of property Id integer values into property name strings. This 63 * will be initialized during the first usage. 64 */ 65 private static final AtomicReference<SparseArray<String>> sPropertyIdToPropertyNameHolder = 66 new AtomicReference<>(); 67 68 /** 69 * CarPropertyHelper only contains static fields and methods and must never be instantiated. 70 */ CarPropertyHelper()71 private CarPropertyHelper() { 72 throw new IllegalArgumentException("Must never be called"); 73 } 74 75 /** 76 * Returns whether the property ID is supported by the current Car Service version. 77 */ isSupported(int propertyId)78 public static boolean isSupported(int propertyId) { 79 return isSystemProperty(propertyId) || isVendorProperty(propertyId); 80 } 81 82 /** 83 * Gets a user-friendly representation of a property. 84 */ toString(int propertyId)85 public static String toString(int propertyId) { 86 String name = cachePropertyIdsToNameMapping().get(propertyId); 87 return name != null ? name : "0x" + Integer.toHexString(propertyId); 88 } 89 90 /** 91 * Gets a user-friendly representation of a list of properties. 92 */ propertyIdsToString(Collection<Integer> propertyIds)93 public static String propertyIdsToString(Collection<Integer> propertyIds) { 94 String names = "["; 95 boolean first = true; 96 for (int propertyId : propertyIds) { 97 if (first) { 98 first = false; 99 } else { 100 names += ", "; 101 } 102 names += toString(propertyId); 103 } 104 return names + "]"; 105 } 106 107 /** 108 * Returns the system error code contained in the error code returned from VHAL. 109 */ 110 @SuppressLint("WrongConstant") getVhalSystemErrorCode(int vhalErrorCode)111 public static @VehicleHalStatusCodeInt int getVhalSystemErrorCode(int vhalErrorCode) { 112 return vhalErrorCode & SYSTEM_ERROR_CODE_MASK; 113 } 114 115 /** 116 * Returns the vendor error code contained in the error code returned from VHAL. 117 */ getVhalVendorErrorCode(int vhalErrorCode)118 public static int getVhalVendorErrorCode(int vhalErrorCode) { 119 return vhalErrorCode >>> VENDOR_ERROR_CODE_SHIFT; 120 } 121 122 /** 123 * Returns {@code true} if {@code vehicleHalStatusCode} is one of the not available 124 * {@link VehicleHalStatusCode} values}. Otherwise returns {@code false}. 125 */ isNotAvailableVehicleHalStatusCode( @ehicleHalStatusCodeInt int vehicleHalStatusCode)126 public static boolean isNotAvailableVehicleHalStatusCode( 127 @VehicleHalStatusCodeInt int vehicleHalStatusCode) { 128 switch (vehicleHalStatusCode) { 129 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE: 130 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED: 131 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW: 132 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH: 133 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_POOR_VISIBILITY: 134 case VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY: 135 return true; 136 default: 137 return false; 138 } 139 } 140 cachePropertyIdsToNameMapping()141 private static SparseArray<String> cachePropertyIdsToNameMapping() { 142 SparseArray<String> propertyIdsToNameMapping = sPropertyIdToPropertyNameHolder.get(); 143 if (propertyIdsToNameMapping == null) { 144 propertyIdsToNameMapping = getPropertyIdsToNameMapping(); 145 sPropertyIdToPropertyNameHolder.compareAndSet(null, propertyIdsToNameMapping); 146 } 147 return propertyIdsToNameMapping; 148 } 149 150 /** 151 * Creates a SparseArray mapping property Ids to their String representations 152 * directly from this class. 153 */ getPropertyIdsToNameMapping()154 private static SparseArray<String> getPropertyIdsToNameMapping() { 155 Field[] classFields = VehiclePropertyIds.class.getDeclaredFields(); 156 SparseArray<String> propertyIdsToNameMapping = new SparseArray<>(classFields.length); 157 for (int i = 0; i < classFields.length; i++) { 158 Field candidateField = classFields[i]; 159 try { 160 if (isPropertyId(candidateField)) { 161 propertyIdsToNameMapping 162 .put(candidateField.getInt(null), candidateField.getName()); 163 } 164 } catch (IllegalAccessException e) { 165 Log.wtf(TAG, "Failed trying to find value for " + candidateField.getName(), e); 166 } 167 } 168 return propertyIdsToNameMapping; 169 } 170 isPropertyId(Field field)171 private static boolean isPropertyId(Field field) { 172 // We only want public static final int values 173 return field.getType() == int.class 174 && field.getModifiers() == (Modifier.STATIC | Modifier.FINAL | Modifier.PUBLIC); 175 } 176 177 /** 178 * Returns whether the property ID is defined as a system property. 179 */ isSystemProperty(int propertyId)180 private static boolean isSystemProperty(int propertyId) { 181 return propertyId != VehiclePropertyIds.INVALID 182 && cachePropertyIdsToNameMapping().contains(propertyId); 183 } 184 185 /** 186 * Returns whether the property ID is defined as a vendor property. 187 */ isVendorProperty(int propertyId)188 private static boolean isVendorProperty(int propertyId) { 189 return (propertyId & VEHICLE_PROPERTY_GROUP_MASK) == VEHICLE_PROPERTY_GROUP_VENDOR; 190 } 191 192 193 /** 194 * Gets the default value for a {@link CarPropertyValue} class type. 195 */ getDefaultValue(Class<T> clazz)196 public static <T> T getDefaultValue(Class<T> clazz) { 197 if (clazz.equals(Boolean.class)) { 198 return (T) Boolean.FALSE; 199 } 200 if (clazz.equals(Integer.class)) { 201 return (T) Integer.valueOf(0); 202 } 203 if (clazz.equals(Long.class)) { 204 return (T) Long.valueOf(0); 205 } 206 if (clazz.equals(Float.class)) { 207 return (T) Float.valueOf(0f); 208 } 209 if (clazz.equals(Integer[].class)) { 210 return (T) new Integer[0]; 211 } 212 if (clazz.equals(Long[].class)) { 213 return (T) new Long[0]; 214 } 215 if (clazz.equals(Float[].class)) { 216 return (T) new Float[0]; 217 } 218 if (clazz.equals(byte[].class)) { 219 return (T) new byte[0]; 220 } 221 if (clazz.equals(Object[].class)) { 222 return (T) new Object[0]; 223 } 224 if (clazz.equals(String.class)) { 225 return (T) new String(""); 226 } 227 throw new IllegalArgumentException("Unexpected class: " + clazz); 228 } 229 } 230