1 /* 2 * Copyright (C) 2022 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 android.car.hardware.property.CarPropertyManager.SENSOR_RATE_ONCHANGE; 20 21 import static com.android.car.internal.property.CarPropertyHelper.SYNC_OP_LIMIT_TRY_AGAIN; 22 23 import static com.google.common.truth.Truth.assertThat; 24 import static com.google.common.truth.Truth.assertWithMessage; 25 26 import static org.junit.Assert.assertThrows; 27 import static org.mockito.ArgumentMatchers.any; 28 import static org.mockito.ArgumentMatchers.anyFloat; 29 import static org.mockito.ArgumentMatchers.anyInt; 30 import static org.mockito.ArgumentMatchers.anyLong; 31 import static org.mockito.ArgumentMatchers.eq; 32 import static org.mockito.Mockito.clearInvocations; 33 import static org.mockito.Mockito.doAnswer; 34 import static org.mockito.Mockito.doNothing; 35 import static org.mockito.Mockito.doThrow; 36 import static org.mockito.Mockito.mock; 37 import static org.mockito.Mockito.never; 38 import static org.mockito.Mockito.timeout; 39 import static org.mockito.Mockito.verify; 40 import static org.mockito.Mockito.when; 41 42 import android.car.VehicleAreaType; 43 import android.car.VehicleAreaWindow; 44 import android.car.VehiclePropertyIds; 45 import android.car.feature.FeatureFlags; 46 import android.car.hardware.CarPropertyConfig; 47 import android.car.hardware.CarPropertyValue; 48 import android.car.hardware.property.AreaIdConfig; 49 import android.car.hardware.property.CarPropertyEvent; 50 import android.car.hardware.property.CarPropertyManager; 51 import android.car.hardware.property.ICarPropertyEventListener; 52 import android.content.Context; 53 import android.os.IBinder; 54 import android.os.RemoteException; 55 import android.os.ServiceSpecificException; 56 import android.platform.test.ravenwood.RavenwoodRule; 57 import android.util.Log; 58 import android.util.SparseArray; 59 60 import com.android.car.hal.PropertyHalService; 61 import com.android.car.internal.property.AsyncPropertyServiceRequest; 62 import com.android.car.internal.property.AsyncPropertyServiceRequestList; 63 import com.android.car.internal.property.CarSubscription; 64 import com.android.car.internal.property.IAsyncPropertyResultCallback; 65 import com.android.car.logging.HistogramFactoryInterface; 66 import com.android.modules.expresslog.Histogram; 67 68 import org.junit.Before; 69 import org.junit.Rule; 70 import org.junit.Test; 71 import org.mockito.ArgumentCaptor; 72 import org.mockito.Captor; 73 import org.mockito.Mock; 74 import org.mockito.MockitoAnnotations; 75 76 import java.time.Duration; 77 import java.util.List; 78 import java.util.concurrent.CountDownLatch; 79 import java.util.concurrent.Executor; 80 import java.util.concurrent.Executors; 81 import java.util.concurrent.TimeUnit; 82 83 public final class CarPropertyServiceUnitTest { 84 private static final String TAG = CarLog.tagFor(CarPropertyServiceUnitTest.class); 85 86 @Rule 87 public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() 88 .setProcessSystem() 89 .setProvideMainThread(true) 90 .build(); 91 92 @Mock 93 private Context mContext; 94 @Mock 95 private PropertyHalService mHalService; 96 @Mock 97 private ICarPropertyEventListener mICarPropertyEventListener; 98 @Mock 99 private IBinder mIBinder; 100 @Mock 101 private IAsyncPropertyResultCallback mAsyncPropertyResultCallback; 102 @Mock 103 private FeatureFlags mFeatureFlags; 104 @Mock 105 private HistogramFactoryInterface mHistogramFactory; 106 @Captor 107 private ArgumentCaptor<List<CarPropertyEvent>> mPropertyEventCaptor; 108 109 private CarPropertyService mService; 110 111 private final SparseArray<CarPropertyConfig<?>> mConfigs = new SparseArray<>(); 112 113 private static final int SPEED_ID = VehiclePropertyIds.PERF_VEHICLE_SPEED; 114 private static final int HVAC_TEMP = VehiclePropertyIds.HVAC_TEMPERATURE_SET; 115 private static final int CONTINUOUS_READ_ONLY_PROPERTY_ID = 98732; 116 private static final int WRITE_ONLY_INT_PROPERTY_ID = 12345; 117 private static final int WRITE_ONLY_LONG_PROPERTY_ID = 22345; 118 private static final int WRITE_ONLY_FLOAT_PROPERTY_ID = 32345; 119 private static final int WRITE_ONLY_ENUM_PROPERTY_ID = 112345; 120 private static final int WRITE_ONLY_OTHER_ENUM_PROPERTY_ID = 121 VehiclePropertyIds.CRUISE_CONTROL_TYPE; 122 private static final int READ_WRITE_INT_PROPERTY_ID = 42345; 123 private static final int ON_CHANGE_ZONED_PROPERTY_ID = 52345; 124 private static final int CONTINUOUS_ZONED_PROPERTY_ID = 62345; 125 126 private static final int ON_CHANGE_READ_WRITE_PROPERTY_ID = 1111; 127 private static final int NO_PERMISSION_PROPERTY_ID = 13292; 128 private static final int GLOBAL_AREA_ID = 0; 129 private static final int NOT_SUPPORTED_AREA_ID = -1; 130 private static final float MIN_SAMPLE_RATE = 2; 131 private static final float MAX_SAMPLE_RATE = 10; 132 private static final long ASYNC_TIMEOUT_MS = 1000L; 133 private static final int MIN_INT_VALUE = 10; 134 private static final int MAX_INT_VALUE = 20; 135 private static final long MIN_LONG_VALUE = 100; 136 private static final long MAX_LONG_VALUE = 200; 137 private static final float MIN_FLOAT_VALUE = 0.5f; 138 private static final float MAX_FLOAT_VALUE = 5.5f; 139 private static final List<Integer> SUPPORTED_ENUM_VALUES = List.of(-1, 0, 1, 2, 4, 5); 140 private static final Integer UNSUPPORTED_ENUM_VALUE = 3; 141 private static final Integer SUPPORTED_ERROR_STATE_ENUM_VALUE = -1; 142 private static final Integer SUPPORTED_OTHER_STATE_ENUM_VALUE = 0; 143 private static final int TEST_VALUE = 18; 144 private static final CarPropertyValue TEST_PROPERTY_VALUE = new CarPropertyValue( 145 READ_WRITE_INT_PROPERTY_ID, /* areaId= */ 0, TEST_VALUE); 146 147 @Before setUp()148 public void setUp() { 149 MockitoAnnotations.initMocks(this); 150 151 when(mICarPropertyEventListener.asBinder()).thenReturn(mIBinder); 152 153 mConfigs.put(SPEED_ID, CarPropertyConfig.newBuilder(Float.class, SPEED_ID, 154 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1).addAreaIdConfig( 155 new AreaIdConfig.Builder<Float>( 156 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ, GLOBAL_AREA_ID) 157 .setSupportVariableUpdateRate(true).build()) 158 .setAccess(CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ).setChangeMode( 159 CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS).setMaxSampleRate( 160 100).setMinSampleRate(1).build()); 161 when(mHalService.isReadable(mContext, SPEED_ID)).thenReturn(true); 162 // HVAC_TEMP is actually not a global property, but for simplicity, make it global here. 163 mConfigs.put(HVAC_TEMP, CarPropertyConfig.newBuilder(Float.class, HVAC_TEMP, 164 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL) 165 .addAreaIdConfig(new AreaIdConfig.Builder( 166 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ, GLOBAL_AREA_ID).build()) 167 .setAccess(CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ).build()); 168 when(mHalService.isReadable(mContext, HVAC_TEMP)).thenReturn(true); 169 mConfigs.put(VehiclePropertyIds.GEAR_SELECTION, 170 CarPropertyConfig.newBuilder(Integer.class, VehiclePropertyIds.GEAR_SELECTION, 171 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL) 172 .addAreaIdConfig(new AreaIdConfig.Builder( 173 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ, GLOBAL_AREA_ID) 174 .build()) 175 .setAccess(CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ).build()); 176 // Property with read or read/write access 177 when(mHalService.isReadable(mContext, CONTINUOUS_READ_ONLY_PROPERTY_ID)) 178 .thenReturn(true); 179 mConfigs.put(CONTINUOUS_READ_ONLY_PROPERTY_ID, CarPropertyConfig.newBuilder(Integer.class, 180 CONTINUOUS_READ_ONLY_PROPERTY_ID, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 181 1).addAreaIdConfig(new AreaIdConfig.Builder<Integer>( 182 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ, GLOBAL_AREA_ID) 183 .setSupportVariableUpdateRate(true).build()).setAccess( 184 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ).setChangeMode( 185 CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS).setMinSampleRate( 186 MIN_SAMPLE_RATE).setMaxSampleRate(MAX_SAMPLE_RATE).build()); 187 when(mHalService.isWritable(mContext, WRITE_ONLY_INT_PROPERTY_ID)) 188 .thenReturn(true); 189 mConfigs.put(WRITE_ONLY_INT_PROPERTY_ID, CarPropertyConfig.newBuilder(Integer.class, 190 WRITE_ONLY_INT_PROPERTY_ID, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1) 191 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>( 192 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE, GLOBAL_AREA_ID) 193 .setMinValue(MIN_INT_VALUE).setMaxValue(MAX_INT_VALUE).build()) 194 .setAccess(CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) 195 .build()); 196 when(mHalService.isWritable(mContext, WRITE_ONLY_LONG_PROPERTY_ID)) 197 .thenReturn(true); 198 mConfigs.put(WRITE_ONLY_LONG_PROPERTY_ID, CarPropertyConfig.newBuilder(Long.class, 199 WRITE_ONLY_LONG_PROPERTY_ID, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1) 200 .addAreaIdConfig(new AreaIdConfig.Builder<Long>( 201 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE, GLOBAL_AREA_ID) 202 .setMinValue(MIN_LONG_VALUE).setMaxValue(MAX_LONG_VALUE).build()) 203 .setAccess(CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) 204 .build()); 205 when(mHalService.isWritable(mContext, WRITE_ONLY_FLOAT_PROPERTY_ID)) 206 .thenReturn(true); 207 mConfigs.put(WRITE_ONLY_FLOAT_PROPERTY_ID, CarPropertyConfig.newBuilder(Float.class, 208 WRITE_ONLY_FLOAT_PROPERTY_ID, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1) 209 .addAreaIdConfig(new AreaIdConfig.Builder<Float>( 210 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE, GLOBAL_AREA_ID) 211 .setMinValue(MIN_FLOAT_VALUE).setMaxValue(MAX_FLOAT_VALUE).build()) 212 .setAccess(CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE) 213 .build()); 214 when(mHalService.isWritable(mContext, WRITE_ONLY_ENUM_PROPERTY_ID)) 215 .thenReturn(true); 216 mConfigs.put(WRITE_ONLY_ENUM_PROPERTY_ID, CarPropertyConfig.newBuilder(Integer.class, 217 WRITE_ONLY_ENUM_PROPERTY_ID, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 218 1).addAreaIdConfig(new AreaIdConfig.Builder( 219 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE, GLOBAL_AREA_ID) 220 .setSupportedEnumValues(SUPPORTED_ENUM_VALUES).build()) 221 .setAccess(CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE).build()); 222 when(mHalService.isWritable(mContext, WRITE_ONLY_OTHER_ENUM_PROPERTY_ID)) 223 .thenReturn(true); 224 mConfigs.put(WRITE_ONLY_OTHER_ENUM_PROPERTY_ID, CarPropertyConfig.newBuilder(Integer.class, 225 WRITE_ONLY_OTHER_ENUM_PROPERTY_ID, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 226 1).addAreaIdConfig(new AreaIdConfig.Builder( 227 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE, GLOBAL_AREA_ID) 228 .setSupportedEnumValues(SUPPORTED_ENUM_VALUES).build()) 229 .setAccess(CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_WRITE).build()); 230 when(mHalService.isReadable(mContext, ON_CHANGE_READ_WRITE_PROPERTY_ID)) 231 .thenReturn(true); 232 when(mHalService.isWritable(mContext, ON_CHANGE_READ_WRITE_PROPERTY_ID)) 233 .thenReturn(true); 234 mConfigs.put(ON_CHANGE_READ_WRITE_PROPERTY_ID, CarPropertyConfig.newBuilder(Integer.class, 235 ON_CHANGE_READ_WRITE_PROPERTY_ID, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1) 236 .addAreaIdConfig(new AreaIdConfig.Builder( 237 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE, GLOBAL_AREA_ID) 238 .build()) 239 .setAccess(CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) 240 .setChangeMode(CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE).build()); 241 mConfigs.put(NO_PERMISSION_PROPERTY_ID, CarPropertyConfig.newBuilder(Integer.class, 242 NO_PERMISSION_PROPERTY_ID, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1) 243 .addAreaIdConfig(new AreaIdConfig.Builder( 244 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE, GLOBAL_AREA_ID) 245 .build()) 246 .setAccess(CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE).build()); 247 248 when(mHalService.isReadable(mContext, READ_WRITE_INT_PROPERTY_ID)) 249 .thenReturn(true); 250 when(mHalService.isWritable(mContext, READ_WRITE_INT_PROPERTY_ID)) 251 .thenReturn(true); 252 mConfigs.put(READ_WRITE_INT_PROPERTY_ID, CarPropertyConfig.newBuilder(Integer.class, 253 READ_WRITE_INT_PROPERTY_ID, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1) 254 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>( 255 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE, GLOBAL_AREA_ID) 256 .setMinValue(MIN_INT_VALUE).setMaxValue(MAX_INT_VALUE).build()) 257 .setAccess(CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE) 258 .build()); 259 260 mConfigs.put(ON_CHANGE_ZONED_PROPERTY_ID, CarPropertyConfig.newBuilder(Float.class, 261 ON_CHANGE_ZONED_PROPERTY_ID, VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW, 1) 262 .addAreaIdConfig(new AreaIdConfig.Builder<Float>( 263 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ, 264 VehicleAreaWindow.WINDOW_ROW_1_LEFT).build()) 265 .addAreaIdConfig(new AreaIdConfig.Builder<Float>( 266 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ, 267 VehicleAreaWindow.WINDOW_ROW_1_RIGHT).build()) 268 .setAccess(CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) 269 .setChangeMode(CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE) 270 .setMaxSampleRate(100) 271 .setMinSampleRate(1) 272 .build()); 273 when(mHalService.isReadable(mContext, ON_CHANGE_ZONED_PROPERTY_ID)).thenReturn(true); 274 275 mConfigs.put(CONTINUOUS_ZONED_PROPERTY_ID, CarPropertyConfig.newBuilder(Float.class, 276 CONTINUOUS_ZONED_PROPERTY_ID, VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW, 1) 277 .addAreaIdConfig(new AreaIdConfig.Builder<Float>( 278 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ, 279 VehicleAreaWindow.WINDOW_ROW_1_LEFT).build()) 280 .addAreaIdConfig(new AreaIdConfig.Builder<Float>( 281 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ, 282 VehicleAreaWindow.WINDOW_ROW_1_RIGHT).build()) 283 .setAccess(CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ) 284 .setChangeMode(CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS) 285 .setMaxSampleRate(100) 286 .setMinSampleRate(1) 287 .build()); 288 when(mHalService.isReadable(mContext, CONTINUOUS_ZONED_PROPERTY_ID)).thenReturn(true); 289 290 when(mHalService.getPropertyList()).thenReturn(mConfigs); 291 292 when(mFeatureFlags.variableUpdateRate()).thenReturn(true); 293 when(mFeatureFlags.subscriptionWithResolution()).thenReturn(true); 294 295 when(mHistogramFactory.newUniformHistogram(any(), anyInt(), anyFloat(), anyFloat())) 296 .thenReturn(mock(Histogram.class)); 297 when(mHistogramFactory.newScaledRangeHistogram(any(), anyInt(), anyInt(), anyFloat(), 298 anyFloat())).thenReturn(mock(Histogram.class)); 299 300 mService = new CarPropertyService.Builder() 301 .setContext(mContext) 302 .setPropertyHalService(mHalService) 303 .setFeatureFlags(mFeatureFlags) 304 .setHistogramFactory(mHistogramFactory) 305 .build(); 306 mService.init(); 307 } 308 309 @Test testGetPropertyConfigList()310 public void testGetPropertyConfigList() { 311 var result = mService.getPropertyConfigList(new int[] { 312 READ_WRITE_INT_PROPERTY_ID, ON_CHANGE_ZONED_PROPERTY_ID}); 313 314 assertThat(result.missingPermissionPropIds.length).isEqualTo(0); 315 assertThat(result.unsupportedPropIds.length).isEqualTo(0); 316 assertThat(result.carPropertyConfigList.getConfigs()).containsExactly( 317 mConfigs.get(READ_WRITE_INT_PROPERTY_ID), 318 mConfigs.get(ON_CHANGE_ZONED_PROPERTY_ID)); 319 } 320 321 @Test testGetPropertyConfigList_noReadOrWritePermission()322 public void testGetPropertyConfigList_noReadOrWritePermission() { 323 var result = mService.getPropertyConfigList(new int[] { 324 READ_WRITE_INT_PROPERTY_ID, NO_PERMISSION_PROPERTY_ID}); 325 326 assertThat(result.missingPermissionPropIds).isEqualTo(new int[] { 327 NO_PERMISSION_PROPERTY_ID}); 328 assertThat(result.unsupportedPropIds.length).isEqualTo(0); 329 assertThat(result.carPropertyConfigList.getConfigs()).containsExactly( 330 mConfigs.get(READ_WRITE_INT_PROPERTY_ID)); 331 } 332 333 @Test testGetPropertyConfigList_noConfig()334 public void testGetPropertyConfigList_noConfig() { 335 int invalidPropertyID = -1; 336 var result = mService.getPropertyConfigList(new int[] { 337 READ_WRITE_INT_PROPERTY_ID, invalidPropertyID}); 338 339 assertThat(result.unsupportedPropIds).isEqualTo(new int[] {invalidPropertyID}); 340 assertThat(result.missingPermissionPropIds.length).isEqualTo(0); 341 assertThat(result.carPropertyConfigList.getConfigs()).containsExactly( 342 mConfigs.get(READ_WRITE_INT_PROPERTY_ID)); 343 } 344 345 @Test testGetPropertiesAsync()346 public void testGetPropertiesAsync() { 347 AsyncPropertyServiceRequest getPropertyServiceRequest = new AsyncPropertyServiceRequest(0, 348 SPEED_ID, 0); 349 List<AsyncPropertyServiceRequest> requests = List.of(getPropertyServiceRequest); 350 351 mService.getPropertiesAsync(new AsyncPropertyServiceRequestList(requests), 352 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS); 353 354 verify(mHalService).getCarPropertyValuesAsync(eq(requests), any(), eq(ASYNC_TIMEOUT_MS), 355 anyLong()); 356 } 357 358 @Test testGetPropertiesAsync_throwsExceptionBecauseOfNullRequests()359 public void testGetPropertiesAsync_throwsExceptionBecauseOfNullRequests() { 360 assertThrows(NullPointerException.class, 361 () -> mService.getPropertiesAsync(null, mAsyncPropertyResultCallback, 362 ASYNC_TIMEOUT_MS)); 363 } 364 365 @Test testGetPropertiesAsync_throwsExceptionBecauseOfNullCallback()366 public void testGetPropertiesAsync_throwsExceptionBecauseOfNullCallback() { 367 assertThrows(NullPointerException.class, 368 () -> mService.getPropertiesAsync(new AsyncPropertyServiceRequestList(List.of()), 369 null, ASYNC_TIMEOUT_MS)); 370 } 371 372 @Test testGetPropertiesAsync_propertyIdNotSupported()373 public void testGetPropertiesAsync_propertyIdNotSupported() { 374 int invalidPropertyID = -1; 375 AsyncPropertyServiceRequest getPropertyServiceRequest = new AsyncPropertyServiceRequest(0, 376 invalidPropertyID, 0); 377 378 assertThrows(IllegalArgumentException.class, () -> mService.getPropertiesAsync( 379 new AsyncPropertyServiceRequestList(List.of(getPropertyServiceRequest)), 380 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 381 } 382 383 @Test testGetPropertiesAsync_noReadPermission()384 public void testGetPropertiesAsync_noReadPermission() { 385 AsyncPropertyServiceRequest getPropertyServiceRequest = new AsyncPropertyServiceRequest(0, 386 SPEED_ID, 0); 387 when(mHalService.isReadable(mContext, SPEED_ID)).thenReturn(false); 388 389 assertThrows(SecurityException.class, () -> mService.getPropertiesAsync( 390 new AsyncPropertyServiceRequestList(List.of(getPropertyServiceRequest)), 391 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 392 } 393 394 @Test testGetPropertiesAsync_propertyNotReadable()395 public void testGetPropertiesAsync_propertyNotReadable() { 396 AsyncPropertyServiceRequest getPropertyServiceRequest = new AsyncPropertyServiceRequest(0, 397 WRITE_ONLY_INT_PROPERTY_ID, 0); 398 399 assertThrows(IllegalArgumentException.class, () -> mService.getPropertiesAsync( 400 new AsyncPropertyServiceRequestList(List.of(getPropertyServiceRequest)), 401 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 402 } 403 404 @Test testGetPropertiesAsync_areaIdNotSupported()405 public void testGetPropertiesAsync_areaIdNotSupported() { 406 AsyncPropertyServiceRequest getPropertyServiceRequest = new AsyncPropertyServiceRequest(0, 407 HVAC_TEMP, NOT_SUPPORTED_AREA_ID); 408 409 assertThrows(IllegalArgumentException.class, () -> mService.getPropertiesAsync( 410 new AsyncPropertyServiceRequestList(List.of(getPropertyServiceRequest)), 411 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 412 } 413 414 @Test testGetPropertiesAsync_timeoutNotPositiveNumber()415 public void testGetPropertiesAsync_timeoutNotPositiveNumber() { 416 AsyncPropertyServiceRequest getPropertyServiceRequest = new AsyncPropertyServiceRequest(0, 417 SPEED_ID, 0); 418 419 assertThrows(IllegalArgumentException.class, () -> mService.getPropertiesAsync( 420 new AsyncPropertyServiceRequestList(List.of(getPropertyServiceRequest)), 421 mAsyncPropertyResultCallback, /* timeoutInMs= */ 0)); 422 } 423 424 @Test testSetPropertiesAsync()425 public void testSetPropertiesAsync() { 426 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest( 427 0, READ_WRITE_INT_PROPERTY_ID, 0, TEST_PROPERTY_VALUE); 428 List<AsyncPropertyServiceRequest> requests = List.of(request); 429 430 mService.setPropertiesAsync(new AsyncPropertyServiceRequestList(requests), 431 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS); 432 433 verify(mHalService).setCarPropertyValuesAsync(eq(requests), any(), eq(ASYNC_TIMEOUT_MS), 434 anyLong()); 435 } 436 437 @Test testSetPropertiesAsync_noWaitForPropertyUpdate()438 public void testSetPropertiesAsync_noWaitForPropertyUpdate() { 439 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest( 440 0, WRITE_ONLY_INT_PROPERTY_ID, 0, new CarPropertyValue(WRITE_ONLY_INT_PROPERTY_ID, 441 /* areaId= */ 0, /* value= */ 13)); 442 request.setWaitForPropertyUpdate(false); 443 List<AsyncPropertyServiceRequest> requests = List.of(request); 444 445 mService.setPropertiesAsync(new AsyncPropertyServiceRequestList(requests), 446 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS); 447 448 verify(mHalService).setCarPropertyValuesAsync(eq(requests), any(), eq(ASYNC_TIMEOUT_MS), 449 anyLong()); 450 } 451 452 @Test testSetPropertiesAsync_throwsExceptionBecauseOfNullRequests()453 public void testSetPropertiesAsync_throwsExceptionBecauseOfNullRequests() { 454 assertThrows(NullPointerException.class, 455 () -> mService.setPropertiesAsync(null, mAsyncPropertyResultCallback, 456 ASYNC_TIMEOUT_MS)); 457 } 458 459 @Test testSetPropertiesAsync_throwsExceptionBecauseOfNullCallback()460 public void testSetPropertiesAsync_throwsExceptionBecauseOfNullCallback() { 461 assertThrows(NullPointerException.class, 462 () -> mService.setPropertiesAsync(new AsyncPropertyServiceRequestList(List.of()), 463 null, ASYNC_TIMEOUT_MS)); 464 } 465 466 @Test testSetPropertiesAsync_mismatchPropertyId()467 public void testSetPropertiesAsync_mismatchPropertyId() { 468 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest( 469 0, READ_WRITE_INT_PROPERTY_ID, 0, new CarPropertyValue(SPEED_ID, 0, 1)); 470 471 assertThrows(IllegalArgumentException.class, () -> mService.setPropertiesAsync( 472 new AsyncPropertyServiceRequestList(List.of(request)), 473 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 474 } 475 476 @Test testSetPropertiesAsync_mismatchAreaId()477 public void testSetPropertiesAsync_mismatchAreaId() { 478 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest( 479 0, READ_WRITE_INT_PROPERTY_ID, 0, new CarPropertyValue( 480 READ_WRITE_INT_PROPERTY_ID, /* areaId= */ 1, 1)); 481 482 assertThrows(IllegalArgumentException.class, () -> mService.setPropertiesAsync( 483 new AsyncPropertyServiceRequestList(List.of(request)), 484 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 485 } 486 487 @Test testSetPropertiesAsync_nullPropertyValueToSet()488 public void testSetPropertiesAsync_nullPropertyValueToSet() { 489 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest( 490 0, READ_WRITE_INT_PROPERTY_ID, 0, /* carPropertyValue= */ null); 491 492 assertThrows(NullPointerException.class, () -> mService.setPropertiesAsync( 493 new AsyncPropertyServiceRequestList(List.of(request)), 494 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 495 } 496 497 @Test testSetPropertiesAsync_nullValueToSet()498 public void testSetPropertiesAsync_nullValueToSet() { 499 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest( 500 0, READ_WRITE_INT_PROPERTY_ID, 0, new CarPropertyValue(READ_WRITE_INT_PROPERTY_ID, 501 0, /* value= */ null)); 502 503 assertThrows(IllegalArgumentException.class, () -> mService.setPropertiesAsync( 504 new AsyncPropertyServiceRequestList(List.of(request)), 505 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 506 } 507 508 @Test testSetPropertiesAsync_mismatchPropertyType()509 public void testSetPropertiesAsync_mismatchPropertyType() { 510 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest( 511 0, READ_WRITE_INT_PROPERTY_ID, 0, new CarPropertyValue(READ_WRITE_INT_PROPERTY_ID, 512 0, Float.valueOf(1.1f))); 513 514 assertThrows(IllegalArgumentException.class, () -> mService.setPropertiesAsync( 515 new AsyncPropertyServiceRequestList(List.of(request)), 516 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 517 } 518 519 @Test testSetPropertiesAsync_valueLargerThanMaxValue()520 public void testSetPropertiesAsync_valueLargerThanMaxValue() { 521 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest( 522 0, READ_WRITE_INT_PROPERTY_ID, 0, new CarPropertyValue(READ_WRITE_INT_PROPERTY_ID, 523 0, MAX_INT_VALUE + 1)); 524 525 assertThrows(IllegalArgumentException.class, () -> mService.setPropertiesAsync( 526 new AsyncPropertyServiceRequestList(List.of(request)), 527 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 528 } 529 530 @Test testSetPropertiesAsync_valueSmallerThanMinValue()531 public void testSetPropertiesAsync_valueSmallerThanMinValue() { 532 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest( 533 0, READ_WRITE_INT_PROPERTY_ID, 0, new CarPropertyValue(READ_WRITE_INT_PROPERTY_ID, 534 0, MIN_INT_VALUE - 1)); 535 536 assertThrows(IllegalArgumentException.class, () -> mService.setPropertiesAsync( 537 new AsyncPropertyServiceRequestList(List.of(request)), 538 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 539 } 540 541 @Test testSetPropertiesAsync_propertyIdNotSupported()542 public void testSetPropertiesAsync_propertyIdNotSupported() { 543 int invalidPropertyID = -1; 544 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest( 545 0, invalidPropertyID, 0, TEST_PROPERTY_VALUE); 546 547 assertThrows(IllegalArgumentException.class, () -> mService.setPropertiesAsync( 548 new AsyncPropertyServiceRequestList(List.of(request)), 549 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 550 } 551 552 @Test testSetPropertiesAsync_noWritePermission()553 public void testSetPropertiesAsync_noWritePermission() { 554 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest( 555 0, READ_WRITE_INT_PROPERTY_ID, 0, TEST_PROPERTY_VALUE); 556 when(mHalService.isWritable(mContext, READ_WRITE_INT_PROPERTY_ID)) 557 .thenReturn(false); 558 559 assertThrows(SecurityException.class, () -> mService.setPropertiesAsync( 560 new AsyncPropertyServiceRequestList(List.of(request)), 561 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 562 } 563 564 @Test testSetPropertiesAsync_propertyNotWrittable()565 public void testSetPropertiesAsync_propertyNotWrittable() { 566 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest(0, 567 SPEED_ID, 0, TEST_PROPERTY_VALUE); 568 569 assertThrows(IllegalArgumentException.class, () -> mService.setPropertiesAsync( 570 new AsyncPropertyServiceRequestList(List.of(request)), 571 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 572 } 573 574 // By default waitForPropertyUpdate is true, which requires the read permisison as well. 575 @Test testSetPropertiesAsync_noReadPermission()576 public void testSetPropertiesAsync_noReadPermission() { 577 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest( 578 0, READ_WRITE_INT_PROPERTY_ID, 0, TEST_PROPERTY_VALUE); 579 when(mHalService.isReadable(mContext, READ_WRITE_INT_PROPERTY_ID)) 580 .thenReturn(false); 581 582 assertThrows(SecurityException.class, () -> mService.setPropertiesAsync( 583 new AsyncPropertyServiceRequestList(List.of(request)), 584 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 585 } 586 587 // By default waitForPropertyUpdate is true, which requires the read permisison as well. 588 @Test testSetPropertiesAsync_propertyNotReadable()589 public void testSetPropertiesAsync_propertyNotReadable() { 590 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest(0, 591 WRITE_ONLY_INT_PROPERTY_ID, 0, TEST_PROPERTY_VALUE); 592 593 assertThrows(IllegalArgumentException.class, () -> mService.setPropertiesAsync( 594 new AsyncPropertyServiceRequestList(List.of(request)), 595 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 596 } 597 598 @Test testSetPropertiesAsync_areaIdNotSupported()599 public void testSetPropertiesAsync_areaIdNotSupported() { 600 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest( 601 0, READ_WRITE_INT_PROPERTY_ID, NOT_SUPPORTED_AREA_ID, TEST_PROPERTY_VALUE); 602 603 assertThrows(IllegalArgumentException.class, () -> mService.setPropertiesAsync( 604 new AsyncPropertyServiceRequestList(List.of(request)), 605 mAsyncPropertyResultCallback, ASYNC_TIMEOUT_MS)); 606 } 607 608 @Test testSetPropertiesAsync_timeoutNotPositiveNumber()609 public void testSetPropertiesAsync_timeoutNotPositiveNumber() { 610 AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest( 611 0, READ_WRITE_INT_PROPERTY_ID, 0, TEST_PROPERTY_VALUE); 612 613 assertThrows(IllegalArgumentException.class, () -> mService.setPropertiesAsync( 614 new AsyncPropertyServiceRequestList(List.of(request)), 615 mAsyncPropertyResultCallback, /* timeoutInMs= */ 0)); 616 } 617 618 @Test testRegisterUnregisterForContinuousProperty()619 public void testRegisterUnregisterForContinuousProperty() throws Exception { 620 ICarPropertyEventListener mMockHandler1 = mock(ICarPropertyEventListener.class); 621 ICarPropertyEventListener mMockHandler2 = mock(ICarPropertyEventListener.class); 622 // Must use two different binders because listener is uniquely identified by binder. 623 IBinder mBinder1 = mock(IBinder.class); 624 IBinder mBinder2 = mock(IBinder.class); 625 when(mMockHandler1.asBinder()).thenReturn(mBinder1); 626 when(mMockHandler2.asBinder()).thenReturn(mBinder2); 627 long timestampNanos = Duration.ofSeconds(1).toNanos(); 628 CarPropertyValue<Float> mValue = 629 new CarPropertyValue<>(SPEED_ID, 0, timestampNanos, 0f); 630 when(mHalService.getProperty(SPEED_ID, 0)).thenReturn(mValue); 631 632 // Register the first listener. 633 mService.registerListener(SPEED_ID, /* updateRateHz= */ 10, mMockHandler1); 634 635 // Wait until we get the on property change event for the initial value. 636 verify(mMockHandler1, timeout(5000)).onEvent(any()); 637 638 verify(mHalService).subscribeProperty(List.of(createCarSubscriptionOption(SPEED_ID, 639 new int[]{0}, 10f, /* enableVur= */ true))); 640 verify(mHalService).getProperty(SPEED_ID, 0); 641 642 // Clean up invocation state. 643 clearInvocations(mHalService); 644 645 // Register the second listener. 646 mService.registerListener(SPEED_ID, /* updateRateHz= */ 20, mMockHandler2); 647 648 // Wait until we get the on property change event for the initial value. 649 verify(mMockHandler2, timeout(5000)).onEvent(any()); 650 651 verify(mHalService).subscribeProperty(List.of(createCarSubscriptionOption(SPEED_ID, 652 new int[]{0}, 20f, /* enableVur= */ true))); 653 verify(mHalService).getProperty(SPEED_ID, 0); 654 655 // Clean up invocation state. 656 clearInvocations(mHalService); 657 658 // Unregister the second listener, the first listener must still be registered. 659 mService.unregisterListener(SPEED_ID, mMockHandler2); 660 661 // The property must not be unsubscribed. 662 verify(mHalService, never()).unsubscribeProperty(anyInt()); 663 // The subscription rate must be updated. 664 verify(mHalService).subscribeProperty(List.of(createCarSubscriptionOption(SPEED_ID, 665 new int[]{0}, 10f, /* enableVur= */ true))); 666 667 // Unregister the first listener. We have no more listeners, must cause unsubscription. 668 mService.unregisterListener(SPEED_ID, mMockHandler1); 669 670 verify(mHalService).unsubscribeProperty(SPEED_ID); 671 } 672 673 @Test testRegisterForMultipleProperties()674 public void testRegisterForMultipleProperties() throws Exception { 675 ICarPropertyEventListener mMockHandler1 = mock(ICarPropertyEventListener.class); 676 // Must use two different binders because listener is uniquely identified by binder. 677 IBinder mBinder1 = mock(IBinder.class); 678 when(mMockHandler1.asBinder()).thenReturn(mBinder1); 679 long timestampNanos = Duration.ofSeconds(1).toNanos(); 680 CarPropertyValue<Float> mValue = 681 new CarPropertyValue<>(HVAC_TEMP, 0, timestampNanos, 0f); 682 when(mHalService.getProperty(HVAC_TEMP, 0)).thenReturn(mValue); 683 mValue = new CarPropertyValue<>(SPEED_ID, 0, timestampNanos, 0f); 684 when(mHalService.getProperty(SPEED_ID, 0)).thenReturn(mValue); 685 686 mService.registerListener(List.of( 687 createCarSubscriptionOption(SPEED_ID, new int[]{0}, 20f), 688 createCarSubscriptionOption(HVAC_TEMP, new int[]{0}, 0f)), 689 mMockHandler1); 690 verify(mMockHandler1, timeout(5000)).onEvent(any()); 691 692 verify(mHalService).subscribeProperty(List.of( 693 createCarSubscriptionOption(SPEED_ID, new int[]{0}, 20f), 694 createCarSubscriptionOption(HVAC_TEMP, new int[]{0}, 0f))); 695 verify(mHalService).getProperty(HVAC_TEMP, 0); 696 verify(mHalService).getProperty(SPEED_ID, 0); 697 698 // Clean up invocation state. 699 clearInvocations(mHalService); 700 701 // Unregister the second listener, the first listener must still be registered. 702 mService.unregisterListener(HVAC_TEMP, mMockHandler1); 703 verify(mHalService).unsubscribeProperty(HVAC_TEMP); 704 705 // Clean up invocation state. 706 clearInvocations(mHalService); 707 708 mService.unregisterListener(SPEED_ID, mMockHandler1); 709 verify(mHalService).unsubscribeProperty(SPEED_ID); 710 } 711 712 @Test testRegisterUnregisterForOnChangeProperty()713 public void testRegisterUnregisterForOnChangeProperty() throws Exception { 714 ICarPropertyEventListener mMockHandler1 = mock(ICarPropertyEventListener.class); 715 ICarPropertyEventListener mMockHandler2 = mock(ICarPropertyEventListener.class); 716 // Must use two different binders because listener is uniquely identified by binder. 717 IBinder mBinder1 = mock(IBinder.class); 718 IBinder mBinder2 = mock(IBinder.class); 719 when(mMockHandler1.asBinder()).thenReturn(mBinder1); 720 when(mMockHandler2.asBinder()).thenReturn(mBinder2); 721 long timestampNanos = Duration.ofSeconds(1).toNanos(); 722 CarPropertyValue<Float> mValue = 723 new CarPropertyValue<>(HVAC_TEMP, 0, timestampNanos, 0f); 724 when(mHalService.getProperty(HVAC_TEMP, 0)).thenReturn(mValue); 725 726 // Register the first listener. 727 mService.registerListener(HVAC_TEMP, /* updateRateHz= */ SENSOR_RATE_ONCHANGE, 728 mMockHandler1); 729 730 // Wait until we get the on property change event for the initial value. 731 verify(mMockHandler1, timeout(5000)).onEvent(any()); 732 733 verify(mHalService).subscribeProperty(List.of(createCarSubscriptionOption( 734 HVAC_TEMP, new int[]{0}, 0f))); 735 verify(mHalService).getProperty(HVAC_TEMP, 0); 736 737 // Clean up invocation state. 738 clearInvocations(mHalService); 739 740 // Register the second listener. 741 mService.registerListener(HVAC_TEMP, /* updateRateHz= */ SENSOR_RATE_ONCHANGE, 742 mMockHandler2); 743 744 // Wait until we get the on property change event for the initial value. 745 verify(mMockHandler2, timeout(5000)).onEvent(any()); 746 747 // Must not subscribe again. 748 verify(mHalService, never()).subscribeProperty(any()); 749 verify(mHalService).getProperty(HVAC_TEMP, 0); 750 751 // Clean up invocation state. 752 clearInvocations(mHalService); 753 754 // Unregister the second listener, the first listener must still be registered. 755 mService.unregisterListener(HVAC_TEMP, mMockHandler2); 756 757 // The property must not be unsubscribed. 758 verify(mHalService, never()).unsubscribeProperty(anyInt()); 759 760 // Unregister the first listener. We have no more listeners, must cause unsubscription. 761 mService.unregisterListener(HVAC_TEMP, mMockHandler1); 762 763 verify(mHalService).unsubscribeProperty(HVAC_TEMP); 764 } 765 766 @Test testRegisterListenerWithSubscription()767 public void testRegisterListenerWithSubscription() throws Exception { 768 ICarPropertyEventListener mockHandler = mock(ICarPropertyEventListener.class); 769 IBinder mockBinder = mock(IBinder.class); 770 when(mockHandler.asBinder()).thenReturn(mockBinder); 771 long timestampNanos = Duration.ofSeconds(1).toNanos(); 772 CarPropertyValue<Float> speedValue = new CarPropertyValue<>( 773 SPEED_ID, 0, timestampNanos, 0f); 774 when(mHalService.getProperty(SPEED_ID, 0)).thenReturn(speedValue); 775 CarPropertyValue<Float> hvacValue = new CarPropertyValue<>( 776 HVAC_TEMP, 0, timestampNanos, 0f); 777 when(mHalService.getProperty(HVAC_TEMP, 0)).thenReturn(hvacValue); 778 779 List<CarSubscription> subscribeOptions = List.of( 780 createCarSubscriptionOption(SPEED_ID, new int[]{0}, 20f), 781 createCarSubscriptionOption(HVAC_TEMP, new int[]{0}, 0f)); 782 mService.registerListener(subscribeOptions, mockHandler); 783 784 // Verify the two initial value responses arrive. 785 verify(mockHandler, timeout(5000)).onEvent(mPropertyEventCaptor.capture()); 786 List<CarPropertyEvent> eventList = mPropertyEventCaptor.getValue(); 787 assertWithMessage("Must receive two initial value events").that(eventList).hasSize(2); 788 assertWithMessage("Received expected speed initial value event").that( 789 eventList.get(0).getCarPropertyValue()).isEqualTo(speedValue); 790 assertWithMessage("Received expected hvac initial value event").that( 791 eventList.get(1).getCarPropertyValue()).isEqualTo(hvacValue); 792 verify(mHalService).subscribeProperty(subscribeOptions); 793 // Verify the initial get value requests are sent. 794 verify(mHalService).getProperty(SPEED_ID, 0); 795 verify(mHalService).getProperty(HVAC_TEMP, 0); 796 } 797 798 @Test testRegisterListenerWithSubscription_enableVurAndResolution()799 public void testRegisterListenerWithSubscription_enableVurAndResolution() throws Exception { 800 ICarPropertyEventListener mockHandler = mock(ICarPropertyEventListener.class); 801 IBinder mockBinder = mock(IBinder.class); 802 when(mockHandler.asBinder()).thenReturn(mockBinder); 803 long timestampNanos = Duration.ofSeconds(1).toNanos(); 804 CarPropertyValue<Float> speedValue = new CarPropertyValue<>( 805 SPEED_ID, 0, timestampNanos, 0f); 806 when(mHalService.getProperty(SPEED_ID, 0)).thenReturn(speedValue); 807 CarPropertyValue<Float> hvacValue = new CarPropertyValue<>( 808 HVAC_TEMP, 0, timestampNanos, 0f); 809 when(mHalService.getProperty(HVAC_TEMP, 0)).thenReturn(hvacValue); 810 811 List<CarSubscription> subscribeOptions = List.of( 812 createCarSubscriptionOption(SPEED_ID, new int[]{0}, 20f, /* enableVur= */ true, 813 /* resolution */ 1.0f), 814 // This is an on-change property, so Vur must have no effect. 815 createCarSubscriptionOption(HVAC_TEMP, new int[]{0}, 0f, /* enableVur= */ true, 816 /* resolution */ 1.0f)); 817 List<CarSubscription> sanitizedOptions = List.of( 818 createCarSubscriptionOption(SPEED_ID, new int[]{0}, 20f, /* enableVur= */ true, 819 /* resolution */ 1.0f), 820 // This is an on-change property, so Vur must have no effect. 821 createCarSubscriptionOption(HVAC_TEMP, new int[]{0}, 0f, /* enableVur= */ false, 822 /* resolution */ 0.0f)); 823 mService.registerListener(subscribeOptions, mockHandler); 824 825 // Verify the two initial value responses arrive. 826 verify(mockHandler, timeout(5000)).onEvent(mPropertyEventCaptor.capture()); 827 verify(mHalService).subscribeProperty(sanitizedOptions); 828 } 829 830 @Test testRegisterListenerWithSubscription_VurFeatureOff()831 public void testRegisterListenerWithSubscription_VurFeatureOff() throws Exception { 832 when(mFeatureFlags.variableUpdateRate()).thenReturn(false); 833 834 ICarPropertyEventListener mockHandler = mock(ICarPropertyEventListener.class); 835 IBinder mockBinder = mock(IBinder.class); 836 when(mockHandler.asBinder()).thenReturn(mockBinder); 837 long timestampNanos = Duration.ofSeconds(1).toNanos(); 838 CarPropertyValue<Float> speedValue = new CarPropertyValue<>( 839 SPEED_ID, 0, timestampNanos, 0f); 840 when(mHalService.getProperty(SPEED_ID, 0)).thenReturn(speedValue); 841 CarPropertyValue<Float> hvacValue = new CarPropertyValue<>( 842 HVAC_TEMP, 0, timestampNanos, 0f); 843 when(mHalService.getProperty(HVAC_TEMP, 0)).thenReturn(hvacValue); 844 845 List<CarSubscription> subscribeOptions = List.of( 846 createCarSubscriptionOption(SPEED_ID, new int[]{0}, 20f, /* enableVur= */ true)); 847 List<CarSubscription> sanitizedOptions = List.of( 848 createCarSubscriptionOption(SPEED_ID, new int[]{0}, 20f, /* enableVur= */ false)); 849 mService.registerListener(subscribeOptions, mockHandler); 850 851 // Verify the two initial value responses arrive. 852 verify(mockHandler, timeout(5000)).onEvent(mPropertyEventCaptor.capture()); 853 verify(mHalService).subscribeProperty(sanitizedOptions); 854 } 855 856 @Test testRegisterListenerWithSubscription_ResolutionFeatureOff()857 public void testRegisterListenerWithSubscription_ResolutionFeatureOff() throws Exception { 858 when(mFeatureFlags.subscriptionWithResolution()).thenReturn(false); 859 860 ICarPropertyEventListener mockHandler = mock(ICarPropertyEventListener.class); 861 IBinder mockBinder = mock(IBinder.class); 862 when(mockHandler.asBinder()).thenReturn(mockBinder); 863 long timestampNanos = Duration.ofSeconds(1).toNanos(); 864 CarPropertyValue<Float> speedValue = new CarPropertyValue<>( 865 SPEED_ID, 0, timestampNanos, 0f); 866 when(mHalService.getProperty(SPEED_ID, 0)).thenReturn(speedValue); 867 CarPropertyValue<Float> hvacValue = new CarPropertyValue<>( 868 HVAC_TEMP, 0, timestampNanos, 0f); 869 when(mHalService.getProperty(HVAC_TEMP, 0)).thenReturn(hvacValue); 870 871 List<CarSubscription> subscribeOptions = List.of( 872 createCarSubscriptionOption(SPEED_ID, new int[]{0}, 20f, /* enableVur= */ true, 873 /* resolution */ 1.0f)); 874 List<CarSubscription> sanitizedOptions = List.of( 875 createCarSubscriptionOption(SPEED_ID, new int[]{0}, 20f, /* enableVur= */ true, 876 /* resolution */ 0.0f)); 877 mService.registerListener(subscribeOptions, mockHandler); 878 879 // Verify the two initial value responses arrive. 880 verify(mockHandler, timeout(5000)).onEvent(mPropertyEventCaptor.capture()); 881 verify(mHalService).subscribeProperty(sanitizedOptions); 882 } 883 884 @Test testRegisterListenerWithSubscription_exceptionFromPropertyHalService()885 public void testRegisterListenerWithSubscription_exceptionFromPropertyHalService() 886 throws Exception { 887 ICarPropertyEventListener mockHandler = mock(ICarPropertyEventListener.class); 888 IBinder mockBinder = mock(IBinder.class); 889 when(mockHandler.asBinder()).thenReturn(mockBinder); 890 doThrow(new ServiceSpecificException(0)).when(mHalService).subscribeProperty(any()); 891 892 List<CarSubscription> subscribeOptions = List.of( 893 createCarSubscriptionOption(SPEED_ID, new int[]{0}, 20f), 894 createCarSubscriptionOption(HVAC_TEMP, new int[]{0}, 0f)); 895 896 assertThrows(ServiceSpecificException.class, () -> 897 mService.registerListener(subscribeOptions, mockHandler)); 898 } 899 900 @Test testRegisterListenerWithSubscription_exceptionFromPropertyHalService_retry()901 public void testRegisterListenerWithSubscription_exceptionFromPropertyHalService_retry() 902 throws Exception { 903 ICarPropertyEventListener mockHandler = mock(ICarPropertyEventListener.class); 904 IBinder mockBinder = mock(IBinder.class); 905 when(mockHandler.asBinder()).thenReturn(mockBinder); 906 doThrow(new ServiceSpecificException(0)).when(mHalService).subscribeProperty(any()); 907 CarPropertyValue mockValue = mock(CarPropertyValue.class); 908 when(mHalService.getProperty(anyInt(), anyInt())).thenReturn(mockValue); 909 910 List<CarSubscription> subscribeOptions = List.of( 911 createCarSubscriptionOption(SPEED_ID, new int[]{0}, 20f), 912 createCarSubscriptionOption(HVAC_TEMP, new int[]{0}, 0f)); 913 914 assertThrows(ServiceSpecificException.class, () -> 915 mService.registerListener(subscribeOptions, mockHandler)); 916 917 // Finish the async get initial value task. 918 mService.finishHandlerTasks(/*timeoutInMs=*/ 1000); 919 920 // Simulate the error goes away. 921 clearInvocations(mHalService); 922 doNothing().when(mHalService).subscribeProperty(any()); 923 924 mService.registerListener(subscribeOptions, mockHandler); 925 926 // Verify that the retry request must go to PropertyHalService. 927 verify(mHalService).subscribeProperty(subscribeOptions); 928 } 929 930 @Test testregisterListener_alreadySubscribedOptionFilteredOut()931 public void testregisterListener_alreadySubscribedOptionFilteredOut() { 932 ICarPropertyEventListener mockHandler1 = mock(ICarPropertyEventListener.class); 933 ICarPropertyEventListener mockHandler2 = mock(ICarPropertyEventListener.class); 934 IBinder mockBinder1 = mock(IBinder.class); 935 IBinder mockBinder2 = mock(IBinder.class); 936 when(mockHandler1.asBinder()).thenReturn(mockBinder1); 937 when(mockHandler2.asBinder()).thenReturn(mockBinder2); 938 CarPropertyValue mockValue = mock(CarPropertyValue.class); 939 when(mHalService.getProperty(anyInt(), anyInt())).thenReturn(mockValue); 940 941 // Client 1 942 // On-change: 943 // [left -> 0f] 944 // Continuous: 945 // [left -> 20f, right -> 20f] 946 List<CarSubscription> firstSubscribeOptions = List.of( 947 createCarSubscriptionOption(ON_CHANGE_ZONED_PROPERTY_ID, new int[]{ 948 VehicleAreaWindow.WINDOW_ROW_1_LEFT}, 0f), 949 createCarSubscriptionOption(CONTINUOUS_ZONED_PROPERTY_ID, new int[]{ 950 VehicleAreaWindow.WINDOW_ROW_1_LEFT, VehicleAreaWindow.WINDOW_ROW_1_RIGHT}, 951 20f)); 952 mService.registerListener(firstSubscribeOptions, mockHandler1); 953 954 verify(mHalService).subscribeProperty(firstSubscribeOptions); 955 956 // Client 2 957 // On-change: 958 // [left -> 0f (filtered out), right -> 0f] 959 // Continuous: 960 // [left -> 10f (filtered out), right -> 30f] 961 // 962 // The request received by PropertyHalService should be: 963 // On-change: 964 // [right -> 0f] 965 // Continuous: 966 // [right -> 30f] 967 List<CarSubscription> secondSubscribeOptions = List.of( 968 createCarSubscriptionOption(ON_CHANGE_ZONED_PROPERTY_ID, new int[]{ 969 VehicleAreaWindow.WINDOW_ROW_1_LEFT, VehicleAreaWindow.WINDOW_ROW_1_RIGHT}, 970 0f), 971 createCarSubscriptionOption(CONTINUOUS_ZONED_PROPERTY_ID, new int[]{ 972 VehicleAreaWindow.WINDOW_ROW_1_LEFT}, 10f), 973 createCarSubscriptionOption(CONTINUOUS_ZONED_PROPERTY_ID, new int[]{ 974 VehicleAreaWindow.WINDOW_ROW_1_RIGHT}, 30f)); 975 mService.registerListener(secondSubscribeOptions, mockHandler2); 976 977 verify(mHalService).subscribeProperty(List.of( 978 createCarSubscriptionOption(ON_CHANGE_ZONED_PROPERTY_ID, new int[]{ 979 VehicleAreaWindow.WINDOW_ROW_1_RIGHT}, 0f), 980 createCarSubscriptionOption(CONTINUOUS_ZONED_PROPERTY_ID, new int[]{ 981 VehicleAreaWindow.WINDOW_ROW_1_RIGHT}, 30f) 982 )); 983 984 clearInvocations(mHalService); 985 986 // Unregister client 1 for on-change [left -> 0f] 987 // Since client2 still registered for on-change [left -> 0f], nothing changes. 988 mService.unregisterListener(ON_CHANGE_ZONED_PROPERTY_ID, mockHandler1); 989 990 verify(mHalService, never()).subscribeProperty(any()); 991 verify(mHalService, never()).unsubscribeProperty(anyInt()); 992 993 // Unregister client 1 for continuous property, left should be 10 because client 2 registers 994 // to left at 10. Right should still be subscribed at 30 and no change. 995 mService.unregisterListener(CONTINUOUS_ZONED_PROPERTY_ID, mockHandler1); 996 997 verify(mHalService).subscribeProperty(List.of( 998 createCarSubscriptionOption(CONTINUOUS_ZONED_PROPERTY_ID, new int[]{ 999 VehicleAreaWindow.WINDOW_ROW_1_LEFT}, 10f))); 1000 1001 // Unregister client 2 for on-change property, should cause unsubscribe for the property. 1002 mService.unregisterListener(ON_CHANGE_ZONED_PROPERTY_ID, mockHandler2); 1003 1004 verify(mHalService).unsubscribeProperty(ON_CHANGE_ZONED_PROPERTY_ID); 1005 1006 // Unregister client 2 for continuous property, should cause unsubscribe for the property. 1007 clearInvocations(mHalService); 1008 mService.unregisterListener(CONTINUOUS_ZONED_PROPERTY_ID, mockHandler2); 1009 1010 verify(mHalService).unsubscribeProperty(CONTINUOUS_ZONED_PROPERTY_ID); 1011 } 1012 1013 @Test testRegisterListenerWithSubscription_unregisterListener()1014 public void testRegisterListenerWithSubscription_unregisterListener() { 1015 ICarPropertyEventListener mockHandler1 = mock(ICarPropertyEventListener.class); 1016 ICarPropertyEventListener mockHandler2 = mock(ICarPropertyEventListener.class); 1017 IBinder mockBinder1 = mock(IBinder.class); 1018 IBinder mockBinder2 = mock(IBinder.class); 1019 when(mockHandler1.asBinder()).thenReturn(mockBinder1); 1020 when(mockHandler2.asBinder()).thenReturn(mockBinder2); 1021 CarPropertyValue mockValue = mock(CarPropertyValue.class); 1022 when(mHalService.getProperty(anyInt(), anyInt())).thenReturn(mockValue); 1023 1024 // Client 1 1025 // On-change: 1026 // [left -> 0f] 1027 // Continuous: 1028 // [left -> 20f, right -> 20f] 1029 List<CarSubscription> firstSubscribeOptions = List.of( 1030 createCarSubscriptionOption(ON_CHANGE_ZONED_PROPERTY_ID, new int[]{ 1031 VehicleAreaWindow.WINDOW_ROW_1_LEFT}, 0f), 1032 createCarSubscriptionOption(CONTINUOUS_ZONED_PROPERTY_ID, new int[]{ 1033 VehicleAreaWindow.WINDOW_ROW_1_LEFT, VehicleAreaWindow.WINDOW_ROW_1_RIGHT}, 1034 20f)); 1035 mService.registerListener(firstSubscribeOptions, mockHandler1); 1036 1037 // Client 2 1038 // On-change: 1039 // [left -> 0f (filtered out), right -> 0f] 1040 // Continuous: 1041 // [left -> 10f (filtered out), right -> 30f] 1042 List<CarSubscription> secondSubscribeOptions = List.of( 1043 createCarSubscriptionOption(ON_CHANGE_ZONED_PROPERTY_ID, new int[]{ 1044 VehicleAreaWindow.WINDOW_ROW_1_LEFT, VehicleAreaWindow.WINDOW_ROW_1_RIGHT}, 1045 0f), 1046 createCarSubscriptionOption(CONTINUOUS_ZONED_PROPERTY_ID, new int[]{ 1047 VehicleAreaWindow.WINDOW_ROW_1_LEFT}, 10f), 1048 createCarSubscriptionOption(CONTINUOUS_ZONED_PROPERTY_ID, new int[]{ 1049 VehicleAreaWindow.WINDOW_ROW_1_RIGHT}, 30f)); 1050 mService.registerListener(secondSubscribeOptions, mockHandler2); 1051 1052 clearInvocations(mHalService); 1053 1054 // Unregister client 2 for on-change. This should cause right to be unsubscribed. However, 1055 // we do not have unsubscribe API for zone, so we will do nothing. 1056 mService.unregisterListener(ON_CHANGE_ZONED_PROPERTY_ID, mockHandler2); 1057 1058 verify(mHalService, never()).subscribeProperty(any()); 1059 verify(mHalService, never()).unsubscribeProperty(anyInt()); 1060 1061 // Unregister client 1 for on-change, now no client is registered to on-change. 1062 mService.unregisterListener(ON_CHANGE_ZONED_PROPERTY_ID, mockHandler1); 1063 1064 verify(mHalService).unsubscribeProperty(ON_CHANGE_ZONED_PROPERTY_ID); 1065 1066 // Unregister client 2 for continuous. This should update right from 30f to 20f. 1067 clearInvocations(mHalService); 1068 mService.unregisterListener(CONTINUOUS_ZONED_PROPERTY_ID, mockHandler2); 1069 1070 verify(mHalService).subscribeProperty(List.of( 1071 createCarSubscriptionOption(CONTINUOUS_ZONED_PROPERTY_ID, new int[]{ 1072 VehicleAreaWindow.WINDOW_ROW_1_RIGHT}, 20f))); 1073 verify(mHalService, never()).unsubscribeProperty(anyInt()); 1074 1075 clearInvocations(mHalService); 1076 // Unregister client 1 for on-change, now no client is registered to continuous. 1077 mService.unregisterListener(CONTINUOUS_ZONED_PROPERTY_ID, mockHandler1); 1078 1079 verify(mHalService, never()).subscribeProperty(any()); 1080 verify(mHalService).unsubscribeProperty(CONTINUOUS_ZONED_PROPERTY_ID); 1081 } 1082 1083 @Test testregisterListener_emptyAreaIds()1084 public void testregisterListener_emptyAreaIds() throws Exception { 1085 ICarPropertyEventListener mockHandler = mock(ICarPropertyEventListener.class); 1086 IBinder mockBinder = mock(IBinder.class); 1087 when(mockHandler.asBinder()).thenReturn(mockBinder); 1088 1089 assertThrows(IllegalArgumentException.class, () -> 1090 mService.registerListener(List.of( 1091 createCarSubscriptionOption(ON_CHANGE_ZONED_PROPERTY_ID, new int[]{}, 0f)), 1092 mockHandler)); 1093 } 1094 1095 @Test testregisterListener_nullAreaIds()1096 public void testregisterListener_nullAreaIds() throws Exception { 1097 ICarPropertyEventListener mockHandler = mock(ICarPropertyEventListener.class); 1098 IBinder mockBinder = mock(IBinder.class); 1099 when(mockHandler.asBinder()).thenReturn(mockBinder); 1100 1101 assertThrows(IllegalArgumentException.class, () -> 1102 mService.registerListener(List.of( 1103 createCarSubscriptionOption(ON_CHANGE_ZONED_PROPERTY_ID, 1104 /* areaId= */ null, 0f)), 1105 mockHandler)); 1106 } 1107 1108 @Test testUnregisterListener_exceptionFromPropertyHalService()1109 public void testUnregisterListener_exceptionFromPropertyHalService() 1110 throws Exception { 1111 ICarPropertyEventListener mockHandler = mock(ICarPropertyEventListener.class); 1112 IBinder mockBinder = mock(IBinder.class); 1113 when(mockHandler.asBinder()).thenReturn(mockBinder); 1114 CarPropertyValue mockValue = mock(CarPropertyValue.class); 1115 when(mHalService.getProperty(anyInt(), anyInt())).thenReturn(mockValue); 1116 doThrow(new ServiceSpecificException(0)).when(mHalService).unsubscribeProperty(anyInt()); 1117 1118 List<CarSubscription> subscribeOptions = List.of( 1119 createCarSubscriptionOption(SPEED_ID, new int[]{0}, 20f), 1120 createCarSubscriptionOption(HVAC_TEMP, new int[]{0}, 0f)); 1121 1122 mService.registerListener(subscribeOptions, mockHandler); 1123 1124 assertThrows(ServiceSpecificException.class, () -> 1125 mService.unregisterListener(SPEED_ID, mockHandler)); 1126 } 1127 1128 @Test testUnregisterListener_exceptionFromPropertyHalService_retry()1129 public void testUnregisterListener_exceptionFromPropertyHalService_retry() 1130 throws Exception { 1131 ICarPropertyEventListener mockHandler = mock(ICarPropertyEventListener.class); 1132 IBinder mockBinder = mock(IBinder.class); 1133 when(mockHandler.asBinder()).thenReturn(mockBinder); 1134 CarPropertyValue mockValue = mock(CarPropertyValue.class); 1135 when(mHalService.getProperty(anyInt(), anyInt())).thenReturn(mockValue); 1136 doThrow(new ServiceSpecificException(0)).when(mHalService).unsubscribeProperty(anyInt()); 1137 1138 List<CarSubscription> subscribeOptions = List.of( 1139 createCarSubscriptionOption(SPEED_ID, new int[]{0}, 20f), 1140 createCarSubscriptionOption(HVAC_TEMP, new int[]{0}, 0f)); 1141 1142 mService.registerListener(subscribeOptions, mockHandler); 1143 1144 assertThrows(ServiceSpecificException.class, () -> 1145 mService.unregisterListener(SPEED_ID, mockHandler)); 1146 1147 // Finish the async get initial value task. 1148 mService.finishHandlerTasks(/*timeoutInMs=*/ 1000); 1149 1150 // Simulate the error goes away. 1151 clearInvocations(mHalService); 1152 doNothing().when(mHalService).unsubscribeProperty(anyInt()); 1153 1154 mService.unregisterListener(SPEED_ID, mockHandler); 1155 1156 // The retry request must go to PropertyHalService. 1157 verify(mHalService).unsubscribeProperty(SPEED_ID); 1158 } 1159 1160 @Test testOnPropertySetError()1161 public void testOnPropertySetError() throws Exception { 1162 int areaId = 0; 1163 int propertyId = WRITE_ONLY_INT_PROPERTY_ID; 1164 CarPropertyValue<Integer> value = new CarPropertyValue<>( 1165 propertyId, areaId, MIN_INT_VALUE); 1166 ICarPropertyEventListener mockEventListener = mock(ICarPropertyEventListener.class); 1167 IBinder mBinder = mock(IBinder.class); 1168 when(mockEventListener.asBinder()).thenReturn(mBinder); 1169 mService.setProperty(value, mockEventListener); 1170 1171 int errorCode = 2; 1172 mService.onPropertySetError(propertyId, areaId, errorCode); 1173 1174 verify(mockEventListener).onEvent(mPropertyEventCaptor.capture()); 1175 1176 List<CarPropertyEvent> events = mPropertyEventCaptor.getValue(); 1177 assertThat(events).containsExactly(CarPropertyEvent.createErrorEventWithErrorCode( 1178 propertyId, areaId, errorCode 1179 )); 1180 } 1181 1182 @Test testOnPropertySetError_unregisterProperty()1183 public void testOnPropertySetError_unregisterProperty() throws Exception { 1184 int areaId = 0; 1185 int propertyId = WRITE_ONLY_INT_PROPERTY_ID; 1186 CarPropertyValue<Integer> value = new CarPropertyValue<>( 1187 propertyId, areaId, MIN_INT_VALUE); 1188 ICarPropertyEventListener mockEventListener = mock(ICarPropertyEventListener.class); 1189 IBinder mBinder = mock(IBinder.class); 1190 when(mockEventListener.asBinder()).thenReturn(mBinder); 1191 mService.setProperty(value, mockEventListener); 1192 1193 int errorCode = 2; 1194 mService.onPropertySetError(propertyId, areaId, errorCode); 1195 1196 verify(mockEventListener).onEvent(any()); 1197 clearInvocations(mockEventListener); 1198 1199 // After unregisterListener, the listener must not receive onPropertyError event. 1200 mService.unregisterListener(propertyId, mockEventListener); 1201 mService.onPropertySetError(propertyId, areaId, errorCode); 1202 1203 verify(mockEventListener, never()).onEvent(any()); 1204 } 1205 1206 private static class EventListener extends ICarPropertyEventListener.Stub{ 1207 private final CarPropertyService mService; 1208 private List<CarPropertyEvent> mEvents; 1209 private Exception mException; 1210 EventListener(CarPropertyService service)1211 EventListener(CarPropertyService service) { 1212 mService = service; 1213 } 1214 1215 @Override onEvent(List<CarPropertyEvent> events)1216 public void onEvent(List<CarPropertyEvent> events) throws RemoteException { 1217 CountDownLatch latch = new CountDownLatch(1); 1218 new Thread(() -> { 1219 // Call a function in CarPropertyService to make sure there is no dead lock. This 1220 // call doesn't actually do anything. We must use a new thread here because 1221 // the same thread can obtain the same lock again even if there is a dead lock. 1222 mService.getReadPermission(0); 1223 latch.countDown(); 1224 }, "onEventThread").start(); 1225 try { 1226 if (!latch.await(5, TimeUnit.SECONDS)) { 1227 mException = new Exception("timeout waiting for getReadPermission, dead lock?"); 1228 return; 1229 } 1230 } catch (InterruptedException e) { 1231 Thread.currentThread().interrupt(); 1232 mException = new Exception("waiting for onEvent thread interrupted"); 1233 return; 1234 } 1235 mEvents = events; 1236 } 1237 getEvents()1238 List<CarPropertyEvent> getEvents() throws Exception { 1239 if (mException != null) { 1240 throw mException; 1241 } 1242 return mEvents; 1243 } 1244 } 1245 1246 @Test testOnEventCallback_MustNotHaveDeadLock()1247 public void testOnEventCallback_MustNotHaveDeadLock() throws Exception { 1248 // This test checks that CarPropertyService must not hold any lock while calling 1249 // ICarPropertyListener's onEvent callback, otherwise it might cause dead lock if 1250 // the callback calls another function in CarPropertyService that requires the same lock. 1251 mService.init(); 1252 1253 CarPropertyValue<Float> value = new CarPropertyValue<Float>(HVAC_TEMP, 0, 1.0f); 1254 when(mHalService.getProperty(HVAC_TEMP, 0)).thenReturn(value); 1255 EventListener listener = new EventListener(mService); 1256 1257 mService.registerListener(HVAC_TEMP, /* updateRateHz= */ SENSOR_RATE_ONCHANGE, listener); 1258 List<CarPropertyEvent> events = List.of(new CarPropertyEvent(0, value)); 1259 mService.onPropertyChange(events); 1260 1261 List<CarPropertyEvent> actualEvents = listener.getEvents(); 1262 1263 assertThat(actualEvents.size()).isEqualTo(events.size()); 1264 for (int i = 0; i < events.size(); i++) { 1265 CarPropertyEvent actualEvent = actualEvents.get(i); 1266 CarPropertyEvent expectedEvent = events.get(i); 1267 assertThat(actualEvent.getEventType()).isEqualTo(expectedEvent.getEventType()); 1268 assertThat(actualEvent.getErrorCode()).isEqualTo(expectedEvent.getErrorCode()); 1269 CarPropertyValue actualCarPropertyValue = actualEvent.getCarPropertyValue(); 1270 CarPropertyValue expectedCarPropertyValue = expectedEvent.getCarPropertyValue(); 1271 assertThat(actualCarPropertyValue.getPropertyId()).isEqualTo( 1272 expectedCarPropertyValue.getPropertyId()); 1273 assertThat(actualCarPropertyValue.getAreaId()).isEqualTo( 1274 expectedCarPropertyValue.getAreaId()); 1275 assertThat(actualCarPropertyValue.getStatus()).isEqualTo( 1276 expectedCarPropertyValue.getStatus()); 1277 assertThat(actualCarPropertyValue.getTimestamp()).isEqualTo( 1278 expectedCarPropertyValue.getTimestamp()); 1279 assertThat(actualCarPropertyValue.getValue()).isEqualTo( 1280 expectedCarPropertyValue.getValue()); 1281 } 1282 } 1283 1284 @Test getProperty_throwsExceptionBecauseOfUnsupportedPropertyId()1285 public void getProperty_throwsExceptionBecauseOfUnsupportedPropertyId() { 1286 assertThrows(IllegalArgumentException.class, 1287 () -> mService.getProperty(VehiclePropertyIds.INVALID, 1288 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 1289 } 1290 1291 @Test getProperty_throwsExceptionBecausePropertyIsNotReadable()1292 public void getProperty_throwsExceptionBecausePropertyIsNotReadable() { 1293 assertThrows(IllegalArgumentException.class, 1294 () -> mService.getProperty(WRITE_ONLY_INT_PROPERTY_ID, 1295 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)); 1296 } 1297 1298 @Test getProperty_throwsSecurityExceptionIfPlatformDoesNotHavePermissionToRead()1299 public void getProperty_throwsSecurityExceptionIfPlatformDoesNotHavePermissionToRead() { 1300 assertThrows(SecurityException.class, 1301 () -> mService.getProperty(VehiclePropertyIds.GEAR_SELECTION, 0)); 1302 } 1303 1304 @Test getProperty_throwsSecurityExceptionIfAppDoesNotHavePermissionToRead()1305 public void getProperty_throwsSecurityExceptionIfAppDoesNotHavePermissionToRead() { 1306 when(mHalService.isReadable(mContext, VehiclePropertyIds.GEAR_SELECTION)) 1307 .thenReturn(false); 1308 assertThrows(SecurityException.class, 1309 () -> mService.getProperty(VehiclePropertyIds.GEAR_SELECTION, 0)); 1310 } 1311 1312 @Test getProperty_throwsExceptionBecauseOfUnsupportedAreaId()1313 public void getProperty_throwsExceptionBecauseOfUnsupportedAreaId() { 1314 assertThrows(IllegalArgumentException.class, 1315 () -> mService.getProperty(ON_CHANGE_READ_WRITE_PROPERTY_ID, 1316 NOT_SUPPORTED_AREA_ID)); 1317 } 1318 1319 @Test setProperty_throwsExceptionBecauseOfNullCarPropertyValue()1320 public void setProperty_throwsExceptionBecauseOfNullCarPropertyValue() { 1321 assertThrows(NullPointerException.class, 1322 () -> mService.setProperty(null, mICarPropertyEventListener)); 1323 } 1324 1325 @Test setProperty_throwsExceptionBecauseOfNullListener()1326 public void setProperty_throwsExceptionBecauseOfNullListener() { 1327 assertThrows(NullPointerException.class, () -> mService.setProperty( 1328 new CarPropertyValue(ON_CHANGE_READ_WRITE_PROPERTY_ID, GLOBAL_AREA_ID, 1329 Integer.MAX_VALUE), null)); 1330 } 1331 1332 @Test setProperty_throwsExceptionBecauseOfUnsupportedPropertyId()1333 public void setProperty_throwsExceptionBecauseOfUnsupportedPropertyId() { 1334 assertThrows(IllegalArgumentException.class, () -> mService.setProperty( 1335 new CarPropertyValue(VehiclePropertyIds.INVALID, GLOBAL_AREA_ID, Integer.MAX_VALUE), 1336 mICarPropertyEventListener)); 1337 } 1338 1339 @Test setProperty_throwsExceptionBecausePropertyIsNotWritable()1340 public void setProperty_throwsExceptionBecausePropertyIsNotWritable() { 1341 assertThrows(IllegalArgumentException.class, () -> mService.setProperty( 1342 new CarPropertyValue(CONTINUOUS_READ_ONLY_PROPERTY_ID, GLOBAL_AREA_ID, 1343 Integer.MAX_VALUE), mICarPropertyEventListener)); 1344 } 1345 1346 @Test setProperty_throwsSecurityExceptionIfPlatformDoesNotHavePermissionToWrite()1347 public void setProperty_throwsSecurityExceptionIfPlatformDoesNotHavePermissionToWrite() { 1348 assertThrows(SecurityException.class, () -> mService.setProperty( 1349 new CarPropertyValue(NO_PERMISSION_PROPERTY_ID, GLOBAL_AREA_ID, Integer.MAX_VALUE), 1350 mICarPropertyEventListener)); 1351 } 1352 1353 @Test setProperty_throwsSecurityExceptionIfAppDoesNotHavePermissionToWrite()1354 public void setProperty_throwsSecurityExceptionIfAppDoesNotHavePermissionToWrite() { 1355 when(mHalService.isWritable(mContext, NO_PERMISSION_PROPERTY_ID)) 1356 .thenReturn(false); 1357 assertThrows(SecurityException.class, () -> mService.setProperty( 1358 new CarPropertyValue(NO_PERMISSION_PROPERTY_ID, GLOBAL_AREA_ID, Integer.MAX_VALUE), 1359 mICarPropertyEventListener)); 1360 } 1361 1362 @Test setProperty_throwsExceptionBecauseOfUnsupportedAreaId()1363 public void setProperty_throwsExceptionBecauseOfUnsupportedAreaId() { 1364 assertThrows(IllegalArgumentException.class, () -> mService.setProperty( 1365 new CarPropertyValue(ON_CHANGE_READ_WRITE_PROPERTY_ID, NOT_SUPPORTED_AREA_ID, 1366 Integer.MAX_VALUE), mICarPropertyEventListener)); 1367 } 1368 1369 @Test setProperty_throwsExceptionBecauseOfNullSetValue()1370 public void setProperty_throwsExceptionBecauseOfNullSetValue() { 1371 assertThrows(IllegalArgumentException.class, () -> mService.setProperty( 1372 new CarPropertyValue(ON_CHANGE_READ_WRITE_PROPERTY_ID, GLOBAL_AREA_ID, null), 1373 mICarPropertyEventListener)); 1374 } 1375 1376 @Test setProperty_throwsExceptionBecauseOfSetValueTypeMismatch()1377 public void setProperty_throwsExceptionBecauseOfSetValueTypeMismatch() { 1378 assertThrows(IllegalArgumentException.class, () -> mService.setProperty( 1379 new CarPropertyValue(WRITE_ONLY_INT_PROPERTY_ID, GLOBAL_AREA_ID, Float.MAX_VALUE), 1380 mICarPropertyEventListener)); 1381 } 1382 1383 @Test setProperty_throwsExceptionBecauseOfIntSetValueIsLessThanMinValue()1384 public void setProperty_throwsExceptionBecauseOfIntSetValueIsLessThanMinValue() { 1385 assertThrows(IllegalArgumentException.class, () -> mService.setProperty( 1386 new CarPropertyValue(WRITE_ONLY_INT_PROPERTY_ID, GLOBAL_AREA_ID, MIN_INT_VALUE - 1), 1387 mICarPropertyEventListener)); 1388 } 1389 1390 @Test setProperty_throwsExceptionBecauseOfIntSetValueIsGreaterThanMaxValue()1391 public void setProperty_throwsExceptionBecauseOfIntSetValueIsGreaterThanMaxValue() { 1392 assertThrows(IllegalArgumentException.class, () -> mService.setProperty( 1393 new CarPropertyValue(WRITE_ONLY_INT_PROPERTY_ID, GLOBAL_AREA_ID, MAX_INT_VALUE + 1), 1394 mICarPropertyEventListener)); 1395 } 1396 1397 @Test setProperty_throwsExceptionBecauseOfLongSetValueIsLessThanMinValue()1398 public void setProperty_throwsExceptionBecauseOfLongSetValueIsLessThanMinValue() { 1399 assertThrows(IllegalArgumentException.class, () -> mService.setProperty( 1400 new CarPropertyValue(WRITE_ONLY_LONG_PROPERTY_ID, GLOBAL_AREA_ID, 1401 MIN_LONG_VALUE - 1), mICarPropertyEventListener)); 1402 } 1403 1404 @Test setProperty_throwsExceptionBecauseOfLongSetValueIsGreaterThanMaxValue()1405 public void setProperty_throwsExceptionBecauseOfLongSetValueIsGreaterThanMaxValue() { 1406 assertThrows(IllegalArgumentException.class, () -> mService.setProperty( 1407 new CarPropertyValue(WRITE_ONLY_LONG_PROPERTY_ID, GLOBAL_AREA_ID, 1408 MAX_LONG_VALUE + 1), mICarPropertyEventListener)); 1409 } 1410 1411 @Test setProperty_throwsExceptionBecauseOfFloatSetValueIsLessThanMinValue()1412 public void setProperty_throwsExceptionBecauseOfFloatSetValueIsLessThanMinValue() { 1413 assertThrows(IllegalArgumentException.class, () -> mService.setProperty( 1414 new CarPropertyValue(WRITE_ONLY_LONG_PROPERTY_ID, GLOBAL_AREA_ID, 1415 MIN_LONG_VALUE - 1), mICarPropertyEventListener)); 1416 } 1417 1418 @Test setProperty_throwsExceptionBecauseOfFloatSetValueIsGreaterThanMaxValue()1419 public void setProperty_throwsExceptionBecauseOfFloatSetValueIsGreaterThanMaxValue() { 1420 assertThrows(IllegalArgumentException.class, () -> mService.setProperty( 1421 new CarPropertyValue(WRITE_ONLY_FLOAT_PROPERTY_ID, GLOBAL_AREA_ID, 1422 MAX_FLOAT_VALUE + 1), mICarPropertyEventListener)); 1423 } 1424 1425 @Test setProperty_throwsExceptionBecauseOfSetValueIsNotInSupportedEnumValues()1426 public void setProperty_throwsExceptionBecauseOfSetValueIsNotInSupportedEnumValues() { 1427 assertThrows(IllegalArgumentException.class, () -> mService.setProperty( 1428 new CarPropertyValue(WRITE_ONLY_ENUM_PROPERTY_ID, GLOBAL_AREA_ID, 1429 UNSUPPORTED_ENUM_VALUE), mICarPropertyEventListener)); 1430 } 1431 1432 @Test setProperty_throwsExceptionBecauseOfSetValueIsErrorStateEnum()1433 public void setProperty_throwsExceptionBecauseOfSetValueIsErrorStateEnum() { 1434 assertThrows(IllegalArgumentException.class, () -> mService.setProperty( 1435 new CarPropertyValue(WRITE_ONLY_OTHER_ENUM_PROPERTY_ID, GLOBAL_AREA_ID, 1436 SUPPORTED_ERROR_STATE_ENUM_VALUE), mICarPropertyEventListener)); 1437 } 1438 1439 @Test setProperty_throwsExceptionBecauseOfSetValueIsOtherStateEnum()1440 public void setProperty_throwsExceptionBecauseOfSetValueIsOtherStateEnum() { 1441 assertThrows(IllegalArgumentException.class, () -> mService.setProperty( 1442 new CarPropertyValue(WRITE_ONLY_OTHER_ENUM_PROPERTY_ID, GLOBAL_AREA_ID, 1443 SUPPORTED_OTHER_STATE_ENUM_VALUE), mICarPropertyEventListener)); 1444 } 1445 1446 @Test registerListener_throwsExceptionBecauseOfNullListener()1447 public void registerListener_throwsExceptionBecauseOfNullListener() { 1448 assertThrows(NullPointerException.class, 1449 () -> mService.registerListener(ON_CHANGE_READ_WRITE_PROPERTY_ID, 1450 CarPropertyManager.SENSOR_RATE_NORMAL, 1451 /* carPropertyEventListener= */ null)); 1452 } 1453 1454 @Test registerListener_throwsExceptionBecauseOfUnsupportedPropertyId()1455 public void registerListener_throwsExceptionBecauseOfUnsupportedPropertyId() { 1456 assertThrows(IllegalArgumentException.class, 1457 () -> mService.registerListener(VehiclePropertyIds.INVALID, 1458 CarPropertyManager.SENSOR_RATE_NORMAL, mICarPropertyEventListener)); 1459 } 1460 1461 @Test registerListener_throwsExceptionBecausePropertyIsNotReadable()1462 public void registerListener_throwsExceptionBecausePropertyIsNotReadable() { 1463 assertThrows(IllegalArgumentException.class, 1464 () -> mService.registerListener(WRITE_ONLY_INT_PROPERTY_ID, 1465 CarPropertyManager.SENSOR_RATE_NORMAL, mICarPropertyEventListener)); 1466 } 1467 1468 @Test registerListener_throwsSecurityExceptionIfPlatformDoesNotHavePermissionToRead()1469 public void registerListener_throwsSecurityExceptionIfPlatformDoesNotHavePermissionToRead() { 1470 assertThrows(SecurityException.class, 1471 () -> mService.registerListener(NO_PERMISSION_PROPERTY_ID, 0, 1472 mICarPropertyEventListener)); 1473 } 1474 1475 @Test registerListener_throwsSecurityExceptionIfAppDoesNotHavePermissionToRead()1476 public void registerListener_throwsSecurityExceptionIfAppDoesNotHavePermissionToRead() { 1477 when(mHalService.isReadable(mContext, NO_PERMISSION_PROPERTY_ID)) 1478 .thenReturn(false); 1479 assertThrows(SecurityException.class, 1480 () -> mService.registerListener(NO_PERMISSION_PROPERTY_ID, 0, 1481 mICarPropertyEventListener)); 1482 } 1483 1484 @Test registerListener_updatesRateForNonContinuousProperty()1485 public void registerListener_updatesRateForNonContinuousProperty() { 1486 mService.registerListener(ON_CHANGE_READ_WRITE_PROPERTY_ID, 1487 CarPropertyManager.SENSOR_RATE_FAST, mICarPropertyEventListener); 1488 verify(mHalService).subscribeProperty(List.of(createCarSubscriptionOption( 1489 ON_CHANGE_READ_WRITE_PROPERTY_ID, new int[]{0}, SENSOR_RATE_ONCHANGE))); 1490 } 1491 1492 @Test registerListener_VurDefaultOn()1493 public void registerListener_VurDefaultOn() { 1494 mService.registerListener(CONTINUOUS_READ_ONLY_PROPERTY_ID, MIN_SAMPLE_RATE, 1495 mICarPropertyEventListener); 1496 1497 verify(mHalService).subscribeProperty(List.of(createCarSubscriptionOption( 1498 CONTINUOUS_READ_ONLY_PROPERTY_ID, new int[]{0}, MIN_SAMPLE_RATE, 1499 /* enableVur= */ true, /* resolution */ 0.0f))); 1500 } 1501 1502 @Test registerListener_VurExplicitOn()1503 public void registerListener_VurExplicitOn() { 1504 mService.registerListener(CONTINUOUS_READ_ONLY_PROPERTY_ID, MIN_SAMPLE_RATE, 1505 /* enableVariableUpdateRate= */ true, 1506 mICarPropertyEventListener); 1507 1508 verify(mHalService).subscribeProperty(List.of(createCarSubscriptionOption( 1509 CONTINUOUS_READ_ONLY_PROPERTY_ID, new int[]{0}, MIN_SAMPLE_RATE, 1510 /* enableVur= */ true, /* resolution */ 0.0f))); 1511 } 1512 1513 @Test registerListener_VurExplicitOff()1514 public void registerListener_VurExplicitOff() { 1515 mService.registerListener(CONTINUOUS_READ_ONLY_PROPERTY_ID, MIN_SAMPLE_RATE, 1516 /* enableVariableUpdateRate= */ false, 1517 mICarPropertyEventListener); 1518 1519 verify(mHalService).subscribeProperty(List.of(createCarSubscriptionOption( 1520 CONTINUOUS_READ_ONLY_PROPERTY_ID, new int[]{0}, MIN_SAMPLE_RATE, 1521 /* enableVur= */ false, /* resolution */ 0.0f))); 1522 } 1523 1524 @Test registerListener_VurFalseWhenFeatureOff()1525 public void registerListener_VurFalseWhenFeatureOff() { 1526 when(mFeatureFlags.variableUpdateRate()).thenReturn(false); 1527 1528 mService.registerListener(CONTINUOUS_READ_ONLY_PROPERTY_ID, MIN_SAMPLE_RATE, 1529 mICarPropertyEventListener); 1530 1531 verify(mHalService).subscribeProperty(List.of(createCarSubscriptionOption( 1532 CONTINUOUS_READ_ONLY_PROPERTY_ID, new int[]{0}, MIN_SAMPLE_RATE, 1533 /* enableVur= */ false, /* resolution */ 0.0f))); 1534 } 1535 1536 @Test registerListener_Resolution()1537 public void registerListener_Resolution() { 1538 mService.registerListener(CONTINUOUS_READ_ONLY_PROPERTY_ID, MIN_SAMPLE_RATE, 1539 /* enableVariableUpdateRate= */ true, /* resolution */ 1.0f, 1540 mICarPropertyEventListener); 1541 1542 verify(mHalService).subscribeProperty(List.of(createCarSubscriptionOption( 1543 CONTINUOUS_READ_ONLY_PROPERTY_ID, new int[]{0}, MIN_SAMPLE_RATE, 1544 /* enableVur= */ true, /* resolution */ 1.0f))); 1545 } 1546 1547 @Test registerListener_ResolutionZeroWhenFeatureOff()1548 public void registerListener_ResolutionZeroWhenFeatureOff() { 1549 when(mFeatureFlags.subscriptionWithResolution()).thenReturn(false); 1550 1551 mService.registerListener(CONTINUOUS_READ_ONLY_PROPERTY_ID, MIN_SAMPLE_RATE, 1552 /* enableVariableUpdateRate= */ true, /* resolution */ 1.0f, 1553 mICarPropertyEventListener); 1554 1555 verify(mHalService).subscribeProperty(List.of(createCarSubscriptionOption( 1556 CONTINUOUS_READ_ONLY_PROPERTY_ID, new int[]{0}, MIN_SAMPLE_RATE, 1557 /* enableVur= */ true, /* resolution */ 0.0f))); 1558 } 1559 1560 @Test registerListener_updatesRateToMinForContinuousProperty()1561 public void registerListener_updatesRateToMinForContinuousProperty() { 1562 mService.registerListener(CONTINUOUS_READ_ONLY_PROPERTY_ID, MIN_SAMPLE_RATE - 1, 1563 mICarPropertyEventListener); 1564 verify(mHalService).subscribeProperty(List.of(createCarSubscriptionOption( 1565 CONTINUOUS_READ_ONLY_PROPERTY_ID, new int[]{0}, MIN_SAMPLE_RATE, 1566 /* enableVur= */ true, /* resolution */ 0.0f))); 1567 } 1568 1569 @Test registerListener_updatesRateToMaxForContinuousProperty()1570 public void registerListener_updatesRateToMaxForContinuousProperty() { 1571 mService.registerListener(CONTINUOUS_READ_ONLY_PROPERTY_ID, MAX_SAMPLE_RATE + 1, 1572 mICarPropertyEventListener); 1573 verify(mHalService).subscribeProperty(List.of(createCarSubscriptionOption( 1574 CONTINUOUS_READ_ONLY_PROPERTY_ID, new int[]{0}, MAX_SAMPLE_RATE, 1575 /* enableVur= */ true, /* resolution */ 0.0f))); 1576 } 1577 1578 @Test registerListenerSafe_VurExplicitOn()1579 public void registerListenerSafe_VurExplicitOn() { 1580 mService.registerListenerSafe(CONTINUOUS_READ_ONLY_PROPERTY_ID, MIN_SAMPLE_RATE, 1581 /* enableVariableUpdateRate= */ true, 1582 mICarPropertyEventListener); 1583 1584 verify(mHalService).subscribeProperty(List.of(createCarSubscriptionOption( 1585 CONTINUOUS_READ_ONLY_PROPERTY_ID, new int[]{0}, MIN_SAMPLE_RATE, 1586 /* enableVur= */ true, /* resolution */ 0.0f))); 1587 } 1588 1589 @Test registerListenerSafe_returnsTrueWhenSuccessful()1590 public void registerListenerSafe_returnsTrueWhenSuccessful() { 1591 assertThat(mService.registerListenerSafe(ON_CHANGE_READ_WRITE_PROPERTY_ID, 1592 CarPropertyManager.SENSOR_RATE_NORMAL, mICarPropertyEventListener)).isTrue(); 1593 } 1594 1595 @Test registerListenerSafe_noExceptionBecauseOfUnsupportedPropertyIdAndReturnsFalse()1596 public void registerListenerSafe_noExceptionBecauseOfUnsupportedPropertyIdAndReturnsFalse() { 1597 assertThat(mService.registerListenerSafe(VehiclePropertyIds.INVALID, 1598 CarPropertyManager.SENSOR_RATE_NORMAL, mICarPropertyEventListener)).isFalse(); 1599 } 1600 1601 @Test unregisterListener_throwsExceptionBecauseOfNullListener()1602 public void unregisterListener_throwsExceptionBecauseOfNullListener() { 1603 assertThrows(NullPointerException.class, 1604 () -> mService.unregisterListener(ON_CHANGE_READ_WRITE_PROPERTY_ID, 1605 /* iCarPropertyEventListener= */ null)); 1606 } 1607 1608 @Test unregisterListener_throwsExceptionBecauseOfUnsupportedPropertyId()1609 public void unregisterListener_throwsExceptionBecauseOfUnsupportedPropertyId() { 1610 assertThrows(IllegalArgumentException.class, 1611 () -> mService.unregisterListener(VehiclePropertyIds.INVALID, 1612 mICarPropertyEventListener)); 1613 } 1614 1615 @Test unregisterListenerSafe_returnsTrueWhenSuccessful()1616 public void unregisterListenerSafe_returnsTrueWhenSuccessful() { 1617 assertThat(mService.unregisterListenerSafe(ON_CHANGE_READ_WRITE_PROPERTY_ID, 1618 mICarPropertyEventListener)).isTrue(); 1619 } 1620 1621 @Test unregisterListenerSafe_noExceptionBecauseOfUnsupportedPropertyIdAndReturnsFalse()1622 public void unregisterListenerSafe_noExceptionBecauseOfUnsupportedPropertyIdAndReturnsFalse() { 1623 assertThat(mService.unregisterListenerSafe(VehiclePropertyIds.INVALID, 1624 mICarPropertyEventListener)).isFalse(); 1625 } 1626 1627 @Test testCancelRequests()1628 public void testCancelRequests() { 1629 int[] requestIds = new int[]{1, 2}; 1630 1631 mService.cancelRequests(requestIds); 1632 1633 verify(mHalService).cancelRequests(requestIds); 1634 } 1635 1636 // Test that limited number of sync operations are allowed at once. 1637 @Test testSyncOperationLimit()1638 public void testSyncOperationLimit() throws Exception { 1639 CountDownLatch startCd = new CountDownLatch(16); 1640 CountDownLatch finishCd = new CountDownLatch(16); 1641 CountDownLatch returnCd = new CountDownLatch(1); 1642 when(mHalService.getProperty(CONTINUOUS_READ_ONLY_PROPERTY_ID, 0)) 1643 .thenReturn(new CarPropertyValue(HVAC_TEMP, /* areaId= */ 0, Float.valueOf(11f))); 1644 doAnswer((invocation) -> { 1645 // Notify the operation has started. 1646 startCd.countDown(); 1647 1648 Log.d(TAG, "simulate sync operation ongoing, waiting for signal before finish."); 1649 1650 // Wait for the signal before finishing the operation. 1651 returnCd.await(10, TimeUnit.SECONDS); 1652 return null; 1653 }).when(mHalService).setProperty(any()); 1654 1655 Executor executor = Executors.newFixedThreadPool(16); 1656 for (int i = 0; i < 16; i++) { 1657 executor.execute(() -> { 1658 mService.setProperty( 1659 new CarPropertyValue(WRITE_ONLY_INT_PROPERTY_ID, GLOBAL_AREA_ID, 1660 Integer.valueOf(11)), 1661 mICarPropertyEventListener); 1662 finishCd.countDown(); 1663 }); 1664 } 1665 1666 // Wait until 16 requests are ongoing. 1667 startCd.await(10, TimeUnit.SECONDS); 1668 1669 // The 17th request must throw exception. 1670 Exception e = assertThrows(ServiceSpecificException.class, () -> mService.getProperty( 1671 CONTINUOUS_READ_ONLY_PROPERTY_ID, /* areaId= */ 0)); 1672 assertThat(((ServiceSpecificException) e).errorCode).isEqualTo(SYNC_OP_LIMIT_TRY_AGAIN); 1673 1674 // Unblock the operations. 1675 returnCd.countDown(); 1676 assertWithMessage("All setProperty operations finished within 10 seconds") 1677 .that(finishCd.await(10, TimeUnit.SECONDS)).isTrue(); 1678 } 1679 1680 @Test getSupportedNoReadPermPropIds()1681 public void getSupportedNoReadPermPropIds() throws Exception { 1682 int invalidPropertyID = -1; 1683 assertThat(mService.getSupportedNoReadPermPropIds(new int[] { 1684 CONTINUOUS_READ_ONLY_PROPERTY_ID, WRITE_ONLY_INT_PROPERTY_ID, invalidPropertyID 1685 })).isEqualTo(new int[]{WRITE_ONLY_INT_PROPERTY_ID}); 1686 } 1687 1688 @Test isSupportedAndHasWritePermissionOnly_unsupported()1689 public void isSupportedAndHasWritePermissionOnly_unsupported() throws Exception { 1690 assertThat(mService.isSupportedAndHasWritePermissionOnly(-1)).isFalse(); 1691 } 1692 1693 @Test isSupportedAndHasWritePermissionOnly_writeOnly()1694 public void isSupportedAndHasWritePermissionOnly_writeOnly() throws Exception { 1695 assertThat(mService.isSupportedAndHasWritePermissionOnly(WRITE_ONLY_INT_PROPERTY_ID)) 1696 .isTrue(); 1697 } 1698 1699 @Test isSupportedAndHasWritePermissionOnly_readOnly()1700 public void isSupportedAndHasWritePermissionOnly_readOnly() throws Exception { 1701 assertThat(mService.isSupportedAndHasWritePermissionOnly(CONTINUOUS_READ_ONLY_PROPERTY_ID)) 1702 .isFalse(); 1703 } 1704 1705 /** Creates a {@code CarSubscription} with Vur off. */ createCarSubscriptionOption(int propertyId, int[] areaId, float updateRateHz)1706 private static CarSubscription createCarSubscriptionOption(int propertyId, 1707 int[] areaId, float updateRateHz) { 1708 return createCarSubscriptionOption(propertyId, areaId, updateRateHz, 1709 /* enableVur= */ false, /*resolution*/ 0.0f); 1710 } 1711 1712 /** Creates a {@code CarSubscription}. */ createCarSubscriptionOption(int propertyId, int[] areaId, float updateRateHz, boolean enableVur)1713 private static CarSubscription createCarSubscriptionOption(int propertyId, 1714 int[] areaId, float updateRateHz, boolean enableVur) { 1715 return createCarSubscriptionOption(propertyId, areaId, updateRateHz, 1716 enableVur, /*resolution*/ 0.0f); 1717 } 1718 1719 /** Creates a {@code CarSubscription}. */ createCarSubscriptionOption(int propertyId, int[] areaId, float updateRateHz, boolean enableVur, float resolution)1720 private static CarSubscription createCarSubscriptionOption(int propertyId, 1721 int[] areaId, float updateRateHz, boolean enableVur, float resolution) { 1722 CarSubscription options = new CarSubscription(); 1723 options.propertyId = propertyId; 1724 options.areaIds = areaId; 1725 options.updateRateHz = updateRateHz; 1726 options.enableVariableUpdateRate = enableVur; 1727 options.resolution = resolution; 1728 return options; 1729 } 1730 } 1731