• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car;
18 
19 import static android.car.hardware.property.CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR;
20 import static android.car.hardware.property.CarPropertyManager.STATUS_ERROR_NOT_AVAILABLE;
21 import static android.car.hardware.property.CarPropertyManager.STATUS_ERROR_TIMEOUT;
22 
23 import static com.google.common.truth.Truth.assertThat;
24 import static com.google.common.truth.Truth.assertWithMessage;
25 
26 import static org.junit.Assert.assertThrows;
27 
28 import android.car.Car;
29 import android.car.VehicleAreaType;
30 import android.car.VehicleAreaWheel;
31 import android.car.VehiclePropertyIds;
32 import android.car.hardware.CarPropertyConfig;
33 import android.car.hardware.CarPropertyValue;
34 import android.car.hardware.property.CarInternalErrorException;
35 import android.car.hardware.property.CarPropertyManager;
36 import android.car.hardware.property.CarPropertyManager.GetPropertyRequest;
37 import android.car.hardware.property.CarPropertyManager.GetPropertyResult;
38 import android.car.hardware.property.CarPropertyManager.PropertyAsyncError;
39 import android.car.hardware.property.CarPropertyManager.SetPropertyRequest;
40 import android.car.hardware.property.CarPropertyManager.SetPropertyResult;
41 import android.car.hardware.property.PropertyAccessDeniedSecurityException;
42 import android.car.hardware.property.PropertyNotAvailableAndRetryException;
43 import android.car.hardware.property.PropertyNotAvailableException;
44 import android.car.hardware.property.VehicleHalStatusCode;
45 import android.hardware.automotive.vehicle.RawPropValues;
46 import android.hardware.automotive.vehicle.VehicleArea;
47 import android.hardware.automotive.vehicle.VehicleAreaSeat;
48 import android.hardware.automotive.vehicle.VehiclePropValue;
49 import android.hardware.automotive.vehicle.VehicleProperty;
50 import android.hardware.automotive.vehicle.VehiclePropertyGroup;
51 import android.hardware.automotive.vehicle.VehiclePropertyStatus;
52 import android.hardware.automotive.vehicle.VehiclePropertyType;
53 import android.hardware.automotive.vehicle.VehicleVendorPermission;
54 import android.os.Build;
55 import android.os.Handler;
56 import android.os.HandlerThread;
57 import android.os.ServiceSpecificException;
58 import android.os.SystemClock;
59 import android.util.ArraySet;
60 import android.util.Log;
61 import android.util.SparseArray;
62 
63 import androidx.annotation.NonNull;
64 import androidx.test.ext.junit.runners.AndroidJUnit4;
65 import androidx.test.filters.MediumTest;
66 
67 import com.android.car.hal.test.AidlMockedVehicleHal.VehicleHalPropertyHandler;
68 import com.android.car.test.TestPropertyAsyncCallback;
69 
70 import com.google.common.truth.Truth;
71 
72 import org.junit.Assert;
73 import org.junit.Rule;
74 import org.junit.Test;
75 import org.junit.rules.TestName;
76 import org.junit.runner.RunWith;
77 
78 import java.time.Duration;
79 import java.util.ArrayList;
80 import java.util.Arrays;
81 import java.util.Collections;
82 import java.util.List;
83 import java.util.Set;
84 import java.util.concurrent.CountDownLatch;
85 import java.util.concurrent.Executor;
86 import java.util.concurrent.TimeUnit;
87 
88 /**
89  * Test for {@link android.car.hardware.property.CarPropertyManager}
90  *
91  * Tests {@link android.car.hardware.property.CarPropertyManager} and the related car service
92  * logic. Uses {@link com.android.car.hal.test.AidlMockedVehicleHal} as the mocked vehicle HAL
93  * implementation.
94  *
95  * Caller should uses {@code addAidlProperty} in {@link #configureMockedHal} to configure the
96  * supported proeprties.
97  *
98  * Caller could also use {@link MockedCarTestBase#getAidlMockedVehicleHal} to further configure
99  * the vehicle HAL.
100  */
101 @RunWith(AndroidJUnit4.class)
102 @MediumTest
103 public class CarPropertyManagerTest extends MockedCarTestBase {
104 
105     private static final String TAG = CarPropertyManagerTest.class.getSimpleName();
106 
107     private static final String TEST_VIN = "test_vin";
108 
109     /**
110      * configArray[0], 1 indicates the property has a String value
111      * configArray[1], 1 indicates the property has a Boolean value .
112      * configArray[2], 1 indicates the property has an Integer value
113      * configArray[3], the number indicates the size of Integer[]  in the property.
114      * configArray[4], 1 indicates the property has a Long value .
115      * configArray[5], the number indicates the size of Long[]  in the property.
116      * configArray[6], 1 indicates the property has a Float value .
117      * configArray[7], the number indicates the size of Float[] in the property.
118      * configArray[8], the number indicates the size of byte[] in the property.
119      */
120     private static final java.util.Collection<Integer> CONFIG_ARRAY_1 =
121             Arrays.asList(1, 0, 1, 0, 1, 0, 0, 0, 0);
122     private static final java.util.Collection<Integer> CONFIG_ARRAY_2 =
123             Arrays.asList(1, 1, 1, 0, 0, 0, 0, 2, 0);
124     private static final java.util.Collection<Integer> CONFIG_ARRAY_3 =
125             Arrays.asList(0, 1, 1, 0, 0, 0, 1, 0, 0);
126     private static final Object[] EXPECTED_VALUE_1 = {"android", 1, 1L};
127     private static final Object[] EXPECTED_VALUE_2 = {"android", true, 3, 1.1f, 2f};
128     private static final Object[] EXPECTED_VALUE_3 = {true, 1, 2.2f};
129 
130     private static final int CUSTOM_SEAT_INT_PROP_1 =
131             0x1201 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.SEAT;
132     private static final int CUSTOM_SEAT_INT_PROP_2 =
133             0x1202 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.SEAT;
134 
135     private static final int CUSTOM_SEAT_MIXED_PROP_ID_1 =
136             0x1101 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.MIXED | VehicleArea.SEAT;
137     private static final int CUSTOM_GLOBAL_MIXED_PROP_ID_2 =
138             0x1102 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.MIXED | VehicleArea.GLOBAL;
139     private static final int CUSTOM_GLOBAL_MIXED_PROP_ID_3 =
140             0x1110 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.MIXED | VehicleArea.GLOBAL;
141 
142     private static final int CUSTOM_GLOBAL_INT_ARRAY_PROP =
143             0x1103 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32_VEC
144                     | VehicleArea.GLOBAL;
145     private static final Integer[] FAKE_INT_ARRAY_VALUE = {1, 2};
146     private static final boolean FAKE_BOOLEAN_PROPERTY_VALUE = true;
147     private static final float FAKE_FLOAT_PROPERTY_VALUE = 3f;
148     private static final int FAKE_INT_PROPERTY_VALUE = 3;
149 
150     // A property that always returns null to simulate an unavailable property.
151     private static final int NULL_VALUE_PROP =
152             0x1108 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32
153                     | VehicleArea.GLOBAL;
154 
155     // Vendor properties for testing exceptions.
156     private static final int PROP_VALUE_STATUS_ERROR_INT_ARRAY =
157             0x1104 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32_VEC
158                     | VehicleArea.GLOBAL;
159     private static final int PROP_VALUE_STATUS_ERROR_BOOLEAN =
160             0x1105 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.BOOLEAN
161                     | VehicleArea.GLOBAL;
162     private static final int PROP_VALUE_STATUS_UNAVAILABLE_FLOAT =
163             0x1106 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.FLOAT
164                     | VehicleArea.GLOBAL;
165     private static final int PROP_VALUE_STATUS_UNAVAILABLE_INT =
166             0x1107 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32
167                     | VehicleArea.GLOBAL;
168     private static final int PROP_VALUE_STATUS_UNKNOWN_INT_ARRAY =
169             0x1108 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32_VEC
170                     | VehicleArea.GLOBAL;
171     private static final int PROP_VALUE_STATUS_UNAVAILABLE_SEAT =
172             0x1109 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32
173                     | VehicleArea.SEAT;
174 
175     private static final int PROP_CAUSE_STATUS_CODE_TRY_AGAIN =
176             0x1201 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
177     private static final int PROP_CAUSE_STATUS_CODE_INVALID_ARG =
178             0x1202 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
179     private static final int PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE =
180             0x1203 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
181     private static final int PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR =
182             0x1204 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
183     private static final int PROP_CAUSE_STATUS_CODE_ACCESS_DENIED =
184             0x1205 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
185     private static final int PROP_CAUSE_STATUS_CODE_UNKNOWN =
186             0x1206 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
187     private static final int PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE_WITH_VENDOR_CODE =
188             0x1207 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
189     private static final int PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR_WITH_VENDOR_CODE =
190             0x1208 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
191 
192     // Vendor properties for testing permissions
193     private static final int PROP_WITH_READ_ONLY_PERMISSION =
194             0x1301 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
195     private static final int PROP_WITH_WRITE_ONLY_PERMISSION =
196             0x1302 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
197     private static final int SUPPORT_CUSTOM_PERMISSION = 287313669;
198     private static final java.util.Collection<Integer> VENDOR_PERMISSION_CONFIG =
199             Collections.unmodifiableList(
200                     Arrays.asList(PROP_WITH_READ_ONLY_PERMISSION,
201                             VehicleVendorPermission.PERMISSION_GET_VENDOR_CATEGORY_1,
202                             VehicleVendorPermission.PERMISSION_NOT_ACCESSIBLE,
203                             PROP_WITH_WRITE_ONLY_PERMISSION,
204                             VehicleVendorPermission.PERMISSION_NOT_ACCESSIBLE,
205                             VehicleVendorPermission.PERMISSION_SET_VENDOR_CATEGORY_1));
206 
207     private static final int PROP_ERROR_EVENT_NOT_AVAILABLE_DISABLED =
208             0x1401 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 |  VehicleArea.GLOBAL;
209 
210 
211     // Use FAKE_PROPERTY_ID to test api return null or throw exception.
212     private static final int FAKE_PROPERTY_ID = 0x111;
213 
214     // This is a property returned by VHAL, but is unsupported in car service.
215     // It must be filtered out at car service layer.
216     private static final int PROP_UNSUPPORTED =
217             0x0100 | VehiclePropertyGroup.SYSTEM | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
218 
219     private static final int DRIVER_SIDE_AREA_ID = VehicleAreaSeat.ROW_1_LEFT
220             | VehicleAreaSeat.ROW_2_LEFT;
221     private static final int PASSENGER_SIDE_AREA_ID = VehicleAreaSeat.ROW_1_RIGHT
222             | VehicleAreaSeat.ROW_2_CENTER
223             | VehicleAreaSeat.ROW_2_RIGHT;
224     private static final float INIT_TEMP_VALUE = 16f;
225     private static final float CHANGED_TEMP_VALUE = 20f;
226     private static final int CALLBACK_SHORT_TIMEOUT_MS = 350; // ms
227     // Wait for CarPropertyManager register/unregister listener
228     private static final long WAIT_FOR_NO_EVENTS = 50;
229     private static final int VENDOR_CODE_FOR_NOT_AVAILABLE = 0x00ab;
230     private static final int VENDOR_CODE_FOR_INTERNAL_ERROR = 0x0abc;
231     private static final int NOT_AVAILABLE_WITH_VENDOR_CODE = VENDOR_CODE_FOR_NOT_AVAILABLE << 16
232             | VehicleHalStatusCode.STATUS_NOT_AVAILABLE;
233     private static final int INTERNAL_ERROR_WITH_VENDOR_CODE = VENDOR_CODE_FOR_INTERNAL_ERROR << 16
234             | VehicleHalStatusCode.STATUS_INTERNAL_ERROR;
235 
236     private static final List<Integer> USER_HAL_PROPERTIES = Arrays.asList(
237             VehiclePropertyIds.INITIAL_USER_INFO,
238             VehiclePropertyIds.SWITCH_USER,
239             VehiclePropertyIds.CREATE_USER,
240             VehiclePropertyIds.REMOVE_USER,
241             VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION
242     );
243 
244     private CarPropertyManager mManager;
245 
246     private final HandlerThread mHandlerThread = new HandlerThread(getClass().getSimpleName());
247     private Handler mHandler;
248 
249     @Rule
250     public TestName mTestName = new TestName();
251 
252     @Override
setUp()253     public void setUp() throws Exception {
254         super.setUp();
255         setUpTargetSdk();
256         mHandlerThread.start();
257         mHandler = new Handler(mHandlerThread.getLooper());
258         mManager = (CarPropertyManager) getCar().getCarManager(Car.PROPERTY_SERVICE);
259         assertThat(mManager).isNotNull();
260     }
261 
262     @Override
tearDown()263     public void tearDown() throws Exception {
264         super.tearDown();
265         mHandlerThread.quitSafely();
266     }
267 
setUpTargetSdk()268     private void setUpTargetSdk() {
269         getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
270         if (mTestName.getMethodName().endsWith("BeforeR")) {
271             getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.Q;
272         } else if (mTestName.getMethodName().endsWith("BeforeS")
273                 || mTestName.getMethodName().endsWith("AfterR")) {
274             getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.R;
275         } else if (mTestName.getMethodName().endsWith("AfterS")) {
276             getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.S;
277         }
278     }
279 
280     @Test
testMixedPropertyConfigs()281     public void testMixedPropertyConfigs() {
282         List<CarPropertyConfig> configs = mManager.getPropertyList();
283         for (CarPropertyConfig cfg : configs) {
284             switch (cfg.getPropertyId()) {
285                 case CUSTOM_SEAT_MIXED_PROP_ID_1:
286                     assertThat(cfg.getConfigArray()).containsExactlyElementsIn(CONFIG_ARRAY_1)
287                             .inOrder();
288                     break;
289                 case CUSTOM_GLOBAL_MIXED_PROP_ID_2:
290                     assertThat(cfg.getConfigArray()).containsExactlyElementsIn(CONFIG_ARRAY_2)
291                             .inOrder();
292                     break;
293                 case CUSTOM_GLOBAL_MIXED_PROP_ID_3:
294                     assertThat(cfg.getConfigArray()).containsExactlyElementsIn(CONFIG_ARRAY_3)
295                             .inOrder();
296                     break;
297                 case VehiclePropertyIds.HVAC_TEMPERATURE_SET:
298                 case PROP_CAUSE_STATUS_CODE_ACCESS_DENIED:
299                 case PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR:
300                 case PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR_WITH_VENDOR_CODE:
301                 case PROP_CAUSE_STATUS_CODE_TRY_AGAIN:
302                 case PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE:
303                 case PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE_WITH_VENDOR_CODE:
304                 case PROP_CAUSE_STATUS_CODE_INVALID_ARG:
305                 case PROP_CAUSE_STATUS_CODE_UNKNOWN:
306                 case CUSTOM_SEAT_INT_PROP_1:
307                 case CUSTOM_SEAT_INT_PROP_2:
308                 case CUSTOM_GLOBAL_INT_ARRAY_PROP:
309                 case PROP_VALUE_STATUS_ERROR_INT_ARRAY:
310                 case PROP_VALUE_STATUS_UNKNOWN_INT_ARRAY:
311                 case PROP_VALUE_STATUS_ERROR_BOOLEAN:
312                 case PROP_VALUE_STATUS_UNAVAILABLE_INT:
313                 case PROP_VALUE_STATUS_UNAVAILABLE_FLOAT:
314                 case PROP_VALUE_STATUS_UNAVAILABLE_SEAT:
315                 case NULL_VALUE_PROP:
316                 case SUPPORT_CUSTOM_PERMISSION:
317                 case PROP_WITH_READ_ONLY_PERMISSION:
318                 case PROP_WITH_WRITE_ONLY_PERMISSION:
319                 case VehiclePropertyIds.INFO_VIN:
320                 case VehiclePropertyIds.TIRE_PRESSURE:
321                 case VehiclePropertyIds.FUEL_DOOR_OPEN:
322                 case VehiclePropertyIds.EPOCH_TIME:
323                 case PROP_ERROR_EVENT_NOT_AVAILABLE_DISABLED:
324                     break;
325                 default:
326                     Assert.fail("Unexpected CarPropertyConfig: " + cfg);
327             }
328         }
329     }
330 
331     @Test
testGetMixTypeProperty()332     public void testGetMixTypeProperty() {
333         mManager.setProperty(Object[].class, CUSTOM_SEAT_MIXED_PROP_ID_1,
334                 DRIVER_SIDE_AREA_ID, EXPECTED_VALUE_1);
335         CarPropertyValue<Object[]> result = mManager.getProperty(
336                 CUSTOM_SEAT_MIXED_PROP_ID_1, DRIVER_SIDE_AREA_ID);
337         assertThat(result.getValue()).isEqualTo(EXPECTED_VALUE_1);
338 
339         mManager.setProperty(Object[].class, CUSTOM_GLOBAL_MIXED_PROP_ID_2,
340                 0, EXPECTED_VALUE_2);
341         result = mManager.getProperty(
342                 CUSTOM_GLOBAL_MIXED_PROP_ID_2, 0);
343         assertThat(result.getValue()).isEqualTo(EXPECTED_VALUE_2);
344 
345         mManager.setProperty(Object[].class, CUSTOM_GLOBAL_MIXED_PROP_ID_3,
346                 0, EXPECTED_VALUE_3);
347         result = mManager.getProperty(
348                 CUSTOM_GLOBAL_MIXED_PROP_ID_3, 0);
349         assertThat(result.getValue()).isEqualTo(EXPECTED_VALUE_3);
350     }
351 
352     /**
353      * Test {@link android.car.hardware.property.CarPropertyManager#getIntArrayProperty(int, int)}
354      */
355     @Test
testGetIntArrayProperty()356     public void testGetIntArrayProperty() {
357         mManager.setProperty(Integer[].class, CUSTOM_GLOBAL_INT_ARRAY_PROP, 0,
358                 FAKE_INT_ARRAY_VALUE);
359 
360         int[] result = mManager.getIntArrayProperty(CUSTOM_GLOBAL_INT_ARRAY_PROP, 0);
361         assertThat(result).asList().containsExactlyElementsIn(FAKE_INT_ARRAY_VALUE);
362     }
363 
364     /**
365      * Test {@link CarPropertyManager#getIntArrayProperty(int, int)} when vhal returns a value with
366      * error status before S.
367      */
368     @Test
testGetIntArrayPropertyWithErrorStatusBeforeS()369     public void testGetIntArrayPropertyWithErrorStatusBeforeS() {
370         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
371                 .isLessThan(Build.VERSION_CODES.S);
372         assertThat(mManager.getIntArrayProperty(PROP_VALUE_STATUS_ERROR_INT_ARRAY, 0)).isEqualTo(
373                 new int[0]);
374     }
375 
376     /**
377      * Test {@link CarPropertyManager#getIntArrayProperty(int, int)} when vhal returns a value with
378      * error status equal or after S.
379      */
380     @Test
testGetIntArrayPropertyWithErrorStatusEqualAfterS()381     public void testGetIntArrayPropertyWithErrorStatusEqualAfterS() {
382         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
383                 .isAtLeast(Build.VERSION_CODES.S);
384         assertThrows(CarInternalErrorException.class,
385                 () -> mManager.getIntArrayProperty(PROP_VALUE_STATUS_ERROR_INT_ARRAY, 0));
386     }
387 
388     /**
389      * Test {@link CarPropertyManager#getIntArrayProperty(int, int)} when vhal returns a value with
390      * unknown status before S.
391      */
392     @Test
testGetIntArrayPropertyWithUnknownStatusBeforeS()393     public void testGetIntArrayPropertyWithUnknownStatusBeforeS() {
394         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
395                 .isLessThan(Build.VERSION_CODES.S);
396         assertThat(mManager.getIntArrayProperty(PROP_VALUE_STATUS_UNKNOWN_INT_ARRAY, 0)).isEqualTo(
397                 new int[0]);
398     }
399 
400     /**
401      * Test {@link CarPropertyManager#getIntArrayProperty(int, int)} when vhal returns a value with
402      * unknown status equal or after S.
403      */
404     @Test
testGetIntArrayPropertyWithUnknownStatusEqualAfterS()405     public void testGetIntArrayPropertyWithUnknownStatusEqualAfterS() {
406         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
407                 .isAtLeast(Build.VERSION_CODES.S);
408         assertThrows(CarInternalErrorException.class,
409                 () -> mManager.getIntArrayProperty(PROP_VALUE_STATUS_UNKNOWN_INT_ARRAY, 0));
410     }
411 
412     /**
413      * Test {@link CarPropertyManager#getIntProperty(int, int)} when vhal returns a value with
414      * unavailable status before S.
415      */
416     @Test
testGetIntPropertyWithUnavailableStatusBeforeS()417     public void testGetIntPropertyWithUnavailableStatusBeforeS() {
418         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
419                 .isLessThan(Build.VERSION_CODES.S);
420         assertThat(
421                 mManager.getIntProperty(PROP_VALUE_STATUS_UNAVAILABLE_INT, 0)).isEqualTo(0);
422     }
423 
424     /**
425      * Test {@link CarPropertyManager#getIntProperty(int, int)} when vhal returns a value with
426      * unavailable status equal or after R.
427      */
428     @Test
testGetIntPropertyWithUnavailableStatusEqualAfterS()429     public void testGetIntPropertyWithUnavailableStatusEqualAfterS() {
430         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
431                 .isAtLeast(Build.VERSION_CODES.R);
432         assertThrows(PropertyNotAvailableException.class,
433                 () -> mManager.getIntProperty(PROP_VALUE_STATUS_UNAVAILABLE_INT, 0));
434     }
435 
436     /**
437      * Test {@link CarPropertyManager#getBooleanProperty(int, int)} when vhal returns a value with
438      * error status before R.
439      */
440     @Test
testGetBooleanPropertyWithErrorStatusBeforeS()441     public void testGetBooleanPropertyWithErrorStatusBeforeS() {
442         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
443                 .isLessThan(Build.VERSION_CODES.S);
444         assertThat(mManager.getBooleanProperty(PROP_VALUE_STATUS_ERROR_BOOLEAN, 0)).isEqualTo(
445                 false);
446     }
447 
448     /**
449      * Test {@link CarPropertyManager#getBooleanProperty(int, int)} when vhal returns a value with
450      * error status equal or after R.
451      */
452     @Test
testGetBooleanPropertyWithErrorStatusEqualAfterS()453     public void testGetBooleanPropertyWithErrorStatusEqualAfterS() {
454         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
455                 .isAtLeast(Build.VERSION_CODES.R);
456         assertThrows(CarInternalErrorException.class,
457                 () -> mManager.getBooleanProperty(PROP_VALUE_STATUS_ERROR_BOOLEAN, 0));
458     }
459 
460     /**
461      * Test {@link CarPropertyManager#getFloatProperty(int, int)} when vhal returns a value with
462      * unavailable status before S.
463      */
464     @Test
testGetFloatPropertyWithUnavailableStatusBeforeS()465     public void testGetFloatPropertyWithUnavailableStatusBeforeS() {
466         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
467                 .isLessThan(Build.VERSION_CODES.S);
468         assertThat(mManager.getFloatProperty(PROP_VALUE_STATUS_UNAVAILABLE_FLOAT, 0)).isEqualTo(0f);
469     }
470 
471     /**
472      * Test {@link CarPropertyManager#getFloatProperty(int, int)} when vhal returns a value with
473      * unavailable status equal or after R.
474      */
475     @Test
testGetFloatPropertyWithUnavailableStatusEqualAfterS()476     public void testGetFloatPropertyWithUnavailableStatusEqualAfterS() {
477         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
478                 .isAtLeast(Build.VERSION_CODES.R);
479         assertThrows(PropertyNotAvailableException.class,
480                 () -> mManager.getFloatProperty(PROP_VALUE_STATUS_UNAVAILABLE_FLOAT, 0));
481     }
482 
483     /**
484      * Test {@link CarPropertyManager#getProperty(Class, int, int)}
485      */
486     @Test
testGetPropertyWithClass()487     public void testGetPropertyWithClass() {
488         mManager.setProperty(Integer[].class, CUSTOM_GLOBAL_INT_ARRAY_PROP, 0,
489                 FAKE_INT_ARRAY_VALUE);
490 
491         CarPropertyValue<Integer[]> result = mManager.getProperty(Integer[].class,
492                 CUSTOM_GLOBAL_INT_ARRAY_PROP, 0);
493         assertThat(result.getValue()).asList().containsExactlyElementsIn(FAKE_INT_ARRAY_VALUE);
494     }
495 
496     /**
497      * Test {@link CarPropertyManager#isPropertyAvailable(int, int)}
498      */
499     @Test
testIsPropertyAvailable()500     public void testIsPropertyAvailable() {
501         assertThat(mManager.isPropertyAvailable(CUSTOM_GLOBAL_INT_ARRAY_PROP, 0)).isFalse();
502 
503         mManager.setProperty(Integer[].class, CUSTOM_GLOBAL_INT_ARRAY_PROP, 0,
504                 FAKE_INT_ARRAY_VALUE);
505 
506         assertThat(mManager.isPropertyAvailable(CUSTOM_GLOBAL_INT_ARRAY_PROP, 0)).isTrue();
507     }
508 
509     /**
510      * Test {@link CarPropertyManager#getWritePermission(int)}
511      * and {@link CarPropertyManager#getWritePermission(int)}
512      */
513     @Test
testGetPermission()514     public void testGetPermission() {
515         String hvacReadPermission = mManager.getReadPermission(
516                 VehiclePropertyIds.HVAC_TEMPERATURE_SET);
517         assertThat(hvacReadPermission).isEqualTo(Car.PERMISSION_CONTROL_CAR_CLIMATE);
518         String hvacWritePermission = mManager.getWritePermission(
519                 VehiclePropertyIds.HVAC_TEMPERATURE_SET);
520         assertThat(hvacWritePermission).isEqualTo(Car.PERMISSION_CONTROL_CAR_CLIMATE);
521 
522         // For read-only property
523         String vinReadPermission = mManager.getReadPermission(VehiclePropertyIds.INFO_VIN);
524         assertThat(vinReadPermission).isEqualTo(Car.PERMISSION_IDENTIFICATION);
525         String vinWritePermission = mManager.getWritePermission(VehiclePropertyIds.INFO_VIN);
526         assertThat(vinWritePermission).isNull();
527     }
528 
529     @Test
testGetPropertyConfig()530     public void testGetPropertyConfig() {
531         CarPropertyConfig<?> config = mManager.getCarPropertyConfig(CUSTOM_SEAT_MIXED_PROP_ID_1);
532         assertThat(config.getPropertyId()).isEqualTo(CUSTOM_SEAT_MIXED_PROP_ID_1);
533         // returns null if it cannot find the propertyConfig for the property.
534         assertThat(mManager.getCarPropertyConfig(FAKE_PROPERTY_ID)).isNull();
535     }
536 
537     @Test
testGetPropertyConfig_withReadOnlyPermission()538     public void testGetPropertyConfig_withReadOnlyPermission() {
539         CarPropertyConfig<?> configForReadOnlyProperty = mManager
540                 .getCarPropertyConfig(PROP_WITH_READ_ONLY_PERMISSION);
541 
542         assertThat(configForReadOnlyProperty).isNotNull();
543         assertThat(configForReadOnlyProperty.getPropertyId())
544                 .isEqualTo(PROP_WITH_READ_ONLY_PERMISSION);
545     }
546 
547     @Test
testGetPropertyConfig_withWriteOnlyPermission()548     public void testGetPropertyConfig_withWriteOnlyPermission() {
549         CarPropertyConfig<?> configForWriteOnlyProperty = mManager
550                 .getCarPropertyConfig(PROP_WITH_WRITE_ONLY_PERMISSION);
551 
552         assertThat(configForWriteOnlyProperty).isNotNull();
553         assertThat(configForWriteOnlyProperty.getPropertyId())
554                 .isEqualTo(PROP_WITH_WRITE_ONLY_PERMISSION);
555     }
556 
557     @Test
testGetAreaId()558     public void testGetAreaId() {
559         int result = mManager.getAreaId(CUSTOM_SEAT_MIXED_PROP_ID_1, VehicleAreaSeat.ROW_1_LEFT);
560         assertThat(result).isEqualTo(DRIVER_SIDE_AREA_ID);
561         //test for the GLOBAL property
562         int globalAreaId =
563                 mManager.getAreaId(CUSTOM_GLOBAL_MIXED_PROP_ID_2, VehicleAreaSeat.ROW_1_LEFT);
564         assertThat(globalAreaId).isEqualTo(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
565         //test exception
566         assertThrows(IllegalArgumentException.class, () -> mManager.getAreaId(
567                 CUSTOM_SEAT_MIXED_PROP_ID_1, VehicleAreaSeat.ROW_3_CENTER));
568         assertThrows(IllegalArgumentException.class, () -> mManager.getAreaId(FAKE_PROPERTY_ID,
569                 VehicleAreaSeat.ROW_1_LEFT));
570     }
571 
572     @Test
testRegisterPropertyGetInitialValueHandleNotAvailableStatusCode()573     public void testRegisterPropertyGetInitialValueHandleNotAvailableStatusCode() throws Exception {
574         TestCallback callback = new TestCallback(/* initValueCount= */ 1,
575                 /* changeEventCount= */ 0, /* errorEventCount= */ 0);
576 
577         mManager.registerCallback(callback, PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
578                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
579 
580         callback.assertRegisterCompleted();
581         List<CarPropertyValue> carPropertyValues = callback.getInitialValues();
582         assertThat(carPropertyValues).hasSize(1);
583         assertThat(carPropertyValues.get(0).getStatus()).isEqualTo(
584                 CarPropertyValue.STATUS_UNAVAILABLE);
585     }
586 
587     @Test
testRegisterPropertyGetInitialValueHandleAccessDeniedStatusCodes()588     public void testRegisterPropertyGetInitialValueHandleAccessDeniedStatusCodes()
589             throws Exception {
590         TestCallback callback = new TestCallback(/* initValueCount= */ 1,
591                 /* changeEventCount= */ 0, /* errorEventCount= */ 0);
592 
593         mManager.registerCallback(callback, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
594                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
595 
596         callback.assertRegisterCompleted();
597         List<CarPropertyValue> carPropertyValues = callback.getInitialValues();
598         assertThat(carPropertyValues).hasSize(1);
599         assertThat(carPropertyValues.get(0).getStatus()).isEqualTo(
600                 CarPropertyValue.STATUS_ERROR);
601     }
602 
603     @Test
testRegisterPropertyGetInitialValueHandleInternalErrorStatusCodes()604     public void testRegisterPropertyGetInitialValueHandleInternalErrorStatusCodes()
605             throws Exception {
606         TestCallback callback = new TestCallback(/* initValueCount= */ 1,
607                 /* changeEventCount= */ 0, /* errorEventCount= */ 0);
608 
609         mManager.registerCallback(callback, PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
610                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
611 
612         callback.assertRegisterCompleted();
613         List<CarPropertyValue> carPropertyValues = callback.getInitialValues();
614         assertThat(carPropertyValues).hasSize(1);
615         assertThat(carPropertyValues.get(0).getStatus()).isEqualTo(
616                 CarPropertyValue.STATUS_ERROR);
617     }
618 
619     @Test
testRegisterPropertyGetInitialValueHandleInvalidArgStatusCode()620     public void testRegisterPropertyGetInitialValueHandleInvalidArgStatusCode() throws Exception {
621         TestCallback callback = new TestCallback(/* initValueCount= */ 1,
622                 /* changeEventCount= */ 0, /* errorEventCount= */ 0);
623 
624         mManager.registerCallback(callback, PROP_CAUSE_STATUS_CODE_INVALID_ARG,
625                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
626 
627         // We should not receive any initial value event.
628         assertThrows(IllegalStateException.class, () -> callback.assertRegisterCompleted(
629                 /* timeoutInMs=*/ 1000));
630     }
631 
632     @Test
testRegisterPropertyGetInitialValueHandleTryAgainStatusCode()633     public void testRegisterPropertyGetInitialValueHandleTryAgainStatusCode() throws Exception {
634         TestCallback callback = new TestCallback(/* initValueCount= */ 1,
635                 /* changeEventCount= */ 0, /* errorEventCount= */ 0);
636 
637         mManager.registerCallback(callback, PROP_CAUSE_STATUS_CODE_TRY_AGAIN,
638                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
639 
640         // We should not receive any initial value event.
641         assertThrows(IllegalStateException.class, () -> callback.assertRegisterCompleted(
642                 /* timeoutInMs=*/ 1000));
643     }
644 
645     @Test
testNotReceiveOnErrorEvent()646     public void testNotReceiveOnErrorEvent() throws Exception {
647         TestCallback callback = new TestCallback(/* initValueCount= */ 2, /* changeEventCount= */ 0,
648                 /* errorEventCount= */ 1);
649         mManager.registerCallback(callback, VehiclePropertyIds.HVAC_TEMPERATURE_SET,
650                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
651         callback.assertRegisterCompleted();
652         injectErrorEvent(VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID,
653                 VehicleHalStatusCode.STATUS_INTERNAL_ERROR);
654         // app never change the value of HVAC_TEMPERATURE_SET, it won't get an error code.
655         callback.assertOnErrorEventNotCalled();
656     }
657 
658     @Test
testReceiveOnErrorEvent()659     public void testReceiveOnErrorEvent() throws Exception {
660         TestCallback callback = new TestCallback(/* initValueCount= */ 2, /* changeEventCount= */ 0,
661                 /* errorEventCount= */ 1);
662         mManager.registerCallback(callback, VehiclePropertyIds.HVAC_TEMPERATURE_SET,
663                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
664         callback.assertRegisterCompleted();
665         mManager.setFloatProperty(
666                 VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID,
667                 CHANGED_TEMP_VALUE);
668         injectErrorEvent(VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID,
669                 VehicleHalStatusCode.STATUS_INTERNAL_ERROR);
670         callback.assertOnErrorEventCalled();
671         assertThat(callback.getErrorCode()).isEqualTo(
672                 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN);
673     }
674 
675     @Test
testNotReceiveOnErrorEventAfterUnregister()676     public void testNotReceiveOnErrorEventAfterUnregister() throws Exception {
677         TestCallback callback1 = new TestCallback(/* initValueCount= */ 2,
678                 /* changeEventCount= */ 0, /* errorEventCount= */ 1);
679         mManager.registerCallback(callback1, VehiclePropertyIds.HVAC_TEMPERATURE_SET,
680                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
681         callback1.assertRegisterCompleted();
682         TestCallback callback2 = new TestCallback(/* initValueCount= */ 2,
683                 /* changeEventCount= */ 0, /* errorEventCount= */ 1);
684         mManager.registerCallback(callback2, VehiclePropertyIds.HVAC_TEMPERATURE_SET,
685                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
686         mManager.setFloatProperty(
687                 VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID,
688                 CHANGED_TEMP_VALUE);
689         mManager.unregisterCallback(callback1, VehiclePropertyIds.HVAC_TEMPERATURE_SET);
690         SystemClock.sleep(WAIT_FOR_NO_EVENTS);
691         injectErrorEvent(VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID,
692                 VehicleHalStatusCode.STATUS_INTERNAL_ERROR);
693         // callback1 is unregistered
694         callback1.assertOnErrorEventNotCalled();
695         callback2.assertOnErrorEventCalled();
696     }
697 
698     @Test
testSetterExceptionsBeforeR()699     public void testSetterExceptionsBeforeR() {
700         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
701                 .isLessThan(Build.VERSION_CODES.R);
702 
703         assertThrows(IllegalStateException.class,
704                 () -> mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
705                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
706         assertThrows(IllegalStateException.class,
707                 () -> mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
708                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
709         assertThrows(IllegalStateException.class,
710                 () -> mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
711                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
712         assertThrows(IllegalArgumentException.class,
713                 () -> mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INVALID_ARG,
714                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
715         assertThrows(RuntimeException.class,
716                 () -> mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
717                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
718     }
719 
720     @Test
testSetterExceptionsEqualAfterR()721     public void testSetterExceptionsEqualAfterR() {
722         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
723                 .isAtLeast(Build.VERSION_CODES.Q);
724 
725         assertThrows(PropertyAccessDeniedSecurityException.class,
726                 () -> mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
727                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
728         assertThrows(PropertyNotAvailableAndRetryException.class,
729                 () -> mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_TRY_AGAIN,
730                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
731         assertThrows(PropertyNotAvailableException.class,
732                 () -> mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
733                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
734         assertThrows(CarInternalErrorException.class,
735                 () -> mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
736                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
737         assertThrows(IllegalArgumentException.class,
738                 () -> mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INVALID_ARG,
739                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
740         assertThrows(IllegalArgumentException.class,
741                 () -> mManager.setProperty(Integer.class, PROP_UNSUPPORTED,
742                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
743     }
744 
745     @Test
testGetterExceptionsBeforeR()746     public void testGetterExceptionsBeforeR() {
747         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
748                 .isLessThan(Build.VERSION_CODES.R);
749 
750         assertThrows(IllegalStateException.class,
751                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 0));
752         assertThrows(IllegalStateException.class,
753                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 0));
754 
755         assertThrows(IllegalArgumentException.class,
756                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG, 0));
757         assertThrows(IllegalArgumentException.class,
758                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG, 0));
759 
760         assertThrows(IllegalStateException.class,
761                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, 0));
762         assertThrows(IllegalStateException.class,
763                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, 0));
764 
765         assertThrows(IllegalStateException.class,
766                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, 0));
767         assertThrows(IllegalStateException.class,
768                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, 0));
769 
770         assertThrows(IllegalStateException.class, () -> mManager.getProperty(NULL_VALUE_PROP, 0));
771         assertThrows(IllegalStateException.class,
772                 () -> mManager.getIntProperty(NULL_VALUE_PROP, 0));
773 
774         Truth.assertThat(mManager.getProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN, 0)).isNull();
775         assertThat(mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN, 0)).isEqualTo(0);
776 
777     }
778 
779     @Test
testGetterExceptionsEqualAfterR()780     public void testGetterExceptionsEqualAfterR() {
781         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
782                 .isAtLeast(Build.VERSION_CODES.R);
783 
784         assertThrows(PropertyAccessDeniedSecurityException.class,
785                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 0));
786         assertThrows(PropertyAccessDeniedSecurityException.class,
787                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, 0));
788         assertThrows(IllegalArgumentException.class,
789                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG, 0));
790         assertThrows(IllegalArgumentException.class,
791                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG, 0));
792 
793         assertThrows(PropertyNotAvailableAndRetryException.class,
794                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN, 0));
795         assertThrows(PropertyNotAvailableAndRetryException.class,
796                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN, 0));
797 
798         assertThrows(PropertyNotAvailableException.class,
799                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, 0));
800         assertThrows(PropertyNotAvailableException.class,
801                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, 0));
802 
803         assertThrows(CarInternalErrorException.class,
804                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, 0));
805         assertThrows(CarInternalErrorException.class,
806                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, 0));
807 
808         assertThrows(PropertyNotAvailableException.class,
809                 () -> mManager.getProperty(NULL_VALUE_PROP, 0));
810         assertThrows(PropertyNotAvailableException.class,
811                 () -> mManager.getIntProperty(NULL_VALUE_PROP, 0));
812         assertThrows(IllegalArgumentException.class,
813                 () -> mManager.getProperty(PROP_UNSUPPORTED, 0));
814         assertThrows(PropertyNotAvailableException.class,
815                 () -> mManager.getIntProperty(NULL_VALUE_PROP, 0));
816     }
817 
818     @Test
testOnChangeEventWithSameAreaId()819     public void testOnChangeEventWithSameAreaId() throws Exception {
820         // init
821         TestCallback callback = new TestCallback(/* initValueCount= */ 2, /* changeEventCount= */ 1,
822                 /* errorEventCount= */ 0);
823         mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_1, 0);
824         callback.assertRegisterCompleted();
825 
826         VehiclePropValue firstFakeValueDriveSide = new VehiclePropValue();
827         firstFakeValueDriveSide.prop = CUSTOM_SEAT_INT_PROP_1;
828         firstFakeValueDriveSide.areaId = DRIVER_SIDE_AREA_ID;
829         firstFakeValueDriveSide.value = new RawPropValues();
830         firstFakeValueDriveSide.value.int32Values = new int[]{2};
831         firstFakeValueDriveSide.timestamp = SystemClock.elapsedRealtimeNanos();
832         VehiclePropValue secFakeValueDriveSide = new VehiclePropValue();
833         secFakeValueDriveSide.prop = CUSTOM_SEAT_INT_PROP_1;
834         secFakeValueDriveSide.areaId = DRIVER_SIDE_AREA_ID;
835         secFakeValueDriveSide.value = new RawPropValues();
836         secFakeValueDriveSide.value.int32Values = new int[]{3}; // 0 in HAL indicate false;
837         secFakeValueDriveSide.timestamp = SystemClock.elapsedRealtimeNanos();
838         // inject the new event first
839         getAidlMockedVehicleHal().injectEvent(secFakeValueDriveSide);
840         // inject the old event
841         getAidlMockedVehicleHal().injectEvent(firstFakeValueDriveSide);
842 
843         List<CarPropertyValue> events = callback.waitAndGetChangeEvents();
844 
845         // Client should only get the new event
846         assertThat(events).hasSize(1);
847         assertThat(events.get(0).getValue()).isEqualTo(3);
848 
849     }
850 
851     @Test
testOnChangeEventWithDifferentAreaId()852     public void testOnChangeEventWithDifferentAreaId() throws Exception {
853         // init
854         TestCallback callback = new TestCallback(/* initValueCount= */ 2, /* changeEventCount= */ 2,
855                 /* errorEventCount= */ 0);
856         mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_2, 0);
857         callback.assertRegisterCompleted();
858         VehiclePropValue fakeValueDriveSide = new VehiclePropValue();
859         fakeValueDriveSide.prop = CUSTOM_SEAT_INT_PROP_2;
860         fakeValueDriveSide.areaId = DRIVER_SIDE_AREA_ID;
861         fakeValueDriveSide.value = new RawPropValues();
862         fakeValueDriveSide.value.int32Values = new int[]{4};
863         fakeValueDriveSide.timestamp = SystemClock.elapsedRealtimeNanos();
864 
865         VehiclePropValue fakeValuePsgSide = new VehiclePropValue();
866         fakeValuePsgSide.prop = CUSTOM_SEAT_INT_PROP_2;
867         fakeValuePsgSide.areaId = PASSENGER_SIDE_AREA_ID;
868         fakeValuePsgSide.value = new RawPropValues();
869         fakeValuePsgSide.value.int32Values = new int[]{5};
870         fakeValuePsgSide.timestamp = SystemClock.elapsedRealtimeNanos();
871 
872         // inject passenger event before driver event
873         getAidlMockedVehicleHal().injectEvent(fakeValuePsgSide);
874         getAidlMockedVehicleHal().injectEvent(fakeValueDriveSide);
875 
876         List<CarPropertyValue> events = callback.waitAndGetChangeEvents();
877 
878         // both events should be received by listener
879         assertThat(events).hasSize(2);
880         assertThat(events.get(0).getValue()).isEqualTo(5);
881         assertThat(events.get(1).getValue()).isEqualTo(4);
882     }
883 
884     @Test
testOnChangeEventPropErrorStatus()885     public void testOnChangeEventPropErrorStatus() throws Exception {
886         // init
887         TestCallback callback = new TestCallback(/* initValueCount= */ 2, /* changeEventCount= */ 1,
888                 /* errorEventCount= */ 0);
889 
890         mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_1, 0);
891 
892         callback.assertRegisterCompleted(/* timeoutMs= */ 1000);
893 
894         VehiclePropValue prop = new VehiclePropValue();
895         prop.prop = CUSTOM_SEAT_INT_PROP_1;
896         prop.areaId = DRIVER_SIDE_AREA_ID;
897         prop.value = new RawPropValues();
898         prop.status = VehiclePropertyStatus.ERROR;
899         prop.timestamp = SystemClock.elapsedRealtimeNanos();
900 
901         getAidlMockedVehicleHal().injectEvent(prop);
902 
903         List<CarPropertyValue> carPropertyValues = callback.waitAndGetChangeEvents();
904         assertThat(carPropertyValues).hasSize(1);
905         assertThat(carPropertyValues.get(0).getStatus()).isEqualTo(CarPropertyValue.STATUS_ERROR);
906     }
907 
908     @Test
testOnChangeEventPropUnavailableStatus()909     public void testOnChangeEventPropUnavailableStatus() throws Exception {
910         // init
911         TestCallback callback = new TestCallback(/* initValueCount= */ 2, /* changeEventCount= */ 1,
912                 /* errorEventCount= */ 0);
913 
914         mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_1, 0);
915 
916         callback.assertRegisterCompleted();
917 
918         VehiclePropValue prop = new VehiclePropValue();
919         prop.prop = CUSTOM_SEAT_INT_PROP_1;
920         prop.areaId = DRIVER_SIDE_AREA_ID;
921         prop.value = new RawPropValues();
922         prop.status = VehiclePropertyStatus.UNAVAILABLE;
923         prop.timestamp = SystemClock.elapsedRealtimeNanos();
924 
925         getAidlMockedVehicleHal().injectEvent(prop);
926 
927         List<CarPropertyValue> carPropertyValues = callback.waitAndGetChangeEvents();
928         assertThat(carPropertyValues).hasSize(1);
929         assertThat(carPropertyValues.get(0).getStatus()).isEqualTo(
930                 CarPropertyValue.STATUS_UNAVAILABLE);
931     }
932 
933     @Test
testOnChangeEventInvalidPayload()934     public void testOnChangeEventInvalidPayload() throws Exception {
935         // init
936         TestCallback callback = new TestCallback(/* initValueCount= */ 2, /* changeEventCount= */ 0,
937                 /* errorEventCount= */ 0);
938         mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_1, 0);
939         callback.assertRegisterCompleted();
940 
941         List<VehiclePropValue> props = new ArrayList<>();
942         VehiclePropValue emptyProp = new VehiclePropValue();
943         emptyProp.prop = CUSTOM_SEAT_INT_PROP_1;
944         props.add(emptyProp);
945 
946         VehiclePropValue twoIntsProp = new VehiclePropValue();
947         twoIntsProp.prop = CUSTOM_SEAT_INT_PROP_1;
948         twoIntsProp.value = new RawPropValues();
949         twoIntsProp.value.int32Values = new int[]{0, 1};
950         props.add(twoIntsProp);
951 
952         VehiclePropValue propWithFloat = new VehiclePropValue();
953         propWithFloat.prop = CUSTOM_SEAT_INT_PROP_1;
954         propWithFloat.value = new RawPropValues();
955         propWithFloat.value.floatValues = new float[]{0f};
956         props.add(propWithFloat);
957 
958         VehiclePropValue propWithString = new VehiclePropValue();
959         propWithString.prop = CUSTOM_SEAT_INT_PROP_1;
960         propWithString.value = new RawPropValues();
961         propWithString.value.stringValue = "1234";
962         props.add(propWithString);
963 
964         for (VehiclePropValue prop : props) {
965             // inject passenger event before driver event
966             getAidlMockedVehicleHal().injectEvent(prop);
967 
968             assertThat(callback.getChangeEventCounter()).isEqualTo(0);
969         }
970     }
971 
972     @Test
registerCallback_handlesContinuousPropertyUpdateRate()973     public void registerCallback_handlesContinuousPropertyUpdateRate() throws Exception {
974         float wheelLeftFrontValue = 11.11f;
975         long wheelLeftFrontTimestampNanos = Duration.ofSeconds(1).toNanos();
976 
977         float notNewEnoughWheelLeftFrontValue = 22.22f;
978         long notNewEnoughWheelLeftFrontTimestampNanos = Duration.ofMillis(1999).toNanos();
979 
980         float newEnoughWheelLeftFrontValue = 33.33f;
981         long newEnoughWheelLeftFrontTimestampNanos = Duration.ofSeconds(2).toNanos();
982 
983         TestCallback callback = new TestCallback(/* initValueCount= */ 4, /* changeEventCount= */ 2,
984                 /* errorEventCount= */ 0);
985         assertThat(mManager.registerCallback(callback, VehiclePropertyIds.TIRE_PRESSURE,
986                 1f)).isTrue();
987         callback.assertRegisterCompleted();
988 
989         long currentElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
990         getAidlMockedVehicleHal().injectEvent(
991                 newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_LEFT_FRONT,
992                         wheelLeftFrontValue,
993                         currentElapsedRealtimeNanos + wheelLeftFrontTimestampNanos));
994         getAidlMockedVehicleHal().injectEvent(
995                 newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_LEFT_FRONT,
996                         notNewEnoughWheelLeftFrontValue,
997                         currentElapsedRealtimeNanos + notNewEnoughWheelLeftFrontTimestampNanos));
998         getAidlMockedVehicleHal().injectEvent(
999                 newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_LEFT_FRONT,
1000                         newEnoughWheelLeftFrontValue,
1001                         currentElapsedRealtimeNanos + newEnoughWheelLeftFrontTimestampNanos));
1002 
1003         List<CarPropertyValue> carPropertyValues = callback.waitAndGetChangeEvents();
1004         assertThat(carPropertyValues).hasSize(2);
1005 
1006         assertTirePressureCarPropertyValue(carPropertyValues.get(0),
1007                 VehicleAreaWheel.WHEEL_LEFT_FRONT, wheelLeftFrontValue,
1008                 currentElapsedRealtimeNanos + wheelLeftFrontTimestampNanos);
1009 
1010         assertTirePressureCarPropertyValue(carPropertyValues.get(1),
1011                 VehicleAreaWheel.WHEEL_LEFT_FRONT, newEnoughWheelLeftFrontValue,
1012                 currentElapsedRealtimeNanos + newEnoughWheelLeftFrontTimestampNanos);
1013     }
1014 
1015     @Test
registerCallback_handlesOutOfTimeOrderEventsWithDifferentAreaIds()1016     public void registerCallback_handlesOutOfTimeOrderEventsWithDifferentAreaIds()
1017             throws Exception {
1018         float wheelLeftFrontValue = 11.11f;
1019         long wheelLeftFrontTimestampNanos = Duration.ofSeconds(4).toNanos();
1020 
1021         float wheelRightFrontValue = 22.22f;
1022         long wheelRightFrontTimestampNanos = Duration.ofSeconds(3).toNanos();
1023 
1024         float wheelLeftRearValue = 33.33f;
1025         long wheelLeftRearTimestampNanos = Duration.ofSeconds(2).toNanos();
1026 
1027         float wheelRightRearValue = 44.44f;
1028         long wheelRightRearTimestampNanos = Duration.ofSeconds(1).toNanos();
1029 
1030         // Initially we have 4 area Ids, so we will have 4 initial values. In the test we will
1031         // inject 4 events which will generate 4 change events.
1032         TestCallback callback = new TestCallback(/* initValueCount= */ 4, /* changeEventCount= */ 4,
1033                 /* errorEventCount= */ 0);
1034         // AidlMockedVehicleHal will not actually genenerate property events for continuous
1035         // property, so the subscription rate does not matter.
1036         assertThat(mManager.registerCallback(callback, VehiclePropertyIds.TIRE_PRESSURE, 1f))
1037                 .isTrue();
1038 
1039         callback.assertRegisterCompleted();
1040 
1041         long currentElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
1042         // inject events in time order from newest to oldest
1043         getAidlMockedVehicleHal().injectEvent(
1044                 newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_LEFT_FRONT,
1045                         wheelLeftFrontValue,
1046                         currentElapsedRealtimeNanos + wheelLeftFrontTimestampNanos));
1047         getAidlMockedVehicleHal().injectEvent(
1048                 newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_RIGHT_FRONT,
1049                         wheelRightFrontValue,
1050                         currentElapsedRealtimeNanos + wheelRightFrontTimestampNanos));
1051         getAidlMockedVehicleHal().injectEvent(
1052                 newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_LEFT_REAR,
1053                         wheelLeftRearValue,
1054                         currentElapsedRealtimeNanos + wheelLeftRearTimestampNanos));
1055         getAidlMockedVehicleHal().injectEvent(
1056                 newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_RIGHT_REAR,
1057                         wheelRightRearValue,
1058                         currentElapsedRealtimeNanos + wheelRightRearTimestampNanos));
1059 
1060         List<CarPropertyValue> carPropertyValues = callback.waitAndGetChangeEvents();
1061         assertThat(carPropertyValues).hasSize(4);
1062 
1063         assertTirePressureCarPropertyValue(carPropertyValues.get(0),
1064                 VehicleAreaWheel.WHEEL_LEFT_FRONT, wheelLeftFrontValue,
1065                 currentElapsedRealtimeNanos + wheelLeftFrontTimestampNanos);
1066 
1067         assertTirePressureCarPropertyValue(carPropertyValues.get(1),
1068                 VehicleAreaWheel.WHEEL_RIGHT_FRONT, wheelRightFrontValue,
1069                 currentElapsedRealtimeNanos + wheelRightFrontTimestampNanos);
1070 
1071         assertTirePressureCarPropertyValue(carPropertyValues.get(2),
1072                 VehicleAreaWheel.WHEEL_LEFT_REAR, wheelLeftRearValue,
1073                 currentElapsedRealtimeNanos + wheelLeftRearTimestampNanos);
1074 
1075         assertTirePressureCarPropertyValue(carPropertyValues.get(3),
1076                 VehicleAreaWheel.WHEEL_RIGHT_REAR, wheelRightRearValue,
1077                 currentElapsedRealtimeNanos + wheelRightRearTimestampNanos);
1078     }
1079 
1080     @Test
testGetPropertiesAsync()1081     public void testGetPropertiesAsync() throws Exception {
1082         List<GetPropertyRequest> getPropertyRequests = new ArrayList<>();
1083         Executor callbackExecutor = new HandlerExecutor(mHandler);
1084         Set<Integer> requestIds = new ArraySet();
1085 
1086         // Regular property.
1087         GetPropertyRequest vinRequest = mManager.generateGetPropertyRequest(
1088                 VehiclePropertyIds.INFO_VIN, /* areaId= */ 0);
1089         // Property with area.
1090         GetPropertyRequest hvacTempDriverRequest = mManager.generateGetPropertyRequest(
1091                 VehiclePropertyIds.HVAC_TEMPERATURE_SET, DRIVER_SIDE_AREA_ID);
1092         GetPropertyRequest hvacTempPsgRequest = mManager.generateGetPropertyRequest(
1093                 VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID);
1094 
1095         getPropertyRequests.add(vinRequest);
1096         getPropertyRequests.add(hvacTempDriverRequest);
1097         getPropertyRequests.add(hvacTempPsgRequest);
1098 
1099         requestIds.add(vinRequest.getRequestId());
1100         requestIds.add(hvacTempDriverRequest.getRequestId());
1101         requestIds.add(hvacTempPsgRequest.getRequestId());
1102 
1103         int resultCount = 3;
1104         int errorCount = 0;
1105 
1106         int vendorErrorRequestId = 0;
1107 
1108         // A list of properties that will generate error results.
1109         for (int propId : List.of(
1110                 PROP_VALUE_STATUS_ERROR_INT_ARRAY,
1111                 PROP_VALUE_STATUS_ERROR_BOOLEAN,
1112                 PROP_VALUE_STATUS_UNAVAILABLE_INT,
1113                 PROP_VALUE_STATUS_UNAVAILABLE_FLOAT,
1114                 PROP_CAUSE_STATUS_CODE_TRY_AGAIN,
1115                 PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
1116                 PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
1117                 PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR_WITH_VENDOR_CODE,
1118                 PROP_CAUSE_STATUS_CODE_INVALID_ARG
1119             )) {
1120             GetPropertyRequest errorRequest = mManager.generateGetPropertyRequest(
1121                     propId, /* areaId= */ 0);
1122             getPropertyRequests.add(errorRequest);
1123             requestIds.add(errorRequest.getRequestId());
1124             errorCount++;
1125             if (propId == PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR_WITH_VENDOR_CODE) {
1126                 vendorErrorRequestId = errorRequest.getRequestId();
1127             }
1128         }
1129 
1130         GetPropertyRequest unavailableDriverRequest = mManager.generateGetPropertyRequest(
1131                 PROP_VALUE_STATUS_UNAVAILABLE_SEAT, DRIVER_SIDE_AREA_ID);
1132         getPropertyRequests.add(unavailableDriverRequest);
1133         requestIds.add(unavailableDriverRequest.getRequestId());
1134         errorCount++;
1135 
1136         GetPropertyRequest unavailablePsgRequest = mManager.generateGetPropertyRequest(
1137                 PROP_VALUE_STATUS_UNAVAILABLE_SEAT, PASSENGER_SIDE_AREA_ID);
1138         getPropertyRequests.add(unavailablePsgRequest);
1139         requestIds.add(unavailablePsgRequest.getRequestId());
1140         errorCount++;
1141 
1142         TestPropertyAsyncCallback callback = new TestPropertyAsyncCallback(
1143                 requestIds);
1144         mManager.getPropertiesAsync(getPropertyRequests, /* timeoutInMs= */ 1000,
1145                 /* cancellationSignal= */ null, callbackExecutor, callback);
1146 
1147         // Make the timeout longer than the timeout specified in getPropertiesAsync since the
1148         // error callback will be delivered after the request timed-out.
1149         callback.waitAndFinish(/* timeoutInMs= */ 2000);
1150 
1151         assertThat(callback.getTestErrors()).isEmpty();
1152         List<GetPropertyResult<?>> results = callback.getGetResultList();
1153         assertThat(results.size()).isEqualTo(resultCount);
1154         assertThat(callback.getErrorList().size()).isEqualTo(errorCount);
1155         for (GetPropertyResult<?> result : results) {
1156             int resultRequestId = result.getRequestId();
1157             if (resultRequestId == vinRequest.getRequestId()) {
1158                 assertThat(result.getValue().getClass()).isEqualTo(String.class);
1159                 assertThat(result.getValue()).isEqualTo(TEST_VIN);
1160             } else if (resultRequestId == hvacTempDriverRequest.getRequestId()
1161                     || resultRequestId == hvacTempPsgRequest.getRequestId()) {
1162                 assertThat(result.getValue().getClass()).isEqualTo(Float.class);
1163                 assertThat(result.getValue()).isEqualTo(INIT_TEMP_VALUE);
1164             } else {
1165                 Assert.fail("unknown result request Id: " + resultRequestId);
1166             }
1167         }
1168         for (PropertyAsyncError error : callback.getErrorList()) {
1169             assertThat(error.getErrorCode()).isNotEqualTo(0);
1170             int propertyId = error.getPropertyId();
1171             if (propertyId == PROP_VALUE_STATUS_ERROR_INT_ARRAY
1172                     || propertyId == PROP_VALUE_STATUS_ERROR_BOOLEAN
1173                     || propertyId == PROP_CAUSE_STATUS_CODE_ACCESS_DENIED
1174                     || propertyId == PROP_CAUSE_STATUS_CODE_INVALID_ARG) {
1175                 assertWithMessage("receive correct error for property ID: " + propertyId)
1176                         .that(error.getErrorCode()).isEqualTo(STATUS_ERROR_INTERNAL_ERROR);
1177                 assertThat(error.getVendorErrorCode()).isEqualTo(0);
1178             }
1179             if (propertyId == PROP_VALUE_STATUS_UNAVAILABLE_INT
1180                     || propertyId == PROP_VALUE_STATUS_UNAVAILABLE_FLOAT
1181                     || propertyId == PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE) {
1182                 assertWithMessage("receive correct error for property ID: " + propertyId)
1183                         .that(error.getErrorCode()).isEqualTo(STATUS_ERROR_NOT_AVAILABLE);
1184                 assertThat(error.getVendorErrorCode()).isEqualTo(0);
1185             }
1186             if (propertyId == PROP_CAUSE_STATUS_CODE_TRY_AGAIN) {
1187                 assertWithMessage("receive correct error for property ID: " + propertyId)
1188                         .that(error.getErrorCode()).isEqualTo(STATUS_ERROR_TIMEOUT);
1189                 assertThat(error.getVendorErrorCode()).isEqualTo(0);
1190             }
1191             if (error.getRequestId() == vendorErrorRequestId) {
1192                 assertWithMessage("receive correct error for property ID: " + propertyId)
1193                         .that(error.getErrorCode()).isEqualTo(STATUS_ERROR_INTERNAL_ERROR);
1194                 assertThat(error.getVendorErrorCode()).isEqualTo(VENDOR_CODE_FOR_INTERNAL_ERROR);
1195             }
1196         }
1197     }
1198 
1199     @Test
testSetPropertiesAsync()1200     public void testSetPropertiesAsync() throws Exception {
1201         List<SetPropertyRequest<?>> setPropertyRequests = new ArrayList<>();
1202         Executor callbackExecutor = new HandlerExecutor(mHandler);
1203         Set<Integer> requestIds = new ArraySet();
1204 
1205         // Global read-write property.
1206         SetPropertyRequest<Boolean> fuelDoorOpenRequest = mManager.generateSetPropertyRequest(
1207                 VehiclePropertyIds.FUEL_DOOR_OPEN, 0, true);
1208         // Seat area type read-write property.
1209         float tempValue1 = 10.1f;
1210         // This is less than minValue: 10, so it should be set to min value instead.
1211         float tempValue2 = 9.9f;
1212         SetPropertyRequest<Float> hvacTempDriverRequest = mManager.generateSetPropertyRequest(
1213                 VehiclePropertyIds.HVAC_TEMPERATURE_SET, DRIVER_SIDE_AREA_ID, tempValue1);
1214         SetPropertyRequest<Float> hvacTempPsgRequest = mManager.generateSetPropertyRequest(
1215                 VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID, tempValue2);
1216         SetPropertyRequest<Long> writeOnlyPropRequest = mManager.generateSetPropertyRequest(
1217                 VehiclePropertyIds.EPOCH_TIME, 0, /* value= */ 1L);
1218         // Write only property with the default waitForProperty set to true should generate error.
1219         writeOnlyPropRequest.setWaitForPropertyUpdate(false);
1220 
1221         setPropertyRequests.add(fuelDoorOpenRequest);
1222         setPropertyRequests.add(hvacTempDriverRequest);
1223         setPropertyRequests.add(hvacTempPsgRequest);
1224         setPropertyRequests.add(writeOnlyPropRequest);
1225 
1226         requestIds.add(fuelDoorOpenRequest.getRequestId());
1227         requestIds.add(hvacTempDriverRequest.getRequestId());
1228         requestIds.add(hvacTempPsgRequest.getRequestId());
1229         requestIds.add(writeOnlyPropRequest.getRequestId());
1230 
1231         List<Integer> successPropIds = List.of(
1232                 VehiclePropertyIds.FUEL_DOOR_OPEN,
1233                 VehiclePropertyIds.HVAC_TEMPERATURE_SET,
1234                 VehiclePropertyIds.EPOCH_TIME);
1235 
1236         int resultCount = requestIds.size();
1237         int errorCount = 0;
1238         int vendorErrorRequestId = 0;
1239 
1240         // A list of properties that will generate error results.
1241         List<Integer> errorPropIds = List.of(
1242                 PROP_CAUSE_STATUS_CODE_TRY_AGAIN,
1243                 PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
1244                 PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
1245                 PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE_WITH_VENDOR_CODE,
1246                 PROP_CAUSE_STATUS_CODE_INVALID_ARG,
1247                 PROP_ERROR_EVENT_NOT_AVAILABLE_DISABLED);
1248         for (int propId : errorPropIds) {
1249             SetPropertyRequest<Integer> errorRequest = mManager.generateSetPropertyRequest(
1250                     propId, /* areaId= */ 0, /* value= */ 1);
1251             setPropertyRequests.add(errorRequest);
1252             requestIds.add(errorRequest.getRequestId());
1253             errorCount++;
1254             if (propId == PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE_WITH_VENDOR_CODE) {
1255                 vendorErrorRequestId = errorRequest.getRequestId();
1256             }
1257         }
1258 
1259         long startTime = SystemClock.elapsedRealtimeNanos();
1260         TestPropertyAsyncCallback callback = new TestPropertyAsyncCallback(requestIds);
1261 
1262         mManager.setPropertiesAsync(setPropertyRequests, /* timeoutInMs= */ 1000,
1263                 /* cancellationSignal= */ null, callbackExecutor, callback);
1264 
1265         // Make the timeout longer than the timeout specified in setPropertiesAsync since the
1266         // error callback will be delivered after the request timed-out.
1267         callback.waitAndFinish(/* timeoutInMs= */ 2000);
1268 
1269         assertThat(callback.getTestErrors()).isEmpty();
1270         List<SetPropertyResult> results = callback.getSetResultList();
1271         assertThat(results.size()).isEqualTo(resultCount);
1272         assertThat(callback.getErrorList().size()).isEqualTo(errorCount);
1273         for (SetPropertyResult result : results) {
1274             assertThat(result.getPropertyId()).isIn(successPropIds);
1275             if (result.getPropertyId() == VehiclePropertyIds.HVAC_TEMPERATURE_SET) {
1276                 assertThat(result.getAreaId()).isIn(List.of(
1277                         DRIVER_SIDE_AREA_ID, PASSENGER_SIDE_AREA_ID));
1278             }
1279             assertThat(result.getUpdateTimestampNanos()).isGreaterThan(startTime);
1280         }
1281         for (PropertyAsyncError error : callback.getErrorList()) {
1282             int propertyId = error.getPropertyId();
1283             assertThat(propertyId).isIn(errorPropIds);
1284             assertThat(error.getAreaId()).isEqualTo(0);
1285             if (propertyId == PROP_CAUSE_STATUS_CODE_ACCESS_DENIED
1286                     || propertyId == PROP_CAUSE_STATUS_CODE_INVALID_ARG) {
1287                 assertWithMessage("receive correct error for property ID: " + propertyId)
1288                         .that(error.getErrorCode()).isEqualTo(STATUS_ERROR_INTERNAL_ERROR);
1289                 assertThat(error.getVendorErrorCode()).isEqualTo(0);
1290             }
1291             if (propertyId == PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE
1292                     || propertyId == PROP_ERROR_EVENT_NOT_AVAILABLE_DISABLED) {
1293                 assertWithMessage("receive correct error for property ID: " + propertyId)
1294                         .that(error.getErrorCode()).isEqualTo(STATUS_ERROR_NOT_AVAILABLE);
1295                 assertThat(error.getVendorErrorCode()).isEqualTo(0);
1296             }
1297             if (propertyId == PROP_CAUSE_STATUS_CODE_TRY_AGAIN) {
1298                 assertWithMessage("receive correct error for property ID: " + propertyId)
1299                         .that(error.getErrorCode()).isEqualTo(STATUS_ERROR_TIMEOUT);
1300                 assertThat(error.getVendorErrorCode()).isEqualTo(0);
1301             }
1302             if (error.getRequestId() == vendorErrorRequestId) {
1303                 assertWithMessage("receive correct error for property ID: " + propertyId)
1304                         .that(error.getErrorCode()).isEqualTo(STATUS_ERROR_NOT_AVAILABLE);
1305                 assertThat(error.getVendorErrorCode()).isEqualTo(VENDOR_CODE_FOR_NOT_AVAILABLE);
1306             }
1307         }
1308     }
1309 
1310     @Test
testGetVendorErrorCode_forGetProperty_throwsNotAvailable_EqualAfterR()1311     public void testGetVendorErrorCode_forGetProperty_throwsNotAvailable_EqualAfterR() {
1312         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
1313                 .isAtLeast(Build.VERSION_CODES.R);
1314         PropertyNotAvailableException thrown = assertThrows(PropertyNotAvailableException.class,
1315                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE_WITH_VENDOR_CODE,
1316                         /* areaId= */ 0));
1317 
1318         assertThat(thrown.getVendorErrorCode()).isEqualTo(VENDOR_CODE_FOR_NOT_AVAILABLE);
1319     }
1320 
1321     @Test
testGetVendorErrorCode_forGetProperty_throwsInternalError_EqualAfterR()1322     public void testGetVendorErrorCode_forGetProperty_throwsInternalError_EqualAfterR() {
1323         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
1324                 .isAtLeast(Build.VERSION_CODES.R);
1325         CarInternalErrorException thrown = assertThrows(CarInternalErrorException.class,
1326                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR_WITH_VENDOR_CODE,
1327                         /* areaId= */ 0));
1328 
1329         assertThat(thrown.getVendorErrorCode()).isEqualTo(VENDOR_CODE_FOR_INTERNAL_ERROR);
1330     }
1331 
1332     @Test
testGetVendorErrorCode_forSetProperty_throwsNotAvailable_EqualAfterR()1333     public void testGetVendorErrorCode_forSetProperty_throwsNotAvailable_EqualAfterR() {
1334         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
1335                 .isAtLeast(Build.VERSION_CODES.R);
1336         PropertyNotAvailableException thrown = assertThrows(PropertyNotAvailableException.class,
1337                 () -> mManager.setProperty(Integer.class,
1338                         PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE_WITH_VENDOR_CODE, /* areaId= */ 0,
1339                         /* val= */ 0));
1340 
1341         assertThat(thrown.getVendorErrorCode()).isEqualTo(VENDOR_CODE_FOR_NOT_AVAILABLE);
1342     }
1343 
1344     @Test
testGetVendorErrorCode_forSetProperty_throwsInternalError_EqualAfterR()1345     public void testGetVendorErrorCode_forSetProperty_throwsInternalError_EqualAfterR() {
1346         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
1347                 .isAtLeast(Build.VERSION_CODES.R);
1348         CarInternalErrorException thrown = assertThrows(CarInternalErrorException.class,
1349                 () -> mManager.setProperty(Integer.class,
1350                         PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR_WITH_VENDOR_CODE, /* areaId= */0,
1351                         /* val= */ 0));
1352 
1353         assertThat(thrown.getVendorErrorCode()).isEqualTo(VENDOR_CODE_FOR_INTERNAL_ERROR);
1354     }
1355 
1356     @Override
configureMockedHal()1357     protected void configureMockedHal() {
1358         PropertyHandler handler = new PropertyHandler();
1359         addAidlProperty(CUSTOM_SEAT_MIXED_PROP_ID_1, handler).setConfigArray(CONFIG_ARRAY_1)
1360                 .addAreaConfig(DRIVER_SIDE_AREA_ID).addAreaConfig(PASSENGER_SIDE_AREA_ID);
1361         addAidlProperty(CUSTOM_GLOBAL_MIXED_PROP_ID_2, handler).setConfigArray(CONFIG_ARRAY_2);
1362         addAidlProperty(CUSTOM_GLOBAL_MIXED_PROP_ID_3, handler).setConfigArray(CONFIG_ARRAY_3);
1363         addAidlProperty(CUSTOM_GLOBAL_INT_ARRAY_PROP, handler);
1364 
1365         addAidlProperty(VehicleProperty.TIRE_PRESSURE, handler).addAreaConfig(
1366                 VehicleAreaWheel.WHEEL_LEFT_REAR).addAreaConfig(
1367                 VehicleAreaWheel.WHEEL_RIGHT_REAR).addAreaConfig(
1368                 VehicleAreaWheel.WHEEL_RIGHT_FRONT).addAreaConfig(
1369                 VehicleAreaWheel.WHEEL_LEFT_FRONT).setChangeMode(
1370                 CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS).setMaxSampleRate(
1371                 10).setMinSampleRate(1);
1372 
1373         VehiclePropValue tempValue = new VehiclePropValue();
1374         tempValue.value = new RawPropValues();
1375         tempValue.value.floatValues = new float[]{INIT_TEMP_VALUE};
1376         tempValue.prop = VehicleProperty.HVAC_TEMPERATURE_SET;
1377         addAidlProperty(VehicleProperty.HVAC_TEMPERATURE_SET, tempValue)
1378                 .addAreaConfig(DRIVER_SIDE_AREA_ID, /* minValue = */ 10, /* maxValue = */ 20)
1379                 .addAreaConfig(PASSENGER_SIDE_AREA_ID, /* minValue = */ 10, /* maxValue = */ 20);
1380         VehiclePropValue vinValue = new VehiclePropValue();
1381         vinValue.value = new RawPropValues();
1382         vinValue.value.stringValue = TEST_VIN;
1383         vinValue.prop = VehicleProperty.INFO_VIN;
1384         addAidlProperty(VehicleProperty.INFO_VIN, vinValue);
1385         addAidlProperty(VehicleProperty.FUEL_DOOR_OPEN);
1386         addAidlProperty(VehicleProperty.ANDROID_EPOCH_TIME);
1387 
1388         addAidlProperty(PROP_VALUE_STATUS_ERROR_INT_ARRAY, handler);
1389         addAidlProperty(PROP_VALUE_STATUS_UNKNOWN_INT_ARRAY, handler);
1390         addAidlProperty(PROP_VALUE_STATUS_UNAVAILABLE_INT, handler);
1391         addAidlProperty(PROP_VALUE_STATUS_UNAVAILABLE_FLOAT, handler);
1392         addAidlProperty(PROP_VALUE_STATUS_ERROR_BOOLEAN, handler);
1393         addAidlProperty(PROP_VALUE_STATUS_UNAVAILABLE_SEAT, handler)
1394                 .addAreaConfig(DRIVER_SIDE_AREA_ID).addAreaConfig(PASSENGER_SIDE_AREA_ID)
1395                 .setChangeMode(CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS)
1396                 .setMaxSampleRate(10).setMinSampleRate(1);
1397 
1398         addAidlProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, handler);
1399         addAidlProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN, handler);
1400         addAidlProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, handler);
1401         addAidlProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG, handler);
1402         addAidlProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, handler);
1403         addAidlProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE_WITH_VENDOR_CODE, handler);
1404         addAidlProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR_WITH_VENDOR_CODE, handler);
1405 
1406         addAidlProperty(PROP_CAUSE_STATUS_CODE_UNKNOWN, handler);
1407 
1408         addAidlProperty(CUSTOM_SEAT_INT_PROP_1, handler).addAreaConfig(DRIVER_SIDE_AREA_ID)
1409                 .addAreaConfig(PASSENGER_SIDE_AREA_ID);
1410         addAidlProperty(CUSTOM_SEAT_INT_PROP_2, handler).addAreaConfig(DRIVER_SIDE_AREA_ID)
1411                 .addAreaConfig(PASSENGER_SIDE_AREA_ID);
1412 
1413         addAidlProperty(NULL_VALUE_PROP, handler);
1414         addAidlProperty(PROP_UNSUPPORTED, handler);
1415         addAidlProperty(PROP_ERROR_EVENT_NOT_AVAILABLE_DISABLED, handler);
1416 
1417         // Add properties for permission testing.
1418         addAidlProperty(SUPPORT_CUSTOM_PERMISSION, handler).setConfigArray(
1419                 VENDOR_PERMISSION_CONFIG);
1420         addAidlProperty(PROP_WITH_READ_ONLY_PERMISSION, handler);
1421         addAidlProperty(PROP_WITH_WRITE_ONLY_PERMISSION, handler);
1422 
1423         addAidlProperty(PROP_UNSUPPORTED, handler);
1424     }
1425 
1426     private class PropertyHandler implements VehicleHalPropertyHandler {
1427         SparseArray<SparseArray<VehiclePropValue>> mValueByAreaIdByPropId = new SparseArray<>();
1428 
1429         @Override
onPropertySet2(VehiclePropValue value)1430         public synchronized boolean onPropertySet2(VehiclePropValue value) {
1431             // Simulate VehicleHal.set() behavior.
1432             if (value.prop == PROP_ERROR_EVENT_NOT_AVAILABLE_DISABLED) {
1433                 injectErrorEvent(PROP_ERROR_EVENT_NOT_AVAILABLE_DISABLED, /* areaId= */ 0,
1434                         VehicleHalStatusCode.STATUS_NOT_AVAILABLE_DISABLED);
1435                 // Don't generate property change event.
1436                 return false;
1437             }
1438 
1439             int statusCode = mapPropertyToVhalStatusCode(value.prop);
1440             if (statusCode != VehicleHalStatusCode.STATUS_OK) {
1441                 // The ServiceSpecificException here would pass the statusCode back to caller.
1442                 throw new ServiceSpecificException(statusCode);
1443             }
1444 
1445             int areaId = value.areaId;
1446             int propId = value.prop;
1447             if (mValueByAreaIdByPropId.get(propId) == null) {
1448                 mValueByAreaIdByPropId.put(propId, new SparseArray<>());
1449             }
1450             mValueByAreaIdByPropId.get(propId).put(areaId, value);
1451             return true;
1452         }
1453 
1454         @Override
onPropertyGet(VehiclePropValue value)1455         public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
1456             // Simulate VehicleHal.get() behavior.
1457             int vhalStatusCode = mapPropertyToVhalStatusCode(value.prop);
1458             if (vhalStatusCode != VehicleHalStatusCode.STATUS_OK) {
1459                 // The ServiceSpecificException here would pass the statusCode back to caller.
1460                 throw new ServiceSpecificException(vhalStatusCode);
1461             }
1462 
1463             if (value.prop == NULL_VALUE_PROP) {
1464                 // Return null to simulate an unavailable property.
1465                 // HAL implementation should return STATUS_TRY_AGAIN when a property is unavailable,
1466                 // however, it may also return null with STATUS_OKAY, and we want to handle this
1467                 // properly.
1468                 return null;
1469             }
1470 
1471             VehiclePropValue returnValue = new VehiclePropValue();
1472             returnValue.prop = value.prop;
1473             returnValue.areaId = value.areaId;
1474             returnValue.timestamp = SystemClock.elapsedRealtimeNanos();
1475             returnValue.value = new RawPropValues();
1476             int propertyStatus = mapPropertyToVehiclePropertyStatus(value.prop);
1477             if (propertyStatus != VehiclePropertyStatus.AVAILABLE) {
1478                 returnValue.status = propertyStatus;
1479                 return returnValue;
1480             }
1481             if (mValueByAreaIdByPropId.get(value.prop) == null) {
1482                 return null;
1483             }
1484             return mValueByAreaIdByPropId.get(value.prop).get(value.areaId);
1485         }
1486 
1487         @Override
onPropertySubscribe(int property, float sampleRate)1488         public synchronized void onPropertySubscribe(int property, float sampleRate) {
1489             Log.d(TAG, "onPropertySubscribe property "
1490                     + property + " sampleRate " + sampleRate);
1491         }
1492 
1493         @Override
onPropertyUnsubscribe(int property)1494         public synchronized void onPropertyUnsubscribe(int property) {
1495             Log.d(TAG, "onPropertyUnSubscribe property " + property);
1496         }
1497     }
1498 
propToString(int propertyId)1499     private static String propToString(int propertyId) {
1500         return VehiclePropertyIds.toString(propertyId) + " (" + propertyId + ")";
1501     }
1502 
mapPropertyToVhalStatusCode(int propId)1503     private static int mapPropertyToVhalStatusCode(int propId) {
1504         switch (propId) {
1505             case PROP_CAUSE_STATUS_CODE_TRY_AGAIN:
1506                 return VehicleHalStatusCode.STATUS_TRY_AGAIN;
1507             case PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE:
1508                 return VehicleHalStatusCode.STATUS_NOT_AVAILABLE;
1509             case PROP_CAUSE_STATUS_CODE_ACCESS_DENIED:
1510                 return VehicleHalStatusCode.STATUS_ACCESS_DENIED;
1511             case PROP_CAUSE_STATUS_CODE_INVALID_ARG:
1512                 return VehicleHalStatusCode.STATUS_INVALID_ARG;
1513             case PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR:
1514                 return VehicleHalStatusCode.STATUS_INTERNAL_ERROR;
1515             case PROP_CAUSE_STATUS_CODE_UNKNOWN:
1516                 return -1;
1517             case PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR_WITH_VENDOR_CODE:
1518                 return INTERNAL_ERROR_WITH_VENDOR_CODE;
1519             case PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE_WITH_VENDOR_CODE:
1520                 return NOT_AVAILABLE_WITH_VENDOR_CODE;
1521             default:
1522                 return VehicleHalStatusCode.STATUS_OK;
1523         }
1524     }
1525 
mapPropertyToVehiclePropertyStatus(int propId)1526     private static int mapPropertyToVehiclePropertyStatus(int propId) {
1527         switch (propId) {
1528             case PROP_VALUE_STATUS_ERROR_INT_ARRAY:
1529             case PROP_VALUE_STATUS_ERROR_BOOLEAN:
1530                 return VehiclePropertyStatus.ERROR;
1531             case PROP_VALUE_STATUS_UNAVAILABLE_INT:
1532             case PROP_VALUE_STATUS_UNAVAILABLE_FLOAT:
1533                 return VehiclePropertyStatus.UNAVAILABLE;
1534             case PROP_VALUE_STATUS_UNKNOWN_INT_ARRAY:
1535                 return -1;
1536             default:
1537                 return VehiclePropertyStatus.AVAILABLE;
1538         }
1539     }
1540 
newTirePressureVehiclePropValue(int areaId, float floatValue, long timestampNanos)1541     private static VehiclePropValue newTirePressureVehiclePropValue(int areaId, float floatValue,
1542             long timestampNanos) {
1543         VehiclePropValue vehiclePropValue = new VehiclePropValue();
1544         vehiclePropValue.prop = VehiclePropertyIds.TIRE_PRESSURE;
1545         vehiclePropValue.areaId = areaId;
1546         vehiclePropValue.value = new RawPropValues();
1547         vehiclePropValue.value.floatValues = new float[]{floatValue};
1548         vehiclePropValue.timestamp = timestampNanos;
1549         return vehiclePropValue;
1550     }
1551 
assertTirePressureCarPropertyValue(CarPropertyValue<?> carPropertyValue, int areaId, float floatValue, long timestampNanos)1552     private static void assertTirePressureCarPropertyValue(CarPropertyValue<?> carPropertyValue,
1553             int areaId, float floatValue, long timestampNanos) {
1554         assertThat(carPropertyValue.getPropertyId()).isEqualTo(VehiclePropertyIds.TIRE_PRESSURE);
1555         assertThat(carPropertyValue.getAreaId()).isEqualTo(areaId);
1556         assertThat(carPropertyValue.getStatus()).isEqualTo(CarPropertyValue.STATUS_AVAILABLE);
1557         assertThat(carPropertyValue.getTimestamp()).isEqualTo(timestampNanos);
1558         assertThat(carPropertyValue.getValue()).isEqualTo(floatValue);
1559     }
1560 
1561     private static class TestCallback implements CarPropertyManager.CarPropertyEventCallback {
1562         private static final String CALLBACK_TAG = "ErrorEventTest";
1563 
1564         private final int mInitValueCount;
1565         private final int mChangeEventCount;
1566         private final int mErrorEventCount;
1567         private CountDownLatch mInitialValueCdLatch;
1568         private CountDownLatch mChangeEventCdLatch;
1569         private CountDownLatch mErrorEventCdLatch;
1570 
1571         private final Object mLock = new Object();
1572         private final List<CarPropertyValue> mChangeEvents = new ArrayList<>();
1573         private final List<CarPropertyValue> mInitialValues = new ArrayList<>();
1574         private Integer mErrorCode;
1575 
TestCallback(int initValueCount, int changeEventCount, int errorEventCount)1576         TestCallback(int initValueCount, int changeEventCount, int errorEventCount) {
1577             mInitValueCount = initValueCount;
1578             mChangeEventCount = changeEventCount;
1579             mErrorEventCount = errorEventCount;
1580             // We expect to receive one initial event for each area.
1581             mInitialValueCdLatch = new CountDownLatch(mInitValueCount);
1582             mChangeEventCdLatch = new CountDownLatch(mChangeEventCount);
1583             mErrorEventCdLatch = new CountDownLatch(mErrorEventCount);
1584         }
1585 
1586         @Override
onChangeEvent(CarPropertyValue carPropertyValue)1587         public void onChangeEvent(CarPropertyValue carPropertyValue) {
1588             Log.d(CALLBACK_TAG, "onChangeEvent: " + carPropertyValue);
1589             synchronized (mLock) {
1590                 if (mInitialValueCdLatch.getCount() > 0) {
1591                     mInitialValueCdLatch.countDown();
1592                     mInitialValues.add(carPropertyValue);
1593                 } else {
1594                     mChangeEventCdLatch.countDown();
1595                     mChangeEvents.add(carPropertyValue);
1596                 }
1597             }
1598         }
1599 
1600         @Override
onErrorEvent(int propId, int areaId)1601         public void onErrorEvent(int propId, int areaId) {
1602             Log.d(CALLBACK_TAG, "onErrorEvent, propId: " + propId + " areaId: " + areaId);
1603             synchronized (mLock) {
1604                 mErrorEventCdLatch.countDown();
1605             }
1606         }
1607 
1608         @Override
onErrorEvent(int propId, int areaId, int errorCode)1609         public void onErrorEvent(int propId, int areaId, int errorCode) {
1610             Log.d(CALLBACK_TAG, "onErrorEvent, propId: " + propId + " areaId: " + areaId
1611                     + "errorCode: " + errorCode);
1612             synchronized (mLock) {
1613                 mErrorCode = errorCode;
1614                 mErrorEventCdLatch.countDown();
1615             }
1616         }
1617 
getErrorCode()1618         public Integer getErrorCode() {
1619             synchronized (mLock) {
1620                 return mErrorCode;
1621             }
1622         }
1623 
assertOnErrorEventCalled()1624         public void assertOnErrorEventCalled() throws InterruptedException {
1625             if (!mErrorEventCdLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
1626                 long got = mErrorEventCount - mErrorEventCdLatch.getCount();
1627                 throw new IllegalStateException("Does not receive enough error events before  "
1628                         + CALLBACK_SHORT_TIMEOUT_MS + " ms, got: " + got
1629                         + ", expected: " + mErrorEventCount);
1630             }
1631         }
1632 
assertOnErrorEventNotCalled()1633         public void assertOnErrorEventNotCalled() throws InterruptedException {
1634             if (mErrorEventCdLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
1635                 long got = mErrorEventCount - mErrorEventCdLatch.getCount();
1636                 throw new IllegalStateException("Receive more error events than expected after  "
1637                         + CALLBACK_SHORT_TIMEOUT_MS + " ms, got: " + got
1638                         + ", expected less than: " + mErrorEventCount);
1639             }
1640         }
1641 
assertRegisterCompleted()1642         public void assertRegisterCompleted() throws InterruptedException {
1643             assertRegisterCompleted(CALLBACK_SHORT_TIMEOUT_MS);
1644         }
1645 
assertRegisterCompleted(int timeoutMs)1646         public void assertRegisterCompleted(int timeoutMs) throws InterruptedException {
1647             if (!mInitialValueCdLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
1648                 long got = mInitValueCount - mInitialValueCdLatch.getCount();
1649                 throw new IllegalStateException("Does not receive enough initial value events "
1650                         + "before  " + CALLBACK_SHORT_TIMEOUT_MS + " ms, got: " + got
1651                         + ", expected: " + mInitValueCount);
1652             }
1653         }
1654 
getChangeEventCounter()1655         public int getChangeEventCounter() {
1656             synchronized (mLock) {
1657                 return mChangeEvents.size();
1658             }
1659         }
1660 
waitAndGetChangeEvents()1661         public List<CarPropertyValue> waitAndGetChangeEvents() throws InterruptedException {
1662             if (!mChangeEventCdLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
1663                 long got = mChangeEventCount - mChangeEventCdLatch.getCount();
1664                 throw new IllegalStateException("Does not receive enough property events before  "
1665                         + CALLBACK_SHORT_TIMEOUT_MS + " ms, got: " + got
1666                         + ", expected: " + mChangeEventCount);
1667             }
1668             synchronized (mLock) {
1669                 return mChangeEvents;
1670             }
1671         }
1672 
getInitialValues()1673         public List<CarPropertyValue> getInitialValues() {
1674             synchronized (mLock) {
1675                 return mInitialValues;
1676             }
1677         }
1678     }
1679 
1680     // This is almost the same as {@link android.os.HandlerExecutor} except that it will not throw
1681     // exception even if the underlying handler is already shut down.
1682     private static class HandlerExecutor implements Executor {
1683         private final Handler mHandler;
1684 
HandlerExecutor(@onNull Handler handler)1685         HandlerExecutor(@NonNull Handler handler) {
1686             mHandler = handler;
1687         }
1688 
1689         @Override
execute(Runnable command)1690         public void execute(Runnable command) {
1691             mHandler.post(command);
1692         }
1693     }
1694 
1695 }
1696