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