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 android.annotation.Nullable; 20 import android.car.VehicleAreaType; 21 import android.car.builtin.util.Slogf; 22 import android.car.feature.Flags; 23 import android.car.hardware.CarPropertyConfig; 24 import android.car.hardware.property.AreaIdConfig; 25 import android.hardware.automotive.vehicle.AnnotationsForVehicleProperty; 26 import android.hardware.automotive.vehicle.HasSupportedValueInfo; 27 import android.hardware.automotive.vehicle.VehicleArea; 28 import android.hardware.automotive.vehicle.VehicleProperty; 29 import android.hardware.automotive.vehicle.VehiclePropertyAccess; 30 import android.hardware.automotive.vehicle.VehiclePropertyChangeMode; 31 import android.hardware.automotive.vehicle.VehiclePropertyType; 32 33 import com.android.car.CarLog; 34 import com.android.car.hal.property.PropertyHalServiceConfigs; 35 import com.android.internal.annotations.VisibleForTesting; 36 37 import java.util.ArrayList; 38 import java.util.List; 39 import java.util.Set; 40 41 /** 42 * HalPropConfig represents a vehicle property config. 43 */ 44 public abstract class HalPropConfig { 45 /** 46 * The expected length for config array for HVAC_TEMPERATURE_SET. 47 */ 48 public static final int HVAC_CONFIG_ARRAY_LENGTH = 6; 49 50 /** 51 * The @legacy_supported_values_in_config annotation defined in VehicleProperty.aidl. 52 */ 53 public static final String ANNOTATION_SUPPORTED_VALUES_IN_CONFIG = 54 "legacy_supported_values_in_config"; 55 56 /** 57 * The @data_enum annotation defined in VehicleProperty.aidl. 58 */ 59 public static final String ANNOTATION_DATA_ENUM = "data_enum"; 60 61 private static final String TAG = CarLog.tagFor(HalPropConfig.class); 62 63 /** 64 * Get the property ID. 65 */ getPropId()66 public abstract int getPropId(); 67 68 /** 69 * Get the access mode. 70 */ getAccess()71 public abstract int getAccess(); 72 73 /** 74 * Get the change mode. 75 */ getChangeMode()76 public abstract int getChangeMode(); 77 78 /** 79 * Get the area configs. 80 */ getAreaConfigs()81 public abstract HalAreaConfig[] getAreaConfigs(); 82 83 /** 84 * Get the config array. 85 */ getConfigArray()86 public abstract int[] getConfigArray(); 87 88 /** 89 * Get the config string. 90 */ getConfigString()91 public abstract String getConfigString(); 92 93 /** 94 * Get the min sample rate. 95 */ getMinSampleRate()96 public abstract float getMinSampleRate(); 97 98 /** 99 * Get the max sample rate. 100 */ getMaxSampleRate()101 public abstract float getMaxSampleRate(); 102 103 /** 104 * Converts to AIDL or HIDL VehiclePropConfig. 105 */ toVehiclePropConfig()106 public abstract Object toVehiclePropConfig(); 107 108 /** 109 * Converts {@link HalPropConfig} to {@link CarPropertyConfig}. 110 * 111 * @param mgrPropertyId The Property ID used by Car Property Manager, different from the 112 * property ID used by VHAL. 113 */ toCarPropertyConfig(int mgrPropertyId, PropertyHalServiceConfigs propertyHalServiceConfigs)114 public CarPropertyConfig<?> toCarPropertyConfig(int mgrPropertyId, 115 PropertyHalServiceConfigs propertyHalServiceConfigs) { 116 return toCarPropertyConfig(mgrPropertyId, propertyHalServiceConfigs, 117 /* isVhalPropId= */ false); 118 } 119 120 /** 121 * Converts {@link HalPropConfig} to {@link CarPropertyConfig}. 122 * 123 * @param mgrPropertyId The Property ID used by Car Property Manager, different from the 124 * property ID used by VHAL. 125 */ toCarPropertyConfig(int mgrPropertyId, PropertyHalServiceConfigs propertyHalServiceConfigs, boolean isVhalPropId)126 public CarPropertyConfig<?> toCarPropertyConfig(int mgrPropertyId, 127 PropertyHalServiceConfigs propertyHalServiceConfigs, boolean isVhalPropId) { 128 int propId = getPropId(); 129 int areaType = getVehicleAreaType(propId & VehicleArea.MASK); 130 Class<?> clazz = CarPropertyUtils.getJavaClass(propId & VehiclePropertyType.MASK); 131 132 int access = getAccess(); 133 CarPropertyConfig.Builder carPropertyConfigBuilder = CarPropertyConfig.newBuilder(clazz, 134 mgrPropertyId, areaType).setAccess(access).setChangeMode( 135 getChangeMode()).setConfigString(getConfigString()); 136 137 float maxSampleRate = 0f; 138 float minSampleRate = 0f; 139 if (getChangeMode() == CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS) { 140 maxSampleRate = getMaxSampleRate(); 141 minSampleRate = getMinSampleRate(); 142 } 143 carPropertyConfigBuilder.setMinSampleRate(minSampleRate).setMaxSampleRate(maxSampleRate); 144 145 int[] configIntArray = getConfigArray(); 146 ArrayList<Integer> configArray = new ArrayList<>(configIntArray.length); 147 long[] supportedEnumValues = null; 148 boolean shouldConfigArrayDefineSupportedEnumValues = 149 shouldConfigArrayDefineSupportedEnumValues(propId); 150 if (shouldConfigArrayDefineSupportedEnumValues) { 151 supportedEnumValues = new long[configIntArray.length]; 152 } 153 for (int i = 0; i < configIntArray.length; i++) { 154 configArray.add(configIntArray[i]); 155 if (shouldConfigArrayDefineSupportedEnumValues) { 156 supportedEnumValues[i] = (long) configIntArray[i]; 157 } 158 } 159 carPropertyConfigBuilder.setConfigArray(configArray); 160 161 HalAreaConfig[] halAreaConfigs = getAreaConfigs(); 162 var allPossibleEnumValues = propertyHalServiceConfigs 163 .getAllPossibleSupportedEnumValues(getPropId()); 164 if (halAreaConfigs.length == 0) { 165 carPropertyConfigBuilder.addAreaIdConfig(generateAreaIdConfig(clazz, 166 allPossibleEnumValues, /* areaId= */ 0, 167 /* minInt32Value= */ 0, /* maxInt32Value= */ 0, 168 /* minFloatValue= */ 0, /* maxFloatValue= */ 0, 169 /* minInt64Value= */ 0, /* maxInt64Value= */ 0, 170 supportedEnumValues, /* supportVariableUpdateRate= */ false, access, 171 /* hasSupportedValueInfo= */ null)); 172 } else { 173 for (HalAreaConfig halAreaConfig : halAreaConfigs) { 174 if (!shouldConfigArrayDefineSupportedEnumValues) { 175 supportedEnumValues = halAreaConfig.getSupportedEnumValues(); 176 } 177 int areaAccess = (halAreaConfig.getAccess() == VehiclePropertyAccess.NONE) 178 ? access : halAreaConfig.getAccess(); 179 carPropertyConfigBuilder.addAreaIdConfig( 180 generateAreaIdConfig(clazz, allPossibleEnumValues, 181 halAreaConfig.getAreaId(), 182 halAreaConfig.getMinInt32Value(), halAreaConfig.getMaxInt32Value(), 183 halAreaConfig.getMinFloatValue(), halAreaConfig.getMaxFloatValue(), 184 halAreaConfig.getMinInt64Value(), halAreaConfig.getMaxInt64Value(), 185 supportedEnumValues, halAreaConfig.isVariableUpdateRateSupported(), 186 areaAccess, halAreaConfig.getHasSupportedValueInfo())); 187 } 188 } 189 carPropertyConfigBuilder.setPropertyIdIsSimulationPropId(isVhalPropId); 190 return carPropertyConfigBuilder.build(); 191 } 192 193 /** 194 * Whether the property is a enum property and config array should be used to define supported 195 * values. 196 */ 197 @VisibleForTesting shouldConfigArrayDefineSupportedEnumValues(int halPropId)198 public static boolean shouldConfigArrayDefineSupportedEnumValues(int halPropId) { 199 var annotations = AnnotationsForVehicleProperty.values.get(halPropId); 200 if (annotations == null) { 201 return false; 202 } 203 return annotations.contains(ANNOTATION_SUPPORTED_VALUES_IN_CONFIG) 204 && annotations.contains(ANNOTATION_DATA_ENUM); 205 } 206 generateAreaIdConfig(Class<?> clazz, @Nullable Set<Integer> allPossibleEnumValues, int areaId, int minInt32Value, int maxInt32Value, float minFloatValue, float maxFloatValue, long minInt64Value, long maxInt64Value, long[] supportedEnumValues, boolean supportVariableUpdateRate, int access, @Nullable HasSupportedValueInfo hasSupportedValueInfo)207 private AreaIdConfig generateAreaIdConfig(Class<?> clazz, 208 @Nullable Set<Integer> allPossibleEnumValues, int areaId, int minInt32Value, 209 int maxInt32Value, float minFloatValue, float maxFloatValue, long minInt64Value, 210 long maxInt64Value, long[] supportedEnumValues, boolean supportVariableUpdateRate, 211 int access, @Nullable HasSupportedValueInfo hasSupportedValueInfo) { 212 AreaIdConfig.Builder areaIdConfigBuilder = Flags.areaIdConfigAccess() 213 ? new AreaIdConfig.Builder(access, areaId) 214 : new AreaIdConfig.Builder(areaId); 215 if (classMatched(Integer.class, clazz)) { 216 if ((minInt32Value != 0 || maxInt32Value != 0)) { 217 areaIdConfigBuilder.setMinValue(minInt32Value).setMaxValue(maxInt32Value); 218 } 219 // The supported enum values for {@code HVAC_FAN_DIRECTION} are specified by 220 // {@code HVAC_FAN_DIRECTION_AVAILABLE} and the supportedEnumValues are never populated. 221 if (getChangeMode() == VehiclePropertyChangeMode.ON_CHANGE && 222 getPropId() != VehicleProperty.HVAC_FAN_DIRECTION) { 223 if (supportedEnumValues != null && supportedEnumValues.length > 0) { 224 List<Integer> managerSupportedEnumValues = new ArrayList<>( 225 supportedEnumValues.length); 226 for (int i = 0; i < supportedEnumValues.length; i++) { 227 managerSupportedEnumValues.add((int) supportedEnumValues[i]); 228 } 229 areaIdConfigBuilder.setSupportedEnumValues(managerSupportedEnumValues); 230 } else if (allPossibleEnumValues != null) { 231 areaIdConfigBuilder.setSupportedEnumValues( 232 new ArrayList(allPossibleEnumValues)); 233 } 234 } 235 } else if (classMatched(Float.class, clazz) && (minFloatValue != 0 || maxFloatValue != 0)) { 236 areaIdConfigBuilder.setMinValue(minFloatValue).setMaxValue(maxFloatValue); 237 } else if (classMatched(Long.class, clazz) && (minInt64Value != 0 || maxInt64Value != 0)) { 238 areaIdConfigBuilder.setMinValue(minInt64Value).setMaxValue(maxInt64Value); 239 } 240 areaIdConfigBuilder.setSupportVariableUpdateRate(supportVariableUpdateRate); 241 if (hasSupportedValueInfo != null) { 242 if (hasSupportedValueInfo.hasMinSupportedValue) { 243 areaIdConfigBuilder.setHasMinSupportedValue(true); 244 } 245 if (hasSupportedValueInfo.hasMaxSupportedValue) { 246 areaIdConfigBuilder.setHasMaxSupportedValue(true); 247 } 248 if (hasSupportedValueInfo.hasSupportedValuesList) { 249 areaIdConfigBuilder.setHasSupportedValuesList(true); 250 } 251 } else { 252 // Special logic for properties whose min/max value or supported values list 253 // may be specified through some other way. 254 switch (getPropId()) { 255 case VehicleProperty.HVAC_FAN_DIRECTION: 256 // The supported values for {@code HVAC_FAN_DIRECTION} are specified by 257 // {@code HVAC_FAN_DIRECTION_AVAILABLE}. 258 // If HVAC_FAN_DIRECTION is supported, HVAC_FAN_DIRECTION_AVAILABLE must be 259 // supported. 260 areaIdConfigBuilder.setHasSupportedValuesList(true); 261 break; 262 case VehicleProperty.HVAC_TEMPERATURE_SET: 263 // The supported values for {@code HVAC_TEMPERATURE_SET} might be specified by 264 // config array. 265 int configArrayLength = getConfigArray().length; 266 if (configArrayLength == HVAC_CONFIG_ARRAY_LENGTH) { 267 areaIdConfigBuilder.setHasSupportedValuesList(true); 268 } else if (configArrayLength != 0) { 269 Slogf.e(TAG, "Unexpected config array length for HVAC_TEMPERATURE_SET, " 270 + "expect: %d, actual config array: %s", HVAC_CONFIG_ARRAY_LENGTH, 271 getConfigArray()); 272 } 273 break; 274 case VehicleProperty.EV_CHARGE_CURRENT_DRAW_LIMIT: 275 // The max value for {@code EV_CHARGE_CURRENT_DRAW_LIMIT} is specified by config 276 // array, the min value is set to 0. 277 if (getConfigArray().length > 0) { 278 areaIdConfigBuilder.setHasMinSupportedValue(true); 279 areaIdConfigBuilder.setHasMaxSupportedValue(true); 280 } else { 281 Slogf.e(TAG, "Expect at least one element in config array for " 282 + "EV_CHARGE_CURRENT_DRAW_LIMIT"); 283 } 284 break; 285 } 286 287 // If the property has annotation: legacy_supported_values_in_config, its supported 288 // values are specified by config array. 289 var annotations = AnnotationsForVehicleProperty.values.get(getPropId()); 290 if (annotations != null && annotations.contains( 291 ANNOTATION_SUPPORTED_VALUES_IN_CONFIG) && getConfigArray().length > 0) { 292 areaIdConfigBuilder.setHasSupportedValuesList(true); 293 } 294 } 295 return areaIdConfigBuilder.build(); 296 } 297 getVehicleAreaType(int halArea)298 private static @VehicleAreaType.VehicleAreaTypeValue int getVehicleAreaType(int halArea) { 299 switch (halArea) { 300 case VehicleArea.GLOBAL: 301 return VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL; 302 case VehicleArea.SEAT: 303 return VehicleAreaType.VEHICLE_AREA_TYPE_SEAT; 304 case VehicleArea.DOOR: 305 return VehicleAreaType.VEHICLE_AREA_TYPE_DOOR; 306 case VehicleArea.WINDOW: 307 return VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW; 308 case VehicleArea.MIRROR: 309 return VehicleAreaType.VEHICLE_AREA_TYPE_MIRROR; 310 case VehicleArea.WHEEL: 311 return VehicleAreaType.VEHICLE_AREA_TYPE_WHEEL; 312 case VehicleArea.VENDOR: 313 return VehicleAreaType.VEHICLE_AREA_TYPE_VENDOR; 314 default: 315 throw new RuntimeException("Unsupported area type " + halArea); 316 } 317 } 318 classMatched(Class<?> class1, Class<?> class2)319 private static boolean classMatched(Class<?> class1, Class<?> class2) { 320 return class1 == class2 || class1.getComponentType() == class2; 321 } 322 } 323