1 /*
2  * Copyright (C) 2022 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.hal.fakevhal;
18 
19 import static org.junit.Assert.assertThrows;
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.ArgumentMatchers.eq;
22 import static org.mockito.Mockito.after;
23 import static org.mockito.Mockito.atLeastOnce;
24 import static org.mockito.Mockito.atMost;
25 import static org.mockito.Mockito.clearInvocations;
26 import static org.mockito.Mockito.doNothing;
27 import static org.mockito.Mockito.doThrow;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.reset;
30 import static org.mockito.Mockito.timeout;
31 import static org.mockito.Mockito.times;
32 import static org.mockito.Mockito.verify;
33 import static org.mockito.Mockito.when;
34 
35 import android.car.hardware.property.CarPropertyManager;
36 import android.hardware.automotive.vehicle.FuelType;
37 import android.hardware.automotive.vehicle.RawPropValues;
38 import android.hardware.automotive.vehicle.StatusCode;
39 import android.hardware.automotive.vehicle.SubscribeOptions;
40 import android.hardware.automotive.vehicle.VehicleAreaConfig;
41 import android.hardware.automotive.vehicle.VehicleAreaDoor;
42 import android.hardware.automotive.vehicle.VehicleAreaSeat;
43 import android.hardware.automotive.vehicle.VehicleAreaWindow;
44 import android.hardware.automotive.vehicle.VehicleOilLevel;
45 import android.hardware.automotive.vehicle.VehiclePropConfig;
46 import android.hardware.automotive.vehicle.VehiclePropValue;
47 import android.hardware.automotive.vehicle.VehicleProperty;
48 import android.hardware.automotive.vehicle.VehiclePropertyAccess;
49 import android.os.RemoteException;
50 import android.os.ServiceSpecificException;
51 import android.os.SystemClock;
52 import android.platform.test.ravenwood.RavenwoodRule;
53 import android.util.SparseArray;
54 
55 import androidx.test.filters.SmallTest;
56 
57 import com.android.car.IVehicleDeathRecipient;
58 import com.android.car.VehicleStub;
59 import com.android.car.VehicleStub.AsyncGetSetRequest;
60 import com.android.car.VehicleStub.GetVehicleStubAsyncResult;
61 import com.android.car.VehicleStub.SetVehicleStubAsyncResult;
62 import com.android.car.VehicleStub.SubscriptionClient;
63 import com.android.car.VehicleStub.VehicleStubCallbackInterface;
64 import com.android.car.hal.AidlHalPropConfig;
65 import com.android.car.hal.HalPropConfig;
66 import com.android.car.hal.HalPropValue;
67 import com.android.car.hal.HalPropValueBuilder;
68 import com.android.car.hal.VehicleHalCallback;
69 import com.android.car.internal.property.CarPropertyErrorCodes;
70 
71 import com.google.common.truth.Expect;
72 
73 import org.junit.Before;
74 import org.junit.Rule;
75 import org.junit.Test;
76 import org.junit.runner.RunWith;
77 import org.mockito.ArgumentCaptor;
78 import org.mockito.Captor;
79 import org.mockito.Mock;
80 import org.mockito.junit.MockitoJUnitRunner;
81 
82 import java.io.File;
83 import java.io.FileDescriptor;
84 import java.io.FileOutputStream;
85 import java.io.InputStream;
86 import java.util.ArrayList;
87 import java.util.List;
88 
89 @SmallTest
90 @RunWith(MockitoJUnitRunner.class)
91 public class FakeVehicleStubUnitTest {
92 
93     private static final int DOOR_1_LEFT = VehicleAreaDoor.ROW_1_LEFT;
94     private static final int WINDOW_1_LEFT = VehicleAreaWindow.ROW_1_LEFT;
95     private static final int SEAT_1_LEFT = VehicleAreaSeat.ROW_1_LEFT;
96     private static final int SEAT_1_RIGHT = VehicleAreaSeat.ROW_1_RIGHT;
97     private static final int HVAC_LEFT = SEAT_1_LEFT | VehicleAreaSeat.ROW_2_LEFT
98             | VehicleAreaSeat.ROW_2_CENTER;
99     private static final int HVAC_ALL = HVAC_LEFT | SEAT_1_RIGHT | VehicleAreaSeat.ROW_2_RIGHT;
100     private static final String PROPERTY_CONFIG_STRING_ON_CHANGE = "{\"property\":"
101             + "\"VehicleProperty::ENGINE_OIL_LEVEL\","
102             + "\"defaultValue\": {\"int32Values\": [\"VehicleOilLevel::NORMAL\"]},"
103             + "\"access\": \"VehiclePropertyAccess::READ_WRITE\","
104             + "\"changeMode\": \"VehiclePropertyChangeMode::ON_CHANGE\"}";
105     private static final String PROPERTY_CONFIG_STRING_CONTINUOUS = "{\"property\":"
106             + "\"VehicleProperty::FUEL_LEVEL\","
107             + "\"defaultValue\": {\"floatValues\": [100]},"
108             + "\"access\": \"VehiclePropertyAccess::READ\","
109             + "\"changeMode\": \"VehiclePropertyChangeMode::CONTINUOUS\","
110             + "\"maxSampleRate\": 100.0,"
111             + "\"minSampleRate\": 1.0}";
112     private static final String PROPERTY_CONFIG_STRING_CONTINUOUS_2 = "{\"property\":"
113             + "\"VehicleProperty::EV_BATTERY_LEVEL\","
114             + "\"defaultValue\": {\"floatValues\": [100]},"
115             + "\"access\": \"VehiclePropertyAccess::READ\","
116             + "\"changeMode\": \"VehiclePropertyChangeMode::CONTINUOUS\","
117             + "\"maxSampleRate\": 100.0,"
118             + "\"minSampleRate\": 1.0}";
119     private static final String PROPERTY_CONFIG_STRING_STATIC = "{\"property\":"
120             + "\"VehicleProperty::INFO_FUEL_TYPE\","
121             + "\"defaultValue\": {\"int32Values\": [\"FuelType::FUEL_TYPE_UNLEADED\"]},"
122             + "\"access\": \"VehiclePropertyAccess::READ\","
123             + "\"changeMode\": \"VehiclePropertyChangeMode::STATIC\"}";
124     private static final String PROPERTY_CONFIG_STRING_HVAC_POWER_ON = "{\"property\":"
125             + "\"VehicleProperty::HVAC_POWER_ON\","
126             + "\"defaultValue\": {\"int32Values\": [1]},"
127             + "\"areas\": [{\"areaId\": \"Constants::HVAC_ALL\"}],"
128             + "\"configArray\": [\"VehicleProperty::HVAC_FAN_SPEED\","
129             + "\"VehicleProperty::HVAC_FAN_DIRECTION\"]}";
130     private static final String PROPERTY_CONFIG_STRING_HVAC_POWER_OFF = "{\"property\":"
131             + "\"VehicleProperty::HVAC_POWER_ON\","
132             + "\"defaultValue\": {\"int32Values\": [0]},"
133             + "\"areas\": [{\"areaId\": \"Constants::HVAC_ALL\"}],"
134             + "\"configArray\": [\"VehicleProperty::HVAC_FAN_SPEED\","
135             + "\"VehicleProperty::HVAC_FAN_DIRECTION\"]}";
136     private static final String PROPERTY_CONFIG_STRING_HVAC_FAN_SPEED = "{\"property\":"
137             + "\"VehicleProperty::HVAC_FAN_SPEED\","
138             + "\"defaultValue\": {\"int32Values\": [3]},"
139             + "\"areas\": [{"
140             + "\"areaId\": \"Constants::HVAC_ALL\","
141             + "\"minInt32Value\": 1,"
142             + "\"maxInt32Value\": 7}]}";
143     private static final int DEFAULT_TIMEOUT = 1000;
144     @Captor
145     private ArgumentCaptor<List<GetVehicleStubAsyncResult>> mGetVehicleStubAsyncResultCaptor;
146     @Captor
147     private ArgumentCaptor<List<SetVehicleStubAsyncResult>> mSetVehicleStubAsyncResultCaptor;
148 
149     @Mock
150     private VehicleStub mMockRealVehicleStub;
151     @Mock
152     private FakeVhalConfigParser mParser;
153     @Mock
154     private VehicleStubCallbackInterface mCallback;
155 
156     @Rule
157     public final Expect expect = Expect.create();
158     @Rule
159     public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
160             .setProvideMainThread(true)
161             .build();
162 
163     @Before
setup()164     public void setup() throws Exception {
165         when(mMockRealVehicleStub.isValid()).thenReturn(true);
166         when(mMockRealVehicleStub.getAllPropConfigs()).thenReturn(new HalPropConfig[0]);
167     }
168 
169     @Test
testGetAllPropConfigsWithoutCustomConfig()170     public void testGetAllPropConfigsWithoutCustomConfig() throws Exception {
171         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
172                 new FakeVhalConfigParser(), new ArrayList<>());
173 
174         HalPropConfig[] allPropConfig = fakeVehicleStub.getAllPropConfigs();
175         HalPropConfig propConfigInfo = getPropConfigByPropId(allPropConfig,
176                 VehicleProperty.INFO_MAKE);
177         HalPropConfig propConfigWindow = getPropConfigByPropId(allPropConfig,
178                 VehicleProperty.WINDOW_POS);
179 
180         expect.that(fakeVehicleStub.isValid()).isTrue();
181         expect.that(propConfigInfo.getPropId()).isEqualTo(VehicleProperty.INFO_MAKE);
182         expect.that(propConfigWindow.getAreaConfigs()[0].getAreaId())
183                 .isEqualTo(VehicleAreaWindow.ROW_1_LEFT);
184         expect.that(propConfigWindow.getAreaConfigs()[0].getMaxInt32Value()).isEqualTo(10);
185     }
186 
187     @Test
testGetAllPropConfigsWithCustomConfigHasExistingPropId()188     public void testGetAllPropConfigsWithCustomConfigHasExistingPropId() throws Exception {
189         // Create a custom config file.
190         String jsonString = "{\"properties\": [{\"property\": \"VehicleProperty::TIRE_PRESSURE\","
191                 + "\"defaultValue\": {\"floatValues\": [200.0]}, \"maxSampleRate\": 2.5}]}";
192         List<File> customFileList = createFilenameList(jsonString);
193 
194         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
195                 new FakeVhalConfigParser(), customFileList);
196         HalPropConfig[] allPropConfig = fakeVehicleStub.getAllPropConfigs();
197         HalPropConfig propConfig = getPropConfigByPropId(allPropConfig,
198                 VehicleProperty.TIRE_PRESSURE);
199 
200         expect.that(propConfig.getPropId()).isEqualTo(VehicleProperty.TIRE_PRESSURE);
201         expect.that(propConfig.getMaxSampleRate()).isEqualTo(2.5f);
202     }
203 
204     @Test
testGetAllPropConfigsWithCustomConfigHasNonExistingPropId()205     public void testGetAllPropConfigsWithCustomConfigHasNonExistingPropId() throws Exception {
206         // Create a custom config file.
207         String jsonString = "{\"properties\": [{\"property\": 123,"
208                 + "\"defaultValue\": {\"floatValues\": [200.0]}, "
209                 + "\"access\": \"VehiclePropertyAccess::READ_WRITE\","
210                 + "\"changeMode\": \"VehiclePropertyChangeMode::STATIC\","
211                 + "\"maxSampleRate\": 5.0}]}";
212         List<File> customFileList = createFilenameList(jsonString);
213 
214         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
215                 new FakeVhalConfigParser(), customFileList);
216         HalPropConfig[] allPropConfig = fakeVehicleStub.getAllPropConfigs();
217         HalPropConfig propConfig = getPropConfigByPropId(allPropConfig, 123);
218 
219         expect.that(propConfig.getPropId()).isEqualTo(123);
220         expect.that(propConfig.getMaxSampleRate()).isEqualTo(5.0f);
221         expect.that(allPropConfig.length).isEqualTo(new FakeVehicleStub(mMockRealVehicleStub,
222                 new FakeVhalConfigParser(), new ArrayList<>()).getAllPropConfigs().length + 1);
223     }
224 
225     @Test
testGetAllPropConfigsWithSpecialProp()226     public void testGetAllPropConfigsWithSpecialProp() throws Exception {
227         // Access permission of VHAL_HEARTBEAT has been overridden by the mocked return result of
228         // AidlVehicleStub.getAllPropConfigs().
229         when(mMockRealVehicleStub.getAllPropConfigs()).thenReturn(new HalPropConfig[]{
230                 new AidlHalPropConfig(createConfig(VehicleProperty.VHAL_HEARTBEAT,
231                         /* sampleRate= */ 0, VehiclePropertyAccess.READ_WRITE, /* areaId= */ 0))});
232         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
233                 new FakeVhalConfigParser(), new ArrayList<>());
234 
235         HalPropConfig propConfig = getPropConfigByPropId(fakeVehicleStub.getAllPropConfigs(),
236                 VehicleProperty.VHAL_HEARTBEAT);
237 
238         verify(mMockRealVehicleStub, atLeastOnce()).getAllPropConfigs();
239         expect.that(propConfig.getAccess()).isEqualTo(VehiclePropertyAccess.READ_WRITE);
240     }
241 
242     @Test
testParseConfigFilesThrowException()243     public void testParseConfigFilesThrowException() throws Exception {
244         when(mParser.parseJsonConfig(any(InputStream.class))).thenThrow(
245                 new IllegalArgumentException("This file does not contain a valid JSONObject."));
246 
247         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
248                 () -> new FakeVehicleStub(mMockRealVehicleStub, mParser, new ArrayList<>()));
249 
250         expect.that(thrown).hasMessageThat().contains("This file does not contain a valid "
251                 + "JSONObject.");
252     }
253 
254     @Test
testNoCustomFile()255     public void testNoCustomFile() throws Exception {
256         SparseArray<ConfigDeclaration> defaultParseResult = createParseResult(
257                 /* propId= */ 123, /* maxSampleRate= */ 10.0f,
258                 /* access= */ VehiclePropertyAccess.NONE, /* areaId= */ 0);
259         when(mParser.parseJsonConfig(any(InputStream.class))).thenReturn(defaultParseResult);
260 
261         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub, mParser,
262                 new ArrayList<>());
263         HalPropConfig[] allPropConfig = fakeVehicleStub.getAllPropConfigs();
264 
265         expect.that(allPropConfig.length).isEqualTo(defaultParseResult.size());
266     }
267 
268     @Test
testCustomFileParseFailure()269     public void testCustomFileParseFailure() throws Exception {
270         // Parse this custom config file will throw an IllegalArgumentException with message
271         // "properties field value is not a valid JSONArray."
272         String jsonString = "{\"property\": 123,"
273                 + "\"defaultValue\": {\"floatValues\": [200.0]}, "
274                 + "\"access\": \"VehiclePropertyAccess::READ_WRITE\","
275                 + "\"changeMode\": \"VehiclePropertyChangeMode::STATIC\","
276                 + "\"maxSampleRate\": 5.0}";
277         List<File> customFileList = createFilenameList(jsonString);
278 
279         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
280                 new FakeVhalConfigParser(), customFileList);
281 
282         expect.that(fakeVehicleStub.isFakeModeEnabled()).isEqualTo(true);
283     }
284 
285     @Test
testCustomFilesOneParseSuccessOneParseFailure()286     public void testCustomFilesOneParseSuccessOneParseFailure() throws Exception {
287         String validJsonString = "{\"properties\":["
288                 + "{\"property\": \"VehicleProperty::INFO_FUEL_TYPE\","
289                 + "\"defaultValue\": {\"floatValues\": [200.0]}, "
290                 + "\"access\": \"VehiclePropertyAccess::READ_WRITE\","
291                 + "\"changeMode\": \"VehiclePropertyChangeMode::STATIC\","
292                 + "\"maxSampleRate\": 5.0}]}";
293         List<File> customFileList = createFilenameList(validJsonString);
294         // Parse this custom config file will throw an IllegalArgumentException with message
295         // "properties field value is not a valid JSONArray."
296         String invalidJsonString = "{\"property\": 123,"
297                 + "\"defaultValue\": {\"floatValues\": [200.0]}, "
298                 + "\"access\": \"VehiclePropertyAccess::READ_WRITE\","
299                 + "\"changeMode\": \"VehiclePropertyChangeMode::STATIC\","
300                 + "\"maxSampleRate\": 5.0}";
301         customFileList.addAll(createFilenameList(invalidJsonString));
302         // Create a request prop value.
303         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
304                 .build(/* prop= */ VehicleProperty.INFO_FUEL_TYPE, /* areaId= */ 0);
305 
306         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
307                 new FakeVhalConfigParser(), customFileList);
308 
309         expect.that(fakeVehicleStub.isFakeModeEnabled()).isEqualTo(true);
310         expect.that(fakeVehicleStub.get(requestPropValue).getPropId())
311                 .isEqualTo(VehicleProperty.INFO_FUEL_TYPE);
312         expect.that(fakeVehicleStub.get(requestPropValue).getFloatValue(0)).isEqualTo(200);
313     }
314 
315     @Test
testGetMethodPropIdNotSupported()316     public void testGetMethodPropIdNotSupported() throws Exception {
317         // Mock config files parsing results to be empty.
318         when(mParser.parseJsonConfig(any(InputStream.class))).thenReturn(new SparseArray<>());
319         // Create a request prop value.
320         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
321                 .build(/* prop= */ VehicleProperty.INFO_FUEL_TYPE, /* areaId= */ 0);
322         // Create a FakeVehicleStub instance.
323         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub, mParser,
324                 new ArrayList<>());
325 
326         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
327                 () -> fakeVehicleStub.get(requestPropValue));
328 
329 
330         expect.that(thrown.errorCode).isEqualTo(StatusCode.INVALID_ARG);
331         expect.that(thrown).hasMessageThat().contains("The propId: "
332                 + VehicleProperty.INFO_FUEL_TYPE + " is not supported.");
333     }
334 
335     @Test
testGetMethodNoReadPermission()336     public void testGetMethodNoReadPermission() throws Exception {
337         // Create a custom config file.
338         String jsonString = "{\"properties\": [{\"property\": "
339                 + "\"VehicleProperty::ANDROID_EPOCH_TIME\","
340                 + "\"access\": \"VehiclePropertyAccess::WRITE\"}]}";
341         List<File> customFileList = createFilenameList(jsonString);
342         // Create a request prop value.
343         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
344                 .build(/* prop= */ VehicleProperty.ANDROID_EPOCH_TIME, /* areaId= */ 0);
345         // Create a FakeVehicleStub instance.
346         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
347                 new FakeVhalConfigParser(), customFileList);
348 
349         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
350                 () -> fakeVehicleStub.get(requestPropValue));
351 
352         expect.that(thrown.errorCode).isEqualTo(StatusCode.ACCESS_DENIED);
353         expect.that(thrown).hasMessageThat().contains("doesn't have read permission");
354     }
355 
356     @Test
testGetMethodPropIdIsGlobalHasNoDefaultValueAreaIdIgnored()357     public void testGetMethodPropIdIsGlobalHasNoDefaultValueAreaIdIgnored() throws Exception {
358         // Create a custom config file.
359         String jsonString = "{\"properties\": [{\"property\": "
360                 + "\"VehicleProperty::INFO_FUEL_CAPACITY\"}]}";
361         List<File> customFileList = createFilenameList(jsonString);
362         // Create a request prop value.
363         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
364                 .build(/* prop= */ VehicleProperty.INFO_FUEL_CAPACITY, /* areaId= */ 123);
365         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
366                 new FakeVhalConfigParser(), customFileList);
367 
368         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
369                 () -> fakeVehicleStub.get(requestPropValue));
370 
371         // For global properties, if default value is not defined, will throw
372         // ServiceSpecificException with StatusCode.NOT_AVAILABLE.
373         expect.that(thrown.errorCode).isEqualTo(StatusCode.NOT_AVAILABLE);
374         expect.that(thrown).hasMessageThat().contains("propId: "
375                 + VehicleProperty.INFO_FUEL_CAPACITY + " has no property value.");
376     }
377 
378     @Test
testGetMethodGlobalPropWithAreaIdNotZero()379     public void testGetMethodGlobalPropWithAreaIdNotZero() throws Exception {
380         // Create a custom config file.
381         String jsonString = "{\"properties\": [{\"property\":"
382                 + "\"VehicleProperty::DISPLAY_BRIGHTNESS\","
383                 + "\"defaultValue\": {\"int32Values\": [100]},"
384                 + "\"areas\": [{\"areaId\": 1, \"minInt32Value\": 0, \"maxInt32Value\": 100}]}]}";
385         List<File> customFileList = createFilenameList(jsonString);
386         // Create a request prop value.
387         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
388                 .build(/* prop= */ VehicleProperty.DISPLAY_BRIGHTNESS, /* areaId= */ 1);
389 
390         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
391                 new FakeVhalConfigParser(), customFileList);
392         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
393                 () -> fakeVehicleStub.get(requestPropValue));
394 
395         // For global properties, if the supported area config array doesn't include 0,
396         // a ServiceSpecificException with StatusCode.INVALID_ARG.
397         expect.that(thrown.errorCode).isEqualTo(StatusCode.INVALID_ARG);
398         expect.that(thrown).hasMessageThat().contains("The areaId: 0 is not supported.");
399     }
400 
401     @Test
testGetMethodPropIdIsGlobalHasDefaultValueAreaIdIgnored()402     public void testGetMethodPropIdIsGlobalHasDefaultValueAreaIdIgnored() throws Exception {
403         // Create a custom config file.
404         String jsonString = "{\"properties\": [{\"property\": \"VehicleProperty::INFO_FUEL_TYPE\","
405                 + "\"defaultValue\": {\"int32Values\": [\"FuelType::FUEL_TYPE_UNLEADED\"]}}]}";
406         List<File> customFileList = createFilenameList(jsonString);
407         // Create a request prop value.
408         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
409                 .build(/* prop= */ VehicleProperty.INFO_FUEL_TYPE, /* areaId= */ 123);
410         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
411                 new FakeVhalConfigParser(), customFileList);
412 
413         HalPropValue propValue = fakeVehicleStub.get(requestPropValue);
414 
415         // For global properties, if default value exists, will return the default value.
416         expect.that(propValue.getAreaId()).isEqualTo(0);
417         expect.that(propValue.getInt32ValuesSize()).isEqualTo(1);
418         expect.that(propValue.getInt32Value(0)).isEqualTo(FuelType.FUEL_TYPE_UNLEADED);
419     }
420 
421     @Test
testGetMethodPropIdHasAreasHasNoDefaultValueHasNoAreaValue()422     public void testGetMethodPropIdHasAreasHasNoDefaultValueHasNoAreaValue() throws Exception {
423         // Create a custom config file.
424         String jsonString = "{\"properties\": [{\"property\": \"VehicleProperty::WINDOW_POS\","
425                 + "\"areas\": [{\"areaId\": \"Constants::WINDOW_1_LEFT\", \"minInt32Value\": 0,"
426                 + "\"maxInt32Value\": 10}]}]}";
427         List<File> customFileList = createFilenameList(jsonString);
428         // Create a request prop value.
429         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
430                 .build(/* prop= */ VehicleProperty.WINDOW_POS, /* areaId= */ WINDOW_1_LEFT);
431         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
432                 new FakeVhalConfigParser(), customFileList);
433 
434         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
435                 () -> fakeVehicleStub.get(requestPropValue));
436 
437         // For properties have areas, if neither default prop value nor area value exists, then
438         // throw a ServiceSpecificException of StatusCode.NOT_AVAILABLE.
439         expect.that(thrown.errorCode).isEqualTo(StatusCode.NOT_AVAILABLE);
440         expect.that(thrown).hasMessageThat().contains("propId: "
441                 + VehicleProperty.WINDOW_POS + ", areaId: " + WINDOW_1_LEFT + " has no property "
442                 + "value");
443     }
444 
445     @Test
testGetMethodPropIdHasAreasHasDefaultValueHasNoAreaValue()446     public void testGetMethodPropIdHasAreasHasDefaultValueHasNoAreaValue() throws Exception {
447         // Create a custom config file.
448         String jsonString = "{\"properties\": [{\"property\": \"VehicleProperty::WINDOW_POS\","
449                 + "\"defaultValue\": {\"int32Values\": [0]},\"areas\": [{\"areaId\":"
450                 + "\"Constants::WINDOW_1_LEFT\", \"minInt32Value\": 0, \"maxInt32Value\": 10}]}]}";
451         List<File> customFileList = createFilenameList(jsonString);
452         // Create a request prop value.
453         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
454                 .build(/* prop= */ VehicleProperty.WINDOW_POS, /* areaId= */ WINDOW_1_LEFT);
455         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
456                 new FakeVhalConfigParser(), customFileList);
457 
458         HalPropValue propValue = fakeVehicleStub.get(requestPropValue);
459 
460         // For properties have areas, expect that default prop value will override area value when
461         // default prop value is set but the area value is not defined.
462         expect.that(propValue.getAreaId()).isEqualTo(WINDOW_1_LEFT);
463         expect.that(propValue.getInt32ValuesSize()).isEqualTo(1);
464         expect.that(propValue.getInt32Value(0)).isEqualTo(0);
465     }
466 
467     @Test
testGetMethodPropIdHasAreasHasAreaValue()468     public void testGetMethodPropIdHasAreasHasAreaValue() throws Exception {
469         // Create a custom config file.
470         String jsonString = "{\"properties\": [{\"property\": \"VehicleProperty::DOOR_LOCK\","
471                 + "\"areas\": [{\"defaultValue\": {\"int32Values\": [1]}, \"areaId\":"
472                 + "\"Constants::DOOR_1_LEFT\"}]}]}";
473         List<File> customFileList = createFilenameList(jsonString);
474         // Create a request prop value.
475         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
476                 .build(VehicleProperty.DOOR_LOCK, DOOR_1_LEFT);
477         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
478                 new FakeVhalConfigParser(), customFileList);
479 
480         HalPropValue propValue = fakeVehicleStub.get(requestPropValue);
481 
482         // For properties have areas, get method returns the area value if it exists.
483         expect.that(propValue.getPropId()).isEqualTo(VehicleProperty.DOOR_LOCK);
484         expect.that(propValue.getInt32Value(0)).isEqualTo(1);
485     }
486 
487     @Test
testGetMethodPropIdHasAreasAreaIdNotSupport()488     public void testGetMethodPropIdHasAreasAreaIdNotSupport() throws Exception {
489         // Create a custom config file.
490         String jsonString = "{\"properties\": [{\"property\":"
491                 + "\"VehicleProperty::SEAT_BELT_BUCKLED\","
492                 + "\"defaultValue\": {\"int32Values\": [0]},"
493                 + "\"areas\": [{\"areaId\": \"Constants::SEAT_1_LEFT\"}]}]}";
494         List<File> customFileList = createFilenameList(jsonString);
495         // Create a request prop value.
496         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
497                 .build(/* prop= */ VehicleProperty.SEAT_BELT_BUCKLED, /* areaId= */ 0);
498         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
499                 new FakeVhalConfigParser(), customFileList);
500 
501         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
502                 () -> fakeVehicleStub.get(requestPropValue));
503 
504         expect.that(thrown.errorCode).isEqualTo(StatusCode.INVALID_ARG);
505         expect.that(thrown).hasMessageThat().contains("areaId: 0 is not supported");
506     }
507 
508     @Test
testGetMethodForHvacProp()509     public void testGetMethodForHvacProp() throws Exception {
510         // Create a custom config file.
511         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_HVAC_POWER_ON
512                 + ", {\"property\": \"VehicleProperty::HVAC_FAN_SPEED\","
513                 + "\"defaultValue\": {\"int32Values\": [3]},"
514                 + "\"areas\": [{"
515                 + "\"areaId\": \"Constants::HVAC_ALL\","
516                 + "\"minInt32Value\": 1,"
517                 + "\"maxInt32Value\": 7}]}]}";
518         List<File> customFileList = createFilenameList(jsonString);
519         // Create a request prop value.
520         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
521                 .build(/* prop= */ VehicleProperty.HVAC_FAN_SPEED, /* areaId= */ HVAC_ALL);
522         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
523                 new FakeVhalConfigParser(), customFileList);
524 
525         HalPropValue propValue = fakeVehicleStub.get(requestPropValue);
526 
527         expect.that(propValue.getPropId()).isEqualTo(VehicleProperty.HVAC_FAN_SPEED);
528         expect.that(propValue.getInt32Value(0)).isEqualTo(3);
529     }
530 
531     @Test
testGetMethodForHvacPropNotAvailable()532     public void testGetMethodForHvacPropNotAvailable() throws Exception {
533         // Create a custom config file.
534         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_HVAC_POWER_OFF + "]}";
535         List<File> customFileList = createFilenameList(jsonString);
536         // Create a request prop value.
537         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
538                 .build(/* prop= */ VehicleProperty.HVAC_FAN_SPEED, /* areaId= */ SEAT_1_LEFT);
539         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
540                 new FakeVhalConfigParser(), customFileList);
541 
542         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
543                 () -> fakeVehicleStub.get(requestPropValue));
544 
545         expect.that(thrown.errorCode).isEqualTo(StatusCode.NOT_AVAILABLE);
546         expect.that(thrown).hasMessageThat().contains("HVAC_POWER_ON is off. PropId: 356517120 "
547                 + "is not available.");
548     }
549 
550     @Test
testGetMethodForHvacPropMatchedBitOrAreaId()551     public void testGetMethodForHvacPropMatchedBitOrAreaId() throws Exception {
552         // Create a custom config file.
553         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_HVAC_POWER_ON
554                 + ", {\"property\": \"VehicleProperty::HVAC_FAN_SPEED\","
555                 + "\"defaultValue\": {\"int32Values\": [3]},"
556                 + "\"areas\": [{"
557                 + "\"areaId\": \"Constants::HVAC_LEFT\","
558                 + "\"minInt32Value\": 1,"
559                 + "\"maxInt32Value\": 7}]}]}";
560         List<File> customFileList = createFilenameList(jsonString);
561         // Create a request prop value.
562         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
563                 .build(/* prop= */ VehicleProperty.HVAC_FAN_SPEED, /* areaId= */ HVAC_LEFT);
564         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
565                 new FakeVhalConfigParser(), customFileList);
566 
567         HalPropValue propValue = fakeVehicleStub.get(requestPropValue);
568 
569         expect.that(propValue.getPropId()).isEqualTo(VehicleProperty.HVAC_FAN_SPEED);
570         expect.that(propValue.getInt32Value(0)).isEqualTo(3);
571     }
572 
573     @Test
testGetMethodForHvacPropNoMatchedAreaId()574     public void testGetMethodForHvacPropNoMatchedAreaId() throws Exception {
575         // Create a custom config file.
576         String jsonString = "{\"properties\": [{\"property\":"
577                 + "\"VehicleProperty::HVAC_POWER_ON\","
578                 + "\"defaultValue\": {\"int32Values\": [1]},"
579                 + "\"areas\": [{\"areaId\": \"Constants::HVAC_RIGHT\"}],"
580                 + "\"configArray\": [\"VehicleProperty::HVAC_FAN_SPEED\","
581                 + "\"VehicleProperty::HVAC_FAN_DIRECTION\"]}"
582                 + ", {\"property\": \"VehicleProperty::HVAC_FAN_SPEED\","
583                 + "\"defaultValue\": {\"int32Values\": [3]},"
584                 + "\"areas\": [{"
585                 + "\"areaId\": \"Constants::HVAC_LEFT\","
586                 + "\"minInt32Value\": 1,"
587                 + "\"maxInt32Value\": 7}]}]}";
588         List<File> customFileList = createFilenameList(jsonString);
589         // Create a request prop value.
590         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
591                 .build(/* prop= */ VehicleProperty.HVAC_FAN_SPEED, /* areaId= */ HVAC_LEFT);
592         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
593                 new FakeVhalConfigParser(), customFileList);
594 
595         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
596                 () -> fakeVehicleStub.get(requestPropValue));
597 
598         expect.that(thrown.errorCode).isEqualTo(StatusCode.INVALID_ARG);
599         expect.that(thrown).hasMessageThat().contains("This areaId: " + HVAC_LEFT + " doesn't match"
600                 + " any supported areaIds in HVAC_POWER_ON");
601     }
602 
603     @Test
testGetMethodForHvacPropWithMoreDefaultValues()604     public void testGetMethodForHvacPropWithMoreDefaultValues() throws Exception {
605         // Create a custom config file.
606         String jsonString = "{\"properties\": [{\"property\":"
607                 + "\"VehicleProperty::HVAC_POWER_ON\","
608                 + "\"defaultValue\": {\"int32Values\": [1, 0]},"
609                 + "\"areas\": [{\"areaId\": \"Constants::HVAC_ALL\"}],"
610                 + "\"configArray\": [\"VehicleProperty::HVAC_FAN_SPEED\","
611                 + "\"VehicleProperty::HVAC_FAN_DIRECTION\"]}"
612                 + ", {\"property\": \"VehicleProperty::HVAC_FAN_SPEED\","
613                 + "\"defaultValue\": {\"int32Values\": [3]},"
614                 + "\"areas\": [{"
615                 + "\"areaId\": \"Constants::HVAC_ALL\","
616                 + "\"minInt32Value\": 1,"
617                 + "\"maxInt32Value\": 7}]}]}";
618         List<File> customFileList = createFilenameList(jsonString);
619         // Create a request prop value.
620         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
621                 .build(/* prop= */ VehicleProperty.HVAC_FAN_SPEED, /* areaId= */ HVAC_ALL);
622         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
623                 new FakeVhalConfigParser(), customFileList);
624 
625         HalPropValue propValue = fakeVehicleStub.get(requestPropValue);
626 
627         expect.that(propValue.getInt32Value(0)).isEqualTo(3);
628     }
629 
630     @Test
testGetAsyncMethodForSingleValue()631     public void testGetAsyncMethodForSingleValue() throws Exception {
632         String jsonString = "{\"properties\": [{\"property\": \"VehicleProperty::INFO_FUEL_TYPE\","
633                 + "\"defaultValue\": {\"int32Values\": [\"FuelType::FUEL_TYPE_UNLEADED\"]}}]}";
634         List<File> customFileList = createFilenameList(jsonString);
635         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
636                 .build(/* prop= */ VehicleProperty.INFO_FUEL_TYPE, /* areaId= */ 123);
637         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
638                 new FakeVhalConfigParser(), customFileList);
639         AsyncGetSetRequest getRequest = new AsyncGetSetRequest(/* serviceRequestId= */ 0,
640                 requestPropValue, DEFAULT_TIMEOUT);
641 
642         fakeVehicleStub.getAsync(List.of(getRequest), mCallback);
643 
644         List<GetVehicleStubAsyncResult> getAsyncResult = captureOnGetAsyncResults();
645         GetVehicleStubAsyncResult asyncResult = getAsyncResult.get(0);
646         HalPropValue propValue = asyncResult.getHalPropValue();
647         expect.that(asyncResult.getServiceRequestId()).isEqualTo(0);
648         // For global properties, if default value exists, will return the default value.
649         expect.that(propValue.getAreaId()).isEqualTo(0);
650         expect.that(propValue.getInt32ValuesSize()).isEqualTo(1);
651         expect.that(propValue.getInt32Value(0)).isEqualTo(FuelType.FUEL_TYPE_UNLEADED);
652     }
653 
654     @Test
testGetAsyncMethodForSingleValueNoReadPermissionError()655     public void testGetAsyncMethodForSingleValueNoReadPermissionError() throws Exception {
656         String jsonString = "{\"properties\": [{\"property\": "
657                 + "\"VehicleProperty::ANDROID_EPOCH_TIME\","
658                 + "\"access\": \"VehiclePropertyAccess::WRITE\"}]}";
659         List<File> customFileList = createFilenameList(jsonString);
660         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
661                 .build(/* prop= */ VehicleProperty.ANDROID_EPOCH_TIME, /* areaId= */ 0);
662         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
663                 new FakeVhalConfigParser(), customFileList);
664         AsyncGetSetRequest getRequest = new AsyncGetSetRequest(/* serviceRequestId= */ 0,
665                 requestPropValue, DEFAULT_TIMEOUT);
666 
667         fakeVehicleStub.getAsync(List.of(getRequest), mCallback);
668 
669         List<GetVehicleStubAsyncResult> getAsyncResult = captureOnGetAsyncResults();
670         GetVehicleStubAsyncResult asyncResult = getAsyncResult.get(0);
671         HalPropValue propValue = asyncResult.getHalPropValue();
672         expect.that(propValue).isNull();
673         expect.that(asyncResult.getServiceRequestId()).isEqualTo(0);
674         expect.that(asyncResult.getErrorCode())
675             .isEqualTo(CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
676     }
677 
678     @Test
testGetAsyncForPropNotAvailable()679     public void testGetAsyncForPropNotAvailable() throws Exception {
680         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_HVAC_POWER_OFF + "]}";
681         List<File> customFileList = createFilenameList(jsonString);
682         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
683                 .build(/* prop= */ VehicleProperty.HVAC_FAN_SPEED, /* areaId= */ SEAT_1_LEFT);
684         AsyncGetSetRequest getRequest = new AsyncGetSetRequest(/* serviceRequestId= */ 0,
685                 requestPropValue, DEFAULT_TIMEOUT);
686         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
687                 new FakeVhalConfigParser(), customFileList);
688 
689         fakeVehicleStub.getAsync(List.of(getRequest), mCallback);
690 
691         List<GetVehicleStubAsyncResult> getAsyncResult = captureOnGetAsyncResults();
692         GetVehicleStubAsyncResult asyncResult = getAsyncResult.get(0);
693         expect.that(asyncResult.getHalPropValue()).isNull();
694         expect.that(asyncResult.getServiceRequestId()).isEqualTo(0);
695         expect.that(asyncResult.getErrorCode())
696                 .isEqualTo(CarPropertyManager.STATUS_ERROR_NOT_AVAILABLE);
697     }
698 
699     @Test
testGetAsyncForSpecialPropInternalError()700     public void testGetAsyncForSpecialPropInternalError() throws Exception {
701         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
702                 .build(VehicleProperty.VHAL_HEARTBEAT, 0);
703         AsyncGetSetRequest getRequest = new AsyncGetSetRequest(/* serviceRequestId= */ 0,
704                 requestPropValue, DEFAULT_TIMEOUT);
705         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
706                 new FakeVhalConfigParser(), new ArrayList<>());
707         when(mMockRealVehicleStub.get(requestPropValue)).thenThrow(RemoteException.class);
708 
709         fakeVehicleStub.getAsync(List.of(getRequest), mCallback);
710 
711         List<GetVehicleStubAsyncResult> getAsyncResult = captureOnGetAsyncResults();
712         GetVehicleStubAsyncResult asyncResult = getAsyncResult.get(0);
713         expect.that(asyncResult.getHalPropValue()).isNull();
714         expect.that(asyncResult.getServiceRequestId()).isEqualTo(0);
715         expect.that(asyncResult.getErrorCode())
716                 .isEqualTo(CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
717     }
718 
719     @Test
testGetAsyncForMultipleGetRequestsSuccessAndError()720     public void testGetAsyncForMultipleGetRequestsSuccessAndError() throws Exception {
721         String jsonString = "{\"properties\": "
722                 + "[{\"property\": \"VehicleProperty::INFO_FUEL_TYPE\","
723                 + "\"defaultValue\": {\"int32Values\": [\"FuelType::FUEL_TYPE_UNLEADED\"]}}, "
724                 + "{\"property\": \"VehicleProperty::ANDROID_EPOCH_TIME\","
725                 + "\"access\": \"VehiclePropertyAccess::WRITE\"}]}";
726         List<File> customFileList = createFilenameList(jsonString);
727         HalPropValue requestPropValue1 = new HalPropValueBuilder(/* isAidl= */ true)
728                 .build(/* prop= */ VehicleProperty.ANDROID_EPOCH_TIME, /* areaId= */ 0);
729         HalPropValue requestPropValue2 = new HalPropValueBuilder(/* isAidl= */ true)
730                 .build(/* prop= */ VehicleProperty.INFO_FUEL_TYPE, /* areaId= */ 123);
731         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
732                 new FakeVhalConfigParser(), customFileList);
733         AsyncGetSetRequest getRequest1 = new AsyncGetSetRequest(/* serviceRequestId= */ 0,
734                 requestPropValue1, DEFAULT_TIMEOUT);
735         AsyncGetSetRequest getRequest2 = new AsyncGetSetRequest(/* serviceRequestId= */ 1,
736                 requestPropValue2, DEFAULT_TIMEOUT);
737 
738         fakeVehicleStub.getAsync(List.of(getRequest1, getRequest2), mCallback);
739 
740         List<GetVehicleStubAsyncResult> getAsyncResult = captureOnGetAsyncResults();
741         GetVehicleStubAsyncResult asyncResult1 = getAsyncResult.get(0);
742         HalPropValue propValue1 = asyncResult1.getHalPropValue();
743         expect.that(asyncResult1.getServiceRequestId()).isEqualTo(0);
744         expect.that(propValue1).isNull();
745         expect.that(asyncResult1.getErrorCode())
746                 .isEqualTo(CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
747         GetVehicleStubAsyncResult asyncResult2 = getAsyncResult.get(1);
748         HalPropValue propValue2 = asyncResult2.getHalPropValue();
749         expect.that(asyncResult2.getServiceRequestId()).isEqualTo(1);
750         expect.that(propValue2.getAreaId()).isEqualTo(0);
751         expect.that(propValue2.getInt32ValuesSize()).isEqualTo(1);
752         expect.that(propValue2.getInt32Value(0)).isEqualTo(FuelType.FUEL_TYPE_UNLEADED);
753     }
754 
755     @Test
testSetMethodPropConfigNotExist()756     public void testSetMethodPropConfigNotExist() throws Exception {
757         // Create a custom config file.
758         String jsonString = "{\"properties\": [{\"property\":"
759                 + "\"VehicleProperty::SEAT_BELT_BUCKLED\","
760                 + "\"defaultValue\": {\"int32Values\": [0]},"
761                 + "\"areas\": [{\"areaId\": \"Constants::SEAT_1_LEFT\"}]}]}";
762         List<File> customFileList = createFilenameList(jsonString);
763         // Create a request prop value.
764         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
765                 .build(/* prop= */ 123456, /* areaId= */ 0);
766         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
767                 new FakeVhalConfigParser(), customFileList);
768 
769         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
770                 () -> fakeVehicleStub.set(requestPropValue));
771 
772         expect.that(thrown).hasMessageThat().contains("The propId: 123456 is not supported.");
773     }
774 
775     @Test
testSetMethodPropDefaultValueExist()776     public void testSetMethodPropDefaultValueExist() throws Exception {
777         // Create a custom config file.
778         String jsonString = "{\"properties\": [{\"property\":"
779                 + "\"VehicleProperty::INFO_FUEL_CAPACITY\","
780                 + "\"defaultValue\": {\"floatValues\": [100.0]},"
781                 + "\"access\": \"VehiclePropertyAccess::READ_WRITE\"}]}";
782         List<File> customFileList = createFilenameList(jsonString);
783         // Create a request prop value.
784         RawPropValues rawPropValues = new RawPropValues();
785         rawPropValues.floatValues = new float[]{10};
786         HalPropValue requestPropValue = buildHalPropValue(
787                 /* propId= */ VehicleProperty.INFO_FUEL_CAPACITY, /* areaId= */ 0,
788                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
789 
790         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
791                 new FakeVhalConfigParser(), customFileList);
792         HalPropValue oldPropValue = fakeVehicleStub.get(requestPropValue);
793         fakeVehicleStub.set(requestPropValue);
794         HalPropValue updatedPropValue = fakeVehicleStub.get(requestPropValue);
795 
796         expect.that(updatedPropValue.getFloatValue(0)).isEqualTo(10.0f);
797         expect.that(updatedPropValue.getFloatValue(0)).isNotEqualTo(oldPropValue.getFloatValue(0));
798         expect.that(updatedPropValue.getTimestamp()).isNotEqualTo(oldPropValue.getTimestamp());
799     }
800 
801     @Test
testSetMethodPropDefaultValueNotExist()802     public void testSetMethodPropDefaultValueNotExist() throws Exception {
803         // Create a custom config file.
804         String jsonString = "{\"properties\": [{\"property\":"
805                 + "\"VehicleProperty::INFO_FUEL_CAPACITY\","
806                 + "\"access\": \"VehiclePropertyAccess::READ_WRITE\"}]}";
807         List<File> customFileList = createFilenameList(jsonString);
808         // Create a request prop value.
809         RawPropValues rawPropValues = new RawPropValues();
810         rawPropValues.int32Values = new int[]{32};
811         HalPropValue requestPropValue = buildHalPropValue(
812                 /* propId= */ VehicleProperty.INFO_FUEL_CAPACITY, /* areaId= */ 0,
813                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
814 
815         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
816                 new FakeVhalConfigParser(), customFileList);
817         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
818                 () -> fakeVehicleStub.get(requestPropValue));
819 
820         expect.that(thrown).hasMessageThat().contains("has no property value");
821 
822         fakeVehicleStub.set(requestPropValue);
823         HalPropValue updatedPropValue = fakeVehicleStub.get(requestPropValue);
824 
825         expect.that(updatedPropValue.getInt32Value(0)).isEqualTo(32);
826     }
827 
828     @Test
testSetMethodValueOutOfRange()829     public void testSetMethodValueOutOfRange() throws Exception {
830         // Create a custom config file.
831         String jsonString = "{\"properties\": [{\"property\":"
832                 + "\"VehicleProperty::SEAT_BELT_HEIGHT_POS\","
833                 + "\"defaultValue\": {\"int32Values\": [10]},"
834                 + "\"areas\": [{\"areaId\": \"Constants::SEAT_1_LEFT\","
835                 + "\"minInt32Value\": 0, \"maxInt32Value\": 10}]}]}";
836         List<File> customFileList = createFilenameList(jsonString);
837         // Create a request prop value.
838         RawPropValues rawPropValues = new RawPropValues();
839         rawPropValues.int32Values = new int[]{15};
840         HalPropValue requestPropValue = buildHalPropValue(
841                 /* propId= */ VehicleProperty.SEAT_BELT_HEIGHT_POS, /* areaId= */ SEAT_1_LEFT,
842                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
843 
844         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
845                 new FakeVhalConfigParser(), customFileList);
846         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
847                 () -> fakeVehicleStub.set(requestPropValue));
848 
849         expect.that(thrown.errorCode).isEqualTo(StatusCode.INVALID_ARG);
850         expect.that(thrown).hasMessageThat().contains("The set value is outside the range");
851     }
852 
853     @Test
testSetMethodGlobalPropWithAreaIdSetValueOutOfRange()854     public void testSetMethodGlobalPropWithAreaIdSetValueOutOfRange() throws Exception {
855         // Create a custom config file.
856         String jsonString = "{\"properties\": [{\"property\":"
857                 + "\"VehicleProperty::DISPLAY_BRIGHTNESS\","
858                 + "\"defaultValue\": {\"int32Values\": [100]},"
859                 + "\"areas\": [{\"areaId\": 0, \"minInt32Value\": 0, \"maxInt32Value\": 100}]}]}";
860         List<File> customFileList = createFilenameList(jsonString);
861         // Create a request prop value.
862         RawPropValues rawPropValues = new RawPropValues();
863         rawPropValues.int32Values = new int[]{120};
864         HalPropValue requestPropValue = buildHalPropValue(
865                 /* propId= */ VehicleProperty.DISPLAY_BRIGHTNESS, /* areaId= */ 0,
866                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
867 
868         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
869                 new FakeVhalConfigParser(), customFileList);
870         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
871                 () -> fakeVehicleStub.set(requestPropValue));
872 
873         expect.that(thrown.errorCode).isEqualTo(StatusCode.INVALID_ARG);
874         expect.that(thrown).hasMessageThat().contains("The set value is outside the range");
875     }
876 
877     @Test
testSetMethodGlobalPropWithNoAreaIdSetValueOutOfRange()878     public void testSetMethodGlobalPropWithNoAreaIdSetValueOutOfRange() throws Exception {
879         // Create a custom config file.
880         String jsonString = "{\"properties\": [{\"property\":"
881                 + "\"VehicleProperty::DISPLAY_BRIGHTNESS\","
882                 + "\"defaultValue\": {\"int32Values\": [100]}}]}";
883         List<File> customFileList = createFilenameList(jsonString);
884         // Create a request prop value.
885         RawPropValues rawPropValues = new RawPropValues();
886         rawPropValues.int32Values = new int[]{120};
887         HalPropValue requestPropValue = buildHalPropValue(
888                 /* propId= */ VehicleProperty.DISPLAY_BRIGHTNESS, /* areaId= */ 0,
889                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
890 
891         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
892                 new FakeVhalConfigParser(), customFileList);
893         fakeVehicleStub.set(requestPropValue);
894         HalPropValue updatedPropValue = fakeVehicleStub.get(requestPropValue);
895 
896         expect.that(updatedPropValue.getInt32Value(0)).isEqualTo(120);
897     }
898 
899     @Test
testSetMethodGlobalPropWithAreaIdNotZero()900     public void testSetMethodGlobalPropWithAreaIdNotZero() throws Exception {
901         // Create a custom config file.
902         String jsonString = "{\"properties\": [{\"property\":"
903                 + "\"VehicleProperty::DISPLAY_BRIGHTNESS\","
904                 + "\"defaultValue\": {\"int32Values\": [100]},"
905                 + "\"areas\": [{\"areaId\": 1, \"minInt32Value\": 0, \"maxInt32Value\": 100}]}]}";
906         List<File> customFileList = createFilenameList(jsonString);
907         // Create a request prop value.
908         RawPropValues rawPropValues = new RawPropValues();
909         rawPropValues.int32Values = new int[]{120};
910         HalPropValue requestPropValue = buildHalPropValue(
911                 /* propId= */ VehicleProperty.DISPLAY_BRIGHTNESS, /* areaId= */ 1,
912                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
913 
914         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
915                 new FakeVhalConfigParser(), customFileList);
916         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
917                 () -> fakeVehicleStub.get(requestPropValue));
918 
919         // For global properties, if the supported area config array doesn't include 0,
920         // a ServiceSpecificException with StatusCode.INVALID_ARG.
921         expect.that(thrown.errorCode).isEqualTo(StatusCode.INVALID_ARG);
922         expect.that(thrown).hasMessageThat().contains("The areaId: 0 is not supported.");
923     }
924 
925     @Test
testSetMethodAreaConfigHasNoLimit()926     public void testSetMethodAreaConfigHasNoLimit() throws Exception {
927         // Create a custom config file.
928         String jsonString = "{\"properties\": [{\"property\":"
929                 + "\"VehicleProperty::SEAT_BELT_BUCKLED\","
930                 + "\"defaultValue\": {\"int32Values\": [0]},"
931                 + "\"areas\": [{\"areaId\": \"Constants::SEAT_1_LEFT\"}]}]}";
932         List<File> customFileList = createFilenameList(jsonString);
933         // Create a request prop value.
934         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
935                 new FakeVhalConfigParser(), customFileList);
936         RawPropValues rawPropValues = new RawPropValues();
937         rawPropValues.int32Values = new int[]{32};
938         HalPropValue requestPropValue = buildHalPropValue(
939                 /* propId= */ VehicleProperty.SEAT_BELT_BUCKLED, /* areaId= */ SEAT_1_LEFT,
940                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
941 
942         HalPropValue oldPropValue = fakeVehicleStub.get(requestPropValue);
943         fakeVehicleStub.set(requestPropValue);
944         HalPropValue updatedPropValue = fakeVehicleStub.get(requestPropValue);
945 
946         expect.that(oldPropValue.getInt32Value(0)).isEqualTo(0);
947         expect.that(updatedPropValue.getInt32Value(0)).isEqualTo(32);
948     }
949 
950     @Test
testSetMethodForHvacProp()951     public void testSetMethodForHvacProp() throws Exception {
952         // Create a custom config file.
953         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_HVAC_POWER_ON
954                 + "," + PROPERTY_CONFIG_STRING_HVAC_FAN_SPEED + "]}";
955         List<File> customFileList = createFilenameList(jsonString);
956         RawPropValues rawPropValues = new RawPropValues();
957         rawPropValues.int32Values = new int[]{5};
958         HalPropValue requestPropValue = buildHalPropValue(
959                 /* propId= */ VehicleProperty.HVAC_FAN_SPEED, /* areaId= */ HVAC_ALL,
960                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
961         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
962                 new FakeVhalConfigParser(), customFileList);
963         HalPropValue oldPropValue = fakeVehicleStub.get(requestPropValue);
964         fakeVehicleStub.set(requestPropValue);
965         HalPropValue updatedPropValue = fakeVehicleStub.get(requestPropValue);
966 
967         expect.that(oldPropValue.getInt32Value(0)).isEqualTo(3);
968         expect.that(updatedPropValue.getInt32Value(0)).isEqualTo(5);
969     }
970 
971     @Test
testSetMethodForHvacPropNotAvailable()972     public void testSetMethodForHvacPropNotAvailable() throws Exception {
973         // Create a custom config file.
974         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_HVAC_POWER_OFF + "]}";
975         List<File> customFileList = createFilenameList(jsonString);
976         // Create a request prop value.
977         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
978                 .build(/* prop= */ VehicleProperty.HVAC_FAN_SPEED, /* areaId= */ SEAT_1_LEFT);
979         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
980                 new FakeVhalConfigParser(), customFileList);
981 
982         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
983                 () -> fakeVehicleStub.set(requestPropValue));
984 
985         expect.that(thrown.errorCode).isEqualTo(StatusCode.NOT_AVAILABLE);
986         expect.that(thrown).hasMessageThat().contains("HVAC_POWER_ON is off. PropId: 356517120 "
987                 + "is not available.");
988     }
989 
990     @Test
testSetAsyncForSingleProp()991     public void testSetAsyncForSingleProp() throws Exception {
992         String jsonString = "{\"properties\": [{\"property\":"
993                 + "\"VehicleProperty::INFO_FUEL_CAPACITY\","
994                 + "\"defaultValue\": {\"floatValues\": [100.0]},"
995                 + "\"access\": \"VehiclePropertyAccess::READ_WRITE\"}]}";
996         List<File> customFileList = createFilenameList(jsonString);
997         RawPropValues rawPropValues = new RawPropValues();
998         rawPropValues.floatValues = new float[]{10};
999         HalPropValue requestPropValue = buildHalPropValue(
1000                 /* propId= */ VehicleProperty.INFO_FUEL_CAPACITY, /* areaId= */ 0,
1001                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
1002         AsyncGetSetRequest request = new AsyncGetSetRequest(/* serviceRequestId= */ 0,
1003                 requestPropValue, DEFAULT_TIMEOUT);
1004         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
1005                 new FakeVhalConfigParser(), customFileList);
1006         fakeVehicleStub.getAsync(List.of(request), mCallback);
1007         List<GetVehicleStubAsyncResult> getOldAsyncResult = captureOnGetAsyncResults();
1008         HalPropValue oldPropValue = getOldAsyncResult.get(0).getHalPropValue();
1009         reset(mCallback);
1010 
1011         fakeVehicleStub.setAsync(List.of(request), mCallback);
1012         List<SetVehicleStubAsyncResult> setAsyncResult = captureOnSetAsyncResults();
1013 
1014         reset(mCallback);
1015         fakeVehicleStub.getAsync(List.of(request), mCallback);
1016         List<GetVehicleStubAsyncResult> getNewAsyncResult = captureOnGetAsyncResults();
1017         GetVehicleStubAsyncResult newAsyncGetResult = getNewAsyncResult.get(0);
1018         SetVehicleStubAsyncResult setVehicleStubAsyncResult = setAsyncResult.get(0);
1019         HalPropValue updatedPropValue = newAsyncGetResult.getHalPropValue();
1020         expect.that(setVehicleStubAsyncResult.getServiceRequestId()).isEqualTo(0);
1021         expect.that(setVehicleStubAsyncResult.getServiceRequestId())
1022                 .isEqualTo(newAsyncGetResult.getServiceRequestId());
1023         expect.that(setVehicleStubAsyncResult.getErrorCode())
1024                 .isEqualTo(CarPropertyErrorCodes.STATUS_OK);
1025         expect.that(updatedPropValue.getFloatValue(0)).isEqualTo(10.0f);
1026         expect.that(updatedPropValue.getFloatValue(0)).isNotEqualTo(oldPropValue.getFloatValue(0));
1027         expect.that(updatedPropValue.getTimestamp()).isNotEqualTo(oldPropValue.getTimestamp());
1028     }
1029 
1030     @Test
testSetAsyncGlobalPropWithAreaIdSetValueOutOfRange()1031     public void testSetAsyncGlobalPropWithAreaIdSetValueOutOfRange() throws Exception {
1032         // Create a custom config file.
1033         String jsonString = "{\"properties\": [{\"property\":"
1034                 + "\"VehicleProperty::DISPLAY_BRIGHTNESS\","
1035                 + "\"defaultValue\": {\"int32Values\": [100]},"
1036                 + "\"areas\": [{\"areaId\": 0, \"minInt32Value\": 0, \"maxInt32Value\": 100}]}]}";
1037         List<File> customFileList = createFilenameList(jsonString);
1038         RawPropValues rawPropValues = new RawPropValues();
1039         rawPropValues.int32Values = new int[]{105};
1040         HalPropValue requestPropValue = buildHalPropValue(
1041                 /* propId= */ VehicleProperty.DISPLAY_BRIGHTNESS, /* areaId= */ 0,
1042                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
1043         AsyncGetSetRequest request = new AsyncGetSetRequest(/* serviceRequestId= */ 0,
1044                 requestPropValue, DEFAULT_TIMEOUT);
1045         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
1046                 new FakeVhalConfigParser(), customFileList);
1047 
1048         fakeVehicleStub.setAsync(List.of(request), mCallback);
1049 
1050         List<SetVehicleStubAsyncResult> setAsyncResult = captureOnSetAsyncResults();
1051         SetVehicleStubAsyncResult setVehicleStubAsyncResult = setAsyncResult.get(0);
1052         expect.that(setVehicleStubAsyncResult.getServiceRequestId()).isEqualTo(0);
1053         expect.that(setVehicleStubAsyncResult.getErrorCode())
1054                 .isEqualTo(CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
1055     }
1056 
1057     @Test
testSetAsyncForSpecialPropRemoteException()1058     public void testSetAsyncForSpecialPropRemoteException() throws Exception {
1059         // Create a request prop value.
1060         RawPropValues rawPropValues = new RawPropValues();
1061         rawPropValues.floatValues = new float[]{10};
1062         HalPropValue requestPropValue = buildHalPropValue(
1063                 /* propId= */ VehicleProperty.SWITCH_USER, /* areaId= */ 0,
1064                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
1065         doThrow(new RemoteException()).when(mMockRealVehicleStub).set(requestPropValue);
1066         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
1067                 new FakeVhalConfigParser(), new ArrayList<>());
1068         AsyncGetSetRequest request = new AsyncGetSetRequest(/* serviceRequestId= */ 0,
1069                 requestPropValue, DEFAULT_TIMEOUT);
1070 
1071         fakeVehicleStub.setAsync(List.of(request), mCallback);
1072 
1073         List<SetVehicleStubAsyncResult> setAsyncResult = captureOnSetAsyncResults();
1074         SetVehicleStubAsyncResult setVehicleStubAsyncResult = setAsyncResult.get(0);
1075         expect.that(setVehicleStubAsyncResult.getServiceRequestId()).isEqualTo(0);
1076         expect.that(setVehicleStubAsyncResult.getErrorCode())
1077                 .isEqualTo(CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
1078         verify(mMockRealVehicleStub).set(requestPropValue);
1079     }
1080 
1081     @Test
testSetAsyncOnMultipleRequests()1082     public void testSetAsyncOnMultipleRequests() throws Exception {
1083         String jsonString = "{\"properties\": [{\"property\":"
1084                 + "\"VehicleProperty::DISPLAY_BRIGHTNESS\","
1085                 + "\"defaultValue\": {\"int32Values\": [100]},"
1086                 + "\"areas\": [{\"areaId\": 0, \"minInt32Value\": 0, \"maxInt32Value\": 100}]}]}";
1087         List<File> customFileList = createFilenameList(jsonString);
1088         RawPropValues rawPropValues1 = new RawPropValues();
1089         rawPropValues1.floatValues = new float[]{10};
1090         HalPropValue requestPropValue1 = buildHalPropValue(
1091                 /* propId= */ VehicleProperty.SWITCH_USER, /* areaId= */ 0,
1092                 SystemClock.elapsedRealtimeNanos(), rawPropValues1);
1093         doThrow(new RemoteException()).when(mMockRealVehicleStub).set(requestPropValue1);
1094         AsyncGetSetRequest request1 = new AsyncGetSetRequest(/* serviceRequestId= */ 0,
1095                 requestPropValue1, DEFAULT_TIMEOUT);
1096         RawPropValues rawPropValues2 = new RawPropValues();
1097         rawPropValues2.int32Values = new int[]{105};
1098         HalPropValue requestPropValue2 = buildHalPropValue(
1099                 /* propId= */ VehicleProperty.DISPLAY_BRIGHTNESS, /* areaId= */ 0,
1100                 SystemClock.elapsedRealtimeNanos(), rawPropValues2);
1101         AsyncGetSetRequest request2 = new AsyncGetSetRequest(/* serviceRequestId= */ 1,
1102                 requestPropValue2, DEFAULT_TIMEOUT);
1103         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
1104                 new FakeVhalConfigParser(), customFileList);
1105 
1106         fakeVehicleStub.setAsync(List.of(request1, request2), mCallback);
1107         List<SetVehicleStubAsyncResult> setAsyncResult = captureOnSetAsyncResults();
1108         SetVehicleStubAsyncResult setVehicleStubAsyncResult1 = setAsyncResult.get(0);
1109         expect.that(setVehicleStubAsyncResult1.getServiceRequestId()).isEqualTo(0);
1110         expect.that(setVehicleStubAsyncResult1.getErrorCode())
1111                 .isEqualTo(CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
1112         verify(mMockRealVehicleStub).set(requestPropValue1);
1113         SetVehicleStubAsyncResult setVehicleStubAsyncResult2 = setAsyncResult.get(1);
1114         expect.that(setVehicleStubAsyncResult2.getServiceRequestId()).isEqualTo(1);
1115         expect.that(setVehicleStubAsyncResult2.getErrorCode())
1116                 .isEqualTo(CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
1117     }
1118 
1119     @Test
testSubscribeOnChangePropOneClientSubscribeOnePropManyTime()1120     public void testSubscribeOnChangePropOneClientSubscribeOnePropManyTime() throws Exception {
1121         // Create a custom config file.
1122         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_ON_CHANGE + "]}";
1123         List<File> customFileList = createFilenameList(jsonString);
1124         // Create subscribe options array.
1125         SubscribeOptions option = new SubscribeOptions();
1126         option.propId = VehicleProperty.ENGINE_OIL_LEVEL;
1127         option.sampleRate = 0.5f;
1128         SubscribeOptions[] options = new SubscribeOptions[]{option};
1129         // Create set value
1130         RawPropValues rawPropValues = new RawPropValues();
1131         rawPropValues.int32Values = new int[]{VehicleOilLevel.LOW};
1132         HalPropValue requestPropValue = buildHalPropValue(
1133                 /* propId= */ VehicleProperty.ENGINE_OIL_LEVEL, /* areaId= */ 0,
1134                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
1135         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1136         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1137                 new FakeVhalConfigParser(), customFileList);
1138 
1139         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1140         try {
1141             client.subscribe(options);
1142             client.subscribe(options);
1143             client.subscribe(options);
1144             fakeVehicleStub.set(requestPropValue);
1145 
1146             verify(callback, times(1)).onPropertyEvent(any(ArrayList.class));
1147         } finally {
1148             client.unsubscribe(VehicleProperty.ENGINE_OIL_LEVEL);
1149         }
1150     }
1151 
1152     @Test
testSubscribeOnChangePropOneClientSubscribeTwoProp()1153     public void testSubscribeOnChangePropOneClientSubscribeTwoProp() throws Exception {
1154         // Create a custom config file.
1155         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_ON_CHANGE + ","
1156                 + "{\"property\": \"VehicleProperty::NIGHT_MODE\","
1157                 + "\"defaultValue\": {\"int32Values\": [0]},"
1158                 + "\"access\": \"VehiclePropertyAccess::READ_WRITE\","
1159                 + "\"changeMode\": \"VehiclePropertyChangeMode::ON_CHANGE\"}]}";
1160         List<File> customFileList = createFilenameList(jsonString);
1161         // Create subscribe options
1162         SubscribeOptions option1 = new SubscribeOptions();
1163         option1.propId = VehicleProperty.ENGINE_OIL_LEVEL;
1164         option1.sampleRate = 0.5f;
1165         SubscribeOptions option2 = new SubscribeOptions();
1166         option2.propId = VehicleProperty.NIGHT_MODE;
1167         option2.sampleRate = 0.5f;
1168         SubscribeOptions[] options = new SubscribeOptions[]{option1, option2};
1169         // Create set value
1170         RawPropValues rawPropValues1 = new RawPropValues();
1171         rawPropValues1.int32Values = new int[]{VehicleOilLevel.LOW};
1172         HalPropValue requestPropValue1 = buildHalPropValue(
1173                 /* propId= */ VehicleProperty.ENGINE_OIL_LEVEL, /* areaId= */ 0,
1174                 SystemClock.elapsedRealtimeNanos(), rawPropValues1);
1175         RawPropValues rawPropValues2 = new RawPropValues();
1176         rawPropValues2.int32Values = new int[]{0};
1177         HalPropValue requestPropValue2 = buildHalPropValue(
1178                 /* propId= */ VehicleProperty.NIGHT_MODE, /* areaId= */ 0,
1179                 SystemClock.elapsedRealtimeNanos(), rawPropValues2);
1180         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1181         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1182                 new FakeVhalConfigParser(), customFileList);
1183 
1184         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1185         try {
1186             client.subscribe(options);
1187             fakeVehicleStub.set(requestPropValue1);
1188             fakeVehicleStub.set(requestPropValue2);
1189 
1190             verify(callback, times(2)).onPropertyEvent(any(ArrayList.class));
1191         } finally {
1192             client.unsubscribe(VehicleProperty.ENGINE_OIL_LEVEL);
1193             client.unsubscribe(VehicleProperty.NIGHT_MODE);
1194         }
1195     }
1196 
1197     @Test
testSubscribeAreaPropertyWithEmptyOptionsAreaIds()1198     public void testSubscribeAreaPropertyWithEmptyOptionsAreaIds() throws Exception {
1199         // Create a custom config file.
1200         String jsonString = "{\"properties\": [{\"property\": "
1201                 + "\"VehicleProperty::SEAT_HEADREST_ANGLE_POS\","
1202                 + "\"defaultValue\": {\"int32Values\": [0]},"
1203                 + "\"areas\":["
1204                 + "{\"areaId\": \"Constants::SEAT_1_LEFT\", \"minInt32Value\": -10,"
1205                 + "\"maxInt32Value\": 10},"
1206                 + "{\"areaId\": \"Constants::SEAT_1_RIGHT\",\"minInt32Value\": -10,"
1207                 + "\"maxInt32Value\": 10}]}]}";
1208         List<File> customFileList = createFilenameList(jsonString);
1209         // Create subscribe options array.
1210         SubscribeOptions option = new SubscribeOptions();
1211         option.propId = VehicleProperty.SEAT_HEADREST_ANGLE_POS;
1212         // option.areaId array is null.
1213         SubscribeOptions[] options = new SubscribeOptions[]{option};
1214         // Create set value
1215         RawPropValues rawPropValues = new RawPropValues();
1216         rawPropValues.int32Values = new int[]{10};
1217         HalPropValue requestPropValue1 = buildHalPropValue(
1218                 /* propId= */ VehicleProperty.SEAT_HEADREST_ANGLE_POS, SEAT_1_LEFT,
1219                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
1220         HalPropValue requestPropValue2 = buildHalPropValue(
1221                 /* propId= */ VehicleProperty.SEAT_HEADREST_ANGLE_POS, SEAT_1_RIGHT,
1222                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
1223         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1224         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1225                 new FakeVhalConfigParser(), customFileList);
1226 
1227         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1228         client.subscribe(options);
1229         fakeVehicleStub.set(requestPropValue1);
1230         fakeVehicleStub.set(requestPropValue2);
1231 
1232         verify(callback, times(2)).onPropertyEvent(any(ArrayList.class));
1233     }
1234 
1235     @Test
testSubscribeOnChangePropTwoClientSubscribeSameProp()1236     public void testSubscribeOnChangePropTwoClientSubscribeSameProp() throws Exception {
1237         // Create a custom config file.
1238         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_ON_CHANGE + "]}";
1239         List<File> customFileList = createFilenameList(jsonString);
1240         // Create subscribe options
1241         SubscribeOptions option = new SubscribeOptions();
1242         option.propId = VehicleProperty.ENGINE_OIL_LEVEL;
1243         option.sampleRate = 2f;
1244         SubscribeOptions[] options = new SubscribeOptions[]{option};
1245         // Create set value
1246         RawPropValues rawPropValues = new RawPropValues();
1247         rawPropValues.int32Values = new int[]{VehicleOilLevel.LOW};
1248         HalPropValue requestPropValue = buildHalPropValue(
1249                 /* propId= */ VehicleProperty.ENGINE_OIL_LEVEL, /* areaId= */ 0,
1250                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
1251         VehicleHalCallback callback1 = mock(VehicleHalCallback.class);
1252         VehicleHalCallback callback2 = mock(VehicleHalCallback.class);
1253         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1254                 new FakeVhalConfigParser(), customFileList);
1255 
1256         VehicleStub.SubscriptionClient client1 = fakeVehicleStub.newSubscriptionClient(callback1);
1257         VehicleStub.SubscriptionClient client2 = fakeVehicleStub.newSubscriptionClient(callback2);
1258         try {
1259             client1.subscribe(options);
1260             client2.subscribe(options);
1261             fakeVehicleStub.set(requestPropValue);
1262 
1263             verify(callback1, times(1)).onPropertyEvent(any(ArrayList.class));
1264             verify(callback2, times(1)).onPropertyEvent(any(ArrayList.class));
1265         } finally {
1266             client1.unsubscribe(VehicleProperty.ENGINE_OIL_LEVEL);
1267             client2.unsubscribe(VehicleProperty.ENGINE_OIL_LEVEL);
1268         }
1269     }
1270 
1271     @Test
testSubscribeContinuousProp()1272     public void testSubscribeContinuousProp() throws Exception {
1273         // Create a custom config file.
1274         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_CONTINUOUS + "]}";
1275         List<File> customFileList = createFilenameList(jsonString);
1276         // Create subscribe options
1277         SubscribeOptions option = new SubscribeOptions();
1278         option.propId = VehicleProperty.FUEL_LEVEL;
1279         option.sampleRate = 100f;
1280         SubscribeOptions[] options = new SubscribeOptions[]{option};
1281         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1282         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1283                 new FakeVhalConfigParser(), customFileList);
1284 
1285         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1286         try {
1287             client.subscribe(options);
1288 
1289             verify(callback, timeout(100).atLeast(5)).onPropertyEvent(any(ArrayList.class));
1290         } finally {
1291             client.unsubscribe(VehicleProperty.FUEL_LEVEL);
1292         }
1293     }
1294 
1295     @Test
testSubscribeContinuousPropDifferentRate()1296     public void testSubscribeContinuousPropDifferentRate() throws Exception {
1297         // Create a custom config file.
1298         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_CONTINUOUS + "]}";
1299         List<File> customFileList = createFilenameList(jsonString);
1300         // Create subscribe options
1301         SubscribeOptions option1 = new SubscribeOptions();
1302         option1.propId = VehicleProperty.FUEL_LEVEL;
1303         option1.sampleRate = 100f;
1304         SubscribeOptions[] options1 = new SubscribeOptions[]{option1};
1305         SubscribeOptions option2 = new SubscribeOptions();
1306         option2.propId = VehicleProperty.FUEL_LEVEL;
1307         option2.sampleRate = 50f;
1308         SubscribeOptions[] options2 = new SubscribeOptions[]{option2};
1309         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1310         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1311                 new FakeVhalConfigParser(), customFileList);
1312 
1313         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1314         try {
1315             client.subscribe(options1);
1316 
1317             verify(callback, timeout(100).atLeast(5)).onPropertyEvent(any(ArrayList.class));
1318             clearInvocations(callback);
1319 
1320             client.subscribe(options2);
1321 
1322             verify(callback, timeout(200).atLeast(5)).onPropertyEvent(any(ArrayList.class));
1323         } finally {
1324             client.unsubscribe(VehicleProperty.FUEL_LEVEL);
1325         }
1326     }
1327 
1328     @Test
testSubscribeContinuousPropRateTooLarge()1329     public void testSubscribeContinuousPropRateTooLarge() throws Exception {
1330         // Create a custom config file.
1331         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_CONTINUOUS + "]}";
1332         List<File> customFileList = createFilenameList(jsonString);
1333         // Create subscribe options
1334         SubscribeOptions option = new SubscribeOptions();
1335         option.propId = VehicleProperty.FUEL_LEVEL;
1336         option.sampleRate = 200f;
1337         SubscribeOptions[] options = new SubscribeOptions[]{option};
1338 
1339         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1340         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
1341                 new FakeVhalConfigParser(), customFileList);
1342         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1343         try {
1344             client.subscribe(options);
1345 
1346             verify(callback, timeout(100).atLeast(5)).onPropertyEvent(any(ArrayList.class));
1347         } finally {
1348             client.unsubscribe(VehicleProperty.FUEL_LEVEL);
1349         }
1350     }
1351 
1352     @Test
testSubscribeContinuousPropRateTooSmall()1353     public void testSubscribeContinuousPropRateTooSmall() throws Exception {
1354         // Create a custom config file.
1355         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_CONTINUOUS + "]}";
1356         List<File> customFileList = createFilenameList(jsonString);
1357         // Create subscribe options
1358         SubscribeOptions option = new SubscribeOptions();
1359         option.propId = VehicleProperty.FUEL_LEVEL;
1360         option.sampleRate = -50f;
1361         SubscribeOptions[] options = new SubscribeOptions[]{option};
1362         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1363         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1364                 new FakeVhalConfigParser(), customFileList);
1365 
1366         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1367         try {
1368             client.subscribe(options);
1369 
1370             verify(callback, timeout(100).atLeast(1)).onPropertyEvent(any(ArrayList.class));
1371         } finally {
1372             client.unsubscribe(VehicleProperty.FUEL_LEVEL);
1373         }
1374     }
1375 
1376     @Test
testSubscribeNonSupportPropID()1377     public void testSubscribeNonSupportPropID() throws Exception {
1378         // Create subscribe options
1379         SubscribeOptions option = new SubscribeOptions();
1380         option.propId = 123;
1381         option.sampleRate = 0f;
1382         SubscribeOptions[] options = new SubscribeOptions[]{option};
1383         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1384         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1385                 new FakeVhalConfigParser(), new ArrayList<>());
1386 
1387         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1388         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
1389                 () -> client.subscribe(options));
1390 
1391         expect.that(thrown.errorCode).isEqualTo(StatusCode.INVALID_ARG);
1392         expect.that(thrown).hasMessageThat().contains("The propId: 123 is not supported.");
1393     }
1394 
1395     @Test
testSubscribeNonSupportAreaId()1396     public void testSubscribeNonSupportAreaId() throws Exception {
1397         // Create a custom config file.
1398         String jsonString = "{\"properties\": [{\"property\":"
1399                 + "\"VehicleProperty::SEAT_BELT_BUCKLED\","
1400                 + "\"defaultValue\": {\"int32Values\": [0]}}]}";
1401         List<File> customFileList = createFilenameList(jsonString);
1402         // Create subscribe options
1403         SubscribeOptions option = new SubscribeOptions();
1404         option.propId = VehicleProperty.SEAT_BELT_BUCKLED;
1405         option.areaIds = new int[]{SEAT_1_LEFT};
1406         option.sampleRate = 1f;
1407         SubscribeOptions[] options = new SubscribeOptions[]{option};
1408         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1409         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1410                 new FakeVhalConfigParser(), customFileList);
1411 
1412         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1413         ServiceSpecificException thrown;
1414         try {
1415             thrown = assertThrows(ServiceSpecificException.class, () -> client.subscribe(options));
1416         } finally {
1417             client.unsubscribe(VehicleProperty.SEAT_BELT_BUCKLED);
1418         }
1419 
1420         expect.that(thrown.errorCode).isEqualTo(StatusCode.INVALID_ARG);
1421         expect.that(thrown).hasMessageThat().contains("The areaId: " + SEAT_1_LEFT
1422                 + " is not supported.");
1423     }
1424 
1425     @Test
testSubscribeStaticPropThrowException()1426     public void testSubscribeStaticPropThrowException() throws Exception {
1427         // Create a custom config file.
1428         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_STATIC + "]}";
1429         List<File> customFileList = createFilenameList(jsonString);
1430         // Create subscribe options
1431         SubscribeOptions option = new SubscribeOptions();
1432         option.propId = VehicleProperty.INFO_FUEL_CAPACITY;
1433         option.sampleRate = 0f;
1434         SubscribeOptions[] options = new SubscribeOptions[]{option};
1435         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1436         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1437                 new FakeVhalConfigParser(), customFileList);
1438 
1439         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1440         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
1441                 () -> client.subscribe(options));
1442 
1443         expect.that(thrown.errorCode).isEqualTo(StatusCode.INVALID_ARG);
1444         expect.that(thrown).hasMessageThat().contains("Static property cannot be subscribed.");
1445     }
1446 
1447     @Test
testUnsubscribePropIdNotSupport()1448     public void testUnsubscribePropIdNotSupport() throws Exception {
1449         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1450         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1451                 new FakeVhalConfigParser(), new ArrayList<>());
1452 
1453         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1454         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
1455                 () -> client.unsubscribe(1234));
1456 
1457         expect.that(thrown.errorCode).isEqualTo(StatusCode.INVALID_ARG);
1458         expect.that(thrown).hasMessageThat().contains("The propId: 1234 is not supported");
1459     }
1460 
1461     @Test
testUnsubscribeStaticProp()1462     public void testUnsubscribeStaticProp() throws Exception {
1463         // Create a custom config file.
1464         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_STATIC + "]}";
1465         List<File> customFileList = createFilenameList(jsonString);
1466         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1467         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1468                 new FakeVhalConfigParser(), customFileList);
1469 
1470         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1471         ServiceSpecificException thrown = assertThrows(ServiceSpecificException.class,
1472                 () -> client.unsubscribe(VehicleProperty.INFO_FUEL_TYPE));
1473 
1474         expect.that(thrown.errorCode).isEqualTo(StatusCode.INVALID_ARG);
1475         expect.that(thrown).hasMessageThat().contains("Static property cannot be unsubscribed.");
1476     }
1477 
1478     @Test
testUnsubscribeOnChangeProp()1479     public void testUnsubscribeOnChangeProp() throws Exception {
1480         // Create a custom config file.
1481         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_ON_CHANGE + "]}";
1482         List<File> customFileList = createFilenameList(jsonString);
1483         // Create subscribe options array.
1484         SubscribeOptions option = new SubscribeOptions();
1485         option.propId = VehicleProperty.ENGINE_OIL_LEVEL;
1486         option.sampleRate = 1f;
1487         SubscribeOptions[] options = new SubscribeOptions[]{option};
1488         // Create set value
1489         RawPropValues rawPropValues = new RawPropValues();
1490         rawPropValues.int32Values = new int[]{VehicleOilLevel.LOW};
1491         HalPropValue requestPropValue = buildHalPropValue(
1492                 /* propId= */ VehicleProperty.ENGINE_OIL_LEVEL, /* areaId= */ 0,
1493                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
1494         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1495         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1496                 new FakeVhalConfigParser(), customFileList);
1497         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1498         client.subscribe(options);
1499         fakeVehicleStub.set(requestPropValue);
1500         verify(callback, times(1)).onPropertyEvent(any(ArrayList.class));
1501 
1502         client.unsubscribe(VehicleProperty.ENGINE_OIL_LEVEL);
1503         clearInvocations(callback);
1504         fakeVehicleStub.set(requestPropValue);
1505 
1506         // Because in the FakeVehicleStub.set method, we copy the subscription client set from the
1507         // lock to a local variable and use it outside the lock to call callbacks. After
1508         // unsubscription, there is still slight chance the callback might be used once.
1509         verify(callback, atMost(1)).onPropertyEvent(any(ArrayList.class));
1510     }
1511 
1512     @Test
testUnsubscribeContinuousPropOneClient()1513     public void testUnsubscribeContinuousPropOneClient() throws Exception {
1514         // Create a custom config file.
1515         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_CONTINUOUS + "]}";
1516         List<File> customFileList = createFilenameList(jsonString);
1517         // Create subscribe options
1518         SubscribeOptions option = new SubscribeOptions();
1519         option.propId = VehicleProperty.FUEL_LEVEL;
1520         option.sampleRate = 100f;
1521         SubscribeOptions[] options = new SubscribeOptions[]{option};
1522         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1523         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1524                 new FakeVhalConfigParser(), customFileList);
1525 
1526         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1527         client.subscribe(options);
1528 
1529         verify(callback, timeout(100).atLeast(5)).onPropertyEvent(any(ArrayList.class));
1530 
1531         client.unsubscribe(VehicleProperty.FUEL_LEVEL);
1532         clearInvocations(callback);
1533 
1534         verify(callback, after(200).atMost(1)).onPropertyEvent(any(ArrayList.class));
1535     }
1536 
1537     @Test
testUnsubscribeContinuousPropTwoClient()1538     public void testUnsubscribeContinuousPropTwoClient() throws Exception {
1539         // Create a custom config file.
1540         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_CONTINUOUS + "]}";
1541         List<File> customFileList = createFilenameList(jsonString);
1542         // Create subscribe options
1543         SubscribeOptions option = new SubscribeOptions();
1544         option.propId = VehicleProperty.FUEL_LEVEL;
1545         option.sampleRate = 100f;
1546         SubscribeOptions[] options = new SubscribeOptions[]{option};
1547         VehicleHalCallback callback1 = mock(VehicleHalCallback.class);
1548         VehicleHalCallback callback2 = mock(VehicleHalCallback.class);
1549         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1550                 new FakeVhalConfigParser(), customFileList);
1551 
1552         VehicleStub.SubscriptionClient client1 = fakeVehicleStub.newSubscriptionClient(callback1);
1553         VehicleStub.SubscriptionClient client2 = fakeVehicleStub.newSubscriptionClient(callback2);
1554         try {
1555             client1.subscribe(options);
1556             client2.subscribe(options);
1557 
1558             verify(callback1, timeout(100).atLeast(5)).onPropertyEvent(any(ArrayList.class));
1559             verify(callback2, timeout(100).atLeast(5)).onPropertyEvent(any(ArrayList.class));
1560 
1561             client1.unsubscribe(VehicleProperty.FUEL_LEVEL);
1562             clearInvocations(callback1);
1563             clearInvocations(callback2);
1564 
1565             verify(callback1, after(200).atMost(1)).onPropertyEvent(any(ArrayList.class));
1566             verify(callback2, timeout(100).atLeast(5)).onPropertyEvent(any(ArrayList.class));
1567         } finally {
1568             client2.unsubscribe(VehicleProperty.FUEL_LEVEL);
1569         }
1570     }
1571 
1572     @Test
testUnsubscribeContinuousPropOneClientTwoSubscription()1573     public void testUnsubscribeContinuousPropOneClientTwoSubscription() throws Exception {
1574         // Create a custom config file.
1575         String jsonString = "{\"properties\": [" + PROPERTY_CONFIG_STRING_CONTINUOUS + ", "
1576                 + PROPERTY_CONFIG_STRING_CONTINUOUS_2 + "]}";
1577         List<File> customFileList = createFilenameList(jsonString);
1578         // Create subscribe options
1579         SubscribeOptions option1 = new SubscribeOptions();
1580         option1.propId = VehicleProperty.FUEL_LEVEL;
1581         option1.sampleRate = 100f;
1582         SubscribeOptions option2 = new SubscribeOptions();
1583         option2.propId = VehicleProperty.EV_BATTERY_LEVEL;
1584         option2.sampleRate = 100f;
1585         SubscribeOptions[] options = new SubscribeOptions[]{option1, option2};
1586         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1587         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
1588                 new FakeVhalConfigParser(), customFileList);
1589 
1590         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1591         client.subscribe(options);
1592 
1593         verify(callback, timeout(100).atLeast(10)).onPropertyEvent(any(ArrayList.class));
1594 
1595         client.unsubscribe(VehicleProperty.FUEL_LEVEL);
1596         clearInvocations(callback);
1597 
1598         verify(callback, timeout(100).atLeast(5)).onPropertyEvent(any(ArrayList.class));
1599     }
1600 
1601     @Test
testGetValueForSpecialProp()1602     public void testGetValueForSpecialProp() throws Exception {
1603         // Create a request prop value.
1604         HalPropValue requestPropValue = new HalPropValueBuilder(/* isAidl= */ true)
1605                 .build(VehicleProperty.VHAL_HEARTBEAT, 0);
1606         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
1607                 new FakeVhalConfigParser(), new ArrayList<>());
1608 
1609         fakeVehicleStub.get(requestPropValue);
1610 
1611         verify(mMockRealVehicleStub).get(requestPropValue);
1612     }
1613 
1614     @Test
testSetValueForSpecialProp()1615     public void testSetValueForSpecialProp() throws Exception {
1616         // Create a request prop value.
1617         RawPropValues rawPropValues = new RawPropValues();
1618         rawPropValues.floatValues = new float[]{10};
1619         HalPropValue requestPropValue = buildHalPropValue(
1620                 /* propId= */ VehicleProperty.SWITCH_USER, /* areaId= */ 0,
1621                 SystemClock.elapsedRealtimeNanos(), rawPropValues);
1622         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
1623                 new FakeVhalConfigParser(), new ArrayList<>());
1624 
1625         fakeVehicleStub.set(requestPropValue);
1626 
1627         verify(mMockRealVehicleStub).set(requestPropValue);
1628     }
1629 
1630     @Test
testSubscribeSpecialProp()1631     public void testSubscribeSpecialProp() throws Exception {
1632         // Create subscribe options
1633         SubscribeOptions option = new SubscribeOptions();
1634         option.propId = VehicleProperty.VHAL_HEARTBEAT;
1635         option.sampleRate = 100f;
1636         SubscribeOptions[] options = new SubscribeOptions[]{option};
1637         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1638         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1639                 new FakeVhalConfigParser(), new ArrayList<>());
1640         SubscriptionClient realClient = mock(SubscriptionClient.class);
1641         when(mMockRealVehicleStub.newSubscriptionClient(callback)).thenReturn(realClient);
1642         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1643 
1644         doNothing().when(realClient).subscribe(options);
1645         client.subscribe(options);
1646 
1647         verify(realClient).subscribe(options);
1648     }
1649 
1650     @Test
testUnsubscribeSpecialProp()1651     public void testUnsubscribeSpecialProp() throws Exception {
1652         // Create subscribe options
1653         SubscribeOptions option = new SubscribeOptions();
1654         option.propId = VehicleProperty.VHAL_HEARTBEAT;
1655         option.sampleRate = 100f;
1656         SubscribeOptions[] options = new SubscribeOptions[]{option};
1657         VehicleHalCallback callback = mock(VehicleHalCallback.class);
1658         FakeVehicleStub fakeVehicleStub =  new FakeVehicleStub(mMockRealVehicleStub,
1659                 new FakeVhalConfigParser(), new ArrayList<>());
1660         SubscriptionClient realClient = mock(SubscriptionClient.class);
1661         when(mMockRealVehicleStub.newSubscriptionClient(callback)).thenReturn(realClient);
1662         VehicleStub.SubscriptionClient client = fakeVehicleStub.newSubscriptionClient(callback);
1663 
1664         doNothing().when(realClient).subscribe(options);
1665         client.subscribe(options);
1666 
1667         verify(realClient).subscribe(options);
1668 
1669         doNothing().when(realClient).unsubscribe(VehicleProperty.VHAL_HEARTBEAT);
1670         client.unsubscribe(VehicleProperty.VHAL_HEARTBEAT);
1671 
1672         verify(realClient).unsubscribe(VehicleProperty.VHAL_HEARTBEAT);
1673     }
1674 
1675     @Test
testLinkToDeath()1676     public void testLinkToDeath() throws Exception {
1677         IVehicleDeathRecipient recipient = mock(IVehicleDeathRecipient.class);
1678         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
1679                 new FakeVhalConfigParser(), new ArrayList<>());
1680 
1681         fakeVehicleStub.linkToDeath(recipient);
1682 
1683         verify(mMockRealVehicleStub).linkToDeath(recipient);
1684     }
1685 
1686     @Test
testUnLinkToDeath()1687     public void testUnLinkToDeath() throws Exception {
1688         IVehicleDeathRecipient recipient = mock(IVehicleDeathRecipient.class);
1689         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
1690                 new FakeVhalConfigParser(), new ArrayList<>());
1691 
1692         fakeVehicleStub.unlinkToDeath(recipient);
1693 
1694         verify(mMockRealVehicleStub).unlinkToDeath(recipient);
1695     }
1696 
1697     @Test
testDump()1698     public void testDump() throws Exception {
1699         FileDescriptor fd = mock(FileDescriptor.class);
1700         FakeVehicleStub fakeVehicleStub = new FakeVehicleStub(mMockRealVehicleStub,
1701                 new FakeVhalConfigParser(), new ArrayList<>());
1702 
1703         fakeVehicleStub.dump(fd, new ArrayList<>());
1704 
1705         verify(mMockRealVehicleStub).dump(eq(fd), eq(new ArrayList<>()));
1706     }
1707 
buildHalPropValue(int propId, int areaId, long timestamp, RawPropValues rawPropValues)1708     private HalPropValue buildHalPropValue(int propId, int areaId, long timestamp,
1709             RawPropValues rawPropValues) {
1710         VehiclePropValue propValue = new VehiclePropValue();
1711         propValue.prop = propId;
1712         propValue.areaId = areaId;
1713         propValue.timestamp = timestamp;
1714         propValue.value = rawPropValues;
1715         return new HalPropValueBuilder(/* isAidl= */ true).build(propValue);
1716     }
1717 
createParseResult(int propId, float maxSampleRate, int access, int areaId)1718     private SparseArray<ConfigDeclaration> createParseResult(int propId, float maxSampleRate,
1719             int access, int areaId) {
1720         SparseArray<ConfigDeclaration> parseResult = new SparseArray<>();
1721         ConfigDeclaration propConfig = new ConfigDeclaration(
1722                 createConfig(propId, maxSampleRate, access, areaId), null, new SparseArray<>());
1723         parseResult.put(propId, propConfig);
1724         return parseResult;
1725     }
1726 
getPropConfigByPropId(HalPropConfig[] propConfigs, int propId)1727     private HalPropConfig getPropConfigByPropId(HalPropConfig[] propConfigs, int propId) {
1728         for (int i = 0; i < propConfigs.length; i++) {
1729             if (propConfigs[i].getPropId() == propId) {
1730                 return propConfigs[i];
1731             }
1732         }
1733         return null;
1734     }
1735 
createConfig(int propId, float sampleRate, int access, int areaId)1736     private VehiclePropConfig createConfig(int propId, float sampleRate, int access, int areaId) {
1737         VehiclePropConfig propConfigValue = new VehiclePropConfig();
1738         propConfigValue.prop = propId;
1739         propConfigValue.access = access;
1740         VehicleAreaConfig vehicleAreaConfig = new VehicleAreaConfig();
1741         vehicleAreaConfig.areaId = areaId;
1742         propConfigValue.areaConfigs = new VehicleAreaConfig[]{vehicleAreaConfig};
1743         propConfigValue.maxSampleRate = sampleRate;
1744         return propConfigValue;
1745     }
1746 
createFilenameList(String fileContent)1747     private List<File> createFilenameList(String fileContent) throws Exception {
1748         List<File> customFileList = new ArrayList<>();
1749         File tempFile = File.createTempFile("custom_config_", ".json");
1750         tempFile.deleteOnExit();
1751         try (FileOutputStream os = new FileOutputStream(tempFile)) {
1752             os.write(fileContent.getBytes());
1753             customFileList.add(tempFile);
1754         }
1755         return customFileList;
1756     }
1757 
captureOnGetAsyncResults()1758     private List<GetVehicleStubAsyncResult> captureOnGetAsyncResults() {
1759         verify(mCallback, timeout(DEFAULT_TIMEOUT)).onGetAsyncResults(
1760                 mGetVehicleStubAsyncResultCaptor.capture());
1761         return mGetVehicleStubAsyncResultCaptor.getValue();
1762     }
1763 
captureOnSetAsyncResults()1764     private List<SetVehicleStubAsyncResult> captureOnSetAsyncResults() {
1765         verify(mCallback, timeout(DEFAULT_TIMEOUT)).onSetAsyncResults(
1766                 mSetVehicleStubAsyncResultCaptor.capture());
1767         return mSetVehicleStubAsyncResultCaptor.getValue();
1768     }
1769 }
1770