1 /* 2 * Copyright (C) 2019 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; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static com.google.common.truth.Truth.assertWithMessage; 21 22 import static org.junit.Assert.assertThrows; 23 import static org.junit.Assume.assumeFalse; 24 import static org.junit.Assume.assumeTrue; 25 26 import android.car.Car; 27 import android.car.VehicleAreaType; 28 import android.car.VehiclePropertyIds; 29 import android.car.VehiclePropertyType; 30 import android.car.cts.utils.BuildUtils; 31 import android.car.cts.utils.ShellPermissionUtils; 32 import android.car.feature.Flags; 33 import android.car.hardware.CarPropertyConfig; 34 import android.car.hardware.property.AreaIdConfig; 35 import android.car.hardware.property.CarPropertyManager; 36 import android.platform.test.annotations.AppModeFull; 37 import android.platform.test.annotations.RequiresDevice; 38 import android.platform.test.annotations.RequiresFlagsDisabled; 39 import android.platform.test.annotations.RequiresFlagsEnabled; 40 import android.platform.test.flag.junit.CheckFlagsRule; 41 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 42 43 import androidx.test.filters.SmallTest; 44 import androidx.test.runner.AndroidJUnit4; 45 46 import org.junit.Assert; 47 import org.junit.Before; 48 import org.junit.Rule; 49 import org.junit.Test; 50 import org.junit.Test.None; 51 import org.junit.runner.RunWith; 52 53 import java.util.Arrays; 54 import java.util.List; 55 56 @SmallTest 57 @RequiresDevice 58 @RunWith(AndroidJUnit4.class) 59 @AppModeFull(reason = "Instant apps cannot get car related permissions.") 60 public final class CarPropertyConfigTest extends AbstractCarTestCase { 61 62 private static final String TAG = CarPropertyConfigTest.class.getSimpleName(); 63 private static final float EPSILON = 0.00001f; 64 65 private List<CarPropertyConfig> mConfigs; 66 67 @Rule 68 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 69 70 @Before setUp()71 public void setUp() throws Exception { 72 CarPropertyManager carPropertyManager = (CarPropertyManager) getCar().getCarManager( 73 Car.PROPERTY_SERVICE); 74 ShellPermissionUtils.runWithShellPermissionIdentity( 75 () -> mConfigs = carPropertyManager.getPropertyList()); 76 assertThat(mConfigs.size()).isAtLeast(4); 77 } 78 79 @Test testGetPropertyId()80 public void testGetPropertyId() { 81 List<Integer> expectedPropertyTypes = Arrays.asList( 82 VehiclePropertyType.STRING, 83 VehiclePropertyType.BOOLEAN, 84 VehiclePropertyType.INT32, 85 VehiclePropertyType.INT32_VEC, 86 VehiclePropertyType.INT64, 87 VehiclePropertyType.INT64_VEC, 88 VehiclePropertyType.FLOAT, 89 VehiclePropertyType.FLOAT_VEC, 90 VehiclePropertyType.BYTES, 91 VehiclePropertyType.MIXED); 92 93 for (CarPropertyConfig cfg : mConfigs) { 94 int propId = cfg.getPropertyId(); 95 96 boolean verifyGroup = 97 (propId & VehiclePropertyGroup.MASK) == VehiclePropertyGroup.VENDOR || 98 (propId & VehiclePropertyGroup.MASK) == VehiclePropertyGroup.SYSTEM; 99 Assert.assertTrue(verifyGroup); 100 101 int propertyType = propId & VehiclePropertyType.MASK; 102 assertThat(expectedPropertyTypes).contains(propertyType); 103 } 104 } 105 106 @Test 107 @RequiresFlagsDisabled(Flags.FLAG_AREA_ID_CONFIG_ACCESS) testGetAccess_AreaIdConfigAccessDisabled()108 public void testGetAccess_AreaIdConfigAccessDisabled() { 109 List<Integer> expectedAccessCodes = Arrays.asList( 110 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_NONE, 111 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ, 112 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE, 113 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE); 114 for (CarPropertyConfig cfg : mConfigs) { 115 int result = cfg.getAccess(); 116 assertThat(expectedAccessCodes).contains(result); 117 } 118 } 119 120 @Test 121 @RequiresFlagsEnabled(Flags.FLAG_AREA_ID_CONFIG_ACCESS) testGetAccess_AreaIdConfigAccessEnabled()122 public void testGetAccess_AreaIdConfigAccessEnabled() { 123 for (CarPropertyConfig<?> cfg : mConfigs) { 124 boolean readOnlyPresent = false; 125 boolean writeOnlyPresent = false; 126 boolean readWritePresent = false; 127 for (AreaIdConfig<?> areaIdConfig : cfg.getAreaIdConfigs()) { 128 int areaIdAccess = areaIdConfig.getAccess(); 129 if (areaIdAccess == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) { 130 readOnlyPresent = true; 131 } else if (areaIdAccess == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) { 132 writeOnlyPresent = true; 133 } else if (areaIdAccess == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) { 134 readWritePresent = true; 135 } else { 136 throw new AssertionError("AreaIdConfig must have an access level of one of the" 137 + " following: {READ, WRITE, READ_WRITE}"); 138 } 139 } 140 assertThat(readOnlyPresent || writeOnlyPresent || readWritePresent).isTrue(); 141 142 int propertyIdAccess = cfg.getAccess(); 143 if (propertyIdAccess == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) { 144 assertThat(readOnlyPresent && !writeOnlyPresent).isTrue(); 145 } else if (propertyIdAccess == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) { 146 assertThat(!readOnlyPresent && writeOnlyPresent && !readWritePresent).isTrue(); 147 } else if (propertyIdAccess == CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) { 148 assertThat(!readOnlyPresent && !writeOnlyPresent && readWritePresent).isTrue(); 149 } else { 150 throw new AssertionError("CarPropertyConfig must have an access level of one of the" 151 + " following: {READ, WRITE, READ_WRITE}"); 152 } 153 } 154 } 155 156 @Test testGetAreaType()157 public void testGetAreaType() { 158 List<Integer> expectedAreaTypes = Arrays.asList( 159 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 160 VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW, 161 VehicleAreaType.VEHICLE_AREA_TYPE_SEAT, 162 VehicleAreaType.VEHICLE_AREA_TYPE_DOOR, 163 VehicleAreaType.VEHICLE_AREA_TYPE_MIRROR, 164 VehicleAreaType.VEHICLE_AREA_TYPE_WHEEL, 165 VehicleAreaType.VEHICLE_AREA_TYPE_VENDOR); 166 for (CarPropertyConfig cfg : mConfigs) { 167 int result = 0; 168 try { 169 result = cfg.getAreaType(); 170 } catch (RuntimeException e) { 171 Assert.fail( 172 "Unexpected Runtime Exception for property: " 173 + VehiclePropertyIds.toString(cfg.getPropertyId())); 174 } 175 176 assertThat(expectedAreaTypes).contains(result); 177 int propertyArea = cfg.getPropertyId() & VehicleArea.MASK; 178 if (result == VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL) { 179 Assert.assertEquals(VehicleArea.GLOBAL, propertyArea); 180 } else if (result == VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW) { 181 Assert.assertEquals(VehicleArea.WINDOW, propertyArea); 182 } else if (result == VehicleAreaType.VEHICLE_AREA_TYPE_SEAT) { 183 Assert.assertEquals(VehicleArea.SEAT, propertyArea); 184 } else if (result == VehicleAreaType.VEHICLE_AREA_TYPE_MIRROR) { 185 Assert.assertEquals(VehicleArea.MIRROR, propertyArea); 186 } else if (result == VehicleAreaType.VEHICLE_AREA_TYPE_DOOR) { 187 Assert.assertEquals(VehicleArea.DOOR, propertyArea); 188 } else if (result == VehicleAreaType.VEHICLE_AREA_TYPE_WHEEL) { 189 Assert.assertEquals(VehicleArea.WHEEL, propertyArea); 190 } else if (result == VehicleAreaType.VEHICLE_AREA_TYPE_VENDOR) { 191 Assert.assertEquals(VehicleArea.VENDOR, propertyArea); 192 } else { 193 Assert.fail("Failed for property: " 194 + VehiclePropertyIds.toString(cfg.getPropertyId())); 195 } 196 } 197 } 198 199 @Test testGetChangeMode()200 public void testGetChangeMode() { 201 List<Integer> expectedChangeModes = Arrays.asList( 202 CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_STATIC, 203 CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE, 204 CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS); 205 for (CarPropertyConfig cfg : mConfigs) { 206 int result = cfg.getChangeMode(); 207 assertThat(expectedChangeModes).contains(result); 208 } 209 } 210 211 @Test testGetConfigArrayAndPropertyId()212 public void testGetConfigArrayAndPropertyId() { 213 for (CarPropertyConfig cfg : mConfigs) { 214 cfg.getPropertyId(); 215 Assert.assertNotNull(cfg.getConfigArray()); 216 } 217 } 218 219 @Test testSampleRate()220 public void testSampleRate() { 221 for (CarPropertyConfig cfg : mConfigs) { 222 float maxSampleRate = cfg.getMaxSampleRate(); 223 float minSampleRate = cfg.getMinSampleRate(); 224 // Only continuous properties have min/max sample rate. 225 if (Math.abs(maxSampleRate - 0.0f) > EPSILON 226 || Math.abs(minSampleRate - 0.0f) > EPSILON) { 227 Assert.assertEquals( 228 CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS, cfg.getChangeMode()); 229 } 230 } 231 } 232 233 @Test testGlobalProperty()234 public void testGlobalProperty() { 235 for (CarPropertyConfig cfg : mConfigs) { 236 Assert.assertEquals( 237 cfg.getAreaType() == VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 238 cfg.isGlobalProperty()); 239 } 240 } 241 242 @Test testGetAreaIds()243 public void testGetAreaIds() { 244 for (CarPropertyConfig<?> cfg : mConfigs) { 245 int[] areaIds = cfg.getAreaIds(); 246 Assert.assertNotNull(areaIds); 247 assertThat(areaIds).isNotEmpty(); 248 Assert.assertTrue(areaIdCheck(areaIds)); 249 assertThat(areaIds.length).isEqualTo(cfg.getAreaIdConfigs().size()); 250 for (int areaId : areaIds) { 251 boolean found = false; 252 for (AreaIdConfig<?> areaIdConfig : cfg.getAreaIdConfigs()) { 253 if (areaIdConfig.getAreaId() == areaId) { 254 found = true; 255 break; 256 } 257 } 258 assertWithMessage("Property ID: " + VehiclePropertyIds.toString(cfg.getPropertyId()) 259 + " area ID: 0x" + Integer.toHexString(areaId) 260 + " must be found in AreaIdConfigs list").that(found).isTrue(); 261 } 262 } 263 } 264 265 @Test testGetAreaIdConfigs()266 public void testGetAreaIdConfigs() { 267 for (CarPropertyConfig<?> cfg : mConfigs) { 268 List<? extends AreaIdConfig<?>> areaIdConfigs = cfg.getAreaIdConfigs(); 269 assertThat(areaIdConfigs).isNotNull(); 270 assertThat(areaIdConfigs).isNotEmpty(); 271 for (AreaIdConfig<?> areaIdConfig : areaIdConfigs) { 272 boolean minMaxCorrectlyDefined = 273 (areaIdConfig.getMinValue() != null && areaIdConfig.getMaxValue() != null) 274 || (areaIdConfig.getMinValue() == null 275 && areaIdConfig.getMaxValue() == null); 276 assertWithMessage("Property ID: " + VehiclePropertyIds.toString(cfg.getPropertyId()) 277 + " area ID: 0x" + Integer.toHexString(areaIdConfig.getAreaId()) 278 + " min/max must be both defined or both null").that( 279 minMaxCorrectlyDefined).isTrue(); 280 if (cfg.getPropertyType().equals(Integer.class)) { 281 if (areaIdConfig.getMinValue() != null) { 282 assertThat((Integer) areaIdConfig.getMaxValue()).isAtLeast( 283 (Integer) areaIdConfig.getMinValue()); 284 if (((Integer) areaIdConfig.getMinValue()).equals(0)) { 285 assertThat((Integer) areaIdConfig.getMaxValue()).isNotEqualTo(0); 286 } 287 } 288 } else if (cfg.getPropertyType().equals(Long.class)) { 289 if (areaIdConfig.getMinValue() != null) { 290 assertThat((Long) areaIdConfig.getMaxValue()).isAtLeast( 291 (Long) areaIdConfig.getMinValue()); 292 if (((Long) areaIdConfig.getMinValue()).equals(0L)) { 293 assertThat((Long) areaIdConfig.getMaxValue()).isNotEqualTo(0L); 294 } 295 } 296 } else if (cfg.getPropertyType().equals(Float.class)) { 297 if (areaIdConfig.getMinValue() != null) { 298 assertThat((Float) areaIdConfig.getMaxValue()).isAtLeast( 299 (Float) areaIdConfig.getMinValue()); 300 if (((Float) areaIdConfig.getMinValue()).equals(0F)) { 301 assertThat((Float) areaIdConfig.getMaxValue()).isNotEqualTo(0F); 302 } 303 } 304 } else { 305 assertThat(areaIdConfig.getMinValue()).isNull(); 306 assertThat(areaIdConfig.getMaxValue()).isNull(); 307 } 308 assertThat(areaIdConfig.getSupportedEnumValues()).isNotNull(); 309 assertThat(areaIdConfig.getSupportedEnumValues()).containsNoDuplicates(); 310 if (Flags.areaIdConfigAccess()) { 311 assertThat(areaIdConfig.getAccess()).isIn(Arrays.asList( 312 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ, 313 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE, 314 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE 315 )); 316 } 317 } 318 } 319 } 320 321 @Test testGetAreaIdConfig()322 public void testGetAreaIdConfig() { 323 for (CarPropertyConfig<?> cfg : mConfigs) { 324 for (int areaId : cfg.getAreaIds()) { 325 AreaIdConfig<?> areaIdConfig = cfg.getAreaIdConfig(areaId); 326 assertThat(areaIdConfig).isNotNull(); 327 assertThat(areaIdConfig.getAreaId()).isEqualTo(areaId); 328 assertThat(areaIdConfig).isIn(cfg.getAreaIdConfigs()); 329 } 330 } 331 } 332 333 @Test(expected = None.class /* no exception expected*/) testGetMinAndMaxValue()334 public void testGetMinAndMaxValue() { 335 for (CarPropertyConfig cfg: mConfigs) { 336 cfg.getMinValue(); 337 cfg.getMaxValue(); 338 for (int areaId : cfg.getAreaIds()) { 339 cfg.getMaxValue(areaId); 340 cfg.getMinValue(areaId); 341 } 342 } 343 } 344 345 @Test 346 @RequiresFlagsEnabled(Flags.FLAG_CAR_PROPERTY_SIMULATION) testIsPropertyIdSimulationPropId()347 public void testIsPropertyIdSimulationPropId() { 348 assumeFalse(BuildUtils.isUserBuild()); 349 for (CarPropertyConfig<?> cfg : mConfigs) { 350 assertThat(cfg.isPropertyIdSimulationPropId()).isFalse(); 351 } 352 } 353 354 @Test 355 @RequiresFlagsEnabled(Flags.FLAG_CAR_PROPERTY_SIMULATION) testIsPropertyIdSimulationPropId_userBuild()356 public void testIsPropertyIdSimulationPropId_userBuild() { 357 assumeTrue(BuildUtils.isUserBuild()); 358 for (CarPropertyConfig<?> cfg : mConfigs) { 359 IllegalStateException thrown = 360 assertThrows( 361 IllegalStateException.class, () -> cfg.isPropertyIdSimulationPropId()); 362 assertThat(thrown).hasMessageThat().contains("Build is not eng or user-debug"); 363 } 364 } 365 366 /** 367 * The property value must be independently controllable in any two different AreaIDs 368 * in the array. An area must only appear once in the array of AreaIDs. That is, an 369 * area must only be part of a single AreaID in the array. 370 * @param areaIds 371 * @return 372 */ areaIdCheck(int[] areaIds)373 private boolean areaIdCheck(int[] areaIds) { 374 for (int i = 0; i < areaIds.length - 1; i++) { 375 for (int j = i + 1; j < areaIds.length; j++) { 376 if ((areaIds[i] & areaIds[j]) != 0) { 377 return false; 378 } 379 } 380 } 381 return true; 382 } 383 384 private final class VehiclePropertyGroup { 385 private static final int SYSTEM = 0x10000000; 386 private static final int VENDOR = 0x20000000; 387 388 private static final int MASK = 0xf0000000; 389 } 390 391 private final class VehicleArea { 392 private static final int GLOBAL = 0x01000000; 393 private static final int WINDOW = 0x03000000; 394 private static final int MIRROR = 0x04000000; 395 private static final int SEAT = 0x05000000; 396 private static final int DOOR = 0x06000000; 397 private static final int WHEEL = 0x07000000; 398 private static final int VENDOR = 0x08000000; 399 400 private static final int MASK = 0x0f000000; 401 402 } 403 } 404