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 android.car.cts.utils; 18 19 import static android.car.cts.utils.ShellPermissionUtils.CHECK_MODE_ASSUME; 20 import static android.car.cts.utils.ShellPermissionUtils.runWithShellPermissionIdentity; 21 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 22 23 import static com.google.common.truth.Truth.assertThat; 24 import static com.google.common.truth.Truth.assertWithMessage; 25 26 import static junit.framework.Assert.fail; 27 28 import static org.junit.Assert.assertThrows; 29 import static org.junit.Assume.assumeThat; 30 import static org.junit.Assume.assumeTrue; 31 32 import android.car.VehicleAreaDoor; 33 import android.car.VehicleAreaMirror; 34 import android.car.VehicleAreaSeat; 35 import android.car.VehicleAreaType; 36 import android.car.VehicleAreaWheel; 37 import android.car.VehicleAreaWindow; 38 import android.car.VehiclePropertyIds; 39 import android.car.VehiclePropertyType; 40 import android.car.feature.Flags; 41 import android.car.hardware.CarHvacFanDirection; 42 import android.car.hardware.CarPropertyConfig; 43 import android.car.hardware.CarPropertyValue; 44 import android.car.hardware.property.AreaIdConfig; 45 import android.car.hardware.property.CarInternalErrorException; 46 import android.car.hardware.property.CarPropertyManager; 47 import android.car.hardware.property.CarPropertyManager.GetPropertyCallback; 48 import android.car.hardware.property.CarPropertyManager.GetPropertyRequest; 49 import android.car.hardware.property.CarPropertyManager.GetPropertyResult; 50 import android.car.hardware.property.CarPropertyManager.PropertyAsyncError; 51 import android.car.hardware.property.CarPropertyManager.SetPropertyCallback; 52 import android.car.hardware.property.CarPropertyManager.SetPropertyRequest; 53 import android.car.hardware.property.CarPropertyManager.SetPropertyResult; 54 import android.car.hardware.property.CarPropertyManager.SupportedValuesChangeCallback; 55 import android.car.hardware.property.ErrorState; 56 import android.car.hardware.property.MinMaxSupportedValue; 57 import android.car.hardware.property.PropertyNotAvailableAndRetryException; 58 import android.car.hardware.property.PropertyNotAvailableErrorCode; 59 import android.car.hardware.property.PropertyNotAvailableException; 60 import android.car.hardware.property.Subscription; 61 import android.content.Context; 62 import android.os.Build; 63 import android.os.SystemClock; 64 import android.util.ArrayMap; 65 import android.util.Log; 66 import android.util.SparseArray; 67 import android.util.SparseIntArray; 68 69 import androidx.annotation.Nullable; 70 import androidx.test.platform.app.InstrumentationRegistry; 71 72 import com.android.bedstead.nene.TestApis; 73 import com.android.bedstead.permissions.PermissionContext; 74 import com.android.internal.annotations.GuardedBy; 75 76 import com.google.common.collect.ImmutableList; 77 import com.google.common.collect.ImmutableSet; 78 import com.google.common.collect.Sets; 79 80 import org.hamcrest.Matchers; 81 import org.junit.AssumptionViolatedException; 82 83 import java.time.Duration; 84 import java.util.ArrayList; 85 import java.util.Arrays; 86 import java.util.Collection; 87 import java.util.Collections; 88 import java.util.List; 89 import java.util.Optional; 90 import java.util.Set; 91 import java.util.concurrent.CountDownLatch; 92 import java.util.concurrent.Executor; 93 import java.util.concurrent.TimeUnit; 94 import java.util.stream.Collectors; 95 import java.util.stream.IntStream; 96 97 /** 98 * A class for verifying the implementation for one VHAL property. 99 * 100 * @param <T> The type for the property. 101 */ 102 public class VehiclePropertyVerifier<T> { 103 private static final String STEP_VERIFY_READ_APIS_PREFIX = "verifyReadApis"; 104 private static final String STEP_VERIFY_WRITE_APIS_PREFIX = "verifyWriteApis"; 105 106 private static final CarSvcPropsParser CAR_SVC_PROPS_PARSER = new CarSvcPropsParser(); 107 108 /** A step to verify {@link CarPropertyManager#getCarPropertyConfig} returns expected config. */ 109 public static final String STEP_VERIFY_PROPERTY_CONFIG = "verifyPropertyConfig"; 110 111 /** A step to verify {@link CarPropertyManager#isPropertyAvailable}. */ 112 public static final String STEP_VERIFY_IS_PROPERTY_AVAILABLE = "isPropertyAvailable"; 113 114 /** A step to verify that expected exceptions are thrown when missing permission. */ 115 public static final String STEP_VERIFY_PERMISSION_NOT_GRANTED_EXCEPTION = 116 "verifyPermissionNotGrantedException"; 117 118 /** 119 * A step to verify {@link CarPropertyManager#getProperty}, {@link 120 * CarPropertyManager#getIntProperty}, {@link CarPropertyManager#getBooleanProperty}, {@link 121 * CarPropertyManager#getFloatProperty}, {@link CarPropertyManager#getIntArrayProperty}. 122 */ 123 public static final String STEP_VERIFY_READ_APIS_GET_PROPERTY_SYNC = 124 STEP_VERIFY_READ_APIS_PREFIX + ".getProperty"; 125 126 /** A step to verify {@link CarPropertyManager#getPropertiesAsync}. */ 127 public static final String STEP_VERIFY_READ_APIS_GET_PROPERTY_ASYNC = 128 STEP_VERIFY_READ_APIS_PREFIX + ".getPropertiesAsync"; 129 130 /** A step to verify {@link CarPropertyManager#subscribePropertyEvents}. */ 131 public static final String STEP_VERIFY_READ_APIS_SUBSCRIBE = 132 STEP_VERIFY_READ_APIS_PREFIX + ".subscribePropertyEvents"; 133 134 /** A step to verify {@link CarPropertyManager#getMinMaxSupportedValue}. */ 135 public static final String STEP_VERIFY_READ_APIS_GET_MIN_MAX_SUPPORTED_VALUE = 136 STEP_VERIFY_READ_APIS_PREFIX + ".getMinMaxSupportedValue"; 137 138 /** A step to verify {@link CarPropertyManager#getSupportedValuesList}. */ 139 public static final String STEP_VERIFY_READ_APIS_GET_SUPPORTED_VALUES_LIST = 140 STEP_VERIFY_READ_APIS_PREFIX + ".getSupportedValuesList"; 141 /** 142 * A step to verify {@link CarPropertyManager#registerSupportedValuesChangeCallback} and {@link 143 * CarPropertyManager#unregisterSupportedValuesChangeCallback} 144 */ 145 public static final String STEP_VERIFY_READ_APIS_REG_UNREG_SUPPORTED_VALUES_CHANGE = 146 STEP_VERIFY_READ_APIS_PREFIX + ".regUnregSupportedValuesChangeCallback"; 147 148 /** 149 * A step to verify that for ADAS properties, if the feature is disabled, the property must 150 * report error state. 151 * 152 * <p>This step is skipped for non-adas properties. 153 */ 154 public static final String STEP_VERIFY_READ_APIS_DISABLE_ADAS_FEATURE_VERIFY_STATE = 155 STEP_VERIFY_READ_APIS_PREFIX + ".disableAdasFeatureVerifyState"; 156 157 /** A step to verify {@link CarPropertyManager#setProperty}. */ 158 public static final String STEP_VERIFY_WRITE_APIS_SET_PROPERTY_SYNC = 159 STEP_VERIFY_WRITE_APIS_PREFIX + ".setProperty"; 160 161 /** A step to verify {@link CarPropertyManager#setPropertiesAsync}. */ 162 public static final String STEP_VERIFY_WRITE_APIS_SET_PROPERTY_ASYNC = 163 STEP_VERIFY_WRITE_APIS_PREFIX + ".setPropertiesAsync"; 164 165 /** 166 * A step to verify that for ADAS properties, if the feature is disabled, the property must 167 * report error state. 168 * 169 * <p>This step is skipped for non-adas properties. 170 */ 171 public static final String STEP_VERIFY_WRITE_APIS_DISABLE_ADAS_FEATURE_VERIFY_STATE = 172 STEP_VERIFY_WRITE_APIS_PREFIX + ".disableAdasFeatureVerifyState"; 173 174 /** 175 * A step to verify that for HVAC power dependant properties, getting should be unavailable when 176 * HVAC power is off. 177 */ 178 public static final String STEP_VERIFY_READ_APIS_DISABLE_HVAC_GET_NOT_AVAILABLE = 179 STEP_VERIFY_READ_APIS_PREFIX + ".turnOffHvacPowerGetNotAvailable"; 180 181 /** 182 * A step to verify that for HVAC power dependant properties, setting should be unavailable when 183 * HVAC power is off. 184 */ 185 public static final String STEP_VERIFY_WRITE_APIS_DISABLE_HVAC_SET_NOT_AVAILABLE = 186 STEP_VERIFY_WRITE_APIS_PREFIX + ".turnOffHvacPowerSetNotAvailable"; 187 188 /** 189 * A step to verify that for read/write properties, if the caller only has read permission, 190 * caller cannot write. 191 */ 192 public static final String STEP_VERIFY_READ_PERMISSION_CANNOT_WRITE = 193 "verifyReadPermissionCannotWrite"; 194 195 /** 196 * A step to verify that for read/write properties, if the caller only has write permission, 197 * caller cannot read. 198 */ 199 public static final String STEP_VERIFY_WRITE_PERMISSION_CANNOT_READ = 200 "verifyWritePermissionCannotRead"; 201 202 private static final String TAG = VehiclePropertyVerifier.class.getSimpleName(); 203 private static final String CAR_PROPERTY_VALUE_SOURCE_GETTER = "Getter"; 204 private static final String CAR_PROPERTY_VALUE_SOURCE_CALLBACK = "Callback"; 205 private static final int GLOBAL_AREA_ID = 0; 206 private static final float FLOAT_INEQUALITY_THRESHOLD = 0.00001f; 207 private static final int VENDOR_ERROR_CODE_MINIMUM_VALUE = 0x0; 208 private static final int VENDOR_ERROR_CODE_MAXIMUM_VALUE = 0xffff; 209 private static final int SET_PROPERTY_CALLBACK_TIMEOUT_SEC = 5; 210 private static final long CPM_ACTION_DELAY_MS = 20; 211 private static final Object sLock = new Object(); 212 private static final ImmutableSet<Integer> WHEEL_AREAS = 213 ImmutableSet.of( 214 VehicleAreaWheel.WHEEL_LEFT_FRONT, VehicleAreaWheel.WHEEL_LEFT_REAR, 215 VehicleAreaWheel.WHEEL_RIGHT_FRONT, VehicleAreaWheel.WHEEL_RIGHT_REAR); 216 private static final ImmutableSet<Integer> ALL_POSSIBLE_WHEEL_AREA_IDS = 217 generateAllPossibleAreaIds(WHEEL_AREAS); 218 private static final ImmutableSet<Integer> WINDOW_AREAS = 219 ImmutableSet.of( 220 VehicleAreaWindow.WINDOW_FRONT_WINDSHIELD, 221 VehicleAreaWindow.WINDOW_REAR_WINDSHIELD, 222 VehicleAreaWindow.WINDOW_ROW_1_LEFT, VehicleAreaWindow.WINDOW_ROW_1_RIGHT, 223 VehicleAreaWindow.WINDOW_ROW_2_LEFT, VehicleAreaWindow.WINDOW_ROW_2_RIGHT, 224 VehicleAreaWindow.WINDOW_ROW_3_LEFT, VehicleAreaWindow.WINDOW_ROW_3_RIGHT, 225 VehicleAreaWindow.WINDOW_ROOF_TOP_1, VehicleAreaWindow.WINDOW_ROOF_TOP_2); 226 private static final ImmutableSet<Integer> ALL_POSSIBLE_WINDOW_AREA_IDS = 227 generateAllPossibleAreaIds(WINDOW_AREAS); 228 private static final ImmutableSet<Integer> MIRROR_AREAS = 229 ImmutableSet.of( 230 VehicleAreaMirror.MIRROR_DRIVER_LEFT, 231 VehicleAreaMirror.MIRROR_DRIVER_RIGHT, 232 VehicleAreaMirror.MIRROR_DRIVER_CENTER); 233 private static final ImmutableSet<Integer> ALL_POSSIBLE_MIRROR_AREA_IDS = 234 generateAllPossibleAreaIds(MIRROR_AREAS); 235 private static final ImmutableSet<Integer> SEAT_AREAS = 236 ImmutableSet.of( 237 VehicleAreaSeat.SEAT_ROW_1_LEFT, 238 VehicleAreaSeat.SEAT_ROW_1_CENTER, 239 VehicleAreaSeat.SEAT_ROW_1_RIGHT, 240 VehicleAreaSeat.SEAT_ROW_2_LEFT, 241 VehicleAreaSeat.SEAT_ROW_2_CENTER, 242 VehicleAreaSeat.SEAT_ROW_2_RIGHT, 243 VehicleAreaSeat.SEAT_ROW_3_LEFT, 244 VehicleAreaSeat.SEAT_ROW_3_CENTER, 245 VehicleAreaSeat.SEAT_ROW_3_RIGHT); 246 private static final ImmutableSet<Integer> ALL_POSSIBLE_SEAT_AREA_IDS = 247 generateAllPossibleAreaIds(SEAT_AREAS); 248 private static final ImmutableSet<Integer> DOOR_AREAS = 249 ImmutableSet.of( 250 VehicleAreaDoor.DOOR_ROW_1_LEFT, VehicleAreaDoor.DOOR_ROW_1_RIGHT, 251 VehicleAreaDoor.DOOR_ROW_2_LEFT, VehicleAreaDoor.DOOR_ROW_2_RIGHT, 252 VehicleAreaDoor.DOOR_ROW_3_LEFT, VehicleAreaDoor.DOOR_ROW_3_RIGHT, 253 VehicleAreaDoor.DOOR_HOOD, VehicleAreaDoor.DOOR_REAR); 254 private static final ImmutableSet<Integer> ALL_POSSIBLE_DOOR_AREA_IDS = 255 generateAllPossibleAreaIds(DOOR_AREAS); 256 private static final ImmutableSet<Integer> PROPERTY_NOT_AVAILABLE_ERROR_CODES = 257 ImmutableSet.of( 258 PropertyNotAvailableErrorCode.NOT_AVAILABLE, 259 PropertyNotAvailableErrorCode.NOT_AVAILABLE_DISABLED, 260 PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_LOW, 261 PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_HIGH, 262 PropertyNotAvailableErrorCode.NOT_AVAILABLE_POOR_VISIBILITY, 263 PropertyNotAvailableErrorCode.NOT_AVAILABLE_SAFETY); 264 private static final boolean AREA_ID_CONFIG_ACCESS_FLAG = 265 isAtLeastV() && Flags.areaIdConfigAccess(); 266 private static final boolean CAR_PROPERTY_SUPPORTED_VALUE_FLAG = 267 isAtLeastB() && Flags.carPropertySupportedValue(); 268 private static final List<Integer> VALID_CAR_PROPERTY_VALUE_STATUSES = 269 Arrays.asList( 270 CarPropertyValue.STATUS_AVAILABLE, 271 CarPropertyValue.STATUS_UNAVAILABLE, 272 CarPropertyValue.STATUS_ERROR); 273 private static final List<Integer> VALID_SET_ERROR_CODES = 274 Arrays.asList( 275 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_TRY_AGAIN, 276 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_INVALID_ARG, 277 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_PROPERTY_NOT_AVAILABLE, 278 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_ACCESS_DENIED, 279 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN); 280 281 private static final CarPropertyValueCallback FAKE_CALLBACK = 282 new CarPropertyValueCallback( 283 /* propertyName= */ "", 284 new int[] {}, 285 /* totalCarPropertyValuesPerAreaId= */ 0, 286 /* timeoutMillis= */ 0); 287 288 private static Class<?> sExceptionClassOnGet; 289 private static Class<?> sExceptionClassOnSet; 290 291 @GuardedBy("sLock") 292 private static long sLastActionElapsedRealtimeNanos = 0; 293 294 private static boolean sIsCarPropertyConfigsCached; 295 // A static cache to store all property configs. This will be reused across multiple test cases 296 // in order to save time. 297 private static SparseArray<CarPropertyConfig<?>> sCachedCarPropertyConfigs = 298 new SparseArray<>(); 299 300 private final Context mContext = 301 InstrumentationRegistry.getInstrumentation().getTargetContext(); 302 private final CarPropertyManager mCarPropertyManager; 303 private final int mPropertyId; 304 private final String mPropertyName; 305 private final ImmutableSet<Integer> mAllowedAccessModes; 306 private final int mAreaType; 307 private final int mChangeMode; 308 private final Class<T> mPropertyType; 309 private final boolean mRequiredProperty; 310 private final Optional<ConfigArrayVerifier> mConfigArrayVerifier; 311 private final Optional<CarPropertyValueVerifier<T>> mCarPropertyValueVerifier; 312 private final Optional<AreaIdsVerifier> mAreaIdsVerifier; 313 private final Optional<CarPropertyConfigVerifier> mCarPropertyConfigVerifier; 314 private final Optional<Integer> mDependentOnPropertyId; 315 private final ImmutableSet<String> mDependentOnPropertyPermissions; 316 private final ImmutableSet<Integer> mPossibleConfigArrayValues; 317 private final boolean mEnumIsBitMap; 318 private final ImmutableSet<T> mAllPossibleEnumValues; 319 private final ImmutableSet<T> mAllPossibleUnwritableValues; 320 private final ImmutableSet<T> mAllPossibleUnavailableValues; 321 private final boolean mRequirePropertyValueToBeInConfigArray; 322 private final boolean mVerifySetterWithConfigArrayValues; 323 private final boolean mRequireMinMaxValues; 324 private final boolean mRequireMinValuesToBeZero; 325 private final boolean mRequireZeroToBeContainedInMinMaxRanges; 326 private final boolean mPossiblyDependentOnHvacPowerOn; 327 private final boolean mVerifyErrorStates; 328 // Delay to wait for power state to propagate to dependent properties. 329 private final int mPowerPropagationDelayMs; 330 private final ImmutableSet<String> mReadPermissions; 331 private final ImmutableList<ImmutableSet<String>> mWritePermissions; 332 private final VerifierContext mVerifierContext; 333 private final List<Integer> mStoredProperties = new ArrayList<>(); 334 335 private SparseArray<SparseArray<?>> mPropertyToAreaIdValues; 336 VehiclePropertyVerifier( CarPropertyManager carPropertyManager, int propertyId, ImmutableSet<Integer> allowedAccessModes, int areaType, int changeMode, Class<T> propertyType, boolean requiredProperty, Optional<ConfigArrayVerifier> configArrayVerifier, Optional<CarPropertyValueVerifier<T>> carPropertyValueVerifier, Optional<AreaIdsVerifier> areaIdsVerifier, Optional<CarPropertyConfigVerifier> carPropertyConfigVerifier, Optional<Integer> dependentPropertyId, ImmutableSet<String> dependentOnPropertyPermissions, ImmutableSet<Integer> possibleConfigArrayValues, boolean enumIsBitMap, ImmutableSet<T> allPossibleEnumValues, ImmutableSet<T> allPossibleUnwritableValues, ImmutableSet<T> allPossibleUnavailableValues, boolean requirePropertyValueToBeInConfigArray, boolean verifySetterWithConfigArrayValues, boolean requireMinMaxValues, boolean requireMinValuesToBeZero, boolean requireZeroToBeContainedInMinMaxRanges, boolean possiblyDependentOnHvacPowerOn, boolean verifyErrorStates, int powerPropagationDelayMs, ImmutableSet<String> readPermissions, ImmutableList<ImmutableSet<String>> writePermissions)337 private VehiclePropertyVerifier( 338 CarPropertyManager carPropertyManager, 339 int propertyId, 340 ImmutableSet<Integer> allowedAccessModes, 341 int areaType, 342 int changeMode, 343 Class<T> propertyType, 344 boolean requiredProperty, 345 Optional<ConfigArrayVerifier> configArrayVerifier, 346 Optional<CarPropertyValueVerifier<T>> carPropertyValueVerifier, 347 Optional<AreaIdsVerifier> areaIdsVerifier, 348 Optional<CarPropertyConfigVerifier> carPropertyConfigVerifier, 349 Optional<Integer> dependentPropertyId, 350 ImmutableSet<String> dependentOnPropertyPermissions, 351 ImmutableSet<Integer> possibleConfigArrayValues, 352 boolean enumIsBitMap, 353 ImmutableSet<T> allPossibleEnumValues, 354 ImmutableSet<T> allPossibleUnwritableValues, 355 ImmutableSet<T> allPossibleUnavailableValues, 356 boolean requirePropertyValueToBeInConfigArray, 357 boolean verifySetterWithConfigArrayValues, 358 boolean requireMinMaxValues, 359 boolean requireMinValuesToBeZero, 360 boolean requireZeroToBeContainedInMinMaxRanges, 361 boolean possiblyDependentOnHvacPowerOn, 362 boolean verifyErrorStates, 363 int powerPropagationDelayMs, 364 ImmutableSet<String> readPermissions, 365 ImmutableList<ImmutableSet<String>> writePermissions) { 366 assertWithMessage("Must set car property manager").that(carPropertyManager).isNotNull(); 367 mCarPropertyManager = carPropertyManager; 368 mPropertyId = propertyId; 369 mPropertyName = VehiclePropertyIds.toString(propertyId); 370 mAllowedAccessModes = allowedAccessModes; 371 mAreaType = areaType; 372 mChangeMode = changeMode; 373 mPropertyType = propertyType; 374 mRequiredProperty = requiredProperty; 375 mConfigArrayVerifier = configArrayVerifier; 376 mCarPropertyValueVerifier = carPropertyValueVerifier; 377 mAreaIdsVerifier = areaIdsVerifier; 378 mCarPropertyConfigVerifier = carPropertyConfigVerifier; 379 mDependentOnPropertyId = dependentPropertyId; 380 mDependentOnPropertyPermissions = dependentOnPropertyPermissions; 381 mPossibleConfigArrayValues = possibleConfigArrayValues; 382 mEnumIsBitMap = enumIsBitMap; 383 mAllPossibleEnumValues = allPossibleEnumValues; 384 mAllPossibleUnwritableValues = allPossibleUnwritableValues; 385 mAllPossibleUnavailableValues = allPossibleUnavailableValues; 386 mRequirePropertyValueToBeInConfigArray = requirePropertyValueToBeInConfigArray; 387 mVerifySetterWithConfigArrayValues = verifySetterWithConfigArrayValues; 388 mRequireMinMaxValues = requireMinMaxValues; 389 mRequireMinValuesToBeZero = requireMinValuesToBeZero; 390 mRequireZeroToBeContainedInMinMaxRanges = requireZeroToBeContainedInMinMaxRanges; 391 mPossiblyDependentOnHvacPowerOn = possiblyDependentOnHvacPowerOn; 392 mVerifyErrorStates = verifyErrorStates; 393 mPowerPropagationDelayMs = powerPropagationDelayMs; 394 mReadPermissions = readPermissions; 395 mWritePermissions = writePermissions; 396 mPropertyToAreaIdValues = new SparseArray<>(); 397 mVerifierContext = new VerifierContext(carPropertyManager); 398 } 399 400 /** Gets a new builder for the verifier. */ newBuilder( int propertyId, int access, int areaType, int changeMode, Class<T> propertyType)401 public static <T> Builder<T> newBuilder( 402 int propertyId, int access, int areaType, int changeMode, Class<T> propertyType) { 403 ImmutableSet.Builder<Integer> allowedAccessModesBuilder = new ImmutableSet.Builder<>(); 404 allowedAccessModesBuilder.add(access); 405 if (access == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) { 406 allowedAccessModesBuilder.add(CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ); 407 } 408 return new Builder<>( 409 propertyId, allowedAccessModesBuilder.build(), areaType, changeMode, propertyType); 410 } 411 412 /** 413 * Gets a new default verifier builder for the property Id. 414 * 415 * <p>The verifier verifies basic information, including access mode, area type, change mode 416 * property type and permissions. 417 */ newDefaultBuilder(int propertyId)418 public static <T> Builder<T> newDefaultBuilder(int propertyId) { 419 var vehiclePropertyIdInfo = CAR_SVC_PROPS_PARSER.getVehiclePropertyIdInfo(propertyId); 420 var builder = 421 new Builder<>( 422 propertyId, 423 vehiclePropertyIdInfo.allowedAccessModes, 424 vehiclePropertyIdInfo.areaType, 425 vehiclePropertyIdInfo.changeMode, 426 (Class<T>) vehiclePropertyIdInfo.propertyType); 427 for (String readPermission : vehiclePropertyIdInfo.readPermissions) { 428 builder.addReadPermission(readPermission); 429 } 430 for (ImmutableSet<String> allOfWritePermission : vehiclePropertyIdInfo.writePermissions) { 431 builder.addWritePermission(allOfWritePermission); 432 } 433 return builder; 434 } 435 436 /** Gets the ID of the property. */ getPropertyId()437 public int getPropertyId() { 438 return mPropertyId; 439 } 440 441 /** Gets the name for the property. */ getPropertyName()442 public String getPropertyName() { 443 return mPropertyName; 444 } 445 446 /** Gets the default value based on the type. */ 447 @Nullable getDefaultValue(Class<U> clazz)448 public static <U> U getDefaultValue(Class<U> clazz) { 449 if (clazz == Boolean.class) { 450 return (U) Boolean.TRUE; 451 } 452 if (clazz == Integer.class) { 453 return (U) (Integer) 2; 454 } 455 if (clazz == Float.class) { 456 return (U) (Float) 2.f; 457 } 458 if (clazz == Long.class) { 459 return (U) (Long) 2L; 460 } 461 if (clazz == Integer[].class) { 462 return (U) new Integer[] {2}; 463 } 464 if (clazz == Float[].class) { 465 return (U) new Float[] {2.f}; 466 } 467 if (clazz == Long[].class) { 468 return (U) new Long[] {2L}; 469 } 470 if (clazz == String.class) { 471 return (U) new String("test"); 472 } 473 if (clazz == byte[].class) { 474 return (U) new byte[] {(byte) 0xbe, (byte) 0xef}; 475 } 476 return null; 477 } 478 accessToString(int access)479 private static String accessToString(int access) { 480 switch (access) { 481 case CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_NONE: 482 return "VEHICLE_PROPERTY_ACCESS_NONE"; 483 case CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ: 484 return "VEHICLE_PROPERTY_ACCESS_READ"; 485 case CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE: 486 return "VEHICLE_PROPERTY_ACCESS_WRITE"; 487 case CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE: 488 return "VEHICLE_PROPERTY_ACCESS_READ_WRITE"; 489 default: 490 return Integer.toString(access); 491 } 492 } 493 accessSetToString(Set<Integer> accessSet)494 private static String accessSetToString(Set<Integer> accessSet) { 495 return accessSet.stream() 496 .map(access -> accessToString(access)) 497 .collect(Collectors.joining(", ")); 498 } 499 areaTypeToString(int areaType)500 private static String areaTypeToString(int areaType) { 501 switch (areaType) { 502 case VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL: 503 return "VEHICLE_AREA_TYPE_GLOBAL"; 504 case VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW: 505 return "VEHICLE_AREA_TYPE_WINDOW"; 506 case VehicleAreaType.VEHICLE_AREA_TYPE_DOOR: 507 return "VEHICLE_AREA_TYPE_DOOR"; 508 case VehicleAreaType.VEHICLE_AREA_TYPE_MIRROR: 509 return "VEHICLE_AREA_TYPE_MIRROR"; 510 case VehicleAreaType.VEHICLE_AREA_TYPE_SEAT: 511 return "VEHICLE_AREA_TYPE_SEAT"; 512 case VehicleAreaType.VEHICLE_AREA_TYPE_WHEEL: 513 return "VEHICLE_AREA_TYPE_WHEEL"; 514 case VehicleAreaType.VEHICLE_AREA_TYPE_VENDOR: 515 return "VEHICLE_AREA_TYPE_VENDOR"; 516 default: 517 return Integer.toString(areaType); 518 } 519 } 520 changeModeToString(int changeMode)521 private static String changeModeToString(int changeMode) { 522 switch (changeMode) { 523 case CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_STATIC: 524 return "VEHICLE_PROPERTY_CHANGE_MODE_STATIC"; 525 case CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE: 526 return "VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE"; 527 case CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS: 528 return "VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS"; 529 default: 530 return Integer.toString(changeMode); 531 } 532 } 533 534 /** 535 * Gets the car property config for the current property or reads from cache if already cached. 536 * 537 * <p>Note that we statically cache all the property configs using the shell permission. 538 */ getCarPropertyConfig()539 public @Nullable CarPropertyConfig<T> getCarPropertyConfig() { 540 return getCarPropertyConfig(/* useCache= */ true); 541 } 542 543 /** 544 * Gets the car property config for the current property. 545 * 546 * @param useCache Whether to use a local cache that prefetched all the configs using shell 547 * permission. 548 */ getCarPropertyConfig(boolean useCache)549 public @Nullable CarPropertyConfig<T> getCarPropertyConfig(boolean useCache) { 550 return (CarPropertyConfig<T>) getCarPropertyConfig(mPropertyId, useCache); 551 } 552 getCarPropertyConfig(int propertyId)553 private @Nullable CarPropertyConfig<?> getCarPropertyConfig(int propertyId) { 554 return getCarPropertyConfig(propertyId, /* useCache= */ true); 555 } 556 getCarPropertyConfig(int propertyId, boolean useCache)557 private @Nullable CarPropertyConfig<?> getCarPropertyConfig(int propertyId, boolean useCache) { 558 if (!useCache) { 559 return mCarPropertyManager.getCarPropertyConfig(propertyId); 560 } 561 562 if (!sIsCarPropertyConfigsCached) { 563 try (PermissionContext p = 564 TestApis.permissions() 565 .withPermission( 566 TestApis.permissions() 567 .adoptablePermissions() 568 .toArray(new String[0]))) { 569 var configs = mCarPropertyManager.getPropertyList(); 570 for (int i = 0; i < configs.size(); i++) { 571 sCachedCarPropertyConfigs.put(configs.get(i).getPropertyId(), configs.get(i)); 572 } 573 } 574 sIsCarPropertyConfigsCached = true; 575 } 576 return sCachedCarPropertyConfigs.get(propertyId); 577 } 578 579 /** Returns whether the property is supported. */ isSupported()580 public boolean isSupported() { 581 return getCarPropertyConfig() != null; 582 } 583 584 /** Gets all verification steps. */ getAllSteps()585 public static ImmutableList<String> getAllSteps() { 586 return ImmutableList.of( 587 STEP_VERIFY_PROPERTY_CONFIG, 588 STEP_VERIFY_IS_PROPERTY_AVAILABLE, 589 STEP_VERIFY_PERMISSION_NOT_GRANTED_EXCEPTION, 590 STEP_VERIFY_READ_APIS_GET_PROPERTY_SYNC, 591 STEP_VERIFY_READ_APIS_GET_PROPERTY_ASYNC, 592 STEP_VERIFY_READ_APIS_SUBSCRIBE, 593 STEP_VERIFY_READ_APIS_GET_MIN_MAX_SUPPORTED_VALUE, 594 STEP_VERIFY_READ_APIS_GET_SUPPORTED_VALUES_LIST, 595 STEP_VERIFY_READ_APIS_REG_UNREG_SUPPORTED_VALUES_CHANGE, 596 STEP_VERIFY_READ_APIS_DISABLE_ADAS_FEATURE_VERIFY_STATE, 597 STEP_VERIFY_WRITE_APIS_SET_PROPERTY_SYNC, 598 STEP_VERIFY_WRITE_APIS_SET_PROPERTY_ASYNC, 599 STEP_VERIFY_WRITE_APIS_DISABLE_ADAS_FEATURE_VERIFY_STATE, 600 STEP_VERIFY_READ_APIS_DISABLE_HVAC_GET_NOT_AVAILABLE, 601 STEP_VERIFY_WRITE_APIS_DISABLE_HVAC_SET_NOT_AVAILABLE, 602 STEP_VERIFY_READ_PERMISSION_CANNOT_WRITE, 603 STEP_VERIFY_WRITE_PERMISSION_CANNOT_READ); 604 } 605 606 /** Runs various verifications on the property. */ verify()607 public void verify() { 608 verify(null); 609 } 610 611 /** Runs a specific verification step. */ verify(String step, @Nullable Class<?> exceptedExceptionClass)612 public void verify(String step, @Nullable Class<?> exceptedExceptionClass) { 613 if (step.equals(STEP_VERIFY_PROPERTY_CONFIG)) { 614 verifyConfig(); 615 return; 616 } 617 if (step.equals(STEP_VERIFY_IS_PROPERTY_AVAILABLE)) { 618 verifyIsPropertyAvailable(); 619 return; 620 } 621 assumeTrue("Property: " + getPropertyName() + " is not supported", isSupported()); 622 if (step.equals(STEP_VERIFY_PERMISSION_NOT_GRANTED_EXCEPTION)) { 623 verifyPermissionNotGrantedException(); 624 } else if (step.startsWith(STEP_VERIFY_READ_APIS_PREFIX)) { 625 verifyReadApis(step, exceptedExceptionClass); 626 } else if (step.startsWith(STEP_VERIFY_WRITE_APIS_PREFIX)) { 627 verifyWriteApis(step, exceptedExceptionClass); 628 } else if (step.equals(STEP_VERIFY_READ_PERMISSION_CANNOT_WRITE)) { 629 verifyReadPermissionCannotWrite(); 630 } else if (step.equals(STEP_VERIFY_WRITE_PERMISSION_CANNOT_READ)) { 631 verifyWritePermissionCannotRead(); 632 } else { 633 throw new IllegalStateException("Unknown step: " + step); 634 } 635 } 636 637 /** 638 * Runs all verification steps on the property with exceptions expected. 639 * 640 * @param exceptedExceptionClass The exception class expected for reading/writing the property. 641 */ verify(@ullable Class<?> exceptedExceptionClass)642 public void verify(@Nullable Class<?> exceptedExceptionClass) { 643 verifySteps(getAllSteps(), exceptedExceptionClass); 644 } 645 verifySteps(List<String> steps, @Nullable Class<?> exceptedExceptionClass)646 private void verifySteps(List<String> steps, @Nullable Class<?> exceptedExceptionClass) { 647 for (int i = 0; i < steps.size(); i++) { 648 try { 649 verify(steps.get(i), exceptedExceptionClass); 650 } catch (AssumptionViolatedException e) { 651 if (steps.get(i).equals(STEP_VERIFY_PROPERTY_CONFIG)) { 652 // If the assumption fails for verifying config. It means the property is not 653 // supported and we should not continue the rest of the steps. 654 throw e; 655 } else { 656 // Otherwise, we allow one step to be skipped. 657 } 658 } 659 } 660 } 661 verifyGetPropertyWhenConfigIsNull(ImmutableSet<String> allPermissions)662 private void verifyGetPropertyWhenConfigIsNull(ImmutableSet<String> allPermissions) { 663 if (!isAtLeastU()) { 664 assertWithMessage( 665 "CarPropertyManager.getProperty must return null if the property is " 666 + "not supported for propertyId: " 667 + mPropertyName) 668 .that(mCarPropertyManager.getProperty(mPropertyId, /* areaId= */ 0)) 669 .isNull(); 670 return; 671 } 672 673 String errorMsg = 674 "CarPropertyManager.getProperty must throw IllegalArgumentException if " 675 + "the property is not supported for propertyId: " 676 + mPropertyName; 677 678 Exception e = 679 assertThrows( 680 errorMsg, 681 Exception.class, 682 () -> mCarPropertyManager.getProperty(mPropertyId, /* areaId= */ 0)); 683 if (e.getClass() == SecurityException.class) { 684 fail( 685 "CarPropertyManager.getProperty must throw IllegalArgumentException if the" 686 + " property is not supported, actually got SecurityException, test may not" 687 + " have correct permission granted. PropertyId: " 688 + mPropertyName 689 + ". Requested permissions: " 690 + allPermissions); 691 return; 692 } 693 assertWithMessage(errorMsg).that(e.getClass()).isEqualTo(IllegalArgumentException.class); 694 } 695 verifySetPropertyWhenConfigIsNull(ImmutableSet<String> allPermissions)696 private void verifySetPropertyWhenConfigIsNull(ImmutableSet<String> allPermissions) { 697 String errorMsg = 698 "CarPropertyManager.setProperty must throw IllegalArgumentException if " 699 + "the property is not supported for propertyId: " 700 + mPropertyName; 701 702 Exception e = 703 assertThrows( 704 errorMsg, 705 Exception.class, 706 () -> 707 mCarPropertyManager.setProperty( 708 mPropertyType, 709 mPropertyId, 710 /* areaId= */ 0, 711 getDefaultValue(mPropertyType))); 712 713 if (e.getClass() == SecurityException.class) { 714 fail( 715 "CarPropertyManager.setProperty must throw IllegalArgumentException if the" 716 + " property is not supported, actually got SecurityException, test may not" 717 + " have correct permission granted. PropertyId: " 718 + mPropertyName 719 + ". Requested permissions: " 720 + allPermissions); 721 return; 722 } 723 assertWithMessage(errorMsg).that(e.getClass()).isEqualTo(IllegalArgumentException.class); 724 } 725 726 /** Verifies the configuration for the property. */ verifyConfig()727 public void verifyConfig() { 728 ImmutableSet.Builder<String> permissionsBuilder = ImmutableSet.<String>builder(); 729 for (ImmutableSet<String> writePermissions : mWritePermissions) { 730 permissionsBuilder.addAll(writePermissions); 731 } 732 ImmutableSet<String> allPermissions = permissionsBuilder.addAll(mReadPermissions).build(); 733 734 runWithShellPermissionIdentity( 735 () -> { 736 CarPropertyConfig<T> carPropertyConfig = 737 getCarPropertyConfig(/* useCache= */ false); 738 if (carPropertyConfig == null) { 739 // The property is not supported. 740 boolean allAllowedAccessCanRead = true; 741 boolean allAllowedAccessCanWrite = true; 742 for (int allowedAccessMode : mAllowedAccessModes) { 743 if (!canRead(allowedAccessMode)) { 744 allAllowedAccessCanRead = false; 745 } 746 if (!canWrite(allowedAccessMode)) { 747 allAllowedAccessCanWrite = false; 748 } 749 } 750 if (allAllowedAccessCanRead) { 751 verifyGetPropertyWhenConfigIsNull(allPermissions); 752 } 753 if (allAllowedAccessCanWrite) { 754 verifySetPropertyWhenConfigIsNull(allPermissions); 755 } 756 } 757 758 if (mRequiredProperty) { 759 assertWithMessage("Must support " + mPropertyName) 760 .that(isSupported()) 761 .isTrue(); 762 } else { 763 assumeThat( 764 "Skipping " 765 + mPropertyName 766 + " CTS test because the property is not supported on " 767 + "this vehicle", 768 carPropertyConfig, 769 Matchers.notNullValue()); 770 } 771 772 verifyCarPropertyConfig(); 773 }, 774 allPermissions.toArray(new String[0])); 775 } 776 777 /** Verifies that caller can call read APIs with read permission. */ verifyReadApis(String step, Class<?> exceptedExceptionClass)778 private void verifyReadApis(String step, Class<?> exceptedExceptionClass) { 779 for (String readPermission : mReadPermissions) { 780 verifyReadPermissionGivesAccessToReadApis(step, readPermission, exceptedExceptionClass); 781 } 782 } 783 784 /** Verifies that caller can call write APIs with write permission. */ verifyWriteApis(String step, Class<?> exceptedExceptionClass)785 private void verifyWriteApis(String step, Class<?> exceptedExceptionClass) { 786 for (ImmutableSet<String> writePermissions : mWritePermissions) { 787 verifyWritePermissionsGiveAccessToWriteApis( 788 step, writePermissions, mReadPermissions, exceptedExceptionClass); 789 } 790 } 791 792 /** Verifies that caller cannot call write APIs with only read permissions. */ verifyReadPermissionCannotWrite()793 private void verifyReadPermissionCannotWrite() { 794 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 795 for (String readPermission : mReadPermissions) { 796 if (AREA_ID_CONFIG_ACCESS_FLAG) { 797 for (int areaId : carPropertyConfig.getAreaIds()) { 798 if (canWrite(carPropertyConfig, areaId)) { 799 verifyReadPermissionCannotWrite(readPermission, mWritePermissions, areaId); 800 } 801 } 802 } else if (canWrite(carPropertyConfig.getAccess())) { 803 verifyReadPermissionCannotWrite( 804 readPermission, mWritePermissions, carPropertyConfig.getAreaIds()[0]); 805 } 806 } 807 } 808 809 /** Verifies that caller cannot call read APIs with only write permissions. */ verifyWritePermissionCannotRead()810 private void verifyWritePermissionCannotRead() { 811 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 812 for (ImmutableSet<String> writePermissions : mWritePermissions) { 813 if (AREA_ID_CONFIG_ACCESS_FLAG) { 814 for (int areaId : carPropertyConfig.getAreaIds()) { 815 if (canRead(carPropertyConfig, areaId)) { 816 verifyWritePermissionsCannotRead( 817 writePermissions, mReadPermissions, areaId); 818 } 819 if (canWrite(carPropertyConfig, areaId) && writePermissions.size() > 1) { 820 verifyIndividualWritePermissionsCannotWrite(writePermissions, areaId); 821 } 822 } 823 } else { 824 int areaId = carPropertyConfig.getAreaIds()[0]; 825 if (canRead(carPropertyConfig.getAccess())) { 826 verifyWritePermissionsCannotRead(writePermissions, mReadPermissions, areaId); 827 } 828 if (canWrite(carPropertyConfig.getAccess()) && writePermissions.size() > 1) { 829 verifyIndividualWritePermissionsCannotWrite(writePermissions, areaId); 830 } 831 } 832 } 833 } 834 hasWritePermissions(ImmutableList<ImmutableSet<String>> writePermissions)835 private boolean hasWritePermissions(ImmutableList<ImmutableSet<String>> writePermissions) { 836 for (ImmutableSet<String> writePermissionSet : writePermissions) { 837 boolean result = true; 838 for (String permission : writePermissionSet) { 839 if (mContext.checkSelfPermission(permission) != PERMISSION_GRANTED) { 840 result = false; 841 break; 842 } 843 } 844 if (result) { 845 return true; 846 } 847 } 848 return false; 849 } 850 verifyReadPermissionCannotWrite( String readPermission, ImmutableList<ImmutableSet<String>> writePermissions, int areaId)851 private void verifyReadPermissionCannotWrite( 852 String readPermission, 853 ImmutableList<ImmutableSet<String>> writePermissions, 854 int areaId) { 855 // If the read permission is the same as the write permission and the property does not 856 // require any other write permissions we skip this permission. 857 for (ImmutableSet<String> writePermissionSet : writePermissions) { 858 if (writePermissionSet.size() == 1 && writePermissionSet.contains(readPermission)) { 859 return; 860 } 861 } 862 // It is possible that the caller has the write permissions without adopting the shell 863 // identity. In this case, we cannot revoke the write permission so we cannot test 864 // setProperty without write permissions. 865 if (hasWritePermissions(writePermissions)) { 866 return; 867 } 868 runWithShellPermissionIdentity( 869 () -> { 870 assertThrows( 871 mPropertyName 872 + " - property ID: " 873 + mPropertyId 874 + " should not be able to be written to without write" 875 + " permissions.", 876 SecurityException.class, 877 () -> 878 mCarPropertyManager.setProperty( 879 mPropertyType, 880 mPropertyId, 881 areaId, 882 getDefaultValue(mPropertyType))); 883 }, 884 readPermission); 885 } 886 verifyReadPermissionGivesAccessToReadApis( String step, String readPermission, Class<?> exceptedExceptionClass)887 private void verifyReadPermissionGivesAccessToReadApis( 888 String step, String readPermission, Class<?> exceptedExceptionClass) { 889 if (step.equals(STEP_VERIFY_READ_APIS_DISABLE_ADAS_FEATURE_VERIFY_STATE)) { 890 assumeTrue("Not an ADAS property", mDependentOnPropertyId.isPresent()); 891 disableAdasFeatureIfAdasStatePropertyAndVerify( 892 ImmutableSet.<String>builder() 893 .add(readPermission) 894 .addAll(mDependentOnPropertyPermissions) 895 .build() 896 .toArray(new String[0]), 897 /* verifySet= */ false); 898 return; 899 } 900 901 try { 902 enableAdasFeatureIfAdasStateProperty(); 903 runWithShellPermissionIdentity( 904 () -> { 905 assertThat(getCarPropertyConfig(/* useCache= */ false)).isNotNull(); 906 turnOnHvacPowerIfHvacPowerDependent(); 907 if (step.equals(STEP_VERIFY_READ_APIS_GET_PROPERTY_SYNC)) { 908 verifyCarPropertyValueGetter(); 909 if (exceptedExceptionClass != null) { 910 assertWithMessage( 911 "Expected " 912 + sExceptionClassOnGet 913 + " to be of type " 914 + exceptedExceptionClass) 915 .that(sExceptionClassOnGet) 916 .isEqualTo(exceptedExceptionClass); 917 } 918 return; 919 } 920 if (exceptedExceptionClass != null) { 921 return; 922 } 923 924 if (step.equals(STEP_VERIFY_READ_APIS_GET_PROPERTY_ASYNC)) { 925 verifyGetPropertiesAsync(); 926 } 927 928 if (step.equals(STEP_VERIFY_READ_APIS_SUBSCRIBE)) { 929 verifyCarPropertyValueCallback(); 930 } 931 932 if (step.equals(STEP_VERIFY_READ_APIS_GET_MIN_MAX_SUPPORTED_VALUE) 933 && CAR_PROPERTY_SUPPORTED_VALUE_FLAG) { 934 verifyGetMinMaxSupportedValue(); 935 } 936 937 if (step.equals(STEP_VERIFY_READ_APIS_GET_SUPPORTED_VALUES_LIST) 938 && CAR_PROPERTY_SUPPORTED_VALUE_FLAG) { 939 verifyGetSupportedValuesList(); 940 } 941 942 if (step.equals(STEP_VERIFY_READ_APIS_REG_UNREG_SUPPORTED_VALUES_CHANGE) 943 && CAR_PROPERTY_SUPPORTED_VALUE_FLAG) { 944 verifyRegisterUnregisterSupportedValuesChangeCallback(); 945 } 946 947 if (step.equals(STEP_VERIFY_READ_APIS_DISABLE_HVAC_GET_NOT_AVAILABLE)) { 948 assumeTrue( 949 "Not depending on HVAC power", mPossiblyDependentOnHvacPowerOn); 950 ImmutableSet<Integer> areaIdsTurnedOff = 951 turnOffHvacPowerIfHvacPowerDependent(); 952 if (!areaIdsTurnedOff.isEmpty()) { 953 verifyGetNotAvailable(areaIdsTurnedOff); 954 } 955 } 956 }, 957 readPermission); 958 } finally { 959 // Restore all property values even if test fails. 960 runWithShellPermissionIdentity( 961 () -> { 962 restoreInitialValues(); 963 }, 964 ImmutableSet.<String>builder() 965 .add(readPermission) 966 .addAll(mDependentOnPropertyPermissions) 967 .build() 968 .toArray(new String[0])); 969 } 970 } 971 hasReadPermissions(ImmutableSet<String> allReadPermissions)972 private boolean hasReadPermissions(ImmutableSet<String> allReadPermissions) { 973 for (String permission : allReadPermissions) { 974 if (mContext.checkSelfPermission(permission) == PERMISSION_GRANTED) { 975 return true; 976 } 977 } 978 return false; 979 } 980 assertGetPropertyThrowsException( String msg, Class<? extends Throwable> exceptionClass, int propertyId, int areaId)981 private void assertGetPropertyThrowsException( 982 String msg, Class<? extends Throwable> exceptionClass, int propertyId, int areaId) { 983 assertThrows( 984 msg, exceptionClass, () -> mCarPropertyManager.getProperty(propertyId, areaId)); 985 assertThrows( 986 msg, 987 exceptionClass, 988 () -> mCarPropertyManager.getBooleanProperty(propertyId, areaId)); 989 assertThrows( 990 msg, exceptionClass, () -> mCarPropertyManager.getIntProperty(propertyId, areaId)); 991 assertThrows( 992 msg, 993 exceptionClass, 994 () -> mCarPropertyManager.getFloatProperty(propertyId, areaId)); 995 assertThrows( 996 msg, 997 exceptionClass, 998 () -> mCarPropertyManager.getIntArrayProperty(propertyId, areaId)); 999 } 1000 verifyWritePermissionsCannotRead( ImmutableSet<String> writePermissions, ImmutableSet<String> allReadPermissions, int areaId)1001 private void verifyWritePermissionsCannotRead( 1002 ImmutableSet<String> writePermissions, 1003 ImmutableSet<String> allReadPermissions, 1004 int areaId) { 1005 // If there is any write permission that is also a read permission we skip the permissions. 1006 if (!Collections.disjoint(writePermissions, allReadPermissions)) { 1007 return; 1008 } 1009 // It is possible that the caller has the read permissions without adopting the shell 1010 // identity. In this case, we cannot revoke the read permissions so we cannot test 1011 // getProperty without read permissions. 1012 if (hasReadPermissions(allReadPermissions)) { 1013 return; 1014 } 1015 runWithShellPermissionIdentity( 1016 () -> { 1017 assertGetPropertyThrowsException( 1018 mPropertyName 1019 + " - property ID: " 1020 + mPropertyId 1021 + " should not be able to be read without read" 1022 + " permissions.", 1023 SecurityException.class, 1024 mPropertyId, 1025 areaId); 1026 assertThrows( 1027 mPropertyName 1028 + " - property ID: " 1029 + mPropertyId 1030 + " should not be able to be listened to without read" 1031 + " permissions.", 1032 SecurityException.class, 1033 () -> verifyCarPropertyValueCallback()); 1034 assertThrows( 1035 mPropertyName 1036 + " - property ID: " 1037 + mPropertyId 1038 + " should not be able to be read without read" 1039 + " permissions.", 1040 SecurityException.class, 1041 () -> verifyGetPropertiesAsync()); 1042 1043 // If the caller only has write permission, registerCallback throws 1044 // SecurityException. 1045 assertThrows( 1046 mPropertyName 1047 + " - property ID: " 1048 + mPropertyId 1049 + " should not be able to be listened to without read" 1050 + " permission.", 1051 SecurityException.class, 1052 () -> 1053 mCarPropertyManager.registerCallback( 1054 FAKE_CALLBACK, mPropertyId, 0f)); 1055 1056 if (isAtLeastV() && Flags.variableUpdateRate()) { 1057 // For the new API, if the caller does not read permission, it throws 1058 // SecurityException. 1059 assertThrows( 1060 mPropertyName 1061 + " - property ID: " 1062 + mPropertyId 1063 + " should not be able to be listened to without read" 1064 + " permission.", 1065 SecurityException.class, 1066 () -> 1067 mCarPropertyManager.subscribePropertyEvents( 1068 mPropertyId, areaId, FAKE_CALLBACK)); 1069 } 1070 }, 1071 writePermissions.toArray(new String[0])); 1072 } 1073 verifyIndividualWritePermissionsCannotWrite( ImmutableSet<String> writePermissions, int areaId)1074 private void verifyIndividualWritePermissionsCannotWrite( 1075 ImmutableSet<String> writePermissions, int areaId) { 1076 // It is possible that the caller has the write permissions without adopting 1077 // the shell identity. In this case, we cannot revoke individual permissions. 1078 if (hasWritePermissions(ImmutableList.of(writePermissions))) { 1079 return; 1080 } 1081 1082 String writePermissionsNeededString = String.join(", ", writePermissions); 1083 for (String writePermission : writePermissions) { 1084 runWithShellPermissionIdentity( 1085 () -> { 1086 assertThat(getCarPropertyConfig(/* useCache= */ false)).isNull(); 1087 assertThrows( 1088 mPropertyName 1089 + " - property ID: " 1090 + mPropertyId 1091 + " should not be able to be written to without all of the" 1092 + " following permissions granted: " 1093 + writePermissionsNeededString, 1094 SecurityException.class, 1095 () -> 1096 mCarPropertyManager.setProperty( 1097 mPropertyType, 1098 mPropertyId, 1099 areaId, 1100 getDefaultValue(mPropertyType))); 1101 }, 1102 writePermission); 1103 } 1104 } 1105 verifyWritePermissionsGiveAccessToWriteApis( String step, ImmutableSet<String> writePermissions, ImmutableSet<String> readPermissions, Class<?> exceptedExceptionClass)1106 private void verifyWritePermissionsGiveAccessToWriteApis( 1107 String step, 1108 ImmutableSet<String> writePermissions, 1109 ImmutableSet<String> readPermissions, 1110 Class<?> exceptedExceptionClass) { 1111 ImmutableSet<String> propertyPermissions = 1112 ImmutableSet.<String>builder() 1113 .addAll(writePermissions) 1114 .addAll(readPermissions) 1115 .build(); 1116 1117 if (step.equals(STEP_VERIFY_WRITE_APIS_DISABLE_ADAS_FEATURE_VERIFY_STATE)) { 1118 assumeTrue("Not an ADAS property", mDependentOnPropertyId.isPresent()); 1119 disableAdasFeatureIfAdasStatePropertyAndVerify( 1120 propertyPermissions.toArray(new String[0]), /* verifySet= */ true); 1121 return; 1122 } 1123 1124 try { 1125 // Store the current value before we call enableAdasFeatureIfAdasStateProperty, which 1126 // might change this. 1127 runWithShellPermissionIdentity( 1128 () -> { 1129 storeCurrentValues(); 1130 }, 1131 propertyPermissions.toArray(new String[0])); 1132 enableAdasFeatureIfAdasStateProperty(); 1133 1134 runWithShellPermissionIdentity( 1135 () -> { 1136 turnOnHvacPowerIfHvacPowerDependent(); 1137 1138 if (step.equals(STEP_VERIFY_WRITE_APIS_SET_PROPERTY_SYNC)) { 1139 verifyCarPropertyValueSetter(); 1140 if (exceptedExceptionClass != null) { 1141 assertWithMessage( 1142 "Expected " 1143 + sExceptionClassOnSet 1144 + " to be of type " 1145 + exceptedExceptionClass) 1146 .that(sExceptionClassOnSet) 1147 .isEqualTo(exceptedExceptionClass); 1148 } 1149 } 1150 1151 if (step.equals(STEP_VERIFY_WRITE_APIS_SET_PROPERTY_ASYNC) 1152 && exceptedExceptionClass == null) { 1153 verifySetPropertiesAsync(); 1154 } 1155 if (step.equals(STEP_VERIFY_WRITE_APIS_DISABLE_HVAC_SET_NOT_AVAILABLE)) { 1156 assumeTrue( 1157 "Not depending on HVAC power", mPossiblyDependentOnHvacPowerOn); 1158 ImmutableSet<Integer> areaIdsTurnedOff = 1159 turnOffHvacPowerIfHvacPowerDependent(); 1160 if (!areaIdsTurnedOff.isEmpty()) { 1161 verifySetNotAvailable(areaIdsTurnedOff); 1162 } 1163 } 1164 }, 1165 propertyPermissions.toArray(new String[0])); 1166 } finally { 1167 // Restore all property values even if test fails. 1168 runWithShellPermissionIdentity( 1169 () -> { 1170 restoreInitialValues(); 1171 }, 1172 ImmutableSet.<String>builder() 1173 .addAll(propertyPermissions) 1174 .addAll(mDependentOnPropertyPermissions) 1175 .build() 1176 .toArray(new String[0])); 1177 } 1178 } 1179 turnOnHvacPowerIfHvacPowerDependent()1180 private void turnOnHvacPowerIfHvacPowerDependent() { 1181 if (!mPossiblyDependentOnHvacPowerOn) { 1182 return; 1183 } 1184 1185 CarPropertyConfig<Boolean> hvacPowerOnCarPropertyConfig = 1186 (CarPropertyConfig<Boolean>) getCarPropertyConfig(VehiclePropertyIds.HVAC_POWER_ON); 1187 if (hvacPowerOnCarPropertyConfig == null 1188 || !hvacPowerOnCarPropertyConfig.getConfigArray().contains(mPropertyId)) { 1189 return; 1190 } 1191 1192 storeCurrentValuesForProperty(hvacPowerOnCarPropertyConfig); 1193 // Turn the power on for all supported HVAC area IDs. 1194 setBooleanPowerPropertyInAllAreaIds( 1195 hvacPowerOnCarPropertyConfig, /* setValue: */ Boolean.TRUE); 1196 } 1197 turnOffHvacPowerIfHvacPowerDependent()1198 private ImmutableSet<Integer> turnOffHvacPowerIfHvacPowerDependent() { 1199 CarPropertyConfig<Boolean> hvacPowerOnCarPropertyConfig = 1200 (CarPropertyConfig<Boolean>) getCarPropertyConfig(VehiclePropertyIds.HVAC_POWER_ON); 1201 if (hvacPowerOnCarPropertyConfig == null 1202 || !hvacPowerOnCarPropertyConfig.getConfigArray().contains(mPropertyId)) { 1203 return ImmutableSet.of(); 1204 } 1205 1206 // Turn the power off for all supported HVAC area IDs. 1207 return setBooleanPowerPropertyInAllAreaIds( 1208 hvacPowerOnCarPropertyConfig, /* setValue: */ Boolean.FALSE); 1209 } 1210 1211 /** Enables the ADAS feature if the property is an ADAS property. */ enableAdasFeatureIfAdasStateProperty()1212 public void enableAdasFeatureIfAdasStateProperty() { 1213 if (!mDependentOnPropertyId.isPresent()) { 1214 return; 1215 } 1216 1217 runWithShellPermissionIdentity( 1218 () -> { 1219 int adasEnabledPropertyId = mDependentOnPropertyId.get(); 1220 CarPropertyConfig<Boolean> adasEnabledCarPropertyConfig = 1221 (CarPropertyConfig<Boolean>) 1222 getCarPropertyConfig(adasEnabledPropertyId); 1223 1224 if (adasEnabledCarPropertyConfig == null 1225 || !canWrite( 1226 getAreaIdAccessOrElseGlobalAccess( 1227 adasEnabledCarPropertyConfig, GLOBAL_AREA_ID))) { 1228 Log.w( 1229 TAG, 1230 "Cannot enable " 1231 + VehiclePropertyIds.toString(adasEnabledPropertyId) 1232 + " for testing " 1233 + VehiclePropertyIds.toString(mPropertyId) 1234 + " because property is either not implemented or READ" 1235 + " only. Manually enable if it's not already enabled."); 1236 return; 1237 } 1238 1239 storeCurrentValuesForProperty(adasEnabledCarPropertyConfig); 1240 // Enable ADAS feature in all supported area IDs. 1241 setBooleanPowerPropertyInAllAreaIds( 1242 adasEnabledCarPropertyConfig, /* setValue: */ Boolean.TRUE); 1243 }, 1244 mDependentOnPropertyPermissions.toArray(new String[0])); 1245 } 1246 disableAdasFeatureIfAdasStatePropertyAndVerify( String[] enabledPermissionsList, boolean verifySet)1247 private void disableAdasFeatureIfAdasStatePropertyAndVerify( 1248 String[] enabledPermissionsList, boolean verifySet) { 1249 try { 1250 ImmutableSet<Integer> areaIdsDisabled = disableAdasFeatureIfAdasStateProperty(); 1251 if (areaIdsDisabled.isEmpty()) { 1252 return; 1253 } 1254 runWithShellPermissionIdentity( 1255 () -> { 1256 if (mVerifyErrorStates) { 1257 verifyAdasPropertyErrorState(areaIdsDisabled); 1258 } else if (verifySet) { 1259 verifySetNotAvailable(areaIdsDisabled); 1260 } else { 1261 verifyGetNotAvailable(areaIdsDisabled); 1262 } 1263 }, 1264 enabledPermissionsList); 1265 } finally { 1266 // Restore all property values even if test fails. 1267 runWithShellPermissionIdentity( 1268 () -> { 1269 restoreInitialValues(); 1270 }, 1271 mDependentOnPropertyPermissions.toArray(new String[0])); 1272 } 1273 } 1274 1275 /** 1276 * Disables the ADAS feature if the property is an ADAS property. 1277 * 1278 * @return all area IDs that are disabled 1279 */ disableAdasFeatureIfAdasStateProperty()1280 public ImmutableSet<Integer> disableAdasFeatureIfAdasStateProperty() { 1281 if (!mDependentOnPropertyId.isPresent()) { 1282 return ImmutableSet.of(); 1283 } 1284 1285 ImmutableSet.Builder<Integer> areaIdsDisabled = ImmutableSet.builder(); 1286 runWithShellPermissionIdentity( 1287 () -> { 1288 int adasEnabledPropertyId = mDependentOnPropertyId.get(); 1289 CarPropertyConfig<Boolean> adasEnabledCarPropertyConfig = 1290 (CarPropertyConfig<Boolean>) 1291 getCarPropertyConfig(adasEnabledPropertyId); 1292 1293 if (adasEnabledCarPropertyConfig == null 1294 || !canWrite( 1295 getAreaIdAccessOrElseGlobalAccess( 1296 adasEnabledCarPropertyConfig, GLOBAL_AREA_ID))) { 1297 return; 1298 } 1299 1300 storeCurrentValuesForProperty(adasEnabledCarPropertyConfig); 1301 1302 // Disable ADAS feature in all supported area IDs. 1303 areaIdsDisabled.addAll( 1304 setBooleanPowerPropertyInAllAreaIds( 1305 adasEnabledCarPropertyConfig, /* setValue: */ Boolean.FALSE)); 1306 }, 1307 mDependentOnPropertyPermissions.toArray(new String[0])); 1308 return areaIdsDisabled.build(); 1309 } 1310 1311 /** Stores the property's current values for all areas so that they can be restored later. */ storeCurrentValues()1312 public void storeCurrentValues() { 1313 storeCurrentValuesForProperty(getCarPropertyConfig()); 1314 } 1315 storeCurrentValuesForProperty(CarPropertyConfig<U> carPropertyConfig)1316 private <U> void storeCurrentValuesForProperty(CarPropertyConfig<U> carPropertyConfig) { 1317 SparseArray<U> areaIdToInitialValue = 1318 getInitialValuesByAreaId(carPropertyConfig, mCarPropertyManager); 1319 if (areaIdToInitialValue == null || areaIdToInitialValue.size() == 0) { 1320 return; 1321 } 1322 var propertyId = carPropertyConfig.getPropertyId(); 1323 if (mPropertyToAreaIdValues.contains(propertyId)) { 1324 throw new IllegalStateException( 1325 "The property: " 1326 + VehiclePropertyIds.toString(propertyId) 1327 + " already has a stored value"); 1328 } 1329 mStoredProperties.add(propertyId); 1330 for (int i = 0; i < areaIdToInitialValue.size(); i++) { 1331 Log.i( 1332 TAG, 1333 "Storing initial value for property:" 1334 + VehiclePropertyIds.toString(propertyId) 1335 + " at area ID: " 1336 + areaIdToInitialValue.keyAt(i) 1337 + " to " 1338 + areaIdToInitialValue.valueAt(i)); 1339 } 1340 mPropertyToAreaIdValues.put(propertyId, areaIdToInitialValue); 1341 } 1342 restoreInitialValue(int propertyId)1343 private void restoreInitialValue(int propertyId) { 1344 var carPropertyConfig = getCarPropertyConfig(propertyId); 1345 SparseArray<?> areaIdToInitialValue = mPropertyToAreaIdValues.get(propertyId); 1346 1347 if (areaIdToInitialValue == null || carPropertyConfig == null) { 1348 Log.w( 1349 TAG, 1350 "No stored values for " 1351 + VehiclePropertyIds.toString(propertyId) 1352 + " to restore to, ignore"); 1353 return; 1354 } 1355 1356 restoreInitialValuesByAreaId(carPropertyConfig, mCarPropertyManager, areaIdToInitialValue); 1357 } 1358 1359 /** 1360 * Restore the property's and dependent properties values to original values stored by previous 1361 * {@link #storeCurrentValues}. 1362 * 1363 * <p>Do nothing if no stored current values are available. 1364 * 1365 * <p>The properties values are restored in the reverse-order as they are stored. 1366 */ restoreInitialValues()1367 public void restoreInitialValues() { 1368 for (int i = mStoredProperties.size() - 1; i >= 0; i--) { 1369 restoreInitialValue(mStoredProperties.get(i)); 1370 } 1371 mStoredProperties.clear(); 1372 mPropertyToAreaIdValues.clear(); 1373 } 1374 1375 // Get a map storing the property's area Ids to the initial values. 1376 @Nullable getInitialValuesByAreaId( CarPropertyConfig<U> carPropertyConfig, CarPropertyManager carPropertyManager)1377 private static <U> SparseArray<U> getInitialValuesByAreaId( 1378 CarPropertyConfig<U> carPropertyConfig, CarPropertyManager carPropertyManager) { 1379 if (!AREA_ID_CONFIG_ACCESS_FLAG 1380 && !(canRead(carPropertyConfig.getAccess()) 1381 && canWrite(carPropertyConfig.getAccess()))) { 1382 return null; 1383 } 1384 SparseArray<U> areaIdToInitialValue = new SparseArray<U>(); 1385 int propertyId = carPropertyConfig.getPropertyId(); 1386 String propertyName = VehiclePropertyIds.toString(propertyId); 1387 for (int areaId : carPropertyConfig.getAreaIds()) { 1388 if (!(canRead(carPropertyConfig, areaId) && canWrite(carPropertyConfig, areaId))) { 1389 continue; 1390 } 1391 CarPropertyValue<U> carPropertyValue; 1392 try { 1393 carPropertyValue = carPropertyManager.getProperty(propertyId, areaId); 1394 } catch (PropertyNotAvailableAndRetryException 1395 | PropertyNotAvailableException 1396 | CarInternalErrorException e) { 1397 Log.w( 1398 TAG, 1399 "Failed to get property:" 1400 + propertyName 1401 + " at area ID: " 1402 + areaId 1403 + " to save initial car property value. Error: " 1404 + e); 1405 continue; 1406 } 1407 if (carPropertyValue == null) { 1408 Log.w( 1409 TAG, 1410 "Failed to get property:" 1411 + propertyName 1412 + " at area ID: " 1413 + areaId 1414 + " to save initial car property value."); 1415 continue; 1416 } 1417 if (carPropertyValue.getStatus() != CarPropertyValue.STATUS_AVAILABLE) { 1418 Log.w( 1419 TAG, 1420 "Cannot save initial value for property:" 1421 + propertyName 1422 + " at area ID: " 1423 + areaId 1424 + " because status: " 1425 + carPropertyValue.getStatus()); 1426 continue; 1427 } 1428 areaIdToInitialValue.put(areaId, (U) carPropertyValue.getValue()); 1429 } 1430 return areaIdToInitialValue; 1431 } 1432 1433 /** 1434 * Set a boolean property that represents the power state to a desired value in all supported 1435 * area IDs. 1436 * 1437 * <p>Power properties include {@code HVAC_POWER_ON} and all ADAS ENABLED properties such as 1438 * {@code AUTOMATIC_EMERGENCY_BRAKING_ENABLED}. 1439 * 1440 * @return all area IDs that are set (or already set) to {@code setValue} 1441 */ setBooleanPowerPropertyInAllAreaIds( CarPropertyConfig<Boolean> booleanCarPropertyConfig, Boolean setValue)1442 private ImmutableSet<Integer> setBooleanPowerPropertyInAllAreaIds( 1443 CarPropertyConfig<Boolean> booleanCarPropertyConfig, Boolean setValue) { 1444 ImmutableSet.Builder<Integer> areaIdsUpdated = ImmutableSet.builder(); 1445 boolean updateSentToVhal = false; 1446 int propertyId = booleanCarPropertyConfig.getPropertyId(); 1447 for (int areaId : booleanCarPropertyConfig.getAreaIds()) { 1448 if (mCarPropertyManager.getBooleanProperty(propertyId, areaId) == setValue) { 1449 areaIdsUpdated.add(areaId); 1450 continue; 1451 } 1452 CarPropertyValue<Boolean> updatedValue = 1453 setPropertyAndWaitForChange( 1454 mCarPropertyManager, propertyId, Boolean.class, areaId, setValue); 1455 if (updatedValue == null) { 1456 continue; 1457 } 1458 areaIdsUpdated.add(areaId); 1459 updateSentToVhal = true; 1460 } 1461 if (updateSentToVhal && mPowerPropagationDelayMs != 0) { 1462 Log.i( 1463 TAG, 1464 "Setting power property:" 1465 + VehiclePropertyIds.toString(propertyId) 1466 + " to value: " 1467 + setValue 1468 + " and sleeping for: " 1469 + mPowerPropagationDelayMs); 1470 // Wait for power status to propagate to dependent properties 1471 SystemClock.sleep(mPowerPropagationDelayMs); 1472 Log.i(TAG, "Completed sleeping for: " + mPowerPropagationDelayMs); 1473 } 1474 return areaIdsUpdated.build(); 1475 } 1476 1477 // Restore the initial values of the property provided by {@code areaIdToInitialValue}. restoreInitialValuesByAreaId( CarPropertyConfig<?> carPropertyConfig, CarPropertyManager carPropertyManager, SparseArray<?> areaIdToInitialValue)1478 private static void restoreInitialValuesByAreaId( 1479 CarPropertyConfig<?> carPropertyConfig, 1480 CarPropertyManager carPropertyManager, 1481 SparseArray<?> areaIdToInitialValue) { 1482 int propertyId = carPropertyConfig.getPropertyId(); 1483 String propertyName = VehiclePropertyIds.toString(propertyId); 1484 for (int i = 0; i < areaIdToInitialValue.size(); i++) { 1485 int areaId = areaIdToInitialValue.keyAt(i); 1486 Object originalValue = areaIdToInitialValue.valueAt(i); 1487 CarPropertyValue<?> currentCarPropertyValue; 1488 try { 1489 currentCarPropertyValue = carPropertyManager.getProperty(propertyId, areaId); 1490 } catch (PropertyNotAvailableAndRetryException 1491 | PropertyNotAvailableException 1492 | CarInternalErrorException e) { 1493 Log.w( 1494 TAG, 1495 "Failed to get property:" 1496 + propertyName 1497 + " at area ID: " 1498 + areaId 1499 + " to restore initial car property value. Error: " 1500 + e); 1501 continue; 1502 } 1503 if (currentCarPropertyValue == null) { 1504 Log.w( 1505 TAG, 1506 "Failed to get property:" 1507 + propertyName 1508 + " at area ID: " 1509 + areaId 1510 + " to restore initial car property value."); 1511 continue; 1512 } 1513 if (currentCarPropertyValue.getStatus() != CarPropertyValue.STATUS_AVAILABLE) { 1514 Log.w( 1515 TAG, 1516 "Cannot restore initial value for property:" 1517 + propertyName 1518 + " at area ID: " 1519 + areaId 1520 + " because status: " 1521 + currentCarPropertyValue.getStatus()); 1522 continue; 1523 } 1524 if (valueEquals(originalValue, currentCarPropertyValue.getValue())) { 1525 continue; 1526 } 1527 Log.i( 1528 TAG, 1529 "Restoring value for: " 1530 + propertyName 1531 + " at area ID: " 1532 + areaId 1533 + " to " 1534 + originalValue); 1535 setPropertyAndWaitForChange( 1536 carPropertyManager, propertyId, Object.class, areaId, originalValue); 1537 } 1538 } 1539 1540 /** 1541 * Gets the possible values that could be set to. 1542 * 1543 * <p>The values returned here must not cause {@code IllegalArgumentException} for set. 1544 * 1545 * <p>Returns {@code null} or empty array if we don't know possible values. 1546 */ getPossibleValues(int areaId)1547 public @Nullable Collection<T> getPossibleValues(int areaId) { 1548 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1549 if (Boolean.class.equals(carPropertyConfig.getPropertyType())) { 1550 return (List<T>) List.of(Boolean.TRUE, Boolean.FALSE); 1551 } else if (Integer.class.equals(carPropertyConfig.getPropertyType())) { 1552 return (List<T>) getPossibleIntegerValues(areaId); 1553 } else if (Float.class.equals(carPropertyConfig.getPropertyType())) { 1554 return getPossibleFloatValues(areaId); 1555 } 1556 return null; 1557 } 1558 isAtLeastU()1559 public static boolean isAtLeastU() { 1560 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 1561 } 1562 isAtLeastV()1563 private static boolean isAtLeastV() { 1564 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM; 1565 } 1566 isAtLeastB()1567 private static boolean isAtLeastB() { 1568 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA; 1569 } 1570 1571 /** Gets the possible values for an integer property. */ getPossibleIntegerValues(int areaId)1572 private List<Integer> getPossibleIntegerValues(int areaId) { 1573 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1574 List<Integer> possibleValues = new ArrayList<>(); 1575 if (mPropertyId == VehiclePropertyIds.HVAC_FAN_DIRECTION) { 1576 int[] availableHvacFanDirections = 1577 mCarPropertyManager.getIntArrayProperty( 1578 VehiclePropertyIds.HVAC_FAN_DIRECTION_AVAILABLE, areaId); 1579 for (int i = 0; i < availableHvacFanDirections.length; i++) { 1580 if (availableHvacFanDirections[i] != CarHvacFanDirection.UNKNOWN) { 1581 possibleValues.add(availableHvacFanDirections[i]); 1582 } 1583 } 1584 return possibleValues; 1585 } 1586 if (mVerifySetterWithConfigArrayValues) { 1587 for (Integer value : carPropertyConfig.getConfigArray()) { 1588 possibleValues.add(value); 1589 } 1590 return possibleValues; 1591 } 1592 1593 if (!mAllPossibleEnumValues.isEmpty() && isAtLeastU()) { 1594 AreaIdConfig areaIdConfig = carPropertyConfig.getAreaIdConfig(areaId); 1595 for (Integer value : (List<Integer>) areaIdConfig.getSupportedEnumValues()) { 1596 if ((mAllPossibleUnwritableValues.isEmpty() 1597 || !mAllPossibleUnwritableValues.contains(value)) 1598 && (mAllPossibleUnavailableValues.isEmpty() 1599 || !mAllPossibleUnavailableValues.contains(value))) { 1600 possibleValues.add(value); 1601 } 1602 } 1603 } else { 1604 Integer minValue = (Integer) carPropertyConfig.getMinValue(areaId); 1605 Integer maxValue = (Integer) carPropertyConfig.getMaxValue(areaId); 1606 assertWithMessage( 1607 "Read-write/Write integer properties should either have a config array" 1608 + " with valid set values, a set of supported enums, or valid min" 1609 + " and max values set in the CarPropertyConfig. However, the" 1610 + " following property has none of these: " 1611 + VehiclePropertyIds.toString(mPropertyId)) 1612 .that(minValue != null && maxValue != null) 1613 .isTrue(); 1614 List<Integer> valuesToSet = 1615 IntStream.rangeClosed(minValue.intValue(), maxValue.intValue()) 1616 .boxed() 1617 .collect(Collectors.toList()); 1618 for (int i = 0; i < valuesToSet.size(); i++) { 1619 possibleValues.add(valuesToSet.get(i)); 1620 } 1621 } 1622 return possibleValues; 1623 } 1624 1625 /** Gets the possible values for a float property. */ getPossibleFloatValues(int areaId)1626 private Collection<T> getPossibleFloatValues(int areaId) { 1627 ImmutableSet.Builder<Float> possibleValuesBuilder = ImmutableSet.builder(); 1628 if (mPropertyId == VehiclePropertyIds.HVAC_TEMPERATURE_SET) { 1629 List<Integer> hvacTempSetConfigArray = getCarPropertyConfig().getConfigArray(); 1630 if (!hvacTempSetConfigArray.isEmpty()) { 1631 // For HVAC_TEMPERATURE_SET, the configArray specifies the supported temperature 1632 // values for the property. configArray[0] is the lower bound of the supported 1633 // temperatures in Celsius. configArray[1] is the upper bound of the supported 1634 // temperatures in Celsius. configArray[2] is the supported temperature increment 1635 // between the two bounds. All configArray values are Celsius*10 since the 1636 // configArray is List<Integer> but HVAC_TEMPERATURE_SET is a Float type property. 1637 for (int possibleHvacTempSetValue = hvacTempSetConfigArray.get(0); 1638 possibleHvacTempSetValue <= hvacTempSetConfigArray.get(1); 1639 possibleHvacTempSetValue += hvacTempSetConfigArray.get(2)) { 1640 possibleValuesBuilder.add((float) possibleHvacTempSetValue / 10.0f); 1641 } 1642 } else { 1643 // If the configArray is not specified, then use min/max values. 1644 Float minValueFloat = (Float) getCarPropertyConfig().getMinValue(areaId); 1645 Float maxValueFloat = (Float) getCarPropertyConfig().getMaxValue(areaId); 1646 possibleValuesBuilder.add(minValueFloat); 1647 possibleValuesBuilder.add(maxValueFloat); 1648 } 1649 } else if (mPropertyId == VehiclePropertyIds.EV_CHARGE_PERCENT_LIMIT) { 1650 List<Integer> evChargePercentLimitConfigArray = getCarPropertyConfig().getConfigArray(); 1651 if (!evChargePercentLimitConfigArray.isEmpty()) { 1652 for (Integer possibleEvChargePercentLimit : evChargePercentLimitConfigArray) { 1653 possibleValuesBuilder.add(possibleEvChargePercentLimit.floatValue()); 1654 } 1655 } else { 1656 // If the configArray is not specified, then values between 0 and 100 percent must 1657 // be supported. 1658 possibleValuesBuilder.add(0f); 1659 possibleValuesBuilder.add(100f); 1660 } 1661 } else if (mPropertyId == VehiclePropertyIds.EV_CHARGE_CURRENT_DRAW_LIMIT) { 1662 // First value in the configArray specifies the max current draw allowed by the vehicle. 1663 Integer vehicleMaxCurrentDrawLimit = getCarPropertyConfig().getConfigArray().get(0); 1664 possibleValuesBuilder.add(vehicleMaxCurrentDrawLimit.floatValue()); 1665 } else if (mPropertyId == VehiclePropertyIds.RANGE_REMAINING) { 1666 // Test when no range is remaining 1667 possibleValuesBuilder.add(0f); 1668 } 1669 return (Collection<T>) possibleValuesBuilder.build(); 1670 } 1671 verifyCarPropertyValueSetter()1672 private void verifyCarPropertyValueSetter() { 1673 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1674 if (!AREA_ID_CONFIG_ACCESS_FLAG && !canWrite(carPropertyConfig.getAccess())) { 1675 verifySetPropertyFails(carPropertyConfig.getAreaIds()[0]); 1676 return; 1677 } 1678 if (Boolean.class.equals(carPropertyConfig.getPropertyType())) { 1679 verifyBooleanPropertySetter(); 1680 } else if (Integer.class.equals(carPropertyConfig.getPropertyType())) { 1681 verifyIntegerPropertySetter(); 1682 } else if (Float.class.equals(carPropertyConfig.getPropertyType())) { 1683 verifyFloatPropertySetter(); 1684 } else if (mPropertyId == VehiclePropertyIds.HVAC_TEMPERATURE_VALUE_SUGGESTION) { 1685 verifyHvacTemperatureValueSuggestionSetter(); 1686 } 1687 } 1688 verifySetPropertyFails(int areaId)1689 private void verifySetPropertyFails(int areaId) { 1690 if (!isAtLeastU()) { 1691 // The precondition check in CarPropertyService that throws an IllegalArgumentException 1692 // when a client tries to write a property that is implemented as read_only was added in 1693 // Android U. API behavior prior to Android U is undefined so skipping this check. 1694 return; 1695 } 1696 assertThrows( 1697 mPropertyName 1698 + " is a read_only property so setProperty should throw an" 1699 + " IllegalArgumentException.", 1700 IllegalArgumentException.class, 1701 () -> 1702 mCarPropertyManager.setProperty( 1703 mPropertyType, 1704 mPropertyId, 1705 areaId, 1706 getDefaultValue(mPropertyType))); 1707 } 1708 verifyBooleanPropertySetter()1709 private void verifyBooleanPropertySetter() { 1710 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1711 for (int areaId : carPropertyConfig.getAreaIds()) { 1712 if (!canWrite(carPropertyConfig, areaId)) { 1713 verifySetPropertyFails(areaId); 1714 continue; 1715 } 1716 for (Boolean valueToSet : List.of(Boolean.TRUE, Boolean.FALSE)) { 1717 verifySetProperty(areaId, (T) valueToSet); 1718 } 1719 } 1720 } 1721 verifyIntegerPropertySetter()1722 private void verifyIntegerPropertySetter() { 1723 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1724 for (int areaId : carPropertyConfig.getAreaIds()) { 1725 if (!canWrite(carPropertyConfig, areaId)) { 1726 verifySetPropertyFails(areaId); 1727 continue; 1728 } 1729 for (Integer valueToSet : getPossibleIntegerValues(areaId)) { 1730 verifySetProperty(areaId, (T) valueToSet); 1731 } 1732 } 1733 if (!mAllPossibleEnumValues.isEmpty() && isAtLeastU()) { 1734 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 1735 if (!canWrite(carPropertyConfig, areaIdConfig.getAreaId())) { 1736 continue; 1737 } 1738 for (T valueToSet : (List<T>) areaIdConfig.getSupportedEnumValues()) { 1739 if (!mAllPossibleUnwritableValues.isEmpty() 1740 && mAllPossibleUnwritableValues.contains(valueToSet)) { 1741 assertThrows( 1742 "Trying to set an unwritable value: " 1743 + valueToSet 1744 + " to property: " 1745 + mPropertyId 1746 + " should throw an " 1747 + "IllegalArgumentException", 1748 IllegalArgumentException.class, 1749 () -> 1750 mCarPropertyManager.setProperty( 1751 carPropertyConfig.getPropertyType(), mPropertyId, 1752 areaIdConfig.getAreaId(), valueToSet)); 1753 } 1754 if (!mAllPossibleUnavailableValues.isEmpty() 1755 && mAllPossibleUnavailableValues.contains(valueToSet)) { 1756 assertThrows( 1757 "Trying to set an unavailable value: " 1758 + valueToSet 1759 + " to property: " 1760 + mPropertyId 1761 + " should throw an " 1762 + "PropertyNotAvailableException", 1763 PropertyNotAvailableException.class, 1764 () -> 1765 mCarPropertyManager.setProperty( 1766 carPropertyConfig.getPropertyType(), mPropertyId, 1767 areaIdConfig.getAreaId(), valueToSet)); 1768 } 1769 } 1770 } 1771 } 1772 } 1773 verifyFloatPropertySetter()1774 private void verifyFloatPropertySetter() { 1775 for (int areaId : getCarPropertyConfig().getAreaIds()) { 1776 for (T valueToSet : getPossibleFloatValues(areaId)) { 1777 verifySetProperty(areaId, valueToSet); 1778 } 1779 } 1780 } 1781 verifySetProperty(int areaId, T valueToSet)1782 private void verifySetProperty(int areaId, T valueToSet) { 1783 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1784 if (!canWrite(carPropertyConfig, areaId)) { 1785 return; 1786 } 1787 1788 verifySetPropertyWithNullValueThrowsException(areaId); 1789 1790 int access = getAreaIdAccessOrElseGlobalAccess(carPropertyConfig, areaId); 1791 if (canWrite(access) && !canRead(access)) { 1792 Log.w( 1793 TAG, 1794 "Property: " 1795 + mPropertyName 1796 + " will be altered during the test and it is" 1797 + " not possible to restore."); 1798 verifySetPropertyOkayOrThrowExpectedExceptions(areaId, valueToSet); 1799 return; 1800 } 1801 try { 1802 CarPropertyValue<T> currentCarPropertyValue = 1803 mCarPropertyManager.getProperty(mPropertyId, areaId); 1804 verifyCarPropertyValue( 1805 currentCarPropertyValue, areaId, CAR_PROPERTY_VALUE_SOURCE_GETTER); 1806 if (currentCarPropertyValue.getStatus() != CarPropertyValue.STATUS_AVAILABLE) { 1807 Log.w( 1808 TAG, 1809 "Skipping SET verification for propertyId: " 1810 + mPropertyId 1811 + " areaId: " 1812 + areaId 1813 + " valueToSet:" 1814 + valueToSet 1815 + " because getProperty did not have an AVAILABLE status - " 1816 + currentCarPropertyValue); 1817 return; 1818 } 1819 if (valueEquals(valueToSet, currentCarPropertyValue.getValue())) { 1820 return; 1821 } 1822 } catch (PropertyNotAvailableAndRetryException e) { 1823 Log.w( 1824 TAG, 1825 "Skipping SET verification for propertyId: " 1826 + mPropertyName 1827 + " areaId: " 1828 + areaId 1829 + " valueToSet:" 1830 + valueToSet 1831 + " because getProperty threw PropertyNotAvailableAndRetryException - " 1832 + e); 1833 return; 1834 } catch (PropertyNotAvailableException e) { 1835 verifyPropertyNotAvailableException(e); 1836 return; 1837 } catch (CarInternalErrorException e) { 1838 verifyInternalErrorException(e); 1839 return; 1840 } 1841 CarPropertyValue<T> updatedCarPropertyValue = 1842 setPropertyAndWaitForChange( 1843 mCarPropertyManager, 1844 mPropertyId, 1845 carPropertyConfig.getPropertyType(), 1846 areaId, 1847 valueToSet); 1848 if (updatedCarPropertyValue != null) { 1849 verifyCarPropertyValue( 1850 updatedCarPropertyValue, areaId, CAR_PROPERTY_VALUE_SOURCE_CALLBACK); 1851 } 1852 } 1853 verifySetPropertyWithNullValueThrowsException(int areaId)1854 private void verifySetPropertyWithNullValueThrowsException(int areaId) { 1855 assertThrows( 1856 NullPointerException.class, 1857 () -> 1858 mCarPropertyManager.setProperty( 1859 mPropertyType, mPropertyId, areaId, /* val= */ null)); 1860 } 1861 verifyHvacTemperatureValueSuggestionSetter()1862 private void verifyHvacTemperatureValueSuggestionSetter() { 1863 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1864 CarPropertyConfig<?> hvacTemperatureSetCarPropertyConfig = 1865 getCarPropertyConfig(VehiclePropertyIds.HVAC_TEMPERATURE_SET); 1866 if (hvacTemperatureSetCarPropertyConfig == null) { 1867 return; 1868 } 1869 List<Integer> hvacTemperatureSetConfigArray = 1870 hvacTemperatureSetCarPropertyConfig.getConfigArray(); 1871 if (hvacTemperatureSetConfigArray.isEmpty()) { 1872 return; 1873 } 1874 float minTempInCelsius = hvacTemperatureSetConfigArray.get(0).floatValue() / 10f; 1875 float minTempInFahrenheit = hvacTemperatureSetConfigArray.get(3).floatValue() / 10f; 1876 1877 Float[] temperatureRequest = 1878 new Float[] { 1879 /* requestedValue= */ minTempInCelsius, 1880 /* units= */ (float) 0x30, // VehicleUnit#CELSIUS 1881 /* suggestedValueInCelsius= */ 0f, 1882 /* suggestedValueInFahrenheit= */ 0f 1883 }; 1884 Float[] expectedTemperatureResponse = 1885 new Float[] { 1886 /* requestedValue= */ minTempInCelsius, 1887 /* units= */ (float) 0x30, // VehicleUnit#CELSIUS 1888 /* suggestedValueInCelsius= */ minTempInCelsius, 1889 /* suggestedValueInFahrenheit= */ minTempInFahrenheit 1890 }; 1891 for (int areaId : carPropertyConfig.getAreaIds()) { 1892 CarPropertyValue<Float[]> updatedCarPropertyValue = 1893 setPropertyAndWaitForChange( 1894 mCarPropertyManager, 1895 mPropertyId, 1896 Float[].class, 1897 areaId, 1898 temperatureRequest, 1899 expectedTemperatureResponse); 1900 if (updatedCarPropertyValue != null) { 1901 verifyCarPropertyValue( 1902 updatedCarPropertyValue, areaId, CAR_PROPERTY_VALUE_SOURCE_CALLBACK); 1903 } 1904 } 1905 } 1906 verifySetPropertyOkayOrThrowExpectedExceptions(int areaId, T valueToSet)1907 private void verifySetPropertyOkayOrThrowExpectedExceptions(int areaId, T valueToSet) { 1908 spaceOutCarPropertyManagerActions(); 1909 try { 1910 mCarPropertyManager.setProperty(mPropertyType, mPropertyId, areaId, valueToSet); 1911 } catch (PropertyNotAvailableAndRetryException e) { 1912 // Do nothing 1913 } catch (PropertyNotAvailableException e) { 1914 verifyPropertyNotAvailableException(e); 1915 } catch (CarInternalErrorException e) { 1916 verifyInternalErrorException(e); 1917 } catch (Exception e) { 1918 assertWithMessage( 1919 "Unexpected exception thrown when trying to setProperty on " 1920 + mPropertyName 1921 + ": " 1922 + e) 1923 .fail(); 1924 } 1925 } 1926 verifyGetNotAvailable(Collection<Integer> areaIdsNotAvailable)1927 private void verifyGetNotAvailable(Collection<Integer> areaIdsNotAvailable) { 1928 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1929 for (int areaId : carPropertyConfig.getAreaIds()) { 1930 if (!isAreaIdSupportedInList(areaId, areaIdsNotAvailable)) { 1931 Log.i( 1932 TAG, 1933 "Skipping verifyGetNotAvailable because power could not be turned off for" 1934 + " power dependent property - " 1935 + mPropertyName 1936 + " at areaId - " 1937 + areaId); 1938 continue; 1939 } 1940 1941 try { 1942 // getProperty may/may not throw exception when the property is not available. 1943 CarPropertyValue<T> currentValue = 1944 mCarPropertyManager.getProperty(mPropertyId, areaId); 1945 assertWithMessage( 1946 "When the power is turned off getProperty should throw" 1947 + " PropertyNotAvailableException when trying to get a" 1948 + " property with StatusCode.NOT_AVAILABLE or return a" 1949 + " CarPropertyValue with status UNAVAILABLE." 1950 + " Returned CarPropertyValue: " 1951 + currentValue.toString()) 1952 .that(currentValue.getStatus()) 1953 .isEqualTo(CarPropertyValue.STATUS_UNAVAILABLE); 1954 } catch (Exception e) { 1955 // If the property is read or read-write, then this should throw 1956 // PropertyNotAvailableException. If the property is write-only, then it will throw 1957 // IllegalArgumentException. 1958 assertWithMessage( 1959 "Getting property " 1960 + mPropertyName 1961 + " when it's not available" 1962 + " should throw either PropertyNotAvailableException or" 1963 + " IllegalArgumentException.") 1964 .that(e.getClass()) 1965 .isAnyOf( 1966 PropertyNotAvailableException.class, 1967 IllegalArgumentException.class); 1968 } 1969 } 1970 } 1971 verifySetNotAvailable(Collection<Integer> areaIdsNotAvailable)1972 private void verifySetNotAvailable(Collection<Integer> areaIdsNotAvailable) { 1973 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1974 if (!AREA_ID_CONFIG_ACCESS_FLAG 1975 && !(canRead(carPropertyConfig.getAccess()) 1976 && canWrite(carPropertyConfig.getAccess()))) { 1977 return; 1978 } 1979 1980 T valueToSet = getDefaultValue(mPropertyType); 1981 if (valueToSet == null) { 1982 assertWithMessage("Testing mixed type property is not supported").fail(); 1983 } 1984 for (int areaId : carPropertyConfig.getAreaIds()) { 1985 if (!(canRead(carPropertyConfig, areaId) && canWrite(carPropertyConfig, areaId))) { 1986 continue; 1987 } 1988 1989 if (!isAreaIdSupportedInList(areaId, areaIdsNotAvailable)) { 1990 Log.i( 1991 TAG, 1992 "Skipping verifySetNotAvailable because power could not be turned off for" 1993 + " power dependent property - " 1994 + mPropertyName 1995 + " at areaId - " 1996 + areaId); 1997 continue; 1998 } 1999 2000 spaceOutCarPropertyManagerActions(); 2001 SetterCallback setterCallback = new SetterCallback(mPropertyId, areaId, valueToSet); 2002 assertWithMessage( 2003 "Failed to register no change setter callback for " 2004 + VehiclePropertyIds.toString(mPropertyId)) 2005 .that( 2006 subscribePropertyEvents( 2007 mCarPropertyManager, 2008 setterCallback, 2009 mPropertyId, 2010 CarPropertyManager.SENSOR_RATE_FASTEST)) 2011 .isTrue(); 2012 2013 try { 2014 mCarPropertyManager.setProperty(mPropertyType, mPropertyId, areaId, valueToSet); 2015 CarPropertyValue<T> updatedValue = 2016 setterCallback.waitForPropertyEvent(SET_PROPERTY_CALLBACK_TIMEOUT_SEC); 2017 if (updatedValue != null 2018 && updatedValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE) { 2019 // If the callback receives a new event with the value set before the timeout, 2020 // then this check will fail. 2021 assertWithMessage( 2022 "Received onChangeEvent(s) for " 2023 + mPropertyName 2024 + " with updated value: " 2025 + valueToSet 2026 + " before 5s timeout. When the power is turned off," 2027 + " this property must not be available to set.") 2028 .that(updatedValue.getValue()) 2029 .isNotEqualTo(valueToSet); 2030 } 2031 } catch (Exception e) { 2032 // In normal cases, this should throw PropertyNotAvailableException. 2033 // In rare cases, the value we are setting is the same as the current value, 2034 // which makes the set operation a no-op. So it is possible that no exception 2035 // is thrown here. 2036 // It is also possible that this may throw IllegalArgumentException if the value to 2037 // set is not valid. 2038 assertWithMessage( 2039 "Setting property " 2040 + mPropertyName 2041 + " when it's not available" 2042 + " should throw either PropertyNotAvailableException or" 2043 + " IllegalArgumentException.") 2044 .that(e.getClass()) 2045 .isAnyOf( 2046 PropertyNotAvailableException.class, 2047 IllegalArgumentException.class); 2048 } finally { 2049 unsubscribePropertyEvents(mCarPropertyManager, setterCallback, mPropertyId); 2050 } 2051 } 2052 } 2053 verifyAdasPropertyErrorState(Collection<Integer> areaIdsDisabled)2054 private void verifyAdasPropertyErrorState(Collection<Integer> areaIdsDisabled) { 2055 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 2056 if (!AREA_ID_CONFIG_ACCESS_FLAG && !canRead(carPropertyConfig.getAccess())) { 2057 return; 2058 } 2059 2060 for (int areaId : carPropertyConfig.getAreaIds()) { 2061 if (!canRead(carPropertyConfig, areaId)) { 2062 continue; 2063 } 2064 if (!isAreaIdSupportedInList(areaId, areaIdsDisabled)) { 2065 Log.i( 2066 TAG, 2067 "Skipping verifyAdasPropertyErrorState because could not disable feature" 2068 + " for ADAS STATE property - " 2069 + mPropertyName 2070 + " at areaId - " 2071 + areaId); 2072 continue; 2073 } 2074 2075 Integer adasState = mCarPropertyManager.getIntProperty(mPropertyId, areaId); 2076 assertWithMessage( 2077 "When ADAS feature is disabled, " 2078 + mPropertyName 2079 + " must be set to " 2080 + ErrorState.NOT_AVAILABLE_DISABLED 2081 + " (ErrorState.NOT_AVAILABLE_DISABLED).") 2082 .that(adasState) 2083 .isEqualTo(ErrorState.NOT_AVAILABLE_DISABLED); 2084 } 2085 } 2086 getUpdatesPerAreaId(int changeMode)2087 private static int getUpdatesPerAreaId(int changeMode) { 2088 return changeMode != CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS ? 1 : 2; 2089 } 2090 getRegisterCallbackTimeoutMillis(int changeMode, float minSampleRate)2091 private static long getRegisterCallbackTimeoutMillis(int changeMode, float minSampleRate) { 2092 long timeoutMillis = 1500; 2093 if (changeMode == CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS) { 2094 float secondsToMillis = 1_000; 2095 long bufferMillis = 1_000; // 1 second 2096 timeoutMillis = 2097 ((long) 2098 ((1.0f / minSampleRate) 2099 * secondsToMillis 2100 * getUpdatesPerAreaId(changeMode))) 2101 + bufferMillis; 2102 } 2103 return timeoutMillis; 2104 } 2105 subscribePropertyEvents( CarPropertyManager carPropertyManager, CarPropertyManager.CarPropertyEventCallback callback, int propertyId, float updateRateHz)2106 private static boolean subscribePropertyEvents( 2107 CarPropertyManager carPropertyManager, 2108 CarPropertyManager.CarPropertyEventCallback callback, 2109 int propertyId, 2110 float updateRateHz) { 2111 if (isAtLeastV() && Flags.variableUpdateRate()) { 2112 // Use new API if at least V. 2113 return carPropertyManager.subscribePropertyEvents( 2114 List.of( 2115 new Subscription.Builder(propertyId) 2116 .setUpdateRateHz(updateRateHz) 2117 .setVariableUpdateRateEnabled(false) 2118 .build()), 2119 /* callbackExecutor= */ null, 2120 callback); 2121 } else { 2122 return carPropertyManager.registerCallback(callback, propertyId, updateRateHz); 2123 } 2124 } 2125 subscribePropertyEvents( CarPropertyManager.CarPropertyEventCallback callback, int propertyId, float updateRateHz)2126 private boolean subscribePropertyEvents( 2127 CarPropertyManager.CarPropertyEventCallback callback, 2128 int propertyId, 2129 float updateRateHz) { 2130 return subscribePropertyEvents(mCarPropertyManager, callback, propertyId, updateRateHz); 2131 } 2132 unsubscribePropertyEvents( CarPropertyManager carPropertyManager, CarPropertyManager.CarPropertyEventCallback callback, int propertyId)2133 private static void unsubscribePropertyEvents( 2134 CarPropertyManager carPropertyManager, 2135 CarPropertyManager.CarPropertyEventCallback callback, 2136 int propertyId) { 2137 if (isAtLeastV() && Flags.variableUpdateRate()) { 2138 // Use new API if at least V. 2139 carPropertyManager.unsubscribePropertyEvents(propertyId, callback); 2140 } else { 2141 carPropertyManager.unregisterCallback(callback, propertyId); 2142 } 2143 } 2144 unsubscribePropertyEvents( CarPropertyManager.CarPropertyEventCallback callback, int propertyId)2145 private void unsubscribePropertyEvents( 2146 CarPropertyManager.CarPropertyEventCallback callback, int propertyId) { 2147 unsubscribePropertyEvents(mCarPropertyManager, callback, propertyId); 2148 } 2149 verifyCarPropertyValueCallback()2150 private void verifyCarPropertyValueCallback() { 2151 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 2152 if (!canReadAllAreaIds(carPropertyConfig)) { 2153 // This means we specify read permission for one property, but the OEM specify it as 2154 // write-only. This will only happen for properties that we allow READ_WRITE or WRITE. 2155 // We currently do not have such system property. 2156 return; 2157 } 2158 int updatesPerAreaId = getUpdatesPerAreaId(mChangeMode); 2159 long timeoutMillis = 2160 getRegisterCallbackTimeoutMillis(mChangeMode, carPropertyConfig.getMinSampleRate()); 2161 2162 CarPropertyValueCallback carPropertyValueCallback = 2163 new CarPropertyValueCallback( 2164 mPropertyName, 2165 carPropertyConfig.getAreaIds(), 2166 updatesPerAreaId, 2167 timeoutMillis); 2168 assertWithMessage("Failed to register callback for " + mPropertyName) 2169 .that( 2170 subscribePropertyEvents( 2171 carPropertyValueCallback, 2172 mPropertyId, 2173 carPropertyConfig.getMaxSampleRate())) 2174 .isTrue(); 2175 SparseArray<List<CarPropertyValue<?>>> areaIdToCarPropertyValues = 2176 carPropertyValueCallback.getAreaIdToCarPropertyValues(); 2177 unsubscribePropertyEvents(carPropertyValueCallback, mPropertyId); 2178 2179 for (int areaId : carPropertyConfig.getAreaIds()) { 2180 List<CarPropertyValue<?>> carPropertyValues = areaIdToCarPropertyValues.get(areaId); 2181 assertWithMessage(mPropertyName + " callback value list is null for area ID: " + areaId) 2182 .that(carPropertyValues) 2183 .isNotNull(); 2184 assertWithMessage( 2185 mPropertyName 2186 + " callback values did not receive " 2187 + updatesPerAreaId 2188 + " updates for area ID: " 2189 + areaId) 2190 .that(carPropertyValues.size()) 2191 .isAtLeast(updatesPerAreaId); 2192 for (CarPropertyValue<?> carPropertyValue : carPropertyValues) { 2193 verifyCarPropertyValue( 2194 carPropertyValue, 2195 carPropertyValue.getAreaId(), 2196 CAR_PROPERTY_VALUE_SOURCE_CALLBACK); 2197 } 2198 } 2199 } 2200 verifyAccess_isSubsetOfOtherAccess(int subAccess, Set<Integer> superAccess)2201 private void verifyAccess_isSubsetOfOtherAccess(int subAccess, Set<Integer> superAccess) { 2202 assertWithMessage( 2203 "access mode for property: " 2204 + mPropertyName 2205 + " must be one of: " 2206 + accessSetToString(superAccess)) 2207 .that(subAccess) 2208 .isIn(superAccess); 2209 } 2210 verifyCarPropertyConfig()2211 private void verifyCarPropertyConfig() { 2212 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 2213 assertWithMessage(mPropertyName + " CarPropertyConfig must have correct property ID") 2214 .that(carPropertyConfig.getPropertyId()) 2215 .isEqualTo(mPropertyId); 2216 int carPropConfigAccess = carPropertyConfig.getAccess(); 2217 verifyAccess_isSubsetOfOtherAccess(carPropConfigAccess, mAllowedAccessModes); 2218 if (AREA_ID_CONFIG_ACCESS_FLAG) { 2219 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 2220 int areaAccess = areaIdConfig.getAccess(); 2221 verifyAccess_isSubsetOfOtherAccess(areaAccess, mAllowedAccessModes); 2222 if (canRead(carPropConfigAccess)) { 2223 assertWithMessage( 2224 "area access mode for property:" 2225 + mPropertyName 2226 + ", areaId: " 2227 + areaIdConfig.getAreaId() 2228 + " must be super-set of global access mode, area" 2229 + " access mode: " 2230 + accessToString(areaAccess) 2231 + ", global access: " 2232 + accessToString(carPropConfigAccess)) 2233 .that(canRead(areaAccess)) 2234 .isTrue(); 2235 } 2236 if (canWrite(carPropConfigAccess)) { 2237 assertWithMessage( 2238 "area access mode for property:" 2239 + mPropertyName 2240 + ", areaId: " 2241 + areaIdConfig.getAreaId() 2242 + " must be super-set of global access mode, area" 2243 + " access mode: " 2244 + accessToString(areaAccess) 2245 + ", global access: " 2246 + accessToString(carPropConfigAccess)) 2247 .that(canWrite(areaAccess)) 2248 .isTrue(); 2249 } 2250 } 2251 } 2252 assertWithMessage(mPropertyName + " must be " + areaTypeToString(mAreaType)) 2253 .that(carPropertyConfig.getAreaType()) 2254 .isEqualTo(mAreaType); 2255 assertWithMessage(mPropertyName + " must be " + changeModeToString(mChangeMode)) 2256 .that(carPropertyConfig.getChangeMode()) 2257 .isEqualTo(mChangeMode); 2258 assertWithMessage(mPropertyName + " must be " + mPropertyType + " type property") 2259 .that(carPropertyConfig.getPropertyType()) 2260 .isEqualTo(mPropertyType); 2261 2262 int[] areaIds = carPropertyConfig.getAreaIds(); 2263 assertWithMessage(mPropertyName + "'s must have at least 1 area ID defined") 2264 .that(areaIds.length) 2265 .isAtLeast(1); 2266 assertWithMessage( 2267 mPropertyName 2268 + "'s area IDs must all be unique: " 2269 + Arrays.toString(areaIds)) 2270 .that( 2271 ImmutableSet.copyOf( 2272 Arrays.stream(areaIds) 2273 .boxed() 2274 .collect(Collectors.toList())) 2275 .size() 2276 == areaIds.length) 2277 .isTrue(); 2278 2279 if (mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL) { 2280 assertWithMessage( 2281 mPropertyName 2282 + "'s AreaIds must contain a single 0 since it is " 2283 + areaTypeToString(mAreaType)) 2284 .that(areaIds) 2285 .isEqualTo(new int[] {0}); 2286 } else if (mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_WHEEL) { 2287 verifyValidAreaIdsForAreaType(ALL_POSSIBLE_WHEEL_AREA_IDS); 2288 verifyNoAreaOverlapInAreaIds(); 2289 } else if (mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW) { 2290 verifyValidAreaIdsForAreaType(ALL_POSSIBLE_WINDOW_AREA_IDS); 2291 verifyNoAreaOverlapInAreaIds(); 2292 } else if (mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_MIRROR) { 2293 verifyValidAreaIdsForAreaType(ALL_POSSIBLE_MIRROR_AREA_IDS); 2294 verifyNoAreaOverlapInAreaIds(); 2295 } else if (mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_SEAT 2296 && mPropertyId != VehiclePropertyIds.INFO_DRIVER_SEAT) { 2297 verifyValidAreaIdsForAreaType(ALL_POSSIBLE_SEAT_AREA_IDS); 2298 verifyNoAreaOverlapInAreaIds(); 2299 } else if (mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_DOOR) { 2300 verifyValidAreaIdsForAreaType(ALL_POSSIBLE_DOOR_AREA_IDS); 2301 verifyNoAreaOverlapInAreaIds(); 2302 } else if (mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_VENDOR) { 2303 verifyNoAreaOverlapInAreaIds(); 2304 } 2305 2306 if (mAreaIdsVerifier.isPresent()) { 2307 mAreaIdsVerifier.get().verify(mVerifierContext, areaIds); 2308 } 2309 2310 if (mChangeMode == CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS) { 2311 verifyContinuousCarPropertyConfig(); 2312 } else { 2313 verifyNonContinuousCarPropertyConfig(); 2314 } 2315 2316 mCarPropertyConfigVerifier.ifPresent( 2317 carPropertyConfigVerifier -> 2318 carPropertyConfigVerifier.verify(mVerifierContext, carPropertyConfig)); 2319 2320 if (!mPossibleConfigArrayValues.isEmpty()) { 2321 assertWithMessage(mPropertyName + " configArray must specify supported values") 2322 .that(carPropertyConfig.getConfigArray().size()) 2323 .isGreaterThan(0); 2324 for (Integer supportedValue : carPropertyConfig.getConfigArray()) { 2325 assertWithMessage( 2326 mPropertyName 2327 + " configArray value must be a defined " 2328 + "value: " 2329 + supportedValue) 2330 .that(supportedValue) 2331 .isIn(mPossibleConfigArrayValues); 2332 } 2333 } 2334 2335 mConfigArrayVerifier.ifPresent( 2336 configArrayVerifier -> 2337 configArrayVerifier.verify( 2338 mVerifierContext, carPropertyConfig.getConfigArray())); 2339 2340 if (mPossibleConfigArrayValues.isEmpty() 2341 && !mConfigArrayVerifier.isPresent() 2342 && !mCarPropertyConfigVerifier.isPresent()) { 2343 assertWithMessage(mPropertyName + " configArray is undefined, so it must be empty") 2344 .that(carPropertyConfig.getConfigArray().size()) 2345 .isEqualTo(0); 2346 } 2347 2348 for (int areaId : areaIds) { 2349 T areaIdMinValue = (T) carPropertyConfig.getMinValue(areaId); 2350 T areaIdMaxValue = (T) carPropertyConfig.getMaxValue(areaId); 2351 if (mRequireMinMaxValues) { 2352 assertWithMessage( 2353 mPropertyName 2354 + " - area ID: " 2355 + areaId 2356 + " must have min value defined") 2357 .that(areaIdMinValue) 2358 .isNotNull(); 2359 assertWithMessage( 2360 mPropertyName 2361 + " - area ID: " 2362 + areaId 2363 + " must have max value defined") 2364 .that(areaIdMaxValue) 2365 .isNotNull(); 2366 2367 if (CAR_PROPERTY_SUPPORTED_VALUE_FLAG) { 2368 assertWithMessage( 2369 mPropertyName 2370 + " - area ID: " 2371 + areaId 2372 + " config must set hasMaxSupportedValue to true") 2373 .that(carPropertyConfig.getAreaIdConfig(areaId).hasMaxSupportedValue()) 2374 .isTrue(); 2375 assertWithMessage( 2376 mPropertyName 2377 + " - area ID: " 2378 + areaId 2379 + " config must set hasMinSupportedValue to true") 2380 .that(carPropertyConfig.getAreaIdConfig(areaId).hasMinSupportedValue()) 2381 .isTrue(); 2382 } 2383 } 2384 2385 if (mRequireMinValuesToBeZero) { 2386 assertWithMessage( 2387 mPropertyName + " - area ID: " + areaId + " min value must be zero") 2388 .that(areaIdMinValue) 2389 .isEqualTo(0); 2390 } 2391 if (mRequireZeroToBeContainedInMinMaxRanges) { 2392 assertWithMessage( 2393 mPropertyName 2394 + " - areaId: " 2395 + areaId 2396 + "'s max and min range must contain zero") 2397 .that(verifyMaxAndMinRangeContainsZero(areaIdMinValue, areaIdMaxValue)) 2398 .isTrue(); 2399 } 2400 if (areaIdMinValue != null || areaIdMaxValue != null) { 2401 assertWithMessage( 2402 mPropertyName 2403 + " - areaId: " 2404 + areaId 2405 + "'s max value must be >= min value") 2406 .that(verifyMaxAndMin(areaIdMinValue, areaIdMaxValue)) 2407 .isTrue(); 2408 } 2409 2410 if (mRequirePropertyValueToBeInConfigArray && isAtLeastU()) { 2411 List<?> supportedEnumValues = 2412 carPropertyConfig.getAreaIdConfig(areaId).getSupportedEnumValues(); 2413 assertWithMessage( 2414 mPropertyName 2415 + " - areaId: " 2416 + areaId 2417 + "'s supported enum values must match the values in the" 2418 + " config array.") 2419 .that(carPropertyConfig.getConfigArray()) 2420 .containsExactlyElementsIn(supportedEnumValues); 2421 } 2422 2423 if (mChangeMode == CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE 2424 && !mAllPossibleEnumValues.isEmpty() 2425 && isAtLeastU()) { 2426 List<?> supportedEnumValues = 2427 carPropertyConfig.getAreaIdConfig(areaId).getSupportedEnumValues(); 2428 assertWithMessage( 2429 mPropertyName 2430 + " - areaId: " 2431 + areaId 2432 + "'s supported enum values must be defined") 2433 .that(supportedEnumValues) 2434 .isNotEmpty(); 2435 assertWithMessage( 2436 mPropertyName 2437 + " - areaId: " 2438 + areaId 2439 + "'s supported enum values must not contain any" 2440 + " duplicates") 2441 .that(supportedEnumValues) 2442 .containsNoDuplicates(); 2443 assertWithMessage( 2444 mPropertyName 2445 + " - areaId: " 2446 + areaId 2447 + "'s supported enum values " 2448 + supportedEnumValues 2449 + " must all exist in all possible enum set " 2450 + mAllPossibleEnumValues) 2451 .that(mAllPossibleEnumValues.containsAll(supportedEnumValues)) 2452 .isTrue(); 2453 if (CAR_PROPERTY_SUPPORTED_VALUE_FLAG) { 2454 assertWithMessage( 2455 mPropertyName 2456 + " - areaId: " 2457 + areaId 2458 + " config must set hasSupportedValuesList to true") 2459 .that( 2460 carPropertyConfig 2461 .getAreaIdConfig(areaId) 2462 .hasSupportedValuesList()) 2463 .isTrue(); 2464 } 2465 } else if (isAtLeastU()) { 2466 assertWithMessage( 2467 mPropertyName 2468 + " - areaId: " 2469 + areaId 2470 + "'s supported enum values must be empty since property" 2471 + " does not support an enum") 2472 .that(carPropertyConfig.getAreaIdConfig(areaId).getSupportedEnumValues()) 2473 .isEmpty(); 2474 } 2475 } 2476 } 2477 verifyMaxAndMinRangeContainsZero(T min, T max)2478 private boolean verifyMaxAndMinRangeContainsZero(T min, T max) { 2479 int propertyType = mPropertyId & VehiclePropertyType.MASK; 2480 switch (propertyType) { 2481 case VehiclePropertyType.INT32: 2482 return (Integer) max >= 0 && (Integer) min <= 0; 2483 case VehiclePropertyType.INT64: 2484 return (Long) max >= 0 && (Long) min <= 0; 2485 case VehiclePropertyType.FLOAT: 2486 return (Float) max >= 0 && (Float) min <= 0; 2487 default: 2488 return false; 2489 } 2490 } 2491 verifyMaxAndMin(T min, T max)2492 private boolean verifyMaxAndMin(T min, T max) { 2493 int propertyType = mPropertyId & VehiclePropertyType.MASK; 2494 switch (propertyType) { 2495 case VehiclePropertyType.INT32: 2496 return (Integer) max >= (Integer) min; 2497 case VehiclePropertyType.INT64: 2498 return (Long) max >= (Long) min; 2499 case VehiclePropertyType.FLOAT: 2500 return (Float) max >= (Float) min; 2501 default: 2502 return false; 2503 } 2504 } 2505 verifyContinuousCarPropertyConfig()2506 private void verifyContinuousCarPropertyConfig() { 2507 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 2508 assertWithMessage( 2509 mPropertyName 2510 + " must define max sample rate since change mode is " 2511 + changeModeToString(mChangeMode)) 2512 .that(carPropertyConfig.getMaxSampleRate()) 2513 .isGreaterThan(0); 2514 assertWithMessage( 2515 mPropertyName 2516 + " must define min sample rate since change mode is " 2517 + changeModeToString(mChangeMode)) 2518 .that(carPropertyConfig.getMinSampleRate()) 2519 .isGreaterThan(0); 2520 assertWithMessage(mPropertyName + " max sample rate must be >= min sample rate") 2521 .that(carPropertyConfig.getMaxSampleRate() >= carPropertyConfig.getMinSampleRate()) 2522 .isTrue(); 2523 } 2524 verifyNonContinuousCarPropertyConfig()2525 private void verifyNonContinuousCarPropertyConfig() { 2526 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 2527 assertWithMessage( 2528 mPropertyName 2529 + " must define max sample rate as 0 since change mode is " 2530 + changeModeToString(mChangeMode)) 2531 .that(carPropertyConfig.getMaxSampleRate()) 2532 .isEqualTo(0); 2533 assertWithMessage( 2534 mPropertyName 2535 + " must define min sample rate as 0 since change mode is " 2536 + changeModeToString(mChangeMode)) 2537 .that(carPropertyConfig.getMinSampleRate()) 2538 .isEqualTo(0); 2539 } 2540 handleGetPropertyExceptions(Exception e)2541 private void handleGetPropertyExceptions(Exception e) { 2542 if (e instanceof PropertyNotAvailableException) { 2543 verifyPropertyNotAvailableException((PropertyNotAvailableException) e); 2544 } else if (e instanceof CarInternalErrorException) { 2545 verifyInternalErrorException((CarInternalErrorException) e); 2546 } 2547 sExceptionClassOnGet = e.getClass(); 2548 } 2549 handleClassSpecificGetPropertyExceptions( Exception e, Class<?> expectedClass, int areaId)2550 private void handleClassSpecificGetPropertyExceptions( 2551 Exception e, Class<?> expectedClass, int areaId) { 2552 if (e instanceof IllegalArgumentException) { 2553 if (mPropertyType.equals(expectedClass)) { 2554 assertWithMessage( 2555 "getProperty for " 2556 + expectedClass 2557 + " class should not throw" 2558 + " IllegalArgumentException for valid propertyId: " 2559 + mPropertyId 2560 + " areaId: " 2561 + areaId 2562 + " of type: " 2563 + mPropertyType 2564 + " if property is" 2565 + " readable") 2566 .fail(); 2567 } 2568 } else { 2569 handleGetPropertyExceptions(e); 2570 } 2571 } 2572 verifyClassSpecificGetPropertyResults( T value, Class<?> expectedClass, int areaId)2573 private void verifyClassSpecificGetPropertyResults( 2574 T value, Class<?> expectedClass, int areaId) { 2575 if (!mPropertyType.equals(expectedClass)) { 2576 assertWithMessage( 2577 "getProperty for " 2578 + expectedClass 2579 + " class should throw" 2580 + " IllegalArgumentException for valid propertyId: " 2581 + mPropertyId 2582 + " areaId: " 2583 + areaId 2584 + " of" 2585 + " type: " 2586 + mPropertyType 2587 + " if property is readable") 2588 .fail(); 2589 } 2590 verifyCarPropertyValue( 2591 mPropertyId, 2592 areaId, 2593 CarPropertyValue.STATUS_AVAILABLE, 2594 SystemClock.elapsedRealtimeNanos(), 2595 value, 2596 areaId, 2597 CAR_PROPERTY_VALUE_SOURCE_GETTER); 2598 } 2599 verifyCarPropertyValueGetter()2600 private void verifyCarPropertyValueGetter() { 2601 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 2602 if (!AREA_ID_CONFIG_ACCESS_FLAG && !canRead(carPropertyConfig.getAccess())) { 2603 verifyGetPropertyFails(carPropertyConfig.getAreaIds()[0]); 2604 return; 2605 } 2606 for (int areaId : carPropertyConfig.getAreaIds()) { 2607 if (!canRead(carPropertyConfig, areaId)) { 2608 verifyGetPropertyFails(areaId); 2609 continue; 2610 } 2611 2612 CarPropertyValue<?> carPropertyValue = null; 2613 try { 2614 carPropertyValue = mCarPropertyManager.getProperty(mPropertyId, areaId); 2615 verifyCarPropertyValue(carPropertyValue, areaId, CAR_PROPERTY_VALUE_SOURCE_GETTER); 2616 } catch (PropertyNotAvailableException | CarInternalErrorException e) { 2617 handleGetPropertyExceptions(e); 2618 } 2619 2620 try { 2621 Boolean value = mCarPropertyManager.getBooleanProperty(mPropertyId, areaId); 2622 verifyClassSpecificGetPropertyResults((T) value, Boolean.class, areaId); 2623 } catch (IllegalArgumentException 2624 | PropertyNotAvailableException 2625 | CarInternalErrorException e) { 2626 handleClassSpecificGetPropertyExceptions(e, Boolean.class, areaId); 2627 } 2628 try { 2629 Integer value = mCarPropertyManager.getIntProperty(mPropertyId, areaId); 2630 verifyClassSpecificGetPropertyResults((T) value, Integer.class, areaId); 2631 } catch (IllegalArgumentException 2632 | PropertyNotAvailableException 2633 | CarInternalErrorException e) { 2634 handleClassSpecificGetPropertyExceptions(e, Integer.class, areaId); 2635 } 2636 try { 2637 Float value = mCarPropertyManager.getFloatProperty(mPropertyId, areaId); 2638 verifyClassSpecificGetPropertyResults((T) value, Float.class, areaId); 2639 } catch (IllegalArgumentException 2640 | PropertyNotAvailableException 2641 | CarInternalErrorException e) { 2642 handleClassSpecificGetPropertyExceptions(e, Float.class, areaId); 2643 } 2644 try { 2645 int[] primitiveArray = mCarPropertyManager.getIntArrayProperty(mPropertyId, areaId); 2646 Integer[] value = new Integer[primitiveArray.length]; 2647 for (int i = 0; i < primitiveArray.length; i++) { 2648 value[i] = (Integer) primitiveArray[i]; 2649 } 2650 verifyClassSpecificGetPropertyResults((T) value, Integer[].class, areaId); 2651 } catch (IllegalArgumentException 2652 | PropertyNotAvailableException 2653 | CarInternalErrorException e) { 2654 handleClassSpecificGetPropertyExceptions(e, Integer[].class, areaId); 2655 } 2656 } 2657 } 2658 verifyGetPropertyFails(int areaId)2659 private void verifyGetPropertyFails(int areaId) { 2660 assertGetPropertyThrowsException( 2661 mPropertyName 2662 + " is a write_only property so getProperty should throw an" 2663 + " IllegalArgumentException.", 2664 IllegalArgumentException.class, 2665 mPropertyId, 2666 areaId); 2667 } 2668 verifyPropertyNotAvailableException(PropertyNotAvailableException e)2669 private static void verifyPropertyNotAvailableException(PropertyNotAvailableException e) { 2670 if (!isAtLeastU()) { 2671 return; 2672 } 2673 assertThat(((PropertyNotAvailableException) e).getDetailedErrorCode()) 2674 .isIn(PROPERTY_NOT_AVAILABLE_ERROR_CODES); 2675 int vendorErrorCode = e.getVendorErrorCode(); 2676 assertThat(vendorErrorCode).isAtLeast(VENDOR_ERROR_CODE_MINIMUM_VALUE); 2677 assertThat(vendorErrorCode).isAtMost(VENDOR_ERROR_CODE_MAXIMUM_VALUE); 2678 } 2679 verifyInternalErrorException(CarInternalErrorException e)2680 private static void verifyInternalErrorException(CarInternalErrorException e) { 2681 if (!isAtLeastU()) { 2682 return; 2683 } 2684 int vendorErrorCode = e.getVendorErrorCode(); 2685 assertThat(vendorErrorCode).isAtLeast(VENDOR_ERROR_CODE_MINIMUM_VALUE); 2686 assertThat(vendorErrorCode).isAtMost(VENDOR_ERROR_CODE_MAXIMUM_VALUE); 2687 } 2688 verifyCarPropertyValue( CarPropertyValue<?> carPropertyValue, int expectedAreaId, String source)2689 private void verifyCarPropertyValue( 2690 CarPropertyValue<?> carPropertyValue, int expectedAreaId, String source) { 2691 verifyCarPropertyValue( 2692 carPropertyValue.getPropertyId(), 2693 carPropertyValue.getAreaId(), 2694 carPropertyValue.getStatus(), 2695 carPropertyValue.getTimestamp(), 2696 (T) carPropertyValue.getValue(), 2697 expectedAreaId, 2698 source); 2699 } 2700 verifyCarPropertyValue( int propertyId, int areaId, int status, long timestampNanos, T value, int expectedAreaId, String source)2701 private void verifyCarPropertyValue( 2702 int propertyId, 2703 int areaId, 2704 int status, 2705 long timestampNanos, 2706 T value, 2707 int expectedAreaId, 2708 String source) { 2709 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 2710 assertWithMessage( 2711 mPropertyName 2712 + " - areaId: " 2713 + areaId 2714 + " - source: " 2715 + source 2716 + " value must have correct property ID") 2717 .that(propertyId) 2718 .isEqualTo(mPropertyId); 2719 assertWithMessage( 2720 mPropertyName 2721 + " - areaId: " 2722 + areaId 2723 + " - source: " 2724 + source 2725 + " value must have correct area id: " 2726 + areaId) 2727 .that(areaId) 2728 .isEqualTo(expectedAreaId); 2729 assertWithMessage( 2730 mPropertyName 2731 + " - areaId: " 2732 + areaId 2733 + " - source: " 2734 + source 2735 + " area ID must be in carPropertyConfig#getAreaIds()") 2736 .that( 2737 Arrays.stream(carPropertyConfig.getAreaIds()) 2738 .boxed() 2739 .collect(Collectors.toList()) 2740 .contains(areaId)) 2741 .isTrue(); 2742 assertWithMessage( 2743 mPropertyName 2744 + " - areaId: " 2745 + areaId 2746 + " - source: " 2747 + source 2748 + " value must have a valid status: " 2749 + VALID_CAR_PROPERTY_VALUE_STATUSES) 2750 .that(VALID_CAR_PROPERTY_VALUE_STATUSES) 2751 .contains(status); 2752 assertWithMessage( 2753 mPropertyName 2754 + " - areaId: " 2755 + areaId 2756 + " - source: " 2757 + source 2758 + " timestamp must use the SystemClock.elapsedRealtimeNanos() time" 2759 + " base") 2760 .that(timestampNanos) 2761 .isAtLeast(0); 2762 assertWithMessage( 2763 mPropertyName 2764 + " - areaId: " 2765 + areaId 2766 + " - source: " 2767 + source 2768 + " timestamp must use the SystemClock.elapsedRealtimeNanos() time" 2769 + " base") 2770 .that(timestampNanos) 2771 .isLessThan(SystemClock.elapsedRealtimeNanos()); 2772 assertWithMessage( 2773 mPropertyName 2774 + " - areaId: " 2775 + areaId 2776 + " - source: " 2777 + source 2778 + " must return " 2779 + mPropertyType 2780 + " type value") 2781 .that(value.getClass()) 2782 .isEqualTo(mPropertyType); 2783 2784 // Only validate value if STATUS_AVAILABLE 2785 if (status != CarPropertyValue.STATUS_AVAILABLE) { 2786 return; 2787 } 2788 2789 mCarPropertyValueVerifier.ifPresent( 2790 propertyValueVerifier -> 2791 propertyValueVerifier.verify( 2792 mVerifierContext, 2793 carPropertyConfig, 2794 propertyId, 2795 areaId, 2796 timestampNanos, 2797 value)); 2798 2799 if (mRequirePropertyValueToBeInConfigArray) { 2800 assertWithMessage( 2801 mPropertyName 2802 + " - areaId: " 2803 + areaId 2804 + " - source: " 2805 + source 2806 + " value must be listed in configArray," 2807 + " configArray:") 2808 .that(carPropertyConfig.getConfigArray()) 2809 .contains(value); 2810 } 2811 2812 if (isAtLeastU()) { 2813 List<T> supportedEnumValues = 2814 carPropertyConfig.getAreaIdConfig(areaId).getSupportedEnumValues(); 2815 if (!supportedEnumValues.isEmpty()) { 2816 if (mEnumIsBitMap) { 2817 int allValidValues = 0; 2818 for (T bitEnumValue : supportedEnumValues) { 2819 allValidValues |= ((Integer) bitEnumValue).intValue(); 2820 } 2821 assertWithMessage( 2822 mPropertyName 2823 + " - areaId: " 2824 + areaId 2825 + " - source: " 2826 + source 2827 + " value must be a combination of values listed in " 2828 + "getSupportedEnumValues()") 2829 .that(((Integer) value).intValue() & allValidValues) 2830 .isEqualTo(value); 2831 } else { 2832 assertWithMessage( 2833 mPropertyName 2834 + " - areaId: " 2835 + areaId 2836 + " - source: " 2837 + source 2838 + " value must be listed in getSupportedEnumValues()") 2839 .that(value) 2840 .isIn(supportedEnumValues); 2841 } 2842 } 2843 } 2844 2845 T areaIdMinValue = (T) carPropertyConfig.getMinValue(areaId); 2846 T areaIdMaxValue = (T) carPropertyConfig.getMaxValue(areaId); 2847 if (areaIdMinValue != null && areaIdMaxValue != null) { 2848 assertWithMessage( 2849 "Property value: " 2850 + value 2851 + " must be between the max: " 2852 + areaIdMaxValue 2853 + " and min: " 2854 + areaIdMinValue 2855 + " values for area ID: " 2856 + Integer.toHexString(areaId)) 2857 .that(verifyValueInRange(areaIdMinValue, areaIdMaxValue, (T) value)) 2858 .isTrue(); 2859 } 2860 2861 if (mVerifyErrorStates) { 2862 assertWithMessage( 2863 "When ADAS feature is enabled, " 2864 + VehiclePropertyIds.toString(mPropertyId) 2865 + " must not be set to " 2866 + ErrorState.NOT_AVAILABLE_DISABLED 2867 + " (ErrorState#NOT_AVAILABLE_DISABLED") 2868 .that((Integer) value) 2869 .isNotEqualTo(ErrorState.NOT_AVAILABLE_DISABLED); 2870 } 2871 } 2872 verifyValueInRange(T min, T max, T value)2873 private boolean verifyValueInRange(T min, T max, T value) { 2874 int propertyType = mPropertyId & VehiclePropertyType.MASK; 2875 switch (propertyType) { 2876 case VehiclePropertyType.INT32: 2877 return ((Integer) value >= (Integer) min && (Integer) value <= (Integer) max); 2878 case VehiclePropertyType.INT64: 2879 return ((Long) value >= (Long) min && (Long) value <= (Long) max); 2880 case VehiclePropertyType.FLOAT: 2881 return (((Float) value > (Float) min || valueEquals(value, min)) 2882 && ((Float) value < (Float) max || valueEquals(value, max))); 2883 default: 2884 return false; 2885 } 2886 } 2887 generateAllPossibleAreaIds(ImmutableSet<Integer> areas)2888 private static ImmutableSet<Integer> generateAllPossibleAreaIds(ImmutableSet<Integer> areas) { 2889 ImmutableSet.Builder<Integer> allPossibleAreaIdsBuilder = ImmutableSet.builder(); 2890 for (int i = 1; i <= areas.size(); i++) { 2891 allPossibleAreaIdsBuilder.addAll( 2892 Sets.combinations(areas, i).stream() 2893 .map( 2894 areaCombo -> { 2895 Integer possibleAreaId = 0; 2896 for (Integer area : areaCombo) { 2897 possibleAreaId |= area; 2898 } 2899 return possibleAreaId; 2900 }) 2901 .collect(Collectors.toList())); 2902 } 2903 return allPossibleAreaIdsBuilder.build(); 2904 } 2905 verifyValidAreaIdsForAreaType(ImmutableSet<Integer> allPossibleAreaIds)2906 private void verifyValidAreaIdsForAreaType(ImmutableSet<Integer> allPossibleAreaIds) { 2907 for (int areaId : getCarPropertyConfig().getAreaIds()) { 2908 assertWithMessage( 2909 mPropertyName 2910 + "'s area ID must be a valid " 2911 + areaTypeToString(mAreaType) 2912 + " area ID") 2913 .that(areaId) 2914 .isIn(allPossibleAreaIds); 2915 } 2916 } 2917 verifyNoAreaOverlapInAreaIds()2918 private void verifyNoAreaOverlapInAreaIds() { 2919 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 2920 if (carPropertyConfig.getAreaIds().length < 2) { 2921 return; 2922 } 2923 ImmutableSet<Integer> areaIds = 2924 ImmutableSet.copyOf( 2925 Arrays.stream(carPropertyConfig.getAreaIds()) 2926 .boxed() 2927 .collect(Collectors.toList())); 2928 List<Integer> areaIdOverlapCheckResults = 2929 Sets.combinations(areaIds, 2).stream() 2930 .map( 2931 areaIdPair -> { 2932 List<Integer> areaIdPairAsList = 2933 areaIdPair.stream().collect(Collectors.toList()); 2934 return areaIdPairAsList.get(0) & areaIdPairAsList.get(1); 2935 }) 2936 .collect(Collectors.toList()); 2937 2938 assertWithMessage( 2939 mPropertyName 2940 + " area IDs: " 2941 + Arrays.toString(carPropertyConfig.getAreaIds()) 2942 + " must contain each area only once (e.g. no bitwise AND overlap)" 2943 + " for the area type: " 2944 + areaTypeToString(mAreaType)) 2945 .that( 2946 Collections.frequency(areaIdOverlapCheckResults, 0) 2947 == areaIdOverlapCheckResults.size()) 2948 .isTrue(); 2949 } 2950 2951 /** Returns whether all AreaIds for the property is readable. */ canReadAllAreaIds(CarPropertyConfig<T> carPropertyConfig)2952 private boolean canReadAllAreaIds(CarPropertyConfig<T> carPropertyConfig) { 2953 if (!AREA_ID_CONFIG_ACCESS_FLAG) { 2954 return canRead(carPropertyConfig.getAccess()); 2955 } 2956 for (int areaId : carPropertyConfig.getAreaIds()) { 2957 if (!canRead(carPropertyConfig, areaId)) { 2958 return false; 2959 } 2960 } 2961 return true; 2962 } 2963 2964 /** Verifies that exceptions are thrown when caller does not have read/write permission. */ verifyPermissionNotGrantedException()2965 private void verifyPermissionNotGrantedException() { 2966 // If the client itself already has read/write permissions without adopting any permissions 2967 // from the shell, skip the test. 2968 if (hasReadPermissions(mReadPermissions) || hasWritePermissions(mWritePermissions)) { 2969 return; 2970 } 2971 2972 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 2973 assertWithMessage( 2974 mPropertyName 2975 + " - property ID: " 2976 + mPropertyId 2977 + " CarPropertyConfig should not be accessible without" 2978 + " permissions.") 2979 .that(getCarPropertyConfig(/* useCache= */ false)) 2980 .isNull(); 2981 2982 int access = carPropertyConfig.getAccess(); 2983 for (int areaId : carPropertyConfig.getAreaIds()) { 2984 if (AREA_ID_CONFIG_ACCESS_FLAG) { 2985 access = carPropertyConfig.getAreaIdConfig(areaId).getAccess(); 2986 } 2987 2988 if (canRead(access)) { 2989 assertGetPropertyThrowsException( 2990 mPropertyName 2991 + " - property ID: " 2992 + mPropertyId 2993 + " - area ID: " 2994 + areaId 2995 + " should not be able to be read without permissions.", 2996 SecurityException.class, 2997 mPropertyId, 2998 areaId); 2999 assertThrows( 3000 SecurityException.class, 3001 () -> { 3002 mCarPropertyManager.isPropertyAvailable(mPropertyId, areaId); 3003 }); 3004 } 3005 if (canWrite(access)) { 3006 assertThrows( 3007 mPropertyName 3008 + " - property ID: " 3009 + mPropertyId 3010 + " - area ID: " 3011 + areaId 3012 + " should not be able to be written to without permissions.", 3013 SecurityException.class, 3014 () -> 3015 mCarPropertyManager.setProperty( 3016 mPropertyType, 3017 mPropertyId, 3018 areaId, 3019 getDefaultValue(mPropertyType))); 3020 } 3021 } 3022 3023 if (!canReadAllAreaIds(carPropertyConfig)) { 3024 return; 3025 } 3026 3027 // We expect a return value of false and not a SecurityException thrown. 3028 // This is because registerCallback first tries to get the CarPropertyConfig for the 3029 // property, but since no permissions have been granted it can't find the CarPropertyConfig, 3030 // so it immediately returns false. 3031 assertWithMessage( 3032 mPropertyName 3033 + " - property ID: " 3034 + mPropertyId 3035 + " should not be able to be listened to without permissions.") 3036 .that(mCarPropertyManager.registerCallback(FAKE_CALLBACK, mPropertyId, 0f)) 3037 .isFalse(); 3038 3039 if (isAtLeastV() && Flags.variableUpdateRate()) { 3040 // For the new API, if the caller does not have read and write permission, it throws 3041 // SecurityException. 3042 assertThrows( 3043 mPropertyName 3044 + " - property ID: " 3045 + mPropertyId 3046 + " should not be able to be listened to without permissions.", 3047 SecurityException.class, 3048 () -> mCarPropertyManager.subscribePropertyEvents(mPropertyId, FAKE_CALLBACK)); 3049 } 3050 } 3051 isAreaIdSupportedInList(int areaIdToFind, Collection<Integer> areaIds)3052 private static boolean isAreaIdSupportedInList(int areaIdToFind, Collection<Integer> areaIds) { 3053 for (Integer areaId : areaIds) { 3054 if ((areaIdToFind & areaId) == areaIdToFind) { 3055 return true; 3056 } 3057 } 3058 return false; 3059 } 3060 getAreaIdAccess( CarPropertyConfig<?> carPropertyConfig, int areaId)3061 private static Optional<Integer> getAreaIdAccess( 3062 CarPropertyConfig<?> carPropertyConfig, int areaId) { 3063 return AREA_ID_CONFIG_ACCESS_FLAG 3064 ? Optional.of(carPropertyConfig.getAreaIdConfig(areaId).getAccess()) 3065 : Optional.empty(); 3066 } 3067 getAreaIdAccessOrElseGlobalAccess( CarPropertyConfig<?> carPropertyConfig, int areaId)3068 private static Integer getAreaIdAccessOrElseGlobalAccess( 3069 CarPropertyConfig<?> carPropertyConfig, int areaId) { 3070 return getAreaIdAccess(carPropertyConfig, areaId).orElse(carPropertyConfig.getAccess()); 3071 } 3072 canRead(int accessMode)3073 private static boolean canRead(int accessMode) { 3074 return accessMode == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ 3075 || accessMode == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE; 3076 } 3077 canWrite(int accessMode)3078 private static boolean canWrite(int accessMode) { 3079 return accessMode == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE 3080 || accessMode == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE; 3081 } 3082 canRead(CarPropertyConfig<?> carPropertyConfig, int areaId)3083 private static boolean canRead(CarPropertyConfig<?> carPropertyConfig, int areaId) { 3084 return doesAreaIdAccessMatch( 3085 carPropertyConfig, areaId, CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) 3086 || doesAreaIdAccessMatch( 3087 carPropertyConfig, 3088 areaId, 3089 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE); 3090 } 3091 canWrite(CarPropertyConfig<?> carPropertyConfig, int areaId)3092 private static boolean canWrite(CarPropertyConfig<?> carPropertyConfig, int areaId) { 3093 return doesAreaIdAccessMatch( 3094 carPropertyConfig, areaId, CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) 3095 || doesAreaIdAccessMatch( 3096 carPropertyConfig, 3097 areaId, 3098 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE); 3099 } 3100 doesAreaIdAccessMatch( CarPropertyConfig<?> carPropertyConfig, int areaId, int expectedAccess)3101 private static boolean doesAreaIdAccessMatch( 3102 CarPropertyConfig<?> carPropertyConfig, int areaId, int expectedAccess) { 3103 return getAreaIdAccess(carPropertyConfig, areaId) 3104 .filter(areaIdAccess -> areaIdAccess == expectedAccess) 3105 .isPresent(); 3106 } 3107 3108 /** Verifies that hvac temperature is valid. */ verifyHvacTemperatureIsValid( float temp, int minTempTimesTen, int maxTempTimesTen, int incrementTimesTen)3109 public static void verifyHvacTemperatureIsValid( 3110 float temp, int minTempTimesTen, int maxTempTimesTen, int incrementTimesTen) { 3111 int intTempTimesTen = (int) (temp * 10f); 3112 assertWithMessage( 3113 "The temperature value " 3114 + intTempTimesTen 3115 + " must be at least " 3116 + minTempTimesTen 3117 + " and at most " 3118 + maxTempTimesTen) 3119 .that(intTempTimesTen >= minTempTimesTen && intTempTimesTen <= maxTempTimesTen) 3120 .isTrue(); 3121 3122 int remainder = (intTempTimesTen - minTempTimesTen) % incrementTimesTen; 3123 assertWithMessage( 3124 "The temperature value " 3125 + intTempTimesTen 3126 + " is not a valid temperature value. Valid values start from " 3127 + minTempTimesTen 3128 + " and increment by " 3129 + incrementTimesTen 3130 + " until the max temperature setting of " 3131 + maxTempTimesTen) 3132 .that(remainder) 3133 .isEqualTo(0); 3134 } 3135 3136 /** 3137 * A structure containing verifier context. 3138 * 3139 * <p>This contains VehiclePropertyVerifier members that might be useful for the verification. 3140 */ 3141 public static class VerifierContext { 3142 private final CarPropertyManager mCarPropertyManager; 3143 VerifierContext(CarPropertyManager carPropertyManager)3144 public VerifierContext(CarPropertyManager carPropertyManager) { 3145 mCarPropertyManager = carPropertyManager; 3146 } 3147 getCarPropertyManager()3148 public CarPropertyManager getCarPropertyManager() { 3149 return mCarPropertyManager; 3150 } 3151 } 3152 3153 /** An interface for verifying the config array. */ 3154 public interface ConfigArrayVerifier { 3155 /** Verifies the config array. Throws exception if not valid. */ verify(VerifierContext verifierContext, List<Integer> configArray)3156 void verify(VerifierContext verifierContext, List<Integer> configArray); 3157 } 3158 3159 /** An interface for verifying the property value. */ 3160 public interface CarPropertyValueVerifier<T> { 3161 /** Verifies the property value. Throws exception if not valid. */ verify( VerifierContext verifierContext, CarPropertyConfig<T> carPropertyConfig, int propertyId, int areaId, long timestampNanos, T value)3162 void verify( 3163 VerifierContext verifierContext, 3164 CarPropertyConfig<T> carPropertyConfig, 3165 int propertyId, 3166 int areaId, 3167 long timestampNanos, 3168 T value); 3169 } 3170 3171 /** An interface for verifying the areaIds. */ 3172 public interface AreaIdsVerifier { 3173 /** Verifies the areaIds. Throws exception if not valid. */ verify(VerifierContext verifierContext, int[] areaIds)3174 void verify(VerifierContext verifierContext, int[] areaIds); 3175 } 3176 3177 /** An interface for verifying the {@link CarPropertyConfig}. */ 3178 public interface CarPropertyConfigVerifier { 3179 /** Verifies the property config. Throws exception if not valid. */ verify(VerifierContext verifierContext, CarPropertyConfig<?> carPropertyConfig)3180 void verify(VerifierContext verifierContext, CarPropertyConfig<?> carPropertyConfig); 3181 } 3182 3183 /** The builder class. */ 3184 public static class Builder<T> { 3185 private final int mPropertyId; 3186 private final ImmutableSet<Integer> mAllowedAccessModes; 3187 private final int mAreaType; 3188 private final int mChangeMode; 3189 private final Class<T> mPropertyType; 3190 private CarPropertyManager mCarPropertyManager; 3191 private boolean mRequiredProperty = false; 3192 private Optional<ConfigArrayVerifier> mConfigArrayVerifier = Optional.empty(); 3193 private Optional<CarPropertyValueVerifier<T>> mCarPropertyValueVerifier = Optional.empty(); 3194 private Optional<AreaIdsVerifier> mAreaIdsVerifier = Optional.empty(); 3195 private Optional<CarPropertyConfigVerifier> mCarPropertyConfigVerifier = Optional.empty(); 3196 private Optional<Integer> mDependentOnPropertyId = Optional.empty(); 3197 private ImmutableSet<String> mDependentOnPropertyPermissions = ImmutableSet.of(); 3198 private ImmutableSet<Integer> mPossibleConfigArrayValues = ImmutableSet.of(); 3199 private boolean mEnumIsBitMap = false; 3200 private ImmutableSet<T> mAllPossibleEnumValues = ImmutableSet.of(); 3201 private ImmutableSet<T> mAllPossibleUnwritableValues = ImmutableSet.of(); 3202 private ImmutableSet<T> mAllPossibleUnavailableValues = ImmutableSet.of(); 3203 private boolean mRequirePropertyValueToBeInConfigArray = false; 3204 private boolean mVerifySetterWithConfigArrayValues = false; 3205 private boolean mRequireMinMaxValues = false; 3206 private boolean mRequireMinValuesToBeZero = false; 3207 private boolean mRequireZeroToBeContainedInMinMaxRanges = false; 3208 private boolean mPossiblyDependentOnHvacPowerOn = false; 3209 private boolean mVerifyErrorStates = false; 3210 private int mPowerPropagationDelayMs = 2000; 3211 private final ImmutableSet.Builder<String> mReadPermissionsBuilder = ImmutableSet.builder(); 3212 private final ImmutableList.Builder<ImmutableSet<String>> mWritePermissionsBuilder = 3213 ImmutableList.builder(); 3214 Builder( int propertyId, ImmutableSet<Integer> allowedAccessModes, int areaType, int changeMode, Class<T> propertyType)3215 private Builder( 3216 int propertyId, 3217 ImmutableSet<Integer> allowedAccessModes, 3218 int areaType, 3219 int changeMode, 3220 Class<T> propertyType) { 3221 mPropertyId = propertyId; 3222 mAllowedAccessModes = allowedAccessModes; 3223 mAreaType = areaType; 3224 mChangeMode = changeMode; 3225 mPropertyType = propertyType; 3226 } 3227 getPropertyId()3228 public int getPropertyId() { 3229 return mPropertyId; 3230 } 3231 isRequired()3232 public boolean isRequired() { 3233 return mRequiredProperty; 3234 } 3235 3236 /** Sets the car property manager. */ setCarPropertyManager(CarPropertyManager carPropertyManager)3237 public Builder<T> setCarPropertyManager(CarPropertyManager carPropertyManager) { 3238 mCarPropertyManager = carPropertyManager; 3239 return this; 3240 } 3241 3242 /** Sets the property as required. Test will fail if the property is not supported. */ requireProperty()3243 public Builder<T> requireProperty() { 3244 mRequiredProperty = true; 3245 return this; 3246 } 3247 3248 /** 3249 * Sets the property as required if condition is true. If condition is true, test will fail 3250 * if the property is not supported. 3251 */ requireProperty(boolean condition)3252 public Builder<T> requireProperty(boolean condition) { 3253 mRequiredProperty = condition; 3254 return this; 3255 } 3256 3257 /** Sets the config array verifier. */ setConfigArrayVerifier(ConfigArrayVerifier configArrayVerifier)3258 public Builder<T> setConfigArrayVerifier(ConfigArrayVerifier configArrayVerifier) { 3259 mConfigArrayVerifier = Optional.of(configArrayVerifier); 3260 return this; 3261 } 3262 3263 /** Sets the car property value verifier. */ setCarPropertyValueVerifier( CarPropertyValueVerifier<T> carPropertyValueVerifier)3264 public Builder<T> setCarPropertyValueVerifier( 3265 CarPropertyValueVerifier<T> carPropertyValueVerifier) { 3266 mCarPropertyValueVerifier = Optional.of(carPropertyValueVerifier); 3267 return this; 3268 } 3269 3270 /** Sets the areaIds verifier. */ setAreaIdsVerifier(AreaIdsVerifier areaIdsVerifier)3271 public Builder<T> setAreaIdsVerifier(AreaIdsVerifier areaIdsVerifier) { 3272 mAreaIdsVerifier = Optional.of(areaIdsVerifier); 3273 return this; 3274 } 3275 3276 /** Sets the car property config verifier. */ setCarPropertyConfigVerifier( CarPropertyConfigVerifier carPropertyConfigVerifier)3277 public Builder<T> setCarPropertyConfigVerifier( 3278 CarPropertyConfigVerifier carPropertyConfigVerifier) { 3279 mCarPropertyConfigVerifier = Optional.of(carPropertyConfigVerifier); 3280 return this; 3281 } 3282 3283 /** Sets that the property is depending on other properties. */ setDependentOnProperty( Integer dependentPropertyId, ImmutableSet<String> dependentPropertyPermissions)3284 public Builder<T> setDependentOnProperty( 3285 Integer dependentPropertyId, ImmutableSet<String> dependentPropertyPermissions) { 3286 mDependentOnPropertyId = Optional.of(dependentPropertyId); 3287 mDependentOnPropertyPermissions = dependentPropertyPermissions; 3288 return this; 3289 } 3290 3291 /** Sets the possible config array values. */ setPossibleConfigArrayValues( ImmutableSet<Integer> possibleConfigArrayValues)3292 public Builder<T> setPossibleConfigArrayValues( 3293 ImmutableSet<Integer> possibleConfigArrayValues) { 3294 mPossibleConfigArrayValues = possibleConfigArrayValues; 3295 return this; 3296 } 3297 3298 /** 3299 * Used to assert that supportedEnum values provided in config are a subset of all possible 3300 * enum values that can be set for the property. 3301 */ setBitMapEnumEnabled(boolean enabled)3302 public Builder<T> setBitMapEnumEnabled(boolean enabled) { 3303 mEnumIsBitMap = enabled; 3304 return this; 3305 } 3306 3307 /* 3308 * Used to assert that supportedEnum values provided in config are a subset of all possible 3309 * enum values that can be set for the property. If enums is defined as a bit map rather 3310 * than a regular integer, setBitMapEnumEnabled(boolean) should be used as well. 3311 */ setAllPossibleEnumValues(ImmutableSet<T> allPossibleEnumValues)3312 public Builder<T> setAllPossibleEnumValues(ImmutableSet<T> allPossibleEnumValues) { 3313 mAllPossibleEnumValues = allPossibleEnumValues; 3314 return this; 3315 } 3316 3317 /** 3318 * Used to assert that certain values that must not be allowed to be written will throw an 3319 * IllegalArgumentException when we try to write them using setProperty. 3320 */ setAllPossibleUnwritableValues( ImmutableSet<T> allPossibleUnwritableValues)3321 public Builder<T> setAllPossibleUnwritableValues( 3322 ImmutableSet<T> allPossibleUnwritableValues) { 3323 mAllPossibleUnwritableValues = allPossibleUnwritableValues; 3324 return this; 3325 } 3326 3327 /** 3328 * Used to assert that certain values that are temporarily unavailable to be written will 3329 * throw a PropertyNotAvailableException when we try to write them using setProperty. 3330 */ setAllPossibleUnavailableValues( ImmutableSet<T> allPossibleUnavailableValues)3331 public Builder<T> setAllPossibleUnavailableValues( 3332 ImmutableSet<T> allPossibleUnavailableValues) { 3333 mAllPossibleUnavailableValues = allPossibleUnavailableValues; 3334 return this; 3335 } 3336 3337 /** 3338 * Requires that the property value must be one of the value defined in the config array. 3339 */ requirePropertyValueTobeInConfigArray()3340 public Builder<T> requirePropertyValueTobeInConfigArray() { 3341 mRequirePropertyValueToBeInConfigArray = true; 3342 return this; 3343 } 3344 3345 /** Uses the config array values to set the property value. */ verifySetterWithConfigArrayValues()3346 public Builder<T> verifySetterWithConfigArrayValues() { 3347 mVerifySetterWithConfigArrayValues = true; 3348 return this; 3349 } 3350 3351 /** Requires minValue and maxValue to be set. */ requireMinMaxValues()3352 public Builder<T> requireMinMaxValues() { 3353 mRequireMinMaxValues = true; 3354 return this; 3355 } 3356 3357 /** Requires minValue to be 0. */ requireMinValuesToBeZero()3358 public Builder<T> requireMinValuesToBeZero() { 3359 mRequireMinValuesToBeZero = true; 3360 return this; 3361 } 3362 3363 /** Requires 0 to be contains within minValue and maxValue. */ requireZeroToBeContainedInMinMaxRanges()3364 public Builder<T> requireZeroToBeContainedInMinMaxRanges() { 3365 mRequireZeroToBeContainedInMinMaxRanges = true; 3366 return this; 3367 } 3368 3369 /** Sets that the property might depend on HVAC_POWER_ON. */ setPossiblyDependentOnHvacPowerOn()3370 public Builder<T> setPossiblyDependentOnHvacPowerOn() { 3371 mPossiblyDependentOnHvacPowerOn = true; 3372 return this; 3373 } 3374 3375 /** Verifies if returning error state, the error state is expected. */ verifyErrorStates()3376 public Builder<T> verifyErrorStates() { 3377 mVerifyErrorStates = true; 3378 return this; 3379 } 3380 3381 /** Sets the delay to wait for the power state to propagate to dependent properties. */ setPowerPropagationDelayMs(int delayMs)3382 public Builder<T> setPowerPropagationDelayMs(int delayMs) { 3383 mPowerPropagationDelayMs = delayMs; 3384 return this; 3385 } 3386 3387 /** Adds the required read permission. */ addReadPermission(String readPermission)3388 public Builder<T> addReadPermission(String readPermission) { 3389 mReadPermissionsBuilder.add(readPermission); 3390 return this; 3391 } 3392 3393 /** 3394 * Adds a single permission that alone can be used to update the property. Any set of 3395 * permissions in {@code mWritePermissionsBuilder} can be used to set the property. 3396 * 3397 * @param writePermission a permission used to update the property 3398 */ addWritePermission(String writePermission)3399 public Builder<T> addWritePermission(String writePermission) { 3400 mWritePermissionsBuilder.add(ImmutableSet.of(writePermission)); 3401 return this; 3402 } 3403 3404 /** 3405 * Adds a set of permissions that together can be used to update the property. Any set of 3406 * permissions in {@code mWritePermissionsBuilder} can be used to set the property. 3407 * 3408 * @param writePermissionSet a set of permissions that together can be used to update the 3409 * property. 3410 */ addWritePermission(ImmutableSet<String> writePermissionSet)3411 public Builder<T> addWritePermission(ImmutableSet<String> writePermissionSet) { 3412 mWritePermissionsBuilder.add(writePermissionSet); 3413 return this; 3414 } 3415 3416 /** Builds the verifier. */ build()3417 public VehiclePropertyVerifier<T> build() { 3418 return new VehiclePropertyVerifier<>( 3419 mCarPropertyManager, 3420 mPropertyId, 3421 mAllowedAccessModes, 3422 mAreaType, 3423 mChangeMode, 3424 mPropertyType, 3425 mRequiredProperty, 3426 mConfigArrayVerifier, 3427 mCarPropertyValueVerifier, 3428 mAreaIdsVerifier, 3429 mCarPropertyConfigVerifier, 3430 mDependentOnPropertyId, 3431 mDependentOnPropertyPermissions, 3432 mPossibleConfigArrayValues, 3433 mEnumIsBitMap, 3434 mAllPossibleEnumValues, 3435 mAllPossibleUnwritableValues, 3436 mAllPossibleUnavailableValues, 3437 mRequirePropertyValueToBeInConfigArray, 3438 mVerifySetterWithConfigArrayValues, 3439 mRequireMinMaxValues, 3440 mRequireMinValuesToBeZero, 3441 mRequireZeroToBeContainedInMinMaxRanges, 3442 mPossiblyDependentOnHvacPowerOn, 3443 mVerifyErrorStates, 3444 mPowerPropagationDelayMs, 3445 mReadPermissionsBuilder.build(), 3446 mWritePermissionsBuilder.build()); 3447 } 3448 } 3449 3450 private static class CarPropertyValueCallback 3451 implements CarPropertyManager.CarPropertyEventCallback { 3452 private final String mPropertyName; 3453 private final int[] mAreaIds; 3454 private final int mTotalCarPropertyValuesPerAreaId; 3455 private final CountDownLatch mCountDownLatch = new CountDownLatch(1); 3456 private final Object mLock = new Object(); 3457 3458 @GuardedBy("mLock") 3459 private final SparseArray<List<CarPropertyValue<?>>> mAreaIdToCarPropertyValues = 3460 new SparseArray<>(); 3461 3462 private final long mTimeoutMillis; 3463 CarPropertyValueCallback( String propertyName, int[] areaIds, int totalCarPropertyValuesPerAreaId, long timeoutMillis)3464 CarPropertyValueCallback( 3465 String propertyName, 3466 int[] areaIds, 3467 int totalCarPropertyValuesPerAreaId, 3468 long timeoutMillis) { 3469 mPropertyName = propertyName; 3470 mAreaIds = areaIds; 3471 mTotalCarPropertyValuesPerAreaId = totalCarPropertyValuesPerAreaId; 3472 mTimeoutMillis = timeoutMillis; 3473 synchronized (mLock) { 3474 for (int areaId : mAreaIds) { 3475 mAreaIdToCarPropertyValues.put(areaId, new ArrayList<>()); 3476 } 3477 } 3478 } 3479 getAreaIdToCarPropertyValues()3480 public SparseArray<List<CarPropertyValue<?>>> getAreaIdToCarPropertyValues() { 3481 boolean awaitSuccess = false; 3482 try { 3483 awaitSuccess = mCountDownLatch.await(mTimeoutMillis, TimeUnit.MILLISECONDS); 3484 } catch (InterruptedException e) { 3485 assertWithMessage( 3486 "Waiting for onChangeEvent callback(s) for " 3487 + mPropertyName 3488 + " threw an exception: " 3489 + e) 3490 .fail(); 3491 } 3492 synchronized (mLock) { 3493 assertWithMessage( 3494 "Never received " 3495 + mTotalCarPropertyValuesPerAreaId 3496 + " CarPropertyValues for all " 3497 + mPropertyName 3498 + "'s areaIds: " 3499 + Arrays.toString(mAreaIds) 3500 + " before " 3501 + mTimeoutMillis 3502 + " ms timeout - " 3503 + mAreaIdToCarPropertyValues) 3504 .that(awaitSuccess) 3505 .isTrue(); 3506 return mAreaIdToCarPropertyValues.clone(); 3507 } 3508 } 3509 3510 @Override onChangeEvent(CarPropertyValue carPropertyValue)3511 public void onChangeEvent(CarPropertyValue carPropertyValue) { 3512 synchronized (mLock) { 3513 if (hasEnoughCarPropertyValuesForEachAreaIdLocked()) { 3514 return; 3515 } 3516 mAreaIdToCarPropertyValues.get(carPropertyValue.getAreaId()).add(carPropertyValue); 3517 if (hasEnoughCarPropertyValuesForEachAreaIdLocked()) { 3518 mCountDownLatch.countDown(); 3519 } 3520 } 3521 } 3522 3523 @GuardedBy("mLock") hasEnoughCarPropertyValuesForEachAreaIdLocked()3524 private boolean hasEnoughCarPropertyValuesForEachAreaIdLocked() { 3525 for (int areaId : mAreaIds) { 3526 List<CarPropertyValue<?>> carPropertyValues = 3527 mAreaIdToCarPropertyValues.get(areaId); 3528 if (carPropertyValues == null 3529 || carPropertyValues.size() < mTotalCarPropertyValuesPerAreaId) { 3530 return false; 3531 } 3532 } 3533 return true; 3534 } 3535 3536 @Override onErrorEvent(int propId, int zone)3537 public void onErrorEvent(int propId, int zone) {} 3538 3539 @Override onErrorEvent(int propId, int areaId, int errorCode)3540 public void onErrorEvent(int propId, int areaId, int errorCode) {} 3541 } 3542 3543 private static class SetterCallback<T> implements CarPropertyManager.CarPropertyEventCallback { 3544 private final int mPropertyId; 3545 private final String mPropertyName; 3546 private final int mAreaId; 3547 private final T mExpectedSetValue; 3548 private final CountDownLatch mCountDownLatch = new CountDownLatch(1); 3549 private final long mCreationTimeNanos = SystemClock.elapsedRealtimeNanos(); 3550 private CarPropertyValue<?> mUpdatedCarPropertyValue = null; 3551 private T mReceivedValue = null; 3552 private Integer mSetErrorCode = null; 3553 SetterCallback(int propertyId, int areaId, T expectedSetValue)3554 SetterCallback(int propertyId, int areaId, T expectedSetValue) { 3555 mPropertyId = propertyId; 3556 mPropertyName = VehiclePropertyIds.toString(propertyId); 3557 mAreaId = areaId; 3558 mExpectedSetValue = expectedSetValue; 3559 } 3560 valueToString(T value)3561 private String valueToString(T value) { 3562 if (value.getClass().isArray()) { 3563 return Arrays.toString((Object[]) value); 3564 } 3565 return value.toString(); 3566 } 3567 3568 /** 3569 * Waits at most {@code timeoutInSec} for a property event that is the result of a {@code 3570 * setProperty} request. 3571 * 3572 * <p>If {@link #onChangeEvent(CarPropertyValue)} is called, then this method will return 3573 * the {@link CarPropertyValue} if: 3574 * 3575 * <ul> 3576 * <li>The property ID and area ID match {@link #mPropertyId} and {@link #mAreaId} 3577 * <li>The event is timestamped after {@link #mCreationTimeNanos} but before {@link 3578 * SystemClock#elapsedRealtimeNanos()} 3579 * <li>One of the following is true: 3580 * <ul> 3581 * <li>{@link CarPropertyValue#getStatus()} is NOT {@link 3582 * CarPropertyValue#STATUS_AVAILABLE} 3583 * <li>{@link CarPropertyValue#getStatus()} is {@link 3584 * CarPropertyValue#STATUS_AVAILABLE} and {@link CarPropertyValue#getValue()} 3585 * equals {@link #mExpectedSetValue}. 3586 * </ul> 3587 * </ul> 3588 * 3589 * <p>If {@link #onErrorEvent(int, int)} is called, then this method will return {@code 3590 * null} if: 3591 * 3592 * <ul> 3593 * <li>The property ID and area ID match {@link #mPropertyId} and {@link #mAreaId} 3594 * </ul> 3595 * 3596 * <p>If {@code timeoutInSec} is reached before any of the above conditions are met or if 3597 * {@link InterruptedException} is thrown, then this method will throw an {@code 3598 * AssertionError} and fail the test. 3599 * 3600 * <p>If the callback receives a valid CarPropertyValue and a valid set property error code, 3601 * then an {@code AssertionError} will be thrown and the test will fail. 3602 * 3603 * @param timeoutInSec maximum time in seconds to wait for an expected event 3604 * @return a valid {@link CarPropertyValue} if all {@link #onChangeEvent(CarPropertyValue)} 3605 * conditions are met, or {@code null} if all {@link #onErrorEvent(int, int)} conditions 3606 * are met. 3607 */ waitForPropertyEvent(int timeoutInSec)3608 public CarPropertyValue<?> waitForPropertyEvent(int timeoutInSec) { 3609 try { 3610 assertWithMessage( 3611 "Never received onChangeEvent(s) or onErrorEvent(s) for " 3612 + mPropertyName 3613 + " new value: " 3614 + valueToString(mExpectedSetValue) 3615 + " before" 3616 + " timeout. Received: " 3617 + (mReceivedValue == null 3618 ? "No value" 3619 : valueToString(mReceivedValue))) 3620 .that(mCountDownLatch.await(timeoutInSec, TimeUnit.SECONDS)) 3621 .isTrue(); 3622 } catch (InterruptedException e) { 3623 assertWithMessage( 3624 "Waiting for onChangeEvent set callback for " 3625 + mPropertyName 3626 + " threw an exception: " 3627 + e) 3628 .fail(); 3629 } 3630 assertWithMessage( 3631 "Received both a set error code" 3632 + mSetErrorCode 3633 + " and a new value: " 3634 + mUpdatedCarPropertyValue 3635 + " for propertyId: " 3636 + mPropertyName 3637 + " areaId: " 3638 + mAreaId) 3639 .that(mUpdatedCarPropertyValue != null && mSetErrorCode != null) 3640 .isFalse(); 3641 return mUpdatedCarPropertyValue; 3642 } 3643 3644 @Override onChangeEvent(CarPropertyValue carPropertyValue)3645 public void onChangeEvent(CarPropertyValue carPropertyValue) { 3646 // Checking whether the updated carPropertyValue is caused by the setProperty request. 3647 if (mUpdatedCarPropertyValue != null 3648 || carPropertyValue.getPropertyId() != mPropertyId 3649 || carPropertyValue.getAreaId() != mAreaId 3650 || carPropertyValue.getTimestamp() <= mCreationTimeNanos 3651 || carPropertyValue.getTimestamp() >= SystemClock.elapsedRealtimeNanos()) { 3652 return; 3653 } 3654 mReceivedValue = (T) carPropertyValue.getValue(); 3655 if (carPropertyValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE 3656 && !valueEquals(mExpectedSetValue, mReceivedValue)) { 3657 return; 3658 } 3659 mUpdatedCarPropertyValue = carPropertyValue; 3660 mCountDownLatch.countDown(); 3661 } 3662 3663 @Override onErrorEvent(int propId, int areaId)3664 public void onErrorEvent(int propId, int areaId) { 3665 onErrorEvent(propId, areaId, CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN); 3666 } 3667 3668 @Override onErrorEvent(int propertyId, int areaId, int errorCode)3669 public void onErrorEvent(int propertyId, int areaId, int errorCode) { 3670 if (mCountDownLatch.getCount() == 0) { 3671 Log.w( 3672 TAG, 3673 "SetterCallback - Dropping onErrorEvent. Received an onErrorEvent call " 3674 + " after the CountDownLatch finished - " 3675 + "propertyId: " 3676 + VehiclePropertyIds.toString(propertyId) 3677 + " areaId: " 3678 + areaId 3679 + " errorCode: " 3680 + errorCode); 3681 return; 3682 } 3683 if (propertyId != mPropertyId) { 3684 Log.w( 3685 TAG, 3686 "SetterCallback - Dropping onErrorEvent. Property ID does not match" 3687 + " expected property ID: " 3688 + mPropertyName 3689 + " - propertyId: " 3690 + VehiclePropertyIds.toString(propertyId) 3691 + " areaId: " 3692 + areaId 3693 + " errorCode: " 3694 + errorCode); 3695 return; 3696 } 3697 if (areaId != mAreaId) { 3698 Log.w( 3699 TAG, 3700 "SetterCallback - Dropping onErrorEvent. Area ID does not match expected" 3701 + " area ID: " 3702 + mAreaId 3703 + " - propertyId: " 3704 + mPropertyName 3705 + " areaId: " 3706 + areaId 3707 + " errorCode: " 3708 + errorCode); 3709 return; 3710 } 3711 if (!VALID_SET_ERROR_CODES.contains(errorCode)) { 3712 Log.w( 3713 TAG, 3714 "SetterCallback - Dropping onErrorEvent. errorCode is not a valid error" 3715 + " code: " 3716 + VALID_SET_ERROR_CODES 3717 + " - propertyId: " 3718 + mPropertyName 3719 + " areaId: " 3720 + areaId 3721 + " errorCode: " 3722 + errorCode); 3723 return; 3724 } 3725 if (mSetErrorCode != null) { 3726 Log.w( 3727 TAG, 3728 "SetterCallback - Dropping onErrorEvent. Already received a valid" 3729 + " errorCode: " 3730 + mSetErrorCode 3731 + " - propertyId: " 3732 + mPropertyName 3733 + " areaId: " 3734 + areaId 3735 + " errorCode: " 3736 + errorCode); 3737 return; 3738 } 3739 Log.w( 3740 TAG, 3741 "SetterCallback - Received setProperty error code: " 3742 + errorCode 3743 + " - propertyId: " 3744 + mPropertyName 3745 + " - areaId: " 3746 + areaId); 3747 mSetErrorCode = Integer.valueOf(errorCode); 3748 mCountDownLatch.countDown(); 3749 } 3750 } 3751 valueEquals(V v1, V v2)3752 private static <V> boolean valueEquals(V v1, V v2) { 3753 return (v1 instanceof Float && floatEquals((Float) v1, (Float) v2)) 3754 || (v1 instanceof Float[] && floatArrayEquals((Float[]) v1, (Float[]) v2)) 3755 || (v1 instanceof Long[] && longArrayEquals((Long[]) v1, (Long[]) v2)) 3756 || (v1 instanceof Integer[] && integerArrayEquals((Integer[]) v1, (Integer[]) v2)) 3757 || v1.equals(v2); 3758 } 3759 floatEquals(float f1, float f2)3760 private static boolean floatEquals(float f1, float f2) { 3761 return Math.abs(f1 - f2) < FLOAT_INEQUALITY_THRESHOLD; 3762 } 3763 floatArrayEquals(Float[] f1, Float[] f2)3764 private static boolean floatArrayEquals(Float[] f1, Float[] f2) { 3765 return Arrays.equals(f1, f2); 3766 } 3767 longArrayEquals(Long[] l1, Long[] l2)3768 private static boolean longArrayEquals(Long[] l1, Long[] l2) { 3769 return Arrays.equals(l1, l2); 3770 } 3771 integerArrayEquals(Integer[] i1, Integer[] i2)3772 private static boolean integerArrayEquals(Integer[] i1, Integer[] i2) { 3773 return Arrays.equals(i1, i2); 3774 } 3775 3776 private static class TestGetPropertyCallback implements GetPropertyCallback { 3777 private final CountDownLatch mCountDownLatch; 3778 private final int mGetPropertyResultsCount; 3779 private final Object mLock = new Object(); 3780 3781 @GuardedBy("mLock") 3782 private final List<GetPropertyResult<?>> mGetPropertyResults = new ArrayList<>(); 3783 3784 @GuardedBy("mLock") 3785 private final List<PropertyAsyncError> mPropertyAsyncErrors = new ArrayList<>(); 3786 waitForResults()3787 public void waitForResults() { 3788 try { 3789 assertWithMessage( 3790 "Received " 3791 + (mGetPropertyResultsCount - mCountDownLatch.getCount()) 3792 + " onSuccess(s), expected " 3793 + mGetPropertyResultsCount 3794 + " onSuccess(s)") 3795 .that(mCountDownLatch.await(5, TimeUnit.SECONDS)) 3796 .isTrue(); 3797 } catch (InterruptedException e) { 3798 assertWithMessage("Waiting for onSuccess threw an exception: " + e).fail(); 3799 } 3800 } 3801 getGetPropertyResults()3802 public List<GetPropertyResult<?>> getGetPropertyResults() { 3803 synchronized (mLock) { 3804 return mGetPropertyResults; 3805 } 3806 } 3807 getPropertyAsyncErrors()3808 public List<PropertyAsyncError> getPropertyAsyncErrors() { 3809 synchronized (mLock) { 3810 return mPropertyAsyncErrors; 3811 } 3812 } 3813 3814 @Override onSuccess(GetPropertyResult getPropertyResult)3815 public void onSuccess(GetPropertyResult getPropertyResult) { 3816 synchronized (mLock) { 3817 mGetPropertyResults.add(getPropertyResult); 3818 mCountDownLatch.countDown(); 3819 } 3820 } 3821 3822 @Override onFailure(PropertyAsyncError propertyAsyncError)3823 public void onFailure(PropertyAsyncError propertyAsyncError) { 3824 synchronized (mLock) { 3825 mPropertyAsyncErrors.add(propertyAsyncError); 3826 mCountDownLatch.countDown(); 3827 } 3828 } 3829 TestGetPropertyCallback(int getPropertyResultsCount)3830 TestGetPropertyCallback(int getPropertyResultsCount) { 3831 mCountDownLatch = new CountDownLatch(getPropertyResultsCount); 3832 mGetPropertyResultsCount = getPropertyResultsCount; 3833 } 3834 } 3835 3836 private static class TestSetPropertyCallback implements SetPropertyCallback { 3837 private final CountDownLatch mCountDownLatch; 3838 private final int mSetPropertyResultsCount; 3839 private final Object mLock = new Object(); 3840 3841 @GuardedBy("mLock") 3842 private final List<SetPropertyResult> mSetPropertyResults = new ArrayList<>(); 3843 3844 @GuardedBy("mLock") 3845 private final List<PropertyAsyncError> mPropertyAsyncErrors = new ArrayList<>(); 3846 waitForResults()3847 public void waitForResults() { 3848 try { 3849 assertWithMessage( 3850 "Received " 3851 + (mSetPropertyResultsCount - mCountDownLatch.getCount()) 3852 + " onSuccess(s), expected " 3853 + mSetPropertyResultsCount 3854 + " onSuccess(s)") 3855 .that(mCountDownLatch.await(5, TimeUnit.SECONDS)) 3856 .isTrue(); 3857 } catch (InterruptedException e) { 3858 assertWithMessage("Waiting for onSuccess threw an exception: " + e).fail(); 3859 } 3860 } 3861 getSetPropertyResults()3862 public List<SetPropertyResult> getSetPropertyResults() { 3863 synchronized (mLock) { 3864 return mSetPropertyResults; 3865 } 3866 } 3867 getPropertyAsyncErrors()3868 public List<PropertyAsyncError> getPropertyAsyncErrors() { 3869 synchronized (mLock) { 3870 return mPropertyAsyncErrors; 3871 } 3872 } 3873 3874 @Override onSuccess(SetPropertyResult setPropertyResult)3875 public void onSuccess(SetPropertyResult setPropertyResult) { 3876 synchronized (mLock) { 3877 mSetPropertyResults.add(setPropertyResult); 3878 mCountDownLatch.countDown(); 3879 } 3880 } 3881 3882 @Override onFailure(PropertyAsyncError propertyAsyncError)3883 public void onFailure(PropertyAsyncError propertyAsyncError) { 3884 synchronized (mLock) { 3885 mPropertyAsyncErrors.add(propertyAsyncError); 3886 mCountDownLatch.countDown(); 3887 } 3888 } 3889 TestSetPropertyCallback(int setPropertyResultsCount)3890 TestSetPropertyCallback(int setPropertyResultsCount) { 3891 mCountDownLatch = new CountDownLatch(setPropertyResultsCount); 3892 mSetPropertyResultsCount = setPropertyResultsCount; 3893 } 3894 } 3895 verifyGetPropertiesAsync()3896 private void verifyGetPropertiesAsync() { 3897 if (!isAtLeastU()) { 3898 return; 3899 } 3900 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 3901 if (!AREA_ID_CONFIG_ACCESS_FLAG && !canRead(carPropertyConfig.getAccess())) { 3902 verifyGetPropertiesAsyncFails(carPropertyConfig.getAreaIds()[0]); 3903 return; 3904 } 3905 3906 List<GetPropertyRequest> getPropertyRequests = new ArrayList<>(); 3907 SparseIntArray requestIdToAreaIdMap = new SparseIntArray(); 3908 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 3909 int areaId = areaIdConfig.getAreaId(); 3910 if (!canRead(carPropertyConfig, areaId)) { 3911 verifyGetPropertiesAsyncFails(areaId); 3912 continue; 3913 } 3914 GetPropertyRequest getPropertyRequest = 3915 mCarPropertyManager.generateGetPropertyRequest(mPropertyId, areaId); 3916 int requestId = getPropertyRequest.getRequestId(); 3917 requestIdToAreaIdMap.put(requestId, areaId); 3918 getPropertyRequests.add(getPropertyRequest); 3919 } 3920 3921 TestGetPropertyCallback testGetPropertyCallback = 3922 new TestGetPropertyCallback(requestIdToAreaIdMap.size()); 3923 mCarPropertyManager.getPropertiesAsync( 3924 getPropertyRequests, /* cancellationSignal: */ 3925 null, 3926 /* callbackExecutor: */ null, 3927 testGetPropertyCallback); 3928 testGetPropertyCallback.waitForResults(); 3929 3930 for (GetPropertyResult<?> getPropertyResult : 3931 testGetPropertyCallback.getGetPropertyResults()) { 3932 int requestId = getPropertyResult.getRequestId(); 3933 int propertyId = getPropertyResult.getPropertyId(); 3934 if (requestIdToAreaIdMap.indexOfKey(requestId) < 0) { 3935 assertWithMessage( 3936 "getPropertiesAsync received GetPropertyResult with unknown" 3937 + " requestId: " 3938 + getPropertyResult) 3939 .fail(); 3940 } 3941 Integer expectedAreaId = requestIdToAreaIdMap.get(requestId); 3942 verifyCarPropertyValue( 3943 propertyId, 3944 getPropertyResult.getAreaId(), 3945 CarPropertyValue.STATUS_AVAILABLE, 3946 getPropertyResult.getTimestampNanos(), 3947 (T) getPropertyResult.getValue(), 3948 expectedAreaId, 3949 CAR_PROPERTY_VALUE_SOURCE_CALLBACK); 3950 } 3951 3952 for (PropertyAsyncError propertyAsyncError : 3953 testGetPropertyCallback.getPropertyAsyncErrors()) { 3954 int requestId = propertyAsyncError.getRequestId(); 3955 // Async errors are ok as long the requestId is valid 3956 if (requestIdToAreaIdMap.indexOfKey(requestId) < 0) { 3957 assertWithMessage( 3958 "getPropertiesAsync received PropertyAsyncError with unknown" 3959 + " requestId: " 3960 + propertyAsyncError) 3961 .fail(); 3962 } 3963 } 3964 } 3965 verifyGetPropertiesAsyncFails(int areaId)3966 private void verifyGetPropertiesAsyncFails(int areaId) { 3967 if (!isAtLeastU()) { 3968 return; 3969 } 3970 List<GetPropertyRequest> getPropertyRequests = new ArrayList<>(); 3971 GetPropertyRequest getPropertyRequest = 3972 mCarPropertyManager.generateGetPropertyRequest(mPropertyId, areaId); 3973 getPropertyRequests.add(getPropertyRequest); 3974 TestGetPropertyCallback testGetPropertyCallback = 3975 new TestGetPropertyCallback(/* getPropertyResultsCount: */ 1); 3976 assertThrows( 3977 mPropertyName 3978 + " is a write_only property so getPropertiesAsync should throw an" 3979 + " IllegalArgumentException.", 3980 IllegalArgumentException.class, 3981 () -> 3982 mCarPropertyManager.getPropertiesAsync( 3983 getPropertyRequests, 3984 /* cancellationSignal: */ null, /* callbackExecutor: */ 3985 null, 3986 testGetPropertyCallback)); 3987 } 3988 verifySetPropertiesAsync()3989 private void verifySetPropertiesAsync() { 3990 if (!isAtLeastU()) { 3991 return; 3992 } 3993 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 3994 if (!AREA_ID_CONFIG_ACCESS_FLAG && !canWrite(carPropertyConfig.getAccess())) { 3995 verifySetPropertiesAsyncFails(carPropertyConfig.getAreaIds()[0]); 3996 return; 3997 } 3998 3999 ArrayMap<Integer, List<T>> areaIdToPossibleValuesMap = new ArrayMap<>(); 4000 // The maximum possible values count for all areaIds. 4001 int maxPossibleValuesCount = 0; 4002 4003 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 4004 int areaId = areaIdConfig.getAreaId(); 4005 Collection<T> possibleValues = getPossibleValues(areaId); 4006 if (possibleValues == null || possibleValues.size() == 0) { 4007 continue; 4008 } 4009 // Convert to a list so that we can access via index later. 4010 areaIdToPossibleValuesMap.put(areaId, new ArrayList<T>(possibleValues)); 4011 if (possibleValues.size() > maxPossibleValuesCount) { 4012 maxPossibleValuesCount = possibleValues.size(); 4013 } 4014 } 4015 4016 // For each possible value index, generate one async request containing all areaIds that has 4017 // possible values defined. 4018 // For example, [value0ForArea1, value0ForArea2], [value1ForArea1, value1ForArea2]. 4019 // If we run out of possible values for one areaId, just use the last possible value for 4020 // that areaId. 4021 for (int i = 0; i < maxPossibleValuesCount; i++) { 4022 SparseIntArray requestIdToAreaIdMap = new SparseIntArray(); 4023 List<SetPropertyRequest<?>> setPropertyRequests = new ArrayList<>(); 4024 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 4025 int areaId = areaIdConfig.getAreaId(); 4026 if (!canWrite(carPropertyConfig, areaId)) { 4027 verifySetPropertiesAsyncFails(areaId); 4028 continue; 4029 } 4030 if (!areaIdToPossibleValuesMap.containsKey(areaId)) { 4031 continue; 4032 } 4033 List<T> possibleValues = areaIdToPossibleValuesMap.get(areaId); 4034 // Always use the last possible value if we run out of possible values. 4035 int index = Math.min(i, possibleValues.size() - 1); 4036 SetPropertyRequest setPropertyRequest = 4037 mCarPropertyManager.generateSetPropertyRequest( 4038 mPropertyId, areaId, possibleValues.get(index)); 4039 if (!canRead(carPropertyConfig, areaId)) { 4040 setPropertyRequest.setWaitForPropertyUpdate(false); 4041 } 4042 requestIdToAreaIdMap.put(setPropertyRequest.getRequestId(), areaId); 4043 setPropertyRequests.add(setPropertyRequest); 4044 } 4045 4046 TestSetPropertyCallback testSetPropertyCallback = 4047 new TestSetPropertyCallback(requestIdToAreaIdMap.size()); 4048 mCarPropertyManager.setPropertiesAsync( 4049 setPropertyRequests, 4050 /* cancellationSignal: */ null, /* callbackExecutor: */ 4051 null, 4052 testSetPropertyCallback); 4053 testSetPropertyCallback.waitForResults(); 4054 4055 for (SetPropertyResult setPropertyResult : 4056 testSetPropertyCallback.getSetPropertyResults()) { 4057 int requestId = setPropertyResult.getRequestId(); 4058 if (requestIdToAreaIdMap.indexOfKey(requestId) < 0) { 4059 assertWithMessage( 4060 "setPropertiesAsync received SetPropertyResult with unknown" 4061 + " requestId: " 4062 + setPropertyResult) 4063 .fail(); 4064 } 4065 assertThat(setPropertyResult.getPropertyId()).isEqualTo(mPropertyId); 4066 assertThat(setPropertyResult.getAreaId()) 4067 .isEqualTo(requestIdToAreaIdMap.get(requestId)); 4068 assertThat(setPropertyResult.getUpdateTimestampNanos()).isAtLeast(0); 4069 assertThat(setPropertyResult.getUpdateTimestampNanos()) 4070 .isLessThan(SystemClock.elapsedRealtimeNanos()); 4071 } 4072 4073 for (PropertyAsyncError propertyAsyncError : 4074 testSetPropertyCallback.getPropertyAsyncErrors()) { 4075 int requestId = propertyAsyncError.getRequestId(); 4076 // Async errors are ok as long the requestId is valid 4077 if (requestIdToAreaIdMap.indexOfKey(requestId) < 0) { 4078 assertWithMessage( 4079 "setPropertiesAsync received PropertyAsyncError with unknown " 4080 + "requestId: " 4081 + propertyAsyncError) 4082 .fail(); 4083 } 4084 } 4085 } 4086 } 4087 verifySetPropertiesAsyncFails(int areaId)4088 private void verifySetPropertiesAsyncFails(int areaId) { 4089 if (!isAtLeastU()) { 4090 return; 4091 } 4092 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 4093 List<SetPropertyRequest<?>> setPropertyRequests = new ArrayList<>(); 4094 SetPropertyRequest setPropertyRequest = 4095 mCarPropertyManager.generateSetPropertyRequest( 4096 mPropertyId, areaId, getDefaultValue(carPropertyConfig.getPropertyType())); 4097 setPropertyRequests.add(setPropertyRequest); 4098 TestSetPropertyCallback testSetPropertyCallback = 4099 new TestSetPropertyCallback(/* setPropertyResultsCount: */ 1); 4100 assertThrows( 4101 mPropertyName 4102 + " is a read_only property so setPropertiesAsync should throw an" 4103 + " IllegalArgumentException.", 4104 IllegalArgumentException.class, 4105 () -> 4106 mCarPropertyManager.setPropertiesAsync( 4107 setPropertyRequests, 4108 /* cancellationSignal: */ null, /* callbackExecutor: */ 4109 null, 4110 testSetPropertyCallback)); 4111 } 4112 verifyGetMinMaxSupportedValue()4113 private void verifyGetMinMaxSupportedValue() { 4114 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 4115 int[] areaIds = carPropertyConfig.getAreaIds(); 4116 for (int areaId : areaIds) { 4117 // Because min/max supported value is dynamic, the value we got here is not necessarily 4118 // the same as the value we got from VehicleAreaConfig. 4119 MinMaxSupportedValue<T> minMaxSupportedValue = 4120 mCarPropertyManager.getMinMaxSupportedValue(mPropertyId, areaId); 4121 AreaIdConfig areaIdConfig = carPropertyConfig.getAreaIdConfig(areaId); 4122 T areaIdMinValue = minMaxSupportedValue.getMinValue(); 4123 T areaIdMaxValue = minMaxSupportedValue.getMaxValue(); 4124 if (areaIdConfig.hasMinSupportedValue()) { 4125 assertWithMessage( 4126 mPropertyName 4127 + " - area ID: " 4128 + areaId 4129 + " minSupportedValue must not be null if" 4130 + " hasMinSupportedValue is true") 4131 .that(areaIdMinValue) 4132 .isNotNull(); 4133 } else { 4134 assertWithMessage( 4135 mPropertyName 4136 + " - area ID: " 4137 + areaId 4138 + " minSupportedValue must be null if hasMinSupportedValue" 4139 + " is false") 4140 .that(areaIdMinValue) 4141 .isNull(); 4142 } 4143 4144 if (areaIdConfig.hasMaxSupportedValue()) { 4145 assertWithMessage( 4146 mPropertyName 4147 + " - area ID: " 4148 + areaId 4149 + " maxSupportedValue must not be null if" 4150 + " hasMaxSupportedValue is true") 4151 .that(areaIdMaxValue) 4152 .isNotNull(); 4153 } else { 4154 assertWithMessage( 4155 mPropertyName 4156 + " - area ID: " 4157 + areaId 4158 + " maxSupportedValue must be null if hasMaxSupportedValue" 4159 + " is false") 4160 .that(areaIdMaxValue) 4161 .isNull(); 4162 } 4163 if (mRequireMinValuesToBeZero) { 4164 assertWithMessage( 4165 mPropertyName + " - area ID: " + areaId + " min value must be zero") 4166 .that(areaIdMinValue) 4167 .isEqualTo(0); 4168 } 4169 if (mRequireZeroToBeContainedInMinMaxRanges) { 4170 assertWithMessage( 4171 mPropertyName 4172 + " - areaId: " 4173 + areaId 4174 + "'s max and min range must contain zero") 4175 .that(verifyMaxAndMinRangeContainsZero(areaIdMinValue, areaIdMaxValue)) 4176 .isTrue(); 4177 } 4178 if (areaIdMinValue != null || areaIdMaxValue != null) { 4179 assertWithMessage( 4180 mPropertyName 4181 + " - areaId: " 4182 + areaId 4183 + "'s max value must be >= min value") 4184 .that(verifyMaxAndMin(areaIdMinValue, areaIdMaxValue)) 4185 .isTrue(); 4186 } 4187 } 4188 } 4189 verifyGetSupportedValuesList()4190 private void verifyGetSupportedValuesList() { 4191 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 4192 int[] areaIds = carPropertyConfig.getAreaIds(); 4193 Class propertyType = carPropertyConfig.getPropertyType(); 4194 for (int areaId : areaIds) { 4195 List<T> supportedValuesList = 4196 mCarPropertyManager.getSupportedValuesList(mPropertyId, areaId); 4197 AreaIdConfig areaIdConfig = carPropertyConfig.getAreaIdConfig(areaId); 4198 if (areaIdConfig.hasSupportedValuesList()) { 4199 assertWithMessage( 4200 mPropertyName 4201 + " - area ID: " 4202 + areaId 4203 + " supportedValuesList must not be null if" 4204 + " hasSupportedValuesList is true") 4205 .that(supportedValuesList) 4206 .isNotNull(); 4207 } else { 4208 assertWithMessage( 4209 mPropertyName 4210 + " - area ID: " 4211 + areaId 4212 + " supportedValuesList must be null if" 4213 + " hasSupportedValuesList is false") 4214 .that(supportedValuesList) 4215 .isNull(); 4216 continue; 4217 } 4218 4219 assertWithMessage( 4220 mPropertyName 4221 + " - area ID: " 4222 + areaId 4223 + " supportedValuesList must not " 4224 + "contain duplicate elements") 4225 .that(supportedValuesList) 4226 .containsNoDuplicates(); 4227 4228 if (propertyType.equals(Integer.class) 4229 || propertyType.equals(Float.class) 4230 || propertyType.equals(Long.class)) { 4231 assertWithMessage( 4232 mPropertyName 4233 + " - area ID: " 4234 + areaId 4235 + " supportedValuesList must be " 4236 + "in ascending order") 4237 .that(supportedValuesList) 4238 .isInOrder(); 4239 } 4240 4241 if (!mAllPossibleEnumValues.isEmpty()) { 4242 for (int i = 0; i < supportedValuesList.size(); i++) { 4243 T supportedValue = supportedValuesList.get(i); 4244 assertWithMessage( 4245 mPropertyName 4246 + " - area ID: " 4247 + areaId 4248 + " supported value: " 4249 + supportedValue 4250 + " is not one of the possible enums") 4251 .that(mAllPossibleEnumValues) 4252 .contains(supportedValue); 4253 } 4254 } 4255 } 4256 } 4257 verifyRegisterUnregisterSupportedValuesChangeCallback()4258 private void verifyRegisterUnregisterSupportedValuesChangeCallback() { 4259 // Do nothing for the callback. 4260 SupportedValuesChangeCallback callback = (propertyId, areaId) -> {}; 4261 // This is an executor that runs inside the test thread. 4262 Executor executor = (r) -> r.run(); 4263 4264 // We are not expecting any supported values change to happen, so here we just call the 4265 // the API and verify it succeed. 4266 assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(mPropertyId, callback)) 4267 .isTrue(); 4268 4269 mCarPropertyManager.unregisterSupportedValuesChangeCallback(mPropertyId); 4270 4271 assertThat( 4272 mCarPropertyManager.registerSupportedValuesChangeCallback( 4273 mPropertyId, executor, callback)) 4274 .isTrue(); 4275 4276 mCarPropertyManager.unregisterSupportedValuesChangeCallback(mPropertyId, callback); 4277 4278 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 4279 int[] areaIds = carPropertyConfig.getAreaIds(); 4280 for (int areaId : areaIds) { 4281 assertThat( 4282 mCarPropertyManager.registerSupportedValuesChangeCallback( 4283 mPropertyId, areaId, callback)) 4284 .isTrue(); 4285 4286 mCarPropertyManager.unregisterSupportedValuesChangeCallback( 4287 mPropertyId, areaId, callback); 4288 4289 assertThat( 4290 mCarPropertyManager.registerSupportedValuesChangeCallback( 4291 mPropertyId, areaId, executor, callback)) 4292 .isTrue(); 4293 4294 mCarPropertyManager.unregisterSupportedValuesChangeCallback( 4295 mPropertyId, areaId, callback); 4296 } 4297 } 4298 verifyIsPropertyAvailable()4299 private void verifyIsPropertyAvailable() { 4300 runWithShellPermissionIdentity( 4301 () -> verifyIsPropertyAvailableWithReadPermission(), 4302 CHECK_MODE_ASSUME, 4303 mReadPermissions.toArray(new String[0])); 4304 } 4305 verifyIsPropertyAvailableWithReadPermission()4306 private void verifyIsPropertyAvailableWithReadPermission() { 4307 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 4308 int[] areaIds; 4309 if (carPropertyConfig == null) { 4310 areaIds = new int[] {0}; 4311 } else { 4312 areaIds = carPropertyConfig.getAreaIds(); 4313 } 4314 for (int areaId : areaIds) { 4315 // Before Android B, isPropertyAvailable might throw 4316 // IllegalArgumentException if the property is not supported or the property is 4317 // not readable. 4318 if (!isAtLeastB()) { 4319 boolean noReadAccess = false; 4320 String reason = ""; 4321 if (carPropertyConfig != null) { 4322 int access = getAreaIdAccessOrElseGlobalAccess(carPropertyConfig, areaId); 4323 if (access == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE 4324 || access == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_NONE) { 4325 noReadAccess = true; 4326 reason = "the property is not readable"; 4327 } 4328 } else { 4329 reason = "the property is not supported"; 4330 } 4331 4332 if (carPropertyConfig == null || noReadAccess) { 4333 String errorMsg = 4334 "carPropertyManager.isPropertyAvailable must return " 4335 + "false or throw IllegalArgumentException if " 4336 + reason 4337 + ", propertyId: " 4338 + mPropertyName 4339 + ", areaId: " 4340 + areaId; 4341 try { 4342 boolean result = 4343 mCarPropertyManager.isPropertyAvailable(mPropertyId, areaId); 4344 assertWithMessage(errorMsg).that(result).isFalse(); 4345 } catch (Exception e) { 4346 assertWithMessage(errorMsg) 4347 .that(e.getClass()) 4348 .isEqualTo(IllegalArgumentException.class); 4349 } 4350 return; 4351 } 4352 } 4353 4354 boolean result = false; 4355 try { 4356 result = mCarPropertyManager.isPropertyAvailable(mPropertyId, areaId); 4357 } catch (Exception e) { 4358 assertWithMessage( 4359 "carPropertyManager.isPropertyAvailable must not throw any" 4360 + " exception, propertyId: " 4361 + mPropertyName 4362 + ", areaId: " 4363 + areaId 4364 + ", exception: " 4365 + e) 4366 .fail(); 4367 } 4368 if (carPropertyConfig == null) { 4369 assertWithMessage( 4370 "carPropertyManager.isPropertyAvailable must return false " 4371 + "if the property is not supported, propertyId: " 4372 + mPropertyName 4373 + ", areaId: " 4374 + areaId) 4375 .that(result) 4376 .isFalse(); 4377 } 4378 } 4379 } 4380 setPropertyAndWaitForChange( CarPropertyManager carPropertyManager, int propertyId, Class<U> propertyType, int areaId, U valueToSet)4381 private static <U> CarPropertyValue<U> setPropertyAndWaitForChange( 4382 CarPropertyManager carPropertyManager, 4383 int propertyId, 4384 Class<U> propertyType, 4385 int areaId, 4386 U valueToSet) { 4387 return setPropertyAndWaitForChange( 4388 carPropertyManager, propertyId, propertyType, areaId, valueToSet, valueToSet); 4389 } 4390 setPropertyAndWaitForChange( CarPropertyManager carPropertyManager, int propertyId, Class<U> propertyType, int areaId, U valueToSet, U expectedValueToGet)4391 private static <U> CarPropertyValue<U> setPropertyAndWaitForChange( 4392 CarPropertyManager carPropertyManager, 4393 int propertyId, 4394 Class<U> propertyType, 4395 int areaId, 4396 U valueToSet, 4397 U expectedValueToGet) { 4398 spaceOutCarPropertyManagerActions(); 4399 SetterCallback setterCallback = new SetterCallback(propertyId, areaId, expectedValueToGet); 4400 assertWithMessage( 4401 "Failed to register setter callback for " 4402 + VehiclePropertyIds.toString(propertyId)) 4403 .that( 4404 subscribePropertyEvents( 4405 carPropertyManager, 4406 setterCallback, 4407 propertyId, 4408 CarPropertyManager.SENSOR_RATE_FASTEST)) 4409 .isTrue(); 4410 try { 4411 carPropertyManager.setProperty(propertyType, propertyId, areaId, valueToSet); 4412 } catch (PropertyNotAvailableAndRetryException e) { 4413 return null; 4414 } catch (PropertyNotAvailableException e) { 4415 verifyPropertyNotAvailableException(e); 4416 sExceptionClassOnSet = e.getClass(); 4417 return null; 4418 } catch (CarInternalErrorException e) { 4419 verifyInternalErrorException(e); 4420 sExceptionClassOnSet = e.getClass(); 4421 return null; 4422 } 4423 4424 CarPropertyValue<U> carPropertyValue = 4425 setterCallback.waitForPropertyEvent(SET_PROPERTY_CALLBACK_TIMEOUT_SEC); 4426 unsubscribePropertyEvents(carPropertyManager, setterCallback, propertyId); 4427 return carPropertyValue; 4428 } 4429 spaceOutCarPropertyManagerActions()4430 private static void spaceOutCarPropertyManagerActions() { 4431 synchronized (sLock) { 4432 do { 4433 long currentElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos(); 4434 long remainingDelayMs = 4435 CPM_ACTION_DELAY_MS 4436 - Duration.ofNanos( 4437 currentElapsedRealtimeNanos 4438 - sLastActionElapsedRealtimeNanos) 4439 .toMillis(); 4440 if (remainingDelayMs <= 0) { 4441 sLastActionElapsedRealtimeNanos = currentElapsedRealtimeNanos; 4442 break; 4443 } 4444 SystemClock.sleep(remainingDelayMs); 4445 } while (true); 4446 } 4447 } 4448 } 4449