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 com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.fail;
22 import static org.testng.Assert.assertThrows;
23 
24 import android.car.Car;
25 import android.car.VehicleAreaType;
26 import android.car.VehiclePropertyIds;
27 import android.car.hardware.CarPropertyConfig;
28 import android.car.hardware.CarPropertyValue;
29 import android.car.hardware.property.CarInternalErrorException;
30 import android.car.hardware.property.CarPropertyManager;
31 import android.car.hardware.property.PropertyAccessDeniedSecurityException;
32 import android.car.hardware.property.PropertyNotAvailableAndRetryException;
33 import android.car.hardware.property.PropertyNotAvailableException;
34 import android.car.hardware.property.VehicleHalStatusCode;
35 import android.car.test.util.Visitor;
36 import android.hardware.automotive.vehicle.V2_0.VehicleArea;
37 import android.hardware.automotive.vehicle.V2_0.VehicleAreaSeat;
38 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
39 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyGroup;
40 import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
41 import android.os.Build;
42 import android.os.ServiceSpecificException;
43 import android.os.SystemClock;
44 import android.util.ArraySet;
45 import android.util.Log;
46 
47 import androidx.test.ext.junit.runners.AndroidJUnit4;
48 import androidx.test.filters.MediumTest;
49 
50 import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
51 
52 import com.google.common.truth.Truth;
53 
54 import org.junit.Assert;
55 import org.junit.Rule;
56 import org.junit.Test;
57 import org.junit.rules.TestName;
58 import org.junit.runner.RunWith;
59 
60 import java.util.ArrayList;
61 import java.util.Arrays;
62 import java.util.HashMap;
63 import java.util.List;
64 import java.util.concurrent.ConcurrentHashMap;
65 import java.util.concurrent.CountDownLatch;
66 import java.util.concurrent.TimeUnit;
67 
68 /**
69  * Test for {@link android.car.hardware.property.CarPropertyManager}
70  */
71 @RunWith(AndroidJUnit4.class)
72 @MediumTest
73 public class CarPropertyManagerTest extends MockedCarTestBase {
74 
75     private static final String TAG = CarPropertyManagerTest.class.getSimpleName();
76 
77     /**
78      * configArray[0], 1 indicates the property has a String value
79      * configArray[1], 1 indicates the property has a Boolean value .
80      * configArray[2], 1 indicates the property has a Integer value
81      * configArray[3], the number indicates the size of Integer[]  in the property.
82      * configArray[4], 1 indicates the property has a Long value .
83      * configArray[5], the number indicates the size of Long[]  in the property.
84      * configArray[6], 1 indicates the property has a Float value .
85      * configArray[7], the number indicates the size of Float[] in the property.
86      * configArray[8], the number indicates the size of byte[] in the property.
87      */
88     private static final java.util.Collection<Integer> CONFIG_ARRAY_1 =
89             Arrays.asList(1, 0, 1, 0, 1, 0, 0, 0, 0);
90     private static final java.util.Collection<Integer> CONFIG_ARRAY_2 =
91             Arrays.asList(1, 1, 1, 0, 0, 0, 0, 2, 0);
92     private static final java.util.Collection<Integer> CONFIG_ARRAY_3 =
93             Arrays.asList(0, 1, 1, 0, 0, 0, 1, 0, 0);
94     private static final Object[] EXPECTED_VALUE_1 = {"android", 1, 1L};
95     private static final Object[] EXPECTED_VALUE_2 = {"android", true, 3, 1.1f, 2f};
96     private static final Object[] EXPECTED_VALUE_3 = {true, 1, 2.2f};
97 
98     private static final int CUSTOM_SEAT_INT_PROP_1 =
99             0x1201 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.SEAT;
100     private static final int CUSTOM_SEAT_INT_PROP_2 =
101             0x1202 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.SEAT;
102 
103     private static final int CUSTOM_SEAT_MIXED_PROP_ID_1 =
104             0x1101 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.MIXED | VehicleArea.SEAT;
105     private static final int CUSTOM_GLOBAL_MIXED_PROP_ID_2 =
106             0x1102 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.MIXED | VehicleArea.GLOBAL;
107     private static final int CUSTOM_GLOBAL_MIXED_PROP_ID_3 =
108             0x1110 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.MIXED | VehicleArea.GLOBAL;
109 
110     private static final int CUSTOM_GLOBAL_INT_ARRAY_PROP =
111             0x1103 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32_VEC
112                     | VehicleArea.GLOBAL;
113     private static final Integer[] FAKE_INT_ARRAY_VALUE = {1, 2};
114 
115     private static final int INT_ARRAY_PROP_STATUS_ERROR =
116             0x1104 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32_VEC
117                     | VehicleArea.GLOBAL;
118 
119     private static final int BOOLEAN_PROP_STATUS_ERROR =
120             0x1105 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.BOOLEAN
121                     | VehicleArea.GLOBAL;
122     private static final boolean FAKE_BOOLEAN_PROPERTY_VALUE = true;
123     private static final int FLOAT_PROP_STATUS_UNAVAILABLE =
124             0x1106 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.FLOAT
125                     | VehicleArea.GLOBAL;
126     private static final float FAKE_FLOAT_PROPERTY_VALUE = 3f;
127     private static final int INT_PROP_STATUS_UNAVAILABLE =
128             0x1107 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32
129                     | VehicleArea.GLOBAL;
130     private static final int FAKE_INT_PROPERTY_VALUE = 3;
131 
132     // Vendor properties for testing exceptions.
133     private static final int PROP_CAUSE_STATUS_CODE_TRY_AGAIN =
134             0x1201 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
135     private static final int PROP_CAUSE_STATUS_CODE_INVALID_ARG =
136             0x1202 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
137     private static final int PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE =
138             0x1203 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
139     private static final int PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR =
140             0x1204 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
141     private static final int PROP_CAUSE_STATUS_CODE_ACCESS_DENIED =
142             0x1205 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
143 
144     // Use FAKE_PROPERTY_ID to test api return null or throw exception.
145     private static final int FAKE_PROPERTY_ID = 0x111;
146 
147     private static final int DRIVER_SIDE_AREA_ID = VehicleAreaSeat.ROW_1_LEFT
148                                                     | VehicleAreaSeat.ROW_2_LEFT;
149     private static final int PASSENGER_SIDE_AREA_ID = VehicleAreaSeat.ROW_1_RIGHT
150                                                     | VehicleAreaSeat.ROW_2_CENTER
151                                                     | VehicleAreaSeat.ROW_2_RIGHT;
152     private static final float INIT_TEMP_VALUE = 16f;
153     private static final float CHANGED_TEMP_VALUE = 20f;
154     private static final int CALLBACK_SHORT_TIMEOUT_MS = 350; // ms
155     // Wait for CarPropertyManager register/unregister listener
156     private static final long WAIT_FOR_NO_EVENTS = 50;
157 
158     private static final List<Integer> USER_HAL_PROPERTIES = Arrays.asList(
159             VehiclePropertyIds.INITIAL_USER_INFO,
160             VehiclePropertyIds.SWITCH_USER,
161             VehiclePropertyIds.CREATE_USER,
162             VehiclePropertyIds.REMOVE_USER,
163             VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION
164             );
165 
166     private CarPropertyManager mManager;
167 
168     @Rule public TestName mTestName = new TestName();
169 
170     @Override
setUp()171     public void setUp() throws Exception {
172         super.setUp();
173         setUpTargetSdk();
174         mManager = (CarPropertyManager) getCar().getCarManager(Car.PROPERTY_SERVICE);
175         assertThat(mManager).isNotNull();
176     }
177 
setUpTargetSdk()178     private void setUpTargetSdk() {
179         if (mTestName.getMethodName().endsWith("InQ")) {
180             getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.Q;
181         } else if (mTestName.getMethodName().endsWith("AfterQ")) {
182             getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.R;
183         } else if (mTestName.getMethodName().endsWith("AfterR")) {
184             getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.S;
185         }
186     }
187 
188     @Test
testMixedPropertyConfigs()189     public void testMixedPropertyConfigs() {
190         List<CarPropertyConfig> configs = mManager.getPropertyList();
191         for (CarPropertyConfig cfg : configs) {
192             switch (cfg.getPropertyId()) {
193                 case CUSTOM_SEAT_MIXED_PROP_ID_1:
194                     assertThat(cfg.getConfigArray()).containsExactlyElementsIn(CONFIG_ARRAY_1)
195                             .inOrder();
196                     break;
197                 case CUSTOM_GLOBAL_MIXED_PROP_ID_2:
198                     assertThat(cfg.getConfigArray()).containsExactlyElementsIn(CONFIG_ARRAY_2)
199                             .inOrder();
200                     break;
201                 case CUSTOM_GLOBAL_MIXED_PROP_ID_3:
202                     assertThat(cfg.getConfigArray()).containsExactlyElementsIn(CONFIG_ARRAY_3)
203                             .inOrder();
204                     break;
205                 case VehiclePropertyIds.HVAC_TEMPERATURE_SET:
206                 case PROP_CAUSE_STATUS_CODE_ACCESS_DENIED:
207                 case PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR:
208                 case PROP_CAUSE_STATUS_CODE_TRY_AGAIN:
209                 case PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE:
210                 case PROP_CAUSE_STATUS_CODE_INVALID_ARG:
211                 case CUSTOM_SEAT_INT_PROP_1:
212                 case CUSTOM_SEAT_INT_PROP_2:
213                 case CUSTOM_GLOBAL_INT_ARRAY_PROP:
214                 case INT_ARRAY_PROP_STATUS_ERROR:
215                 case BOOLEAN_PROP_STATUS_ERROR:
216                 case INT_PROP_STATUS_UNAVAILABLE:
217                 case FLOAT_PROP_STATUS_UNAVAILABLE:
218                 case VehiclePropertyIds.INFO_VIN:
219                     break;
220                 default:
221                     Assert.fail("Unexpected CarPropertyConfig: " + cfg.toString());
222             }
223         }
224     }
225 
226     @Test
testGetMixTypeProperty()227     public void testGetMixTypeProperty() {
228         mManager.setProperty(Object[].class, CUSTOM_SEAT_MIXED_PROP_ID_1,
229                 0, EXPECTED_VALUE_1);
230         CarPropertyValue<Object[]> result = mManager.getProperty(
231                 CUSTOM_SEAT_MIXED_PROP_ID_1, 0);
232         assertThat(result.getValue()).isEqualTo(EXPECTED_VALUE_1);
233 
234         mManager.setProperty(Object[].class, CUSTOM_GLOBAL_MIXED_PROP_ID_2,
235                 0, EXPECTED_VALUE_2);
236         result = mManager.getProperty(
237                 CUSTOM_GLOBAL_MIXED_PROP_ID_2, 0);
238         assertThat(result.getValue()).isEqualTo(EXPECTED_VALUE_2);
239 
240         mManager.setProperty(Object[].class, CUSTOM_GLOBAL_MIXED_PROP_ID_3,
241                 0, EXPECTED_VALUE_3);
242         result = mManager.getProperty(
243                 CUSTOM_GLOBAL_MIXED_PROP_ID_3, 0);
244         assertThat(result.getValue()).isEqualTo(EXPECTED_VALUE_3);
245     }
246 
247     /**
248      * Test {@link android.car.hardware.property.CarPropertyManager#getIntArrayProperty(int, int)}
249      */
250     @Test
testGetIntArrayProperty()251     public void testGetIntArrayProperty() {
252         mManager.setProperty(Integer[].class, CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL,
253                 FAKE_INT_ARRAY_VALUE);
254 
255         int[] result = mManager.getIntArrayProperty(CUSTOM_GLOBAL_INT_ARRAY_PROP,
256                 VehicleArea.GLOBAL);
257         assertThat(result).asList().containsExactlyElementsIn(FAKE_INT_ARRAY_VALUE);
258     }
259 
260     /**
261      * Test {@link CarPropertyManager#getIntArrayProperty(int, int)} when vhal returns a value with
262      * error status.
263      */
264     @Test
testGetIntArrayPropertyWithErrorStatusAfterR()265     public void testGetIntArrayPropertyWithErrorStatusAfterR() {
266         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
267                 .isGreaterThan(Build.VERSION_CODES.R);
268         mManager.setProperty(Integer[].class, INT_ARRAY_PROP_STATUS_ERROR,
269                 VehicleArea.GLOBAL, FAKE_INT_ARRAY_VALUE);
270         assertThrows(CarInternalErrorException.class,
271                 () -> mManager.getIntArrayProperty(INT_ARRAY_PROP_STATUS_ERROR,
272                         VehicleArea.GLOBAL));
273     }
274 
275     /**
276      * Test {@link CarPropertyManager#getIntProperty(int, int)} when vhal returns a value with
277      * unavailable status.
278      */
279     @Test
testGetIntPropertyWithUnavailableStatusAfterR()280     public void testGetIntPropertyWithUnavailableStatusAfterR() {
281         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
282                 .isGreaterThan(Build.VERSION_CODES.R);
283         mManager.setProperty(Integer.class, INT_PROP_STATUS_UNAVAILABLE,
284                 VehicleArea.GLOBAL, FAKE_INT_PROPERTY_VALUE);
285         assertThrows(PropertyNotAvailableException.class,
286                 () -> mManager.getIntProperty(INT_PROP_STATUS_UNAVAILABLE, VehicleArea.GLOBAL));
287 
288     }
289 
290     /**
291      * Test {@link CarPropertyManager#getBooleanProperty(int, int)} when vhal returns a value with
292      * error status.
293      */
294     @Test
testGetBooleanPropertyWithErrorStatusAfterR()295     public void testGetBooleanPropertyWithErrorStatusAfterR() {
296         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
297                 .isGreaterThan(Build.VERSION_CODES.R);
298         mManager.setProperty(Boolean.class, BOOLEAN_PROP_STATUS_ERROR,
299                 VehicleArea.GLOBAL, FAKE_BOOLEAN_PROPERTY_VALUE);
300         assertThrows(CarInternalErrorException.class,
301                 () -> mManager.getBooleanProperty(BOOLEAN_PROP_STATUS_ERROR, VehicleArea.GLOBAL));
302     }
303 
304     /**
305      * Test {@link CarPropertyManager#getFloatProperty(int, int)} when vhal returns a value with
306      * unavailable status.
307      */
308     @Test
testGetFloatPropertyWithUnavailableStatusAfterR()309     public void testGetFloatPropertyWithUnavailableStatusAfterR() {
310         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
311                 .isGreaterThan(Build.VERSION_CODES.R);
312         mManager.setProperty(Float.class, FLOAT_PROP_STATUS_UNAVAILABLE,
313                 VehicleArea.GLOBAL, FAKE_FLOAT_PROPERTY_VALUE);
314         assertThrows(PropertyNotAvailableException.class,
315                 () -> mManager.getFloatProperty(FLOAT_PROP_STATUS_UNAVAILABLE, VehicleArea.GLOBAL));
316     }
317 
318     /**
319      * Test {@link CarPropertyManager#getProperty(Class, int, int)}
320      */
321     @Test
testGetPropertyWithClass()322     public void testGetPropertyWithClass() {
323         mManager.setProperty(Integer[].class, CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL,
324                 FAKE_INT_ARRAY_VALUE);
325 
326         CarPropertyValue<Integer[]> result = mManager.getProperty(Integer[].class,
327                 CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL);
328         assertThat(result.getValue()).asList().containsExactlyElementsIn(FAKE_INT_ARRAY_VALUE);
329     }
330 
331     /**
332      * Test {@link CarPropertyManager#isPropertyAvailable(int, int)}
333      */
334     @Test
testIsPropertyAvailable()335     public void testIsPropertyAvailable() {
336         assertThat(mManager.isPropertyAvailable(FAKE_PROPERTY_ID, VehicleArea.GLOBAL)).isFalse();
337         assertThat(mManager.isPropertyAvailable(CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL))
338                 .isTrue();
339     }
340 
341     /**
342      * Test {@link CarPropertyManager#getWritePermission(int)}
343      * and {@link CarPropertyManager#getWritePermission(int)}
344      */
345     @Test
testGetPermission()346     public void testGetPermission() {
347         String hvacReadPermission = mManager.getReadPermission(
348                 VehiclePropertyIds.HVAC_TEMPERATURE_SET);
349         assertThat(hvacReadPermission).isEqualTo(Car.PERMISSION_CONTROL_CAR_CLIMATE);
350         String hvacWritePermission = mManager.getWritePermission(
351                 VehiclePropertyIds.HVAC_TEMPERATURE_SET);
352         assertThat(hvacWritePermission).isEqualTo(Car.PERMISSION_CONTROL_CAR_CLIMATE);
353 
354         // For read-only property
355         String vinReadPermission = mManager.getReadPermission(VehiclePropertyIds.INFO_VIN);
356         assertThat(vinReadPermission).isEqualTo(Car.PERMISSION_IDENTIFICATION);
357         String vinWritePermission = mManager.getWritePermission(VehiclePropertyIds.INFO_VIN);
358         assertThat(vinWritePermission).isNull();
359     }
360 
361     @Test
testGetPropertyConfig()362     public void testGetPropertyConfig() {
363         CarPropertyConfig config = mManager.getCarPropertyConfig(CUSTOM_SEAT_MIXED_PROP_ID_1);
364         assertThat(config.getPropertyId()).isEqualTo(CUSTOM_SEAT_MIXED_PROP_ID_1);
365         // return null if can not find the propertyConfig for the property.
366         assertThat(mManager.getCarPropertyConfig(FAKE_PROPERTY_ID)).isNull();
367     }
368 
369     @Test
testGetAreaId()370     public void testGetAreaId() {
371         int result = mManager.getAreaId(CUSTOM_SEAT_MIXED_PROP_ID_1, VehicleAreaSeat.ROW_1_LEFT);
372         assertThat(result).isEqualTo(DRIVER_SIDE_AREA_ID);
373         //test for the GLOBAL property
374         int globalAreaId =
375                 mManager.getAreaId(CUSTOM_GLOBAL_MIXED_PROP_ID_2, VehicleAreaSeat.ROW_1_LEFT);
376         assertThat(globalAreaId).isEqualTo(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
377         //test exception
378         assertThrows(IllegalArgumentException.class, () -> mManager.getAreaId(
379                 CUSTOM_SEAT_MIXED_PROP_ID_1, VehicleAreaSeat.ROW_3_CENTER));
380         assertThrows(IllegalArgumentException.class, () -> mManager.getAreaId(FAKE_PROPERTY_ID,
381                 VehicleAreaSeat.ROW_1_LEFT));
382     }
383 
384     @Test
testRegisterPropertyUnavailable()385     public void testRegisterPropertyUnavailable() throws Exception {
386         TestSequenceCallback callback = new TestSequenceCallback(1);
387         // Registering a property which has an unavailable initial value
388         // won't throw ServiceSpecificException.
389         mManager.registerCallback(callback, PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
390                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
391         // initial value is unavailable, should not get any callback.
392         assertThrows(IllegalStateException.class, callback::assertOnChangeEventCalled);
393     }
394 
395     @Test
testNotReceiveOnErrorEvent()396     public void testNotReceiveOnErrorEvent() throws Exception {
397         TestErrorCallback callback = new TestErrorCallback();
398         mManager.registerCallback(callback, VehiclePropertyIds.HVAC_TEMPERATURE_SET,
399                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
400         callback.assertRegisterCompleted();
401         injectErrorEvent(VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID,
402                 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN);
403         // app never change the value of HVAC_TEMPERATURE_SET, it won't get an error code.
404         callback.assertOnErrorEventNotCalled();
405     }
406 
407     @Test
testReceiveOnErrorEvent()408     public void testReceiveOnErrorEvent() throws Exception {
409         TestErrorCallback callback = new TestErrorCallback();
410         mManager.registerCallback(callback, VehiclePropertyIds.HVAC_TEMPERATURE_SET,
411                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
412         callback.assertRegisterCompleted();
413         mManager.setFloatProperty(
414                 VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID,
415                 CHANGED_TEMP_VALUE);
416         injectErrorEvent(VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID,
417                 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN);
418         callback.assertOnErrorEventCalled();
419         assertThat(callback.mReceivedErrorEventWithErrorCode).isTrue();
420         assertThat(callback.mErrorCode).isEqualTo(
421                 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN);
422         assertThat(callback.mReceivedErrorEventWithOutErrorCode).isFalse();
423     }
424 
425     @Test
testNotReceiveOnErrorEventAfterUnregister()426     public void testNotReceiveOnErrorEventAfterUnregister() throws Exception {
427         TestErrorCallback callback1 = new TestErrorCallback();
428         mManager.registerCallback(callback1, VehiclePropertyIds.HVAC_TEMPERATURE_SET,
429                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
430         callback1.assertRegisterCompleted();
431         TestErrorCallback callback2 = new TestErrorCallback();
432         mManager.registerCallback(callback2, VehiclePropertyIds.HVAC_TEMPERATURE_SET,
433                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
434         mManager.setFloatProperty(
435                 VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID,
436                 CHANGED_TEMP_VALUE);
437         mManager.unregisterCallback(callback1, VehiclePropertyIds.HVAC_TEMPERATURE_SET);
438         SystemClock.sleep(WAIT_FOR_NO_EVENTS);
439         injectErrorEvent(VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID,
440                 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN);
441         // callback1 is unregistered
442         callback1.assertOnErrorEventNotCalled();
443         callback2.assertOnErrorEventCalled();
444     }
445     @Test
testSetterExceptionsInQ()446     public void testSetterExceptionsInQ() {
447         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
448                 .isEqualTo(Build.VERSION_CODES.Q);
449 
450         assertThrows(IllegalStateException.class,
451                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
452                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
453         assertThrows(IllegalStateException.class,
454                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
455                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
456         assertThrows(IllegalStateException.class,
457                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
458                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
459         assertThrows(IllegalArgumentException.class,
460                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INVALID_ARG,
461                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
462         assertThrows(RuntimeException.class,
463                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
464                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
465     }
466 
467     @Test
testSetterExceptionsAfterQ()468     public void testSetterExceptionsAfterQ() {
469         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
470                 .isGreaterThan(Build.VERSION_CODES.Q);
471 
472         assertThrows(PropertyAccessDeniedSecurityException.class,
473                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
474                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
475         assertThrows(PropertyNotAvailableAndRetryException.class,
476                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_TRY_AGAIN,
477                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
478         assertThrows(PropertyNotAvailableException.class,
479                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
480                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
481         assertThrows(CarInternalErrorException.class,
482                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
483                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
484         assertThrows(IllegalArgumentException.class,
485                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INVALID_ARG,
486                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
487     }
488 
489     @Test
testGetterExceptionsInQ()490     public void testGetterExceptionsInQ() {
491         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
492                 .isEqualTo(Build.VERSION_CODES.Q);
493 
494         assertThrows(IllegalStateException.class,
495                 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
496                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
497         assertThrows(IllegalStateException.class,
498                 ()->mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
499                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
500 
501         assertThrows(IllegalArgumentException.class,
502                 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG,
503                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
504         assertThrows(IllegalArgumentException.class,
505                 ()->mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG,
506                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
507 
508         assertThrows(IllegalStateException.class,
509                 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
510                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
511         assertThrows(IllegalStateException.class,
512                 ()->mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
513                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
514 
515         assertThrows(IllegalStateException.class,
516                 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
517                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
518         assertThrows(IllegalStateException.class,
519                 ()->mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
520                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
521 
522         Truth.assertThat(mManager.getProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN,
523                 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)).isNull();
524     }
525 
526     @Test
testGetterExceptionsAfterQ()527     public void testGetterExceptionsAfterQ() {
528         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
529                 .isAtLeast(Build.VERSION_CODES.R);
530 
531         assertThrows(PropertyAccessDeniedSecurityException.class,
532                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
533                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
534         assertThrows(PropertyAccessDeniedSecurityException.class,
535                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
536                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
537 
538         assertThrows(IllegalArgumentException.class,
539                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG,
540                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
541         assertThrows(IllegalArgumentException.class,
542                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG,
543                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
544 
545         assertThrows(PropertyNotAvailableAndRetryException.class,
546                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN,
547                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
548         assertThrows(PropertyNotAvailableAndRetryException.class,
549                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN,
550                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
551 
552         assertThrows(PropertyNotAvailableException.class,
553                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
554                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
555         assertThrows(PropertyNotAvailableException.class,
556                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
557                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
558 
559         assertThrows(CarInternalErrorException.class,
560                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
561                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
562         assertThrows(CarInternalErrorException.class,
563                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
564                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
565     }
566 
567     @Test
testOnChangeEventWithSameAreaId()568     public void testOnChangeEventWithSameAreaId() throws Exception {
569         // init
570         mManager.setProperty(Integer.class,
571                 CUSTOM_SEAT_INT_PROP_1, DRIVER_SIDE_AREA_ID, 1);
572         TestSequenceCallback callback = new TestSequenceCallback(1);
573         mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_1, 0);
574         callback.assertRegisterCompleted();
575 
576         VehiclePropValue firstFakeValueDriveSide = new VehiclePropValue();
577         firstFakeValueDriveSide.prop = CUSTOM_SEAT_INT_PROP_1;
578         firstFakeValueDriveSide.areaId = DRIVER_SIDE_AREA_ID;
579         firstFakeValueDriveSide.value.int32Values.add(2);
580         firstFakeValueDriveSide.timestamp = SystemClock.elapsedRealtimeNanos();
581         VehiclePropValue secFakeValueDriveSide = new VehiclePropValue();
582         secFakeValueDriveSide.prop = CUSTOM_SEAT_INT_PROP_1;
583         secFakeValueDriveSide.areaId = DRIVER_SIDE_AREA_ID;
584         secFakeValueDriveSide.value.int32Values.add(3); // 0 in HAL indicate false;
585         secFakeValueDriveSide.timestamp = SystemClock.elapsedRealtimeNanos();
586         // inject the new event first
587         getMockedVehicleHal().injectEvent(secFakeValueDriveSide);
588         // inject the old event
589         getMockedVehicleHal().injectEvent(firstFakeValueDriveSide);
590         callback.assertOnChangeEventCalled();
591         // Client should only get the new event
592         assertThat((int) callback.getLastCarPropertyValue(CUSTOM_SEAT_INT_PROP_1).getValue())
593                 .isEqualTo(3);
594         assertThat(callback.getEventCounter()).isEqualTo(1);
595 
596     }
597 
598     @Test
testOnChangeEventWithDifferentAreaId()599     public void testOnChangeEventWithDifferentAreaId() throws Exception {
600         // init
601         mManager.setProperty(Integer.class,
602                 CUSTOM_SEAT_INT_PROP_2, DRIVER_SIDE_AREA_ID, 1);
603         TestSequenceCallback callback = new TestSequenceCallback(2);
604         mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_2, 0);
605         callback.assertRegisterCompleted();
606         VehiclePropValue fakeValueDriveSide = new VehiclePropValue();
607         fakeValueDriveSide.prop = CUSTOM_SEAT_INT_PROP_2;
608         fakeValueDriveSide.areaId = DRIVER_SIDE_AREA_ID;
609         fakeValueDriveSide.value.int32Values.add(4);
610         fakeValueDriveSide.timestamp = SystemClock.elapsedRealtimeNanos();
611 
612         VehiclePropValue fakeValuePsgSide = new VehiclePropValue();
613         fakeValuePsgSide.prop = CUSTOM_SEAT_INT_PROP_2;
614         fakeValuePsgSide.areaId = PASSENGER_SIDE_AREA_ID;
615         fakeValuePsgSide.value.int32Values.add(5);
616         fakeValuePsgSide.timestamp = SystemClock.elapsedRealtimeNanos();
617 
618         // inject passenger event before driver event
619         getMockedVehicleHal().injectEvent(fakeValuePsgSide);
620         getMockedVehicleHal().injectEvent(fakeValueDriveSide);
621         callback.assertOnChangeEventCalled();
622 
623         // both events should be received by listener
624         assertThat((int) callback.getLastCarPropertyValue(CUSTOM_SEAT_INT_PROP_2).getValue())
625                 .isEqualTo(4);
626         assertThat(callback.getEventCounter()).isEqualTo(2);
627     }
628 
629     @Test
testUserHal_getProperty()630     public void testUserHal_getProperty() {
631         userHalPropertiesTest("getProperty()", (prop) ->
632                 mManager.getProperty(prop, /* areaId= */ 0));
633     }
634 
635     @Test
testUserHal_getBooleanProperty()636     public void testUserHal_getBooleanProperty() {
637         userHalPropertiesTest("getBooleanProperty()", (prop) ->
638                 mManager.getBooleanProperty(prop, /* areaId= */ 0));
639     }
640 
641     @Test
testUserHal_getIntProperty()642     public void testUserHal_getIntProperty() {
643         userHalPropertiesTest("getIntProperty()", (prop) ->
644                 mManager.getIntProperty(prop, /* areaId= */ 0));
645     }
646 
647     @Test
testUserHal_getIntArrayProperty()648     public void testUserHal_getIntArrayProperty() {
649         userHalPropertiesTest("getIntArrayProperty()", (prop) ->
650                 mManager.getIntArrayProperty(prop, /* areaId= */ 0));
651     }
652 
653     @Test
testUserHal_getFloatProperty()654     public void testUserHal_getFloatProperty() {
655         userHalPropertiesTest("getFloatProperty()", (prop) ->
656                 mManager.getFloatProperty(prop, /* areaId= */ 0));
657     }
658 
659     @Test
testUserHal_getPropertyList()660     public void testUserHal_getPropertyList() {
661         userHalPropertiesTest("getPropertyList()", (prop) -> {
662             ArraySet<Integer> list = new ArraySet<>();
663             list.add(prop);
664             mManager.getPropertyList(list);
665         });
666     }
667 
668     @Test
testUserHal_getCarPropertyConfig()669     public void testUserHal_getCarPropertyConfig() {
670         userHalPropertiesTest("getCarPropertyConfig()", (prop) ->
671                 mManager.getCarPropertyConfig(prop));
672     }
673 
674     @Test
testUserHal_getAreaId()675     public void testUserHal_getAreaId() {
676         userHalPropertiesTest("getAreaId()", (prop) ->
677                 mManager.getAreaId(prop, /* areaId= */ 0));
678     }
679 
680     @Test
testUserHal_getReadPermission()681     public void testUserHal_getReadPermission() {
682         userHalPropertiesTest("getReadPermission()", (prop) ->
683                 mManager.getReadPermission(prop));
684     }
685 
686     @Test
testUserHal_getWritePermission()687     public void testUserHal_getWritePermission() {
688         userHalPropertiesTest("getWritePermission()", (prop) ->
689                 mManager.getWritePermission(prop));
690     }
691 
692     @Test
testUserHal_isPropertyAvailable()693     public void testUserHal_isPropertyAvailable() {
694         userHalPropertiesTest("isPropertyAvailable()", (prop) ->
695                 mManager.isPropertyAvailable(prop, /* area= */ 0));
696     }
697 
698     @Test
testUserHal_setProperty()699     public void testUserHal_setProperty() {
700         userHalPropertiesTest("setProperty()", (prop) ->
701                 mManager.setProperty(Object.class, prop, /* areaId= */ 0, /* val= */ null));
702     }
703 
704     @Test
testUserHal_setBooleanProperty()705     public void testUserHal_setBooleanProperty() {
706         userHalPropertiesTest("setBooleanProperty()", (prop) ->
707                 mManager.setBooleanProperty(prop, /* areaId= */ 0, /* val= */ true));
708     }
709 
710     @Test
testUserHal_setFloatProperty()711     public void testUserHal_setFloatProperty() {
712         userHalPropertiesTest("setFloatProperty()", (prop) ->
713                 mManager.setFloatProperty(prop, /* areaId= */ 0, /* val= */ 0.0F));
714     }
715 
716     @Test
testUserHal_setIntProperty()717     public void testUserHal_setIntProperty() {
718         userHalPropertiesTest("setIntProperty()", (prop) ->
719                 mManager.setIntProperty(prop, /* areaId= */ 0, /* val= */ 0));
720     }
721 
userHalPropertiesTest(String method, Visitor<Integer> visitor)722     private void userHalPropertiesTest(String method, Visitor<Integer> visitor) {
723         List<String> failedProperties = new ArrayList<String>();
724         for (int propertyId : USER_HAL_PROPERTIES) {
725             try {
726                 visitor.visit(propertyId);
727                 failedProperties.add(propToString(propertyId));
728             } catch (IllegalArgumentException e) {
729                 // expected
730             }
731         }
732         if (!failedProperties.isEmpty()) {
733             fail(method + " should not support these properties: " + failedProperties);
734         }
735     }
736 
737     @Override
configureMockedHal()738     protected synchronized void configureMockedHal() {
739         PropertyHandler handler = new PropertyHandler();
740         addProperty(CUSTOM_SEAT_MIXED_PROP_ID_1, handler).setConfigArray(CONFIG_ARRAY_1)
741                 .addAreaConfig(DRIVER_SIDE_AREA_ID).addAreaConfig(PASSENGER_SIDE_AREA_ID);
742         addProperty(CUSTOM_GLOBAL_MIXED_PROP_ID_2, handler).setConfigArray(CONFIG_ARRAY_2);
743         addProperty(CUSTOM_GLOBAL_MIXED_PROP_ID_3, handler).setConfigArray(CONFIG_ARRAY_3);
744         addProperty(CUSTOM_GLOBAL_INT_ARRAY_PROP, handler);
745 
746         addProperty(INT_ARRAY_PROP_STATUS_ERROR, handler);
747         addProperty(INT_PROP_STATUS_UNAVAILABLE, handler);
748         addProperty(FLOAT_PROP_STATUS_UNAVAILABLE, handler);
749         addProperty(BOOLEAN_PROP_STATUS_ERROR, handler);
750 
751         VehiclePropValue tempValue = new VehiclePropValue();
752         tempValue.value.floatValues.add(INIT_TEMP_VALUE);
753         tempValue.prop = VehiclePropertyIds.HVAC_TEMPERATURE_SET;
754         addProperty(VehiclePropertyIds.HVAC_TEMPERATURE_SET, tempValue)
755                 .addAreaConfig(DRIVER_SIDE_AREA_ID).addAreaConfig(PASSENGER_SIDE_AREA_ID);
756         addProperty(VehiclePropertyIds.INFO_VIN);
757 
758         addProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, handler);
759         addProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN, handler);
760         addProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, handler);
761         addProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG, handler);
762         addProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, handler);
763 
764         addProperty(CUSTOM_SEAT_INT_PROP_1, handler).addAreaConfig(DRIVER_SIDE_AREA_ID)
765                                                         .addAreaConfig(PASSENGER_SIDE_AREA_ID);
766         addProperty(CUSTOM_SEAT_INT_PROP_2, handler).addAreaConfig(DRIVER_SIDE_AREA_ID)
767                                                         .addAreaConfig(PASSENGER_SIDE_AREA_ID);
768     }
769 
770     private class PropertyHandler implements VehicleHalPropertyHandler {
771         HashMap<Integer, VehiclePropValue> mMap = new HashMap<>();
772         @Override
onPropertySet(VehiclePropValue value)773         public synchronized void onPropertySet(VehiclePropValue value) {
774             // Simulate HalClient.set() behavior.
775             int statusCode = mapPropertyToVhalStatusCode(value.prop);
776             if (statusCode == VehicleHalStatusCode.STATUS_INVALID_ARG) {
777                 throw new IllegalArgumentException();
778             }
779 
780             if (statusCode != VehicleHalStatusCode.STATUS_OK) {
781                 throw new ServiceSpecificException(statusCode);
782             }
783 
784             mMap.put(value.prop, value);
785         }
786 
787         @Override
onPropertyGet(VehiclePropValue value)788         public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
789             // Simulate HalClient.get() behavior.
790             int vhalStatusCode = mapPropertyToVhalStatusCode(value.prop);
791             if (vhalStatusCode == VehicleHalStatusCode.STATUS_INVALID_ARG) {
792                 throw new IllegalArgumentException();
793             }
794 
795             if (vhalStatusCode != VehicleHalStatusCode.STATUS_OK) {
796                 throw new ServiceSpecificException(vhalStatusCode);
797             }
798 
799             int propertyStatus = mapPropertyToCarPropertyStatusCode(value.prop);
800             VehiclePropValue currentValue = mMap.get(value.prop);
801             if (currentValue == null) {
802                 return value;
803             } else {
804                 currentValue.status = propertyStatus;
805             }
806             return currentValue;
807         }
808 
809         @Override
onPropertySubscribe(int property, float sampleRate)810         public synchronized void onPropertySubscribe(int property, float sampleRate) {
811             Log.d(TAG, "onPropertySubscribe property "
812                     + property + " sampleRate " + sampleRate);
813         }
814 
815         @Override
onPropertyUnsubscribe(int property)816         public synchronized void onPropertyUnsubscribe(int property) {
817             Log.d(TAG, "onPropertyUnSubscribe property " + property);
818         }
819     }
820 
propToString(int propertyId)821     private static String propToString(int propertyId) {
822         return VehiclePropertyIds.toString(propertyId) + " (" + propertyId + ")";
823     }
824 
mapPropertyToVhalStatusCode(int propId)825     private static int mapPropertyToVhalStatusCode(int propId) {
826         switch (propId) {
827             case PROP_CAUSE_STATUS_CODE_TRY_AGAIN:
828                 return VehicleHalStatusCode.STATUS_TRY_AGAIN;
829             case PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE:
830                 return VehicleHalStatusCode.STATUS_NOT_AVAILABLE;
831             case PROP_CAUSE_STATUS_CODE_ACCESS_DENIED:
832                 return VehicleHalStatusCode.STATUS_ACCESS_DENIED;
833             case PROP_CAUSE_STATUS_CODE_INVALID_ARG:
834                 return VehicleHalStatusCode.STATUS_INVALID_ARG;
835             case PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR:
836                 return VehicleHalStatusCode.STATUS_INTERNAL_ERROR;
837             default:
838                 return VehicleHalStatusCode.STATUS_OK;
839         }
840     }
841 
mapPropertyToCarPropertyStatusCode(int propId)842     private static int mapPropertyToCarPropertyStatusCode(int propId) {
843         switch (propId) {
844             case INT_ARRAY_PROP_STATUS_ERROR:
845             case BOOLEAN_PROP_STATUS_ERROR:
846                 return CarPropertyValue.STATUS_ERROR;
847             case INT_PROP_STATUS_UNAVAILABLE:
848             case FLOAT_PROP_STATUS_UNAVAILABLE:
849                 return CarPropertyValue.STATUS_UNAVAILABLE;
850             default:
851                 return CarPropertyValue.STATUS_AVAILABLE;
852         }
853     }
854 
855     private static class TestErrorCallback implements CarPropertyManager.CarPropertyEventCallback {
856 
857         private static final String CALLBACK_TAG = "ErrorEventTest";
858         private boolean mReceivedErrorEventWithErrorCode = false;
859         private boolean mReceivedErrorEventWithOutErrorCode = false;
860         private int mErrorCode;
861         private final CountDownLatch mEventsCountDownLatch = new CountDownLatch(1);
862         private final CountDownLatch mRegisterCountDownLatch = new CountDownLatch(2);
863         @Override
onChangeEvent(CarPropertyValue value)864         public void onChangeEvent(CarPropertyValue value) {
865             Log.d(CALLBACK_TAG, "onChangeEvent: " + value);
866             mRegisterCountDownLatch.countDown();
867         }
868 
869         @Override
onErrorEvent(int propId, int zone)870         public void onErrorEvent(int propId, int zone) {
871             mReceivedErrorEventWithOutErrorCode = true;
872             Log.d(CALLBACK_TAG, "onErrorEvent, propId: " + propId + " zone: " + zone);
873             mEventsCountDownLatch.countDown();
874         }
875 
876         @Override
onErrorEvent(int propId, int areaId, int errorCode)877         public void onErrorEvent(int propId, int areaId, int errorCode) {
878             mReceivedErrorEventWithErrorCode = true;
879             mErrorCode = errorCode;
880             Log.d(CALLBACK_TAG, "onErrorEvent, propId: " + propId + " areaId: " + areaId
881                     + "errorCode: " + errorCode);
882             mEventsCountDownLatch.countDown();
883         }
884 
assertOnErrorEventCalled()885         public void assertOnErrorEventCalled() throws InterruptedException {
886             if (!mEventsCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
887                 throw new IllegalStateException("Callback is not called in "
888                         + CALLBACK_SHORT_TIMEOUT_MS + " ms.");
889             }
890         }
891 
assertOnErrorEventNotCalled()892         public void assertOnErrorEventNotCalled() throws InterruptedException {
893             if (mEventsCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
894                 throw new IllegalStateException("Callback is called in " + CALLBACK_SHORT_TIMEOUT_MS
895                         + " ms.");
896             }
897         }
898 
assertRegisterCompleted()899         public void assertRegisterCompleted() throws InterruptedException {
900             if (!mRegisterCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
901                 throw new IllegalStateException("Register failed in " + CALLBACK_SHORT_TIMEOUT_MS
902                         + " ms.");
903             }
904         }
905     }
906 
907     private class TestSequenceCallback implements CarPropertyManager.CarPropertyEventCallback {
908 
909         private ConcurrentHashMap<Integer, CarPropertyValue> mRecorder = new ConcurrentHashMap<>();
910         private int mCounter = 0;
911         private final CountDownLatch mEventsCountDownLatch;
912         private final CountDownLatch mRegisterCountDownLatch = new CountDownLatch(2);
913         @Override
onChangeEvent(CarPropertyValue value)914         public void onChangeEvent(CarPropertyValue value) {
915             Log.e(TAG, "onChanged get a event " + value);
916             mRecorder.put(value.getPropertyId(), value);
917             mRegisterCountDownLatch.countDown();
918             // Skip initial events
919             if (value.getTimestamp() != 0) {
920                 mCounter++;
921                 mEventsCountDownLatch.countDown();
922             }
923         }
924 
TestSequenceCallback(int expectedTimes)925         TestSequenceCallback(int expectedTimes) {
926             mEventsCountDownLatch = new CountDownLatch(expectedTimes);
927         }
928 
929         @Override
onErrorEvent(int properId, int zone)930         public void onErrorEvent(int properId, int zone) {
931             Log.e(TAG, "TestSequenceCallback get an onErrorEvent");
932         }
933 
getLastCarPropertyValue(int propId)934         public CarPropertyValue getLastCarPropertyValue(int propId) {
935             return mRecorder.get(propId);
936         }
937 
getEventCounter()938         public int getEventCounter() {
939             return mCounter;
940         }
941 
assertOnChangeEventCalled()942         public void assertOnChangeEventCalled() throws InterruptedException {
943             if (!mEventsCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
944                 throw new IllegalStateException("Callback is not called in "
945                         + CALLBACK_SHORT_TIMEOUT_MS + " ms.");
946             }
947         }
948 
assertRegisterCompleted()949         public void assertRegisterCompleted() throws InterruptedException {
950             if (!mRegisterCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
951                 throw new IllegalStateException("Register failed in " + CALLBACK_SHORT_TIMEOUT_MS
952                         + " ms.");
953             }
954         }
955     }
956 
957 }
958