blob: 79d32827b328173a7fb7684c9e53c8d408df4f26 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.user;
import static android.car.test.mocks.AndroidMockitoHelper.getResult;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmCreateUser;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUserInfo;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUsers;
import static android.car.test.util.UserTestingHelper.UserInfoBuilder;
import static android.content.pm.UserInfo.FLAG_ADMIN;
import static android.content.pm.UserInfo.FLAG_EPHEMERAL;
import static android.content.pm.UserInfo.FLAG_GUEST;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.car.CarOccupantZoneManager.OccupantTypeEnum;
import android.car.CarOccupantZoneManager.OccupantZoneInfo;
import android.car.settings.CarSettings;
import android.car.test.mocks.AbstractExtendedMockitoTestCase;
import android.car.test.mocks.AndroidMockitoHelper;
import android.car.test.mocks.BlockingAnswer;
import android.car.test.util.BlockingResultReceiver;
import android.car.testapi.BlockingUserLifecycleListener;
import android.car.user.CarUserManager;
import android.car.user.CarUserManager.UserLifecycleEvent;
import android.car.user.CarUserManager.UserLifecycleEventType;
import android.car.user.CarUserManager.UserLifecycleListener;
import android.car.user.UserCreationResult;
import android.car.user.UserIdentificationAssociationResponse;
import android.car.user.UserRemovalResult;
import android.car.user.UserSwitchResult;
import android.car.userlib.CarUserManagerHelper;
import android.car.userlib.HalCallback;
import android.car.userlib.HalCallback.HalCallbackStatus;
import android.car.userlib.UserHalHelper;
import android.car.userlib.UserHelper;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.hardware.automotive.vehicle.V2_0.CreateUserRequest;
import android.hardware.automotive.vehicle.V2_0.CreateUserResponse;
import android.hardware.automotive.vehicle.V2_0.CreateUserStatus;
import android.hardware.automotive.vehicle.V2_0.InitialUserInfoRequestType;
import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse;
import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponseAction;
import android.hardware.automotive.vehicle.V2_0.RemoveUserRequest;
import android.hardware.automotive.vehicle.V2_0.SwitchUserRequest;
import android.hardware.automotive.vehicle.V2_0.SwitchUserResponse;
import android.hardware.automotive.vehicle.V2_0.SwitchUserStatus;
import android.hardware.automotive.vehicle.V2_0.UserFlags;
import android.hardware.automotive.vehicle.V2_0.UserIdentificationAssociation;
import android.hardware.automotive.vehicle.V2_0.UserIdentificationGetRequest;
import android.hardware.automotive.vehicle.V2_0.UserIdentificationResponse;
import android.hardware.automotive.vehicle.V2_0.UserIdentificationSetRequest;
import android.hardware.automotive.vehicle.V2_0.UsersInfo;
import android.location.LocationManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.sysprop.CarProperties;
import android.util.Log;
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
import com.android.car.hal.UserHalService;
import com.android.internal.R;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.Preconditions;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Captor;
import org.mockito.Mock;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
* This class contains unit tests for the {@link CarUserService}.
*
* The following mocks are used:
* <ol>
* <li> {@link Context} provides system services and resources.
* <li> {@link IActivityManager} provides current user.
* <li> {@link UserManager} provides user creation and user info.
* <li> {@link Resources} provides user icon.
* <li> {@link Drawable} provides bitmap of user icon.
* <ol/>
*/
public final class CarUserServiceTest extends AbstractExtendedMockitoTestCase {
private static final String TAG = CarUserServiceTest.class.getSimpleName();
private static final int NO_USER_INFO_FLAGS = 0;
private static final int NON_EXISTING_USER = 55; // must not be on mExistingUsers
private static final long DEFAULT_LIFECYCLE_TIMESTAMP = 1;
@Mock private Context mMockContext;
@Mock private Context mApplicationContext;
@Mock private LocationManager mLocationManager;
@Mock private UserHalService mUserHal;
@Mock private CarUserManagerHelper mMockedCarUserManagerHelper;
@Mock private IActivityManager mMockedIActivityManager;
@Mock private UserManager mMockedUserManager;
@Mock private Resources mMockedResources;
@Mock private Drawable mMockedDrawable;
@Mock private UserMetrics mUserMetrics;
@Mock IResultReceiver mSwitchUserUiReceiver;
@Mock PackageManager mPackageManager;
private final BlockingUserLifecycleListener mUserLifecycleListener =
BlockingUserLifecycleListener.forAnyEvent().build();
@Captor private ArgumentCaptor<UsersInfo> mUsersInfoCaptor;
private CarUserService mCarUserService;
private boolean mUser0TaskExecuted;
private FakeCarOccupantZoneService mFakeCarOccupantZoneService;
private final int mGetUserInfoRequestType = InitialUserInfoRequestType.COLD_BOOT;
private final AndroidFuture<UserSwitchResult> mUserSwitchFuture = new AndroidFuture<>();
private final AndroidFuture<UserCreationResult> mUserCreationFuture = new AndroidFuture<>();
private final AndroidFuture<UserIdentificationAssociationResponse> mUserAssociationRespFuture =
new AndroidFuture<>();
private final int mAsyncCallTimeoutMs = 100;
private final BlockingResultReceiver mReceiver =
new BlockingResultReceiver(mAsyncCallTimeoutMs);
private final InitialUserInfoResponse mGetUserInfoResponse = new InitialUserInfoResponse();
private final SwitchUserResponse mSwitchUserResponse = new SwitchUserResponse();
private final @NonNull UserInfo mAdminUser = new UserInfoBuilder(100)
.setAdmin(true)
.build();
private final @NonNull UserInfo mGuestUser = new UserInfoBuilder(111)
.setGuest(true)
.setEphemeral(true)
.build();
private final @NonNull UserInfo mRegularUser = new UserInfoBuilder(222)
.build();
private final List<UserInfo> mExistingUsers =
Arrays.asList(mAdminUser, mGuestUser, mRegularUser);
@Override
protected void onSessionBuilder(CustomMockitoSessionBuilder builder) {
builder
.spyStatic(ActivityManager.class)
// TODO(b/156299496): it cannot spy on UserManager, as it would slow down the tests
// considerably (more than 5 minutes total, instead of just a couple seconds). So, it's
// mocking UserHelper.isHeadlessSystemUser() (on mockIsHeadlessSystemUser()) instead...
.spyStatic(UserHelper.class)
.spyStatic(CarProperties.class);
}
/**
* Initialize all of the objects with the @Mock annotation.
*/
@Before
public void setUpMocks() {
doReturn(mApplicationContext).when(mMockContext).getApplicationContext();
doReturn(mLocationManager).when(mMockContext).getSystemService(Context.LOCATION_SERVICE);
doReturn(InstrumentationRegistry.getTargetContext().getContentResolver())
.when(mMockContext).getContentResolver();
doReturn(false).when(mMockedUserManager).isUserUnlockingOrUnlocked(anyInt());
doReturn(mMockedResources).when(mMockContext).getResources();
doReturn(mMockedDrawable).when(mMockedResources)
.getDrawable(eq(R.drawable.ic_account_circle), eq(null));
doReturn(mMockedDrawable).when(mMockedDrawable).mutate();
doReturn(1).when(mMockedDrawable).getIntrinsicWidth();
doReturn(1).when(mMockedDrawable).getIntrinsicHeight();
doReturn(Optional.of(mAsyncCallTimeoutMs)).when(() -> CarProperties.user_hal_timeout());
mCarUserService =
new CarUserService(
mMockContext,
mUserHal,
mMockedCarUserManagerHelper,
mMockedUserManager,
mMockedIActivityManager,
3, mUserMetrics);
mFakeCarOccupantZoneService = new FakeCarOccupantZoneService(mCarUserService);
}
@Test
public void testAddUserLifecycleListener_checkNullParameter() {
assertThrows(NullPointerException.class,
() -> mCarUserService.addUserLifecycleListener(null));
}
@Test
public void testRemoveUserLifecycleListener_checkNullParameter() {
assertThrows(NullPointerException.class,
() -> mCarUserService.removeUserLifecycleListener(null));
}
@Test
public void testOnUserLifecycleEvent_notifyListener() throws Exception {
// Arrange
mCarUserService.addUserLifecycleListener(mUserLifecycleListener);
mockExistingUsers();
// Act
sendUserSwitchingEvent(mAdminUser.id, mRegularUser.id);
// Verify
verifyListenerOnEventInvoked(mRegularUser.id,
CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING);
}
@Test
public void testOnUserLifecycleEvent_ensureAllListenersAreNotified() throws Exception {
// Arrange: add two listeners, one to fail on onEvent
// Adding the failure listener first.
UserLifecycleListener failureListener = mock(UserLifecycleListener.class);
doThrow(new RuntimeException("Failed onEvent invocation")).when(
failureListener).onEvent(any(UserLifecycleEvent.class));
mCarUserService.addUserLifecycleListener(failureListener);
mockExistingUsers();
// Adding the non-failure listener later.
mCarUserService.addUserLifecycleListener(mUserLifecycleListener);
// Act
sendUserSwitchingEvent(mAdminUser.id, mRegularUser.id);
// Verify
verifyListenerOnEventInvoked(mRegularUser.id,
CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING);
}
private void verifyListenerOnEventInvoked(int expectedNewUserId, int expectedEventType)
throws Exception {
UserLifecycleEvent actualEvent = mUserLifecycleListener.waitForAnyEvent();
assertThat(actualEvent.getEventType()).isEqualTo(expectedEventType);
assertThat(actualEvent.getUserId()).isEqualTo(expectedNewUserId);
}
private void verifyLastActiveUserSet(@UserIdInt int userId) {
verify(mMockedCarUserManagerHelper).setLastActiveUser(userId);
}
private void verifyLastActiveUserNotSet() {
verify(mMockedCarUserManagerHelper, never()).setLastActiveUser(anyInt());
}
/**
* Test that the {@link CarUserService} disables the location service for headless user 0 upon
* first run.
*/
@Test
public void testDisableLocationForHeadlessSystemUserOnFirstRun() {
sendUserUnlockedEvent(UserHandle.USER_SYSTEM);
verify(mLocationManager).setLocationEnabledForUser(
/* enabled= */ false, UserHandle.of(UserHandle.USER_SYSTEM));
}
/**
* Test that the {@link CarUserService} updates last active user on user switch in non-headless
* system user mode.
*/
@Test
public void testLastActiveUserUpdatedOnUserSwitch_nonHeadlessSystemUser() throws Exception {
mockIsHeadlessSystemUser(mRegularUser.id, false);
mockExistingUsers();
sendUserSwitchingEvent(mAdminUser.id, mRegularUser.id);
verifyLastActiveUserSet(mRegularUser.id);
}
/**
* Test that the {@link CarUserService} sets default guest restrictions on first boot.
*/
@Test
public void testInitializeGuestRestrictions_IfNotAlreadySet() {
sendUserUnlockedEvent(UserHandle.USER_SYSTEM);
assertThat(getSettingsInt(CarSettings.Global.DEFAULT_USER_RESTRICTIONS_SET)).isEqualTo(1);
}
@Test
public void testRunOnUser0UnlockImmediate() {
mUser0TaskExecuted = false;
sendUserUnlockedEvent(UserHandle.USER_SYSTEM);
mCarUserService.runOnUser0Unlock(() -> {
mUser0TaskExecuted = true;
});
assertTrue(mUser0TaskExecuted);
}
@Test
public void testRunOnUser0UnlockLater() {
mUser0TaskExecuted = false;
mCarUserService.runOnUser0Unlock(() -> {
mUser0TaskExecuted = true;
});
assertFalse(mUser0TaskExecuted);
sendUserUnlockedEvent(UserHandle.USER_SYSTEM);
assertTrue(mUser0TaskExecuted);
}
/**
* Test is lengthy as it is testing LRU logic.
*/
@Test
public void testBackgroundUserList() throws RemoteException {
int user1 = 101;
int user2 = 102;
int user3 = 103;
int user4Guest = 104;
int user5 = 105;
UserInfo user1Info = new UserInfo(user1, "user1", NO_USER_INFO_FLAGS);
UserInfo user2Info = new UserInfo(user2, "user2", NO_USER_INFO_FLAGS);
UserInfo user3Info = new UserInfo(user3, "user3", NO_USER_INFO_FLAGS);
UserInfo user4GuestInfo = new UserInfo(
user4Guest, "user4Guest", FLAG_EPHEMERAL | FLAG_GUEST);
UserInfo user5Info = new UserInfo(user5, "user5", NO_USER_INFO_FLAGS);
doReturn(user1Info).when(mMockedUserManager).getUserInfo(user1);
doReturn(user2Info).when(mMockedUserManager).getUserInfo(user2);
doReturn(user3Info).when(mMockedUserManager).getUserInfo(user3);
doReturn(user4GuestInfo).when(mMockedUserManager).getUserInfo(user4Guest);
doReturn(user5Info).when(mMockedUserManager).getUserInfo(user5);
mockGetCurrentUser(user1);
sendUserUnlockedEvent(UserHandle.USER_SYSTEM);
// user 0 should never go to that list.
assertTrue(mCarUserService.getBackgroundUsersToRestart().isEmpty());
sendUserUnlockedEvent(user1);
assertEquals(new Integer[]{user1},
mCarUserService.getBackgroundUsersToRestart().toArray());
// user 2 background, ignore in restart list
sendUserUnlockedEvent(user2);
assertEquals(new Integer[]{user1},
mCarUserService.getBackgroundUsersToRestart().toArray());
mockGetCurrentUser(user3);
sendUserUnlockedEvent(user3);
assertEquals(new Integer[]{user3, user1},
mCarUserService.getBackgroundUsersToRestart().toArray());
mockGetCurrentUser(user4Guest);
sendUserUnlockedEvent(user4Guest);
assertEquals(new Integer[]{user3, user1},
mCarUserService.getBackgroundUsersToRestart().toArray());
mockGetCurrentUser(user5);
sendUserUnlockedEvent(user5);
assertEquals(new Integer[]{user5, user3},
mCarUserService.getBackgroundUsersToRestart().toArray());
}
/**
* Test is lengthy as it is testing LRU logic.
*/
@Test
public void testBackgroundUsersStartStopKeepBackgroundUserList() throws Exception {
int user1 = 101;
int user2 = 102;
int user3 = 103;
UserInfo user1Info = new UserInfo(user1, "user1", NO_USER_INFO_FLAGS);
UserInfo user2Info = new UserInfo(user2, "user2", NO_USER_INFO_FLAGS);
UserInfo user3Info = new UserInfo(user3, "user3", NO_USER_INFO_FLAGS);
doReturn(user1Info).when(mMockedUserManager).getUserInfo(user1);
doReturn(user2Info).when(mMockedUserManager).getUserInfo(user2);
doReturn(user3Info).when(mMockedUserManager).getUserInfo(user3);
mockGetCurrentUser(user1);
sendUserUnlockedEvent(UserHandle.USER_SYSTEM);
sendUserUnlockedEvent(user1);
mockGetCurrentUser(user2);
sendUserUnlockedEvent(user2);
sendUserUnlockedEvent(user1);
mockGetCurrentUser(user3);
sendUserUnlockedEvent(user3);
assertEquals(new Integer[]{user3, user2},
mCarUserService.getBackgroundUsersToRestart().toArray());
doReturn(true).when(mMockedIActivityManager).startUserInBackground(user2);
doReturn(true).when(mMockedIActivityManager).unlockUser(user2,
null, null, null);
assertEquals(new Integer[]{user2},
mCarUserService.startAllBackgroundUsers().toArray());
sendUserUnlockedEvent(user2);
assertEquals(new Integer[]{user3, user2},
mCarUserService.getBackgroundUsersToRestart().toArray());
doReturn(ActivityManager.USER_OP_SUCCESS).when(mMockedIActivityManager).stopUser(user2,
true, null);
// should not stop the current fg user
assertFalse(mCarUserService.stopBackgroundUser(user3));
assertTrue(mCarUserService.stopBackgroundUser(user2));
assertEquals(new Integer[]{user3, user2},
mCarUserService.getBackgroundUsersToRestart().toArray());
assertEquals(new Integer[]{user3, user2},
mCarUserService.getBackgroundUsersToRestart().toArray());
}
@Test
public void testStopBackgroundUserForSystemUser() {
assertFalse(mCarUserService.stopBackgroundUser(UserHandle.USER_SYSTEM));
}
@Test
public void testStopBackgroundUserForFgUser() throws RemoteException {
int user1 = 101;
mockGetCurrentUser(user1);
assertFalse(mCarUserService.stopBackgroundUser(UserHandle.USER_SYSTEM));
}
@Test
public void testCreateAdminDriver_IfCurrentUserIsAdminUser() throws Exception {
when(mMockedUserManager.isSystemUser()).thenReturn(true);
mockUmCreateUser(mMockedUserManager, "testUser", UserManager.USER_TYPE_FULL_SECONDARY,
UserInfo.FLAG_ADMIN, 10);
mockHalCreateUser(HalCallback.STATUS_OK, CreateUserStatus.SUCCESS);
AndroidFuture<UserCreationResult> future = mCarUserService.createDriver("testUser", true);
assertThat(getResult(future).getUser().name).isEqualTo("testUser");
assertThat(getResult(future).getUser().id).isEqualTo(10);
}
@Test
public void testCreateAdminDriver_IfCurrentUserIsNotSystemUser() throws Exception {
when(mMockedUserManager.isSystemUser()).thenReturn(false);
AndroidFuture<UserCreationResult> future = mCarUserService.createDriver("testUser", true);
assertThat(getResult(future).getStatus())
.isEqualTo(UserCreationResult.STATUS_INVALID_REQUEST);
}
@Test
public void testCreateNonAdminDriver() throws Exception {
mockUmCreateUser(mMockedUserManager, "testUser", UserManager.USER_TYPE_FULL_SECONDARY,
NO_USER_INFO_FLAGS, 10);
mockHalCreateUser(HalCallback.STATUS_OK, CreateUserStatus.SUCCESS);
AndroidFuture<UserCreationResult> future = mCarUserService.createDriver("testUser", false);
UserInfo userInfo = getResult(future).getUser();
assertThat(userInfo.name).isEqualTo("testUser");
assertThat(userInfo.id).isEqualTo(10);
}
@Test
public void testCreatePassenger() {
int driverId = 90;
int passengerId = 99;
String userName = "testUser";
UserInfo userInfo = new UserInfo(passengerId, userName, NO_USER_INFO_FLAGS);
doReturn(userInfo).when(mMockedUserManager).createProfileForUser(eq(userName),
eq(UserManager.USER_TYPE_PROFILE_MANAGED), eq(0), eq(driverId));
UserInfo driverInfo = new UserInfo(driverId, "driver", NO_USER_INFO_FLAGS);
doReturn(driverInfo).when(mMockedUserManager).getUserInfo(driverId);
assertEquals(userInfo, mCarUserService.createPassenger(userName, driverId));
}
@Test
public void testCreatePassenger_IfMaximumProfileAlreadyCreated() {
int driverId = 90;
String userName = "testUser";
doReturn(null).when(mMockedUserManager).createProfileForUser(eq(userName),
eq(UserManager.USER_TYPE_PROFILE_MANAGED), anyInt(), anyInt());
UserInfo driverInfo = new UserInfo(driverId, "driver", NO_USER_INFO_FLAGS);
doReturn(driverInfo).when(mMockedUserManager).getUserInfo(driverId);
assertEquals(null, mCarUserService.createPassenger(userName, driverId));
}
@Test
public void testCreatePassenger_IfDriverIsGuest() {
int driverId = 90;
String userName = "testUser";
UserInfo driverInfo = new UserInfo(driverId, "driver", UserInfo.FLAG_GUEST);
doReturn(driverInfo).when(mMockedUserManager).getUserInfo(driverId);
assertEquals(null, mCarUserService.createPassenger(userName, driverId));
}
@Test
public void testSwitchDriver() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int requestId = 42;
mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
mSwitchUserResponse.requestId = requestId;
mockHalSwitch(mAdminUser.id, mRegularUser, mSwitchUserResponse);
mockAmSwitchUser(mRegularUser, true);
when(mMockedUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH))
.thenReturn(false);
mCarUserService.switchDriver(mRegularUser.id, mUserSwitchFuture);
assertThat(getUserSwitchResult().getStatus())
.isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
}
@Test
public void testSwitchDriver_IfUserSwitchIsNotAllowed() throws Exception {
when(mMockedUserManager.getUserSwitchability())
.thenReturn(UserManager.SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED);
mCarUserService.switchDriver(mRegularUser.id, mUserSwitchFuture);
assertThat(getUserSwitchResult().getStatus())
.isEqualTo(UserSwitchResult.STATUS_INVALID_REQUEST);
}
@Test
public void testSwitchDriver_IfSwitchedToCurrentUser() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
when(mMockedUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH))
.thenReturn(false);
mCarUserService.switchDriver(mAdminUser.id, mUserSwitchFuture);
assertThat(getUserSwitchResult().getStatus())
.isEqualTo(UserSwitchResult.STATUS_ALREADY_REQUESTED_USER);
}
@Test
public void testStartPassenger() throws RemoteException {
int passenger1Id = 91;
int passenger2Id = 92;
int passenger3Id = 93;
int zone1Id = 1;
int zone2Id = 2;
doReturn(true).when(mMockedIActivityManager)
.startUserInBackgroundWithListener(anyInt(), eq(null));
assertTrue(mCarUserService.startPassenger(passenger1Id, zone1Id));
assertTrue(mCarUserService.startPassenger(passenger2Id, zone2Id));
assertFalse(mCarUserService.startPassenger(passenger3Id, zone2Id));
}
@Test
public void testStopPassenger() throws RemoteException {
int user1Id = 11;
int passenger1Id = 91;
int passenger2Id = 92;
int zoneId = 1;
UserInfo user1Info = new UserInfo(user1Id, "user1", NO_USER_INFO_FLAGS);
UserInfo passenger1Info =
new UserInfo(passenger1Id, "passenger1", UserInfo.FLAG_MANAGED_PROFILE);
associateParentChild(user1Info, passenger1Info);
doReturn(passenger1Info).when(mMockedUserManager).getUserInfo(passenger1Id);
doReturn(null).when(mMockedUserManager).getUserInfo(passenger2Id);
mockGetCurrentUser(user1Id);
doReturn(true).when(mMockedIActivityManager)
.startUserInBackgroundWithListener(anyInt(), eq(null));
assertTrue(mCarUserService.startPassenger(passenger1Id, zoneId));
assertTrue(mCarUserService.stopPassenger(passenger1Id));
// Test of stopping an already stopped passenger.
assertTrue(mCarUserService.stopPassenger(passenger1Id));
// Test of stopping a non-existing passenger.
assertFalse(mCarUserService.stopPassenger(passenger2Id));
}
private static void associateParentChild(UserInfo parent, UserInfo child) {
parent.profileGroupId = parent.id;
child.profileGroupId = parent.id;
}
private static List<UserInfo> prepareUserList() {
List<UserInfo> users = new ArrayList<>(Arrays.asList(
new UserInfo(10, "test10", UserInfo.FLAG_PRIMARY | UserInfo.FLAG_ADMIN),
new UserInfo(11, "test11", NO_USER_INFO_FLAGS),
new UserInfo(12, "test12", UserInfo.FLAG_MANAGED_PROFILE),
new UserInfo(13, "test13", NO_USER_INFO_FLAGS),
new UserInfo(14, "test14", UserInfo.FLAG_GUEST),
new UserInfo(15, "test15", UserInfo.FLAG_EPHEMERAL),
new UserInfo(16, "test16", UserInfo.FLAG_DISABLED),
new UserInfo(17, "test17", UserInfo.FLAG_MANAGED_PROFILE),
new UserInfo(18, "test18", UserInfo.FLAG_MANAGED_PROFILE),
new UserInfo(19, "test19", NO_USER_INFO_FLAGS)
));
// Parent: test10, child: test12
associateParentChild(users.get(0), users.get(2));
// Parent: test13, child: test17
associateParentChild(users.get(3), users.get(7));
// Parent: test13, child: test18
associateParentChild(users.get(3), users.get(8));
return users;
}
@Test
public void testGetAllPossibleDrivers() {
Set<Integer> expected = new HashSet<Integer>(Arrays.asList(10, 11, 13, 14));
when(mMockedUserManager.getUsers(true)).thenReturn(prepareUserList());
mockIsHeadlessSystemUser(19, true);
for (UserInfo user : mCarUserService.getAllDrivers()) {
assertThat(expected).contains(user.id);
expected.remove(user.id);
}
assertThat(expected).isEmpty();
}
@Test
public void testGetAllPassengers() {
SparseArray<HashSet<Integer>> testCases = new SparseArray<HashSet<Integer>>() {
{
put(0, new HashSet<Integer>());
put(10, new HashSet<Integer>(Arrays.asList(12)));
put(11, new HashSet<Integer>());
put(13, new HashSet<Integer>(Arrays.asList(17)));
}
};
mockIsHeadlessSystemUser(18, true);
for (int i = 0; i < testCases.size(); i++) {
when(mMockedUserManager.getUsers(true)).thenReturn(prepareUserList());
List<UserInfo> passengers = mCarUserService.getPassengers(testCases.keyAt(i));
HashSet<Integer> expected = testCases.valueAt(i);
for (UserInfo user : passengers) {
assertThat(expected).contains(user.id);
expected.remove(user.id);
}
assertThat(expected).isEmpty();
}
}
@Test
public void testRemoveUser_currentUserCannotBeRemoved() throws Exception {
mockCurrentUser(mAdminUser);
UserRemovalResult result = mCarUserService.removeUser(mAdminUser.id);
assertThat(result.getStatus())
.isEqualTo(UserRemovalResult.STATUS_TARGET_USER_IS_CURRENT_USER);
}
@Test
public void testRemoveUser_userNotExist() throws Exception {
UserRemovalResult result = mCarUserService.removeUser(15);
assertThat(result.getStatus())
.isEqualTo(UserRemovalResult.STATUS_USER_DOES_NOT_EXIST);
}
@Test
public void testRemoveUser_lastAdminUser() throws Exception {
mockCurrentUser(mRegularUser);
mockExistingUsers();
UserRemovalResult result = mCarUserService.removeUser(mAdminUser.id);
assertThat(result.getStatus())
.isEqualTo(UserRemovalResult.STATUS_TARGET_USER_IS_LAST_ADMIN_USER);
}
@Test
public void testRemoveUser_notLastAdminUser_success() throws Exception {
// Give admin rights to regular user.
UserInfo currentUser = mRegularUser;
currentUser.flags = currentUser.flags | FLAG_ADMIN;
mockExistingUsersAndCurrentUser(currentUser);
int removeUserId = mAdminUser.id;
when(mMockedUserManager.removeUser(removeUserId)).thenReturn(true);
UserRemovalResult result = mCarUserService.removeUser(removeUserId);
assertThat(result.getStatus()).isEqualTo(UserRemovalResult.STATUS_SUCCESSFUL);
assertHalRemove(currentUser.id, removeUserId);
}
@Test
public void testRemoveUser_success() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int removeUserId = mRegularUser.id;
when(mMockedUserManager.removeUser(removeUserId)).thenReturn(true);
UserRemovalResult result = mCarUserService.removeUser(removeUserId);
assertThat(result.getStatus()).isEqualTo(UserRemovalResult.STATUS_SUCCESSFUL);
assertHalRemove(mAdminUser.id, removeUserId);
}
@Test
public void testRemoveUser_androidFailure() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int targetUserId = mRegularUser.id;
when(mMockedUserManager.removeUser(targetUserId)).thenReturn(false);
UserRemovalResult result = mCarUserService.removeUser(targetUserId);
assertThat(result.getStatus()).isEqualTo(UserRemovalResult.STATUS_ANDROID_FAILURE);
}
@Test
public void testSwitchUser_nullReceiver() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
assertThrows(NullPointerException.class, () -> mCarUserService
.switchUser(mAdminUser.id, mAsyncCallTimeoutMs, null));
}
@Test
public void testSwitchUser_nonExistingTarget() throws Exception {
assertThrows(IllegalArgumentException.class, () -> mCarUserService
.switchUser(NON_EXISTING_USER, mAsyncCallTimeoutMs, mUserSwitchFuture));
}
@Test
public void testSwitchUser_targetSameAsCurrentUser() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
mCarUserService.switchUser(mAdminUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
assertThat(getUserSwitchResult().getStatus())
.isEqualTo(UserSwitchResult.STATUS_ALREADY_REQUESTED_USER);
}
@Test
public void testSwitchUser_HalSuccessAndroidSuccess() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int requestId = 42;
mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
mSwitchUserResponse.requestId = requestId;
mockHalSwitch(mAdminUser.id, mGuestUser, mSwitchUserResponse);
mockAmSwitchUser(mGuestUser, true);
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
assertThat(getUserSwitchResult().getStatus()).isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
// update current user due to successful user switch
mockCurrentUser(mGuestUser);
sendUserUnlockedEvent(mGuestUser.id);
assertPostSwitch(requestId, mGuestUser.id, mGuestUser.id);
}
@Test
public void testSwitchUser_HalSuccessAndroidFailure() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int requestId = 42;
mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
mSwitchUserResponse.requestId = requestId;
mockHalSwitch(mAdminUser.id, mGuestUser, mSwitchUserResponse);
mockAmSwitchUser(mGuestUser, false);
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
assertThat(getUserSwitchResult().getStatus())
.isEqualTo(UserSwitchResult.STATUS_ANDROID_FAILURE);
assertPostSwitch(requestId, mAdminUser.id, mGuestUser.id);
}
@Test
public void testSwitchUser_HalFailure() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
mSwitchUserResponse.status = SwitchUserStatus.FAILURE;
mSwitchUserResponse.errorMessage = "Error Message";
mockHalSwitch(mAdminUser.id, mGuestUser, mSwitchUserResponse);
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
UserSwitchResult result = getUserSwitchResult();
assertThat(result.getStatus()).isEqualTo(UserSwitchResult.STATUS_HAL_FAILURE);
assertThat(result.getErrorMessage()).isEqualTo(mSwitchUserResponse.errorMessage);
}
@Test
public void testSwitchUser_error_badCallbackStatus() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
mockHalSwitch(mAdminUser.id, HalCallback.STATUS_WRONG_HAL_RESPONSE, mSwitchUserResponse,
mGuestUser);
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
assertThat(getUserSwitchResult().getStatus())
.isEqualTo(UserSwitchResult.STATUS_HAL_INTERNAL_FAILURE);
}
@Test
public void testSwitchUser_multipleCallsDifferentUser_beforeFirstUserUnlocked()
throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int requestId = 42;
mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
mSwitchUserResponse.requestId = requestId;
mockHalSwitch(mAdminUser.id, mGuestUser, mSwitchUserResponse);
mockAmSwitchUser(mGuestUser, true);
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
// calling another user switch before unlock
int newRequestId = 43;
SwitchUserResponse switchUserResponse = new SwitchUserResponse();
switchUserResponse.status = SwitchUserStatus.SUCCESS;
switchUserResponse.requestId = newRequestId;
mockHalSwitch(mAdminUser.id, mRegularUser, switchUserResponse);
mockAmSwitchUser(mRegularUser, true);
AndroidFuture<UserSwitchResult> futureNewRequest = new AndroidFuture<>();
mCarUserService.switchUser(mRegularUser.id, mAsyncCallTimeoutMs, futureNewRequest);
assertThat(getUserSwitchResult().getStatus()).isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
assertThat(getResult(futureNewRequest).getStatus())
.isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
assertNoPostSwitch();
assertHalSwitch(mAdminUser.id, mGuestUser.id);
assertHalSwitch(mAdminUser.id, mRegularUser.id);
}
@Test
public void testSwitchUser_multipleCallsDifferentUser_beforeFirstUserUnlock_abandonFirstCall()
throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int requestId = 42;
mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
mSwitchUserResponse.requestId = requestId;
mockHalSwitch(mAdminUser.id, mGuestUser, mSwitchUserResponse);
mockAmSwitchUser(mGuestUser, true);
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
// calling another user switch before unlock
int newRequestId = 43;
SwitchUserResponse switchUserResponse = new SwitchUserResponse();
switchUserResponse.status = SwitchUserStatus.SUCCESS;
switchUserResponse.requestId = newRequestId;
mockHalSwitch(mAdminUser.id, mRegularUser, switchUserResponse);
mockAmSwitchUser(mRegularUser, true);
AndroidFuture<UserSwitchResult> futureNewRequest = new AndroidFuture<>();
mCarUserService.switchUser(mRegularUser.id, mAsyncCallTimeoutMs, futureNewRequest);
mockCurrentUser(mRegularUser);
sendUserUnlockedEvent(mRegularUser.id);
assertThat(getResult(futureNewRequest).getStatus())
.isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
assertPostSwitch(newRequestId, mRegularUser.id, mRegularUser.id);
assertHalSwitch(mAdminUser.id, mGuestUser.id);
assertHalSwitch(mAdminUser.id, mRegularUser.id);
}
@Test
public void testSwitchUser_multipleCallsDifferentUser_beforeHALResponded()
throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int requestId = 42;
mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
mSwitchUserResponse.requestId = requestId;
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
// calling another user switch before unlock
int newRequestId = 43;
SwitchUserResponse switchUserResponse = new SwitchUserResponse();
switchUserResponse.status = SwitchUserStatus.SUCCESS;
switchUserResponse.requestId = newRequestId;
mockHalSwitch(mAdminUser.id, mRegularUser, switchUserResponse);
mockAmSwitchUser(mRegularUser, true);
AndroidFuture<UserSwitchResult> futureNewRequest = new AndroidFuture<>();
mCarUserService.switchUser(mRegularUser.id, mAsyncCallTimeoutMs, futureNewRequest);
assertThat(getResult(futureNewRequest).getStatus())
.isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
assertNoPostSwitch();
assertHalSwitch(mAdminUser.id, mGuestUser.id);
assertHalSwitch(mAdminUser.id, mRegularUser.id);
}
@Test
public void testSwitchUser_multipleCallsDifferentUser_beforeHALResponded_abandonFirstCall()
throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int requestId = 42;
mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
mSwitchUserResponse.requestId = requestId;
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
// calling another user switch before unlock
int newRequestId = 43;
SwitchUserResponse switchUserResponse = new SwitchUserResponse();
switchUserResponse.status = SwitchUserStatus.SUCCESS;
switchUserResponse.requestId = newRequestId;
mockHalSwitch(mAdminUser.id, mRegularUser, switchUserResponse);
mockAmSwitchUser(mRegularUser, true);
AndroidFuture<UserSwitchResult> futureNewRequest = new AndroidFuture<>();
mCarUserService.switchUser(mRegularUser.id, mAsyncCallTimeoutMs, futureNewRequest);
mockCurrentUser(mRegularUser);
sendUserUnlockedEvent(mRegularUser.id);
assertThat(getResult(futureNewRequest).getStatus())
.isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
assertPostSwitch(newRequestId, mRegularUser.id, mRegularUser.id);
assertHalSwitch(mAdminUser.id, mGuestUser.id);
assertHalSwitch(mAdminUser.id, mRegularUser.id);
}
@Test
public void testSwitchUser_multipleCallsDifferentUser_HALRespondedLate_abandonFirstCall()
throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int requestId = 42;
mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
mSwitchUserResponse.requestId = requestId;
BlockingAnswer<Void> blockingAnswer = mockHalSwitchLateResponse(mAdminUser.id, mGuestUser,
mSwitchUserResponse);
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
// calling another user switch before unlock
int newRequestId = 43;
SwitchUserResponse switchUserResponse = new SwitchUserResponse();
switchUserResponse.status = SwitchUserStatus.SUCCESS;
switchUserResponse.requestId = newRequestId;
mockHalSwitch(mAdminUser.id, mRegularUser, switchUserResponse);
mockAmSwitchUser(mRegularUser, true);
AndroidFuture<UserSwitchResult> futureNewRequest = new AndroidFuture<>();
mCarUserService.switchUser(mRegularUser.id, mAsyncCallTimeoutMs, futureNewRequest);
mockCurrentUser(mRegularUser);
sendUserUnlockedEvent(mRegularUser.id);
blockingAnswer.unblock();
UserSwitchResult result = getUserSwitchResult();
assertThat(result.getStatus())
.isEqualTo(UserSwitchResult.STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST);
assertThat(getResult(futureNewRequest).getStatus())
.isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
assertPostSwitch(newRequestId, mRegularUser.id, mRegularUser.id);
assertHalSwitch(mAdminUser.id, mGuestUser.id);
assertHalSwitch(mAdminUser.id, mRegularUser.id);
}
@Test
public void testSwitchUser_multipleCallsSameUser_beforeHALResponded() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int requestId = 42;
mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
mSwitchUserResponse.requestId = requestId;
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
// calling another user switch before unlock
AndroidFuture<UserSwitchResult> futureNewRequest = new AndroidFuture<>();
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, futureNewRequest);
assertThat(getResult(futureNewRequest).getStatus())
.isEqualTo(UserSwitchResult.STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO);
assertNoPostSwitch();
assertHalSwitch(mAdminUser.id, mGuestUser.id);
}
@Test
public void testSwitchUser_multipleCallsSameUser_beforeFirstUserUnlocked() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int requestId = 42;
mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
mSwitchUserResponse.requestId = requestId;
mockHalSwitch(mAdminUser.id, mGuestUser, mSwitchUserResponse);
mockAmSwitchUser(mGuestUser, true);
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
// calling another user switch before unlock
AndroidFuture<UserSwitchResult> futureNewRequest = new AndroidFuture<>();
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, futureNewRequest);
assertThat(getUserSwitchResult().getStatus()).isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
assertThat(getResult(futureNewRequest).getStatus())
.isEqualTo(UserSwitchResult.STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO);
assertNoPostSwitch();
assertHalSwitch(mAdminUser.id, mGuestUser.id);
}
@Test
public void testSwitchUser_multipleCallsSameUser_beforeFirstUserUnlocked_noAffectOnFirstCall()
throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int requestId = 42;
mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
mSwitchUserResponse.requestId = requestId;
mockHalSwitch(mAdminUser.id, mGuestUser, mSwitchUserResponse);
mockAmSwitchUser(mGuestUser, true);
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
int newRequestId = 43;
mSwitchUserResponse.requestId = newRequestId;
// calling another user switch before unlock
AndroidFuture<UserSwitchResult> futureNewRequest = new AndroidFuture<>();
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, futureNewRequest);
mockCurrentUser(mGuestUser);
sendUserUnlockedEvent(mGuestUser.id);
assertThat(getUserSwitchResult().getStatus()).isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
assertThat(getResult(futureNewRequest).getStatus())
.isEqualTo(UserSwitchResult.STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO);
assertPostSwitch(requestId, mGuestUser.id, mGuestUser.id);
assertHalSwitch(mAdminUser.id, mGuestUser.id);
}
@Test
public void testSwitchUser_InvalidPermission() throws Exception {
mockManageUsersPermission(android.Manifest.permission.MANAGE_USERS, false);
assertThrows(SecurityException.class, () -> mCarUserService
.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture));
}
@Test
public void testLegacyUserSwitch_ok() throws Exception {
mockExistingUsers();
sendUserSwitchingEvent(mAdminUser.id, mRegularUser.id);
verify(mUserHal).legacyUserSwitch(isSwitchUserRequest(0, mAdminUser.id, mRegularUser.id));
}
@Test
public void testLegacyUserSwitch_notCalledAfterNormalSwitch() throws Exception {
// Arrange - emulate normal switch
mockExistingUsersAndCurrentUser(mAdminUser);
int requestId = 42;
mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
mSwitchUserResponse.requestId = requestId;
mockHalSwitch(mAdminUser.id, mGuestUser, mSwitchUserResponse);
mockAmSwitchUser(mGuestUser, true);
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
// Act - trigger legacy switch
sendUserSwitchingEvent(mAdminUser.id, mGuestUser.id);
// Assert
verify(mUserHal, never()).legacyUserSwitch(any());
}
@Test
public void testSetSwitchUserUI_receiverSetAndCalled() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int callerId = Binder.getCallingUid();
mockCallerUid(callerId, true);
int requestId = 42;
mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
mSwitchUserResponse.requestId = requestId;
mockHalSwitch(mAdminUser.id, mGuestUser, mSwitchUserResponse);
mockAmSwitchUser(mGuestUser, true);
mCarUserService.setUserSwitchUiCallback(mSwitchUserUiReceiver);
mCarUserService.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
// update current user due to successful user switch
verify(mSwitchUserUiReceiver).send(mGuestUser.id, null);
}
@Test
public void testSetSwitchUserUI_nonCarSysUiCaller() throws Exception {
int callerId = Binder.getCallingUid();
mockCallerUid(callerId, false);
assertThrows(SecurityException.class,
() -> mCarUserService.setUserSwitchUiCallback(mSwitchUserUiReceiver));
}
@Test
public void testSwitchUser_OEMRequest_success() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
mockAmSwitchUser(mRegularUser, true);
int requestId = -1;
mCarUserService.switchAndroidUserFromHal(requestId, mRegularUser.id);
mockCurrentUser(mRegularUser);
sendUserUnlockedEvent(mRegularUser.id);
assertPostSwitch(requestId, mRegularUser.id, mRegularUser.id);
}
@Test
public void testSwitchUser_OEMRequest_failure() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
mockAmSwitchUser(mRegularUser, false);
int requestId = -1;
mCarUserService.switchAndroidUserFromHal(requestId, mRegularUser.id);
assertPostSwitch(requestId, mAdminUser.id, mRegularUser.id);
}
@Test
public void testCreateUser_nullType() throws Exception {
assertThrows(NullPointerException.class, () -> mCarUserService
.createUser("dude", null, 108, mAsyncCallTimeoutMs, mUserCreationFuture));
}
@Test
public void testCreateUser_nullReceiver() throws Exception {
assertThrows(NullPointerException.class, () -> mCarUserService
.createUser("dude", "TypeONegative", 108, mAsyncCallTimeoutMs, null));
}
@Test
public void testCreateUser_umCreateReturnsNull() throws Exception {
// No need to mock um.createUser() to return null
mCarUserService.createUser("dude", "TypeONegative", 108, mAsyncCallTimeoutMs,
mUserCreationFuture);
UserCreationResult result = getUserCreationResult();
assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_ANDROID_FAILURE);
assertThat(result.getUser()).isNull();
assertThat(result.getErrorMessage()).isNull();
assertNoHalUserCreation();
verifyNoUserRemoved();
}
@Test
public void testCreateUser_umCreateThrowsException() throws Exception {
mockUmCreateUser(mMockedUserManager, "dude", "TypeONegative", 108,
new RuntimeException("D'OH!"));
mCarUserService.createUser("dude", "TypeONegative", 108, mAsyncCallTimeoutMs,
mUserCreationFuture);
UserCreationResult result = getUserCreationResult();
assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_ANDROID_FAILURE);
assertThat(result.getUser()).isNull();
assertThat(result.getErrorMessage()).isNull();
assertNoHalUserCreation();
verifyNoUserRemoved();
}
@Test
public void testCreateUser_internalHalFailure() throws Exception {
mockUmCreateUser(mMockedUserManager, "dude", "TypeONegative", 108, 42);
mockHalCreateUser(HalCallback.STATUS_INVALID, /* not_used_status= */ -1);
mCarUserService.createUser("dude", "TypeONegative", 108, mAsyncCallTimeoutMs,
mUserCreationFuture);
UserCreationResult result = getUserCreationResult();
assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_HAL_INTERNAL_FAILURE);
assertThat(result.getUser()).isNull();
assertThat(result.getErrorMessage()).isNull();
verifyUserRemoved(42);
}
@Test
public void testCreateUser_halFailure() throws Exception {
mockUmCreateUser(mMockedUserManager, "dude", "TypeONegative", 108, 42);
mockHalCreateUser(HalCallback.STATUS_OK, CreateUserStatus.FAILURE);
mCarUserService.createUser("dude", "TypeONegative", 108, mAsyncCallTimeoutMs,
mUserCreationFuture);
UserCreationResult result = getUserCreationResult();
assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_HAL_FAILURE);
assertThat(result.getUser()).isNull();
assertThat(result.getErrorMessage()).isNull();
verifyUserRemoved(42);
}
@Test
public void testCreateUser_halServiceThrowsRuntimeException() throws Exception {
mockUmCreateUser(mMockedUserManager, "dude", "TypeONegative", 108, 42);
mockHalCreateUserThrowsRuntimeException();
mCarUserService.createUser("dude", "TypeONegative", 108, mAsyncCallTimeoutMs,
mUserCreationFuture);
UserCreationResult result = getUserCreationResult();
assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_HAL_INTERNAL_FAILURE);
assertThat(result.getUser()).isNull();
assertThat(result.getErrorMessage()).isNull();
verifyUserRemoved(42);
}
@Test
public void testCreateUser_success() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int userId = mGuestUser.id;
mockUmCreateUser(mMockedUserManager, "dude", UserManager.USER_TYPE_FULL_GUEST,
UserInfo.FLAG_EPHEMERAL, userId);
ArgumentCaptor<CreateUserRequest> requestCaptor =
mockHalCreateUser(HalCallback.STATUS_OK, CreateUserStatus.SUCCESS);
mCarUserService.createUser("dude", UserManager.USER_TYPE_FULL_GUEST,
UserInfo.FLAG_EPHEMERAL, mAsyncCallTimeoutMs, mUserCreationFuture);
// Assert request
CreateUserRequest request = requestCaptor.getValue();
Log.d(TAG, "createUser() request: " + request);
assertThat(request.newUserName).isEqualTo("dude");
assertThat(request.newUserInfo.userId).isEqualTo(userId);
assertThat(request.newUserInfo.flags).isEqualTo(UserFlags.GUEST | UserFlags.EPHEMERAL);
assertDefaultUsersInfo(request.usersInfo, mAdminUser);
UserCreationResult result = getUserCreationResult();
assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_SUCCESSFUL);
assertThat(result.getErrorMessage()).isNull();
UserInfo newUser = result.getUser();
assertThat(newUser).isNotNull();
assertThat(newUser.id).isEqualTo(userId);
assertThat(newUser.name).isEqualTo("dude");
assertThat(newUser.userType).isEqualTo(UserManager.USER_TYPE_FULL_GUEST);
assertThat(newUser.flags).isEqualTo(UserInfo.FLAG_EPHEMERAL);
verifyNoUserRemoved();
}
@Test
public void testCreateUser_success_nullName() throws Exception {
String nullName = null;
mockExistingUsersAndCurrentUser(mAdminUser);
int userId = mGuestUser.id;
mockUmCreateUser(mMockedUserManager, nullName, UserManager.USER_TYPE_FULL_GUEST,
UserInfo.FLAG_EPHEMERAL, userId);
ArgumentCaptor<CreateUserRequest> requestCaptor =
mockHalCreateUser(HalCallback.STATUS_OK, CreateUserStatus.SUCCESS);
mCarUserService.createUser(nullName, UserManager.USER_TYPE_FULL_GUEST,
UserInfo.FLAG_EPHEMERAL, mAsyncCallTimeoutMs, mUserCreationFuture);
// Assert request
CreateUserRequest request = requestCaptor.getValue();
Log.d(TAG, "createUser() request: " + request);
assertThat(request.newUserName).isEmpty();
assertThat(request.newUserInfo.userId).isEqualTo(userId);
assertThat(request.newUserInfo.flags).isEqualTo(UserFlags.GUEST | UserFlags.EPHEMERAL);
assertDefaultUsersInfo(request.usersInfo, mAdminUser);
UserCreationResult result = getUserCreationResult();
assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_SUCCESSFUL);
assertThat(result.getErrorMessage()).isNull();
UserInfo newUser = result.getUser();
assertThat(newUser).isNotNull();
assertThat(newUser.id).isEqualTo(userId);
assertThat(newUser.name).isNull();
assertThat(newUser.userType).isEqualTo(UserManager.USER_TYPE_FULL_GUEST);
assertThat(newUser.flags).isEqualTo(UserInfo.FLAG_EPHEMERAL);
verifyNoUserRemoved();
}
@Test
public void testGetUserInfo_nullReceiver() throws Exception {
assertThrows(NullPointerException.class, () -> mCarUserService
.getInitialUserInfo(mGetUserInfoRequestType, mAsyncCallTimeoutMs, null));
}
@Test
public void testGetInitialUserInfo_validReceiver_invalidPermission() throws Exception {
mockManageUsersPermission(android.Manifest.permission.MANAGE_USERS, false);
assertThrows(SecurityException.class,
() -> mCarUserService.getInitialUserInfo(42, 108, mReceiver));
}
@Test
public void testGetUserInfo_defaultResponse() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
mGetUserInfoResponse.action = InitialUserInfoResponseAction.DEFAULT;
mockGetInitialInfo(mAdminUser.id, mGetUserInfoResponse);
mCarUserService.getInitialUserInfo(mGetUserInfoRequestType, mAsyncCallTimeoutMs, mReceiver);
assertThat(mReceiver.getResultCode()).isEqualTo(HalCallback.STATUS_OK);
Bundle resultData = mReceiver.getResultData();
assertThat(resultData).isNotNull();
assertInitialInfoAction(resultData, mGetUserInfoResponse.action);
assertInitialInfoUserLocales(resultData, null);
}
@Test
public void testGetUserInfo_defaultResponse_withLocale() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
mGetUserInfoResponse.action = InitialUserInfoResponseAction.DEFAULT;
mGetUserInfoResponse.userLocales = "LOL";
mockGetInitialInfo(mAdminUser.id, mGetUserInfoResponse);
mCarUserService.getInitialUserInfo(mGetUserInfoRequestType, mAsyncCallTimeoutMs, mReceiver);
assertThat(mReceiver.getResultCode()).isEqualTo(HalCallback.STATUS_OK);
Bundle resultData = mReceiver.getResultData();
assertThat(resultData).isNotNull();
assertInitialInfoAction(resultData, mGetUserInfoResponse.action);
assertInitialInfoUserLocales(resultData, "LOL");
}
@Test
public void testGetUserInfo_switchUserResponse() throws Exception {
int switchUserId = mGuestUser.id;
mockExistingUsersAndCurrentUser(mAdminUser);
mGetUserInfoResponse.action = InitialUserInfoResponseAction.SWITCH;
mGetUserInfoResponse.userToSwitchOrCreate.userId = switchUserId;
mockGetInitialInfo(mAdminUser.id, mGetUserInfoResponse);
mCarUserService.getInitialUserInfo(mGetUserInfoRequestType, mAsyncCallTimeoutMs, mReceiver);
assertThat(mReceiver.getResultCode()).isEqualTo(HalCallback.STATUS_OK);
Bundle resultData = mReceiver.getResultData();
assertThat(resultData).isNotNull();
assertInitialInfoAction(resultData, mGetUserInfoResponse.action);
assertUserId(resultData, switchUserId);
assertNoUserFlags(resultData);
assertNoUserName(resultData);
}
@Test
public void testGetUserInfo_createUserResponse() throws Exception {
int newUserFlags = 42;
String newUserName = "TheDude";
mockExistingUsersAndCurrentUser(mAdminUser);
mGetUserInfoResponse.action = InitialUserInfoResponseAction.CREATE;
mGetUserInfoResponse.userToSwitchOrCreate.flags = newUserFlags;
mGetUserInfoResponse.userNameToCreate = newUserName;
mockGetInitialInfo(mAdminUser.id, mGetUserInfoResponse);
mCarUserService.getInitialUserInfo(mGetUserInfoRequestType, mAsyncCallTimeoutMs, mReceiver);
assertThat(mReceiver.getResultCode()).isEqualTo(HalCallback.STATUS_OK);
Bundle resultData = mReceiver.getResultData();
assertThat(resultData).isNotNull();
assertInitialInfoAction(resultData, mGetUserInfoResponse.action);
assertNoUserId(resultData);
assertUserFlags(resultData, newUserFlags);
assertUserName(resultData, newUserName);
}
/**
* Tests the {@code getUserInfo()} that's used by other services.
*/
@Test
public void testGetInitialUserInfo() throws Exception {
int requestType = 42;
mockExistingUsersAndCurrentUser(mAdminUser);
HalCallback<InitialUserInfoResponse> callback = (s, r) -> { };
mCarUserService.getInitialUserInfo(requestType, callback);
verify(mUserHal).getInitialUserInfo(eq(requestType), anyInt(), mUsersInfoCaptor.capture(),
same(callback));
assertDefaultUsersInfo(mUsersInfoCaptor.getValue(), mAdminUser);
}
@Test
public void testGetInitialUserInfo_nullCallback() throws Exception {
assertThrows(NullPointerException.class,
() -> mCarUserService.getInitialUserInfo(42, null));
}
@Test
public void testGetInitialUserInfo_invalidPermission() throws Exception {
mockManageUsersPermission(android.Manifest.permission.MANAGE_USERS, false);
assertThrows(SecurityException.class,
() -> mCarUserService.getInitialUserInfo(42, (s, r) -> { }));
}
@Test
public void testGetInitialUser_invalidPermission() throws Exception {
mockManageUsersPermission(android.Manifest.permission.INTERACT_ACROSS_USERS, false);
mockManageUsersPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, false);
assertThrows(SecurityException.class, () -> mCarUserService.getInitialUser());
}
@Test
public void testGetInitialUser_ok() throws Exception {
assertThat(mCarUserService.getInitialUser()).isNull();
UserInfo user = new UserInfo();
mCarUserService.setInitialUser(user);
assertThat(mCarUserService.getInitialUser()).isSameAs(user);
}
@Test
public void testIsHalSupported() throws Exception {
when(mUserHal.isSupported()).thenReturn(true);
assertThat(mCarUserService.isUserHalSupported()).isTrue();
}
@Test
public void testGetUserIdentificationAssociation_nullTypes() throws Exception {
assertThrows(IllegalArgumentException.class,
() -> mCarUserService.getUserIdentificationAssociation(null));
}
@Test
public void testGetUserIdentificationAssociation_emptyTypes() throws Exception {
assertThrows(IllegalArgumentException.class,
() -> mCarUserService.getUserIdentificationAssociation(new int[] {}));
}
@Test
public void testGetUserIdentificationAssociation_noPermission() throws Exception {
mockManageUsersPermission(android.Manifest.permission.MANAGE_USERS, false);
assertThrows(SecurityException.class,
() -> mCarUserService.getUserIdentificationAssociation(new int[] { 42 }));
}
@Test
public void testGetUserIdentificationAssociation_noSuchUser() throws Exception {
// Should fail because we're not mocking UserManager.getUserInfo() to set the flag
assertThrows(IllegalArgumentException.class,
() -> mCarUserService.getUserIdentificationAssociation(new int[] { 42 }));
}
@Test
public void testGetUserIdentificationAssociation_service_returnNull() throws Exception {
mockCurrentUserForBinderCalls();
// Not mocking service call, so it will return null
UserIdentificationAssociationResponse response = mCarUserService
.getUserIdentificationAssociation(new int[] { 108 });
assertThat(response.isSuccess()).isFalse();
assertThat(response.getValues()).isNull();
assertThat(response.getErrorMessage()).isNull();
}
@Test
public void testGetUserIdentificationAssociation_ok() throws Exception {
UserInfo currentUser = mockCurrentUserForBinderCalls();
int[] types = new int[] { 1, 2, 3 };
int[] values = new int[] { 10, 20, 30 };
mockHalGetUserIdentificationAssociation(currentUser, types, values, "D'OH!");
UserIdentificationAssociationResponse response = mCarUserService
.getUserIdentificationAssociation(types);
assertThat(response.isSuccess()).isTrue();
assertThat(response.getValues()).asList().containsAllOf(10, 20, 30).inOrder();
assertThat(response.getErrorMessage()).isEqualTo("D'OH!");
}
@Test
public void testSetUserIdentificationAssociation_nullTypes() throws Exception {
assertThrows(IllegalArgumentException.class, () -> mCarUserService
.setUserIdentificationAssociation(mAsyncCallTimeoutMs,
null, new int[] {42}, mUserAssociationRespFuture));
}
@Test
public void testSetUserIdentificationAssociation_emptyTypes() throws Exception {
assertThrows(IllegalArgumentException.class, () -> mCarUserService
.setUserIdentificationAssociation(mAsyncCallTimeoutMs,
new int[0], new int[] {42}, mUserAssociationRespFuture));
}
@Test
public void testSetUserIdentificationAssociation_nullValues() throws Exception {
assertThrows(IllegalArgumentException.class, () -> mCarUserService
.setUserIdentificationAssociation(mAsyncCallTimeoutMs,
new int[] {42}, null, mUserAssociationRespFuture));
}
@Test
public void testSetUserIdentificationAssociation_sizeMismatch() throws Exception {
assertThrows(IllegalArgumentException.class, () -> mCarUserService
.setUserIdentificationAssociation(mAsyncCallTimeoutMs,
new int[] {1}, new int[] {2, 2}, mUserAssociationRespFuture));
}
@Test
public void testSetUserIdentificationAssociation_nullFuture() throws Exception {
assertThrows(IllegalArgumentException.class, () -> mCarUserService
.setUserIdentificationAssociation(mAsyncCallTimeoutMs,
new int[] {42}, new int[] {42}, null));
}
@Test
public void testSetUserIdentificationAssociation_noPermission() throws Exception {
mockManageUsersPermission(android.Manifest.permission.MANAGE_USERS, false);
assertThrows(SecurityException.class, () -> mCarUserService
.setUserIdentificationAssociation(mAsyncCallTimeoutMs,
new int[] {42}, new int[] {42}, mUserAssociationRespFuture));
}
@Test
public void testSetUserIdentificationAssociation_noCurrentUser() throws Exception {
// Should fail because we're not mocking UserManager.getUserInfo() to set the flag
assertThrows(IllegalArgumentException.class, () -> mCarUserService
.setUserIdentificationAssociation(mAsyncCallTimeoutMs,
new int[] {42}, new int[] {42}, mUserAssociationRespFuture));
}
@Test
public void testSetUserIdentificationAssociation_halFailedWithErrorMessage() throws Exception {
mockCurrentUserForBinderCalls();
mockHalSetUserIdentificationAssociationFailure("D'OH!");
int[] types = new int[] { 1, 2, 3 };
int[] values = new int[] { 10, 20, 30 };
mCarUserService.setUserIdentificationAssociation(mAsyncCallTimeoutMs, types, values,
mUserAssociationRespFuture);
UserIdentificationAssociationResponse response = getUserAssociationRespResult();
assertThat(response.isSuccess()).isFalse();
assertThat(response.getValues()).isNull();
assertThat(response.getErrorMessage()).isEqualTo("D'OH!");
}
@Test
public void testSetUserIdentificationAssociation_halFailedWithoutErrorMessage()
throws Exception {
mockCurrentUserForBinderCalls();
mockHalSetUserIdentificationAssociationFailure(/* errorMessage= */ null);
int[] types = new int[] { 1, 2, 3 };
int[] values = new int[] { 10, 20, 30 };
mCarUserService.setUserIdentificationAssociation(mAsyncCallTimeoutMs, types, values,
mUserAssociationRespFuture);
UserIdentificationAssociationResponse response = getUserAssociationRespResult();
assertThat(response.isSuccess()).isFalse();
assertThat(response.getValues()).isNull();
assertThat(response.getErrorMessage()).isNull();
}
@Test
public void testSetUserIdentificationAssociation_ok() throws Exception {
UserInfo currentUser = mockCurrentUserForBinderCalls();
int[] types = new int[] { 1, 2, 3 };
int[] values = new int[] { 10, 20, 30 };
mockHalSetUserIdentificationAssociationSuccess(currentUser, types, values, "D'OH!");
mCarUserService.setUserIdentificationAssociation(mAsyncCallTimeoutMs, types, values,
mUserAssociationRespFuture);
UserIdentificationAssociationResponse response = getUserAssociationRespResult();
assertThat(response.isSuccess()).isTrue();
assertThat(response.getValues()).asList().containsAllOf(10, 20, 30).inOrder();
assertThat(response.getErrorMessage()).isEqualTo("D'OH!");
}
@Test
public void testUserMetric_SendEvent() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
sendUserSwitchingEvent(mAdminUser.id, mRegularUser.id);
verify(mUserMetrics).onEvent(CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING,
DEFAULT_LIFECYCLE_TIMESTAMP, mAdminUser.id, mRegularUser.id);
}
@Test
public void testUserMetric_FirstUnlock() {
int userId = 99;
long timestampMs = 0;
long duration = 153;
int halResponseTime = 5;
mCarUserService.onFirstUserUnlocked(userId, timestampMs, duration, halResponseTime);
verify(mUserMetrics).logFirstUnlockedUser(userId, timestampMs, duration, halResponseTime);
}
@NonNull
private UserSwitchResult getUserSwitchResult() throws Exception {
return getResult(mUserSwitchFuture);
}
@NonNull
private UserCreationResult getUserCreationResult() throws Exception {
return getResult(mUserCreationFuture);
}
@NonNull
private UserIdentificationAssociationResponse getUserAssociationRespResult()
throws Exception {
return getResult(mUserAssociationRespFuture);
}
/**
* This method must be called for cases where the service infers the user id of the caller
* using Binder - it's not worth the effort of mocking such (native) calls.
*/
@NonNull
private UserInfo mockCurrentUserForBinderCalls() {
int currentUserId = ActivityManager.getCurrentUser();
Log.d(TAG, "testetUserIdentificationAssociation_ok(): current user is " + currentUserId);
UserInfo currentUser = mockUmGetUserInfo(mMockedUserManager, currentUserId,
UserInfo.FLAG_ADMIN);
return currentUser;
}
/**
* Mock calls that generate a {@code UsersInfo}.
*/
private void mockExistingUsersAndCurrentUser(@NonNull UserInfo user)
throws Exception {
mockExistingUsers();
mockCurrentUser(user);
}
private void mockExistingUsers() {
mockUmGetUsers(mMockedUserManager, mExistingUsers);
for (UserInfo user : mExistingUsers) {
AndroidMockitoHelper.mockUmGetUserInfo(mMockedUserManager, user);
}
}
private void mockCurrentUser(@NonNull UserInfo user) throws Exception {
when(mMockedIActivityManager.getCurrentUser()).thenReturn(user);
mockGetCurrentUser(user.id);
}
private void mockAmSwitchUser(@NonNull UserInfo user, boolean result) throws Exception {
when(mMockedIActivityManager.switchUser(user.id)).thenReturn(result);
}
private void mockGetInitialInfo(@UserIdInt int currentUserId,
@NonNull InitialUserInfoResponse response) {
UsersInfo usersInfo = newUsersInfo(currentUserId);
doAnswer((invocation) -> {
Log.d(TAG, "Answering " + invocation + " with " + response);
@SuppressWarnings("unchecked")
HalCallback<InitialUserInfoResponse> callback =
(HalCallback<InitialUserInfoResponse>) invocation.getArguments()[3];
callback.onResponse(HalCallback.STATUS_OK, response);
return null;
}).when(mUserHal).getInitialUserInfo(eq(mGetUserInfoRequestType), eq(mAsyncCallTimeoutMs),
eq(usersInfo), notNull());
}
private void mockIsHeadlessSystemUser(@UserIdInt int userId, boolean mode) {
doReturn(mode).when(() -> UserHelper.isHeadlessSystemUser(userId));
}
private void mockHalSwitch(@UserIdInt int currentUserId, @NonNull UserInfo androidTargetUser,
@Nullable SwitchUserResponse response) {
mockHalSwitch(currentUserId, HalCallback.STATUS_OK, response, androidTargetUser);
}
@NonNull
private ArgumentCaptor<CreateUserRequest> mockHalCreateUser(
@HalCallbackStatus int callbackStatus, int responseStatus) {
CreateUserResponse response = new CreateUserResponse();
response.status = responseStatus;
ArgumentCaptor<CreateUserRequest> captor = ArgumentCaptor.forClass(CreateUserRequest.class);
doAnswer((invocation) -> {
Log.d(TAG, "Answering " + invocation + " with " + response);
@SuppressWarnings("unchecked")
HalCallback<CreateUserResponse> callback =
(HalCallback<CreateUserResponse>) invocation.getArguments()[2];
callback.onResponse(callbackStatus, response);
return null;
}).when(mUserHal).createUser(captor.capture(), eq(mAsyncCallTimeoutMs), notNull());
return captor;
}
private void mockHalCreateUserThrowsRuntimeException() {
doThrow(new RuntimeException("D'OH!"))
.when(mUserHal).createUser(any(), eq(mAsyncCallTimeoutMs), notNull());
}
private void mockCallerUid(int uid, boolean returnCorrectUid) throws Exception {
String packageName = "packageName";
String className = "className";
when(mMockedResources.getString(anyInt())).thenReturn(packageName + "/" + className);
when(mMockContext.createContextAsUser(any(), anyInt())).thenReturn(mMockContext);
when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
if (returnCorrectUid) {
when(mPackageManager.getPackageUid(any(), anyInt())).thenReturn(uid);
} else {
when(mPackageManager.getPackageUid(any(), anyInt())).thenReturn(uid + 1);
}
}
private BlockingAnswer<Void> mockHalSwitchLateResponse(@UserIdInt int currentUserId,
@NonNull UserInfo androidTargetUser, @Nullable SwitchUserResponse response) {
android.hardware.automotive.vehicle.V2_0.UserInfo halTargetUser =
new android.hardware.automotive.vehicle.V2_0.UserInfo();
halTargetUser.userId = androidTargetUser.id;
halTargetUser.flags = UserHalHelper.convertFlags(androidTargetUser);
UsersInfo usersInfo = newUsersInfo(currentUserId);
SwitchUserRequest request = new SwitchUserRequest();
request.targetUser = halTargetUser;
request.usersInfo = usersInfo;
BlockingAnswer<Void> blockingAnswer = BlockingAnswer.forVoidReturn(10_000, (invocation) -> {
Log.d(TAG, "Answering " + invocation + " with " + response);
@SuppressWarnings("unchecked")
HalCallback<SwitchUserResponse> callback = (HalCallback<SwitchUserResponse>) invocation
.getArguments()[2];
callback.onResponse(HalCallback.STATUS_OK, response);
});
doAnswer(blockingAnswer).when(mUserHal).switchUser(eq(request), eq(mAsyncCallTimeoutMs),
notNull());
return blockingAnswer;
}
private void mockHalSwitch(@UserIdInt int currentUserId,
@HalCallback.HalCallbackStatus int callbackStatus,
@Nullable SwitchUserResponse response, @NonNull UserInfo androidTargetUser) {
android.hardware.automotive.vehicle.V2_0.UserInfo halTargetUser =
new android.hardware.automotive.vehicle.V2_0.UserInfo();
halTargetUser.userId = androidTargetUser.id;
halTargetUser.flags = UserHalHelper.convertFlags(androidTargetUser);
UsersInfo usersInfo = newUsersInfo(currentUserId);
SwitchUserRequest request = new SwitchUserRequest();
request.targetUser = halTargetUser;
request.usersInfo = usersInfo;
doAnswer((invocation) -> {
Log.d(TAG, "Answering " + invocation + " with " + response);
@SuppressWarnings("unchecked")
HalCallback<SwitchUserResponse> callback =
(HalCallback<SwitchUserResponse>) invocation.getArguments()[2];
callback.onResponse(callbackStatus, response);
return null;
}).when(mUserHal).switchUser(eq(request), eq(mAsyncCallTimeoutMs), notNull());
}
private void mockHalGetUserIdentificationAssociation(@NonNull UserInfo user,
@NonNull int[] types, @NonNull int[] values, @Nullable String errorMessage) {
assertWithMessage("mismatch on number of types and values").that(types.length)
.isEqualTo(values.length);
UserIdentificationResponse response = new UserIdentificationResponse();
response.numberAssociation = types.length;
response.errorMessage = errorMessage;
for (int i = 0; i < types.length; i++) {
UserIdentificationAssociation association = new UserIdentificationAssociation();
association.type = types[i];
association.value = values[i];
response.associations.add(association);
}
when(mUserHal.getUserAssociation(isUserIdentificationGetRequest(user, types)))
.thenReturn(response);
}
private void mockHalSetUserIdentificationAssociationSuccess(@NonNull UserInfo user,
@NonNull int[] types, @NonNull int[] values, @Nullable String errorMessage) {
assertWithMessage("mismatch on number of types and values").that(types.length)
.isEqualTo(values.length);
UserIdentificationResponse response = new UserIdentificationResponse();
response.numberAssociation = types.length;
response.errorMessage = errorMessage;
for (int i = 0; i < types.length; i++) {
UserIdentificationAssociation association = new UserIdentificationAssociation();
association.type = types[i];
association.value = values[i];
response.associations.add(association);
}
doAnswer((invocation) -> {
Log.d(TAG, "Answering " + invocation + " with " + response);
@SuppressWarnings("unchecked")
UserIdentificationSetRequest request =
(UserIdentificationSetRequest) invocation.getArguments()[1];
assertWithMessage("Wrong user on %s", request)
.that(request.userInfo.userId)
.isEqualTo(user.id);
assertWithMessage("Wrong flags on %s", request)
.that(UserHalHelper.toUserInfoFlags(request.userInfo.flags))
.isEqualTo(user.flags);
@SuppressWarnings("unchecked")
HalCallback<UserIdentificationResponse> callback =
(HalCallback<UserIdentificationResponse>) invocation.getArguments()[2];
callback.onResponse(HalCallback.STATUS_OK, response);
return null;
}).when(mUserHal).setUserAssociation(eq(mAsyncCallTimeoutMs), notNull(), notNull());
}
private void mockHalSetUserIdentificationAssociationFailure(@NonNull String errorMessage) {
UserIdentificationResponse response = new UserIdentificationResponse();
response.errorMessage = errorMessage;
doAnswer((invocation) -> {
Log.d(TAG, "Answering " + invocation + " with " + response);
@SuppressWarnings("unchecked")
HalCallback<UserIdentificationResponse> callback =
(HalCallback<UserIdentificationResponse>) invocation.getArguments()[2];
callback.onResponse(HalCallback.STATUS_WRONG_HAL_RESPONSE, response);
return null;
}).when(mUserHal).setUserAssociation(eq(mAsyncCallTimeoutMs), notNull(), notNull());
}
private void mockManageUsersPermission(String permission, boolean granted) {
int result;
if (granted) {
result = android.content.pm.PackageManager.PERMISSION_GRANTED;
} else {
result = android.content.pm.PackageManager.PERMISSION_DENIED;
}
doReturn(result).when(() -> ActivityManager.checkComponentPermission(eq(permission),
anyInt(), anyInt(), eq(true)));
}
/**
* Asserts a {@link UsersInfo} that was created based on {@link #mockCurrentUsers(UserInfo)}.
*/
private void assertDefaultUsersInfo(UsersInfo actual, UserInfo currentUser) {
// TODO(b/150413515): figure out why this method is not called in other places
assertThat(actual).isNotNull();
assertSameUser(actual.currentUser, currentUser);
assertThat(actual.numberUsers).isEqualTo(mExistingUsers.size());
for (int i = 0; i < actual.numberUsers; i++) {
assertSameUser(actual.existingUsers.get(i), mExistingUsers.get(i));
}
}
private void assertSameUser(android.hardware.automotive.vehicle.V2_0.UserInfo halUser,
UserInfo androidUser) {
assertThat(halUser.userId).isEqualTo(androidUser.id);
assertWithMessage("flags mismatch: hal=%s, android=%s",
UserInfo.flagsToString(androidUser.flags),
UserHalHelper.userFlagsToString(halUser.flags))
.that(halUser.flags).isEqualTo(UserHalHelper.convertFlags(androidUser));
}
private void verifyUserRemoved(@UserIdInt int userId) {
verify(mMockedUserManager).removeUser(userId);
}
private void verifyNoUserRemoved() {
verify(mMockedUserManager, never()).removeUser(anyInt());
}
@NonNull
private UsersInfo newUsersInfo(@UserIdInt int currentUserId) {
UsersInfo infos = new UsersInfo();
infos.numberUsers = mExistingUsers.size();
boolean foundCurrentUser = false;
for (UserInfo info : mExistingUsers) {
android.hardware.automotive.vehicle.V2_0.UserInfo existingUser =
new android.hardware.automotive.vehicle.V2_0.UserInfo();
int flags = UserFlags.NONE;
if (info.id == UserHandle.USER_SYSTEM) {
flags |= UserFlags.SYSTEM;
}
if (info.isAdmin()) {
flags |= UserFlags.ADMIN;
}
if (info.isGuest()) {
flags |= UserFlags.GUEST;
}
if (info.isEphemeral()) {
flags |= UserFlags.EPHEMERAL;
}
existingUser.userId = info.id;
existingUser.flags = flags;
if (info.id == currentUserId) {
foundCurrentUser = true;
infos.currentUser.userId = info.id;
infos.currentUser.flags = flags;
}
infos.existingUsers.add(existingUser);
}
Preconditions.checkArgument(foundCurrentUser,
"no user with id " + currentUserId + " on " + mExistingUsers);
return infos;
}
private void assertUserId(@NonNull Bundle resultData, int expectedUserId) {
int actualUserId = resultData.getInt(CarUserService.BUNDLE_USER_ID);
assertWithMessage("wrong user id on bundle extra %s", CarUserService.BUNDLE_USER_ID)
.that(actualUserId).isEqualTo(expectedUserId);
}
private void assertNoUserId(@NonNull Bundle resultData) {
assertNoExtra(resultData, CarUserService.BUNDLE_USER_ID);
}
private void assertUserFlags(@NonNull Bundle resultData, int expectedUserFlags) {
int actualUserFlags = resultData.getInt(CarUserService.BUNDLE_USER_FLAGS);
assertWithMessage("wrong user flags on bundle extra %s", CarUserService.BUNDLE_USER_FLAGS)
.that(actualUserFlags).isEqualTo(expectedUserFlags);
}
private void assertNoUserFlags(@NonNull Bundle resultData) {
assertNoExtra(resultData, CarUserService.BUNDLE_USER_FLAGS);
}
private void assertUserName(@NonNull Bundle resultData, @NonNull String expectedName) {
String actualName = resultData.getString(CarUserService.BUNDLE_USER_NAME);
assertWithMessage("wrong user name on bundle extra %s",
CarUserService.BUNDLE_USER_FLAGS).that(actualName).isEqualTo(expectedName);
}
private void assertNoUserName(@NonNull Bundle resultData) {
assertNoExtra(resultData, CarUserService.BUNDLE_USER_NAME);
}
private void assertNoExtra(@NonNull Bundle resultData, @NonNull String extra) {
Object value = resultData.get(extra);
assertWithMessage("should not have extra %s", extra).that(value).isNull();
}
private void assertInitialInfoAction(@NonNull Bundle resultData, int expectedAction) {
int actualAction = resultData.getInt(CarUserService.BUNDLE_INITIAL_INFO_ACTION);
assertWithMessage("wrong request type on bundle extra %s",
CarUserService.BUNDLE_INITIAL_INFO_ACTION).that(actualAction)
.isEqualTo(expectedAction);
}
private void assertInitialInfoUserLocales(Bundle resultData, String expectedLocales) {
String actualLocales = resultData.getString(CarUserService.BUNDLE_USER_LOCALES);
assertWithMessage("wrong locales on bundle extra %s",
CarUserService.BUNDLE_USER_LOCALES).that(actualLocales)
.isEqualTo(expectedLocales);
}
private void assertNoPostSwitch() {
verify(mUserHal, never()).postSwitchResponse(any());
}
private void assertPostSwitch(int requestId, int currentId, int targetId) {
verify(mUserHal).postSwitchResponse(isSwitchUserRequest(requestId, currentId, targetId));
}
private void assertHalSwitch(int currentId, int targetId) {
verify(mUserHal).switchUser(isSwitchUserRequest(0, currentId, targetId),
eq(mAsyncCallTimeoutMs), any());
}
private void assertNoHalUserCreation() {
verify(mUserHal, never()).createUser(any(), eq(mAsyncCallTimeoutMs), any());
}
private void assertHalRemove(int currentId, int removeUserId) {
ArgumentCaptor<RemoveUserRequest> request =
ArgumentCaptor.forClass(RemoveUserRequest.class);
verify(mUserHal).removeUser(request.capture());
assertThat(request.getValue().removedUserInfo.userId).isEqualTo(removeUserId);
assertThat(request.getValue().usersInfo.currentUser.userId).isEqualTo(currentId);
}
@NonNull
private static SwitchUserRequest isSwitchUserRequest(int requestId,
@UserIdInt int currentUserId, @UserIdInt int targetUserId) {
return argThat(new SwitchUserRequestMatcher(requestId, currentUserId, targetUserId));
}
static final class FakeCarOccupantZoneService {
private final SparseArray<Integer> mZoneUserMap = new SparseArray<Integer>();
private final CarUserService.ZoneUserBindingHelper mZoneUserBindigHelper =
new CarUserService.ZoneUserBindingHelper() {
@Override
@NonNull
public List<OccupantZoneInfo> getOccupantZones(
@OccupantTypeEnum int occupantType) {
return null;
}
@Override
public boolean assignUserToOccupantZone(@UserIdInt int userId, int zoneId) {
if (mZoneUserMap.get(zoneId) != null) {
return false;
}
mZoneUserMap.put(zoneId, userId);
return true;
}
@Override
public boolean unassignUserFromOccupantZone(@UserIdInt int userId) {
for (int index = 0; index < mZoneUserMap.size(); index++) {
if (mZoneUserMap.valueAt(index) == userId) {
mZoneUserMap.removeAt(index);
break;
}
}
return true;
}
@Override
public boolean isPassengerDisplayAvailable() {
return true;
}
};
FakeCarOccupantZoneService(CarUserService carUserService) {
carUserService.setZoneUserBindingHelper(mZoneUserBindigHelper);
}
}
private void sendUserLifecycleEvent(@UserIdInt int fromUserId, @UserIdInt int toUserId,
@UserLifecycleEventType int eventType) {
mCarUserService.onUserLifecycleEvent(eventType, DEFAULT_LIFECYCLE_TIMESTAMP, fromUserId,
toUserId);
}
private void sendUserUnlockedEvent(@UserIdInt int userId) {
sendUserLifecycleEvent(/* fromUser */ 0, userId,
CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED);
}
private void sendUserSwitchingEvent(@UserIdInt int fromUserId, @UserIdInt int toUserId) {
sendUserLifecycleEvent(fromUserId, toUserId,
CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING);
}
@NonNull
private static UserIdentificationGetRequest isUserIdentificationGetRequest(
@NonNull UserInfo user, @NonNull int[] types) {
return argThat(new UserIdentificationGetRequestMatcher(user, types));
}
private static class UserIdentificationGetRequestMatcher implements
ArgumentMatcher<UserIdentificationGetRequest> {
private static final String MY_TAG =
UserIdentificationGetRequestMatcher.class.getSimpleName();
private final @UserIdInt int mUserId;
private final int mHalFlags;
private final @NonNull int[] mTypes;
private UserIdentificationGetRequestMatcher(@NonNull UserInfo user, @NonNull int[] types) {
mUserId = user.id;
mHalFlags = UserHalHelper.convertFlags(user);
mTypes = types;
}
@Override
public boolean matches(UserIdentificationGetRequest argument) {
if (argument == null) {
Log.w(MY_TAG, "null argument");
return false;
}
if (argument.userInfo.userId != mUserId) {
Log.w(MY_TAG, "wrong user id on " + argument + "; expected " + mUserId);
return false;
}
if (argument.userInfo.flags != mHalFlags) {
Log.w(MY_TAG, "wrong flags on " + argument + "; expected " + mHalFlags);
return false;
}
if (argument.numberAssociationTypes != mTypes.length) {
Log.w(MY_TAG, "wrong numberAssociationTypes on " + argument + "; expected "
+ mTypes.length);
return false;
}
if (argument.associationTypes.size() != mTypes.length) {
Log.w(MY_TAG, "wrong associationTypes size on " + argument + "; expected "
+ mTypes.length);
return false;
}
for (int i = 0; i < mTypes.length; i++) {
if (argument.associationTypes.get(i) != mTypes[i]) {
Log.w(MY_TAG, "wrong association type on index " + i + " on " + argument
+ "; expected types: " + Arrays.toString(mTypes));
return false;
}
}
Log.d(MY_TAG, "Good News, Everyone! " + argument + " matches " + this);
return true;
}
@Override
public String toString() {
return "isUserIdentificationGetRequest(userId=" + mUserId + ", flags="
+ UserHalHelper.userFlagsToString(mHalFlags) + ", types="
+ Arrays.toString(mTypes) + ")";
}
}
private static final class SwitchUserRequestMatcher
implements ArgumentMatcher<SwitchUserRequest> {
private static final String MY_TAG = UsersInfo.class.getSimpleName();
private final int mRequestId;
private final @UserIdInt int mCurrentUserId;
private final @UserIdInt int mTargetUserId;
private SwitchUserRequestMatcher(int requestId, @UserIdInt int currentUserId,
@UserIdInt int targetUserId) {
mCurrentUserId = currentUserId;
mTargetUserId = targetUserId;
mRequestId = requestId;
}
@Override
public boolean matches(SwitchUserRequest argument) {
if (argument == null) {
Log.w(MY_TAG, "null argument");
return false;
}
if (argument.usersInfo.currentUser.userId != mCurrentUserId) {
Log.w(MY_TAG,
"wrong current user id on " + argument + "; expected " + mCurrentUserId);
return false;
}
if (argument.targetUser.userId != mTargetUserId) {
Log.w(MY_TAG,
"wrong target user id on " + argument + "; expected " + mTargetUserId);
return false;
}
if (argument.requestId != mRequestId) {
Log.w(MY_TAG,
"wrong request Id on " + argument + "; expected " + mTargetUserId);
return false;
}
Log.d(MY_TAG, "Good News, Everyone! " + argument + " matches " + this);
return true;
}
}
}