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 java.util.Collection<Integer> CONFIG_ARRAY_3 = 93 Arrays.asList(0, 1, 1, 0, 0, 0, 1, 0, 0); 94 private static final Object[] EXPECTED_VALUE_1 = {"android", 1, 1L}; 95 private static final Object[] EXPECTED_VALUE_2 = {"android", true, 3, 1.1f, 2f}; 96 private static final Object[] EXPECTED_VALUE_3 = {true, 1, 2.2f}; 97 98 private static final int CUSTOM_SEAT_INT_PROP_1 = 99 0x1201 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.SEAT; 100 private static final int CUSTOM_SEAT_INT_PROP_2 = 101 0x1202 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.SEAT; 102 103 private static final int CUSTOM_SEAT_MIXED_PROP_ID_1 = 104 0x1101 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.MIXED | VehicleArea.SEAT; 105 private static final int CUSTOM_GLOBAL_MIXED_PROP_ID_2 = 106 0x1102 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.MIXED | VehicleArea.GLOBAL; 107 private static final int CUSTOM_GLOBAL_MIXED_PROP_ID_3 = 108 0x1110 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.MIXED | VehicleArea.GLOBAL; 109 110 private static final int CUSTOM_GLOBAL_INT_ARRAY_PROP = 111 0x1103 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32_VEC 112 | VehicleArea.GLOBAL; 113 private static final Integer[] FAKE_INT_ARRAY_VALUE = {1, 2}; 114 115 private static final int INT_ARRAY_PROP_STATUS_ERROR = 116 0x1104 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32_VEC 117 | VehicleArea.GLOBAL; 118 119 private static final int BOOLEAN_PROP_STATUS_ERROR = 120 0x1105 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.BOOLEAN 121 | VehicleArea.GLOBAL; 122 private static final boolean FAKE_BOOLEAN_PROPERTY_VALUE = true; 123 private static final int FLOAT_PROP_STATUS_UNAVAILABLE = 124 0x1106 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.FLOAT 125 | VehicleArea.GLOBAL; 126 private static final float FAKE_FLOAT_PROPERTY_VALUE = 3f; 127 private static final int INT_PROP_STATUS_UNAVAILABLE = 128 0x1107 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 129 | VehicleArea.GLOBAL; 130 private static final int FAKE_INT_PROPERTY_VALUE = 3; 131 132 // Vendor properties for testing exceptions. 133 private static final int PROP_CAUSE_STATUS_CODE_TRY_AGAIN = 134 0x1201 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL; 135 private static final int PROP_CAUSE_STATUS_CODE_INVALID_ARG = 136 0x1202 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL; 137 private static final int PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE = 138 0x1203 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL; 139 private static final int PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR = 140 0x1204 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL; 141 private static final int PROP_CAUSE_STATUS_CODE_ACCESS_DENIED = 142 0x1205 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL; 143 144 // Use FAKE_PROPERTY_ID to test api return null or throw exception. 145 private static final int FAKE_PROPERTY_ID = 0x111; 146 147 private static final int DRIVER_SIDE_AREA_ID = VehicleAreaSeat.ROW_1_LEFT 148 | VehicleAreaSeat.ROW_2_LEFT; 149 private static final int PASSENGER_SIDE_AREA_ID = VehicleAreaSeat.ROW_1_RIGHT 150 | VehicleAreaSeat.ROW_2_CENTER 151 | VehicleAreaSeat.ROW_2_RIGHT; 152 private static final float INIT_TEMP_VALUE = 16f; 153 private static final float CHANGED_TEMP_VALUE = 20f; 154 private static final int CALLBACK_SHORT_TIMEOUT_MS = 350; // ms 155 // Wait for CarPropertyManager register/unregister listener 156 private static final long WAIT_FOR_NO_EVENTS = 50; 157 158 private static final List<Integer> USER_HAL_PROPERTIES = Arrays.asList( 159 VehiclePropertyIds.INITIAL_USER_INFO, 160 VehiclePropertyIds.SWITCH_USER, 161 VehiclePropertyIds.CREATE_USER, 162 VehiclePropertyIds.REMOVE_USER, 163 VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION 164 ); 165 166 private CarPropertyManager mManager; 167 168 @Rule public TestName mTestName = new TestName(); 169 170 @Override setUp()171 public void setUp() throws Exception { 172 super.setUp(); 173 setUpTargetSdk(); 174 mManager = (CarPropertyManager) getCar().getCarManager(Car.PROPERTY_SERVICE); 175 assertThat(mManager).isNotNull(); 176 } 177 setUpTargetSdk()178 private void setUpTargetSdk() { 179 if (mTestName.getMethodName().endsWith("InQ")) { 180 getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.Q; 181 } else if (mTestName.getMethodName().endsWith("AfterQ")) { 182 getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.R; 183 } else if (mTestName.getMethodName().endsWith("AfterR")) { 184 getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.S; 185 } 186 } 187 188 @Test testMixedPropertyConfigs()189 public void testMixedPropertyConfigs() { 190 List<CarPropertyConfig> configs = mManager.getPropertyList(); 191 for (CarPropertyConfig cfg : configs) { 192 switch (cfg.getPropertyId()) { 193 case CUSTOM_SEAT_MIXED_PROP_ID_1: 194 assertThat(cfg.getConfigArray()).containsExactlyElementsIn(CONFIG_ARRAY_1) 195 .inOrder(); 196 break; 197 case CUSTOM_GLOBAL_MIXED_PROP_ID_2: 198 assertThat(cfg.getConfigArray()).containsExactlyElementsIn(CONFIG_ARRAY_2) 199 .inOrder(); 200 break; 201 case CUSTOM_GLOBAL_MIXED_PROP_ID_3: 202 assertThat(cfg.getConfigArray()).containsExactlyElementsIn(CONFIG_ARRAY_3) 203 .inOrder(); 204 break; 205 case VehiclePropertyIds.HVAC_TEMPERATURE_SET: 206 case PROP_CAUSE_STATUS_CODE_ACCESS_DENIED: 207 case PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR: 208 case PROP_CAUSE_STATUS_CODE_TRY_AGAIN: 209 case PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE: 210 case PROP_CAUSE_STATUS_CODE_INVALID_ARG: 211 case CUSTOM_SEAT_INT_PROP_1: 212 case CUSTOM_SEAT_INT_PROP_2: 213 case CUSTOM_GLOBAL_INT_ARRAY_PROP: 214 case INT_ARRAY_PROP_STATUS_ERROR: 215 case BOOLEAN_PROP_STATUS_ERROR: 216 case INT_PROP_STATUS_UNAVAILABLE: 217 case FLOAT_PROP_STATUS_UNAVAILABLE: 218 case VehiclePropertyIds.INFO_VIN: 219 break; 220 default: 221 Assert.fail("Unexpected CarPropertyConfig: " + cfg.toString()); 222 } 223 } 224 } 225 226 @Test testGetMixTypeProperty()227 public void testGetMixTypeProperty() { 228 mManager.setProperty(Object[].class, CUSTOM_SEAT_MIXED_PROP_ID_1, 229 0, EXPECTED_VALUE_1); 230 CarPropertyValue<Object[]> result = mManager.getProperty( 231 CUSTOM_SEAT_MIXED_PROP_ID_1, 0); 232 assertThat(result.getValue()).isEqualTo(EXPECTED_VALUE_1); 233 234 mManager.setProperty(Object[].class, CUSTOM_GLOBAL_MIXED_PROP_ID_2, 235 0, EXPECTED_VALUE_2); 236 result = mManager.getProperty( 237 CUSTOM_GLOBAL_MIXED_PROP_ID_2, 0); 238 assertThat(result.getValue()).isEqualTo(EXPECTED_VALUE_2); 239 240 mManager.setProperty(Object[].class, CUSTOM_GLOBAL_MIXED_PROP_ID_3, 241 0, EXPECTED_VALUE_3); 242 result = mManager.getProperty( 243 CUSTOM_GLOBAL_MIXED_PROP_ID_3, 0); 244 assertThat(result.getValue()).isEqualTo(EXPECTED_VALUE_3); 245 } 246 247 /** 248 * Test {@link android.car.hardware.property.CarPropertyManager#getIntArrayProperty(int, int)} 249 */ 250 @Test testGetIntArrayProperty()251 public void testGetIntArrayProperty() { 252 mManager.setProperty(Integer[].class, CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL, 253 FAKE_INT_ARRAY_VALUE); 254 255 int[] result = mManager.getIntArrayProperty(CUSTOM_GLOBAL_INT_ARRAY_PROP, 256 VehicleArea.GLOBAL); 257 assertThat(result).asList().containsExactlyElementsIn(FAKE_INT_ARRAY_VALUE); 258 } 259 260 /** 261 * Test {@link CarPropertyManager#getIntArrayProperty(int, int)} when vhal returns a value with 262 * error status. 263 */ 264 @Test testGetIntArrayPropertyWithErrorStatusAfterR()265 public void testGetIntArrayPropertyWithErrorStatusAfterR() { 266 Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion) 267 .isGreaterThan(Build.VERSION_CODES.R); 268 mManager.setProperty(Integer[].class, INT_ARRAY_PROP_STATUS_ERROR, 269 VehicleArea.GLOBAL, FAKE_INT_ARRAY_VALUE); 270 assertThrows(CarInternalErrorException.class, 271 () -> mManager.getIntArrayProperty(INT_ARRAY_PROP_STATUS_ERROR, 272 VehicleArea.GLOBAL)); 273 } 274 275 /** 276 * Test {@link CarPropertyManager#getIntProperty(int, int)} when vhal returns a value with 277 * unavailable status. 278 */ 279 @Test testGetIntPropertyWithUnavailableStatusAfterR()280 public void testGetIntPropertyWithUnavailableStatusAfterR() { 281 Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion) 282 .isGreaterThan(Build.VERSION_CODES.R); 283 mManager.setProperty(Integer.class, INT_PROP_STATUS_UNAVAILABLE, 284 VehicleArea.GLOBAL, FAKE_INT_PROPERTY_VALUE); 285 assertThrows(PropertyNotAvailableException.class, 286 () -> mManager.getIntProperty(INT_PROP_STATUS_UNAVAILABLE, VehicleArea.GLOBAL)); 287 288 } 289 290 /** 291 * Test {@link CarPropertyManager#getBooleanProperty(int, int)} when vhal returns a value with 292 * error status. 293 */ 294 @Test testGetBooleanPropertyWithErrorStatusAfterR()295 public void testGetBooleanPropertyWithErrorStatusAfterR() { 296 Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion) 297 .isGreaterThan(Build.VERSION_CODES.R); 298 mManager.setProperty(Boolean.class, BOOLEAN_PROP_STATUS_ERROR, 299 VehicleArea.GLOBAL, FAKE_BOOLEAN_PROPERTY_VALUE); 300 assertThrows(CarInternalErrorException.class, 301 () -> mManager.getBooleanProperty(BOOLEAN_PROP_STATUS_ERROR, VehicleArea.GLOBAL)); 302 } 303 304 /** 305 * Test {@link CarPropertyManager#getFloatProperty(int, int)} when vhal returns a value with 306 * unavailable status. 307 */ 308 @Test testGetFloatPropertyWithUnavailableStatusAfterR()309 public void testGetFloatPropertyWithUnavailableStatusAfterR() { 310 Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion) 311 .isGreaterThan(Build.VERSION_CODES.R); 312 mManager.setProperty(Float.class, FLOAT_PROP_STATUS_UNAVAILABLE, 313 VehicleArea.GLOBAL, FAKE_FLOAT_PROPERTY_VALUE); 314 assertThrows(PropertyNotAvailableException.class, 315 () -> mManager.getFloatProperty(FLOAT_PROP_STATUS_UNAVAILABLE, VehicleArea.GLOBAL)); 316 } 317 318 /** 319 * Test {@link CarPropertyManager#getProperty(Class, int, int)} 320 */ 321 @Test testGetPropertyWithClass()322 public void testGetPropertyWithClass() { 323 mManager.setProperty(Integer[].class, CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL, 324 FAKE_INT_ARRAY_VALUE); 325 326 CarPropertyValue<Integer[]> result = mManager.getProperty(Integer[].class, 327 CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL); 328 assertThat(result.getValue()).asList().containsExactlyElementsIn(FAKE_INT_ARRAY_VALUE); 329 } 330 331 /** 332 * Test {@link CarPropertyManager#isPropertyAvailable(int, int)} 333 */ 334 @Test testIsPropertyAvailable()335 public void testIsPropertyAvailable() { 336 assertThat(mManager.isPropertyAvailable(FAKE_PROPERTY_ID, VehicleArea.GLOBAL)).isFalse(); 337 assertThat(mManager.isPropertyAvailable(CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL)) 338 .isTrue(); 339 } 340 341 /** 342 * Test {@link CarPropertyManager#getWritePermission(int)} 343 * and {@link CarPropertyManager#getWritePermission(int)} 344 */ 345 @Test testGetPermission()346 public void testGetPermission() { 347 String hvacReadPermission = mManager.getReadPermission( 348 VehiclePropertyIds.HVAC_TEMPERATURE_SET); 349 assertThat(hvacReadPermission).isEqualTo(Car.PERMISSION_CONTROL_CAR_CLIMATE); 350 String hvacWritePermission = mManager.getWritePermission( 351 VehiclePropertyIds.HVAC_TEMPERATURE_SET); 352 assertThat(hvacWritePermission).isEqualTo(Car.PERMISSION_CONTROL_CAR_CLIMATE); 353 354 // For read-only property 355 String vinReadPermission = mManager.getReadPermission(VehiclePropertyIds.INFO_VIN); 356 assertThat(vinReadPermission).isEqualTo(Car.PERMISSION_IDENTIFICATION); 357 String vinWritePermission = mManager.getWritePermission(VehiclePropertyIds.INFO_VIN); 358 assertThat(vinWritePermission).isNull(); 359 } 360 361 @Test testGetPropertyConfig()362 public void testGetPropertyConfig() { 363 CarPropertyConfig config = mManager.getCarPropertyConfig(CUSTOM_SEAT_MIXED_PROP_ID_1); 364 assertThat(config.getPropertyId()).isEqualTo(CUSTOM_SEAT_MIXED_PROP_ID_1); 365 // return null if can not find the propertyConfig for the property. 366 assertThat(mManager.getCarPropertyConfig(FAKE_PROPERTY_ID)).isNull(); 367 } 368 369 @Test testGetAreaId()370 public void testGetAreaId() { 371 int result = mManager.getAreaId(CUSTOM_SEAT_MIXED_PROP_ID_1, VehicleAreaSeat.ROW_1_LEFT); 372 assertThat(result).isEqualTo(DRIVER_SIDE_AREA_ID); 373 //test for the GLOBAL property 374 int globalAreaId = 375 mManager.getAreaId(CUSTOM_GLOBAL_MIXED_PROP_ID_2, VehicleAreaSeat.ROW_1_LEFT); 376 assertThat(globalAreaId).isEqualTo(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL); 377 //test exception 378 assertThrows(IllegalArgumentException.class, () -> mManager.getAreaId( 379 CUSTOM_SEAT_MIXED_PROP_ID_1, VehicleAreaSeat.ROW_3_CENTER)); 380 assertThrows(IllegalArgumentException.class, () -> mManager.getAreaId(FAKE_PROPERTY_ID, 381 VehicleAreaSeat.ROW_1_LEFT)); 382 } 383 384 @Test testRegisterPropertyUnavailable()385 public void testRegisterPropertyUnavailable() throws Exception { 386 TestSequenceCallback callback = new TestSequenceCallback(1); 387 // Registering a property which has an unavailable initial value 388 // won't throw ServiceSpecificException. 389 mManager.registerCallback(callback, PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, 390 CarPropertyManager.SENSOR_RATE_ONCHANGE); 391 // initial value is unavailable, should not get any callback. 392 assertThrows(IllegalStateException.class, callback::assertOnChangeEventCalled); 393 } 394 395 @Test testNotReceiveOnErrorEvent()396 public void testNotReceiveOnErrorEvent() throws Exception { 397 TestErrorCallback callback = new TestErrorCallback(); 398 mManager.registerCallback(callback, VehiclePropertyIds.HVAC_TEMPERATURE_SET, 399 CarPropertyManager.SENSOR_RATE_ONCHANGE); 400 callback.assertRegisterCompleted(); 401 injectErrorEvent(VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID, 402 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN); 403 // app never change the value of HVAC_TEMPERATURE_SET, it won't get an error code. 404 callback.assertOnErrorEventNotCalled(); 405 } 406 407 @Test testReceiveOnErrorEvent()408 public void testReceiveOnErrorEvent() throws Exception { 409 TestErrorCallback callback = new TestErrorCallback(); 410 mManager.registerCallback(callback, VehiclePropertyIds.HVAC_TEMPERATURE_SET, 411 CarPropertyManager.SENSOR_RATE_ONCHANGE); 412 callback.assertRegisterCompleted(); 413 mManager.setFloatProperty( 414 VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID, 415 CHANGED_TEMP_VALUE); 416 injectErrorEvent(VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID, 417 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN); 418 callback.assertOnErrorEventCalled(); 419 assertThat(callback.mReceivedErrorEventWithErrorCode).isTrue(); 420 assertThat(callback.mErrorCode).isEqualTo( 421 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN); 422 assertThat(callback.mReceivedErrorEventWithOutErrorCode).isFalse(); 423 } 424 425 @Test testNotReceiveOnErrorEventAfterUnregister()426 public void testNotReceiveOnErrorEventAfterUnregister() throws Exception { 427 TestErrorCallback callback1 = new TestErrorCallback(); 428 mManager.registerCallback(callback1, VehiclePropertyIds.HVAC_TEMPERATURE_SET, 429 CarPropertyManager.SENSOR_RATE_ONCHANGE); 430 callback1.assertRegisterCompleted(); 431 TestErrorCallback callback2 = new TestErrorCallback(); 432 mManager.registerCallback(callback2, VehiclePropertyIds.HVAC_TEMPERATURE_SET, 433 CarPropertyManager.SENSOR_RATE_ONCHANGE); 434 mManager.setFloatProperty( 435 VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID, 436 CHANGED_TEMP_VALUE); 437 mManager.unregisterCallback(callback1, VehiclePropertyIds.HVAC_TEMPERATURE_SET); 438 SystemClock.sleep(WAIT_FOR_NO_EVENTS); 439 injectErrorEvent(VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID, 440 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN); 441 // callback1 is unregistered 442 callback1.assertOnErrorEventNotCalled(); 443 callback2.assertOnErrorEventCalled(); 444 } 445 @Test testSetterExceptionsInQ()446 public void testSetterExceptionsInQ() { 447 Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion) 448 .isEqualTo(Build.VERSION_CODES.Q); 449 450 assertThrows(IllegalStateException.class, 451 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 452 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 453 assertThrows(IllegalStateException.class, 454 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, 455 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 456 assertThrows(IllegalStateException.class, 457 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, 458 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 459 assertThrows(IllegalArgumentException.class, 460 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INVALID_ARG, 461 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 462 assertThrows(RuntimeException.class, 463 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 464 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 465 } 466 467 @Test testSetterExceptionsAfterQ()468 public void testSetterExceptionsAfterQ() { 469 Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion) 470 .isGreaterThan(Build.VERSION_CODES.Q); 471 472 assertThrows(PropertyAccessDeniedSecurityException.class, 473 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 474 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 475 assertThrows(PropertyNotAvailableAndRetryException.class, 476 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_TRY_AGAIN, 477 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 478 assertThrows(PropertyNotAvailableException.class, 479 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, 480 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 481 assertThrows(CarInternalErrorException.class, 482 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, 483 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 484 assertThrows(IllegalArgumentException.class, 485 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INVALID_ARG, 486 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1)); 487 } 488 489 @Test testGetterExceptionsInQ()490 public void testGetterExceptionsInQ() { 491 Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion) 492 .isEqualTo(Build.VERSION_CODES.Q); 493 494 assertThrows(IllegalStateException.class, 495 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 496 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 497 assertThrows(IllegalStateException.class, 498 ()->mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 499 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 500 501 assertThrows(IllegalArgumentException.class, 502 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG, 503 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 504 assertThrows(IllegalArgumentException.class, 505 ()->mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG, 506 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 507 508 assertThrows(IllegalStateException.class, 509 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, 510 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 511 assertThrows(IllegalStateException.class, 512 ()->mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, 513 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 514 515 assertThrows(IllegalStateException.class, 516 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, 517 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 518 assertThrows(IllegalStateException.class, 519 ()->mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, 520 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 521 522 Truth.assertThat(mManager.getProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN, 523 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)).isNull(); 524 } 525 526 @Test testGetterExceptionsAfterQ()527 public void testGetterExceptionsAfterQ() { 528 Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion) 529 .isAtLeast(Build.VERSION_CODES.R); 530 531 assertThrows(PropertyAccessDeniedSecurityException.class, 532 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 533 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 534 assertThrows(PropertyAccessDeniedSecurityException.class, 535 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 536 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 537 538 assertThrows(IllegalArgumentException.class, 539 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG, 540 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 541 assertThrows(IllegalArgumentException.class, 542 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG, 543 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 544 545 assertThrows(PropertyNotAvailableAndRetryException.class, 546 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN, 547 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 548 assertThrows(PropertyNotAvailableAndRetryException.class, 549 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN, 550 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 551 552 assertThrows(PropertyNotAvailableException.class, 553 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, 554 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 555 assertThrows(PropertyNotAvailableException.class, 556 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, 557 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 558 559 assertThrows(CarInternalErrorException.class, 560 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, 561 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 562 assertThrows(CarInternalErrorException.class, 563 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, 564 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 565 } 566 567 @Test testOnChangeEventWithSameAreaId()568 public void testOnChangeEventWithSameAreaId() throws Exception { 569 // init 570 mManager.setProperty(Integer.class, 571 CUSTOM_SEAT_INT_PROP_1, DRIVER_SIDE_AREA_ID, 1); 572 TestSequenceCallback callback = new TestSequenceCallback(1); 573 mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_1, 0); 574 callback.assertRegisterCompleted(); 575 576 VehiclePropValue firstFakeValueDriveSide = new VehiclePropValue(); 577 firstFakeValueDriveSide.prop = CUSTOM_SEAT_INT_PROP_1; 578 firstFakeValueDriveSide.areaId = DRIVER_SIDE_AREA_ID; 579 firstFakeValueDriveSide.value.int32Values.add(2); 580 firstFakeValueDriveSide.timestamp = SystemClock.elapsedRealtimeNanos(); 581 VehiclePropValue secFakeValueDriveSide = new VehiclePropValue(); 582 secFakeValueDriveSide.prop = CUSTOM_SEAT_INT_PROP_1; 583 secFakeValueDriveSide.areaId = DRIVER_SIDE_AREA_ID; 584 secFakeValueDriveSide.value.int32Values.add(3); // 0 in HAL indicate false; 585 secFakeValueDriveSide.timestamp = SystemClock.elapsedRealtimeNanos(); 586 // inject the new event first 587 getMockedVehicleHal().injectEvent(secFakeValueDriveSide); 588 // inject the old event 589 getMockedVehicleHal().injectEvent(firstFakeValueDriveSide); 590 callback.assertOnChangeEventCalled(); 591 // Client should only get the new event 592 assertThat((int) callback.getLastCarPropertyValue(CUSTOM_SEAT_INT_PROP_1).getValue()) 593 .isEqualTo(3); 594 assertThat(callback.getEventCounter()).isEqualTo(1); 595 596 } 597 598 @Test testOnChangeEventWithDifferentAreaId()599 public void testOnChangeEventWithDifferentAreaId() throws Exception { 600 // init 601 mManager.setProperty(Integer.class, 602 CUSTOM_SEAT_INT_PROP_2, DRIVER_SIDE_AREA_ID, 1); 603 TestSequenceCallback callback = new TestSequenceCallback(2); 604 mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_2, 0); 605 callback.assertRegisterCompleted(); 606 VehiclePropValue fakeValueDriveSide = new VehiclePropValue(); 607 fakeValueDriveSide.prop = CUSTOM_SEAT_INT_PROP_2; 608 fakeValueDriveSide.areaId = DRIVER_SIDE_AREA_ID; 609 fakeValueDriveSide.value.int32Values.add(4); 610 fakeValueDriveSide.timestamp = SystemClock.elapsedRealtimeNanos(); 611 612 VehiclePropValue fakeValuePsgSide = new VehiclePropValue(); 613 fakeValuePsgSide.prop = CUSTOM_SEAT_INT_PROP_2; 614 fakeValuePsgSide.areaId = PASSENGER_SIDE_AREA_ID; 615 fakeValuePsgSide.value.int32Values.add(5); 616 fakeValuePsgSide.timestamp = SystemClock.elapsedRealtimeNanos(); 617 618 // inject passenger event before driver event 619 getMockedVehicleHal().injectEvent(fakeValuePsgSide); 620 getMockedVehicleHal().injectEvent(fakeValueDriveSide); 621 callback.assertOnChangeEventCalled(); 622 623 // both events should be received by listener 624 assertThat((int) callback.getLastCarPropertyValue(CUSTOM_SEAT_INT_PROP_2).getValue()) 625 .isEqualTo(4); 626 assertThat(callback.getEventCounter()).isEqualTo(2); 627 } 628 629 @Test testUserHal_getProperty()630 public void testUserHal_getProperty() { 631 userHalPropertiesTest("getProperty()", (prop) -> 632 mManager.getProperty(prop, /* areaId= */ 0)); 633 } 634 635 @Test testUserHal_getBooleanProperty()636 public void testUserHal_getBooleanProperty() { 637 userHalPropertiesTest("getBooleanProperty()", (prop) -> 638 mManager.getBooleanProperty(prop, /* areaId= */ 0)); 639 } 640 641 @Test testUserHal_getIntProperty()642 public void testUserHal_getIntProperty() { 643 userHalPropertiesTest("getIntProperty()", (prop) -> 644 mManager.getIntProperty(prop, /* areaId= */ 0)); 645 } 646 647 @Test testUserHal_getIntArrayProperty()648 public void testUserHal_getIntArrayProperty() { 649 userHalPropertiesTest("getIntArrayProperty()", (prop) -> 650 mManager.getIntArrayProperty(prop, /* areaId= */ 0)); 651 } 652 653 @Test testUserHal_getFloatProperty()654 public void testUserHal_getFloatProperty() { 655 userHalPropertiesTest("getFloatProperty()", (prop) -> 656 mManager.getFloatProperty(prop, /* areaId= */ 0)); 657 } 658 659 @Test testUserHal_getPropertyList()660 public void testUserHal_getPropertyList() { 661 userHalPropertiesTest("getPropertyList()", (prop) -> { 662 ArraySet<Integer> list = new ArraySet<>(); 663 list.add(prop); 664 mManager.getPropertyList(list); 665 }); 666 } 667 668 @Test testUserHal_getCarPropertyConfig()669 public void testUserHal_getCarPropertyConfig() { 670 userHalPropertiesTest("getCarPropertyConfig()", (prop) -> 671 mManager.getCarPropertyConfig(prop)); 672 } 673 674 @Test testUserHal_getAreaId()675 public void testUserHal_getAreaId() { 676 userHalPropertiesTest("getAreaId()", (prop) -> 677 mManager.getAreaId(prop, /* areaId= */ 0)); 678 } 679 680 @Test testUserHal_getReadPermission()681 public void testUserHal_getReadPermission() { 682 userHalPropertiesTest("getReadPermission()", (prop) -> 683 mManager.getReadPermission(prop)); 684 } 685 686 @Test testUserHal_getWritePermission()687 public void testUserHal_getWritePermission() { 688 userHalPropertiesTest("getWritePermission()", (prop) -> 689 mManager.getWritePermission(prop)); 690 } 691 692 @Test testUserHal_isPropertyAvailable()693 public void testUserHal_isPropertyAvailable() { 694 userHalPropertiesTest("isPropertyAvailable()", (prop) -> 695 mManager.isPropertyAvailable(prop, /* area= */ 0)); 696 } 697 698 @Test testUserHal_setProperty()699 public void testUserHal_setProperty() { 700 userHalPropertiesTest("setProperty()", (prop) -> 701 mManager.setProperty(Object.class, prop, /* areaId= */ 0, /* val= */ null)); 702 } 703 704 @Test testUserHal_setBooleanProperty()705 public void testUserHal_setBooleanProperty() { 706 userHalPropertiesTest("setBooleanProperty()", (prop) -> 707 mManager.setBooleanProperty(prop, /* areaId= */ 0, /* val= */ true)); 708 } 709 710 @Test testUserHal_setFloatProperty()711 public void testUserHal_setFloatProperty() { 712 userHalPropertiesTest("setFloatProperty()", (prop) -> 713 mManager.setFloatProperty(prop, /* areaId= */ 0, /* val= */ 0.0F)); 714 } 715 716 @Test testUserHal_setIntProperty()717 public void testUserHal_setIntProperty() { 718 userHalPropertiesTest("setIntProperty()", (prop) -> 719 mManager.setIntProperty(prop, /* areaId= */ 0, /* val= */ 0)); 720 } 721 userHalPropertiesTest(String method, Visitor<Integer> visitor)722 private void userHalPropertiesTest(String method, Visitor<Integer> visitor) { 723 List<String> failedProperties = new ArrayList<String>(); 724 for (int propertyId : USER_HAL_PROPERTIES) { 725 try { 726 visitor.visit(propertyId); 727 failedProperties.add(propToString(propertyId)); 728 } catch (IllegalArgumentException e) { 729 // expected 730 } 731 } 732 if (!failedProperties.isEmpty()) { 733 fail(method + " should not support these properties: " + failedProperties); 734 } 735 } 736 737 @Override configureMockedHal()738 protected synchronized void configureMockedHal() { 739 PropertyHandler handler = new PropertyHandler(); 740 addProperty(CUSTOM_SEAT_MIXED_PROP_ID_1, handler).setConfigArray(CONFIG_ARRAY_1) 741 .addAreaConfig(DRIVER_SIDE_AREA_ID).addAreaConfig(PASSENGER_SIDE_AREA_ID); 742 addProperty(CUSTOM_GLOBAL_MIXED_PROP_ID_2, handler).setConfigArray(CONFIG_ARRAY_2); 743 addProperty(CUSTOM_GLOBAL_MIXED_PROP_ID_3, handler).setConfigArray(CONFIG_ARRAY_3); 744 addProperty(CUSTOM_GLOBAL_INT_ARRAY_PROP, handler); 745 746 addProperty(INT_ARRAY_PROP_STATUS_ERROR, handler); 747 addProperty(INT_PROP_STATUS_UNAVAILABLE, handler); 748 addProperty(FLOAT_PROP_STATUS_UNAVAILABLE, handler); 749 addProperty(BOOLEAN_PROP_STATUS_ERROR, handler); 750 751 VehiclePropValue tempValue = new VehiclePropValue(); 752 tempValue.value.floatValues.add(INIT_TEMP_VALUE); 753 tempValue.prop = VehiclePropertyIds.HVAC_TEMPERATURE_SET; 754 addProperty(VehiclePropertyIds.HVAC_TEMPERATURE_SET, tempValue) 755 .addAreaConfig(DRIVER_SIDE_AREA_ID).addAreaConfig(PASSENGER_SIDE_AREA_ID); 756 addProperty(VehiclePropertyIds.INFO_VIN); 757 758 addProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, handler); 759 addProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN, handler); 760 addProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, handler); 761 addProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG, handler); 762 addProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, handler); 763 764 addProperty(CUSTOM_SEAT_INT_PROP_1, handler).addAreaConfig(DRIVER_SIDE_AREA_ID) 765 .addAreaConfig(PASSENGER_SIDE_AREA_ID); 766 addProperty(CUSTOM_SEAT_INT_PROP_2, handler).addAreaConfig(DRIVER_SIDE_AREA_ID) 767 .addAreaConfig(PASSENGER_SIDE_AREA_ID); 768 } 769 770 private class PropertyHandler implements VehicleHalPropertyHandler { 771 HashMap<Integer, VehiclePropValue> mMap = new HashMap<>(); 772 @Override onPropertySet(VehiclePropValue value)773 public synchronized void onPropertySet(VehiclePropValue value) { 774 // Simulate HalClient.set() behavior. 775 int statusCode = mapPropertyToVhalStatusCode(value.prop); 776 if (statusCode == VehicleHalStatusCode.STATUS_INVALID_ARG) { 777 throw new IllegalArgumentException(); 778 } 779 780 if (statusCode != VehicleHalStatusCode.STATUS_OK) { 781 throw new ServiceSpecificException(statusCode); 782 } 783 784 mMap.put(value.prop, value); 785 } 786 787 @Override onPropertyGet(VehiclePropValue value)788 public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) { 789 // Simulate HalClient.get() behavior. 790 int vhalStatusCode = mapPropertyToVhalStatusCode(value.prop); 791 if (vhalStatusCode == VehicleHalStatusCode.STATUS_INVALID_ARG) { 792 throw new IllegalArgumentException(); 793 } 794 795 if (vhalStatusCode != VehicleHalStatusCode.STATUS_OK) { 796 throw new ServiceSpecificException(vhalStatusCode); 797 } 798 799 int propertyStatus = mapPropertyToCarPropertyStatusCode(value.prop); 800 VehiclePropValue currentValue = mMap.get(value.prop); 801 if (currentValue == null) { 802 return value; 803 } else { 804 currentValue.status = propertyStatus; 805 } 806 return currentValue; 807 } 808 809 @Override onPropertySubscribe(int property, float sampleRate)810 public synchronized void onPropertySubscribe(int property, float sampleRate) { 811 Log.d(TAG, "onPropertySubscribe property " 812 + property + " sampleRate " + sampleRate); 813 } 814 815 @Override onPropertyUnsubscribe(int property)816 public synchronized void onPropertyUnsubscribe(int property) { 817 Log.d(TAG, "onPropertyUnSubscribe property " + property); 818 } 819 } 820 propToString(int propertyId)821 private static String propToString(int propertyId) { 822 return VehiclePropertyIds.toString(propertyId) + " (" + propertyId + ")"; 823 } 824 mapPropertyToVhalStatusCode(int propId)825 private static int mapPropertyToVhalStatusCode(int propId) { 826 switch (propId) { 827 case PROP_CAUSE_STATUS_CODE_TRY_AGAIN: 828 return VehicleHalStatusCode.STATUS_TRY_AGAIN; 829 case PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE: 830 return VehicleHalStatusCode.STATUS_NOT_AVAILABLE; 831 case PROP_CAUSE_STATUS_CODE_ACCESS_DENIED: 832 return VehicleHalStatusCode.STATUS_ACCESS_DENIED; 833 case PROP_CAUSE_STATUS_CODE_INVALID_ARG: 834 return VehicleHalStatusCode.STATUS_INVALID_ARG; 835 case PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR: 836 return VehicleHalStatusCode.STATUS_INTERNAL_ERROR; 837 default: 838 return VehicleHalStatusCode.STATUS_OK; 839 } 840 } 841 mapPropertyToCarPropertyStatusCode(int propId)842 private static int mapPropertyToCarPropertyStatusCode(int propId) { 843 switch (propId) { 844 case INT_ARRAY_PROP_STATUS_ERROR: 845 case BOOLEAN_PROP_STATUS_ERROR: 846 return CarPropertyValue.STATUS_ERROR; 847 case INT_PROP_STATUS_UNAVAILABLE: 848 case FLOAT_PROP_STATUS_UNAVAILABLE: 849 return CarPropertyValue.STATUS_UNAVAILABLE; 850 default: 851 return CarPropertyValue.STATUS_AVAILABLE; 852 } 853 } 854 855 private static class TestErrorCallback implements CarPropertyManager.CarPropertyEventCallback { 856 857 private static final String CALLBACK_TAG = "ErrorEventTest"; 858 private boolean mReceivedErrorEventWithErrorCode = false; 859 private boolean mReceivedErrorEventWithOutErrorCode = false; 860 private int mErrorCode; 861 private final CountDownLatch mEventsCountDownLatch = new CountDownLatch(1); 862 private final CountDownLatch mRegisterCountDownLatch = new CountDownLatch(2); 863 @Override onChangeEvent(CarPropertyValue value)864 public void onChangeEvent(CarPropertyValue value) { 865 Log.d(CALLBACK_TAG, "onChangeEvent: " + value); 866 mRegisterCountDownLatch.countDown(); 867 } 868 869 @Override onErrorEvent(int propId, int zone)870 public void onErrorEvent(int propId, int zone) { 871 mReceivedErrorEventWithOutErrorCode = true; 872 Log.d(CALLBACK_TAG, "onErrorEvent, propId: " + propId + " zone: " + zone); 873 mEventsCountDownLatch.countDown(); 874 } 875 876 @Override onErrorEvent(int propId, int areaId, int errorCode)877 public void onErrorEvent(int propId, int areaId, int errorCode) { 878 mReceivedErrorEventWithErrorCode = true; 879 mErrorCode = errorCode; 880 Log.d(CALLBACK_TAG, "onErrorEvent, propId: " + propId + " areaId: " + areaId 881 + "errorCode: " + errorCode); 882 mEventsCountDownLatch.countDown(); 883 } 884 assertOnErrorEventCalled()885 public void assertOnErrorEventCalled() throws InterruptedException { 886 if (!mEventsCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 887 throw new IllegalStateException("Callback is not called in " 888 + CALLBACK_SHORT_TIMEOUT_MS + " ms."); 889 } 890 } 891 assertOnErrorEventNotCalled()892 public void assertOnErrorEventNotCalled() throws InterruptedException { 893 if (mEventsCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 894 throw new IllegalStateException("Callback is called in " + CALLBACK_SHORT_TIMEOUT_MS 895 + " ms."); 896 } 897 } 898 assertRegisterCompleted()899 public void assertRegisterCompleted() throws InterruptedException { 900 if (!mRegisterCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 901 throw new IllegalStateException("Register failed in " + CALLBACK_SHORT_TIMEOUT_MS 902 + " ms."); 903 } 904 } 905 } 906 907 private class TestSequenceCallback implements CarPropertyManager.CarPropertyEventCallback { 908 909 private ConcurrentHashMap<Integer, CarPropertyValue> mRecorder = new ConcurrentHashMap<>(); 910 private int mCounter = 0; 911 private final CountDownLatch mEventsCountDownLatch; 912 private final CountDownLatch mRegisterCountDownLatch = new CountDownLatch(2); 913 @Override onChangeEvent(CarPropertyValue value)914 public void onChangeEvent(CarPropertyValue value) { 915 Log.e(TAG, "onChanged get a event " + value); 916 mRecorder.put(value.getPropertyId(), value); 917 mRegisterCountDownLatch.countDown(); 918 // Skip initial events 919 if (value.getTimestamp() != 0) { 920 mCounter++; 921 mEventsCountDownLatch.countDown(); 922 } 923 } 924 TestSequenceCallback(int expectedTimes)925 TestSequenceCallback(int expectedTimes) { 926 mEventsCountDownLatch = new CountDownLatch(expectedTimes); 927 } 928 929 @Override onErrorEvent(int properId, int zone)930 public void onErrorEvent(int properId, int zone) { 931 Log.e(TAG, "TestSequenceCallback get an onErrorEvent"); 932 } 933 getLastCarPropertyValue(int propId)934 public CarPropertyValue getLastCarPropertyValue(int propId) { 935 return mRecorder.get(propId); 936 } 937 getEventCounter()938 public int getEventCounter() { 939 return mCounter; 940 } 941 assertOnChangeEventCalled()942 public void assertOnChangeEventCalled() throws InterruptedException { 943 if (!mEventsCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 944 throw new IllegalStateException("Callback is not called in " 945 + CALLBACK_SHORT_TIMEOUT_MS + " ms."); 946 } 947 } 948 assertRegisterCompleted()949 public void assertRegisterCompleted() throws InterruptedException { 950 if (!mRegisterCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 951 throw new IllegalStateException("Register failed in " + CALLBACK_SHORT_TIMEOUT_MS 952 + " ms."); 953 } 954 } 955 } 956 957 } 958