• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 package com.android.tradefed.suite.checker;
17 
18 import java.util.List;
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 
22 import com.android.tradefed.config.Option;
23 import com.android.tradefed.config.OptionClass;
24 import com.android.tradefed.device.DeviceNotAvailableException;
25 import com.android.tradefed.device.ITestDevice;
26 import com.android.tradefed.log.LogUtil.CLog;
27 import com.android.tradefed.suite.checker.StatusCheckerResult.CheckStatus;
28 import com.android.tradefed.util.UserUtil;
29 import com.android.tradefed.util.UserUtil.UserSwitchFailedException;
30 
31 /**
32  * Checks if users have changed during the test.
33  *
34  * <p>Optionally can also setup the current user.
35  */
36 @OptionClass(alias = "user-system-checker")
37 public class UserChecker implements ISystemStatusChecker {
38 
39     @Option(
40         name = "user-type",
41         description = "The type of user to switch to before each module run."
42     )
43     private UserUtil.UserType mUserToSwitchTo = UserUtil.UserType.CURRENT;
44 
45     public static final String DEFAULT_NAME = "TFauto";
46 
47     private DeviceUserState mPreExecutionUserState;
48 
49     /** {@inheritDoc} */
50     @Override
preExecutionCheck(ITestDevice device)51     public StatusCheckerResult preExecutionCheck(ITestDevice device)
52             throws DeviceNotAvailableException {
53 
54         String userSwitchErrorMsg = null;
55         try {
56             switchToExistingOrCreateUserType(device);
57         } catch (UserSwitchFailedException err) {
58             userSwitchErrorMsg = err.toString();
59         }
60 
61         mPreExecutionUserState = new DeviceUserState(device);
62         CLog.d("preExecutionUsers=" + mPreExecutionUserState);
63 
64         if (userSwitchErrorMsg == null) {
65             return new StatusCheckerResult(CheckStatus.SUCCESS);
66         } else {
67             StatusCheckerResult result = new StatusCheckerResult(CheckStatus.FAILED);
68             result.setErrorMessage(userSwitchErrorMsg);
69             return result;
70         }
71     }
72 
73     /** {@inheritDoc} */
74     @Override
postExecutionCheck(ITestDevice device)75     public StatusCheckerResult postExecutionCheck(ITestDevice device)
76             throws DeviceNotAvailableException {
77         DeviceUserState postDeviceUserState = new DeviceUserState(device);
78         CLog.d("postExecutionUsers=" + postDeviceUserState);
79 
80         ArrayList<String> errors = new ArrayList<>();
81 
82         for (Integer removedUser : mPreExecutionUserState.findRemovedUsers(postDeviceUserState)) {
83             errors.add(String.format("User %d no longer exists after test", removedUser));
84         }
85 
86         for (Integer addedUser : mPreExecutionUserState.findAddedUsers(postDeviceUserState)) {
87             errors.add(
88                     String.format(
89                             "User %d was created during the test and not deleted", addedUser));
90         }
91 
92         if (mPreExecutionUserState.currentUserChanged(postDeviceUserState)) {
93             errors.add(
94                     String.format(
95                             "User %d was the currentUser before, has changed to %d",
96                             mPreExecutionUserState.getCurrentUser(),
97                             postDeviceUserState.getCurrentUser()));
98         }
99 
100         for (int userId : mPreExecutionUserState.findStoppedUsers(postDeviceUserState)) {
101             CLog.w("User %d was running but is now stopped.", userId);
102         }
103 
104         for (int userId : mPreExecutionUserState.findStartedUsers(postDeviceUserState)) {
105             CLog.w("User %d was stopped but is now running.", userId);
106         }
107 
108         if (errors.size() > 0) {
109             StatusCheckerResult result = new StatusCheckerResult(CheckStatus.FAILED);
110             result.setErrorMessage(String.join("\n", errors));
111             return result;
112         } else {
113             return new StatusCheckerResult(CheckStatus.SUCCESS);
114         }
115     }
116 
117     /**
118      * Switches to the mUserType, creating if necessary.
119      *
120      * <p>Returns null if success, the error string if there is an error.
121      */
switchToExistingOrCreateUserType(ITestDevice device)122     private void switchToExistingOrCreateUserType(ITestDevice device)
123             throws DeviceNotAvailableException, UserSwitchFailedException {
124         try {
125             UserUtil.switchToUserType(device, mUserToSwitchTo);
126         } catch (UserUtil.SecondaryUserNotFoundException attemptCreate) {
127             CLog.d("No secondary users exist, creating one.");
128             int secondary = device.createUserNoThrow(DEFAULT_NAME);
129             if (secondary <= 0) {
130                 throw new UserSwitchFailedException("Failed to create secondary user");
131             }
132             UserUtil.switchToUserType(device, mUserToSwitchTo);
133         }
134     }
135 
136     /** Class for monitoring changes to the user state between pre/post check. */
137     static class DeviceUserState {
138         private final int mCurrentUser;
139         private final ArrayList<Integer> mUsers;
140         private final HashMap<Integer, Boolean> mUserRunningStates;
141 
DeviceUserState(ITestDevice device)142         DeviceUserState(ITestDevice device) throws DeviceNotAvailableException {
143             mCurrentUser = device.getCurrentUser();
144             mUsers = device.listUsers();
145             mUserRunningStates = new HashMap<>(mUsers.size());
146             for (Integer userId : mUsers) {
147                 mUserRunningStates.put(userId, device.isUserRunning(userId));
148             }
149         }
150 
getCurrentUser()151         public int getCurrentUser() {
152             return mCurrentUser;
153         }
154 
155         @Override
toString()156         public String toString() {
157             StringBuilder builder = new StringBuilder();
158             builder.append(String.format("currentUser=%d;", getCurrentUser()));
159             for (Integer userId : mUsers) {
160                 String running = mUserRunningStates.get(userId) ? "running" : "stopped";
161                 builder.append(String.format(" %d:%s", userId, running));
162             }
163             return builder.toString();
164         }
165 
findRemovedUsers(DeviceUserState otherState)166         List<Integer> findRemovedUsers(DeviceUserState otherState) {
167             ArrayList<Integer> removedUsers = new ArrayList<>();
168             for (Integer userId : mUsers) {
169                 if (!otherState.containsUser(userId)) {
170                     removedUsers.add(userId);
171                 }
172             }
173             return removedUsers;
174         }
175 
findAddedUsers(DeviceUserState otherState)176         List<Integer> findAddedUsers(DeviceUserState otherState) {
177             ArrayList<Integer> addedUsers = new ArrayList<>();
178             for (Integer userId : otherState.mUsers) {
179                 if (!this.containsUser(userId)) {
180                     addedUsers.add(userId);
181                 }
182             }
183             return addedUsers;
184         }
185 
currentUserChanged(DeviceUserState otherState)186         boolean currentUserChanged(DeviceUserState otherState) {
187             return this.getCurrentUser() != otherState.getCurrentUser();
188         }
189 
findStartedUsers(DeviceUserState otherState)190         List<Integer> findStartedUsers(DeviceUserState otherState) {
191             ArrayList<Integer> startedUsers = new ArrayList<>();
192             for (Integer userId : mUsers) {
193                 if (!this.isUserRunning(userId) && otherState.isUserRunning(userId)) {
194                     startedUsers.add(userId);
195                 }
196             }
197             return startedUsers;
198         }
199 
findStoppedUsers(DeviceUserState otherState)200         List<Integer> findStoppedUsers(DeviceUserState otherState) {
201             ArrayList<Integer> stoppedUsers = new ArrayList<>();
202             for (Integer userId : mUsers) {
203                 if (this.isUserRunning(userId) && !otherState.isUserRunning(userId)) {
204                     stoppedUsers.add(userId);
205                 }
206             }
207             return stoppedUsers;
208         }
209 
containsUser(int userId)210         private boolean containsUser(int userId) {
211             return mUserRunningStates.containsKey(userId);
212         }
213 
isUserRunning(int userId)214         private boolean isUserRunning(int userId) {
215             return mUserRunningStates.getOrDefault(userId, /* default= */ false);
216         }
217     }
218 }
219