• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.car.test.mocks.CarArgumentMatchers.isProperty;
25 import static android.car.test.mocks.CarArgumentMatchers.isPropertyWithValues;
26 import static android.car.test.util.VehicleHalTestingHelper.newConfig;
27 import static android.car.test.util.VehicleHalTestingHelper.newSubscribableConfig;
28 import static android.hardware.automotive.vehicle.V2_0.InitialUserInfoRequestType.COLD_BOOT;
29 import static android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationSetValue.ASSOCIATE_CURRENT_USER;
30 import static android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationType.CUSTOM_1;
31 import static android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationType.KEY_FOB;
32 import static android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociationValue.ASSOCIATED_CURRENT_USER;
33 
34 import static com.google.common.truth.Truth.assertThat;
35 import static com.google.common.truth.Truth.assertWithMessage;
36 
37 import static org.junit.Assert.fail;
38 import static org.mockito.ArgumentMatchers.anyInt;
39 import static org.mockito.Mockito.doAnswer;
40 import static org.mockito.Mockito.doReturn;
41 import static org.mockito.Mockito.doThrow;
42 import static org.mockito.Mockito.never;
43 import static org.mockito.Mockito.spy;
44 import static org.mockito.Mockito.verify;
45 import static org.mockito.Mockito.when;
46 import static org.testng.Assert.assertThrows;
47 
48 import android.annotation.NonNull;
49 import android.car.hardware.property.VehicleHalStatusCode;
50 import android.car.userlib.HalCallback;
51 import android.car.userlib.UserHalHelper;
52 import android.hardware.automotive.vehicle.V2_0.CreateUserRequest;
53 import android.hardware.automotive.vehicle.V2_0.CreateUserResponse;
54 import android.hardware.automotive.vehicle.V2_0.CreateUserStatus;
55 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse;
56 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponseAction;
57 import android.hardware.automotive.vehicle.V2_0.RemoveUserRequest;
58 import android.hardware.automotive.vehicle.V2_0.SwitchUserMessageType;
59 import android.hardware.automotive.vehicle.V2_0.SwitchUserRequest;
60 import android.hardware.automotive.vehicle.V2_0.SwitchUserResponse;
61 import android.hardware.automotive.vehicle.V2_0.SwitchUserStatus;
62 import android.hardware.automotive.vehicle.V2_0.UserFlags;
63 import android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociation;
64 import android.hardware.automotive.vehicle.V2_0.UserIdentificationGetRequest;
65 import android.hardware.automotive.vehicle.V2_0.UserIdentificationResponse;
66 import android.hardware.automotive.vehicle.V2_0.UserIdentificationSetAssociation;
67 import android.hardware.automotive.vehicle.V2_0.UserIdentificationSetRequest;
68 import android.hardware.automotive.vehicle.V2_0.UserInfo;
69 import android.hardware.automotive.vehicle.V2_0.UsersInfo;
70 import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
71 import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
72 import android.os.Handler;
73 import android.os.Looper;
74 import android.os.ServiceSpecificException;
75 import android.os.SystemClock;
76 import android.os.UserHandle;
77 import android.util.Log;
78 import android.util.Pair;
79 
80 import com.android.car.CarLocalServices;
81 import com.android.car.user.CarUserService;
82 
83 import org.junit.After;
84 import org.junit.Before;
85 import org.junit.Test;
86 import org.junit.runner.RunWith;
87 import org.mockito.ArgumentCaptor;
88 import org.mockito.Mock;
89 import org.mockito.junit.MockitoJUnitRunner;
90 
91 import java.util.ArrayList;
92 import java.util.Arrays;
93 import java.util.Collections;
94 import java.util.List;
95 import java.util.concurrent.CountDownLatch;
96 import java.util.concurrent.TimeUnit;
97 import java.util.concurrent.atomic.AtomicReference;
98 
99 @RunWith(MockitoJUnitRunner.class)
100 public final class UserHalServiceTest {
101 
102     private static final String TAG = UserHalServiceTest.class.getSimpleName();
103 
104     /**
105      * Timeout passed to {@link UserHalService} methods. This is the timeout for which
106      * {@link UserHalService} wait for the property change event and return
107      * {@link HalCallback.STATUS_HAL_RESPONSE_TIMEOUT} if HAL doesn't respond. This timeout is used
108      * where we expect HAL to return something and {@link UserHalService} methods are not expected
109      * to timeout. Tests are not supposed to wait for this much so this value can be high. If test
110      * requires HAL to timeout, then use {@link HAL_TIMEOUT_MS}.
111      */
112     private static final int HAL_TIMEOUT_MS = 5_000;
113 
114     /**
115      * Timeout passed to {@link UserHalService} methods only if test expects that call will be
116      * timeout and {@link HalCallback.STATUS_HAL_RESPONSE_TIMEOUT} is expected response. A higher
117      * value for this is going to slow down tests. If this timeout is used, then it is expected that
118      * {@link HalCallback.STATUS_HAL_RESPONSE_TIMEOUT} is checked.
119      */
120     private static final int HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS = 250;
121 
122     /**
123      * If tests expect {@link UserHalService} call to timeout, and if they require to sleep then
124      * sleep for this much time. This value should be higher than {@link HAL_TIMEOUT_MS}. A much
125      * higher value for this is going to slow down the test.
126      */
127     private static final int WAITING_TIME_FOR_NEGATIVE_TESTS_MS = HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS
128             + 100;
129 
130     /**
131      * Timeout for {@link GenericHalCallback#assertCalled()} for tests. In each case,
132      * {@link UserHalService} is supposed to return something - either a valid response or a
133      * timeout. This timeout is for the callback to wait for the response. A higher value of this
134      * timeout should not affect the test duration.
135      */
136     private static final int CALLBACK_TIMEOUT = 5_000;
137 
138     // Used when crafting a request property - the real value will be set by the mock.
139     private static final int REQUEST_ID_PLACE_HOLDER = 1111;
140 
141     private static final int DEFAULT_REQUEST_ID = 2222;
142 
143     private static final int DEFAULT_USER_ID = 333;
144     private static final int DEFAULT_USER_FLAGS = 444;
145 
146     private static final int INITIAL_USER_INFO_RESPONSE_ACTION = 108;
147 
148     @Mock
149     private VehicleHal mVehicleHal;
150     @Mock
151     private CarUserService mCarUserService;
152 
153     private final Handler mHandler = new Handler(Looper.getMainLooper());
154 
155     private final UserInfo mUser0 = new UserInfo();
156     private final UserInfo mUser10 = new UserInfo();
157 
158     private final UsersInfo mUsersInfo = new UsersInfo();
159 
160     // Must be a spy so we can mock getNextRequestId()
161     private UserHalService mUserHalService;
162 
163     @Before
setFixtures()164     public void setFixtures() {
165         mUserHalService = spy(new UserHalService(mVehicleHal, mHandler));
166         // Needs at least one property, otherwise isSupported() and isUserAssociationSupported()
167         // will return false
168         mUserHalService.takeProperties(Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
169                 newSubscribableConfig(CREATE_USER), newSubscribableConfig(REMOVE_USER),
170                 newSubscribableConfig(SWITCH_USER),
171                 newSubscribableConfig(USER_IDENTIFICATION_ASSOCIATION)));
172 
173         mUser0.userId = 0;
174         mUser0.flags = 100;
175         mUser10.userId = 10;
176         mUser10.flags = 110;
177 
178         mUsersInfo.currentUser = mUser0;
179         mUsersInfo.numberUsers = 2;
180         mUsersInfo.existingUsers = new ArrayList<>(2);
181         mUsersInfo.existingUsers.add(mUser0);
182         mUsersInfo.existingUsers.add(mUser10);
183 
184         CarLocalServices.addService(CarUserService.class, mCarUserService);
185     }
186 
187     @After
clearFixtures()188     public void clearFixtures() {
189         CarLocalServices.removeServiceForTest(CarUserService.class);
190     }
191 
192     @Test
testTakeSupportedProperties_supportedNoProperties()193     public void testTakeSupportedProperties_supportedNoProperties() {
194         // Cannot use mUserHalService because it's already set with supported properties
195         UserHalService myHalService = new UserHalService(mVehicleHal);
196 
197         myHalService.takeProperties(Collections.emptyList());
198         assertThat(myHalService.isSupported()).isFalse();
199         assertThat(myHalService.isUserAssociationSupported()).isFalse();
200     }
201 
202     @Test
testTakeSupportedProperties_supportedFewProperties()203     public void testTakeSupportedProperties_supportedFewProperties() {
204         // Cannot use mUserHalService because it's already set with supported properties
205         UserHalService myHalService = new UserHalService(mVehicleHal);
206         myHalService.takeProperties(Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
207                 newSubscribableConfig(CREATE_USER), newSubscribableConfig(REMOVE_USER)));
208 
209         assertThat(myHalService.isSupported()).isFalse();
210         assertThat(myHalService.isUserAssociationSupported()).isFalse();
211     }
212 
213     @Test
testTakeSupportedProperties_supportedAllCoreProperties()214     public void testTakeSupportedProperties_supportedAllCoreProperties() {
215         // Cannot use mUserHalService because it's already set with supported properties
216         UserHalService myHalService = new UserHalService(mVehicleHal);
217         myHalService.takeProperties(Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
218                 newSubscribableConfig(CREATE_USER), newSubscribableConfig(REMOVE_USER),
219                 newSubscribableConfig(SWITCH_USER)));
220 
221         assertThat(myHalService.isSupported()).isTrue();
222         assertThat(myHalService.isUserAssociationSupported()).isFalse();
223     }
224 
225     @Test
testTakeSupportedProperties_supportedAllProperties()226     public void testTakeSupportedProperties_supportedAllProperties() {
227         // Cannot use mUserHalService because it's already set with supported properties
228         UserHalService myHalService = new UserHalService(mVehicleHal);
229         myHalService.takeProperties(Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
230                 newSubscribableConfig(CREATE_USER), newSubscribableConfig(REMOVE_USER),
231                 newSubscribableConfig(SWITCH_USER),
232                 newSubscribableConfig(USER_IDENTIFICATION_ASSOCIATION)));
233 
234         assertThat(myHalService.isSupported()).isTrue();
235         assertThat(myHalService.isUserAssociationSupported()).isTrue();
236     }
237 
238     @Test
testTakeSupportedPropertiesAndInit()239     public void testTakeSupportedPropertiesAndInit() {
240         // Cannot use mUserHalService because it's already set with supported properties
241         UserHalService myHalService = new UserHalService(mVehicleHal);
242         VehiclePropConfig unsupportedConfig = newConfig(CURRENT_GEAR);
243 
244         myHalService.takeProperties(Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
245                 newSubscribableConfig(CREATE_USER), newSubscribableConfig(REMOVE_USER),
246                 newSubscribableConfig(SWITCH_USER), unsupportedConfig,
247                 newSubscribableConfig(USER_IDENTIFICATION_ASSOCIATION)));
248 
249 
250         // Ideally there should be 2 test methods (one for takeSupportedProperties() and one for
251         // init()), but on "real life" VehicleHal calls these 2 methods in sequence, and the latter
252         // depends on the properties set by the former, so it's ok to test both here...
253         myHalService.init();
254         verify(mVehicleHal).subscribeProperty(myHalService, INITIAL_USER_INFO);
255         verify(mVehicleHal).subscribeProperty(myHalService, CREATE_USER);
256         verify(mVehicleHal).subscribeProperty(myHalService, REMOVE_USER);
257         verify(mVehicleHal).subscribeProperty(myHalService, SWITCH_USER);
258         verify(mVehicleHal).subscribeProperty(myHalService, USER_IDENTIFICATION_ASSOCIATION);
259     }
260 
261     @Test
testSupportedProperties()262     public void testSupportedProperties() {
263         assertThat(mUserHalService.getAllSupportedProperties()).asList().containsExactly(
264                 INITIAL_USER_INFO, CREATE_USER, REMOVE_USER, SWITCH_USER,
265                 USER_IDENTIFICATION_ASSOCIATION);
266     }
267 
268     @Test
testGetUserInfo_noHalSupported()269     public void testGetUserInfo_noHalSupported() {
270         // Cannot use mUserHalService because it's already set with supported properties
271         UserHalService myHalService = new UserHalService(mVehicleHal);
272 
273         assertThrows(IllegalStateException.class, () -> myHalService.getInitialUserInfo(COLD_BOOT,
274                 HAL_TIMEOUT_MS, mUsersInfo, noOpCallback()));
275     }
276 
277     @Test
testGetUserInfo_invalidTimeout()278     public void testGetUserInfo_invalidTimeout() {
279         assertThrows(IllegalArgumentException.class, () ->
280                 mUserHalService.getInitialUserInfo(COLD_BOOT, 0, mUsersInfo, noOpCallback()));
281         assertThrows(IllegalArgumentException.class, () ->
282                 mUserHalService.getInitialUserInfo(COLD_BOOT, -1, mUsersInfo, noOpCallback()));
283     }
284 
285     @Test
testGetUserInfo_noUsersInfo()286     public void testGetUserInfo_noUsersInfo() {
287         assertThrows(NullPointerException.class, () -> mUserHalService.getInitialUserInfo(COLD_BOOT,
288                 HAL_TIMEOUT_MS, null, noOpCallback()));
289     }
290 
291     @Test
testGetUserInfo_noCallback()292     public void testGetUserInfo_noCallback() {
293         assertThrows(NullPointerException.class,
294                 () -> mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_MS,
295                         mUsersInfo, null));
296     }
297 
298     @Test
testGetUserInfo_halSetTimedOut()299     public void testGetUserInfo_halSetTimedOut() throws Exception {
300         replySetPropertyWithTimeoutException(INITIAL_USER_INFO);
301 
302         GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
303                 CALLBACK_TIMEOUT);
304         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_MS, mUsersInfo,
305                 callback);
306 
307         callback.assertCalled();
308         assertCallbackStatus(callback, HalCallback.STATUS_HAL_SET_TIMEOUT);
309         assertThat(callback.response).isNull();
310 
311         // Make sure the pending request was removed
312         SystemClock.sleep(WAITING_TIME_FOR_NEGATIVE_TESTS_MS);
313         callback.assertNotCalledAgain();
314     }
315 
316     @Test
testGetUserInfo_halDidNotReply()317     public void testGetUserInfo_halDidNotReply() throws Exception {
318         GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
319                 CALLBACK_TIMEOUT);
320         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS, mUsersInfo,
321                 callback);
322 
323         callback.assertCalled();
324         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
325         assertThat(callback.response).isNull();
326     }
327 
328     @Test
testGetUserInfo_secondCallFailWhilePending()329     public void testGetUserInfo_secondCallFailWhilePending() throws Exception {
330         GenericHalCallback<InitialUserInfoResponse> callback1 = new GenericHalCallback<>(
331                 CALLBACK_TIMEOUT);
332         GenericHalCallback<InitialUserInfoResponse> callback2 = new GenericHalCallback<>(
333                 CALLBACK_TIMEOUT);
334         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS, mUsersInfo,
335                 callback1);
336         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_MS, mUsersInfo,
337                 callback2);
338 
339         callback1.assertCalled();
340         assertCallbackStatus(callback1, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
341         assertThat(callback1.response).isNull();
342 
343         callback2.assertCalled();
344         assertCallbackStatus(callback2, HalCallback.STATUS_CONCURRENT_OPERATION);
345         assertThat(callback1.response).isNull();
346     }
347 
348     @Test
testGetUserInfo_halReplyWithWrongRequestId()349     public void testGetUserInfo_halReplyWithWrongRequestId() throws Exception {
350         VehiclePropValue propResponse = UserHalHelper.createPropRequest(INITIAL_USER_INFO,
351                     REQUEST_ID_PLACE_HOLDER, INITIAL_USER_INFO_RESPONSE_ACTION);
352 
353         replySetPropertyWithOnChangeEvent(INITIAL_USER_INFO, propResponse,
354                 /* rightRequestId= */ false);
355 
356         GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
357                 CALLBACK_TIMEOUT);
358         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS, mUsersInfo,
359                 callback);
360 
361         callback.assertCalled();
362         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
363         assertThat(callback.response).isNull();
364     }
365 
366     @Test
testGetUserInfo_halReturnedInvalidAction()367     public void testGetUserInfo_halReturnedInvalidAction() throws Exception {
368         VehiclePropValue propResponse = UserHalHelper.createPropRequest(INITIAL_USER_INFO,
369                     REQUEST_ID_PLACE_HOLDER, INITIAL_USER_INFO_RESPONSE_ACTION);
370 
371         AtomicReference<VehiclePropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
372                 INITIAL_USER_INFO, propResponse, /* rightRequestId= */ true);
373 
374         GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
375                 CALLBACK_TIMEOUT);
376         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_MS, mUsersInfo,
377                 callback);
378 
379         callback.assertCalled();
380 
381         // Make sure the arguments were properly converted
382         assertInitialUserInfoSetRequest(reqCaptor.get(), COLD_BOOT);
383 
384         // Assert response
385         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
386         assertThat(callback.response).isNull();
387     }
388 
389     @Test
testGetUserInfo_successDefault()390     public void testGetUserInfo_successDefault() throws Exception {
391         VehiclePropValue propResponse = UserHalHelper.createPropRequest(INITIAL_USER_INFO,
392                     REQUEST_ID_PLACE_HOLDER, InitialUserInfoResponseAction.DEFAULT);
393 
394         AtomicReference<VehiclePropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
395                 INITIAL_USER_INFO, propResponse, /* rightRequestId= */ true);
396 
397         GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
398                 CALLBACK_TIMEOUT);
399         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_MS, mUsersInfo,
400                 callback);
401 
402         callback.assertCalled();
403 
404         // Make sure the arguments were properly converted
405         assertInitialUserInfoSetRequest(reqCaptor.get(), COLD_BOOT);
406 
407         // Assert response
408         assertCallbackStatus(callback, HalCallback.STATUS_OK);
409         InitialUserInfoResponse actualResponse = callback.response;
410         assertThat(actualResponse.action).isEqualTo(InitialUserInfoResponseAction.DEFAULT);
411         assertThat(actualResponse.userNameToCreate).isEmpty();
412         assertThat(actualResponse.userToSwitchOrCreate).isNotNull();
413         assertThat(actualResponse.userToSwitchOrCreate.userId).isEqualTo(UserHandle.USER_NULL);
414         assertThat(actualResponse.userToSwitchOrCreate.flags).isEqualTo(UserFlags.NONE);
415     }
416 
417     @Test
testGetUserInfo_successSwitchUser()418     public void testGetUserInfo_successSwitchUser() throws Exception {
419         int userIdToSwitch = 42;
420         VehiclePropValue propResponse = UserHalHelper.createPropRequest(INITIAL_USER_INFO,
421                     REQUEST_ID_PLACE_HOLDER, InitialUserInfoResponseAction.SWITCH);
422         propResponse.value.int32Values.add(userIdToSwitch);
423 
424         AtomicReference<VehiclePropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
425                 INITIAL_USER_INFO, propResponse, /* rightRequestId= */ true);
426 
427         GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
428                 CALLBACK_TIMEOUT);
429         mUserHalService.getInitialUserInfo(COLD_BOOT, HAL_TIMEOUT_MS, mUsersInfo,
430                 callback);
431 
432         callback.assertCalled();
433 
434         // Make sure the arguments were properly converted
435         assertInitialUserInfoSetRequest(reqCaptor.get(), COLD_BOOT);
436 
437         assertCallbackStatus(callback, HalCallback.STATUS_OK);
438         InitialUserInfoResponse actualResponse = callback.response;
439         assertThat(actualResponse.action).isEqualTo(InitialUserInfoResponseAction.SWITCH);
440         assertThat(actualResponse.userNameToCreate).isEmpty();
441         UserInfo userToSwitch = actualResponse.userToSwitchOrCreate;
442         assertThat(userToSwitch).isNotNull();
443         assertThat(userToSwitch.userId).isEqualTo(userIdToSwitch);
444         assertThat(userToSwitch.flags).isEqualTo(UserFlags.NONE);
445     }
446 
447     @Test
testGetUserInfo_successCreateUser()448     public void testGetUserInfo_successCreateUser() throws Exception {
449         int newUserFlags = 108;
450         String newUserName = "Groot";
451         VehiclePropValue propResponse = UserHalHelper.createPropRequest(INITIAL_USER_INFO,
452                     REQUEST_ID_PLACE_HOLDER, InitialUserInfoResponseAction.CREATE);
453         propResponse.value.int32Values.add(666); // userId (not used)
454         propResponse.value.int32Values.add(newUserFlags);
455         propResponse.value.stringValue = "||" + newUserName;
456 
457         AtomicReference<VehiclePropValue> 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         assertCallbackStatus(callback, HalCallback.STATUS_OK);
471         assertThat(callback.status).isEqualTo(HalCallback.STATUS_OK);
472         InitialUserInfoResponse actualResponse = callback.response;
473         assertThat(actualResponse.action).isEqualTo(InitialUserInfoResponseAction.CREATE);
474         assertThat(actualResponse.userLocales).isEmpty();
475         assertThat(actualResponse.userNameToCreate).isEqualTo(newUserName);
476         UserInfo newUser = actualResponse.userToSwitchOrCreate;
477         assertThat(newUser).isNotNull();
478         assertThat(newUser.userId).isEqualTo(UserHandle.USER_NULL);
479         assertThat(newUser.flags).isEqualTo(newUserFlags);
480     }
481 
482     @Test
testGetUserInfo_twoSuccessfulCalls()483     public void testGetUserInfo_twoSuccessfulCalls() throws Exception {
484         testGetUserInfo_successDefault();
485         testGetUserInfo_successDefault();
486     }
487 
488     @Test
testSwitchUser_noHalSupported()489     public void testSwitchUser_noHalSupported() {
490         // Cannot use mUserHalService because it's already set with supported properties
491         UserHalService myHalService = new UserHalService(mVehicleHal);
492 
493         assertThrows(IllegalStateException.class,
494                 () -> myHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo),
495                         HAL_TIMEOUT_MS, noOpCallback()));
496     }
497 
498     @Test
testSwitchUser_invalidTimeout()499     public void testSwitchUser_invalidTimeout() {
500         assertThrows(IllegalArgumentException.class, () -> mUserHalService
501                 .switchUser(createUserSwitchRequest(mUser10, mUsersInfo), 0, noOpCallback()));
502         assertThrows(IllegalArgumentException.class, () -> mUserHalService
503                 .switchUser(createUserSwitchRequest(mUser10, mUsersInfo), -1, noOpCallback()));
504     }
505 
506     @Test
testSwitchUser_noUsersInfo()507     public void testSwitchUser_noUsersInfo() {
508         assertThrows(IllegalArgumentException.class, () -> mUserHalService
509                 .switchUser(createUserSwitchRequest(mUser10, null), HAL_TIMEOUT_MS,
510                         noOpCallback()));
511     }
512 
513     @Test
testSwitchUser_noCallback()514     public void testSwitchUser_noCallback() {
515         assertThrows(NullPointerException.class, () -> mUserHalService
516                 .switchUser(createUserSwitchRequest(mUser10, mUsersInfo), HAL_TIMEOUT_MS, null));
517     }
518 
519     @Test
testSwitchUser_nullRequest()520     public void testSwitchUser_nullRequest() {
521         assertThrows(NullPointerException.class, () -> mUserHalService
522                 .switchUser(null, HAL_TIMEOUT_MS, noOpCallback()));
523     }
524 
525     @Test
testSwitchUser_noTarget()526     public void testSwitchUser_noTarget() {
527         assertThrows(NullPointerException.class, () -> mUserHalService
528                 .switchUser(createUserSwitchRequest(null, mUsersInfo), HAL_TIMEOUT_MS,
529                         noOpCallback()));
530     }
531 
532     @Test
testSwitchUser_halSetTimedOut()533     public void testSwitchUser_halSetTimedOut() throws Exception {
534         replySetPropertyWithTimeoutException(SWITCH_USER);
535 
536         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
537                 CALLBACK_TIMEOUT);
538         mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo),
539                 HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
540                 callback);
541 
542         callback.assertCalled();
543         assertCallbackStatus(callback, HalCallback.STATUS_HAL_SET_TIMEOUT);
544         assertThat(callback.response).isNull();
545 
546         // Make sure the pending request was removed
547         SystemClock.sleep(WAITING_TIME_FOR_NEGATIVE_TESTS_MS);
548         callback.assertNotCalledAgain();
549     }
550 
551     @Test
testSwitchUser_halDidNotReply()552     public void testSwitchUser_halDidNotReply() throws Exception {
553         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
554                 CALLBACK_TIMEOUT);
555         mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo),
556                 HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
557                 callback);
558 
559         callback.assertCalled();
560         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
561         assertThat(callback.response).isNull();
562     }
563 
564     @Test
testSwitchUser_halReplyWithWrongRequestId()565     public void testSwitchUser_halReplyWithWrongRequestId() throws Exception {
566         VehiclePropValue propResponse = UserHalHelper.createPropRequest(SWITCH_USER,
567                     REQUEST_ID_PLACE_HOLDER, InitialUserInfoResponseAction.SWITCH);
568 
569         replySetPropertyWithOnChangeEvent(SWITCH_USER, propResponse,
570                 /* rightRequestId= */ false);
571 
572         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
573                 CALLBACK_TIMEOUT);
574         mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo),
575                 HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
576                 callback);
577 
578         callback.assertCalled();
579         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
580         assertThat(callback.response).isNull();
581     }
582 
583     @Test
testSwitchUser_halReturnedInvalidMessageType()584     public void testSwitchUser_halReturnedInvalidMessageType() throws Exception {
585         VehiclePropValue propResponse = UserHalHelper.createPropRequest(SWITCH_USER,
586                 REQUEST_ID_PLACE_HOLDER, SwitchUserMessageType.LEGACY_ANDROID_SWITCH);
587         propResponse.value.int32Values.add(SwitchUserStatus.SUCCESS);
588 
589         AtomicReference<VehiclePropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
590                 SWITCH_USER, propResponse, /* rightRequestId= */ true);
591 
592         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
593                 CALLBACK_TIMEOUT);
594         mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo), HAL_TIMEOUT_MS,
595                 callback);
596 
597         callback.assertCalled();
598 
599         // Make sure the arguments were properly converted
600         assertHalSetSwitchUserRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH,
601                 mUser10);
602 
603         // Assert response
604         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
605         assertThat(callback.response).isNull();
606     }
607 
608     @Test
testSwitchUser_success()609     public void testSwitchUser_success() throws Exception {
610         VehiclePropValue propResponse = UserHalHelper.createPropRequest(SWITCH_USER,
611                     REQUEST_ID_PLACE_HOLDER, SwitchUserMessageType.VEHICLE_RESPONSE);
612         propResponse.value.int32Values.add(SwitchUserStatus.SUCCESS);
613 
614         AtomicReference<VehiclePropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
615                 SWITCH_USER, propResponse, /* rightRequestId= */ true);
616 
617         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
618                 CALLBACK_TIMEOUT);
619         mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo), HAL_TIMEOUT_MS,
620                 callback);
621 
622         callback.assertCalled();
623 
624         // Make sure the arguments were properly converted
625         assertHalSetSwitchUserRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH,
626                 mUser10);
627 
628         // Assert response
629         assertCallbackStatus(callback, HalCallback.STATUS_OK);
630         SwitchUserResponse actualResponse = callback.response;
631         assertThat(actualResponse.status).isEqualTo(SwitchUserStatus.SUCCESS);
632         assertThat(actualResponse.messageType).isEqualTo(SwitchUserMessageType.VEHICLE_RESPONSE);
633         assertThat(actualResponse.errorMessage).isEmpty();
634     }
635 
636     @Test
testSwitchUser_failure()637     public void testSwitchUser_failure() throws Exception {
638         VehiclePropValue propResponse = UserHalHelper.createPropRequest(SWITCH_USER,
639                     REQUEST_ID_PLACE_HOLDER, SwitchUserMessageType.VEHICLE_RESPONSE);
640         propResponse.value.int32Values.add(SwitchUserStatus.FAILURE);
641         propResponse.value.stringValue = "D'OH!";
642 
643         AtomicReference<VehiclePropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
644                 SWITCH_USER, propResponse, /* rightRequestId= */ true);
645 
646         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
647                 CALLBACK_TIMEOUT);
648         mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo), HAL_TIMEOUT_MS,
649                 callback);
650 
651         callback.assertCalled();
652 
653         // Make sure the arguments were properly converted
654         assertHalSetSwitchUserRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH,
655                 mUser10);
656 
657         // Assert response
658         assertCallbackStatus(callback, HalCallback.STATUS_OK);
659         SwitchUserResponse actualResponse = callback.response;
660         assertThat(actualResponse.status).isEqualTo(SwitchUserStatus.FAILURE);
661         assertThat(actualResponse.messageType).isEqualTo(SwitchUserMessageType.VEHICLE_RESPONSE);
662         assertThat(actualResponse.errorMessage).isEqualTo("D'OH!");
663     }
664 
665     @Test
testSwitchUser_secondCallFailWhilePending()666     public void testSwitchUser_secondCallFailWhilePending() throws Exception {
667         GenericHalCallback<SwitchUserResponse> callback1 = new GenericHalCallback<>(
668                 CALLBACK_TIMEOUT);
669         GenericHalCallback<SwitchUserResponse> callback2 = new GenericHalCallback<>(
670                 CALLBACK_TIMEOUT);
671         mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo),
672                 HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
673                 callback1);
674         mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo), HAL_TIMEOUT_MS,
675                 callback2);
676 
677         callback1.assertCalled();
678         assertCallbackStatus(callback1, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
679         assertThat(callback1.response).isNull();
680 
681         callback2.assertCalled();
682         assertCallbackStatus(callback2, HalCallback.STATUS_CONCURRENT_OPERATION);
683         assertThat(callback1.response).isNull();
684     }
685 
686     @Test
testSwitchUser_halReturnedInvalidStatus()687     public void testSwitchUser_halReturnedInvalidStatus() throws Exception {
688         VehiclePropValue propResponse = UserHalHelper.createPropRequest(SWITCH_USER,
689                     REQUEST_ID_PLACE_HOLDER, SwitchUserMessageType.VEHICLE_RESPONSE);
690         propResponse.value.int32Values.add(/*status =*/ 110); // an invalid status
691 
692         AtomicReference<VehiclePropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
693                 SWITCH_USER, propResponse, /* rightRequestId= */ true);
694 
695         GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
696                 CALLBACK_TIMEOUT);
697         mUserHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo), HAL_TIMEOUT_MS,
698                 callback);
699 
700         callback.assertCalled();
701 
702         // Make sure the arguments were properly converted
703         assertHalSetSwitchUserRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH,
704                 mUser10);
705 
706         // Assert response
707         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
708         assertThat(callback.response).isNull();
709     }
710 
711     @Test
testUserSwitch_OEMRequest_success()712     public void testUserSwitch_OEMRequest_success() throws Exception {
713         int requestId = -4;
714         int targetUserId = 11;
715         VehiclePropValue propResponse = UserHalHelper.createPropRequest(SWITCH_USER,
716                 requestId, SwitchUserMessageType.VEHICLE_REQUEST);
717 
718         propResponse.value.int32Values.add(targetUserId);
719 
720         mUserHalService.onHalEvents(Arrays.asList(propResponse));
721         waitForHandler();
722 
723         verify(mCarUserService).switchAndroidUserFromHal(requestId, targetUserId);
724     }
725 
726     @Test
testUserSwitch_OEMRequest_failure_positiveRequestId()727     public void testUserSwitch_OEMRequest_failure_positiveRequestId() throws Exception {
728         int requestId = 4;
729         int targetUserId = 11;
730         VehiclePropValue propResponse = UserHalHelper.createPropRequest(SWITCH_USER,
731                 requestId, SwitchUserMessageType.VEHICLE_REQUEST);
732         propResponse.value.int32Values.add(targetUserId);
733 
734         mUserHalService.onHalEvents(Arrays.asList(propResponse));
735         waitForHandler();
736 
737         verify(mCarUserService, never()).switchAndroidUserFromHal(anyInt(), anyInt());
738     }
739 
740     @Test
testPostSwitchResponse_noHalSupported()741     public void testPostSwitchResponse_noHalSupported() {
742         // Cannot use mUserHalService because it's already set with supported properties
743         UserHalService myHalService = new UserHalService(mVehicleHal);
744 
745         assertThrows(IllegalStateException.class,
746                 () -> myHalService.postSwitchResponse(new SwitchUserRequest()));
747     }
748 
749     @Test
testPostSwitchResponse_nullRequest()750     public void testPostSwitchResponse_nullRequest() {
751         assertThrows(NullPointerException.class, () -> mUserHalService.postSwitchResponse(null));
752     }
753 
754     @Test
testPostSwitchResponse_noUsersInfo()755     public void testPostSwitchResponse_noUsersInfo() {
756         SwitchUserRequest request = createUserSwitchRequest(mUser10, null);
757         request.requestId = 42;
758         assertThrows(IllegalArgumentException.class,
759                 () -> mUserHalService.postSwitchResponse(request));
760     }
761 
762     @Test
testPostSwitchResponse_HalCalledWithCorrectProp()763     public void testPostSwitchResponse_HalCalledWithCorrectProp() {
764         SwitchUserRequest request = createUserSwitchRequest(mUser10, mUsersInfo);
765         request.requestId = 42;
766         mUserHalService.postSwitchResponse(request);
767         ArgumentCaptor<VehiclePropValue> propCaptor =
768                 ArgumentCaptor.forClass(VehiclePropValue.class);
769         verify(mVehicleHal).set(propCaptor.capture());
770         VehiclePropValue prop = propCaptor.getValue();
771         assertHalSetSwitchUserRequest(prop, SwitchUserMessageType.ANDROID_POST_SWITCH, mUser10);
772     }
773 
774     @Test
testLegacyUserSwitch_nullRequest()775     public void testLegacyUserSwitch_nullRequest() {
776         assertThrows(NullPointerException.class, () -> mUserHalService.legacyUserSwitch(null));
777     }
778 
779     @Test
testLegacyUserSwitch_noMessageType()780     public void testLegacyUserSwitch_noMessageType() {
781         SwitchUserRequest request = new SwitchUserRequest();
782 
783         assertThrows(IllegalArgumentException.class,
784                 () -> mUserHalService.legacyUserSwitch(request));
785     }
786 
787     @Test
testLegacyUserSwitch_noTargetUserInfo()788     public void testLegacyUserSwitch_noTargetUserInfo() {
789         SwitchUserRequest request = new SwitchUserRequest();
790         request.messageType = SwitchUserMessageType.ANDROID_SWITCH;
791 
792         assertThrows(IllegalArgumentException.class,
793                 () -> mUserHalService.legacyUserSwitch(request));
794     }
795 
796     @Test
testRemoveUser_noHalSupported()797     public void testRemoveUser_noHalSupported() {
798         // Cannot use mUserHalService because it's already set with supported properties
799         UserHalService myHalService = new UserHalService(mVehicleHal);
800 
801         assertThrows(IllegalStateException.class,
802                 () -> myHalService.removeUser(new RemoveUserRequest()));
803     }
804 
805     @Test
testRemoveUser_nullRequest()806     public void testRemoveUser_nullRequest() {
807         RemoveUserRequest request = null;
808 
809         assertThrows(NullPointerException.class,
810                 () -> mUserHalService.removeUser(request));
811     }
812 
813     @Test
testRemoveUser_noRequestId()814     public void testRemoveUser_noRequestId() {
815         RemoveUserRequest request = new RemoveUserRequest();
816 
817         assertThrows(IllegalArgumentException.class,
818                 () -> mUserHalService.removeUser(request));
819     }
820 
821     @Test
testRemoveUser_noRemovedUserInfo()822     public void testRemoveUser_noRemovedUserInfo() {
823         RemoveUserRequest request = new RemoveUserRequest();
824         request.requestId = 1;
825 
826         assertThrows(IllegalArgumentException.class,
827                 () -> mUserHalService.removeUser(request));
828     }
829 
830     @Test
testRemoveUser_noUsersInfo()831     public void testRemoveUser_noUsersInfo() {
832         RemoveUserRequest request = new RemoveUserRequest();
833         request.requestId = 1;
834         request.removedUserInfo = mUser10;
835 
836         assertThrows(IllegalArgumentException.class,
837                 () -> mUserHalService.removeUser(request));
838     }
839 
840     @Test
testRemoveUser_HalCalledWithCorrectProp()841     public void testRemoveUser_HalCalledWithCorrectProp() {
842         RemoveUserRequest request = new RemoveUserRequest();
843         request.removedUserInfo = mUser10;
844         request.usersInfo = mUsersInfo;
845         ArgumentCaptor<VehiclePropValue> propCaptor =
846                 ArgumentCaptor.forClass(VehiclePropValue.class);
847 
848         mUserHalService.removeUser(request);
849 
850         verify(mVehicleHal).set(propCaptor.capture());
851         assertHalSetRemoveUserRequest(propCaptor.getValue(), mUser10);
852     }
853 
854     @Test
testLegacyUserSwitch_noHalSupported()855     public void testLegacyUserSwitch_noHalSupported() {
856         // Cannot use mUserHalService because it's already set with supported properties
857         UserHalService myHalService = new UserHalService(mVehicleHal);
858 
859         assertThrows(IllegalStateException.class,
860                 () -> myHalService.legacyUserSwitch(new SwitchUserRequest()));
861     }
862 
863     @Test
testLegacyUserSwitch_noUsersInfo()864     public void testLegacyUserSwitch_noUsersInfo() {
865         SwitchUserRequest request = new SwitchUserRequest();
866         request.messageType = SwitchUserMessageType.ANDROID_SWITCH;
867         request.targetUser = mUser10;
868 
869         assertThrows(IllegalArgumentException.class,
870                 () -> mUserHalService.legacyUserSwitch(request));
871     }
872 
873     @Test
testLegacyUserSwitch_HalCalledWithCorrectProp()874     public void testLegacyUserSwitch_HalCalledWithCorrectProp() {
875         SwitchUserRequest request = new SwitchUserRequest();
876         request.messageType = SwitchUserMessageType.LEGACY_ANDROID_SWITCH;
877         request.targetUser = mUser10;
878         request.usersInfo = mUsersInfo;
879 
880         mUserHalService.legacyUserSwitch(request);
881         ArgumentCaptor<VehiclePropValue> propCaptor =
882                 ArgumentCaptor.forClass(VehiclePropValue.class);
883         verify(mVehicleHal).set(propCaptor.capture());
884         VehiclePropValue prop = propCaptor.getValue();
885         assertHalSetSwitchUserRequest(prop, SwitchUserMessageType.LEGACY_ANDROID_SWITCH,
886                 mUser10);
887     }
888 
889     @Test
testCreateUser_noHalSupported()890     public void testCreateUser_noHalSupported() {
891         // Cannot use mUserHalService because it's already set with supported properties
892         UserHalService myHalService = new UserHalService(mVehicleHal);
893 
894         assertThrows(IllegalStateException.class,
895                 () -> myHalService.createUser(new CreateUserRequest(), HAL_TIMEOUT_MS,
896                         noOpCallback()));
897     }
898 
899     @Test
testCreateUser_noRequest()900     public void testCreateUser_noRequest() {
901         assertThrows(NullPointerException.class, () -> mUserHalService
902                 .createUser(null, HAL_TIMEOUT_MS, noOpCallback()));
903     }
904 
905     @Test
testCreateUser_invalidTimeout()906     public void testCreateUser_invalidTimeout() {
907         assertThrows(IllegalArgumentException.class, () -> mUserHalService
908                 .createUser(new CreateUserRequest(), 0, noOpCallback()));
909         assertThrows(IllegalArgumentException.class, () -> mUserHalService
910                 .createUser(new CreateUserRequest(), -1, noOpCallback()));
911     }
912 
913     @Test
testCreateUser_noCallback()914     public void testCreateUser_noCallback() {
915         CreateUserRequest request = new CreateUserRequest();
916         request.newUserInfo.userId = 10;
917         request.usersInfo.existingUsers.add(request.newUserInfo);
918 
919         assertThrows(NullPointerException.class, () -> mUserHalService
920                 .createUser(request, HAL_TIMEOUT_MS, null));
921     }
922 
923     /**
924      * Creates a valid {@link CreateUserRequest} for tests that doesn't check its contents.
925      */
926     @NonNull
newValidCreateUserRequest()927     private CreateUserRequest newValidCreateUserRequest() {
928         CreateUserRequest request = new CreateUserRequest();
929         request.newUserInfo = mUser10;
930         request.usersInfo = mUsersInfo;
931         return request;
932     }
933 
934     @Test
testCreateUser_halSetTimedOut()935     public void testCreateUser_halSetTimedOut() throws Exception {
936         replySetPropertyWithTimeoutException(CREATE_USER);
937 
938         GenericHalCallback<CreateUserResponse> callback = new GenericHalCallback<>(
939                 CALLBACK_TIMEOUT);
940         mUserHalService.createUser(newValidCreateUserRequest(), HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
941                 callback);
942 
943         callback.assertCalled();
944         assertCallbackStatus(callback, HalCallback.STATUS_HAL_SET_TIMEOUT);
945         assertThat(callback.response).isNull();
946 
947         // Make sure the pending request was removed
948         SystemClock.sleep(WAITING_TIME_FOR_NEGATIVE_TESTS_MS);
949         callback.assertNotCalledAgain();
950     }
951 
952     @Test
testCreateUser_halDidNotReply()953     public void testCreateUser_halDidNotReply() throws Exception {
954         GenericHalCallback<CreateUserResponse> callback = new GenericHalCallback<>(
955                 CALLBACK_TIMEOUT);
956         mUserHalService.createUser(newValidCreateUserRequest(), HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
957                 callback);
958 
959         callback.assertCalled();
960         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
961         assertThat(callback.response).isNull();
962     }
963 
964     @Test
testCreateUser_halReplyWithWrongRequestId()965     public void testCreateUser_halReplyWithWrongRequestId() throws Exception {
966         VehiclePropValue propResponse =
967                 UserHalHelper.createPropRequest(CREATE_USER, REQUEST_ID_PLACE_HOLDER);
968 
969         replySetPropertyWithOnChangeEvent(CREATE_USER, propResponse,
970                 /* rightRequestId= */ false);
971 
972         GenericHalCallback<CreateUserResponse> callback = new GenericHalCallback<>(
973                 CALLBACK_TIMEOUT);
974         mUserHalService.createUser(newValidCreateUserRequest(), HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
975                 callback);
976 
977         callback.assertCalled();
978         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
979         assertThat(callback.response).isNull();
980     }
981 
982     @Test
testCreateUser_success()983     public void testCreateUser_success() throws Exception {
984         VehiclePropValue propResponse =
985                 UserHalHelper.createPropRequest(CREATE_USER, REQUEST_ID_PLACE_HOLDER);
986         propResponse.value.int32Values.add(CreateUserStatus.SUCCESS);
987 
988         AtomicReference<VehiclePropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
989                 CREATE_USER, propResponse, /* rightRequestId= */ true);
990 
991         CreateUserRequest request = new CreateUserRequest();
992         request.newUserInfo = mUser10;
993         request.usersInfo = mUsersInfo;
994         GenericHalCallback<CreateUserResponse> callback = new GenericHalCallback<>(
995                 CALLBACK_TIMEOUT);
996         mUserHalService.createUser(request, HAL_TIMEOUT_MS, callback);
997 
998         callback.assertCalled();
999 
1000         // Make sure the arguments were properly converted
1001         assertHalSetCreateUserRequest(reqCaptor.get(), request);
1002 
1003         // Assert response
1004         assertCallbackStatus(callback, HalCallback.STATUS_OK);
1005         CreateUserResponse actualResponse = callback.response;
1006         assertThat(actualResponse.status).isEqualTo(CreateUserStatus.SUCCESS);
1007         assertThat(actualResponse.errorMessage).isEmpty();
1008     }
1009 
1010     @Test
testCreateUser_failure()1011     public void testCreateUser_failure() throws Exception {
1012         VehiclePropValue propResponse =
1013                 UserHalHelper.createPropRequest(CREATE_USER, REQUEST_ID_PLACE_HOLDER);
1014         propResponse.value.int32Values.add(CreateUserStatus.FAILURE);
1015         propResponse.value.stringValue = "D'OH!";
1016 
1017         AtomicReference<VehiclePropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
1018                 CREATE_USER, propResponse, /* rightRequestId= */ true);
1019 
1020         CreateUserRequest request = new CreateUserRequest();
1021         request.newUserInfo = mUser10;
1022         request.usersInfo = mUsersInfo;
1023         GenericHalCallback<CreateUserResponse> callback = new GenericHalCallback<>(
1024                 CALLBACK_TIMEOUT);
1025         mUserHalService.createUser(request, HAL_TIMEOUT_MS, callback);
1026 
1027         callback.assertCalled();
1028 
1029         // Make sure the arguments were properly converted
1030         assertHalSetCreateUserRequest(reqCaptor.get(), request);
1031 
1032         // Assert response
1033         assertCallbackStatus(callback, HalCallback.STATUS_OK);
1034         CreateUserResponse actualResponse = callback.response;
1035         assertThat(actualResponse.status).isEqualTo(CreateUserStatus.FAILURE);
1036         assertThat(actualResponse.errorMessage).isEqualTo("D'OH!");
1037     }
1038 
1039     @Test
testCreateUser_secondCallFailWhilePending()1040     public void testCreateUser_secondCallFailWhilePending() throws Exception {
1041         GenericHalCallback<CreateUserResponse> callback1 = new GenericHalCallback<>(
1042                 CALLBACK_TIMEOUT);
1043         GenericHalCallback<CreateUserResponse> callback2 = new GenericHalCallback<>(
1044                 CALLBACK_TIMEOUT);
1045         mUserHalService.createUser(newValidCreateUserRequest(), HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS,
1046                 callback1);
1047         mUserHalService.createUser(newValidCreateUserRequest(), HAL_TIMEOUT_MS, callback2);
1048 
1049         callback1.assertCalled();
1050         assertCallbackStatus(callback1, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
1051         assertThat(callback1.response).isNull();
1052 
1053         callback2.assertCalled();
1054         assertCallbackStatus(callback2, HalCallback.STATUS_CONCURRENT_OPERATION);
1055         assertThat(callback1.response).isNull();
1056     }
1057 
1058     @Test
testCreateUser_halReturnedInvalidStatus()1059     public void testCreateUser_halReturnedInvalidStatus() throws Exception {
1060         VehiclePropValue propResponse =
1061                 UserHalHelper.createPropRequest(CREATE_USER, REQUEST_ID_PLACE_HOLDER);
1062         propResponse.value.int32Values.add(/*status =*/ -1); // an invalid status
1063 
1064         AtomicReference<VehiclePropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
1065                 CREATE_USER, propResponse, /* rightRequestId= */ true);
1066 
1067         GenericHalCallback<CreateUserResponse> callback = new GenericHalCallback<>(
1068                 CALLBACK_TIMEOUT);
1069         mUserHalService.createUser(newValidCreateUserRequest(), HAL_TIMEOUT_MS, callback);
1070 
1071         callback.assertCalled();
1072 
1073         // Assert response
1074         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
1075         assertThat(callback.response).isNull();
1076     }
1077 
1078     @Test
testGetUserAssociation_noHalSupported()1079     public void testGetUserAssociation_noHalSupported() {
1080         // Cannot use mUserHalService because it's already set with supported properties
1081         UserHalService myHalService = new UserHalService(mVehicleHal);
1082 
1083         assertThrows(IllegalStateException.class,
1084                 () -> myHalService.getUserAssociation(new UserIdentificationGetRequest()));
1085     }
1086 
1087     @Test
testGetUserAssociation_nullRequest()1088     public void testGetUserAssociation_nullRequest() {
1089         assertThrows(NullPointerException.class, () -> mUserHalService.getUserAssociation(null));
1090     }
1091 
1092     @Test
testGetUserAssociation_requestWithDuplicatedTypes()1093     public void testGetUserAssociation_requestWithDuplicatedTypes() {
1094         UserIdentificationGetRequest request = new UserIdentificationGetRequest();
1095         request.numberAssociationTypes = 2;
1096         request.associationTypes.add(KEY_FOB);
1097         request.associationTypes.add(KEY_FOB);
1098 
1099         assertThrows(IllegalArgumentException.class,
1100                 () -> mUserHalService.getUserAssociation(request));
1101     }
1102 
1103     @Test
testGetUserAssociation_invalidResponse()1104     public void testGetUserAssociation_invalidResponse() {
1105         VehiclePropValue propResponse = new VehiclePropValue();
1106         propResponse.prop = USER_IDENTIFICATION_ASSOCIATION;
1107         propResponse.value.int32Values.add(DEFAULT_REQUEST_ID);
1108         propResponse.value.int32Values.add(1); // 1 associations
1109         propResponse.value.int32Values.add(KEY_FOB); // type only, it's missing value
1110         UserIdentificationGetRequest request = replyToValidGetUserIdentificationRequest(
1111                 propResponse);
1112 
1113         assertThat(mUserHalService.getUserAssociation(request)).isNull();
1114     }
1115 
1116     @Test
testGetUserAssociation_nullResponse()1117     public void testGetUserAssociation_nullResponse() {
1118         UserIdentificationGetRequest request = replyToValidGetUserIdentificationRequest(null);
1119 
1120         assertThat(mUserHalService.getUserAssociation(request)).isNull();
1121 
1122         verifyValidGetUserIdentificationRequestMade();
1123     }
1124 
1125     @Test
testGetUserAssociation_wrongNumberOfAssociationsOnResponse()1126     public void testGetUserAssociation_wrongNumberOfAssociationsOnResponse() {
1127         VehiclePropValue propResponse = new VehiclePropValue();
1128         propResponse.prop = USER_IDENTIFICATION_ASSOCIATION;
1129         propResponse.value.int32Values.add(DEFAULT_REQUEST_ID);
1130         propResponse.value.int32Values.add(2); // 2 associations
1131         propResponse.value.int32Values.add(KEY_FOB);
1132         propResponse.value.int32Values.add(ASSOCIATED_CURRENT_USER);
1133         propResponse.value.int32Values.add(CUSTOM_1);
1134         propResponse.value.int32Values.add(ASSOCIATED_CURRENT_USER);
1135         UserIdentificationGetRequest request = replyToValidGetUserIdentificationRequest(
1136                 propResponse);
1137 
1138         assertThat(mUserHalService.getUserAssociation(request)).isNull();
1139 
1140         verifyValidGetUserIdentificationRequestMade();
1141     }
1142 
1143     @Test
testGetUserAssociation_typesOnResponseMismatchTypesOnRequest()1144     public void testGetUserAssociation_typesOnResponseMismatchTypesOnRequest() {
1145         VehiclePropValue propResponse = new VehiclePropValue();
1146         propResponse.prop = USER_IDENTIFICATION_ASSOCIATION;
1147         propResponse.value.int32Values.add(DEFAULT_REQUEST_ID);
1148         propResponse.value.int32Values.add(1); // 1 association
1149         propResponse.value.int32Values.add(CUSTOM_1);
1150         propResponse.value.int32Values.add(ASSOCIATED_CURRENT_USER);
1151         UserIdentificationGetRequest request = replyToValidGetUserIdentificationRequest(
1152                 propResponse);
1153 
1154         assertThat(mUserHalService.getUserAssociation(request)).isNull();
1155 
1156         verifyValidGetUserIdentificationRequestMade();
1157     }
1158 
1159     @Test
testGetUserAssociation_requestIdMismatch()1160     public void testGetUserAssociation_requestIdMismatch() {
1161         VehiclePropValue propResponse = new VehiclePropValue();
1162         propResponse.prop = USER_IDENTIFICATION_ASSOCIATION;
1163         propResponse.value.int32Values.add(DEFAULT_REQUEST_ID + 1);
1164         propResponse.value.int32Values.add(1); // 1 association
1165         propResponse.value.int32Values.add(KEY_FOB);
1166         propResponse.value.int32Values.add(ASSOCIATED_CURRENT_USER);
1167         UserIdentificationGetRequest request = replyToValidGetUserIdentificationRequest(
1168                 propResponse);
1169 
1170         assertThat(mUserHalService.getUserAssociation(request)).isNull();
1171 
1172         verifyValidGetUserIdentificationRequestMade();
1173     }
1174 
1175     @Test
testGetUserAssociation_ok()1176     public void testGetUserAssociation_ok() {
1177         VehiclePropValue propResponse = new VehiclePropValue();
1178         propResponse.prop = USER_IDENTIFICATION_ASSOCIATION;
1179         propResponse.value.int32Values.add(DEFAULT_REQUEST_ID);
1180         propResponse.value.int32Values.add(1); // 1 association
1181         propResponse.value.int32Values.add(KEY_FOB);
1182         propResponse.value.int32Values.add(ASSOCIATED_CURRENT_USER);
1183         UserIdentificationGetRequest request = replyToValidGetUserIdentificationRequest(
1184                 propResponse);
1185 
1186         UserIdentificationResponse response = mUserHalService.getUserAssociation(request);
1187 
1188         assertThat(response.requestId).isEqualTo(DEFAULT_REQUEST_ID);
1189         assertThat(response.numberAssociation).isEqualTo(1);
1190         assertThat(response.associations).hasSize(1);
1191         UserIdentificationAssociation actualAssociation = response.associations.get(0);
1192         assertThat(actualAssociation.type).isEqualTo(KEY_FOB);
1193         assertThat(actualAssociation.value).isEqualTo(ASSOCIATED_CURRENT_USER);
1194     }
1195 
1196     @Test
testSetUserAssociation_noHalSupported()1197     public void testSetUserAssociation_noHalSupported() {
1198         // Cannot use mUserHalService because it's already set with supported properties
1199         UserHalService myHalService = new UserHalService(mVehicleHal);
1200 
1201         assertThrows(IllegalStateException.class,
1202                 () -> myHalService.setUserAssociation(HAL_TIMEOUT_MS,
1203                         new UserIdentificationSetRequest(), noOpCallback()));
1204     }
1205 
1206     @Test
testSetUserAssociation_invalidTimeout()1207     public void testSetUserAssociation_invalidTimeout() {
1208         UserIdentificationSetRequest request = new UserIdentificationSetRequest();
1209         assertThrows(IllegalArgumentException.class, () ->
1210                 mUserHalService.setUserAssociation(0, request, noOpCallback()));
1211         assertThrows(IllegalArgumentException.class, () ->
1212                 mUserHalService.setUserAssociation(-1, request, noOpCallback()));
1213     }
1214 
1215     @Test
testSetUserAssociation_nullRequest()1216     public void testSetUserAssociation_nullRequest() {
1217         assertThrows(NullPointerException.class, () ->
1218                 mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, null, noOpCallback()));
1219     }
1220 
1221     @Test
testSetUserAssociation_nullCallback()1222     public void testSetUserAssociation_nullCallback() {
1223         UserIdentificationSetRequest request = new UserIdentificationSetRequest();
1224         assertThrows(NullPointerException.class, () ->
1225                 mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, request, null));
1226     }
1227 
1228     @Test
testSetUserAssociation_requestWithDuplicatedTypes()1229     public void testSetUserAssociation_requestWithDuplicatedTypes() {
1230         UserIdentificationSetRequest request = new UserIdentificationSetRequest();
1231         request.numberAssociations = 2;
1232         UserIdentificationSetAssociation association1 = new UserIdentificationSetAssociation();
1233         association1.type = KEY_FOB;
1234         association1.value = ASSOCIATE_CURRENT_USER;
1235         request.associations.add(association1);
1236         request.associations.add(association1);
1237 
1238         assertThrows(IllegalArgumentException.class, () ->
1239                 mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, request, noOpCallback()));
1240     }
1241 
1242     @Test
testSetUserAssociation_halSetTimedOut()1243     public void testSetUserAssociation_halSetTimedOut() throws Exception {
1244         UserIdentificationSetRequest request = validUserIdentificationSetRequest();
1245         GenericHalCallback<UserIdentificationResponse> callback = new GenericHalCallback<>(
1246                 CALLBACK_TIMEOUT);
1247         replySetPropertyWithTimeoutException(USER_IDENTIFICATION_ASSOCIATION);
1248 
1249         mUserHalService.setUserAssociation(HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS, request, callback);
1250 
1251         callback.assertCalled();
1252         assertCallbackStatus(callback, HalCallback.STATUS_HAL_SET_TIMEOUT);
1253         assertThat(callback.response).isNull();
1254 
1255         // Make sure the pending request was removed
1256         SystemClock.sleep(WAITING_TIME_FOR_NEGATIVE_TESTS_MS);
1257         callback.assertNotCalledAgain();
1258     }
1259 
1260     @Test
testSetUserAssociation_halDidNotReply()1261     public void testSetUserAssociation_halDidNotReply() throws Exception {
1262         UserIdentificationSetRequest request = validUserIdentificationSetRequest();
1263         GenericHalCallback<UserIdentificationResponse> callback = new GenericHalCallback<>(
1264                 CALLBACK_TIMEOUT);
1265 
1266         mUserHalService.setUserAssociation(HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS, request, callback);
1267 
1268         callback.assertCalled();
1269         assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
1270         assertThat(callback.response).isNull();
1271     }
1272 
1273     @Test
testSetUserAssociation_secondCallFailWhilePending()1274     public void testSetUserAssociation_secondCallFailWhilePending() throws Exception {
1275         UserIdentificationSetRequest request = validUserIdentificationSetRequest();
1276         GenericHalCallback<UserIdentificationResponse> callback1 = new GenericHalCallback<>(
1277                 CALLBACK_TIMEOUT);
1278         GenericHalCallback<UserIdentificationResponse> callback2 = new GenericHalCallback<>(
1279                 CALLBACK_TIMEOUT);
1280 
1281         mUserHalService.setUserAssociation(HAL_TIMEOUT_FOR_NEGATIVE_TESTS_MS, request, callback1);
1282         mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, request, callback2);
1283 
1284         callback1.assertCalled();
1285         assertCallbackStatus(callback1, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
1286         assertThat(callback1.response).isNull();
1287 
1288         callback2.assertCalled();
1289         assertCallbackStatus(callback2, HalCallback.STATUS_CONCURRENT_OPERATION);
1290         assertThat(callback1.response).isNull();
1291     }
1292 
1293     @Test
testSetUserAssociation_responseWithWrongRequestId()1294     public void testSetUserAssociation_responseWithWrongRequestId() throws Exception {
1295         VehiclePropValue propResponse = new VehiclePropValue();
1296         propResponse.prop = USER_IDENTIFICATION_ASSOCIATION;
1297         propResponse.value.int32Values.add(DEFAULT_REQUEST_ID + 1);
1298         AtomicReference<VehiclePropValue> propRequest = replySetPropertyWithOnChangeEvent(
1299                 USER_IDENTIFICATION_ASSOCIATION, propResponse, /* rightRequestId= */ true);
1300         UserIdentificationSetRequest request = replyToValidSetUserIdentificationRequest();
1301         GenericHalCallback<UserIdentificationResponse> callback = new GenericHalCallback<>(
1302                 CALLBACK_TIMEOUT);
1303 
1304         mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, request, callback);
1305 
1306         // Assert request
1307         verifyValidSetUserIdentificationRequestMade(propRequest.get());
1308         // Assert response
1309         callback.assertCalled();
1310         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
1311         assertThat(callback.response).isNull();
1312     }
1313 
1314     @Test
testSetUserAssociation_notEnoughValuesOnResponse()1315     public void testSetUserAssociation_notEnoughValuesOnResponse() throws Exception {
1316         VehiclePropValue propResponse = new VehiclePropValue();
1317         propResponse.prop = USER_IDENTIFICATION_ASSOCIATION;
1318         // need at least 4: requestId, number associations, type1, value1
1319         propResponse.value.int32Values.add(1);
1320         propResponse.value.int32Values.add(2);
1321         propResponse.value.int32Values.add(3);
1322 
1323         AtomicReference<VehiclePropValue> propRequest = replySetPropertyWithOnChangeEvent(
1324                 USER_IDENTIFICATION_ASSOCIATION, propResponse, /* rightRequestId= */ true);
1325         UserIdentificationSetRequest request = replyToValidSetUserIdentificationRequest();
1326         GenericHalCallback<UserIdentificationResponse> callback = new GenericHalCallback<>(
1327                 CALLBACK_TIMEOUT);
1328 
1329         mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, request, callback);
1330 
1331         // Assert request
1332         verifyValidSetUserIdentificationRequestMade(propRequest.get());
1333         // Assert response
1334         callback.assertCalled();
1335         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
1336         assertThat(callback.response).isNull();
1337     }
1338 
1339     @Test
testSetUserAssociation_wrongNumberOfAssociationsOnResponse()1340     public void testSetUserAssociation_wrongNumberOfAssociationsOnResponse() throws Exception {
1341         VehiclePropValue propResponse = new VehiclePropValue();
1342         propResponse.prop = USER_IDENTIFICATION_ASSOCIATION;
1343         propResponse.value.int32Values.add(DEFAULT_REQUEST_ID);
1344         propResponse.value.int32Values.add(2); // 2 associations; request is just 1
1345         propResponse.value.int32Values.add(KEY_FOB);
1346         propResponse.value.int32Values.add(ASSOCIATED_CURRENT_USER);
1347         propResponse.value.int32Values.add(CUSTOM_1);
1348         propResponse.value.int32Values.add(ASSOCIATED_CURRENT_USER);
1349 
1350         AtomicReference<VehiclePropValue> propRequest = replySetPropertyWithOnChangeEvent(
1351                 USER_IDENTIFICATION_ASSOCIATION, propResponse, /* rightRequestId= */ true);
1352         UserIdentificationSetRequest request = replyToValidSetUserIdentificationRequest();
1353         GenericHalCallback<UserIdentificationResponse> callback = new GenericHalCallback<>(
1354                 CALLBACK_TIMEOUT);
1355 
1356         mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, request, callback);
1357 
1358         // Assert request
1359         verifyValidSetUserIdentificationRequestMade(propRequest.get());
1360         // Assert response
1361         callback.assertCalled();
1362         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
1363         assertThat(callback.response).isNull();
1364     }
1365 
1366     @Test
testSetUserAssociation_typeMismatchOnResponse()1367     public void testSetUserAssociation_typeMismatchOnResponse() throws Exception {
1368         VehiclePropValue propResponse = new VehiclePropValue();
1369         propResponse.prop = USER_IDENTIFICATION_ASSOCIATION;
1370         propResponse.value.int32Values.add(DEFAULT_REQUEST_ID);
1371         propResponse.value.int32Values.add(1); // 1 association
1372         propResponse.value.int32Values.add(CUSTOM_1); // request is KEY_FOB
1373         propResponse.value.int32Values.add(ASSOCIATED_CURRENT_USER);
1374 
1375         AtomicReference<VehiclePropValue> propRequest = replySetPropertyWithOnChangeEvent(
1376                 USER_IDENTIFICATION_ASSOCIATION, propResponse, /* rightRequestId= */ true);
1377         UserIdentificationSetRequest request = replyToValidSetUserIdentificationRequest();
1378         GenericHalCallback<UserIdentificationResponse> callback = new GenericHalCallback<>(
1379                 CALLBACK_TIMEOUT);
1380 
1381         mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, request, callback);
1382 
1383         // Assert request
1384         verifyValidSetUserIdentificationRequestMade(propRequest.get());
1385         // Assert response
1386         callback.assertCalled();
1387         assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
1388         assertThat(callback.response).isNull();
1389     }
1390 
1391     @Test
testSetUserAssociation_ok()1392     public void testSetUserAssociation_ok() throws Exception {
1393         VehiclePropValue propResponse = new VehiclePropValue();
1394         propResponse.prop = USER_IDENTIFICATION_ASSOCIATION;
1395         propResponse.value.int32Values.add(DEFAULT_REQUEST_ID);
1396         propResponse.value.int32Values.add(1); // 1 association
1397         propResponse.value.int32Values.add(KEY_FOB);
1398         propResponse.value.int32Values.add(ASSOCIATED_CURRENT_USER);
1399 
1400         AtomicReference<VehiclePropValue> propRequest = replySetPropertyWithOnChangeEvent(
1401                 USER_IDENTIFICATION_ASSOCIATION, propResponse, /* rightRequestId= */ true);
1402         UserIdentificationSetRequest request = replyToValidSetUserIdentificationRequest();
1403         GenericHalCallback<UserIdentificationResponse> callback = new GenericHalCallback<>(
1404                 CALLBACK_TIMEOUT);
1405 
1406         mUserHalService.setUserAssociation(HAL_TIMEOUT_MS, request, callback);
1407 
1408         // Assert request
1409         verifyValidSetUserIdentificationRequestMade(propRequest.get());
1410         // Assert response
1411         callback.assertCalled();
1412         assertCallbackStatus(callback, HalCallback.STATUS_OK);
1413 
1414         UserIdentificationResponse actualResponse = callback.response;
1415 
1416         assertThat(actualResponse.requestId).isEqualTo(DEFAULT_REQUEST_ID);
1417         assertThat(actualResponse.numberAssociation).isEqualTo(1);
1418         assertThat(actualResponse.associations).hasSize(1);
1419         UserIdentificationAssociation actualAssociation = actualResponse.associations.get(0);
1420         assertThat(actualAssociation.type).isEqualTo(KEY_FOB);
1421         assertThat(actualAssociation.value).isEqualTo(ASSOCIATED_CURRENT_USER);
1422     }
1423 
1424     /**
1425      * Asserts the given {@link UsersInfo} is properly represented in the {@link VehiclePropValue}.
1426      *
1427      * @param value property containing the info
1428      * @param info info to be checked
1429      * @param initialIndex first index of the info values in the property's {@code int32Values}
1430      */
assertUsersInfo(VehiclePropValue value, UsersInfo info, int initialIndex)1431     private void assertUsersInfo(VehiclePropValue value, UsersInfo info, int initialIndex) {
1432         // TODO: consider using UserHalHelper to convert the property into a specific request,
1433         // and compare the request's UsersInfo.
1434         // But such method is not needed in production code yet.
1435         ArrayList<Integer> values = value.value.int32Values;
1436         assertWithMessage("wrong values size").that(values)
1437                 .hasSize(initialIndex + 3 + info.numberUsers * 2);
1438 
1439         int i = initialIndex;
1440         assertWithMessage("currentUser.id mismatch at index %s", i).that(values.get(i))
1441                 .isEqualTo(info.currentUser.userId);
1442         i++;
1443         assertWithMessage("currentUser.flags mismatch at index %s", i).that(values.get(i))
1444             .isEqualTo(info.currentUser.flags);
1445         i++;
1446         assertWithMessage("numberUsers mismatch at index %s", i).that(values.get(i))
1447             .isEqualTo(info.numberUsers);
1448         i++;
1449 
1450         for (int j = 0; j < info.numberUsers; j++) {
1451             int actualUserId = values.get(i++);
1452             int actualUserFlags = values.get(i++);
1453             UserInfo expectedUser = info.existingUsers.get(j);
1454             assertWithMessage("wrong id for existing user#%s at index %s", j, i)
1455                 .that(actualUserId).isEqualTo(expectedUser.userId);
1456             assertWithMessage("wrong flags for existing user#%s at index %s", j, i)
1457                 .that(actualUserFlags).isEqualTo(expectedUser.flags);
1458         }
1459     }
1460 
1461     /**
1462      * Sets the VHAL mock to emulate a property change event upon a call to set a property.
1463      *
1464      * @param prop prop to be set
1465      * @param response response to be set on event
1466      * @param rightRequestId whether the response id should match the request
1467      * @return
1468      *
1469      * @return reference to the value passed to {@code set()}.
1470      */
replySetPropertyWithOnChangeEvent(int prop, VehiclePropValue response, boolean rightRequestId)1471     private AtomicReference<VehiclePropValue> replySetPropertyWithOnChangeEvent(int prop,
1472             VehiclePropValue response, boolean rightRequestId) throws Exception {
1473         AtomicReference<VehiclePropValue> ref = new AtomicReference<>();
1474         doAnswer((inv) -> {
1475             VehiclePropValue request = inv.getArgument(0);
1476             ref.set(request);
1477             int requestId = request.value.int32Values.get(0);
1478             int responseId = rightRequestId ? requestId : requestId + 1000;
1479             response.value.int32Values.set(0, responseId);
1480             Log.d(TAG, "replySetPropertyWithOnChangeEvent(): resp=" + response + " for req="
1481                     + request);
1482             mUserHalService.onHalEvents(Arrays.asList(response));
1483             return null;
1484         }).when(mVehicleHal).set(isProperty(prop));
1485         return ref;
1486     }
1487 
1488     /**
1489      * Sets the VHAL mock to emulate a property timeout exception upon a call to set a property.
1490      */
replySetPropertyWithTimeoutException(int prop)1491     private void replySetPropertyWithTimeoutException(int prop) throws Exception {
1492         doThrow(new ServiceSpecificException(VehicleHalStatusCode.STATUS_TRY_AGAIN,
1493                 "PropId: 0x" + Integer.toHexString(prop))).when(mVehicleHal).set(isProperty(prop));
1494     }
1495 
1496     @NonNull
createUserSwitchRequest(@onNull UserInfo targetUser, @NonNull UsersInfo usersInfo)1497     private SwitchUserRequest createUserSwitchRequest(@NonNull UserInfo targetUser,
1498             @NonNull UsersInfo usersInfo) {
1499         SwitchUserRequest request = new SwitchUserRequest();
1500         request.targetUser = targetUser;
1501         request.usersInfo = usersInfo;
1502         return request;
1503     }
1504 
1505     /**
1506      * Creates and set expectations for a valid request.
1507      */
replyToValidGetUserIdentificationRequest( @onNull VehiclePropValue response)1508     private UserIdentificationGetRequest replyToValidGetUserIdentificationRequest(
1509             @NonNull VehiclePropValue response) {
1510         mockNextRequestId(DEFAULT_REQUEST_ID);
1511 
1512         UserIdentificationGetRequest request = new UserIdentificationGetRequest();
1513         request.userInfo.userId = DEFAULT_USER_ID;
1514         request.userInfo.flags = DEFAULT_USER_FLAGS;
1515         request.numberAssociationTypes = 1;
1516         request.associationTypes.add(KEY_FOB);
1517 
1518         when(mVehicleHal.get(isPropertyWithValues(USER_IDENTIFICATION_ASSOCIATION,
1519                 DEFAULT_REQUEST_ID, DEFAULT_USER_ID, DEFAULT_USER_FLAGS,
1520                 /* numberAssociations= */ 1, KEY_FOB)))
1521             .thenReturn(response);
1522 
1523         return request;
1524     }
1525 
1526     /**
1527      * Creates and set expectations for a valid request.
1528      */
replyToValidSetUserIdentificationRequest()1529     private UserIdentificationSetRequest replyToValidSetUserIdentificationRequest() {
1530         mockNextRequestId(DEFAULT_REQUEST_ID);
1531         return validUserIdentificationSetRequest();
1532     }
1533 
1534     /**
1535      * Creates a valid request that can be used in test cases where its content is not asserted.
1536      */
validUserIdentificationSetRequest()1537     private UserIdentificationSetRequest validUserIdentificationSetRequest() {
1538         UserIdentificationSetRequest request = new UserIdentificationSetRequest();
1539         request.userInfo.userId = DEFAULT_USER_ID;
1540         request.userInfo.flags = DEFAULT_USER_FLAGS;
1541         request.numberAssociations = 1;
1542         UserIdentificationSetAssociation association1 = new UserIdentificationSetAssociation();
1543         association1.type = KEY_FOB;
1544         association1.value = ASSOCIATE_CURRENT_USER;
1545         request.associations.add(association1);
1546         return request;
1547     }
1548 
1549     /**
1550      * Run empty runnable to make sure that all posted handlers are done.
1551      */
waitForHandler()1552     private void waitForHandler() {
1553         mHandler.runWithScissors(() -> { }, /* Default timeout */ CALLBACK_TIMEOUT);
1554     }
1555 
mockNextRequestId(int requestId)1556     private void mockNextRequestId(int requestId) {
1557         doReturn(requestId).when(mUserHalService).getNextRequestId();
1558     }
1559 
assertInitialUserInfoSetRequest(VehiclePropValue req, int requestType)1560     private void assertInitialUserInfoSetRequest(VehiclePropValue req, int requestType) {
1561         assertThat(req.value.int32Values.get(1)).isEqualTo(requestType);
1562         assertUsersInfo(req, mUsersInfo, 2);
1563     }
1564 
assertHalSetSwitchUserRequest(VehiclePropValue req, int messageType, UserInfo targetUserInfo)1565     private void assertHalSetSwitchUserRequest(VehiclePropValue req, int messageType,
1566             UserInfo targetUserInfo) {
1567         assertThat(req.prop).isEqualTo(SWITCH_USER);
1568         assertWithMessage("wrong request Id on %s", req).that(req.value.int32Values.get(0))
1569             .isAtLeast(1);
1570         assertThat(req.value.int32Values.get(1)).isEqualTo(messageType);
1571         assertWithMessage("targetuser.id mismatch on %s", req).that(req.value.int32Values.get(2))
1572                 .isEqualTo(targetUserInfo.userId);
1573         assertWithMessage("targetuser.flags mismatch on %s", req).that(req.value.int32Values.get(3))
1574                 .isEqualTo(targetUserInfo.flags);
1575         assertUsersInfo(req, mUsersInfo, 4);
1576     }
1577 
assertHalSetRemoveUserRequest(VehiclePropValue req, UserInfo userInfo)1578     private void assertHalSetRemoveUserRequest(VehiclePropValue req, UserInfo userInfo) {
1579         assertThat(req.prop).isEqualTo(REMOVE_USER);
1580         assertWithMessage("wrong request Id on %s", req).that(req.value.int32Values.get(0))
1581                 .isAtLeast(1);
1582         assertWithMessage("user.id mismatch on %s", req).that(req.value.int32Values.get(1))
1583                 .isEqualTo(userInfo.userId);
1584         assertWithMessage("user.flags mismatch on %s", req).that(req.value.int32Values.get(2))
1585                 .isEqualTo(userInfo.flags);
1586         assertUsersInfo(req, mUsersInfo, 3);
1587     }
1588 
assertHalSetCreateUserRequest(VehiclePropValue prop, CreateUserRequest request)1589     private void assertHalSetCreateUserRequest(VehiclePropValue prop, CreateUserRequest request) {
1590         assertThat(prop.prop).isEqualTo(CREATE_USER);
1591         assertWithMessage("wrong request Id on %s", prop).that(prop.value.int32Values.get(0))
1592                 .isEqualTo(request.requestId);
1593         assertWithMessage("newUser.userId mismatch on %s", prop).that(prop.value.int32Values.get(1))
1594                 .isEqualTo(request.newUserInfo.userId);
1595         assertWithMessage("newUser.flags mismatch on %s", prop).that(prop.value.int32Values.get(2))
1596                 .isEqualTo(request.newUserInfo.flags);
1597         assertUsersInfo(prop, request.usersInfo, 3);
1598     }
1599 
assertCallbackStatus(GenericHalCallback<?> callback, int expectedStatus)1600     private void assertCallbackStatus(GenericHalCallback<?> callback, int expectedStatus) {
1601         int actualStatus = callback.status;
1602         if (actualStatus == expectedStatus) return;
1603 
1604         fail("Wrong callback status; expected "
1605                 + UserHalHelper.halCallbackStatusToString(expectedStatus) + ", got "
1606                 + UserHalHelper.halCallbackStatusToString(actualStatus));
1607     }
1608 
1609     /**
1610      * Verifies {@code hal.get()} was called with the values used on
1611      * {@link #replyToValidGetUserIdentificationRequest(VehiclePropValue)}.
1612      */
verifyValidGetUserIdentificationRequestMade()1613     private void verifyValidGetUserIdentificationRequestMade() {
1614         verify(mVehicleHal).get(isPropertyWithValues(USER_IDENTIFICATION_ASSOCIATION,
1615                 DEFAULT_REQUEST_ID, DEFAULT_USER_ID, DEFAULT_USER_FLAGS,
1616                 /* numberAssociations= */ 1, KEY_FOB));
1617     }
1618 
1619     /**
1620      * Verifies {@code hal.set()} was called with the values used on
1621      * {@link #replyToValidSetUserIdentificationRequest(VehiclePropValue)}.
1622      */
verifyValidSetUserIdentificationRequestMade(@onNull VehiclePropValue request)1623     private void verifyValidSetUserIdentificationRequestMade(@NonNull VehiclePropValue request) {
1624         assertThat(request.prop).isEqualTo(USER_IDENTIFICATION_ASSOCIATION);
1625         assertThat(request.value.int32Values).containsExactly(DEFAULT_REQUEST_ID, DEFAULT_USER_ID,
1626                 DEFAULT_USER_FLAGS,
1627                 /* numberAssociations= */ 1, KEY_FOB, ASSOCIATE_CURRENT_USER);
1628     }
1629 
noOpCallback()1630     private static <T> HalCallback<T> noOpCallback() {
1631         return (i, r) -> { };
1632     }
1633 
1634     private final class GenericHalCallback<R> implements HalCallback<R> {
1635 
1636         private final CountDownLatch mLatch = new CountDownLatch(1);
1637         private final int mTimeout;
1638         private final List<Pair<Integer, R>> mExtraCalls = new ArrayList<>();
1639 
1640         public int status;
1641         public R response;
1642 
GenericHalCallback(int timeout)1643         GenericHalCallback(int timeout) {
1644             this.mTimeout = timeout;
1645         }
1646 
1647         @Override
onResponse(int status, R response)1648         public void onResponse(int status, R response) {
1649             Log.d(TAG, "onResponse(): status=" + status + ", response=" + response);
1650             if (mLatch.getCount() == 0) {
1651                 Log.e(TAG, "Already responded");
1652                 mExtraCalls.add(new Pair<>(status, response));
1653                 return;
1654             }
1655             this.status = status;
1656             this.response = response;
1657             mLatch.countDown();
1658         }
1659 
1660         /**
1661          * Asserts that the callback was called, or fail if it timed out.
1662          */
assertCalled()1663         public void assertCalled() throws InterruptedException {
1664             Log.d(TAG, "assertCalled(): waiting " + mTimeout + "ms");
1665             if (!mLatch.await(mTimeout, TimeUnit.MILLISECONDS)) {
1666                 throw new AssertionError("callback not called in " + mTimeout + "ms");
1667             }
1668         }
1669 
1670         /**
1671          * Asserts that the callback was not called more than once.
1672          */
assertNotCalledAgain()1673         public void assertNotCalledAgain() {
1674             if (mExtraCalls.isEmpty()) return;
1675             throw new AssertionError("Called " + mExtraCalls.size() + " times more than expected: "
1676                     + mExtraCalls);
1677         }
1678     }
1679 }