blob: da9f7c9094996b09c7ce86300481445138a8ea46 [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.content.pm.UserInfo.FLAG_EPHEMERAL;
import static android.content.pm.UserInfo.FLAG_GUEST;
import static com.google.common.truth.Truth.assertThat;
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.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.car.settings.CarSettings;
import android.car.userlib.CarUserManagerHelper;
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
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/>
*/
@RunWith(AndroidJUnit4.class)
public class CarUserServiceTest {
private static final int NO_USER_INFO_FLAGS = 0;
@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock private Context mMockContext;
@Mock private Context mApplicationContext;
@Mock private LocationManager mLocationManager;
@Mock private CarUserManagerHelper mMockedCarUserManagerHelper;
@Mock private IActivityManager mMockedIActivityManager;
@Mock private UserManager mMockedUserManager;
@Mock private Resources mMockedResources;
@Mock private Drawable mMockedDrawable;
private CarUserService mCarUserService;
private boolean mUser0TaskExecuted;
/**
* 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();
mCarUserService =
new CarUserService(
mMockContext,
mMockedCarUserManagerHelper,
mMockedUserManager,
mMockedIActivityManager,
3);
// Restore default value at the beginning of each test.
putSettingsInt(CarSettings.Global.DEFAULT_USER_RESTRICTIONS_SET, 0);
}
/**
* Test that the {@link CarUserService} does set the disable modify account permission for
* user 0 upon user 0 unlock when user 0 is headless.
*/
@Test
public void testDisableModifyAccountsForHeadlessSystemUserOnFirstRun() {
mCarUserService.setUserLockStatus(UserHandle.USER_SYSTEM, true);
verify(mMockedUserManager)
.setUserRestriction(
UserManager.DISALLOW_MODIFY_ACCOUNTS,
true,
UserHandle.of(UserHandle.USER_SYSTEM));
}
/**
* Test that the {@link CarUserService} does not set restrictions on user 0 if they have already
* been set.
*/
@Test
public void testDoesNotSetSystemUserRestrictions_IfRestrictionsAlreadySet() {
putSettingsInt(CarSettings.Global.DEFAULT_USER_RESTRICTIONS_SET, 1);
mCarUserService.setUserLockStatus(UserHandle.USER_SYSTEM, true);
verify(mMockedUserManager, never())
.setUserRestriction(
UserManager.DISALLOW_MODIFY_ACCOUNTS,
true,
UserHandle.of(UserHandle.USER_SYSTEM));
}
/**
* Test that the {@link CarUserService} disables the location service for headless user 0 upon
* first run.
*/
@Test
public void testDisableLocationForHeadlessSystemUserOnFirstRun() {
mCarUserService.setUserLockStatus(UserHandle.USER_SYSTEM, true);
verify(mLocationManager).setLocationEnabledForUser(
/* enabled= */ false, UserHandle.of(UserHandle.USER_SYSTEM));
}
/**
* Test that the {@link CarUserService} updates last active user on user switch.
*/
@Test
public void testLastActiveUserUpdatedOnUserSwitch() {
int lastActiveUserId = 99;
UserInfo persistentUser = new UserInfo(lastActiveUserId, "persistent user",
NO_USER_INFO_FLAGS);
doReturn(persistentUser).when(mMockedUserManager).getUserInfo(lastActiveUserId);
mCarUserService.onSwitchUser(lastActiveUserId);
verify(mMockedCarUserManagerHelper).setLastActiveUser(lastActiveUserId);
}
/**
* Test that the {@link CarUserService} sets default guest restrictions on first boot.
*/
@Test
public void testInitializeGuestRestrictions_IfNotAlreadySet() {
mCarUserService.setUserLockStatus(UserHandle.USER_SYSTEM, true);
assertThat(getSettingsInt(CarSettings.Global.DEFAULT_USER_RESTRICTIONS_SET)).isEqualTo(1);
}
/**
* Test that the {@link CarUserService} does not set restrictions after they have been set once.
*/
@Test
public void test_DoesNotInitializeGuestRestrictions_IfAlreadySet() {
putSettingsInt(CarSettings.Global.DEFAULT_USER_RESTRICTIONS_SET, 1);
mCarUserService.setUserLockStatus(UserHandle.USER_SYSTEM, true);
verify(mMockedUserManager, never()).setDefaultGuestRestrictions(any(Bundle.class));
}
@Test
public void testRunOnUser0UnlockImmediate() {
mUser0TaskExecuted = false;
mCarUserService.setUserLockStatus(UserHandle.USER_SYSTEM, true);
mCarUserService.runOnUser0Unlock(() -> {
mUser0TaskExecuted = true;
});
assertTrue(mUser0TaskExecuted);
}
@Test
public void testRunOnUser0UnlockLater() {
mUser0TaskExecuted = false;
mCarUserService.runOnUser0Unlock(() -> {
mUser0TaskExecuted = true;
});
assertFalse(mUser0TaskExecuted);
mCarUserService.setUserLockStatus(UserHandle.USER_SYSTEM, true);
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);
doReturn(user1).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
mCarUserService.setUserLockStatus(UserHandle.USER_SYSTEM, true);
// user 0 should never go to that list.
assertTrue(mCarUserService.getBackgroundUsersToRestart().isEmpty());
mCarUserService.setUserLockStatus(user1, true);
assertEquals(new Integer[]{user1},
mCarUserService.getBackgroundUsersToRestart().toArray());
// user 2 background, ignore in restart list
mCarUserService.setUserLockStatus(user2, true);
mCarUserService.setUserLockStatus(user1, false);
assertEquals(new Integer[]{user1},
mCarUserService.getBackgroundUsersToRestart().toArray());
doReturn(user3).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
mCarUserService.setUserLockStatus(user3, true);
mCarUserService.setUserLockStatus(user2, false);
assertEquals(new Integer[]{user3, user1},
mCarUserService.getBackgroundUsersToRestart().toArray());
doReturn(user4Guest).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
mCarUserService.setUserLockStatus(user4Guest, true);
mCarUserService.setUserLockStatus(user3, false);
assertEquals(new Integer[]{user3, user1},
mCarUserService.getBackgroundUsersToRestart().toArray());
doReturn(user5).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
mCarUserService.setUserLockStatus(user5, true);
mCarUserService.setUserLockStatus(user4Guest, false);
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);
doReturn(user1).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
mCarUserService.setUserLockStatus(UserHandle.USER_SYSTEM, true);
mCarUserService.setUserLockStatus(user1, true);
doReturn(user2).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
mCarUserService.setUserLockStatus(user2, true);
mCarUserService.setUserLockStatus(user1, false);
doReturn(user3).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
mCarUserService.setUserLockStatus(user3, true);
mCarUserService.setUserLockStatus(user2, false);
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());
mCarUserService.setUserLockStatus(user2, true);
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());
mCarUserService.setUserLockStatus(user2, false);
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;
UserInfo user1Info = new UserInfo(user1, "user1", NO_USER_INFO_FLAGS);
doReturn(user1).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
assertFalse(mCarUserService.stopBackgroundUser(UserHandle.USER_SYSTEM));
}
@Test
public void testCreateAdminDriver_IfCurrentUserIsAdminUser() {
doReturn(true).when(mMockedUserManager).isSystemUser();
String userName = "testUser";
UserInfo userInfo = new UserInfo();
doReturn(userInfo).when(mMockedUserManager).createUser(userName, UserInfo.FLAG_ADMIN);
assertEquals(userInfo, mCarUserService.createDriver(userName, true));
}
@Test
public void testCreateAdminDriver_IfCurrentUserIsNotSystemUser() {
doReturn(false).when(mMockedUserManager).isSystemUser();
assertEquals(null, mCarUserService.createDriver("testUser", true));
}
@Test
public void testCreateNonAdminDriver() {
String userName = "testUser";
UserInfo userInfo = new UserInfo();
doReturn(userInfo).when(mMockedCarUserManagerHelper).createNewNonAdminUser(userName);
assertEquals(userInfo, mCarUserService.createDriver(userName, false));
}
@Test
public void testCreateNonAdminDriver_IfMaximumUserAlreadyCreated() {
String userName = "testUser";
doReturn(null).when(mMockedUserManager).createUser(userName, NO_USER_INFO_FLAGS);
assertEquals(null, mCarUserService.createDriver(userName, false));
}
@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(UserInfo.FLAG_MANAGED_PROFILE), 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(UserInfo.FLAG_MANAGED_PROFILE), 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 RemoteException {
int currentId = 11;
int targetId = 12;
UserInfo userInfo = new UserInfo(currentId, "test11", NO_USER_INFO_FLAGS);
doReturn(currentId).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
doReturn(true).when(mMockedIActivityManager).switchUser(targetId);
doReturn(false).when(mMockedUserManager)
.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH);
assertTrue(mCarUserService.switchDriver(targetId));
}
@Test
public void testSwitchDriver_IfUserSwitchIsNotAllowed() throws RemoteException {
int currentId = 11;
int targetId = 12;
UserInfo userInfo = new UserInfo(currentId, "test11", NO_USER_INFO_FLAGS);
doReturn(currentId).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
doReturn(true).when(mMockedIActivityManager).switchUser(targetId);
doReturn(UserManager.SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED).when(mMockedUserManager)
.getUserSwitchability();
assertFalse(mCarUserService.switchDriver(targetId));
}
@Test
public void testSwitchDriver_IfSwitchedToCurrentUser() throws RemoteException {
UserInfo userInfo = new UserInfo(11, "test11", NO_USER_INFO_FLAGS);
doReturn(userInfo.id).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
doReturn(false).when(mMockedUserManager)
.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH);
assertTrue(mCarUserService.switchDriver(11));
}
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(0, "test0", UserInfo.FLAG_SYSTEM),
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)
));
// Parent: test10, child: test12
associateParentChild(users.get(1), users.get(3));
// Parent: test13, child: test17
associateParentChild(users.get(4), users.get(8));
// Parent: test13, child: test18
associateParentChild(users.get(4), users.get(9));
return users;
}
@Test
public void testGetAllPossibleDrivers() {
Set<Integer> expected = new HashSet<Integer>(Arrays.asList(10, 11, 13, 14));
doReturn(prepareUserList()).when(mMockedUserManager).getUsers(true);
for (UserInfo user : mCarUserService.getAllDrivers()) {
assertTrue(expected.contains(user.id));
expected.remove(user.id);
}
assertEquals(0, expected.size());
}
@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, 18)));
}
};
for (int i = 0; i < testCases.size(); i++) {
doReturn(prepareUserList()).when(mMockedUserManager).getUsers(true);
List<UserInfo> passengers = mCarUserService.getPassengers(testCases.keyAt(i));
HashSet<Integer> expected = testCases.valueAt(i);
for (UserInfo user : passengers) {
assertTrue(expected.contains(user.id));
expected.remove(user.id);
}
assertEquals(0, expected.size());
}
}
// TODO(b/139190199): add tests for startPassenger() and stopPassenger().
private void putSettingsInt(String key, int value) {
Settings.Global.putInt(InstrumentationRegistry.getTargetContext().getContentResolver(),
key, value);
}
private int getSettingsInt(String key) {
return Settings.Global.getInt(
InstrumentationRegistry.getTargetContext().getContentResolver(),
key, /* default= */ 0);
}
}