1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.app.sdksandbox.hosttestutils; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static com.google.common.truth.Truth.assertWithMessage; 21 22 import static org.junit.Assert.fail; 23 24 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 25 26 public class SecondaryUserUtils { 27 28 private static final long NUMBER_OF_POLLS = 5 * 60; 29 private static final long POLL_INTERVAL_IN_MILLIS = 1000; 30 31 private final BaseHostJUnit4Test mTest; 32 33 private int mOriginalUserId = -1; 34 private int mSecondaryUserId = -1; 35 SecondaryUserUtils(BaseHostJUnit4Test test)36 public SecondaryUserUtils(BaseHostJUnit4Test test) { 37 mTest = test; 38 } 39 isMultiUserSupported()40 public boolean isMultiUserSupported() throws Exception { 41 return mTest.getDevice().isMultiUserSupported(); 42 } 43 createAndStartSecondaryUser()44 public int createAndStartSecondaryUser() throws Exception { 45 if (mSecondaryUserId != -1) { 46 throw new IllegalStateException("Cannot create secondary user, it already exists"); 47 } 48 mOriginalUserId = mTest.getDevice().getCurrentUser(); 49 String name = "SdkSandboxStorageHost_User" + System.currentTimeMillis(); 50 mSecondaryUserId = mTest.getDevice().createUser(name); 51 // Note we can't install apps on a locked user, so we wait 52 mTest.getDevice().startUser(mSecondaryUserId, /*waitFlag=*/ true); 53 return mSecondaryUserId; 54 } 55 removeSecondaryUserIfNecessary()56 public void removeSecondaryUserIfNecessary() throws Exception { 57 if (mSecondaryUserId == -1) { 58 return; 59 } 60 61 final int userBeingRemoved = mSecondaryUserId; 62 // Set to -1 so that we can create new users later, even when removal goes wrong 63 mSecondaryUserId = -1; 64 65 if (mOriginalUserId != -1 && userBeingRemoved != -1) { 66 // Can't remove the 2nd user without switching out of it 67 assertThat(mTest.getDevice().switchUser(mOriginalUserId)).isTrue(); 68 removeUser(userBeingRemoved); 69 waitForUserDataDeletion(userBeingRemoved); 70 } 71 } 72 switchToSecondaryUser()73 public void switchToSecondaryUser() throws Exception { 74 mTest.getDevice().switchUser(mSecondaryUserId); 75 for (int i = 0; i < NUMBER_OF_POLLS; ++i) { 76 if (mTest.getDevice().getCurrentUser() == mSecondaryUserId) { 77 return; 78 } 79 Thread.sleep(POLL_INTERVAL_IN_MILLIS); 80 } 81 fail("Could not switch to user " + mSecondaryUserId); 82 } 83 84 // TODO(b/346794242): Remove this method once tradefed adds api for removing user with wait. removeUser(int userId)85 private void removeUser(int userId) throws Exception { 86 assertWithMessage("Remove user command output") 87 .that(mTest.getDevice().executeShellCommand("pm remove-user --wait " + userId)) 88 .contains("Success: removed user"); 89 } 90 waitForUserDataDeletion(int userId)91 private void waitForUserDataDeletion(int userId) throws Exception { 92 final String deSdkSandboxDataRootPath = "/data/misc_de/" + userId + "/sdksandbox"; 93 for (int i = 0; i < NUMBER_OF_POLLS; ++i) { 94 if (!mTest.getDevice().isDirectory(deSdkSandboxDataRootPath)) { 95 return; 96 } 97 Thread.sleep(POLL_INTERVAL_IN_MILLIS); 98 } 99 fail("User data was not deleted for UserId " + userId); 100 } 101 } 102