blob: 604adab21098f0d84fa6d81c4589d7f590a20f0d [file] [log] [blame]
/*
* Copyright (C) 2020 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 android.car.cts;
import static com.android.tradefed.device.NativeDevice.INVALID_USER_ID;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
import android.platform.test.annotations.Presubmit;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Tests for pre-created users.
*/
@RunWith(DeviceJUnit4ClassRunner.class)
public final class PreCreateUsersHostTest extends CarHostJUnit4TestCase {
private static int sNumberCreateadUsers;
@Before
public void checkMultiUserSupport() throws Exception {
assumeSupportsMultipleUsers();
}
@After
public void uninstallPackage() throws Exception {
getDevice().uninstallPackage(APP_PKG);
}
/**
* Makes sure an app installed for a regular user is not visible to a pre-created user.
*/
@Presubmit
@Test
public void testAppsAreNotInstalledOnPreCreatedUser() throws Exception {
appsAreNotInstalledOnPreCreatedUserTest(/* isGuest= */ false, /* afterReboot= */ false);
}
/**
* Same as {@link #testAppsAreNotInstalledOnPreCreatedUser()}, but for a guest user.
*/
@Presubmit
@Test
public void testAppsAreNotInstalledOnPreCreatedGuest() throws Exception {
appsAreNotInstalledOnPreCreatedUserTest(/* isGuest= */ true, /* afterReboot= */ false);
}
/**
* Makes sure an app installed for a regular user is not visible to a pre-created user, even
* after the system restarts
*/
@Presubmit
@Test
public void testAppsAreNotInstalledOnPreCreatedUserAfterReboot() throws Exception {
appsAreNotInstalledOnPreCreatedUserTest(/* isGuest= */ false, /* afterReboot= */ true);
}
/**
* Same as {@link #testAppsAreNotInstalledOnPreCreatedUserAfterReboot()}, but for a guest
* user.
*/
@Presubmit
@Test
public void testAppsAreNotInstalledOnPreCreatedGuestAfterReboot() throws Exception {
appsAreNotInstalledOnPreCreatedUserTest(/* isGuest= */ true, /* afterReboot= */ true);
}
private void appsAreNotInstalledOnPreCreatedUserTest(boolean isGuest,
boolean afterReboot) throws Exception {
deletePreCreatedUsers();
requiresExtraUsers(1);
int initialUserId = getCurrentUserId();
int preCreatedUserId = preCreateUser(isGuest);
installPackageAsUser(APP_APK, /* grantPermission= */ false, initialUserId);
assertAppInstalledForUser(APP_PKG, initialUserId);
assertAppNotInstalledForUser(APP_PKG, preCreatedUserId);
if (afterReboot) {
restartSystem();
// Checks again
assertAppInstalledForUser(APP_PKG, initialUserId);
assertAppNotInstalledForUser(APP_PKG, preCreatedUserId);
}
convertPreCreatedUser(isGuest, preCreatedUserId);
assertAppNotInstalledForUser(APP_PKG, preCreatedUserId);
}
/**
* Verifies a pre-created user have same packages as non-precreated users.
*/
@Presubmit
@Test
public void testAppPermissionsPreCreatedUserPackages() throws Exception {
appPermissionsPreCreatedUserPackagesTest(/* isGuest= */ false, /* afterReboot= */ false);
}
/**
* Verifies a pre-created guest have same packages as non-precreated users.
*/
@Presubmit
@Test
public void testAppPermissionsPreCreatedGuestPackages() throws Exception {
appPermissionsPreCreatedUserPackagesTest(/* isGuest= */ true, /* afterReboot= */ false);
}
/**
* Verifies a pre-created user have same packages as non-precreated users.
*/
@Presubmit
@Test
public void testAppPermissionsPreCreatedUserPackagesAfterReboot() throws Exception {
appPermissionsPreCreatedUserPackagesTest(/* isGuest= */ false, /* afterReboot= */ true);
}
/**
* Verifies a pre-created guest have same packages as non-precreated users.
*/
@Presubmit
@Test
public void testAppPermissionsPreCreatedGuestPackagesAfterReboot() throws Exception {
appPermissionsPreCreatedUserPackagesTest(/* isGuest= */ true, /* afterReboot= */ true);
}
private void appPermissionsPreCreatedUserPackagesTest(boolean isGuest, boolean afterReboot)
throws Exception {
deletePreCreatedUsers();
requiresExtraUsers(2);
// Create a normal reference user
int referenceUserId = isGuest
? createGuestUser("PreCreatedUsersTest_Reference_Guest")
: createFullUser("PreCreatedUsersTest_Reference_User");
// Some permissions (e.g. Role permission) are given only after initialization.
switchUser(referenceUserId);
waitUntilUserPermissionsIsReady(/* waitTime= */ 30, referenceUserId);
Map<String, List<String>> refPkgMap = getPackagesAndPermissionsForUser(referenceUserId);
// There can be just one guest by default, so remove it now otherwise
// convertPreCreatedUser() below will fail
if (isGuest && !afterReboot) {
removeUser(referenceUserId);
}
int preCreatedUserId = preCreateUser(isGuest);
if (afterReboot) {
restartSystem();
}
convertPreCreatedUser(isGuest, preCreatedUserId);
// Some permissions (e.g. Role permission) are given only after initialization.
switchUser(preCreatedUserId);
waitUntilUserPermissionsIsReady(/* waitTime= */ 20, preCreatedUserId);
Map<String, List<String>> actualPkgMap = getPackagesAndPermissionsForUser(preCreatedUserId);
List<String> errors = new ArrayList<>();
for (String pkg: refPkgMap.keySet()) {
addError(errors, () ->
assertWithMessage("permissions state mismatch for %s", pkg)
.that(actualPkgMap.get(pkg))
.isEqualTo(refPkgMap.get(pkg)));
}
if (!errors.isEmpty()) {
// if there are errors, wait for some more time and check again.
waitUntilUserPermissionsIsReady(/* waitTime= */ 20, preCreatedUserId);
Map<String, List<String>> actualPkgMap2 = getPackagesAndPermissionsForUser(
preCreatedUserId);
errors = new ArrayList<>();
for (String pkg : refPkgMap.keySet()) {
addError(errors, () -> assertWithMessage("permissions state mismatch for %s", pkg)
.that(actualPkgMap2.get(pkg))
.isEqualTo(refPkgMap.get(pkg)));
}
}
assertWithMessage("found %s error", errors.size()).that(errors).isEmpty();
}
private void addError(List<String> error, Runnable r) {
try {
r.run();
} catch (Throwable t) {
error.add(t.getMessage());
}
}
private void assertHasPreCreatedUser(int userId) throws Exception {
List<Integer> existingIds = getPreCreatedUsers();
CLog.d("assertHasPreCreatedUser(%d): pool=%s", userId, existingIds);
assertWithMessage("pre-created user not found").that(existingIds).contains(userId);
}
private List<Integer> getPreCreatedUsers() throws Exception {
return onAllUsers((allUsers) -> allUsers.stream()
.filter((u) -> u.otherState.contains("(pre-created)")
&& !u.flags.contains("DISABLED"))
.map((u) -> u.id).collect(Collectors.toList()));
}
private int preCreateUser(boolean isGuest) throws Exception {
return executeAndParseCommand((output) -> {
int userId = INVALID_USER_ID;
if (output.startsWith("Success")) {
try {
userId = Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim());
CLog.i("Pre-created user with id %d; waiting until it's initialized", userId);
markUserForRemovalAfterTest(userId);
waitForUserInitialized(userId);
assertHasPreCreatedUser(userId);
waitUntilUserDataIsPersisted(userId);
} catch (Exception e) {
CLog.e("Exception pre-creating %s: %s", (isGuest ? "guest" : "user"), e);
}
}
if (userId == INVALID_USER_ID) {
throw new IllegalStateException("failed to pre-create user");
}
return userId;
}, "pm create-user --pre-create-only%s", (isGuest ? " --guest" : ""));
}
// TODO(b/169588446): remove method and callers once it's not needed anymore
private void waitUntilUserDataIsPersisted(int userId) throws InterruptedException {
int napTimeSec = 10;
CLog.i("Sleeping %ds to make sure user data for user %d is persisted", napTimeSec, userId);
sleep(napTimeSec * 1_000);
}
// TODO(b/170263003): update this method after core framewokr's refactoring for proto
private void waitUntilUserPermissionsIsReady(int waitTime, int userId)
throws InterruptedException {
CLog.i("Sleeping %ds to make permissions for user %d is ready", waitTime, userId);
sleep(waitTime * 1_000);
}
private void deletePreCreatedUsers() throws Exception {
List<Integer> userIds = getPreCreatedUsers();
for (int userId : userIds) {
getDevice().removeUser(userId);
}
}
private void convertPreCreatedUser(boolean isGuest, int expectedId) throws Exception {
assertHasPreCreatedUser(expectedId);
String type = isGuest ? "guest" : "user";
int suffix = ++sNumberCreateadUsers;
int newUserId = isGuest
? createGuestUser("PreCreatedUsersTest_ConvertedGuest_" + suffix)
: createFullUser("PreCreatedUsersTest_ConvertedUser_" + suffix);
if (newUserId == expectedId) {
CLog.i("Created new %s from pre-created %s with id %d", type, type, newUserId);
return;
}
fail("Created new " + type + " with id " + newUserId + ", which doesn't match pre-created "
+ "id " + expectedId);
}
private void restartSystem() throws Exception {
// Restart the system to make sure PackageManager preserves the installed bit
restartSystemServer();
}
}