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