1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.hal;
18 
19 import static android.car.Car.PERMISSION_VENDOR_EXTENSION;
20 import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_SET;
21 import static android.car.VehiclePropertyIds.PERF_VEHICLE_SPEED;
22 import static android.car.hardware.property.VehicleHalStatusCode.STATUS_INTERNAL_ERROR;
23 import static android.car.hardware.property.VehicleHalStatusCode.STATUS_NOT_AVAILABLE;
24 import static android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_ENGINE;
25 import static android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO;
26 import static android.car.hardware.property.VehicleVendorPermission.PERMISSION_SET_CAR_VENDOR_CATEGORY_ENGINE;
27 import static android.hardware.automotive.vehicle.VehicleProperty.SUPPORT_CUSTOMIZE_VENDOR_PERMISSION;
28 
29 import static com.android.car.internal.property.CarPropertyErrorCodes.STATUS_OK;
30 
31 import static com.google.common.truth.Truth.assertThat;
32 import static com.google.common.truth.Truth.assertWithMessage;
33 
34 import static org.junit.Assert.assertThrows;
35 import static org.mockito.ArgumentMatchers.anyList;
36 import static org.mockito.Mockito.any;
37 import static org.mockito.Mockito.anyInt;
38 import static org.mockito.Mockito.clearInvocations;
39 import static org.mockito.Mockito.doAnswer;
40 import static org.mockito.Mockito.doNothing;
41 import static org.mockito.Mockito.doReturn;
42 import static org.mockito.Mockito.doThrow;
43 import static org.mockito.Mockito.eq;
44 import static org.mockito.Mockito.mock;
45 import static org.mockito.Mockito.never;
46 import static org.mockito.Mockito.reset;
47 import static org.mockito.Mockito.timeout;
48 import static org.mockito.Mockito.times;
49 import static org.mockito.Mockito.verify;
50 import static org.mockito.Mockito.when;
51 
52 import android.car.VehiclePropertyIds;
53 import android.car.hardware.CarPropertyConfig;
54 import android.car.hardware.CarPropertyValue;
55 import android.car.hardware.property.CarPropertyManager;
56 import android.hardware.automotive.vehicle.VehiclePropError;
57 import android.hardware.automotive.vehicle.VehicleProperty;
58 import android.hardware.automotive.vehicle.VehiclePropertyChangeMode;
59 import android.hardware.automotive.vehicle.VehiclePropertyStatus;
60 import android.hardware.automotive.vehicle.VehicleVendorPermission;
61 import android.os.IBinder;
62 import android.os.RemoteException;
63 import android.os.ServiceSpecificException;
64 import android.util.ArraySet;
65 
66 import androidx.test.runner.AndroidJUnit4;
67 
68 import com.android.car.VehicleStub.AsyncGetSetRequest;
69 import com.android.car.VehicleStub.GetVehicleStubAsyncResult;
70 import com.android.car.VehicleStub.SetVehicleStubAsyncResult;
71 import com.android.car.VehicleStub.VehicleStubCallbackInterface;
72 import com.android.car.hal.VehicleHal.HalSubscribeOptions;
73 import com.android.car.hal.property.PropertyHalServiceConfigs;
74 import com.android.car.hal.test.AidlVehiclePropValueBuilder;
75 import com.android.car.internal.property.AsyncPropertyServiceRequest;
76 import com.android.car.internal.property.CarPropertyErrorCodes;
77 import com.android.car.internal.property.CarSubscription;
78 import com.android.car.internal.property.GetSetValueResult;
79 import com.android.car.internal.property.GetSetValueResultList;
80 import com.android.car.internal.property.IAsyncPropertyResultCallback;
81 import com.android.internal.annotations.VisibleForTesting;
82 
83 import org.junit.After;
84 import org.junit.Before;
85 import org.junit.Rule;
86 import org.junit.Test;
87 import org.junit.runner.RunWith;
88 import org.mockito.ArgumentCaptor;
89 import org.mockito.Captor;
90 import org.mockito.Mock;
91 import org.mockito.invocation.InvocationOnMock;
92 import org.mockito.junit.MockitoJUnit;
93 import org.mockito.junit.MockitoRule;
94 import org.mockito.stubbing.Answer;
95 
96 import java.util.ArrayList;
97 import java.util.HashMap;
98 import java.util.List;
99 import java.util.Map;
100 import java.util.Set;
101 
102 @RunWith(AndroidJUnit4.class)
103 public class PropertyHalServiceTest {
104     @Rule
105     public MockitoRule mMockitoRule = MockitoJUnit.rule();
106 
107     @Mock
108     private VehicleHal mVehicleHal;
109 
110     @Mock
111     private IAsyncPropertyResultCallback mGetAsyncPropertyResultCallback;
112     @Mock
113     private IAsyncPropertyResultCallback mSetAsyncPropertyResultCallback;
114     @Mock
115     private IBinder mGetAsyncPropertyResultBinder;
116     @Mock
117     private IBinder mSetAsyncPropertyResultBinder;
118     @Mock
119     private PropertyHalService.PropertyHalListener mPropertyHalListener;
120     @Captor
121     private ArgumentCaptor<GetSetValueResultList> mAsyncResultCaptor;
122     @Captor
123     private ArgumentCaptor<IBinder.DeathRecipient> mDeathRecipientCaptor;
124     @Captor
125     private ArgumentCaptor<List> mListArgumentCaptor;
126     @Mock
127     private CarPropertyConfig mMockCarPropertyConfig1;
128     @Mock
129     private CarPropertyConfig mMockCarPropertyConfig2;
130 
131     private PropertyHalService mPropertyHalService;
132     private static final int REQUEST_ID_1 = 1;
133     private static final int REQUEST_ID_2 = 2;
134     private static final int REQUEST_ID_3 = 3;
135     private static final int REQUEST_ID_4 = 4;
136     private static final int REQUEST_ID_5 = 5;
137     private static final int RECEIVED_REQUEST_ID_1 = 0;
138     private static final int RECEIVED_REQUEST_ID_2 = 1;
139     private static final int RECEIVED_REQUEST_ID_3 = 2;
140     private static final int INT32_PROP = VehiclePropertyIds.INFO_FUEL_DOOR_LOCATION;
141     private static final int PROPERTY_VALUE = 123;
142     private static final int VENDOR_ERROR_CODE = 1234;
143     private static final int SYSTEM_ERROR_CODE = 4321;
144     private static final int VENDOR_PROPERTY_1 = 0x21e01111;
145     private static final int VENDOR_PROPERTY_2 = 0x21e01112;
146     private static final int VENDOR_PROPERTY_3 = 0x21e01113;
147     private static final float SAMPLE_RATE_HZ = 17.0f;
148     private static final AsyncPropertyServiceRequest GET_PROPERTY_SERVICE_REQUEST_1 =
149             new AsyncPropertyServiceRequest(REQUEST_ID_1, HVAC_TEMPERATURE_SET, /* areaId= */ 0);
150     private static final AsyncPropertyServiceRequest GET_PROPERTY_SERVICE_REQUEST_2 =
151             new AsyncPropertyServiceRequest(REQUEST_ID_2, HVAC_TEMPERATURE_SET, /* areaId= */ 0);
152     private static final AsyncPropertyServiceRequest GET_PROPERTY_SERVICE_REQUEST_STATIC_1 =
153             new AsyncPropertyServiceRequest(REQUEST_ID_1, INT32_PROP, /* areaId= */ 0);
154     private static final AsyncPropertyServiceRequest SET_HVAC_REQUEST_ID_1 =
155             new AsyncPropertyServiceRequest(REQUEST_ID_1, HVAC_TEMPERATURE_SET, /* areaId= */ 0,
156                     new CarPropertyValue(HVAC_TEMPERATURE_SET, /* areaId= */ 0, SAMPLE_RATE_HZ));
157     private static final AsyncPropertyServiceRequest SET_SPEED_REQUEST_ID_2 =
158             new AsyncPropertyServiceRequest(REQUEST_ID_2, PERF_VEHICLE_SPEED, /* areaId= */ 0,
159                     new CarPropertyValue(PERF_VEHICLE_SPEED, /* areaId= */ 0, SAMPLE_RATE_HZ));
160     private static final AsyncPropertyServiceRequest SET_SPEED_REQUEST_ID_3 =
161             new AsyncPropertyServiceRequest(REQUEST_ID_3, PERF_VEHICLE_SPEED, /* areaId= */ 0,
162                     new CarPropertyValue(PERF_VEHICLE_SPEED, /* areaId= */ 0, SAMPLE_RATE_HZ));
163     private static final AsyncPropertyServiceRequest SET_VEHICLE_SPEED_AREA_ID_1_REQUEST =
164             new AsyncPropertyServiceRequest(REQUEST_ID_4, PERF_VEHICLE_SPEED, /* areaId= */ 1,
165                     new CarPropertyValue(PERF_VEHICLE_SPEED, /* areaId= */ 1, SAMPLE_RATE_HZ));
166     private static final AsyncPropertyServiceRequest SET_VEHICLE_SPEED_AREA_ID_2_REQUEST =
167             new AsyncPropertyServiceRequest(REQUEST_ID_5, PERF_VEHICLE_SPEED, /* areaId= */ 2,
168                     new CarPropertyValue(PERF_VEHICLE_SPEED, /* areaId= */ 2, SAMPLE_RATE_HZ));
169 
170     private static final long TEST_UPDATE_TIMESTAMP_NANOS = 1234;
171     private final HalPropValueBuilder mPropValueBuilder = new HalPropValueBuilder(
172             /* isAidl= */ true);
173     private final HalPropValue mPropValue = mPropValueBuilder.build(
174             HVAC_TEMPERATURE_SET, /* areaId= */ 0, TEST_UPDATE_TIMESTAMP_NANOS, /* status= */ 0,
175             SAMPLE_RATE_HZ);
176     private final HalPropValue mNonTargetPropValue = mPropValueBuilder.build(
177             HVAC_TEMPERATURE_SET, /* areaId= */ 0, TEST_UPDATE_TIMESTAMP_NANOS, /* status= */ 0,
178             16.0f);
179     private final HalPropValue mPropValue2 = mPropValueBuilder.build(
180             PERF_VEHICLE_SPEED, /* areaId= */ 0, TEST_UPDATE_TIMESTAMP_NANOS, /* status= */ 0,
181             SAMPLE_RATE_HZ);
182     private final HalPropValue mPropValue3 = mPropValueBuilder.build(
183             INT32_PROP, /* areaId= */ 0, TEST_UPDATE_TIMESTAMP_NANOS, /* status= */ 0,
184             3);
185 
copyRequest(AsyncPropertyServiceRequest request)186     private AsyncPropertyServiceRequest copyRequest(AsyncPropertyServiceRequest request) {
187         return new AsyncPropertyServiceRequest(request.getRequestId(), request.getPropertyId(),
188                 request.getAreaId(), request.getCarPropertyValue());
189     }
190 
191     @Before
setUp()192     public void setUp() {
193         when(mVehicleHal.getHalPropValueBuilder()).thenReturn(mPropValueBuilder);
194         mPropertyHalService = new PropertyHalService(mVehicleHal);
195         mPropertyHalService.setPropertyHalServiceConfigs(PropertyHalServiceConfigs.newConfigs());
196         mPropertyHalService.init();
197 
198         HalPropConfig mockPropConfig1 = mock(HalPropConfig.class);
199         when(mockPropConfig1.getPropId()).thenReturn(VehicleProperty.HVAC_TEMPERATURE_SET);
200         when(mockPropConfig1.getChangeMode()).thenReturn(VehiclePropertyChangeMode.ON_CHANGE);
201         when(mockPropConfig1.toCarPropertyConfig(eq(VehicleProperty.HVAC_TEMPERATURE_SET), any()))
202                 .thenReturn(mMockCarPropertyConfig1);
203         when(mMockCarPropertyConfig1.getChangeMode())
204                 .thenReturn(VehiclePropertyChangeMode.ON_CHANGE);
205         when(mMockCarPropertyConfig1.getAreaIds()).thenReturn(new int[]{0});
206 
207         HalPropConfig mockPropConfig2 = mock(HalPropConfig.class);
208         when(mockPropConfig2.getPropId()).thenReturn(VehicleProperty.PERF_VEHICLE_SPEED);
209         when(mockPropConfig2.getChangeMode()).thenReturn(VehiclePropertyChangeMode.CONTINUOUS);
210         when(mockPropConfig2.getMinSampleRate()).thenReturn(20.0f);
211         when(mockPropConfig2.getMaxSampleRate()).thenReturn(100.0f);
212         when(mockPropConfig2.toCarPropertyConfig(eq(VehicleProperty.PERF_VEHICLE_SPEED), any()))
213                 .thenReturn(mMockCarPropertyConfig2);
214         when(mMockCarPropertyConfig2.getChangeMode())
215                 .thenReturn(VehiclePropertyChangeMode.CONTINUOUS);
216         when(mMockCarPropertyConfig2.getMinSampleRate()).thenReturn(20.0f);
217         when(mMockCarPropertyConfig2.getMaxSampleRate()).thenReturn(100.0f);
218         when(mMockCarPropertyConfig2.getAreaIds()).thenReturn(new int[]{0});
219 
220         HalPropConfig mockPropConfig3 = mock(HalPropConfig.class);
221         when(mockPropConfig3.getChangeMode()).thenReturn(VehiclePropertyChangeMode.STATIC);
222         when(mockPropConfig3.getPropId()).thenReturn(INT32_PROP);
223 
224         mPropertyHalService.takeProperties(List.of(mockPropConfig1, mockPropConfig2,
225                 mockPropConfig3));
226         mPropertyHalService.getPropertyList();
227     }
228 
229     @After
tearDown()230     public void tearDown() {
231         mPropertyHalService.release();
232         mPropertyHalService = null;
233     }
234 
verifyNoPendingRequest()235     private void verifyNoPendingRequest() {
236         assertWithMessage("No pending async requests after test finish")
237                 .that(mPropertyHalService.countPendingAsyncRequests()).isEqualTo(0);
238         assertWithMessage("No pending async set request waiting for update after test finish")
239                 .that(mPropertyHalService.countHalPropIdToWaitForUpdateRequests()).isEqualTo(0);
240     }
241 
deliverResult(InvocationOnMock invocation, Integer expectedServiceRequestId, int errorCode, HalPropValue propValue, boolean get)242     private Object deliverResult(InvocationOnMock invocation, Integer expectedServiceRequestId,
243             int errorCode, HalPropValue propValue, boolean get) {
244         CarPropertyErrorCodes carPropertyErrorCodes =
245                 new CarPropertyErrorCodes(
246                         errorCode,
247                         /* vendorErrorCode= */ 0,
248                         /* systemErrorCode= */ 0);
249         return deliverResult(
250                 invocation, expectedServiceRequestId, carPropertyErrorCodes, propValue, get);
251     }
252 
deliverResult(InvocationOnMock invocation, Integer expectedServiceRequestId, CarPropertyErrorCodes carPropertyErrorCodes, HalPropValue propValue, boolean get)253     private Object deliverResult(InvocationOnMock invocation, Integer expectedServiceRequestId,
254             CarPropertyErrorCodes carPropertyErrorCodes, HalPropValue propValue, boolean get) {
255         Object[] args = invocation.getArguments();
256         List getVehicleHalRequests = (List) args[0];
257         Map<VehicleStubCallbackInterface, List<GetVehicleStubAsyncResult>> callbackToGetResults =
258                 new HashMap<>();
259         Map<VehicleStubCallbackInterface, List<SetVehicleStubAsyncResult>> callbackToSetResults =
260                 new HashMap<>();
261         for (int i = 0; i < getVehicleHalRequests.size(); i++) {
262             AsyncGetSetRequest getVehicleHalRequest = (AsyncGetSetRequest)
263                     getVehicleHalRequests.get(i);
264             VehicleStubCallbackInterface callback = (VehicleStubCallbackInterface) args[1];
265             if (callbackToGetResults.get(callback) == null) {
266                 callbackToGetResults.put(callback, new ArrayList<>());
267             }
268             if (callbackToSetResults.get(callback) == null) {
269                 callbackToSetResults.put(callback, new ArrayList<>());
270             }
271 
272             int serviceRequestId = getVehicleHalRequest.getServiceRequestId();
273             if (expectedServiceRequestId != null) {
274                 assertThat(serviceRequestId).isEqualTo(expectedServiceRequestId);
275             }
276 
277             if (get) {
278                 if (propValue != null) {
279                     callbackToGetResults.get(callback).add(new GetVehicleStubAsyncResult(
280                             serviceRequestId, propValue));
281                 } else {
282                     callbackToGetResults.get(callback).add(new GetVehicleStubAsyncResult(
283                             serviceRequestId, carPropertyErrorCodes));
284                 }
285             } else {
286                 callbackToSetResults.get(callback).add(new SetVehicleStubAsyncResult(
287                         serviceRequestId, carPropertyErrorCodes));
288             }
289         }
290 
291         for (VehicleStubCallbackInterface callback : callbackToGetResults.keySet()) {
292             callback.onGetAsyncResults(callbackToGetResults.get(callback));
293         }
294         for (VehicleStubCallbackInterface callback : callbackToSetResults.keySet()) {
295             callback.onSetAsyncResults(callbackToSetResults.get(callback));
296         }
297 
298         return null;
299     }
300 
deliverOkayGetResult(InvocationOnMock invocation)301     private Object deliverOkayGetResult(InvocationOnMock invocation) {
302         return deliverOkayGetResult(invocation, mPropValue);
303     }
304 
deliverOkayGetResult(InvocationOnMock invocation, HalPropValue propValue)305     private Object deliverOkayGetResult(InvocationOnMock invocation, HalPropValue propValue) {
306         return deliverResult(invocation, /* expectedServiceRequestId= */ null,
307                 STATUS_OK, propValue, true);
308     }
309 
deliverOkayGetResult(InvocationOnMock invocation, Integer expectedServiceRequestId)310     private Object deliverOkayGetResult(InvocationOnMock invocation,
311             Integer expectedServiceRequestId) {
312         return deliverResult(invocation, expectedServiceRequestId, STATUS_OK,
313                 mPropValue, true);
314     }
315 
deliverOkaySetResult(InvocationOnMock invocation)316     private Object deliverOkaySetResult(InvocationOnMock invocation) {
317         return deliverOkaySetResult(invocation, /* expectedServiceRequestId= */ null);
318     }
319 
deliverOkaySetResult(InvocationOnMock invocation, Integer expectedServiceRequestId)320     private Object deliverOkaySetResult(InvocationOnMock invocation,
321             Integer expectedServiceRequestId) {
322         return deliverResult(invocation, expectedServiceRequestId, STATUS_OK,
323                 null, false);
324     }
325 
deliverTryAgainGetResult(InvocationOnMock invocation)326     private Object deliverTryAgainGetResult(InvocationOnMock invocation) {
327         return deliverTryAgainGetResult(invocation, /* expectedServiceRequestId= */ null);
328     }
329 
deliverTryAgainGetResult(InvocationOnMock invocation, Integer expectedServiceRequestId)330     private Object deliverTryAgainGetResult(InvocationOnMock invocation,
331             Integer expectedServiceRequestId) {
332         return deliverResult(invocation, expectedServiceRequestId,
333                 CarPropertyErrorCodes.STATUS_TRY_AGAIN, null, true);
334     }
335 
deliverTryAgainSetResult(InvocationOnMock invocation)336     private Object deliverTryAgainSetResult(InvocationOnMock invocation) {
337         return deliverTryAgainSetResult(invocation, /* expectedServiceRequestId= */ null);
338     }
339 
deliverTryAgainSetResult(InvocationOnMock invocation, Integer expectedServiceRequestId)340     private Object deliverTryAgainSetResult(InvocationOnMock invocation,
341             Integer expectedServiceRequestId) {
342         return deliverResult(invocation, expectedServiceRequestId,
343                 CarPropertyErrorCodes.STATUS_TRY_AGAIN, null, false);
344     }
345 
deliverErrorGetResult(InvocationOnMock invocation, int errorCode)346     private Object deliverErrorGetResult(InvocationOnMock invocation, int errorCode) {
347         return deliverErrorGetResult(invocation, /* expectedServiceRequestId= */ null, errorCode);
348     }
349 
deliverErrorGetResult(InvocationOnMock invocation, Integer expectedServiceRequestId, int errorCode)350     private Object deliverErrorGetResult(InvocationOnMock invocation,
351             Integer expectedServiceRequestId, int errorCode) {
352         return deliverResult(invocation, expectedServiceRequestId, errorCode, null, true);
353     }
354 
deliverErrorSetResult(InvocationOnMock invocation, int errorCode)355     private Object deliverErrorSetResult(InvocationOnMock invocation, int errorCode) {
356         return deliverErrorSetResult(invocation, /* expectedServiceRequestId= */ null, errorCode);
357     }
358 
deliverErrorSetResult(InvocationOnMock invocation, Integer expectedServiceRequestId, int errorCode)359     private Object deliverErrorSetResult(InvocationOnMock invocation,
360             Integer expectedServiceRequestId, int errorCode) {
361         return deliverResult(invocation, expectedServiceRequestId, errorCode, null, false);
362     }
363 
364     @Test
testGetCarPropertyValuesAsync()365     public void testGetCarPropertyValuesAsync() {
366         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
367 
368         mPropertyHalService.getCarPropertyValuesAsync(List.of(GET_PROPERTY_SERVICE_REQUEST_1),
369                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
370                 /* asyncRequestStartTime= */ 0);
371 
372         ArgumentCaptor<List<AsyncGetSetRequest>> captor =
373                 ArgumentCaptor.forClass(List.class);
374         verify(mVehicleHal).getAsync(captor.capture(),
375                 any(VehicleStubCallbackInterface.class));
376         AsyncGetSetRequest gotRequest = captor.getValue().get(0);
377         assertThat(gotRequest.getServiceRequestId()).isEqualTo(RECEIVED_REQUEST_ID_1);
378         assertThat(gotRequest.getHalPropValue().getPropId()).isEqualTo(HVAC_TEMPERATURE_SET);
379         assertThat(gotRequest.getTimeoutUptimeMs()).isGreaterThan(1000);
380 
381         mPropertyHalService.cancelRequests(new int[]{REQUEST_ID_1});
382         verify(mVehicleHal, timeout(1000)).cancelRequests(List.of(0));
383 
384         verifyNoPendingRequest();
385     }
386 
387     @Test
testGetCarPropertyValuesAsync_multipleRequests()388     public void testGetCarPropertyValuesAsync_multipleRequests() {
389         List<AsyncPropertyServiceRequest> getPropertyServiceRequests = new ArrayList<>();
390         getPropertyServiceRequests.add(GET_PROPERTY_SERVICE_REQUEST_1);
391         getPropertyServiceRequests.add(GET_PROPERTY_SERVICE_REQUEST_2);
392         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
393 
394         mPropertyHalService.getCarPropertyValuesAsync(getPropertyServiceRequests,
395                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
396                 /* asyncRequestStartTime= */ 0);
397 
398         ArgumentCaptor<List<AsyncGetSetRequest>> captor =
399                 ArgumentCaptor.forClass(List.class);
400         verify(mVehicleHal).getAsync(captor.capture(),
401                 any(VehicleStubCallbackInterface.class));
402         AsyncGetSetRequest gotRequest0 = captor.getValue().get(0);
403         assertThat(gotRequest0.getServiceRequestId()).isEqualTo(RECEIVED_REQUEST_ID_1);
404         assertThat(gotRequest0.getHalPropValue().getPropId()).isEqualTo(
405                 HVAC_TEMPERATURE_SET);
406         assertThat(gotRequest0.getHalPropValue().getAreaId()).isEqualTo(0);
407         assertThat(gotRequest0.getTimeoutUptimeMs()).isGreaterThan(1000);
408         AsyncGetSetRequest gotRequest1 = captor.getValue().get(1);
409         assertThat(gotRequest1.getServiceRequestId()).isEqualTo(RECEIVED_REQUEST_ID_2);
410         assertThat(gotRequest1.getHalPropValue().getPropId()).isEqualTo(
411                 HVAC_TEMPERATURE_SET);
412         assertThat(gotRequest1.getHalPropValue().getAreaId()).isEqualTo(0);
413         assertThat(gotRequest1.getTimeoutUptimeMs()).isGreaterThan(1000);
414 
415         mPropertyHalService.cancelRequests(new int[]{REQUEST_ID_1, REQUEST_ID_2});
416 
417         verifyNoPendingRequest();
418     }
419 
420     @Test
testGetCarPropertyValuesAsync_staticCacheMultipleRequests()421     public void testGetCarPropertyValuesAsync_staticCacheMultipleRequests() throws Exception {
422         doAnswer((invocation) -> {
423             return deliverOkayGetResult(invocation, mPropValue3);
424         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
425 
426         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
427 
428         mPropertyHalService.getCarPropertyValuesAsync(
429                 List.of(GET_PROPERTY_SERVICE_REQUEST_STATIC_1), mGetAsyncPropertyResultCallback,
430                 /* timeoutInMs= */ 1000, /* asyncRequestStartTime= */ 0);
431 
432         verify(mGetAsyncPropertyResultCallback, timeout(1000)).onGetValueResults(
433                 mAsyncResultCaptor.capture());
434         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
435         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
436         assertThat(result.getCarPropertyValue().getValue()).isEqualTo(3);
437         assertThat(result.getCarPropertyValue().getAreaId()).isEqualTo(0);
438         reset(mVehicleHal);
439         reset(mGetAsyncPropertyResultCallback);
440         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
441 
442         mPropertyHalService.getCarPropertyValuesAsync(
443                 List.of(GET_PROPERTY_SERVICE_REQUEST_STATIC_1), mGetAsyncPropertyResultCallback,
444                 /* timeoutInMs= */ 1000, /* asyncRequestStartTime= */ 0);
445         verify(mVehicleHal, never()).getAsync(any(), any());
446         verify(mGetAsyncPropertyResultCallback, timeout(1000)).onGetValueResults(
447                 mAsyncResultCaptor.capture());
448         GetSetValueResult cachedResult = mAsyncResultCaptor.getValue().getList().get(0);
449         assertThat(cachedResult.getRequestId()).isEqualTo(REQUEST_ID_1);
450         assertThat(cachedResult.getCarPropertyValue().getValue()).isEqualTo(3);
451         assertThat(cachedResult.getCarPropertyValue().getAreaId()).isEqualTo(0);
452     }
453 
454     @Test
testGetCarPropertyValuesAsync_linkToDeath()455     public void testGetCarPropertyValuesAsync_linkToDeath() throws RemoteException {
456         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
457         List<AsyncPropertyServiceRequest> getPropertyServiceRequests = mock(List.class);
458         mPropertyHalService.getCarPropertyValuesAsync(getPropertyServiceRequests,
459                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
460                 /* asyncRequestStartTime= */ 0);
461         verify(mGetAsyncPropertyResultBinder).linkToDeath(any(IBinder.DeathRecipient.class),
462                 anyInt());
463 
464         mPropertyHalService.cancelRequests(new int[]{REQUEST_ID_1});
465 
466         verifyNoPendingRequest();
467     }
468 
469     @Test
testGetCarPropertyValuesAsync_binderDiedBeforeLinkToDeath()470     public void testGetCarPropertyValuesAsync_binderDiedBeforeLinkToDeath() throws RemoteException {
471         IBinder mockBinder = mock(IBinder.class);
472         when(mGetAsyncPropertyResultCallback.asBinder()).thenReturn(mockBinder);
473         doThrow(new RemoteException()).when(mockBinder).linkToDeath(any(), anyInt());
474         List<AsyncPropertyServiceRequest> getPropertyServiceRequests = mock(List.class);
475 
476         assertThrows(IllegalStateException.class, () -> {
477             mPropertyHalService.getCarPropertyValuesAsync(getPropertyServiceRequests,
478                     mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
479                     /* asyncRequestStartTime= */ 0);
480         });
481 
482         verifyNoPendingRequest();
483     }
484 
485     @Test
testGetCarPropertyValuesAsync_unlinkToDeath_onBinderDied()486     public void testGetCarPropertyValuesAsync_unlinkToDeath_onBinderDied() throws RemoteException {
487         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
488         List<AsyncPropertyServiceRequest> getPropertyServiceRequests = mock(List.class);
489         mPropertyHalService.getCarPropertyValuesAsync(getPropertyServiceRequests,
490                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
491                 /* asyncRequestStartTime= */ 0);
492 
493         ArgumentCaptor<IBinder.DeathRecipient> recipientCaptor = ArgumentCaptor.forClass(
494                 IBinder.DeathRecipient.class);
495         verify(mGetAsyncPropertyResultBinder).linkToDeath(recipientCaptor.capture(), eq(0));
496 
497         recipientCaptor.getValue().binderDied();
498 
499         verify(mGetAsyncPropertyResultBinder).unlinkToDeath(recipientCaptor.getValue(), 0);
500 
501         verifyNoPendingRequest();
502     }
503 
504     @Test
testGetCarPropertyValuesAsync_normalResult()505     public void testGetCarPropertyValuesAsync_normalResult() throws RemoteException {
506         doAnswer((invocation) -> {
507             return deliverOkayGetResult(invocation, RECEIVED_REQUEST_ID_1);
508         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
509 
510         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
511 
512         mPropertyHalService.getCarPropertyValuesAsync(List.of(GET_PROPERTY_SERVICE_REQUEST_1),
513                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
514                 /* asyncRequestStartTime= */ 0);
515 
516 
517         verify(mGetAsyncPropertyResultCallback, timeout(1000)).onGetValueResults(
518                 mAsyncResultCaptor.capture());
519         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
520         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
521         assertThat(result.getCarPropertyValue().getValue()).isEqualTo(SAMPLE_RATE_HZ);
522         assertThat(result.getCarPropertyValue().getAreaId()).isEqualTo(0);
523 
524         verifyNoPendingRequest();
525     }
526 
527     @Test
testGetCarPropertyValuesAsync_retryTwiceAndSucceed()528     public void testGetCarPropertyValuesAsync_retryTwiceAndSucceed() throws RemoteException {
529         doAnswer(new Answer() {
530             private int mCount = 0;
531 
532             public Object answer(InvocationOnMock invocation) {
533                 Object[] args = invocation.getArguments();
534                 List<AsyncGetSetRequest> getVehicleHalRequests = (List<AsyncGetSetRequest>) args[0];
535                 assertThat(getVehicleHalRequests.size()).isEqualTo(1);
536 
537                 switch (mCount++) {
538                     case 0:
539                         return deliverTryAgainGetResult(invocation, RECEIVED_REQUEST_ID_1);
540                     case 1:
541                         return deliverTryAgainGetResult(invocation, RECEIVED_REQUEST_ID_2);
542                     case 2:
543                         return deliverOkayGetResult(invocation, RECEIVED_REQUEST_ID_3);
544                     default:
545                         throw new IllegalStateException("Only expect 3 calls");
546                 }
547             }
548         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
549 
550         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
551 
552         mPropertyHalService.getCarPropertyValuesAsync(List.of(GET_PROPERTY_SERVICE_REQUEST_1),
553                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
554                 /* asyncRequestStartTime= */ 0);
555 
556         verify(mGetAsyncPropertyResultCallback, timeout(1000)).onGetValueResults(
557                 mAsyncResultCaptor.capture());
558         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
559         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
560         assertThat(result.getCarPropertyValue().getValue()).isEqualTo(SAMPLE_RATE_HZ);
561         assertThat(result.getCarPropertyValue().getAreaId()).isEqualTo(0);
562 
563         verifyNoPendingRequest();
564     }
565 
566     @Test
testGetCarPropertyValuesAsync_retryAndTimeout()567     public void testGetCarPropertyValuesAsync_retryAndTimeout() throws RemoteException {
568         doAnswer((invocation) -> {
569             // For every request, we return retry result.
570             return deliverTryAgainGetResult(invocation);
571         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
572 
573         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
574 
575         mPropertyHalService.getCarPropertyValuesAsync(List.of(GET_PROPERTY_SERVICE_REQUEST_1),
576                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 10,
577                 /* asyncRequestStartTime= */ 0);
578 
579         verify(mGetAsyncPropertyResultCallback, timeout(1000)).onGetValueResults(
580                 mAsyncResultCaptor.capture());
581         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
582         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
583         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode()).isEqualTo(
584                 CarPropertyManager.STATUS_ERROR_TIMEOUT);
585 
586         verifyNoPendingRequest();
587     }
588 
589     @Test
testGetCarPropertyValuesAsync_noResultTimeout()590     public void testGetCarPropertyValuesAsync_noResultTimeout() throws RemoteException {
591         doNothing().when(mVehicleHal).getAsync(anyList(),
592                 any(VehicleStubCallbackInterface.class));
593         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
594 
595         mPropertyHalService.getCarPropertyValuesAsync(List.of(GET_PROPERTY_SERVICE_REQUEST_1),
596                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 10,
597                 /* asyncRequestStartTime= */ 0);
598 
599         verify(mGetAsyncPropertyResultCallback, timeout(1000)).onGetValueResults(
600                 mAsyncResultCaptor.capture());
601         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
602         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
603         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode()).isEqualTo(
604                 CarPropertyManager.STATUS_ERROR_TIMEOUT);
605 
606         verifyNoPendingRequest();
607     }
608 
609     @Test
testGetCarPropertyValuesAsync_timeoutFromVehicleStub()610     public void testGetCarPropertyValuesAsync_timeoutFromVehicleStub() throws RemoteException {
611         doAnswer((invocation) -> {
612             Object[] args = invocation.getArguments();
613             List getVehicleHalRequests = (List) args[0];
614             AsyncGetSetRequest getVehicleHalRequest =
615                     (AsyncGetSetRequest) getVehicleHalRequests.get(0);
616             VehicleStubCallbackInterface getVehicleStubAsyncCallback =
617                     (VehicleStubCallbackInterface) args[1];
618             // Simulate the request has already timed-out.
619             getVehicleStubAsyncCallback.onRequestsTimeout(List.of(
620                     getVehicleHalRequest.getServiceRequestId()));
621             return null;
622         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
623 
624         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
625 
626         mPropertyHalService.getCarPropertyValuesAsync(List.of(GET_PROPERTY_SERVICE_REQUEST_1),
627                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
628                 /* asyncRequestStartTime= */ 0);
629 
630         verify(mGetAsyncPropertyResultCallback, timeout(1000)).onGetValueResults(
631                 mAsyncResultCaptor.capture());
632         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
633         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
634         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode()).isEqualTo(
635                 CarPropertyManager.STATUS_ERROR_TIMEOUT);
636 
637         verifyNoPendingRequest();
638     }
639 
640     @Test
testGetCarPropertyValuesAsync_errorResult()641     public void testGetCarPropertyValuesAsync_errorResult() throws RemoteException {
642         doAnswer((invocation) -> {
643             return deliverErrorGetResult(invocation, RECEIVED_REQUEST_ID_1,
644                     CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
645         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
646 
647         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
648 
649         mPropertyHalService.getCarPropertyValuesAsync(List.of(GET_PROPERTY_SERVICE_REQUEST_1),
650                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
651                 /* asyncRequestStartTime= */ 0);
652 
653         verify(mGetAsyncPropertyResultCallback, timeout(1000)).onGetValueResults(
654                 mAsyncResultCaptor.capture());
655         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
656         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
657         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode()).isEqualTo(
658                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
659         assertThat(result.getCarPropertyValue()).isEqualTo(null);
660 
661         verifyNoPendingRequest();
662     }
663 
664     @Test
testGetCarPropertyValuesAsync_errorResultVendorErrorCode()665     public void testGetCarPropertyValuesAsync_errorResultVendorErrorCode() throws RemoteException {
666         doAnswer((invocation) -> {
667             CarPropertyErrorCodes errorCodes = new CarPropertyErrorCodes(
668                     CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR,
669                     VENDOR_ERROR_CODE,
670                     SYSTEM_ERROR_CODE);
671             return deliverResult(invocation, RECEIVED_REQUEST_ID_1, errorCodes,
672                     /* propValue= */ null, /* get= */ true);
673         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
674 
675         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
676 
677         mPropertyHalService.getCarPropertyValuesAsync(List.of(GET_PROPERTY_SERVICE_REQUEST_1),
678                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
679                 /* asyncRequestStartTime= */ 0);
680 
681         verify(mGetAsyncPropertyResultCallback, timeout(1000)).onGetValueResults(
682                 mAsyncResultCaptor.capture());
683         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
684         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
685         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode()).isEqualTo(
686                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
687         assertThat(result.getCarPropertyErrorCodes().getVendorErrorCode())
688                 .isEqualTo(VENDOR_ERROR_CODE);
689         assertThat(result.getCarPropertyErrorCodes().getSystemErrorCode()).isEqualTo(
690                 SYSTEM_ERROR_CODE);
691         assertThat(result.getCarPropertyValue()).isEqualTo(null);
692 
693         verifyNoPendingRequest();
694     }
695 
696     @Test
testGetCarPropertyValuesAsync_propStatusUnavailable()697     public void testGetCarPropertyValuesAsync_propStatusUnavailable() throws RemoteException {
698         doAnswer((invocation) -> {
699             HalPropValue propValue = mPropValueBuilder.build(
700                     HVAC_TEMPERATURE_SET, /* areaId= */ 0, TEST_UPDATE_TIMESTAMP_NANOS,
701                     VehiclePropertyStatus.UNAVAILABLE, SAMPLE_RATE_HZ);
702             return deliverOkayGetResult(invocation, propValue);
703         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
704 
705         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
706 
707         mPropertyHalService.getCarPropertyValuesAsync(List.of(GET_PROPERTY_SERVICE_REQUEST_1),
708                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
709                 /* asyncRequestStartTime= */ 0);
710 
711         verify(mGetAsyncPropertyResultCallback, timeout(1000)).onGetValueResults(
712                 mAsyncResultCaptor.capture());
713         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
714         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
715         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode()).isEqualTo(
716                 CarPropertyManager.STATUS_ERROR_NOT_AVAILABLE);
717         assertThat(result.getCarPropertyErrorCodes().getVendorErrorCode()).isEqualTo(0);
718         assertThat(result.getCarPropertyErrorCodes().getSystemErrorCode()).isEqualTo(0);
719         assertThat(result.getCarPropertyValue()).isEqualTo(null);
720 
721         verifyNoPendingRequest();
722     }
723 
724     @Test
testGetCarPropertyValuesAsync_propStatusError()725     public void testGetCarPropertyValuesAsync_propStatusError() throws RemoteException {
726         doAnswer((invocation) -> {
727             HalPropValue propValue = mPropValueBuilder.build(
728                     HVAC_TEMPERATURE_SET, /* areaId= */ 0, TEST_UPDATE_TIMESTAMP_NANOS,
729                     VehiclePropertyStatus.ERROR, SAMPLE_RATE_HZ);
730             return deliverOkayGetResult(invocation, propValue);
731         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
732 
733         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
734 
735         mPropertyHalService.getCarPropertyValuesAsync(List.of(GET_PROPERTY_SERVICE_REQUEST_1),
736                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
737                 /* asyncRequestStartTime= */ 0);
738 
739         verify(mGetAsyncPropertyResultCallback, timeout(1000)).onGetValueResults(
740                 mAsyncResultCaptor.capture());
741         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
742         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
743         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode()).isEqualTo(
744                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
745         assertThat(result.getCarPropertyErrorCodes().getVendorErrorCode()).isEqualTo(0);
746         assertThat(result.getCarPropertyErrorCodes().getSystemErrorCode()).isEqualTo(0);
747         assertThat(result.getCarPropertyValue()).isEqualTo(null);
748 
749         verifyNoPendingRequest();
750     }
751 
752     @Test
testGetCarPropertyValuesAsync_multipleResultsSameCall()753     public void testGetCarPropertyValuesAsync_multipleResultsSameCall() throws RemoteException {
754         doAnswer((invocation) -> {
755             Object[] args = invocation.getArguments();
756             List<AsyncGetSetRequest> getVehicleHalRequests = (List) args[0];
757             VehicleStubCallbackInterface getVehicleStubAsyncCallback =
758                     (VehicleStubCallbackInterface) args[1];
759 
760             assertThat(getVehicleHalRequests.size()).isEqualTo(2);
761             assertThat(getVehicleHalRequests.get(0).getServiceRequestId()).isEqualTo(
762                     RECEIVED_REQUEST_ID_1);
763             assertThat(getVehicleHalRequests.get(0).getHalPropValue().getPropId()).isEqualTo(
764                     HVAC_TEMPERATURE_SET);
765             assertThat(getVehicleHalRequests.get(1).getServiceRequestId()).isEqualTo(
766                     RECEIVED_REQUEST_ID_2);
767             assertThat(getVehicleHalRequests.get(1).getHalPropValue().getPropId()).isEqualTo(
768                     HVAC_TEMPERATURE_SET);
769 
770             List<GetVehicleStubAsyncResult> getVehicleStubAsyncResults =
771                     new ArrayList<>();
772             getVehicleStubAsyncResults.add(
773                     new GetVehicleStubAsyncResult(RECEIVED_REQUEST_ID_1, mPropValue));
774             getVehicleStubAsyncResults.add(
775                     new GetVehicleStubAsyncResult(RECEIVED_REQUEST_ID_2, mPropValue));
776             getVehicleStubAsyncCallback.onGetAsyncResults(getVehicleStubAsyncResults);
777             return null;
778         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
779 
780         List<AsyncPropertyServiceRequest> getPropertyServiceRequests = new ArrayList<>();
781         getPropertyServiceRequests.add(GET_PROPERTY_SERVICE_REQUEST_1);
782         getPropertyServiceRequests.add(GET_PROPERTY_SERVICE_REQUEST_2);
783         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
784 
785         mPropertyHalService.getCarPropertyValuesAsync(getPropertyServiceRequests,
786                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
787                 /* asyncRequestStartTime= */ 0);
788 
789         verify(mGetAsyncPropertyResultCallback, timeout(1000)).onGetValueResults(
790                 mAsyncResultCaptor.capture());
791         GetSetValueResult result1 = mAsyncResultCaptor.getValue().getList().get(0);
792         GetSetValueResult result2 = mAsyncResultCaptor.getValue().getList().get(1);
793         assertThat(result1.getRequestId()).isEqualTo(REQUEST_ID_1);
794         assertThat(result1.getCarPropertyValue().getValue()).isEqualTo(SAMPLE_RATE_HZ);
795         assertThat(result2.getRequestId()).isEqualTo(REQUEST_ID_2);
796         assertThat(result2.getCarPropertyValue().getValue()).isEqualTo(SAMPLE_RATE_HZ);
797 
798         verifyNoPendingRequest();
799     }
800 
801     @Test
testGetCarPropertyValuesAsync_multipleResultsDifferentCalls()802     public void testGetCarPropertyValuesAsync_multipleResultsDifferentCalls()
803             throws RemoteException {
804         doAnswer((invocation) -> {
805             Object[] args = invocation.getArguments();
806             List<AsyncGetSetRequest> getVehicleHalRequests = (List) args[0];
807             VehicleStubCallbackInterface getVehicleStubAsyncCallback =
808                     (VehicleStubCallbackInterface) args[1];
809 
810             assertThat(getVehicleHalRequests.size()).isEqualTo(2);
811             assertThat(getVehicleHalRequests.get(0).getServiceRequestId()).isEqualTo(
812                     RECEIVED_REQUEST_ID_1);
813             assertThat(getVehicleHalRequests.get(0).getHalPropValue().getPropId()).isEqualTo(
814                     HVAC_TEMPERATURE_SET);
815             assertThat(getVehicleHalRequests.get(1).getServiceRequestId()).isEqualTo(
816                     RECEIVED_REQUEST_ID_2);
817             assertThat(getVehicleHalRequests.get(1).getHalPropValue().getPropId()).isEqualTo(
818                     HVAC_TEMPERATURE_SET);
819 
820             getVehicleStubAsyncCallback.onGetAsyncResults(
821                     List.of(new GetVehicleStubAsyncResult(RECEIVED_REQUEST_ID_1,
822                             mPropValue)));
823             getVehicleStubAsyncCallback.onGetAsyncResults(
824                     List.of(new GetVehicleStubAsyncResult(RECEIVED_REQUEST_ID_2,
825                             mPropValue)));
826             return null;
827         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
828 
829         List<AsyncPropertyServiceRequest> getPropertyServiceRequests = new ArrayList<>();
830         getPropertyServiceRequests.add(GET_PROPERTY_SERVICE_REQUEST_1);
831         getPropertyServiceRequests.add(GET_PROPERTY_SERVICE_REQUEST_2);
832         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
833 
834         mPropertyHalService.getCarPropertyValuesAsync(getPropertyServiceRequests,
835                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
836                 /* asyncRequestStartTime= */ 0);
837 
838         verify(mGetAsyncPropertyResultCallback, timeout(1000).times(2))
839                 .onGetValueResults(mAsyncResultCaptor.capture());
840         List<GetSetValueResultList> getValuesResults = mAsyncResultCaptor.getAllValues();
841         assertThat(getValuesResults.get(0).getList().get(0).getRequestId()).isEqualTo(REQUEST_ID_1);
842         assertThat(getValuesResults.get(0).getList().get(0).getCarPropertyValue().getValue())
843                 .isEqualTo(SAMPLE_RATE_HZ);
844         assertThat(getValuesResults.get(1).getList().get(0).getRequestId()).isEqualTo(REQUEST_ID_2);
845         assertThat(getValuesResults.get(1).getList().get(0).getCarPropertyValue().getValue())
846                 .isEqualTo(SAMPLE_RATE_HZ);
847 
848         verifyNoPendingRequest();
849     }
850 
851     @Test
testSetCarPropertyValuesAsync()852     public void testSetCarPropertyValuesAsync() {
853         Set<Integer> vhalCancelledRequestIds = new ArraySet<>();
854         doAnswer((invocation) -> {
855             List<Integer> ids = (List<Integer>) invocation.getArgument(0);
856             for (int i = 0; i < ids.size(); i++) {
857                 vhalCancelledRequestIds.add(ids.get(i));
858             }
859             return null;
860         }).when(mVehicleHal).cancelRequests(any());
861         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
862 
863         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_HVAC_REQUEST_ID_1),
864                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
865                 /* asyncRequestStartTime= */ 0);
866 
867         ArgumentCaptor<List<AsyncGetSetRequest>> captor =
868                 ArgumentCaptor.forClass(List.class);
869         verify(mVehicleHal).setAsync(captor.capture(), any(VehicleStubCallbackInterface.class));
870         AsyncGetSetRequest gotRequest = captor.getValue().get(0);
871         assertThat(gotRequest.getServiceRequestId()).isEqualTo(RECEIVED_REQUEST_ID_1);
872         assertThat(gotRequest.getHalPropValue().getPropId()).isEqualTo(HVAC_TEMPERATURE_SET);
873         assertThat(gotRequest.getHalPropValue().getFloatValue(0)).isEqualTo(SAMPLE_RATE_HZ);
874         assertThat(gotRequest.getTimeoutUptimeMs()).isGreaterThan(1000);
875 
876         mPropertyHalService.cancelRequests(new int[]{REQUEST_ID_1});
877 
878         // Because we cancel the onging async set property request, the ongoing get initial value
879         // request should be cancelled as well.
880         verify(mVehicleHal, timeout(1000).times(2)).cancelRequests(any());
881         assertThat(vhalCancelledRequestIds).containsExactlyElementsIn(new Integer[]{0, 1});
882 
883         verifyNoPendingRequest();
884     }
885 
886     @Test
testSetCarPropertyValuesAsync_configNotFound()887     public void testSetCarPropertyValuesAsync_configNotFound() {
888         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
889         AsyncPropertyServiceRequest request = new AsyncPropertyServiceRequest(
890                 REQUEST_ID_1, /* propertyId= */ 1, /* areaId= */ 0,
891                 new CarPropertyValue(/* propertyId= */ 1, /* areaId= */ 0, SAMPLE_RATE_HZ));
892 
893         assertThrows(IllegalArgumentException.class, () -> {
894             mPropertyHalService.setCarPropertyValuesAsync(List.of(request),
895                     mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
896                     /* asyncRequestStartTime= */ 0);
897         });
898 
899         verifyNoPendingRequest();
900     }
901 
902     @Test
testSetCarPropertyValuesAsync_linkToDeath()903     public void testSetCarPropertyValuesAsync_linkToDeath() throws RemoteException {
904         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
905         List<AsyncPropertyServiceRequest> setPropertyServiceRequests = mock(List.class);
906 
907         mPropertyHalService.setCarPropertyValuesAsync(setPropertyServiceRequests,
908                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
909                 /* asyncRequestStartTime= */ 0);
910 
911         verify(mSetAsyncPropertyResultBinder).linkToDeath(any(IBinder.DeathRecipient.class),
912                 anyInt());
913 
914         verifyNoPendingRequest();
915     }
916 
917     // Test the case where we don't wait for property update event. The success callback should be
918     // immediately called when the set request succeeded.
919     @Test
testSetCarPropertyValuesAsync_noWaitForPropertyUpdate()920     public void testSetCarPropertyValuesAsync_noWaitForPropertyUpdate()
921             throws Exception {
922         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
923 
924         doAnswer((invocation) -> {
925             setInvocationWrap.add(invocation);
926             return null;
927         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
928         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
929 
930         AsyncPropertyServiceRequest request = copyRequest(SET_HVAC_REQUEST_ID_1);
931         request.setWaitForPropertyUpdate(false);
932 
933         mPropertyHalService.setCarPropertyValuesAsync(List.of(request),
934                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
935                 /* asyncRequestStartTime= */ 0);
936 
937         // Must not subscribe to the property for update events.
938         verify(mVehicleHal, never()).subscribeProperty(any(), anyList());
939         // Must not send get initial value request.
940         verify(mVehicleHal, never()).getAsync(any(), any());
941         assertThat(setInvocationWrap).hasSize(1);
942 
943         // Returns the set value result.
944         deliverOkaySetResult(setInvocationWrap.get(0));
945 
946         verify(mSetAsyncPropertyResultCallback).onSetValueResults(mAsyncResultCaptor.capture());
947         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
948         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
949         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode())
950                 .isEqualTo(STATUS_OK);
951         // This should be the time when the request is successfully sent.
952         assertThat(result.getUpdateTimestampNanos()).isGreaterThan(0);
953 
954         verifyNoPendingRequest();
955     }
956 
957     @Test
testSetCarPropertyValuesAsync_noWaitForPropertyUpdateWithMultipleAreaRequests()958     public void testSetCarPropertyValuesAsync_noWaitForPropertyUpdateWithMultipleAreaRequests()
959             throws Exception {
960         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
961 
962         doAnswer((invocation) -> {
963             setInvocationWrap.add(invocation);
964             return null;
965         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
966         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
967 
968         AsyncPropertyServiceRequest request1 = copyRequest(SET_VEHICLE_SPEED_AREA_ID_1_REQUEST);
969         AsyncPropertyServiceRequest request2 = copyRequest(SET_VEHICLE_SPEED_AREA_ID_2_REQUEST);
970         request1.setWaitForPropertyUpdate(false);
971         request2.setWaitForPropertyUpdate(false);
972 
973         mPropertyHalService.setCarPropertyValuesAsync(List.of(request1, request2),
974                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
975                 /* asyncRequestStartTime= */ 0);
976 
977         // Must not subscribe to the property for update events.
978         verify(mVehicleHal, never()).subscribeProperty(any(), anyList());
979         verify(mVehicleHal, never()).subscribeProperty(any(), anyList());
980         // Must not send get initial value request.
981         verify(mVehicleHal, never()).getAsync(any(), any());
982         assertThat(setInvocationWrap).hasSize(1);
983 
984         // Returns the set value result.
985         deliverOkaySetResult(setInvocationWrap.get(0));
986 
987         verify(mSetAsyncPropertyResultCallback).onSetValueResults(mAsyncResultCaptor.capture());
988         GetSetValueResult result1 = mAsyncResultCaptor.getValue().getList().get(0);
989         assertThat(result1.getRequestId()).isEqualTo(REQUEST_ID_4);
990         assertThat(result1.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode())
991                 .isEqualTo(STATUS_OK);
992         verify(mSetAsyncPropertyResultCallback).onSetValueResults(mAsyncResultCaptor.capture());
993         GetSetValueResult result2 = mAsyncResultCaptor.getValue().getList().get(1);
994         assertThat(result2.getRequestId()).isEqualTo(REQUEST_ID_5);
995         assertThat(result2.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode())
996                 .isEqualTo(STATUS_OK);
997 
998         // This should be the time when the request is successfully sent.
999         assertThat(result1.getUpdateTimestampNanos()).isGreaterThan(0);
1000         assertThat(result2.getUpdateTimestampNanos()).isGreaterThan(0);
1001 
1002         verifyNoPendingRequest();
1003     }
1004 
1005     @Test
testSetCarPropertyValuesAsync_mixWaitNoWaitForPropertyUpdate()1006     public void testSetCarPropertyValuesAsync_mixWaitNoWaitForPropertyUpdate() throws Exception {
1007         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1008         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1009         List<HalServiceBase> serviceWrap = new ArrayList<>();
1010 
1011         doAnswer((invocation) -> {
1012             setInvocationWrap.add(invocation);
1013             return null;
1014         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1015         doAnswer((invocation) -> {
1016             getInvocationWrap.add(invocation);
1017             return null;
1018         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1019         doAnswer((invocation) -> {
1020             serviceWrap.add(invocation.getArgument(0));
1021             return null;
1022         }).when(mVehicleHal).subscribeProperty(any(), anyList());
1023         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1024 
1025         AsyncPropertyServiceRequest request1 = copyRequest(SET_HVAC_REQUEST_ID_1);
1026         AsyncPropertyServiceRequest request2 = copyRequest(SET_SPEED_REQUEST_ID_2);
1027         request2.setWaitForPropertyUpdate(false);
1028 
1029         mPropertyHalService.setCarPropertyValuesAsync(List.of(request1, request2),
1030                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
1031                 /* asyncRequestStartTime= */ 0);
1032 
1033         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1034         assertThat(mListArgumentCaptor.getValue()).containsExactly(hvacHalSubscribeOption());
1035         assertThat(setInvocationWrap).hasSize(1);
1036         assertThat(getInvocationWrap).hasSize(1);
1037 
1038         // Returns the get initial value result, assume we failed to get initial values.
1039         deliverErrorGetResult(getInvocationWrap.get(0),
1040                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
1041 
1042         verify(mSetAsyncPropertyResultCallback, never()).onSetValueResults(any());
1043 
1044         // Returns the set value result.
1045         deliverOkaySetResult(setInvocationWrap.get(0));
1046 
1047         // We should receive the success callback for request 2 since the set request is already
1048         // sent.
1049         verify(mSetAsyncPropertyResultCallback).onSetValueResults(
1050                 mAsyncResultCaptor.capture());
1051 
1052         // Deliver updated value for request 1. Request 2 don't need an updated value.
1053         assertThat(serviceWrap).hasSize(1);
1054         serviceWrap.get(0).onHalEvents(List.of(mPropValue));
1055 
1056         // Get init value result must not be passed to the client.
1057         verify(mSetAsyncPropertyResultCallback, never()).onGetValueResults(any());
1058         // We should receive the success callback for request 1.
1059         verify(mSetAsyncPropertyResultCallback, times(2)).onSetValueResults(
1060                 mAsyncResultCaptor.capture());
1061         for (GetSetValueResultList results: mAsyncResultCaptor.getAllValues()) {
1062             GetSetValueResult result = results.getList().get(0);
1063             assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode())
1064                     .isEqualTo(STATUS_OK);
1065             if (result.getRequestId() == REQUEST_ID_1) {
1066                 assertThat(result.getUpdateTimestampNanos()).isEqualTo(
1067                         TEST_UPDATE_TIMESTAMP_NANOS);
1068             } else {
1069                 assertThat(result.getUpdateTimestampNanos()).isNotEqualTo(
1070                         TEST_UPDATE_TIMESTAMP_NANOS);
1071                 // This should be elapsedRealTimeNano when the set is done.
1072                 assertThat(result.getUpdateTimestampNanos()).isGreaterThan(0);
1073             }
1074         }
1075         // After the result comes, we must unsubscribe the property.
1076         verify(mVehicleHal).unsubscribeProperty(any(), eq(HVAC_TEMPERATURE_SET));
1077 
1078         verifyNoPendingRequest();
1079     }
1080 
1081     // Test the case where we get the result for the init value before we get the result for
1082     // async set. The init value is the same as the target value.
1083     @Test
testSetCarPropertyValuesAsync_initValueSameAsTargetValue_beforeSetResult()1084     public void testSetCarPropertyValuesAsync_initValueSameAsTargetValue_beforeSetResult()
1085             throws Exception {
1086         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1087         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1088 
1089         doAnswer((invocation) -> {
1090             setInvocationWrap.add(invocation);
1091             return null;
1092         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1093         doAnswer((invocation) -> {
1094             getInvocationWrap.add(invocation);
1095             return null;
1096         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1097         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1098 
1099         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_HVAC_REQUEST_ID_1),
1100                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
1101                 /* asyncRequestStartTime= */ 0);
1102 
1103         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1104         assertThat(mListArgumentCaptor.getValue()).containsExactly(hvacHalSubscribeOption());
1105         assertThat(setInvocationWrap).hasSize(1);
1106         assertThat(getInvocationWrap).hasSize(1);
1107 
1108         // Returns the get initial value result.
1109         deliverOkayGetResult(getInvocationWrap.get(0));
1110 
1111         verify(mSetAsyncPropertyResultCallback, never()).onSetValueResults(any());
1112 
1113         // Returns the set value result.
1114         deliverOkaySetResult(setInvocationWrap.get(0));
1115 
1116         // Get init value result must not be passed to the client.
1117         verify(mSetAsyncPropertyResultCallback, never()).onGetValueResults(any());
1118         verify(mSetAsyncPropertyResultCallback).onSetValueResults(mAsyncResultCaptor.capture());
1119         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
1120         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
1121         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode())
1122                 .isEqualTo(STATUS_OK);
1123         assertThat(result.getUpdateTimestampNanos()).isEqualTo(TEST_UPDATE_TIMESTAMP_NANOS);
1124         // After the result comes, we must unsubscribe the property.
1125         verify(mVehicleHal).unsubscribeProperty(any(), eq(HVAC_TEMPERATURE_SET));
1126 
1127         verifyNoPendingRequest();
1128     }
1129 
1130     // Test the case where we get the result for the init value after we get the result for
1131     // async set. The init value is the same as the target value.
1132     @Test
testSetCarPropertyValuesAsync_initValueSameAsTargetValue_afterSetResult()1133     public void testSetCarPropertyValuesAsync_initValueSameAsTargetValue_afterSetResult()
1134             throws Exception {
1135         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1136         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1137 
1138         doAnswer((invocation) -> {
1139             setInvocationWrap.add(invocation);
1140             return null;
1141         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1142         doAnswer((invocation) -> {
1143             getInvocationWrap.add(invocation);
1144             return null;
1145         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1146         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1147 
1148         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_HVAC_REQUEST_ID_1),
1149                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
1150                 /* asyncRequestStartTime= */ 0);
1151 
1152         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1153         assertThat(mListArgumentCaptor.getValue()).containsExactly(hvacHalSubscribeOption());
1154         assertThat(setInvocationWrap).hasSize(1);
1155         assertThat(getInvocationWrap).hasSize(1);
1156 
1157         // Returns the set value result.
1158         deliverOkaySetResult(setInvocationWrap.get(0));
1159 
1160         verify(mSetAsyncPropertyResultCallback, never()).onSetValueResults(any());
1161 
1162         // Returns the get initial value result.
1163         deliverOkayGetResult(getInvocationWrap.get(0));
1164 
1165         // Get init value result must not be passed to the client.
1166         verify(mSetAsyncPropertyResultCallback, never()).onGetValueResults(any());
1167         verify(mSetAsyncPropertyResultCallback).onSetValueResults(mAsyncResultCaptor.capture());
1168         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
1169         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
1170         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode())
1171                 .isEqualTo(STATUS_OK);
1172         assertThat(result.getUpdateTimestampNanos()).isEqualTo(TEST_UPDATE_TIMESTAMP_NANOS);
1173         // After the result comes, we must unsubscribe the property.
1174         verify(mVehicleHal).unsubscribeProperty(any(), eq(HVAC_TEMPERATURE_SET));
1175 
1176         verifyNoPendingRequest();
1177     }
1178 
1179     // Test the case where the get initial value and set value request retried once.
1180     // The init value is the same as the target value.
1181     @Test
testSetCarPropertyValuesAsync_initValueSameAsTargetValue_retry()1182     public void testSetCarPropertyValuesAsync_initValueSameAsTargetValue_retry()
1183             throws Exception {
1184         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1185         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1186 
1187         doAnswer((invocation) -> {
1188             setInvocationWrap.add(invocation);
1189             return null;
1190         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1191         doAnswer((invocation) -> {
1192             getInvocationWrap.add(invocation);
1193             return null;
1194         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1195         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1196 
1197         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_HVAC_REQUEST_ID_1),
1198                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
1199                 /* asyncRequestStartTime= */ 0);
1200 
1201         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1202         assertThat(mListArgumentCaptor.getValue()).containsExactly(hvacHalSubscribeOption());
1203         assertThat(setInvocationWrap).hasSize(1);
1204         assertThat(getInvocationWrap).hasSize(1);
1205 
1206         // Retry get initial value request.
1207         deliverTryAgainGetResult(getInvocationWrap.get(0));
1208         // Retry set initial value request.
1209         deliverTryAgainSetResult(setInvocationWrap.get(0));
1210 
1211         // Wait until the retry happens.
1212         verify(mVehicleHal, timeout(1000).times(2)).getAsync(any(), any());
1213         verify(mVehicleHal, timeout(1000).times(2)).setAsync(any(), any());
1214 
1215         // Returns the set value result.
1216         deliverOkaySetResult(setInvocationWrap.get(1));
1217         // Returns the get initial value result.
1218         deliverOkayGetResult(getInvocationWrap.get(1));
1219 
1220         verify(mSetAsyncPropertyResultCallback, never()).onGetValueResults(any());
1221         verify(mSetAsyncPropertyResultCallback).onSetValueResults(mAsyncResultCaptor.capture());
1222         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
1223         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
1224         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode())
1225                 .isEqualTo(STATUS_OK);
1226         assertThat(result.getUpdateTimestampNanos()).isEqualTo(TEST_UPDATE_TIMESTAMP_NANOS);
1227         // After the result comes, we must unsubscribe the property.
1228         verify(mVehicleHal).unsubscribeProperty(any(), eq(HVAC_TEMPERATURE_SET));
1229 
1230         verifyNoPendingRequest();
1231     }
1232 
1233     // Test the case where the get initial value returns a different value than target value.
1234     @Test
testSetCarPropertyValuesAsync_initValueDiffTargetValue_timeout()1235     public void testSetCarPropertyValuesAsync_initValueDiffTargetValue_timeout()
1236             throws Exception {
1237         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1238         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1239 
1240         doAnswer((invocation) -> {
1241             setInvocationWrap.add(invocation);
1242             return null;
1243         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1244         doAnswer((invocation) -> {
1245             getInvocationWrap.add(invocation);
1246             return null;
1247         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1248         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1249 
1250         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_HVAC_REQUEST_ID_1),
1251                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 100,
1252                 /* asyncRequestStartTime= */ 0);
1253 
1254         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1255         assertThat(mListArgumentCaptor.getValue()).containsExactly(hvacHalSubscribeOption());
1256         assertThat(setInvocationWrap).hasSize(1);
1257         assertThat(getInvocationWrap).hasSize(1);
1258 
1259         // Returns the get initial value result.
1260         deliverOkayGetResult(getInvocationWrap.get(0), mPropValueBuilder.build(
1261                 HVAC_TEMPERATURE_SET, /* areaId= */ 0, 16.0f));
1262         // Returns the set value result.
1263         deliverOkaySetResult(setInvocationWrap.get(0));
1264 
1265         // Get init value result must not be passed to the client.
1266         verify(mSetAsyncPropertyResultCallback, never()).onGetValueResults(any());
1267         // Eventually the request should time out.
1268         verify(mSetAsyncPropertyResultCallback, timeout(1000)).onSetValueResults(
1269                 mAsyncResultCaptor.capture());
1270         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
1271         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
1272         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode()).isEqualTo(
1273                 CarPropertyManager.STATUS_ERROR_TIMEOUT);
1274 
1275         verifyNoPendingRequest();
1276     }
1277 
1278     // Test the case where we get set value error result after we get the initial value result.
1279     @Test
testSetCarPropertyValuesAsync_errorSetResultAfterTargetInitValueResult()1280     public void testSetCarPropertyValuesAsync_errorSetResultAfterTargetInitValueResult()
1281             throws Exception {
1282         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1283         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1284 
1285         doAnswer((invocation) -> {
1286             setInvocationWrap.add(invocation);
1287             return null;
1288         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1289         doAnswer((invocation) -> {
1290             getInvocationWrap.add(invocation);
1291             return null;
1292         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1293         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1294 
1295         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_HVAC_REQUEST_ID_1),
1296                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
1297                 /* asyncRequestStartTime= */ 0);
1298 
1299         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1300         assertThat(mListArgumentCaptor.getValue()).containsExactly(hvacHalSubscribeOption());
1301         assertThat(setInvocationWrap).hasSize(1);
1302         assertThat(getInvocationWrap).hasSize(1);
1303 
1304         // Returns the get initial value result.
1305         deliverOkayGetResult(getInvocationWrap.get(0));
1306         // Returns the set value result.
1307         deliverErrorSetResult(setInvocationWrap.get(0),
1308                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
1309 
1310         // Get init value result must not be passed to the client.
1311         verify(mSetAsyncPropertyResultCallback, never()).onGetValueResults(any());
1312         verify(mSetAsyncPropertyResultCallback).onSetValueResults(mAsyncResultCaptor.capture());
1313         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
1314         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
1315         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode())
1316                 .isEqualTo(CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
1317         // After the result comes, we must unsubscribe the property.
1318         verify(mVehicleHal).unsubscribeProperty(any(), eq(HVAC_TEMPERATURE_SET));
1319 
1320         verifyNoPendingRequest();
1321     }
1322 
1323     // Test the callback is only invoked once even though the same result is returned multiple
1324     // times.
1325     @Test
testSetCarPropertyValuesAsync_callbackOnlyCalledOncePerRequest()1326     public void testSetCarPropertyValuesAsync_callbackOnlyCalledOncePerRequest()
1327             throws Exception {
1328         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1329 
1330         doAnswer((invocation) -> {
1331             setInvocationWrap.add(invocation);
1332             return null;
1333         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1334         doAnswer((invocation) -> {
1335             return null;
1336         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1337         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1338 
1339         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_HVAC_REQUEST_ID_1),
1340                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
1341                 /* asyncRequestStartTime= */ 0);
1342 
1343         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1344         assertThat(mListArgumentCaptor.getValue()).containsExactly(hvacHalSubscribeOption());
1345         deliverErrorSetResult(setInvocationWrap.get(0),
1346                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
1347 
1348         // The same result is returned again.
1349         deliverErrorSetResult(setInvocationWrap.get(0),
1350                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
1351 
1352         // We must only call callback once.
1353         verify(mSetAsyncPropertyResultCallback, times(1)).onSetValueResults(any());
1354         // After the result comes, we must unsubscribe the property.
1355         verify(mVehicleHal).unsubscribeProperty(any(), eq(HVAC_TEMPERATURE_SET));
1356 
1357         verifyNoPendingRequest();
1358     }
1359 
1360     // Test the case where the get init value request has error result. The result must be ignored.
1361     @Test
testSetCarPropertyValuesAsync_errorInitValueResult_timeout()1362     public void testSetCarPropertyValuesAsync_errorInitValueResult_timeout()
1363             throws Exception {
1364         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1365         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1366 
1367         doAnswer((invocation) -> {
1368             setInvocationWrap.add(invocation);
1369             return null;
1370         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1371         doAnswer((invocation) -> {
1372             getInvocationWrap.add(invocation);
1373             return null;
1374         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1375         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1376 
1377         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_HVAC_REQUEST_ID_1),
1378                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 100,
1379                 /* asyncRequestStartTime= */ 0);
1380 
1381         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1382         assertThat(mListArgumentCaptor.getValue()).containsExactly(hvacHalSubscribeOption());
1383         assertThat(setInvocationWrap).hasSize(1);
1384         assertThat(getInvocationWrap).hasSize(1);
1385 
1386         // Returns the get initial value result, assume we failed to get initial values.
1387         deliverErrorGetResult(getInvocationWrap.get(0),
1388                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
1389         // Returns the set value result.
1390         deliverOkaySetResult(setInvocationWrap.get(0));
1391 
1392         // Get init value result must not be passed to the client.
1393         verify(mSetAsyncPropertyResultCallback, never()).onGetValueResults(any());
1394         // Eventually the request should time out.
1395         verify(mSetAsyncPropertyResultCallback, timeout(1000)).onSetValueResults(
1396                 mAsyncResultCaptor.capture());
1397         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
1398         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
1399         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode()).isEqualTo(
1400                 CarPropertyManager.STATUS_ERROR_TIMEOUT);
1401 
1402         verifyNoPendingRequest();
1403     }
1404 
1405     @Test
testSetCarPropertyValuesAsync_propertyUpdatedThroughEvent()1406     public void testSetCarPropertyValuesAsync_propertyUpdatedThroughEvent()
1407             throws Exception {
1408         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1409         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1410         List<HalServiceBase> serviceWrap = new ArrayList<>();
1411 
1412         doAnswer((invocation) -> {
1413             setInvocationWrap.add(invocation);
1414             return null;
1415         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1416         doAnswer((invocation) -> {
1417             getInvocationWrap.add(invocation);
1418             return null;
1419         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1420         doAnswer((invocation) -> {
1421             serviceWrap.add(invocation.getArgument(0));
1422             return null;
1423         }).when(mVehicleHal).subscribeProperty(any(), anyList());
1424         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1425 
1426         float testSampleRate = 20.0f;
1427         AsyncPropertyServiceRequest request2 = copyRequest(SET_SPEED_REQUEST_ID_2);
1428         request2.setUpdateRateHz(testSampleRate);
1429 
1430         mPropertyHalService.setCarPropertyValuesAsync(List.of(
1431                 SET_HVAC_REQUEST_ID_1, request2),
1432                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
1433                 /* asyncRequestStartTime= */ 0);
1434 
1435         assertThat(setInvocationWrap).hasSize(1);
1436         assertThat(getInvocationWrap).hasSize(1);
1437 
1438         // Returns the get initial value result.
1439         deliverErrorGetResult(getInvocationWrap.get(0),
1440                 CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
1441         // Returns the set value result.
1442         deliverOkaySetResult(setInvocationWrap.get(0));
1443 
1444         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1445         assertThat(mListArgumentCaptor.getValue()).containsExactly(
1446                 hvacHalSubscribeOption(), speedHalSubscribeOption(testSampleRate));
1447 
1448         // Notify the property is updated to the target value.
1449         assertThat(serviceWrap).hasSize(1);
1450         serviceWrap.get(0).onHalEvents(List.of(mPropValue));
1451         serviceWrap.get(0).onHalEvents(List.of(mPropValue2));
1452 
1453         verify(mSetAsyncPropertyResultCallback, times(2)).onSetValueResults(
1454                 mAsyncResultCaptor.capture());
1455         for (GetSetValueResultList results : mAsyncResultCaptor.getAllValues()) {
1456             GetSetValueResult result = results.getList().get(0);
1457             assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode())
1458                     .isEqualTo(STATUS_OK);
1459             assertThat(result.getUpdateTimestampNanos()).isEqualTo(TEST_UPDATE_TIMESTAMP_NANOS);
1460         }
1461         // After the result comes, we must unsubscribe the property.
1462         verify(mVehicleHal).unsubscribeProperty(any(), eq(HVAC_TEMPERATURE_SET));
1463         verify(mVehicleHal).unsubscribeProperty(any(), eq(PERF_VEHICLE_SPEED));
1464 
1465         verifyNoPendingRequest();
1466     }
1467 
1468     @Test
testSetCarPropertyValuesAsync_propertyUpdatedThroughEvent_ignoreNonTargetEvent()1469     public void testSetCarPropertyValuesAsync_propertyUpdatedThroughEvent_ignoreNonTargetEvent()
1470             throws Exception {
1471         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1472         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1473         List<HalServiceBase> serviceWrap = new ArrayList<>();
1474 
1475         doAnswer((invocation) -> {
1476             setInvocationWrap.add(invocation);
1477             return null;
1478         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1479         doAnswer((invocation) -> {
1480             getInvocationWrap.add(invocation);
1481             return null;
1482         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1483         // Because HVAC_TEMPERATURE_SET is ON_CHANGE property, the sample rate is 0.
1484         doAnswer((invocation) -> {
1485             serviceWrap.add(invocation.getArgument(0));
1486             return null;
1487         }).when(mVehicleHal).subscribeProperty(any(), anyList());
1488         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1489 
1490         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_HVAC_REQUEST_ID_1),
1491                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
1492                 /* asyncRequestStartTime= */ 0);
1493 
1494         assertThat(setInvocationWrap).hasSize(1);
1495         assertThat(getInvocationWrap).hasSize(1);
1496 
1497         // Returns the get initial value result. This is not the target value.
1498         deliverOkayGetResult(getInvocationWrap.get(0), mPropValueBuilder.build(
1499                 HVAC_TEMPERATURE_SET, /* areaId= */ 0, 16.0f));
1500         // Returns the set value result.
1501         deliverOkaySetResult(setInvocationWrap.get(0));
1502 
1503         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1504         assertThat(mListArgumentCaptor.getValue()).containsExactly(hvacHalSubscribeOption());
1505 
1506         // Generate a property update event with non-target value.
1507         serviceWrap.get(0).onHalEvents(List.of(mNonTargetPropValue));
1508 
1509         verify(mSetAsyncPropertyResultCallback, never()).onSetValueResults(any());
1510 
1511         // Notify the property is updated to the target value.
1512         serviceWrap.get(0).onHalEvents(List.of(mPropValue));
1513 
1514         verify(mSetAsyncPropertyResultCallback).onSetValueResults(mAsyncResultCaptor.capture());
1515         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
1516         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
1517         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode())
1518                 .isEqualTo(STATUS_OK);
1519         assertThat(result.getUpdateTimestampNanos()).isEqualTo(TEST_UPDATE_TIMESTAMP_NANOS);
1520         // After the result comes, we must unsubscribe the property.
1521         verify(mVehicleHal).unsubscribeProperty(any(), eq(HVAC_TEMPERATURE_SET));
1522 
1523         verifyNoPendingRequest();
1524     }
1525 
1526     @Test
testSetCarPropertyValuesAsync_updateSubscriptionRate()1527     public void testSetCarPropertyValuesAsync_updateSubscriptionRate()
1528             throws Exception {
1529         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1530         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1531 
1532         doAnswer((invocation) -> {
1533             setInvocationWrap.add(invocation);
1534             return null;
1535         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1536         doAnswer((invocation) -> {
1537             getInvocationWrap.add(invocation);
1538             return null;
1539         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1540         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1541 
1542         AsyncPropertyServiceRequest request = copyRequest(SET_SPEED_REQUEST_ID_2);
1543         request.setUpdateRateHz(20.0f);
1544         mPropertyHalService.setCarPropertyValuesAsync(List.of(request),
1545                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
1546                 /* asyncRequestStartTime= */ 0);
1547 
1548         assertThat(setInvocationWrap).hasSize(1);
1549         assertThat(getInvocationWrap).hasSize(1);
1550 
1551         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1552         assertThat(mListArgumentCaptor.getValue()).containsExactly(speedHalSubscribeOption(20f));
1553         clearInvocations(mVehicleHal);
1554         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1555 
1556         mPropertyHalService.subscribeProperty(List.of(createCarSubscriptionOption(
1557                 PERF_VEHICLE_SPEED, new int[]{0}, /* updateRateHz= */ 40.0f,
1558                 /* enableVur= */ true)));
1559 
1560         // Subscription rate has to be updated according to client subscription.
1561         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1562         assertThat(mListArgumentCaptor.getValue()).containsExactly(speedHalSubscribeOption(40f));
1563         clearInvocations(mVehicleHal);
1564         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1565 
1566         // After client unsubscribe, revert back to the internal subscription rate.
1567         mPropertyHalService.unsubscribeProperty(PERF_VEHICLE_SPEED);
1568 
1569         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1570         assertThat(mListArgumentCaptor.getValue()).containsExactly(speedHalSubscribeOption(20f));
1571         clearInvocations(mVehicleHal);
1572         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1573 
1574         // New client subscription must overwrite the internal rate.
1575         mPropertyHalService.subscribeProperty(List.of(createCarSubscriptionOption(
1576                 PERF_VEHICLE_SPEED, new int[]{0}, /* updateRateHz= */ 50.0f,
1577                 /* enableVur= */ true)));
1578 
1579         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1580         assertThat(mListArgumentCaptor.getValue()).containsExactly(speedHalSubscribeOption(50f));
1581         clearInvocations(mVehicleHal);
1582         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1583 
1584         // Finish the async set request.
1585         deliverOkaySetResult(setInvocationWrap.get(0));
1586         deliverOkayGetResult(getInvocationWrap.get(0), mPropValue2);
1587 
1588         verify(mSetAsyncPropertyResultCallback).onSetValueResults(mAsyncResultCaptor.capture());
1589         assertThat(
1590                 mAsyncResultCaptor.getValue().getList().get(0)
1591                         .getCarPropertyErrorCodes().getCarPropertyManagerErrorCode())
1592                 .isEqualTo(STATUS_OK);
1593 
1594         // After the internal subscription is finished, the client subscription must be kept,
1595         // which causes no update to update rate.
1596         verify(mVehicleHal, never()).subscribeProperty(any(), any());
1597 
1598         mPropertyHalService.unsubscribeProperty(PERF_VEHICLE_SPEED);
1599 
1600         // After both client and internal unsubscription, the property must be unsubscribed.
1601         verify(mVehicleHal).unsubscribeProperty(any(), eq(PERF_VEHICLE_SPEED));
1602 
1603         verifyNoPendingRequest();
1604     }
1605 
1606     @Test
testMultipleSetCarPropertyValuesAsync_updateSubscriptionRateWithDifferentAreaIds()1607     public void testMultipleSetCarPropertyValuesAsync_updateSubscriptionRateWithDifferentAreaIds()
1608             throws Exception {
1609         when(mMockCarPropertyConfig2.getAreaIds()).thenReturn(new int[]{0, 1, 2});
1610         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1611         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1612 
1613         doAnswer((invocation) -> {
1614             setInvocationWrap.add(invocation);
1615             return null;
1616         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1617         doAnswer((invocation) -> {
1618             getInvocationWrap.add(invocation);
1619             return null;
1620         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1621         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1622 
1623         AsyncPropertyServiceRequest request1 = copyRequest(SET_VEHICLE_SPEED_AREA_ID_1_REQUEST);
1624         request1.setUpdateRateHz(20.0f);
1625         AsyncPropertyServiceRequest request2 = copyRequest(SET_VEHICLE_SPEED_AREA_ID_2_REQUEST);
1626         request2.setUpdateRateHz(21.0f);
1627         mPropertyHalService.setCarPropertyValuesAsync(List.of(request1, request2),
1628                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
1629                 /* asyncRequestStartTime= */ 0);
1630 
1631         assertThat(setInvocationWrap).hasSize(1);
1632         assertThat(getInvocationWrap).hasSize(1);
1633 
1634 
1635         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1636         assertThat(mListArgumentCaptor.getValue()).containsExactly(
1637                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{1}, 20f,
1638                         /* enableVariableUpdateRate= */ true),
1639                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{2}, 21f,
1640                         /* enableVariableUpdateRate= */ true));
1641         clearInvocations(mVehicleHal);
1642         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1643 
1644 
1645         List<CarSubscription> carSubscriptions = new ArrayList<>();
1646         carSubscriptions.add(createCarSubscriptionOption(PERF_VEHICLE_SPEED, new int[] {1},
1647                 /* updateRateHz= */ 40.0f, /* enableVur= */ true));
1648         mPropertyHalService.subscribeProperty(carSubscriptions);
1649         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1650         assertThat(mListArgumentCaptor.getValue()).containsExactly(
1651                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{1}, 40f,
1652                         /* enableVariableUpdateRate= */ true));
1653         clearInvocations(mVehicleHal);
1654         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1655 
1656         carSubscriptions = new ArrayList<>();
1657         carSubscriptions.add(createCarSubscriptionOption(PERF_VEHICLE_SPEED, new int[] {2},
1658                 /* updateRateHz= */ 41.0f, /* enableVur= */ true));
1659         mPropertyHalService.subscribeProperty(carSubscriptions);
1660         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1661         assertThat(mListArgumentCaptor.getValue()).containsExactly(
1662                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{2}, 41f,
1663                         /* enableVariableUpdateRate= */ true));
1664         clearInvocations(mVehicleHal);
1665         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1666 
1667         mPropertyHalService.unsubscribeProperty(PERF_VEHICLE_SPEED);
1668 
1669         // After client unsubscribe, revert back to the internal subscription rate
1670 
1671         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1672         assertThat(mListArgumentCaptor.getValue()).containsExactly(
1673                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{1}, 20f,
1674                         /* enableVariableUpdateRate= */ true),
1675                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{2}, 21f,
1676                         /* enableVariableUpdateRate= */ true));
1677         clearInvocations(mVehicleHal);
1678         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1679 
1680 
1681         carSubscriptions = new ArrayList<>();
1682         carSubscriptions.add(createCarSubscriptionOption(PERF_VEHICLE_SPEED, new int[] {1},
1683                 /* updateRateHz= */ 30.0f, /* enableVur= */ true));
1684         mPropertyHalService.subscribeProperty(carSubscriptions);
1685         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1686         assertThat(mListArgumentCaptor.getValue()).containsExactly(
1687                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{1}, 30f,
1688                         /* enableVariableUpdateRate= */ true));
1689         clearInvocations(mVehicleHal);
1690         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1691 
1692         carSubscriptions = new ArrayList<>();
1693         carSubscriptions.add(createCarSubscriptionOption(PERF_VEHICLE_SPEED, new int[] {2},
1694                 /* updateRateHz= */ 31.0f, /* enableVur= */ true));
1695         mPropertyHalService.subscribeProperty(carSubscriptions);
1696         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1697         assertThat(mListArgumentCaptor.getValue()).containsExactly(
1698                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{2}, 31f,
1699                         /* enableVariableUpdateRate= */ true));
1700         clearInvocations(mVehicleHal);
1701         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1702 
1703         deliverOkaySetResult(setInvocationWrap.get(0));
1704         deliverOkayGetResult(getInvocationWrap.get(0), mPropValue2);
1705 
1706         // After the internal subscription is finished, the client subscription must be kept.
1707         // The internal subscription rate is lower than the client rate, so no rate change.
1708         verify(mVehicleHal, never()).subscribeProperty(any(), any());
1709 
1710         verify(mSetAsyncPropertyResultCallback).onSetValueResults(mAsyncResultCaptor.capture());
1711         assertThat(mAsyncResultCaptor.getValue().getList().get(0).getCarPropertyErrorCodes()
1712                 .getCarPropertyManagerErrorCode()).isEqualTo(STATUS_OK);
1713         assertThat(mAsyncResultCaptor.getValue().getList().get(1).getCarPropertyErrorCodes()
1714                 .getCarPropertyManagerErrorCode()).isEqualTo(STATUS_OK);
1715 
1716         mPropertyHalService.unsubscribeProperty(PERF_VEHICLE_SPEED);
1717 
1718         // After both client and internal unsubscription, the property must be unsubscribed.
1719         verify(mVehicleHal).unsubscribeProperty(any(), eq(PERF_VEHICLE_SPEED));
1720 
1721         verifyNoPendingRequest();
1722     }
1723 
1724     @Test
testMultipleSetCarPropertyValuesAsync_overlappingSetAsync()1725     public void testMultipleSetCarPropertyValuesAsync_overlappingSetAsync()
1726             throws Exception {
1727         when(mMockCarPropertyConfig2.getAreaIds()).thenReturn(new int[]{0, 1, 2});
1728         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1729         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1730 
1731         doAnswer((invocation) -> {
1732             setInvocationWrap.add(invocation);
1733             return null;
1734         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1735         doAnswer((invocation) -> {
1736             getInvocationWrap.add(invocation);
1737             return null;
1738         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1739         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1740 
1741         AsyncPropertyServiceRequest request1 = copyRequest(SET_VEHICLE_SPEED_AREA_ID_1_REQUEST);
1742         request1.setUpdateRateHz(20.0f);
1743         AsyncPropertyServiceRequest request2 = copyRequest(SET_VEHICLE_SPEED_AREA_ID_2_REQUEST);
1744         request2.setUpdateRateHz(21.0f);
1745         mPropertyHalService.setCarPropertyValuesAsync(List.of(request1),
1746                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
1747                 /* asyncRequestStartTime= */ 0);
1748         mPropertyHalService.setCarPropertyValuesAsync(List.of(request2),
1749                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
1750                 /* asyncRequestStartTime= */ 0);
1751 
1752         assertThat(setInvocationWrap).hasSize(2);
1753         assertThat(getInvocationWrap).hasSize(2);
1754 
1755         verify(mVehicleHal, times(2)).subscribeProperty(any(), mListArgumentCaptor.capture());
1756         assertThat(mListArgumentCaptor.getAllValues().get(0)).containsExactly(
1757                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{1}, 20f,
1758                         /* enableVariableUpdateRate= */ true));
1759         assertThat(mListArgumentCaptor.getAllValues().get(1)).containsExactly(
1760                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{2}, 21f,
1761                         /* enableVariableUpdateRate= */ true));
1762         clearInvocations(mVehicleHal);
1763         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1764 
1765         List<CarSubscription> carSubscriptions = new ArrayList<>();
1766         carSubscriptions.add(createCarSubscriptionOption(PERF_VEHICLE_SPEED, new int[] {1},
1767                 /* updateRateHz= */ 40.0f, /* enableVur= */ true));
1768         carSubscriptions.add(createCarSubscriptionOption(PERF_VEHICLE_SPEED, new int[] {2},
1769                 /* updateRateHz= */ 41.0f, /* enableVur= */ true));
1770         mPropertyHalService.subscribeProperty(carSubscriptions);
1771 
1772         // Subscription rate has to be updated according to client subscription.
1773         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1774         assertThat(mListArgumentCaptor.getValue()).containsExactly(
1775                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{1}, 40f,
1776                         /* enableVariableUpdateRate= */ true),
1777                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{2}, 41f,
1778                         /* enableVariableUpdateRate= */ true));
1779         clearInvocations(mVehicleHal);
1780 
1781         deliverOkaySetResult(setInvocationWrap.get(0));
1782         deliverOkayGetResult(getInvocationWrap.get(0), mPropValue2);
1783 
1784         // Unsubscribing to internal rate 20.0f and 21.0f does not change the current client rate
1785         // 40 and 41.
1786         verify(mVehicleHal, never()).subscribeProperty(any(), any());
1787 
1788         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1789 
1790         mPropertyHalService.unsubscribeProperty(PERF_VEHICLE_SPEED);
1791         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1792         assertThat(mListArgumentCaptor.getValue()).containsExactly(new HalSubscribeOptions(
1793                 PERF_VEHICLE_SPEED, new int[]{2}, 21f, /* enableVariableUpdateRate= */ true));
1794         clearInvocations(mVehicleHal);
1795 
1796         deliverOkaySetResult(setInvocationWrap.get(1));
1797         deliverOkayGetResult(getInvocationWrap.get(1), mPropValue2);
1798 
1799         // Subscription rate has to be updated according to client subscription.
1800         verify(mVehicleHal).unsubscribeProperty(any(), eq(PERF_VEHICLE_SPEED));
1801 
1802         verifyNoPendingRequest();
1803     }
1804 
1805     @Test
testSetCarPropertyValuesAsync_withSubscriptionFromDifferentAreaIds()1806     public void testSetCarPropertyValuesAsync_withSubscriptionFromDifferentAreaIds()
1807             throws Exception {
1808         when(mMockCarPropertyConfig2.getAreaIds()).thenReturn(new int[]{0, 1});
1809         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1810         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1811 
1812         doAnswer((invocation) -> {
1813             setInvocationWrap.add(invocation);
1814             return null;
1815         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1816         doAnswer((invocation) -> {
1817             getInvocationWrap.add(invocation);
1818             return null;
1819         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1820         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1821 
1822         AsyncPropertyServiceRequest request = copyRequest(SET_VEHICLE_SPEED_AREA_ID_1_REQUEST);
1823         request.setUpdateRateHz(20.0f);
1824         mPropertyHalService.setCarPropertyValuesAsync(List.of(request),
1825                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
1826                 /* asyncRequestStartTime= */ 0);
1827 
1828         assertThat(setInvocationWrap).hasSize(1);
1829         assertThat(getInvocationWrap).hasSize(1);
1830 
1831         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1832         assertThat(mListArgumentCaptor.getAllValues().get(0)).containsExactly(
1833                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{1}, 20f,
1834                         /* enableVariableUpdateRate= */ true));
1835         clearInvocations(mVehicleHal);
1836         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1837 
1838         mPropertyHalService.subscribeProperty(List.of(createCarSubscriptionOption(
1839                 PERF_VEHICLE_SPEED, new int[]{0, 1}, /* updateRateHz= */ 40.0f,
1840                 /* enableVur= */ true)));
1841 
1842         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1843         assertThat(mListArgumentCaptor.getAllValues().get(0)).containsExactly(
1844                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{0, 1}, 40f,
1845                         /* enableVariableUpdateRate= */ true));
1846         clearInvocations(mVehicleHal);
1847         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1848 
1849         // After client unsubscribe, revert back to the internal subscription rate.
1850         mPropertyHalService.unsubscribeProperty(PERF_VEHICLE_SPEED);
1851 
1852         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1853         assertThat(mListArgumentCaptor.getAllValues().get(0)).containsExactly(
1854                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{1}, 20f,
1855                         /* enableVariableUpdateRate= */ true));
1856         clearInvocations(mVehicleHal);
1857         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1858 
1859         // New client subscription must overwrite the internal rate.
1860         List<CarSubscription> carSubscriptions = new ArrayList<>();
1861         carSubscriptions.add(createCarSubscriptionOption(PERF_VEHICLE_SPEED, new int[] {1},
1862                 /* updateRateHz= */ 50.0f, /* enableVur= */ true));
1863         mPropertyHalService.subscribeProperty(carSubscriptions);
1864 
1865         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1866         assertThat(mListArgumentCaptor.getAllValues().get(0)).containsExactly(
1867                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{1}, 50f,
1868                         /* enableVariableUpdateRate= */ true));
1869         clearInvocations(mVehicleHal);
1870         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1871         clearInvocations(mVehicleHal);
1872 
1873         // Finish the async set request.
1874         deliverOkaySetResult(setInvocationWrap.get(0));
1875         deliverOkayGetResult(getInvocationWrap.get(0), mPropValue2);
1876 
1877         verify(mSetAsyncPropertyResultCallback).onSetValueResults(mAsyncResultCaptor.capture());
1878         assertThat(mAsyncResultCaptor.getValue().getList().get(0).getCarPropertyErrorCodes()
1879                 .getCarPropertyManagerErrorCode()).isEqualTo(STATUS_OK);
1880 
1881         // After the internal subscription is finished, the client is still subscribed at 50hz
1882         // and no update rate change is required.
1883         verify(mVehicleHal, never()).subscribeProperty(any(), any());
1884 
1885         mPropertyHalService.unsubscribeProperty(PERF_VEHICLE_SPEED);
1886 
1887         // After both client and internal unsubscription, the property must be unsubscribed.
1888         verify(mVehicleHal).unsubscribeProperty(any(), eq(PERF_VEHICLE_SPEED));
1889 
1890         verifyNoPendingRequest();
1891     }
1892 
1893     @Test
testSetCarPropertyValuesAsync_defaultSubscriptionRate()1894     public void testSetCarPropertyValuesAsync_defaultSubscriptionRate()
1895             throws Exception {
1896         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1897         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1898 
1899         doAnswer((invocation) -> {
1900             setInvocationWrap.add(invocation);
1901             return null;
1902         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1903         doAnswer((invocation) -> {
1904             getInvocationWrap.add(invocation);
1905             return null;
1906         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1907         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1908 
1909         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_SPEED_REQUEST_ID_2),
1910                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 10,
1911                 /* asyncRequestStartTime= */ 0);
1912 
1913         assertThat(setInvocationWrap).hasSize(1);
1914         assertThat(getInvocationWrap).hasSize(1);
1915 
1916         // Default sample rate is set to max sample rate.
1917         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1918         assertThat(mListArgumentCaptor.getValue()).containsExactly(speedHalSubscribeOption(100f));
1919 
1920         // Finish the async set request.
1921         deliverOkaySetResult(setInvocationWrap.get(0));
1922         deliverOkayGetResult(getInvocationWrap.get(0), mPropValue2);
1923     }
1924 
1925     @Test
testSetCarPropertyValuesAsync_defaultSubscriptionRateDifferentAreaIds()1926     public void testSetCarPropertyValuesAsync_defaultSubscriptionRateDifferentAreaIds() {
1927         when(mMockCarPropertyConfig2.getAreaIds()).thenReturn(new int[]{0, 1});
1928         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1929         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1930 
1931         doAnswer((invocation) -> {
1932             setInvocationWrap.add(invocation);
1933             return null;
1934         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1935         doAnswer((invocation) -> {
1936             getInvocationWrap.add(invocation);
1937             return null;
1938         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1939         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1940 
1941         mPropertyHalService.setCarPropertyValuesAsync(
1942                 List.of(SET_VEHICLE_SPEED_AREA_ID_1_REQUEST, SET_HVAC_REQUEST_ID_1),
1943                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 10,
1944                 /* asyncRequestStartTime= */ 0);
1945 
1946         assertThat(setInvocationWrap).hasSize(1);
1947         assertThat(getInvocationWrap).hasSize(1);
1948 
1949         // Default sample rate is set to max sample rate.
1950         float maxSampleRate = 100.0f;
1951         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1952         assertThat(mListArgumentCaptor.getValue()).containsExactly(
1953                 hvacHalSubscribeOption(),
1954                 new HalSubscribeOptions(PERF_VEHICLE_SPEED, new int[]{1}, maxSampleRate,
1955                         /* enableVariableUpdateRate= */ true));
1956 
1957         // Finish the async set request.
1958         deliverOkaySetResult(setInvocationWrap.get(0));
1959         deliverOkayGetResult(getInvocationWrap.get(0), mPropValue2);
1960     }
1961 
1962     @Test
testSetCarPropertyValuesAsync_setUpdateRateHz()1963     public void testSetCarPropertyValuesAsync_setUpdateRateHz()
1964             throws Exception {
1965         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
1966         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
1967 
1968         doAnswer((invocation) -> {
1969             setInvocationWrap.add(invocation);
1970             return null;
1971         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
1972         doAnswer((invocation) -> {
1973             getInvocationWrap.add(invocation);
1974             return null;
1975         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
1976         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
1977 
1978         mPropertyHalService.subscribeProperty(List.of(createCarSubscriptionOption(
1979                 PERF_VEHICLE_SPEED, new int[]{0}, /* updateRateHz= */ 22.0f,
1980                 /* enableVur= */ true)));
1981 
1982         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1983         assertThat(mListArgumentCaptor.getAllValues().get(0)).containsExactly(
1984                 speedHalSubscribeOption(22.0f));
1985         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
1986         clearInvocations(mVehicleHal);
1987 
1988         AsyncPropertyServiceRequest request1 = copyRequest(SET_SPEED_REQUEST_ID_2);
1989         request1.setUpdateRateHz(23.1f);
1990         AsyncPropertyServiceRequest request2 = copyRequest(SET_SPEED_REQUEST_ID_3);
1991         request2.setUpdateRateHz(23.2f);
1992         mPropertyHalService.setCarPropertyValuesAsync(List.of(request1, request2),
1993                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
1994                 /* asyncRequestStartTime= */ 0);
1995 
1996         assertThat(setInvocationWrap).hasSize(1);
1997         assertThat(getInvocationWrap).hasSize(1);
1998         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
1999         assertThat(mListArgumentCaptor.getAllValues().get(0)).containsExactly(
2000                 speedHalSubscribeOption(23.2f));
2001         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
2002         clearInvocations(mVehicleHal);
2003 
2004         // Finish the async set request.
2005         deliverOkaySetResult(setInvocationWrap.get(0));
2006         deliverOkayGetResult(getInvocationWrap.get(0), mPropValue2);
2007 
2008         verify(mSetAsyncPropertyResultCallback).onSetValueResults(mAsyncResultCaptor.capture());
2009         // Both request must succeed.
2010         assertThat(mAsyncResultCaptor.getValue().getList()).hasSize(2);
2011         assertThat(mAsyncResultCaptor.getValue().getList().get(0).getCarPropertyErrorCodes()
2012                 .getCarPropertyManagerErrorCode()).isEqualTo(STATUS_OK);
2013         assertThat(mAsyncResultCaptor.getValue().getList().get(1).getCarPropertyErrorCodes()
2014                 .getCarPropertyManagerErrorCode()).isEqualTo(STATUS_OK);
2015 
2016         // After internal subscription complete, the client subscription rate must be kept.
2017         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
2018         assertThat(mListArgumentCaptor.getValue()).containsExactly(speedHalSubscribeOption(22.0f));
2019 
2020         verifyNoPendingRequest();
2021     }
2022 
2023     @Test
testSetCarPropertyValuesAsync_cancelledBeforePropertyUpdateEvent()2024     public void testSetCarPropertyValuesAsync_cancelledBeforePropertyUpdateEvent()
2025             throws Exception {
2026         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
2027         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
2028         List<HalServiceBase> serviceWrap = new ArrayList<>();
2029 
2030         doAnswer((invocation) -> {
2031             setInvocationWrap.add(invocation);
2032             return null;
2033         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
2034         doAnswer((invocation) -> {
2035             getInvocationWrap.add(invocation);
2036             return null;
2037         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
2038         // Because HVAC_TEMPERATURE_SET is ON_CHANGE property, the sample rate is 0.
2039         doAnswer((invocation) -> {
2040             serviceWrap.add(invocation.getArgument(0));
2041             return null;
2042         }).when(mVehicleHal).subscribeProperty(any(), anyList());
2043         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
2044 
2045         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_HVAC_REQUEST_ID_1),
2046                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
2047                 /* asyncRequestStartTime= */ 0);
2048 
2049         assertThat(setInvocationWrap).hasSize(1);
2050         assertThat(getInvocationWrap).hasSize(1);
2051 
2052         // Returns the set value result.
2053         deliverOkaySetResult(setInvocationWrap.get(0));
2054 
2055         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
2056         assertThat(mListArgumentCaptor.getValue()).containsExactly(hvacHalSubscribeOption());
2057 
2058         // Cancel the ongoing request.
2059         mPropertyHalService.cancelRequests(new int[]{REQUEST_ID_1});
2060 
2061         // Notify the property is updated to the target value after the request is cancelled.
2062         serviceWrap.get(0).onHalEvents(List.of(mPropValue));
2063 
2064         verify(mSetAsyncPropertyResultCallback, never()).onSetValueResults(any());
2065         verify(mVehicleHal).unsubscribeProperty(any(), eq(HVAC_TEMPERATURE_SET));
2066 
2067         verifyNoPendingRequest();
2068     }
2069 
2070     @Test
testSetCarPropertyValuesAsync_timeoutBeforePropertyUpdateEvent()2071     public void testSetCarPropertyValuesAsync_timeoutBeforePropertyUpdateEvent()
2072             throws Exception {
2073         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
2074         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
2075         List<HalServiceBase> serviceWrap = new ArrayList<>();
2076 
2077         doAnswer((invocation) -> {
2078             setInvocationWrap.add(invocation);
2079             return null;
2080         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
2081         doAnswer((invocation) -> {
2082             getInvocationWrap.add(invocation);
2083             return null;
2084         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
2085         // Because HVAC_TEMPERATURE_SET is ON_CHANGE property, the sample rate is 0.
2086         doAnswer((invocation) -> {
2087             serviceWrap.add(invocation.getArgument(0));
2088             return null;
2089         }).when(mVehicleHal).subscribeProperty(any(), anyList());
2090         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
2091 
2092         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_HVAC_REQUEST_ID_1),
2093                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
2094                 /* asyncRequestStartTime= */ 0);
2095 
2096         assertThat(setInvocationWrap).hasSize(1);
2097         assertThat(getInvocationWrap).hasSize(1);
2098 
2099         // Returns the set value result.
2100         deliverOkaySetResult(setInvocationWrap.get(0));
2101 
2102         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
2103         assertThat(mListArgumentCaptor.getValue()).containsExactly(hvacHalSubscribeOption());
2104 
2105         List<AsyncGetSetRequest> setAsyncRequests = setInvocationWrap.get(0).getArgument(0);
2106         VehicleStubCallbackInterface callback = setInvocationWrap.get(0).getArgument(1);
2107         callback.onRequestsTimeout(
2108                 List.of(setAsyncRequests.get(0).getServiceRequestId()));
2109 
2110         // Notify the property is updated to the target value after the request is cancelled.
2111         serviceWrap.get(0).onHalEvents(List.of(mPropValue));
2112 
2113         verify(mSetAsyncPropertyResultCallback).onSetValueResults(mAsyncResultCaptor.capture());
2114         assertThat(mAsyncResultCaptor.getValue().getList()).hasSize(1);
2115         assertThat(
2116                 mAsyncResultCaptor.getValue().getList().get(0).getCarPropertyErrorCodes()
2117                         .getCarPropertyManagerErrorCode())
2118                 .isEqualTo(CarPropertyManager.STATUS_ERROR_TIMEOUT);
2119         verify(mVehicleHal).unsubscribeProperty(any(), eq(HVAC_TEMPERATURE_SET));
2120 
2121         verifyNoPendingRequest();
2122     }
2123 
2124     // If we receive errors for the [propId, areaId] we are setting via onPropertySetError, we must
2125     // fail the pending request.
2126     @Test
testSetCarPropertyValuesAsync_onPropertySetError()2127     public void testSetCarPropertyValuesAsync_onPropertySetError() throws RemoteException {
2128         when(mMockCarPropertyConfig1.getAreaIds()).thenReturn(new int[]{1});
2129         List<HalServiceBase> serviceWrap = new ArrayList<>();
2130         AsyncPropertyServiceRequest setPropertyRequest =
2131                 new AsyncPropertyServiceRequest(1, HVAC_TEMPERATURE_SET, /* areaId= */ 1);
2132 
2133         doNothing().when(mVehicleHal).setAsync(anyList(),
2134                 any(VehicleStubCallbackInterface.class));
2135         doNothing().when(mVehicleHal).getAsync(anyList(),
2136                 any(VehicleStubCallbackInterface.class));
2137         doAnswer((invocation) -> {
2138             serviceWrap.add(invocation.getArgument(0));
2139             return null;
2140         }).when(mVehicleHal).subscribeProperty(any(), anyList());
2141         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
2142 
2143         mPropertyHalService.setCarPropertyValuesAsync(List.of(setPropertyRequest),
2144                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
2145                 /* asyncRequestStartTime= */ 0);
2146 
2147         ArrayList<VehiclePropError> vehiclePropErrors = new ArrayList<>();
2148         VehiclePropError error1 = new VehiclePropError();
2149         error1.propId = HVAC_TEMPERATURE_SET;
2150         error1.areaId = 1;
2151         error1.errorCode = STATUS_NOT_AVAILABLE | (0x1234 << 16);
2152         // Error 2 has the wrong area ID and must be ignored.
2153         VehiclePropError error2 = new VehiclePropError();
2154         error2.propId = HVAC_TEMPERATURE_SET;
2155         error2.areaId = 2;
2156         error2.errorCode = STATUS_INTERNAL_ERROR;
2157         vehiclePropErrors.add(error1);
2158         vehiclePropErrors.add(error2);
2159         assertThat(serviceWrap).hasSize(1);
2160         serviceWrap.get(0).onPropertySetError(vehiclePropErrors);
2161 
2162         verify(mSetAsyncPropertyResultCallback).onSetValueResults(mAsyncResultCaptor.capture());
2163         assertThat(mAsyncResultCaptor.getValue().getList()).hasSize(1);
2164         assertThat(
2165                 mAsyncResultCaptor.getValue().getList().get(0).getCarPropertyErrorCodes()
2166                         .getCarPropertyManagerErrorCode())
2167                 .isEqualTo(CarPropertyManager.STATUS_ERROR_NOT_AVAILABLE);
2168         assertThat(
2169                 mAsyncResultCaptor.getValue().getList().get(0).getCarPropertyErrorCodes()
2170                         .getVendorErrorCode())
2171                 .isEqualTo(0x1234);
2172         assertThat(mAsyncResultCaptor.getValue().getList().get(0).getCarPropertyErrorCodes()
2173                         .getSystemErrorCode())
2174                 .isEqualTo(STATUS_NOT_AVAILABLE);
2175         verify(mVehicleHal).unsubscribeProperty(any(), eq(HVAC_TEMPERATURE_SET));
2176 
2177         verifyNoPendingRequest();
2178     }
2179 
2180     @Test
testOnSetAsyncResults_RetryAndTimeout()2181     public void testOnSetAsyncResults_RetryAndTimeout() throws RemoteException {
2182         doAnswer((invocation) -> {
2183             // For every request, we return retry result.
2184             return deliverTryAgainSetResult(invocation);
2185         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
2186         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
2187 
2188         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_HVAC_REQUEST_ID_1),
2189                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 10,
2190                 /* asyncRequestStartTime= */ 0);
2191 
2192         verify(mSetAsyncPropertyResultCallback, timeout(1000)).onSetValueResults(
2193                 mAsyncResultCaptor.capture());
2194         assertThat(mAsyncResultCaptor.getValue().getList().get(0).getRequestId())
2195                 .isEqualTo(REQUEST_ID_1);
2196         assertThat(
2197                 mAsyncResultCaptor.getValue().getList().get(0).getCarPropertyErrorCodes()
2198                         .getCarPropertyManagerErrorCode())
2199                 .isEqualTo(CarPropertyManager.STATUS_ERROR_TIMEOUT);
2200 
2201         verifyNoPendingRequest();
2202     }
2203 
2204     @Test
testOnSetAsyncResults_TimeoutFromVehicleStub()2205     public void testOnSetAsyncResults_TimeoutFromVehicleStub() throws RemoteException {
2206         doAnswer((invocation) -> {
2207             Object[] args = invocation.getArguments();
2208             List requests = (List) args[0];
2209             AsyncGetSetRequest request = (AsyncGetSetRequest) requests.get(0);
2210             VehicleStubCallbackInterface callback = (VehicleStubCallbackInterface) args[1];
2211             // Simulate the request has already timed-out.
2212             callback.onRequestsTimeout(List.of(request.getServiceRequestId()));
2213             return null;
2214         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
2215 
2216         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
2217 
2218         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_HVAC_REQUEST_ID_1),
2219                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
2220                 /* asyncRequestStartTime= */ 0);
2221 
2222         verify(mSetAsyncPropertyResultCallback, timeout(1000)).onSetValueResults(
2223                 mAsyncResultCaptor.capture());
2224         assertThat(mAsyncResultCaptor.getValue().getList().get(0).getRequestId())
2225                 .isEqualTo(REQUEST_ID_1);
2226         assertThat(mAsyncResultCaptor.getValue().getList().get(0).getCarPropertyErrorCodes()
2227                         .getCarPropertyManagerErrorCode())
2228                 .isEqualTo(CarPropertyManager.STATUS_ERROR_TIMEOUT);
2229 
2230         verifyNoPendingRequest();
2231     }
2232 
2233     @Test
testOnSetAsyncResults_errorResult()2234     public void testOnSetAsyncResults_errorResult() throws RemoteException {
2235         doAnswer((invocation) -> {
2236             return deliverErrorSetResult(invocation, RECEIVED_REQUEST_ID_1,
2237                     CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
2238         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
2239         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
2240 
2241         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_HVAC_REQUEST_ID_1),
2242                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
2243                 /* asyncRequestStartTime= */ 0);
2244 
2245         verify(mSetAsyncPropertyResultCallback, timeout(1000)).onSetValueResults(
2246                 mAsyncResultCaptor.capture());
2247         GetSetValueResult result = mAsyncResultCaptor.getValue().getList().get(0);
2248         assertThat(result.getRequestId()).isEqualTo(REQUEST_ID_1);
2249         assertThat(result.getCarPropertyErrorCodes().getCarPropertyManagerErrorCode())
2250                 .isEqualTo(CarPropertyManager.STATUS_ERROR_INTERNAL_ERROR);
2251         assertThat(result.getCarPropertyValue()).isEqualTo(null);
2252 
2253         verifyNoPendingRequest();
2254     }
2255 
2256     @Test
setProperty_handlesHalAndMgrPropIdMismatch()2257     public void setProperty_handlesHalAndMgrPropIdMismatch() {
2258         HalPropConfig mockPropConfig = mock(HalPropConfig.class);
2259         CarPropertyValue mockCarPropertyValue = mock(CarPropertyValue.class);
2260         when(mockCarPropertyValue.getPropertyId()).thenReturn(
2261                 VehiclePropertyIds.VEHICLE_SPEED_DISPLAY_UNITS);
2262         when(mockCarPropertyValue.getAreaId()).thenReturn(0);
2263         when(mockCarPropertyValue.getValue()).thenReturn(1.0f);
2264         when(mockPropConfig.getPropId()).thenReturn(VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS);
2265         mPropertyHalService.takeProperties(List.of(mockPropConfig));
2266 
2267         mPropertyHalService.setProperty(mockCarPropertyValue);
2268 
2269         HalPropValue value = mPropValueBuilder.build(
2270                 VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS, /* areaId= */ 0, /* value= */ 1.0f);
2271         verify(mVehicleHal).set(value);
2272 
2273         verifyNoPendingRequest();
2274     }
2275 
2276     @Test
testCancelRequests_getCarPropertyValuesAsync()2277     public void testCancelRequests_getCarPropertyValuesAsync() throws Exception {
2278         doReturn(mGetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
2279         List<InvocationOnMock> invocationWrap = new ArrayList<>();
2280         doAnswer((invocation) -> {
2281             invocationWrap.add(invocation);
2282             return null;
2283         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
2284 
2285         mPropertyHalService.getCarPropertyValuesAsync(List.of(
2286                 GET_PROPERTY_SERVICE_REQUEST_1, GET_PROPERTY_SERVICE_REQUEST_2),
2287                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
2288                 /* asyncRequestStartTime= */ 0);
2289 
2290         assertThat(invocationWrap).hasSize(1);
2291 
2292         // Cancel the first request.
2293         mPropertyHalService.cancelRequests(new int[]{REQUEST_ID_1});
2294 
2295         verify(mVehicleHal).cancelRequests(List.of(0));
2296 
2297         // Deliver the results.
2298         deliverOkayGetResult(invocationWrap.get(0));
2299 
2300         // We should only get the result for request 2.
2301         verify(mGetAsyncPropertyResultCallback, timeout(1000)).onGetValueResults(
2302                 mAsyncResultCaptor.capture());
2303         assertThat(mAsyncResultCaptor.getValue().getList().get(0).getRequestId())
2304                 .isEqualTo(REQUEST_ID_2);
2305 
2306         verifyNoPendingRequest();
2307     }
2308 
2309     @Test
testCancelRequests_setCarPropertyValuesAsync()2310     public void testCancelRequests_setCarPropertyValuesAsync() throws Exception {
2311         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
2312         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
2313         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
2314 
2315         doAnswer((invocation) -> {
2316             setInvocationWrap.add(invocation);
2317             return null;
2318         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
2319         doAnswer((invocation) -> {
2320             getInvocationWrap.add(invocation);
2321             return null;
2322         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
2323 
2324         mPropertyHalService.setCarPropertyValuesAsync(List.of(
2325                 SET_HVAC_REQUEST_ID_1, SET_SPEED_REQUEST_ID_2),
2326                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
2327                 /* asyncRequestStartTime= */ 0);
2328 
2329         assertThat(setInvocationWrap).hasSize(1);
2330         assertThat(getInvocationWrap).hasSize(1);
2331 
2332         // Cancel the first request.
2333         mPropertyHalService.cancelRequests(new int[]{REQUEST_ID_1});
2334 
2335         // Note that because both the async set and the get init value request has the same manager
2336         // request ID, so they will all be cancelled.
2337         verify(mVehicleHal, timeout(1000).times(2)).cancelRequests(any());
2338 
2339         // Returns the set value result.
2340         deliverOkaySetResult(setInvocationWrap.get(0));
2341         // Returns the get initial value result.
2342         deliverOkayGetResult(getInvocationWrap.get(0));
2343 
2344         // We should only get the result for request 2.
2345         verify(mSetAsyncPropertyResultCallback, timeout(1000)).onSetValueResults(
2346                 mAsyncResultCaptor.capture());
2347         assertThat(mAsyncResultCaptor.getValue().getList().get(0).getRequestId())
2348                 .isEqualTo(REQUEST_ID_2);
2349 
2350         verifyNoPendingRequest();
2351     }
2352 
2353     @Test
testCancelRequests_noPendingRequests()2354     public void testCancelRequests_noPendingRequests() {
2355         mPropertyHalService.cancelRequests(new int[]{0});
2356 
2357         verify(mVehicleHal, never()).cancelRequests(any());
2358 
2359         verifyNoPendingRequest();
2360     }
2361 
2362     // Test that when the client binder died, all pending async requests must be cleared.
2363     @Test
testOnBinderDied()2364     public void testOnBinderDied() throws Exception {
2365         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
2366         // We return the same binder object for both set and get here.
2367         doReturn(mSetAsyncPropertyResultBinder).when(mGetAsyncPropertyResultCallback).asBinder();
2368         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
2369         List<InvocationOnMock> getInvocationWrap = new ArrayList<>();
2370 
2371         doAnswer((invocation) -> {
2372             setInvocationWrap.add(invocation);
2373             return null;
2374         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
2375         doAnswer((invocation) -> {
2376             getInvocationWrap.add(invocation);
2377             return null;
2378         }).when(mVehicleHal).getAsync(anyList(), any(VehicleStubCallbackInterface.class));
2379 
2380         mPropertyHalService.getCarPropertyValuesAsync(List.of(GET_PROPERTY_SERVICE_REQUEST_1),
2381                 mGetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
2382                 /* asyncRequestStartTime= */ 0);
2383         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_SPEED_REQUEST_ID_2),
2384                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
2385                 /* asyncRequestStartTime= */ 0);
2386 
2387         verify(mSetAsyncPropertyResultBinder).linkToDeath(mDeathRecipientCaptor.capture(),
2388                 anyInt());
2389 
2390         assertThat(setInvocationWrap).hasSize(1);
2391         // One is for async get, one is for initial value.
2392         assertThat(getInvocationWrap).hasSize(2);
2393 
2394         // Simulate client binder died.
2395         mDeathRecipientCaptor.getValue().binderDied();
2396 
2397         verify(mSetAsyncPropertyResultBinder).unlinkToDeath(any(), anyInt());
2398 
2399         // All async pending requests must be cleared.
2400         verifyNoPendingRequest();
2401     }
2402 
2403     @Test
testGetPropertySync()2404     public void testGetPropertySync() throws Exception {
2405         HalPropValue value = mPropValueBuilder.build(INT32_PROP, /* areaId= */ 0, PROPERTY_VALUE);
2406         when(mVehicleHal.get(INT32_PROP, /* areaId= */ 0)).thenReturn(value);
2407 
2408         CarPropertyValue carPropValue = mPropertyHalService.getProperty(
2409                 INT32_PROP, /* areaId= */ 0);
2410 
2411         assertThat(carPropValue.getValue()).isEqualTo(PROPERTY_VALUE);
2412     }
2413 
2414     @Test
testGetPropertySyncWithCache()2415     public void testGetPropertySyncWithCache() throws Exception {
2416         HalPropValue value = mPropValueBuilder.build(INT32_PROP, /* areaId= */ 0, PROPERTY_VALUE);
2417         when(mVehicleHal.get(INT32_PROP, /* areaId= */ 0)).thenReturn(value);
2418         mPropertyHalService.getProperty(INT32_PROP, /* areaId= */ 0);
2419         reset(mVehicleHal);
2420 
2421         CarPropertyValue carPropValue = mPropertyHalService.getProperty(
2422                 INT32_PROP, /* areaId= */ 0);
2423 
2424         assertWithMessage("CarPropertyValue cached value").that(carPropValue.getValue())
2425                 .isEqualTo(PROPERTY_VALUE);
2426         verify(mVehicleHal, never()).get(anyInt(), anyInt());
2427     }
2428 
2429     @Test
testGetPropertySyncErrorPropStatus()2430     public void testGetPropertySyncErrorPropStatus() throws Exception {
2431         HalPropValue value = mPropValueBuilder.build(
2432                 AidlVehiclePropValueBuilder.newBuilder(INT32_PROP)
2433                         .setStatus(VehiclePropertyStatus.ERROR).addIntValues(0).build());
2434         when(mVehicleHal.get(INT32_PROP, /* areaId= */ 0)).thenReturn(value);
2435 
2436         assertThat(mPropertyHalService.getProperty(INT32_PROP, /*areaId=*/0)).isEqualTo(
2437                 new CarPropertyValue<>(INT32_PROP, /*areaId=*/0,
2438                         CarPropertyValue.STATUS_ERROR, /*timestampNanos=*/0, Integer.valueOf(0)));
2439     }
2440 
2441     @Test
testGetPropertySyncUnavailablePropStatus()2442     public void testGetPropertySyncUnavailablePropStatus() throws Exception {
2443         HalPropValue value = mPropValueBuilder.build(
2444                 AidlVehiclePropValueBuilder.newBuilder(INT32_PROP)
2445                         .setStatus(VehiclePropertyStatus.UNAVAILABLE).addIntValues(0).build());
2446         when(mVehicleHal.get(INT32_PROP, /* areaId= */ 0)).thenReturn(value);
2447 
2448         assertThat(mPropertyHalService.getProperty(INT32_PROP, /*areaId=*/0)).isEqualTo(
2449                 new CarPropertyValue<>(INT32_PROP, /*areaId=*/0,
2450                         CarPropertyValue.STATUS_UNAVAILABLE, /*timestampNanos=*/0,
2451                         Integer.valueOf(0)));
2452     }
2453 
2454     @Test
testGetPropertySyncInvalidProp()2455     public void testGetPropertySyncInvalidProp() throws Exception {
2456         // This property has no valid int array element.
2457         HalPropValue value = mPropValueBuilder.build(
2458                 AidlVehiclePropValueBuilder.newBuilder(INT32_PROP).build());
2459         when(mVehicleHal.get(INT32_PROP, /* areaId= */ 0)).thenReturn(value);
2460 
2461         assertThat(mPropertyHalService.getProperty(INT32_PROP, /*areaId=*/0)).isEqualTo(
2462                 new CarPropertyValue<>(INT32_PROP, /*areaId=*/0,
2463                         CarPropertyValue.STATUS_ERROR, /*timestampNanos=*/0, Integer.valueOf(0)));
2464     }
2465 
2466     @Test
testOnPropertySetError()2467     public void testOnPropertySetError() throws Exception {
2468         ArrayList<VehiclePropError> vehiclePropErrors = new ArrayList<>();
2469         VehiclePropError error1 = new VehiclePropError();
2470         error1.propId = HVAC_TEMPERATURE_SET;
2471         error1.areaId = 1;
2472         error1.errorCode = STATUS_NOT_AVAILABLE | (0x1234 << 16);
2473         VehiclePropError error2 = new VehiclePropError();
2474         error2.propId = PERF_VEHICLE_SPEED;
2475         error2.areaId = 0;
2476         error2.errorCode = STATUS_INTERNAL_ERROR;
2477         vehiclePropErrors.add(error1);
2478         vehiclePropErrors.add(error2);
2479 
2480         mPropertyHalService.setPropertyHalListener(mPropertyHalListener);
2481         mPropertyHalService.onPropertySetError(vehiclePropErrors);
2482 
2483         verify(mPropertyHalListener).onPropertySetError(HVAC_TEMPERATURE_SET, 1,
2484                 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_PROPERTY_NOT_AVAILABLE);
2485         verify(mPropertyHalListener).onPropertySetError(PERF_VEHICLE_SPEED, 0,
2486                 CarPropertyManager.CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN);
2487     }
2488 
2489     @Test
testNoCustomizeVendorPermission()2490     public void testNoCustomizeVendorPermission() throws Exception {
2491         HalPropConfig vendor1Config = mock(HalPropConfig.class);
2492         when(vendor1Config.getPropId()).thenReturn(VENDOR_PROPERTY_1);
2493         when(mVehicleHal.getPropConfig(SUPPORT_CUSTOMIZE_VENDOR_PERMISSION)).thenReturn(null);
2494 
2495         mPropertyHalService.takeProperties(List.of(vendor1Config));
2496 
2497         // By default we require PERMISSION_VENDOR_EXTENSION for getting/setting vendor props.
2498         assertThat(mPropertyHalService.getReadPermission(VENDOR_PROPERTY_1))
2499                 .isEqualTo(PERMISSION_VENDOR_EXTENSION);
2500         assertThat(mPropertyHalService.getWritePermission(VENDOR_PROPERTY_1))
2501                 .isEqualTo(PERMISSION_VENDOR_EXTENSION);
2502     }
2503 
2504     @Test
testCustomizeVendorPermission()2505     public void testCustomizeVendorPermission() throws Exception {
2506         HalPropConfig mockVendorPermConfig = mock(HalPropConfig.class);
2507         // Use the same test config we used in PropertyHalServiceTest.
2508         when(mockVendorPermConfig.getConfigArray()).thenReturn(new int[]{
2509                 VENDOR_PROPERTY_1,
2510                 VehicleVendorPermission.PERMISSION_DEFAULT,
2511                 VehicleVendorPermission.PERMISSION_NOT_ACCESSIBLE,
2512                 VENDOR_PROPERTY_2,
2513                 VehicleVendorPermission.PERMISSION_GET_VENDOR_CATEGORY_ENGINE,
2514                 VehicleVendorPermission.PERMISSION_SET_VENDOR_CATEGORY_ENGINE,
2515                 VENDOR_PROPERTY_3,
2516                 VehicleVendorPermission.PERMISSION_GET_VENDOR_CATEGORY_INFO,
2517                 VehicleVendorPermission.PERMISSION_DEFAULT
2518         });
2519         when(mVehicleHal.getPropConfig(SUPPORT_CUSTOMIZE_VENDOR_PERMISSION)).thenReturn(
2520                 mockVendorPermConfig);
2521         HalPropConfig vendor1Config = mock(HalPropConfig.class);
2522         when(vendor1Config.getPropId()).thenReturn(VENDOR_PROPERTY_1);
2523         HalPropConfig vendor2Config = mock(HalPropConfig.class);
2524         when(vendor2Config.getPropId()).thenReturn(VENDOR_PROPERTY_2);
2525         HalPropConfig vendor3Config = mock(HalPropConfig.class);
2526         when(vendor3Config.getPropId()).thenReturn(VENDOR_PROPERTY_3);
2527 
2528         mPropertyHalService.takeProperties(List.of(vendor1Config, vendor2Config,
2529                 vendor3Config));
2530 
2531         assertThat(mPropertyHalService.getReadPermission(VENDOR_PROPERTY_1))
2532                 .isEqualTo(PERMISSION_VENDOR_EXTENSION);
2533         assertThat(mPropertyHalService.getWritePermission(VENDOR_PROPERTY_1)).isNull();
2534         assertThat(mPropertyHalService.getReadPermission(VENDOR_PROPERTY_2))
2535                 .isEqualTo(PERMISSION_GET_CAR_VENDOR_CATEGORY_ENGINE);
2536         assertThat(mPropertyHalService.getWritePermission(VENDOR_PROPERTY_2))
2537                 .isEqualTo(PERMISSION_SET_CAR_VENDOR_CATEGORY_ENGINE);
2538         assertThat(mPropertyHalService.getReadPermission(VENDOR_PROPERTY_3))
2539                 .isEqualTo(PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO);
2540         assertThat(mPropertyHalService.getWritePermission(VENDOR_PROPERTY_3))
2541                 .isEqualTo(PERMISSION_VENDOR_EXTENSION);
2542     }
2543 
2544     @Test
testHalSubscribeOptions_equals()2545     public void testHalSubscribeOptions_equals() {
2546         HalSubscribeOptions options1 = new HalSubscribeOptions(/* halPropId= */ 5,
2547                 /* areaIds= */ new int[]{0, 3}, /* updateRateHz= */ 53f);
2548         HalSubscribeOptions options2 = new HalSubscribeOptions(/* halPropId= */ 5,
2549                 /* areaIds= */ new int[]{0, 3}, /* updateRateHz= */ 53f);
2550 
2551         assertWithMessage("Equal hal subscribe options")
2552                 .that(options1.equals(options2)).isTrue();
2553     }
2554 
2555     @Test
testHalSubscribeOptions_notEquals()2556     public void testHalSubscribeOptions_notEquals() {
2557         HalSubscribeOptions options1 = new HalSubscribeOptions(/* halPropId= */ 5,
2558                 /* areaIds= */ new int[]{0, 3}, /* updateRateHz= */ 55f,
2559                 /* enableVariableUpdateRate= */ true);
2560         HalSubscribeOptions options2 = new HalSubscribeOptions(/* halPropId= */ 5,
2561                 /* areaIds= */ new int[]{0, 3}, /* updateRateHz= */ 53f,
2562                 /* enableVariableUpdateRate= */ true);
2563 
2564         assertWithMessage("Non-equal hal subscribe options")
2565                 .that(options1.equals(options2)).isFalse();
2566     }
2567 
2568     @Test
testHalSubscribeOptions_hashcode()2569     public void testHalSubscribeOptions_hashcode() {
2570         HalSubscribeOptions options1 = new HalSubscribeOptions(/* halPropId= */ 5,
2571                 /* areaIds= */ new int[]{0, 3}, /* updateRateHz= */ 53f,
2572                 /* enableVariableUpdateRate= */ true);
2573         HalSubscribeOptions options2 = new HalSubscribeOptions(/* halPropId= */ 5,
2574                 /* areaIds= */ new int[]{0, 3}, /* updateRateHz= */ 53f,
2575                 /* enableVariableUpdateRate= */ true);
2576 
2577         assertWithMessage("Hashcode hal subscribe options")
2578                 .that(options1.hashCode()).isEqualTo(options2.hashCode());
2579     }
2580 
2581     /**
2582      * This test is for verifying when we need to update sample rate for multiple properties, both
2583      * subscribeProperty and unsubscribeProperty may happen.
2584      */
2585     @Test
testOnHalEvnts_unsubscribeAndUpdateSampleRates()2586     public void testOnHalEvnts_unsubscribeAndUpdateSampleRates()
2587             throws Exception {
2588         List<InvocationOnMock> setInvocationWrap = new ArrayList<>();
2589         List<HalServiceBase> serviceWrap = new ArrayList<>();
2590 
2591         doAnswer((invocation) -> {
2592             setInvocationWrap.add(invocation);
2593             return null;
2594         }).when(mVehicleHal).setAsync(anyList(), any(VehicleStubCallbackInterface.class));
2595         doAnswer((invocation) -> {
2596             serviceWrap.add(invocation.getArgument(0));
2597             return null;
2598         }).when(mVehicleHal).subscribeProperty(any(), anyList());
2599         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
2600 
2601         // Subscribe to speed at 40hz.
2602         mPropertyHalService.subscribeProperty(List.of(
2603                 createCarSubscriptionOption(
2604                         PERF_VEHICLE_SPEED, new int[]{0}, /* updateRateHz= */ 40.0f,
2605                         /* enableVur= */ true)));
2606         // Issues set async request, which should cause speed to be subscribed at 100hz.
2607         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_HVAC_REQUEST_ID_1),
2608                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
2609                 /* asyncRequestStartTime= */ 0);
2610         // Issues set async request, which should cause hvac to be subscribed at 0hz.
2611         mPropertyHalService.setCarPropertyValuesAsync(List.of(SET_SPEED_REQUEST_ID_2),
2612                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
2613                 /* asyncRequestStartTime= */ 0);
2614 
2615         // Returns the set value result.
2616         deliverOkaySetResult(setInvocationWrap.get(0));
2617         deliverOkaySetResult(setInvocationWrap.get(1));
2618 
2619         verify(mVehicleHal, times(3)).subscribeProperty(any(), mListArgumentCaptor.capture());
2620         // This is caused by subscribeProperty.
2621         assertThat(mListArgumentCaptor.getAllValues().get(0)).containsExactly(
2622                 speedHalSubscribeOption(40.0f));
2623         // This is caused by setCarPropertyValuesAsync.
2624         assertThat(mListArgumentCaptor.getAllValues().get(1)).containsExactly(
2625                 hvacHalSubscribeOption());
2626         // This is caused by setCarPropertyValuesAsync.
2627         assertThat(mListArgumentCaptor.getAllValues().get(2)).containsExactly(
2628                 speedHalSubscribeOption(100.0f));
2629 
2630         clearInvocations(mVehicleHal);
2631 
2632         // Send change value events for set requests.
2633         serviceWrap.get(0).onHalEvents(List.of(mPropValue, mPropValue2));
2634 
2635         // We no longer need to subscribe to temp.
2636         verify(mVehicleHal).unsubscribeProperty(any(), eq(HVAC_TEMPERATURE_SET));
2637         // Speed should be subscribed at 40hz.
2638         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
2639         assertThat(mListArgumentCaptor.getAllValues().get(3)).containsExactly(
2640                 speedHalSubscribeOption(40.0f));
2641 
2642         verifyNoPendingRequest();
2643     }
2644 
2645     @Test
testSubscribeProperty_exceptionFromVhal()2646     public void testSubscribeProperty_exceptionFromVhal() throws Exception {
2647         doThrow(new ServiceSpecificException(0)).when(mVehicleHal).subscribeProperty(
2648                 any(), anyList());
2649 
2650         assertThrows(ServiceSpecificException.class, () ->
2651                 mPropertyHalService.subscribeProperty(List.of(
2652                         createCarSubscriptionOption(
2653                                 PERF_VEHICLE_SPEED, new int[]{0}, /* updateRateHz= */ 40.0f,
2654                                 /* enableVur= */ true))));
2655     }
2656 
2657     /**
2658      * Tests that if we receive exception from underlying layer, client must be able to retry the
2659      * operation and causes a retry to VHAL. If the error goes away in VHAL, the retry must succeed.
2660      */
2661     @Test
testSubscribeProperty_exceptionFromVhal_retryFixed()2662     public void testSubscribeProperty_exceptionFromVhal_retryFixed() throws Exception {
2663         doThrow(new ServiceSpecificException(0)).when(mVehicleHal).subscribeProperty(
2664                 any(), anyList());
2665 
2666         assertThrows(ServiceSpecificException.class, () ->
2667                 mPropertyHalService.subscribeProperty(List.of(
2668                         createCarSubscriptionOption(
2669                                 PERF_VEHICLE_SPEED, new int[]{0}, /* updateRateHz= */ 40.0f,
2670                                 /* enableVur= */ true))));
2671 
2672         clearInvocations(mVehicleHal);
2673         // Simulate the error has been fixed.
2674         doNothing().when(mVehicleHal).subscribeProperty(any(), anyList());
2675 
2676         // Retry the operation.
2677         mPropertyHalService.subscribeProperty(List.of(
2678                 createCarSubscriptionOption(
2679                         PERF_VEHICLE_SPEED, new int[]{0}, /* updateRateHz= */ 40.0f,
2680                         /* enableVur= */ true)));
2681 
2682         // The retry request must go to VehicleHal.
2683         verify(mVehicleHal).subscribeProperty(any(), anyList());
2684 
2685         mPropertyHalService.unsubscribeProperty(PERF_VEHICLE_SPEED);
2686 
2687         verify(mVehicleHal).unsubscribeProperty(any(), eq(PERF_VEHICLE_SPEED));
2688 
2689         verifyNoPendingRequest();
2690     }
2691 
2692     @Test
testSubscribeProperty_enableVur()2693     public void testSubscribeProperty_enableVur() throws Exception {
2694         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
2695 
2696         mPropertyHalService.subscribeProperty(List.of(createCarSubscriptionOption(
2697                 PERF_VEHICLE_SPEED, new int[]{0}, /* updateRateHz= */ 40.0f,
2698                 /* enableVur= */ true)));
2699 
2700         // Subscription rate has to be updated according to client subscription.
2701         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
2702         assertThat(mListArgumentCaptor.getValue()).containsExactly(speedHalSubscribeOption(40f));
2703     }
2704 
2705     @Test
testSubscribeProperty_disablesVur()2706     public void testSubscribeProperty_disablesVur() throws Exception {
2707         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
2708 
2709         mPropertyHalService.subscribeProperty(List.of(createCarSubscriptionOption(
2710                 PERF_VEHICLE_SPEED, new int[]{0}, /* updateRateHz= */ 40.0f,
2711                 /* enableVur= */ false)));
2712 
2713         // Subscription rate has to be updated according to client subscription.
2714         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
2715         assertThat(mListArgumentCaptor.getValue()).containsExactly(new HalSubscribeOptions(
2716                 PERF_VEHICLE_SPEED, new int[]{0}, 40.0f, /* enableVariableUpdateRate= */ false));
2717     }
2718 
2719     @Test
testSubscribeProperty_withResolution()2720     public void testSubscribeProperty_withResolution() throws Exception {
2721         mListArgumentCaptor = ArgumentCaptor.forClass(List.class);
2722 
2723         mPropertyHalService.subscribeProperty(List.of(createCarSubscriptionOption(
2724                 PERF_VEHICLE_SPEED, new int[]{0}, /* updateRateHz= */ 40.0f,
2725                 /* enableVur= */ false, /* resolution */ 1.0f)));
2726 
2727         // Subscription rate has to be updated according to client subscription.
2728         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
2729         assertThat(mListArgumentCaptor.getValue()).containsExactly(new HalSubscribeOptions(
2730                 PERF_VEHICLE_SPEED, new int[]{0}, 40.0f, /* enableVariableUpdateRate= */ false,
2731                 /* resolution */ 1.0f));
2732     }
2733 
2734     @Test
testSubscribeProperty_setAsync_clientDisablesVur()2735     public void testSubscribeProperty_setAsync_clientDisablesVur() throws Exception {
2736         doReturn(mSetAsyncPropertyResultBinder).when(mSetAsyncPropertyResultCallback).asBinder();
2737         AsyncPropertyServiceRequest request = copyRequest(SET_SPEED_REQUEST_ID_2);
2738         request.setUpdateRateHz(20.0f);
2739 
2740         mPropertyHalService.setCarPropertyValuesAsync(List.of(request),
2741                 mSetAsyncPropertyResultCallback, /* timeoutInMs= */ 1000,
2742                 /* asyncRequestStartTime= */ 0);
2743 
2744         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
2745         assertThat(mListArgumentCaptor.getValue()).containsExactly(speedHalSubscribeOption(20f));
2746         clearInvocations(mVehicleHal);
2747 
2748         // Client disables Vur.
2749         mPropertyHalService.subscribeProperty(List.of(createCarSubscriptionOption(
2750                 PERF_VEHICLE_SPEED, new int[]{0}, /* updateRateHz= */ 10.0f,
2751                 /* enableVur= */ false)));
2752 
2753         // Vur must be turned off.
2754         verify(mVehicleHal).subscribeProperty(any(), mListArgumentCaptor.capture());
2755         assertThat(mListArgumentCaptor.getValue()).containsExactly(new HalSubscribeOptions(
2756                 PERF_VEHICLE_SPEED, new int[]{0}, 20.0f, /* enableVariableUpdateRate= */ false));
2757 
2758         mPropertyHalService.cancelRequests(new int[]{REQUEST_ID_2});
2759     }
2760 
2761     @Test
testUnubscribeProperty_exceptionFromVhal()2762     public void testUnubscribeProperty_exceptionFromVhal() throws Exception {
2763         mPropertyHalService.subscribeProperty(List.of(
2764                 createCarSubscriptionOption(
2765                         PERF_VEHICLE_SPEED, new int[]{0}, /* updateRateHz= */ 40.0f,
2766                         /* enableVur= */ true)));
2767         doThrow(new ServiceSpecificException(0)).when(mVehicleHal).unsubscribeProperty(
2768                 any(), anyInt());
2769 
2770         assertThrows(ServiceSpecificException.class, () ->
2771                 mPropertyHalService.unsubscribeProperty(PERF_VEHICLE_SPEED));
2772     }
2773 
2774     /**
2775      * Tests that if we receive exception from underlying layer, client must be able to retry the
2776      * operation and causes a retry to VHAL. If the error goes away in VHAL, the retry must succeed.
2777      */
2778     @Test
tesUnsubscribeProperty_exceptionFromVhal_retryFixed()2779     public void tesUnsubscribeProperty_exceptionFromVhal_retryFixed() throws Exception {
2780         mPropertyHalService.subscribeProperty(List.of(
2781                 createCarSubscriptionOption(
2782                         PERF_VEHICLE_SPEED, new int[]{0}, /* updateRateHz= */ 40.0f,
2783                         /* enableVur= */ true)));
2784         doThrow(new ServiceSpecificException(0)).when(mVehicleHal).unsubscribeProperty(
2785                 any(), anyInt());
2786 
2787         assertThrows(ServiceSpecificException.class, () ->
2788                 mPropertyHalService.unsubscribeProperty(PERF_VEHICLE_SPEED));
2789 
2790         // Simulate the error has been fixed.
2791         clearInvocations(mVehicleHal);
2792         doNothing().when(mVehicleHal).unsubscribeProperty(any(), anyInt());
2793 
2794         mPropertyHalService.unsubscribeProperty(PERF_VEHICLE_SPEED);
2795 
2796         // The retry request must go to VehicleHal.
2797         verify(mVehicleHal).unsubscribeProperty(any(), eq(PERF_VEHICLE_SPEED));
2798     }
2799 
2800     /** Creates a {@code CarSubscription} with Vur off. */
2801     @VisibleForTesting
createCarSubscriptionOption(int propertyId, int[] areaId, float updateRateHz)2802     public static CarSubscription createCarSubscriptionOption(int propertyId,
2803             int[] areaId, float updateRateHz) {
2804         return createCarSubscriptionOption(propertyId, areaId, updateRateHz,
2805                 /* enableVur= */ false, /*resolution*/ 0.0f);
2806     }
2807 
2808     /** Creates a {@code CarSubscription}. */
2809     @VisibleForTesting
createCarSubscriptionOption(int propertyId, int[] areaId, float updateRateHz, boolean enableVur)2810     public static CarSubscription createCarSubscriptionOption(int propertyId,
2811             int[] areaId, float updateRateHz, boolean enableVur) {
2812         return createCarSubscriptionOption(propertyId, areaId, updateRateHz,
2813                 enableVur, /*resolution*/ 0.0f);
2814     }
2815 
2816     /** Creates a {@code CarSubscription}. */
2817     @VisibleForTesting
createCarSubscriptionOption(int propertyId, int[] areaId, float updateRateHz, boolean enableVur, float resolution)2818     public static CarSubscription createCarSubscriptionOption(int propertyId,
2819             int[] areaId, float updateRateHz, boolean enableVur, float resolution) {
2820         CarSubscription options = new CarSubscription();
2821         options.propertyId = propertyId;
2822         options.areaIds = areaId;
2823         options.updateRateHz = updateRateHz;
2824         options.enableVariableUpdateRate = enableVur;
2825         options.resolution = resolution;
2826         return options;
2827     }
2828 
hvacHalSubscribeOption()2829     private static HalSubscribeOptions hvacHalSubscribeOption() {
2830         return new HalSubscribeOptions(HVAC_TEMPERATURE_SET, new int[]{0}, 0.0f,
2831                 /* enableVariableUpdateRate= */ false);
2832     }
2833 
speedHalSubscribeOption(float speed)2834     private static HalSubscribeOptions speedHalSubscribeOption(float speed) {
2835         return new HalSubscribeOptions(
2836                 PERF_VEHICLE_SPEED, new int[]{0}, speed, /* enableVariableUpdateRate= */ true);
2837     }
2838 }
2839