1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.car.hardware.property;
18 
19 import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
20 import static android.car.VehiclePropertyIds.FUEL_DOOR_OPEN;
21 import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET;
22 import static android.car.VehiclePropertyIds.INFO_EV_CONNECTOR_TYPE;
23 import static android.car.VehiclePropertyIds.INFO_FUEL_DOOR_LOCATION;
24 import static android.car.VehiclePropertyIds.INITIAL_USER_INFO;
25 import static android.car.VehiclePropertyIds.INVALID;
26 import static android.car.hardware.property.CarPropertyManager.GetPropertyResult;
27 import static android.car.hardware.property.CarPropertyManager.PropertyAsyncError;
28 import static android.car.hardware.property.CarPropertyManager.SENSOR_RATE_ONCHANGE;
29 import static android.car.hardware.property.CarPropertyManager.SetPropertyRequest;
30 import static android.car.hardware.property.CarPropertyManager.SetPropertyResult;
31 
32 import static com.android.car.internal.property.CarPropertyHelper.SYNC_OP_LIMIT_TRY_AGAIN;
33 
34 import static com.google.common.truth.Truth.assertThat;
35 import static com.google.common.truth.Truth.assertWithMessage;
36 
37 import static org.junit.Assert.assertThrows;
38 import static org.mockito.ArgumentMatchers.any;
39 import static org.mockito.ArgumentMatchers.anyInt;
40 import static org.mockito.ArgumentMatchers.anyLong;
41 import static org.mockito.ArgumentMatchers.eq;
42 import static org.mockito.Mockito.clearInvocations;
43 import static org.mockito.Mockito.doAnswer;
44 import static org.mockito.Mockito.doNothing;
45 import static org.mockito.Mockito.doThrow;
46 import static org.mockito.Mockito.mock;
47 import static org.mockito.Mockito.never;
48 import static org.mockito.Mockito.timeout;
49 import static org.mockito.Mockito.times;
50 import static org.mockito.Mockito.verify;
51 import static org.mockito.Mockito.when;
52 
53 import android.car.VehicleAreaSeat;
54 import android.car.VehicleAreaType;
55 import android.car.VehiclePropertyIds;
56 import android.car.feature.FeatureFlags;
57 import android.car.hardware.CarPropertyConfig;
58 import android.car.hardware.CarPropertyValue;
59 import android.content.Context;
60 import android.content.pm.ApplicationInfo;
61 import android.os.Build;
62 import android.os.CancellationSignal;
63 import android.os.Handler;
64 import android.os.Looper;
65 import android.os.RemoteException;
66 import android.os.ServiceSpecificException;
67 import android.platform.test.annotations.IgnoreUnderRavenwood;
68 import android.platform.test.ravenwood.RavenwoodRule;
69 import android.util.ArraySet;
70 import android.util.SparseArray;
71 
72 import com.android.car.internal.ICarBase;
73 import com.android.car.internal.property.AsyncPropertyServiceRequest;
74 import com.android.car.internal.property.AsyncPropertyServiceRequestList;
75 import com.android.car.internal.property.CarPropertyConfigList;
76 import com.android.car.internal.property.CarPropertyErrorCodes;
77 import com.android.car.internal.property.CarSubscription;
78 import com.android.car.internal.property.GetPropertyConfigListResult;
79 import com.android.car.internal.property.GetSetValueResult;
80 import com.android.car.internal.property.GetSetValueResultList;
81 import com.android.car.internal.property.IAsyncPropertyResultCallback;
82 import com.android.car.internal.util.IntArray;
83 
84 import org.junit.Before;
85 import org.junit.Rule;
86 import org.junit.Test;
87 import org.junit.runner.RunWith;
88 import org.mockito.ArgumentCaptor;
89 import org.mockito.Captor;
90 import org.mockito.Mock;
91 import org.mockito.junit.MockitoJUnitRunner;
92 
93 import java.time.Duration;
94 import java.util.ArrayList;
95 import java.util.List;
96 import java.util.Set;
97 import java.util.concurrent.Executor;
98 
99 /**
100  * <p>This class contains unit tests for the {@link CarPropertyManager}.
101  */
102 @RunWith(MockitoJUnitRunner.class)
103 public final class CarPropertyManagerUnitTest {
104     // Required to set the process ID and set the "main" thread for this test, otherwise
105     // getMainLooper will return null.
106     @Rule
107     public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
108             .setProcessApp()
109             .setProvideMainThread(true)
110             .build();
111 
112     // Defined as vendor property.
113     private static final int VENDOR_CONTINUOUS_PROPERTY = 0x21100111;
114     private static final int VENDOR_ON_CHANGE_PROPERTY = 0x21100222;
115     private static final int VENDOR_STATIC_PROPERTY = 0x21100333;
116 
117     private static final int BOOLEAN_PROP = FUEL_DOOR_OPEN;
118     private static final int INT32_PROP = INFO_FUEL_DOOR_LOCATION;
119     private static final int INT32_VEC_PROP = INFO_EV_CONNECTOR_TYPE;
120     private static final int FLOAT_PROP = HVAC_TEMPERATURE_SET;
121 
122     private static final float MIN_UPDATE_RATE_HZ = 10;
123     private static final float MAX_UPDATE_RATE_HZ = 100;
124     private static final float FIRST_UPDATE_RATE_HZ = 50;
125     private static final float LARGER_UPDATE_RATE_HZ = 50.1f;
126     private static final float SMALLER_UPDATE_RATE_HZ = 49.9f;
127 
128     private static final int VENDOR_ERROR_CODE = 0x2;
129     private static final int VENDOR_ERROR_CODE_SHIFT = 16;
130     private static final int UNKNOWN_ERROR = -101;
131 
132     private static final long TEST_TIMESTAMP = 1234;
133 
134     private Handler mMainHandler;
135 
136     private CarPropertyConfig mContinuousCarPropertyConfig;
137     private CarPropertyConfig mOnChangeCarPropertyConfig;
138     private CarPropertyConfig mStaticCarPropertyConfig;
139     private final SparseArray<CarPropertyConfig> mCarPropertyConfigsById = new SparseArray<>();
140     private final ArraySet<Integer> mUnsupportedPropIds = new ArraySet<>();
141     private final ArraySet<Integer> mMissingPermissionPropIds = new ArraySet<>();
142 
143     @Mock
144     private ICarBase mCar;
145     @Mock
146     private ApplicationInfo mApplicationInfo;
147     @Mock
148     private ICarProperty mICarProperty;
149     @Mock
150     private Context mContext;
151     @Mock
152     private CarPropertyManager.CarPropertyEventCallback mCarPropertyEventCallback;
153     @Mock
154     private CarPropertyManager.CarPropertyEventCallback mCarPropertyEventCallback2;
155     @Mock
156     private CarPropertyManager.GetPropertyCallback mGetPropertyCallback;
157     @Mock
158     private CarPropertyManager.SetPropertyCallback mSetPropertyCallback;
159     @Mock
160     private Executor mMockExecutor1;
161     @Mock
162     private Executor mMockExecutor2;
163     @Mock
164     private FeatureFlags mFeatureFlags;
165 
166     @Captor
167     private ArgumentCaptor<Integer> mPropertyIdCaptor;
168     @Captor
169     private ArgumentCaptor<List> mCarSubscriptionCaptor;
170     @Captor
171     private ArgumentCaptor<AsyncPropertyServiceRequestList> mAsyncPropertyServiceRequestCaptor;
172     @Captor
173     private ArgumentCaptor<PropertyAsyncError> mPropertyAsyncErrorCaptor;
174     @Captor
175     private ArgumentCaptor<GetPropertyResult<?>> mGetPropertyResultCaptor;
176     @Captor
177     private ArgumentCaptor<SetPropertyResult> mSetPropertyResultCaptor;
178     private CarPropertyManager mCarPropertyManager;
179 
combineErrors(int systemError, int vendorError)180     private static int combineErrors(int systemError, int vendorError) {
181         return vendorError << VENDOR_ERROR_CODE_SHIFT | systemError;
182     }
183 
createErrorCarPropertyEventList()184     private static List<CarPropertyEvent> createErrorCarPropertyEventList() {
185         CarPropertyValue<Integer> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
186                 CarPropertyValue.STATUS_AVAILABLE, 0, -1);
187         CarPropertyEvent carPropertyEvent = new CarPropertyEvent(
188                 CarPropertyEvent.PROPERTY_EVENT_ERROR, value,
189                 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN);
190         return List.of(carPropertyEvent);
191     }
192 
createCarPropertyEventList()193     private static List<CarPropertyEvent> createCarPropertyEventList() {
194         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0, 17.0f);
195         CarPropertyEvent carPropertyEvent = new CarPropertyEvent(
196                 CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, value);
197         return List.of(carPropertyEvent);
198     }
199 
createCarSubscriptionOption(int propertyId, int[] areaId, float updateRateHz)200     private static CarSubscription createCarSubscriptionOption(int propertyId,
201             int[] areaId, float updateRateHz) {
202         return createCarSubscriptionOption(propertyId, areaId, updateRateHz,
203                 /* enableVur= */ false);
204     }
205 
createCarSubscriptionOption(int propertyId, int[] areaId, float updateRateHz, boolean enableVur)206     private static CarSubscription createCarSubscriptionOption(int propertyId,
207             int[] areaId, float updateRateHz, boolean enableVur) {
208         CarSubscription options = new CarSubscription();
209         options.propertyId = propertyId;
210         options.areaIds = areaId;
211         options.updateRateHz = updateRateHz;
212         options.enableVariableUpdateRate = enableVur;
213         return options;
214     }
215 
addCarPropertyConfig(CarPropertyConfig config)216     private void addCarPropertyConfig(CarPropertyConfig config) {
217         mCarPropertyConfigsById.put(config.getPropertyId(), config);
218     }
219 
setPropIdWithoutPermission(int propId)220     private void setPropIdWithoutPermission(int propId) {
221         mCarPropertyConfigsById.remove(propId);
222         mMissingPermissionPropIds.add(propId);
223     }
224 
setPropIdWithoutConfig(int propId)225     private void setPropIdWithoutConfig(int propId) {
226         mCarPropertyConfigsById.remove(propId);
227         mUnsupportedPropIds.add(propId);
228     }
229 
230     @Before
setUp()231     public void setUp() throws RemoteException {
232         mMainHandler = new Handler(Looper.getMainLooper());
233         when(mCar.getContext()).thenReturn(mContext);
234         when(mCar.getEventHandler()).thenReturn(mMainHandler);
235         when(mCar.handleRemoteExceptionFromCarService(any(RemoteException.class), any()))
236                 .thenAnswer((inv) -> {
237                     return inv.getArgument(1);
238                 });
239 
240         mApplicationInfo.targetSdkVersion = Build.VERSION_CODES.R;
241         when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo);
242 
243         mContinuousCarPropertyConfig = CarPropertyConfig.newBuilder(Integer.class,
244                 VENDOR_CONTINUOUS_PROPERTY, VEHICLE_AREA_TYPE_GLOBAL)
245                 .setChangeMode(CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS)
246                 .setMinSampleRate(MIN_UPDATE_RATE_HZ)
247                 .setMaxSampleRate(MAX_UPDATE_RATE_HZ)
248                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(0)
249                         .setSupportVariableUpdateRate(true).build())
250                 .build();
251         mOnChangeCarPropertyConfig = CarPropertyConfig.newBuilder(Integer.class,
252                 VENDOR_ON_CHANGE_PROPERTY, VEHICLE_AREA_TYPE_GLOBAL)
253                 .setChangeMode(CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE)
254                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(0).build())
255                 .build();
256         mStaticCarPropertyConfig = CarPropertyConfig.newBuilder(Integer.class,
257                 VENDOR_STATIC_PROPERTY, VEHICLE_AREA_TYPE_GLOBAL)
258                 .setChangeMode(CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_STATIC)
259                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(0).build())
260                 .build();
261 
262         when(mICarProperty.getPropertyConfigList(any())).thenAnswer((invocation) -> {
263             Object[] args = invocation.getArguments();
264             int[] propIds = (int[]) args[0];
265             GetPropertyConfigListResult result = new GetPropertyConfigListResult();
266             IntArray unsupportedPropIds = new IntArray();
267             IntArray missingPermissionPropIds = new IntArray();
268             List<CarPropertyConfig> configs = new ArrayList<>();
269             for (int propId : propIds) {
270                 if (mUnsupportedPropIds.contains(propId)) {
271                     unsupportedPropIds.add(propId);
272                     continue;
273                 }
274                 if (mMissingPermissionPropIds.contains(propId)) {
275                     missingPermissionPropIds.add(propId);
276                     continue;
277                 }
278                 var config = mCarPropertyConfigsById.get(propId);
279                 if (config == null) {
280                     unsupportedPropIds.add(propId);
281                     continue;
282                 }
283                 configs.add(config);
284             }
285 
286             result.carPropertyConfigList = new CarPropertyConfigList(configs);
287             result.unsupportedPropIds = unsupportedPropIds.toArray();
288             result.missingPermissionPropIds = missingPermissionPropIds.toArray();
289             return result;
290         });
291 
292         addCarPropertyConfig(mContinuousCarPropertyConfig);
293         addCarPropertyConfig(mOnChangeCarPropertyConfig);
294         addCarPropertyConfig(mStaticCarPropertyConfig);
295         when(mICarProperty.getSupportedNoReadPermPropIds(any())).thenReturn(new int[0]);
296         mCarPropertyManager = new CarPropertyManager(mCar, mICarProperty);
297         // Enable the features.
298         when(mFeatureFlags.variableUpdateRate()).thenReturn(true);
299         mCarPropertyManager.setFeatureFlags(mFeatureFlags);
300     }
301 
302     @Test
testGetProperty_returnsValue()303     public void testGetProperty_returnsValue() throws RemoteException {
304         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0, 17.0f);
305 
306         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
307 
308         assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0)).isEqualTo(value);
309     }
310 
311     @Test
testGetProperty_unsupportedProperty_exceptionAtU()312     public void testGetProperty_unsupportedProperty_exceptionAtU() throws Exception {
313         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
314 
315         assertThrows(IllegalArgumentException.class,
316                 () -> mCarPropertyManager.getProperty(INVALID, 0));
317     }
318 
319     @Test
testGetProperty_unsupportedProperty_nullBeforeU()320     public void testGetProperty_unsupportedProperty_nullBeforeU() throws Exception {
321         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
322 
323         assertThat(mCarPropertyManager.getProperty(INVALID, 0)).isNull();
324     }
325 
326     @Test
testGetProperty_unsupportedPropertyInSvc_exceptionAtU()327     public void testGetProperty_unsupportedPropertyInSvc_exceptionAtU() throws Exception {
328         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
329         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
330                         new IllegalArgumentException());
331 
332         assertThrows(IllegalArgumentException.class,
333                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
334     }
335 
336     @Test
testGetProperty_unsupportedPropertyInSvc_nullBeforeU()337     public void testGetProperty_unsupportedPropertyInSvc_nullBeforeU() throws Exception {
338         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
339         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
340                         new IllegalArgumentException());
341 
342         assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0)).isNull();
343     }
344 
345     @Test
testGetProperty_syncOpTryAgain()346     public void testGetProperty_syncOpTryAgain() throws RemoteException {
347         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0, 17.0f);
348 
349         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
350                 new ServiceSpecificException(SYNC_OP_LIMIT_TRY_AGAIN)).thenReturn(value);
351 
352         assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0)).isEqualTo(value);
353         verify(mICarProperty, times(2)).getProperty(HVAC_TEMPERATURE_SET, 0);
354     }
355 
356     @Test
testGetProperty_syncOpTryAgain_exceedRetryCountLimit()357     public void testGetProperty_syncOpTryAgain_exceedRetryCountLimit() throws RemoteException {
358         // Car service will throw CarInternalException with version >= R.
359         setAppTargetSdk(Build.VERSION_CODES.R);
360 
361         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
362                 new ServiceSpecificException(SYNC_OP_LIMIT_TRY_AGAIN));
363 
364         assertThrows(CarInternalErrorException.class, () ->
365                 mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
366         verify(mICarProperty, times(10)).getProperty(HVAC_TEMPERATURE_SET, 0);
367     }
368 
setAppTargetSdk(int appTargetSdk)369     private void setAppTargetSdk(int appTargetSdk) {
370         mApplicationInfo.targetSdkVersion = appTargetSdk;
371         mCarPropertyManager = new CarPropertyManager(mCar, mICarProperty);
372     }
373 
374     @Test
testGetProperty_notAvailableBeforeR()375     public void testGetProperty_notAvailableBeforeR() throws Exception {
376         setAppTargetSdk(Build.VERSION_CODES.Q);
377         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
378                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE));
379 
380         assertThrows(IllegalStateException.class,
381                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
382     }
383 
384     @Test
testGetProperty_notAvailableEqualAfterR()385     public void testGetProperty_notAvailableEqualAfterR() throws Exception {
386         setAppTargetSdk(Build.VERSION_CODES.R);
387         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
388                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE));
389 
390         assertThrows(PropertyNotAvailableException.class,
391                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
392     }
393 
394     @Test
testGetProperty_tryAgainBeforeR()395     public void testGetProperty_tryAgainBeforeR() throws Exception {
396         setAppTargetSdk(Build.VERSION_CODES.Q);
397         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
398                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
399 
400         assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0)).isNull();
401     }
402 
403     @Test
testGetProperty_tryAgainEqualAfterR()404     public void testGetProperty_tryAgainEqualAfterR() throws Exception {
405         setAppTargetSdk(Build.VERSION_CODES.R);
406         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
407                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
408 
409         assertThrows(PropertyNotAvailableAndRetryException.class,
410                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
411     }
412 
413     @Test
testGetProperty_accessDeniedBeforeR()414     public void testGetProperty_accessDeniedBeforeR() throws Exception {
415         setAppTargetSdk(Build.VERSION_CODES.Q);
416         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
417                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_ACCESS_DENIED));
418 
419         assertThrows(IllegalStateException.class,
420                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
421     }
422 
423     @Test
testGetProperty_accessDeniedEqualAfterR()424     public void testGetProperty_accessDeniedEqualAfterR() throws Exception {
425         setAppTargetSdk(Build.VERSION_CODES.R);
426         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
427                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_ACCESS_DENIED));
428 
429         assertThrows(PropertyAccessDeniedSecurityException.class,
430                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
431     }
432 
433     @Test
testGetProperty_internalErrorBeforeR()434     public void testGetProperty_internalErrorBeforeR() throws Exception {
435         setAppTargetSdk(Build.VERSION_CODES.Q);
436         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
437                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR));
438 
439         assertThrows(IllegalStateException.class,
440                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
441     }
442 
443     @Test
testGetProperty_internalErrorEqualAfterR()444     public void testGetProperty_internalErrorEqualAfterR() throws Exception {
445         setAppTargetSdk(Build.VERSION_CODES.R);
446         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
447                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR));
448 
449         assertThrows(CarInternalErrorException.class,
450                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
451     }
452 
453     @Test
testGetProperty_internalErrorEqualAfterU_withVendorErrorCode()454     public void testGetProperty_internalErrorEqualAfterU_withVendorErrorCode() throws Exception {
455         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
456         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
457                 new ServiceSpecificException(combineErrors(
458                         VehicleHalStatusCode.STATUS_INTERNAL_ERROR, VENDOR_ERROR_CODE)));
459 
460         CarInternalErrorException exception = assertThrows(CarInternalErrorException.class,
461                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
462 
463         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
464     }
465 
466     @Test
testGetProperty_unknownErrorBeforeR()467     public void testGetProperty_unknownErrorBeforeR() throws Exception {
468         setAppTargetSdk(Build.VERSION_CODES.Q);
469         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
470                 new ServiceSpecificException(UNKNOWN_ERROR));
471 
472         assertThrows(IllegalStateException.class,
473                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
474     }
475 
476     @Test
testGetProperty_unknownErrorEqualAfterR()477     public void testGetProperty_unknownErrorEqualAfterR() throws Exception {
478         setAppTargetSdk(Build.VERSION_CODES.R);
479         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
480                 new ServiceSpecificException(UNKNOWN_ERROR));
481 
482         assertThrows(CarInternalErrorException.class,
483                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
484     }
485 
486     @Test
testGetProperty_returnsValueWithUnavailableStatusBeforeU()487     public void testGetProperty_returnsValueWithUnavailableStatusBeforeU() throws RemoteException {
488         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
489         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
490                 CarPropertyValue.STATUS_UNAVAILABLE, TEST_TIMESTAMP, 17.0f);
491 
492         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
493 
494         assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, /*areaId=*/ 0)).isEqualTo(
495                 new CarPropertyValue<>(HVAC_TEMPERATURE_SET, /*areaId=*/0,
496                         CarPropertyValue.STATUS_UNAVAILABLE, TEST_TIMESTAMP, 17.0f));
497     }
498 
499     @Test
testGetProperty_valueWithUnavailableStatusThrowsAfterU()500     public void testGetProperty_valueWithUnavailableStatusThrowsAfterU() throws RemoteException {
501         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
502         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
503                 CarPropertyValue.STATUS_UNAVAILABLE, TEST_TIMESTAMP, 17.0f);
504 
505         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
506 
507         assertThrows(PropertyNotAvailableException.class,
508                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
509     }
510 
511     @Test
testGetProperty_returnsValueWithErrorStatusBeforeU()512     public void testGetProperty_returnsValueWithErrorStatusBeforeU() throws RemoteException {
513         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
514         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
515                 CarPropertyValue.STATUS_ERROR, TEST_TIMESTAMP, 17.0f);
516 
517         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
518 
519         assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, /*areaId=*/ 0)).isEqualTo(
520                 new CarPropertyValue<>(HVAC_TEMPERATURE_SET, /*areaId=*/0,
521                         CarPropertyValue.STATUS_ERROR, TEST_TIMESTAMP, 17.0f));
522     }
523 
524     @Test
testGetProperty_valueWithErrorStatusThrowsAfterU()525     public void testGetProperty_valueWithErrorStatusThrowsAfterU() throws RemoteException {
526         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
527         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
528                 CarPropertyValue.STATUS_ERROR, TEST_TIMESTAMP, 17.0f);
529 
530         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
531 
532         assertThrows(CarInternalErrorException.class,
533                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
534     }
535 
536     @Test
testGetProperty_returnsValueWithUnknownStatusBeforeU()537     public void testGetProperty_returnsValueWithUnknownStatusBeforeU() throws RemoteException {
538         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
539         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
540                 /*unknown status=*/999, TEST_TIMESTAMP, 17.0f);
541 
542         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
543 
544         assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, /*areaId=*/ 0)).isEqualTo(
545                 new CarPropertyValue<>(HVAC_TEMPERATURE_SET, /*areaId=*/0, /*status=*/999,
546                         TEST_TIMESTAMP, 17.0f));
547     }
548 
549     @Test
testGetProperty_valueWithUnknownStatusThrowsAfterU()550     public void testGetProperty_valueWithUnknownStatusThrowsAfterU() throws RemoteException {
551         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
552         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
553                 /*unknown status=*/999, TEST_TIMESTAMP, 17.0f);
554 
555         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
556 
557         assertThrows(CarInternalErrorException.class,
558                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
559     }
560 
561     @Test
testGetPropertyWithClass()562     public void testGetPropertyWithClass() throws Exception {
563         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0, 17.0f);
564 
565         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
566 
567         assertThat(mCarPropertyManager.getProperty(Float.class, HVAC_TEMPERATURE_SET, 0).getValue())
568                 .isEqualTo(17.0f);
569     }
570 
571     @Test
testGetPropertyWithClass_mismatchClass()572     public void testGetPropertyWithClass_mismatchClass() throws Exception {
573         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0, 17.0f);
574 
575         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
576 
577         assertThrows(IllegalArgumentException.class,
578                 () -> mCarPropertyManager.getProperty(Integer.class, HVAC_TEMPERATURE_SET, 0));
579     }
580 
581     @Test
testGetPropertyWithClass_tryAgainBeforeR()582     public void testGetPropertyWithClass_tryAgainBeforeR() throws Exception {
583         setAppTargetSdk(Build.VERSION_CODES.Q);
584         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
585                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
586 
587         assertThat(mCarPropertyManager.getProperty(Float.class, HVAC_TEMPERATURE_SET, 0))
588                 .isNull();
589     }
590 
591     @Test
testGetPropertyWithClass_tryAgainEqualAfterR()592     public void testGetPropertyWithClass_tryAgainEqualAfterR() throws Exception {
593         setAppTargetSdk(Build.VERSION_CODES.R);
594         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
595                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
596 
597         assertThrows(PropertyNotAvailableAndRetryException.class,
598                 () -> mCarPropertyManager.getProperty(Float.class, HVAC_TEMPERATURE_SET, 0));
599     }
600 
601 
602     @Test
testGetBooleanProperty()603     public void testGetBooleanProperty() throws Exception {
604         setAppTargetSdk(Build.VERSION_CODES.R);
605         CarPropertyValue<Boolean> value = new CarPropertyValue<>(BOOLEAN_PROP, 0, true);
606         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenReturn(value);
607 
608         assertThat(mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0)).isTrue();
609     }
610 
611     @Test
testGetBooleanProperty_notAvailableBeforeR()612     public void testGetBooleanProperty_notAvailableBeforeR() throws Exception {
613         setAppTargetSdk(Build.VERSION_CODES.Q);
614         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
615                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE));
616 
617         assertThrows(IllegalStateException.class,
618                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
619     }
620 
621     @Test
testGetBooleanProperty_notAvailableEqualAfterR()622     public void testGetBooleanProperty_notAvailableEqualAfterR() throws Exception {
623         setAppTargetSdk(Build.VERSION_CODES.R);
624         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
625                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE));
626 
627         assertThrows(PropertyNotAvailableException.class,
628                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
629     }
630 
631     @Test
testGetProperty_notAvailableEqualAfterU_withVendorErrorCode()632     public void testGetProperty_notAvailableEqualAfterU_withVendorErrorCode() throws Exception {
633         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
634         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
635                 new ServiceSpecificException(combineErrors(
636                         VehicleHalStatusCode.STATUS_NOT_AVAILABLE, VENDOR_ERROR_CODE)));
637 
638         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
639                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
640 
641         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
642     }
643 
644     @Test
testGetProperty_notAvailableDisabledBeforeU()645     public void testGetProperty_notAvailableDisabledBeforeU() throws Exception {
646         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
647         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
648                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED));
649 
650         assertThrows(PropertyNotAvailableException.class,
651                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
652     }
653 
654     @Test
testGetProperty_notAvailableDisabledAfterU()655     public void testGetProperty_notAvailableDisabledAfterU() throws Exception {
656         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
657         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
658                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED));
659 
660         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
661                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
662         assertThat(exception.getDetailedErrorCode())
663                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_DISABLED);
664     }
665 
666     @Test
testGetProperty_notAvailableDisabledAfterU_withVendorErrorCode()667     public void testGetProperty_notAvailableDisabledAfterU_withVendorErrorCode() throws Exception {
668         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
669         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
670                 new ServiceSpecificException(combineErrors(
671                         VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED, VENDOR_ERROR_CODE)));
672 
673         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
674                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
675 
676         assertThat(exception.getDetailedErrorCode())
677                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_DISABLED);
678         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
679     }
680 
681     @Test
testGetProperty_notAvailableSafetyBeforeU()682     public void testGetProperty_notAvailableSafetyBeforeU() throws Exception {
683         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
684         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
685                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY));
686 
687         assertThrows(PropertyNotAvailableException.class,
688                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
689     }
690 
691     @Test
testGetProperty_notAvailableSafetyAfterU()692     public void testGetProperty_notAvailableSafetyAfterU() throws Exception {
693         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
694         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
695                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY));
696 
697         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
698                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
699         assertThat(exception.getDetailedErrorCode())
700                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SAFETY);
701     }
702 
703     @Test
testGetProperty_notAvailableSafetyAfterU_withVendorErrorCode()704     public void testGetProperty_notAvailableSafetyAfterU_withVendorErrorCode() throws Exception {
705         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
706         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
707                 new ServiceSpecificException(combineErrors(
708                         VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY, VENDOR_ERROR_CODE)));
709 
710         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
711                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
712 
713         assertThat(exception.getDetailedErrorCode())
714                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SAFETY);
715         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
716     }
717 
718     @Test
testGetProperty_notAvailableSpeedHighBeforeU()719     public void testGetProperty_notAvailableSpeedHighBeforeU() throws Exception {
720         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
721         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
722                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH));
723 
724         assertThrows(PropertyNotAvailableException.class,
725                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
726     }
727 
728     @Test
testGetProperty_notAvailableSpeedHighAfterU()729     public void testGetProperty_notAvailableSpeedHighAfterU() throws Exception {
730         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
731         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
732                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH));
733 
734         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
735                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
736         assertThat(exception.getDetailedErrorCode())
737                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_HIGH);
738     }
739 
740     @Test
testGetProperty_notAvailableSpeedHighAfterU_withVendorErrorCode()741     public void testGetProperty_notAvailableSpeedHighAfterU_withVendorErrorCode() throws Exception {
742         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
743         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
744                 new ServiceSpecificException(combineErrors(
745                         VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH, VENDOR_ERROR_CODE)));
746 
747         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
748                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
749 
750         assertThat(exception.getDetailedErrorCode())
751                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_HIGH);
752         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
753     }
754 
755     @Test
testGetProperty_notAvailableSpeedLowBeforeU()756     public void testGetProperty_notAvailableSpeedLowBeforeU() throws Exception {
757         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
758         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
759                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW));
760 
761         assertThrows(PropertyNotAvailableException.class,
762                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
763     }
764 
765     @Test
testGetProperty_notAvailableSpeedLowAfterU()766     public void testGetProperty_notAvailableSpeedLowAfterU() throws Exception {
767         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
768         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
769                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW));
770 
771         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
772                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
773         assertThat(exception.getDetailedErrorCode())
774                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_LOW);
775     }
776 
777     @Test
testGetProperty_notAvailableSpeedLowAfterU_withVendorErrorCode()778     public void testGetProperty_notAvailableSpeedLowAfterU_withVendorErrorCode() throws Exception {
779         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
780         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
781                 new ServiceSpecificException(combineErrors(
782                         VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW, VENDOR_ERROR_CODE)));
783 
784         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
785                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
786 
787         assertThat(exception.getDetailedErrorCode())
788                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_LOW);
789         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
790     }
791 
792     @Test
testGetBooleanProperty_tryAgainBeforeR()793     public void testGetBooleanProperty_tryAgainBeforeR() throws Exception {
794         setAppTargetSdk(Build.VERSION_CODES.Q);
795         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
796                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
797 
798         assertThat(mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0)).isFalse();
799     }
800 
801     @Test
testGetBooleanProperty_tryAgainEqualAfterR()802     public void testGetBooleanProperty_tryAgainEqualAfterR() throws Exception {
803         setAppTargetSdk(Build.VERSION_CODES.R);
804         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
805                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
806 
807         assertThrows(PropertyNotAvailableAndRetryException.class,
808                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
809     }
810 
811     @Test
testGetBooleanProperty_accessDeniedBeforeR()812     public void testGetBooleanProperty_accessDeniedBeforeR() throws Exception {
813         setAppTargetSdk(Build.VERSION_CODES.Q);
814         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
815                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_ACCESS_DENIED));
816 
817         assertThrows(IllegalStateException.class,
818                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
819     }
820 
821     @Test
testGetBooleanProperty_accessDeniedEqualAfterR()822     public void testGetBooleanProperty_accessDeniedEqualAfterR() throws Exception {
823         setAppTargetSdk(Build.VERSION_CODES.R);
824         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
825                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_ACCESS_DENIED));
826 
827         assertThrows(PropertyAccessDeniedSecurityException.class,
828                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
829     }
830 
831     @Test
testGetBooleanProperty_internalErrorBeforeR()832     public void testGetBooleanProperty_internalErrorBeforeR() throws Exception {
833         setAppTargetSdk(Build.VERSION_CODES.Q);
834         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
835                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR));
836 
837         assertThrows(IllegalStateException.class,
838                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
839     }
840 
841     @Test
testGetBooleanProperty_internalErrorEqualAfterR()842     public void testGetBooleanProperty_internalErrorEqualAfterR() throws Exception {
843         setAppTargetSdk(Build.VERSION_CODES.R);
844         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
845                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR));
846 
847         assertThrows(CarInternalErrorException.class,
848                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
849     }
850 
851     @Test
testGetBooleanProperty_unknownErrorBeforeR()852     public void testGetBooleanProperty_unknownErrorBeforeR() throws Exception {
853         setAppTargetSdk(Build.VERSION_CODES.Q);
854         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
855                 new ServiceSpecificException(UNKNOWN_ERROR));
856 
857         assertThrows(IllegalStateException.class,
858                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
859     }
860 
861     @Test
testGetBooleanProperty_unknownErrorEqualAfterR()862     public void testGetBooleanProperty_unknownErrorEqualAfterR() throws Exception {
863         setAppTargetSdk(Build.VERSION_CODES.R);
864         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
865                 new ServiceSpecificException(UNKNOWN_ERROR));
866 
867         assertThrows(CarInternalErrorException.class,
868                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
869     }
870 
871     @Test
testGetIntProperty()872     public void testGetIntProperty() throws Exception {
873         setAppTargetSdk(Build.VERSION_CODES.R);
874         CarPropertyValue<Integer> value = new CarPropertyValue<>(INT32_PROP, 0, 1);
875         when(mICarProperty.getProperty(INT32_PROP, 0)).thenReturn(value);
876 
877         assertThat(mCarPropertyManager.getIntProperty(INT32_PROP, 0)).isEqualTo(1);
878     }
879 
880     @Test
testGetIntProperty_tryAgainBeforeR()881     public void testGetIntProperty_tryAgainBeforeR() throws Exception {
882         setAppTargetSdk(Build.VERSION_CODES.Q);
883         when(mICarProperty.getProperty(INT32_PROP, 0)).thenThrow(
884                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
885 
886         assertThat(mCarPropertyManager.getIntProperty(INT32_PROP, 0)).isEqualTo(0);
887     }
888 
889     @Test
testGetIntProperty_tryAgainEqualAfterR()890     public void testGetIntProperty_tryAgainEqualAfterR() throws Exception {
891         setAppTargetSdk(Build.VERSION_CODES.R);
892         when(mICarProperty.getProperty(INT32_PROP, 0)).thenThrow(
893                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
894 
895         assertThrows(PropertyNotAvailableAndRetryException.class,
896                 () -> mCarPropertyManager.getIntProperty(INT32_PROP, 0));
897     }
898 
899     @Test
testGetIntProperty_wrongType()900     public void testGetIntProperty_wrongType() throws Exception {
901         CarPropertyValue<Integer> value = new CarPropertyValue<>(INT32_PROP, 0, 1);
902         when(mICarProperty.getProperty(INT32_PROP, 0)).thenReturn(value);
903 
904         assertThrows(IllegalArgumentException.class,
905                 () -> mCarPropertyManager.getFloatProperty(INT32_PROP, 0));
906     }
907 
908     @Test
testGetIntArrayProperty()909     public void testGetIntArrayProperty() throws Exception {
910         setAppTargetSdk(Build.VERSION_CODES.R);
911         CarPropertyValue<Float> value = new CarPropertyValue<>(FLOAT_PROP, 0, 1f);
912         when(mICarProperty.getProperty(FLOAT_PROP, 0)).thenReturn(value);
913 
914         assertThrows(IllegalArgumentException.class,
915                 () -> mCarPropertyManager.getIntArrayProperty(FLOAT_PROP, 0));
916     }
917 
918     @Test
testGetIntArrayProperty_tryAgainBeforeR()919     public void testGetIntArrayProperty_tryAgainBeforeR() throws Exception {
920         setAppTargetSdk(Build.VERSION_CODES.Q);
921         when(mICarProperty.getProperty(INT32_VEC_PROP, 0)).thenThrow(
922                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
923 
924         assertThat(mCarPropertyManager.getIntArrayProperty(INT32_VEC_PROP, 0)).isEqualTo(
925                 new int[0]);
926     }
927 
928     @Test
testGetIntArrayProperty_tryAgainEqualAfterR()929     public void testGetIntArrayProperty_tryAgainEqualAfterR() throws Exception {
930         setAppTargetSdk(Build.VERSION_CODES.R);
931         when(mICarProperty.getProperty(INT32_VEC_PROP, 0)).thenThrow(
932                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
933 
934         assertThrows(PropertyNotAvailableAndRetryException.class,
935                 () -> mCarPropertyManager.getIntArrayProperty(INT32_VEC_PROP, 0));
936     }
937 
938     @Test
testGetFloatProperty()939     public void testGetFloatProperty() throws Exception {
940         setAppTargetSdk(Build.VERSION_CODES.R);
941         CarPropertyValue<Float> value = new CarPropertyValue<>(FLOAT_PROP, 0, 1.f);
942         when(mICarProperty.getProperty(FLOAT_PROP, 0)).thenReturn(value);
943 
944         assertThat(mCarPropertyManager.getFloatProperty(FLOAT_PROP, 0)).isEqualTo(1.f);
945     }
946 
947     @Test
testGetFloatProperty_tryAgainBeforeR()948     public void testGetFloatProperty_tryAgainBeforeR() throws Exception {
949         setAppTargetSdk(Build.VERSION_CODES.Q);
950         when(mICarProperty.getProperty(FLOAT_PROP, 0)).thenThrow(
951                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
952 
953         assertThat(mCarPropertyManager.getFloatProperty(FLOAT_PROP, 0)).isEqualTo(0.f);
954     }
955 
956     @Test
testGetFloatProperty_tryAgainEqualAfterR()957     public void testGetFloatProperty_tryAgainEqualAfterR() throws Exception {
958         setAppTargetSdk(Build.VERSION_CODES.R);
959         when(mICarProperty.getProperty(FLOAT_PROP, 0)).thenThrow(
960                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
961 
962         assertThrows(PropertyNotAvailableAndRetryException.class,
963                 () -> mCarPropertyManager.getFloatProperty(FLOAT_PROP, 0));
964     }
965 
966     @Test
testGetFloatProperty_wrongType()967     public void testGetFloatProperty_wrongType() throws Exception {
968         CarPropertyValue<Integer> value = new CarPropertyValue<>(INT32_PROP, 0, 1);
969         when(mICarProperty.getProperty(INT32_PROP, 0)).thenReturn(value);
970 
971         assertThrows(IllegalArgumentException.class,
972                 () -> mCarPropertyManager.getFloatProperty(INT32_PROP, 0));
973     }
974 
createGetPropertyRequest()975     private CarPropertyManager.GetPropertyRequest createGetPropertyRequest() {
976         return mCarPropertyManager.generateGetPropertyRequest(HVAC_TEMPERATURE_SET, 0);
977     }
978 
979     @Test
testGetPropertiesAsync()980     public void testGetPropertiesAsync() throws RemoteException {
981         mCarPropertyManager.getPropertiesAsync(List.of(createGetPropertyRequest()), null, null,
982                 mGetPropertyCallback);
983 
984         ArgumentCaptor<AsyncPropertyServiceRequestList> argumentCaptor = ArgumentCaptor.forClass(
985                 AsyncPropertyServiceRequestList.class);
986         verify(mICarProperty).getPropertiesAsync(argumentCaptor.capture(), any(), anyLong());
987         assertThat(argumentCaptor.getValue().getList().get(0).getRequestId()).isEqualTo(0);
988         assertThat(argumentCaptor.getValue().getList().get(0).getPropertyId())
989                 .isEqualTo(HVAC_TEMPERATURE_SET);
990         assertThat(argumentCaptor.getValue().getList().get(0).getAreaId()).isEqualTo(0);
991     }
992 
993     @Test
testGetPropertiesAsyncWithTimeout()994     public void testGetPropertiesAsyncWithTimeout() throws RemoteException {
995         mCarPropertyManager.getPropertiesAsync(List.of(createGetPropertyRequest()),
996                 /* timeoutInMs= */ 1000, null, null, mGetPropertyCallback);
997 
998         ArgumentCaptor<AsyncPropertyServiceRequestList> argumentCaptor = ArgumentCaptor.forClass(
999                 AsyncPropertyServiceRequestList.class);
1000         verify(mICarProperty).getPropertiesAsync(argumentCaptor.capture(), any(), eq(1000L));
1001         assertThat(argumentCaptor.getValue().getList().get(0).getRequestId()).isEqualTo(0);
1002         assertThat(argumentCaptor.getValue().getList().get(0).getPropertyId())
1003                 .isEqualTo(HVAC_TEMPERATURE_SET);
1004         assertThat(argumentCaptor.getValue().getList().get(0).getAreaId()).isEqualTo(0);
1005     }
1006 
1007     @Test
testGetPropertiesAsync_illegalArgumentException()1008     public void testGetPropertiesAsync_illegalArgumentException() throws RemoteException {
1009         IllegalArgumentException exception = new IllegalArgumentException();
1010         doThrow(exception).when(mICarProperty).getPropertiesAsync(any(
1011                 AsyncPropertyServiceRequestList.class),
1012                 any(IAsyncPropertyResultCallback.class), anyLong());
1013 
1014         assertThrows(IllegalArgumentException.class,
1015                 () -> mCarPropertyManager.getPropertiesAsync(
1016                         List.of(createGetPropertyRequest()), null, null, mGetPropertyCallback));
1017     }
1018 
1019     @Test
testGetPropertiesAsync_SecurityException()1020     public void testGetPropertiesAsync_SecurityException() throws RemoteException {
1021         SecurityException exception = new SecurityException();
1022         doThrow(exception).when(mICarProperty).getPropertiesAsync(any(
1023                 AsyncPropertyServiceRequestList.class),
1024                 any(IAsyncPropertyResultCallback.class), anyLong());
1025 
1026         assertThrows(SecurityException.class,
1027                 () -> mCarPropertyManager.getPropertiesAsync(
1028                         List.of(createGetPropertyRequest()), null, null, mGetPropertyCallback));
1029     }
1030 
1031     @Test
tsetGetPropertiesAsync_unsupportedProperty()1032     public void tsetGetPropertiesAsync_unsupportedProperty() throws Exception {
1033         assertThrows(IllegalArgumentException.class,
1034                 () -> mCarPropertyManager.getPropertiesAsync(
1035                         List.of(mCarPropertyManager.generateGetPropertyRequest(INVALID, 0)), null,
1036                         null, mGetPropertyCallback));
1037     }
1038 
1039     @Test
testGetPropertiesAsync_remoteException()1040     public void testGetPropertiesAsync_remoteException() throws RemoteException {
1041         RemoteException remoteException = new RemoteException();
1042         doThrow(remoteException).when(mICarProperty).getPropertiesAsync(any(
1043                 AsyncPropertyServiceRequestList.class),
1044                 any(IAsyncPropertyResultCallback.class), anyLong());
1045 
1046         mCarPropertyManager.getPropertiesAsync(List.of(createGetPropertyRequest()), null, null,
1047                 mGetPropertyCallback);
1048 
1049         verify(mCar).handleRemoteExceptionFromCarService(any(RemoteException.class));
1050     }
1051 
1052     @Test
testGetPropertiesAsync_clearRequestIdAfterFailed()1053     public void testGetPropertiesAsync_clearRequestIdAfterFailed() throws RemoteException {
1054         CarPropertyManager.GetPropertyRequest getPropertyRequest = createGetPropertyRequest();
1055         IllegalArgumentException exception = new IllegalArgumentException();
1056         doThrow(exception).when(mICarProperty).getPropertiesAsync(any(
1057                 AsyncPropertyServiceRequestList.class),
1058                 any(IAsyncPropertyResultCallback.class), anyLong());
1059 
1060         assertThrows(IllegalArgumentException.class,
1061                 () -> mCarPropertyManager.getPropertiesAsync(List.of(getPropertyRequest), null,
1062                         null, mGetPropertyCallback));
1063 
1064         clearInvocations(mICarProperty);
1065         doNothing().when(mICarProperty).getPropertiesAsync(any(
1066                 AsyncPropertyServiceRequestList.class),
1067                 any(IAsyncPropertyResultCallback.class), anyLong());
1068         ArgumentCaptor<AsyncPropertyServiceRequestList> argumentCaptor = ArgumentCaptor.forClass(
1069                 AsyncPropertyServiceRequestList.class);
1070 
1071         mCarPropertyManager.getPropertiesAsync(List.of(getPropertyRequest), null, null,
1072                 mGetPropertyCallback);
1073 
1074         verify(mICarProperty).getPropertiesAsync(argumentCaptor.capture(), any(), anyLong());
1075         assertThat(argumentCaptor.getValue().getList().get(0).getRequestId()).isEqualTo(0);
1076         assertThat(argumentCaptor.getValue().getList().get(0).getPropertyId())
1077                 .isEqualTo(HVAC_TEMPERATURE_SET);
1078         assertThat(argumentCaptor.getValue().getList().get(0).getAreaId()).isEqualTo(0);
1079     }
1080 
1081     @Test
1082     @IgnoreUnderRavenwood(blockedBy = android.os.CancellationSignal.class)
testGetPropertiesAsync_cancellationSignalCancelRequests()1083     public void testGetPropertiesAsync_cancellationSignalCancelRequests() throws Exception {
1084         CarPropertyManager.GetPropertyRequest getPropertyRequest = createGetPropertyRequest();
1085         CancellationSignal cancellationSignal = new CancellationSignal();
1086         List<IAsyncPropertyResultCallback> callbackWrapper = new ArrayList<>();
1087         doAnswer((invocation) -> {
1088             Object[] args = invocation.getArguments();
1089             callbackWrapper.add((IAsyncPropertyResultCallback) args[1]);
1090             return null;
1091         }).when(mICarProperty).getPropertiesAsync(any(), any(), anyLong());
1092 
1093         mCarPropertyManager.getPropertiesAsync(List.of(getPropertyRequest), cancellationSignal,
1094                 /* callbackExecutor= */ null, mGetPropertyCallback);
1095 
1096         // Cancel the pending request.
1097         cancellationSignal.cancel();
1098 
1099         verify(mICarProperty).cancelRequests(new int[]{0});
1100 
1101         // Call the manager callback after the request is already cancelled.
1102         GetSetValueResult getValueResult = GetSetValueResult.newErrorResult(
1103                 0,
1104                 new CarPropertyErrorCodes(
1105                         CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR,
1106                         /* vendorErrorCode= */ 0,
1107                         /* systemErrorCode= */ 0));
1108         assertThat(callbackWrapper.size()).isEqualTo(1);
1109         callbackWrapper.get(0).onGetValueResults(
1110                 new GetSetValueResultList(List.of(getValueResult)));
1111 
1112         // No client callbacks should be called.
1113         verify(mGetPropertyCallback, never()).onFailure(any());
1114         verify(mGetPropertyCallback, never()).onSuccess(any());
1115     }
1116 
1117     @Test
testOnGetValueResult_onSuccess()1118     public void testOnGetValueResult_onSuccess() throws RemoteException {
1119         doAnswer((invocation) -> {
1120             Object[] args = invocation.getArguments();
1121             AsyncPropertyServiceRequestList asyncGetPropertyServiceRequestList =
1122                     (AsyncPropertyServiceRequestList) args[0];
1123             List getPropertyServiceList = asyncGetPropertyServiceRequestList.getList();
1124             AsyncPropertyServiceRequest getPropertyServiceRequest =
1125                     (AsyncPropertyServiceRequest) getPropertyServiceList.get(0);
1126             IAsyncPropertyResultCallback getAsyncPropertyResultCallback =
1127                     (IAsyncPropertyResultCallback) args[1];
1128 
1129             assertThat(getPropertyServiceRequest.getRequestId()).isEqualTo(0);
1130             assertThat(getPropertyServiceRequest.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1131 
1132             CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0, 17.0f);
1133             GetSetValueResult getValueResult = GetSetValueResult.newGetValueResult(0, value);
1134 
1135             getAsyncPropertyResultCallback.onGetValueResults(
1136                     new GetSetValueResultList(List.of(getValueResult)));
1137             return null;
1138         }).when(mICarProperty).getPropertiesAsync(any(), any(), anyLong());
1139 
1140         mCarPropertyManager.getPropertiesAsync(List.of(createGetPropertyRequest()), null, null,
1141                 mGetPropertyCallback);
1142 
1143         verify(mGetPropertyCallback, timeout(1000)).onSuccess(
1144                 mGetPropertyResultCaptor.capture());
1145         GetPropertyResult<Float> gotResult = (GetPropertyResult<Float>)
1146                 mGetPropertyResultCaptor.getValue();
1147         assertThat(gotResult.getRequestId()).isEqualTo(0);
1148         assertThat(gotResult.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1149         assertThat(gotResult.getAreaId()).isEqualTo(0);
1150         assertThat(gotResult.getValue()).isEqualTo(17.0f);
1151     }
1152 
1153     @Test
testOnGetValueResult_onSuccessMultipleRequests()1154     public void testOnGetValueResult_onSuccessMultipleRequests() throws RemoteException {
1155         doAnswer((invocation) -> {
1156             Object[] args = invocation.getArguments();
1157             AsyncPropertyServiceRequestList getPropertyServiceRequest =
1158                     (AsyncPropertyServiceRequestList) args[0];
1159             List<AsyncPropertyServiceRequest> getPropertyServiceRequests =
1160                     getPropertyServiceRequest.getList();
1161             IAsyncPropertyResultCallback getAsyncPropertyResultCallback =
1162                     (IAsyncPropertyResultCallback) args[1];
1163 
1164             assertThat(getPropertyServiceRequests.size()).isEqualTo(2);
1165             assertThat(getPropertyServiceRequests.get(0).getRequestId()).isEqualTo(0);
1166             assertThat(getPropertyServiceRequests.get(0).getPropertyId()).isEqualTo(
1167                     HVAC_TEMPERATURE_SET);
1168             assertThat(getPropertyServiceRequests.get(1).getRequestId()).isEqualTo(1);
1169             assertThat(getPropertyServiceRequests.get(1).getPropertyId()).isEqualTo(
1170                     HVAC_TEMPERATURE_SET);
1171 
1172             CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0, 17.0f);
1173             List<GetSetValueResult> getValueResults = List.of(
1174                     GetSetValueResult.newGetValueResult(0, value),
1175                     GetSetValueResult.newGetValueResult(1, value));
1176 
1177             getAsyncPropertyResultCallback.onGetValueResults(
1178                     new GetSetValueResultList(getValueResults));
1179             return null;
1180         }).when(mICarProperty).getPropertiesAsync(any(), any(), anyLong());
1181 
1182         List<CarPropertyManager.GetPropertyRequest> getPropertyRequests = new ArrayList<>();
1183         getPropertyRequests.add(createGetPropertyRequest());
1184         getPropertyRequests.add(createGetPropertyRequest());
1185 
1186         mCarPropertyManager.getPropertiesAsync(getPropertyRequests, null, null,
1187                 mGetPropertyCallback);
1188 
1189         verify(mGetPropertyCallback, timeout(1000).times(2)).onSuccess(
1190                     mGetPropertyResultCaptor.capture());
1191         List<GetPropertyResult<?>> gotPropertyResults = mGetPropertyResultCaptor.getAllValues();
1192         assertThat(gotPropertyResults.get(0).getRequestId()).isEqualTo(0);
1193         assertThat(gotPropertyResults.get(0).getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1194         assertThat(gotPropertyResults.get(0).getAreaId()).isEqualTo(0);
1195         assertThat(gotPropertyResults.get(0).getValue()).isEqualTo(17.0f);
1196         assertThat(gotPropertyResults.get(1).getRequestId()).isEqualTo(1);
1197         assertThat(gotPropertyResults.get(1).getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1198         assertThat(gotPropertyResults.get(1).getAreaId()).isEqualTo(0);
1199         assertThat(gotPropertyResults.get(1).getValue()).isEqualTo(17.0f);
1200     }
1201 
1202     @Test
testOnGetValueResult_onFailure()1203     public void testOnGetValueResult_onFailure() throws RemoteException {
1204         doAnswer((invocation) -> {
1205             Object[] args = invocation.getArguments();
1206             AsyncPropertyServiceRequestList asyncPropertyServiceREquestList =
1207                     (AsyncPropertyServiceRequestList) args[0];
1208             List getPropertyServiceList = asyncPropertyServiceREquestList.getList();
1209             AsyncPropertyServiceRequest getPropertyServiceRequest =
1210                     (AsyncPropertyServiceRequest) getPropertyServiceList.get(0);
1211             IAsyncPropertyResultCallback getAsyncPropertyResultCallback =
1212                     (IAsyncPropertyResultCallback) args[1];
1213 
1214             assertThat(getPropertyServiceRequest.getRequestId()).isEqualTo(0);
1215             assertThat(getPropertyServiceRequest.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1216 
1217             GetSetValueResult getValueResult = GetSetValueResult.newErrorResult(
1218                     0,
1219                     new CarPropertyErrorCodes(
1220                             CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR, VENDOR_ERROR_CODE, 0));
1221 
1222             getAsyncPropertyResultCallback.onGetValueResults(
1223                     new GetSetValueResultList(List.of(getValueResult)));
1224             return null;
1225         }).when(mICarProperty).getPropertiesAsync(any(), any(), anyLong());
1226 
1227 
1228         mCarPropertyManager.getPropertiesAsync(List.of(createGetPropertyRequest()), null, null,
1229                 mGetPropertyCallback);
1230 
1231         verify(mGetPropertyCallback, timeout(1000)).onFailure(mPropertyAsyncErrorCaptor.capture());
1232         PropertyAsyncError error = mPropertyAsyncErrorCaptor.getValue();
1233         assertThat(error.getRequestId()).isEqualTo(0);
1234         assertThat(error.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1235         assertThat(error.getAreaId()).isEqualTo(0);
1236         assertThat(error.getErrorCode()).isEqualTo(
1237                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
1238         assertThat(error.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1239     }
1240 
1241     @Test
testSetProperty_setsValue()1242     public void testSetProperty_setsValue() throws RemoteException {
1243         mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f);
1244 
1245         ArgumentCaptor<CarPropertyValue> value = ArgumentCaptor.forClass(CarPropertyValue.class);
1246 
1247         verify(mICarProperty).setProperty(value.capture(), any());
1248         assertThat(value.getValue().getValue()).isEqualTo(17.0f);
1249     }
1250 
1251     @Test
testSetProperty_syncOpTryAgain()1252     public void testSetProperty_syncOpTryAgain() throws RemoteException {
1253         doThrow(new ServiceSpecificException(SYNC_OP_LIMIT_TRY_AGAIN)).doNothing()
1254                 .when(mICarProperty).setProperty(any(), any());
1255 
1256         mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f);
1257 
1258         verify(mICarProperty, times(2)).setProperty(any(), any());
1259     }
1260 
1261     @Test
testSetProperty_unsupportedProperty()1262     public void testSetProperty_unsupportedProperty() throws RemoteException {
1263         assertThrows(IllegalArgumentException.class,
1264                 () -> mCarPropertyManager.setProperty(Float.class, INVALID, 0, 17.0f));
1265     }
1266 
createSetPropertyRequest()1267     private SetPropertyRequest<Float> createSetPropertyRequest() {
1268         return mCarPropertyManager.generateSetPropertyRequest(HVAC_TEMPERATURE_SET, 0,
1269                 Float.valueOf(17.0f));
1270     }
1271 
1272     @Test
testSetPropertiesAsync()1273     public void testSetPropertiesAsync() throws RemoteException {
1274         SetPropertyRequest<Float> setPropertyRequest = createSetPropertyRequest();
1275         setPropertyRequest.setUpdateRateHz(10.1f);
1276         setPropertyRequest.setWaitForPropertyUpdate(false);
1277         mCarPropertyManager.setPropertiesAsync(List.of(setPropertyRequest), null, null,
1278                 mSetPropertyCallback);
1279 
1280         verify(mICarProperty).setPropertiesAsync(mAsyncPropertyServiceRequestCaptor.capture(),
1281                 any(), anyLong());
1282 
1283         AsyncPropertyServiceRequest request = mAsyncPropertyServiceRequestCaptor.getValue()
1284                 .getList().get(0);
1285 
1286         assertThat(request.getRequestId()).isEqualTo(0);
1287         assertThat(request.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1288         assertThat(request.getAreaId()).isEqualTo(0);
1289         assertThat(request.isWaitForPropertyUpdate()).isFalse();
1290         assertThat(request.getUpdateRateHz()).isEqualTo(10.1f);
1291         CarPropertyValue requestValue = request.getCarPropertyValue();
1292         assertThat(requestValue).isNotNull();
1293         assertThat(requestValue.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1294         assertThat(requestValue.getAreaId()).isEqualTo(0);
1295         assertThat(requestValue.getValue()).isEqualTo(17.0f);
1296     }
1297 
1298     @Test
testSetPropertiesAsync_nullRequests()1299     public void testSetPropertiesAsync_nullRequests() throws RemoteException {
1300         assertThrows(NullPointerException.class,
1301                 () -> mCarPropertyManager.setPropertiesAsync(
1302                         null, null, null, mSetPropertyCallback));
1303     }
1304 
1305     @Test
testSetPropertiesAsync_nullCallback()1306     public void testSetPropertiesAsync_nullCallback() throws RemoteException {
1307         assertThrows(NullPointerException.class,
1308                 () -> mCarPropertyManager.setPropertiesAsync(
1309                         List.of(createSetPropertyRequest()), null, null, null));
1310     }
1311 
1312     @Test
testSetPropertiesAsyncWithTimeout()1313     public void testSetPropertiesAsyncWithTimeout() throws RemoteException {
1314         mCarPropertyManager.setPropertiesAsync(List.of(createSetPropertyRequest()),
1315                 /* timeoutInMs= */ 1000, /* cancellationSignal= */ null,
1316                 /* callbackExecutor= */ null, mSetPropertyCallback);
1317 
1318         verify(mICarProperty).setPropertiesAsync(mAsyncPropertyServiceRequestCaptor.capture(),
1319                 any(), eq(1000L));
1320         AsyncPropertyServiceRequest request = mAsyncPropertyServiceRequestCaptor.getValue()
1321                 .getList().get(0);
1322         assertThat(request.getRequestId()).isEqualTo(0);
1323         assertThat(request.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1324         assertThat(request.getAreaId()).isEqualTo(0);
1325         CarPropertyValue requestValue = request.getCarPropertyValue();
1326         assertThat(requestValue).isNotNull();
1327         assertThat(requestValue.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1328         assertThat(requestValue.getAreaId()).isEqualTo(0);
1329         assertThat(requestValue.getValue()).isEqualTo(17.0f);
1330     }
1331 
1332     @Test
testSetPropertiesAsync_illegalArgumentException()1333     public void testSetPropertiesAsync_illegalArgumentException() throws RemoteException {
1334         doThrow(new IllegalArgumentException()).when(mICarProperty).setPropertiesAsync(
1335                 any(AsyncPropertyServiceRequestList.class), any(IAsyncPropertyResultCallback.class),
1336                 anyLong());
1337 
1338         assertThrows(IllegalArgumentException.class,
1339                 () -> mCarPropertyManager.setPropertiesAsync(
1340                         List.of(createSetPropertyRequest()), null, null, mSetPropertyCallback));
1341     }
1342 
1343     @Test
testSetPropertiesAsync_SecurityException()1344     public void testSetPropertiesAsync_SecurityException() throws RemoteException {
1345         doThrow(new SecurityException()).when(mICarProperty).setPropertiesAsync(
1346                 any(AsyncPropertyServiceRequestList.class),
1347                 any(IAsyncPropertyResultCallback.class), anyLong());
1348 
1349         assertThrows(SecurityException.class,
1350                 () -> mCarPropertyManager.setPropertiesAsync(
1351                         List.of(createSetPropertyRequest()), null, null, mSetPropertyCallback));
1352     }
1353 
1354     @Test
testSetPropertiesAsync_unsupportedProperty()1355     public void testSetPropertiesAsync_unsupportedProperty() throws Exception {
1356         assertThrows(IllegalArgumentException.class,
1357                 () -> mCarPropertyManager.setPropertiesAsync(
1358                         List.of(mCarPropertyManager.generateSetPropertyRequest(
1359                                 INVALID, 0, Integer.valueOf(0))),
1360                         null, null, mSetPropertyCallback));
1361     }
1362 
1363     @Test
testSetPropertiesAsync_remoteException()1364     public void testSetPropertiesAsync_remoteException() throws RemoteException {
1365         doThrow(new RemoteException()).when(mICarProperty).setPropertiesAsync(
1366                 any(AsyncPropertyServiceRequestList.class),
1367                 any(IAsyncPropertyResultCallback.class), anyLong());
1368 
1369         mCarPropertyManager.setPropertiesAsync(List.of(createSetPropertyRequest()), null, null,
1370                 mSetPropertyCallback);
1371 
1372         verify(mCar).handleRemoteExceptionFromCarService(any(RemoteException.class));
1373     }
1374 
1375     @Test
testSetPropertiesAsync_duplicateRequestId()1376     public void testSetPropertiesAsync_duplicateRequestId() throws RemoteException {
1377         SetPropertyRequest request = createSetPropertyRequest();
1378 
1379         mCarPropertyManager.setPropertiesAsync(List.of(request), null, null,
1380                 mSetPropertyCallback);
1381 
1382         // Send the same request again with the same request ID is not allowed.
1383         assertThrows(IllegalArgumentException.class,
1384                 () -> mCarPropertyManager.setPropertiesAsync(List.of(request), null, null,
1385                         mSetPropertyCallback));
1386     }
1387 
1388     @Test
testSetPropertiesAsync_clearRequestIdAfterFailed()1389     public void testSetPropertiesAsync_clearRequestIdAfterFailed() throws RemoteException {
1390         SetPropertyRequest setPropertyRequest = createSetPropertyRequest();
1391         IllegalArgumentException exception = new IllegalArgumentException();
1392         doThrow(exception).when(mICarProperty).setPropertiesAsync(
1393                 any(AsyncPropertyServiceRequestList.class), any(IAsyncPropertyResultCallback.class),
1394                 anyLong());
1395 
1396         assertThrows(IllegalArgumentException.class,
1397                 () -> mCarPropertyManager.setPropertiesAsync(List.of(setPropertyRequest), null,
1398                         null, mSetPropertyCallback));
1399 
1400         clearInvocations(mICarProperty);
1401         doNothing().when(mICarProperty).setPropertiesAsync(
1402                 any(AsyncPropertyServiceRequestList.class),
1403                 any(IAsyncPropertyResultCallback.class), anyLong());
1404 
1405         // After the first request failed, the request ID map should be cleared so we can use the
1406         // same request ID again.
1407         mCarPropertyManager.setPropertiesAsync(List.of(setPropertyRequest), null, null,
1408                 mSetPropertyCallback);
1409 
1410         verify(mICarProperty).setPropertiesAsync(any(), any(), anyLong());
1411     }
1412 
1413     @Test
testSetProperty_notAvailableAfterU_withVendorErrorCode()1414     public void testSetProperty_notAvailableAfterU_withVendorErrorCode() throws Exception {
1415         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1416         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1417                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1418         doThrow(new ServiceSpecificException(combineErrors(
1419                 VehicleHalStatusCode.STATUS_NOT_AVAILABLE, VENDOR_ERROR_CODE))).when(mICarProperty)
1420                 .setProperty(eq(carPropertyValue), any());
1421 
1422         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1423                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1424         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1425     }
1426 
1427     @Test
testSetProperty_internalErrorAfterU_withVendorErrorCode()1428     public void testSetProperty_internalErrorAfterU_withVendorErrorCode() throws Exception {
1429         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1430         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1431                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1432         doThrow(new ServiceSpecificException(combineErrors(
1433                 VehicleHalStatusCode.STATUS_INTERNAL_ERROR, VENDOR_ERROR_CODE))).when(mICarProperty)
1434                 .setProperty(eq(carPropertyValue), any());
1435 
1436         CarInternalErrorException exception =  assertThrows(CarInternalErrorException.class,
1437                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1438         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1439     }
1440 
1441     @Test
testSetProperty_notAvailableDisabledBeforeU()1442     public void testSetProperty_notAvailableDisabledBeforeU() throws Exception {
1443         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
1444         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1445                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1446         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED))
1447                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1448 
1449         assertThrows(PropertyNotAvailableException.class,
1450                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1451     }
1452 
1453     @Test
testSetProperty_notAvailableDisabledAfterU()1454     public void testSetProperty_notAvailableDisabledAfterU() throws Exception {
1455         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1456         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1457                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1458         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED))
1459                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1460 
1461         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1462                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1463         assertThat(exception.getDetailedErrorCode())
1464                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_DISABLED);
1465     }
1466 
1467     @Test
testSetProperty_notAvailableDisabledAfterU_withVendorErrorCode()1468     public void testSetProperty_notAvailableDisabledAfterU_withVendorErrorCode() throws Exception {
1469         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1470         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1471                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1472         doThrow(new ServiceSpecificException(combineErrors(
1473                 VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED, VENDOR_ERROR_CODE)))
1474                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1475 
1476         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1477                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1478         assertThat(exception.getDetailedErrorCode())
1479                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_DISABLED);
1480         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1481     }
1482 
1483     @Test
testSetProperty_notAvailableSafetyBeforeU()1484     public void testSetProperty_notAvailableSafetyBeforeU() throws Exception {
1485         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
1486         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1487                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1488         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY))
1489                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1490 
1491         assertThrows(PropertyNotAvailableException.class,
1492                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1493     }
1494 
1495     @Test
testSetProperty_notAvailableSafetyAfterU()1496     public void testSetProperty_notAvailableSafetyAfterU() throws Exception {
1497         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1498         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1499                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1500         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY))
1501                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1502 
1503         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1504                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1505         assertThat(exception.getDetailedErrorCode())
1506                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SAFETY);
1507     }
1508 
1509     @Test
testSetProperty_notAvailableSafetyAfterU_withVendorErrorCode()1510     public void testSetProperty_notAvailableSafetyAfterU_withVendorErrorCode() throws Exception {
1511         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1512         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1513                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1514         doThrow(new ServiceSpecificException(combineErrors(
1515                 VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY, VENDOR_ERROR_CODE)))
1516                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1517 
1518         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1519                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1520         assertThat(exception.getDetailedErrorCode())
1521                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SAFETY);
1522         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1523     }
1524 
1525     @Test
testSetProperty_notAvailableSpeedHighBeforeU()1526     public void testSetProperty_notAvailableSpeedHighBeforeU() throws Exception {
1527         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
1528         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1529                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1530         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH))
1531                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1532 
1533         assertThrows(PropertyNotAvailableException.class,
1534                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1535     }
1536 
1537     @Test
testSetProperty_notAvailableSpeedHighAfterU()1538     public void testSetProperty_notAvailableSpeedHighAfterU() throws Exception {
1539         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1540         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1541                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1542         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH))
1543                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1544 
1545         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1546                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1547         assertThat(exception.getDetailedErrorCode())
1548                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_HIGH);
1549     }
1550 
1551     @Test
testSetProperty_notAvailableSpeedHighAfterU_withVendorErrorCode()1552     public void testSetProperty_notAvailableSpeedHighAfterU_withVendorErrorCode() throws Exception {
1553         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1554         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1555                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1556         doThrow(new ServiceSpecificException(combineErrors(
1557                 VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH, VENDOR_ERROR_CODE)))
1558                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1559 
1560         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1561                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1562         assertThat(exception.getDetailedErrorCode())
1563                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_HIGH);
1564         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1565     }
1566 
1567     @Test
testSetProperty_notAvailableSpeedLowBeforeU()1568     public void testSetProperty_notAvailableSpeedLowBeforeU() throws Exception {
1569         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
1570         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1571                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1572         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW))
1573                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1574 
1575         assertThrows(PropertyNotAvailableException.class,
1576                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1577     }
1578 
1579     @Test
testSetProperty_notAvailableSpeedLowAfterU()1580     public void testSetProperty_notAvailableSpeedLowAfterU() throws Exception {
1581         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1582         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1583                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1584         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW))
1585                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1586 
1587         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1588                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1589         assertThat(exception.getDetailedErrorCode())
1590                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_LOW);
1591     }
1592 
1593     @Test
testSetProperty_notAvailableSpeedLowAfterU_withVendorErrorCode()1594     public void testSetProperty_notAvailableSpeedLowAfterU_withVendorErrorCode() throws Exception {
1595         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1596         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1597                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1598         doThrow(new ServiceSpecificException(combineErrors(
1599                 VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW, VENDOR_ERROR_CODE)))
1600                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1601 
1602         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1603                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1604         assertThat(exception.getDetailedErrorCode())
1605                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_LOW);
1606         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1607     }
1608 
1609     @Test
testOnSetValueResult_onSuccess()1610     public void testOnSetValueResult_onSuccess() throws RemoteException {
1611         doAnswer((invocation) -> {
1612             Object[] args = invocation.getArguments();
1613             AsyncPropertyServiceRequestList setPropertyRequest =
1614                     (AsyncPropertyServiceRequestList) args[0];
1615             List setPropertyServiceList = setPropertyRequest.getList();
1616             AsyncPropertyServiceRequest setPropertyServiceRequest =
1617                     (AsyncPropertyServiceRequest) setPropertyServiceList.get(0);
1618             IAsyncPropertyResultCallback setAsyncPropertyResultCallback =
1619                     (IAsyncPropertyResultCallback) args[1];
1620 
1621             assertThat(setPropertyServiceRequest.getRequestId()).isEqualTo(0);
1622             assertThat(setPropertyServiceRequest.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1623             assertThat(setPropertyServiceRequest.getCarPropertyValue().getValue()).isEqualTo(
1624                     17.0f);
1625 
1626             GetSetValueResult setValueResult = GetSetValueResult.newSetValueResult(0,
1627                         /* updateTimestampNanos= */ TEST_TIMESTAMP);
1628 
1629             setAsyncPropertyResultCallback.onSetValueResults(
1630                     new GetSetValueResultList(List.of(setValueResult)));
1631             return null;
1632         }).when(mICarProperty).setPropertiesAsync(any(), any(), anyLong());
1633 
1634 
1635         mCarPropertyManager.setPropertiesAsync(List.of(createSetPropertyRequest()), null, null,
1636                 mSetPropertyCallback);
1637 
1638         verify(mSetPropertyCallback, timeout(1000)).onSuccess(mSetPropertyResultCaptor.capture());
1639         SetPropertyResult gotResult = mSetPropertyResultCaptor.getValue();
1640         assertThat(gotResult.getRequestId()).isEqualTo(0);
1641         assertThat(gotResult.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1642         assertThat(gotResult.getAreaId()).isEqualTo(0);
1643         assertThat(gotResult.getUpdateTimestampNanos()).isEqualTo(TEST_TIMESTAMP);
1644     }
1645 
1646     @Test
testOnSetValueResult_onSuccessMultipleRequests()1647     public void testOnSetValueResult_onSuccessMultipleRequests() throws RemoteException {
1648         doAnswer((invocation) -> {
1649             Object[] args = invocation.getArguments();
1650             AsyncPropertyServiceRequestList setPropertyServiceRequest =
1651                     (AsyncPropertyServiceRequestList) args[0];
1652             List<AsyncPropertyServiceRequest> setPropertyServiceRequests =
1653                     setPropertyServiceRequest.getList();
1654             IAsyncPropertyResultCallback setAsyncPropertyResultCallback =
1655                     (IAsyncPropertyResultCallback) args[1];
1656 
1657             assertThat(setPropertyServiceRequests.size()).isEqualTo(2);
1658             assertThat(setPropertyServiceRequests.get(0).getRequestId()).isEqualTo(0);
1659             assertThat(setPropertyServiceRequests.get(0).getPropertyId()).isEqualTo(
1660                     HVAC_TEMPERATURE_SET);
1661             assertThat(setPropertyServiceRequests.get(1).getRequestId()).isEqualTo(1);
1662             assertThat(setPropertyServiceRequests.get(1).getPropertyId()).isEqualTo(
1663                     HVAC_TEMPERATURE_SET);
1664 
1665             List<GetSetValueResult> setValueResults = List.of(
1666                     GetSetValueResult.newSetValueResult(0,
1667                             /* updateTimestampNanos= */ TEST_TIMESTAMP),
1668                     GetSetValueResult.newSetValueResult(1,
1669                             /* updateTimestampNanos= */ TEST_TIMESTAMP));
1670 
1671             setAsyncPropertyResultCallback.onSetValueResults(
1672                     new GetSetValueResultList(setValueResults));
1673             return null;
1674         }).when(mICarProperty).setPropertiesAsync(any(), any(), anyLong());
1675 
1676         List<SetPropertyRequest<?>> setPropertyRequests = new ArrayList<>();
1677         setPropertyRequests.add(createSetPropertyRequest());
1678         setPropertyRequests.add(createSetPropertyRequest());
1679 
1680         mCarPropertyManager.setPropertiesAsync(setPropertyRequests, null, null,
1681                 mSetPropertyCallback);
1682 
1683         verify(mSetPropertyCallback, timeout(1000).times(2)).onSuccess(
1684                 mSetPropertyResultCaptor.capture());
1685         List<SetPropertyResult> gotPropertyResults = mSetPropertyResultCaptor.getAllValues();
1686         assertThat(gotPropertyResults.get(0).getRequestId()).isEqualTo(0);
1687         assertThat(gotPropertyResults.get(0).getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1688         assertThat(gotPropertyResults.get(0).getAreaId()).isEqualTo(0);
1689         assertThat(gotPropertyResults.get(0).getUpdateTimestampNanos()).isEqualTo(TEST_TIMESTAMP);
1690         assertThat(gotPropertyResults.get(1).getRequestId()).isEqualTo(1);
1691         assertThat(gotPropertyResults.get(1).getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1692         assertThat(gotPropertyResults.get(1).getAreaId()).isEqualTo(0);
1693         assertThat(gotPropertyResults.get(1).getUpdateTimestampNanos()).isEqualTo(TEST_TIMESTAMP);
1694     }
1695 
1696     @Test
testOnSetValueResult_onFailure()1697     public void testOnSetValueResult_onFailure() throws RemoteException {
1698         doAnswer((invocation) -> {
1699             Object[] args = invocation.getArguments();
1700             AsyncPropertyServiceRequestList setPropertyService =
1701                     (AsyncPropertyServiceRequestList) args[0];
1702             List setPropertyServiceList = setPropertyService.getList();
1703             AsyncPropertyServiceRequest setPropertyServiceRequest =
1704                     (AsyncPropertyServiceRequest) setPropertyServiceList.get(0);
1705             IAsyncPropertyResultCallback setAsyncPropertyResultCallback =
1706                     (IAsyncPropertyResultCallback) args[1];
1707 
1708             assertThat(setPropertyServiceRequest.getRequestId()).isEqualTo(0);
1709             assertThat(setPropertyServiceRequest.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1710 
1711             GetSetValueResult setValueResult = GetSetValueResult.newErrorSetValueResult(
1712                     0,
1713                     new CarPropertyErrorCodes(
1714                             CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR, VENDOR_ERROR_CODE, 0));
1715 
1716             setAsyncPropertyResultCallback.onSetValueResults(
1717                     new GetSetValueResultList(List.of(setValueResult)));
1718             return null;
1719         }).when(mICarProperty).setPropertiesAsync(any(), any(), anyLong());
1720 
1721         mCarPropertyManager.setPropertiesAsync(List.of(createSetPropertyRequest()), null, null,
1722                 mSetPropertyCallback);
1723 
1724         verify(mSetPropertyCallback, timeout(1000)).onFailure(mPropertyAsyncErrorCaptor.capture());
1725         PropertyAsyncError error = mPropertyAsyncErrorCaptor.getValue();
1726         assertThat(error.getRequestId()).isEqualTo(0);
1727         assertThat(error.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1728         assertThat(error.getAreaId()).isEqualTo(0);
1729         assertThat(error.getErrorCode()).isEqualTo(
1730                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
1731         assertThat(error.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1732     }
1733 
1734     @Test
testRegisterCallback_returnsFalseIfPropertyIdNotSupportedInVehicle()1735     public void testRegisterCallback_returnsFalseIfPropertyIdNotSupportedInVehicle()
1736             throws RemoteException {
1737         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1738                 VehiclePropertyIds.INVALID, FIRST_UPDATE_RATE_HZ)).isFalse();
1739         verify(mICarProperty, never()).registerListener(any(),
1740                 any(ICarPropertyEventListener.class));
1741     }
1742 
1743     @Test
testRegisterCallback_registersWithServiceOnFirstCallback()1744     public void testRegisterCallback_registersWithServiceOnFirstCallback() throws RemoteException {
1745         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1746                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
1747         verify(mICarProperty).registerListener(eq(List.of(
1748                 createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
1749                         FIRST_UPDATE_RATE_HZ))), any(ICarPropertyEventListener.class));
1750     }
1751 
1752     @Test
testRegisterCallback_registersWithMaxUpdateRateOnFirstCallback()1753     public void testRegisterCallback_registersWithMaxUpdateRateOnFirstCallback()
1754             throws RemoteException {
1755         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1756                 VENDOR_CONTINUOUS_PROPERTY, MAX_UPDATE_RATE_HZ + 1)).isTrue();
1757         verify(mICarProperty).registerListener(eq(List.of(
1758                 createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
1759                         MAX_UPDATE_RATE_HZ))), any(ICarPropertyEventListener.class));
1760     }
1761 
1762     @Test
testRegisterCallback_registersWithMinUpdateRateOnFirstCallback()1763     public void testRegisterCallback_registersWithMinUpdateRateOnFirstCallback()
1764             throws RemoteException {
1765         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1766                 VENDOR_CONTINUOUS_PROPERTY, MIN_UPDATE_RATE_HZ - 1)).isTrue();
1767         verify(mICarProperty).registerListener(eq(List.of(
1768                 createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
1769                         MIN_UPDATE_RATE_HZ))), any(ICarPropertyEventListener.class));
1770     }
1771 
1772     @Test
testRegisterCallback_registersWithOnChangeRateForOnChangeProperty()1773     public void testRegisterCallback_registersWithOnChangeRateForOnChangeProperty()
1774             throws RemoteException {
1775         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1776                 VENDOR_ON_CHANGE_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
1777         verify(mICarProperty).registerListener(eq(List.of(
1778                 createCarSubscriptionOption(VENDOR_ON_CHANGE_PROPERTY, new int[]{0},
1779                         CarPropertyManager.SENSOR_RATE_ONCHANGE))),
1780                 any(ICarPropertyEventListener.class));
1781     }
1782 
1783     @Test
testRegisterCallback_registersWithOnChangeRateForStaticProperty()1784     public void testRegisterCallback_registersWithOnChangeRateForStaticProperty()
1785             throws RemoteException {
1786         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1787                 VENDOR_STATIC_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
1788         verify(mICarProperty).registerListener(eq(List.of(
1789                 createCarSubscriptionOption(VENDOR_STATIC_PROPERTY, new int[]{0},
1790                         CarPropertyManager.SENSOR_RATE_ONCHANGE))),
1791                 any(ICarPropertyEventListener.class));
1792     }
1793 
1794     @Test
testRegisterCallback_returnsFalseForRemoteException()1795     public void testRegisterCallback_returnsFalseForRemoteException() throws RemoteException {
1796         RemoteException remoteException = new RemoteException();
1797         doThrow(remoteException).when(mICarProperty).registerListener(
1798                 eq(List.of(createCarSubscriptionOption(VENDOR_ON_CHANGE_PROPERTY, new int[] {0},
1799                         CarPropertyManager.SENSOR_RATE_ONCHANGE))),
1800                 any(ICarPropertyEventListener.class));
1801         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1802                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isFalse();
1803     }
1804 
1805     @Test
testRegisterCallback_recoversAfterFirstRemoteException()1806     public void testRegisterCallback_recoversAfterFirstRemoteException() throws RemoteException {
1807         RemoteException remoteException = new RemoteException();
1808         doThrow(remoteException).doNothing().when(mICarProperty)
1809                 .registerListener(eq(List.of(
1810                         createCarSubscriptionOption(VENDOR_ON_CHANGE_PROPERTY, new int[] {0},
1811                         SENSOR_RATE_ONCHANGE))),
1812                 any(ICarPropertyEventListener.class));
1813         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1814                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isFalse();
1815         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1816                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
1817         verify(mICarProperty, times(2)).registerListener(
1818                 eq(List.of(createCarSubscriptionOption(VENDOR_ON_CHANGE_PROPERTY, new int[] {0},
1819                         CarPropertyManager.SENSOR_RATE_ONCHANGE))),
1820                 any(ICarPropertyEventListener.class));
1821     }
1822 
1823     @Test
testRegisterCallback_returnsFalseForIllegalArgumentException()1824     public void testRegisterCallback_returnsFalseForIllegalArgumentException()
1825             throws RemoteException {
1826         doThrow(IllegalArgumentException.class).when(mICarProperty)
1827                 .registerListener(eq(List.of(createCarSubscriptionOption(
1828                         VENDOR_ON_CHANGE_PROPERTY, new int[] {0}, SENSOR_RATE_ONCHANGE))),
1829                 any(ICarPropertyEventListener.class));
1830 
1831         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1832                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isFalse();
1833     }
1834 
1835     @Test
testRegisterCallback_registersTwiceWithHigherRateCallback()1836     public void testRegisterCallback_registersTwiceWithHigherRateCallback() throws RemoteException {
1837         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1838                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
1839         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback2,
1840                 VENDOR_CONTINUOUS_PROPERTY, LARGER_UPDATE_RATE_HZ)).isTrue();
1841         verify(mICarProperty, times(2)).registerListener(
1842                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
1843         assertThat(mCarSubscriptionCaptor.getAllValues()).containsExactly(
1844                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1845                         LARGER_UPDATE_RATE_HZ)),
1846                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1847                         FIRST_UPDATE_RATE_HZ)));
1848     }
1849 
1850     @Test
testRegisterCallback_registersOnSecondHigherRateWithSameCallback()1851     public void testRegisterCallback_registersOnSecondHigherRateWithSameCallback()
1852             throws RemoteException {
1853         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1854                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
1855         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback2,
1856                 VENDOR_CONTINUOUS_PROPERTY, LARGER_UPDATE_RATE_HZ)).isTrue();
1857         verify(mICarProperty, times(2)).registerListener(
1858                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
1859         assertThat(mCarSubscriptionCaptor.getAllValues()).containsExactly(
1860                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1861                         LARGER_UPDATE_RATE_HZ)),
1862                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1863                         FIRST_UPDATE_RATE_HZ)));
1864     }
1865 
1866     @Test
testRegisterCallback_registersOnSecondLowerRateWithSameCallback()1867     public void testRegisterCallback_registersOnSecondLowerRateWithSameCallback()
1868             throws RemoteException {
1869         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1870                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
1871         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1872                 VENDOR_CONTINUOUS_PROPERTY, SMALLER_UPDATE_RATE_HZ)).isTrue();
1873         verify(mICarProperty, times(2)).registerListener(
1874                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
1875         assertThat(mCarSubscriptionCaptor.getAllValues()).containsExactly(
1876                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1877                         FIRST_UPDATE_RATE_HZ)),
1878                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1879                         SMALLER_UPDATE_RATE_HZ)));
1880     }
1881 
1882     @Test
testRegisterCallback_doesNotRegistersOnSecondLowerRateCallback()1883     public void testRegisterCallback_doesNotRegistersOnSecondLowerRateCallback()
1884             throws RemoteException {
1885         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1886                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
1887         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback2,
1888                 VENDOR_CONTINUOUS_PROPERTY, SMALLER_UPDATE_RATE_HZ)).isTrue();
1889         verify(mICarProperty).registerListener(eq(List.of(
1890                 createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
1891                         FIRST_UPDATE_RATE_HZ))),
1892                 any(ICarPropertyEventListener.class));
1893     }
1894 
1895     @Test
testRegisterCallback_registersTwiceForDifferentProperties()1896     public void testRegisterCallback_registersTwiceForDifferentProperties() throws RemoteException {
1897         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1898                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
1899         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1900                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
1901         verify(mICarProperty, times(2)).registerListener(
1902                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
1903         assertThat(mCarSubscriptionCaptor.getAllValues()).containsExactly(
1904                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1905                         FIRST_UPDATE_RATE_HZ)),
1906                 List.of(createCarSubscriptionOption(VENDOR_ON_CHANGE_PROPERTY, new int[] {0},
1907                         CarPropertyManager.SENSOR_RATE_ONCHANGE)));
1908     }
1909 
1910     @Test
testRegisterCallback_registerInvalidProp()1911     public void testRegisterCallback_registerInvalidProp() throws Exception {
1912         assertThat(mCarPropertyManager.registerCallback(
1913                 mCarPropertyEventCallback, /* propertyId= */ -1, FIRST_UPDATE_RATE_HZ)).isFalse();
1914     }
1915 
1916     // We annotate the update rate to be within 0 and 100, however, in the actual implementation,
1917     // we will fit update rate outside this range to the min and max update rate.
1918     @Test
testRegisterCallback_updateRateOutsideFloatRangeMustNotThrow_tooLarge()1919     public void testRegisterCallback_updateRateOutsideFloatRangeMustNotThrow_tooLarge()
1920             throws Exception {
1921         assertThat(mCarPropertyManager.registerCallback(
1922                 mCarPropertyEventCallback, VENDOR_CONTINUOUS_PROPERTY, /* updateRateHz= */ 101.f))
1923                 .isTrue();
1924 
1925         verify(mICarProperty).registerListener(
1926                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
1927         assertThat(mCarSubscriptionCaptor.getValue()).isEqualTo(
1928                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1929                         MAX_UPDATE_RATE_HZ)));
1930     }
1931 
1932     @Test
testRegisterCallback_updateRateOutsideFloatRangeMustNotThrow_tooSmall()1933     public void testRegisterCallback_updateRateOutsideFloatRangeMustNotThrow_tooSmall()
1934             throws Exception {
1935         assertThat(mCarPropertyManager.registerCallback(
1936                 mCarPropertyEventCallback, VENDOR_CONTINUOUS_PROPERTY, /* updateRateHz= */ -1.f))
1937                 .isTrue();
1938 
1939         verify(mICarProperty).registerListener(
1940                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
1941         assertThat(mCarSubscriptionCaptor.getValue()).isEqualTo(
1942                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1943                         MIN_UPDATE_RATE_HZ)));
1944     }
1945 
1946     @Test
testRegisterCallback_isSupportedAndHasWritePermissionOnly()1947     public void testRegisterCallback_isSupportedAndHasWritePermissionOnly() throws Exception {
1948         int propId = VENDOR_CONTINUOUS_PROPERTY;
1949         when(mICarProperty.isSupportedAndHasWritePermissionOnly(propId)).thenReturn(true);
1950 
1951         assertThrows(SecurityException.class, () -> mCarPropertyManager.registerCallback(
1952                 mCarPropertyEventCallback, VENDOR_CONTINUOUS_PROPERTY, 0));
1953     }
1954 
1955     @Test
testRegisterCallback_isSupportedAndHasWritePermissionOnly_remoteException()1956     public void testRegisterCallback_isSupportedAndHasWritePermissionOnly_remoteException()
1957             throws Exception {
1958         int propId = VENDOR_CONTINUOUS_PROPERTY;
1959         when(mICarProperty.isSupportedAndHasWritePermissionOnly(propId)).thenThrow(
1960                 new RemoteException());
1961 
1962         assertThat(mCarPropertyManager.registerCallback(
1963                 mCarPropertyEventCallback, VENDOR_CONTINUOUS_PROPERTY, 0)).isFalse();
1964     }
1965 
1966     @Test
testSubscribePropertyEvents_registerMultipleEvents()1967     public void testSubscribePropertyEvents_registerMultipleEvents() throws Exception {
1968         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
1969                 new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
1970                         .setUpdateRateFast().build(),
1971                 new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).addAreaId(0).build()),
1972                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
1973 
1974         verify(mICarProperty).registerListener(
1975                 eq(List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
1976                         CarPropertyManager.SENSOR_RATE_FAST, /* enableVur= */ true),
1977                         createCarSubscriptionOption(VENDOR_ON_CHANGE_PROPERTY, new int[]{0},
1978                                 CarPropertyManager.SENSOR_RATE_ONCHANGE))),
1979                 any(ICarPropertyEventListener.class));
1980     }
1981 
1982     @Test
testSubscribePropertyEvents_sanitizeSampleRate()1983     public void testSubscribePropertyEvents_sanitizeSampleRate() throws Exception {
1984         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
1985                 new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
1986                         .setUpdateRateHz(0.f).build()),
1987                 /* callbackExecutor= */ null, mCarPropertyEventCallback))
1988                 .isTrue();
1989 
1990         verify(mICarProperty).registerListener(
1991                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
1992         assertThat(mCarSubscriptionCaptor.getValue()).isEqualTo(
1993                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1994                         MIN_UPDATE_RATE_HZ, /* enableVur= */ true)));
1995     }
1996 
1997     @Test
testSubscribePropertyEvents_registerMultipleEventsSameProperty_throws()1998     public void testSubscribePropertyEvents_registerMultipleEventsSameProperty_throws()
1999             throws Exception {
2000         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
2001                 () -> mCarPropertyManager.subscribePropertyEvents(List.of(
2002                                 new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2003                                         .addAreaId(0).setUpdateRateFast().build(),
2004                                 new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2005                                         .addAreaId(0).build()),
2006                         /* callbackExecutor= */ null, mCarPropertyEventCallback));
2007 
2008         assertWithMessage("Overlapping areaIds").that(thrown).hasMessageThat()
2009                 .contains("Subscribe options contain overlapping propertyId");
2010     }
2011 
2012     @Test
testSubscribePropertyEvents_registerMultipleEvents_unsubscribe()2013     public void testSubscribePropertyEvents_registerMultipleEvents_unsubscribe() throws Exception {
2014         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2015                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2016                                 .setUpdateRateFast().build(),
2017                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).addAreaId(0)
2018                                 .build()),
2019                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2020 
2021         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback);
2022         verify(mICarProperty).unregisterListener(eq(VENDOR_CONTINUOUS_PROPERTY),
2023                 any(ICarPropertyEventListener.class));
2024         verify(mICarProperty).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2025                 any(ICarPropertyEventListener.class));
2026     }
2027 
2028     @Test
testSubscribePropertyEvents_registerMultipleEventsWithSameProperty_unsubscribe()2029     public void testSubscribePropertyEvents_registerMultipleEventsWithSameProperty_unsubscribe()
2030             throws Exception {
2031         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2032                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2033                                 .setUpdateRateFastest().build(),
2034                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).addAreaId(0)
2035                                 .build()),
2036                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2037         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2038                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2039                                 .setUpdateRateFast().build()),
2040                 /* callbackExecutor= */ null, mCarPropertyEventCallback2)).isTrue();
2041         clearInvocations(mICarProperty);
2042         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback);
2043 
2044         verify(mICarProperty).registerListener(eq(List.of(
2045                         createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2046                                 CarPropertyManager.SENSOR_RATE_FAST, /* enableVur= */ true))),
2047                 any(ICarPropertyEventListener.class));
2048         verify(mICarProperty).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2049                 any(ICarPropertyEventListener.class));
2050     }
2051 
2052     @Test
testSubscribePropertyEvents_withPropertyIdCallback()2053     public void testSubscribePropertyEvents_withPropertyIdCallback() throws Exception {
2054         assertThat(mCarPropertyManager.subscribePropertyEvents(
2055                 VENDOR_CONTINUOUS_PROPERTY, mCarPropertyEventCallback)).isTrue();
2056 
2057         verify(mICarProperty).registerListener(eq(List.of(
2058                         createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2059                                 MIN_UPDATE_RATE_HZ, /* enableVur= */ true))),
2060                 any(ICarPropertyEventListener.class));
2061     }
2062 
2063     @Test
testSubscribePropertyEvents_withPropertyIdUpdateRateCallback()2064     public void testSubscribePropertyEvents_withPropertyIdUpdateRateCallback() throws Exception {
2065         assertThat(mCarPropertyManager.subscribePropertyEvents(
2066                 VENDOR_CONTINUOUS_PROPERTY, CarPropertyManager.SENSOR_RATE_FAST,
2067                 mCarPropertyEventCallback)).isTrue();
2068 
2069         verify(mICarProperty).registerListener(eq(List.of(
2070                         createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2071                                 CarPropertyManager.SENSOR_RATE_FAST, /* enableVur= */ true))),
2072                 any(ICarPropertyEventListener.class));
2073     }
2074 
2075     @Test
testSubscribePropertyEvents_withPropertyIdAreaIdCallback()2076     public void testSubscribePropertyEvents_withPropertyIdAreaIdCallback() throws Exception {
2077         assertThat(mCarPropertyManager.subscribePropertyEvents(
2078                 VENDOR_CONTINUOUS_PROPERTY, /* areaId= */ 0,
2079                 mCarPropertyEventCallback)).isTrue();
2080 
2081         verify(mICarProperty).registerListener(eq(List.of(
2082                         createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2083                                 MIN_UPDATE_RATE_HZ, /* enableVur= */ true))),
2084                 any(ICarPropertyEventListener.class));
2085     }
2086 
2087     @Test
testSubscribePropertyEvents_withPropertyIdAreaIdUpdateRateCallback()2088     public void testSubscribePropertyEvents_withPropertyIdAreaIdUpdateRateCallback()
2089                 throws Exception {
2090         assertThat(mCarPropertyManager.subscribePropertyEvents(
2091                 VENDOR_CONTINUOUS_PROPERTY, /* areaId= */ 0,
2092                 CarPropertyManager.SENSOR_RATE_FAST,
2093                 mCarPropertyEventCallback)).isTrue();
2094 
2095         verify(mICarProperty).registerListener(eq(List.of(
2096                         createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2097                                 CarPropertyManager.SENSOR_RATE_FAST, /* enableVur= */ false))),
2098                 any(ICarPropertyEventListener.class));
2099     }
2100 
2101     @Test
testSubscribePropertyEvents_noReadPermission()2102     public void testSubscribePropertyEvents_noReadPermission() throws Exception {
2103         int propId = VENDOR_CONTINUOUS_PROPERTY;
2104         when(mICarProperty.getSupportedNoReadPermPropIds(any()))
2105                 .thenReturn(new int[] {propId});
2106 
2107         assertThrows(SecurityException.class, () -> mCarPropertyManager.subscribePropertyEvents(
2108                 propId, mCarPropertyEventCallback));
2109     }
2110 
2111     @Test
testSubscribePropertyEvents_getSupportedNoReadPermPropIds_remoteException()2112     public void testSubscribePropertyEvents_getSupportedNoReadPermPropIds_remoteException()
2113             throws Exception {
2114         int propId = VENDOR_CONTINUOUS_PROPERTY;
2115         when(mICarProperty.getSupportedNoReadPermPropIds(any()))
2116                 .thenThrow(new RemoteException());
2117 
2118         assertThat(mCarPropertyManager.subscribePropertyEvents(
2119                 propId, mCarPropertyEventCallback)).isFalse();
2120     }
2121 
2122     @Test
testRegisterCallback_registersAgainWithDifferentExecutor_throws()2123     public void testRegisterCallback_registersAgainWithDifferentExecutor_throws() throws Exception {
2124         mCarPropertyManager.subscribePropertyEvents(List.of(
2125                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2126                                 .setUpdateRateFast().build(),
2127                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).addAreaId(0)
2128                                 .build()),
2129                 mMockExecutor1, mCarPropertyEventCallback);
2130 
2131         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
2132                 mCarPropertyManager.subscribePropertyEvents(List.of(
2133                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2134                                 .setUpdateRateFast().build(),
2135                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).addAreaId(0)
2136                                 .build()),
2137                 mMockExecutor2, mCarPropertyEventCallback));
2138         assertWithMessage("Two executor subscription").that(thrown).hasMessageThat()
2139                 .contains("A different executor is already associated with this callback,"
2140                         + " please use the same executor");
2141     }
2142 
2143     @Test
testRegisterCallback_registersAgainWithSameExecutor()2144     public void testRegisterCallback_registersAgainWithSameExecutor() throws Exception {
2145         mCarPropertyManager.subscribePropertyEvents(List.of(
2146                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2147                                 .setUpdateRateFast().build(),
2148                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).addAreaId(0)
2149                                 .build()),
2150                 mMockExecutor1, mCarPropertyEventCallback);
2151         mCarPropertyManager.subscribePropertyEvents(List.of(
2152                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2153                                 .setUpdateRateFast().build(),
2154                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).addAreaId(0)
2155                                 .build()),
2156                 mMockExecutor1, mCarPropertyEventCallback);
2157 
2158         verify(mICarProperty).registerListener(
2159                 eq(List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2160                         CarPropertyManager.SENSOR_RATE_FAST, /* enableVur= */ true),
2161                         createCarSubscriptionOption(VENDOR_ON_CHANGE_PROPERTY, new int[]{0},
2162                                 CarPropertyManager.SENSOR_RATE_ONCHANGE))),
2163                 any(ICarPropertyEventListener.class));
2164     }
2165 
2166     @Test
testRegisterCallback_registersAgainIfTheFirstCallbackReturnsFalse()2167     public void testRegisterCallback_registersAgainIfTheFirstCallbackReturnsFalse()
2168             throws RemoteException {
2169         doThrow(IllegalArgumentException.class).doNothing().when(mICarProperty)
2170                 .registerListener(eq(List.of(
2171                                 createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY,
2172                                         new int[]{0}, FIRST_UPDATE_RATE_HZ))),
2173                 any(ICarPropertyEventListener.class));
2174 
2175         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2176                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isFalse();
2177         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2178                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2179     }
2180 
2181     @Test
testRegisterCallback_restoresOriginalRateHzIfTheSecondCallbackReturnsFalse()2182     public void testRegisterCallback_restoresOriginalRateHzIfTheSecondCallbackReturnsFalse()
2183             throws RemoteException {
2184         Float smallerUpdate = 1F;
2185         Float largerUpdate = 2F;
2186         doThrow(IllegalArgumentException.class).when(mICarProperty)
2187                 .registerListener(eq(List.of(createCarSubscriptionOption(
2188                         HVAC_TEMPERATURE_SET, new int[]{0}, largerUpdate))),
2189                 any(ICarPropertyEventListener.class));
2190 
2191         CarPropertyValue<Float> goodValue = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
2192                 Duration.ofSeconds(1).toNanos(), 17.0f);
2193         CarPropertyValue<Float> almostFreshValue = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
2194                 Duration.ofMillis(1899).toNanos(), 18.0f);
2195         List<CarPropertyEvent> eventList = List.of(
2196                 new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, goodValue),
2197                 new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE,
2198                         almostFreshValue)
2199         );
2200         CarPropertyConfig config = CarPropertyConfig.newBuilder(Float.class, HVAC_TEMPERATURE_SET,
2201                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
2202                 .addAreaIdConfig(new AreaIdConfig.Builder<Float>(0).build())
2203                 .setChangeMode(CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS)
2204                 .setMinSampleRate(0f)
2205                 .setMaxSampleRate(10f).build();
2206         addCarPropertyConfig(config);
2207         ICarPropertyEventListener listener = getCarPropertyEventListener();
2208         ArgumentCaptor<CarPropertyValue> valueCaptor =
2209                 ArgumentCaptor.forClass(CarPropertyValue.class);
2210 
2211         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2212                 HVAC_TEMPERATURE_SET, smallerUpdate)).isTrue();
2213         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2214                 HVAC_TEMPERATURE_SET, largerUpdate)).isFalse();
2215         verify(mICarProperty, times(3)).registerListener(
2216                 any(), any(ICarPropertyEventListener.class));
2217 
2218         listener.onEvent(eventList);
2219         verify(mCarPropertyEventCallback, timeout(5000)).onChangeEvent(valueCaptor.capture());
2220         assertThat(valueCaptor.getAllValues()).containsExactly(goodValue);
2221     }
2222 
2223     @Test
testUnregisterCallback_doesNothingIfNothingRegistered()2224     public void testUnregisterCallback_doesNothingIfNothingRegistered() throws Exception {
2225         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback, VENDOR_STATIC_PROPERTY);
2226         verify(mICarProperty, never()).unregisterListener(anyInt(),
2227                 any(ICarPropertyEventListener.class));
2228     }
2229 
2230     @Test
testUnsubscribePropertyEvents_doesNothingIfNothingRegistered()2231     public void testUnsubscribePropertyEvents_doesNothingIfNothingRegistered() throws Exception {
2232         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_STATIC_PROPERTY,
2233                 mCarPropertyEventCallback);
2234         verify(mICarProperty, never()).unregisterListener(anyInt(),
2235                 any(ICarPropertyEventListener.class));
2236     }
2237 
2238     @Test
testUnregisterCallback_doesNothingIfPropertyIsNotRegisteredForCallback()2239     public void testUnregisterCallback_doesNothingIfPropertyIsNotRegisteredForCallback()
2240             throws Exception {
2241         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2242                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2243 
2244         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback, VENDOR_STATIC_PROPERTY);
2245         verify(mICarProperty, never()).unregisterListener(anyInt(),
2246                 any(ICarPropertyEventListener.class));
2247     }
2248 
2249     @Test
testUnsubscribePropertyEvents_doesNothingIfNothingIsNotRegisteredForCallback()2250     public void testUnsubscribePropertyEvents_doesNothingIfNothingIsNotRegisteredForCallback()
2251             throws Exception {
2252         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2253                 new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2254                         .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2255                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2256 
2257         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_STATIC_PROPERTY,
2258                 mCarPropertyEventCallback);
2259         verify(mICarProperty, never()).unregisterListener(anyInt(),
2260                 any(ICarPropertyEventListener.class));
2261     }
2262 
2263     @Test
testUnregisterCallback_doesNothingIfCallbackIsNotRegisteredForProperty()2264     public void testUnregisterCallback_doesNothingIfCallbackIsNotRegisteredForProperty()
2265             throws Exception {
2266         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2267                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2268 
2269         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback2,
2270                 VENDOR_CONTINUOUS_PROPERTY);
2271         verify(mICarProperty, never()).unregisterListener(anyInt(),
2272                 any(ICarPropertyEventListener.class));
2273     }
2274 
2275     @Test
testUnsubscribePropertyEvents_doesNothingIfCallbackIsNotRegisteredForProperty()2276     public void testUnsubscribePropertyEvents_doesNothingIfCallbackIsNotRegisteredForProperty()
2277             throws Exception {
2278         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2279                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2280                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2281                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2282 
2283         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_CONTINUOUS_PROPERTY,
2284                 mCarPropertyEventCallback2);
2285         verify(mICarProperty, never()).unregisterListener(anyInt(),
2286                 any(ICarPropertyEventListener.class));
2287     }
2288 
2289     @Test
testUnregisterCallback_doesNothingIfCarPropertyConfigNull()2290     public void testUnregisterCallback_doesNothingIfCarPropertyConfigNull() throws Exception {
2291         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2292                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2293 
2294         setPropIdWithoutConfig(VENDOR_CONTINUOUS_PROPERTY);
2295 
2296         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2297                 VENDOR_CONTINUOUS_PROPERTY);
2298         verify(mICarProperty, never()).unregisterListener(anyInt(),
2299                 any(ICarPropertyEventListener.class));
2300     }
2301 
2302     @Test
testUnregisterCallback_doesNothingIfNoPermission()2303     public void testUnregisterCallback_doesNothingIfNoPermission() throws Exception {
2304         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2305                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2306 
2307         setPropIdWithoutPermission(VENDOR_CONTINUOUS_PROPERTY);
2308 
2309         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2310                 VENDOR_CONTINUOUS_PROPERTY);
2311         verify(mICarProperty, never()).unregisterListener(anyInt(),
2312                 any(ICarPropertyEventListener.class));
2313     }
2314 
2315     @Test
testUnsubscribePropertyEvents_doesNothingIfCarPropertyConfigNull()2316     public void testUnsubscribePropertyEvents_doesNothingIfCarPropertyConfigNull()
2317             throws Exception {
2318         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2319                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2320                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2321                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2322 
2323         setPropIdWithoutConfig(VENDOR_CONTINUOUS_PROPERTY);
2324 
2325         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_CONTINUOUS_PROPERTY,
2326                 mCarPropertyEventCallback);
2327         verify(mICarProperty, never()).unregisterListener(anyInt(),
2328                 any(ICarPropertyEventListener.class));
2329     }
2330 
2331     @Test
testUnsubscribePropertyEvents_doesNothingIfNoPermission()2332     public void testUnsubscribePropertyEvents_doesNothingIfNoPermission()
2333             throws Exception {
2334         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2335                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2336                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2337                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2338 
2339         setPropIdWithoutPermission(VENDOR_CONTINUOUS_PROPERTY);
2340 
2341         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_CONTINUOUS_PROPERTY,
2342                 mCarPropertyEventCallback);
2343         verify(mICarProperty, never()).unregisterListener(anyInt(),
2344                 any(ICarPropertyEventListener.class));
2345     }
2346 
2347     @Test
testUnregisterCallback_unregistersCallbackForSingleProperty()2348     public void testUnregisterCallback_unregistersCallbackForSingleProperty()
2349             throws Exception {
2350         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2351                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2352 
2353         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2354                 VENDOR_CONTINUOUS_PROPERTY);
2355         verify(mICarProperty).unregisterListener(eq(VENDOR_CONTINUOUS_PROPERTY),
2356                 any(ICarPropertyEventListener.class));
2357     }
2358 
2359     @Test
testUnregisterCallback_unsubscribeCallbackForSingleProperty()2360     public void testUnregisterCallback_unsubscribeCallbackForSingleProperty()
2361             throws Exception {
2362         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2363                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2364                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2365                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2366 
2367         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2368                 VENDOR_CONTINUOUS_PROPERTY);
2369         verify(mICarProperty).unregisterListener(eq(VENDOR_CONTINUOUS_PROPERTY),
2370                 any(ICarPropertyEventListener.class));
2371     }
2372 
2373     @Test
testUnregisterCallback_unregistersCallbackForSpecificProperty()2374     public void testUnregisterCallback_unregistersCallbackForSpecificProperty()
2375             throws Exception {
2376         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2377                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2378         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2379                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
2380 
2381         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2382                 VENDOR_ON_CHANGE_PROPERTY);
2383         verify(mICarProperty).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2384                 any(ICarPropertyEventListener.class));
2385     }
2386 
2387     @Test
testUnsubscribePropertyEvents_ignoreUserHalProp()2388     public void testUnsubscribePropertyEvents_ignoreUserHalProp()
2389             throws Exception {
2390         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
2391 
2392         mCarPropertyManager.unsubscribePropertyEvents(INITIAL_USER_INFO,
2393                 mCarPropertyEventCallback);
2394 
2395         verify(mICarProperty, never()).unregisterListener(anyInt(), any());
2396     }
2397 
2398     @Test
testUnregisterCallback_ignoreUserHalProp_beforeU()2399     public void testUnregisterCallback_ignoreUserHalProp_beforeU()
2400             throws Exception {
2401         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
2402 
2403         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback, INITIAL_USER_INFO);
2404 
2405         verify(mICarProperty, never()).unregisterListener(anyInt(), any());
2406     }
2407 
2408     @Test
testUnregisterCallback_ignoreUserHalProp_afterU()2409     public void testUnregisterCallback_ignoreUserHalProp_afterU()
2410             throws Exception {
2411         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
2412 
2413         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback, INITIAL_USER_INFO);
2414 
2415         verify(mICarProperty, never()).unregisterListener(anyInt(), any());
2416     }
2417 
2418     @Test
testUnsubscribePropertyEvents_unsubscribeCallbackForSpecificProperty()2419     public void testUnsubscribePropertyEvents_unsubscribeCallbackForSpecificProperty()
2420             throws Exception {
2421         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2422                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2423                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build(),
2424                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY)
2425                                 .build()),
2426                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2427 
2428         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2429                 VENDOR_CONTINUOUS_PROPERTY);
2430         verify(mICarProperty).unregisterListener(eq(VENDOR_CONTINUOUS_PROPERTY),
2431                 any(ICarPropertyEventListener.class));
2432         verify(mICarProperty, never()).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2433                 any(ICarPropertyEventListener.class));
2434     }
2435 
2436     @Test
testUnregisterCallback_unregistersCallbackForBothProperties()2437     public void testUnregisterCallback_unregistersCallbackForBothProperties()
2438             throws Exception {
2439         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2440                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2441         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2442                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
2443 
2444         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback);
2445         verify(mICarProperty, times(2)).unregisterListener(mPropertyIdCaptor.capture(),
2446                 any(ICarPropertyEventListener.class));
2447         assertThat(mPropertyIdCaptor.getAllValues()).containsExactly(VENDOR_CONTINUOUS_PROPERTY,
2448                 VENDOR_ON_CHANGE_PROPERTY);
2449     }
2450 
2451     @Test
testUnsubscribePropertyEvents_unsubscribeCallbackForBothProperties()2452     public void testUnsubscribePropertyEvents_unsubscribeCallbackForBothProperties()
2453             throws Exception {
2454         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2455                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2456                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build(),
2457                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY)
2458                                 .setUpdateRateNormal().build()),
2459                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2460 
2461         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_CONTINUOUS_PROPERTY,
2462                 mCarPropertyEventCallback);
2463         verify(mICarProperty).unregisterListener(eq(VENDOR_CONTINUOUS_PROPERTY),
2464                 any(ICarPropertyEventListener.class));
2465     }
2466 
2467     @Test
testUnregisterCallback_unregistersAllCallbackForSingleProperty()2468     public void testUnregisterCallback_unregistersAllCallbackForSingleProperty()
2469             throws Exception {
2470         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2471             VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2472         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback2,
2473             VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
2474 
2475         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback);
2476         verify(mICarProperty).unregisterListener(eq(VENDOR_CONTINUOUS_PROPERTY),
2477                 any(ICarPropertyEventListener.class));
2478     }
2479 
2480     @Test
testUnsubscribePropertyEvents_unsubscribeAllCallbackForSingleProperty()2481     public void testUnsubscribePropertyEvents_unsubscribeAllCallbackForSingleProperty()
2482             throws Exception {
2483         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2484                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2485                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2486                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2487         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2488                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY)
2489                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2490                 /* callbackExecutor= */ null, mCarPropertyEventCallback2)).isTrue();
2491 
2492         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_CONTINUOUS_PROPERTY,
2493                 mCarPropertyEventCallback);
2494         verify(mICarProperty).unregisterListener(eq(VENDOR_CONTINUOUS_PROPERTY),
2495                 any(ICarPropertyEventListener.class));
2496         verify(mICarProperty, never()).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2497                 any(ICarPropertyEventListener.class));
2498     }
2499 
2500     @Test
testUnregisterCallback_registerCalledIfBiggerRateRemoved()2501     public void testUnregisterCallback_registerCalledIfBiggerRateRemoved() throws Exception {
2502         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2503                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2504         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback2,
2505                 VENDOR_CONTINUOUS_PROPERTY, LARGER_UPDATE_RATE_HZ)).isTrue();
2506 
2507         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback2);
2508         verify(mICarProperty, never()).unregisterListener(anyInt(),
2509                 any(ICarPropertyEventListener.class));
2510         verify(mICarProperty, times(3)).registerListener(
2511                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
2512         assertThat(mCarSubscriptionCaptor.getAllValues()).containsExactly(
2513                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2514                         FIRST_UPDATE_RATE_HZ)),
2515                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2516                         LARGER_UPDATE_RATE_HZ)),
2517                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2518                         FIRST_UPDATE_RATE_HZ))
2519         );
2520     }
2521 
2522     @Test
testUnsubscribePropertyEvents_registerCalledIfBiggerRateRemoved()2523     public void testUnsubscribePropertyEvents_registerCalledIfBiggerRateRemoved()
2524             throws Exception {
2525         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2526                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2527                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2528                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2529         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2530                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2531                                 .setUpdateRateHz(LARGER_UPDATE_RATE_HZ).build()),
2532                 /* callbackExecutor= */ null, mCarPropertyEventCallback2)).isTrue();
2533         clearInvocations(mICarProperty);
2534 
2535         mCarPropertyManager.unsubscribePropertyEvents(mCarPropertyEventCallback2);
2536 
2537         verify(mICarProperty, never()).unregisterListener(anyInt(),
2538                 any(ICarPropertyEventListener.class));
2539         verify(mICarProperty, times(1)).registerListener(
2540                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
2541         assertThat(mCarSubscriptionCaptor.getAllValues()).containsExactly(
2542                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2543                         FIRST_UPDATE_RATE_HZ, /* enableVur= */ true))
2544         );
2545     }
2546 
2547     @Test
testUnregisterCallback_registerNotCalledIfSmallerRateRemoved()2548     public void testUnregisterCallback_registerNotCalledIfSmallerRateRemoved()
2549             throws Exception {
2550         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2551                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2552         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback2,
2553                 VENDOR_CONTINUOUS_PROPERTY, LARGER_UPDATE_RATE_HZ)).isTrue();
2554 
2555         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback);
2556         verify(mICarProperty, never()).unregisterListener(anyInt(),
2557                 any(ICarPropertyEventListener.class));
2558         verify(mICarProperty, times(2)).registerListener(
2559                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
2560         assertThat(mCarSubscriptionCaptor.getAllValues()).containsExactly(
2561                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2562                         FIRST_UPDATE_RATE_HZ)),
2563                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2564                         LARGER_UPDATE_RATE_HZ)));
2565     }
2566 
2567     @Test
testUnsubscribePropertyEvents_subscribeNotCalledIfSmallerRateremoved()2568     public void testUnsubscribePropertyEvents_subscribeNotCalledIfSmallerRateremoved()
2569             throws Exception {
2570         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2571                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2572                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2573                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2574         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2575                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2576                                 .setUpdateRateHz(LARGER_UPDATE_RATE_HZ).build()),
2577                 /* callbackExecutor= */ null, mCarPropertyEventCallback2)).isTrue();
2578         clearInvocations(mICarProperty);
2579 
2580         mCarPropertyManager.unsubscribePropertyEvents(mCarPropertyEventCallback2);
2581 
2582         verify(mICarProperty, never()).unregisterListener(anyInt(),
2583                 any(ICarPropertyEventListener.class));
2584     }
2585 
2586     @Test
testUnregisterCallback_doesNothingWithPropertyIdIfNothingRegistered()2587     public void testUnregisterCallback_doesNothingWithPropertyIdIfNothingRegistered()
2588             throws Exception {
2589         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback);
2590         verify(mICarProperty, never()).unregisterListener(anyInt(),
2591                 any(ICarPropertyEventListener.class));
2592     }
2593 
2594     @Test
testUnsubscribePropertyEvents_doesNothingWithPropertyIdIfNothingRegistered()2595     public void testUnsubscribePropertyEvents_doesNothingWithPropertyIdIfNothingRegistered()
2596             throws Exception {
2597         mCarPropertyManager.unsubscribePropertyEvents(mCarPropertyEventCallback);
2598         verify(mICarProperty, never()).unregisterListener(anyInt(),
2599                 any(ICarPropertyEventListener.class));
2600     }
2601 
2602     @Test
testUnregisterCallback_returnsVoidForIllegalArgumentException()2603     public void testUnregisterCallback_returnsVoidForIllegalArgumentException()
2604             throws RemoteException {
2605         doThrow(IllegalArgumentException.class).when(mICarProperty).unregisterListener(
2606                 eq(VENDOR_ON_CHANGE_PROPERTY), any(ICarPropertyEventListener.class));
2607 
2608         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2609                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
2610         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2611                 VENDOR_ON_CHANGE_PROPERTY);
2612 
2613         verify(mICarProperty).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2614                 any(ICarPropertyEventListener.class));
2615     }
2616 
2617     @Test
testUnsubscribePropertyEvents_returnsVoidForIllegalArgumentException()2618     public void testUnsubscribePropertyEvents_returnsVoidForIllegalArgumentException()
2619             throws Exception {
2620         doThrow(IllegalArgumentException.class).when(mICarProperty).unregisterListener(
2621                 eq(VENDOR_ON_CHANGE_PROPERTY), any(ICarPropertyEventListener.class));
2622 
2623         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2624                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).build()),
2625                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2626         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2627                 VENDOR_ON_CHANGE_PROPERTY);
2628 
2629         verify(mICarProperty).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2630                 any(ICarPropertyEventListener.class));
2631     }
2632 
2633     @Test
testUnregisterCallback_SecurityException()2634     public void testUnregisterCallback_SecurityException() throws Exception {
2635         doThrow(SecurityException.class).when(mICarProperty).unregisterListener(
2636                 eq(VENDOR_ON_CHANGE_PROPERTY), any(ICarPropertyEventListener.class));
2637 
2638         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2639                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
2640         assertThrows(SecurityException.class,
2641                 () -> mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2642                         VENDOR_ON_CHANGE_PROPERTY));
2643     }
2644 
2645     @Test
testUnsubscribePropertyEvents_SecurityException()2646     public void testUnsubscribePropertyEvents_SecurityException() throws Exception {
2647         doThrow(SecurityException.class).when(mICarProperty).unregisterListener(
2648                 eq(VENDOR_ON_CHANGE_PROPERTY), any(ICarPropertyEventListener.class));
2649 
2650         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2651                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).build()),
2652                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2653 
2654         assertThrows(SecurityException.class,
2655                 () -> mCarPropertyManager.unsubscribePropertyEvents(VENDOR_ON_CHANGE_PROPERTY,
2656                         mCarPropertyEventCallback));
2657     }
2658 
2659     @Test
testUnregisterCallback_recoversAfterSecurityException()2660     public void testUnregisterCallback_recoversAfterSecurityException() throws Exception {
2661         doThrow(SecurityException.class).doNothing().when(mICarProperty).unregisterListener(
2662                 eq(VENDOR_ON_CHANGE_PROPERTY), any(ICarPropertyEventListener.class));
2663 
2664         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2665                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
2666         assertThrows(SecurityException.class,
2667                 () -> mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2668                         VENDOR_ON_CHANGE_PROPERTY));
2669 
2670         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2671                 VENDOR_ON_CHANGE_PROPERTY);
2672 
2673         verify(mICarProperty, times(2)).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2674                 any(ICarPropertyEventListener.class));
2675     }
2676 
2677     @Test
testUnsubscribePropertyEvents_recoversAfterSecurityException()2678     public void testUnsubscribePropertyEvents_recoversAfterSecurityException() throws Exception {
2679         doThrow(SecurityException.class).doNothing().when(mICarProperty).unregisterListener(
2680                 eq(VENDOR_ON_CHANGE_PROPERTY), any(ICarPropertyEventListener.class));
2681         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2682                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).build()),
2683                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2684 
2685         assertThrows(SecurityException.class,
2686                 () -> mCarPropertyManager.unsubscribePropertyEvents(VENDOR_ON_CHANGE_PROPERTY,
2687                         mCarPropertyEventCallback));
2688         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_ON_CHANGE_PROPERTY,
2689                 mCarPropertyEventCallback);
2690 
2691         verify(mICarProperty, times(2)).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2692                 any(ICarPropertyEventListener.class));
2693     }
2694 
2695     @Test
testOnErrorEvent_callbackIsCalledWithErrorEvent()2696     public void testOnErrorEvent_callbackIsCalledWithErrorEvent() throws RemoteException {
2697         List<CarPropertyEvent> eventList = createErrorCarPropertyEventList();
2698         CarPropertyConfig config = CarPropertyConfig.newBuilder(Float.class, HVAC_TEMPERATURE_SET,
2699                 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
2700                 .addAreaIdConfig(new AreaIdConfig.Builder<Float>(0).build()).build();
2701         addCarPropertyConfig(config);
2702         ICarPropertyEventListener listener = getCarPropertyEventListener();
2703 
2704         listener.onEvent(eventList);
2705 
2706         // Wait until we get the on error event for the initial value.
2707         verify(mCarPropertyEventCallback, timeout(5000)).onErrorEvent(HVAC_TEMPERATURE_SET, 0,
2708                 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN);
2709     }
2710 
2711     @Test
testOnChangeEvent_callbackIsCalledWithEvent()2712     public void testOnChangeEvent_callbackIsCalledWithEvent() throws RemoteException {
2713         List<CarPropertyEvent> eventList = createCarPropertyEventList();
2714         CarPropertyConfig config = CarPropertyConfig.newBuilder(Float.class, HVAC_TEMPERATURE_SET,
2715                 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
2716                 .addAreaIdConfig(new AreaIdConfig.Builder<Float>(0).build()).build();
2717         addCarPropertyConfig(config);
2718         ICarPropertyEventListener listener = getCarPropertyEventListener();
2719         ArgumentCaptor<CarPropertyValue> value = ArgumentCaptor.forClass(CarPropertyValue.class);
2720 
2721         listener.onEvent(eventList);
2722 
2723         // Wait until we get the on property change event for the initial value.
2724         verify(mCarPropertyEventCallback, timeout(5000)).onChangeEvent(value.capture());
2725         assertThat(value.getValue().getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
2726         assertThat(value.getValue().getValue()).isEqualTo(17.0f);
2727     }
2728 
getCarPropertyEventListener()2729     private ICarPropertyEventListener getCarPropertyEventListener() throws RemoteException {
2730         ArgumentCaptor<ICarPropertyEventListener> carPropertyEventListenerArgumentCaptor =
2731                 ArgumentCaptor.forClass(ICarPropertyEventListener.class);
2732         mCarPropertyManager.registerCallback(mCarPropertyEventCallback, HVAC_TEMPERATURE_SET,
2733                 SENSOR_RATE_ONCHANGE);
2734 
2735         verify(mICarProperty).registerListener(eq(List.of(
2736                 createCarSubscriptionOption(HVAC_TEMPERATURE_SET, new int[]{0},
2737                         SENSOR_RATE_ONCHANGE))),
2738                 carPropertyEventListenerArgumentCaptor.capture());
2739 
2740         return carPropertyEventListenerArgumentCaptor.getValue();
2741     }
2742 
2743     @Test
testGetPropertyList()2744     public void testGetPropertyList() throws Exception {
2745         List<CarPropertyConfig> expectedConfigs = mock(List.class);
2746         when(mICarProperty.getPropertyList())
2747                 .thenReturn(new CarPropertyConfigList(expectedConfigs));
2748 
2749 
2750         assertThat(mCarPropertyManager.getPropertyList()).isEqualTo(expectedConfigs);
2751     }
2752 
2753     @Test
testGetPropertyList_withPropertyIds()2754     public void testGetPropertyList_withPropertyIds() throws Exception {
2755         ArraySet<Integer> requestedPropertyIds = new ArraySet<>(Set.of(
2756                 VENDOR_CONTINUOUS_PROPERTY, HVAC_TEMPERATURE_SET));
2757         CarPropertyConfig config1 = CarPropertyConfig.newBuilder(Integer.class,
2758                 VENDOR_CONTINUOUS_PROPERTY,
2759                 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
2760                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(0).build()).build();
2761         CarPropertyConfig config2 = CarPropertyConfig.newBuilder(Float.class,
2762                 HVAC_TEMPERATURE_SET,
2763                 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
2764                 .addAreaIdConfig(new AreaIdConfig.Builder<Float>(0).build()).build();
2765         addCarPropertyConfig(config1);
2766         addCarPropertyConfig(config2);
2767 
2768         assertThat(mCarPropertyManager.getPropertyList(requestedPropertyIds))
2769                 .containsExactly(config1, config2);
2770     }
2771 
2772     @Test
testGetPropertyList_filterUnsupportedPropertyIds()2773     public void testGetPropertyList_filterUnsupportedPropertyIds() throws Exception {
2774         ArraySet<Integer> requestedPropertyIds = new ArraySet<>(Set.of(
2775                 0, 1, VENDOR_CONTINUOUS_PROPERTY, HVAC_TEMPERATURE_SET));
2776         CarPropertyConfig config1 = CarPropertyConfig.newBuilder(Integer.class,
2777                 VENDOR_CONTINUOUS_PROPERTY,
2778                 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
2779                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(0).build()).build();
2780         CarPropertyConfig config2 = CarPropertyConfig.newBuilder(Float.class,
2781                 HVAC_TEMPERATURE_SET,
2782                 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
2783                 .addAreaIdConfig(new AreaIdConfig.Builder<Float>(0).build()).build();
2784         addCarPropertyConfig(config1);
2785         addCarPropertyConfig(config2);
2786 
2787         assertThat(mCarPropertyManager.getPropertyList(requestedPropertyIds))
2788                 .containsExactly(config1, config2);
2789     }
2790 
2791     @Test
testGetCarPropertyConfig()2792     public void testGetCarPropertyConfig() throws Exception {
2793         assertThat(mCarPropertyManager.getCarPropertyConfig(VENDOR_ON_CHANGE_PROPERTY))
2794                 .isEqualTo(mOnChangeCarPropertyConfig);
2795     }
2796 
2797     @Test
testGetCarPropertyConfig_noConfigReturned_notSupported()2798     public void testGetCarPropertyConfig_noConfigReturned_notSupported() throws Exception {
2799         setPropIdWithoutConfig(HVAC_TEMPERATURE_SET);
2800 
2801         assertThat(mCarPropertyManager.getCarPropertyConfig(HVAC_TEMPERATURE_SET)).isNull();
2802     }
2803 
2804     @Test
testGetCarPropertyConfig_noConfigReturned_noPermission()2805     public void testGetCarPropertyConfig_noConfigReturned_noPermission() throws Exception {
2806         setPropIdWithoutPermission(HVAC_TEMPERATURE_SET);
2807 
2808         assertThat(mCarPropertyManager.getCarPropertyConfig(HVAC_TEMPERATURE_SET)).isNull();
2809     }
2810 
2811     @Test
testGetCarPropertyConfig_unsupported()2812     public void testGetCarPropertyConfig_unsupported() throws Exception {
2813         assertThat(mCarPropertyManager.getCarPropertyConfig(/* propId= */ 0)).isNull();
2814     }
2815 
2816     @Test
testGetAreaId_global()2817     public void testGetAreaId_global() throws Exception {
2818         assertThat(mCarPropertyManager.getAreaId(VENDOR_ON_CHANGE_PROPERTY, 0)).isEqualTo(0);
2819     }
2820 
2821     @Test
testGetAreaId_withArea()2822     public void testGetAreaId_withArea() throws Exception {
2823         int areaId = VehicleAreaSeat.SEAT_ROW_1_LEFT | VehicleAreaSeat.SEAT_ROW_1_CENTER;
2824         CarPropertyConfig config1 = CarPropertyConfig.newBuilder(Integer.class,
2825                 HVAC_TEMPERATURE_SET,
2826                 VehicleAreaType.VEHICLE_AREA_TYPE_SEAT)
2827                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId)
2828                 .build()).build();
2829         addCarPropertyConfig(config1);
2830 
2831         assertThat(mCarPropertyManager.getAreaId(
2832                 HVAC_TEMPERATURE_SET, VehicleAreaSeat.SEAT_ROW_1_LEFT)).isEqualTo(areaId);
2833         assertThat(mCarPropertyManager.getAreaId(
2834                 HVAC_TEMPERATURE_SET, VehicleAreaSeat.SEAT_ROW_1_CENTER)).isEqualTo(areaId);
2835     }
2836 
2837     @Test
testGetAreaId_areaNotSupported()2838     public void testGetAreaId_areaNotSupported() throws Exception {
2839         int areaId = VehicleAreaSeat.SEAT_ROW_1_LEFT | VehicleAreaSeat.SEAT_ROW_1_CENTER;
2840         CarPropertyConfig config1 = CarPropertyConfig.newBuilder(Integer.class,
2841                 HVAC_TEMPERATURE_SET,
2842                 VehicleAreaType.VEHICLE_AREA_TYPE_SEAT)
2843                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId)
2844                 .build()).build();
2845         addCarPropertyConfig(config1);
2846 
2847         assertThrows(IllegalArgumentException.class, () -> mCarPropertyManager.getAreaId(
2848                 HVAC_TEMPERATURE_SET, VehicleAreaSeat.SEAT_ROW_1_RIGHT));
2849     }
2850 
2851     @Test
testGetAreaId_propertyNotSupported()2852     public void testGetAreaId_propertyNotSupported() throws Exception {
2853         setPropIdWithoutConfig(HVAC_TEMPERATURE_SET);
2854 
2855         assertThrows(IllegalArgumentException.class, () -> mCarPropertyManager.getAreaId(
2856                 HVAC_TEMPERATURE_SET, 0));
2857     }
2858 
2859     @Test
testGetAreaId_noPermissionToProperty()2860     public void testGetAreaId_noPermissionToProperty() throws Exception {
2861         setPropIdWithoutPermission(HVAC_TEMPERATURE_SET);
2862 
2863         assertThrows(IllegalArgumentException.class, () -> mCarPropertyManager.getAreaId(
2864                 HVAC_TEMPERATURE_SET, 0));
2865     }
2866 
2867     @Test
testGetAreaId_remoteExceptionFromCarService()2868     public void testGetAreaId_remoteExceptionFromCarService() throws Exception {
2869         when(mICarProperty.getPropertyConfigList(new int[]{HVAC_TEMPERATURE_SET}))
2870                 .thenThrow(new RemoteException());
2871 
2872         assertThrows(IllegalArgumentException.class, () -> mCarPropertyManager.getAreaId(
2873                 HVAC_TEMPERATURE_SET, 0));
2874     }
2875 
2876     @Test
testIsPropertyAvailable_withValueWithNotAvailableStatus()2877     public void testIsPropertyAvailable_withValueWithNotAvailableStatus() throws Exception {
2878         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
2879                 CarPropertyValue.STATUS_ERROR, TEST_TIMESTAMP, 17.0f);
2880         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
2881 
2882         assertThat(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_SET, /* areaId= */ 0))
2883                 .isFalse();
2884     }
2885 
2886     @Test
testIsPropertyAvailable()2887     public void testIsPropertyAvailable() throws Exception {
2888         CarPropertyValue<Integer> expectedValue = new CarPropertyValue<>(HVAC_TEMPERATURE_SET,
2889                 /* areaId= */ 0, CarPropertyValue.STATUS_AVAILABLE, /* timestamp= */ 0,
2890                 /* value= */ 1);
2891         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(expectedValue);
2892 
2893         assertThat(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_SET, /* areaId= */ 0))
2894                 .isTrue();
2895     }
2896 
2897     @Test
testIsPropertyAvailable_syncOpTryAgain()2898     public void testIsPropertyAvailable_syncOpTryAgain() throws Exception {
2899         CarPropertyValue<Integer> expectedValue = new CarPropertyValue<>(HVAC_TEMPERATURE_SET,
2900                 /* areaId= */ 0, CarPropertyValue.STATUS_AVAILABLE, /* timestamp= */ 0,
2901                 /* value= */ 1);
2902         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
2903                 new ServiceSpecificException(SYNC_OP_LIMIT_TRY_AGAIN)).thenReturn(expectedValue);
2904 
2905         assertThat(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_SET, /* areaId= */ 0))
2906                 .isTrue();
2907         verify(mICarProperty, times(2)).getProperty(HVAC_TEMPERATURE_SET, 0);
2908     }
2909 
2910     @Test
testIsPropertyAvailable_notAvailable()2911     public void testIsPropertyAvailable_notAvailable() throws Exception {
2912         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
2913                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE));
2914 
2915         assertThat(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_SET, /* areaId= */ 0))
2916                 .isFalse();
2917     }
2918 
2919     @Test
testIsPropertyAvailable_tryAgain()2920     public void testIsPropertyAvailable_tryAgain() throws Exception {
2921         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
2922                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
2923 
2924         assertThat(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_SET, /* areaId= */ 0))
2925                 .isFalse();
2926     }
2927 
2928 
2929     @Test
testIsPropertyAvailable_RemoteException()2930     public void testIsPropertyAvailable_RemoteException() throws Exception {
2931         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
2932                 new RemoteException());
2933 
2934         assertThat(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_SET, /* areaId= */ 0))
2935                 .isFalse();
2936     }
2937 
2938     @Test
testIsPropertyAvailable_unsupported()2939     public void testIsPropertyAvailable_unsupported() throws Exception {
2940         assertThat(mCarPropertyManager.isPropertyAvailable(/* propId= */ 0, /* areaId= */ 0))
2941                 .isFalse();
2942     }
2943 
2944     @Test
testGetCarPropertyConfig_userHalProperty()2945     public void testGetCarPropertyConfig_userHalProperty() throws Exception {
2946         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
2947 
2948         assertThrows(IllegalArgumentException.class,
2949                 () -> mCarPropertyManager.getCarPropertyConfig(INITIAL_USER_INFO));
2950     }
2951 
2952     @Test
testGetPropertyList_userHalProperty()2953     public void testGetPropertyList_userHalProperty() throws Exception {
2954         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
2955 
2956         assertThrows(IllegalArgumentException.class,
2957                 () -> mCarPropertyManager.getPropertyList(
2958                         new ArraySet<Integer>(Set.of(HVAC_TEMPERATURE_SET, INITIAL_USER_INFO))));
2959     }
2960 
2961     @Test
testGetAreaId_userHalProperty()2962     public void testGetAreaId_userHalProperty()  throws Exception {
2963         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
2964 
2965         assertThrows(IllegalArgumentException.class,
2966                 () -> mCarPropertyManager.getAreaId(INITIAL_USER_INFO, /* area= */ 0));
2967     }
2968 
2969     @Test
testGetReadPermission_userHalProperty()2970     public void testGetReadPermission_userHalProperty() throws Exception {
2971         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
2972 
2973         assertThrows(IllegalArgumentException.class,
2974                 () -> mCarPropertyManager.getReadPermission(INITIAL_USER_INFO));
2975     }
2976 
2977     @Test
testGetWritePermission_userHalProperty()2978     public void testGetWritePermission_userHalProperty() throws Exception {
2979         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
2980 
2981         assertThrows(IllegalArgumentException.class,
2982                 () -> mCarPropertyManager.getWritePermission(INITIAL_USER_INFO));
2983     }
2984 
2985     @Test
testIsPropertyAvailable_userHalProperty()2986     public void testIsPropertyAvailable_userHalProperty() throws Exception {
2987         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
2988 
2989         assertThrows(IllegalArgumentException.class,
2990                 () -> mCarPropertyManager.isPropertyAvailable(INITIAL_USER_INFO, /* areaId= */ 0));
2991     }
2992 
2993     @Test
testGetProperty_userHalProperty()2994     public void testGetProperty_userHalProperty() throws Exception {
2995         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
2996 
2997         assertThrows(IllegalArgumentException.class,
2998                 () -> mCarPropertyManager.getProperty(INITIAL_USER_INFO, /* areaId= */ 0));
2999     }
3000 
3001     @Test
testGetBooleanProperty_userHalProperty()3002     public void testGetBooleanProperty_userHalProperty() throws Exception {
3003         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
3004 
3005         assertThrows(IllegalArgumentException.class,
3006                 () -> mCarPropertyManager.getBooleanProperty(INITIAL_USER_INFO, /* areaId= */ 0));
3007     }
3008 
3009     @Test
testSetBooleanProperty_userHalProperty()3010     public void testSetBooleanProperty_userHalProperty() throws Exception {
3011         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
3012 
3013         assertThrows(IllegalArgumentException.class,
3014                 () -> mCarPropertyManager.setBooleanProperty(INITIAL_USER_INFO, /* areaId= */ 0,
3015                         true));
3016     }
3017 }
3018