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 import static com.android.car.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
34 
35 import static com.google.common.truth.Truth.assertThat;
36 import static com.google.common.truth.Truth.assertWithMessage;
37 
38 import static org.junit.Assert.assertThrows;
39 import static org.mockito.ArgumentMatchers.any;
40 import static org.mockito.ArgumentMatchers.anyInt;
41 import static org.mockito.ArgumentMatchers.anyLong;
42 import static org.mockito.ArgumentMatchers.eq;
43 import static org.mockito.Mockito.clearInvocations;
44 import static org.mockito.Mockito.doAnswer;
45 import static org.mockito.Mockito.doNothing;
46 import static org.mockito.Mockito.doThrow;
47 import static org.mockito.Mockito.mock;
48 import static org.mockito.Mockito.never;
49 import static org.mockito.Mockito.timeout;
50 import static org.mockito.Mockito.times;
51 import static org.mockito.Mockito.verify;
52 import static org.mockito.Mockito.when;
53 
54 import android.car.VehicleAreaSeat;
55 import android.car.VehicleAreaType;
56 import android.car.VehiclePropertyIds;
57 import android.car.feature.FeatureFlags;
58 import android.car.feature.Flags;
59 import android.car.hardware.CarPropertyConfig;
60 import android.car.hardware.CarPropertyValue;
61 import android.car.test.AbstractExpectableTestCase;
62 import android.content.Context;
63 import android.content.pm.ApplicationInfo;
64 import android.os.Build;
65 import android.os.CancellationSignal;
66 import android.os.Handler;
67 import android.os.Looper;
68 import android.os.RemoteException;
69 import android.os.ServiceSpecificException;
70 import android.platform.test.annotations.EnableFlags;
71 import android.platform.test.annotations.IgnoreUnderRavenwood;
72 import android.platform.test.flag.junit.SetFlagsRule;
73 import android.platform.test.ravenwood.RavenwoodRule;
74 import android.util.ArraySet;
75 import android.util.SparseArray;
76 
77 import com.android.car.internal.ICarBase;
78 import com.android.car.internal.property.AsyncPropertyServiceRequest;
79 import com.android.car.internal.property.AsyncPropertyServiceRequestList;
80 import com.android.car.internal.property.CarPropertyConfigList;
81 import com.android.car.internal.property.CarPropertyErrorCodes;
82 import com.android.car.internal.property.CarSubscription;
83 import com.android.car.internal.property.GetPropertyConfigListResult;
84 import com.android.car.internal.property.GetSetValueResult;
85 import com.android.car.internal.property.GetSetValueResultList;
86 import com.android.car.internal.property.IAsyncPropertyResultCallback;
87 import com.android.car.internal.property.ISupportedValuesChangeCallback;
88 import com.android.car.internal.property.MinMaxSupportedPropertyValue;
89 import com.android.car.internal.property.PropIdAreaId;
90 import com.android.car.internal.property.RawPropertyValue;
91 import com.android.car.internal.util.IntArray;
92 
93 import org.junit.Before;
94 import org.junit.Rule;
95 import org.junit.Test;
96 import org.junit.runner.RunWith;
97 import org.mockito.ArgumentCaptor;
98 import org.mockito.Captor;
99 import org.mockito.Mock;
100 import org.mockito.junit.MockitoJUnitRunner;
101 
102 import java.time.Duration;
103 import java.util.ArrayList;
104 import java.util.List;
105 import java.util.Set;
106 import java.util.concurrent.Executor;
107 
108 /**
109  * <p>This class contains unit tests for the {@link CarPropertyManager}.
110  */
111 @RunWith(MockitoJUnitRunner.class)
112 public final class CarPropertyManagerUnitTest extends AbstractExpectableTestCase {
113     // Required to set the process ID and set the "main" thread for this test, otherwise
114     // getMainLooper will return null.
115     @Rule
116     public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
117             .setProcessApp()
118             .setProvideMainThread(true)
119             .build();
120 
121     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
122 
123     // Defined as vendor property.
124     private static final int VENDOR_CONTINUOUS_PROPERTY = 0x21100111;
125     private static final int VENDOR_ON_CHANGE_PROPERTY = 0x21100222;
126     private static final int VENDOR_STATIC_PROPERTY = 0x21100333;
127 
128     private static final int BOOLEAN_PROP = FUEL_DOOR_OPEN;
129     private static final int INT32_PROP = INFO_FUEL_DOOR_LOCATION;
130     private static final int INT32_VEC_PROP = INFO_EV_CONNECTOR_TYPE;
131     private static final int FLOAT_PROP = HVAC_TEMPERATURE_SET;
132 
133     private static final float MIN_UPDATE_RATE_HZ = 10;
134     private static final float MAX_UPDATE_RATE_HZ = 100;
135     private static final float FIRST_UPDATE_RATE_HZ = 50;
136     private static final float LARGER_UPDATE_RATE_HZ = 50.1f;
137     private static final float SMALLER_UPDATE_RATE_HZ = 49.9f;
138 
139     private static final int VENDOR_ERROR_CODE = 0x2;
140     private static final int VENDOR_ERROR_CODE_SHIFT = 16;
141     private static final int UNKNOWN_ERROR = -101;
142 
143     private static final long TEST_TIMESTAMP = 1234;
144 
145     private Handler mMainHandler;
146 
147     private CarPropertyConfig mContinuousCarPropertyConfig;
148     private CarPropertyConfig mOnChangeCarPropertyConfig;
149     private CarPropertyConfig mStaticCarPropertyConfig;
150     private final SparseArray<CarPropertyConfig> mCarPropertyConfigsById = new SparseArray<>();
151     private final ArraySet<Integer> mUnsupportedPropIds = new ArraySet<>();
152     private final ArraySet<Integer> mMissingPermissionPropIds = new ArraySet<>();
153 
154     @Mock
155     private ICarBase mCar;
156     @Mock
157     private ApplicationInfo mApplicationInfo;
158     @Mock
159     private ICarProperty mICarProperty;
160     @Mock
161     private Context mContext;
162     @Mock
163     private CarPropertyManager.CarPropertyEventCallback mCarPropertyEventCallback;
164     @Mock
165     private CarPropertyManager.CarPropertyEventCallback mCarPropertyEventCallback2;
166     @Mock
167     private CarPropertyManager.GetPropertyCallback mGetPropertyCallback;
168     @Mock
169     private CarPropertyManager.SetPropertyCallback mSetPropertyCallback;
170     @Mock
171     private Executor mMockExecutor1;
172     @Mock
173     private Executor mMockExecutor2;
174     @Mock
175     private FeatureFlags mFeatureFlags;
176     @Mock
177     private CarPropertyManager.SupportedValuesChangeCallback mSupportedValuesChangeCallback;
178     @Mock
179     private CarPropertyManager.SupportedValuesChangeCallback mSupportedValuesChangeCallback2;
180 
181     @Captor
182     private ArgumentCaptor<Integer> mPropertyIdCaptor;
183     @Captor
184     private ArgumentCaptor<List> mCarSubscriptionCaptor;
185     @Captor
186     private ArgumentCaptor<AsyncPropertyServiceRequestList> mAsyncPropertyServiceRequestCaptor;
187     @Captor
188     private ArgumentCaptor<PropertyAsyncError> mPropertyAsyncErrorCaptor;
189     @Captor
190     private ArgumentCaptor<GetPropertyResult<?>> mGetPropertyResultCaptor;
191     @Captor
192     private ArgumentCaptor<SetPropertyResult> mSetPropertyResultCaptor;
193     private CarPropertyManager mCarPropertyManager;
194 
combineErrors(int systemError, int vendorError)195     private static int combineErrors(int systemError, int vendorError) {
196         return vendorError << VENDOR_ERROR_CODE_SHIFT | systemError;
197     }
198 
createErrorCarPropertyEventList()199     private static List<CarPropertyEvent> createErrorCarPropertyEventList() {
200         CarPropertyValue<Integer> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
201                 CarPropertyValue.STATUS_AVAILABLE, 0, -1);
202         CarPropertyEvent carPropertyEvent = new CarPropertyEvent(
203                 CarPropertyEvent.PROPERTY_EVENT_ERROR, value,
204                 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN);
205         return List.of(carPropertyEvent);
206     }
207 
createCarPropertyEventList()208     private static List<CarPropertyEvent> createCarPropertyEventList() {
209         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0, 17.0f);
210         CarPropertyEvent carPropertyEvent = new CarPropertyEvent(
211                 CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, value);
212         return List.of(carPropertyEvent);
213     }
214 
createCarSubscriptionOption(int propertyId, int[] areaId, float updateRateHz)215     private static CarSubscription createCarSubscriptionOption(int propertyId,
216             int[] areaId, float updateRateHz) {
217         return createCarSubscriptionOption(propertyId, areaId, updateRateHz,
218                 /* enableVur= */ false);
219     }
220 
createCarSubscriptionOption(int propertyId, int[] areaId, float updateRateHz, boolean enableVur)221     private static CarSubscription createCarSubscriptionOption(int propertyId,
222             int[] areaId, float updateRateHz, boolean enableVur) {
223         CarSubscription options = new CarSubscription();
224         options.propertyId = propertyId;
225         options.areaIds = areaId;
226         options.updateRateHz = updateRateHz;
227         options.enableVariableUpdateRate = enableVur;
228         return options;
229     }
230 
addCarPropertyConfig(CarPropertyConfig config)231     private void addCarPropertyConfig(CarPropertyConfig config) {
232         mCarPropertyConfigsById.put(config.getPropertyId(), config);
233     }
234 
setPropIdWithoutPermission(int propId)235     private void setPropIdWithoutPermission(int propId) {
236         mCarPropertyConfigsById.remove(propId);
237         mMissingPermissionPropIds.add(propId);
238     }
239 
setPropIdWithoutConfig(int propId)240     private void setPropIdWithoutConfig(int propId) {
241         mCarPropertyConfigsById.remove(propId);
242         mUnsupportedPropIds.add(propId);
243     }
244 
245     @Before
setUp()246     public void setUp() throws RemoteException {
247         mMainHandler = new Handler(Looper.getMainLooper());
248         when(mCar.getContext()).thenReturn(mContext);
249         when(mCar.getEventHandler()).thenReturn(mMainHandler);
250         when(mCar.handleRemoteExceptionFromCarService(any(RemoteException.class), any()))
251                 .thenAnswer((inv) -> {
252                     return inv.getArgument(1);
253                 });
254 
255         mApplicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
256         when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo);
257 
258         mContinuousCarPropertyConfig = CarPropertyConfig.newBuilder(Integer.class,
259                 VENDOR_CONTINUOUS_PROPERTY, VEHICLE_AREA_TYPE_GLOBAL)
260                 .setChangeMode(CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS)
261                 .setMinSampleRate(MIN_UPDATE_RATE_HZ)
262                 .setMaxSampleRate(MAX_UPDATE_RATE_HZ)
263                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(0)
264                         .setSupportVariableUpdateRate(true).build())
265                 .build();
266         mOnChangeCarPropertyConfig = CarPropertyConfig.newBuilder(Integer.class,
267                 VENDOR_ON_CHANGE_PROPERTY, VEHICLE_AREA_TYPE_GLOBAL)
268                 .setChangeMode(CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE)
269                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(0).build())
270                 .build();
271         mStaticCarPropertyConfig = CarPropertyConfig.newBuilder(Integer.class,
272                 VENDOR_STATIC_PROPERTY, VEHICLE_AREA_TYPE_GLOBAL)
273                 .setChangeMode(CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_STATIC)
274                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(0).build())
275                 .build();
276 
277         when(mICarProperty.getPropertyConfigList(any())).thenAnswer((invocation) -> {
278             Object[] args = invocation.getArguments();
279             int[] propIds = (int[]) args[0];
280             GetPropertyConfigListResult result = new GetPropertyConfigListResult();
281             IntArray unsupportedPropIds = new IntArray();
282             IntArray missingPermissionPropIds = new IntArray();
283             List<CarPropertyConfig> configs = new ArrayList<>();
284             for (int propId : propIds) {
285                 if (mUnsupportedPropIds.contains(propId)) {
286                     unsupportedPropIds.add(propId);
287                     continue;
288                 }
289                 if (mMissingPermissionPropIds.contains(propId)) {
290                     missingPermissionPropIds.add(propId);
291                     continue;
292                 }
293                 var config = mCarPropertyConfigsById.get(propId);
294                 if (config == null) {
295                     unsupportedPropIds.add(propId);
296                     continue;
297                 }
298                 configs.add(config);
299             }
300 
301             result.carPropertyConfigList = new CarPropertyConfigList(configs);
302             result.unsupportedPropIds = unsupportedPropIds.toArray();
303             result.missingPermissionPropIds = missingPermissionPropIds.toArray();
304             return result;
305         });
306 
307         addCarPropertyConfig(mContinuousCarPropertyConfig);
308         addCarPropertyConfig(mOnChangeCarPropertyConfig);
309         addCarPropertyConfig(mStaticCarPropertyConfig);
310         when(mICarProperty.getSupportedNoReadPermPropIds(any())).thenReturn(new int[0]);
311         mCarPropertyManager = new CarPropertyManager(mCar, mICarProperty);
312         // Enable the features.
313         when(mFeatureFlags.variableUpdateRate()).thenReturn(true);
314         when(mFeatureFlags.handlePropertyEventsInBinderThread()).thenReturn(true);
315         when(mFeatureFlags.alwaysSendInitialValueEvent()).thenReturn(true);
316 
317         mCarPropertyManager.setFeatureFlags(mFeatureFlags);
318     }
319 
320     @Test
testGetProperty_returnsValue()321     public void testGetProperty_returnsValue() throws RemoteException {
322         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0, 17.0f);
323 
324         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
325 
326         assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0)).isEqualTo(value);
327     }
328 
329     @Test
testGetProperty_unsupportedProperty_exceptionAtU()330     public void testGetProperty_unsupportedProperty_exceptionAtU() throws Exception {
331         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
332 
333         assertThrows(IllegalArgumentException.class,
334                 () -> mCarPropertyManager.getProperty(INVALID, 0));
335     }
336 
337     @Test
testGetProperty_unsupportedProperty_nullBeforeU()338     public void testGetProperty_unsupportedProperty_nullBeforeU() throws Exception {
339         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
340 
341         assertThat(mCarPropertyManager.getProperty(INVALID, 0)).isNull();
342     }
343 
344     @Test
testGetProperty_unsupportedPropertyInSvc_exceptionAtU()345     public void testGetProperty_unsupportedPropertyInSvc_exceptionAtU() throws Exception {
346         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
347         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
348                         new IllegalArgumentException());
349 
350         assertThrows(IllegalArgumentException.class,
351                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
352     }
353 
354     @Test
testGetProperty_unsupportedPropertyInSvc_nullBeforeU()355     public void testGetProperty_unsupportedPropertyInSvc_nullBeforeU() throws Exception {
356         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
357         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
358                         new IllegalArgumentException());
359 
360         assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0)).isNull();
361     }
362 
363     @Test
testGetProperty_syncOpTryAgain()364     public void testGetProperty_syncOpTryAgain() throws RemoteException {
365         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0, 17.0f);
366 
367         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
368                 new ServiceSpecificException(SYNC_OP_LIMIT_TRY_AGAIN)).thenReturn(value);
369 
370         assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0)).isEqualTo(value);
371         verify(mICarProperty, times(2)).getProperty(HVAC_TEMPERATURE_SET, 0);
372     }
373 
374     @Test
testGetProperty_syncOpTryAgain_exceedRetryCountLimit()375     public void testGetProperty_syncOpTryAgain_exceedRetryCountLimit() throws RemoteException {
376         // Car service will throw CarInternalException with version >= R.
377         setAppTargetSdk(Build.VERSION_CODES.R);
378 
379         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
380                 new ServiceSpecificException(SYNC_OP_LIMIT_TRY_AGAIN));
381 
382         assertThrows(CarInternalErrorException.class, () ->
383                 mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
384         verify(mICarProperty, times(10)).getProperty(HVAC_TEMPERATURE_SET, 0);
385     }
386 
setAppTargetSdk(int appTargetSdk)387     private void setAppTargetSdk(int appTargetSdk) {
388         mApplicationInfo.targetSdkVersion = appTargetSdk;
389         mCarPropertyManager = new CarPropertyManager(mCar, mICarProperty);
390         mCarPropertyManager.setFeatureFlags(mFeatureFlags);
391     }
392 
393     @Test
testGetProperty_notAvailableBeforeR()394     public void testGetProperty_notAvailableBeforeR() throws Exception {
395         setAppTargetSdk(Build.VERSION_CODES.Q);
396         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
397                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE));
398 
399         assertThrows(IllegalStateException.class,
400                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
401     }
402 
403     @Test
testGetProperty_notAvailableEqualAfterR()404     public void testGetProperty_notAvailableEqualAfterR() throws Exception {
405         setAppTargetSdk(Build.VERSION_CODES.R);
406         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
407                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE));
408 
409         assertThrows(PropertyNotAvailableException.class,
410                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
411     }
412 
413     @Test
testGetProperty_tryAgainBeforeR()414     public void testGetProperty_tryAgainBeforeR() throws Exception {
415         setAppTargetSdk(Build.VERSION_CODES.Q);
416         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
417                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
418 
419         assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0)).isNull();
420     }
421 
422     @Test
testGetProperty_tryAgainEqualAfterR()423     public void testGetProperty_tryAgainEqualAfterR() throws Exception {
424         setAppTargetSdk(Build.VERSION_CODES.R);
425         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
426                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
427 
428         assertThrows(PropertyNotAvailableAndRetryException.class,
429                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
430     }
431 
432     @Test
testGetProperty_accessDeniedBeforeR()433     public void testGetProperty_accessDeniedBeforeR() throws Exception {
434         setAppTargetSdk(Build.VERSION_CODES.Q);
435         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
436                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_ACCESS_DENIED));
437 
438         assertThrows(IllegalStateException.class,
439                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
440     }
441 
442     @Test
testGetProperty_accessDeniedEqualAfterR()443     public void testGetProperty_accessDeniedEqualAfterR() throws Exception {
444         setAppTargetSdk(Build.VERSION_CODES.R);
445         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
446                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_ACCESS_DENIED));
447 
448         assertThrows(PropertyAccessDeniedSecurityException.class,
449                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
450     }
451 
452     @Test
testGetProperty_internalErrorBeforeR()453     public void testGetProperty_internalErrorBeforeR() throws Exception {
454         setAppTargetSdk(Build.VERSION_CODES.Q);
455         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
456                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR));
457 
458         assertThrows(IllegalStateException.class,
459                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
460     }
461 
462     @Test
testGetProperty_internalErrorEqualAfterR()463     public void testGetProperty_internalErrorEqualAfterR() throws Exception {
464         setAppTargetSdk(Build.VERSION_CODES.R);
465         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
466                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR));
467 
468         assertThrows(CarInternalErrorException.class,
469                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
470     }
471 
472     @Test
testGetProperty_internalErrorEqualAfterU_withVendorErrorCode()473     public void testGetProperty_internalErrorEqualAfterU_withVendorErrorCode() throws Exception {
474         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
475         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
476                 new ServiceSpecificException(combineErrors(
477                         VehicleHalStatusCode.STATUS_INTERNAL_ERROR, VENDOR_ERROR_CODE)));
478 
479         CarInternalErrorException exception = assertThrows(CarInternalErrorException.class,
480                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
481 
482         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
483     }
484 
485     @Test
testGetProperty_unknownErrorBeforeR()486     public void testGetProperty_unknownErrorBeforeR() throws Exception {
487         setAppTargetSdk(Build.VERSION_CODES.Q);
488         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
489                 new ServiceSpecificException(UNKNOWN_ERROR));
490 
491         assertThrows(IllegalStateException.class,
492                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
493     }
494 
495     @Test
testGetProperty_unknownErrorEqualAfterR()496     public void testGetProperty_unknownErrorEqualAfterR() throws Exception {
497         setAppTargetSdk(Build.VERSION_CODES.R);
498         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
499                 new ServiceSpecificException(UNKNOWN_ERROR));
500 
501         assertThrows(CarInternalErrorException.class,
502                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
503     }
504 
505     @Test
testGetProperty_returnsValueWithUnavailableStatusBeforeU()506     public void testGetProperty_returnsValueWithUnavailableStatusBeforeU() throws RemoteException {
507         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
508         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
509                 CarPropertyValue.STATUS_UNAVAILABLE, TEST_TIMESTAMP, 17.0f);
510 
511         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
512 
513         assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, /*areaId=*/ 0)).isEqualTo(
514                 new CarPropertyValue<>(HVAC_TEMPERATURE_SET, /*areaId=*/0,
515                         CarPropertyValue.STATUS_UNAVAILABLE, TEST_TIMESTAMP, 17.0f));
516     }
517 
518     @Test
testGetProperty_valueWithUnavailableStatusThrowsAfterU()519     public void testGetProperty_valueWithUnavailableStatusThrowsAfterU() throws RemoteException {
520         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
521         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
522                 CarPropertyValue.STATUS_UNAVAILABLE, TEST_TIMESTAMP, 17.0f);
523 
524         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
525 
526         assertThrows(PropertyNotAvailableException.class,
527                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
528     }
529 
530     @Test
testGetProperty_returnsValueWithErrorStatusBeforeU()531     public void testGetProperty_returnsValueWithErrorStatusBeforeU() throws RemoteException {
532         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
533         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
534                 CarPropertyValue.STATUS_ERROR, TEST_TIMESTAMP, 17.0f);
535 
536         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
537 
538         assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, /*areaId=*/ 0)).isEqualTo(
539                 new CarPropertyValue<>(HVAC_TEMPERATURE_SET, /*areaId=*/0,
540                         CarPropertyValue.STATUS_ERROR, TEST_TIMESTAMP, 17.0f));
541     }
542 
543     @Test
testGetProperty_valueWithErrorStatusThrowsAfterU()544     public void testGetProperty_valueWithErrorStatusThrowsAfterU() throws RemoteException {
545         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
546         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
547                 CarPropertyValue.STATUS_ERROR, TEST_TIMESTAMP, 17.0f);
548 
549         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
550 
551         assertThrows(CarInternalErrorException.class,
552                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
553     }
554 
555     @Test
testGetProperty_returnsValueWithUnknownStatusBeforeU()556     public void testGetProperty_returnsValueWithUnknownStatusBeforeU() throws RemoteException {
557         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
558         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
559                 /*unknown status=*/999, TEST_TIMESTAMP, 17.0f);
560 
561         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
562 
563         assertThat(mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, /*areaId=*/ 0)).isEqualTo(
564                 new CarPropertyValue<>(HVAC_TEMPERATURE_SET, /*areaId=*/0, /*status=*/999,
565                         TEST_TIMESTAMP, 17.0f));
566     }
567 
568     @Test
testGetProperty_valueWithUnknownStatusThrowsAfterU()569     public void testGetProperty_valueWithUnknownStatusThrowsAfterU() throws RemoteException {
570         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
571         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
572                 /*unknown status=*/999, TEST_TIMESTAMP, 17.0f);
573 
574         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
575 
576         assertThrows(CarInternalErrorException.class,
577                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
578     }
579 
580     @Test
testGetPropertyWithClass()581     public void testGetPropertyWithClass() throws Exception {
582         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0, 17.0f);
583 
584         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
585 
586         assertThat(mCarPropertyManager.getProperty(Float.class, HVAC_TEMPERATURE_SET, 0).getValue())
587                 .isEqualTo(17.0f);
588     }
589 
590     @Test
testGetPropertyWithClass_mismatchClass()591     public void testGetPropertyWithClass_mismatchClass() throws Exception {
592         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0, 17.0f);
593 
594         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
595 
596         assertThrows(IllegalArgumentException.class,
597                 () -> mCarPropertyManager.getProperty(Integer.class, HVAC_TEMPERATURE_SET, 0));
598     }
599 
600     @Test
testGetPropertyWithClass_tryAgainBeforeR()601     public void testGetPropertyWithClass_tryAgainBeforeR() throws Exception {
602         setAppTargetSdk(Build.VERSION_CODES.Q);
603         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
604                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
605 
606         assertThat(mCarPropertyManager.getProperty(Float.class, HVAC_TEMPERATURE_SET, 0))
607                 .isNull();
608     }
609 
610     @Test
testGetPropertyWithClass_tryAgainEqualAfterR()611     public void testGetPropertyWithClass_tryAgainEqualAfterR() throws Exception {
612         setAppTargetSdk(Build.VERSION_CODES.R);
613         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
614                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
615 
616         assertThrows(PropertyNotAvailableAndRetryException.class,
617                 () -> mCarPropertyManager.getProperty(Float.class, HVAC_TEMPERATURE_SET, 0));
618     }
619 
620 
621     @Test
testGetBooleanProperty()622     public void testGetBooleanProperty() throws Exception {
623         setAppTargetSdk(Build.VERSION_CODES.R);
624         CarPropertyValue<Boolean> value = new CarPropertyValue<>(BOOLEAN_PROP, 0, true);
625         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenReturn(value);
626 
627         assertThat(mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0)).isTrue();
628     }
629 
630     @Test
testGetBooleanProperty_notAvailableBeforeR()631     public void testGetBooleanProperty_notAvailableBeforeR() throws Exception {
632         setAppTargetSdk(Build.VERSION_CODES.Q);
633         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
634                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE));
635 
636         assertThrows(IllegalStateException.class,
637                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
638     }
639 
640     @Test
testGetBooleanProperty_notAvailableEqualAfterR()641     public void testGetBooleanProperty_notAvailableEqualAfterR() throws Exception {
642         setAppTargetSdk(Build.VERSION_CODES.R);
643         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
644                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE));
645 
646         assertThrows(PropertyNotAvailableException.class,
647                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
648     }
649 
650     @Test
testGetProperty_notAvailableEqualAfterU_withVendorErrorCode()651     public void testGetProperty_notAvailableEqualAfterU_withVendorErrorCode() throws Exception {
652         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
653         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
654                 new ServiceSpecificException(combineErrors(
655                         VehicleHalStatusCode.STATUS_NOT_AVAILABLE, VENDOR_ERROR_CODE)));
656 
657         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
658                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
659 
660         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
661     }
662 
663     @Test
testGetProperty_notAvailableDisabledBeforeU()664     public void testGetProperty_notAvailableDisabledBeforeU() throws Exception {
665         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
666         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
667                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED));
668 
669         assertThrows(PropertyNotAvailableException.class,
670                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
671     }
672 
673     @Test
testGetProperty_notAvailableDisabledAfterU()674     public void testGetProperty_notAvailableDisabledAfterU() throws Exception {
675         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
676         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
677                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED));
678 
679         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
680                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
681         assertThat(exception.getDetailedErrorCode())
682                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_DISABLED);
683     }
684 
685     @Test
testGetProperty_notAvailableDisabledAfterU_withVendorErrorCode()686     public void testGetProperty_notAvailableDisabledAfterU_withVendorErrorCode() throws Exception {
687         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
688         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
689                 new ServiceSpecificException(combineErrors(
690                         VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED, VENDOR_ERROR_CODE)));
691 
692         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
693                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
694 
695         assertThat(exception.getDetailedErrorCode())
696                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_DISABLED);
697         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
698     }
699 
700     @Test
testGetProperty_notAvailableSafetyBeforeU()701     public void testGetProperty_notAvailableSafetyBeforeU() throws Exception {
702         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
703         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
704                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY));
705 
706         assertThrows(PropertyNotAvailableException.class,
707                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
708     }
709 
710     @Test
testGetProperty_notAvailableSafetyAfterU()711     public void testGetProperty_notAvailableSafetyAfterU() throws Exception {
712         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
713         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
714                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY));
715 
716         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
717                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
718         assertThat(exception.getDetailedErrorCode())
719                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SAFETY);
720     }
721 
722     @Test
testGetProperty_notAvailableSafetyAfterU_withVendorErrorCode()723     public void testGetProperty_notAvailableSafetyAfterU_withVendorErrorCode() throws Exception {
724         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
725         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
726                 new ServiceSpecificException(combineErrors(
727                         VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY, VENDOR_ERROR_CODE)));
728 
729         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
730                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
731 
732         assertThat(exception.getDetailedErrorCode())
733                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SAFETY);
734         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
735     }
736 
737     @Test
testGetProperty_notAvailableSpeedHighBeforeU()738     public void testGetProperty_notAvailableSpeedHighBeforeU() throws Exception {
739         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
740         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
741                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH));
742 
743         assertThrows(PropertyNotAvailableException.class,
744                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
745     }
746 
747     @Test
testGetProperty_notAvailableSpeedHighAfterU()748     public void testGetProperty_notAvailableSpeedHighAfterU() throws Exception {
749         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
750         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
751                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH));
752 
753         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
754                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
755         assertThat(exception.getDetailedErrorCode())
756                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_HIGH);
757     }
758 
759     @Test
testGetProperty_notAvailableSpeedHighAfterU_withVendorErrorCode()760     public void testGetProperty_notAvailableSpeedHighAfterU_withVendorErrorCode() throws Exception {
761         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
762         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
763                 new ServiceSpecificException(combineErrors(
764                         VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH, VENDOR_ERROR_CODE)));
765 
766         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
767                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
768 
769         assertThat(exception.getDetailedErrorCode())
770                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_HIGH);
771         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
772     }
773 
774     @Test
testGetProperty_notAvailableSpeedLowBeforeU()775     public void testGetProperty_notAvailableSpeedLowBeforeU() throws Exception {
776         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
777         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
778                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW));
779 
780         assertThrows(PropertyNotAvailableException.class,
781                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
782     }
783 
784     @Test
testGetProperty_notAvailableSpeedLowAfterU()785     public void testGetProperty_notAvailableSpeedLowAfterU() throws Exception {
786         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
787         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
788                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW));
789 
790         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
791                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
792         assertThat(exception.getDetailedErrorCode())
793                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_LOW);
794     }
795 
796     @Test
testGetProperty_notAvailableSpeedLowAfterU_withVendorErrorCode()797     public void testGetProperty_notAvailableSpeedLowAfterU_withVendorErrorCode() throws Exception {
798         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
799         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
800                 new ServiceSpecificException(combineErrors(
801                         VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW, VENDOR_ERROR_CODE)));
802 
803         PropertyNotAvailableException exception = assertThrows(PropertyNotAvailableException.class,
804                 () -> mCarPropertyManager.getProperty(HVAC_TEMPERATURE_SET, 0));
805 
806         assertThat(exception.getDetailedErrorCode())
807                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_LOW);
808         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
809     }
810 
811     @Test
testGetBooleanProperty_tryAgainBeforeR()812     public void testGetBooleanProperty_tryAgainBeforeR() throws Exception {
813         setAppTargetSdk(Build.VERSION_CODES.Q);
814         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
815                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
816 
817         assertThat(mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0)).isFalse();
818     }
819 
820     @Test
testGetBooleanProperty_tryAgainEqualAfterR()821     public void testGetBooleanProperty_tryAgainEqualAfterR() throws Exception {
822         setAppTargetSdk(Build.VERSION_CODES.R);
823         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
824                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
825 
826         assertThrows(PropertyNotAvailableAndRetryException.class,
827                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
828     }
829 
830     @Test
testGetBooleanProperty_accessDeniedBeforeR()831     public void testGetBooleanProperty_accessDeniedBeforeR() throws Exception {
832         setAppTargetSdk(Build.VERSION_CODES.Q);
833         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
834                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_ACCESS_DENIED));
835 
836         assertThrows(IllegalStateException.class,
837                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
838     }
839 
840     @Test
testGetBooleanProperty_accessDeniedEqualAfterR()841     public void testGetBooleanProperty_accessDeniedEqualAfterR() throws Exception {
842         setAppTargetSdk(Build.VERSION_CODES.R);
843         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
844                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_ACCESS_DENIED));
845 
846         assertThrows(PropertyAccessDeniedSecurityException.class,
847                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
848     }
849 
850     @Test
testGetBooleanProperty_internalErrorBeforeR()851     public void testGetBooleanProperty_internalErrorBeforeR() throws Exception {
852         setAppTargetSdk(Build.VERSION_CODES.Q);
853         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
854                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR));
855 
856         assertThrows(IllegalStateException.class,
857                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
858     }
859 
860     @Test
testGetBooleanProperty_internalErrorEqualAfterR()861     public void testGetBooleanProperty_internalErrorEqualAfterR() throws Exception {
862         setAppTargetSdk(Build.VERSION_CODES.R);
863         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
864                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_INTERNAL_ERROR));
865 
866         assertThrows(CarInternalErrorException.class,
867                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
868     }
869 
870     @Test
testGetBooleanProperty_unknownErrorBeforeR()871     public void testGetBooleanProperty_unknownErrorBeforeR() throws Exception {
872         setAppTargetSdk(Build.VERSION_CODES.Q);
873         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
874                 new ServiceSpecificException(UNKNOWN_ERROR));
875 
876         assertThrows(IllegalStateException.class,
877                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
878     }
879 
880     @Test
testGetBooleanProperty_unknownErrorEqualAfterR()881     public void testGetBooleanProperty_unknownErrorEqualAfterR() throws Exception {
882         setAppTargetSdk(Build.VERSION_CODES.R);
883         when(mICarProperty.getProperty(BOOLEAN_PROP, 0)).thenThrow(
884                 new ServiceSpecificException(UNKNOWN_ERROR));
885 
886         assertThrows(CarInternalErrorException.class,
887                 () -> mCarPropertyManager.getBooleanProperty(BOOLEAN_PROP, 0));
888     }
889 
890     @Test
testGetIntProperty()891     public void testGetIntProperty() throws Exception {
892         setAppTargetSdk(Build.VERSION_CODES.R);
893         CarPropertyValue<Integer> value = new CarPropertyValue<>(INT32_PROP, 0, 1);
894         when(mICarProperty.getProperty(INT32_PROP, 0)).thenReturn(value);
895 
896         assertThat(mCarPropertyManager.getIntProperty(INT32_PROP, 0)).isEqualTo(1);
897     }
898 
899     @Test
testGetIntProperty_tryAgainBeforeR()900     public void testGetIntProperty_tryAgainBeforeR() throws Exception {
901         setAppTargetSdk(Build.VERSION_CODES.Q);
902         when(mICarProperty.getProperty(INT32_PROP, 0)).thenThrow(
903                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
904 
905         assertThat(mCarPropertyManager.getIntProperty(INT32_PROP, 0)).isEqualTo(0);
906     }
907 
908     @Test
testGetIntProperty_tryAgainEqualAfterR()909     public void testGetIntProperty_tryAgainEqualAfterR() throws Exception {
910         setAppTargetSdk(Build.VERSION_CODES.R);
911         when(mICarProperty.getProperty(INT32_PROP, 0)).thenThrow(
912                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
913 
914         assertThrows(PropertyNotAvailableAndRetryException.class,
915                 () -> mCarPropertyManager.getIntProperty(INT32_PROP, 0));
916     }
917 
918     @Test
testGetIntProperty_wrongType()919     public void testGetIntProperty_wrongType() throws Exception {
920         CarPropertyValue<Integer> value = new CarPropertyValue<>(INT32_PROP, 0, 1);
921         when(mICarProperty.getProperty(INT32_PROP, 0)).thenReturn(value);
922 
923         assertThrows(IllegalArgumentException.class,
924                 () -> mCarPropertyManager.getFloatProperty(INT32_PROP, 0));
925     }
926 
927     @Test
testGetIntArrayProperty()928     public void testGetIntArrayProperty() throws Exception {
929         setAppTargetSdk(Build.VERSION_CODES.R);
930         CarPropertyValue<Float> value = new CarPropertyValue<>(FLOAT_PROP, 0, 1f);
931         when(mICarProperty.getProperty(FLOAT_PROP, 0)).thenReturn(value);
932 
933         assertThrows(IllegalArgumentException.class,
934                 () -> mCarPropertyManager.getIntArrayProperty(FLOAT_PROP, 0));
935     }
936 
937     @Test
testGetIntArrayProperty_tryAgainBeforeR()938     public void testGetIntArrayProperty_tryAgainBeforeR() throws Exception {
939         setAppTargetSdk(Build.VERSION_CODES.Q);
940         when(mICarProperty.getProperty(INT32_VEC_PROP, 0)).thenThrow(
941                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
942 
943         assertThat(mCarPropertyManager.getIntArrayProperty(INT32_VEC_PROP, 0)).isEqualTo(
944                 new int[0]);
945     }
946 
947     @Test
testGetIntArrayProperty_tryAgainEqualAfterR()948     public void testGetIntArrayProperty_tryAgainEqualAfterR() throws Exception {
949         setAppTargetSdk(Build.VERSION_CODES.R);
950         when(mICarProperty.getProperty(INT32_VEC_PROP, 0)).thenThrow(
951                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
952 
953         assertThrows(PropertyNotAvailableAndRetryException.class,
954                 () -> mCarPropertyManager.getIntArrayProperty(INT32_VEC_PROP, 0));
955     }
956 
957     @Test
testGetFloatProperty()958     public void testGetFloatProperty() throws Exception {
959         setAppTargetSdk(Build.VERSION_CODES.R);
960         CarPropertyValue<Float> value = new CarPropertyValue<>(FLOAT_PROP, 0, 1.f);
961         when(mICarProperty.getProperty(FLOAT_PROP, 0)).thenReturn(value);
962 
963         assertThat(mCarPropertyManager.getFloatProperty(FLOAT_PROP, 0)).isEqualTo(1.f);
964     }
965 
966     @Test
testGetFloatProperty_tryAgainBeforeR()967     public void testGetFloatProperty_tryAgainBeforeR() throws Exception {
968         setAppTargetSdk(Build.VERSION_CODES.Q);
969         when(mICarProperty.getProperty(FLOAT_PROP, 0)).thenThrow(
970                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
971 
972         assertThat(mCarPropertyManager.getFloatProperty(FLOAT_PROP, 0)).isEqualTo(0.f);
973     }
974 
975     @Test
testGetFloatProperty_tryAgainEqualAfterR()976     public void testGetFloatProperty_tryAgainEqualAfterR() throws Exception {
977         setAppTargetSdk(Build.VERSION_CODES.R);
978         when(mICarProperty.getProperty(FLOAT_PROP, 0)).thenThrow(
979                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
980 
981         assertThrows(PropertyNotAvailableAndRetryException.class,
982                 () -> mCarPropertyManager.getFloatProperty(FLOAT_PROP, 0));
983     }
984 
985     @Test
testGetFloatProperty_wrongType()986     public void testGetFloatProperty_wrongType() throws Exception {
987         CarPropertyValue<Integer> value = new CarPropertyValue<>(INT32_PROP, 0, 1);
988         when(mICarProperty.getProperty(INT32_PROP, 0)).thenReturn(value);
989 
990         assertThrows(IllegalArgumentException.class,
991                 () -> mCarPropertyManager.getFloatProperty(INT32_PROP, 0));
992     }
993 
createGetPropertyRequest()994     private CarPropertyManager.GetPropertyRequest createGetPropertyRequest() {
995         return mCarPropertyManager.generateGetPropertyRequest(HVAC_TEMPERATURE_SET, 0);
996     }
997 
998     @Test
testGetPropertiesAsync()999     public void testGetPropertiesAsync() throws RemoteException {
1000         mCarPropertyManager.getPropertiesAsync(List.of(createGetPropertyRequest()), null, null,
1001                 mGetPropertyCallback);
1002 
1003         ArgumentCaptor<AsyncPropertyServiceRequestList> argumentCaptor = ArgumentCaptor.forClass(
1004                 AsyncPropertyServiceRequestList.class);
1005         verify(mICarProperty).getPropertiesAsync(argumentCaptor.capture(), any(), anyLong());
1006         assertThat(argumentCaptor.getValue().getList().get(0).getRequestId()).isEqualTo(0);
1007         assertThat(argumentCaptor.getValue().getList().get(0).getPropertyId())
1008                 .isEqualTo(HVAC_TEMPERATURE_SET);
1009         assertThat(argumentCaptor.getValue().getList().get(0).getAreaId()).isEqualTo(0);
1010     }
1011 
1012     @Test
testGetPropertiesAsyncWithTimeout()1013     public void testGetPropertiesAsyncWithTimeout() throws RemoteException {
1014         mCarPropertyManager.getPropertiesAsync(List.of(createGetPropertyRequest()),
1015                 /* timeoutInMs= */ 1000, null, null, mGetPropertyCallback);
1016 
1017         ArgumentCaptor<AsyncPropertyServiceRequestList> argumentCaptor = ArgumentCaptor.forClass(
1018                 AsyncPropertyServiceRequestList.class);
1019         verify(mICarProperty).getPropertiesAsync(argumentCaptor.capture(), any(), eq(1000L));
1020         assertThat(argumentCaptor.getValue().getList().get(0).getRequestId()).isEqualTo(0);
1021         assertThat(argumentCaptor.getValue().getList().get(0).getPropertyId())
1022                 .isEqualTo(HVAC_TEMPERATURE_SET);
1023         assertThat(argumentCaptor.getValue().getList().get(0).getAreaId()).isEqualTo(0);
1024     }
1025 
1026     @Test
testGetPropertiesAsync_illegalArgumentException()1027     public void testGetPropertiesAsync_illegalArgumentException() throws RemoteException {
1028         IllegalArgumentException exception = new IllegalArgumentException();
1029         doThrow(exception).when(mICarProperty).getPropertiesAsync(any(
1030                 AsyncPropertyServiceRequestList.class),
1031                 any(IAsyncPropertyResultCallback.class), anyLong());
1032 
1033         assertThrows(IllegalArgumentException.class,
1034                 () -> mCarPropertyManager.getPropertiesAsync(
1035                         List.of(createGetPropertyRequest()), null, null, mGetPropertyCallback));
1036     }
1037 
1038     @Test
testGetPropertiesAsync_SecurityException()1039     public void testGetPropertiesAsync_SecurityException() throws RemoteException {
1040         SecurityException exception = new SecurityException();
1041         doThrow(exception).when(mICarProperty).getPropertiesAsync(any(
1042                 AsyncPropertyServiceRequestList.class),
1043                 any(IAsyncPropertyResultCallback.class), anyLong());
1044 
1045         assertThrows(SecurityException.class,
1046                 () -> mCarPropertyManager.getPropertiesAsync(
1047                         List.of(createGetPropertyRequest()), null, null, mGetPropertyCallback));
1048     }
1049 
1050     @Test
tsetGetPropertiesAsync_unsupportedProperty()1051     public void tsetGetPropertiesAsync_unsupportedProperty() throws Exception {
1052         assertThrows(IllegalArgumentException.class,
1053                 () -> mCarPropertyManager.getPropertiesAsync(
1054                         List.of(mCarPropertyManager.generateGetPropertyRequest(INVALID, 0)), null,
1055                         null, mGetPropertyCallback));
1056     }
1057 
1058     @Test
testGetPropertiesAsync_remoteException()1059     public void testGetPropertiesAsync_remoteException() throws RemoteException {
1060         RemoteException remoteException = new RemoteException();
1061         doThrow(remoteException).when(mICarProperty).getPropertiesAsync(any(
1062                 AsyncPropertyServiceRequestList.class),
1063                 any(IAsyncPropertyResultCallback.class), anyLong());
1064 
1065         mCarPropertyManager.getPropertiesAsync(List.of(createGetPropertyRequest()), null, null,
1066                 mGetPropertyCallback);
1067 
1068         verify(mCar).handleRemoteExceptionFromCarService(any(RemoteException.class));
1069     }
1070 
1071     @Test
testGetPropertiesAsync_clearRequestIdAfterFailed()1072     public void testGetPropertiesAsync_clearRequestIdAfterFailed() throws RemoteException {
1073         CarPropertyManager.GetPropertyRequest getPropertyRequest = createGetPropertyRequest();
1074         IllegalArgumentException exception = new IllegalArgumentException();
1075         doThrow(exception).when(mICarProperty).getPropertiesAsync(any(
1076                 AsyncPropertyServiceRequestList.class),
1077                 any(IAsyncPropertyResultCallback.class), anyLong());
1078 
1079         assertThrows(IllegalArgumentException.class,
1080                 () -> mCarPropertyManager.getPropertiesAsync(List.of(getPropertyRequest), null,
1081                         null, mGetPropertyCallback));
1082 
1083         clearInvocations(mICarProperty);
1084         doNothing().when(mICarProperty).getPropertiesAsync(any(
1085                 AsyncPropertyServiceRequestList.class),
1086                 any(IAsyncPropertyResultCallback.class), anyLong());
1087         ArgumentCaptor<AsyncPropertyServiceRequestList> argumentCaptor = ArgumentCaptor.forClass(
1088                 AsyncPropertyServiceRequestList.class);
1089 
1090         mCarPropertyManager.getPropertiesAsync(List.of(getPropertyRequest), null, null,
1091                 mGetPropertyCallback);
1092 
1093         verify(mICarProperty).getPropertiesAsync(argumentCaptor.capture(), any(), anyLong());
1094         assertThat(argumentCaptor.getValue().getList().get(0).getRequestId()).isEqualTo(0);
1095         assertThat(argumentCaptor.getValue().getList().get(0).getPropertyId())
1096                 .isEqualTo(HVAC_TEMPERATURE_SET);
1097         assertThat(argumentCaptor.getValue().getList().get(0).getAreaId()).isEqualTo(0);
1098     }
1099 
1100     @Test
1101     @IgnoreUnderRavenwood(blockedBy = android.os.CancellationSignal.class)
testGetPropertiesAsync_cancellationSignalCancelRequests()1102     public void testGetPropertiesAsync_cancellationSignalCancelRequests() throws Exception {
1103         CarPropertyManager.GetPropertyRequest getPropertyRequest = createGetPropertyRequest();
1104         CancellationSignal cancellationSignal = new CancellationSignal();
1105         List<IAsyncPropertyResultCallback> callbackWrapper = new ArrayList<>();
1106         doAnswer((invocation) -> {
1107             Object[] args = invocation.getArguments();
1108             callbackWrapper.add((IAsyncPropertyResultCallback) args[1]);
1109             return null;
1110         }).when(mICarProperty).getPropertiesAsync(any(), any(), anyLong());
1111 
1112         mCarPropertyManager.getPropertiesAsync(List.of(getPropertyRequest), cancellationSignal,
1113                 /* callbackExecutor= */ null, mGetPropertyCallback);
1114 
1115         // Cancel the pending request.
1116         cancellationSignal.cancel();
1117 
1118         verify(mICarProperty).cancelRequests(new int[]{0});
1119 
1120         // Call the manager callback after the request is already cancelled.
1121         GetSetValueResult getValueResult = GetSetValueResult.newErrorResult(
1122                 0,
1123                 new CarPropertyErrorCodes(
1124                         CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR,
1125                         /* vendorErrorCode= */ 0,
1126                         /* systemErrorCode= */ 0));
1127         assertThat(callbackWrapper.size()).isEqualTo(1);
1128         callbackWrapper.get(0).onGetValueResults(
1129                 new GetSetValueResultList(List.of(getValueResult)));
1130 
1131         // No client callbacks should be called.
1132         verify(mGetPropertyCallback, never()).onFailure(any());
1133         verify(mGetPropertyCallback, never()).onSuccess(any());
1134     }
1135 
1136     @Test
testOnGetValueResult_onSuccess()1137     public void testOnGetValueResult_onSuccess() throws RemoteException {
1138         doAnswer((invocation) -> {
1139             Object[] args = invocation.getArguments();
1140             AsyncPropertyServiceRequestList asyncGetPropertyServiceRequestList =
1141                     (AsyncPropertyServiceRequestList) args[0];
1142             List getPropertyServiceList = asyncGetPropertyServiceRequestList.getList();
1143             AsyncPropertyServiceRequest getPropertyServiceRequest =
1144                     (AsyncPropertyServiceRequest) getPropertyServiceList.get(0);
1145             IAsyncPropertyResultCallback getAsyncPropertyResultCallback =
1146                     (IAsyncPropertyResultCallback) args[1];
1147 
1148             assertThat(getPropertyServiceRequest.getRequestId()).isEqualTo(0);
1149             assertThat(getPropertyServiceRequest.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1150 
1151             CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0, 17.0f);
1152             GetSetValueResult getValueResult = GetSetValueResult.newGetValueResult(0, value);
1153 
1154             getAsyncPropertyResultCallback.onGetValueResults(
1155                     new GetSetValueResultList(List.of(getValueResult)));
1156             return null;
1157         }).when(mICarProperty).getPropertiesAsync(any(), any(), anyLong());
1158 
1159         mCarPropertyManager.getPropertiesAsync(List.of(createGetPropertyRequest()), null, null,
1160                 mGetPropertyCallback);
1161 
1162         verify(mGetPropertyCallback, timeout(1000)).onSuccess(
1163                 mGetPropertyResultCaptor.capture());
1164         GetPropertyResult<Float> gotResult = (GetPropertyResult<Float>)
1165                 mGetPropertyResultCaptor.getValue();
1166         assertThat(gotResult.getRequestId()).isEqualTo(0);
1167         assertThat(gotResult.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1168         assertThat(gotResult.getAreaId()).isEqualTo(0);
1169         assertThat(gotResult.getValue()).isEqualTo(17.0f);
1170     }
1171 
1172     @Test
testOnGetValueResult_onSuccessMultipleRequests()1173     public void testOnGetValueResult_onSuccessMultipleRequests() throws RemoteException {
1174         doAnswer((invocation) -> {
1175             Object[] args = invocation.getArguments();
1176             AsyncPropertyServiceRequestList getPropertyServiceRequest =
1177                     (AsyncPropertyServiceRequestList) args[0];
1178             List<AsyncPropertyServiceRequest> getPropertyServiceRequests =
1179                     getPropertyServiceRequest.getList();
1180             IAsyncPropertyResultCallback getAsyncPropertyResultCallback =
1181                     (IAsyncPropertyResultCallback) args[1];
1182 
1183             assertThat(getPropertyServiceRequests.size()).isEqualTo(2);
1184             assertThat(getPropertyServiceRequests.get(0).getRequestId()).isEqualTo(0);
1185             assertThat(getPropertyServiceRequests.get(0).getPropertyId()).isEqualTo(
1186                     HVAC_TEMPERATURE_SET);
1187             assertThat(getPropertyServiceRequests.get(1).getRequestId()).isEqualTo(1);
1188             assertThat(getPropertyServiceRequests.get(1).getPropertyId()).isEqualTo(
1189                     HVAC_TEMPERATURE_SET);
1190 
1191             CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0, 17.0f);
1192             List<GetSetValueResult> getValueResults = List.of(
1193                     GetSetValueResult.newGetValueResult(0, value),
1194                     GetSetValueResult.newGetValueResult(1, value));
1195 
1196             getAsyncPropertyResultCallback.onGetValueResults(
1197                     new GetSetValueResultList(getValueResults));
1198             return null;
1199         }).when(mICarProperty).getPropertiesAsync(any(), any(), anyLong());
1200 
1201         List<CarPropertyManager.GetPropertyRequest> getPropertyRequests = new ArrayList<>();
1202         getPropertyRequests.add(createGetPropertyRequest());
1203         getPropertyRequests.add(createGetPropertyRequest());
1204 
1205         mCarPropertyManager.getPropertiesAsync(getPropertyRequests, null, null,
1206                 mGetPropertyCallback);
1207 
1208         verify(mGetPropertyCallback, timeout(1000).times(2)).onSuccess(
1209                     mGetPropertyResultCaptor.capture());
1210         List<GetPropertyResult<?>> gotPropertyResults = mGetPropertyResultCaptor.getAllValues();
1211         assertThat(gotPropertyResults.get(0).getRequestId()).isEqualTo(0);
1212         assertThat(gotPropertyResults.get(0).getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1213         assertThat(gotPropertyResults.get(0).getAreaId()).isEqualTo(0);
1214         assertThat(gotPropertyResults.get(0).getValue()).isEqualTo(17.0f);
1215         assertThat(gotPropertyResults.get(1).getRequestId()).isEqualTo(1);
1216         assertThat(gotPropertyResults.get(1).getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1217         assertThat(gotPropertyResults.get(1).getAreaId()).isEqualTo(0);
1218         assertThat(gotPropertyResults.get(1).getValue()).isEqualTo(17.0f);
1219     }
1220 
1221     @Test
testOnGetValueResult_onFailure()1222     public void testOnGetValueResult_onFailure() throws RemoteException {
1223         doAnswer((invocation) -> {
1224             Object[] args = invocation.getArguments();
1225             AsyncPropertyServiceRequestList asyncPropertyServiceRequestList =
1226                     (AsyncPropertyServiceRequestList) args[0];
1227             List getPropertyServiceList = asyncPropertyServiceRequestList.getList();
1228             AsyncPropertyServiceRequest getPropertyServiceRequest =
1229                     (AsyncPropertyServiceRequest) getPropertyServiceList.get(0);
1230             IAsyncPropertyResultCallback getAsyncPropertyResultCallback =
1231                     (IAsyncPropertyResultCallback) args[1];
1232 
1233             assertThat(getPropertyServiceRequest.getRequestId()).isEqualTo(0);
1234             assertThat(getPropertyServiceRequest.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1235 
1236             GetSetValueResult getValueResult = GetSetValueResult.newErrorResult(
1237                      /* requestId= */ 0,
1238                     new CarPropertyErrorCodes(
1239                             CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR, VENDOR_ERROR_CODE, 0));
1240 
1241             getAsyncPropertyResultCallback.onGetValueResults(
1242                     new GetSetValueResultList(List.of(getValueResult)));
1243             return null;
1244         }).when(mICarProperty).getPropertiesAsync(any(), any(), anyLong());
1245 
1246 
1247         mCarPropertyManager.getPropertiesAsync(List.of(createGetPropertyRequest()), null, null,
1248                 mGetPropertyCallback);
1249 
1250         verify(mGetPropertyCallback, timeout(1000)).onFailure(mPropertyAsyncErrorCaptor.capture());
1251         PropertyAsyncError error = mPropertyAsyncErrorCaptor.getValue();
1252         assertThat(error.getRequestId()).isEqualTo(0);
1253         assertThat(error.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1254         assertThat(error.getAreaId()).isEqualTo(0);
1255         assertThat(error.getErrorCode()).isEqualTo(
1256                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
1257         assertThat(error.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1258     }
1259 
1260     @Test
1261     @EnableFlags(Flags.FLAG_CAR_PROPERTY_DETAILED_ERROR_CODES)
testPropertyAsyncError_GetDetailedErrorCode()1262     public void testPropertyAsyncError_GetDetailedErrorCode() throws RemoteException {
1263         doAnswer((invocation) -> {
1264             Object[] args = invocation.getArguments();
1265             IAsyncPropertyResultCallback getAsyncPropertyResultCallback =
1266                     (IAsyncPropertyResultCallback) args[1];
1267 
1268             GetSetValueResult getValueResult = GetSetValueResult.newErrorResult(
1269                     /* requestId= */ 0,
1270                     new CarPropertyErrorCodes(
1271                             CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR,
1272                             /* vendorErrorCode= */ 0,
1273                             VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH));
1274 
1275             getAsyncPropertyResultCallback.onGetValueResults(
1276                     new GetSetValueResultList(List.of(getValueResult)));
1277             return null;
1278         }).when(mICarProperty).getPropertiesAsync(any(), any(), anyLong());
1279 
1280         mCarPropertyManager.getPropertiesAsync(List.of(createGetPropertyRequest()), null, null,
1281                 mGetPropertyCallback);
1282 
1283         verify(mGetPropertyCallback, timeout(1000)).onFailure(mPropertyAsyncErrorCaptor.capture());
1284         PropertyAsyncError error = mPropertyAsyncErrorCaptor.getValue();
1285         assertThat(error.getDetailedErrorCode()).isEqualTo(
1286                 DetailedErrorCode.NOT_AVAILABLE_SPEED_HIGH);
1287     }
1288 
1289     @Test
testSetProperty_setsValue()1290     public void testSetProperty_setsValue() throws RemoteException {
1291         mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f);
1292 
1293         ArgumentCaptor<CarPropertyValue> value = ArgumentCaptor.forClass(CarPropertyValue.class);
1294 
1295         verify(mICarProperty).setProperty(value.capture(), any());
1296         assertThat(value.getValue().getValue()).isEqualTo(17.0f);
1297     }
1298 
1299     @Test
testSetProperty_syncOpTryAgain()1300     public void testSetProperty_syncOpTryAgain() throws RemoteException {
1301         doThrow(new ServiceSpecificException(SYNC_OP_LIMIT_TRY_AGAIN)).doNothing()
1302                 .when(mICarProperty).setProperty(any(), any());
1303 
1304         mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f);
1305 
1306         verify(mICarProperty, times(2)).setProperty(any(), any());
1307     }
1308 
1309     @Test
testSetProperty_unsupportedProperty()1310     public void testSetProperty_unsupportedProperty() throws RemoteException {
1311         assertThrows(IllegalArgumentException.class,
1312                 () -> mCarPropertyManager.setProperty(Float.class, INVALID, 0, 17.0f));
1313     }
1314 
createSetPropertyRequest()1315     private SetPropertyRequest<Float> createSetPropertyRequest() {
1316         return mCarPropertyManager.generateSetPropertyRequest(HVAC_TEMPERATURE_SET, 0,
1317                 Float.valueOf(17.0f));
1318     }
1319 
1320     @Test
testSetPropertiesAsync()1321     public void testSetPropertiesAsync() throws RemoteException {
1322         SetPropertyRequest<Float> setPropertyRequest = createSetPropertyRequest();
1323         setPropertyRequest.setUpdateRateHz(10.1f);
1324         setPropertyRequest.setWaitForPropertyUpdate(false);
1325         mCarPropertyManager.setPropertiesAsync(List.of(setPropertyRequest), null, null,
1326                 mSetPropertyCallback);
1327 
1328         verify(mICarProperty).setPropertiesAsync(mAsyncPropertyServiceRequestCaptor.capture(),
1329                 any(), anyLong());
1330 
1331         AsyncPropertyServiceRequest request = mAsyncPropertyServiceRequestCaptor.getValue()
1332                 .getList().get(0);
1333 
1334         assertThat(request.getRequestId()).isEqualTo(0);
1335         assertThat(request.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1336         assertThat(request.getAreaId()).isEqualTo(0);
1337         assertThat(request.isWaitForPropertyUpdate()).isFalse();
1338         assertThat(request.getUpdateRateHz()).isEqualTo(10.1f);
1339         CarPropertyValue requestValue = request.getCarPropertyValue();
1340         assertThat(requestValue).isNotNull();
1341         assertThat(requestValue.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1342         assertThat(requestValue.getAreaId()).isEqualTo(0);
1343         assertThat(requestValue.getValue()).isEqualTo(17.0f);
1344     }
1345 
1346     @Test
testSetPropertiesAsync_nullRequests()1347     public void testSetPropertiesAsync_nullRequests() throws RemoteException {
1348         assertThrows(NullPointerException.class,
1349                 () -> mCarPropertyManager.setPropertiesAsync(
1350                         null, null, null, mSetPropertyCallback));
1351     }
1352 
1353     @Test
testSetPropertiesAsync_nullCallback()1354     public void testSetPropertiesAsync_nullCallback() throws RemoteException {
1355         assertThrows(NullPointerException.class,
1356                 () -> mCarPropertyManager.setPropertiesAsync(
1357                         List.of(createSetPropertyRequest()), null, null, null));
1358     }
1359 
1360     @Test
testSetPropertiesAsyncWithTimeout()1361     public void testSetPropertiesAsyncWithTimeout() throws RemoteException {
1362         mCarPropertyManager.setPropertiesAsync(List.of(createSetPropertyRequest()),
1363                 /* timeoutInMs= */ 1000, /* cancellationSignal= */ null,
1364                 /* callbackExecutor= */ null, mSetPropertyCallback);
1365 
1366         verify(mICarProperty).setPropertiesAsync(mAsyncPropertyServiceRequestCaptor.capture(),
1367                 any(), eq(1000L));
1368         AsyncPropertyServiceRequest request = mAsyncPropertyServiceRequestCaptor.getValue()
1369                 .getList().get(0);
1370         assertThat(request.getRequestId()).isEqualTo(0);
1371         assertThat(request.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1372         assertThat(request.getAreaId()).isEqualTo(0);
1373         CarPropertyValue requestValue = request.getCarPropertyValue();
1374         assertThat(requestValue).isNotNull();
1375         assertThat(requestValue.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1376         assertThat(requestValue.getAreaId()).isEqualTo(0);
1377         assertThat(requestValue.getValue()).isEqualTo(17.0f);
1378     }
1379 
1380     @Test
testSetPropertiesAsync_illegalArgumentException()1381     public void testSetPropertiesAsync_illegalArgumentException() throws RemoteException {
1382         doThrow(new IllegalArgumentException()).when(mICarProperty).setPropertiesAsync(
1383                 any(AsyncPropertyServiceRequestList.class), any(IAsyncPropertyResultCallback.class),
1384                 anyLong());
1385 
1386         assertThrows(IllegalArgumentException.class,
1387                 () -> mCarPropertyManager.setPropertiesAsync(
1388                         List.of(createSetPropertyRequest()), null, null, mSetPropertyCallback));
1389     }
1390 
1391     @Test
testSetPropertiesAsync_SecurityException()1392     public void testSetPropertiesAsync_SecurityException() throws RemoteException {
1393         doThrow(new SecurityException()).when(mICarProperty).setPropertiesAsync(
1394                 any(AsyncPropertyServiceRequestList.class),
1395                 any(IAsyncPropertyResultCallback.class), anyLong());
1396 
1397         assertThrows(SecurityException.class,
1398                 () -> mCarPropertyManager.setPropertiesAsync(
1399                         List.of(createSetPropertyRequest()), null, null, mSetPropertyCallback));
1400     }
1401 
1402     @Test
testSetPropertiesAsync_unsupportedProperty()1403     public void testSetPropertiesAsync_unsupportedProperty() throws Exception {
1404         assertThrows(IllegalArgumentException.class,
1405                 () -> mCarPropertyManager.setPropertiesAsync(
1406                         List.of(mCarPropertyManager.generateSetPropertyRequest(
1407                                 INVALID, 0, Integer.valueOf(0))),
1408                         null, null, mSetPropertyCallback));
1409     }
1410 
1411     @Test
testSetPropertiesAsync_remoteException()1412     public void testSetPropertiesAsync_remoteException() throws RemoteException {
1413         doThrow(new RemoteException()).when(mICarProperty).setPropertiesAsync(
1414                 any(AsyncPropertyServiceRequestList.class),
1415                 any(IAsyncPropertyResultCallback.class), anyLong());
1416 
1417         mCarPropertyManager.setPropertiesAsync(List.of(createSetPropertyRequest()), null, null,
1418                 mSetPropertyCallback);
1419 
1420         verify(mCar).handleRemoteExceptionFromCarService(any(RemoteException.class));
1421     }
1422 
1423     @Test
testSetPropertiesAsync_duplicateRequestId()1424     public void testSetPropertiesAsync_duplicateRequestId() throws RemoteException {
1425         SetPropertyRequest request = createSetPropertyRequest();
1426 
1427         mCarPropertyManager.setPropertiesAsync(List.of(request), null, null,
1428                 mSetPropertyCallback);
1429 
1430         // Send the same request again with the same request ID is not allowed.
1431         assertThrows(IllegalArgumentException.class,
1432                 () -> mCarPropertyManager.setPropertiesAsync(List.of(request), null, null,
1433                         mSetPropertyCallback));
1434     }
1435 
1436     @Test
testSetPropertiesAsync_clearRequestIdAfterFailed()1437     public void testSetPropertiesAsync_clearRequestIdAfterFailed() throws RemoteException {
1438         SetPropertyRequest setPropertyRequest = createSetPropertyRequest();
1439         IllegalArgumentException exception = new IllegalArgumentException();
1440         doThrow(exception).when(mICarProperty).setPropertiesAsync(
1441                 any(AsyncPropertyServiceRequestList.class), any(IAsyncPropertyResultCallback.class),
1442                 anyLong());
1443 
1444         assertThrows(IllegalArgumentException.class,
1445                 () -> mCarPropertyManager.setPropertiesAsync(List.of(setPropertyRequest), null,
1446                         null, mSetPropertyCallback));
1447 
1448         clearInvocations(mICarProperty);
1449         doNothing().when(mICarProperty).setPropertiesAsync(
1450                 any(AsyncPropertyServiceRequestList.class),
1451                 any(IAsyncPropertyResultCallback.class), anyLong());
1452 
1453         // After the first request failed, the request ID map should be cleared so we can use the
1454         // same request ID again.
1455         mCarPropertyManager.setPropertiesAsync(List.of(setPropertyRequest), null, null,
1456                 mSetPropertyCallback);
1457 
1458         verify(mICarProperty).setPropertiesAsync(any(), any(), anyLong());
1459     }
1460 
1461     @Test
testSetProperty_notAvailableAfterU_withVendorErrorCode()1462     public void testSetProperty_notAvailableAfterU_withVendorErrorCode() throws Exception {
1463         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1464         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1465                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1466         doThrow(new ServiceSpecificException(combineErrors(
1467                 VehicleHalStatusCode.STATUS_NOT_AVAILABLE, VENDOR_ERROR_CODE))).when(mICarProperty)
1468                 .setProperty(eq(carPropertyValue), any());
1469 
1470         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1471                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1472         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1473     }
1474 
1475     @Test
testSetProperty_internalErrorAfterU_withVendorErrorCode()1476     public void testSetProperty_internalErrorAfterU_withVendorErrorCode() throws Exception {
1477         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1478         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1479                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1480         doThrow(new ServiceSpecificException(combineErrors(
1481                 VehicleHalStatusCode.STATUS_INTERNAL_ERROR, VENDOR_ERROR_CODE))).when(mICarProperty)
1482                 .setProperty(eq(carPropertyValue), any());
1483 
1484         CarInternalErrorException exception =  assertThrows(CarInternalErrorException.class,
1485                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1486         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1487     }
1488 
1489     @Test
testSetProperty_notAvailableDisabledBeforeU()1490     public void testSetProperty_notAvailableDisabledBeforeU() throws Exception {
1491         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
1492         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1493                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1494         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED))
1495                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1496 
1497         assertThrows(PropertyNotAvailableException.class,
1498                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1499     }
1500 
1501     @Test
testSetProperty_notAvailableDisabledAfterU()1502     public void testSetProperty_notAvailableDisabledAfterU() throws Exception {
1503         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1504         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1505                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1506         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED))
1507                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1508 
1509         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1510                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1511         assertThat(exception.getDetailedErrorCode())
1512                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_DISABLED);
1513     }
1514 
1515     @Test
testSetProperty_notAvailableDisabledAfterU_withVendorErrorCode()1516     public void testSetProperty_notAvailableDisabledAfterU_withVendorErrorCode() throws Exception {
1517         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1518         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1519                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1520         doThrow(new ServiceSpecificException(combineErrors(
1521                 VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED, VENDOR_ERROR_CODE)))
1522                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1523 
1524         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1525                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1526         assertThat(exception.getDetailedErrorCode())
1527                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_DISABLED);
1528         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1529     }
1530 
1531     @Test
testSetProperty_notAvailableSafetyBeforeU()1532     public void testSetProperty_notAvailableSafetyBeforeU() throws Exception {
1533         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
1534         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1535                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1536         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY))
1537                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1538 
1539         assertThrows(PropertyNotAvailableException.class,
1540                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1541     }
1542 
1543     @Test
testSetProperty_notAvailableSafetyAfterU()1544     public void testSetProperty_notAvailableSafetyAfterU() throws Exception {
1545         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1546         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1547                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1548         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY))
1549                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1550 
1551         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1552                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1553         assertThat(exception.getDetailedErrorCode())
1554                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SAFETY);
1555     }
1556 
1557     @Test
testSetProperty_notAvailableSafetyAfterU_withVendorErrorCode()1558     public void testSetProperty_notAvailableSafetyAfterU_withVendorErrorCode() throws Exception {
1559         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1560         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1561                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1562         doThrow(new ServiceSpecificException(combineErrors(
1563                 VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SAFETY, VENDOR_ERROR_CODE)))
1564                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1565 
1566         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1567                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1568         assertThat(exception.getDetailedErrorCode())
1569                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SAFETY);
1570         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1571     }
1572 
1573     @Test
testSetProperty_notAvailableSpeedHighBeforeU()1574     public void testSetProperty_notAvailableSpeedHighBeforeU() throws Exception {
1575         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
1576         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1577                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1578         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH))
1579                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1580 
1581         assertThrows(PropertyNotAvailableException.class,
1582                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1583     }
1584 
1585     @Test
testSetProperty_notAvailableSpeedHighAfterU()1586     public void testSetProperty_notAvailableSpeedHighAfterU() throws Exception {
1587         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1588         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1589                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1590         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH))
1591                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1592 
1593         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1594                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1595         assertThat(exception.getDetailedErrorCode())
1596                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_HIGH);
1597     }
1598 
1599     @Test
testSetProperty_notAvailableSpeedHighAfterU_withVendorErrorCode()1600     public void testSetProperty_notAvailableSpeedHighAfterU_withVendorErrorCode() throws Exception {
1601         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1602         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1603                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1604         doThrow(new ServiceSpecificException(combineErrors(
1605                 VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_HIGH, VENDOR_ERROR_CODE)))
1606                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1607 
1608         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1609                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1610         assertThat(exception.getDetailedErrorCode())
1611                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_HIGH);
1612         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1613     }
1614 
1615     @Test
testSetProperty_notAvailableSpeedLowBeforeU()1616     public void testSetProperty_notAvailableSpeedLowBeforeU() throws Exception {
1617         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
1618         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1619                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1620         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW))
1621                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1622 
1623         assertThrows(PropertyNotAvailableException.class,
1624                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1625     }
1626 
1627     @Test
testSetProperty_notAvailableSpeedLowAfterU()1628     public void testSetProperty_notAvailableSpeedLowAfterU() throws Exception {
1629         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1630         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1631                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1632         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW))
1633                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1634 
1635         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1636                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1637         assertThat(exception.getDetailedErrorCode())
1638                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_LOW);
1639     }
1640 
1641     @Test
testSetProperty_notAvailableSpeedLowAfterU_withVendorErrorCode()1642     public void testSetProperty_notAvailableSpeedLowAfterU_withVendorErrorCode() throws Exception {
1643         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
1644         CarPropertyValue<Float> carPropertyValue = new CarPropertyValue<>(
1645                 HVAC_TEMPERATURE_SET, 0, 17.0f);
1646         doThrow(new ServiceSpecificException(combineErrors(
1647                 VehicleHalStatusCode.STATUS_NOT_AVAILABLE_SPEED_LOW, VENDOR_ERROR_CODE)))
1648                 .when(mICarProperty).setProperty(eq(carPropertyValue), any());
1649 
1650         PropertyNotAvailableException exception =  assertThrows(PropertyNotAvailableException.class,
1651                 () -> mCarPropertyManager.setProperty(Float.class, HVAC_TEMPERATURE_SET, 0, 17.0f));
1652         assertThat(exception.getDetailedErrorCode())
1653                 .isEqualTo(PropertyNotAvailableErrorCode.NOT_AVAILABLE_SPEED_LOW);
1654         assertThat(exception.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1655     }
1656 
1657     @Test
testOnSetValueResult_onSuccess()1658     public void testOnSetValueResult_onSuccess() throws RemoteException {
1659         doAnswer((invocation) -> {
1660             Object[] args = invocation.getArguments();
1661             AsyncPropertyServiceRequestList setPropertyRequest =
1662                     (AsyncPropertyServiceRequestList) args[0];
1663             List setPropertyServiceList = setPropertyRequest.getList();
1664             AsyncPropertyServiceRequest setPropertyServiceRequest =
1665                     (AsyncPropertyServiceRequest) setPropertyServiceList.get(0);
1666             IAsyncPropertyResultCallback setAsyncPropertyResultCallback =
1667                     (IAsyncPropertyResultCallback) args[1];
1668 
1669             assertThat(setPropertyServiceRequest.getRequestId()).isEqualTo(0);
1670             assertThat(setPropertyServiceRequest.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1671             assertThat(setPropertyServiceRequest.getCarPropertyValue().getValue()).isEqualTo(
1672                     17.0f);
1673 
1674             GetSetValueResult setValueResult = GetSetValueResult.newSetValueResult(0,
1675                         /* updateTimestampNanos= */ TEST_TIMESTAMP);
1676 
1677             setAsyncPropertyResultCallback.onSetValueResults(
1678                     new GetSetValueResultList(List.of(setValueResult)));
1679             return null;
1680         }).when(mICarProperty).setPropertiesAsync(any(), any(), anyLong());
1681 
1682 
1683         mCarPropertyManager.setPropertiesAsync(List.of(createSetPropertyRequest()), null, null,
1684                 mSetPropertyCallback);
1685 
1686         verify(mSetPropertyCallback, timeout(1000)).onSuccess(mSetPropertyResultCaptor.capture());
1687         SetPropertyResult gotResult = mSetPropertyResultCaptor.getValue();
1688         assertThat(gotResult.getRequestId()).isEqualTo(0);
1689         assertThat(gotResult.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1690         assertThat(gotResult.getAreaId()).isEqualTo(0);
1691         assertThat(gotResult.getUpdateTimestampNanos()).isEqualTo(TEST_TIMESTAMP);
1692     }
1693 
1694     @Test
testOnSetValueResult_onSuccessMultipleRequests()1695     public void testOnSetValueResult_onSuccessMultipleRequests() throws RemoteException {
1696         doAnswer((invocation) -> {
1697             Object[] args = invocation.getArguments();
1698             AsyncPropertyServiceRequestList setPropertyServiceRequest =
1699                     (AsyncPropertyServiceRequestList) args[0];
1700             List<AsyncPropertyServiceRequest> setPropertyServiceRequests =
1701                     setPropertyServiceRequest.getList();
1702             IAsyncPropertyResultCallback setAsyncPropertyResultCallback =
1703                     (IAsyncPropertyResultCallback) args[1];
1704 
1705             assertThat(setPropertyServiceRequests.size()).isEqualTo(2);
1706             assertThat(setPropertyServiceRequests.get(0).getRequestId()).isEqualTo(0);
1707             assertThat(setPropertyServiceRequests.get(0).getPropertyId()).isEqualTo(
1708                     HVAC_TEMPERATURE_SET);
1709             assertThat(setPropertyServiceRequests.get(1).getRequestId()).isEqualTo(1);
1710             assertThat(setPropertyServiceRequests.get(1).getPropertyId()).isEqualTo(
1711                     HVAC_TEMPERATURE_SET);
1712 
1713             List<GetSetValueResult> setValueResults = List.of(
1714                     GetSetValueResult.newSetValueResult(0,
1715                             /* updateTimestampNanos= */ TEST_TIMESTAMP),
1716                     GetSetValueResult.newSetValueResult(1,
1717                             /* updateTimestampNanos= */ TEST_TIMESTAMP));
1718 
1719             setAsyncPropertyResultCallback.onSetValueResults(
1720                     new GetSetValueResultList(setValueResults));
1721             return null;
1722         }).when(mICarProperty).setPropertiesAsync(any(), any(), anyLong());
1723 
1724         List<SetPropertyRequest<?>> setPropertyRequests = new ArrayList<>();
1725         setPropertyRequests.add(createSetPropertyRequest());
1726         setPropertyRequests.add(createSetPropertyRequest());
1727 
1728         mCarPropertyManager.setPropertiesAsync(setPropertyRequests, null, null,
1729                 mSetPropertyCallback);
1730 
1731         verify(mSetPropertyCallback, timeout(1000).times(2)).onSuccess(
1732                 mSetPropertyResultCaptor.capture());
1733         List<SetPropertyResult> gotPropertyResults = mSetPropertyResultCaptor.getAllValues();
1734         assertThat(gotPropertyResults.get(0).getRequestId()).isEqualTo(0);
1735         assertThat(gotPropertyResults.get(0).getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1736         assertThat(gotPropertyResults.get(0).getAreaId()).isEqualTo(0);
1737         assertThat(gotPropertyResults.get(0).getUpdateTimestampNanos()).isEqualTo(TEST_TIMESTAMP);
1738         assertThat(gotPropertyResults.get(1).getRequestId()).isEqualTo(1);
1739         assertThat(gotPropertyResults.get(1).getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1740         assertThat(gotPropertyResults.get(1).getAreaId()).isEqualTo(0);
1741         assertThat(gotPropertyResults.get(1).getUpdateTimestampNanos()).isEqualTo(TEST_TIMESTAMP);
1742     }
1743 
1744     @Test
testOnSetValueResult_onFailure()1745     public void testOnSetValueResult_onFailure() throws RemoteException {
1746         doAnswer((invocation) -> {
1747             Object[] args = invocation.getArguments();
1748             AsyncPropertyServiceRequestList setPropertyService =
1749                     (AsyncPropertyServiceRequestList) args[0];
1750             List setPropertyServiceList = setPropertyService.getList();
1751             AsyncPropertyServiceRequest setPropertyServiceRequest =
1752                     (AsyncPropertyServiceRequest) setPropertyServiceList.get(0);
1753             IAsyncPropertyResultCallback setAsyncPropertyResultCallback =
1754                     (IAsyncPropertyResultCallback) args[1];
1755 
1756             assertThat(setPropertyServiceRequest.getRequestId()).isEqualTo(0);
1757             assertThat(setPropertyServiceRequest.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1758 
1759             GetSetValueResult setValueResult = GetSetValueResult.newErrorSetValueResult(
1760                     0,
1761                     new CarPropertyErrorCodes(
1762                             CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR, VENDOR_ERROR_CODE, 0));
1763 
1764             setAsyncPropertyResultCallback.onSetValueResults(
1765                     new GetSetValueResultList(List.of(setValueResult)));
1766             return null;
1767         }).when(mICarProperty).setPropertiesAsync(any(), any(), anyLong());
1768 
1769         mCarPropertyManager.setPropertiesAsync(List.of(createSetPropertyRequest()), null, null,
1770                 mSetPropertyCallback);
1771 
1772         verify(mSetPropertyCallback, timeout(1000)).onFailure(mPropertyAsyncErrorCaptor.capture());
1773         PropertyAsyncError error = mPropertyAsyncErrorCaptor.getValue();
1774         assertThat(error.getRequestId()).isEqualTo(0);
1775         assertThat(error.getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
1776         assertThat(error.getAreaId()).isEqualTo(0);
1777         assertThat(error.getErrorCode()).isEqualTo(
1778                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
1779         assertThat(error.getVendorErrorCode()).isEqualTo(VENDOR_ERROR_CODE);
1780     }
1781 
1782     @Test
testRegisterCallback_returnsFalseIfPropertyIdNotSupportedInVehicle()1783     public void testRegisterCallback_returnsFalseIfPropertyIdNotSupportedInVehicle()
1784             throws RemoteException {
1785         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1786                 VehiclePropertyIds.INVALID, FIRST_UPDATE_RATE_HZ)).isFalse();
1787         verify(mICarProperty, never()).registerListener(any(),
1788                 any(ICarPropertyEventListener.class));
1789     }
1790 
1791     @Test
testRegisterCallback_registersWithServiceOnFirstCallback()1792     public void testRegisterCallback_registersWithServiceOnFirstCallback() throws RemoteException {
1793         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1794                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
1795         verify(mICarProperty).registerListener(eq(List.of(
1796                 createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
1797                         FIRST_UPDATE_RATE_HZ))), any(ICarPropertyEventListener.class));
1798     }
1799 
1800     @Test
testRegisterCallback_registersWithMaxUpdateRateOnFirstCallback()1801     public void testRegisterCallback_registersWithMaxUpdateRateOnFirstCallback()
1802             throws RemoteException {
1803         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1804                 VENDOR_CONTINUOUS_PROPERTY, MAX_UPDATE_RATE_HZ + 1)).isTrue();
1805         verify(mICarProperty).registerListener(eq(List.of(
1806                 createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
1807                         MAX_UPDATE_RATE_HZ))), any(ICarPropertyEventListener.class));
1808     }
1809 
1810     @Test
testRegisterCallback_registersWithMinUpdateRateOnFirstCallback()1811     public void testRegisterCallback_registersWithMinUpdateRateOnFirstCallback()
1812             throws RemoteException {
1813         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1814                 VENDOR_CONTINUOUS_PROPERTY, MIN_UPDATE_RATE_HZ - 1)).isTrue();
1815         verify(mICarProperty).registerListener(eq(List.of(
1816                 createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
1817                         MIN_UPDATE_RATE_HZ))), any(ICarPropertyEventListener.class));
1818     }
1819 
1820     @Test
testRegisterCallback_registersWithOnChangeRateForOnChangeProperty()1821     public void testRegisterCallback_registersWithOnChangeRateForOnChangeProperty()
1822             throws RemoteException {
1823         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1824                 VENDOR_ON_CHANGE_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
1825         verify(mICarProperty).registerListener(eq(List.of(
1826                 createCarSubscriptionOption(VENDOR_ON_CHANGE_PROPERTY, new int[]{0},
1827                         CarPropertyManager.SENSOR_RATE_ONCHANGE))),
1828                 any(ICarPropertyEventListener.class));
1829     }
1830 
1831     @Test
testRegisterCallback_registersWithOnChangeRateForStaticProperty()1832     public void testRegisterCallback_registersWithOnChangeRateForStaticProperty()
1833             throws RemoteException {
1834         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1835                 VENDOR_STATIC_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
1836         verify(mICarProperty).registerListener(eq(List.of(
1837                 createCarSubscriptionOption(VENDOR_STATIC_PROPERTY, new int[]{0},
1838                         CarPropertyManager.SENSOR_RATE_ONCHANGE))),
1839                 any(ICarPropertyEventListener.class));
1840     }
1841 
1842     @Test
testRegisterCallback_returnsFalseForRemoteException()1843     public void testRegisterCallback_returnsFalseForRemoteException() throws RemoteException {
1844         RemoteException remoteException = new RemoteException();
1845         doThrow(remoteException).when(mICarProperty).registerListener(
1846                 eq(List.of(createCarSubscriptionOption(VENDOR_ON_CHANGE_PROPERTY, new int[] {0},
1847                         CarPropertyManager.SENSOR_RATE_ONCHANGE))),
1848                 any(ICarPropertyEventListener.class));
1849         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1850                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isFalse();
1851     }
1852 
1853     @Test
testRegisterCallback_recoversAfterFirstRemoteException()1854     public void testRegisterCallback_recoversAfterFirstRemoteException() throws RemoteException {
1855         RemoteException remoteException = new RemoteException();
1856         doThrow(remoteException).doNothing().when(mICarProperty)
1857                 .registerListener(eq(List.of(
1858                         createCarSubscriptionOption(VENDOR_ON_CHANGE_PROPERTY, new int[] {0},
1859                         SENSOR_RATE_ONCHANGE))),
1860                 any(ICarPropertyEventListener.class));
1861         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1862                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isFalse();
1863         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1864                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
1865         verify(mICarProperty, times(2)).registerListener(
1866                 eq(List.of(createCarSubscriptionOption(VENDOR_ON_CHANGE_PROPERTY, new int[] {0},
1867                         CarPropertyManager.SENSOR_RATE_ONCHANGE))),
1868                 any(ICarPropertyEventListener.class));
1869     }
1870 
1871     @Test
testRegisterCallback_returnsFalseForIllegalArgumentException()1872     public void testRegisterCallback_returnsFalseForIllegalArgumentException()
1873             throws RemoteException {
1874         doThrow(IllegalArgumentException.class).when(mICarProperty)
1875                 .registerListener(eq(List.of(createCarSubscriptionOption(
1876                         VENDOR_ON_CHANGE_PROPERTY, new int[] {0}, SENSOR_RATE_ONCHANGE))),
1877                 any(ICarPropertyEventListener.class));
1878 
1879         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1880                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isFalse();
1881     }
1882 
1883     @Test
testRegisterCallback_registersTwiceWithHigherRateCallback()1884     public void testRegisterCallback_registersTwiceWithHigherRateCallback() 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, LARGER_UPDATE_RATE_HZ)).isTrue();
1889         verify(mICarProperty, times(2)).registerListener(
1890                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
1891         assertThat(mCarSubscriptionCaptor.getAllValues()).containsExactly(
1892                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1893                         LARGER_UPDATE_RATE_HZ)),
1894                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1895                         FIRST_UPDATE_RATE_HZ)));
1896     }
1897 
1898     @Test
testRegisterCallback_registersOnSecondHigherRateWithSameCallback()1899     public void testRegisterCallback_registersOnSecondHigherRateWithSameCallback()
1900             throws RemoteException {
1901         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1902                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
1903         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback2,
1904                 VENDOR_CONTINUOUS_PROPERTY, LARGER_UPDATE_RATE_HZ)).isTrue();
1905         verify(mICarProperty, times(2)).registerListener(
1906                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
1907         assertThat(mCarSubscriptionCaptor.getAllValues()).containsExactly(
1908                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1909                         LARGER_UPDATE_RATE_HZ)),
1910                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1911                         FIRST_UPDATE_RATE_HZ)));
1912     }
1913 
1914     @Test
testRegisterCallback_registersOnSecondLowerRateWithSameCallback()1915     public void testRegisterCallback_registersOnSecondLowerRateWithSameCallback()
1916             throws RemoteException {
1917         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1918                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
1919         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1920                 VENDOR_CONTINUOUS_PROPERTY, SMALLER_UPDATE_RATE_HZ)).isTrue();
1921         verify(mICarProperty, times(2)).registerListener(
1922                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
1923         assertThat(mCarSubscriptionCaptor.getAllValues()).containsExactly(
1924                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1925                         FIRST_UPDATE_RATE_HZ)),
1926                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1927                         SMALLER_UPDATE_RATE_HZ)));
1928     }
1929 
1930     @Test
testRegisterCallback_doesNotRegistersOnSecondLowerRateCallback()1931     public void testRegisterCallback_doesNotRegistersOnSecondLowerRateCallback()
1932             throws RemoteException {
1933         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1934                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
1935         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback2,
1936                 VENDOR_CONTINUOUS_PROPERTY, SMALLER_UPDATE_RATE_HZ)).isTrue();
1937         verify(mICarProperty).registerListener(eq(List.of(
1938                 createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
1939                         FIRST_UPDATE_RATE_HZ))),
1940                 any(ICarPropertyEventListener.class));
1941     }
1942 
1943     @Test
testRegisterCallback_registersTwiceForDifferentProperties()1944     public void testRegisterCallback_registersTwiceForDifferentProperties() throws RemoteException {
1945         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1946                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
1947         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
1948                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
1949         verify(mICarProperty, times(2)).registerListener(
1950                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
1951         assertThat(mCarSubscriptionCaptor.getAllValues()).containsExactly(
1952                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1953                         FIRST_UPDATE_RATE_HZ)),
1954                 List.of(createCarSubscriptionOption(VENDOR_ON_CHANGE_PROPERTY, new int[] {0},
1955                         CarPropertyManager.SENSOR_RATE_ONCHANGE)));
1956     }
1957 
1958     @Test
testRegisterCallback_registerInvalidProp()1959     public void testRegisterCallback_registerInvalidProp() throws Exception {
1960         assertThat(mCarPropertyManager.registerCallback(
1961                 mCarPropertyEventCallback, /* propertyId= */ -1, FIRST_UPDATE_RATE_HZ)).isFalse();
1962     }
1963 
1964     // We annotate the update rate to be within 0 and 100, however, in the actual implementation,
1965     // we will fit update rate outside this range to the min and max update rate.
1966     @Test
testRegisterCallback_updateRateOutsideFloatRangeMustNotThrow_tooLarge()1967     public void testRegisterCallback_updateRateOutsideFloatRangeMustNotThrow_tooLarge()
1968             throws Exception {
1969         assertThat(mCarPropertyManager.registerCallback(
1970                 mCarPropertyEventCallback, VENDOR_CONTINUOUS_PROPERTY, /* updateRateHz= */ 101.f))
1971                 .isTrue();
1972 
1973         verify(mICarProperty).registerListener(
1974                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
1975         assertThat(mCarSubscriptionCaptor.getValue()).isEqualTo(
1976                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1977                         MAX_UPDATE_RATE_HZ)));
1978     }
1979 
1980     @Test
testRegisterCallback_updateRateOutsideFloatRangeMustNotThrow_tooSmall()1981     public void testRegisterCallback_updateRateOutsideFloatRangeMustNotThrow_tooSmall()
1982             throws Exception {
1983         assertThat(mCarPropertyManager.registerCallback(
1984                 mCarPropertyEventCallback, VENDOR_CONTINUOUS_PROPERTY, /* updateRateHz= */ -1.f))
1985                 .isTrue();
1986 
1987         verify(mICarProperty).registerListener(
1988                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
1989         assertThat(mCarSubscriptionCaptor.getValue()).isEqualTo(
1990                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
1991                         MIN_UPDATE_RATE_HZ)));
1992     }
1993 
1994     @Test
testRegisterCallback_isSupportedAndHasWritePermissionOnly()1995     public void testRegisterCallback_isSupportedAndHasWritePermissionOnly() throws Exception {
1996         int propId = VENDOR_CONTINUOUS_PROPERTY;
1997         when(mICarProperty.isSupportedAndHasWritePermissionOnly(propId)).thenReturn(true);
1998 
1999         assertThrows(SecurityException.class, () -> mCarPropertyManager.registerCallback(
2000                 mCarPropertyEventCallback, VENDOR_CONTINUOUS_PROPERTY, 0));
2001     }
2002 
2003     @Test
testRegisterCallback_isSupportedAndHasWritePermissionOnly_remoteException()2004     public void testRegisterCallback_isSupportedAndHasWritePermissionOnly_remoteException()
2005             throws Exception {
2006         int propId = VENDOR_CONTINUOUS_PROPERTY;
2007         when(mICarProperty.isSupportedAndHasWritePermissionOnly(propId)).thenThrow(
2008                 new RemoteException());
2009 
2010         assertThat(mCarPropertyManager.registerCallback(
2011                 mCarPropertyEventCallback, VENDOR_CONTINUOUS_PROPERTY, 0)).isFalse();
2012     }
2013 
2014     @Test
testSubscribePropertyEvents_registerMultipleEvents()2015     public void testSubscribePropertyEvents_registerMultipleEvents() throws Exception {
2016         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2017                 new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2018                         .setUpdateRateFast().build(),
2019                 new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).addAreaId(0).build()),
2020                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2021 
2022         verify(mICarProperty).registerListener(
2023                 eq(List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2024                         CarPropertyManager.SENSOR_RATE_FAST, /* enableVur= */ true),
2025                         createCarSubscriptionOption(VENDOR_ON_CHANGE_PROPERTY, new int[]{0},
2026                                 CarPropertyManager.SENSOR_RATE_ONCHANGE))),
2027                 any(ICarPropertyEventListener.class));
2028     }
2029 
2030     @Test
testSubscribePropertyEvents_sanitizeSampleRate()2031     public void testSubscribePropertyEvents_sanitizeSampleRate() throws Exception {
2032         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2033                 new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2034                         .setUpdateRateHz(0.f).build()),
2035                 /* callbackExecutor= */ null, mCarPropertyEventCallback))
2036                 .isTrue();
2037 
2038         verify(mICarProperty).registerListener(
2039                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
2040         assertThat(mCarSubscriptionCaptor.getValue()).isEqualTo(
2041                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[] {0},
2042                         MIN_UPDATE_RATE_HZ, /* enableVur= */ true)));
2043     }
2044 
2045     @Test
testSubscribePropertyEvents_registerMultipleEventsSameProperty_throws()2046     public void testSubscribePropertyEvents_registerMultipleEventsSameProperty_throws()
2047             throws Exception {
2048         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
2049                 () -> mCarPropertyManager.subscribePropertyEvents(List.of(
2050                                 new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2051                                         .addAreaId(0).setUpdateRateFast().build(),
2052                                 new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2053                                         .addAreaId(0).build()),
2054                         /* callbackExecutor= */ null, mCarPropertyEventCallback));
2055 
2056         assertWithMessage("Overlapping areaIds").that(thrown).hasMessageThat()
2057                 .contains("Subscribe options contain overlapping propertyId");
2058     }
2059 
2060     @Test
testSubscribePropertyEvents_registerMultipleEvents_unsubscribe()2061     public void testSubscribePropertyEvents_registerMultipleEvents_unsubscribe() throws Exception {
2062         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2063                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2064                                 .setUpdateRateFast().build(),
2065                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).addAreaId(0)
2066                                 .build()),
2067                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2068 
2069         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback);
2070         verify(mICarProperty).unregisterListener(eq(VENDOR_CONTINUOUS_PROPERTY),
2071                 any(ICarPropertyEventListener.class));
2072         verify(mICarProperty).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2073                 any(ICarPropertyEventListener.class));
2074     }
2075 
2076     @Test
testSubscribePropertyEvents_registerMultipleEventsWithSameProperty_unsubscribe()2077     public void testSubscribePropertyEvents_registerMultipleEventsWithSameProperty_unsubscribe()
2078             throws Exception {
2079         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2080                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2081                                 .setUpdateRateFastest().build(),
2082                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).addAreaId(0)
2083                                 .build()),
2084                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2085         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2086                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2087                                 .setUpdateRateFast().build()),
2088                 /* callbackExecutor= */ null, mCarPropertyEventCallback2)).isTrue();
2089         clearInvocations(mICarProperty);
2090         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback);
2091 
2092         verify(mICarProperty).registerListener(eq(List.of(
2093                         createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2094                                 CarPropertyManager.SENSOR_RATE_FAST, /* enableVur= */ true))),
2095                 any(ICarPropertyEventListener.class));
2096         verify(mICarProperty).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2097                 any(ICarPropertyEventListener.class));
2098     }
2099 
2100     @Test
testSubscribePropertyEvents_withPropertyIdCallback()2101     public void testSubscribePropertyEvents_withPropertyIdCallback() throws Exception {
2102         assertThat(mCarPropertyManager.subscribePropertyEvents(
2103                 VENDOR_CONTINUOUS_PROPERTY, mCarPropertyEventCallback)).isTrue();
2104 
2105         verify(mICarProperty).registerListener(eq(List.of(
2106                         createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2107                                 MIN_UPDATE_RATE_HZ, /* enableVur= */ true))),
2108                 any(ICarPropertyEventListener.class));
2109     }
2110 
2111     @Test
testSubscribePropertyEvents_withPropertyIdUpdateRateCallback()2112     public void testSubscribePropertyEvents_withPropertyIdUpdateRateCallback() throws Exception {
2113         assertThat(mCarPropertyManager.subscribePropertyEvents(
2114                 VENDOR_CONTINUOUS_PROPERTY, CarPropertyManager.SENSOR_RATE_FAST,
2115                 mCarPropertyEventCallback)).isTrue();
2116 
2117         verify(mICarProperty).registerListener(eq(List.of(
2118                         createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2119                                 CarPropertyManager.SENSOR_RATE_FAST, /* enableVur= */ true))),
2120                 any(ICarPropertyEventListener.class));
2121     }
2122 
2123     @Test
testSubscribePropertyEvents_withPropertyIdAreaIdCallback()2124     public void testSubscribePropertyEvents_withPropertyIdAreaIdCallback() throws Exception {
2125         assertThat(mCarPropertyManager.subscribePropertyEvents(
2126                 VENDOR_CONTINUOUS_PROPERTY, /* areaId= */ 0,
2127                 mCarPropertyEventCallback)).isTrue();
2128 
2129         verify(mICarProperty).registerListener(eq(List.of(
2130                         createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2131                                 MIN_UPDATE_RATE_HZ, /* enableVur= */ true))),
2132                 any(ICarPropertyEventListener.class));
2133     }
2134 
2135     @Test
testSubscribePropertyEvents_withPropertyIdAreaIdUpdateRateCallback()2136     public void testSubscribePropertyEvents_withPropertyIdAreaIdUpdateRateCallback()
2137                 throws Exception {
2138         assertThat(mCarPropertyManager.subscribePropertyEvents(
2139                 VENDOR_CONTINUOUS_PROPERTY, /* areaId= */ 0,
2140                 CarPropertyManager.SENSOR_RATE_FAST,
2141                 mCarPropertyEventCallback)).isTrue();
2142 
2143         verify(mICarProperty).registerListener(eq(List.of(
2144                         createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2145                                 CarPropertyManager.SENSOR_RATE_FAST, /* enableVur= */ true))),
2146                 any(ICarPropertyEventListener.class));
2147     }
2148 
2149     // TODO(b/372530534): remove this once we remove the flag.
2150     @Test
testSubscribePropertyEvents_doNotSendGetInitialValueRequest_flagDisabled()2151     public void testSubscribePropertyEvents_doNotSendGetInitialValueRequest_flagDisabled()
2152             throws Exception {
2153         when(mFeatureFlags.alwaysSendInitialValueEvent()).thenReturn(false);
2154 
2155         mCarPropertyManager.subscribePropertyEvents(
2156                 VENDOR_ON_CHANGE_PROPERTY, /* areaId= */ 0,
2157                 mCarPropertyEventCallback);
2158         mCarPropertyManager.subscribePropertyEvents(
2159                 VENDOR_ON_CHANGE_PROPERTY, /* areaId= */ 0,
2160                 mCarPropertyEventCallback);
2161 
2162         verify(mICarProperty, never()).getAndDispatchInitialValue(any(), any());
2163     }
2164 
2165     @Test
testSubscribePropertyEvents_doNotSendGetInitialValueRequest_legacyClient()2166     public void testSubscribePropertyEvents_doNotSendGetInitialValueRequest_legacyClient()
2167             throws Exception {
2168         setAppTargetSdk(Build.VERSION_CODES.VANILLA_ICE_CREAM);
2169 
2170         mCarPropertyManager.subscribePropertyEvents(
2171                 VENDOR_ON_CHANGE_PROPERTY, /* areaId= */ 0,
2172                 mCarPropertyEventCallback);
2173         mCarPropertyManager.subscribePropertyEvents(
2174                 VENDOR_ON_CHANGE_PROPERTY, /* areaId= */ 0,
2175                 mCarPropertyEventCallback);
2176 
2177         verify(mICarProperty, never()).getAndDispatchInitialValue(any(), any());
2178     }
2179 
2180     // Make sure that if a the subscription request does not need to be sent to car service, we will
2181     // send the getAndDispatchInitialValue request to make sure initial values are generated.
2182     @Test
testSubscribePropertyEvents_sendGetInitialValueRequest_sameCallback()2183     public void testSubscribePropertyEvents_sendGetInitialValueRequest_sameCallback()
2184             throws Exception {
2185         mCarPropertyManager.subscribePropertyEvents(
2186                 VENDOR_ON_CHANGE_PROPERTY, /* areaId= */ 0,
2187                 mCarPropertyEventCallback);
2188         mCarPropertyManager.subscribePropertyEvents(
2189                 VENDOR_ON_CHANGE_PROPERTY, /* areaId= */ 0,
2190                 mCarPropertyEventCallback);
2191 
2192         PropIdAreaId propIdAreaId = new PropIdAreaId();
2193         propIdAreaId.propId = VENDOR_ON_CHANGE_PROPERTY;
2194         propIdAreaId.areaId = 0;
2195         verify(mICarProperty).getAndDispatchInitialValue(eq(List.of(propIdAreaId)),
2196                 any(ICarPropertyEventListener.class));
2197     }
2198 
2199     // Make sure that if a the subscription request does not need to be sent to car service, we will
2200     // send the getAndDispatchInitialValue request to make sure initial values are generated.
2201     @Test
testSubscribePropertyEvents_sendGetInitialValueRequest_differentCallback()2202     public void testSubscribePropertyEvents_sendGetInitialValueRequest_differentCallback()
2203             throws Exception {
2204         mCarPropertyManager.subscribePropertyEvents(
2205                 VENDOR_ON_CHANGE_PROPERTY, /* areaId= */ 0,
2206                 mCarPropertyEventCallback);
2207         mCarPropertyManager.subscribePropertyEvents(List.of(
2208                 new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2209                         .setUpdateRateFast().build(),
2210                 new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).addAreaId(0)
2211                         .build()), /* callbackExecutor= */ null, mCarPropertyEventCallback2);
2212 
2213         // VENDOR_ON_CHANGE_PROPERTY is already subscribed, so we need initial value event for it.
2214         PropIdAreaId propIdAreaId = new PropIdAreaId();
2215         propIdAreaId.propId = VENDOR_ON_CHANGE_PROPERTY;
2216         propIdAreaId.areaId = 0;
2217         verify(mICarProperty).getAndDispatchInitialValue(eq(List.of(propIdAreaId)),
2218                 any(ICarPropertyEventListener.class));
2219     }
2220 
2221     @Test
testSubscribePropertyEvents_noReadPermission()2222     public void testSubscribePropertyEvents_noReadPermission() throws Exception {
2223         int propId = VENDOR_CONTINUOUS_PROPERTY;
2224         when(mICarProperty.getSupportedNoReadPermPropIds(any()))
2225                 .thenReturn(new int[] {propId});
2226 
2227         assertThrows(SecurityException.class, () -> mCarPropertyManager.subscribePropertyEvents(
2228                 propId, mCarPropertyEventCallback));
2229     }
2230 
2231     @Test
testSubscribePropertyEvents_getSupportedNoReadPermPropIds_remoteException()2232     public void testSubscribePropertyEvents_getSupportedNoReadPermPropIds_remoteException()
2233             throws Exception {
2234         int propId = VENDOR_CONTINUOUS_PROPERTY;
2235         when(mICarProperty.getSupportedNoReadPermPropIds(any()))
2236                 .thenThrow(new RemoteException());
2237 
2238         assertThat(mCarPropertyManager.subscribePropertyEvents(
2239                 propId, mCarPropertyEventCallback)).isFalse();
2240     }
2241 
2242     @Test
testRegisterCallback_registersAgainWithDifferentExecutor_throws()2243     public void testRegisterCallback_registersAgainWithDifferentExecutor_throws() throws Exception {
2244         mCarPropertyManager.subscribePropertyEvents(List.of(
2245                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2246                                 .setUpdateRateFast().build(),
2247                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).addAreaId(0)
2248                                 .build()),
2249                 mMockExecutor1, mCarPropertyEventCallback);
2250 
2251         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
2252                 mCarPropertyManager.subscribePropertyEvents(List.of(
2253                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2254                                 .setUpdateRateFast().build(),
2255                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).addAreaId(0)
2256                                 .build()),
2257                 mMockExecutor2, mCarPropertyEventCallback));
2258         assertWithMessage("Two executor subscription").that(thrown).hasMessageThat()
2259                 .contains("A different executor is already associated with this callback,"
2260                         + " please use the same executor");
2261     }
2262 
2263     @Test
testRegisterCallback_registersAgainWithSameExecutor()2264     public void testRegisterCallback_registersAgainWithSameExecutor() throws Exception {
2265         mCarPropertyManager.subscribePropertyEvents(List.of(
2266                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2267                                 .setUpdateRateFast().build(),
2268                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).addAreaId(0)
2269                                 .build()),
2270                 mMockExecutor1, mCarPropertyEventCallback);
2271         mCarPropertyManager.subscribePropertyEvents(List.of(
2272                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY).addAreaId(0)
2273                                 .setUpdateRateFast().build(),
2274                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).addAreaId(0)
2275                                 .build()),
2276                 mMockExecutor1, mCarPropertyEventCallback);
2277 
2278         verify(mICarProperty).registerListener(
2279                 eq(List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2280                         CarPropertyManager.SENSOR_RATE_FAST, /* enableVur= */ true),
2281                         createCarSubscriptionOption(VENDOR_ON_CHANGE_PROPERTY, new int[]{0},
2282                                 CarPropertyManager.SENSOR_RATE_ONCHANGE))),
2283                 any(ICarPropertyEventListener.class));
2284     }
2285 
2286     @Test
testRegisterCallback_registersAgainIfTheFirstCallbackReturnsFalse()2287     public void testRegisterCallback_registersAgainIfTheFirstCallbackReturnsFalse()
2288             throws RemoteException {
2289         doThrow(IllegalArgumentException.class).doNothing().when(mICarProperty)
2290                 .registerListener(eq(List.of(
2291                                 createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY,
2292                                         new int[]{0}, FIRST_UPDATE_RATE_HZ))),
2293                 any(ICarPropertyEventListener.class));
2294 
2295         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2296                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isFalse();
2297         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2298                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2299     }
2300 
2301     @Test
testRegisterCallback_restoresOriginalRateHzIfTheSecondCallbackReturnsFalse()2302     public void testRegisterCallback_restoresOriginalRateHzIfTheSecondCallbackReturnsFalse()
2303             throws RemoteException {
2304         Float smallerUpdate = 1F;
2305         Float largerUpdate = 2F;
2306         doThrow(IllegalArgumentException.class).when(mICarProperty)
2307                 .registerListener(eq(List.of(createCarSubscriptionOption(
2308                         HVAC_TEMPERATURE_SET, new int[]{0}, largerUpdate))),
2309                 any(ICarPropertyEventListener.class));
2310 
2311         CarPropertyValue<Float> goodValue = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
2312                 Duration.ofSeconds(1).toNanos(), 17.0f);
2313         CarPropertyValue<Float> almostFreshValue = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
2314                 Duration.ofMillis(1899).toNanos(), 18.0f);
2315         List<CarPropertyEvent> eventList = List.of(
2316                 new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, goodValue),
2317                 new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE,
2318                         almostFreshValue)
2319         );
2320         CarPropertyConfig config = CarPropertyConfig.newBuilder(Float.class, HVAC_TEMPERATURE_SET,
2321                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
2322                 .addAreaIdConfig(new AreaIdConfig.Builder<Float>(0).build())
2323                 .setChangeMode(CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS)
2324                 .setMinSampleRate(0f)
2325                 .setMaxSampleRate(10f).build();
2326         addCarPropertyConfig(config);
2327         ICarPropertyEventListener listener = getCarPropertyEventListener();
2328         ArgumentCaptor<CarPropertyValue> valueCaptor =
2329                 ArgumentCaptor.forClass(CarPropertyValue.class);
2330 
2331         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2332                 HVAC_TEMPERATURE_SET, smallerUpdate)).isTrue();
2333         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2334                 HVAC_TEMPERATURE_SET, largerUpdate)).isFalse();
2335         verify(mICarProperty, times(3)).registerListener(
2336                 any(), any(ICarPropertyEventListener.class));
2337 
2338         listener.onEvent(eventList);
2339         verify(mCarPropertyEventCallback, timeout(5000)).onChangeEvent(valueCaptor.capture());
2340         assertThat(valueCaptor.getAllValues()).containsExactly(goodValue);
2341     }
2342 
2343     @Test
testUnregisterCallback_doesNothingIfNothingRegistered()2344     public void testUnregisterCallback_doesNothingIfNothingRegistered() throws Exception {
2345         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback, VENDOR_STATIC_PROPERTY);
2346         verify(mICarProperty, never()).unregisterListener(anyInt(),
2347                 any(ICarPropertyEventListener.class));
2348     }
2349 
2350     @Test
testUnsubscribePropertyEvents_doesNothingIfNothingRegistered()2351     public void testUnsubscribePropertyEvents_doesNothingIfNothingRegistered() throws Exception {
2352         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_STATIC_PROPERTY,
2353                 mCarPropertyEventCallback);
2354         verify(mICarProperty, never()).unregisterListener(anyInt(),
2355                 any(ICarPropertyEventListener.class));
2356     }
2357 
2358     @Test
testUnregisterCallback_doesNothingIfPropertyIsNotRegisteredForCallback()2359     public void testUnregisterCallback_doesNothingIfPropertyIsNotRegisteredForCallback()
2360             throws Exception {
2361         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2362                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2363 
2364         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback, VENDOR_STATIC_PROPERTY);
2365         verify(mICarProperty, never()).unregisterListener(anyInt(),
2366                 any(ICarPropertyEventListener.class));
2367     }
2368 
2369     @Test
testUnsubscribePropertyEvents_doesNothingIfNothingIsNotRegisteredForCallback()2370     public void testUnsubscribePropertyEvents_doesNothingIfNothingIsNotRegisteredForCallback()
2371             throws Exception {
2372         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2373                 new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2374                         .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2375                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2376 
2377         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_STATIC_PROPERTY,
2378                 mCarPropertyEventCallback);
2379         verify(mICarProperty, never()).unregisterListener(anyInt(),
2380                 any(ICarPropertyEventListener.class));
2381     }
2382 
2383     @Test
testUnregisterCallback_doesNothingIfCallbackIsNotRegisteredForProperty()2384     public void testUnregisterCallback_doesNothingIfCallbackIsNotRegisteredForProperty()
2385             throws Exception {
2386         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2387                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2388 
2389         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback2,
2390                 VENDOR_CONTINUOUS_PROPERTY);
2391         verify(mICarProperty, never()).unregisterListener(anyInt(),
2392                 any(ICarPropertyEventListener.class));
2393     }
2394 
2395     @Test
testUnsubscribePropertyEvents_doesNothingIfCallbackIsNotRegisteredForProperty()2396     public void testUnsubscribePropertyEvents_doesNothingIfCallbackIsNotRegisteredForProperty()
2397             throws Exception {
2398         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2399                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2400                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2401                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2402 
2403         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_CONTINUOUS_PROPERTY,
2404                 mCarPropertyEventCallback2);
2405         verify(mICarProperty, never()).unregisterListener(anyInt(),
2406                 any(ICarPropertyEventListener.class));
2407     }
2408 
2409     @Test
testUnregisterCallback_doesNothingIfCarPropertyConfigNull()2410     public void testUnregisterCallback_doesNothingIfCarPropertyConfigNull() throws Exception {
2411         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2412                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2413 
2414         setPropIdWithoutConfig(VENDOR_CONTINUOUS_PROPERTY);
2415 
2416         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2417                 VENDOR_CONTINUOUS_PROPERTY);
2418         verify(mICarProperty, never()).unregisterListener(anyInt(),
2419                 any(ICarPropertyEventListener.class));
2420     }
2421 
2422     @Test
testUnregisterCallback_doesNothingIfNoPermission()2423     public void testUnregisterCallback_doesNothingIfNoPermission() throws Exception {
2424         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2425                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2426 
2427         setPropIdWithoutPermission(VENDOR_CONTINUOUS_PROPERTY);
2428 
2429         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2430                 VENDOR_CONTINUOUS_PROPERTY);
2431         verify(mICarProperty, never()).unregisterListener(anyInt(),
2432                 any(ICarPropertyEventListener.class));
2433     }
2434 
2435     @Test
testUnsubscribePropertyEvents_doesNothingIfCarPropertyConfigNull()2436     public void testUnsubscribePropertyEvents_doesNothingIfCarPropertyConfigNull()
2437             throws Exception {
2438         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2439                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2440                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2441                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2442 
2443         setPropIdWithoutConfig(VENDOR_CONTINUOUS_PROPERTY);
2444 
2445         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_CONTINUOUS_PROPERTY,
2446                 mCarPropertyEventCallback);
2447         verify(mICarProperty, never()).unregisterListener(anyInt(),
2448                 any(ICarPropertyEventListener.class));
2449     }
2450 
2451     @Test
testUnsubscribePropertyEvents_doesNothingIfNoPermission()2452     public void testUnsubscribePropertyEvents_doesNothingIfNoPermission()
2453             throws Exception {
2454         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2455                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2456                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2457                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2458 
2459         setPropIdWithoutPermission(VENDOR_CONTINUOUS_PROPERTY);
2460 
2461         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_CONTINUOUS_PROPERTY,
2462                 mCarPropertyEventCallback);
2463         verify(mICarProperty, never()).unregisterListener(anyInt(),
2464                 any(ICarPropertyEventListener.class));
2465     }
2466 
2467     @Test
testUnregisterCallback_unregistersCallbackForSingleProperty()2468     public void testUnregisterCallback_unregistersCallbackForSingleProperty()
2469             throws Exception {
2470         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2471                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2472 
2473         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2474                 VENDOR_CONTINUOUS_PROPERTY);
2475         verify(mICarProperty).unregisterListener(eq(VENDOR_CONTINUOUS_PROPERTY),
2476                 any(ICarPropertyEventListener.class));
2477     }
2478 
2479     @Test
testUnregisterCallback_unsubscribeCallbackForSingleProperty()2480     public void testUnregisterCallback_unsubscribeCallbackForSingleProperty()
2481             throws Exception {
2482         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2483                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2484                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2485                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2486 
2487         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2488                 VENDOR_CONTINUOUS_PROPERTY);
2489         verify(mICarProperty).unregisterListener(eq(VENDOR_CONTINUOUS_PROPERTY),
2490                 any(ICarPropertyEventListener.class));
2491     }
2492 
2493     @Test
testUnregisterCallback_unregistersCallbackForSpecificProperty()2494     public void testUnregisterCallback_unregistersCallbackForSpecificProperty()
2495             throws Exception {
2496         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2497                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2498         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2499                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
2500 
2501         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2502                 VENDOR_ON_CHANGE_PROPERTY);
2503         verify(mICarProperty).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2504                 any(ICarPropertyEventListener.class));
2505     }
2506 
2507     @Test
testUnsubscribePropertyEvents_ignoreUserHalProp()2508     public void testUnsubscribePropertyEvents_ignoreUserHalProp()
2509             throws Exception {
2510         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
2511 
2512         mCarPropertyManager.unsubscribePropertyEvents(INITIAL_USER_INFO,
2513                 mCarPropertyEventCallback);
2514 
2515         verify(mICarProperty, never()).unregisterListener(anyInt(), any());
2516     }
2517 
2518     @Test
testUnregisterCallback_ignoreUserHalProp_beforeU()2519     public void testUnregisterCallback_ignoreUserHalProp_beforeU()
2520             throws Exception {
2521         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
2522 
2523         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback, INITIAL_USER_INFO);
2524 
2525         verify(mICarProperty, never()).unregisterListener(anyInt(), any());
2526     }
2527 
2528     @Test
testUnregisterCallback_ignoreUserHalProp_afterU()2529     public void testUnregisterCallback_ignoreUserHalProp_afterU()
2530             throws Exception {
2531         setAppTargetSdk(Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
2532 
2533         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback, INITIAL_USER_INFO);
2534 
2535         verify(mICarProperty, never()).unregisterListener(anyInt(), any());
2536     }
2537 
2538     @Test
testUnsubscribePropertyEvents_unsubscribeCallbackForSpecificProperty()2539     public void testUnsubscribePropertyEvents_unsubscribeCallbackForSpecificProperty()
2540             throws Exception {
2541         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2542                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2543                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build(),
2544                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY)
2545                                 .build()),
2546                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2547 
2548         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2549                 VENDOR_CONTINUOUS_PROPERTY);
2550         verify(mICarProperty).unregisterListener(eq(VENDOR_CONTINUOUS_PROPERTY),
2551                 any(ICarPropertyEventListener.class));
2552         verify(mICarProperty, never()).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2553                 any(ICarPropertyEventListener.class));
2554     }
2555 
2556     @Test
testUnregisterCallback_unregistersCallbackForBothProperties()2557     public void testUnregisterCallback_unregistersCallbackForBothProperties()
2558             throws Exception {
2559         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2560                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2561         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2562                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
2563 
2564         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback);
2565         verify(mICarProperty, times(2)).unregisterListener(mPropertyIdCaptor.capture(),
2566                 any(ICarPropertyEventListener.class));
2567         assertThat(mPropertyIdCaptor.getAllValues()).containsExactly(VENDOR_CONTINUOUS_PROPERTY,
2568                 VENDOR_ON_CHANGE_PROPERTY);
2569     }
2570 
2571     @Test
testUnsubscribePropertyEvents_unsubscribeCallbackForBothProperties()2572     public void testUnsubscribePropertyEvents_unsubscribeCallbackForBothProperties()
2573             throws Exception {
2574         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2575                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2576                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build(),
2577                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY)
2578                                 .setUpdateRateNormal().build()),
2579                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2580 
2581         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_CONTINUOUS_PROPERTY,
2582                 mCarPropertyEventCallback);
2583         verify(mICarProperty).unregisterListener(eq(VENDOR_CONTINUOUS_PROPERTY),
2584                 any(ICarPropertyEventListener.class));
2585     }
2586 
2587     @Test
testUnregisterCallback_unregistersAllCallbackForSingleProperty()2588     public void testUnregisterCallback_unregistersAllCallbackForSingleProperty()
2589             throws Exception {
2590         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2591             VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2592         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback2,
2593             VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
2594 
2595         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback);
2596         verify(mICarProperty).unregisterListener(eq(VENDOR_CONTINUOUS_PROPERTY),
2597                 any(ICarPropertyEventListener.class));
2598     }
2599 
2600     @Test
testUnsubscribePropertyEvents_unsubscribeAllCallbackForSingleProperty()2601     public void testUnsubscribePropertyEvents_unsubscribeAllCallbackForSingleProperty()
2602             throws Exception {
2603         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2604                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2605                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2606                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2607         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2608                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY)
2609                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2610                 /* callbackExecutor= */ null, mCarPropertyEventCallback2)).isTrue();
2611 
2612         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_CONTINUOUS_PROPERTY,
2613                 mCarPropertyEventCallback);
2614         verify(mICarProperty).unregisterListener(eq(VENDOR_CONTINUOUS_PROPERTY),
2615                 any(ICarPropertyEventListener.class));
2616         verify(mICarProperty, never()).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2617                 any(ICarPropertyEventListener.class));
2618     }
2619 
2620     @Test
testUnregisterCallback_registerCalledIfBiggerRateRemoved()2621     public void testUnregisterCallback_registerCalledIfBiggerRateRemoved() throws Exception {
2622         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2623                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2624         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback2,
2625                 VENDOR_CONTINUOUS_PROPERTY, LARGER_UPDATE_RATE_HZ)).isTrue();
2626 
2627         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback2);
2628         verify(mICarProperty, never()).unregisterListener(anyInt(),
2629                 any(ICarPropertyEventListener.class));
2630         verify(mICarProperty, times(3)).registerListener(
2631                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
2632         assertThat(mCarSubscriptionCaptor.getAllValues()).containsExactly(
2633                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2634                         FIRST_UPDATE_RATE_HZ)),
2635                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2636                         LARGER_UPDATE_RATE_HZ)),
2637                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2638                         FIRST_UPDATE_RATE_HZ))
2639         );
2640     }
2641 
2642     @Test
testUnsubscribePropertyEvents_registerCalledIfBiggerRateRemoved()2643     public void testUnsubscribePropertyEvents_registerCalledIfBiggerRateRemoved()
2644             throws Exception {
2645         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2646                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2647                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2648                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2649         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2650                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2651                                 .setUpdateRateHz(LARGER_UPDATE_RATE_HZ).build()),
2652                 /* callbackExecutor= */ null, mCarPropertyEventCallback2)).isTrue();
2653         clearInvocations(mICarProperty);
2654 
2655         mCarPropertyManager.unsubscribePropertyEvents(mCarPropertyEventCallback2);
2656 
2657         verify(mICarProperty, never()).unregisterListener(anyInt(),
2658                 any(ICarPropertyEventListener.class));
2659         verify(mICarProperty, times(1)).registerListener(
2660                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
2661         assertThat(mCarSubscriptionCaptor.getAllValues()).containsExactly(
2662                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2663                         FIRST_UPDATE_RATE_HZ, /* enableVur= */ true))
2664         );
2665     }
2666 
2667     @Test
testUnregisterCallback_registerNotCalledIfSmallerRateRemoved()2668     public void testUnregisterCallback_registerNotCalledIfSmallerRateRemoved()
2669             throws Exception {
2670         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2671                 VENDOR_CONTINUOUS_PROPERTY, FIRST_UPDATE_RATE_HZ)).isTrue();
2672         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback2,
2673                 VENDOR_CONTINUOUS_PROPERTY, LARGER_UPDATE_RATE_HZ)).isTrue();
2674 
2675         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback);
2676         verify(mICarProperty, never()).unregisterListener(anyInt(),
2677                 any(ICarPropertyEventListener.class));
2678         verify(mICarProperty, times(2)).registerListener(
2679                 mCarSubscriptionCaptor.capture(), any(ICarPropertyEventListener.class));
2680         assertThat(mCarSubscriptionCaptor.getAllValues()).containsExactly(
2681                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2682                         FIRST_UPDATE_RATE_HZ)),
2683                 List.of(createCarSubscriptionOption(VENDOR_CONTINUOUS_PROPERTY, new int[]{0},
2684                         LARGER_UPDATE_RATE_HZ)));
2685     }
2686 
2687     @Test
testUnsubscribePropertyEvents_subscribeNotCalledIfSmallerRateremoved()2688     public void testUnsubscribePropertyEvents_subscribeNotCalledIfSmallerRateremoved()
2689             throws Exception {
2690         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2691                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2692                                 .setUpdateRateHz(FIRST_UPDATE_RATE_HZ).build()),
2693                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2694         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2695                         new Subscription.Builder(VENDOR_CONTINUOUS_PROPERTY)
2696                                 .setUpdateRateHz(LARGER_UPDATE_RATE_HZ).build()),
2697                 /* callbackExecutor= */ null, mCarPropertyEventCallback2)).isTrue();
2698         clearInvocations(mICarProperty);
2699 
2700         mCarPropertyManager.unsubscribePropertyEvents(mCarPropertyEventCallback2);
2701 
2702         verify(mICarProperty, never()).unregisterListener(anyInt(),
2703                 any(ICarPropertyEventListener.class));
2704     }
2705 
2706     @Test
testUnregisterCallback_doesNothingWithPropertyIdIfNothingRegistered()2707     public void testUnregisterCallback_doesNothingWithPropertyIdIfNothingRegistered()
2708             throws Exception {
2709         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback);
2710         verify(mICarProperty, never()).unregisterListener(anyInt(),
2711                 any(ICarPropertyEventListener.class));
2712     }
2713 
2714     @Test
testUnsubscribePropertyEvents_doesNothingWithPropertyIdIfNothingRegistered()2715     public void testUnsubscribePropertyEvents_doesNothingWithPropertyIdIfNothingRegistered()
2716             throws Exception {
2717         mCarPropertyManager.unsubscribePropertyEvents(mCarPropertyEventCallback);
2718         verify(mICarProperty, never()).unregisterListener(anyInt(),
2719                 any(ICarPropertyEventListener.class));
2720     }
2721 
2722     @Test
testUnregisterCallback_returnsVoidForIllegalArgumentException()2723     public void testUnregisterCallback_returnsVoidForIllegalArgumentException()
2724             throws RemoteException {
2725         doThrow(IllegalArgumentException.class).when(mICarProperty).unregisterListener(
2726                 eq(VENDOR_ON_CHANGE_PROPERTY), any(ICarPropertyEventListener.class));
2727 
2728         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2729                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
2730         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2731                 VENDOR_ON_CHANGE_PROPERTY);
2732 
2733         verify(mICarProperty).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2734                 any(ICarPropertyEventListener.class));
2735     }
2736 
2737     @Test
testUnsubscribePropertyEvents_returnsVoidForIllegalArgumentException()2738     public void testUnsubscribePropertyEvents_returnsVoidForIllegalArgumentException()
2739             throws Exception {
2740         doThrow(IllegalArgumentException.class).when(mICarProperty).unregisterListener(
2741                 eq(VENDOR_ON_CHANGE_PROPERTY), any(ICarPropertyEventListener.class));
2742 
2743         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2744                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).build()),
2745                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2746         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2747                 VENDOR_ON_CHANGE_PROPERTY);
2748 
2749         verify(mICarProperty).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2750                 any(ICarPropertyEventListener.class));
2751     }
2752 
2753     @Test
testUnregisterCallback_SecurityException()2754     public void testUnregisterCallback_SecurityException() throws Exception {
2755         doThrow(SecurityException.class).when(mICarProperty).unregisterListener(
2756                 eq(VENDOR_ON_CHANGE_PROPERTY), any(ICarPropertyEventListener.class));
2757 
2758         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2759                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
2760         assertThrows(SecurityException.class,
2761                 () -> mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2762                         VENDOR_ON_CHANGE_PROPERTY));
2763     }
2764 
2765     @Test
testUnsubscribePropertyEvents_SecurityException()2766     public void testUnsubscribePropertyEvents_SecurityException() throws Exception {
2767         doThrow(SecurityException.class).when(mICarProperty).unregisterListener(
2768                 eq(VENDOR_ON_CHANGE_PROPERTY), any(ICarPropertyEventListener.class));
2769 
2770         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2771                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).build()),
2772                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2773 
2774         assertThrows(SecurityException.class,
2775                 () -> mCarPropertyManager.unsubscribePropertyEvents(VENDOR_ON_CHANGE_PROPERTY,
2776                         mCarPropertyEventCallback));
2777     }
2778 
2779     @Test
testUnregisterCallback_recoversAfterSecurityException()2780     public void testUnregisterCallback_recoversAfterSecurityException() throws Exception {
2781         doThrow(SecurityException.class).doNothing().when(mICarProperty).unregisterListener(
2782                 eq(VENDOR_ON_CHANGE_PROPERTY), any(ICarPropertyEventListener.class));
2783 
2784         assertThat(mCarPropertyManager.registerCallback(mCarPropertyEventCallback,
2785                 VENDOR_ON_CHANGE_PROPERTY, CarPropertyManager.SENSOR_RATE_ONCHANGE)).isTrue();
2786         assertThrows(SecurityException.class,
2787                 () -> mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2788                         VENDOR_ON_CHANGE_PROPERTY));
2789 
2790         mCarPropertyManager.unregisterCallback(mCarPropertyEventCallback,
2791                 VENDOR_ON_CHANGE_PROPERTY);
2792 
2793         verify(mICarProperty, times(2)).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2794                 any(ICarPropertyEventListener.class));
2795     }
2796 
2797     @Test
testUnsubscribePropertyEvents_recoversAfterSecurityException()2798     public void testUnsubscribePropertyEvents_recoversAfterSecurityException() throws Exception {
2799         doThrow(SecurityException.class).doNothing().when(mICarProperty).unregisterListener(
2800                 eq(VENDOR_ON_CHANGE_PROPERTY), any(ICarPropertyEventListener.class));
2801         assertThat(mCarPropertyManager.subscribePropertyEvents(List.of(
2802                         new Subscription.Builder(VENDOR_ON_CHANGE_PROPERTY).build()),
2803                 /* callbackExecutor= */ null, mCarPropertyEventCallback)).isTrue();
2804 
2805         assertThrows(SecurityException.class,
2806                 () -> mCarPropertyManager.unsubscribePropertyEvents(VENDOR_ON_CHANGE_PROPERTY,
2807                         mCarPropertyEventCallback));
2808         mCarPropertyManager.unsubscribePropertyEvents(VENDOR_ON_CHANGE_PROPERTY,
2809                 mCarPropertyEventCallback);
2810 
2811         verify(mICarProperty, times(2)).unregisterListener(eq(VENDOR_ON_CHANGE_PROPERTY),
2812                 any(ICarPropertyEventListener.class));
2813     }
2814 
2815     @Test
testOnErrorEvent_callbackIsCalledWithErrorEvent()2816     public void testOnErrorEvent_callbackIsCalledWithErrorEvent() throws RemoteException {
2817         List<CarPropertyEvent> eventList = createErrorCarPropertyEventList();
2818         CarPropertyConfig config = CarPropertyConfig.newBuilder(Float.class, HVAC_TEMPERATURE_SET,
2819                 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
2820                 .addAreaIdConfig(new AreaIdConfig.Builder<Float>(0).build()).build();
2821         addCarPropertyConfig(config);
2822         ICarPropertyEventListener listener = getCarPropertyEventListener();
2823 
2824         listener.onEvent(eventList);
2825 
2826         // Wait until we get the on error event for the initial value.
2827         verify(mCarPropertyEventCallback, timeout(5000)).onErrorEvent(HVAC_TEMPERATURE_SET, 0,
2828                 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN);
2829     }
2830 
2831     @Test
testOnChangeEvent_callbackIsCalledWithEvent()2832     public void testOnChangeEvent_callbackIsCalledWithEvent() throws RemoteException {
2833         List<CarPropertyEvent> eventList = createCarPropertyEventList();
2834         CarPropertyConfig config = CarPropertyConfig.newBuilder(Float.class, HVAC_TEMPERATURE_SET,
2835                 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
2836                 .addAreaIdConfig(new AreaIdConfig.Builder<Float>(0).build()).build();
2837         addCarPropertyConfig(config);
2838         ICarPropertyEventListener listener = getCarPropertyEventListener();
2839         ArgumentCaptor<CarPropertyValue> value = ArgumentCaptor.forClass(CarPropertyValue.class);
2840 
2841         listener.onEvent(eventList);
2842 
2843         // Wait until we get the on property change event for the initial value.
2844         verify(mCarPropertyEventCallback, timeout(5000)).onChangeEvent(value.capture());
2845         assertThat(value.getValue().getPropertyId()).isEqualTo(HVAC_TEMPERATURE_SET);
2846         assertThat(value.getValue().getValue()).isEqualTo(17.0f);
2847     }
2848 
getCarPropertyEventListener()2849     private ICarPropertyEventListener getCarPropertyEventListener() throws RemoteException {
2850         ArgumentCaptor<ICarPropertyEventListener> carPropertyEventListenerArgumentCaptor =
2851                 ArgumentCaptor.forClass(ICarPropertyEventListener.class);
2852         mCarPropertyManager.registerCallback(mCarPropertyEventCallback, HVAC_TEMPERATURE_SET,
2853                 SENSOR_RATE_ONCHANGE);
2854 
2855         verify(mICarProperty).registerListener(eq(List.of(
2856                 createCarSubscriptionOption(HVAC_TEMPERATURE_SET, new int[]{0},
2857                         SENSOR_RATE_ONCHANGE))),
2858                 carPropertyEventListenerArgumentCaptor.capture());
2859 
2860         return carPropertyEventListenerArgumentCaptor.getValue();
2861     }
2862 
2863     @Test
testGetPropertyList()2864     public void testGetPropertyList() throws Exception {
2865         List<CarPropertyConfig> expectedConfigs = mock(List.class);
2866         when(mICarProperty.getPropertyList())
2867                 .thenReturn(new CarPropertyConfigList(expectedConfigs));
2868 
2869 
2870         assertThat(mCarPropertyManager.getPropertyList()).isEqualTo(expectedConfigs);
2871     }
2872 
2873     @Test
testGetPropertyList_withPropertyIds()2874     public void testGetPropertyList_withPropertyIds() throws Exception {
2875         ArraySet<Integer> requestedPropertyIds = new ArraySet<>(Set.of(
2876                 VENDOR_CONTINUOUS_PROPERTY, HVAC_TEMPERATURE_SET));
2877         CarPropertyConfig config1 = CarPropertyConfig.newBuilder(Integer.class,
2878                 VENDOR_CONTINUOUS_PROPERTY,
2879                 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
2880                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(0).build()).build();
2881         CarPropertyConfig config2 = CarPropertyConfig.newBuilder(Float.class,
2882                 HVAC_TEMPERATURE_SET,
2883                 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
2884                 .addAreaIdConfig(new AreaIdConfig.Builder<Float>(0).build()).build();
2885         addCarPropertyConfig(config1);
2886         addCarPropertyConfig(config2);
2887 
2888         assertThat(mCarPropertyManager.getPropertyList(requestedPropertyIds))
2889                 .containsExactly(config1, config2);
2890     }
2891 
2892     @Test
testGetPropertyList_filterUnsupportedPropertyIds()2893     public void testGetPropertyList_filterUnsupportedPropertyIds() throws Exception {
2894         ArraySet<Integer> requestedPropertyIds = new ArraySet<>(Set.of(
2895                 0, 1, VENDOR_CONTINUOUS_PROPERTY, HVAC_TEMPERATURE_SET));
2896         CarPropertyConfig config1 = CarPropertyConfig.newBuilder(Integer.class,
2897                 VENDOR_CONTINUOUS_PROPERTY,
2898                 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
2899                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(0).build()).build();
2900         CarPropertyConfig config2 = CarPropertyConfig.newBuilder(Float.class,
2901                 HVAC_TEMPERATURE_SET,
2902                 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
2903                 .addAreaIdConfig(new AreaIdConfig.Builder<Float>(0).build()).build();
2904         addCarPropertyConfig(config1);
2905         addCarPropertyConfig(config2);
2906 
2907         assertThat(mCarPropertyManager.getPropertyList(requestedPropertyIds))
2908                 .containsExactly(config1, config2);
2909     }
2910 
2911     @Test
testGetCarPropertyConfig()2912     public void testGetCarPropertyConfig() throws Exception {
2913         assertThat(mCarPropertyManager.getCarPropertyConfig(VENDOR_ON_CHANGE_PROPERTY))
2914                 .isEqualTo(mOnChangeCarPropertyConfig);
2915     }
2916 
2917     @Test
testGetCarPropertyConfig_noConfigReturned_notSupported()2918     public void testGetCarPropertyConfig_noConfigReturned_notSupported() throws Exception {
2919         setPropIdWithoutConfig(HVAC_TEMPERATURE_SET);
2920 
2921         assertThat(mCarPropertyManager.getCarPropertyConfig(HVAC_TEMPERATURE_SET)).isNull();
2922     }
2923 
2924     @Test
testGetCarPropertyConfig_noConfigReturned_noPermission()2925     public void testGetCarPropertyConfig_noConfigReturned_noPermission() throws Exception {
2926         setPropIdWithoutPermission(HVAC_TEMPERATURE_SET);
2927 
2928         assertThat(mCarPropertyManager.getCarPropertyConfig(HVAC_TEMPERATURE_SET)).isNull();
2929     }
2930 
2931     @Test
testGetCarPropertyConfig_unsupported()2932     public void testGetCarPropertyConfig_unsupported() throws Exception {
2933         assertThat(mCarPropertyManager.getCarPropertyConfig(/* propId= */ 0)).isNull();
2934     }
2935 
2936     @Test
testGetAreaId_global()2937     public void testGetAreaId_global() throws Exception {
2938         assertThat(mCarPropertyManager.getAreaId(VENDOR_ON_CHANGE_PROPERTY, 0)).isEqualTo(0);
2939     }
2940 
2941     @Test
testGetAreaId_withArea()2942     public void testGetAreaId_withArea() throws Exception {
2943         int areaId = VehicleAreaSeat.SEAT_ROW_1_LEFT | VehicleAreaSeat.SEAT_ROW_1_CENTER;
2944         CarPropertyConfig config1 = CarPropertyConfig.newBuilder(Integer.class,
2945                 HVAC_TEMPERATURE_SET,
2946                 VehicleAreaType.VEHICLE_AREA_TYPE_SEAT)
2947                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId)
2948                 .build()).build();
2949         addCarPropertyConfig(config1);
2950 
2951         assertThat(mCarPropertyManager.getAreaId(
2952                 HVAC_TEMPERATURE_SET, VehicleAreaSeat.SEAT_ROW_1_LEFT)).isEqualTo(areaId);
2953         assertThat(mCarPropertyManager.getAreaId(
2954                 HVAC_TEMPERATURE_SET, VehicleAreaSeat.SEAT_ROW_1_CENTER)).isEqualTo(areaId);
2955     }
2956 
2957     @Test
testGetAreaId_areaNotSupported()2958     public void testGetAreaId_areaNotSupported() throws Exception {
2959         int areaId = VehicleAreaSeat.SEAT_ROW_1_LEFT | VehicleAreaSeat.SEAT_ROW_1_CENTER;
2960         CarPropertyConfig config1 = CarPropertyConfig.newBuilder(Integer.class,
2961                 HVAC_TEMPERATURE_SET,
2962                 VehicleAreaType.VEHICLE_AREA_TYPE_SEAT)
2963                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId)
2964                 .build()).build();
2965         addCarPropertyConfig(config1);
2966 
2967         assertThrows(IllegalArgumentException.class, () -> mCarPropertyManager.getAreaId(
2968                 HVAC_TEMPERATURE_SET, VehicleAreaSeat.SEAT_ROW_1_RIGHT));
2969     }
2970 
2971     @Test
testGetAreaId_propertyNotSupported()2972     public void testGetAreaId_propertyNotSupported() throws Exception {
2973         setPropIdWithoutConfig(HVAC_TEMPERATURE_SET);
2974 
2975         assertThrows(IllegalArgumentException.class, () -> mCarPropertyManager.getAreaId(
2976                 HVAC_TEMPERATURE_SET, 0));
2977     }
2978 
2979     @Test
testGetAreaId_noPermissionToProperty()2980     public void testGetAreaId_noPermissionToProperty() throws Exception {
2981         setPropIdWithoutPermission(HVAC_TEMPERATURE_SET);
2982 
2983         assertThrows(IllegalArgumentException.class, () -> mCarPropertyManager.getAreaId(
2984                 HVAC_TEMPERATURE_SET, 0));
2985     }
2986 
2987     @Test
testGetAreaId_remoteExceptionFromCarService()2988     public void testGetAreaId_remoteExceptionFromCarService() throws Exception {
2989         when(mICarProperty.getPropertyConfigList(new int[]{HVAC_TEMPERATURE_SET}))
2990                 .thenThrow(new RemoteException());
2991 
2992         assertThrows(IllegalArgumentException.class, () -> mCarPropertyManager.getAreaId(
2993                 HVAC_TEMPERATURE_SET, 0));
2994     }
2995 
2996     @Test
testIsPropertyAvailable_withValueWithNotAvailableStatus()2997     public void testIsPropertyAvailable_withValueWithNotAvailableStatus() throws Exception {
2998         CarPropertyValue<Float> value = new CarPropertyValue<>(HVAC_TEMPERATURE_SET, 0,
2999                 CarPropertyValue.STATUS_ERROR, TEST_TIMESTAMP, 17.0f);
3000         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(value);
3001 
3002         assertThat(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_SET, /* areaId= */ 0))
3003                 .isFalse();
3004     }
3005 
3006     @Test
testIsPropertyAvailable()3007     public void testIsPropertyAvailable() throws Exception {
3008         CarPropertyValue<Integer> expectedValue = new CarPropertyValue<>(HVAC_TEMPERATURE_SET,
3009                 /* areaId= */ 0, CarPropertyValue.STATUS_AVAILABLE, /* timestamp= */ 0,
3010                 /* value= */ 1);
3011         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenReturn(expectedValue);
3012 
3013         assertThat(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_SET, /* areaId= */ 0))
3014                 .isTrue();
3015     }
3016 
3017     @Test
testIsPropertyAvailable_syncOpTryAgain()3018     public void testIsPropertyAvailable_syncOpTryAgain() throws Exception {
3019         CarPropertyValue<Integer> expectedValue = new CarPropertyValue<>(HVAC_TEMPERATURE_SET,
3020                 /* areaId= */ 0, CarPropertyValue.STATUS_AVAILABLE, /* timestamp= */ 0,
3021                 /* value= */ 1);
3022         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
3023                 new ServiceSpecificException(SYNC_OP_LIMIT_TRY_AGAIN)).thenReturn(expectedValue);
3024 
3025         assertThat(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_SET, /* areaId= */ 0))
3026                 .isTrue();
3027         verify(mICarProperty, times(2)).getProperty(HVAC_TEMPERATURE_SET, 0);
3028     }
3029 
3030     @Test
testIsPropertyAvailable_notAvailable()3031     public void testIsPropertyAvailable_notAvailable() throws Exception {
3032         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
3033                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_NOT_AVAILABLE));
3034 
3035         assertThat(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_SET, /* areaId= */ 0))
3036                 .isFalse();
3037     }
3038 
3039     @Test
testIsPropertyAvailable_tryAgain()3040     public void testIsPropertyAvailable_tryAgain() throws Exception {
3041         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
3042                 new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
3043 
3044         assertThat(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_SET, /* areaId= */ 0))
3045                 .isFalse();
3046     }
3047 
3048 
3049     @Test
testIsPropertyAvailable_RemoteException()3050     public void testIsPropertyAvailable_RemoteException() throws Exception {
3051         when(mICarProperty.getProperty(HVAC_TEMPERATURE_SET, 0)).thenThrow(
3052                 new RemoteException());
3053 
3054         assertThat(mCarPropertyManager.isPropertyAvailable(HVAC_TEMPERATURE_SET, /* areaId= */ 0))
3055                 .isFalse();
3056     }
3057 
3058     @Test
testIsPropertyAvailable_unsupported()3059     public void testIsPropertyAvailable_unsupported() throws Exception {
3060         assertThat(mCarPropertyManager.isPropertyAvailable(/* propId= */ 0, /* areaId= */ 0))
3061                 .isFalse();
3062     }
3063 
3064     @Test
testGetCarPropertyConfig_userHalProperty()3065     public void testGetCarPropertyConfig_userHalProperty() throws Exception {
3066         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
3067 
3068         assertThrows(IllegalArgumentException.class,
3069                 () -> mCarPropertyManager.getCarPropertyConfig(INITIAL_USER_INFO));
3070     }
3071 
3072     @Test
testGetPropertyList_userHalProperty()3073     public void testGetPropertyList_userHalProperty() throws Exception {
3074         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
3075 
3076         assertThrows(IllegalArgumentException.class,
3077                 () -> mCarPropertyManager.getPropertyList(
3078                         new ArraySet<Integer>(Set.of(HVAC_TEMPERATURE_SET, INITIAL_USER_INFO))));
3079     }
3080 
3081     @Test
testGetAreaId_userHalProperty()3082     public void testGetAreaId_userHalProperty()  throws Exception {
3083         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
3084 
3085         assertThrows(IllegalArgumentException.class,
3086                 () -> mCarPropertyManager.getAreaId(INITIAL_USER_INFO, /* area= */ 0));
3087     }
3088 
3089     @Test
testGetReadPermission_userHalProperty()3090     public void testGetReadPermission_userHalProperty() throws Exception {
3091         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
3092 
3093         assertThrows(IllegalArgumentException.class,
3094                 () -> mCarPropertyManager.getReadPermission(INITIAL_USER_INFO));
3095     }
3096 
3097     @Test
testGetWritePermission_userHalProperty()3098     public void testGetWritePermission_userHalProperty() throws Exception {
3099         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
3100 
3101         assertThrows(IllegalArgumentException.class,
3102                 () -> mCarPropertyManager.getWritePermission(INITIAL_USER_INFO));
3103     }
3104 
3105     @Test
testIsPropertyAvailable_userHalProperty()3106     public void testIsPropertyAvailable_userHalProperty() throws Exception {
3107         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
3108 
3109         assertThrows(IllegalArgumentException.class,
3110                 () -> mCarPropertyManager.isPropertyAvailable(INITIAL_USER_INFO, /* areaId= */ 0));
3111     }
3112 
3113     @Test
testGetProperty_userHalProperty()3114     public void testGetProperty_userHalProperty() throws Exception {
3115         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
3116 
3117         assertThrows(IllegalArgumentException.class,
3118                 () -> mCarPropertyManager.getProperty(INITIAL_USER_INFO, /* areaId= */ 0));
3119     }
3120 
3121     @Test
testGetBooleanProperty_userHalProperty()3122     public void testGetBooleanProperty_userHalProperty() throws Exception {
3123         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
3124 
3125         assertThrows(IllegalArgumentException.class,
3126                 () -> mCarPropertyManager.getBooleanProperty(INITIAL_USER_INFO, /* areaId= */ 0));
3127     }
3128 
3129     @Test
testSetBooleanProperty_userHalProperty()3130     public void testSetBooleanProperty_userHalProperty() throws Exception {
3131         setAppTargetSdk(Build.VERSION_CODES.TIRAMISU);
3132 
3133         assertThrows(IllegalArgumentException.class,
3134                 () -> mCarPropertyManager.setBooleanProperty(INITIAL_USER_INFO, /* areaId= */ 0,
3135                         true));
3136     }
3137 
3138     @Test
3139     @IgnoreUnderRavenwood(blockedBy = android.os.ParcelableHolder.class)
testGetMinMaxSupportedValue()3140     public void testGetMinMaxSupportedValue() throws Exception {
3141         int minValue = 123;
3142         int maxValue = 321;
3143         var minMaxSupportedPropertyValue = new MinMaxSupportedPropertyValue();
3144         minMaxSupportedPropertyValue.minValue.setParcelable(new RawPropertyValue(minValue));
3145         minMaxSupportedPropertyValue.maxValue.setParcelable(new RawPropertyValue(maxValue));
3146 
3147         when(mICarProperty.getMinMaxSupportedValue(INT32_PROP, 0)).thenReturn(
3148                 minMaxSupportedPropertyValue);
3149 
3150         MinMaxSupportedValue<Integer> result = mCarPropertyManager.getMinMaxSupportedValue(
3151                 INT32_PROP, 0);
3152 
3153         expectThat(result.getMinValue()).isEqualTo(minValue);
3154         expectThat(result.getMaxValue()).isEqualTo(maxValue);
3155     }
3156 
3157     @Test
3158     @IgnoreUnderRavenwood(blockedBy = android.os.ParcelableHolder.class)
testGetMinMaxSupportedValue_nullMinValue()3159     public void testGetMinMaxSupportedValue_nullMinValue() throws Exception {
3160         int maxValue = 321;
3161         var minMaxSupportedPropertyValue = new MinMaxSupportedPropertyValue();
3162         minMaxSupportedPropertyValue.maxValue.setParcelable(new RawPropertyValue(maxValue));
3163 
3164         when(mICarProperty.getMinMaxSupportedValue(INT32_PROP, 0)).thenReturn(
3165                 minMaxSupportedPropertyValue);
3166 
3167         MinMaxSupportedValue<Integer> result = mCarPropertyManager.getMinMaxSupportedValue(
3168                 INT32_PROP, 0);
3169 
3170         expectThat(result.getMinValue()).isNull();
3171         expectThat(result.getMaxValue()).isEqualTo(maxValue);
3172     }
3173 
3174     @Test
3175     @IgnoreUnderRavenwood(blockedBy = android.os.ParcelableHolder.class)
testGetMinMaxSupportedValue_nullMaxValue()3176     public void testGetMinMaxSupportedValue_nullMaxValue() throws Exception {
3177         int minValue = 123;
3178         var minMaxSupportedPropertyValue = new MinMaxSupportedPropertyValue();
3179         minMaxSupportedPropertyValue.minValue.setParcelable(new RawPropertyValue(minValue));
3180 
3181         when(mICarProperty.getMinMaxSupportedValue(INT32_PROP, 0)).thenReturn(
3182                 minMaxSupportedPropertyValue);
3183 
3184         MinMaxSupportedValue<Integer> result = mCarPropertyManager.getMinMaxSupportedValue(
3185                 INT32_PROP, 0);
3186 
3187         expectThat(result.getMinValue()).isEqualTo(minValue);
3188         expectThat(result.getMaxValue()).isNull();
3189     }
3190 
3191     @Test
testGetMinMaxSupportedValue_notSupportedPropertyId()3192     public void testGetMinMaxSupportedValue_notSupportedPropertyId() throws Exception {
3193         assertThrows(IllegalArgumentException.class,
3194                 () -> mCarPropertyManager.getMinMaxSupportedValue(INVALID, 0));
3195     }
3196 
3197     @Test
testGetMinMaxSupportedValue_remoteExceptionFromCarService()3198     public void testGetMinMaxSupportedValue_remoteExceptionFromCarService() throws Exception {
3199         when(mICarProperty.getMinMaxSupportedValue(anyInt(), anyInt())).thenThrow(
3200                 new RemoteException());
3201 
3202         var minMaxSupportedValue = mCarPropertyManager.getMinMaxSupportedValue(INT32_PROP, 0);
3203 
3204         assertThat(minMaxSupportedValue).isNotNull();
3205         expectThat(minMaxSupportedValue.getMaxValue()).isNull();
3206         expectThat(minMaxSupportedValue.getMinValue()).isNull();
3207     }
3208 
3209     @Test
testGetMinMaxSupportedValue_IllegalArgumentExceptionFromCarService()3210     public void testGetMinMaxSupportedValue_IllegalArgumentExceptionFromCarService()
3211             throws Exception {
3212         when(mICarProperty.getMinMaxSupportedValue(anyInt(), anyInt())).thenThrow(
3213                 new IllegalArgumentException());
3214 
3215         assertThrows(IllegalArgumentException.class, () ->
3216                 mCarPropertyManager.getMinMaxSupportedValue(INT32_PROP, 0));
3217     }
3218 
3219     @Test
testGetMinMaxSupportedValue_SecurityExceptionFromCarService()3220     public void testGetMinMaxSupportedValue_SecurityExceptionFromCarService()
3221             throws Exception {
3222         when(mICarProperty.getMinMaxSupportedValue(anyInt(), anyInt())).thenThrow(
3223                 new SecurityException());
3224 
3225         assertThrows(SecurityException.class, () ->
3226                 mCarPropertyManager.getMinMaxSupportedValue(INT32_PROP, 0));
3227     }
3228 
3229     @Test
testGetMinMaxSupportedValue_ServiceSpecificExceptionFromCarService()3230     public void testGetMinMaxSupportedValue_ServiceSpecificExceptionFromCarService()
3231             throws Exception {
3232         when(mICarProperty.getMinMaxSupportedValue(anyInt(), anyInt())).thenThrow(
3233                 new ServiceSpecificException(1));
3234 
3235         assertThrows(CarInternalErrorException.class, () ->
3236                 mCarPropertyManager.getMinMaxSupportedValue(INT32_PROP, 0));
3237     }
3238 
3239     @Test
testGetSupportedValuesList()3240     public void testGetSupportedValuesList() throws Exception {
3241         List<RawPropertyValue> resultsFromCarService = new ArrayList<>();
3242         resultsFromCarService.add(new RawPropertyValue(0));
3243         resultsFromCarService.add(new RawPropertyValue(1));
3244 
3245         when(mICarProperty.getSupportedValuesList(INT32_PROP, 0)).thenReturn(resultsFromCarService);
3246 
3247         List<Integer> result = mCarPropertyManager.getSupportedValuesList(INT32_PROP, 0);
3248 
3249         assertThat(result).containsExactly(0, 1);
3250     }
3251 
3252     @Test
testGetSupportedValuesList_notSupportedPropertyId()3253     public void testGetSupportedValuesList_notSupportedPropertyId() throws Exception {
3254         assertThrows(IllegalArgumentException.class,
3255                 () -> mCarPropertyManager.getSupportedValuesList(INVALID, 0));
3256     }
3257 
3258     @Test
testGetSupportedValuesList_nullFromCarService()3259     public void testGetSupportedValuesList_nullFromCarService() throws Exception {
3260         when(mICarProperty.getSupportedValuesList(INT32_PROP, 0)).thenReturn(null);
3261 
3262         List<Integer> result = mCarPropertyManager.getSupportedValuesList(INT32_PROP, 0);
3263 
3264         assertThat(result).isNull();
3265     }
3266 
3267     @Test
testGetSupportedValuesList_RemoteExceptionFromCarService()3268     public void testGetSupportedValuesList_RemoteExceptionFromCarService() throws Exception {
3269         when(mICarProperty.getSupportedValuesList(INT32_PROP, 0)).thenThrow(new RemoteException());
3270 
3271         List<Integer> result = mCarPropertyManager.getSupportedValuesList(INT32_PROP, 0);
3272 
3273         assertThat(result).isNull();
3274     }
3275 
3276     @Test
testGetSupportedValuesList_SecurityExceptionFromCarService()3277     public void testGetSupportedValuesList_SecurityExceptionFromCarService() throws Exception {
3278         when(mICarProperty.getSupportedValuesList(INT32_PROP, 0)).thenThrow(
3279                 new SecurityException());
3280 
3281         assertThrows(SecurityException.class,
3282                 () -> mCarPropertyManager.getSupportedValuesList(INT32_PROP, 0));
3283     }
3284 
3285     @Test
testGetSupportedValuesList_ServiceSpecificExceptionFromCarService()3286     public void testGetSupportedValuesList_ServiceSpecificExceptionFromCarService()
3287             throws Exception {
3288         when(mICarProperty.getSupportedValuesList(INT32_PROP, 0)).thenThrow(
3289                 new ServiceSpecificException(1));
3290 
3291         assertThrows(CarInternalErrorException.class,
3292                 () -> mCarPropertyManager.getSupportedValuesList(INT32_PROP, 0));
3293     }
3294 
3295     @Test
testGetSupportedValuesList_IllegalArgumentExceptionFromCarService()3296     public void testGetSupportedValuesList_IllegalArgumentExceptionFromCarService()
3297             throws Exception {
3298         when(mICarProperty.getSupportedValuesList(INT32_PROP, 0)).thenThrow(
3299                 new IllegalArgumentException());
3300 
3301         assertThrows(IllegalArgumentException.class,
3302                 () -> mCarPropertyManager.getSupportedValuesList(INT32_PROP, 0));
3303     }
3304 
3305     @Test
testRegisterSupportedValuesChangeCallback_propertyId_cb()3306     public void testRegisterSupportedValuesChangeCallback_propertyId_cb() throws Exception {
3307         int propertyId = INT32_PROP;
3308         int areaId1 = 1;
3309         int areaId2 = 2;
3310         addCarPropertyConfig(CarPropertyConfig.newBuilder(Integer.class,
3311                 INT32_PROP, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
3312                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId1).build())
3313                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId2).build())
3314                 .build());
3315 
3316         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3317                 mSupportedValuesChangeCallback)).isTrue();
3318 
3319         ArgumentCaptor<List> propIdAreaIdsCaptor = ArgumentCaptor.forClass(List.class);
3320         ArgumentCaptor<ISupportedValuesChangeCallback> callbackCaptor =
3321                 ArgumentCaptor.forClass(ISupportedValuesChangeCallback.class);
3322 
3323         verify(mICarProperty).registerSupportedValuesChangeCallback(
3324                 propIdAreaIdsCaptor.capture(), callbackCaptor.capture());
3325 
3326         List<PropIdAreaId> propIdAreaIds = (List<PropIdAreaId>) propIdAreaIdsCaptor.getValue();
3327         assertThat(propIdAreaIds).hasSize(2);
3328         expectThat(propIdAreaIds.get(0).propId).isEqualTo(propertyId);
3329         expectThat(propIdAreaIds.get(1).propId).isEqualTo(propertyId);
3330         expectThat(propIdAreaIds.get(0).areaId).isEqualTo(areaId1);
3331         expectThat(propIdAreaIds.get(1).areaId).isEqualTo(areaId2);
3332 
3333         ISupportedValuesChangeCallback callback = callbackCaptor.getValue();
3334 
3335         PropIdAreaId propIdAreaId1 = new PropIdAreaId();
3336         propIdAreaId1.propId = propertyId;
3337         propIdAreaId1.areaId = areaId1;
3338         PropIdAreaId propIdAreaId2 = new PropIdAreaId();
3339         propIdAreaId2.propId = propertyId;
3340         propIdAreaId2.areaId = areaId2;
3341         callback.onSupportedValuesChange(List.of(propIdAreaId1, propIdAreaId2));
3342 
3343         verify(mSupportedValuesChangeCallback, timeout(1000)).onSupportedValuesChange(propertyId,
3344                 areaId1);
3345         verify(mSupportedValuesChangeCallback, timeout(1000)).onSupportedValuesChange(propertyId,
3346                 areaId2);
3347     }
3348 
3349     @Test
testRegisterSupportedValuesChangeCallback_propertyId_areaId_cb()3350     public void testRegisterSupportedValuesChangeCallback_propertyId_areaId_cb()
3351             throws Exception {
3352         int propertyId = INT32_PROP;
3353         int areaId1 = 1;
3354         int areaId2 = 2;
3355         addCarPropertyConfig(CarPropertyConfig.newBuilder(Integer.class,
3356                 INT32_PROP, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
3357                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId1).build())
3358                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId2).build())
3359                 .build());
3360 
3361         // Only register to areaId2.
3362         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId, areaId2,
3363                 mSupportedValuesChangeCallback)).isTrue();
3364 
3365         ArgumentCaptor<List> propIdAreaIdsCaptor = ArgumentCaptor.forClass(List.class);
3366         ArgumentCaptor<ISupportedValuesChangeCallback> callbackCaptor =
3367                 ArgumentCaptor.forClass(ISupportedValuesChangeCallback.class);
3368 
3369         verify(mICarProperty).registerSupportedValuesChangeCallback(
3370                 propIdAreaIdsCaptor.capture(), callbackCaptor.capture());
3371 
3372         List<PropIdAreaId> propIdAreaIds = (List<PropIdAreaId>) propIdAreaIdsCaptor.getValue();
3373         assertThat(propIdAreaIds).hasSize(1);
3374         expectThat(propIdAreaIds.get(0).propId).isEqualTo(propertyId);
3375         expectThat(propIdAreaIds.get(0).areaId).isEqualTo(areaId2);
3376 
3377         ISupportedValuesChangeCallback callback = callbackCaptor.getValue();
3378 
3379         PropIdAreaId propIdAreaId1 = new PropIdAreaId();
3380         propIdAreaId1.propId = propertyId;
3381         propIdAreaId1.areaId = areaId1;
3382         PropIdAreaId propIdAreaId2 = new PropIdAreaId();
3383         propIdAreaId2.propId = propertyId;
3384         propIdAreaId2.areaId = areaId2;
3385         // The update for areaId1 must be ignored.
3386         callback.onSupportedValuesChange(List.of(propIdAreaId1, propIdAreaId2));
3387 
3388         verify(mSupportedValuesChangeCallback, timeout(1000).times(1)).onSupportedValuesChange(
3389                 propertyId, areaId2);
3390     }
3391 
3392     @Test
testRegisterSupportedValuesChangeCallback_propertyId_executor_cb()3393     public void testRegisterSupportedValuesChangeCallback_propertyId_executor_cb()
3394             throws Exception {
3395         int propertyId = INT32_PROP;
3396         int areaId1 = 1;
3397         int areaId2 = 2;
3398         addCarPropertyConfig(CarPropertyConfig.newBuilder(Integer.class,
3399                 INT32_PROP, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
3400                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId1).build())
3401                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId2).build())
3402                 .build());
3403 
3404         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3405                 DIRECT_EXECUTOR, mSupportedValuesChangeCallback)).isTrue();
3406 
3407         ArgumentCaptor<List> propIdAreaIdsCaptor = ArgumentCaptor.forClass(List.class);
3408         ArgumentCaptor<ISupportedValuesChangeCallback> callbackCaptor =
3409                 ArgumentCaptor.forClass(ISupportedValuesChangeCallback.class);
3410 
3411         verify(mICarProperty).registerSupportedValuesChangeCallback(
3412                 propIdAreaIdsCaptor.capture(), callbackCaptor.capture());
3413 
3414         List<PropIdAreaId> propIdAreaIds = (List<PropIdAreaId>) propIdAreaIdsCaptor.getValue();
3415         assertThat(propIdAreaIds).hasSize(2);
3416         expectThat(propIdAreaIds.get(0).propId).isEqualTo(propertyId);
3417         expectThat(propIdAreaIds.get(1).propId).isEqualTo(propertyId);
3418         expectThat(propIdAreaIds.get(0).areaId).isEqualTo(areaId1);
3419         expectThat(propIdAreaIds.get(1).areaId).isEqualTo(areaId2);
3420 
3421         ISupportedValuesChangeCallback callback = callbackCaptor.getValue();
3422 
3423         PropIdAreaId propIdAreaId1 = new PropIdAreaId();
3424         propIdAreaId1.propId = propertyId;
3425         propIdAreaId1.areaId = areaId1;
3426         PropIdAreaId propIdAreaId2 = new PropIdAreaId();
3427         propIdAreaId2.propId = propertyId;
3428         propIdAreaId2.areaId = areaId2;
3429         callback.onSupportedValuesChange(List.of(propIdAreaId1, propIdAreaId2));
3430 
3431         // Because we use a DirectExecutor, the callback is invoked synchronously.
3432         verify(mSupportedValuesChangeCallback).onSupportedValuesChange(propertyId, areaId1);
3433         verify(mSupportedValuesChangeCallback).onSupportedValuesChange(propertyId, areaId2);
3434     }
3435 
3436     @Test
testRegisterSupportedValuesChangeCallback_propertyId_areaId_executor_cb()3437     public void testRegisterSupportedValuesChangeCallback_propertyId_areaId_executor_cb()
3438             throws Exception {
3439         int propertyId = INT32_PROP;
3440         int areaId1 = 1;
3441         int areaId2 = 2;
3442         addCarPropertyConfig(CarPropertyConfig.newBuilder(Integer.class,
3443                 INT32_PROP, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
3444                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId1).build())
3445                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId2).build())
3446                 .build());
3447 
3448         // Only register to areaId2.
3449         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId, areaId2,
3450                 DIRECT_EXECUTOR, mSupportedValuesChangeCallback)).isTrue();
3451 
3452         ArgumentCaptor<List> propIdAreaIdsCaptor = ArgumentCaptor.forClass(List.class);
3453         ArgumentCaptor<ISupportedValuesChangeCallback> callbackCaptor =
3454                 ArgumentCaptor.forClass(ISupportedValuesChangeCallback.class);
3455 
3456         verify(mICarProperty).registerSupportedValuesChangeCallback(
3457                 propIdAreaIdsCaptor.capture(), callbackCaptor.capture());
3458 
3459         List<PropIdAreaId> propIdAreaIds = (List<PropIdAreaId>) propIdAreaIdsCaptor.getValue();
3460         assertThat(propIdAreaIds).hasSize(1);
3461         expectThat(propIdAreaIds.get(0).propId).isEqualTo(propertyId);
3462         expectThat(propIdAreaIds.get(0).areaId).isEqualTo(areaId2);
3463 
3464         ISupportedValuesChangeCallback callback = callbackCaptor.getValue();
3465 
3466         PropIdAreaId propIdAreaId1 = new PropIdAreaId();
3467         propIdAreaId1.propId = propertyId;
3468         propIdAreaId1.areaId = areaId1;
3469         PropIdAreaId propIdAreaId2 = new PropIdAreaId();
3470         propIdAreaId2.propId = propertyId;
3471         propIdAreaId2.areaId = areaId2;
3472         // The update for areaId1 must be ignored.
3473         callback.onSupportedValuesChange(List.of(propIdAreaId1, propIdAreaId2));
3474 
3475         // Because we use a DirectExecutor, the callback is invoked synchronously.
3476         verify(mSupportedValuesChangeCallback, times(1)).onSupportedValuesChange(
3477                 propertyId, areaId2);
3478     }
3479 
3480     @Test
testRegisterSupportedValuesChangeCallback_nullCallback()3481     public void testRegisterSupportedValuesChangeCallback_nullCallback() throws Exception {
3482         assertThrows(NullPointerException.class, () -> {
3483             mCarPropertyManager.registerSupportedValuesChangeCallback(INT32_PROP, /* cb= */ null);
3484         });
3485     }
3486 
3487     @Test
testRegisterSupportedValuesChangeCallback_nullExecutor()3488     public void testRegisterSupportedValuesChangeCallback_nullExecutor() throws Exception {
3489         assertThrows(NullPointerException.class, () -> {
3490             mCarPropertyManager.registerSupportedValuesChangeCallback(INT32_PROP,
3491                     /* executor= */ null, mSupportedValuesChangeCallback);
3492         });
3493     }
3494 
3495     @Test
testRegisterSupportedValuesChangeCallback_withAreaId_nullExecutor()3496     public void testRegisterSupportedValuesChangeCallback_withAreaId_nullExecutor()
3497             throws Exception {
3498         assertThrows(NullPointerException.class, () -> {
3499             mCarPropertyManager.registerSupportedValuesChangeCallback(INT32_PROP,
3500                     /* areaId= */ 0, /* executor= */ null, mSupportedValuesChangeCallback);
3501         });
3502     }
3503 
3504     @Test
testRegisterSupportedValuesChangeCallback_propertyIdNotSupported()3505     public void testRegisterSupportedValuesChangeCallback_propertyIdNotSupported()
3506             throws Exception {
3507         assertThrows(IllegalArgumentException.class, () -> {
3508             mCarPropertyManager.registerSupportedValuesChangeCallback(INVALID,
3509                     mSupportedValuesChangeCallback);
3510         });
3511     }
3512 
3513     @Test
testRegisterSupportedValuesChangeCallback_noPermission()3514     public void testRegisterSupportedValuesChangeCallback_noPermission() throws Exception {
3515         setPropIdWithoutPermission(INT32_PROP);
3516 
3517         assertThrows(SecurityException.class, () -> {
3518             mCarPropertyManager.registerSupportedValuesChangeCallback(INT32_PROP,
3519                     mSupportedValuesChangeCallback);
3520         });
3521     }
3522 
3523     @Test
testRegisterSupportedValuesChangeCallback_wrongExecutor()3524     public void testRegisterSupportedValuesChangeCallback_wrongExecutor() throws Exception {
3525         int propertyId = INT32_PROP;
3526         int areaId1 = 1;
3527         int areaId2 = 2;
3528         addCarPropertyConfig(CarPropertyConfig.newBuilder(Integer.class,
3529                 INT32_PROP, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
3530                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId1).build())
3531                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId2).build())
3532                 .build());
3533 
3534         // Associate the callback with the default executor.
3535         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3536                 mSupportedValuesChangeCallback)).isTrue();
3537 
3538         // Associating the same callback with a different executor is not allowed.
3539         assertThrows(IllegalArgumentException.class, () -> {
3540             mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3541                     DIRECT_EXECUTOR, mSupportedValuesChangeCallback);
3542         });
3543     }
3544 
3545     @Test
testRegisterSupportedValuesChangeCallback_RemoteExceptionFromCarService()3546     public void testRegisterSupportedValuesChangeCallback_RemoteExceptionFromCarService()
3547             throws Exception {
3548         int propertyId = INT32_PROP;
3549         int areaId1 = 1;
3550         int areaId2 = 2;
3551         addCarPropertyConfig(CarPropertyConfig.newBuilder(Integer.class,
3552                 INT32_PROP, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
3553                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId1).build())
3554                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId2).build())
3555                 .build());
3556 
3557         doThrow(new RemoteException()).when(mICarProperty)
3558                 .registerSupportedValuesChangeCallback(any(), any());
3559 
3560         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3561                 mSupportedValuesChangeCallback)).isFalse();
3562     }
3563 
3564     @Test
testRegisterSupportedValuesChangeCallback_ServiceSpecificExceptionFromCarService()3565     public void testRegisterSupportedValuesChangeCallback_ServiceSpecificExceptionFromCarService()
3566             throws Exception {
3567         int propertyId = INT32_PROP;
3568         int areaId1 = 1;
3569         int areaId2 = 2;
3570         addCarPropertyConfig(CarPropertyConfig.newBuilder(Integer.class,
3571                 INT32_PROP, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
3572                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId1).build())
3573                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId2).build())
3574                 .build());
3575 
3576         doThrow(new ServiceSpecificException(0)).when(mICarProperty)
3577                 .registerSupportedValuesChangeCallback(any(), any());
3578 
3579         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3580                 mSupportedValuesChangeCallback)).isFalse();
3581     }
3582 
3583     @Test
testUnregisterSupportedValuesChangeCallback_propertyId()3584     public void testUnregisterSupportedValuesChangeCallback_propertyId() throws Exception {
3585         int propertyId = INT32_PROP;
3586         int areaId1 = 1;
3587         int areaId2 = 2;
3588         addCarPropertyConfig(CarPropertyConfig.newBuilder(Integer.class,
3589                 INT32_PROP, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
3590                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId1).build())
3591                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId2).build())
3592                 .build());
3593 
3594         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3595                 DIRECT_EXECUTOR, mSupportedValuesChangeCallback)).isTrue();
3596         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3597                 DIRECT_EXECUTOR, mSupportedValuesChangeCallback2)).isTrue();
3598 
3599         mCarPropertyManager.unregisterSupportedValuesChangeCallback(propertyId);
3600 
3601         ArgumentCaptor<List> propIdAreaIdsCaptor = ArgumentCaptor.forClass(List.class);
3602         ArgumentCaptor<ISupportedValuesChangeCallback> callbackCaptor =
3603                 ArgumentCaptor.forClass(ISupportedValuesChangeCallback.class);
3604 
3605         verify(mICarProperty, times(2)).registerSupportedValuesChangeCallback(any(),
3606                 callbackCaptor.capture());
3607         verify(mICarProperty).unregisterSupportedValuesChangeCallback(propIdAreaIdsCaptor.capture(),
3608                 any());
3609         List<PropIdAreaId> propIdAreaIds = (List<PropIdAreaId>) propIdAreaIdsCaptor.getValue();
3610         assertThat(propIdAreaIds).hasSize(2);
3611         expectThat(propIdAreaIds.get(0).propId).isEqualTo(propertyId);
3612         expectThat(propIdAreaIds.get(1).propId).isEqualTo(propertyId);
3613         expectThat(propIdAreaIds.get(0).areaId).isEqualTo(areaId1);
3614         expectThat(propIdAreaIds.get(1).areaId).isEqualTo(areaId2);
3615 
3616         // After unregister, a new update event must not trigger the callback.
3617         ISupportedValuesChangeCallback callback = callbackCaptor.getValue();
3618         PropIdAreaId propIdAreaId = new PropIdAreaId();
3619         propIdAreaId.propId = propertyId;
3620         propIdAreaId.areaId = areaId1;
3621         callback.onSupportedValuesChange(List.of(propIdAreaId));
3622 
3623         verify(mSupportedValuesChangeCallback, never()).onSupportedValuesChange(anyInt(), anyInt());
3624         verify(mSupportedValuesChangeCallback2, never()).onSupportedValuesChange(anyInt(),
3625                 anyInt());
3626     }
3627 
3628     @Test
testUnregisterSupportedValuesChangeCallback_propertyId_cb()3629     public void testUnregisterSupportedValuesChangeCallback_propertyId_cb() throws Exception {
3630         int propertyId = INT32_PROP;
3631         int areaId1 = 1;
3632         int areaId2 = 2;
3633         addCarPropertyConfig(CarPropertyConfig.newBuilder(Integer.class,
3634                 INT32_PROP, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
3635                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId1).build())
3636                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId2).build())
3637                 .build());
3638 
3639         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3640                 DIRECT_EXECUTOR, mSupportedValuesChangeCallback)).isTrue();
3641         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3642                 DIRECT_EXECUTOR, mSupportedValuesChangeCallback2)).isTrue();
3643 
3644         mCarPropertyManager.unregisterSupportedValuesChangeCallback(propertyId,
3645                 mSupportedValuesChangeCallback2);
3646 
3647         ArgumentCaptor<ISupportedValuesChangeCallback> callbackCaptor =
3648                 ArgumentCaptor.forClass(ISupportedValuesChangeCallback.class);
3649 
3650         verify(mICarProperty, times(2)).registerSupportedValuesChangeCallback(any(),
3651                 callbackCaptor.capture());
3652         verify(mICarProperty, never()).unregisterSupportedValuesChangeCallback(any(), any());
3653 
3654         // After unregister, a new update event must not trigger the callback.
3655         ISupportedValuesChangeCallback callback = callbackCaptor.getValue();
3656         PropIdAreaId propIdAreaId = new PropIdAreaId();
3657         propIdAreaId.propId = propertyId;
3658         propIdAreaId.areaId = areaId1;
3659         callback.onSupportedValuesChange(List.of(propIdAreaId));
3660 
3661         verify(mSupportedValuesChangeCallback).onSupportedValuesChange(propertyId, areaId1);
3662         verify(mSupportedValuesChangeCallback2, never()).onSupportedValuesChange(anyInt(),
3663                 anyInt());
3664     }
3665 
3666     @Test
testUnregisterSupportedValuesChangeCallback_propertyId_areaId_cb()3667     public void testUnregisterSupportedValuesChangeCallback_propertyId_areaId_cb()
3668             throws Exception {
3669         int propertyId = INT32_PROP;
3670         int areaId1 = 1;
3671         int areaId2 = 2;
3672         addCarPropertyConfig(CarPropertyConfig.newBuilder(Integer.class,
3673                 INT32_PROP, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
3674                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId1).build())
3675                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId2).build())
3676                 .build());
3677 
3678         // areaId1, areaid2 is registered for mSupportedValuesChangeCallback
3679         // areaId1, areaid2 is registered for mSupportedValuesChangeCallback2
3680         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3681                 DIRECT_EXECUTOR, mSupportedValuesChangeCallback)).isTrue();
3682         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3683                 DIRECT_EXECUTOR, mSupportedValuesChangeCallback2)).isTrue();
3684 
3685         // areaId1 is registered for mSupportedValuesChangeCallback
3686         // areaId1, areaId2 is registered for mSupportedValuesChangeCallback2
3687         mCarPropertyManager.unregisterSupportedValuesChangeCallback(propertyId, areaId2,
3688                 mSupportedValuesChangeCallback);
3689 
3690         ArgumentCaptor<List> propIdAreaIdsCaptor = ArgumentCaptor.forClass(List.class);
3691         ArgumentCaptor<ISupportedValuesChangeCallback> callbackCaptor =
3692                 ArgumentCaptor.forClass(ISupportedValuesChangeCallback.class);
3693 
3694         verify(mICarProperty, times(2)).registerSupportedValuesChangeCallback(any(),
3695                 callbackCaptor.capture());
3696         verify(mICarProperty, never()).unregisterSupportedValuesChangeCallback(any(), any());
3697 
3698         ISupportedValuesChangeCallback callback = callbackCaptor.getValue();
3699         PropIdAreaId propIdAreaId1 = new PropIdAreaId();
3700         propIdAreaId1.propId = propertyId;
3701         propIdAreaId1.areaId = areaId1;
3702         PropIdAreaId propIdAreaId2 = new PropIdAreaId();
3703         propIdAreaId2.propId = propertyId;
3704         propIdAreaId2.areaId = areaId2;
3705         callback.onSupportedValuesChange(List.of(propIdAreaId1, propIdAreaId2));
3706 
3707         verify(mSupportedValuesChangeCallback).onSupportedValuesChange(propertyId, areaId1);
3708         verify(mSupportedValuesChangeCallback, never()).onSupportedValuesChange(propertyId,
3709                 areaId2);
3710         verify(mSupportedValuesChangeCallback2).onSupportedValuesChange(propertyId, areaId1);
3711         verify(mSupportedValuesChangeCallback2).onSupportedValuesChange(propertyId, areaId2);
3712 
3713         // All callbacks for areaId2 is unregistered.
3714         // areaId1 is registered for mSupportedValuesChangeCallback
3715         // areaId1 is registered for mSupportedValuesChangeCallback2
3716         mCarPropertyManager.unregisterSupportedValuesChangeCallback(propertyId, areaId2,
3717                 mSupportedValuesChangeCallback2);
3718 
3719         verify(mICarProperty).unregisterSupportedValuesChangeCallback(propIdAreaIdsCaptor.capture(),
3720                 any());
3721         List<PropIdAreaId> propIdAreaIds = (List<PropIdAreaId>) propIdAreaIdsCaptor.getValue();
3722         assertThat(propIdAreaIds).hasSize(1);
3723         expectThat(propIdAreaIds.get(0).propId).isEqualTo(propertyId);
3724         expectThat(propIdAreaIds.get(0).areaId).isEqualTo(areaId2);
3725 
3726         clearInvocations(mSupportedValuesChangeCallback);
3727         clearInvocations(mSupportedValuesChangeCallback2);
3728 
3729         // Now both callbacks are only registered for areaId1.
3730         callback.onSupportedValuesChange(List.of(propIdAreaId1, propIdAreaId2));
3731 
3732         verify(mSupportedValuesChangeCallback).onSupportedValuesChange(propertyId, areaId1);
3733         verify(mSupportedValuesChangeCallback, never()).onSupportedValuesChange(propertyId,
3734                 areaId2);
3735         verify(mSupportedValuesChangeCallback2).onSupportedValuesChange(propertyId, areaId1);
3736         verify(mSupportedValuesChangeCallback2, never()).onSupportedValuesChange(propertyId,
3737                 areaId2);
3738     }
3739 
3740     @Test
testUnregisterSupportedValuesChangeCallback_ignoreUnregistered()3741     public void testUnregisterSupportedValuesChangeCallback_ignoreUnregistered() throws Exception {
3742         int propertyId = INT32_PROP;
3743         int areaId1 = 1;
3744         int areaId2 = 2;
3745         addCarPropertyConfig(CarPropertyConfig.newBuilder(Integer.class,
3746                 INT32_PROP, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
3747                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId1).build())
3748                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId2).build())
3749                 .build());
3750 
3751         // This does nothing.
3752         mCarPropertyManager.unregisterSupportedValuesChangeCallback(propertyId);
3753     }
3754 
3755     @Test
testUnregisterSupportedValuesChangeCallback_ignoreUnregisteredAreaId()3756     public void testUnregisterSupportedValuesChangeCallback_ignoreUnregisteredAreaId()
3757             throws Exception {
3758         int propertyId = INT32_PROP;
3759         int areaId1 = 1;
3760         int areaId2 = 2;
3761         addCarPropertyConfig(CarPropertyConfig.newBuilder(Integer.class,
3762                 INT32_PROP, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
3763                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId1).build())
3764                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId2).build())
3765                 .build());
3766 
3767         mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId, areaId1,
3768                 mSupportedValuesChangeCallback);
3769 
3770         // This does nothing.
3771         mCarPropertyManager.unregisterSupportedValuesChangeCallback(propertyId, areaId2,
3772                 mSupportedValuesChangeCallback);
3773     }
3774 
3775     @Test
testUnregisterSupportedValuesChangeCallback_remoteExceptionIgnored()3776     public void testUnregisterSupportedValuesChangeCallback_remoteExceptionIgnored()
3777             throws Exception {
3778         int propertyId = INT32_PROP;
3779         int areaId1 = 1;
3780         int areaId2 = 2;
3781         addCarPropertyConfig(CarPropertyConfig.newBuilder(Integer.class,
3782                 INT32_PROP, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
3783                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId1).build())
3784                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId2).build())
3785                 .build());
3786         doThrow(new RemoteException()).when(mICarProperty).unregisterSupportedValuesChangeCallback(
3787                 any(), any());
3788 
3789         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3790                 DIRECT_EXECUTOR, mSupportedValuesChangeCallback)).isTrue();
3791 
3792         // This must not throw any exception.
3793         mCarPropertyManager.unregisterSupportedValuesChangeCallback(propertyId);
3794     }
3795 
3796     @Test
testUnregisterSupportedValuesChangeCallback_unsetExecutor()3797     public void testUnregisterSupportedValuesChangeCallback_unsetExecutor()
3798             throws Exception {
3799         int propertyId = INT32_PROP;
3800         int areaId1 = 1;
3801         int areaId2 = 2;
3802         addCarPropertyConfig(CarPropertyConfig.newBuilder(Integer.class,
3803                 INT32_PROP, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)
3804                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId1).build())
3805                 .addAreaIdConfig(new AreaIdConfig.Builder<Integer>(areaId2).build())
3806                 .build());
3807 
3808         // areaId1, areaid2 is registered for mSupportedValuesChangeCallback
3809         // areaId1, areaid2 is registered for mSupportedValuesChangeCallback2
3810         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3811                 DIRECT_EXECUTOR, mSupportedValuesChangeCallback)).isTrue();
3812         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3813                 DIRECT_EXECUTOR, mSupportedValuesChangeCallback2)).isTrue();
3814 
3815         // After this mSupportedValuesChangeCallback is unlinked with DIRECT_EXECUTOR.
3816         mCarPropertyManager.unregisterSupportedValuesChangeCallback(propertyId,
3817                 mSupportedValuesChangeCallback);
3818 
3819         // Associate mSupportedValuesChangeCallback with the default executor should be allowed.
3820         assertThat(mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3821                 mSupportedValuesChangeCallback)).isTrue();
3822 
3823         // At this point, mSupportedValuesChangeCallback2 is still registered for
3824         // [propertyId, areaId2] and linked with DEFAULT_EXECUTOR.
3825         mCarPropertyManager.unregisterSupportedValuesChangeCallback(propertyId,
3826                 areaId1, mSupportedValuesChangeCallback2);
3827 
3828         // Linking mSupportedValuesChangeCallback2 with the default executor must not be allowed.
3829         assertThrows(IllegalArgumentException.class, () -> {
3830             mCarPropertyManager.registerSupportedValuesChangeCallback(propertyId,
3831                     areaId1, mSupportedValuesChangeCallback2);
3832         });
3833     }
3834 }
3835