• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.common.truth.Truth.assertWithMessage;
21 
22 import static org.junit.Assert.fail;
23 import static org.testng.Assert.assertThrows;
24 
25 import android.car.Car;
26 import android.car.VehicleAreaType;
27 import android.car.VehicleAreaWheel;
28 import android.car.VehiclePropertyIds;
29 import android.car.hardware.CarPropertyConfig;
30 import android.car.hardware.CarPropertyValue;
31 import android.car.hardware.property.CarInternalErrorException;
32 import android.car.hardware.property.CarPropertyManager;
33 import android.car.hardware.property.PropertyAccessDeniedSecurityException;
34 import android.car.hardware.property.PropertyNotAvailableAndRetryException;
35 import android.car.hardware.property.PropertyNotAvailableException;
36 import android.car.hardware.property.VehicleHalStatusCode;
37 import android.car.test.util.Visitor;
38 import android.hardware.automotive.vehicle.RawPropValues;
39 import android.hardware.automotive.vehicle.VehicleArea;
40 import android.hardware.automotive.vehicle.VehicleAreaSeat;
41 import android.hardware.automotive.vehicle.VehiclePropValue;
42 import android.hardware.automotive.vehicle.VehiclePropertyGroup;
43 import android.hardware.automotive.vehicle.VehiclePropertyType;
44 import android.hardware.automotive.vehicle.VehicleVendorPermission;
45 import android.os.Build;
46 import android.os.ServiceSpecificException;
47 import android.os.SystemClock;
48 import android.util.ArraySet;
49 import android.util.Log;
50 
51 import androidx.test.ext.junit.runners.AndroidJUnit4;
52 import androidx.test.filters.MediumTest;
53 
54 import com.android.car.hal.test.AidlMockedVehicleHal.VehicleHalPropertyHandler;
55 
56 import com.google.common.truth.Truth;
57 
58 import org.junit.Assert;
59 import org.junit.Rule;
60 import org.junit.Test;
61 import org.junit.rules.TestName;
62 import org.junit.runner.RunWith;
63 
64 import java.time.Duration;
65 import java.util.ArrayList;
66 import java.util.Arrays;
67 import java.util.Collections;
68 import java.util.HashMap;
69 import java.util.List;
70 import java.util.concurrent.ConcurrentHashMap;
71 import java.util.concurrent.CountDownLatch;
72 import java.util.concurrent.TimeUnit;
73 
74 /**
75  * Test for {@link android.car.hardware.property.CarPropertyManager}
76  */
77 @RunWith(AndroidJUnit4.class)
78 @MediumTest
79 public class CarPropertyManagerTest extends MockedCarTestBase {
80 
81     private static final String TAG = CarPropertyManagerTest.class.getSimpleName();
82 
83     /**
84      * configArray[0], 1 indicates the property has a String value
85      * configArray[1], 1 indicates the property has a Boolean value .
86      * configArray[2], 1 indicates the property has a Integer value
87      * configArray[3], the number indicates the size of Integer[]  in the property.
88      * configArray[4], 1 indicates the property has a Long value .
89      * configArray[5], the number indicates the size of Long[]  in the property.
90      * configArray[6], 1 indicates the property has a Float value .
91      * configArray[7], the number indicates the size of Float[] in the property.
92      * configArray[8], the number indicates the size of byte[] in the property.
93      */
94     private static final java.util.Collection<Integer> CONFIG_ARRAY_1 =
95             Arrays.asList(1, 0, 1, 0, 1, 0, 0, 0, 0);
96     private static final java.util.Collection<Integer> CONFIG_ARRAY_2 =
97             Arrays.asList(1, 1, 1, 0, 0, 0, 0, 2, 0);
98     private static final java.util.Collection<Integer> CONFIG_ARRAY_3 =
99             Arrays.asList(0, 1, 1, 0, 0, 0, 1, 0, 0);
100     private static final Object[] EXPECTED_VALUE_1 = {"android", 1, 1L};
101     private static final Object[] EXPECTED_VALUE_2 = {"android", true, 3, 1.1f, 2f};
102     private static final Object[] EXPECTED_VALUE_3 = {true, 1, 2.2f};
103 
104     private static final int CUSTOM_SEAT_INT_PROP_1 =
105             0x1201 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.SEAT;
106     private static final int CUSTOM_SEAT_INT_PROP_2 =
107             0x1202 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.SEAT;
108 
109     private static final int CUSTOM_SEAT_MIXED_PROP_ID_1 =
110             0x1101 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.MIXED | VehicleArea.SEAT;
111     private static final int CUSTOM_GLOBAL_MIXED_PROP_ID_2 =
112             0x1102 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.MIXED | VehicleArea.GLOBAL;
113     private static final int CUSTOM_GLOBAL_MIXED_PROP_ID_3 =
114             0x1110 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.MIXED | VehicleArea.GLOBAL;
115 
116     private static final int CUSTOM_GLOBAL_INT_ARRAY_PROP =
117             0x1103 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32_VEC
118                     | VehicleArea.GLOBAL;
119     private static final Integer[] FAKE_INT_ARRAY_VALUE = {1, 2};
120 
121     private static final int INT_ARRAY_PROP_STATUS_ERROR =
122             0x1104 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32_VEC
123                     | VehicleArea.GLOBAL;
124 
125     private static final int BOOLEAN_PROP_STATUS_ERROR =
126             0x1105 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.BOOLEAN
127                     | VehicleArea.GLOBAL;
128     private static final boolean FAKE_BOOLEAN_PROPERTY_VALUE = true;
129     private static final int FLOAT_PROP_STATUS_UNAVAILABLE =
130             0x1106 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.FLOAT
131                     | VehicleArea.GLOBAL;
132     private static final float FAKE_FLOAT_PROPERTY_VALUE = 3f;
133     private static final int INT_PROP_STATUS_UNAVAILABLE =
134             0x1107 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32
135                     | VehicleArea.GLOBAL;
136     private static final int FAKE_INT_PROPERTY_VALUE = 3;
137     // A property that always returns null to simulate an unavailable property.
138     private static final int NULL_VALUE_PROP =
139             0x1108 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32
140                     | VehicleArea.GLOBAL;
141 
142     // Vendor properties for testing exceptions.
143     private static final int PROP_CAUSE_STATUS_CODE_TRY_AGAIN =
144             0x1201 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
145     private static final int PROP_CAUSE_STATUS_CODE_INVALID_ARG =
146             0x1202 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
147     private static final int PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE =
148             0x1203 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
149     private static final int PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR =
150             0x1204 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
151     private static final int PROP_CAUSE_STATUS_CODE_ACCESS_DENIED =
152             0x1205 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
153 
154     // Vendor properties for testing permissions
155     private static final int PROP_WITH_READ_ONLY_PERMISSION =
156             0x1301 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
157     private static final int PROP_WITH_WRITE_ONLY_PERMISSION =
158             0x1302 | VehiclePropertyGroup.VENDOR | VehiclePropertyType.INT32 | VehicleArea.GLOBAL;
159     private static final int SUPPORT_CUSTOM_PERMISSION = 287313669;
160     private static final java.util.Collection<Integer> VENDOR_PERMISSION_CONFIG =
161             Collections.unmodifiableList(
162                     Arrays.asList(PROP_WITH_READ_ONLY_PERMISSION,
163                     VehicleVendorPermission.PERMISSION_GET_VENDOR_CATEGORY_1,
164                     VehicleVendorPermission.PERMISSION_NOT_ACCESSIBLE,
165                     PROP_WITH_WRITE_ONLY_PERMISSION,
166                     VehicleVendorPermission.PERMISSION_NOT_ACCESSIBLE,
167                     VehicleVendorPermission.PERMISSION_SET_VENDOR_CATEGORY_1));
168 
169 
170     // Use FAKE_PROPERTY_ID to test api return null or throw exception.
171     private static final int FAKE_PROPERTY_ID = 0x111;
172 
173     private static final int DRIVER_SIDE_AREA_ID = VehicleAreaSeat.ROW_1_LEFT
174                                                     | VehicleAreaSeat.ROW_2_LEFT;
175     private static final int PASSENGER_SIDE_AREA_ID = VehicleAreaSeat.ROW_1_RIGHT
176                                                     | VehicleAreaSeat.ROW_2_CENTER
177                                                     | VehicleAreaSeat.ROW_2_RIGHT;
178     private static final float INIT_TEMP_VALUE = 16f;
179     private static final float CHANGED_TEMP_VALUE = 20f;
180     private static final int CALLBACK_SHORT_TIMEOUT_MS = 350; // ms
181     // Wait for CarPropertyManager register/unregister listener
182     private static final long WAIT_FOR_NO_EVENTS = 50;
183 
184     private static final List<Integer> USER_HAL_PROPERTIES = Arrays.asList(
185             VehiclePropertyIds.INITIAL_USER_INFO,
186             VehiclePropertyIds.SWITCH_USER,
187             VehiclePropertyIds.CREATE_USER,
188             VehiclePropertyIds.REMOVE_USER,
189             VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION
190             );
191 
192     private CarPropertyManager mManager;
193 
194     @Rule public TestName mTestName = new TestName();
195 
196     @Override
setUp()197     public void setUp() throws Exception {
198         super.setUp();
199         setUpTargetSdk();
200         mManager = (CarPropertyManager) getCar().getCarManager(Car.PROPERTY_SERVICE);
201         assertThat(mManager).isNotNull();
202     }
203 
setUpTargetSdk()204     private void setUpTargetSdk() {
205         if (mTestName.getMethodName().endsWith("InQ")) {
206             getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.Q;
207         } else if (mTestName.getMethodName().endsWith("AfterQ")) {
208             getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.R;
209         } else if (mTestName.getMethodName().endsWith("AfterR")) {
210             getContext().getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.S;
211         }
212     }
213 
214     @Test
testMixedPropertyConfigs()215     public void testMixedPropertyConfigs() {
216         List<CarPropertyConfig> configs = mManager.getPropertyList();
217         for (CarPropertyConfig cfg : configs) {
218             switch (cfg.getPropertyId()) {
219                 case CUSTOM_SEAT_MIXED_PROP_ID_1:
220                     assertThat(cfg.getConfigArray()).containsExactlyElementsIn(CONFIG_ARRAY_1)
221                             .inOrder();
222                     break;
223                 case CUSTOM_GLOBAL_MIXED_PROP_ID_2:
224                     assertThat(cfg.getConfigArray()).containsExactlyElementsIn(CONFIG_ARRAY_2)
225                             .inOrder();
226                     break;
227                 case CUSTOM_GLOBAL_MIXED_PROP_ID_3:
228                     assertThat(cfg.getConfigArray()).containsExactlyElementsIn(CONFIG_ARRAY_3)
229                             .inOrder();
230                     break;
231                 case VehiclePropertyIds.HVAC_TEMPERATURE_SET:
232                 case PROP_CAUSE_STATUS_CODE_ACCESS_DENIED:
233                 case PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR:
234                 case PROP_CAUSE_STATUS_CODE_TRY_AGAIN:
235                 case PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE:
236                 case PROP_CAUSE_STATUS_CODE_INVALID_ARG:
237                 case CUSTOM_SEAT_INT_PROP_1:
238                 case CUSTOM_SEAT_INT_PROP_2:
239                 case CUSTOM_GLOBAL_INT_ARRAY_PROP:
240                 case INT_ARRAY_PROP_STATUS_ERROR:
241                 case BOOLEAN_PROP_STATUS_ERROR:
242                 case INT_PROP_STATUS_UNAVAILABLE:
243                 case FLOAT_PROP_STATUS_UNAVAILABLE:
244                 case VehiclePropertyIds.INFO_VIN:
245                 case NULL_VALUE_PROP:
246                 case SUPPORT_CUSTOM_PERMISSION:
247                 case PROP_WITH_READ_ONLY_PERMISSION:
248                 case PROP_WITH_WRITE_ONLY_PERMISSION:
249                 case VehiclePropertyIds.TIRE_PRESSURE:
250                     break;
251                 default:
252                     Assert.fail("Unexpected CarPropertyConfig: " + cfg.toString());
253             }
254         }
255     }
256 
257     @Test
testGetMixTypeProperty()258     public void testGetMixTypeProperty() {
259         mManager.setProperty(Object[].class, CUSTOM_SEAT_MIXED_PROP_ID_1,
260                 0, EXPECTED_VALUE_1);
261         CarPropertyValue<Object[]> result = mManager.getProperty(
262                 CUSTOM_SEAT_MIXED_PROP_ID_1, 0);
263         assertThat(result.getValue()).isEqualTo(EXPECTED_VALUE_1);
264 
265         mManager.setProperty(Object[].class, CUSTOM_GLOBAL_MIXED_PROP_ID_2,
266                 0, EXPECTED_VALUE_2);
267         result = mManager.getProperty(
268                 CUSTOM_GLOBAL_MIXED_PROP_ID_2, 0);
269         assertThat(result.getValue()).isEqualTo(EXPECTED_VALUE_2);
270 
271         mManager.setProperty(Object[].class, CUSTOM_GLOBAL_MIXED_PROP_ID_3,
272                 0, EXPECTED_VALUE_3);
273         result = mManager.getProperty(
274                 CUSTOM_GLOBAL_MIXED_PROP_ID_3, 0);
275         assertThat(result.getValue()).isEqualTo(EXPECTED_VALUE_3);
276     }
277 
278     /**
279      * Test {@link android.car.hardware.property.CarPropertyManager#getIntArrayProperty(int, int)}
280      */
281     @Test
testGetIntArrayProperty()282     public void testGetIntArrayProperty() {
283         mManager.setProperty(Integer[].class, CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL,
284                 FAKE_INT_ARRAY_VALUE);
285 
286         int[] result = mManager.getIntArrayProperty(CUSTOM_GLOBAL_INT_ARRAY_PROP,
287                 VehicleArea.GLOBAL);
288         assertThat(result).asList().containsExactlyElementsIn(FAKE_INT_ARRAY_VALUE);
289     }
290 
291     /**
292      * Test {@link CarPropertyManager#getIntArrayProperty(int, int)} when vhal returns a value with
293      * error status.
294      */
295     @Test
testGetIntArrayPropertyWithErrorStatusAfterR()296     public void testGetIntArrayPropertyWithErrorStatusAfterR() {
297         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
298                 .isGreaterThan(Build.VERSION_CODES.R);
299         mManager.setProperty(Integer[].class, INT_ARRAY_PROP_STATUS_ERROR,
300                 VehicleArea.GLOBAL, FAKE_INT_ARRAY_VALUE);
301         assertThrows(CarInternalErrorException.class,
302                 () -> mManager.getIntArrayProperty(INT_ARRAY_PROP_STATUS_ERROR,
303                         VehicleArea.GLOBAL));
304     }
305 
306     /**
307      * Test {@link CarPropertyManager#getIntProperty(int, int)} when vhal returns a value with
308      * unavailable status.
309      */
310     @Test
testGetIntPropertyWithUnavailableStatusAfterR()311     public void testGetIntPropertyWithUnavailableStatusAfterR() {
312         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
313                 .isGreaterThan(Build.VERSION_CODES.R);
314         mManager.setProperty(Integer.class, INT_PROP_STATUS_UNAVAILABLE,
315                 VehicleArea.GLOBAL, FAKE_INT_PROPERTY_VALUE);
316         assertThrows(PropertyNotAvailableException.class,
317                 () -> mManager.getIntProperty(INT_PROP_STATUS_UNAVAILABLE, VehicleArea.GLOBAL));
318 
319     }
320 
321     /**
322      * Test {@link CarPropertyManager#getBooleanProperty(int, int)} when vhal returns a value with
323      * error status.
324      */
325     @Test
testGetBooleanPropertyWithErrorStatusAfterR()326     public void testGetBooleanPropertyWithErrorStatusAfterR() {
327         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
328                 .isGreaterThan(Build.VERSION_CODES.R);
329         mManager.setProperty(Boolean.class, BOOLEAN_PROP_STATUS_ERROR,
330                 VehicleArea.GLOBAL, FAKE_BOOLEAN_PROPERTY_VALUE);
331         assertThrows(CarInternalErrorException.class,
332                 () -> mManager.getBooleanProperty(BOOLEAN_PROP_STATUS_ERROR, VehicleArea.GLOBAL));
333     }
334 
335     /**
336      * Test {@link CarPropertyManager#getFloatProperty(int, int)} when vhal returns a value with
337      * unavailable status.
338      */
339     @Test
testGetFloatPropertyWithUnavailableStatusAfterR()340     public void testGetFloatPropertyWithUnavailableStatusAfterR() {
341         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
342                 .isGreaterThan(Build.VERSION_CODES.R);
343         mManager.setProperty(Float.class, FLOAT_PROP_STATUS_UNAVAILABLE,
344                 VehicleArea.GLOBAL, FAKE_FLOAT_PROPERTY_VALUE);
345         assertThrows(PropertyNotAvailableException.class,
346                 () -> mManager.getFloatProperty(FLOAT_PROP_STATUS_UNAVAILABLE, VehicleArea.GLOBAL));
347     }
348 
349     /**
350      * Test {@link CarPropertyManager#getProperty(Class, int, int)}
351      */
352     @Test
testGetPropertyWithClass()353     public void testGetPropertyWithClass() {
354         mManager.setProperty(Integer[].class, CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL,
355                 FAKE_INT_ARRAY_VALUE);
356 
357         CarPropertyValue<Integer[]> result = mManager.getProperty(Integer[].class,
358                 CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL);
359         assertThat(result.getValue()).asList().containsExactlyElementsIn(FAKE_INT_ARRAY_VALUE);
360     }
361 
362     /**
363      * Test {@link CarPropertyManager#isPropertyAvailable(int, int)}
364      */
365     @Test
testIsPropertyAvailable()366     public void testIsPropertyAvailable() {
367         assertThat(mManager.isPropertyAvailable(FAKE_PROPERTY_ID, VehicleArea.GLOBAL)).isFalse();
368         assertThat(mManager.isPropertyAvailable(CUSTOM_GLOBAL_INT_ARRAY_PROP, VehicleArea.GLOBAL))
369                 .isTrue();
370     }
371 
372     /**
373      * Test {@link CarPropertyManager#getWritePermission(int)}
374      * and {@link CarPropertyManager#getWritePermission(int)}
375      */
376     @Test
testGetPermission()377     public void testGetPermission() {
378         String hvacReadPermission = mManager.getReadPermission(
379                 VehiclePropertyIds.HVAC_TEMPERATURE_SET);
380         assertThat(hvacReadPermission).isEqualTo(Car.PERMISSION_CONTROL_CAR_CLIMATE);
381         String hvacWritePermission = mManager.getWritePermission(
382                 VehiclePropertyIds.HVAC_TEMPERATURE_SET);
383         assertThat(hvacWritePermission).isEqualTo(Car.PERMISSION_CONTROL_CAR_CLIMATE);
384 
385         // For read-only property
386         String vinReadPermission = mManager.getReadPermission(VehiclePropertyIds.INFO_VIN);
387         assertThat(vinReadPermission).isEqualTo(Car.PERMISSION_IDENTIFICATION);
388         String vinWritePermission = mManager.getWritePermission(VehiclePropertyIds.INFO_VIN);
389         assertThat(vinWritePermission).isNull();
390     }
391 
392     @Test
testGetPropertyConfig()393     public void testGetPropertyConfig() {
394         CarPropertyConfig config = mManager.getCarPropertyConfig(CUSTOM_SEAT_MIXED_PROP_ID_1);
395         assertThat(config.getPropertyId()).isEqualTo(CUSTOM_SEAT_MIXED_PROP_ID_1);
396         // return null if can not find the propertyConfig for the property.
397         assertThat(mManager.getCarPropertyConfig(FAKE_PROPERTY_ID)).isNull();
398     }
399 
400     @Test
testGetPropertyConfig_withReadOnlyPermission()401     public void testGetPropertyConfig_withReadOnlyPermission() {
402         CarPropertyConfig configForReadOnlyProperty = mManager
403                 .getCarPropertyConfig(PROP_WITH_READ_ONLY_PERMISSION);
404 
405         assertThat(configForReadOnlyProperty).isNotNull();
406         assertThat(configForReadOnlyProperty.getPropertyId())
407                 .isEqualTo(PROP_WITH_READ_ONLY_PERMISSION);
408     }
409 
410     @Test
testGetPropertyConfig_withWriteOnlyPermission()411     public void testGetPropertyConfig_withWriteOnlyPermission() {
412         CarPropertyConfig configForWriteOnlyProperty = mManager
413                 .getCarPropertyConfig(PROP_WITH_WRITE_ONLY_PERMISSION);
414 
415         assertThat(configForWriteOnlyProperty).isNotNull();
416         assertThat(configForWriteOnlyProperty.getPropertyId())
417                 .isEqualTo(PROP_WITH_WRITE_ONLY_PERMISSION);
418     }
419 
420     @Test
testGetAreaId()421     public void testGetAreaId() {
422         int result = mManager.getAreaId(CUSTOM_SEAT_MIXED_PROP_ID_1, VehicleAreaSeat.ROW_1_LEFT);
423         assertThat(result).isEqualTo(DRIVER_SIDE_AREA_ID);
424         //test for the GLOBAL property
425         int globalAreaId =
426                 mManager.getAreaId(CUSTOM_GLOBAL_MIXED_PROP_ID_2, VehicleAreaSeat.ROW_1_LEFT);
427         assertThat(globalAreaId).isEqualTo(VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);
428         //test exception
429         assertThrows(IllegalArgumentException.class, () -> mManager.getAreaId(
430                 CUSTOM_SEAT_MIXED_PROP_ID_1, VehicleAreaSeat.ROW_3_CENTER));
431         assertThrows(IllegalArgumentException.class, () -> mManager.getAreaId(FAKE_PROPERTY_ID,
432                 VehicleAreaSeat.ROW_1_LEFT));
433     }
434 
435     @Test
testRegisterPropertyUnavailable()436     public void testRegisterPropertyUnavailable() throws Exception {
437         TestSequenceCallback callback = new TestSequenceCallback(1);
438         // Registering a property which has an unavailable initial value
439         // won't throw ServiceSpecificException.
440         mManager.registerCallback(callback, PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
441                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
442         // initial value is unavailable, should not get any callback.
443         assertThrows(IllegalStateException.class, callback::assertOnChangeEventCalled);
444     }
445 
446     @Test
testNotReceiveOnErrorEvent()447     public void testNotReceiveOnErrorEvent() throws Exception {
448         TestErrorCallback callback = new TestErrorCallback();
449         mManager.registerCallback(callback, VehiclePropertyIds.HVAC_TEMPERATURE_SET,
450                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
451         callback.assertRegisterCompleted();
452         injectErrorEvent(VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID,
453                 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN);
454         // app never change the value of HVAC_TEMPERATURE_SET, it won't get an error code.
455         callback.assertOnErrorEventNotCalled();
456     }
457 
458     @Test
testReceiveOnErrorEvent()459     public void testReceiveOnErrorEvent() throws Exception {
460         TestErrorCallback callback = new TestErrorCallback();
461         mManager.registerCallback(callback, VehiclePropertyIds.HVAC_TEMPERATURE_SET,
462                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
463         callback.assertRegisterCompleted();
464         mManager.setFloatProperty(
465                 VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID,
466                 CHANGED_TEMP_VALUE);
467         injectErrorEvent(VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID,
468                 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN);
469         callback.assertOnErrorEventCalled();
470         assertThat(callback.mReceivedErrorEventWithErrorCode).isTrue();
471         assertThat(callback.mErrorCode).isEqualTo(
472                 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN);
473         assertThat(callback.mReceivedErrorEventWithOutErrorCode).isFalse();
474     }
475 
476     @Test
testNotReceiveOnErrorEventAfterUnregister()477     public void testNotReceiveOnErrorEventAfterUnregister() throws Exception {
478         TestErrorCallback callback1 = new TestErrorCallback();
479         mManager.registerCallback(callback1, VehiclePropertyIds.HVAC_TEMPERATURE_SET,
480                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
481         callback1.assertRegisterCompleted();
482         TestErrorCallback callback2 = new TestErrorCallback();
483         mManager.registerCallback(callback2, VehiclePropertyIds.HVAC_TEMPERATURE_SET,
484                 CarPropertyManager.SENSOR_RATE_ONCHANGE);
485         mManager.setFloatProperty(
486                 VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID,
487                 CHANGED_TEMP_VALUE);
488         mManager.unregisterCallback(callback1, VehiclePropertyIds.HVAC_TEMPERATURE_SET);
489         SystemClock.sleep(WAIT_FOR_NO_EVENTS);
490         injectErrorEvent(VehiclePropertyIds.HVAC_TEMPERATURE_SET, PASSENGER_SIDE_AREA_ID,
491                 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN);
492         // callback1 is unregistered
493         callback1.assertOnErrorEventNotCalled();
494         callback2.assertOnErrorEventCalled();
495     }
496     @Test
testSetterExceptionsInQ()497     public void testSetterExceptionsInQ() {
498         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
499                 .isEqualTo(Build.VERSION_CODES.Q);
500 
501         assertThrows(IllegalStateException.class,
502                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
503                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
504         assertThrows(IllegalStateException.class,
505                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
506                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
507         assertThrows(IllegalStateException.class,
508                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
509                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
510         assertThrows(IllegalArgumentException.class,
511                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INVALID_ARG,
512                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
513         assertThrows(RuntimeException.class,
514                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
515                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
516     }
517 
518     @Test
testSetterExceptionsAfterQ()519     public void testSetterExceptionsAfterQ() {
520         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
521                 .isGreaterThan(Build.VERSION_CODES.Q);
522 
523         assertThrows(PropertyAccessDeniedSecurityException.class,
524                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
525                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
526         assertThrows(PropertyNotAvailableAndRetryException.class,
527                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_TRY_AGAIN,
528                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
529         assertThrows(PropertyNotAvailableException.class,
530                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
531                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
532         assertThrows(CarInternalErrorException.class,
533                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
534                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
535         assertThrows(IllegalArgumentException.class,
536                 ()->mManager.setProperty(Integer.class, PROP_CAUSE_STATUS_CODE_INVALID_ARG,
537                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, 1));
538     }
539 
540     @Test
testGetterExceptionsInQ()541     public void testGetterExceptionsInQ() {
542         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
543                 .isEqualTo(Build.VERSION_CODES.Q);
544 
545         assertThrows(IllegalStateException.class,
546                 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
547                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
548         assertThrows(IllegalStateException.class,
549                 ()->mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
550                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
551 
552         assertThrows(IllegalArgumentException.class,
553                 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG,
554                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
555         assertThrows(IllegalArgumentException.class,
556                 ()->mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG,
557                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
558 
559         assertThrows(IllegalStateException.class,
560                 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
561                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
562         assertThrows(IllegalStateException.class,
563                 ()->mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
564                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
565 
566         assertThrows(IllegalStateException.class,
567                 ()->mManager.getProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
568                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
569         assertThrows(IllegalStateException.class,
570                 ()->mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
571                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
572 
573         assertThrows(IllegalStateException.class,
574                 ()->mManager.getProperty(NULL_VALUE_PROP,
575                     VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
576 
577         Truth.assertThat(mManager.getProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN,
578                 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL)).isNull();
579 
580     }
581 
582     @Test
testGetterExceptionsAfterQ()583     public void testGetterExceptionsAfterQ() {
584         Truth.assertThat(getContext().getApplicationInfo().targetSdkVersion)
585                 .isAtLeast(Build.VERSION_CODES.R);
586 
587         assertThrows(PropertyAccessDeniedSecurityException.class,
588                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
589                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
590         assertThrows(PropertyAccessDeniedSecurityException.class,
591                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED,
592                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
593         assertThrows(IllegalArgumentException.class,
594                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG,
595                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
596         assertThrows(IllegalArgumentException.class,
597                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG,
598                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
599 
600         assertThrows(PropertyNotAvailableAndRetryException.class,
601                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN,
602                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
603         assertThrows(PropertyNotAvailableAndRetryException.class,
604                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN,
605                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
606 
607         assertThrows(PropertyNotAvailableException.class,
608                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
609                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
610         assertThrows(PropertyNotAvailableException.class,
611                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE,
612                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
613 
614         assertThrows(CarInternalErrorException.class,
615                 () -> mManager.getProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
616                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
617         assertThrows(CarInternalErrorException.class,
618                 () -> mManager.getIntProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR,
619                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
620 
621         assertThrows(PropertyNotAvailableException.class,
622                 ()->mManager.getProperty(NULL_VALUE_PROP,
623                     VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL));
624     }
625 
626     @Test
testOnChangeEventWithSameAreaId()627     public void testOnChangeEventWithSameAreaId() throws Exception {
628         // init
629         mManager.setProperty(Integer.class,
630                 CUSTOM_SEAT_INT_PROP_1, DRIVER_SIDE_AREA_ID, 1);
631         TestSequenceCallback callback = new TestSequenceCallback(1);
632         mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_1, 0);
633         callback.assertRegisterCompleted();
634 
635         VehiclePropValue firstFakeValueDriveSide = new VehiclePropValue();
636         firstFakeValueDriveSide.prop = CUSTOM_SEAT_INT_PROP_1;
637         firstFakeValueDriveSide.areaId = DRIVER_SIDE_AREA_ID;
638         firstFakeValueDriveSide.value = new RawPropValues();
639         firstFakeValueDriveSide.value.int32Values = new int[]{2};
640         firstFakeValueDriveSide.timestamp = SystemClock.elapsedRealtimeNanos();
641         VehiclePropValue secFakeValueDriveSide = new VehiclePropValue();
642         secFakeValueDriveSide.prop = CUSTOM_SEAT_INT_PROP_1;
643         secFakeValueDriveSide.areaId = DRIVER_SIDE_AREA_ID;
644         secFakeValueDriveSide.value = new RawPropValues();
645         secFakeValueDriveSide.value.int32Values = new int[]{3}; // 0 in HAL indicate false;
646         secFakeValueDriveSide.timestamp = SystemClock.elapsedRealtimeNanos();
647         // inject the new event first
648         getAidlMockedVehicleHal().injectEvent(secFakeValueDriveSide);
649         // inject the old event
650         getAidlMockedVehicleHal().injectEvent(firstFakeValueDriveSide);
651         callback.assertOnChangeEventCalled();
652         // Client should only get the new event
653         assertThat((int) callback.getLastCarPropertyValue(CUSTOM_SEAT_INT_PROP_1).getValue())
654                 .isEqualTo(3);
655         assertThat(callback.getEventCounter()).isEqualTo(1);
656 
657     }
658 
659     @Test
testOnChangeEventWithDifferentAreaId()660     public void testOnChangeEventWithDifferentAreaId() throws Exception {
661         // init
662         mManager.setProperty(Integer.class,
663                 CUSTOM_SEAT_INT_PROP_2, DRIVER_SIDE_AREA_ID, 1);
664         TestSequenceCallback callback = new TestSequenceCallback(2);
665         mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_2, 0);
666         callback.assertRegisterCompleted();
667         VehiclePropValue fakeValueDriveSide = new VehiclePropValue();
668         fakeValueDriveSide.prop = CUSTOM_SEAT_INT_PROP_2;
669         fakeValueDriveSide.areaId = DRIVER_SIDE_AREA_ID;
670         fakeValueDriveSide.value = new RawPropValues();
671         fakeValueDriveSide.value.int32Values = new int[]{4};
672         fakeValueDriveSide.timestamp = SystemClock.elapsedRealtimeNanos();
673 
674         VehiclePropValue fakeValuePsgSide = new VehiclePropValue();
675         fakeValuePsgSide.prop = CUSTOM_SEAT_INT_PROP_2;
676         fakeValuePsgSide.areaId = PASSENGER_SIDE_AREA_ID;
677         fakeValuePsgSide.value = new RawPropValues();
678         fakeValuePsgSide.value.int32Values = new int[]{5};
679         fakeValuePsgSide.timestamp = SystemClock.elapsedRealtimeNanos();
680 
681         // inject passenger event before driver event
682         getAidlMockedVehicleHal().injectEvent(fakeValuePsgSide);
683         getAidlMockedVehicleHal().injectEvent(fakeValueDriveSide);
684         callback.assertOnChangeEventCalled();
685 
686         // both events should be received by listener
687         assertThat((int) callback.getLastCarPropertyValue(CUSTOM_SEAT_INT_PROP_2).getValue())
688                 .isEqualTo(4);
689         assertThat(callback.getEventCounter()).isEqualTo(2);
690     }
691 
692     @Test
testOnChangeEventInvalidPayload()693     public void testOnChangeEventInvalidPayload() throws Exception {
694         // init
695         mManager.setProperty(Integer.class, CUSTOM_SEAT_INT_PROP_1, DRIVER_SIDE_AREA_ID, 1);
696         TestSequenceCallback callback = new TestSequenceCallback(0);
697         mManager.registerCallback(callback, CUSTOM_SEAT_INT_PROP_1, 0);
698         callback.assertRegisterCompleted();
699 
700         List<VehiclePropValue> props = new ArrayList<VehiclePropValue>();
701         VehiclePropValue emptyProp = new VehiclePropValue();
702         emptyProp.prop = CUSTOM_SEAT_INT_PROP_1;
703         props.add(emptyProp);
704 
705         VehiclePropValue twoIntsProp = new VehiclePropValue();
706         twoIntsProp.prop = CUSTOM_SEAT_INT_PROP_1;
707         twoIntsProp.value = new RawPropValues();
708         twoIntsProp.value.int32Values = new int[]{0, 1};
709         props.add(twoIntsProp);
710 
711         VehiclePropValue propWithFloat = new VehiclePropValue();
712         propWithFloat.prop = CUSTOM_SEAT_INT_PROP_1;
713         propWithFloat.value = new RawPropValues();
714         propWithFloat.value.floatValues = new float[]{0f};
715         props.add(propWithFloat);
716 
717         VehiclePropValue propWithString = new VehiclePropValue();
718         propWithString.prop = CUSTOM_SEAT_INT_PROP_1;
719         propWithString.value = new RawPropValues();
720         propWithString.value.stringValue = "1234";
721         props.add(propWithString);
722 
723         for (VehiclePropValue prop: props) {
724             // inject passenger event before driver event
725             getAidlMockedVehicleHal().injectEvent(prop);
726             assertThat(callback.getEventCounter()).isEqualTo(0);
727         }
728     }
729 
730     @Test
testUserHal_getProperty()731     public void testUserHal_getProperty() {
732         userHalPropertiesTest("getProperty()", (prop) ->
733                 mManager.getProperty(prop, /* areaId= */ 0));
734     }
735 
736     @Test
testUserHal_getBooleanProperty()737     public void testUserHal_getBooleanProperty() {
738         userHalPropertiesTest("getBooleanProperty()", (prop) ->
739                 mManager.getBooleanProperty(prop, /* areaId= */ 0));
740     }
741 
742     @Test
testUserHal_getIntProperty()743     public void testUserHal_getIntProperty() {
744         userHalPropertiesTest("getIntProperty()", (prop) ->
745                 mManager.getIntProperty(prop, /* areaId= */ 0));
746     }
747 
748     @Test
testUserHal_getIntArrayProperty()749     public void testUserHal_getIntArrayProperty() {
750         userHalPropertiesTest("getIntArrayProperty()", (prop) ->
751                 mManager.getIntArrayProperty(prop, /* areaId= */ 0));
752     }
753 
754     @Test
testUserHal_getFloatProperty()755     public void testUserHal_getFloatProperty() {
756         userHalPropertiesTest("getFloatProperty()", (prop) ->
757                 mManager.getFloatProperty(prop, /* areaId= */ 0));
758     }
759 
760     @Test
testUserHal_getPropertyList()761     public void testUserHal_getPropertyList() {
762         userHalPropertiesTest("getPropertyList()", (prop) -> {
763             ArraySet<Integer> list = new ArraySet<>();
764             list.add(prop);
765             mManager.getPropertyList(list);
766         });
767     }
768 
769     @Test
testUserHal_getCarPropertyConfig()770     public void testUserHal_getCarPropertyConfig() {
771         userHalPropertiesTest("getCarPropertyConfig()", (prop) ->
772                 mManager.getCarPropertyConfig(prop));
773     }
774 
775     @Test
testUserHal_getAreaId()776     public void testUserHal_getAreaId() {
777         userHalPropertiesTest("getAreaId()", (prop) ->
778                 mManager.getAreaId(prop, /* areaId= */ 0));
779     }
780 
781     @Test
testUserHal_getReadPermission()782     public void testUserHal_getReadPermission() {
783         userHalPropertiesTest("getReadPermission()", (prop) ->
784                 mManager.getReadPermission(prop));
785     }
786 
787     @Test
testUserHal_getWritePermission()788     public void testUserHal_getWritePermission() {
789         userHalPropertiesTest("getWritePermission()", (prop) ->
790                 mManager.getWritePermission(prop));
791     }
792 
793     @Test
testUserHal_isPropertyAvailable()794     public void testUserHal_isPropertyAvailable() {
795         userHalPropertiesTest("isPropertyAvailable()", (prop) ->
796                 mManager.isPropertyAvailable(prop, /* area= */ 0));
797     }
798 
799     @Test
testUserHal_setProperty()800     public void testUserHal_setProperty() {
801         userHalPropertiesTest("setProperty()", (prop) ->
802                 mManager.setProperty(Object.class, prop, /* areaId= */ 0, /* val= */ null));
803     }
804 
805     @Test
testUserHal_setBooleanProperty()806     public void testUserHal_setBooleanProperty() {
807         userHalPropertiesTest("setBooleanProperty()", (prop) ->
808                 mManager.setBooleanProperty(prop, /* areaId= */ 0, /* val= */ true));
809     }
810 
811     @Test
testUserHal_setFloatProperty()812     public void testUserHal_setFloatProperty() {
813         userHalPropertiesTest("setFloatProperty()", (prop) ->
814                 mManager.setFloatProperty(prop, /* areaId= */ 0, /* val= */ 0.0F));
815     }
816 
817     @Test
testUserHal_setIntProperty()818     public void testUserHal_setIntProperty() {
819         userHalPropertiesTest("setIntProperty()", (prop) ->
820                 mManager.setIntProperty(prop, /* areaId= */ 0, /* val= */ 0));
821     }
822 
823     @Test
registerCallback_handlesContinuousPropertyUpdateRate()824     public void registerCallback_handlesContinuousPropertyUpdateRate() {
825         float wheelLeftFrontValue = 11.11f;
826         long wheelLeftFrontTimestampNanos = Duration.ofSeconds(1).toNanos();
827 
828         float notNewEnoughWheelLeftFrontValue = 22.22f;
829         long notNewEnoughWheelLeftFrontTimestampNanos = Duration.ofMillis(1999).toNanos();
830 
831         float newEnoughWheelLeftFrontValue = 33.33f;
832         long newEnoughWheelLeftFrontTimestampNanos = Duration.ofSeconds(2).toNanos();
833 
834         TestCallback testCallback = new TestCallback(2);
835         assertThat(mManager.registerCallback(testCallback, VehiclePropertyIds.TIRE_PRESSURE,
836                 1f)).isTrue();
837 
838         getAidlMockedVehicleHal().injectEvent(
839                 newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_LEFT_FRONT,
840                         wheelLeftFrontValue, wheelLeftFrontTimestampNanos));
841         getAidlMockedVehicleHal().injectEvent(
842                 newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_LEFT_FRONT,
843                         notNewEnoughWheelLeftFrontValue, notNewEnoughWheelLeftFrontTimestampNanos));
844         getAidlMockedVehicleHal().injectEvent(
845                 newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_LEFT_FRONT,
846                         newEnoughWheelLeftFrontValue, newEnoughWheelLeftFrontTimestampNanos));
847 
848         List<CarPropertyValue<?>> carPropertyValues = testCallback.getCarPropertyValues();
849         assertThat(carPropertyValues).hasSize(2);
850 
851         assertTirePressureCarPropertyValue(carPropertyValues.get(0),
852                 VehicleAreaWheel.WHEEL_LEFT_FRONT, wheelLeftFrontValue,
853                 wheelLeftFrontTimestampNanos);
854 
855         assertTirePressureCarPropertyValue(carPropertyValues.get(1),
856                 VehicleAreaWheel.WHEEL_LEFT_FRONT, newEnoughWheelLeftFrontValue,
857                 newEnoughWheelLeftFrontTimestampNanos);
858     }
859 
860     @Test
registerCallback_handlesOutOfTimeOrderEventsWithDifferentAreaIds()861     public void registerCallback_handlesOutOfTimeOrderEventsWithDifferentAreaIds() {
862         float wheelLeftFrontValue = 11.11f;
863         long wheelLeftFrontTimestampNanos = Duration.ofSeconds(4).toNanos();
864 
865         float wheelRightFrontValue = 22.22f;
866         long wheelRightFrontTimestampNanos = Duration.ofSeconds(3).toNanos();
867 
868         float wheelLeftRearValue = 33.33f;
869         long wheelLeftRearTimestampNanos = Duration.ofSeconds(2).toNanos();
870 
871         float wheelRightRearValue = 44.44f;
872         long wheelRightRearTimestampNanos = Duration.ofSeconds(1).toNanos();
873 
874         TestCallback testCallback = new TestCallback(4);
875         assertThat(mManager.registerCallback(testCallback, VehiclePropertyIds.TIRE_PRESSURE,
876                 1f)).isTrue();
877 
878         // inject events in time order from newest to oldest
879         getAidlMockedVehicleHal().injectEvent(
880                 newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_LEFT_FRONT,
881                         wheelLeftFrontValue, wheelLeftFrontTimestampNanos));
882         getAidlMockedVehicleHal().injectEvent(
883                 newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_RIGHT_FRONT,
884                         wheelRightFrontValue, wheelRightFrontTimestampNanos));
885         getAidlMockedVehicleHal().injectEvent(
886                 newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_LEFT_REAR,
887                         wheelLeftRearValue, wheelLeftRearTimestampNanos));
888         getAidlMockedVehicleHal().injectEvent(
889                 newTirePressureVehiclePropValue(VehicleAreaWheel.WHEEL_RIGHT_REAR,
890                         wheelRightRearValue, wheelRightRearTimestampNanos));
891 
892         List<CarPropertyValue<?>> carPropertyValues = testCallback.getCarPropertyValues();
893         assertThat(carPropertyValues).hasSize(4);
894 
895         assertTirePressureCarPropertyValue(carPropertyValues.get(0),
896                 VehicleAreaWheel.WHEEL_LEFT_FRONT, wheelLeftFrontValue,
897                 wheelLeftFrontTimestampNanos);
898 
899         assertTirePressureCarPropertyValue(carPropertyValues.get(1),
900                 VehicleAreaWheel.WHEEL_RIGHT_FRONT, wheelRightFrontValue,
901                 wheelRightFrontTimestampNanos);
902 
903         assertTirePressureCarPropertyValue(carPropertyValues.get(2),
904                 VehicleAreaWheel.WHEEL_LEFT_REAR, wheelLeftRearValue, wheelLeftRearTimestampNanos);
905 
906         assertTirePressureCarPropertyValue(carPropertyValues.get(3),
907                 VehicleAreaWheel.WHEEL_RIGHT_REAR, wheelRightRearValue,
908                 wheelRightRearTimestampNanos);
909     }
910 
userHalPropertiesTest(String method, Visitor<Integer> visitor)911     private void userHalPropertiesTest(String method, Visitor<Integer> visitor) {
912         List<String> failedProperties = new ArrayList<String>();
913         for (int propertyId : USER_HAL_PROPERTIES) {
914             try {
915                 visitor.visit(propertyId);
916                 failedProperties.add(propToString(propertyId));
917             } catch (IllegalArgumentException e) {
918                 // expected
919             }
920         }
921         if (!failedProperties.isEmpty()) {
922             fail(method + " should not support these properties: " + failedProperties);
923         }
924     }
925 
926     @Override
configureMockedHal()927     protected void configureMockedHal() {
928         PropertyHandler handler = new PropertyHandler();
929         addAidlProperty(CUSTOM_SEAT_MIXED_PROP_ID_1, handler).setConfigArray(CONFIG_ARRAY_1)
930                 .addAreaConfig(DRIVER_SIDE_AREA_ID).addAreaConfig(PASSENGER_SIDE_AREA_ID);
931         addAidlProperty(CUSTOM_GLOBAL_MIXED_PROP_ID_2, handler).setConfigArray(CONFIG_ARRAY_2);
932         addAidlProperty(CUSTOM_GLOBAL_MIXED_PROP_ID_3, handler).setConfigArray(CONFIG_ARRAY_3);
933         addAidlProperty(CUSTOM_GLOBAL_INT_ARRAY_PROP, handler);
934 
935         addAidlProperty(INT_ARRAY_PROP_STATUS_ERROR, handler);
936         addAidlProperty(INT_PROP_STATUS_UNAVAILABLE, handler);
937         addAidlProperty(FLOAT_PROP_STATUS_UNAVAILABLE, handler);
938         addAidlProperty(BOOLEAN_PROP_STATUS_ERROR, handler);
939         addAidlProperty(VehiclePropertyIds.TIRE_PRESSURE, handler).addAreaConfig(
940                 VehicleAreaWheel.WHEEL_LEFT_REAR).addAreaConfig(
941                 VehicleAreaWheel.WHEEL_RIGHT_REAR).addAreaConfig(
942                 VehicleAreaWheel.WHEEL_RIGHT_FRONT).addAreaConfig(
943                 VehicleAreaWheel.WHEEL_LEFT_FRONT).setChangeMode(
944                 CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS).setMaxSampleRate(
945                 10).setMinSampleRate(1);
946 
947         VehiclePropValue tempValue = new VehiclePropValue();
948         tempValue.value = new RawPropValues();
949         tempValue.value.floatValues = new float[]{INIT_TEMP_VALUE};
950         tempValue.prop = VehiclePropertyIds.HVAC_TEMPERATURE_SET;
951         addAidlProperty(VehiclePropertyIds.HVAC_TEMPERATURE_SET, tempValue)
952                 .addAreaConfig(DRIVER_SIDE_AREA_ID).addAreaConfig(PASSENGER_SIDE_AREA_ID);
953         addAidlProperty(VehiclePropertyIds.INFO_VIN);
954 
955         addAidlProperty(PROP_CAUSE_STATUS_CODE_ACCESS_DENIED, handler);
956         addAidlProperty(PROP_CAUSE_STATUS_CODE_TRY_AGAIN, handler);
957         addAidlProperty(PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR, handler);
958         addAidlProperty(PROP_CAUSE_STATUS_CODE_INVALID_ARG, handler);
959         addAidlProperty(PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE, handler);
960 
961         addAidlProperty(CUSTOM_SEAT_INT_PROP_1, handler).addAreaConfig(DRIVER_SIDE_AREA_ID)
962                                                         .addAreaConfig(PASSENGER_SIDE_AREA_ID);
963         addAidlProperty(CUSTOM_SEAT_INT_PROP_2, handler).addAreaConfig(DRIVER_SIDE_AREA_ID)
964                                                         .addAreaConfig(PASSENGER_SIDE_AREA_ID);
965 
966         addAidlProperty(NULL_VALUE_PROP, handler);
967 
968         // Add properties for permission testing.
969         addAidlProperty(SUPPORT_CUSTOM_PERMISSION, handler).setConfigArray(
970                 VENDOR_PERMISSION_CONFIG);
971         addAidlProperty(PROP_WITH_READ_ONLY_PERMISSION, handler);
972         addAidlProperty(PROP_WITH_WRITE_ONLY_PERMISSION, handler);
973     }
974 
975     private class PropertyHandler implements VehicleHalPropertyHandler {
976         HashMap<Integer, VehiclePropValue> mMap = new HashMap<>();
977         @Override
onPropertySet(VehiclePropValue value)978         public synchronized void onPropertySet(VehiclePropValue value) {
979             // Simulate HalClient.set() behavior.
980             int statusCode = mapPropertyToVhalStatusCode(value.prop);
981             if (statusCode != VehicleHalStatusCode.STATUS_OK) {
982                 // The ServiceSpecificException here would pass the statusCode back to caller.
983                 throw new ServiceSpecificException(statusCode);
984             }
985 
986             mMap.put(value.prop, value);
987         }
988 
989         @Override
onPropertyGet(VehiclePropValue value)990         public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
991             // Simulate HalClient.get() behavior.
992             int vhalStatusCode = mapPropertyToVhalStatusCode(value.prop);
993             if (vhalStatusCode != VehicleHalStatusCode.STATUS_OK) {
994                 // The ServiceSpecificException here would pass the statusCode back to caller.
995                 throw new ServiceSpecificException(vhalStatusCode);
996             }
997 
998             int propertyStatus = mapPropertyToCarPropertyStatusCode(value.prop);
999             if (value.prop == NULL_VALUE_PROP) {
1000                 // Return null to simulate an unavailable property.
1001                 // HAL implementation should return STATUS_TRY_AGAIN when a property is unavailable,
1002                 // however, it may also return null with STATUS_OKAY and we want to handle this
1003                 // properly.
1004                 return null;
1005             }
1006             VehiclePropValue currentValue = mMap.get(value.prop);
1007             if (currentValue == null) {
1008                 return value;
1009             } else {
1010                 currentValue.status = propertyStatus;
1011             }
1012             return currentValue;
1013         }
1014 
1015         @Override
onPropertySubscribe(int property, float sampleRate)1016         public synchronized void onPropertySubscribe(int property, float sampleRate) {
1017             Log.d(TAG, "onPropertySubscribe property "
1018                     + property + " sampleRate " + sampleRate);
1019         }
1020 
1021         @Override
onPropertyUnsubscribe(int property)1022         public synchronized void onPropertyUnsubscribe(int property) {
1023             Log.d(TAG, "onPropertyUnSubscribe property " + property);
1024         }
1025     }
1026 
propToString(int propertyId)1027     private static String propToString(int propertyId) {
1028         return VehiclePropertyIds.toString(propertyId) + " (" + propertyId + ")";
1029     }
1030 
mapPropertyToVhalStatusCode(int propId)1031     private static int mapPropertyToVhalStatusCode(int propId) {
1032         switch (propId) {
1033             case PROP_CAUSE_STATUS_CODE_TRY_AGAIN:
1034                 return VehicleHalStatusCode.STATUS_TRY_AGAIN;
1035             case PROP_CAUSE_STATUS_CODE_NOT_AVAILABLE:
1036                 return VehicleHalStatusCode.STATUS_NOT_AVAILABLE;
1037             case PROP_CAUSE_STATUS_CODE_ACCESS_DENIED:
1038                 return VehicleHalStatusCode.STATUS_ACCESS_DENIED;
1039             case PROP_CAUSE_STATUS_CODE_INVALID_ARG:
1040                 return VehicleHalStatusCode.STATUS_INVALID_ARG;
1041             case PROP_CAUSE_STATUS_CODE_INTERNAL_ERROR:
1042                 return VehicleHalStatusCode.STATUS_INTERNAL_ERROR;
1043             default:
1044                 return VehicleHalStatusCode.STATUS_OK;
1045         }
1046     }
1047 
mapPropertyToCarPropertyStatusCode(int propId)1048     private static int mapPropertyToCarPropertyStatusCode(int propId) {
1049         switch (propId) {
1050             case INT_ARRAY_PROP_STATUS_ERROR:
1051             case BOOLEAN_PROP_STATUS_ERROR:
1052                 return CarPropertyValue.STATUS_ERROR;
1053             case INT_PROP_STATUS_UNAVAILABLE:
1054             case FLOAT_PROP_STATUS_UNAVAILABLE:
1055                 return CarPropertyValue.STATUS_UNAVAILABLE;
1056             default:
1057                 return CarPropertyValue.STATUS_AVAILABLE;
1058         }
1059     }
1060 
newTirePressureVehiclePropValue(int areaId, float floatValue, long timestampNanos)1061     private static VehiclePropValue newTirePressureVehiclePropValue(int areaId, float floatValue,
1062             long timestampNanos) {
1063         VehiclePropValue vehiclePropValue = new VehiclePropValue();
1064         vehiclePropValue.prop = VehiclePropertyIds.TIRE_PRESSURE;
1065         vehiclePropValue.areaId = areaId;
1066         vehiclePropValue.value = new RawPropValues();
1067         vehiclePropValue.value.floatValues = new float[]{floatValue};
1068         vehiclePropValue.timestamp = timestampNanos;
1069         return vehiclePropValue;
1070     }
1071 
assertTirePressureCarPropertyValue(CarPropertyValue<?> carPropertyValue, int areaId, float floatValue, long timestampNanos)1072     private static void assertTirePressureCarPropertyValue(CarPropertyValue<?> carPropertyValue,
1073             int areaId, float floatValue, long timestampNanos) {
1074         assertThat(carPropertyValue.getPropertyId()).isEqualTo(VehiclePropertyIds.TIRE_PRESSURE);
1075         assertThat(carPropertyValue.getAreaId()).isEqualTo(areaId);
1076         assertThat(carPropertyValue.getStatus()).isEqualTo(CarPropertyValue.STATUS_AVAILABLE);
1077         assertThat(carPropertyValue.getTimestamp()).isEqualTo(timestampNanos);
1078         assertThat(carPropertyValue.getValue()).isEqualTo(floatValue);
1079     }
1080 
1081     private static class TestErrorCallback implements CarPropertyManager.CarPropertyEventCallback {
1082 
1083         private static final String CALLBACK_TAG = "ErrorEventTest";
1084         private boolean mReceivedErrorEventWithErrorCode = false;
1085         private boolean mReceivedErrorEventWithOutErrorCode = false;
1086         private int mErrorCode;
1087         private final CountDownLatch mEventsCountDownLatch = new CountDownLatch(1);
1088         private final CountDownLatch mRegisterCountDownLatch = new CountDownLatch(2);
1089         @Override
onChangeEvent(CarPropertyValue value)1090         public void onChangeEvent(CarPropertyValue value) {
1091             Log.d(CALLBACK_TAG, "onChangeEvent: " + value);
1092             mRegisterCountDownLatch.countDown();
1093         }
1094 
1095         @Override
onErrorEvent(int propId, int zone)1096         public void onErrorEvent(int propId, int zone) {
1097             mReceivedErrorEventWithOutErrorCode = true;
1098             Log.d(CALLBACK_TAG, "onErrorEvent, propId: " + propId + " zone: " + zone);
1099             mEventsCountDownLatch.countDown();
1100         }
1101 
1102         @Override
onErrorEvent(int propId, int areaId, int errorCode)1103         public void onErrorEvent(int propId, int areaId, int errorCode) {
1104             mReceivedErrorEventWithErrorCode = true;
1105             mErrorCode = errorCode;
1106             Log.d(CALLBACK_TAG, "onErrorEvent, propId: " + propId + " areaId: " + areaId
1107                     + "errorCode: " + errorCode);
1108             mEventsCountDownLatch.countDown();
1109         }
1110 
assertOnErrorEventCalled()1111         public void assertOnErrorEventCalled() throws InterruptedException {
1112             if (!mEventsCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
1113                 throw new IllegalStateException("Callback is not called in "
1114                         + CALLBACK_SHORT_TIMEOUT_MS + " ms.");
1115             }
1116         }
1117 
assertOnErrorEventNotCalled()1118         public void assertOnErrorEventNotCalled() throws InterruptedException {
1119             if (mEventsCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
1120                 throw new IllegalStateException("Callback is called in " + CALLBACK_SHORT_TIMEOUT_MS
1121                         + " ms.");
1122             }
1123         }
1124 
assertRegisterCompleted()1125         public void assertRegisterCompleted() throws InterruptedException {
1126             if (!mRegisterCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
1127                 throw new IllegalStateException("Register failed in " + CALLBACK_SHORT_TIMEOUT_MS
1128                         + " ms.");
1129             }
1130         }
1131     }
1132 
1133     private static class TestCallback implements CarPropertyManager.CarPropertyEventCallback {
1134         private final List<CarPropertyValue<?>> mCarPropertyValues = new ArrayList<>();
1135         private final int mNumberOfExpectedCarPropertyValues;
1136         private final CountDownLatch mCountDownLatch;
1137 
TestCallback(int numberOfExpectedCarPropertyValues)1138         TestCallback(int numberOfExpectedCarPropertyValues) {
1139             mNumberOfExpectedCarPropertyValues = numberOfExpectedCarPropertyValues;
1140             mCountDownLatch = new CountDownLatch(numberOfExpectedCarPropertyValues);
1141         }
1142 
1143         @Override
onChangeEvent(CarPropertyValue carPropertyValue)1144         public void onChangeEvent(CarPropertyValue carPropertyValue) {
1145             mCarPropertyValues.add(carPropertyValue);
1146             mCountDownLatch.countDown();
1147         }
1148 
1149         @Override
onErrorEvent(int propertyId, int areaId)1150         public void onErrorEvent(int propertyId, int areaId) {
1151             Log.e(TAG, "TestCallback onErrorEvent - property ID: " + propertyId + " areaId: "
1152                     + areaId);
1153         }
1154 
getCarPropertyValues()1155         public List<CarPropertyValue<?>> getCarPropertyValues() {
1156             try {
1157                 assertWithMessage("Expected " + mNumberOfExpectedCarPropertyValues
1158                         + " CarPropertyValues before timeout, but only received: "
1159                         + mCarPropertyValues.size()).that(
1160                         mCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS,
1161                                 TimeUnit.MILLISECONDS)).isTrue();
1162 
1163             } catch (InterruptedException e) {
1164                 fail("TestCallback was interrupted: " + e);
1165             }
1166             return mCarPropertyValues;
1167         }
1168     }
1169 
1170     private class TestSequenceCallback implements CarPropertyManager.CarPropertyEventCallback {
1171 
1172         private ConcurrentHashMap<Integer, CarPropertyValue> mRecorder = new ConcurrentHashMap<>();
1173         private int mCounter = 0;
1174         private final CountDownLatch mEventsCountDownLatch;
1175         private final CountDownLatch mRegisterCountDownLatch = new CountDownLatch(2);
1176         @Override
onChangeEvent(CarPropertyValue value)1177         public void onChangeEvent(CarPropertyValue value) {
1178             Log.e(TAG, "onChanged get a event " + value);
1179             mRecorder.put(value.getPropertyId(), value);
1180             mRegisterCountDownLatch.countDown();
1181             // Skip initial events
1182             if (value.getTimestamp() != 0) {
1183                 mCounter++;
1184                 mEventsCountDownLatch.countDown();
1185             }
1186         }
1187 
TestSequenceCallback(int expectedTimes)1188         TestSequenceCallback(int expectedTimes) {
1189             mEventsCountDownLatch = new CountDownLatch(expectedTimes);
1190         }
1191 
1192         @Override
onErrorEvent(int properId, int zone)1193         public void onErrorEvent(int properId, int zone) {
1194             Log.e(TAG, "TestSequenceCallback get an onErrorEvent");
1195         }
1196 
getLastCarPropertyValue(int propId)1197         public CarPropertyValue getLastCarPropertyValue(int propId) {
1198             return mRecorder.get(propId);
1199         }
1200 
getEventCounter()1201         public int getEventCounter() {
1202             return mCounter;
1203         }
1204 
assertOnChangeEventCalled()1205         public void assertOnChangeEventCalled() throws InterruptedException {
1206             if (!mEventsCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
1207                 throw new IllegalStateException("Callback is not called in "
1208                         + CALLBACK_SHORT_TIMEOUT_MS + " ms.");
1209             }
1210         }
1211 
assertRegisterCompleted()1212         public void assertRegisterCompleted() throws InterruptedException {
1213             if (!mRegisterCountDownLatch.await(CALLBACK_SHORT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
1214                 throw new IllegalStateException("Register failed in " + CALLBACK_SHORT_TIMEOUT_MS
1215                         + " ms.");
1216             }
1217         }
1218     }
1219 
1220 }
1221