1 /* 2 * Copyright (C) 2020 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 android.car.test.util; 17 18 import static android.car.PlatformVersion.VERSION_CODES.UPSIDE_DOWN_CAKE_0; 19 20 import static com.android.compatibility.common.util.ShellUtils.runShellCommand; 21 22 import static org.junit.Assume.assumeTrue; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.annotation.UserIdInt; 27 import android.app.ActivityManager; 28 import android.car.Car; 29 import android.car.CarOccupantZoneManager; 30 import android.content.Context; 31 import android.content.pm.UserInfo; 32 import android.content.pm.UserInfo.UserInfoFlag; 33 import android.os.UserHandle; 34 import android.os.UserManager; 35 import android.util.Log; 36 37 import com.android.internal.util.Preconditions; 38 39 import org.junit.AssumptionViolatedException; 40 41 import java.util.Arrays; 42 import java.util.List; 43 import java.util.stream.Collectors; 44 45 /** 46 * Provides utilities for Android User related tasks. 47 */ 48 public final class UserTestingHelper { 49 50 private static final String TAG = UserTestingHelper.class.getSimpleName(); 51 52 /** 53 * Checks if the target device supports MUMD (multi-user multi-display). 54 * @throws AssumptionViolatedException if the device does not support MUMD. 55 */ 56 // TODO(b/250108245): Currently doing this because using DeviceState rule is very heavy. We 57 // may want to use PermissionsCheckerRule as a light-weight feature check 58 // (and probably rename it to something like DeviceStateLite). requireMumd(Context context)59 public static void requireMumd(Context context) { 60 assumeTrue( 61 "The device does not support multiple users on multiple displays", 62 Car.getPlatformVersion().isAtLeast(UPSIDE_DOWN_CAKE_0) 63 && context.getSystemService(UserManager.class).isVisibleBackgroundUsersSupported()); 64 } 65 66 /** 67 * Returns a display that is available to start a background user on. 68 * 69 * @return the id of a secondary display that is not assigned to any user, if any. 70 * @throws IllegalStateException when there is no secondary display available. 71 */ getDisplayForStartingBackgroundUser( Context context, CarOccupantZoneManager occupantZoneManager)72 public static int getDisplayForStartingBackgroundUser( 73 Context context, CarOccupantZoneManager occupantZoneManager) { 74 int[] displayIds = context.getSystemService(ActivityManager.class) 75 .getDisplayIdsForStartingVisibleBackgroundUsers(); 76 Log.d(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers() display IDs" 77 + " returned by AM: " + Arrays.toString(displayIds)); 78 if (displayIds == null || displayIds.length == 0) { 79 throw new IllegalStateException("No secondary display is available to start a user."); 80 } 81 82 for (int displayId : displayIds) { 83 int userId = occupantZoneManager.getUserForDisplayId(displayId); 84 if (userId == CarOccupantZoneManager.INVALID_USER_ID) { 85 Log.d(TAG, "Returning first available display: " + displayId); 86 return displayId; 87 } 88 Log.d(TAG, "Display " + displayId + "is curretnly assigned to user " + userId); 89 } 90 91 throw new IllegalStateException( 92 "All secondary displays are assigned. No secondary display is available."); 93 } 94 95 /** 96 * Creates a simple {@link UserInfo}, containing just the given {@code userId}. 97 */ 98 @NonNull newUser(@serIdInt int userId)99 public static UserInfo newUser(@UserIdInt int userId) { 100 return new UserInfoBuilder(userId).build(); 101 } 102 103 /** 104 * Creates a simple {@link UserInfo}, containing just the given {@code userId} 105 * and {@code userName}. 106 */ 107 @NonNull newUser(@serIdInt int userId, @NonNull String userName)108 public static UserInfo newUser(@UserIdInt int userId, @NonNull String userName) { 109 return new UserInfoBuilder(userId).setName(userName).build(); 110 } 111 112 /** 113 * Creates a list of {@link UserInfo UserInfos}, each containing just the given user ids. 114 */ 115 @NonNull newUsers(@serIdInt int... userIds)116 public static List<UserInfo> newUsers(@UserIdInt int... userIds) { 117 return Arrays.stream(userIds) 118 .mapToObj(id -> newUser(id)) 119 .collect(Collectors.toList()); 120 } 121 122 /** 123 * Creates a list of {@link UserHandle UserHandles}, each containing just the given user ids. 124 */ 125 @NonNull newUserHandles(@serIdInt int... userIds)126 public static List<UserHandle> newUserHandles(@UserIdInt int... userIds) { 127 return Arrays.stream(userIds) 128 .mapToObj(id -> UserHandle.of(id)) 129 .collect(Collectors.toList()); 130 } 131 132 /** 133 * Creates a list of {@link UserInfo UserInfos}. 134 */ 135 @NonNull toList(@onNull UserInfo... users)136 public static List<UserInfo> toList(@NonNull UserInfo... users) { 137 return Arrays.stream(users).collect(Collectors.toList()); 138 } 139 140 /** 141 * Creates a list of {@link UserHandle UserHandles}. 142 */ 143 @NonNull toList(@onNull UserHandle... users)144 public static List<UserHandle> toList(@NonNull UserHandle... users) { 145 return Arrays.stream(users).collect(Collectors.toList()); 146 } 147 148 /** 149 * Creates a {@link UserInfo} with the type explicitly set and with the given {@code userId}. 150 */ 151 @NonNull newSecondaryUser(@serIdInt int userId)152 public static UserInfo newSecondaryUser(@UserIdInt int userId) { 153 return new UserInfoBuilder(userId).setType(UserManager.USER_TYPE_FULL_SECONDARY).build(); 154 } 155 156 /** 157 * Creates a new guest with the given {@code userId} and proper flags and types set. 158 */ 159 @NonNull newGuestUser(@serIdInt int userId, boolean ephemeral)160 public static UserInfo newGuestUser(@UserIdInt int userId, boolean ephemeral) { 161 return new UserInfoBuilder(userId).setGuest(true).setEphemeral(ephemeral).build(); 162 } 163 164 /** 165 * Creates a new guest with the given {@code userId} and without any flag.. 166 */ 167 @NonNull newGuestUser(@serIdInt int userId)168 public static UserInfo newGuestUser(@UserIdInt int userId) { 169 return new UserInfoBuilder(userId).setGuest(true).build(); 170 } 171 172 /** 173 * Gets the default {@link UserInfo#userType} for a guest / regular user. 174 */ 175 @NonNull getDefaultUserType(boolean isGuest)176 public static String getDefaultUserType(boolean isGuest) { 177 return isGuest ? UserManager.USER_TYPE_FULL_GUEST : UserManager.USER_TYPE_FULL_SECONDARY; 178 } 179 180 /** 181 * Sets the property that defines the maximum number of uses allowed in the device. 182 */ setMaxSupportedUsers(int max)183 public static void setMaxSupportedUsers(int max) { 184 runShellCommand("setprop fw.max_users %d", max); 185 } 186 187 /** 188 * Configures the user to use PIN credentials. 189 */ setUserLockCredentials(@serIdInt int userId, int pin)190 public static void setUserLockCredentials(@UserIdInt int userId, int pin) { 191 runShellCommand("locksettings set-pin %s --user %d ", pin, userId); 192 } 193 194 /** 195 * Clears the user credentials using current PIN. 196 */ clearUserLockCredentials(@serIdInt int userId, int pin)197 public static void clearUserLockCredentials(@UserIdInt int userId, int pin) { 198 runShellCommand("locksettings clear --old %d --user %d ", pin, userId); 199 } 200 201 /** 202 * Builder for {@link UserInfo} objects. 203 */ 204 public static final class UserInfoBuilder { 205 206 @UserIdInt 207 private final int mUserId; 208 209 @UserInfoFlag 210 private int mFlags; 211 212 @Nullable 213 private String mName; 214 215 @Nullable 216 private String mType; 217 218 private boolean mGuest; 219 private boolean mEphemeral; 220 private boolean mAdmin; 221 private boolean mInitialized; 222 223 /** 224 * Default constructor. 225 */ UserInfoBuilder(@serIdInt int userId)226 public UserInfoBuilder(@UserIdInt int userId) { 227 mUserId = userId; 228 } 229 230 /** 231 * Sets the user name. 232 */ 233 @NonNull setName(@ullable String name)234 public UserInfoBuilder setName(@Nullable String name) { 235 mName = name; 236 return this; 237 } 238 239 /** 240 * Sets the user type. 241 */ 242 @NonNull setType(@ullable String type)243 public UserInfoBuilder setType(@Nullable String type) { 244 Preconditions.checkState(!mGuest, "cannot set type (" + mType + ") after setting it as " 245 + "guest"); 246 mType = type; 247 return this; 248 } 249 250 /** 251 * Sets whether the user is a guest. 252 */ 253 @NonNull setGuest(boolean guest)254 public UserInfoBuilder setGuest(boolean guest) { 255 Preconditions.checkState(mType == null, "cannot set guest after setting type (" + mType 256 + ")"); 257 mGuest = guest; 258 return this; 259 } 260 261 /** 262 * Sets the user flags 263 */ 264 @NonNull setFlags(@serInfoFlag int flags)265 public UserInfoBuilder setFlags(@UserInfoFlag int flags) { 266 mFlags = flags; 267 return this; 268 } 269 270 /** 271 * Sets whether the user is ephemeral. 272 */ 273 @NonNull setEphemeral(boolean ephemeral)274 public UserInfoBuilder setEphemeral(boolean ephemeral) { 275 mEphemeral = ephemeral; 276 return this; 277 } 278 279 /** 280 * Sets whether the user is an admin. 281 */ 282 @NonNull setAdmin(boolean admin)283 public UserInfoBuilder setAdmin(boolean admin) { 284 mAdmin = admin; 285 return this; 286 } 287 288 /** 289 * Sets whether the user is initialized. 290 */ 291 @NonNull setInitialized(boolean initialized)292 public UserInfoBuilder setInitialized(boolean initialized) { 293 mInitialized = initialized; 294 return this; 295 } 296 297 /** 298 * Creates a new {@link UserInfo}. 299 */ 300 @NonNull build()301 public UserInfo build() { 302 int flags = mFlags; 303 if (mEphemeral) { 304 flags |= UserInfo.FLAG_EPHEMERAL; 305 } 306 if (mAdmin) { 307 flags |= UserInfo.FLAG_ADMIN; 308 } 309 if (mInitialized) { 310 flags |= UserInfo.FLAG_INITIALIZED; 311 } 312 if (mGuest) { 313 mType = UserManager.USER_TYPE_FULL_GUEST; 314 } 315 UserInfo info = new UserInfo(mUserId, mName, /* iconPath= */ null, flags, mType); 316 return info; 317 } 318 } 319 UserTestingHelper()320 private UserTestingHelper() { 321 throw new UnsupportedOperationException("contains only static methods"); 322 } 323 } 324