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.runWithShellPermissionIdentity; 20 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 21 22 import static com.google.common.truth.Truth.assertThat; 23 import static com.google.common.truth.Truth.assertWithMessage; 24 25 import static org.junit.Assert.assertThrows; 26 import static org.junit.Assume.assumeThat; 27 28 import android.car.VehicleAreaDoor; 29 import android.car.VehicleAreaMirror; 30 import android.car.VehicleAreaSeat; 31 import android.car.VehicleAreaType; 32 import android.car.VehicleAreaWheel; 33 import android.car.VehicleAreaWindow; 34 import android.car.VehiclePropertyIds; 35 import android.car.VehiclePropertyType; 36 import android.car.feature.Flags; 37 import android.car.hardware.CarHvacFanDirection; 38 import android.car.hardware.CarPropertyConfig; 39 import android.car.hardware.CarPropertyValue; 40 import android.car.hardware.property.AreaIdConfig; 41 import android.car.hardware.property.CarInternalErrorException; 42 import android.car.hardware.property.CarPropertyManager; 43 import android.car.hardware.property.CarPropertyManager.GetPropertyCallback; 44 import android.car.hardware.property.CarPropertyManager.GetPropertyRequest; 45 import android.car.hardware.property.CarPropertyManager.GetPropertyResult; 46 import android.car.hardware.property.CarPropertyManager.PropertyAsyncError; 47 import android.car.hardware.property.CarPropertyManager.SetPropertyCallback; 48 import android.car.hardware.property.CarPropertyManager.SetPropertyRequest; 49 import android.car.hardware.property.CarPropertyManager.SetPropertyResult; 50 import android.car.hardware.property.ErrorState; 51 import android.car.hardware.property.PropertyNotAvailableAndRetryException; 52 import android.car.hardware.property.PropertyNotAvailableErrorCode; 53 import android.car.hardware.property.PropertyNotAvailableException; 54 import android.content.Context; 55 import android.os.Build; 56 import android.os.SystemClock; 57 import android.util.Log; 58 import android.util.SparseArray; 59 import android.util.SparseIntArray; 60 61 import androidx.annotation.Nullable; 62 import androidx.test.platform.app.InstrumentationRegistry; 63 64 import com.android.internal.annotations.GuardedBy; 65 66 import com.google.common.collect.ImmutableList; 67 import com.google.common.collect.ImmutableSet; 68 import com.google.common.collect.Sets; 69 70 import org.hamcrest.Matchers; 71 72 import java.util.ArrayList; 73 import java.util.Arrays; 74 import java.util.Collection; 75 import java.util.Collections; 76 import java.util.List; 77 import java.util.Optional; 78 import java.util.concurrent.CountDownLatch; 79 import java.util.concurrent.TimeUnit; 80 import java.util.concurrent.atomic.AtomicBoolean; 81 import java.util.stream.Collectors; 82 import java.util.stream.IntStream; 83 84 /** 85 * A class for verifying the implementation for one VHAL property. 86 * 87 * @param T The type for the property. 88 */ 89 public class VehiclePropertyVerifier<T> { 90 private static final String TAG = VehiclePropertyVerifier.class.getSimpleName(); 91 private static final String CAR_PROPERTY_VALUE_SOURCE_GETTER = "Getter"; 92 private static final String CAR_PROPERTY_VALUE_SOURCE_CALLBACK = "Callback"; 93 private static final int GLOBAL_AREA_ID = 0; 94 private static final float FLOAT_INEQUALITY_THRESHOLD = 0.00001f; 95 private static final int VENDOR_ERROR_CODE_MINIMUM_VALUE = 0x0; 96 private static final int VENDOR_ERROR_CODE_MAXIMUM_VALUE = 0xffff; 97 private static final ImmutableSet<Integer> WHEEL_AREAS = ImmutableSet.of( 98 VehicleAreaWheel.WHEEL_LEFT_FRONT, VehicleAreaWheel.WHEEL_LEFT_REAR, 99 VehicleAreaWheel.WHEEL_RIGHT_FRONT, VehicleAreaWheel.WHEEL_RIGHT_REAR); 100 private static final ImmutableSet<Integer> ALL_POSSIBLE_WHEEL_AREA_IDS = 101 generateAllPossibleAreaIds(WHEEL_AREAS); 102 private static final ImmutableSet<Integer> WINDOW_AREAS = ImmutableSet.of( 103 VehicleAreaWindow.WINDOW_FRONT_WINDSHIELD, VehicleAreaWindow.WINDOW_REAR_WINDSHIELD, 104 VehicleAreaWindow.WINDOW_ROW_1_LEFT, VehicleAreaWindow.WINDOW_ROW_1_RIGHT, 105 VehicleAreaWindow.WINDOW_ROW_2_LEFT, VehicleAreaWindow.WINDOW_ROW_2_RIGHT, 106 VehicleAreaWindow.WINDOW_ROW_3_LEFT, VehicleAreaWindow.WINDOW_ROW_3_RIGHT, 107 VehicleAreaWindow.WINDOW_ROOF_TOP_1, VehicleAreaWindow.WINDOW_ROOF_TOP_2); 108 private static final ImmutableSet<Integer> ALL_POSSIBLE_WINDOW_AREA_IDS = 109 generateAllPossibleAreaIds(WINDOW_AREAS); 110 private static final ImmutableSet<Integer> MIRROR_AREAS = ImmutableSet.of( 111 VehicleAreaMirror.MIRROR_DRIVER_LEFT, VehicleAreaMirror.MIRROR_DRIVER_RIGHT, 112 VehicleAreaMirror.MIRROR_DRIVER_CENTER); 113 private static final ImmutableSet<Integer> ALL_POSSIBLE_MIRROR_AREA_IDS = 114 generateAllPossibleAreaIds(MIRROR_AREAS); 115 private static final ImmutableSet<Integer> SEAT_AREAS = ImmutableSet.of( 116 VehicleAreaSeat.SEAT_ROW_1_LEFT, VehicleAreaSeat.SEAT_ROW_1_CENTER, 117 VehicleAreaSeat.SEAT_ROW_1_RIGHT, VehicleAreaSeat.SEAT_ROW_2_LEFT, 118 VehicleAreaSeat.SEAT_ROW_2_CENTER, VehicleAreaSeat.SEAT_ROW_2_RIGHT, 119 VehicleAreaSeat.SEAT_ROW_3_LEFT, VehicleAreaSeat.SEAT_ROW_3_CENTER, 120 VehicleAreaSeat.SEAT_ROW_3_RIGHT); 121 private static final ImmutableSet<Integer> ALL_POSSIBLE_SEAT_AREA_IDS = 122 generateAllPossibleAreaIds(SEAT_AREAS); 123 private static final ImmutableSet<Integer> DOOR_AREAS = ImmutableSet.of( 124 VehicleAreaDoor.DOOR_ROW_1_LEFT, VehicleAreaDoor.DOOR_ROW_1_RIGHT, 125 VehicleAreaDoor.DOOR_ROW_2_LEFT, VehicleAreaDoor.DOOR_ROW_2_RIGHT, 126 VehicleAreaDoor.DOOR_ROW_3_LEFT, VehicleAreaDoor.DOOR_ROW_3_RIGHT, 127 VehicleAreaDoor.DOOR_HOOD, VehicleAreaDoor.DOOR_REAR); 128 private static final ImmutableSet<Integer> ALL_POSSIBLE_DOOR_AREA_IDS = 129 generateAllPossibleAreaIds(DOOR_AREAS); 130 private static final ImmutableSet<Integer> PROPERTY_NOT_AVAILABLE_ERROR_CODES = 131 ImmutableSet.of( 132 PropertyNotAvailableErrorCode.NOT_AVAILABLE, 133 PropertyNotAvailableErrorCode.NOT_AVAILABLE_DISABLED, 134 PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_LOW, 135 PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_HIGH, 136 PropertyNotAvailableErrorCode.NOT_AVAILABLE_POOR_VISIBILITY, 137 PropertyNotAvailableErrorCode.NOT_AVAILABLE_SAFETY); 138 139 private static Class<?> sExceptionClassOnGet; 140 private static Class<?> sExceptionClassOnSet; 141 142 private final Context mContext = 143 InstrumentationRegistry.getInstrumentation().getTargetContext(); 144 private final CarPropertyManager mCarPropertyManager; 145 private final int mPropertyId; 146 private final String mPropertyName; 147 private final int mAccess; 148 private final int mAreaType; 149 private final int mChangeMode; 150 private final Class<T> mPropertyType; 151 private final boolean mRequiredProperty; 152 private final Optional<ConfigArrayVerifier> mConfigArrayVerifier; 153 private final Optional<CarPropertyValueVerifier<T>> mCarPropertyValueVerifier; 154 private final Optional<AreaIdsVerifier> mAreaIdsVerifier; 155 private final Optional<CarPropertyConfigVerifier> mCarPropertyConfigVerifier; 156 private final Optional<Integer> mDependentOnPropertyId; 157 private final ImmutableSet<String> mDependentOnPropertyPermissions; 158 private final ImmutableSet<Integer> mPossibleConfigArrayValues; 159 private final boolean mEnumIsBitMap; 160 private final ImmutableSet<T> mAllPossibleEnumValues; 161 private final ImmutableSet<T> mAllPossibleUnwritableValues; 162 private final ImmutableSet<T> mAllPossibleUnavailableValues; 163 private final boolean mRequirePropertyValueToBeInConfigArray; 164 private final boolean mVerifySetterWithConfigArrayValues; 165 private final boolean mRequireMinMaxValues; 166 private final boolean mRequireMinValuesToBeZero; 167 private final boolean mRequireZeroToBeContainedInMinMaxRanges; 168 private final boolean mPossiblyDependentOnHvacPowerOn; 169 private final boolean mVerifyErrorStates; 170 private final ImmutableSet<String> mReadPermissions; 171 private final ImmutableList<ImmutableSet<String>> mWritePermissions; 172 173 private boolean mIsCarPropertyConfigCached; 174 private CarPropertyConfig<T> mCachedCarPropertyConfig; 175 private SparseArray<SparseArray<?>> mPropertyToAreaIdValues; 176 VehiclePropertyVerifier( CarPropertyManager carPropertyManager, int propertyId, int access, 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, ImmutableSet<String> readPermissions, ImmutableList<ImmutableSet<String>> writePermissions)177 private VehiclePropertyVerifier( 178 CarPropertyManager carPropertyManager, 179 int propertyId, 180 int access, 181 int areaType, 182 int changeMode, 183 Class<T> propertyType, 184 boolean requiredProperty, 185 Optional<ConfigArrayVerifier> configArrayVerifier, 186 Optional<CarPropertyValueVerifier<T>> carPropertyValueVerifier, 187 Optional<AreaIdsVerifier> areaIdsVerifier, 188 Optional<CarPropertyConfigVerifier> carPropertyConfigVerifier, 189 Optional<Integer> dependentPropertyId, 190 ImmutableSet<String> dependentOnPropertyPermissions, 191 ImmutableSet<Integer> possibleConfigArrayValues, 192 boolean enumIsBitMap, 193 ImmutableSet<T> allPossibleEnumValues, 194 ImmutableSet<T> allPossibleUnwritableValues, 195 ImmutableSet<T> allPossibleUnavailableValues, 196 boolean requirePropertyValueToBeInConfigArray, 197 boolean verifySetterWithConfigArrayValues, 198 boolean requireMinMaxValues, 199 boolean requireMinValuesToBeZero, 200 boolean requireZeroToBeContainedInMinMaxRanges, 201 boolean possiblyDependentOnHvacPowerOn, 202 boolean verifyErrorStates, 203 ImmutableSet<String> readPermissions, 204 ImmutableList<ImmutableSet<String>> writePermissions) { 205 assertWithMessage("Must set car property manager").that(carPropertyManager).isNotNull(); 206 mCarPropertyManager = carPropertyManager; 207 mPropertyId = propertyId; 208 mPropertyName = VehiclePropertyIds.toString(propertyId); 209 mAccess = access; 210 mAreaType = areaType; 211 mChangeMode = changeMode; 212 mPropertyType = propertyType; 213 mRequiredProperty = requiredProperty; 214 mConfigArrayVerifier = configArrayVerifier; 215 mCarPropertyValueVerifier = carPropertyValueVerifier; 216 mAreaIdsVerifier = areaIdsVerifier; 217 mCarPropertyConfigVerifier = carPropertyConfigVerifier; 218 mDependentOnPropertyId = dependentPropertyId; 219 mDependentOnPropertyPermissions = dependentOnPropertyPermissions; 220 mPossibleConfigArrayValues = possibleConfigArrayValues; 221 mEnumIsBitMap = enumIsBitMap; 222 mAllPossibleEnumValues = allPossibleEnumValues; 223 mAllPossibleUnwritableValues = allPossibleUnwritableValues; 224 mAllPossibleUnavailableValues = allPossibleUnavailableValues; 225 mRequirePropertyValueToBeInConfigArray = requirePropertyValueToBeInConfigArray; 226 mVerifySetterWithConfigArrayValues = verifySetterWithConfigArrayValues; 227 mRequireMinMaxValues = requireMinMaxValues; 228 mRequireMinValuesToBeZero = requireMinValuesToBeZero; 229 mRequireZeroToBeContainedInMinMaxRanges = requireZeroToBeContainedInMinMaxRanges; 230 mPossiblyDependentOnHvacPowerOn = possiblyDependentOnHvacPowerOn; 231 mVerifyErrorStates = verifyErrorStates; 232 mReadPermissions = readPermissions; 233 mWritePermissions = writePermissions; 234 mPropertyToAreaIdValues = new SparseArray<>(); 235 } 236 237 /** 238 * Gets a new builder for the verifier. 239 */ newBuilder( int propertyId, int access, int areaType, int changeMode, Class<T> propertyType, CarPropertyManager carPropertyManager)240 public static <T> Builder<T> newBuilder( 241 int propertyId, int access, int areaType, int changeMode, Class<T> propertyType, 242 CarPropertyManager carPropertyManager) { 243 return new Builder<>(propertyId, access, areaType, changeMode, propertyType, 244 carPropertyManager); 245 } 246 247 /** 248 * Gets the ID of the property. 249 */ getPropertyId()250 public int getPropertyId() { 251 return mPropertyId; 252 } 253 254 /** 255 * Gets the name for the property. 256 */ getPropertyName()257 public String getPropertyName() { 258 return mPropertyName; 259 } 260 261 /** 262 * Gets the default value based on the type. 263 */ 264 @Nullable getDefaultValue(Class<?> clazz)265 public static <U> U getDefaultValue(Class<?> clazz) { 266 if (clazz == Boolean.class) { 267 return (U) Boolean.TRUE; 268 } 269 if (clazz == Integer.class) { 270 return (U) (Integer) 2; 271 } 272 if (clazz == Float.class) { 273 return (U) (Float) 2.f; 274 } 275 if (clazz == Long.class) { 276 return (U) (Long) 2L; 277 } 278 if (clazz == Integer[].class) { 279 return (U) new Integer[]{2}; 280 } 281 if (clazz == Float[].class) { 282 return (U) new Float[]{2.f}; 283 } 284 if (clazz == Long[].class) { 285 return (U) new Long[]{2L}; 286 } 287 if (clazz == String.class) { 288 return (U) new String("test"); 289 } 290 if (clazz == byte[].class) { 291 return (U) new byte[]{(byte) 0xbe, (byte) 0xef}; 292 } 293 return null; 294 } 295 accessToString(int access)296 private static String accessToString(int access) { 297 switch (access) { 298 case CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_NONE: 299 return "VEHICLE_PROPERTY_ACCESS_NONE"; 300 case CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ: 301 return "VEHICLE_PROPERTY_ACCESS_READ"; 302 case CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE: 303 return "VEHICLE_PROPERTY_ACCESS_WRITE"; 304 case CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE: 305 return "VEHICLE_PROPERTY_ACCESS_READ_WRITE"; 306 default: 307 return Integer.toString(access); 308 } 309 } 310 areaTypeToString(int areaType)311 private static String areaTypeToString(int areaType) { 312 switch (areaType) { 313 case VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL: 314 return "VEHICLE_AREA_TYPE_GLOBAL"; 315 case VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW: 316 return "VEHICLE_AREA_TYPE_WINDOW"; 317 case VehicleAreaType.VEHICLE_AREA_TYPE_DOOR: 318 return "VEHICLE_AREA_TYPE_DOOR"; 319 case VehicleAreaType.VEHICLE_AREA_TYPE_MIRROR: 320 return "VEHICLE_AREA_TYPE_MIRROR"; 321 case VehicleAreaType.VEHICLE_AREA_TYPE_SEAT: 322 return "VEHICLE_AREA_TYPE_SEAT"; 323 case VehicleAreaType.VEHICLE_AREA_TYPE_WHEEL: 324 return "VEHICLE_AREA_TYPE_WHEEL"; 325 case VehicleAreaType.VEHICLE_AREA_TYPE_VENDOR: 326 return "VEHICLE_AREA_TYPE_VENDOR"; 327 default: 328 return Integer.toString(areaType); 329 } 330 } 331 changeModeToString(int changeMode)332 private static String changeModeToString(int changeMode) { 333 switch (changeMode) { 334 case CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_STATIC: 335 return "VEHICLE_PROPERTY_CHANGE_MODE_STATIC"; 336 case CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE: 337 return "VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE"; 338 case CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS: 339 return "VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS"; 340 default: 341 return Integer.toString(changeMode); 342 } 343 } 344 345 /** 346 * Gets the car property config for the current property or reads from cache if already cached. 347 */ getCarPropertyConfig()348 public @Nullable CarPropertyConfig<T> getCarPropertyConfig() { 349 if (!mIsCarPropertyConfigCached) { 350 mCachedCarPropertyConfig = (CarPropertyConfig<T>) mCarPropertyManager 351 .getCarPropertyConfig(mPropertyId); 352 mIsCarPropertyConfigCached = true; 353 } 354 return mCachedCarPropertyConfig; 355 } 356 357 /** 358 * Returns whether the property is supported. 359 */ isSupported()360 public boolean isSupported() { 361 return getCarPropertyConfig() != null; 362 } 363 364 /** 365 * Runs various verifications on the property. 366 */ verify()367 public void verify() { 368 verify(null); 369 } 370 371 /** 372 * Runs various verifications on the property with exceptions expected. 373 * 374 * @param exceptedExceptionClass The exception class expected for reading/writing the property. 375 */ verify(@ullable Class<?> exceptedExceptionClass)376 public void verify(@Nullable Class<?> exceptedExceptionClass) { 377 verifyConfig(); 378 verifyPermissionNotGrantedException(); 379 verifyReadPermissions(exceptedExceptionClass); 380 verifyWritePermissions(exceptedExceptionClass); 381 } 382 assertGetPropertyNotSupported(String msg)383 private void assertGetPropertyNotSupported(String msg) { 384 if (isAtLeastU()) { 385 assertThrows(msg, IllegalArgumentException.class, 386 () -> mCarPropertyManager.getProperty(mPropertyId, /*areaId=*/ 0)); 387 } else { 388 assertThat(mCarPropertyManager.getProperty(mPropertyId, /* areaId= */ 0)).isNull(); 389 } 390 } 391 verifyConfig()392 private void verifyConfig() { 393 ImmutableSet.Builder<String> permissionsBuilder = ImmutableSet.<String>builder(); 394 for (ImmutableSet<String> writePermissions: mWritePermissions) { 395 permissionsBuilder.addAll(writePermissions); 396 } 397 ImmutableSet<String> allPermissions = permissionsBuilder.addAll(mReadPermissions).build(); 398 399 runWithShellPermissionIdentity( 400 () -> { 401 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 402 if (carPropertyConfig == null) { 403 if (mAccess == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ || mAccess 404 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) { 405 assertGetPropertyNotSupported( 406 "Test does not have correct permissions granted for " 407 + mPropertyName + ". Requested permissions: " + allPermissions); 408 } else if (mAccess == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) { 409 assertThrows("Test does not have correct permissions granted for " 410 + mPropertyName + ". Requested permissions: " 411 + allPermissions, 412 IllegalArgumentException.class, 413 () -> mCarPropertyManager.setProperty(mPropertyType, 414 mPropertyId, /*areaId=*/ 415 0, getDefaultValue(mPropertyType))); 416 } 417 } 418 419 if (mRequiredProperty) { 420 assertWithMessage("Must support " + mPropertyName).that(isSupported()) 421 .isTrue(); 422 } else { 423 assumeThat("Skipping " + mPropertyName 424 + " CTS test because the property is not supported on " 425 + "this vehicle", 426 carPropertyConfig, Matchers.notNullValue()); 427 } 428 429 verifyCarPropertyConfig(); 430 }, allPermissions.toArray(new String[0])); 431 } 432 verifyReadPermissions(Class<?> exceptedExceptionClass)433 private void verifyReadPermissions(Class<?> exceptedExceptionClass) { 434 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 435 for (String readPermission: mReadPermissions) { 436 if (Flags.areaIdConfigAccess()) { 437 for (int areaId : carPropertyConfig.getAreaIds()) { 438 if (carPropertyConfig.getAreaIdConfig(areaId).getAccess() 439 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) { 440 verifyReadPermissionCannotWrite(readPermission, mWritePermissions, areaId); 441 } 442 } 443 } else if (carPropertyConfig.getAccess() 444 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) { 445 verifyReadPermissionCannotWrite(readPermission, mWritePermissions, 446 carPropertyConfig.getAreaIds()[0]); 447 } 448 verifyReadPermissionGivesAccessToReadApis(readPermission, exceptedExceptionClass); 449 } 450 } 451 verifyWritePermissions(Class<?> exceptedExceptionClass)452 private void verifyWritePermissions(Class<?> exceptedExceptionClass) { 453 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 454 for (ImmutableSet<String> writePermissions: mWritePermissions) { 455 if (Flags.areaIdConfigAccess()) { 456 for (int areaId : carPropertyConfig.getAreaIds()) { 457 int access = carPropertyConfig.getAreaIdConfig(areaId).getAccess(); 458 if (access != CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) { 459 verifyWritePermissionsCannotRead(writePermissions, mReadPermissions, 460 areaId); 461 } 462 if (access != CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ 463 && writePermissions.size() > 1) { 464 verifyIndividualWritePermissionsCannotWrite(writePermissions, areaId); 465 } 466 } 467 } else { 468 int areaId = carPropertyConfig.getAreaIds()[0]; 469 if (carPropertyConfig.getAccess() 470 != CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) { 471 verifyWritePermissionsCannotRead(writePermissions, mReadPermissions, areaId); 472 } 473 if (carPropertyConfig.getAccess() 474 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) { 475 return; 476 } 477 if (writePermissions.size() > 1) { 478 verifyIndividualWritePermissionsCannotWrite(writePermissions, areaId); 479 } 480 } 481 verifyWritePermissionsGiveAccessToWriteApis(writePermissions, mReadPermissions, 482 exceptedExceptionClass); 483 } 484 } 485 hasWritePermissions(ImmutableList<ImmutableSet<String>> writePermissions)486 private boolean hasWritePermissions(ImmutableList<ImmutableSet<String>> writePermissions) { 487 for (ImmutableSet<String> writePermissionSet: writePermissions) { 488 boolean result = true; 489 for (String permission : writePermissionSet) { 490 if (mContext.checkSelfPermission(permission) != PERMISSION_GRANTED) { 491 result = false; 492 break; 493 } 494 } 495 if (result) { 496 return true; 497 } 498 } 499 return false; 500 } 501 verifyReadPermissionCannotWrite(String readPermission, ImmutableList<ImmutableSet<String>> writePermissions, int areaId)502 private void verifyReadPermissionCannotWrite(String readPermission, 503 ImmutableList<ImmutableSet<String>> writePermissions, int areaId) { 504 // If the read permission is the same as the write permission and the property does not 505 // require any other write permissions we skip this permission. 506 for (ImmutableSet<String> writePermissionSet: writePermissions) { 507 if (writePermissionSet.size() == 1 && writePermissionSet.contains(readPermission)) { 508 return; 509 } 510 } 511 // It is possible that the caller has the write permissions without adopting the shell 512 // identity. In this case, we cannot revoke the write permission so we cannot test 513 // setProperty without write permissions. 514 if (hasWritePermissions(writePermissions)) { 515 return; 516 } 517 runWithShellPermissionIdentity( 518 () -> { 519 assertThrows( 520 mPropertyName 521 + " - property ID: " 522 + mPropertyId 523 + " should not be able to be written to without write" 524 + " permissions.", 525 SecurityException.class, 526 () -> mCarPropertyManager.setProperty(mPropertyType, mPropertyId, 527 areaId, getDefaultValue(mPropertyType))); 528 }, readPermission); 529 } 530 verifyReadPermissionGivesAccessToReadApis(String readPermission, Class<?> exceptedExceptionClass)531 private void verifyReadPermissionGivesAccessToReadApis(String readPermission, 532 Class<?> exceptedExceptionClass) { 533 try { 534 enableAdasFeatureIfAdasStateProperty(); 535 runWithShellPermissionIdentity(() -> { 536 assertThat(mCarPropertyManager.getCarPropertyConfig(mPropertyId)).isNotNull(); 537 turnOnHvacPowerIfHvacPowerDependent(); 538 verifyCarPropertyValueGetter(); 539 if (exceptedExceptionClass != null) { 540 assertWithMessage("Expected " + sExceptionClassOnGet + " to be of type " 541 + exceptedExceptionClass).that(sExceptionClassOnGet) 542 .isEqualTo(exceptedExceptionClass); 543 } else { 544 verifyCarPropertyValueCallback(); 545 verifyGetPropertiesAsync(); 546 } 547 }, readPermission); 548 549 disableAdasFeatureIfAdasStatePropertyAndVerify(ImmutableSet.<String>builder() 550 .add(readPermission) 551 .addAll(mDependentOnPropertyPermissions) 552 .build().toArray(new String[0])); 553 } finally { 554 // Restore all property values even if test fails. 555 runWithShellPermissionIdentity(() -> { 556 restoreInitialValues(); 557 }, ImmutableSet.<String>builder() 558 .add(readPermission) 559 .addAll(mDependentOnPropertyPermissions) 560 .build().toArray(new String[0])); 561 } 562 } 563 hasReadPermissions(ImmutableSet<String> allReadPermissions)564 private boolean hasReadPermissions(ImmutableSet<String> allReadPermissions) { 565 for (String permission : allReadPermissions) { 566 if (mContext.checkSelfPermission(permission) == PERMISSION_GRANTED) { 567 return true; 568 } 569 } 570 return false; 571 } 572 assertGetPropertyThrowsException(String msg, Class<? extends Throwable> exceptionClass, int propertyId, int areaId)573 private void assertGetPropertyThrowsException(String msg, 574 Class<? extends Throwable> exceptionClass, int propertyId, int areaId) { 575 assertThrows(msg, exceptionClass, 576 () -> mCarPropertyManager.getProperty(mPropertyId, areaId)); 577 assertThrows(msg, exceptionClass, 578 () -> mCarPropertyManager.getBooleanProperty(mPropertyId, areaId)); 579 assertThrows(msg, exceptionClass, 580 () -> mCarPropertyManager.getIntProperty(mPropertyId, areaId)); 581 assertThrows(msg, exceptionClass, 582 () -> mCarPropertyManager.getFloatProperty(mPropertyId, areaId)); 583 assertThrows(msg, exceptionClass, 584 () -> mCarPropertyManager.getIntArrayProperty(mPropertyId, areaId)); 585 } 586 verifyWritePermissionsCannotRead(ImmutableSet<String> writePermissions, ImmutableSet<String> allReadPermissions, int areaId)587 private void verifyWritePermissionsCannotRead(ImmutableSet<String> writePermissions, 588 ImmutableSet<String> allReadPermissions, int areaId) { 589 // If there is any write permission that is also a read permission we skip the permissions. 590 if (!Collections.disjoint(writePermissions, allReadPermissions)) { 591 return; 592 } 593 // It is possible that the caller has the read permissions without adopting the shell 594 // identity. In this case, we cannot revoke the read permissions so we cannot test 595 // getProperty without read permissions. 596 if (hasReadPermissions(allReadPermissions)) { 597 return; 598 } 599 runWithShellPermissionIdentity( 600 () -> { 601 assertGetPropertyThrowsException( 602 mPropertyName 603 + " - property ID: " 604 + mPropertyId 605 + " should not be able to be read without read" 606 + " permissions.", 607 SecurityException.class, mPropertyId, areaId); 608 assertThrows( 609 mPropertyName 610 + " - property ID: " 611 + mPropertyId 612 + " should not be able to be listened to without read" 613 + " permissions.", 614 SecurityException.class, 615 () -> verifyCarPropertyValueCallback()); 616 assertThrows( 617 mPropertyName 618 + " - property ID: " 619 + mPropertyId 620 + " should not be able to be read without read" 621 + " permissions.", 622 SecurityException.class, 623 () -> verifyGetPropertiesAsync()); 624 }, writePermissions.toArray(new String[0])); 625 } 626 verifyIndividualWritePermissionsCannotWrite( ImmutableSet<String> writePermissions, int areaId)627 private void verifyIndividualWritePermissionsCannotWrite( 628 ImmutableSet<String> writePermissions, int areaId) { 629 // It is possible that the caller has the write permissions without adopting 630 // the shell identity. In this case, we cannot revoke individual permissions. 631 if (hasWritePermissions(ImmutableList.of(writePermissions))) { 632 return; 633 } 634 635 String writePermissionsNeededString = String.join(", ", writePermissions); 636 for (String writePermission: writePermissions) { 637 runWithShellPermissionIdentity( 638 () -> { 639 assertThat(mCarPropertyManager.getCarPropertyConfig(mPropertyId)).isNull(); 640 assertThrows( 641 mPropertyName 642 + " - property ID: " 643 + mPropertyId 644 + " should not be able to be written to without all of the" 645 + " following permissions granted: " 646 + writePermissionsNeededString, 647 SecurityException.class, 648 () -> mCarPropertyManager.setProperty(mPropertyType, mPropertyId, 649 areaId, getDefaultValue(mPropertyType))); 650 }, writePermission); 651 } 652 } 653 verifyWritePermissionsGiveAccessToWriteApis(ImmutableSet<String> writePermissions, ImmutableSet<String> readPermissions, Class<?> exceptedExceptionClass)654 private void verifyWritePermissionsGiveAccessToWriteApis(ImmutableSet<String> writePermissions, 655 ImmutableSet<String> readPermissions, Class<?> exceptedExceptionClass) { 656 ImmutableSet<String> propertyPermissions = 657 ImmutableSet.<String>builder() 658 .addAll(writePermissions) 659 .addAll(readPermissions) 660 .build(); 661 662 try { 663 enableAdasFeatureIfAdasStateProperty(); 664 runWithShellPermissionIdentity(() -> { 665 turnOnHvacPowerIfHvacPowerDependent(); 666 storeCurrentValues(); 667 verifyCarPropertyValueSetter(); 668 if (exceptedExceptionClass != null) { 669 assertWithMessage("Expected " + sExceptionClassOnSet + " to be of type " 670 + exceptedExceptionClass).that(sExceptionClassOnSet) 671 .isEqualTo(exceptedExceptionClass); 672 } else { 673 verifySetPropertiesAsync(); 674 } 675 if (turnOffHvacPowerIfHvacPowerDependent()) { 676 verifySetNotAvailable(); 677 } 678 }, propertyPermissions.toArray(new String[0])); 679 680 disableAdasFeatureIfAdasStatePropertyAndVerify( 681 propertyPermissions.toArray(new String[0])); 682 } finally { 683 // Restore all property values even if test fails. 684 runWithShellPermissionIdentity(() -> { 685 restoreInitialValues(); 686 }, ImmutableSet.<String>builder() 687 .addAll(propertyPermissions) 688 .addAll(mDependentOnPropertyPermissions) 689 .build().toArray(new String[0])); 690 } 691 } 692 turnOnHvacPowerIfHvacPowerDependent()693 private void turnOnHvacPowerIfHvacPowerDependent() { 694 if (!mPossiblyDependentOnHvacPowerOn) { 695 return; 696 } 697 698 CarPropertyConfig<Boolean> hvacPowerOnCarPropertyConfig = (CarPropertyConfig<Boolean>) 699 mCarPropertyManager.getCarPropertyConfig(VehiclePropertyIds.HVAC_POWER_ON); 700 if (hvacPowerOnCarPropertyConfig == null 701 || !hvacPowerOnCarPropertyConfig.getConfigArray().contains(mPropertyId)) { 702 return; 703 } 704 705 storeCurrentValuesForProperty(hvacPowerOnCarPropertyConfig); 706 // Turn the power on for all supported HVAC area IDs. 707 setBooleanPropertyInAllAreaIds(hvacPowerOnCarPropertyConfig, /* setValue: */ Boolean.TRUE); 708 } 709 turnOffHvacPowerIfHvacPowerDependent()710 private boolean turnOffHvacPowerIfHvacPowerDependent() { 711 if (!mPossiblyDependentOnHvacPowerOn) { 712 return false; 713 } 714 715 CarPropertyConfig<Boolean> hvacPowerOnCarPropertyConfig = (CarPropertyConfig<Boolean>) 716 mCarPropertyManager.getCarPropertyConfig(VehiclePropertyIds.HVAC_POWER_ON); 717 if (hvacPowerOnCarPropertyConfig == null 718 || !hvacPowerOnCarPropertyConfig.getConfigArray().contains(mPropertyId)) { 719 return false; 720 } 721 722 // Turn the power off for all supported HVAC area IDs. 723 setBooleanPropertyInAllAreaIds(hvacPowerOnCarPropertyConfig, /* setValue: */ Boolean.FALSE); 724 return true; 725 } 726 727 /** 728 * Enables the ADAS feature if the property is an ADAS property. 729 */ enableAdasFeatureIfAdasStateProperty()730 public void enableAdasFeatureIfAdasStateProperty() { 731 if (!mDependentOnPropertyId.isPresent()) { 732 return; 733 } 734 735 runWithShellPermissionIdentity(() -> { 736 int adasEnabledPropertyId = mDependentOnPropertyId.get(); 737 CarPropertyConfig<Boolean> adasEnabledCarPropertyConfig = (CarPropertyConfig<Boolean>) 738 mCarPropertyManager.getCarPropertyConfig(adasEnabledPropertyId); 739 740 if (adasEnabledCarPropertyConfig == null || (Flags.areaIdConfigAccess() 741 ? adasEnabledCarPropertyConfig.getAreaIdConfig(GLOBAL_AREA_ID).getAccess() 742 : adasEnabledCarPropertyConfig.getAccess()) 743 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) { 744 Log.w(TAG, "Cannot enable " + VehiclePropertyIds.toString(adasEnabledPropertyId) 745 + " for testing " + VehiclePropertyIds.toString(mPropertyId) 746 + " because property is either not implemented or READ only." 747 + " Manually enable if it's not already enabled."); 748 return; 749 } 750 751 storeCurrentValuesForProperty(adasEnabledCarPropertyConfig); 752 // Enable ADAS feature in all supported area IDs. 753 setBooleanPropertyInAllAreaIds(adasEnabledCarPropertyConfig, 754 /* setValue: */ Boolean.TRUE); 755 }, mDependentOnPropertyPermissions.toArray(new String[0])); 756 } 757 disableAdasFeatureIfAdasStatePropertyAndVerify(String[] enabledPermissionsList)758 private void disableAdasFeatureIfAdasStatePropertyAndVerify(String[] enabledPermissionsList) { 759 if (disableAdasFeatureIfAdasStateProperty()) { 760 runWithShellPermissionIdentity(() -> { 761 verifyAdasPropertyDisabled(); 762 }, enabledPermissionsList); 763 } 764 } 765 766 /** 767 * Disables the ADAS feature if the property is an ADAS property. 768 */ disableAdasFeatureIfAdasStateProperty()769 public boolean disableAdasFeatureIfAdasStateProperty() { 770 if (!mDependentOnPropertyId.isPresent()) { 771 return false; 772 } 773 774 AtomicBoolean isDisabled = new AtomicBoolean(false); 775 runWithShellPermissionIdentity(() -> { 776 int adasEnabledPropertyId = mDependentOnPropertyId.get(); 777 CarPropertyConfig<Boolean> adasEnabledCarPropertyConfig = (CarPropertyConfig<Boolean>) 778 mCarPropertyManager.getCarPropertyConfig(adasEnabledPropertyId); 779 780 if (adasEnabledCarPropertyConfig == null || (Flags.areaIdConfigAccess() 781 ? adasEnabledCarPropertyConfig.getAreaIdConfig(GLOBAL_AREA_ID).getAccess() 782 : adasEnabledCarPropertyConfig.getAccess()) 783 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) { 784 return; 785 } 786 787 // Disable ADAS feature in all supported area IDs. 788 setBooleanPropertyInAllAreaIds(adasEnabledCarPropertyConfig, 789 /* setValue: */ Boolean.FALSE); 790 isDisabled.set(true); 791 }, mDependentOnPropertyPermissions.toArray(new String[0])); 792 return isDisabled.get(); 793 } 794 795 /** 796 * Stores the property's current values for all areas so that they can be restored later. 797 */ storeCurrentValues()798 public void storeCurrentValues() { 799 storeCurrentValuesForProperty(getCarPropertyConfig()); 800 } 801 storeCurrentValuesForProperty(CarPropertyConfig<U> carPropertyConfig)802 private <U> void storeCurrentValuesForProperty(CarPropertyConfig<U> carPropertyConfig) { 803 SparseArray<U> areaIdToInitialValue = 804 getInitialValuesByAreaId(carPropertyConfig, mCarPropertyManager); 805 if (areaIdToInitialValue == null || areaIdToInitialValue.size() == 0) { 806 return; 807 } 808 mPropertyToAreaIdValues.put(carPropertyConfig.getPropertyId(), areaIdToInitialValue); 809 } 810 811 /** 812 * Restore the property's and dependent properties values to original values stored by previous 813 * {@link #storeCurrentValues}. 814 * 815 * Do nothing if no stored current values are available. 816 */ restoreInitialValues()817 public <U> void restoreInitialValues() { 818 for (int i = 0; i < mPropertyToAreaIdValues.size(); i++) { 819 int propertyId = mPropertyToAreaIdValues.keyAt(i); 820 CarPropertyConfig<U> carPropertyConfig = (CarPropertyConfig<U>) 821 mCarPropertyManager.getCarPropertyConfig(propertyId); 822 SparseArray<U> areaIdToInitialValue = (SparseArray<U>) 823 mPropertyToAreaIdValues.get(propertyId); 824 825 if (areaIdToInitialValue == null || carPropertyConfig == null) { 826 Log.w(TAG, "No stored values for " + VehiclePropertyIds.toString(propertyId) 827 + " to restore to, ignore"); 828 return; 829 } 830 831 restoreInitialValuesByAreaId(carPropertyConfig, mCarPropertyManager, 832 areaIdToInitialValue); 833 } 834 } 835 836 // Get a map storing the property's area Ids to the initial values. 837 @Nullable getInitialValuesByAreaId( CarPropertyConfig<U> carPropertyConfig, CarPropertyManager carPropertyManager)838 private static <U> SparseArray<U> getInitialValuesByAreaId( 839 CarPropertyConfig<U> carPropertyConfig, CarPropertyManager carPropertyManager) { 840 if (!Flags.areaIdConfigAccess() && carPropertyConfig.getAccess() 841 != CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) { 842 return null; 843 } 844 SparseArray<U> areaIdToInitialValue = new SparseArray<U>(); 845 int propertyId = carPropertyConfig.getPropertyId(); 846 String propertyName = VehiclePropertyIds.toString(propertyId); 847 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 848 if (Flags.areaIdConfigAccess() && areaIdConfig.getAccess() 849 != CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) { 850 continue; 851 } 852 int areaId = areaIdConfig.getAreaId(); 853 CarPropertyValue<U> carPropertyValue = null; 854 try { 855 carPropertyValue = carPropertyManager.getProperty(propertyId, areaId); 856 } catch (PropertyNotAvailableAndRetryException | PropertyNotAvailableException 857 | CarInternalErrorException e) { 858 Log.w(TAG, "Failed to get property:" + propertyName + " at area ID: " + areaId 859 + " to save initial car property value. Error: " + e); 860 continue; 861 } 862 if (carPropertyValue == null) { 863 Log.w(TAG, "Failed to get property:" + propertyName + " at area ID: " + areaId 864 + " to save initial car property value."); 865 continue; 866 } 867 areaIdToInitialValue.put(areaId, (U) carPropertyValue.getValue()); 868 } 869 return areaIdToInitialValue; 870 } 871 872 /** 873 * Set boolean property to a desired value in all supported area IDs. 874 */ setBooleanPropertyInAllAreaIds(CarPropertyConfig<Boolean> booleanCarPropertyConfig, Boolean setValue)875 private void setBooleanPropertyInAllAreaIds(CarPropertyConfig<Boolean> booleanCarPropertyConfig, 876 Boolean setValue) { 877 int propertyId = booleanCarPropertyConfig.getPropertyId(); 878 for (int areaId : booleanCarPropertyConfig.getAreaIds()) { 879 if (mCarPropertyManager.getBooleanProperty(propertyId, areaId) == setValue) { 880 continue; 881 } 882 CarPropertyValue<Boolean> carPropertyValue = setPropertyAndWaitForChange( 883 mCarPropertyManager, propertyId, Boolean.class, areaId, setValue); 884 assertWithMessage( 885 VehiclePropertyIds.toString(propertyId) 886 + " carPropertyValue is null for area id: " + areaId) 887 .that(carPropertyValue).isNotNull(); 888 } 889 } 890 891 // Restore the initial values of the property provided by {@code areaIdToInitialValue}. restoreInitialValuesByAreaId(CarPropertyConfig<U> carPropertyConfig, CarPropertyManager carPropertyManager, SparseArray<U> areaIdToInitialValue)892 private static <U> void restoreInitialValuesByAreaId(CarPropertyConfig<U> carPropertyConfig, 893 CarPropertyManager carPropertyManager, SparseArray<U> areaIdToInitialValue) { 894 int propertyId = carPropertyConfig.getPropertyId(); 895 String propertyName = VehiclePropertyIds.toString(propertyId); 896 for (int i = 0; i < areaIdToInitialValue.size(); i++) { 897 int areaId = areaIdToInitialValue.keyAt(i); 898 U originalValue = areaIdToInitialValue.valueAt(i); 899 CarPropertyValue<U> currentCarPropertyValue = null; 900 try { 901 currentCarPropertyValue = carPropertyManager.getProperty(propertyId, areaId); 902 } catch (PropertyNotAvailableAndRetryException | PropertyNotAvailableException 903 | CarInternalErrorException e) { 904 Log.w(TAG, "Failed to get property:" + propertyName + " at area ID: " + areaId 905 + " to restore initial car property value. Error: " + e); 906 continue; 907 } 908 if (currentCarPropertyValue == null) { 909 Log.w(TAG, "Failed to get property:" + propertyName + " at area ID: " + areaId 910 + " to restore initial car property value."); 911 continue; 912 } 913 U currentValue = (U) currentCarPropertyValue.getValue(); 914 if (valueEquals(originalValue, currentValue)) { 915 continue; 916 } 917 CarPropertyValue<U> carPropertyValue = setPropertyAndWaitForChange(carPropertyManager, 918 propertyId, carPropertyConfig.getPropertyType(), areaId, originalValue); 919 assertWithMessage( 920 "Failed to restore car property value for property: " + propertyName 921 + " at area ID: " + areaId + " to its original value: " + originalValue 922 + ", current value: " + currentValue) 923 .that(carPropertyValue).isNotNull(); 924 } 925 } 926 927 /** 928 * Gets the possible values that could be set to. 929 * 930 * The values returned here must not cause {@code IllegalArgumentException} for set. 931 * 932 * Returns {@code null} or empty array if we don't know possible values. 933 */ getPossibleValues(int areaId)934 public @Nullable Collection<T> getPossibleValues(int areaId) { 935 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 936 if (Boolean.class.equals(carPropertyConfig.getPropertyType())) { 937 return (List<T>) List.of(Boolean.TRUE, Boolean.FALSE); 938 } else if (Integer.class.equals(carPropertyConfig.getPropertyType())) { 939 return (List<T>) getPossibleIntegerValues(areaId); 940 } else if (Float.class.equals(carPropertyConfig.getPropertyType())) { 941 return getPossibleFloatValues(areaId); 942 } 943 return null; 944 } 945 isAtLeastU()946 private boolean isAtLeastU() { 947 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 948 } 949 950 /** 951 * Gets the possible values for an integer property. 952 */ getPossibleIntegerValues(int areaId)953 private List<Integer> getPossibleIntegerValues(int areaId) { 954 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 955 List<Integer> possibleValues = new ArrayList<>(); 956 if (mPropertyId == VehiclePropertyIds.HVAC_FAN_DIRECTION) { 957 int[] availableHvacFanDirections = mCarPropertyManager.getIntArrayProperty( 958 VehiclePropertyIds.HVAC_FAN_DIRECTION_AVAILABLE, areaId); 959 for (int i = 0; i < availableHvacFanDirections.length; i++) { 960 if (availableHvacFanDirections[i] != CarHvacFanDirection.UNKNOWN) { 961 possibleValues.add(availableHvacFanDirections[i]); 962 } 963 } 964 return possibleValues; 965 } 966 if (mVerifySetterWithConfigArrayValues) { 967 for (Integer value : carPropertyConfig.getConfigArray()) { 968 possibleValues.add(value); 969 } 970 return possibleValues; 971 } 972 973 if (!mAllPossibleEnumValues.isEmpty() && isAtLeastU()) { 974 AreaIdConfig areaIdConfig = carPropertyConfig.getAreaIdConfig(areaId); 975 for (Integer value : (List<Integer>) areaIdConfig.getSupportedEnumValues()) { 976 if ((mAllPossibleUnwritableValues.isEmpty() 977 || !mAllPossibleUnwritableValues.contains(value)) 978 && (mAllPossibleUnavailableValues.isEmpty() 979 || !mAllPossibleUnavailableValues.contains(value))) { 980 possibleValues.add(value); 981 } 982 } 983 } else { 984 Integer minValue = (Integer) carPropertyConfig.getMinValue(areaId); 985 Integer maxValue = (Integer) carPropertyConfig.getMaxValue(areaId); 986 assertWithMessage("Read-write/Write integer properties should either have a config " 987 + "array with valid set values, a set of supported enums, or valid min and max " 988 + "values set in the CarPropertyConfig. However, the following property has " 989 + "none of these: " + VehiclePropertyIds.toString(mPropertyId)) 990 .that(minValue != null && maxValue != null).isTrue(); 991 List<Integer> valuesToSet = IntStream.rangeClosed( 992 minValue.intValue(), maxValue.intValue()).boxed().collect(Collectors.toList()); 993 for (int i = 0; i < valuesToSet.size(); i++) { 994 possibleValues.add(valuesToSet.get(i)); 995 } 996 } 997 return possibleValues; 998 } 999 1000 /** 1001 * Gets the possible values for a float property. 1002 */ getPossibleFloatValues(int areaId)1003 private Collection<T> getPossibleFloatValues(int areaId) { 1004 ImmutableSet.Builder<Float> possibleValuesBuilder = ImmutableSet.builder(); 1005 if (mPropertyId == VehiclePropertyIds.HVAC_TEMPERATURE_SET) { 1006 List<Integer> hvacTempSetConfigArray = getCarPropertyConfig().getConfigArray(); 1007 if (!hvacTempSetConfigArray.isEmpty()) { 1008 // For HVAC_TEMPERATURE_SET, the configArray specifies the supported temperature 1009 // values for the property. configArray[0] is the lower bound of the supported 1010 // temperatures in Celsius. configArray[1] is the upper bound of the supported 1011 // temperatures in Celsius. configArray[2] is the supported temperature increment 1012 // between the two bounds. All configArray values are Celsius*10 since the 1013 // configArray is List<Integer> but HVAC_TEMPERATURE_SET is a Float type property. 1014 for (int possibleHvacTempSetValue = hvacTempSetConfigArray.get(0); 1015 possibleHvacTempSetValue <= hvacTempSetConfigArray.get(1); 1016 possibleHvacTempSetValue += hvacTempSetConfigArray.get(2)) { 1017 possibleValuesBuilder.add((float) possibleHvacTempSetValue / 10.0f); 1018 } 1019 } else { 1020 // If the configArray is not specified, then use min/max values. 1021 Float minValueFloat = 1022 (Float) getCarPropertyConfig().getAreaIdConfig(areaId).getMinValue(); 1023 Float maxValueFloat = 1024 (Float) getCarPropertyConfig().getAreaIdConfig(areaId).getMaxValue(); 1025 possibleValuesBuilder.add(minValueFloat); 1026 possibleValuesBuilder.add(maxValueFloat); 1027 } 1028 } else if (mPropertyId == VehiclePropertyIds.EV_CHARGE_PERCENT_LIMIT) { 1029 List<Integer> evChargePercentLimitConfigArray = getCarPropertyConfig().getConfigArray(); 1030 if (!evChargePercentLimitConfigArray.isEmpty()) { 1031 for (Integer possibleEvChargePercentLimit : evChargePercentLimitConfigArray) { 1032 possibleValuesBuilder.add(possibleEvChargePercentLimit.floatValue()); 1033 } 1034 } else { 1035 // If the configArray is not specified, then values between 0 and 100 percent must 1036 // be supported. 1037 possibleValuesBuilder.add(0f); 1038 possibleValuesBuilder.add(100f); 1039 } 1040 } else if (mPropertyId == VehiclePropertyIds.EV_CHARGE_CURRENT_DRAW_LIMIT) { 1041 // First value in the configArray specifies the max current draw allowed by the vehicle. 1042 Integer vehicleMaxCurrentDrawLimit = getCarPropertyConfig().getConfigArray().get(0); 1043 possibleValuesBuilder.add(vehicleMaxCurrentDrawLimit.floatValue()); 1044 } else if (mPropertyId == VehiclePropertyIds.RANGE_REMAINING) { 1045 // Test when no range is remaining 1046 possibleValuesBuilder.add(0f); 1047 } 1048 return (Collection<T>) possibleValuesBuilder.build(); 1049 } 1050 verifyCarPropertyValueSetter()1051 private void verifyCarPropertyValueSetter() { 1052 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1053 if (!Flags.areaIdConfigAccess() && carPropertyConfig.getAccess() 1054 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) { 1055 verifySetPropertyFails(carPropertyConfig.getAreaIds()[0]); 1056 return; 1057 } 1058 if (Boolean.class.equals(carPropertyConfig.getPropertyType())) { 1059 verifyBooleanPropertySetter(); 1060 } else if (Integer.class.equals(carPropertyConfig.getPropertyType())) { 1061 verifyIntegerPropertySetter(); 1062 } else if (Float.class.equals(carPropertyConfig.getPropertyType())) { 1063 verifyFloatPropertySetter(); 1064 } else if (mPropertyId == VehiclePropertyIds.HVAC_TEMPERATURE_VALUE_SUGGESTION) { 1065 verifyHvacTemperatureValueSuggestionSetter(); 1066 } 1067 } 1068 verifySetPropertyFails(int areaId)1069 private void verifySetPropertyFails(int areaId) { 1070 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1071 assertThrows( 1072 mPropertyName 1073 + " is a read_only property so setProperty should throw an" 1074 + " IllegalArgumentException.", 1075 IllegalArgumentException.class, 1076 () -> mCarPropertyManager.setProperty(mPropertyType, mPropertyId, areaId, 1077 getDefaultValue(mPropertyType))); 1078 } 1079 verifyBooleanPropertySetter()1080 private void verifyBooleanPropertySetter() { 1081 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1082 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 1083 int areaId = areaIdConfig.getAreaId(); 1084 if (Flags.areaIdConfigAccess() && areaIdConfig.getAccess() 1085 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) { 1086 verifySetPropertyFails(areaId); 1087 continue; 1088 } 1089 for (Boolean valueToSet: List.of(Boolean.TRUE, Boolean.FALSE)) { 1090 verifySetProperty(areaId, (T) valueToSet); 1091 } 1092 } 1093 } 1094 1095 verifyIntegerPropertySetter()1096 private void verifyIntegerPropertySetter() { 1097 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1098 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 1099 int areaId = areaIdConfig.getAreaId(); 1100 if (Flags.areaIdConfigAccess() && areaIdConfig.getAccess() 1101 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) { 1102 verifySetPropertyFails(areaId); 1103 continue; 1104 } 1105 for (Integer valueToSet : getPossibleIntegerValues(areaId)) { 1106 verifySetProperty(areaId, (T) valueToSet); 1107 } 1108 } 1109 if (!mAllPossibleEnumValues.isEmpty() && isAtLeastU()) { 1110 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 1111 if (Flags.areaIdConfigAccess() && areaIdConfig.getAccess() 1112 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) { 1113 continue; 1114 } 1115 for (T valueToSet : (List<T>) areaIdConfig.getSupportedEnumValues()) { 1116 if (!mAllPossibleUnwritableValues.isEmpty() 1117 && mAllPossibleUnwritableValues.contains(valueToSet)) { 1118 assertThrows("Trying to set an unwritable value: " + valueToSet 1119 + " to property: " + mPropertyId + " should throw an " 1120 + "IllegalArgumentException", 1121 IllegalArgumentException.class, 1122 () -> setPropertyAndWaitForChange( 1123 mCarPropertyManager, mPropertyId, 1124 carPropertyConfig.getPropertyType(), 1125 areaIdConfig.getAreaId(), valueToSet)); 1126 } 1127 if (!mAllPossibleUnavailableValues.isEmpty() 1128 && mAllPossibleUnavailableValues.contains(valueToSet)) { 1129 assertThrows("Trying to set an unavailable value: " + valueToSet 1130 + " to property: " + mPropertyId + " should throw an " 1131 + "PropertyNotAvailableException", 1132 PropertyNotAvailableException.class, 1133 () -> mCarPropertyManager.setProperty( 1134 carPropertyConfig.getPropertyType(), mPropertyId, 1135 areaIdConfig.getAreaId(), valueToSet)); 1136 } 1137 } 1138 } 1139 } 1140 } 1141 verifyFloatPropertySetter()1142 private void verifyFloatPropertySetter() { 1143 for (int areaId : getCarPropertyConfig().getAreaIds()) { 1144 for (T valueToSet : getPossibleFloatValues(areaId)) { 1145 verifySetProperty(areaId, valueToSet); 1146 } 1147 } 1148 } 1149 verifySetProperty(int areaId, T valueToSet)1150 private void verifySetProperty(int areaId, T valueToSet) { 1151 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1152 if (Flags.areaIdConfigAccess() && carPropertyConfig.getAreaIdConfig(areaId).getAccess() 1153 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) { 1154 return; 1155 } 1156 if ((Flags.areaIdConfigAccess() ? carPropertyConfig.getAreaIdConfig(areaId).getAccess() 1157 : carPropertyConfig.getAccess()) 1158 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) { 1159 Log.w(TAG, "Property: " + mPropertyName + " will be altered during the test and it is" 1160 + " not possible to restore."); 1161 verifySetPropertyOkayOrThrowExpectedExceptions(areaId, valueToSet); 1162 return; 1163 } 1164 try { 1165 CarPropertyValue<T> currentCarPropertyValue = 1166 mCarPropertyManager.getProperty(mPropertyId, areaId); 1167 verifyCarPropertyValue(currentCarPropertyValue, areaId, 1168 CAR_PROPERTY_VALUE_SOURCE_GETTER); 1169 if (valueEquals(valueToSet, currentCarPropertyValue.getValue())) { 1170 return; 1171 } 1172 } catch (PropertyNotAvailableException e) { 1173 verifyPropertyNotAvailableException(e); 1174 } catch (CarInternalErrorException e) { 1175 verifyInternalErrorException(e); 1176 } 1177 CarPropertyValue<T> updatedCarPropertyValue = setPropertyAndWaitForChange( 1178 mCarPropertyManager, mPropertyId, carPropertyConfig.getPropertyType(), areaId, 1179 valueToSet); 1180 if (sExceptionClassOnSet == null) { 1181 verifyCarPropertyValue(updatedCarPropertyValue, areaId, 1182 CAR_PROPERTY_VALUE_SOURCE_CALLBACK); 1183 } 1184 } 1185 verifyHvacTemperatureValueSuggestionSetter()1186 private void verifyHvacTemperatureValueSuggestionSetter() { 1187 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1188 CarPropertyConfig<?> hvacTemperatureSetCarPropertyConfig = 1189 mCarPropertyManager.getCarPropertyConfig(VehiclePropertyIds.HVAC_TEMPERATURE_SET); 1190 if (hvacTemperatureSetCarPropertyConfig == null) { 1191 return; 1192 } 1193 List<Integer> hvacTemperatureSetConfigArray = 1194 hvacTemperatureSetCarPropertyConfig.getConfigArray(); 1195 if (hvacTemperatureSetConfigArray.isEmpty()) { 1196 return; 1197 } 1198 float minTempInCelsius = hvacTemperatureSetConfigArray.get(0).floatValue() / 10f; 1199 float minTempInFahrenheit = hvacTemperatureSetConfigArray.get(3).floatValue() / 10f; 1200 1201 Float[] temperatureRequest = new Float[] { 1202 /* requestedValue = */ minTempInCelsius, 1203 /* units = */ (float) 0x30, // VehicleUnit#CELSIUS 1204 /* suggestedValueInCelsius = */ 0f, 1205 /* suggestedValueInFahrenheit = */ 0f 1206 }; 1207 Float[] expectedTemperatureResponse = new Float[] { 1208 /* requestedValue = */ minTempInCelsius, 1209 /* units = */ (float) 0x30, // VehicleUnit#CELSIUS 1210 /* suggestedValueInCelsius = */ minTempInCelsius, 1211 /* suggestedValueInFahrenheit = */ minTempInFahrenheit 1212 }; 1213 for (int areaId: carPropertyConfig.getAreaIds()) { 1214 CarPropertyValue<Float[]> updatedCarPropertyValue = setPropertyAndWaitForChange( 1215 mCarPropertyManager, mPropertyId, Float[].class, areaId, 1216 temperatureRequest, expectedTemperatureResponse); 1217 verifyCarPropertyValue(updatedCarPropertyValue, areaId, 1218 CAR_PROPERTY_VALUE_SOURCE_CALLBACK); 1219 verifyHvacTemperatureValueSuggestionResponse(updatedCarPropertyValue.getValue()); 1220 } 1221 } 1222 verifySetPropertyOkayOrThrowExpectedExceptions(int areaId, T valueToSet)1223 private void verifySetPropertyOkayOrThrowExpectedExceptions(int areaId, T valueToSet) { 1224 try { 1225 mCarPropertyManager.setProperty(mPropertyType, mPropertyId, areaId, valueToSet); 1226 } catch (PropertyNotAvailableAndRetryException e) { 1227 } catch (PropertyNotAvailableException e) { 1228 verifyPropertyNotAvailableException(e); 1229 } catch (CarInternalErrorException e) { 1230 verifyInternalErrorException(e); 1231 } catch (Exception e) { 1232 assertWithMessage("Unexpected exception thrown when trying to setProperty on " 1233 + mPropertyName + ": " + e).fail(); 1234 } 1235 } 1236 verifySetNotAvailable()1237 private void verifySetNotAvailable() { 1238 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1239 if (!Flags.areaIdConfigAccess() && carPropertyConfig.getAccess() 1240 != CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) { 1241 return; 1242 } 1243 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 1244 if (Flags.areaIdConfigAccess() && areaIdConfig.getAccess() 1245 != CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) { 1246 continue; 1247 } 1248 int areaId = areaIdConfig.getAreaId(); 1249 CarPropertyValue<T> currentValue = null; 1250 try { 1251 // getProperty may/may not throw exception when the property is not available. 1252 currentValue = mCarPropertyManager.getProperty(mPropertyId, areaId); 1253 T valueToSet = getDefaultValue(mPropertyType); 1254 if (valueToSet == null) { 1255 assertWithMessage("Testing mixed type property is not supported").fail(); 1256 } 1257 verifySetProperty(areaId, valueToSet); 1258 } catch (Exception e) { 1259 // In normal cases, this should throw PropertyNotAvailableException. 1260 // In rare cases, the value we are setting is the same as the current value, 1261 // which makes the set operation a no-op. So it is possible that no exception 1262 // is thrown here. 1263 // It is also possible that this may throw IllegalArgumentException if the value to 1264 // set is not valid. 1265 assertWithMessage( 1266 "Setting property " + mPropertyName + " when it's not available" 1267 + " should throw either PropertyNotAvailableException or" 1268 + " IllegalArgumentException.") 1269 .that(e.getClass()) 1270 .isAnyOf(PropertyNotAvailableException.class, 1271 IllegalArgumentException.class); 1272 } 1273 if (currentValue == null) { 1274 // If the property is not available for getting, continue. 1275 continue; 1276 } 1277 CarPropertyValue<T> newValue = mCarPropertyManager.getProperty(mPropertyId, areaId); 1278 assertWithMessage( 1279 "Setting property " + mPropertyName + " while power is off or required" 1280 + " property is disabled must have no effect.") 1281 .that(newValue.getValue()) 1282 .isEqualTo(currentValue.getValue()); 1283 } 1284 } 1285 verifyAdasPropertyDisabled()1286 private void verifyAdasPropertyDisabled() { 1287 if (!mVerifyErrorStates) { 1288 verifySetNotAvailable(); 1289 return; 1290 } 1291 1292 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1293 if (!Flags.areaIdConfigAccess() && carPropertyConfig.getAccess() 1294 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) { 1295 return; 1296 } 1297 1298 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 1299 if (Flags.areaIdConfigAccess() && areaIdConfig.getAccess() 1300 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) { 1301 continue; 1302 } 1303 Integer adasState = mCarPropertyManager.getIntProperty(mPropertyId, 1304 areaIdConfig.getAreaId()); 1305 assertWithMessage( 1306 "When ADAS feature is disabled, " 1307 + VehiclePropertyIds.toString(mPropertyId) 1308 + " must be set to " + ErrorState.NOT_AVAILABLE_DISABLED 1309 + " (ErrorState.NOT_AVAILABLE_DISABLED).") 1310 .that(adasState) 1311 .isEqualTo(ErrorState.NOT_AVAILABLE_DISABLED); 1312 } 1313 } 1314 getUpdatesPerAreaId(int changeMode)1315 private static int getUpdatesPerAreaId(int changeMode) { 1316 return changeMode != CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS 1317 ? 1 : 2; 1318 } 1319 getRegisterCallbackTimeoutMillis(int changeMode, float minSampleRate)1320 private static long getRegisterCallbackTimeoutMillis(int changeMode, float minSampleRate) { 1321 long timeoutMillis = 1500; 1322 if (changeMode == CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS) { 1323 float secondsToMillis = 1_000; 1324 long bufferMillis = 1_000; // 1 second 1325 timeoutMillis = ((long) ((1.0f / minSampleRate) * secondsToMillis 1326 * getUpdatesPerAreaId(changeMode))) + bufferMillis; 1327 } 1328 return timeoutMillis; 1329 } 1330 verifyCarPropertyValueCallback()1331 private void verifyCarPropertyValueCallback() { 1332 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1333 if ((Flags.areaIdConfigAccess() ? carPropertyConfig.getAreaIdConfigs().get(0).getAccess() 1334 : carPropertyConfig.getAccess()) 1335 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) { 1336 verifyCallbackFails(); 1337 return; 1338 } 1339 int updatesPerAreaId = getUpdatesPerAreaId(mChangeMode); 1340 long timeoutMillis = getRegisterCallbackTimeoutMillis(mChangeMode, 1341 carPropertyConfig.getMinSampleRate()); 1342 1343 CarPropertyValueCallback carPropertyValueCallback = new CarPropertyValueCallback( 1344 mPropertyName, carPropertyConfig.getAreaIds(), updatesPerAreaId, timeoutMillis); 1345 assertWithMessage("Failed to register callback for " + mPropertyName) 1346 .that( 1347 mCarPropertyManager.registerCallback(carPropertyValueCallback, mPropertyId, 1348 carPropertyConfig.getMaxSampleRate())) 1349 .isTrue(); 1350 SparseArray<List<CarPropertyValue<?>>> areaIdToCarPropertyValues = 1351 carPropertyValueCallback.getAreaIdToCarPropertyValues(); 1352 mCarPropertyManager.unregisterCallback(carPropertyValueCallback, mPropertyId); 1353 1354 for (int areaId : carPropertyConfig.getAreaIds()) { 1355 List<CarPropertyValue<?>> carPropertyValues = areaIdToCarPropertyValues.get(areaId); 1356 assertWithMessage( 1357 mPropertyName + " callback value list is null for area ID: " + areaId).that( 1358 carPropertyValues).isNotNull(); 1359 assertWithMessage(mPropertyName + " callback values did not receive " + updatesPerAreaId 1360 + " updates for area ID: " + areaId).that(carPropertyValues.size()).isAtLeast( 1361 updatesPerAreaId); 1362 for (CarPropertyValue<?> carPropertyValue : carPropertyValues) { 1363 verifyCarPropertyValue(carPropertyValue, carPropertyValue.getAreaId(), 1364 CAR_PROPERTY_VALUE_SOURCE_CALLBACK); 1365 if (mPropertyId == VehiclePropertyIds.HVAC_TEMPERATURE_VALUE_SUGGESTION) { 1366 verifyHvacTemperatureValueSuggestionResponse( 1367 (Float[]) carPropertyValue.getValue()); 1368 } 1369 } 1370 } 1371 } 1372 verifyCallbackFails()1373 private void verifyCallbackFails() { 1374 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1375 int updatesPerAreaId = getUpdatesPerAreaId(mChangeMode); 1376 long timeoutMillis = getRegisterCallbackTimeoutMillis(mChangeMode, 1377 carPropertyConfig.getMinSampleRate()); 1378 1379 CarPropertyValueCallback carPropertyValueCallback = new CarPropertyValueCallback( 1380 mPropertyName, carPropertyConfig.getAreaIds(), updatesPerAreaId, timeoutMillis); 1381 assertThrows( 1382 mPropertyName 1383 + " is a write_only property so registerCallback should throw an" 1384 + " IllegalArgumentException.", 1385 IllegalArgumentException.class, 1386 () -> mCarPropertyManager.registerCallback(carPropertyValueCallback, mPropertyId, 1387 carPropertyConfig.getMaxSampleRate())); 1388 } 1389 verifyAccess_isSubsetOfOtherAccess(int subAccess, int superAccess)1390 private void verifyAccess_isSubsetOfOtherAccess(int subAccess, int superAccess) { 1391 if (superAccess == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) { 1392 assertWithMessage( 1393 mPropertyName 1394 + " must be " 1395 + accessToString(CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) 1396 + " or " 1397 + accessToString( 1398 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE)) 1399 .that(subAccess) 1400 .isIn( 1401 ImmutableSet.of( 1402 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ, 1403 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE)); 1404 } else { 1405 assertWithMessage(mPropertyName + " must be " + accessToString(superAccess)) 1406 .that(subAccess) 1407 .isEqualTo(superAccess); 1408 } 1409 } 1410 verifyCarPropertyConfig()1411 private void verifyCarPropertyConfig() { 1412 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1413 assertWithMessage(mPropertyName + " CarPropertyConfig must have correct property ID") 1414 .that(carPropertyConfig.getPropertyId()) 1415 .isEqualTo(mPropertyId); 1416 int carPropConfigAccess = carPropertyConfig.getAccess(); 1417 verifyAccess_isSubsetOfOtherAccess(carPropConfigAccess, mAccess); 1418 if (Flags.areaIdConfigAccess()) { 1419 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 1420 int areaAccess = areaIdConfig.getAccess(); 1421 verifyAccess_isSubsetOfOtherAccess(areaAccess, mAccess); 1422 verifyAccess_isSubsetOfOtherAccess(carPropConfigAccess, areaAccess); 1423 } 1424 } 1425 assertWithMessage(mPropertyName + " must be " + areaTypeToString(mAreaType)) 1426 .that(carPropertyConfig.getAreaType()) 1427 .isEqualTo(mAreaType); 1428 assertWithMessage(mPropertyName + " must be " + changeModeToString(mChangeMode)) 1429 .that(carPropertyConfig.getChangeMode()) 1430 .isEqualTo(mChangeMode); 1431 assertWithMessage(mPropertyName + " must be " + mPropertyType + " type property") 1432 .that(carPropertyConfig.getPropertyType()) 1433 .isEqualTo(mPropertyType); 1434 1435 int[] areaIds = carPropertyConfig.getAreaIds(); 1436 assertWithMessage(mPropertyName + "'s must have at least 1 area ID defined") 1437 .that(areaIds.length).isAtLeast(1); 1438 assertWithMessage(mPropertyName + "'s area IDs must all be unique: " + Arrays.toString( 1439 areaIds)).that(ImmutableSet.copyOf(Arrays.stream( 1440 areaIds).boxed().collect(Collectors.toList())).size() 1441 == areaIds.length).isTrue(); 1442 1443 if (mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL) { 1444 assertWithMessage( 1445 mPropertyName 1446 + "'s AreaIds must contain a single 0 since it is " 1447 + areaTypeToString(mAreaType)) 1448 .that(areaIds) 1449 .isEqualTo(new int[] {0}); 1450 } else if (mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_WHEEL) { 1451 verifyValidAreaIdsForAreaType(ALL_POSSIBLE_WHEEL_AREA_IDS); 1452 verifyNoAreaOverlapInAreaIds(WHEEL_AREAS); 1453 } else if (mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW) { 1454 verifyValidAreaIdsForAreaType(ALL_POSSIBLE_WINDOW_AREA_IDS); 1455 verifyNoAreaOverlapInAreaIds(WINDOW_AREAS); 1456 } else if (mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_MIRROR) { 1457 verifyValidAreaIdsForAreaType(ALL_POSSIBLE_MIRROR_AREA_IDS); 1458 verifyNoAreaOverlapInAreaIds(MIRROR_AREAS); 1459 } else if (mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_SEAT 1460 && mPropertyId != VehiclePropertyIds.INFO_DRIVER_SEAT) { 1461 verifyValidAreaIdsForAreaType(ALL_POSSIBLE_SEAT_AREA_IDS); 1462 verifyNoAreaOverlapInAreaIds(SEAT_AREAS); 1463 } else if (mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_DOOR) { 1464 verifyValidAreaIdsForAreaType(ALL_POSSIBLE_DOOR_AREA_IDS); 1465 verifyNoAreaOverlapInAreaIds(DOOR_AREAS); 1466 } else if (mAreaType == VehicleAreaType.VEHICLE_AREA_TYPE_VENDOR) { 1467 assertWithMessage(mPropertyName 1468 + " has an unsupported area type " 1469 + areaTypeToString(mAreaType) 1470 + " since associated feature flag is false") 1471 .that(Flags.androidVicVehicleProperties()) 1472 .isTrue(); 1473 1474 ImmutableSet<Integer> setOfAreaIds = 1475 ImmutableSet.copyOf(Arrays.stream(areaIds).boxed().collect(Collectors.toSet())); 1476 verifyNoAreaOverlapInAreaIds(setOfAreaIds); 1477 } 1478 1479 if (mAreaIdsVerifier.isPresent()) { 1480 mAreaIdsVerifier.get().verify(areaIds); 1481 } 1482 1483 if (mChangeMode == CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS) { 1484 verifyContinuousCarPropertyConfig(); 1485 } else { 1486 verifyNonContinuousCarPropertyConfig(); 1487 } 1488 1489 mCarPropertyConfigVerifier.ifPresent( 1490 carPropertyConfigVerifier -> carPropertyConfigVerifier.verify(carPropertyConfig)); 1491 1492 if (!mPossibleConfigArrayValues.isEmpty()) { 1493 assertWithMessage(mPropertyName + " configArray must specify supported values") 1494 .that(carPropertyConfig.getConfigArray().size()) 1495 .isGreaterThan(0); 1496 for (Integer supportedValue : carPropertyConfig.getConfigArray()) { 1497 assertWithMessage( 1498 mPropertyName 1499 + " configArray value must be a defined " 1500 + "value: " 1501 + supportedValue) 1502 .that(supportedValue) 1503 .isIn(mPossibleConfigArrayValues); 1504 } 1505 } 1506 1507 mConfigArrayVerifier.ifPresent(configArrayVerifier -> configArrayVerifier.verify( 1508 carPropertyConfig.getConfigArray())); 1509 1510 if (mPossibleConfigArrayValues.isEmpty() && !mConfigArrayVerifier.isPresent() 1511 && !mCarPropertyConfigVerifier.isPresent()) { 1512 assertWithMessage(mPropertyName + " configArray is undefined, so it must be empty") 1513 .that(carPropertyConfig.getConfigArray().size()) 1514 .isEqualTo(0); 1515 } 1516 1517 for (int areaId : areaIds) { 1518 T areaIdMinValue = (T) carPropertyConfig.getMinValue(areaId); 1519 T areaIdMaxValue = (T) carPropertyConfig.getMaxValue(areaId); 1520 if (mRequireMinMaxValues) { 1521 assertWithMessage(mPropertyName + " - area ID: " + areaId 1522 + " must have min value defined").that(areaIdMinValue).isNotNull(); 1523 assertWithMessage(mPropertyName + " - area ID: " + areaId 1524 + " must have max value defined").that(areaIdMaxValue).isNotNull(); 1525 } 1526 if (mRequireMinValuesToBeZero) { 1527 assertWithMessage( 1528 mPropertyName + " - area ID: " + areaId + " min value must be zero").that( 1529 areaIdMinValue).isEqualTo(0); 1530 } 1531 if (mRequireZeroToBeContainedInMinMaxRanges) { 1532 assertWithMessage(mPropertyName + " - areaId: " + areaId 1533 + "'s max and min range must contain zero").that( 1534 verifyMaxAndMinRangeContainsZero(areaIdMinValue, areaIdMaxValue)).isTrue(); 1535 1536 } 1537 if (areaIdMinValue != null || areaIdMaxValue != null) { 1538 assertWithMessage( 1539 mPropertyName 1540 + " - areaId: " 1541 + areaId 1542 + "'s max value must be >= min value") 1543 .that(verifyMaxAndMin(areaIdMinValue, areaIdMaxValue)) 1544 .isTrue(); 1545 } 1546 1547 if (mRequirePropertyValueToBeInConfigArray && isAtLeastU()) { 1548 List<?> supportedEnumValues = carPropertyConfig.getAreaIdConfig( 1549 areaId).getSupportedEnumValues(); 1550 assertWithMessage(mPropertyName + " - areaId: " + areaId 1551 + "'s supported enum values must match the values in the config array.") 1552 .that(carPropertyConfig.getConfigArray()) 1553 .containsExactlyElementsIn(supportedEnumValues); 1554 } 1555 1556 if (mChangeMode == CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE 1557 && !mAllPossibleEnumValues.isEmpty() && isAtLeastU()) { 1558 List<?> supportedEnumValues = carPropertyConfig.getAreaIdConfig( 1559 areaId).getSupportedEnumValues(); 1560 assertWithMessage(mPropertyName + " - areaId: " + areaId 1561 + "'s supported enum values must be defined").that( 1562 supportedEnumValues).isNotEmpty(); 1563 assertWithMessage(mPropertyName + " - areaId: " + areaId 1564 + "'s supported enum values must not contain any duplicates").that( 1565 supportedEnumValues).containsNoDuplicates(); 1566 assertWithMessage( 1567 mPropertyName + " - areaId: " + areaId + "'s supported enum values " 1568 + supportedEnumValues + " must all exist in all possible enum set " 1569 + mAllPossibleEnumValues).that( 1570 mAllPossibleEnumValues.containsAll(supportedEnumValues)).isTrue(); 1571 } else if (isAtLeastU()) { 1572 assertWithMessage(mPropertyName + " - areaId: " + areaId 1573 + "'s supported enum values must be empty since property does not support" 1574 + " an enum").that( 1575 carPropertyConfig.getAreaIdConfig( 1576 areaId).getSupportedEnumValues()).isEmpty(); 1577 } 1578 } 1579 } 1580 verifyMaxAndMinRangeContainsZero(T min, T max)1581 private boolean verifyMaxAndMinRangeContainsZero(T min, T max) { 1582 int propertyType = mPropertyId & VehiclePropertyType.MASK; 1583 switch (propertyType) { 1584 case VehiclePropertyType.INT32: 1585 return (Integer) max >= 0 && (Integer) min <= 0; 1586 case VehiclePropertyType.INT64: 1587 return (Long) max >= 0 && (Long) min <= 0; 1588 case VehiclePropertyType.FLOAT: 1589 return (Float) max >= 0 && (Float) min <= 0; 1590 default: 1591 return false; 1592 } 1593 } 1594 verifyMaxAndMin(T min, T max)1595 private boolean verifyMaxAndMin(T min, T max) { 1596 int propertyType = mPropertyId & VehiclePropertyType.MASK; 1597 switch (propertyType) { 1598 case VehiclePropertyType.INT32: 1599 return (Integer) max >= (Integer) min; 1600 case VehiclePropertyType.INT64: 1601 return (Long) max >= (Long) min; 1602 case VehiclePropertyType.FLOAT: 1603 return (Float) max >= (Float) min; 1604 default: 1605 return false; 1606 } 1607 } 1608 verifyContinuousCarPropertyConfig()1609 private void verifyContinuousCarPropertyConfig() { 1610 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1611 assertWithMessage( 1612 mPropertyName 1613 + " must define max sample rate since change mode is " 1614 + changeModeToString(mChangeMode)) 1615 .that(carPropertyConfig.getMaxSampleRate()) 1616 .isGreaterThan(0); 1617 assertWithMessage( 1618 mPropertyName 1619 + " must define min sample rate since change mode is " 1620 + changeModeToString(mChangeMode)) 1621 .that(carPropertyConfig.getMinSampleRate()) 1622 .isGreaterThan(0); 1623 assertWithMessage(mPropertyName + " max sample rate must be >= min sample rate") 1624 .that(carPropertyConfig.getMaxSampleRate() >= carPropertyConfig.getMinSampleRate()) 1625 .isTrue(); 1626 } 1627 verifyNonContinuousCarPropertyConfig()1628 private void verifyNonContinuousCarPropertyConfig() { 1629 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1630 assertWithMessage( 1631 mPropertyName 1632 + " must define max sample rate as 0 since change mode is " 1633 + changeModeToString(mChangeMode)) 1634 .that(carPropertyConfig.getMaxSampleRate()) 1635 .isEqualTo(0); 1636 assertWithMessage( 1637 mPropertyName 1638 + " must define min sample rate as 0 since change mode is " 1639 + changeModeToString(mChangeMode)) 1640 .that(carPropertyConfig.getMinSampleRate()) 1641 .isEqualTo(0); 1642 } 1643 handleGetPropertyExceptions(Exception e)1644 private void handleGetPropertyExceptions(Exception e) { 1645 if (e instanceof PropertyNotAvailableException) { 1646 verifyPropertyNotAvailableException((PropertyNotAvailableException) e); 1647 } else if (e instanceof CarInternalErrorException) { 1648 verifyInternalErrorException((CarInternalErrorException) e); 1649 } 1650 sExceptionClassOnGet = e.getClass(); 1651 } 1652 handleClassSpecificGetPropertyExceptions(Exception e, Class<?> expectedClass, int areaId)1653 private void handleClassSpecificGetPropertyExceptions(Exception e, Class<?> expectedClass, 1654 int areaId) { 1655 if (e instanceof IllegalArgumentException) { 1656 if (mPropertyType.equals(expectedClass)) { 1657 assertWithMessage("getProperty for " + expectedClass + " class should not throw" 1658 + " IllegalArgumentException for valid propertyId: " + mPropertyId 1659 + " areaId: " + areaId + " of type: " + mPropertyType + " if property is" 1660 + " readable").fail(); 1661 } 1662 } else { 1663 handleGetPropertyExceptions(e); 1664 } 1665 } 1666 verifyClassSpecificGetPropertyResults(T value, Class<?> expectedClass, int areaId)1667 private void verifyClassSpecificGetPropertyResults(T value, Class<?> expectedClass, 1668 int areaId) { 1669 if (!mPropertyType.equals(expectedClass)) { 1670 assertWithMessage("getProperty for " + expectedClass + " class should throw" 1671 + " IllegalArgumentException for valid propertyId: " + mPropertyId + " areaId: " 1672 + areaId + " of" + " type: " + mPropertyType + " if property is readable") 1673 .fail(); 1674 } 1675 verifyCarPropertyValue(mPropertyId, areaId, CarPropertyValue.STATUS_AVAILABLE, 1676 SystemClock.elapsedRealtimeNanos(), value, areaId, 1677 CAR_PROPERTY_VALUE_SOURCE_GETTER); 1678 } 1679 verifyCarPropertyValueGetter()1680 private void verifyCarPropertyValueGetter() { 1681 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1682 if (!Flags.areaIdConfigAccess() && carPropertyConfig.getAccess() 1683 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) { 1684 verifyGetPropertyFails(carPropertyConfig.getAreaIds()[0]); 1685 return; 1686 } 1687 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 1688 int areaId = areaIdConfig.getAreaId(); 1689 if (Flags.areaIdConfigAccess() && areaIdConfig.getAccess() 1690 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) { 1691 verifyGetPropertyFails(areaId); 1692 continue; 1693 } 1694 1695 CarPropertyValue<?> carPropertyValue = null; 1696 try { 1697 carPropertyValue = mCarPropertyManager.getProperty(mPropertyId, areaId); 1698 verifyCarPropertyValue(carPropertyValue, areaId, CAR_PROPERTY_VALUE_SOURCE_GETTER); 1699 if (mPropertyId == VehiclePropertyIds.HVAC_TEMPERATURE_VALUE_SUGGESTION) { 1700 verifyHvacTemperatureValueSuggestionResponse( 1701 (Float[]) carPropertyValue.getValue()); 1702 } 1703 } catch (PropertyNotAvailableException | CarInternalErrorException e) { 1704 handleGetPropertyExceptions(e); 1705 } 1706 1707 try { 1708 Boolean value = mCarPropertyManager.getBooleanProperty(mPropertyId, areaId); 1709 verifyClassSpecificGetPropertyResults((T) value, Boolean.class, areaId); 1710 } catch (IllegalArgumentException | PropertyNotAvailableException 1711 | CarInternalErrorException e) { 1712 handleClassSpecificGetPropertyExceptions(e, Boolean.class, areaId); 1713 } 1714 try { 1715 Integer value = mCarPropertyManager.getIntProperty(mPropertyId, areaId); 1716 verifyClassSpecificGetPropertyResults((T) value, Integer.class, areaId); 1717 } catch (IllegalArgumentException | PropertyNotAvailableException 1718 | CarInternalErrorException e) { 1719 handleClassSpecificGetPropertyExceptions(e, Integer.class, areaId); 1720 } 1721 try { 1722 Float value = mCarPropertyManager.getFloatProperty(mPropertyId, areaId); 1723 verifyClassSpecificGetPropertyResults((T) value, Float.class, areaId); 1724 } catch (IllegalArgumentException | PropertyNotAvailableException 1725 | CarInternalErrorException e) { 1726 handleClassSpecificGetPropertyExceptions(e, Float.class, areaId); 1727 } 1728 try { 1729 int[] primitiveArray = mCarPropertyManager.getIntArrayProperty(mPropertyId, areaId); 1730 Integer[] value = new Integer[primitiveArray.length]; 1731 for (int i = 0; i < primitiveArray.length; i++) { 1732 value[i] = (Integer) primitiveArray[i]; 1733 } 1734 verifyClassSpecificGetPropertyResults((T) value, Integer[].class, areaId); 1735 } catch (IllegalArgumentException | PropertyNotAvailableException 1736 | CarInternalErrorException e) { 1737 handleClassSpecificGetPropertyExceptions(e, Integer[].class, areaId); 1738 } 1739 } 1740 } 1741 verifyGetPropertyFails(int areaId)1742 private void verifyGetPropertyFails(int areaId) { 1743 assertGetPropertyThrowsException( 1744 mPropertyName 1745 + " is a write_only property so getProperty should throw an" 1746 + " IllegalArgumentException.", 1747 IllegalArgumentException.class, mPropertyId, areaId); 1748 } 1749 verifyPropertyNotAvailableException(PropertyNotAvailableException e)1750 private static void verifyPropertyNotAvailableException(PropertyNotAvailableException e) { 1751 assertThat(((PropertyNotAvailableException) e).getDetailedErrorCode()) 1752 .isIn(PROPERTY_NOT_AVAILABLE_ERROR_CODES); 1753 int vendorErrorCode = e.getVendorErrorCode(); 1754 assertThat(vendorErrorCode).isAtLeast(VENDOR_ERROR_CODE_MINIMUM_VALUE); 1755 assertThat(vendorErrorCode).isAtMost(VENDOR_ERROR_CODE_MAXIMUM_VALUE); 1756 } 1757 verifyInternalErrorException(CarInternalErrorException e)1758 private static void verifyInternalErrorException(CarInternalErrorException e) { 1759 int vendorErrorCode = e.getVendorErrorCode(); 1760 assertThat(vendorErrorCode).isAtLeast(VENDOR_ERROR_CODE_MINIMUM_VALUE); 1761 assertThat(vendorErrorCode).isAtMost(VENDOR_ERROR_CODE_MAXIMUM_VALUE); 1762 } 1763 verifyCarPropertyValue(CarPropertyValue<?> carPropertyValue, int expectedAreaId, String source)1764 private void verifyCarPropertyValue(CarPropertyValue<?> carPropertyValue, int expectedAreaId, 1765 String source) { 1766 verifyCarPropertyValue(carPropertyValue.getPropertyId(), 1767 carPropertyValue.getAreaId(), carPropertyValue.getStatus(), 1768 carPropertyValue.getTimestamp(), (T) carPropertyValue.getValue(), expectedAreaId, 1769 source); 1770 } 1771 verifyCarPropertyValue( int propertyId, int areaId, int status, long timestampNanos, T value, int expectedAreaId, String source)1772 private void verifyCarPropertyValue( 1773 int propertyId, int areaId, int status, long timestampNanos, T value, 1774 int expectedAreaId, String source) { 1775 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1776 mCarPropertyValueVerifier.ifPresent( 1777 propertyValueVerifier -> propertyValueVerifier.verify(carPropertyConfig, propertyId, 1778 areaId, timestampNanos, value)); 1779 assertWithMessage( 1780 mPropertyName 1781 + " - areaId: " 1782 + areaId 1783 + " - source: " 1784 + source 1785 + " value must have correct property ID") 1786 .that(propertyId) 1787 .isEqualTo(mPropertyId); 1788 assertWithMessage( 1789 mPropertyName 1790 + " - areaId: " 1791 + areaId 1792 + " - source: " 1793 + source 1794 + " value must have correct area id: " 1795 + areaId) 1796 .that(areaId) 1797 .isEqualTo(expectedAreaId); 1798 assertWithMessage(mPropertyName + " - areaId: " + areaId + " - source: " + source 1799 + " area ID must be in carPropertyConfig#getAreaIds()").that(Arrays.stream( 1800 carPropertyConfig.getAreaIds()).boxed().collect(Collectors.toList()).contains( 1801 areaId)).isTrue(); 1802 assertWithMessage( 1803 mPropertyName 1804 + " - areaId: " 1805 + areaId 1806 + " - source: " 1807 + source 1808 + " value must have AVAILABLE status") 1809 .that(status) 1810 .isEqualTo(CarPropertyValue.STATUS_AVAILABLE); 1811 assertWithMessage( 1812 mPropertyName 1813 + " - areaId: " 1814 + areaId 1815 + " - source: " 1816 + source 1817 + " timestamp must use the SystemClock.elapsedRealtimeNanos() time" 1818 + " base") 1819 .that(timestampNanos) 1820 .isAtLeast(0); 1821 assertWithMessage( 1822 mPropertyName 1823 + " - areaId: " 1824 + areaId 1825 + " - source: " 1826 + source 1827 + " timestamp must use the SystemClock.elapsedRealtimeNanos() time" 1828 + " base") 1829 .that(timestampNanos) 1830 .isLessThan(SystemClock.elapsedRealtimeNanos()); 1831 assertWithMessage( 1832 mPropertyName 1833 + " - areaId: " 1834 + areaId 1835 + " - source: " 1836 + source 1837 + " must return " 1838 + mPropertyType 1839 + " type value") 1840 .that(value.getClass()) 1841 .isEqualTo(mPropertyType); 1842 1843 if (mRequirePropertyValueToBeInConfigArray) { 1844 assertWithMessage( 1845 mPropertyName 1846 + " - areaId: " 1847 + areaId 1848 + " - source: " 1849 + source 1850 + " value must be listed in configArray," 1851 + " configArray:") 1852 .that(carPropertyConfig.getConfigArray()) 1853 .contains(value); 1854 } 1855 1856 if (isAtLeastU()) { 1857 List<T> supportedEnumValues = carPropertyConfig.getAreaIdConfig( 1858 areaId).getSupportedEnumValues(); 1859 if (!supportedEnumValues.isEmpty()) { 1860 if (mEnumIsBitMap) { 1861 int allValidValues = 0; 1862 for (T bitEnumValue : supportedEnumValues) { 1863 allValidValues |= ((Integer) bitEnumValue).intValue(); 1864 } 1865 assertWithMessage(mPropertyName + " - areaId: " + areaId + " - source: " 1866 + source + " value must be a combination of values listed in " 1867 + "getSupportedEnumValues()") 1868 .that(((Integer) value).intValue() & allValidValues).isEqualTo(value); 1869 } else { 1870 assertWithMessage(mPropertyName + " - areaId: " + areaId + " - source: " 1871 + source + " value must be listed in getSupportedEnumValues()").that( 1872 value).isIn(supportedEnumValues); 1873 } 1874 } 1875 } 1876 1877 T areaIdMinValue = (T) carPropertyConfig.getMinValue(areaId); 1878 T areaIdMaxValue = (T) carPropertyConfig.getMaxValue(areaId); 1879 if (areaIdMinValue != null && areaIdMaxValue != null) { 1880 assertWithMessage( 1881 "Property value: " + value + " must be between the max: " 1882 + areaIdMaxValue + " and min: " + areaIdMinValue 1883 + " values for area ID: " + Integer.toHexString(areaId)).that( 1884 verifyValueInRange( 1885 areaIdMinValue, 1886 areaIdMaxValue, 1887 (T) value)) 1888 .isTrue(); 1889 } 1890 1891 if (mVerifyErrorStates) { 1892 assertWithMessage( 1893 "When ADAS feature is enabled, " 1894 + VehiclePropertyIds.toString(mPropertyId) 1895 + " must not be set to " + ErrorState.NOT_AVAILABLE_DISABLED 1896 + " (ErrorState#NOT_AVAILABLE_DISABLED") 1897 .that((Integer) value) 1898 .isNotEqualTo(ErrorState.NOT_AVAILABLE_DISABLED); 1899 } 1900 } 1901 verifyValueInRange(T min, T max, T value)1902 private boolean verifyValueInRange(T min, T max, T value) { 1903 int propertyType = mPropertyId & VehiclePropertyType.MASK; 1904 switch (propertyType) { 1905 case VehiclePropertyType.INT32: 1906 return ((Integer) value >= (Integer) min && (Integer) value <= (Integer) max); 1907 case VehiclePropertyType.INT64: 1908 return ((Long) value >= (Long) min && (Long) value <= (Long) max); 1909 case VehiclePropertyType.FLOAT: 1910 return ((Float) value >= (Float) min && (Float) value <= (Float) max); 1911 default: 1912 return false; 1913 } 1914 } 1915 generateAllPossibleAreaIds(ImmutableSet<Integer> areas)1916 private static ImmutableSet<Integer> generateAllPossibleAreaIds(ImmutableSet<Integer> areas) { 1917 ImmutableSet.Builder<Integer> allPossibleAreaIdsBuilder = ImmutableSet.builder(); 1918 for (int i = 1; i <= areas.size(); i++) { 1919 allPossibleAreaIdsBuilder.addAll(Sets.combinations(areas, i).stream().map(areaCombo -> { 1920 Integer possibleAreaId = 0; 1921 for (Integer area : areaCombo) { 1922 possibleAreaId |= area; 1923 } 1924 return possibleAreaId; 1925 }).collect(Collectors.toList())); 1926 } 1927 return allPossibleAreaIdsBuilder.build(); 1928 } 1929 verifyValidAreaIdsForAreaType(ImmutableSet<Integer> allPossibleAreaIds)1930 private void verifyValidAreaIdsForAreaType(ImmutableSet<Integer> allPossibleAreaIds) { 1931 for (int areaId : getCarPropertyConfig().getAreaIds()) { 1932 assertWithMessage( 1933 mPropertyName + "'s area ID must be a valid " + areaTypeToString(mAreaType) 1934 + " area ID").that(areaId).isIn(allPossibleAreaIds); 1935 } 1936 } 1937 verifyNoAreaOverlapInAreaIds(ImmutableSet<Integer> areas)1938 private void verifyNoAreaOverlapInAreaIds(ImmutableSet<Integer> areas) { 1939 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1940 if (carPropertyConfig.getAreaIds().length < 2) { 1941 return; 1942 } 1943 ImmutableSet<Integer> areaIds = ImmutableSet.copyOf(Arrays.stream( 1944 carPropertyConfig.getAreaIds()).boxed().collect(Collectors.toList())); 1945 List<Integer> areaIdOverlapCheckResults = Sets.combinations(areaIds, 2).stream().map( 1946 areaIdPair -> { 1947 List<Integer> areaIdPairAsList = areaIdPair.stream().collect( 1948 Collectors.toList()); 1949 return areaIdPairAsList.get(0) & areaIdPairAsList.get(1); 1950 }).collect(Collectors.toList()); 1951 1952 assertWithMessage( 1953 mPropertyName + " area IDs: " + Arrays.toString(carPropertyConfig.getAreaIds()) 1954 + " must contain each area only once (e.g. no bitwise AND overlap) for " 1955 + "the area type: " + areaTypeToString(mAreaType)).that( 1956 Collections.frequency(areaIdOverlapCheckResults, 0) 1957 == areaIdOverlapCheckResults.size()).isTrue(); 1958 } 1959 verifyPermissionNotGrantedException()1960 private void verifyPermissionNotGrantedException() { 1961 // If the client itself already has read/write permissions without adopting any permissions 1962 // from the shell, skip the test. 1963 if (hasReadPermissions(mReadPermissions) || hasWritePermissions(mWritePermissions)) { 1964 return; 1965 } 1966 1967 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 1968 assertWithMessage( 1969 mPropertyName 1970 + " - property ID: " 1971 + mPropertyId 1972 + " CarPropertyConfig should not be accessible without permissions.") 1973 .that(mCarPropertyManager.getCarPropertyConfig(mPropertyId)) 1974 .isNull(); 1975 1976 int access = carPropertyConfig.getAccess(); 1977 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 1978 if (Flags.areaIdConfigAccess()) { 1979 access = areaIdConfig.getAccess(); 1980 } 1981 1982 int areaId = areaIdConfig.getAreaId(); 1983 if (access == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ 1984 || access == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) { 1985 assertGetPropertyThrowsException( 1986 mPropertyName 1987 + " - property ID: " 1988 + mPropertyId 1989 + " - area ID: " 1990 + areaId 1991 + " should not be able to be read without permissions.", 1992 SecurityException.class, mPropertyId, areaId); 1993 } 1994 if (access == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE 1995 || access == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) { 1996 assertThrows( 1997 mPropertyName 1998 + " - property ID: " 1999 + mPropertyId 2000 + " - area ID: " 2001 + areaId 2002 + " should not be able to be written to without permissions.", 2003 SecurityException.class, 2004 () -> mCarPropertyManager.setProperty(mPropertyType, mPropertyId, areaId, 2005 getDefaultValue(mPropertyType))); 2006 } 2007 } 2008 2009 if (access == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) { 2010 return; 2011 } 2012 2013 int updatesPerAreaId = getUpdatesPerAreaId(mChangeMode); 2014 long timeoutMillis = getRegisterCallbackTimeoutMillis(mChangeMode, 2015 carPropertyConfig.getMinSampleRate()); 2016 CarPropertyValueCallback carPropertyValueCallback = new CarPropertyValueCallback( 2017 mPropertyName, carPropertyConfig.getAreaIds(), updatesPerAreaId, timeoutMillis); 2018 2019 // We expect a return value of false and not a SecurityException thrown. 2020 // This is because registerCallback first tries to get the CarPropertyConfig for the 2021 // property, but since no permissions have been granted it can't find the CarPropertyConfig, 2022 // so it immediately returns false. 2023 assertWithMessage( 2024 mPropertyName 2025 + " - property ID: " 2026 + mPropertyId 2027 + " should not be able to be listened to without permissions.") 2028 .that( 2029 mCarPropertyManager.registerCallback(carPropertyValueCallback, mPropertyId, 2030 carPropertyConfig.getMaxSampleRate())) 2031 .isFalse(); 2032 } 2033 verifyHvacTemperatureValueSuggestionResponse(Float[] temperatureSuggestion)2034 private void verifyHvacTemperatureValueSuggestionResponse(Float[] temperatureSuggestion) { 2035 Float suggestedTempInCelsius = temperatureSuggestion[2]; 2036 Float suggestedTempInFahrenheit = temperatureSuggestion[3]; 2037 CarPropertyConfig<?> hvacTemperatureSetCarPropertyConfig = 2038 mCarPropertyManager.getCarPropertyConfig(VehiclePropertyIds.HVAC_TEMPERATURE_SET); 2039 if (hvacTemperatureSetCarPropertyConfig == null) { 2040 return; 2041 } 2042 List<Integer> hvacTemperatureSetConfigArray = 2043 hvacTemperatureSetCarPropertyConfig.getConfigArray(); 2044 if (hvacTemperatureSetConfigArray.isEmpty()) { 2045 return; 2046 } 2047 Integer minTempInCelsiusTimesTen = 2048 hvacTemperatureSetConfigArray.get(0); 2049 Integer maxTempInCelsiusTimesTen = 2050 hvacTemperatureSetConfigArray.get(1); 2051 Integer incrementInCelsiusTimesTen = 2052 hvacTemperatureSetConfigArray.get(2); 2053 verifyHvacTemperatureIsValid(suggestedTempInCelsius, minTempInCelsiusTimesTen, 2054 maxTempInCelsiusTimesTen, incrementInCelsiusTimesTen); 2055 2056 Integer minTempInFahrenheitTimesTen = 2057 hvacTemperatureSetConfigArray.get(3); 2058 Integer maxTempInFahrenheitTimesTen = 2059 hvacTemperatureSetConfigArray.get(4); 2060 Integer incrementInFahrenheitTimesTen = 2061 hvacTemperatureSetConfigArray.get(5); 2062 verifyHvacTemperatureIsValid(suggestedTempInFahrenheit, minTempInFahrenheitTimesTen, 2063 maxTempInFahrenheitTimesTen, incrementInFahrenheitTimesTen); 2064 2065 int suggestedTempInCelsiusTimesTen = (int) (suggestedTempInCelsius * 10f); 2066 int suggestedTempInFahrenheitTimesTen = (int) (suggestedTempInFahrenheit * 10f); 2067 int numIncrementsCelsius = 2068 Math.round((suggestedTempInCelsiusTimesTen - minTempInCelsiusTimesTen) 2069 / incrementInCelsiusTimesTen.floatValue()); 2070 int numIncrementsFahrenheit = 2071 Math.round((suggestedTempInFahrenheitTimesTen - minTempInFahrenheitTimesTen) 2072 / incrementInFahrenheitTimesTen.floatValue()); 2073 assertWithMessage( 2074 "The temperature in celsius must map to the same temperature in fahrenheit" 2075 + " using the HVAC_TEMPERATURE_SET config array: " 2076 + hvacTemperatureSetConfigArray) 2077 .that(numIncrementsFahrenheit) 2078 .isEqualTo(numIncrementsCelsius); 2079 } 2080 2081 /** 2082 * Verifies that hvac temperature is valid. 2083 */ verifyHvacTemperatureIsValid(float temp, int minTempTimesTen, int maxTempTimesTen, int incrementTimesTen)2084 public static void verifyHvacTemperatureIsValid(float temp, int minTempTimesTen, 2085 int maxTempTimesTen, int incrementTimesTen) { 2086 int intTempTimesTen = (int) (temp * 10f); 2087 assertWithMessage( 2088 "The temperature value " + intTempTimesTen + " must be at least " 2089 + minTempTimesTen + " and at most " + maxTempTimesTen) 2090 .that(intTempTimesTen >= minTempTimesTen && intTempTimesTen <= maxTempTimesTen) 2091 .isTrue(); 2092 2093 int remainder = (intTempTimesTen - minTempTimesTen) % incrementTimesTen; 2094 assertWithMessage( 2095 "The temperature value " + intTempTimesTen 2096 + " is not a valid temperature value. Valid values start from " 2097 + minTempTimesTen 2098 + " and increment by " + incrementTimesTen 2099 + " until the max temperature setting of " + maxTempTimesTen) 2100 .that(remainder) 2101 .isEqualTo(0); 2102 } 2103 2104 /** 2105 * An interface for verifying the config array. 2106 */ 2107 public interface ConfigArrayVerifier { 2108 /** 2109 * Verifies the config array. Throws exception if not valid. 2110 */ verify(List<Integer> configArray)2111 void verify(List<Integer> configArray); 2112 } 2113 2114 /** 2115 * An interface for verifying the property value. 2116 */ 2117 public interface CarPropertyValueVerifier<T> { 2118 /** 2119 * Verifies the property value. Throws exception if not valid. 2120 */ verify(CarPropertyConfig<T> carPropertyConfig, int propertyId, int areaId, long timestampNanos, T value)2121 void verify(CarPropertyConfig<T> carPropertyConfig, int propertyId, int areaId, 2122 long timestampNanos, T value); 2123 } 2124 2125 /** 2126 * An interface for verifying the areaIds. 2127 */ 2128 public interface AreaIdsVerifier { 2129 /** 2130 * Verifies the areaIds. Throws exception if not valid. 2131 */ verify(int[] areaIds)2132 void verify(int[] areaIds); 2133 } 2134 2135 /** 2136 * An interface for verifying the {@link CarPropertyConfig}. 2137 */ 2138 public interface CarPropertyConfigVerifier { 2139 /** 2140 * Verifies the property config. Throws exception if not valid. 2141 */ verify(CarPropertyConfig<?> carPropertyConfig)2142 void verify(CarPropertyConfig<?> carPropertyConfig); 2143 } 2144 2145 /** 2146 * The builder class. 2147 */ 2148 public static class Builder<T> { 2149 private final int mPropertyId; 2150 private final int mAccess; 2151 private final int mAreaType; 2152 private final int mChangeMode; 2153 private final Class<T> mPropertyType; 2154 private final CarPropertyManager mCarPropertyManager; 2155 private boolean mRequiredProperty = false; 2156 private Optional<ConfigArrayVerifier> mConfigArrayVerifier = Optional.empty(); 2157 private Optional<CarPropertyValueVerifier<T>> mCarPropertyValueVerifier = Optional.empty(); 2158 private Optional<AreaIdsVerifier> mAreaIdsVerifier = Optional.empty(); 2159 private Optional<CarPropertyConfigVerifier> mCarPropertyConfigVerifier = Optional.empty(); 2160 private Optional<Integer> mDependentOnPropertyId = Optional.empty(); 2161 private ImmutableSet<String> mDependentOnPropertyPermissions = ImmutableSet.of(); 2162 private ImmutableSet<Integer> mPossibleConfigArrayValues = ImmutableSet.of(); 2163 private boolean mEnumIsBitMap = false; 2164 private ImmutableSet<T> mAllPossibleEnumValues = ImmutableSet.of(); 2165 private ImmutableSet<T> mAllPossibleUnwritableValues = ImmutableSet.of(); 2166 private ImmutableSet<T> mAllPossibleUnavailableValues = ImmutableSet.of(); 2167 private boolean mRequirePropertyValueToBeInConfigArray = false; 2168 private boolean mVerifySetterWithConfigArrayValues = false; 2169 private boolean mRequireMinMaxValues = false; 2170 private boolean mRequireMinValuesToBeZero = false; 2171 private boolean mRequireZeroToBeContainedInMinMaxRanges = false; 2172 private boolean mPossiblyDependentOnHvacPowerOn = false; 2173 private boolean mVerifyErrorStates = false; 2174 private final ImmutableSet.Builder<String> mReadPermissionsBuilder = ImmutableSet.builder(); 2175 private final ImmutableList.Builder<ImmutableSet<String>> mWritePermissionsBuilder = 2176 ImmutableList.builder(); 2177 Builder(int propertyId, int access, int areaType, int changeMode, Class<T> propertyType, CarPropertyManager carPropertyManager)2178 private Builder(int propertyId, int access, int areaType, int changeMode, 2179 Class<T> propertyType, CarPropertyManager carPropertyManager) { 2180 mPropertyId = propertyId; 2181 mAccess = access; 2182 mAreaType = areaType; 2183 mChangeMode = changeMode; 2184 mPropertyType = propertyType; 2185 mCarPropertyManager = carPropertyManager; 2186 } 2187 2188 /** 2189 * Sets the property as required. Test will fail if the property is not supported. 2190 */ requireProperty()2191 public Builder<T> requireProperty() { 2192 mRequiredProperty = true; 2193 return this; 2194 } 2195 2196 /** 2197 * Sets the config array verifier. 2198 */ setConfigArrayVerifier(ConfigArrayVerifier configArrayVerifier)2199 public Builder<T> setConfigArrayVerifier(ConfigArrayVerifier configArrayVerifier) { 2200 mConfigArrayVerifier = Optional.of(configArrayVerifier); 2201 return this; 2202 } 2203 2204 /** 2205 * Sets the car property value verifier. 2206 */ setCarPropertyValueVerifier( CarPropertyValueVerifier<T> carPropertyValueVerifier)2207 public Builder<T> setCarPropertyValueVerifier( 2208 CarPropertyValueVerifier<T> carPropertyValueVerifier) { 2209 mCarPropertyValueVerifier = Optional.of(carPropertyValueVerifier); 2210 return this; 2211 } 2212 2213 /** 2214 * Sets the areaIds verifier. 2215 */ setAreaIdsVerifier(AreaIdsVerifier areaIdsVerifier)2216 public Builder<T> setAreaIdsVerifier(AreaIdsVerifier areaIdsVerifier) { 2217 mAreaIdsVerifier = Optional.of(areaIdsVerifier); 2218 return this; 2219 } 2220 2221 /** 2222 * Sets the car property config verifier. 2223 */ setCarPropertyConfigVerifier( CarPropertyConfigVerifier carPropertyConfigVerifier)2224 public Builder<T> setCarPropertyConfigVerifier( 2225 CarPropertyConfigVerifier carPropertyConfigVerifier) { 2226 mCarPropertyConfigVerifier = Optional.of(carPropertyConfigVerifier); 2227 return this; 2228 } 2229 2230 /** 2231 * Sets that the property is depending on other properties. 2232 */ setDependentOnProperty(Integer dependentPropertyId, ImmutableSet<String> dependentPropertyPermissions)2233 public Builder<T> setDependentOnProperty(Integer dependentPropertyId, 2234 ImmutableSet<String> dependentPropertyPermissions) { 2235 mDependentOnPropertyId = Optional.of(dependentPropertyId); 2236 mDependentOnPropertyPermissions = dependentPropertyPermissions; 2237 return this; 2238 } 2239 2240 /** 2241 * Sets the possible config array values. 2242 */ setPossibleConfigArrayValues( ImmutableSet<Integer> possibleConfigArrayValues)2243 public Builder<T> setPossibleConfigArrayValues( 2244 ImmutableSet<Integer> possibleConfigArrayValues) { 2245 mPossibleConfigArrayValues = possibleConfigArrayValues; 2246 return this; 2247 } 2248 2249 /** 2250 * Used to assert that supportedEnum values provided in config are a subset of all possible 2251 * enum values that can be set for the property. 2252 */ setBitMapEnumEnabled(boolean enabled)2253 public Builder<T> setBitMapEnumEnabled(boolean enabled) { 2254 mEnumIsBitMap = enabled; 2255 return this; 2256 } 2257 2258 /* 2259 * Used to assert that supportedEnum values provided in config are a subset of all possible 2260 * enum values that can be set for the property. If enums is defined as a bit map rather 2261 * than a regular integer, setBitMapEnumEnabled(boolean) should be used as well. 2262 */ setAllPossibleEnumValues(ImmutableSet<T> allPossibleEnumValues)2263 public Builder<T> setAllPossibleEnumValues(ImmutableSet<T> allPossibleEnumValues) { 2264 mAllPossibleEnumValues = allPossibleEnumValues; 2265 return this; 2266 } 2267 2268 /** 2269 * Used to assert that certain values that must not be allowed to be written will throw an 2270 * IllegalArgumentException when we try to write them using setProperty. 2271 */ setAllPossibleUnwritableValues( ImmutableSet<T> allPossibleUnwritableValues)2272 public Builder<T> setAllPossibleUnwritableValues( 2273 ImmutableSet<T> allPossibleUnwritableValues) { 2274 mAllPossibleUnwritableValues = allPossibleUnwritableValues; 2275 return this; 2276 } 2277 2278 /** 2279 * Used to assert that certain values that are temporarily unavailable to be written will 2280 * throw a PropertyNotAvailableException when we try to write them using setProperty. 2281 */ setAllPossibleUnavailableValues( ImmutableSet<T> allPossibleUnavailableValues)2282 public Builder<T> setAllPossibleUnavailableValues( 2283 ImmutableSet<T> allPossibleUnavailableValues) { 2284 mAllPossibleUnavailableValues = allPossibleUnavailableValues; 2285 return this; 2286 } 2287 2288 /** 2289 * Requires that the property value must be one of the value defined in the config array. 2290 */ requirePropertyValueTobeInConfigArray()2291 public Builder<T> requirePropertyValueTobeInConfigArray() { 2292 mRequirePropertyValueToBeInConfigArray = true; 2293 return this; 2294 } 2295 2296 /** 2297 * Uses the config array values to set the property value. 2298 */ verifySetterWithConfigArrayValues()2299 public Builder<T> verifySetterWithConfigArrayValues() { 2300 mVerifySetterWithConfigArrayValues = true; 2301 return this; 2302 } 2303 2304 /** 2305 * Requires minValue and maxValue to be set. 2306 */ requireMinMaxValues()2307 public Builder<T> requireMinMaxValues() { 2308 mRequireMinMaxValues = true; 2309 return this; 2310 } 2311 2312 /** 2313 * Requires minValue to be 0. 2314 */ requireMinValuesToBeZero()2315 public Builder<T> requireMinValuesToBeZero() { 2316 mRequireMinValuesToBeZero = true; 2317 return this; 2318 } 2319 2320 /** 2321 * Requires 0 to be contains within minValue and maxValue. 2322 */ requireZeroToBeContainedInMinMaxRanges()2323 public Builder<T> requireZeroToBeContainedInMinMaxRanges() { 2324 mRequireZeroToBeContainedInMinMaxRanges = true; 2325 return this; 2326 } 2327 2328 /** 2329 * Sets that the property might depend on HVAC_POEWR_ON. 2330 */ setPossiblyDependentOnHvacPowerOn()2331 public Builder<T> setPossiblyDependentOnHvacPowerOn() { 2332 mPossiblyDependentOnHvacPowerOn = true; 2333 return this; 2334 } 2335 2336 /** 2337 * Verifies if returning error state, the error state is expected. 2338 */ verifyErrorStates()2339 public Builder<T> verifyErrorStates() { 2340 mVerifyErrorStates = true; 2341 return this; 2342 } 2343 2344 /** 2345 * Adds the required read permission. 2346 */ addReadPermission(String readPermission)2347 public Builder<T> addReadPermission(String readPermission) { 2348 mReadPermissionsBuilder.add(readPermission); 2349 return this; 2350 } 2351 2352 /** 2353 * Adds a single permission that alone can be used to update the property. Any set of 2354 * permissions in {@code mWritePermissionsBuilder} can be used to set the property. 2355 * 2356 * @param writePermission a permission used to update the property 2357 */ addWritePermission(String writePermission)2358 public Builder<T> addWritePermission(String writePermission) { 2359 mWritePermissionsBuilder.add(ImmutableSet.of(writePermission)); 2360 return this; 2361 } 2362 2363 /** 2364 * Adds a set of permissions that together can be used to update the property. Any set of 2365 * permissions in {@code mWritePermissionsBuilder} can be used to set the property. 2366 * 2367 * @param writePermissionSet a set of permissions that together can be used to update the 2368 * property. 2369 */ addWritePermission(ImmutableSet<String> writePermissionSet)2370 public Builder<T> addWritePermission(ImmutableSet<String> writePermissionSet) { 2371 mWritePermissionsBuilder.add(writePermissionSet); 2372 return this; 2373 } 2374 2375 /** 2376 * Builds the verifier. 2377 */ build()2378 public VehiclePropertyVerifier<T> build() { 2379 return new VehiclePropertyVerifier<>( 2380 mCarPropertyManager, 2381 mPropertyId, 2382 mAccess, 2383 mAreaType, 2384 mChangeMode, 2385 mPropertyType, 2386 mRequiredProperty, 2387 mConfigArrayVerifier, 2388 mCarPropertyValueVerifier, 2389 mAreaIdsVerifier, 2390 mCarPropertyConfigVerifier, 2391 mDependentOnPropertyId, 2392 mDependentOnPropertyPermissions, 2393 mPossibleConfigArrayValues, 2394 mEnumIsBitMap, 2395 mAllPossibleEnumValues, 2396 mAllPossibleUnwritableValues, 2397 mAllPossibleUnavailableValues, 2398 mRequirePropertyValueToBeInConfigArray, 2399 mVerifySetterWithConfigArrayValues, 2400 mRequireMinMaxValues, 2401 mRequireMinValuesToBeZero, 2402 mRequireZeroToBeContainedInMinMaxRanges, 2403 mPossiblyDependentOnHvacPowerOn, 2404 mVerifyErrorStates, 2405 mReadPermissionsBuilder.build(), 2406 mWritePermissionsBuilder.build()); 2407 } 2408 } 2409 2410 private static class CarPropertyValueCallback implements 2411 CarPropertyManager.CarPropertyEventCallback { 2412 private final String mPropertyName; 2413 private final int[] mAreaIds; 2414 private final int mTotalCarPropertyValuesPerAreaId; 2415 private final CountDownLatch mCountDownLatch = new CountDownLatch(1); 2416 private final Object mLock = new Object(); 2417 @GuardedBy("mLock") 2418 private final SparseArray<List<CarPropertyValue<?>>> mAreaIdToCarPropertyValues = 2419 new SparseArray<>(); 2420 private final long mTimeoutMillis; 2421 CarPropertyValueCallback(String propertyName, int[] areaIds, int totalCarPropertyValuesPerAreaId, long timeoutMillis)2422 CarPropertyValueCallback(String propertyName, int[] areaIds, 2423 int totalCarPropertyValuesPerAreaId, long timeoutMillis) { 2424 mPropertyName = propertyName; 2425 mAreaIds = areaIds; 2426 mTotalCarPropertyValuesPerAreaId = totalCarPropertyValuesPerAreaId; 2427 mTimeoutMillis = timeoutMillis; 2428 synchronized (mLock) { 2429 for (int areaId : mAreaIds) { 2430 mAreaIdToCarPropertyValues.put(areaId, new ArrayList<>()); 2431 } 2432 } 2433 } 2434 getAreaIdToCarPropertyValues()2435 public SparseArray<List<CarPropertyValue<?>>> getAreaIdToCarPropertyValues() { 2436 boolean awaitSuccess = false; 2437 try { 2438 awaitSuccess = mCountDownLatch.await(mTimeoutMillis, TimeUnit.MILLISECONDS); 2439 } catch (InterruptedException e) { 2440 assertWithMessage("Waiting for onChangeEvent callback(s) for " + mPropertyName 2441 + " threw an exception: " + e).fail(); 2442 } 2443 synchronized (mLock) { 2444 assertWithMessage("Never received " + mTotalCarPropertyValuesPerAreaId 2445 + " CarPropertyValues for all " + mPropertyName + "'s areaIds: " 2446 + Arrays.toString(mAreaIds) + " before " + mTimeoutMillis + " ms timeout - " 2447 + mAreaIdToCarPropertyValues).that(awaitSuccess).isTrue(); 2448 return mAreaIdToCarPropertyValues.clone(); 2449 } 2450 } 2451 2452 @Override onChangeEvent(CarPropertyValue carPropertyValue)2453 public void onChangeEvent(CarPropertyValue carPropertyValue) { 2454 synchronized (mLock) { 2455 if (hasEnoughCarPropertyValuesForEachAreaIdLocked()) { 2456 return; 2457 } 2458 mAreaIdToCarPropertyValues.get(carPropertyValue.getAreaId()).add(carPropertyValue); 2459 if (hasEnoughCarPropertyValuesForEachAreaIdLocked()) { 2460 mCountDownLatch.countDown(); 2461 } 2462 } 2463 } 2464 2465 @GuardedBy("mLock") hasEnoughCarPropertyValuesForEachAreaIdLocked()2466 private boolean hasEnoughCarPropertyValuesForEachAreaIdLocked() { 2467 for (int areaId : mAreaIds) { 2468 List<CarPropertyValue<?>> carPropertyValues = mAreaIdToCarPropertyValues.get( 2469 areaId); 2470 if (carPropertyValues == null 2471 || carPropertyValues.size() < mTotalCarPropertyValuesPerAreaId) { 2472 return false; 2473 } 2474 } 2475 return true; 2476 } 2477 2478 @Override onErrorEvent(int propId, int zone)2479 public void onErrorEvent(int propId, int zone) { 2480 } 2481 2482 @Override onErrorEvent(int propId, int areaId, int errorCode)2483 public void onErrorEvent(int propId, int areaId, int errorCode) { 2484 } 2485 } 2486 2487 2488 private static class SetterCallback<T> implements CarPropertyManager.CarPropertyEventCallback { 2489 private final int mPropertyId; 2490 private final String mPropertyName; 2491 private final int mAreaId; 2492 private final T mExpectedSetValue; 2493 private final CountDownLatch mCountDownLatch = new CountDownLatch(1); 2494 private final long mCreationTimeNanos = SystemClock.elapsedRealtimeNanos(); 2495 private CarPropertyValue<?> mUpdatedCarPropertyValue = null; 2496 private T mReceivedValue = null; 2497 SetterCallback(int propertyId, int areaId, T expectedSetValue)2498 SetterCallback(int propertyId, int areaId, T expectedSetValue) { 2499 mPropertyId = propertyId; 2500 mPropertyName = VehiclePropertyIds.toString(propertyId); 2501 mAreaId = areaId; 2502 mExpectedSetValue = expectedSetValue; 2503 } 2504 valueToString(T value)2505 private String valueToString(T value) { 2506 if (value.getClass().isArray()) { 2507 return Arrays.toString((Object[]) value); 2508 } 2509 return value.toString(); 2510 } 2511 waitForUpdatedCarPropertyValue()2512 public CarPropertyValue<?> waitForUpdatedCarPropertyValue() { 2513 try { 2514 assertWithMessage( 2515 "Never received onChangeEvent(s) for " + mPropertyName + " new value: " 2516 + valueToString(mExpectedSetValue) + " before 5s timeout." 2517 + " Received: " 2518 + (mReceivedValue == null 2519 ? "No value" 2520 : valueToString(mReceivedValue))) 2521 .that(mCountDownLatch.await(5, TimeUnit.SECONDS)).isTrue(); 2522 } catch (InterruptedException e) { 2523 assertWithMessage("Waiting for onChangeEvent set callback for " 2524 + mPropertyName + " threw an exception: " + e).fail(); 2525 } 2526 return mUpdatedCarPropertyValue; 2527 } 2528 2529 @Override onChangeEvent(CarPropertyValue carPropertyValue)2530 public void onChangeEvent(CarPropertyValue carPropertyValue) { 2531 if (mUpdatedCarPropertyValue != null || carPropertyValue.getPropertyId() != mPropertyId 2532 || carPropertyValue.getAreaId() != mAreaId 2533 || carPropertyValue.getTimestamp() <= mCreationTimeNanos 2534 || carPropertyValue.getTimestamp() >= SystemClock.elapsedRealtimeNanos()) { 2535 return; 2536 } 2537 mReceivedValue = (T) carPropertyValue.getValue(); 2538 if (carPropertyValue.getStatus() == CarPropertyValue.STATUS_AVAILABLE 2539 && !valueEquals(mExpectedSetValue, mReceivedValue)) { 2540 return; 2541 } 2542 mUpdatedCarPropertyValue = carPropertyValue; 2543 mCountDownLatch.countDown(); 2544 } 2545 2546 @Override onErrorEvent(int propId, int zone)2547 public void onErrorEvent(int propId, int zone) { 2548 } 2549 } 2550 valueEquals(V v1, V v2)2551 private static <V> boolean valueEquals(V v1, V v2) { 2552 return (v1 instanceof Float && floatEquals((Float) v1, (Float) v2)) 2553 || (v1 instanceof Float[] && floatArrayEquals((Float[]) v1, (Float[]) v2)) 2554 || (v1 instanceof Long[] && longArrayEquals((Long[]) v1, (Long[]) v2)) 2555 || (v1 instanceof Integer[] && integerArrayEquals((Integer[]) v1, (Integer[]) v2)) 2556 || v1.equals(v2); 2557 } 2558 floatEquals(float f1, float f2)2559 private static boolean floatEquals(float f1, float f2) { 2560 return Math.abs(f1 - f2) < FLOAT_INEQUALITY_THRESHOLD; 2561 } 2562 floatArrayEquals(Float[] f1, Float[] f2)2563 private static boolean floatArrayEquals(Float[] f1, Float[] f2) { 2564 return Arrays.equals(f1, f2); 2565 } 2566 longArrayEquals(Long[] l1, Long[] l2)2567 private static boolean longArrayEquals(Long[] l1, Long[] l2) { 2568 return Arrays.equals(l1, l2); 2569 } 2570 integerArrayEquals(Integer[] i1, Integer[] i2)2571 private static boolean integerArrayEquals(Integer[] i1, Integer[] i2) { 2572 return Arrays.equals(i1, i2); 2573 } 2574 2575 private class TestGetPropertyCallback implements GetPropertyCallback { 2576 private final CountDownLatch mCountDownLatch; 2577 private final int mGetPropertyResultsCount; 2578 private final Object mLock = new Object(); 2579 @GuardedBy("mLock") 2580 private final List<GetPropertyResult<?>> mGetPropertyResults = new ArrayList<>(); 2581 @GuardedBy("mLock") 2582 private final List<PropertyAsyncError> mPropertyAsyncErrors = new ArrayList<>(); 2583 waitForResults()2584 public void waitForResults() { 2585 try { 2586 assertWithMessage("Received " + (mGetPropertyResultsCount 2587 - mCountDownLatch.getCount()) + " onSuccess(s), expected " 2588 + mGetPropertyResultsCount + " onSuccess(s)").that(mCountDownLatch.await( 2589 5, TimeUnit.SECONDS)).isTrue(); 2590 } catch (InterruptedException e) { 2591 assertWithMessage("Waiting for onSuccess threw an exception: " + e).fail(); 2592 } 2593 } getGetPropertyResults()2594 public List<GetPropertyResult<?>> getGetPropertyResults() { 2595 synchronized (mLock) { 2596 return mGetPropertyResults; 2597 } 2598 } 2599 getPropertyAsyncErrors()2600 public List<PropertyAsyncError> getPropertyAsyncErrors() { 2601 synchronized (mLock) { 2602 return mPropertyAsyncErrors; 2603 } 2604 } 2605 2606 @Override onSuccess(GetPropertyResult getPropertyResult)2607 public void onSuccess(GetPropertyResult getPropertyResult) { 2608 synchronized (mLock) { 2609 mGetPropertyResults.add(getPropertyResult); 2610 mCountDownLatch.countDown(); 2611 } 2612 } 2613 2614 @Override onFailure(PropertyAsyncError propertyAsyncError)2615 public void onFailure(PropertyAsyncError propertyAsyncError) { 2616 synchronized (mLock) { 2617 mPropertyAsyncErrors.add(propertyAsyncError); 2618 mCountDownLatch.countDown(); 2619 } 2620 } 2621 TestGetPropertyCallback(int getPropertyResultsCount)2622 TestGetPropertyCallback(int getPropertyResultsCount) { 2623 mCountDownLatch = new CountDownLatch(getPropertyResultsCount); 2624 mGetPropertyResultsCount = getPropertyResultsCount; 2625 } 2626 } 2627 2628 private class TestSetPropertyCallback implements SetPropertyCallback { 2629 private final CountDownLatch mCountDownLatch; 2630 private final int mSetPropertyResultsCount; 2631 private final Object mLock = new Object(); 2632 @GuardedBy("mLock") 2633 private final List<SetPropertyResult> mSetPropertyResults = new ArrayList<>(); 2634 @GuardedBy("mLock") 2635 private final List<PropertyAsyncError> mPropertyAsyncErrors = new ArrayList<>(); 2636 waitForResults()2637 public void waitForResults() { 2638 try { 2639 assertWithMessage("Received " + (mSetPropertyResultsCount 2640 - mCountDownLatch.getCount()) + " onSuccess(s), expected " 2641 + mSetPropertyResultsCount + " onSuccess(s)").that(mCountDownLatch.await( 2642 5, TimeUnit.SECONDS)).isTrue(); 2643 } catch (InterruptedException e) { 2644 assertWithMessage("Waiting for onSuccess threw an exception: " + e 2645 ).fail(); 2646 } 2647 } getSetPropertyResults()2648 public List<SetPropertyResult> getSetPropertyResults() { 2649 synchronized (mLock) { 2650 return mSetPropertyResults; 2651 } 2652 } 2653 getPropertyAsyncErrors()2654 public List<PropertyAsyncError> getPropertyAsyncErrors() { 2655 synchronized (mLock) { 2656 return mPropertyAsyncErrors; 2657 } 2658 } 2659 2660 @Override onSuccess(SetPropertyResult setPropertyResult)2661 public void onSuccess(SetPropertyResult setPropertyResult) { 2662 synchronized (mLock) { 2663 mSetPropertyResults.add(setPropertyResult); 2664 mCountDownLatch.countDown(); 2665 } 2666 } 2667 2668 @Override onFailure(PropertyAsyncError propertyAsyncError)2669 public void onFailure(PropertyAsyncError propertyAsyncError) { 2670 synchronized (mLock) { 2671 mPropertyAsyncErrors.add(propertyAsyncError); 2672 mCountDownLatch.countDown(); 2673 } 2674 } 2675 TestSetPropertyCallback(int setPropertyResultsCount)2676 TestSetPropertyCallback(int setPropertyResultsCount) { 2677 mCountDownLatch = new CountDownLatch(setPropertyResultsCount); 2678 mSetPropertyResultsCount = setPropertyResultsCount; 2679 } 2680 } 2681 verifyGetPropertiesAsync()2682 private void verifyGetPropertiesAsync() { 2683 if (!isAtLeastU()) { 2684 return; 2685 } 2686 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 2687 if (!Flags.areaIdConfigAccess() && carPropertyConfig.getAccess() 2688 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) { 2689 verifyGetPropertiesAsyncFails(carPropertyConfig.getAreaIds()[0]); 2690 return; 2691 } 2692 2693 List<GetPropertyRequest> getPropertyRequests = new ArrayList<>(); 2694 SparseIntArray requestIdToAreaIdMap = new SparseIntArray(); 2695 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 2696 int areaId = areaIdConfig.getAreaId(); 2697 if (Flags.areaIdConfigAccess() && areaIdConfig.getAccess() 2698 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) { 2699 verifyGetPropertiesAsyncFails(areaId); 2700 continue; 2701 } 2702 GetPropertyRequest getPropertyRequest = mCarPropertyManager.generateGetPropertyRequest( 2703 mPropertyId, areaId); 2704 int requestId = getPropertyRequest.getRequestId(); 2705 requestIdToAreaIdMap.put(requestId, areaId); 2706 getPropertyRequests.add(getPropertyRequest); 2707 } 2708 2709 TestGetPropertyCallback testGetPropertyCallback = new TestGetPropertyCallback( 2710 requestIdToAreaIdMap.size()); 2711 mCarPropertyManager.getPropertiesAsync(getPropertyRequests, /* cancellationSignal: */ null, 2712 /* callbackExecutor: */ null, testGetPropertyCallback); 2713 testGetPropertyCallback.waitForResults(); 2714 2715 for (GetPropertyResult<?> getPropertyResult : 2716 testGetPropertyCallback.getGetPropertyResults()) { 2717 int requestId = getPropertyResult.getRequestId(); 2718 int propertyId = getPropertyResult.getPropertyId(); 2719 if (requestIdToAreaIdMap.indexOfKey(requestId) < 0) { 2720 assertWithMessage( 2721 "getPropertiesAsync received GetPropertyResult with unknown requestId: " 2722 + getPropertyResult).fail(); 2723 } 2724 Integer expectedAreaId = requestIdToAreaIdMap.get(requestId); 2725 verifyCarPropertyValue(propertyId, getPropertyResult.getAreaId(), 2726 CarPropertyValue.STATUS_AVAILABLE, getPropertyResult.getTimestampNanos(), 2727 (T) getPropertyResult.getValue(), expectedAreaId, 2728 CAR_PROPERTY_VALUE_SOURCE_CALLBACK); 2729 if (mPropertyId == VehiclePropertyIds.HVAC_TEMPERATURE_VALUE_SUGGESTION) { 2730 verifyHvacTemperatureValueSuggestionResponse( 2731 (Float[]) getPropertyResult.getValue()); 2732 } 2733 } 2734 2735 for (PropertyAsyncError propertyAsyncError : 2736 testGetPropertyCallback.getPropertyAsyncErrors()) { 2737 int requestId = propertyAsyncError.getRequestId(); 2738 if (requestIdToAreaIdMap.indexOfKey(requestId) < 0) { 2739 assertWithMessage( 2740 "getPropertiesAsync received PropertyAsyncError with unknown requestId: " 2741 + propertyAsyncError).fail(); 2742 } 2743 assertWithMessage("Received PropertyAsyncError when testing getPropertiesAsync: " 2744 + propertyAsyncError).fail(); 2745 } 2746 } 2747 verifyGetPropertiesAsyncFails(int areaId)2748 private void verifyGetPropertiesAsyncFails(int areaId) { 2749 if (!isAtLeastU()) { 2750 return; 2751 } 2752 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 2753 List<GetPropertyRequest> getPropertyRequests = new ArrayList<>(); 2754 GetPropertyRequest getPropertyRequest = mCarPropertyManager.generateGetPropertyRequest( 2755 mPropertyId, areaId); 2756 getPropertyRequests.add(getPropertyRequest); 2757 TestGetPropertyCallback testGetPropertyCallback = new TestGetPropertyCallback( 2758 /* getPropertyResultsCount: */ 1); 2759 assertThrows( 2760 mPropertyName 2761 + " is a write_only property so getPropertiesAsync should throw an" 2762 + " IllegalArgumentException.", 2763 IllegalArgumentException.class, 2764 () -> mCarPropertyManager.getPropertiesAsync(getPropertyRequests, 2765 /* cancellationSignal: */ null, /* callbackExecutor: */ null, 2766 testGetPropertyCallback)); 2767 } 2768 verifySetPropertiesAsync()2769 private void verifySetPropertiesAsync() { 2770 if (!isAtLeastU()) { 2771 return; 2772 } 2773 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 2774 if (!Flags.areaIdConfigAccess() && carPropertyConfig.getAccess() 2775 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) { 2776 verifySetPropertiesAsyncFails(carPropertyConfig.getAreaIds()[0]); 2777 return; 2778 } 2779 2780 List<SetPropertyRequest<?>> setPropertyRequests = new ArrayList<>(); 2781 SparseIntArray requestIdToAreaIdMap = new SparseIntArray(); 2782 2783 for (AreaIdConfig<?> areaIdConfig : carPropertyConfig.getAreaIdConfigs()) { 2784 int areaId = areaIdConfig.getAreaId(); 2785 if (Flags.areaIdConfigAccess() && areaIdConfig.getAccess() 2786 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) { 2787 verifySetPropertiesAsyncFails(areaId); 2788 continue; 2789 } 2790 Collection<T> possibleValues = getPossibleValues(areaId); 2791 if (possibleValues == null || possibleValues.size() == 0) { 2792 continue; 2793 } 2794 for (T possibleValue : possibleValues) { 2795 SetPropertyRequest setPropertyRequest = 2796 mCarPropertyManager.generateSetPropertyRequest(mPropertyId, areaId, 2797 possibleValue); 2798 if ((Flags.areaIdConfigAccess() ? areaIdConfig.getAccess() 2799 : carPropertyConfig.getAccess()) 2800 == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) { 2801 setPropertyRequest.setWaitForPropertyUpdate(false); 2802 } 2803 requestIdToAreaIdMap.put(setPropertyRequest.getRequestId(), areaId); 2804 setPropertyRequests.add(setPropertyRequest); 2805 } 2806 } 2807 2808 TestSetPropertyCallback testSetPropertyCallback = new TestSetPropertyCallback( 2809 requestIdToAreaIdMap.size()); 2810 mCarPropertyManager.setPropertiesAsync(setPropertyRequests, /* cancellationSignal: */ null, 2811 /* callbackExecutor: */ null, testSetPropertyCallback); 2812 testSetPropertyCallback.waitForResults(); 2813 2814 for (SetPropertyResult setPropertyResult : 2815 testSetPropertyCallback.getSetPropertyResults()) { 2816 int requestId = setPropertyResult.getRequestId(); 2817 if (requestIdToAreaIdMap.indexOfKey(requestId) < 0) { 2818 assertWithMessage( 2819 "setPropertiesAsync received SetPropertyResult with unknown requestId: " 2820 + setPropertyResult).fail(); 2821 } 2822 assertThat(setPropertyResult.getPropertyId()).isEqualTo(mPropertyId); 2823 assertThat(setPropertyResult.getAreaId()).isEqualTo( 2824 requestIdToAreaIdMap.get(requestId)); 2825 assertThat(setPropertyResult.getUpdateTimestampNanos()).isAtLeast(0); 2826 assertThat(setPropertyResult.getUpdateTimestampNanos()).isLessThan( 2827 SystemClock.elapsedRealtimeNanos()); 2828 } 2829 2830 for (PropertyAsyncError propertyAsyncError : 2831 testSetPropertyCallback.getPropertyAsyncErrors()) { 2832 int requestId = propertyAsyncError.getRequestId(); 2833 if (requestIdToAreaIdMap.indexOfKey(requestId) < 0) { 2834 assertWithMessage("setPropertiesAsync received PropertyAsyncError with unknown " 2835 + "requestId: " + propertyAsyncError).fail(); 2836 } 2837 assertWithMessage("Received PropertyAsyncError when testing setPropertiesAsync: " 2838 + propertyAsyncError).fail(); 2839 } 2840 } 2841 verifySetPropertiesAsyncFails(int areaId)2842 private void verifySetPropertiesAsyncFails(int areaId) { 2843 if (!isAtLeastU()) { 2844 return; 2845 } 2846 CarPropertyConfig<T> carPropertyConfig = getCarPropertyConfig(); 2847 List<SetPropertyRequest<?>> setPropertyRequests = new ArrayList<>(); 2848 SetPropertyRequest setPropertyRequest = mCarPropertyManager.generateSetPropertyRequest( 2849 mPropertyId, areaId, getDefaultValue(carPropertyConfig.getPropertyType())); 2850 setPropertyRequests.add(setPropertyRequest); 2851 TestSetPropertyCallback testSetPropertyCallback = new TestSetPropertyCallback( 2852 /* setPropertyResultsCount: */ 1); 2853 assertThrows( 2854 mPropertyName 2855 + " is a read_only property so setPropertiesAsync should throw an" 2856 + " IllegalArgumentException.", 2857 IllegalArgumentException.class, 2858 () -> mCarPropertyManager.setPropertiesAsync(setPropertyRequests, 2859 /* cancellationSignal: */ null, /* callbackExecutor: */ null, 2860 testSetPropertyCallback)); 2861 } 2862 setPropertyAndWaitForChange( CarPropertyManager carPropertyManager, int propertyId, Class<U> propertyType, int areaId, U valueToSet)2863 private static <U> CarPropertyValue<U> setPropertyAndWaitForChange( 2864 CarPropertyManager carPropertyManager, int propertyId, Class<U> propertyType, 2865 int areaId, U valueToSet) { 2866 return setPropertyAndWaitForChange(carPropertyManager, propertyId, propertyType, areaId, 2867 valueToSet, valueToSet); 2868 } 2869 setPropertyAndWaitForChange( CarPropertyManager carPropertyManager, int propertyId, Class<U> propertyType, int areaId, U valueToSet, U expectedValueToGet)2870 private static <U> CarPropertyValue<U> setPropertyAndWaitForChange( 2871 CarPropertyManager carPropertyManager, int propertyId, Class<U> propertyType, 2872 int areaId, U valueToSet, U expectedValueToGet) { 2873 SetterCallback setterCallback = new SetterCallback(propertyId, areaId, expectedValueToGet); 2874 assertWithMessage("Failed to register setter callback for " + VehiclePropertyIds.toString( 2875 propertyId)).that(carPropertyManager.registerCallback(setterCallback, propertyId, 2876 CarPropertyManager.SENSOR_RATE_FASTEST)).isTrue(); 2877 try { 2878 carPropertyManager.setProperty(propertyType, propertyId, areaId, valueToSet); 2879 } catch (PropertyNotAvailableException e) { 2880 verifyPropertyNotAvailableException(e); 2881 sExceptionClassOnSet = e.getClass(); 2882 return null; 2883 } catch (CarInternalErrorException e) { 2884 verifyInternalErrorException(e); 2885 sExceptionClassOnSet = e.getClass(); 2886 return null; 2887 } 2888 2889 CarPropertyValue<U> carPropertyValue = setterCallback.waitForUpdatedCarPropertyValue(); 2890 carPropertyManager.unregisterCallback(setterCallback, propertyId); 2891 return carPropertyValue; 2892 } 2893 } 2894