1 /* 2 * Copyright (C) 2021 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 com.android.bedstead.nene.users; 18 19 import static android.Manifest.permission.CREATE_USERS; 20 import static android.Manifest.permission.INTERACT_ACROSS_USERS; 21 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; 22 import static android.Manifest.permission.QUERY_USERS; 23 import static android.os.Build.VERSION.SDK_INT; 24 import static android.os.Build.VERSION_CODES.S; 25 import static android.os.Build.VERSION_CODES.S_V2; 26 import static android.os.Build.VERSION_CODES.TIRAMISU; 27 import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; 28 import static android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM; 29 import static android.os.Process.myUserHandle; 30 import static com.android.bedstead.nene.users.UserType.MANAGED_PROFILE_TYPE_NAME; 31 import static com.android.bedstead.nene.users.UserType.SECONDARY_USER_TYPE_NAME; 32 import static com.android.bedstead.nene.users.UserType.SYSTEM_USER_TYPE_NAME; 33 import static com.android.bedstead.testapisreflection.TestApisConstants.STOP_USER_ON_SWITCH_DEFAULT; 34 import static com.android.bedstead.testapisreflection.TestApisConstants.STOP_USER_ON_SWITCH_FALSE; 35 import static com.android.bedstead.testapisreflection.TestApisConstants.STOP_USER_ON_SWITCH_TRUE; 36 37 import android.annotation.SuppressLint; 38 import android.app.ActivityManager; 39 import android.content.Context; 40 import android.cts.testapisreflection.TestApisReflectionKt; 41 import android.os.Build; 42 import android.os.UserHandle; 43 import android.os.UserManager; 44 import android.util.Log; 45 46 import androidx.annotation.CheckResult; 47 import androidx.annotation.Nullable; 48 49 import com.android.bedstead.nene.TestApis; 50 import com.android.bedstead.nene.annotations.Experimental; 51 import com.android.bedstead.nene.exceptions.AdbException; 52 import com.android.bedstead.nene.exceptions.AdbParseException; 53 import com.android.bedstead.nene.exceptions.NeneException; 54 import com.android.bedstead.permissions.PermissionContext; 55 import com.android.bedstead.permissions.Permissions; 56 import com.android.bedstead.nene.types.OptionalBoolean; 57 import com.android.bedstead.nene.utils.Poll; 58 import com.android.bedstead.nene.utils.ShellCommand; 59 import com.android.bedstead.nene.utils.Versions; 60 61 import java.time.Duration; 62 import java.util.ArrayList; 63 import java.util.Arrays; 64 import java.util.Collection; 65 import java.util.Comparator; 66 import java.util.HashMap; 67 import java.util.HashSet; 68 import java.util.Iterator; 69 import java.util.List; 70 import java.util.Map; 71 import java.util.Set; 72 import java.util.concurrent.ConcurrentHashMap; 73 import java.util.function.Function; 74 import java.util.stream.Collectors; 75 import java.util.stream.Stream; 76 77 public final class Users { 78 79 private static final String LOG_TAG = "Users"; 80 81 static final int SYSTEM_USER_ID = 0; 82 private static final Duration WAIT_FOR_USER_TIMEOUT = Duration.ofMinutes(4); 83 84 private Map<Integer, AdbUser> mCachedUsers = null; 85 private Map<String, UserType> mCachedUserTypes = null; 86 private Set<UserType> mCachedUserTypeValues = null; 87 private final AdbUserParser mParser; 88 private static final UserManager sUserManager = 89 TestApis.context().instrumentedContext().getSystemService(UserManager.class); 90 private Map<Integer, UserReference> mUsers = new ConcurrentHashMap<>(); 91 92 public static final Users sInstance = new Users(); 93 Users()94 private Users() { 95 mParser = AdbUserParser.get(SDK_INT); 96 } 97 98 /** Get all {@link UserReference}s on the device. */ all()99 public Collection<UserReference> all() { 100 if (!Versions.meetsMinimumSdkVersionRequirement(S)) { 101 fillCache(); 102 return mCachedUsers.keySet().stream().map(UserReference::new) 103 .collect(Collectors.toSet()); 104 } 105 106 return users().map( 107 ui -> find(ui.getId()) 108 ).collect(Collectors.toSet()); 109 } 110 111 /** Get all {@link UserReference}s in the instrumented user's profile group. */ 112 @Experimental profileGroup()113 public Collection<UserReference> profileGroup() { 114 return profileGroup(TestApis.users().instrumented()); 115 } 116 117 /** Get all {@link UserReference}s in the given profile group. */ 118 @Experimental profileGroup(UserReference user)119 public Collection<UserReference> profileGroup(UserReference user) { 120 return users().filter(ui -> ui.getProfileGroupId() == user.id()) 121 .map(ui -> find(ui.getId())).collect(Collectors.toSet()); 122 } 123 124 /** 125 * Gets a {@link UserReference} for the initial user for the device. 126 * 127 * <p>This will be the {@link #system()} user on most systems.</p> 128 */ initial()129 public UserReference initial() { 130 if (!isHeadlessSystemUserMode()) { 131 return system(); 132 } 133 if (TestApis.packages().features().contains("android.hardware.type.automotive")) { 134 try { 135 UserReference user = 136 ShellCommand.builder("cmd car_service get-initial-user") 137 .executeAndParseOutput(i -> find(Integer.parseInt(i.trim()))); 138 139 if (user.exists()) { 140 return user; 141 } else { 142 Log.d(LOG_TAG, "Initial user " + user + " does not exist." 143 + "Finding first non-system full user"); 144 } 145 } catch (AdbException e) { 146 throw new NeneException("Error finding initial user on Auto", e); 147 } 148 } 149 150 List<UserReference> users = new ArrayList<>(all()); 151 users.sort(Comparator.comparingInt(UserReference::id)); 152 153 for (UserReference user : users) { 154 if (user.parent() != null) { 155 continue; 156 } 157 if (user.id() == 0) { 158 continue; 159 } 160 161 return user; 162 } 163 164 throw new NeneException("No initial user available"); 165 } 166 167 /** Get a {@link UserReference} for the user currently switched to. */ current()168 public UserReference current() { 169 if (Versions.meetsMinimumSdkVersionRequirement(S)) { 170 try (PermissionContext p = 171 TestApis.permissions().withPermission(INTERACT_ACROSS_USERS_FULL)) { 172 int currentUserId = ActivityManager.getCurrentUser(); 173 Log.d(LOG_TAG, "current(): finding " + currentUserId); 174 return find(currentUserId); 175 } 176 } 177 178 try { 179 return find((int) ShellCommand.builder("am get-current-user") 180 .executeAndParseOutput(i -> Integer.parseInt(i.trim()))); 181 } catch (AdbException e) { 182 throw new NeneException("Error getting current user", e); 183 } 184 } 185 186 /** Get a {@link UserReference} for the user running the current test process. */ instrumented()187 public UserReference instrumented() { 188 return find(myUserHandle()); 189 } 190 191 /** Get a {@link UserReference} for the system user. */ system()192 public UserReference system() { 193 return find(0); 194 } 195 196 /** Get a {@link UserReference} for the main user, if one exists. Null otherwise. */ 197 @Nullable 198 @SuppressLint("NewApi") main()199 public UserReference main() { 200 UserHandle mainUser; 201 try (PermissionContext p = 202 TestApis.permissions().withPermission(QUERY_USERS)) { 203 mainUser = sUserManager.getMainUser(); 204 } 205 if (mainUser == null) { 206 return null; 207 } 208 return find(mainUser); 209 } 210 211 /** Get a {@link UserReference} by {@code id}. */ find(int id)212 public UserReference find(int id) { 213 if (!mUsers.containsKey(id)) { 214 mUsers.put(id, new UserReference(id)); 215 } 216 return mUsers.get(id); 217 } 218 219 /** Get a {@link UserReference} by {@code userHandle}. */ find(UserHandle userHandle)220 public UserReference find(UserHandle userHandle) { 221 return find(userHandle.getIdentifier()); 222 } 223 224 /** Get all supported {@link UserType}s. */ supportedTypes()225 public Set<UserType> supportedTypes() { 226 // TODO(b/203557600): Stop using adb 227 ensureSupportedTypesCacheFilled(); 228 return mCachedUserTypeValues; 229 } 230 231 /** Get a {@link UserType} with the given {@code typeName}, or {@code null} */ 232 @Nullable supportedType(String typeName)233 public UserType supportedType(String typeName) { 234 ensureSupportedTypesCacheFilled(); 235 return mCachedUserTypes.get(typeName); 236 } 237 238 /** 239 * Find all users which have the given {@link UserType}. 240 */ findUsersOfType(UserType userType)241 public Set<UserReference> findUsersOfType(UserType userType) { 242 if (userType == null) { 243 throw new NullPointerException(); 244 } 245 246 if (userType.baseType().contains(UserType.BaseType.PROFILE)) { 247 throw new NeneException("Cannot use findUsersOfType with profile type " + userType); 248 } 249 250 return all().stream() 251 .filter(u -> { 252 try { 253 return u.type().equals(userType); 254 } catch (NeneException e) { 255 return false; 256 } 257 }) 258 .collect(Collectors.toSet()); 259 } 260 261 /** 262 * Find a single user which has the given {@link UserType}. 263 * 264 * <p>If there are no users of the given type, {@code Null} will be returned. 265 * 266 * <p>If there is more than one user of the given type, {@link NeneException} will be thrown. 267 */ 268 @Nullable 269 public UserReference findUserOfType(UserType userType) { 270 Set<UserReference> users = findUsersOfType(userType); 271 272 if (users.isEmpty()) { 273 return null; 274 } else if (users.size() > 1) { 275 throw new NeneException("findUserOfType called but there is more than 1 user of type " 276 + userType + ". Found: " + users); 277 } 278 279 return users.iterator().next(); 280 } 281 282 /** 283 * Find all users which have the given {@link UserType} and the given parent. 284 */ 285 public Set<UserReference> findProfilesOfType(UserType userType, UserReference parent) { 286 if (userType == null || parent == null) { 287 throw new NullPointerException(); 288 } 289 290 if (!userType.baseType().contains(UserType.BaseType.PROFILE)) { 291 throw new NeneException("Cannot use findProfilesOfType with non-profile type " 292 + userType); 293 } 294 295 return all().stream() 296 .filter(u -> parent.equals(u.parent()) 297 && u.type().equals(userType)) 298 .collect(Collectors.toSet()); 299 } 300 301 /** 302 * Find all users which have the given {@link UserType} and the given parent. 303 * 304 * <p>If there are no users of the given type and parent, {@code Null} will be returned. 305 * 306 * <p>If there is more than one user of the given type and parent, {@link NeneException} will 307 * be thrown. 308 */ 309 @Nullable 310 public UserReference findProfileOfType(UserType userType, UserReference parent) { 311 Set<UserReference> profiles = findProfilesOfType(userType, parent); 312 313 if (profiles.isEmpty()) { 314 return null; 315 } else if (profiles.size() > 1) { 316 throw new NeneException("findProfileOfType called but there is more than 1 user of " 317 + "type " + userType + " with parent " + parent + ". Found: " + profiles); 318 } 319 320 return profiles.iterator().next(); 321 } 322 323 324 /** 325 * Find all users which have the given {@link UserType} and the instrumented user as parent. 326 * 327 * <p>If there are no users of the given type and parent, {@code Null} will be returned. 328 * 329 * <p>If there is more than one user of the given type and parent, {@link NeneException} will 330 * be thrown. 331 */ 332 @Nullable 333 public UserReference findProfileOfType(UserType userType) { 334 return findProfileOfType(userType, TestApis.users().instrumented()); 335 } 336 337 private void ensureSupportedTypesCacheFilled() { 338 if (mCachedUserTypes != null) { 339 // SupportedTypes don't change so don't need to be refreshed 340 return; 341 } 342 if (SDK_INT < Build.VERSION_CODES.R) { 343 mCachedUserTypes = new HashMap<>(); 344 mCachedUserTypes.put(MANAGED_PROFILE_TYPE_NAME, managedProfileUserType()); 345 mCachedUserTypes.put(SYSTEM_USER_TYPE_NAME, systemUserType()); 346 mCachedUserTypes.put(SECONDARY_USER_TYPE_NAME, secondaryUserType()); 347 mCachedUserTypeValues = new HashSet<>(); 348 mCachedUserTypeValues.addAll(mCachedUserTypes.values()); 349 return; 350 } 351 352 fillCache(); 353 } 354 355 private UserType managedProfileUserType() { 356 UserType.MutableUserType managedProfileMutableUserType = new UserType.MutableUserType(); 357 managedProfileMutableUserType.mName = MANAGED_PROFILE_TYPE_NAME; 358 managedProfileMutableUserType.mBaseType = new HashSet<>(Arrays.asList(UserType.BaseType.PROFILE)); 359 managedProfileMutableUserType.mEnabled = true; 360 managedProfileMutableUserType.mMaxAllowed = -1; 361 managedProfileMutableUserType.mMaxAllowedPerParent = 1; 362 return new UserType(managedProfileMutableUserType); 363 } 364 365 private UserType systemUserType() { 366 UserType.MutableUserType managedProfileMutableUserType = new UserType.MutableUserType(); 367 managedProfileMutableUserType.mName = SYSTEM_USER_TYPE_NAME; 368 managedProfileMutableUserType.mBaseType = 369 new HashSet<>(Arrays.asList(UserType.BaseType.FULL, UserType.BaseType.SYSTEM)); 370 managedProfileMutableUserType.mEnabled = true; 371 managedProfileMutableUserType.mMaxAllowed = -1; 372 managedProfileMutableUserType.mMaxAllowedPerParent = -1; 373 return new UserType(managedProfileMutableUserType); 374 } 375 376 private UserType secondaryUserType() { 377 UserType.MutableUserType managedProfileMutableUserType = new UserType.MutableUserType(); 378 managedProfileMutableUserType.mName = SECONDARY_USER_TYPE_NAME; 379 managedProfileMutableUserType.mBaseType = new HashSet<>(Arrays.asList(UserType.BaseType.FULL)); 380 managedProfileMutableUserType.mEnabled = true; 381 managedProfileMutableUserType.mMaxAllowed = -1; 382 managedProfileMutableUserType.mMaxAllowedPerParent = -1; 383 return new UserType(managedProfileMutableUserType); 384 } 385 386 /** 387 * Create a new user. 388 */ 389 @CheckResult 390 public UserBuilder createUser() { 391 return new UserBuilder(); 392 } 393 394 /** 395 * Get a {@link UserReference} to a user who does not exist. 396 */ 397 public UserReference nonExisting() { 398 Set<Integer> userIds; 399 if (Versions.meetsMinimumSdkVersionRequirement(S)) { 400 userIds = users().map(ui -> ui.getId()).collect(Collectors.toSet()); 401 } else { 402 fillCache(); 403 userIds = mCachedUsers.keySet(); 404 } 405 406 int id = 0; 407 408 while (userIds.contains(id)) { 409 id++; 410 } 411 412 return find(id); 413 } 414 415 private void fillCache() { 416 try { 417 // TODO: Replace use of adb on supported versions of Android 418 String userDumpsysOutput = ShellCommand.builder("dumpsys user").execute(); 419 AdbUserParser.ParseResult result = mParser.parse(userDumpsysOutput); 420 421 mCachedUsers = result.mUsers; 422 if (result.mUserTypes != null) { 423 mCachedUserTypes = result.mUserTypes; 424 } else { 425 ensureSupportedTypesCacheFilled(); 426 } 427 428 Iterator<Map.Entry<Integer, AdbUser>> iterator = mCachedUsers.entrySet().iterator(); 429 430 while (iterator.hasNext()) { 431 Map.Entry<Integer, AdbUser> entry = iterator.next(); 432 433 if (entry.getValue().isRemoving()) { 434 // We don't expose users who are currently being removed 435 iterator.remove(); 436 continue; 437 } 438 439 AdbUser.MutableUser mutableUser = entry.getValue().mMutableUser; 440 441 if (SDK_INT < Build.VERSION_CODES.R) { 442 if (entry.getValue().id() == SYSTEM_USER_ID) { 443 mutableUser.mType = supportedType(SYSTEM_USER_TYPE_NAME); 444 mutableUser.mIsPrimary = true; 445 } else if (entry.getValue().hasFlag(AdbUser.FLAG_MANAGED_PROFILE)) { 446 mutableUser.mType = 447 supportedType(MANAGED_PROFILE_TYPE_NAME); 448 mutableUser.mIsPrimary = false; 449 } else { 450 mutableUser.mType = 451 supportedType(SECONDARY_USER_TYPE_NAME); 452 mutableUser.mIsPrimary = false; 453 } 454 } 455 456 if (SDK_INT < S) { 457 if (mutableUser.mType.baseType() 458 .contains(UserType.BaseType.PROFILE)) { 459 // We assume that all profiles before S were on the System User 460 mutableUser.mParent = find(SYSTEM_USER_ID); 461 } 462 } 463 } 464 465 mCachedUserTypeValues = new HashSet<>(); 466 mCachedUserTypeValues.addAll(mCachedUserTypes.values()); 467 468 } catch (AdbException | AdbParseException e) { 469 throw new RuntimeException("Error filling cache", e); 470 } 471 } 472 473 /** 474 * Block until the user with the given {@code userReference} to not exist or to be in the 475 * correct state. 476 * 477 * <p>If this cannot be met before a timeout, a {@link NeneException} will be thrown. 478 */ 479 @Nullable 480 UserReference waitForUserToNotExistOrMatch( 481 UserReference userReference, Function<UserReference, Boolean> userChecker) { 482 return waitForUserToMatch(userReference, userChecker, /* waitForExist= */ false); 483 } 484 485 @Nullable 486 private UserReference waitForUserToMatch( 487 UserReference userReference, Function<UserReference, Boolean> userChecker, 488 boolean waitForExist) { 489 // TODO(scottjonathan): This is pretty heavy because we resolve everything when we know we 490 // are throwing away everything except one user. Optimise 491 try { 492 return Poll.forValue("user", () -> userReference) 493 .toMeet((user) -> { 494 if (user == null) { 495 return !waitForExist; 496 } 497 return userChecker.apply(user); 498 }).timeout(WAIT_FOR_USER_TIMEOUT) 499 .errorOnFail("Expected user to meet requirement") 500 .await(); 501 } catch (AssertionError e) { 502 if (!userReference.exists()) { 503 throw new NeneException( 504 "Timed out waiting for user state for user " 505 + userReference + ". User does not exist.", e); 506 } 507 throw new NeneException( 508 "Timed out waiting for user state, current state " + userReference, e 509 ); 510 } 511 } 512 513 /** Checks if private profile usertupe is supported on the device */ 514 public boolean canAddPrivateProfile() { 515 if (Versions.meetsMinimumSdkVersionRequirement(VANILLA_ICE_CREAM)) { 516 try (PermissionContext p = TestApis.permissions().withPermission(CREATE_USERS)) { 517 return TestApisReflectionKt.canAddPrivateProfile(sUserManager); 518 } 519 } 520 return false; 521 } 522 523 /** See {@link UserManager#isHeadlessSystemUserMode()}. */ 524 @SuppressWarnings("NewApi") 525 public boolean isHeadlessSystemUserMode() { 526 if (Versions.meetsMinimumSdkVersionRequirement(S)) { 527 boolean value = UserManager.isHeadlessSystemUserMode(); 528 Log.d(LOG_TAG, "isHeadlessSystemUserMode: " + value); 529 return value; 530 } 531 532 Log.d(LOG_TAG, "isHeadlessSystemUserMode pre-S: false"); 533 return false; 534 } 535 536 /** See {@link UserManager#isVisibleBackgroundUsersSupported()}. */ 537 @SuppressWarnings("NewApi") 538 public boolean isVisibleBackgroundUsersSupported() { 539 if (Versions.meetsMinimumSdkVersionRequirement(UPSIDE_DOWN_CAKE)) { 540 return TestApisReflectionKt.isVisibleBackgroundUsersSupported(sUserManager); 541 } 542 543 return false; 544 } 545 546 /** See {@link UserManager#isVisibleBackgroundUsersOnDefaultDisplaySupported()}. */ 547 @SuppressWarnings("NewApi") 548 public boolean isVisibleBackgroundUsersOnDefaultDisplaySupported() { 549 if (Versions.meetsMinimumSdkVersionRequirement(UPSIDE_DOWN_CAKE)) { 550 return TestApisReflectionKt.isVisibleBackgroundUsersOnDefaultDisplaySupported( 551 sUserManager); 552 } 553 554 return false; 555 } 556 557 /** 558 * Set the stopBgUsersOnSwitch property. 559 * 560 * <p>This affects if background users will be swapped when switched away from on some devices. 561 */ 562 public void setStopBgUsersOnSwitch(OptionalBoolean value) { 563 int intValue = 564 (value == OptionalBoolean.TRUE) 565 ? STOP_USER_ON_SWITCH_TRUE 566 : (value == OptionalBoolean.FALSE) 567 ? STOP_USER_ON_SWITCH_FALSE 568 : STOP_USER_ON_SWITCH_DEFAULT; 569 if (!Versions.meetsMinimumSdkVersionRequirement(S_V2)) { 570 return; 571 } 572 Context context = TestApis.context().instrumentedContext(); 573 try (PermissionContext p = TestApis.permissions() 574 .withPermission(INTERACT_ACROSS_USERS)) { 575 TestApisReflectionKt.setStopUserOnSwitch( 576 context.getSystemService(ActivityManager.class), intValue); 577 } 578 } 579 580 @Nullable 581 AdbUser fetchUser(int id) { 582 fillCache(); 583 return mCachedUsers.get(id); 584 } 585 586 @Experimental 587 public boolean supportsMultipleUsers() { 588 return UserManager.supportsMultipleUsers(); 589 } 590 591 /** 592 * Note: This method should not be run on < S. 593 */ 594 static Stream<UserInfo> users() { 595 Versions.requireMinimumVersion(S); 596 597 if (Permissions.sIgnorePermissions.get()) { 598 return getUsers(); 599 } 600 601 try (PermissionContext p = 602 TestApis.permissions().withPermission(CREATE_USERS) 603 .withPermissionOnVersionAtLeast(Versions.U, QUERY_USERS)) { 604 return getUsers(); 605 } 606 } 607 608 private static Stream<UserInfo> getUsers() { 609 return TestApisReflectionKt.getUsers(sUserManager, 610 /* excludePartial= */ false, 611 /* excludeDying= */ true, 612 /* excludePreCreated= */ false).stream() 613 .map(ui -> new UserInfo(ui)); 614 } 615 } 616