1 /*
2  * Copyright (C) 2020 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 package com.android.car.hal;
17 
18 import static android.car.VehiclePropertyIds.CREATE_USER;
19 import static android.car.VehiclePropertyIds.CURRENT_GEAR;
20 import static android.car.VehiclePropertyIds.INITIAL_USER_INFO;
21 import static android.car.VehiclePropertyIds.REMOVE_USER;
22 import static android.car.VehiclePropertyIds.SWITCH_USER;
23 import static android.car.VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION;
24 import static android.hardware.automotive.vehicle.InitialUserInfoRequestType.COLD_BOOT;
25 import static android.hardware.automotive.vehicle.UserIdentificationAssociationSetValue.ASSOCIATE_CURRENT_USER;
26 import static android.hardware.automotive.vehicle.UserIdentificationAssociationType.CUSTOM_1;
27 import static android.hardware.automotive.vehicle.UserIdentificationAssociationType.KEY_FOB;
28 import static android.hardware.automotive.vehicle.UserIdentificationAssociationValue.ASSOCIATED_CURRENT_USER;
29 
30 import static com.android.car.hal.HalPropValueMatcher.isProperty;
31 import static com.android.car.hal.HalPropValueMatcher.isPropertyWithValues;
32 import static com.android.car.hal.VehicleHalTestingHelper.newConfig;
33 import static com.android.car.hal.VehicleHalTestingHelper.newSubscribableConfig;
34 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
35 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
36 
37 import static com.google.common.truth.Truth.assertThat;
38 import static com.google.common.truth.Truth.assertWithMessage;
39 
40 import static org.junit.Assert.assertThrows;
41 import static org.junit.Assert.fail;
42 import static org.mockito.ArgumentMatchers.anyInt;
43 import static org.mockito.ArgumentMatchers.anyString;
44 import static org.mockito.ArgumentMatchers.eq;
45 import static org.mockito.Mockito.doAnswer;
46 import static org.mockito.Mockito.doThrow;
47 import static org.mockito.Mockito.never;
48 import static org.mockito.Mockito.spy;
49 import static org.mockito.Mockito.timeout;
50 import static org.mockito.Mockito.times;
51 import static org.mockito.Mockito.when;
52 
53 import android.annotation.NonNull;
54 import android.annotation.Nullable;
55 import android.car.hardware.property.VehicleHalStatusCode;
56 import android.car.test.mocks.AbstractExtendedMockitoTestCase;
57 import android.car.test.mocks.JavaMockitoHelper;
58 import android.hardware.automotive.vehicle.CreateUserRequest;
59 import android.hardware.automotive.vehicle.CreateUserResponse;
60 import android.hardware.automotive.vehicle.CreateUserStatus;
61 import android.hardware.automotive.vehicle.InitialUserInfoResponse;
62 import android.hardware.automotive.vehicle.InitialUserInfoResponseAction;
63 import android.hardware.automotive.vehicle.RemoveUserRequest;
64 import android.hardware.automotive.vehicle.SwitchUserMessageType;
65 import android.hardware.automotive.vehicle.SwitchUserRequest;
66 import android.hardware.automotive.vehicle.SwitchUserResponse;
67 import android.hardware.automotive.vehicle.SwitchUserStatus;
68 import android.hardware.automotive.vehicle.UserIdentificationAssociation;
69 import android.hardware.automotive.vehicle.UserIdentificationGetRequest;
70 import android.hardware.automotive.vehicle.UserIdentificationResponse;
71 import android.hardware.automotive.vehicle.UserIdentificationSetAssociation;
72 import android.hardware.automotive.vehicle.UserIdentificationSetRequest;
73 import android.hardware.automotive.vehicle.UserInfo;
74 import android.hardware.automotive.vehicle.UsersInfo;
75 import android.os.Handler;
76 import android.os.Looper;
77 import android.os.ServiceSpecificException;
78 import android.os.SystemClock;
79 import android.os.UserHandle;
80 import android.util.Log;
81 import android.util.Pair;
82 
83 import com.android.car.CarLocalServices;
84 import com.android.car.CarStatsLog;
85 import com.android.car.internal.os.CarSystemProperties;
86 import com.android.car.user.CarUserService;
87 
88 import org.junit.After;
89 import org.junit.Before;
90 import org.junit.Test;
91 import org.junit.runner.RunWith;
92 import org.mockito.ArgumentCaptor;
93 import org.mockito.Mock;
94 import org.mockito.junit.MockitoJUnitRunner;
95 
96 import java.util.ArrayList;
97 import java.util.Arrays;
98 import java.util.Collections;
99 import java.util.List;
100 import java.util.Optional;
101 import java.util.concurrent.CountDownLatch;
102 import java.util.concurrent.TimeUnit;
103 import java.util.concurrent.atomic.AtomicReference;
104 
105 @RunWith(MockitoJUnitRunner.class)
106 public final class UserHalServiceTest extends AbstractExtendedMockitoTestCase {
107 
108     private static final String TAG = UserHalServiceTest.class.getSimpleName();
109 
110     /**
111      * Timeout passed to {@link UserHalService} methods. This is the timeout for which
112      * {@link UserHalService} wait for the property change event and return
113      * {@link HalCallback.STATUS_HAL_RESPONSE_TIMEOUT} if HAL doesn't respond. This timeout is used
114      * where we expect HAL to return something and {@link UserHalService} methods are not expected
115      * to timeout. Tests are not supposed to wait for this much so this value can be high. If test
116      * requires HAL to timeout, then use {@link HAL_TIMEOUT_MS}.
117      */
118     private static final int HAL_TIMEOUT_MS = 5_000;
119 
120     /**
121      * Timeout passed to {@link UserHalService} methods only if test expects that call will be
122      * timeout and {@link HalCallback.STATUS_HAL_RESPONSE_TIMEOUT} is expected response. A higher
123      * value for this is going to slow down tests. If this timeout is used, then it is expected that
124      * {@link HalCallback.STATUS_HAL_RESPONSE_TIMEOUT} is checked.
125      */
126     private static final int HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS = 250;
127 
128     /**
129      * If tests expect {@link UserHalService} call to timeout, and if they require to sleep then
130      * sleep for this much time. This value should be higher than {@link HAL_TIMEOUT_MS}. A much
131      * higher value for this is going to slow down the test.
132      */
133     private static final int WAITING_TIME_FOR_NEGATIVE_TESTS_MS = HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS
134             + 100;
135 
136     /**
137      * Timeout for {@link GenericHalCallback#assertCalled()} for tests. In each case,
138      * {@link UserHalService} is supposed to return something - either a valid response or a
139      * timeout. This timeout is for the callback to wait for the response. A higher value of this
140      * timeout should not affect the test duration.
141      */
142     private static final int CALLBACK_TIMEOUT = 5_000;
143 
144     // Used when crafting a request property - the real value will be set by the mock.
145     private static final int REQUEST_ID_PLACE_HOLDER = 1111;
146 
147     private static final int DEFAULT_REQUEST_ID = 2222;
148 
149     private static final int DEFAULT_USER_ID = 333;
150     private static final int DEFAULT_USER_FLAGS = 444;
151 
152     private static final int INITIAL_USER_INFO_RESPONSE_ACTION = 108;
153 
154     @Mock
155     private VehicleHal mVehicleHal;
156     @Mock
157     private CarUserService mCarUserService;
158 
159     private final Handler mHandler = new Handler(Looper.getMainLooper());
160 
161     private final UserInfo mUser0 = new UserInfo();
162     private final UserInfo mUser100 = new UserInfo();
163 
164     private final UsersInfo mUsersInfo = UserHalHelper.emptyUsersInfo();
165 
166     // Must be a spy so we can mock getNextRequestId()
167     private UserHalService mUserHalService;
168 
169     private final HalPropValueBuilder mPropValueBuilder = new HalPropValueBuilder(/*isAidl=*/true);
170 
UserHalServiceTest()171     public UserHalServiceTest() {
172         super(UserHalService.TAG);
173     }
174 
175     @Override
onSessionBuilder(CustomMockitoSessionBuilder builder)176     protected void onSessionBuilder(CustomMockitoSessionBuilder builder) {
177         builder.spyStatic(CarSystemProperties.class)
178                 .spyStatic(CarStatsLog.class);
179     }
180 
181     @Before
setFixtures()182     public void setFixtures() {
183         mockUserHalEnabled(true);
184         when(mVehicleHal.getHalPropValueBuilder()).thenReturn(mPropValueBuilder);
185         mUserHalService = spy(new UserHalService(mVehicleHal, mHandler));
186         // Needs at least one property, otherwise isSupported() and isUserAssociationSupported()
187         // will return false
188         mUserHalService.takeProperties(
189                 Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
190                 newSubscribableConfig(CREATE_USER),
191                 newSubscribableConfig(REMOVE_USER),
192                 newSubscribableConfig(SWITCH_USER),
193                 newSubscribableConfig(USER_IDENTIFICATION_ASSOCIATION)));
194 
195         mUser0.userId = 0;
196         mUser0.flags = 100;
197         mUser100.userId = 100;
198         mUser100.flags = 110;
199 
200         mUsersInfo.currentUser = mUser0;
201         mUsersInfo.numberUsers = 2;
202         mUsersInfo.existingUsers = new UserInfo[]{mUser0, mUser100};
203 
204         CarLocalServices.addService(CarUserService.class, mCarUserService);
205     }
206 
207     @After
clearFixtures()208     public void clearFixtures() {
209         CarLocalServices.removeServiceForTest(CarUserService.class);
210     }
211 
212     @Test
testTakeSupportedProperties_supportedNoProperties()213     public void testTakeSupportedProperties_supportedNoProperties() {
214         // Cannot use mUserHalService because it's already set with supported properties
215         UserHalService myHalService = new UserHalService(mVehicleHal);
216 
217         myHalService.takeProperties(Collections.emptyList());
218         assertThat(myHalService.isSupported()).isFalse();
219         assertThat(myHalService.isUserAssociationSupported()).isFalse();
220     }
221 
222     @Test
testTakeSupportedProperties_supportedFewProperties()223     public void testTakeSupportedProperties_supportedFewProperties() {
224         // Cannot use mUserHalService because it's already set with supported properties
225         UserHalService myHalService = new UserHalService(mVehicleHal);
226         myHalService.takeProperties(
227                 Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
228                 newSubscribableConfig(CREATE_USER),
229                 newSubscribableConfig(REMOVE_USER)));
230 
231         assertThat(myHalService.isSupported()).isFalse();
232         assertThat(myHalService.isUserAssociationSupported()).isFalse();
233     }
234 
235     @Test
testTakeSupportedProperties_supportedAllCorePropertiesButEnabledPropertyNotSet()236     public void testTakeSupportedProperties_supportedAllCorePropertiesButEnabledPropertyNotSet() {
237         mockUserHalEnabled(null);
238         // Cannot use mUserHalService because it's already set with supported properties
239         UserHalService myHalService = new UserHalService(mVehicleHal);
240         myHalService.takeProperties(
241                 Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
242                 newSubscribableConfig(CREATE_USER), newSubscribableConfig(REMOVE_USER),
243                 newSubscribableConfig(SWITCH_USER)));
244 
245         assertThat(myHalService.isSupported()).isFalse();
246         assertThat(myHalService.isUserAssociationSupported()).isFalse();
247     }
248 
249     @Test
testTakeSupportedProperties_supportedAllCorePropertiesButDisabled()250     public void testTakeSupportedProperties_supportedAllCorePropertiesButDisabled() {
251         mockUserHalEnabled(false);
252         // Cannot use mUserHalService because it's already set with supported properties
253         UserHalService myHalService = new UserHalService(mVehicleHal);
254         myHalService.takeProperties(
255                 Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
256                 newSubscribableConfig(CREATE_USER), newSubscribableConfig(REMOVE_USER),
257                 newSubscribableConfig(SWITCH_USER)));
258 
259         assertThat(myHalService.isSupported()).isFalse();
260         assertThat(myHalService.isUserAssociationSupported()).isFalse();
261     }
262 
263     @Test
testTakeSupportedProperties_supportedAllCoreProperties()264     public void testTakeSupportedProperties_supportedAllCoreProperties() {
265         // Cannot use mUserHalService because it's already set with supported properties
266         UserHalService myHalService = new UserHalService(mVehicleHal);
267         myHalService.takeProperties(
268                 Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
269                 newSubscribableConfig(CREATE_USER),
270                 newSubscribableConfig(REMOVE_USER),
271                 newSubscribableConfig(SWITCH_USER)));
272 
273         assertThat(myHalService.isSupported()).isTrue();
274         assertThat(myHalService.isUserAssociationSupported()).isFalse();
275     }
276 
277     @Test
testTakeSupportedProperties_supportedAllPropertiesButDisabled()278     public void testTakeSupportedProperties_supportedAllPropertiesButDisabled() {
279         mockUserHalEnabled(false);
280         // Cannot use mUserHalService because it's already set with supported properties
281         UserHalService myHalService = new UserHalService(mVehicleHal);
282         myHalService.takeProperties(
283                 Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
284                 newSubscribableConfig(CREATE_USER), newSubscribableConfig(REMOVE_USER),
285                 newSubscribableConfig(SWITCH_USER),
286                 newSubscribableConfig(USER_IDENTIFICATION_ASSOCIATION)));
287 
288         assertThat(myHalService.isSupported()).isFalse();
289         assertThat(myHalService.isUserAssociationSupported()).isTrue();
290     }
291 
292     @Test
testTakeSupportedProperties_supportedAllPropertiesButEnablePropertyNotSet()293     public void testTakeSupportedProperties_supportedAllPropertiesButEnablePropertyNotSet() {
294         mockUserHalEnabled(null);
295         // Cannot use mUserHalService because it's already set with supported properties
296         UserHalService myHalService = new UserHalService(mVehicleHal);
297         myHalService.takeProperties(
298                 Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
299                 newSubscribableConfig(CREATE_USER), newSubscribableConfig(REMOVE_USER),
300                 newSubscribableConfig(SWITCH_USER),
301                 newSubscribableConfig(USER_IDENTIFICATION_ASSOCIATION)));
302 
303         assertThat(myHalService.isSupported()).isFalse();
304         assertThat(myHalService.isUserAssociationSupported()).isTrue();
305     }
306 
307     @Test
testTakeSupportedProperties_supportedAllProperties()308     public void testTakeSupportedProperties_supportedAllProperties() {
309         // Cannot use mUserHalService because it's already set with supported properties
310         UserHalService myHalService = new UserHalService(mVehicleHal);
311         myHalService.takeProperties(
312                 Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
313                 newSubscribableConfig(CREATE_USER),
314                 newSubscribableConfig(REMOVE_USER),
315                 newSubscribableConfig(SWITCH_USER),
316                 newSubscribableConfig(USER_IDENTIFICATION_ASSOCIATION)));
317 
318         assertThat(myHalService.isSupported()).isTrue();
319         assertThat(myHalService.isUserAssociationSupported()).isTrue();
320     }
321 
322     @Test
testTakeSupportedPropertiesAndInit()323     public void testTakeSupportedPropertiesAndInit() {
324         // Cannot use mUserHalService because it's already set with supported properties
325         UserHalService myHalService = new UserHalService(mVehicleHal);
326         HalPropConfig unsupportedConfig = newConfig(CURRENT_GEAR);
327 
328         myHalService.takeProperties(
329                 Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
330                 newSubscribableConfig(CREATE_USER),
331                 newSubscribableConfig(REMOVE_USER),
332                 newSubscribableConfig(SWITCH_USER), unsupportedConfig,
333                 newSubscribableConfig(USER_IDENTIFICATION_ASSOCIATION)));
334 
335 
336         // Ideally there should be 2 test methods (one for takeSupportedProperties() and one for
337         // init()), but on "real life" VehicleHal calls these 2 methods in sequence, and the latter
338         // depends on the properties set by the former, so it's ok to test both here...
339         myHalService.init();
340         verify(mVehicleHal).subscribePropertySafe(myHalService, INITIAL_USER_INFO);
341         verify(mVehicleHal).subscribePropertySafe(myHalService, CREATE_USER);
342         verify(mVehicleHal).subscribePropertySafe(myHalService, REMOVE_USER);
343         verify(mVehicleHal).subscribePropertySafe(myHalService, SWITCH_USER);
344         verify(mVehicleHal).subscribePropertySafe(myHalService, USER_IDENTIFICATION_ASSOCIATION);
345     }
346 
347     @Test
testSupportedProperties()348     public void testSupportedProperties() {
349         assertThat(mUserHalService.getAllSupportedProperties()).asList().containsExactly(
350                 INITIAL_USER_INFO, CREATE_USER, REMOVE_USER, SWITCH_USER,
351                 USER_IDENTIFICATION_ASSOCIATION);
352     }
353 
354     @Test
testGetUserInfo_noHalSupported()355     public void testGetUserInfo_noHalSupported() {
356         // Cannot use mUserHalService because it's already set with supported properties
357         UserHalService myHalService = new UserHalService(mVehicleHal);
358 
359         assertThrows(IllegalStateException.class, () -> myHalService.getInitialUserInfo(COLD_BOOT,
360                 HAL_TIMEOUT_MS, mUsersInfo, noOpCallback()));
361     }
362 
363     @Test
testGetUserInfo_invalidTimeout()364     public void testGetUserInfo_invalidTimeout() {
365         assertThrows(IllegalArgumentException.class, () ->
366                 mUserHalService.getInitialUserInfo(COLD_BOOT, 0, mUsersInfo, noOpCallback()));
367         assertThrows(IllegalArgumentException.class, () ->
368                 mUserHalService.getInitialUserInfo(COLD_BOOT, -1, mUsersInfo, noOpCallback()));
369     }
370 
371     @Test
testGetUserInfo_noUsersInfo()372     public void testGetUserInfo_noUsersInfo() {
373         assertThrows(NullPointerException.class, () -> mUserHalService.getInitialUserInfo(COLD_BOOT,
374                 HAL_TIMEOUT_MS, null, noOpCallback()));
375     }
376 
377     @Test
testGetUserInfo_noCallback()378     public void testGetUserInfo_noCallback() {
379         assertThrows(NullPointerException.class,
380                 () -> mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_MS,
381                         mUsersInfo, null));
382     }
383 
384     @Test
testGetUserInfo_halSetTimedOut()385     public void testGetUserInfo_halSetTimedOut() throws Exception {
386         replySetPropertyWithTimeoutException(INITIAL_USER_INFO);
387 
388         GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
389                 CALLBACK_TIMEOUT);
390         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_MS, mUsersInfo,
391                 callback);
392 
393         callback.assertCalled();
394         assertCallbackStatus(callback, HalCallback.STATUS_HAL_SET_TIMEOUT);
395         assertThat(callback.response).isNull();
396 
397         // Make sure the pending request was removed
398         SystemClock.sleep(WAITING_TIME_FOR_NEGATIVE_TESTS_MS);
399         callback.assertNotCalledAgain();
400     }
401 
402     @Test
testGetUserInfo_halDidNotReply()403     public void testGetUserInfo_halDidNotReply() throws Exception {
404         GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
405                 CALLBACK_TIMEOUT);
406         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS, mUsersInfo,
407                 callback);
408 
409         callback.assertCalled();
410         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
411         assertThat(callback.response).isNull();
412     }
413 
414     @Test
testGetUserInfo_secondCallFailWhilePending()415     public void testGetUserInfo_secondCallFailWhilePending() throws Exception {
416         GenericHalCallback<InitialUserInfoResponse> callback1 = new GenericHalCallback<>(
417                 CALLBACK_TIMEOUT);
418         GenericHalCallback<InitialUserInfoResponse> callback2 = new GenericHalCallback<>(
419                 CALLBACK_TIMEOUT);
420         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS, mUsersInfo,
421                 callback1);
422         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_MS, mUsersInfo,
423                 callback2);
424 
425         callback1.assertCalled();
426         assertCallbackStatus(callback1, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
427         assertThat(callback1.response).isNull();
428 
429         callback2.assertCalled();
430         assertCallbackStatus(callback2, HalCallback.STATUS_CONCURRENT_OPERATION);
431         assertThat(callback1.response).isNull();
432     }
433 
434     @Test
testGetUserInfo_halReplyWithWrongRequestId()435     public void testGetUserInfo_halReplyWithWrongRequestId() throws Exception {
436         HalPropValue propResponse = createPropRequest(INITIAL_USER_INFO,
437                     REQUEST_ID_PLACE_HOLDER, INITIAL_USER_INFO_RESPONSE_ACTION);
438 
439         replySetPropertyWithOnChangeEvent(INITIAL_USER_INFO, propResponse,
440                 /* rightRequestId= */ false);
441 
442         GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
443                 CALLBACK_TIMEOUT);
444         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS, mUsersInfo,
445                 callback);
446 
447         callback.assertCalled();
448         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
449         assertThat(callback.response).isNull();
450     }
451 
452     @Test
testGetUserInfo_halReturnedInvalidAction()453     public void testGetUserInfo_halReturnedInvalidAction() throws Exception {
454         HalPropValue propResponse = createPropRequest(INITIAL_USER_INFO,
455                     REQUEST_ID_PLACE_HOLDER, INITIAL_USER_INFO_RESPONSE_ACTION);
456 
457         AtomicReference<HalPropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
458                 INITIAL_USER_INFO, propResponse, /* rightRequestId= */ true);
459 
460         GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
461                 CALLBACK_TIMEOUT);
462         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_MS, mUsersInfo,
463                 callback);
464 
465         callback.assertCalled();
466 
467         // Make sure the arguments were properly converted
468         assertInitialUserInfoSetRequest(reqCaptor.get(), COLD_BOOT);
469 
470         // Assert response
471         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
472         assertThat(callback.response).isNull();
473     }
474 
475     @Test
testGetUserInfo_successDefault()476     public void testGetUserInfo_successDefault() throws Exception {
477         getUserInfoSuccessTest(1);
478     }
479 
getUserInfoSuccessTest(int count)480     private void getUserInfoSuccessTest(int count) throws Exception {
481         HalPropValue propResponse = createPropRequest(INITIAL_USER_INFO,
482                     REQUEST_ID_PLACE_HOLDER, InitialUserInfoResponseAction.DEFAULT);
483 
484         AtomicReference<HalPropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
485                 INITIAL_USER_INFO, propResponse, /* rightRequestId= */ true);
486 
487         GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
488                 CALLBACK_TIMEOUT);
489         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_MS, mUsersInfo,
490                 callback);
491 
492         callback.assertCalled();
493 
494         // Make sure the arguments were properly converted
495         assertInitialUserInfoSetRequest(reqCaptor.get(), COLD_BOOT);
496 
497         // Assert response
498         assertCallbackStatus(callback, HalCallback.STATUS_OK);
499         InitialUserInfoResponse actualResponse = callback.response;
500         assertThat(actualResponse.action).isEqualTo(InitialUserInfoResponseAction.DEFAULT);
501         assertThat(actualResponse.userNameToCreate).isEmpty();
502         assertThat(actualResponse.userToSwitchOrCreate).isNotNull();
503         assertThat(actualResponse.userToSwitchOrCreate.userId).isEqualTo(UserHandle.USER_NULL);
504         assertThat(actualResponse.userToSwitchOrCreate.flags).isEqualTo(0);
505 
506         int responseActionDefault = CarStatsLog
507                 .CAR_USER_HAL_INITIAL_USER_INFO_RESPONSE_REPORTED__RESPONSE_ACTION__DEFAULT;
508         verify(() -> CarStatsLog.write(
509                 eq(CarStatsLog.CAR_USER_HAL_INITIAL_USER_INFO_REQUEST_REPORTED),
510                 anyInt(),
511                 eq(CarStatsLog
512                         .CAR_USER_HAL_INITIAL_USER_INFO_REQUEST_REPORTED__REQUEST_TYPE__COLD_BOOT),
513                 eq(HAL_TIMEOUT_MS)), times(count));
514         verify(() -> CarStatsLog.write(
515                 eq(CarStatsLog.CAR_USER_HAL_INITIAL_USER_INFO_RESPONSE_REPORTED),
516                 anyInt(),
517                 eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__OK),
518                 eq(responseActionDefault),
519                 eq(UserHandle.USER_NULL), eq(0), eq("")), timeout(CALLBACK_TIMEOUT).times(count));
520     }
521 
522     @Test
testGetUserInfo_successSwitchUser()523     public void testGetUserInfo_successSwitchUser() throws Exception {
524         int userIdToSwitch = 42;
525         HalPropValue propResponse = createPropRequest(INITIAL_USER_INFO,
526                     REQUEST_ID_PLACE_HOLDER, InitialUserInfoResponseAction.SWITCH,
527                     new int[]{userIdToSwitch});
528 
529         AtomicReference<HalPropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
530                 INITIAL_USER_INFO, propResponse, /* rightRequestId= */ true);
531 
532         GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
533                 CALLBACK_TIMEOUT);
534         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_MS, mUsersInfo,
535                 callback);
536 
537         callback.assertCalled();
538 
539         // Make sure the arguments were properly converted
540         assertInitialUserInfoSetRequest(reqCaptor.get(), COLD_BOOT);
541 
542         assertCallbackStatus(callback, HalCallback.STATUS_OK);
543         InitialUserInfoResponse actualResponse = callback.response;
544         assertThat(actualResponse.action).isEqualTo(InitialUserInfoResponseAction.SWITCH);
545         assertThat(actualResponse.userNameToCreate).isEmpty();
546         UserInfo userToSwitch = actualResponse.userToSwitchOrCreate;
547         assertThat(userToSwitch).isNotNull();
548         assertThat(userToSwitch.userId).isEqualTo(userIdToSwitch);
549         assertThat(userToSwitch.flags).isEqualTo(0);
550 
551         verify(() -> CarStatsLog.write(
552                 eq(CarStatsLog.CAR_USER_HAL_INITIAL_USER_INFO_REQUEST_REPORTED),
553                 anyInt(),
554                 eq(CarStatsLog
555                         .CAR_USER_HAL_INITIAL_USER_INFO_REQUEST_REPORTED__REQUEST_TYPE__COLD_BOOT),
556                 eq(HAL_TIMEOUT_MS)));
557         verify(() -> CarStatsLog.write(
558                 eq(CarStatsLog.CAR_USER_HAL_INITIAL_USER_INFO_RESPONSE_REPORTED),
559                 anyInt(),
560                 eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__OK),
561                 eq(CarStatsLog
562                         .CAR_USER_HAL_INITIAL_USER_INFO_RESPONSE_REPORTED__RESPONSE_ACTION__SWITCH),
563                 eq(userIdToSwitch), eq(0), eq("")), timeout(CALLBACK_TIMEOUT));
564     }
565 
566     @Test
testGetUserInfo_successCreateUser()567     public void testGetUserInfo_successCreateUser() throws Exception {
568         int newUserFlags = 108;
569         String newUserName = "Groot";
570         int unusedUserId = 666;
571         HalPropValue propResponse = createPropRequest(INITIAL_USER_INFO,
572                     REQUEST_ID_PLACE_HOLDER, InitialUserInfoResponseAction.CREATE,
573                     new int[]{unusedUserId, newUserFlags}, "||" + newUserName);
574 
575         AtomicReference<HalPropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
576                 INITIAL_USER_INFO, propResponse, /* rightRequestId= */ true);
577 
578         GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
579                 CALLBACK_TIMEOUT);
580         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_MS, mUsersInfo,
581                 callback);
582 
583         callback.assertCalled();
584 
585         // Make sure the arguments were properly converted
586         assertInitialUserInfoSetRequest(reqCaptor.get(), COLD_BOOT);
587 
588         assertCallbackStatus(callback, HalCallback.STATUS_OK);
589         assertThat(callback.status).isEqualTo(HalCallback.STATUS_OK);
590         InitialUserInfoResponse actualResponse = callback.response;
591         assertThat(actualResponse.action).isEqualTo(InitialUserInfoResponseAction.CREATE);
592         assertThat(actualResponse.userLocales).isEmpty();
593         assertThat(actualResponse.userNameToCreate).isEqualTo(newUserName);
594         UserInfo newUser = actualResponse.userToSwitchOrCreate;
595         assertThat(newUser).isNotNull();
596         assertThat(newUser.userId).isEqualTo(UserHandle.USER_NULL);
597         assertThat(newUser.flags).isEqualTo(newUserFlags);
598 
599         verify(() -> CarStatsLog.write(
600                 eq(CarStatsLog.CAR_USER_HAL_INITIAL_USER_INFO_REQUEST_REPORTED),
601                 anyInt(),
602                 eq(CarStatsLog
603                         .CAR_USER_HAL_INITIAL_USER_INFO_REQUEST_REPORTED__REQUEST_TYPE__COLD_BOOT),
604                 eq(HAL_TIMEOUT_MS)));
605         verify(() -> CarStatsLog.write(
606                 eq(CarStatsLog.CAR_USER_HAL_INITIAL_USER_INFO_RESPONSE_REPORTED),
607                 anyInt(),
608                 eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__OK),
609                 eq(CarStatsLog
610                         .CAR_USER_HAL_INITIAL_USER_INFO_RESPONSE_REPORTED__RESPONSE_ACTION__CREATE),
611                 eq(UserHandle.USER_NULL), eq(newUserFlags), eq("")), timeout(CALLBACK_TIMEOUT));
612     }
613 
614     @Test
testGetUserInfo_twoSuccessfulCalls()615     public void testGetUserInfo_twoSuccessfulCalls() throws Exception {
616         getUserInfoSuccessTest(1);
617 
618         getUserInfoSuccessTest(2);
619     }
620 
621     @Test
testSwitchUser_noHalSupported()622     public void testSwitchUser_noHalSupported() {
623         // Cannot use mUserHalService because it's already set with supported properties
624         UserHalService myHalService = new UserHalService(mVehicleHal);
625 
626         assertThrows(IllegalStateException.class,
627                 () -> myHalService.switchUser(createUserSwitchRequest(mUser100, mUsersInfo),
628                         HAL_TIMEOUT_MS, noOpCallback()));
629     }
630 
631     @Test
testSwitchUser_invalidTimeout()632     public void testSwitchUser_invalidTimeout() {
633         assertThrows(IllegalArgumentException.class, () -> mUserHalService
634                 .switchUser(createUserSwitchRequest(mUser100, mUsersInfo), 0, noOpCallback()));
635         assertThrows(IllegalArgumentException.class, () -> mUserHalService
636                 .switchUser(createUserSwitchRequest(mUser100, mUsersInfo), -1, noOpCallback()));
637     }
638 
639     @Test
testSwitchUser_noUsersInfo()640     public void testSwitchUser_noUsersInfo() {
641         assertThrows(IllegalArgumentException.class, () -> mUserHalService
642                 .switchUser(createUserSwitchRequest(mUser100, null), HAL_TIMEOUT_MS,
643                         noOpCallback()));
644     }
645 
646     @Test
testSwitchUser_noCallback()647     public void testSwitchUser_noCallback() {
648         assertThrows(NullPointerException.class, () -> mUserHalService
649                 .switchUser(createUserSwitchRequest(mUser100, mUsersInfo), HAL_TIMEOUT_MS, null));
650     }
651 
652     @Test
testSwitchUser_nullRequest()653     public void testSwitchUser_nullRequest() {
654         assertThrows(NullPointerException.class, () -> mUserHalService
655                 .switchUser(null, HAL_TIMEOUT_MS, noOpCallback()));
656     }
657 
658     @Test
testSwitchUser_noTarget()659     public void testSwitchUser_noTarget() {
660         assertThrows(NullPointerException.class, () -> mUserHalService
661                 .switchUser(createUserSwitchRequest(null, mUsersInfo), HAL_TIMEOUT_MS,
662                         noOpCallback()));
663     }
664 
665     @Test
testSwitchUser_halSetTimedOut()666     public void testSwitchUser_halSetTimedOut() throws Exception {
667         replySetPropertyWithTimeoutException(SWITCH_USER);
668 
669         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
670                 CALLBACK_TIMEOUT);
671         mUserHalService.switchUser(createUserSwitchRequest(mUser100, mUsersInfo),
672                 HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
673                 callback);
674 
675         callback.assertCalled();
676         assertCallbackStatus(callback, HalCallback.STATUS_HAL_SET_TIMEOUT);
677         assertThat(callback.response).isNull();
678 
679         // Make sure the pending request was removed
680         SystemClock.sleep(WAITING_TIME_FOR_NEGATIVE_TESTS_MS);
681         callback.assertNotCalledAgain();
682     }
683 
684     @Test
testSwitchUser_halDidNotReply()685     public void testSwitchUser_halDidNotReply() throws Exception {
686         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
687                 CALLBACK_TIMEOUT);
688         mUserHalService.switchUser(createUserSwitchRequest(mUser100, mUsersInfo),
689                 HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
690                 callback);
691 
692         callback.assertCalled();
693         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
694         assertThat(callback.response).isNull();
695     }
696 
697     @Test
testSwitchUser_halReplyWithWrongRequestId()698     public void testSwitchUser_halReplyWithWrongRequestId() throws Exception {
699         HalPropValue propResponse = createPropRequest(SWITCH_USER,
700                     REQUEST_ID_PLACE_HOLDER, SwitchUserMessageType.VEHICLE_RESPONSE,
701                     new int[]{SwitchUserStatus.SUCCESS});
702 
703         replySetPropertyWithOnChangeEvent(SWITCH_USER, propResponse,
704                 /* rightRequestId= */ false);
705 
706         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
707                 CALLBACK_TIMEOUT);
708         mUserHalService.switchUser(createUserSwitchRequest(mUser100, mUsersInfo),
709                 HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
710                 callback);
711 
712         callback.assertCalled();
713         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
714         assertThat(callback.response).isNull();
715 
716         int switchRequestAndroid = CarStatsLog
717                 .CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED__REQUEST_TYPE__SWITCH_REQUEST_ANDROID;
718         int statusUnspecified =
719                 CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__REQUEST_STATUS__UNSPECIFIED;
720         int statusWrongHalResponse = CarStatsLog
721                 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__WRONG_HAL_RESPONSE;
722 
723         verify(() -> CarStatsLog.write(
724                 eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED),
725                 anyInt(),
726                 eq(switchRequestAndroid),
727                 eq(0), eq(100), eq(100), eq(110), eq(HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS)));
728         verify(() -> CarStatsLog.write(
729                 eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED),
730                 anyInt(),
731                 eq(statusWrongHalResponse),
732                 eq(statusUnspecified)), timeout(CALLBACK_TIMEOUT));
733     }
734 
735     @Test
testSwitchUser_halReturnedInvalidMessageType()736     public void testSwitchUser_halReturnedInvalidMessageType() throws Exception {
737         HalPropValue propResponse = createPropRequest(SWITCH_USER,
738                 REQUEST_ID_PLACE_HOLDER, SwitchUserMessageType.LEGACY_ANDROID_SWITCH,
739                 new int[]{SwitchUserStatus.SUCCESS});
740 
741         AtomicReference<HalPropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
742                 SWITCH_USER, propResponse, /* rightRequestId= */ true);
743 
744         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
745                 CALLBACK_TIMEOUT);
746         mUserHalService.switchUser(createUserSwitchRequest(mUser100, mUsersInfo), HAL_TIMEOUT_MS,
747                 callback);
748 
749         callback.assertCalled();
750 
751         // Make sure the arguments were properly converted
752         assertHalSetSwitchUserRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH,
753                 mUser100);
754 
755         // Assert response
756         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
757         assertThat(callback.response).isNull();
758     }
759 
760     @Test
testSwitchUser_success()761     public void testSwitchUser_success() throws Exception {
762         HalPropValue propResponse = createPropRequest(SWITCH_USER,
763                     REQUEST_ID_PLACE_HOLDER, SwitchUserMessageType.VEHICLE_RESPONSE,
764                     new int[]{SwitchUserStatus.SUCCESS});
765 
766         AtomicReference<HalPropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
767                 SWITCH_USER, propResponse, /* rightRequestId= */ true);
768 
769         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
770                 CALLBACK_TIMEOUT);
771         mUserHalService.switchUser(createUserSwitchRequest(mUser100, mUsersInfo), HAL_TIMEOUT_MS,
772                 callback);
773 
774         callback.assertCalled();
775 
776         // Make sure the arguments were properly converted
777         assertHalSetSwitchUserRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH,
778                 mUser100);
779 
780         // Assert response
781         assertCallbackStatus(callback, HalCallback.STATUS_OK);
782         SwitchUserResponse actualResponse = callback.response;
783         assertThat(actualResponse.status).isEqualTo(SwitchUserStatus.SUCCESS);
784         assertThat(actualResponse.messageType).isEqualTo(SwitchUserMessageType.VEHICLE_RESPONSE);
785         assertThat(actualResponse.errorMessage).isEmpty();
786 
787         int switchRequestAndroid = CarStatsLog
788                 .CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED__REQUEST_TYPE__SWITCH_REQUEST_ANDROID;
789         int statusSuccess = CarStatsLog
790                 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__REQUEST_STATUS__SUCCESS;
791 
792         verify(() -> CarStatsLog.write(
793                 eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED),
794                 anyInt(),
795                 eq(switchRequestAndroid),
796                 eq(0), eq(100), eq(100), eq(110), eq(HAL_TIMEOUT_MS)));
797         verify(() -> CarStatsLog.write(
798                 eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED),
799                 anyInt(),
800                 eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__OK),
801                 eq(statusSuccess)), timeout(CALLBACK_TIMEOUT));
802     }
803 
804     @Test
testSwitchUser_failure()805     public void testSwitchUser_failure() throws Exception {
806         HalPropValue propResponse = createPropRequest(SWITCH_USER,
807                     REQUEST_ID_PLACE_HOLDER, SwitchUserMessageType.VEHICLE_RESPONSE,
808                     new int[]{SwitchUserStatus.FAILURE}, "D'OH!");
809 
810         AtomicReference<HalPropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
811                 SWITCH_USER, propResponse, /* rightRequestId= */ true);
812 
813         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
814                 CALLBACK_TIMEOUT);
815         mUserHalService.switchUser(createUserSwitchRequest(mUser100, mUsersInfo), HAL_TIMEOUT_MS,
816                 callback);
817 
818         callback.assertCalled();
819 
820         // Make sure the arguments were properly converted
821         assertHalSetSwitchUserRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH,
822                 mUser100);
823 
824         // Assert response
825         assertCallbackStatus(callback, HalCallback.STATUS_OK);
826         SwitchUserResponse actualResponse = callback.response;
827         assertThat(actualResponse.status).isEqualTo(SwitchUserStatus.FAILURE);
828         assertThat(actualResponse.messageType).isEqualTo(SwitchUserMessageType.VEHICLE_RESPONSE);
829         assertThat(actualResponse.errorMessage).isEqualTo("D'OH!");
830 
831         int switchRequestAndroid = CarStatsLog
832                 .CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED__REQUEST_TYPE__SWITCH_REQUEST_ANDROID;
833         int statusFailure = CarStatsLog
834                 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__REQUEST_STATUS__FAILURE;
835 
836         verify(() -> CarStatsLog.write(
837                 eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED),
838                 anyInt(),
839                 eq(switchRequestAndroid),
840                 eq(0), eq(100), eq(100), eq(110), eq(HAL_TIMEOUT_MS)));
841         verify(() -> CarStatsLog.write(
842                 eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED),
843                 anyInt(),
844                 eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__OK),
845                 eq(statusFailure)), timeout(CALLBACK_TIMEOUT));
846     }
847 
848     @Test
testSwitchUser_secondCallFailWhilePending()849     public void testSwitchUser_secondCallFailWhilePending() throws Exception {
850         GenericHalCallback<SwitchUserResponse> callback1 = new GenericHalCallback<>(
851                 CALLBACK_TIMEOUT);
852         GenericHalCallback<SwitchUserResponse> callback2 = new GenericHalCallback<>(
853                 CALLBACK_TIMEOUT);
854         mUserHalService.switchUser(createUserSwitchRequest(mUser100, mUsersInfo),
855                 HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
856                 callback1);
857         mUserHalService.switchUser(createUserSwitchRequest(mUser100, mUsersInfo), HAL_TIMEOUT_MS,
858                 callback2);
859 
860         callback1.assertCalled();
861         assertCallbackStatus(callback1, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
862         assertThat(callback1.response).isNull();
863 
864         callback2.assertCalled();
865         assertCallbackStatus(callback2, HalCallback.STATUS_CONCURRENT_OPERATION);
866         assertThat(callback1.response).isNull();
867     }
868 
869     @Test
testSwitchUser_halReturnedInvalidStatus()870     public void testSwitchUser_halReturnedInvalidStatus() throws Exception {
871         HalPropValue propResponse = createPropRequest(SWITCH_USER,
872                     REQUEST_ID_PLACE_HOLDER, SwitchUserMessageType.VEHICLE_RESPONSE,
873                     new int[]{/*status =*/ 110});
874 
875         AtomicReference<HalPropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
876                 SWITCH_USER, propResponse, /* rightRequestId= */ true);
877 
878         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
879                 CALLBACK_TIMEOUT);
880         mUserHalService.switchUser(createUserSwitchRequest(mUser100, mUsersInfo), HAL_TIMEOUT_MS,
881                 callback);
882 
883         callback.assertCalled();
884 
885         // Make sure the arguments were properly converted
886         assertHalSetSwitchUserRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH,
887                 mUser100);
888 
889         // Assert response
890         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
891         assertThat(callback.response).isNull();
892 
893         int switchRequestAndroid = CarStatsLog
894                 .CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED__REQUEST_TYPE__SWITCH_REQUEST_ANDROID;
895         int statusFailure = CarStatsLog
896                 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__REQUEST_STATUS__FAILURE;
897         int statusWrongHalResponse = CarStatsLog
898                 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__WRONG_HAL_RESPONSE;
899         verify(() -> CarStatsLog.write(
900                 eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED),
901                 anyInt(),
902                 eq(switchRequestAndroid),
903                 eq(0), eq(100), eq(100), eq(110), eq(HAL_TIMEOUT_MS)));
904         verify(() -> CarStatsLog.write(
905                 eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED),
906                 anyInt(),
907                 eq(statusWrongHalResponse),
908                 eq(statusFailure)), timeout(CALLBACK_TIMEOUT));
909     }
910 
911     @Test
testSwitchUser_halResponseAfterTimeout()912     public void testSwitchUser_halResponseAfterTimeout() throws Exception {
913         HalPropValue propResponse = createPropRequest(SWITCH_USER,
914                     REQUEST_ID_PLACE_HOLDER, SwitchUserMessageType.VEHICLE_RESPONSE,
915                     new int[]{SwitchUserStatus.SUCCESS});
916         CountDownLatch latch = new CountDownLatch(1);
917 
918         replySetPropertyWithOnChangeEvent(SWITCH_USER, propResponse, /* rightRequestId= */ true,
919                 latch);
920 
921         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
922                 CALLBACK_TIMEOUT);
923 
924         mUserHalService.switchUser(createUserSwitchRequest(mUser100, mUsersInfo),
925                 HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS, callback);
926 
927         int switchRequestAndroid = CarStatsLog
928                 .CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED__REQUEST_TYPE__SWITCH_REQUEST_ANDROID;
929         verify(() -> CarStatsLog.write(eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED),
930                 anyInt(),
931                 eq(switchRequestAndroid),
932                 eq(0), eq(100), eq(100), eq(110), eq(HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS)));
933 
934         // The event has not been sent before the callback.
935         callback.assertCalled();
936         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
937         assertThat(callback.response).isNull();
938 
939         // Now send the event after the request already timed-out.
940         latch.countDown();
941 
942         int statusUnspecified =
943                 CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__REQUEST_STATUS__UNSPECIFIED;
944         int statusWrongHalResponse = CarStatsLog
945                 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__WRONG_HAL_RESPONSE;
946 
947         verify(() -> CarStatsLog.write(
948                 eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED),
949                 anyInt(),
950                 eq(statusWrongHalResponse),
951                 eq(statusUnspecified)), timeout(CALLBACK_TIMEOUT));
952     }
953 
954     @Test
testUserSwitch_OEMRequest_success()955     public void testUserSwitch_OEMRequest_success() throws Exception {
956         int requestId = -4;
957         int targetUserId = 11;
958         HalPropValue propResponse = createPropRequest(SWITCH_USER, requestId,
959                 SwitchUserMessageType.VEHICLE_REQUEST, new int[]{targetUserId});
960 
961         mUserHalService.onHalEvents(Arrays.asList(propResponse));
962         waitForHandler();
963 
964         verify(mCarUserService).switchAndroidUserFromHal(requestId, targetUserId);
965 
966         int switchRequestOem = CarStatsLog
967                 .CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED__REQUEST_TYPE__SWITCH_REQUEST_OEM;
968 
969         verify(() -> CarStatsLog.write(
970                 eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED),
971                 /* requestId= */ anyInt(),
972                 eq(switchRequestOem),
973                 /* current user id= */ eq(-1),
974                 /* current user flag= */ eq(-1),
975                 /* targetUserId= */ eq(11),
976                 /* target user flag= */ eq(-1),
977                 /* timeout_ms= */ eq(-1)));
978     }
979 
980     @Test
testUserSwitch_OEMRequest_failure_positiveRequestId()981     public void testUserSwitch_OEMRequest_failure_positiveRequestId() throws Exception {
982         int requestId = 4;
983         int targetUserId = 11;
984         HalPropValue propResponse = createPropRequest(SWITCH_USER, requestId,
985                 SwitchUserMessageType.VEHICLE_REQUEST, new int[]{targetUserId});
986 
987         mUserHalService.onHalEvents(Arrays.asList(propResponse));
988         waitForHandler();
989 
990         verify(mCarUserService, never()).switchAndroidUserFromHal(anyInt(), anyInt());
991     }
992 
993     @Test
testPostSwitchResponse_noHalSupported()994     public void testPostSwitchResponse_noHalSupported() {
995         // Cannot use mUserHalService because it's already set with supported properties
996         UserHalService myHalService = new UserHalService(mVehicleHal);
997 
998         assertThrows(IllegalStateException.class,
999                 () -> myHalService.postSwitchResponse(UserHalHelper.emptySwitchUserRequest()));
1000     }
1001 
1002     @Test
testPostSwitchResponse_nullRequest()1003     public void testPostSwitchResponse_nullRequest() {
1004         assertThrows(NullPointerException.class, () -> mUserHalService.postSwitchResponse(null));
1005     }
1006 
1007     @Test
testPostSwitchResponse_noUsersInfo()1008     public void testPostSwitchResponse_noUsersInfo() {
1009         SwitchUserRequest request = createUserSwitchRequest(mUser100, null);
1010         request.requestId = 42;
1011         assertThrows(IllegalArgumentException.class,
1012                 () -> mUserHalService.postSwitchResponse(request));
1013     }
1014 
1015     @Test
testPostSwitchResponse_HalCalledWithCorrectProp()1016     public void testPostSwitchResponse_HalCalledWithCorrectProp() {
1017         SwitchUserRequest request = createUserSwitchRequest(mUser100, mUsersInfo);
1018         request.requestId = 42;
1019         mUserHalService.postSwitchResponse(request);
1020         ArgumentCaptor<HalPropValue> propCaptor =
1021                 ArgumentCaptor.forClass(HalPropValue.class);
1022         verify(mVehicleHal).set(propCaptor.capture());
1023         HalPropValue prop = propCaptor.getValue();
1024         assertHalSetSwitchUserRequest(prop, SwitchUserMessageType.ANDROID_POST_SWITCH, mUser100);
1025     }
1026 
1027     @Test
testLegacyUserSwitch_nullRequest()1028     public void testLegacyUserSwitch_nullRequest() {
1029         assertThrows(NullPointerException.class, () -> mUserHalService.legacyUserSwitch(null));
1030     }
1031 
1032     @Test
testLegacyUserSwitch_noMessageType()1033     public void testLegacyUserSwitch_noMessageType() {
1034         SwitchUserRequest request = UserHalHelper.emptySwitchUserRequest();
1035 
1036         assertThrows(IllegalArgumentException.class,
1037                 () -> mUserHalService.legacyUserSwitch(request));
1038     }
1039 
1040     @Test
testLegacyUserSwitch_noTargetUserInfo()1041     public void testLegacyUserSwitch_noTargetUserInfo() {
1042         SwitchUserRequest request = UserHalHelper.emptySwitchUserRequest();
1043         request.messageType = SwitchUserMessageType.ANDROID_SWITCH;
1044 
1045         assertThrows(IllegalArgumentException.class,
1046                 () -> mUserHalService.legacyUserSwitch(request));
1047     }
1048 
1049     @Test
testRemoveUser_noHalSupported()1050     public void testRemoveUser_noHalSupported() {
1051         // Cannot use mUserHalService because it's already set with supported properties
1052         UserHalService myHalService = new UserHalService(mVehicleHal);
1053 
1054         assertThrows(IllegalStateException.class,
1055                 () -> myHalService.removeUser(UserHalHelper.emptyRemoveUserRequest()));
1056     }
1057 
1058     @Test
testRemoveUser_nullRequest()1059     public void testRemoveUser_nullRequest() {
1060         RemoveUserRequest request = null;
1061 
1062         assertThrows(NullPointerException.class,
1063                 () -> mUserHalService.removeUser(request));
1064     }
1065 
1066     @Test
testRemoveUser_noRequestId()1067     public void testRemoveUser_noRequestId() {
1068         RemoveUserRequest request = UserHalHelper.emptyRemoveUserRequest();
1069 
1070         assertThrows(IllegalArgumentException.class,
1071                 () -> mUserHalService.removeUser(request));
1072     }
1073 
1074     @Test
testRemoveUser_noRemovedUserInfo()1075     public void testRemoveUser_noRemovedUserInfo() {
1076         RemoveUserRequest request = UserHalHelper.emptyRemoveUserRequest();
1077         request.requestId = 1;
1078 
1079         assertThrows(IllegalArgumentException.class,
1080                 () -> mUserHalService.removeUser(request));
1081     }
1082 
1083     @Test
testRemoveUser_noUsersInfo()1084     public void testRemoveUser_noUsersInfo() {
1085         RemoveUserRequest request = UserHalHelper.emptyRemoveUserRequest();
1086         request.requestId = 1;
1087         request.removedUserInfo = mUser100;
1088 
1089         assertThrows(IllegalArgumentException.class,
1090                 () -> mUserHalService.removeUser(request));
1091     }
1092 
1093     @Test
testRemoveUser_HalCalledWithCorrectProp()1094     public void testRemoveUser_HalCalledWithCorrectProp() {
1095         RemoveUserRequest request = UserHalHelper.emptyRemoveUserRequest();
1096         request.removedUserInfo = mUser100;
1097         request.usersInfo = mUsersInfo;
1098         ArgumentCaptor<HalPropValue> propCaptor =
1099                 ArgumentCaptor.forClass(HalPropValue.class);
1100 
1101         mUserHalService.removeUser(request);
1102 
1103         verify(mVehicleHal).set(propCaptor.capture());
1104         assertHalSetRemoveUserRequest(propCaptor.getValue(), mUser100);
1105     }
1106 
1107     @Test
testLegacyUserSwitch_noHalSupported()1108     public void testLegacyUserSwitch_noHalSupported() {
1109         // Cannot use mUserHalService because it's already set with supported properties
1110         UserHalService myHalService = new UserHalService(mVehicleHal);
1111 
1112         assertThrows(IllegalStateException.class,
1113                 () -> myHalService.legacyUserSwitch(UserHalHelper.emptySwitchUserRequest()));
1114     }
1115 
1116     @Test
testLegacyUserSwitch_noUsersInfo()1117     public void testLegacyUserSwitch_noUsersInfo() {
1118         SwitchUserRequest request = UserHalHelper.emptySwitchUserRequest();
1119         request.messageType = SwitchUserMessageType.ANDROID_SWITCH;
1120         request.targetUser = mUser100;
1121 
1122         assertThrows(IllegalArgumentException.class,
1123                 () -> mUserHalService.legacyUserSwitch(request));
1124     }
1125 
1126     @Test
testLegacyUserSwitch_HalCalledWithCorrectProp()1127     public void testLegacyUserSwitch_HalCalledWithCorrectProp() {
1128         SwitchUserRequest request = UserHalHelper.emptySwitchUserRequest();
1129         request.messageType = SwitchUserMessageType.LEGACY_ANDROID_SWITCH;
1130         request.targetUser = mUser100;
1131         request.usersInfo = mUsersInfo;
1132 
1133         mUserHalService.legacyUserSwitch(request);
1134         ArgumentCaptor<HalPropValue> propCaptor =
1135                 ArgumentCaptor.forClass(HalPropValue.class);
1136         verify(mVehicleHal).set(propCaptor.capture());
1137         HalPropValue prop = propCaptor.getValue();
1138         assertHalSetSwitchUserRequest(prop, SwitchUserMessageType.LEGACY_ANDROID_SWITCH,
1139                 mUser100);
1140     }
1141 
1142     @Test
testCreateUser_noHalSupported()1143     public void testCreateUser_noHalSupported() {
1144         // Cannot use mUserHalService because it's already set with supported properties
1145         UserHalService myHalService = new UserHalService(mVehicleHal);
1146 
1147         assertThrows(IllegalStateException.class,
1148                 () -> myHalService.createUser(UserHalHelper.emptyCreateUserRequest(),
1149                         HAL_TIMEOUT_MS, noOpCallback()));
1150     }
1151 
1152     @Test
testCreateUser_noRequest()1153     public void testCreateUser_noRequest() {
1154         assertThrows(NullPointerException.class, () -> mUserHalService
1155                 .createUser(null, HAL_TIMEOUT_MS, noOpCallback()));
1156     }
1157 
1158     @Test
testCreateUser_invalidTimeout()1159     public void testCreateUser_invalidTimeout() {
1160         assertThrows(IllegalArgumentException.class, () -> mUserHalService
1161                 .createUser(UserHalHelper.emptyCreateUserRequest(), 0, noOpCallback()));
1162         assertThrows(IllegalArgumentException.class, () -> mUserHalService
1163                 .createUser(UserHalHelper.emptyCreateUserRequest(), -1, noOpCallback()));
1164     }
1165 
1166     @Test
testCreateUser_noCallback()1167     public void testCreateUser_noCallback() {
1168         CreateUserRequest request = UserHalHelper.emptyCreateUserRequest();
1169         request.newUserInfo.userId = 10;
1170         request.usersInfo.existingUsers = new UserInfo[]{request.newUserInfo};
1171 
1172         assertThrows(NullPointerException.class, () -> mUserHalService
1173                 .createUser(request, HAL_TIMEOUT_MS, null));
1174     }
1175 
1176     /**
1177      * Creates a valid {@link CreateUserRequest} for tests that doesn't check its contents.
1178      */
1179     @NonNull
newValidCreateUserRequest()1180     private CreateUserRequest newValidCreateUserRequest() {
1181         CreateUserRequest request = UserHalHelper.emptyCreateUserRequest();
1182         request.newUserInfo = mUser100;
1183         request.usersInfo = mUsersInfo;
1184         return request;
1185     }
1186 
1187     @Test
testCreateUser_halSetTimedOut()1188     public void testCreateUser_halSetTimedOut() throws Exception {
1189         replySetPropertyWithTimeoutException(CREATE_USER);
1190 
1191         GenericHalCallback<CreateUserResponse> callback = new GenericHalCallback<>(
1192                 CALLBACK_TIMEOUT);
1193         mUserHalService.createUser(newValidCreateUserRequest(), HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
1194                 callback);
1195 
1196         callback.assertCalled();
1197         assertCallbackStatus(callback, HalCallback.STATUS_HAL_SET_TIMEOUT);
1198         assertThat(callback.response).isNull();
1199 
1200         // Make sure the pending request was removed
1201         SystemClock.sleep(WAITING_TIME_FOR_NEGATIVE_TESTS_MS);
1202         callback.assertNotCalledAgain();
1203     }
1204 
1205     @Test
testCreateUser_halDidNotReply()1206     public void testCreateUser_halDidNotReply() throws Exception {
1207         GenericHalCallback<CreateUserResponse> callback = new GenericHalCallback<>(
1208                 CALLBACK_TIMEOUT);
1209         mUserHalService.createUser(newValidCreateUserRequest(), HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
1210                 callback);
1211 
1212         callback.assertCalled();
1213         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
1214         assertThat(callback.response).isNull();
1215     }
1216 
1217     @Test
testCreateUser_halReplyWithWrongRequestId()1218     public void testCreateUser_halReplyWithWrongRequestId() throws Exception {
1219         HalPropValue propResponse = createPropRequest(CREATE_USER, REQUEST_ID_PLACE_HOLDER);
1220 
1221         replySetPropertyWithOnChangeEvent(CREATE_USER, propResponse,
1222                 /* rightRequestId= */ false);
1223 
1224         GenericHalCallback<CreateUserResponse> callback = new GenericHalCallback<>(
1225                 CALLBACK_TIMEOUT);
1226         mUserHalService.createUser(newValidCreateUserRequest(), HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
1227                 callback);
1228 
1229         callback.assertCalled();
1230         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
1231         assertThat(callback.response).isNull();
1232     }
1233 
1234     @Test
testCreateUser_success()1235     public void testCreateUser_success() throws Exception {
1236         HalPropValue propResponse = createPropRequest(CREATE_USER, REQUEST_ID_PLACE_HOLDER,
1237                 new int[]{CreateUserStatus.SUCCESS});
1238 
1239         AtomicReference<HalPropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
1240                 CREATE_USER, propResponse, /* rightRequestId= */ true);
1241 
1242         CreateUserRequest request = UserHalHelper.emptyCreateUserRequest();
1243         request.newUserInfo = mUser100;
1244         request.usersInfo = mUsersInfo;
1245         GenericHalCallback<CreateUserResponse> callback = new GenericHalCallback<>(
1246                 CALLBACK_TIMEOUT);
1247         mUserHalService.createUser(request, HAL_TIMEOUT_MS, callback);
1248 
1249         callback.assertCalled();
1250 
1251         // Make sure the arguments were properly converted
1252         assertHalSetCreateUserRequest(reqCaptor.get(), request);
1253 
1254         // Assert response
1255         assertCallbackStatus(callback, HalCallback.STATUS_OK);
1256         CreateUserResponse actualResponse = callback.response;
1257         assertThat(actualResponse.status).isEqualTo(CreateUserStatus.SUCCESS);
1258         assertThat(actualResponse.errorMessage).isEmpty();
1259 
1260         int createRequest = CarStatsLog
1261                 .CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED__REQUEST_TYPE__CREATE_REQUEST;
1262         int statusOk = CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__OK;
1263         int statusSuccess = CarStatsLog
1264                 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__REQUEST_STATUS__SUCCESS;
1265         verify(() -> CarStatsLog.write(eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED),
1266                 anyInt(), eq(createRequest), eq(0), eq(100), eq(100), eq(110),
1267                 eq(HAL_TIMEOUT_MS)));
1268         verify(() -> CarStatsLog.write(eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED),
1269                 anyInt(), eq(statusOk), eq(statusSuccess)), timeout(CALLBACK_TIMEOUT));
1270     }
1271 
1272     @Test
testCreateUser_failure()1273     public void testCreateUser_failure() throws Exception {
1274         HalPropValue propResponse = createPropRequest(CREATE_USER, REQUEST_ID_PLACE_HOLDER,
1275                 /* requestType= */ null,  new int[]{CreateUserStatus.FAILURE}, "D'OH!");
1276 
1277         AtomicReference<HalPropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
1278                 CREATE_USER, propResponse, /* rightRequestId= */ true);
1279 
1280         CreateUserRequest request = UserHalHelper.emptyCreateUserRequest();
1281         request.newUserInfo = mUser100;
1282         request.usersInfo = mUsersInfo;
1283         GenericHalCallback<CreateUserResponse> callback = new GenericHalCallback<>(
1284                 CALLBACK_TIMEOUT);
1285         mUserHalService.createUser(request, HAL_TIMEOUT_MS, callback);
1286 
1287         callback.assertCalled();
1288 
1289         // Make sure the arguments were properly converted
1290         assertHalSetCreateUserRequest(reqCaptor.get(), request);
1291 
1292         // Assert response
1293         assertCallbackStatus(callback, HalCallback.STATUS_OK);
1294         CreateUserResponse actualResponse = callback.response;
1295         assertThat(actualResponse.status).isEqualTo(CreateUserStatus.FAILURE);
1296         assertThat(actualResponse.errorMessage).isEqualTo("D'OH!");
1297 
1298         int createRequest = CarStatsLog
1299                 .CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED__REQUEST_TYPE__CREATE_REQUEST;
1300         int statusOk = CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__OK;
1301         int statusFailure = CarStatsLog
1302                 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__REQUEST_STATUS__FAILURE;
1303         verify(() -> CarStatsLog.write(eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_REQUEST_REPORTED),
1304                 anyInt(), eq(createRequest), eq(0), eq(100), eq(100), eq(110),
1305                 eq(HAL_TIMEOUT_MS)));
1306         verify(() -> CarStatsLog.write(eq(CarStatsLog.CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED),
1307                 anyInt(), eq(statusOk), eq(statusFailure)), timeout(CALLBACK_TIMEOUT));
1308     }
1309 
1310     @Test
testCreateUser_secondCallFailWhilePending()1311     public void testCreateUser_secondCallFailWhilePending() throws Exception {
1312         GenericHalCallback<CreateUserResponse> callback1 = new GenericHalCallback<>(
1313                 CALLBACK_TIMEOUT);
1314         GenericHalCallback<CreateUserResponse> callback2 = new GenericHalCallback<>(
1315                 CALLBACK_TIMEOUT);
1316         mUserHalService.createUser(newValidCreateUserRequest(), HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
1317                 callback1);
1318         mUserHalService.createUser(newValidCreateUserRequest(), HAL_TIMEOUT_MS, callback2);
1319 
1320         callback1.assertCalled();
1321         assertCallbackStatus(callback1, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
1322         assertThat(callback1.response).isNull();
1323 
1324         callback2.assertCalled();
1325         assertCallbackStatus(callback2, HalCallback.STATUS_CONCURRENT_OPERATION);
1326         assertThat(callback1.response).isNull();
1327     }
1328 
1329     @Test
testCreateUser_halReturnedInvalidStatus()1330     public void testCreateUser_halReturnedInvalidStatus() throws Exception {
1331         HalPropValue propResponse = createPropRequest(CREATE_USER, REQUEST_ID_PLACE_HOLDER,
1332                 new int[]{/*status =*/ -1});
1333 
1334         replySetPropertyWithOnChangeEvent(CREATE_USER, propResponse, /* rightRequestId= */ true);
1335 
1336         GenericHalCallback<CreateUserResponse> callback = new GenericHalCallback<>(
1337                 CALLBACK_TIMEOUT);
1338         mUserHalService.createUser(newValidCreateUserRequest(), HAL_TIMEOUT_MS, callback);
1339 
1340         callback.assertCalled();
1341 
1342         // Assert response
1343         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
1344         assertThat(callback.response).isNull();
1345     }
1346 
1347     @Test
testGetUserAssociation_noHalSupported()1348     public void testGetUserAssociation_noHalSupported() {
1349         // Cannot use mUserHalService because it's already set with supported properties
1350         UserHalService myHalService = new UserHalService(mVehicleHal);
1351 
1352         assertThrows(IllegalStateException.class,
1353                 () -> myHalService.getUserAssociation(new UserIdentificationGetRequest()));
1354     }
1355 
1356     @Test
testGetUserAssociation_nullRequest()1357     public void testGetUserAssociation_nullRequest() {
1358         assertThrows(NullPointerException.class, () -> mUserHalService.getUserAssociation(null));
1359     }
1360 
1361     @Test
testGetUserAssociation_requestWithDuplicatedTypes()1362     public void testGetUserAssociation_requestWithDuplicatedTypes() {
1363         UserIdentificationGetRequest request = new UserIdentificationGetRequest();
1364         request.numberAssociationTypes = 2;
1365         request.associationTypes = new int[]{KEY_FOB, KEY_FOB};
1366 
1367         assertThrows(IllegalArgumentException.class,
1368                 () -> mUserHalService.getUserAssociation(request));
1369     }
1370 
1371     @Test
testGetUserAssociation_invalidResponse()1372     public void testGetUserAssociation_invalidResponse() {
1373         HalPropValue propResponse = mPropValueBuilder.build(USER_IDENTIFICATION_ASSOCIATION,
1374                 /* areaId= */ 0,
1375                 new int[]{
1376                     DEFAULT_REQUEST_ID,
1377                     // 1 associations
1378                     1,
1379                     // type only, it's missing value
1380                     KEY_FOB});
1381 
1382         UserIdentificationGetRequest request = replyToValidGetUserIdentificationRequest(
1383                 propResponse);
1384 
1385         assertThat(mUserHalService.getUserAssociation(request)).isNull();
1386     }
1387 
1388     @Test
testGetUserAssociation_nullResponse()1389     public void testGetUserAssociation_nullResponse() {
1390         UserIdentificationGetRequest request = replyToValidGetUserIdentificationRequest(null);
1391 
1392         assertThat(mUserHalService.getUserAssociation(request)).isNull();
1393 
1394         verifyValidGetUserIdentificationRequestMade();
1395     }
1396 
1397     @Test
testGetUserAssociation_ServiceExceptionFromHal()1398     public void testGetUserAssociation_ServiceExceptionFromHal() {
1399         UserIdentificationGetRequest request =
1400                 replyToValidGetUserIdentificationRequestWithException(
1401                         new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN));
1402 
1403         assertThat(mUserHalService.getUserAssociation(request)).isNull();
1404 
1405         verifyValidGetUserIdentificationRequestMade();
1406     }
1407 
1408     @Test
testGetUserAssociation_wrongNumberOfAssociationsOnResponse()1409     public void testGetUserAssociation_wrongNumberOfAssociationsOnResponse() {
1410         HalPropValue propResponse = mPropValueBuilder.build(USER_IDENTIFICATION_ASSOCIATION,
1411                 /* areaId= */ 0,
1412                 new int[]{
1413                     DEFAULT_REQUEST_ID,
1414                     // 2 associations
1415                     2,
1416                     KEY_FOB,
1417                     ASSOCIATED_CURRENT_USER,
1418                     CUSTOM_1,
1419                     ASSOCIATED_CURRENT_USER
1420                 });
1421         UserIdentificationGetRequest request = replyToValidGetUserIdentificationRequest(
1422                 propResponse);
1423 
1424         assertThat(mUserHalService.getUserAssociation(request)).isNull();
1425 
1426         verifyValidGetUserIdentificationRequestMade();
1427     }
1428 
1429     @Test
testGetUserAssociation_typesOnResponseMismatchTypesOnRequest()1430     public void testGetUserAssociation_typesOnResponseMismatchTypesOnRequest() {
1431         HalPropValue propResponse = mPropValueBuilder.build(USER_IDENTIFICATION_ASSOCIATION,
1432                 /* areaId= */ 0,
1433                 new int[]{
1434                     DEFAULT_REQUEST_ID,
1435                     // 1 association
1436                     1,
1437                     CUSTOM_1,
1438                     ASSOCIATED_CURRENT_USER
1439                 });
1440         UserIdentificationGetRequest request = replyToValidGetUserIdentificationRequest(
1441                 propResponse);
1442 
1443         assertThat(mUserHalService.getUserAssociation(request)).isNull();
1444 
1445         verifyValidGetUserIdentificationRequestMade();
1446     }
1447 
1448     @Test
testGetUserAssociation_requestIdMismatch()1449     public void testGetUserAssociation_requestIdMismatch() {
1450         HalPropValue propResponse = mPropValueBuilder.build(USER_IDENTIFICATION_ASSOCIATION,
1451                 /* areaId= */ 0,
1452                 new int[]{
1453                     DEFAULT_REQUEST_ID + 1,
1454                     // 1 association
1455                     1,
1456                     KEY_FOB,
1457                     ASSOCIATED_CURRENT_USER
1458                 });
1459         UserIdentificationGetRequest request = replyToValidGetUserIdentificationRequest(
1460                 propResponse);
1461 
1462         assertThat(mUserHalService.getUserAssociation(request)).isNull();
1463 
1464         verifyValidGetUserIdentificationRequestMade();
1465     }
1466 
1467     @Test
testGetUserAssociation_ok()1468     public void testGetUserAssociation_ok() {
1469         HalPropValue propResponse = mPropValueBuilder.build(USER_IDENTIFICATION_ASSOCIATION,
1470                 /* areaId= */ 0,
1471                 new int[]{
1472                     DEFAULT_REQUEST_ID,
1473                     // 1 association
1474                     1,
1475                     KEY_FOB,
1476                     ASSOCIATED_CURRENT_USER
1477                 });
1478         UserIdentificationGetRequest request = replyToValidGetUserIdentificationRequest(
1479                 propResponse);
1480 
1481         UserIdentificationResponse response = mUserHalService.getUserAssociation(request);
1482 
1483         assertThat(response.requestId).isEqualTo(DEFAULT_REQUEST_ID);
1484         assertThat(response.numberAssociation).isEqualTo(1);
1485         assertThat(response.associations.length).isEqualTo(1);
1486         UserIdentificationAssociation actualAssociation = response.associations[0];
1487         assertThat(actualAssociation.type).isEqualTo(KEY_FOB);
1488         assertThat(actualAssociation.value).isEqualTo(ASSOCIATED_CURRENT_USER);
1489     }
1490 
1491     @Test
testSetUserAssociation_noHalSupported()1492     public void testSetUserAssociation_noHalSupported() {
1493         // Cannot use mUserHalService because it's already set with supported properties
1494         UserHalService myHalService = new UserHalService(mVehicleHal);
1495 
1496         assertThrows(IllegalStateException.class,
1497                 () -> myHalService.setUserAssociation(HAL_TIMEOUT_MS,
1498                         UserHalHelper.emptyUserIdentificationSetRequest(), noOpCallback()));
1499     }
1500 
1501     @Test
testSetUserAssociation_invalidTimeout()1502     public void testSetUserAssociation_invalidTimeout() {
1503         UserIdentificationSetRequest request = UserHalHelper.emptyUserIdentificationSetRequest();
1504         assertThrows(IllegalArgumentException.class, () ->
1505                 mUserHalService.setUserAssociation(0, request, noOpCallback()));
1506         assertThrows(IllegalArgumentException.class, () ->
1507                 mUserHalService.setUserAssociation(-1, request, noOpCallback()));
1508     }
1509 
1510     @Test
testSetUserAssociation_nullRequest()1511     public void testSetUserAssociation_nullRequest() {
1512         assertThrows(NullPointerException.class, () ->
1513                 mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, null, noOpCallback()));
1514     }
1515 
1516     @Test
testSetUserAssociation_nullCallback()1517     public void testSetUserAssociation_nullCallback() {
1518         UserIdentificationSetRequest request = UserHalHelper.emptyUserIdentificationSetRequest();
1519         assertThrows(NullPointerException.class, () ->
1520                 mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, request, null));
1521     }
1522 
1523     @Test
testSetUserAssociation_requestWithDuplicatedTypes()1524     public void testSetUserAssociation_requestWithDuplicatedTypes() {
1525         UserIdentificationSetRequest request = UserHalHelper.emptyUserIdentificationSetRequest();
1526         request.numberAssociations = 2;
1527         UserIdentificationSetAssociation association1 = new UserIdentificationSetAssociation();
1528         association1.type = KEY_FOB;
1529         association1.value = ASSOCIATE_CURRENT_USER;
1530         request.associations = new UserIdentificationSetAssociation[]{association1, association1};
1531 
1532         assertThrows(IllegalArgumentException.class, () ->
1533                 mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, request, noOpCallback()));
1534     }
1535 
1536     @Test
testSetUserAssociation_halSetTimedOut()1537     public void testSetUserAssociation_halSetTimedOut() throws Exception {
1538         UserIdentificationSetRequest request = validUserIdentificationSetRequest();
1539         GenericHalCallback<UserIdentificationResponse> callback = new GenericHalCallback<>(
1540                 CALLBACK_TIMEOUT);
1541         replySetPropertyWithTimeoutException(USER_IDENTIFICATION_ASSOCIATION);
1542 
1543         mUserHalService.setUserAssociation(HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS, request, callback);
1544 
1545         callback.assertCalled();
1546         assertCallbackStatus(callback, HalCallback.STATUS_HAL_SET_TIMEOUT);
1547         assertThat(callback.response).isNull();
1548 
1549         // Make sure the pending request was removed
1550         SystemClock.sleep(WAITING_TIME_FOR_NEGATIVE_TESTS_MS);
1551         callback.assertNotCalledAgain();
1552     }
1553 
1554     @Test
testSetUserAssociation_halDidNotReply()1555     public void testSetUserAssociation_halDidNotReply() throws Exception {
1556         UserIdentificationSetRequest request = validUserIdentificationSetRequest();
1557         GenericHalCallback<UserIdentificationResponse> callback = new GenericHalCallback<>(
1558                 CALLBACK_TIMEOUT);
1559 
1560         mUserHalService.setUserAssociation(HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS, request, callback);
1561 
1562         callback.assertCalled();
1563         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
1564         assertThat(callback.response).isNull();
1565     }
1566 
1567     @Test
testSetUserAssociation_secondCallFailWhilePending()1568     public void testSetUserAssociation_secondCallFailWhilePending() throws Exception {
1569         UserIdentificationSetRequest request = validUserIdentificationSetRequest();
1570         GenericHalCallback<UserIdentificationResponse> callback1 = new GenericHalCallback<>(
1571                 CALLBACK_TIMEOUT);
1572         GenericHalCallback<UserIdentificationResponse> callback2 = new GenericHalCallback<>(
1573                 CALLBACK_TIMEOUT);
1574 
1575         mUserHalService.setUserAssociation(HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS, request, callback1);
1576         mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, request, callback2);
1577 
1578         callback1.assertCalled();
1579         assertCallbackStatus(callback1, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
1580         assertThat(callback1.response).isNull();
1581 
1582         callback2.assertCalled();
1583         assertCallbackStatus(callback2, HalCallback.STATUS_CONCURRENT_OPERATION);
1584         assertThat(callback1.response).isNull();
1585     }
1586 
1587     @Test
testSetUserAssociation_responseWithWrongRequestId()1588     public void testSetUserAssociation_responseWithWrongRequestId() throws Exception {
1589         HalPropValue propResponse = mPropValueBuilder.build(USER_IDENTIFICATION_ASSOCIATION,
1590                 /* areaId= */ 0, new int[]{
1591                     DEFAULT_REQUEST_ID,
1592                     // 1 association
1593                     1,
1594                     KEY_FOB,
1595                     ASSOCIATED_CURRENT_USER
1596                 });
1597 
1598         AtomicReference<HalPropValue> propRequest = replySetPropertyWithOnChangeEvent(
1599                 USER_IDENTIFICATION_ASSOCIATION, propResponse, /* rightRequestId= */ false);
1600         UserIdentificationSetRequest request = replyToValidSetUserIdentificationRequest();
1601         GenericHalCallback<UserIdentificationResponse> callback = new GenericHalCallback<>(
1602                 CALLBACK_TIMEOUT);
1603 
1604         mUserHalService.setUserAssociation(HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS, request, callback);
1605 
1606         // Assert request
1607         verifyValidSetUserIdentificationRequestMade(propRequest.get());
1608         // Assert response
1609         callback.assertCalled();
1610         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
1611         assertThat(callback.response).isNull();
1612         verify(() -> CarStatsLog.write(
1613                 eq(CarStatsLog.CAR_USER_HAL_USER_ASSOCIATION_REQUEST_REPORTED),
1614                 anyInt(),
1615                 eq(CarStatsLog.CAR_USER_HAL_USER_ASSOCIATION_REQUEST_REPORTED__REQUEST_TYPE__SET),
1616                 eq(DEFAULT_USER_ID), eq(DEFAULT_USER_FLAGS), eq(1), anyString(), anyString()));
1617     }
1618 
1619     @Test
testSetUserAssociation_notEnoughValuesOnResponse()1620     public void testSetUserAssociation_notEnoughValuesOnResponse() throws Exception {
1621         // need at least 4: requestId, number associations, type1, value1
1622         HalPropValue propResponse = mPropValueBuilder.build(USER_IDENTIFICATION_ASSOCIATION,
1623                 /* areaId= */ 0, new int[]{1, 2, 3});
1624 
1625         AtomicReference<HalPropValue> propRequest = replySetPropertyWithOnChangeEvent(
1626                 USER_IDENTIFICATION_ASSOCIATION, propResponse, /* rightRequestId= */ true);
1627         UserIdentificationSetRequest request = replyToValidSetUserIdentificationRequest();
1628         GenericHalCallback<UserIdentificationResponse> callback = new GenericHalCallback<>(
1629                 CALLBACK_TIMEOUT);
1630 
1631         mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, request, callback);
1632 
1633         // Assert request
1634         verifyValidSetUserIdentificationRequestMade(propRequest.get());
1635         // Assert response
1636         callback.assertCalled();
1637         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
1638         assertThat(callback.response).isNull();
1639 
1640         int wrongHalResponse = CarStatsLog
1641                 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__WRONG_HAL_RESPONSE;
1642         verify(() -> CarStatsLog.write(
1643                 eq(CarStatsLog.CAR_USER_HAL_USER_ASSOCIATION_REQUEST_REPORTED),
1644                 anyInt(),
1645                 eq(CarStatsLog.CAR_USER_HAL_USER_ASSOCIATION_REQUEST_REPORTED__REQUEST_TYPE__SET),
1646                 eq(DEFAULT_USER_ID), eq(DEFAULT_USER_FLAGS), eq(1), anyString(), anyString()));
1647         verify(() -> CarStatsLog.write(
1648                 eq(CarStatsLog.CAR_USER_HAL_SET_USER_ASSOCIATION_RESPONSE_REPORTED),
1649                 anyInt(), eq(wrongHalResponse), eq(0), eq(""), eq("")), timeout(CALLBACK_TIMEOUT));
1650     }
1651 
1652     @Test
testSetUserAssociation_wrongNumberOfAssociationsOnResponse()1653     public void testSetUserAssociation_wrongNumberOfAssociationsOnResponse() throws Exception {
1654         HalPropValue propResponse = mPropValueBuilder.build(USER_IDENTIFICATION_ASSOCIATION,
1655                 /* areaId= */ 0, new int[]{
1656                     DEFAULT_REQUEST_ID,
1657                     // 2 associations; request is just 1
1658                     2,
1659                     KEY_FOB,
1660                     ASSOCIATED_CURRENT_USER,
1661                     CUSTOM_1,
1662                     ASSOCIATED_CURRENT_USER
1663                 });
1664 
1665         AtomicReference<HalPropValue> propRequest = replySetPropertyWithOnChangeEvent(
1666                 USER_IDENTIFICATION_ASSOCIATION, propResponse, /* rightRequestId= */ true);
1667         UserIdentificationSetRequest request = replyToValidSetUserIdentificationRequest();
1668         GenericHalCallback<UserIdentificationResponse> callback = new GenericHalCallback<>(
1669                 CALLBACK_TIMEOUT);
1670 
1671         mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, request, callback);
1672 
1673         // Assert request
1674         verifyValidSetUserIdentificationRequestMade(propRequest.get());
1675         // Assert response
1676         callback.assertCalled();
1677         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
1678         assertThat(callback.response).isNull();
1679 
1680         int wrongHalResponse = CarStatsLog
1681                 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__WRONG_HAL_RESPONSE;
1682         verify(() -> CarStatsLog.write(
1683                 eq(CarStatsLog.CAR_USER_HAL_USER_ASSOCIATION_REQUEST_REPORTED),
1684                 anyInt(),
1685                 eq(CarStatsLog.CAR_USER_HAL_USER_ASSOCIATION_REQUEST_REPORTED__REQUEST_TYPE__SET),
1686                 eq(DEFAULT_USER_ID), eq(DEFAULT_USER_FLAGS), eq(1), anyString(), anyString()));
1687         verify(() -> CarStatsLog.write(
1688                 eq(CarStatsLog.CAR_USER_HAL_SET_USER_ASSOCIATION_RESPONSE_REPORTED),
1689                 anyInt(), eq(wrongHalResponse), eq(2), anyString(), anyString()),
1690                 timeout(CALLBACK_TIMEOUT));
1691     }
1692 
1693     @Test
testSetUserAssociation_typeMismatchOnResponse()1694     public void testSetUserAssociation_typeMismatchOnResponse() throws Exception {
1695         HalPropValue propResponse = mPropValueBuilder.build(USER_IDENTIFICATION_ASSOCIATION,
1696                 /* areaId= */ 0, new int[]{
1697                     DEFAULT_REQUEST_ID,
1698                     // 1 association
1699                     1,
1700                     // request is KEY_FOB
1701                     CUSTOM_1,
1702                     ASSOCIATED_CURRENT_USER
1703                 });
1704 
1705         AtomicReference<HalPropValue> propRequest = replySetPropertyWithOnChangeEvent(
1706                 USER_IDENTIFICATION_ASSOCIATION, propResponse, /* rightRequestId= */ true);
1707         UserIdentificationSetRequest request = replyToValidSetUserIdentificationRequest();
1708         GenericHalCallback<UserIdentificationResponse> callback = new GenericHalCallback<>(
1709                 CALLBACK_TIMEOUT);
1710 
1711         mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, request, callback);
1712 
1713         // Assert request
1714         verifyValidSetUserIdentificationRequestMade(propRequest.get());
1715         // Assert response
1716         callback.assertCalled();
1717         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
1718         assertThat(callback.response).isNull();
1719 
1720         int wrongHalResponse = CarStatsLog
1721                 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__WRONG_HAL_RESPONSE;
1722         verify(() -> CarStatsLog.write(
1723                 eq(CarStatsLog.CAR_USER_HAL_USER_ASSOCIATION_REQUEST_REPORTED),
1724                 anyInt(),
1725                 eq(CarStatsLog.CAR_USER_HAL_USER_ASSOCIATION_REQUEST_REPORTED__REQUEST_TYPE__SET),
1726                 eq(DEFAULT_USER_ID), eq(DEFAULT_USER_FLAGS), eq(1), anyString(), anyString()));
1727         verify(() -> CarStatsLog.write(
1728                 eq(CarStatsLog.CAR_USER_HAL_SET_USER_ASSOCIATION_RESPONSE_REPORTED),
1729                 anyInt(), eq(wrongHalResponse), eq(1), anyString(), anyString()),
1730                 timeout(CALLBACK_TIMEOUT));
1731     }
1732 
1733     @Test
testSetUserAssociation_ok()1734     public void testSetUserAssociation_ok() throws Exception {
1735         HalPropValue propResponse = mPropValueBuilder.build(USER_IDENTIFICATION_ASSOCIATION,
1736                 /* areaId= */ 0, new int[]{
1737                     DEFAULT_REQUEST_ID,
1738                     // 1 association
1739                     1,
1740                     KEY_FOB,
1741                     ASSOCIATED_CURRENT_USER
1742                 });
1743 
1744         AtomicReference<HalPropValue> propRequest = replySetPropertyWithOnChangeEvent(
1745                 USER_IDENTIFICATION_ASSOCIATION, propResponse, /* rightRequestId= */ true);
1746         UserIdentificationSetRequest request = replyToValidSetUserIdentificationRequest();
1747         GenericHalCallback<UserIdentificationResponse> callback = new GenericHalCallback<>(
1748                 CALLBACK_TIMEOUT);
1749 
1750         mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, request, callback);
1751 
1752         // Assert request
1753         verifyValidSetUserIdentificationRequestMade(propRequest.get());
1754         // Assert response
1755         callback.assertCalled();
1756         assertCallbackStatus(callback, HalCallback.STATUS_OK);
1757 
1758         UserIdentificationResponse actualResponse = callback.response;
1759 
1760         assertThat(actualResponse.requestId).isEqualTo(DEFAULT_REQUEST_ID);
1761         assertThat(actualResponse.numberAssociation).isEqualTo(1);
1762         assertThat(actualResponse.associations.length).isEqualTo(1);
1763         UserIdentificationAssociation actualAssociation = actualResponse.associations[0];
1764         assertThat(actualAssociation.type).isEqualTo(KEY_FOB);
1765         assertThat(actualAssociation.value).isEqualTo(ASSOCIATED_CURRENT_USER);
1766 
1767         int statusOk = CarStatsLog
1768                 .CAR_USER_HAL_MODIFY_USER_RESPONSE_REPORTED__CALLBACK_STATUS__OK;
1769         verify(() -> CarStatsLog.write(
1770                 eq(CarStatsLog.CAR_USER_HAL_USER_ASSOCIATION_REQUEST_REPORTED),
1771                 anyInt(),
1772                 eq(CarStatsLog.CAR_USER_HAL_USER_ASSOCIATION_REQUEST_REPORTED__REQUEST_TYPE__SET),
1773                 eq(DEFAULT_USER_ID), eq(DEFAULT_USER_FLAGS), eq(1), anyString(), anyString()));
1774         verify(() -> CarStatsLog.write(
1775                 eq(CarStatsLog.CAR_USER_HAL_SET_USER_ASSOCIATION_RESPONSE_REPORTED),
1776                 anyInt(), eq(statusOk), eq(1), anyString(), anyString()),
1777                 timeout(CALLBACK_TIMEOUT));
1778     }
1779 
1780     /**
1781      * Asserts the given {@link UsersInfo} is properly represented in the {@link HalPropValue}.
1782      *
1783      * @param value property containing the info
1784      * @param info info to be checked
1785      * @param initialIndex first index of the info values in the property's {@code int32Values}
1786      */
assertUsersInfo(HalPropValue value, UsersInfo info, int initialIndex)1787     private void assertUsersInfo(HalPropValue value, UsersInfo info, int initialIndex) {
1788         // TODO: consider using UserHalHelper to convert the property into a specific request,
1789         // and compare the request's UsersInfo.
1790         // But such method is not needed in production code yet.
1791         List<Integer> values = getIntValues(value);
1792         assertWithMessage("wrong values size").that(values)
1793                 .hasSize(initialIndex + 3 + info.numberUsers * 2);
1794 
1795         int i = initialIndex;
1796         assertWithMessage("currentUser.id mismatch at index %s", i).that(values.get(i))
1797                 .isEqualTo(info.currentUser.userId);
1798         i++;
1799         assertWithMessage("currentUser.flags mismatch at index %s", i).that(values.get(i))
1800             .isEqualTo(info.currentUser.flags);
1801         i++;
1802         assertWithMessage("numberUsers mismatch at index %s", i).that(values.get(i))
1803             .isEqualTo(info.numberUsers);
1804         i++;
1805 
1806         for (int j = 0; j < info.numberUsers; j++) {
1807             int actualUserId = values.get(i++);
1808             int actualUserFlags = values.get(i++);
1809             UserInfo expectedUser = info.existingUsers[j];
1810             assertWithMessage("wrong id for existing user#%s at index %s", j, i)
1811                 .that(actualUserId).isEqualTo(expectedUser.userId);
1812             assertWithMessage("wrong flags for existing user#%s at index %s", j, i)
1813                 .that(actualUserFlags).isEqualTo(expectedUser.flags);
1814         }
1815     }
1816 
1817     /**
1818      * @see #replySetPropertyWithOnChangeEvent(int, HalPropValue, boolean, int)
1819      */
replySetPropertyWithOnChangeEvent(int prop, HalPropValue response, boolean rightRequestId)1820     private AtomicReference<HalPropValue> replySetPropertyWithOnChangeEvent(int prop,
1821             HalPropValue response, boolean rightRequestId) throws Exception {
1822         return replySetPropertyWithOnChangeEvent(prop, response, rightRequestId,
1823                 /* countDownLatch= */ null);
1824     }
1825 
1826     /**
1827      * Sets the VHAL mock to emulate a property change event upon a call to set a property.
1828      *
1829      * @param prop prop to be set
1830      * @param response response to be set on event
1831      * @param rightRequestId whether the response id should match the request
1832      * @param latch A {@link CountDownLatch} to control when to send the event.
1833      * @return A copy of the update property value.
1834      *
1835      * @return reference to the value passed to {@code set()}.
1836      */
replySetPropertyWithOnChangeEvent(int prop, HalPropValue response, boolean rightRequestId, CountDownLatch latch)1837     private AtomicReference<HalPropValue> replySetPropertyWithOnChangeEvent(int prop,
1838             HalPropValue response, boolean rightRequestId, CountDownLatch latch)
1839             throws Exception {
1840         AtomicReference<HalPropValue> ref = new AtomicReference<>();
1841         doAnswer((inv) -> {
1842             HalPropValue request = inv.getArgument(0);
1843             ref.set(request);
1844             int requestId = request.getInt32Value(0);
1845             int responseId = rightRequestId ? requestId : requestId + 1000;
1846 
1847             int[] intValues = new int[response.getInt32ValuesSize()];
1848             for (int i = 0; i < response.getInt32ValuesSize(); i++) {
1849                 if (i == 0) {
1850                     intValues[i] = responseId;
1851                     continue;
1852                 }
1853                 intValues[i] = response.getInt32Value(i);
1854             }
1855             HalPropValue responseCopy = mPropValueBuilder.build(response.getPropId(),
1856                     response.getAreaId(), response.getTimestamp(), response.getStatus(), intValues,
1857                     /*floatValues=*/new float[0], /*int64Values=*/new long[0],
1858                     response.getStringValue(), /*byteValues=*/new byte[0]);
1859 
1860             if (latch == null) {
1861                 Log.d(TAG, "replySetPropertyWithOnChangeEvent(): resp=" + responseCopy + " for req="
1862                         + request);
1863                 mUserHalService.onHalEvents(Arrays.asList(responseCopy));
1864                 return null;
1865             }
1866 
1867             new Thread(() -> {
1868                 JavaMockitoHelper.silentAwait(latch, CALLBACK_TIMEOUT);
1869                 Log.d(TAG, "replySetPropertyWithOnChangeEvent(): resp=" + responseCopy + " for req="
1870                         + request);
1871                 mUserHalService.onHalEvents(Arrays.asList(responseCopy));
1872             }, "replySetpropertyWithOnChangeEventThread").start();
1873 
1874             return null;
1875         }).when(mVehicleHal).set(isProperty(prop));
1876         return ref;
1877     }
1878 
1879     /**
1880      * Sets the VHAL mock to emulate a property timeout exception upon a call to set a property.
1881      */
replySetPropertyWithTimeoutException(int prop)1882     private void replySetPropertyWithTimeoutException(int prop) throws Exception {
1883         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN,
1884                 "PropId: 0x" + Integer.toHexString(prop))).when(mVehicleHal)
1885                 .set(isProperty(prop));
1886     }
1887 
1888     @NonNull
createUserSwitchRequest(@onNull UserInfo targetUser, @NonNull UsersInfo usersInfo)1889     private SwitchUserRequest createUserSwitchRequest(@NonNull UserInfo targetUser,
1890             @NonNull UsersInfo usersInfo) {
1891         SwitchUserRequest request = UserHalHelper.emptySwitchUserRequest();
1892         request.targetUser = targetUser;
1893         request.usersInfo = usersInfo;
1894         return request;
1895     }
1896 
1897     /**
1898      * Creates and set expectations for a valid request.
1899      */
replyToValidGetUserIdentificationRequest( @onNull HalPropValue response)1900     private UserIdentificationGetRequest replyToValidGetUserIdentificationRequest(
1901             @NonNull HalPropValue response) {
1902         mockNextRequestId(DEFAULT_REQUEST_ID);
1903 
1904         UserIdentificationGetRequest request = new UserIdentificationGetRequest();
1905         request.userInfo = new UserInfo();
1906         request.userInfo.userId = DEFAULT_USER_ID;
1907         request.userInfo.flags = DEFAULT_USER_FLAGS;
1908         request.numberAssociationTypes = 1;
1909         request.associationTypes = new int[]{KEY_FOB};
1910 
1911         when(mVehicleHal.get(isPropertyWithValues(USER_IDENTIFICATION_ASSOCIATION,
1912                 DEFAULT_REQUEST_ID, DEFAULT_USER_ID, DEFAULT_USER_FLAGS,
1913                 /* numberAssociations= */ 1, KEY_FOB)))
1914             .thenReturn(response);
1915 
1916         return request;
1917     }
1918 
replyToValidGetUserIdentificationRequestWithException( Exception e)1919     private UserIdentificationGetRequest replyToValidGetUserIdentificationRequestWithException(
1920             Exception e) {
1921         mockNextRequestId(DEFAULT_REQUEST_ID);
1922 
1923         UserIdentificationGetRequest request = new UserIdentificationGetRequest();
1924         request.userInfo = new UserInfo();
1925         request.userInfo.userId = DEFAULT_USER_ID;
1926         request.userInfo.flags = DEFAULT_USER_FLAGS;
1927         request.numberAssociationTypes = 1;
1928         request.associationTypes = new int[]{KEY_FOB};
1929 
1930         when(mVehicleHal.get(isPropertyWithValues(USER_IDENTIFICATION_ASSOCIATION,
1931                 DEFAULT_REQUEST_ID, DEFAULT_USER_ID, DEFAULT_USER_FLAGS,
1932                 /* numberAssociations= */ 1, KEY_FOB))).thenThrow(e);
1933 
1934         return request;
1935     }
1936 
1937     /**
1938      * Creates and set expectations for a valid request.
1939      */
replyToValidSetUserIdentificationRequest()1940     private UserIdentificationSetRequest replyToValidSetUserIdentificationRequest() {
1941         mockNextRequestId(DEFAULT_REQUEST_ID);
1942         return validUserIdentificationSetRequest();
1943     }
1944 
1945     /**
1946      * Creates a valid request that can be used in test cases where its content is not asserted.
1947      */
validUserIdentificationSetRequest()1948     private UserIdentificationSetRequest validUserIdentificationSetRequest() {
1949         UserIdentificationSetRequest request = UserHalHelper.emptyUserIdentificationSetRequest();
1950         request.userInfo.userId = DEFAULT_USER_ID;
1951         request.userInfo.flags = DEFAULT_USER_FLAGS;
1952         request.numberAssociations = 1;
1953         UserIdentificationSetAssociation association1 = new UserIdentificationSetAssociation();
1954         association1.type = KEY_FOB;
1955         association1.value = ASSOCIATE_CURRENT_USER;
1956         request.associations = new UserIdentificationSetAssociation[]{association1};
1957         return request;
1958     }
1959 
1960     /**
1961      * Run empty runnable to make sure that all posted handlers are done.
1962      */
waitForHandler()1963     private void waitForHandler() {
1964         mHandler.runWithScissors(() -> { }, /* Default timeout */ CALLBACK_TIMEOUT);
1965     }
1966 
mockNextRequestId(int requestId)1967     private void mockNextRequestId(int requestId) {
1968         doReturn(requestId).when(mUserHalService).getNextRequestId();
1969     }
1970 
mockUserHalEnabled(@ullable Boolean enabled)1971     private void mockUserHalEnabled(@Nullable Boolean enabled) {
1972         Optional<Boolean> value = enabled != null ? Optional.of(enabled) : Optional.empty();
1973         doReturn(value).when(() -> CarSystemProperties.getUserHalEnabled());
1974     }
1975 
assertInitialUserInfoSetRequest(HalPropValue req, int requestType)1976     private void assertInitialUserInfoSetRequest(HalPropValue req, int requestType) {
1977         assertThat(req.getInt32Value(1)).isEqualTo(requestType);
1978         assertUsersInfo(req, mUsersInfo, 2);
1979     }
1980 
assertHalSetSwitchUserRequest(HalPropValue req, int messageType, UserInfo targetUserInfo)1981     private void assertHalSetSwitchUserRequest(HalPropValue req, int messageType,
1982             UserInfo targetUserInfo) {
1983         assertThat(req.getPropId()).isEqualTo(SWITCH_USER);
1984         assertWithMessage("wrong request Id on %s", req).that(req.getInt32Value(0))
1985             .isAtLeast(1);
1986         assertThat(req.getInt32Value(1)).isEqualTo(messageType);
1987         assertWithMessage("targetuser.id mismatch on %s", req).that(req.getInt32Value(2))
1988                 .isEqualTo(targetUserInfo.userId);
1989         assertWithMessage("targetuser.flags mismatch on %s", req).that(req.getInt32Value(3))
1990                 .isEqualTo(targetUserInfo.flags);
1991         assertUsersInfo(req, mUsersInfo, 4);
1992     }
1993 
assertHalSetRemoveUserRequest(HalPropValue req, UserInfo userInfo)1994     private void assertHalSetRemoveUserRequest(HalPropValue req, UserInfo userInfo) {
1995         assertThat(req.getPropId()).isEqualTo(REMOVE_USER);
1996         assertWithMessage("wrong request Id on %s", req).that(req.getInt32Value(0))
1997                 .isAtLeast(1);
1998         assertWithMessage("user.id mismatch on %s", req).that(req.getInt32Value(1))
1999                 .isEqualTo(userInfo.userId);
2000         assertWithMessage("user.flags mismatch on %s", req).that(req.getInt32Value(2))
2001                 .isEqualTo(userInfo.flags);
2002         assertUsersInfo(req, mUsersInfo, 3);
2003     }
2004 
assertHalSetCreateUserRequest(HalPropValue prop, CreateUserRequest request)2005     private void assertHalSetCreateUserRequest(HalPropValue prop, CreateUserRequest request) {
2006         assertThat(prop.getPropId()).isEqualTo(CREATE_USER);
2007         assertWithMessage("wrong request Id on %s", prop).that(prop.getInt32Value(0))
2008                 .isEqualTo(request.requestId);
2009         assertWithMessage("newUser.userId mismatch on %s", prop).that(prop.getInt32Value(1))
2010                 .isEqualTo(request.newUserInfo.userId);
2011         assertWithMessage("newUser.flags mismatch on %s", prop).that(prop.getInt32Value(2))
2012                 .isEqualTo(request.newUserInfo.flags);
2013         assertUsersInfo(prop, request.usersInfo, 3);
2014     }
2015 
assertCallbackStatus(GenericHalCallback<?> callback, int expectedStatus)2016     private void assertCallbackStatus(GenericHalCallback<?> callback, int expectedStatus) {
2017         int actualStatus = callback.status;
2018         if (actualStatus == expectedStatus) return;
2019 
2020         fail("Wrong callback status; expected "
2021                 + UserHalHelper.halCallbackStatusToString(expectedStatus) + ", got "
2022                 + UserHalHelper.halCallbackStatusToString(actualStatus));
2023     }
2024 
2025     /**
2026      * Verifies {@code hal.get()} was called with the values used on
2027      * {@link #replyToValidGetUserIdentificationRequest(HalPropValue)}.
2028      */
verifyValidGetUserIdentificationRequestMade()2029     private void verifyValidGetUserIdentificationRequestMade() {
2030         verify(mVehicleHal).get(isPropertyWithValues(USER_IDENTIFICATION_ASSOCIATION,
2031                 DEFAULT_REQUEST_ID, DEFAULT_USER_ID, DEFAULT_USER_FLAGS,
2032                 /* numberAssociations= */ 1, KEY_FOB));
2033     }
2034 
2035     /**
2036      * Verifies {@code hal.set()} was called with the values used on
2037      * {@link #replyToValidSetUserIdentificationRequest(HalPropValue)}.
2038      */
verifyValidSetUserIdentificationRequestMade(@onNull HalPropValue request)2039     private void verifyValidSetUserIdentificationRequestMade(@NonNull HalPropValue request) {
2040         assertThat(request.getPropId()).isEqualTo(USER_IDENTIFICATION_ASSOCIATION);
2041 
2042         assertThat(getIntValues(request)).containsExactly(DEFAULT_REQUEST_ID, DEFAULT_USER_ID,
2043                 DEFAULT_USER_FLAGS,
2044                 /* numberAssociations= */ 1, KEY_FOB, ASSOCIATE_CURRENT_USER);
2045     }
2046 
createPropRequest(int propId, int requestId)2047     private HalPropValue createPropRequest(int propId, int requestId) {
2048         return createPropRequest(propId, requestId, null, new int[0], new String());
2049     }
2050 
createPropRequest(int propId, int requestId, int[] intValues)2051     private HalPropValue createPropRequest(int propId, int requestId, int[] intValues) {
2052         return createPropRequest(propId, requestId, null, intValues, new String());
2053     }
2054 
createPropRequest(int propId, int requestId, int requestType)2055     private HalPropValue createPropRequest(int propId, int requestId, int requestType) {
2056         return createPropRequest(propId, requestId, requestType, new int[0], new String());
2057     }
2058 
createPropRequest(int propId, int requestId, int requestType, int[] intValues)2059     private HalPropValue createPropRequest(int propId, int requestId, int requestType,
2060             int[] intValues) {
2061         return createPropRequest(propId, requestId, requestType, intValues, new String());
2062     }
2063 
createPropRequest(int propId, int requestId, Integer requestType, int[] intValues, String stringValue)2064     private HalPropValue createPropRequest(int propId, int requestId, Integer requestType,
2065             int[] intValues, String stringValue) {
2066         int intLength = intValues.length + 1;
2067         if (requestType != null) {
2068             intLength += 1;
2069         }
2070         int[] values = new int[intLength];
2071         values[0] = requestId;
2072         int start = 1;
2073 
2074         if (requestType != null) {
2075             values[1] = requestType;
2076             start = 2;
2077         }
2078         for (int i = 0; i < intValues.length; i++) {
2079             values[i + start] = intValues[i];
2080         }
2081         return mPropValueBuilder.build(propId, /* areaId= */ 0,
2082                 SystemClock.elapsedRealtime(), /* status= */ 0, values,
2083                 new float[0], new long[0], stringValue, new byte[0]);
2084     }
2085 
noOpCallback()2086     private static <T> HalCallback<T> noOpCallback() {
2087         return (i, r) -> { };
2088     }
2089 
getIntValues(HalPropValue value)2090     private static List<Integer> getIntValues(HalPropValue value) {
2091         ArrayList<Integer> values = new ArrayList<Integer>();
2092         for (int i = 0; i < value.getInt32ValuesSize(); i++) {
2093             values.add(value.getInt32Value(i));
2094         }
2095         return values;
2096     }
2097 
2098     private static final class GenericHalCallback<R> implements HalCallback<R> {
2099 
2100         private final CountDownLatch mLatch = new CountDownLatch(1);
2101         private final int mTimeout;
2102         private final List<Pair<Integer, R>> mExtraCalls = new ArrayList<>();
2103 
2104         public int status;
2105         public R response;
2106 
GenericHalCallback(int timeout)2107         GenericHalCallback(int timeout) {
2108             this.mTimeout = timeout;
2109         }
2110 
2111         @Override
onResponse(int status, R response)2112         public void onResponse(int status, R response) {
2113             Log.d(TAG, "onResponse(): status=" + status + ", response=" + response);
2114             if (mLatch.getCount() == 0) {
2115                 Log.e(TAG, "Already responded");
2116                 mExtraCalls.add(new Pair<>(status, response));
2117                 return;
2118             }
2119             this.status = status;
2120             this.response = response;
2121             mLatch.countDown();
2122         }
2123 
2124         /**
2125          * Asserts that the callback was called, or fail if it timed out.
2126          */
assertCalled()2127         public void assertCalled() throws InterruptedException {
2128             Log.d(TAG, "assertCalled(): waiting " + mTimeout + "ms");
2129             if (!mLatch.await(mTimeout, TimeUnit.MILLISECONDS)) {
2130                 throw new AssertionError("callback not called in " + mTimeout + "ms");
2131             }
2132         }
2133 
2134         /**
2135          * Asserts that the callback was not called more than once.
2136          */
assertNotCalledAgain()2137         public void assertNotCalledAgain() {
2138             if (mExtraCalls.isEmpty()) return;
2139             throw new AssertionError("Called " + mExtraCalls.size() + " times more than expected: "
2140                     + mExtraCalls);
2141         }
2142     }
2143 }
2144