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 com.android.car; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.junit.Assert.fail; 22 import static org.testng.Assert.assertThrows; 23 24 import android.car.Car; 25 import android.car.VehicleAreaType; 26 import android.car.VehiclePropertyIds; 27 import android.car.hardware.CarPropertyConfig; 28 import android.car.hardware.CarPropertyValue; 29 import android.car.hardware.property.CarInternalErrorException; 30 import android.car.hardware.property.CarPropertyManager; 31 import android.car.hardware.property.PropertyAccessDeniedSecurityException; 32 import android.car.hardware.property.PropertyNotAvailableAndRetryException; 33 import android.car.hardware.property.PropertyNotAvailableException; 34 import android.car.hardware.property.VehicleHalStatusCode; 35 import android.car.test.util.Visitor; 36 import android.hardware.automotive.vehicle.V2_0.VehicleArea; 37 import android.hardware.automotive.vehicle.V2_0.VehicleAreaSeat; 38 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue; 39 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyGroup; 40 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType; 41 import android.os.Build; 42 import android.os.ServiceSpecificException; 43 import android.os.SystemClock; 44 import android.util.ArraySet; 45 import android.util.Log; 46 47 import androidx.test.ext.junit.runners.AndroidJUnit4; 48 import androidx.test.filters.MediumTest; 49 50 import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler; 51 52 import com.google.common.truth.Truth; 53 54 import org.junit.Assert; 55 import org.junit.Rule; 56 import org.junit.Test; 57 import org.junit.rules.TestName; 58 import org.junit.runner.RunWith; 59 60 import java.util.ArrayList; 61 import java.util.Arrays; 62 import java.util.HashMap; 63 import java.util.List; 64 import java.util.concurrent.ConcurrentHashMap; 65 import java.util.concurrent.CountDownLatch; 66 import java.util.concurrent.TimeUnit; 67 68 /** 69 * Test for {@link android.car.hardware.property.CarPropertyManager} 70 */ 71 @RunWith(AndroidJUnit4.class) 72 @MediumTest 73 public class CarPropertyManagerTest extends MockedCarTestBase { 74 75 private static final String TAG = CarPropertyManagerTest.class.getSimpleName(); 76 77 /** 78 * configArray[0], 1 indicates the property has a String value 79 * configArray[1], 1 indicates the property has a Boolean value . 80 * configArray[2], 1 indicates the property has a Integer value 81 * configArray[3], the number indicates the size of Integer[] in the property. 82 * configArray[4], 1 indicates the property has a Long value . 83 * configArray[5], the number indicates the size of Long[] in the property. 84 * configArray[6], 1 indicates the property has a Float value . 85 * configArray[7], the number indicates the size of Float[] in the property. 86 * configArray[8], the number indicates the size of byte[] in the property. 87 */ 88 private static final java.util.Collection<Integer> CONFIG_ARRAY_1 = 89 Arrays.asList(1, 0, 1, 0, 1, 0, 0, 0, 0); 90 private static final java.util.Collection<Integer> CONFIG_ARRAY_2 = 91 Arrays.asList(1, 1, 1, 0, 0, 0, 0, 2, 0); 92 private static final Object[] EXPECTED_VALUE_1 = {"android", 1, 1L}; 93 private static final Object[] EXPECTED_VALUE_2 = {"android", true, 3, 1.1f, 2f}; 94 95 private static final int CUSTOM_SEAT_INT_PROP_1 = 96 0x1201 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.SEAT; 97 private static final int CUSTOM_SEAT_INT_PROP_2 = 98 0x1202 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.SEAT; 99 100 private static final int CUSTOM_SEAT_MIXED_PROP_ID_1 = 101 0x1101 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.MIXED | VehicleArea.SEAT; 102 private static final int CUSTOM_GLOBAL_MIXED_PROP_ID_2 = 103 0x1102 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.MIXED | VehicleArea.GLOBAL; 104 105 private static final int CUSTOM_GLOBAL_INT_ARRAY_PROP = 106 0x1103 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32_VEC 107 | VehicleArea.GLOBAL; 108 private static final Integer[] FAKE_INT_ARRAY_VALUE = {1, 2}; 109 110 // Vendor properties for testing exceptions. 111 private static final int PROP_CAUSE_STATUS_CODE_TRY_AGAIN = 112 0x1201 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL; 113 private static final int PROP_CAUSE_STATUS_CODE_INVALID_ARG = 114 0x1202 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL; 115 private static final int PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE = 116 0x1203 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL; 117 private static final int PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR = 118 0x1204 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL; 119 private static final int PROP_CAUSE_STATUS_CODE_ACCESS_DENIED = 120 0x1205 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL; 121 122 // Use FAKE_PROPERTY_ID to test api return null or throw exception. 123 private static final int FAKE_PROPERTY_ID = 0x111; 124 125 private static final int DRIVER_SIDE_AREA_ID = VehicleAreaSeat.ROW_1_LEFT 126 | VehicleAreaSeat.ROW_2_LEFT; 127 private static final int PASSENGER_SIDE_AREA_ID = VehicleAreaSeat.ROW_1_RIGHT 128 | VehicleAreaSeat.ROW_2_CENTER 129 | VehicleAreaSeat.ROW_2_RIGHT; 130 private static final float INIT_TEMP_VALUE = 16f; 131 private static final float CHANGED_TEMP_VALUE = 20f; 132 private static final int CALLBACK_SHORT_TIMEOUT_MS = 250; // ms 133 // Wait for CarPropertyManager register/unregister listener 134 private static final long WAIT_FOR_NO_EVENTS = 50; 135 136 private static final List<Integer> USER_HAL_PROPERTIES = Arrays.asList( 137 VehiclePropertyIds.INITIAL_USER_INFO, 138 VehiclePropertyIds.SWITCH_USER, 139 VehiclePropertyIds.CREATE_USER, 140 VehiclePropertyIds.REMOVE_USER, 141 VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION 142 ); 143 144 private CarPropertyManager mManager; 145 146 @Rule public TestName mTestName = new TestName(); 147 148 @Override setUp()149 public void setUp() throws Exception { 150 super.setUp(); 151 setUpTargetSdk(); 152 mManager = (CarPropertyManager) getCar().getCarManager(Car.PROPERTY_SERVICE); 153 assertThat(mManager).isNotNull(); 154 } 155 setUpTargetSdk()156 private void setUpTargetSdk() { 157 if (mTestName.getMethodName().endsWith("InQ")) { 158 getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.Q; 159 } else if (mTestName.getMethodName().endsWith("InR")) { 160 getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.R; 161 } 162 } 163 164 @Test testMixedPropertyConfigs()165 public void testMixedPropertyConfigs() { 166 List<CarPropertyConfig> configs = mManager.getPropertyList(); 167 for (CarPropertyConfig cfg : configs) { 168 switch (cfg.getPropertyId()) { 169 case CUSTOM_SEAT_MIXED_PROP_ID_1: 170 assertThat(cfg.getConfigArray()).containsExactlyElementsIn(CONFIG_ARRAY_1) 171 .inOrder(); 172 break; 173 case CUSTOM_GLOBAL_MIXED_PROP_ID_2: 174 assertThat(cfg.getConfigArray()).containsExactlyElementsIn(CONFIG_ARRAY_2) 175 .inOrder(); 176 break; 177 case VehiclePropertyIds.HVAC_TEMPERATURE_SET: 178 case PROP_CAUSE_STATUS_CODE_ACCESS_DENIED: 179 case PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR: 180 case PROP_CAUSE_STATUS_CODE_TRY_AGAIN: 181 case PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE: 182 case PROP_CAUSE_STATUS_CODE_INVALID_ARG: 183 case CUSTOM_SEAT_INT_PROP_1: 184 case CUSTOM_SEAT_INT_PROP_2: 185 case CUSTOM_GLOBAL_INT_ARRAY_PROP: 186 case VehiclePropertyIds.INFO_VIN: 187 break; 188 default: 189 Assert.fail("Unexpected CarPropertyConfig: " + cfg.toString()); 190 } 191 } 192 } 193 194 @Test testGetMixTypeProperty()195 public void testGetMixTypeProperty() { 196 mManager.setProperty(Object[].class, CUSTOM_SEAT_MIXED_PROP_ID_1, 197 0, EXPECTED_VALUE_1); 198 CarPropertyValue<Object[]> result = mManager.getProperty( 199 CUSTOM_SEAT_MIXED_PROP_ID_1, 0); 200 assertThat(result.getValue()).isEqualTo(EXPECTED_VALUE_1); 201 mManager.setProperty(Object[].class, CUSTOM_GLOBAL_MIXED_PROP_ID_2, 202 0, EXPECTED_VALUE_2); 203 result = mManager.getProperty( 204 CUSTOM_GLOBAL_MIXED_PROP_ID_2, 0); 205 assertThat(result.getValue()).isEqualTo(EXPECTED_VALUE_2); 206 } 207 208 /** 209 * Test {@link android.car.hardware.property.CarPropertyManager#getIntArrayProperty(int, int)} 210 */ 211 @Test testGetIntArrayProperty()212 public void testGetIntArrayProperty() { 213 mManager.setProperty(Integer[].class, CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL, 214 FAKE_INT_ARRAY_VALUE); 215 216 int[] result = mManager.getIntArrayProperty(CUSTOM_GLOBAL_INT_ARRAY_PROP, 217 VehicleArea.GLOBAL); 218 assertThat(result).asList().containsExactlyElementsIn(FAKE_INT_ARRAY_VALUE); 219 } 220 221 /** 222 * Test {@link CarPropertyManager#getProperty(Class, int, int)} 223 */ 224 @Test testGetPropertyWithClass()225 public void testGetPropertyWithClass() { 226 mManager.setProperty(Integer[].class, CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL, 227 FAKE_INT_ARRAY_VALUE); 228 229 CarPropertyValue<Integer[]> result = mManager.getProperty(Integer[].class, 230 CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL); 231 assertThat(result.getValue()).asList().containsExactlyElementsIn(FAKE_INT_ARRAY_VALUE); 232 } 233 234 /** 235 * Test {@link CarPropertyManager#isPropertyAvailable(int, int)} 236 */ 237 @Test testIsPropertyAvailable()238 public void testIsPropertyAvailable() { 239 assertThat(mManager.isPropertyAvailable(FAKE_PROPERTY_ID, VehicleArea.GLOBAL)).isFalse(); 240 assertThat(mManager.isPropertyAvailable(CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL)) 241 .isTrue(); 242 } 243 244 /** 245 * Test {@link CarPropertyManager#getWritePermission(int)} 246 * and {@link CarPropertyManager#getWritePermission(int)} 247 */ 248 @Test testGetPermission()249 public void testGetPermission() { 250 String hvacReadPermission = mManager.getReadPermission( 251 VehiclePropertyIds.HVAC_TEMPERATURE_SET); 252 assertThat(hvacReadPermission).isEqualTo(Car.PERMISSION_CONTROL_CAR_CLIMATE); 253 String hvacWritePermission = mManager.getWritePermission( 254 VehiclePropertyIds.HVAC_TEMPERATURE_SET); 255 assertThat(hvacWritePermission).isEqualTo(Car.PERMISSION_CONTROL_CAR_CLIMATE); 256 257 // For read-only property 258 String vinReadPermission = mManager.getReadPermission(VehiclePropertyIds.INFO_VIN); 259 assertThat(vinReadPermission).isEqualTo(Car.PERMISSION_IDENTIFICATION); 260 String vinWritePermission = mManager.getWritePermission(VehiclePropertyIds.INFO_VIN); 261 assertThat(vinWritePermission).isNull(); 262 } 263 264 @Test testGetPropertyConfig()265 public void testGetPropertyConfig() { 266 CarPropertyConfig config = mManager.getCarPropertyConfig(CUSTOM_SEAT_MIXED_PROP_ID_1); 267 assertThat(config.getPropertyId()).isEqualTo(CUSTOM_SEAT_MIXED_PROP_ID_1); 268 // return null if can not find the propertyConfig for the property. 269 assertThat(mManager.getCarPropertyConfig(FAKE_PROPERTY_ID)).isNull(); 270 } 271 272 @Test testGetAreaId()273 public void testGetAreaId() { 274 int result = mManager.getAreaId(CUSTOM_SEAT_MIXED_PROP_ID_1, VehicleAreaSeat.ROW_1_LEFT); 275 assertThat(result).isEqualTo(DRIVER_SIDE_AREA_ID); 276 //test for the GLOBAL property 277 int globalAreaId = 278 mManager.getAreaId(CUSTOM_GLOBAL_MIXED_PROP_ID_2, VehicleAreaSeat.ROW_1_LEFT); 279 assertThat(globalAreaId).isEqualTo(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL); 280 //test exception 281 assertThrows(IllegalArgumentException.class, () -> mManager.getAreaId( 282 CUSTOM_SEAT_MIXED_PROP_ID_1, VehicleAreaSeat.ROW_3_CENTER)); 283 assertThrows(IllegalArgumentException.class, () -> mManager.getAreaId(FAKE_PROPERTY_ID, 284 VehicleAreaSeat.ROW_1_LEFT)); 285 } 286 287 @Test testNotReceiveOnErrorEvent()288 public void testNotReceiveOnErrorEvent() throws Exception { 289 TestErrorCallback callback = new TestErrorCallback(); 290 mManager.registerCallback(callback, VehiclePropertyIds.HVAC_TEMPERATURE_SET, 291 CarPropertyManager.SENSOR_RATE_ONCHANGE); 292 callback.assertRegisterCompleted(); 293 injectErrorEvent(VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID, 294 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN); 295 // app never change the value of HVAC_TEMPERATURE_SET, it won't get an error code. 296 callback.assertOnErrorEventNotCalled(); 297 } 298 299 @Test testReceiveOnErrorEvent()300 public void testReceiveOnErrorEvent() throws Exception { 301 TestErrorCallback callback = new TestErrorCallback(); 302 mManager.registerCallback(callback, VehiclePropertyIds.HVAC_TEMPERATURE_SET, 303 CarPropertyManager.SENSOR_RATE_ONCHANGE); 304 callback.assertRegisterCompleted(); 305 mManager.setFloatProperty( 306 VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID, 307 CHANGED_TEMP_VALUE); 308 injectErrorEvent(VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID, 309 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN); 310 callback.assertOnErrorEventCalled(); 311 assertThat(callback.mReceivedErrorEventWithErrorCode).isTrue(); 312 assertThat(callback.mErrorCode).isEqualTo( 313 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN); 314 assertThat(callback.mReceivedErrorEventWithOutErrorCode).isFalse(); 315 } 316 317 @Test testNotReceiveOnErrorEventAfterUnregister()318 public void testNotReceiveOnErrorEventAfterUnregister() throws Exception { 319 TestErrorCallback callback1 = new TestErrorCallback(); 320 mManager.registerCallback(callback1, VehiclePropertyIds.HVAC_TEMPERATURE_SET, 321 CarPropertyManager.SENSOR_RATE_ONCHANGE); 322 callback1.assertRegisterCompleted(); 323 TestErrorCallback callback2 = new TestErrorCallback(); 324 mManager.registerCallback(callback2, VehiclePropertyIds.HVAC_TEMPERATURE_SET, 325 CarPropertyManager.SENSOR_RATE_ONCHANGE); 326 mManager.setFloatProperty( 327 VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID, 328 CHANGED_TEMP_VALUE); 329 mManager.unregisterCallback(callback1, VehiclePropertyIds.HVAC_TEMPERATURE_SET); 330 SystemClock.sleep(WAIT_FOR_NO_EVENTS); 331 injectErrorEvent(VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID, 332 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN); 333 // callback1 is unregistered 334 callback1.assertOnErrorEventNotCalled(); 335 callback2.assertOnErrorEventCalled(); 336 } 337 @Test testSetterExceptionsInQ()338 public void testSetterExceptionsInQ() { 339 Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion) 340 .isEqualTo(Build.VERSION_CODES.Q); 341 342 assertThrows(IllegalStateException.class, 343 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 344 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 345 assertThrows(IllegalStateException.class, 346 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, 347 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 348 assertThrows(IllegalStateException.class, 349 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, 350 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 351 assertThrows(IllegalArgumentException.class, 352 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INVALID_ARG, 353 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 354 assertThrows(RuntimeException.class, 355 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 356 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 357 } 358 359 @Test testSetterExceptionsInR()360 public void testSetterExceptionsInR() { 361 Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion) 362 .isEqualTo(Build.VERSION_CODES.R); 363 364 assertThrows(PropertyAccessDeniedSecurityException.class, 365 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 366 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 367 assertThrows(PropertyNotAvailableAndRetryException.class, 368 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_TRY_AGAIN, 369 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 370 assertThrows(PropertyNotAvailableException.class, 371 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, 372 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 373 assertThrows(CarInternalErrorException.class, 374 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, 375 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 376 assertThrows(IllegalArgumentException.class, 377 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INVALID_ARG, 378 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 379 } 380 381 @Test testGetterExceptionsInQ()382 public void testGetterExceptionsInQ() { 383 Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion) 384 .isEqualTo(Build.VERSION_CODES.Q); 385 386 assertThrows(IllegalStateException.class, 387 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 388 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 389 assertThrows(IllegalArgumentException.class, 390 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG, 391 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 392 assertThrows(IllegalStateException.class, 393 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, 394 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 395 assertThrows(IllegalStateException.class, 396 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, 397 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 398 Truth.assertThat(mManager.getProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN, 399 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)).isNull(); 400 } 401 402 @Test testGetterExceptionsInR()403 public void testGetterExceptionsInR() { 404 Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion) 405 .isEqualTo(Build.VERSION_CODES.R); 406 407 assertThrows(PropertyAccessDeniedSecurityException.class, 408 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 409 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 410 assertThrows(IllegalArgumentException.class, 411 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG, 412 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 413 assertThrows(PropertyNotAvailableAndRetryException.class, 414 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN, 415 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 416 assertThrows(PropertyNotAvailableException.class, 417 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, 418 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 419 assertThrows(CarInternalErrorException.class, 420 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, 421 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 422 } 423 424 @Test testOnChangeEventWithSameAreaId()425 public void testOnChangeEventWithSameAreaId() throws Exception { 426 // init 427 mManager.setProperty(Integer.class, 428 CUSTOM_SEAT_INT_PROP_1, DRIVER_SIDE_AREA_ID, 1); 429 TestSequenceCallback callback = new TestSequenceCallback(1); 430 mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_1, 0); 431 callback.assertRegisterCompleted(); 432 433 VehiclePropValue firstFakeValueDriveSide = new VehiclePropValue(); 434 firstFakeValueDriveSide.prop = CUSTOM_SEAT_INT_PROP_1; 435 firstFakeValueDriveSide.areaId = DRIVER_SIDE_AREA_ID; 436 firstFakeValueDriveSide.value.int32Values.add(2); 437 firstFakeValueDriveSide.timestamp = SystemClock.elapsedRealtimeNanos(); 438 VehiclePropValue secFakeValueDriveSide = new VehiclePropValue(); 439 secFakeValueDriveSide.prop = CUSTOM_SEAT_INT_PROP_1; 440 secFakeValueDriveSide.areaId = DRIVER_SIDE_AREA_ID; 441 secFakeValueDriveSide.value.int32Values.add(3); // 0 in HAL indicate false; 442 secFakeValueDriveSide.timestamp = SystemClock.elapsedRealtimeNanos(); 443 // inject the new event first 444 getMockedVehicleHal().injectEvent(secFakeValueDriveSide); 445 // inject the old event 446 getMockedVehicleHal().injectEvent(firstFakeValueDriveSide); 447 callback.assertOnChangeEventCalled(); 448 // Client should only get the new event 449 assertThat((int) callback.getLastCarPropertyValue(CUSTOM_SEAT_INT_PROP_1).getValue()) 450 .isEqualTo(3); 451 assertThat(callback.getEventCounter()).isEqualTo(1); 452 453 } 454 455 @Test testOnChangeEventWithDifferentAreaId()456 public void testOnChangeEventWithDifferentAreaId() throws Exception { 457 // init 458 mManager.setProperty(Integer.class, 459 CUSTOM_SEAT_INT_PROP_2, DRIVER_SIDE_AREA_ID, 1); 460 TestSequenceCallback callback = new TestSequenceCallback(2); 461 mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_2, 0); 462 callback.assertRegisterCompleted(); 463 VehiclePropValue fakeValueDriveSide = new VehiclePropValue(); 464 fakeValueDriveSide.prop = CUSTOM_SEAT_INT_PROP_2; 465 fakeValueDriveSide.areaId = DRIVER_SIDE_AREA_ID; 466 fakeValueDriveSide.value.int32Values.add(4); 467 fakeValueDriveSide.timestamp = SystemClock.elapsedRealtimeNanos(); 468 469 VehiclePropValue fakeValuePsgSide = new VehiclePropValue(); 470 fakeValuePsgSide.prop = CUSTOM_SEAT_INT_PROP_2; 471 fakeValuePsgSide.areaId = PASSENGER_SIDE_AREA_ID; 472 fakeValuePsgSide.value.int32Values.add(5); 473 fakeValuePsgSide.timestamp = SystemClock.elapsedRealtimeNanos(); 474 475 // inject passenger event before driver event 476 getMockedVehicleHal().injectEvent(fakeValuePsgSide); 477 getMockedVehicleHal().injectEvent(fakeValueDriveSide); 478 callback.assertOnChangeEventCalled(); 479 480 // both events should be received by listener 481 assertThat((int) callback.getLastCarPropertyValue(CUSTOM_SEAT_INT_PROP_2).getValue()) 482 .isEqualTo(4); 483 assertThat(callback.getEventCounter()).isEqualTo(2); 484 } 485 486 @Test testUserHal_getProperty()487 public void testUserHal_getProperty() { 488 userHalPropertiesTest("getProperty()", (prop) -> 489 mManager.getProperty(prop, /* areaId= */ 0)); 490 } 491 492 @Test testUserHal_getBooleanProperty()493 public void testUserHal_getBooleanProperty() { 494 userHalPropertiesTest("getBooleanProperty()", (prop) -> 495 mManager.getBooleanProperty(prop, /* areaId= */ 0)); 496 } 497 498 @Test testUserHal_getIntProperty()499 public void testUserHal_getIntProperty() { 500 userHalPropertiesTest("getIntProperty()", (prop) -> 501 mManager.getIntProperty(prop, /* areaId= */ 0)); 502 } 503 504 @Test testUserHal_getIntArrayProperty()505 public void testUserHal_getIntArrayProperty() { 506 userHalPropertiesTest("getIntArrayProperty()", (prop) -> 507 mManager.getIntArrayProperty(prop, /* areaId= */ 0)); 508 } 509 510 @Test testUserHal_getFloatProperty()511 public void testUserHal_getFloatProperty() { 512 userHalPropertiesTest("getFloatProperty()", (prop) -> 513 mManager.getFloatProperty(prop, /* areaId= */ 0)); 514 } 515 516 @Test testUserHal_getPropertyList()517 public void testUserHal_getPropertyList() { 518 userHalPropertiesTest("getPropertyList()", (prop) -> { 519 ArraySet<Integer> list = new ArraySet<>(); 520 list.add(prop); 521 mManager.getPropertyList(list); 522 }); 523 } 524 525 @Test testUserHal_getCarPropertyConfig()526 public void testUserHal_getCarPropertyConfig() { 527 userHalPropertiesTest("getCarPropertyConfig()", (prop) -> 528 mManager.getCarPropertyConfig(prop)); 529 } 530 531 @Test testUserHal_getAreaId()532 public void testUserHal_getAreaId() { 533 userHalPropertiesTest("getAreaId()", (prop) -> 534 mManager.getAreaId(prop, /* areaId= */ 0)); 535 } 536 537 @Test testUserHal_getReadPermission()538 public void testUserHal_getReadPermission() { 539 userHalPropertiesTest("getReadPermission()", (prop) -> 540 mManager.getReadPermission(prop)); 541 } 542 543 @Test testUserHal_getWritePermission()544 public void testUserHal_getWritePermission() { 545 userHalPropertiesTest("getWritePermission()", (prop) -> 546 mManager.getWritePermission(prop)); 547 } 548 549 @Test testUserHal_isPropertyAvailable()550 public void testUserHal_isPropertyAvailable() { 551 userHalPropertiesTest("isPropertyAvailable()", (prop) -> 552 mManager.isPropertyAvailable(prop, /* area= */ 0)); 553 } 554 555 @Test testUserHal_setProperty()556 public void testUserHal_setProperty() { 557 userHalPropertiesTest("setProperty()", (prop) -> 558 mManager.setProperty(Object.class, prop, /* areaId= */ 0, /* val= */ null)); 559 } 560 561 @Test testUserHal_setBooleanProperty()562 public void testUserHal_setBooleanProperty() { 563 userHalPropertiesTest("setBooleanProperty()", (prop) -> 564 mManager.setBooleanProperty(prop, /* areaId= */ 0, /* val= */ true)); 565 } 566 567 @Test testUserHal_setFloatProperty()568 public void testUserHal_setFloatProperty() { 569 userHalPropertiesTest("setFloatProperty()", (prop) -> 570 mManager.setFloatProperty(prop, /* areaId= */ 0, /* val= */ 0.0F)); 571 } 572 573 @Test testUserHal_setIntProperty()574 public void testUserHal_setIntProperty() { 575 userHalPropertiesTest("setIntProperty()", (prop) -> 576 mManager.setIntProperty(prop, /* areaId= */ 0, /* val= */ 0)); 577 } 578 userHalPropertiesTest(String method, Visitor<Integer> visitor)579 private void userHalPropertiesTest(String method, Visitor<Integer> visitor) { 580 List<String> failedProperties = new ArrayList<String>(); 581 for (int propertyId : USER_HAL_PROPERTIES) { 582 try { 583 visitor.visit(propertyId); 584 failedProperties.add(propToString(propertyId)); 585 } catch (IllegalArgumentException e) { 586 // expected 587 } 588 } 589 if (!failedProperties.isEmpty()) { 590 fail(method + " should not support these properties: " + failedProperties); 591 } 592 } 593 594 @Override configureMockedHal()595 protected synchronized void configureMockedHal() { 596 PropertyHandler handler = new PropertyHandler(); 597 addProperty(CUSTOM_SEAT_MIXED_PROP_ID_1, handler).setConfigArray(CONFIG_ARRAY_1) 598 .addAreaConfig(DRIVER_SIDE_AREA_ID).addAreaConfig(PASSENGER_SIDE_AREA_ID); 599 addProperty(CUSTOM_GLOBAL_MIXED_PROP_ID_2, handler).setConfigArray(CONFIG_ARRAY_2); 600 addProperty(CUSTOM_GLOBAL_INT_ARRAY_PROP, handler); 601 602 VehiclePropValue tempValue = new VehiclePropValue(); 603 tempValue.value.floatValues.add(INIT_TEMP_VALUE); 604 tempValue.prop = VehiclePropertyIds.HVAC_TEMPERATURE_SET; 605 addProperty(VehiclePropertyIds.HVAC_TEMPERATURE_SET, tempValue) 606 .addAreaConfig(DRIVER_SIDE_AREA_ID).addAreaConfig(PASSENGER_SIDE_AREA_ID); 607 addProperty(VehiclePropertyIds.INFO_VIN); 608 609 addProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, handler); 610 addProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN, handler); 611 addProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, handler); 612 addProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG, handler); 613 addProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, handler); 614 615 addProperty(CUSTOM_SEAT_INT_PROP_1, handler).addAreaConfig(DRIVER_SIDE_AREA_ID) 616 .addAreaConfig(PASSENGER_SIDE_AREA_ID); 617 addProperty(CUSTOM_SEAT_INT_PROP_2, handler).addAreaConfig(DRIVER_SIDE_AREA_ID) 618 .addAreaConfig(PASSENGER_SIDE_AREA_ID); 619 } 620 621 private class PropertyHandler implements VehicleHalPropertyHandler { 622 HashMap<Integer, VehiclePropValue> mMap = new HashMap<>(); 623 @Override onPropertySet(VehiclePropValue value)624 public synchronized void onPropertySet(VehiclePropValue value) { 625 // Simulate HalClient.set() behavior. 626 int statusCode = mapPropertyToStatusCode(value.prop); 627 if (statusCode == VehicleHalStatusCode.STATUS_INVALID_ARG) { 628 throw new IllegalArgumentException(); 629 } 630 631 if (statusCode != VehicleHalStatusCode.STATUS_OK) { 632 throw new ServiceSpecificException(statusCode); 633 } 634 635 mMap.put(value.prop, value); 636 } 637 638 @Override onPropertyGet(VehiclePropValue value)639 public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) { 640 // Simulate HalClient.get() behavior. 641 int statusCode = mapPropertyToStatusCode(value.prop); 642 if (statusCode == VehicleHalStatusCode.STATUS_INVALID_ARG) { 643 throw new IllegalArgumentException(); 644 } 645 646 if (statusCode != VehicleHalStatusCode.STATUS_OK) { 647 throw new ServiceSpecificException(statusCode); 648 } 649 650 VehiclePropValue currentValue = mMap.get(value.prop); 651 return currentValue != null ? currentValue : value; 652 } 653 654 @Override onPropertySubscribe(int property, float sampleRate)655 public synchronized void onPropertySubscribe(int property, float sampleRate) { 656 Log.d(TAG, "onPropertySubscribe property " 657 + property + " sampleRate " + sampleRate); 658 } 659 660 @Override onPropertyUnsubscribe(int property)661 public synchronized void onPropertyUnsubscribe(int property) { 662 Log.d(TAG, "onPropertyUnSubscribe property " + property); 663 } 664 } 665 propToString(int propertyId)666 private static String propToString(int propertyId) { 667 return VehiclePropertyIds.toString(propertyId) + " (" + propertyId + ")"; 668 } 669 mapPropertyToStatusCode(int propId)670 private static int mapPropertyToStatusCode(int propId) { 671 switch (propId) { 672 case PROP_CAUSE_STATUS_CODE_TRY_AGAIN: 673 return VehicleHalStatusCode.STATUS_TRY_AGAIN; 674 case PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE: 675 return VehicleHalStatusCode.STATUS_NOT_AVAILABLE; 676 case PROP_CAUSE_STATUS_CODE_ACCESS_DENIED: 677 return VehicleHalStatusCode.STATUS_ACCESS_DENIED; 678 case PROP_CAUSE_STATUS_CODE_INVALID_ARG: 679 return VehicleHalStatusCode.STATUS_INVALID_ARG; 680 case PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR: 681 return VehicleHalStatusCode.STATUS_INTERNAL_ERROR; 682 default: 683 return VehicleHalStatusCode.STATUS_OK; 684 } 685 } 686 687 private static class TestErrorCallback implements CarPropertyManager.CarPropertyEventCallback { 688 689 private static final String CALLBACK_TAG = "ErrorEventTest"; 690 private boolean mReceivedErrorEventWithErrorCode = false; 691 private boolean mReceivedErrorEventWithOutErrorCode = false; 692 private int mErrorCode; 693 private final CountDownLatch mEventsCountDownLatch = new CountDownLatch(1); 694 private final CountDownLatch mRegisterCountDownLatch = new CountDownLatch(2); 695 @Override onChangeEvent(CarPropertyValue value)696 public void onChangeEvent(CarPropertyValue value) { 697 Log.d(CALLBACK_TAG, "onChangeEvent: " + value); 698 mRegisterCountDownLatch.countDown(); 699 } 700 701 @Override onErrorEvent(int propId, int zone)702 public void onErrorEvent(int propId, int zone) { 703 mReceivedErrorEventWithOutErrorCode = true; 704 Log.d(CALLBACK_TAG, "onErrorEvent, propId: " + propId + " zone: " + zone); 705 mEventsCountDownLatch.countDown(); 706 } 707 708 @Override onErrorEvent(int propId, int areaId, int errorCode)709 public void onErrorEvent(int propId, int areaId, int errorCode) { 710 mReceivedErrorEventWithErrorCode = true; 711 mErrorCode = errorCode; 712 Log.d(CALLBACK_TAG, "onErrorEvent, propId: " + propId + " areaId: " + areaId 713 + "errorCode: " + errorCode); 714 mEventsCountDownLatch.countDown(); 715 } 716 assertOnErrorEventCalled()717 public void assertOnErrorEventCalled() throws InterruptedException { 718 if (!mEventsCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 719 throw new IllegalStateException("Callback is not called in " 720 + CALLBACK_SHORT_TIMEOUT_MS + " ms."); 721 } 722 } 723 assertOnErrorEventNotCalled()724 public void assertOnErrorEventNotCalled() throws InterruptedException { 725 if (mEventsCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 726 throw new IllegalStateException("Callback is called in " + CALLBACK_SHORT_TIMEOUT_MS 727 + " ms."); 728 } 729 } 730 assertRegisterCompleted()731 public void assertRegisterCompleted() throws InterruptedException { 732 if (!mRegisterCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 733 throw new IllegalStateException("Register failed in " + CALLBACK_SHORT_TIMEOUT_MS 734 + " ms."); 735 } 736 } 737 } 738 739 private class TestSequenceCallback implements CarPropertyManager.CarPropertyEventCallback { 740 741 private ConcurrentHashMap<Integer, CarPropertyValue> mRecorder = new ConcurrentHashMap<>(); 742 private int mCounter = 0; 743 private final CountDownLatch mEventsCountDownLatch; 744 private final CountDownLatch mRegisterCountDownLatch = new CountDownLatch(2); 745 @Override onChangeEvent(CarPropertyValue value)746 public void onChangeEvent(CarPropertyValue value) { 747 Log.e(TAG, "onChanged get a event " + value); 748 mRecorder.put(value.getPropertyId(), value); 749 mRegisterCountDownLatch.countDown(); 750 // Skip initial events 751 if (value.getTimestamp() != 0) { 752 mCounter++; 753 mEventsCountDownLatch.countDown(); 754 } 755 } 756 TestSequenceCallback(int expectedTimes)757 TestSequenceCallback(int expectedTimes) { 758 mEventsCountDownLatch = new CountDownLatch(expectedTimes); 759 } 760 761 @Override onErrorEvent(int properId, int zone)762 public void onErrorEvent(int properId, int zone) { 763 Log.e(TAG, "TestSequenceCallback get an onErrorEvent"); 764 } 765 getLastCarPropertyValue(int propId)766 public CarPropertyValue getLastCarPropertyValue(int propId) { 767 return mRecorder.get(propId); 768 } 769 getEventCounter()770 public int getEventCounter() { 771 return mCounter; 772 } 773 assertOnChangeEventCalled()774 public void assertOnChangeEventCalled() throws InterruptedException { 775 if (!mEventsCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 776 throw new IllegalStateException("Callback is not called in " 777 + CALLBACK_SHORT_TIMEOUT_MS + " ms."); 778 } 779 } 780 assertRegisterCompleted()781 public void assertRegisterCompleted() throws InterruptedException { 782 if (!mRegisterCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 783 throw new IllegalStateException("Register failed in " + CALLBACK_SHORT_TIMEOUT_MS 784 + " ms."); 785 } 786 } 787 } 788 789 } 790