1 /* <lambda>null2 * Copyright (C) 2024 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.bedstead.multiuser 17 18 import android.content.pm.PackageManager 19 import android.os.UserManager 20 import android.util.Log 21 import com.android.bedstead.harrier.AnnotationExecutorUtil 22 import com.android.bedstead.harrier.BedsteadServiceLocator 23 import com.android.bedstead.harrier.DeviceState 24 import com.android.bedstead.harrier.DeviceStateComponent 25 import com.android.bedstead.harrier.annotations.FailureMode 26 import com.android.bedstead.harrier.components.UserTypeResolver 27 import com.android.bedstead.multiuser.annotations.EnsureCanAddUser 28 import com.android.bedstead.multiuser.annotations.OtherUser 29 import com.android.bedstead.multiuser.annotations.RequireUserSupported 30 import com.android.bedstead.multiuser.annotations.meta.EnsureHasNoProfileAnnotation 31 import com.android.bedstead.multiuser.annotations.meta.EnsureHasProfileAnnotation 32 import com.android.bedstead.multiuser.annotations.meta.RequireRunOnProfileAnnotation 33 import com.android.bedstead.nene.TestApis 34 import com.android.bedstead.nene.TestApis.context 35 import com.android.bedstead.nene.TestApis.packages 36 import com.android.bedstead.nene.TestApis.users 37 import com.android.bedstead.nene.exceptions.NeneException 38 import com.android.bedstead.nene.types.OptionalBoolean 39 import com.android.bedstead.nene.userrestrictions.CommonUserRestrictions 40 import com.android.bedstead.nene.users.UserBuilder 41 import com.android.bedstead.nene.users.UserReference 42 import com.android.bedstead.nene.users.UserType 43 import com.android.bedstead.nene.utils.Poll 44 import com.google.common.base.Objects 45 import com.google.errorprone.annotations.CanIgnoreReturnValue 46 import java.time.Duration 47 import org.junit.Assume 48 import org.junit.AssumptionViolatedException 49 50 /** 51 * contains state and logic for managing users in context of DeviceState 52 * this class shouldn't be used by tests directly 53 */ 54 class UsersComponent(locator: BedsteadServiceLocator) : DeviceStateComponent { 55 56 private val enterpriseMediator: MultiUserToEnterpriseMediator? by lazy { 57 locator.getOrNull("com.android.bedstead.enterprise.MultiUserToEnterpriseMediatorImpl") 58 } 59 private val userTypeResolver: UserTypeResolver by locator 60 private val context = context().instrumentedContext() 61 private val createdUsers: MutableList<UserReference> = mutableListOf() 62 private val mRemovedUsers: MutableList<RemovedUser> = mutableListOf() 63 private var mOriginalSwitchedUser: UserReference? = null 64 private var mAdditionalUser: UserReference? = null 65 private var mAnnotationHasSwitchedUser = false 66 private val mUsers: MutableMap<UserType, UserReference> = HashMap() 67 private var otherUserType: com.android.bedstead.harrier.UserType? = null 68 private val profiles: MutableMap<UserType, MutableMap<UserReference, UserReference>> = 69 mutableMapOf() 70 71 /** 72 * Remove the user and record the change 73 */ 74 fun removeAndRecordUser(userReference: UserReference?) { 75 if (userReference == null) { 76 return 77 } 78 switchFromUser(userReference) 79 if (!createdUsers.remove(userReference)) { 80 mRemovedUsers.add( 81 RemovedUser( 82 users().createUser() 83 .name(userReference.name()) 84 .type(userReference.type()) 85 .parent(userReference.parent()), 86 userReference.isRunning(), 87 Objects.equal(mOriginalSwitchedUser, userReference) 88 ) 89 ) 90 } 91 userReference.remove() 92 } 93 94 private fun switchFromUser(user: UserReference) { 95 val currentUser = users().current() 96 if (currentUser != user) { 97 return 98 } 99 100 // We need to find a different user to switch to 101 // full users only, starting with lowest ID 102 val users = users().all().sortedBy { it.id() } 103 for (otherUser in users) { 104 if (otherUser == user) { 105 continue 106 } 107 if (otherUser.parent() != null) { 108 continue 109 } 110 if (!otherUser.isRunning()) { 111 continue 112 } 113 if (!otherUser.canBeSwitchedTo()) { 114 continue 115 } 116 switchToUser(otherUser) 117 return 118 } 119 120 // There are no users to switch to so we'll create one. 121 // In HSUM, an additional user needs to be created to switch from the existing user. 122 ensureHasAdditionalUser( 123 installInstrumentedApp = OptionalBoolean.ANY, 124 switchedToUser = OptionalBoolean.TRUE 125 ) 126 } 127 128 private fun switchToUser(user: UserReference) { 129 val currentUser = users().current() 130 if (currentUser != user) { 131 if (mOriginalSwitchedUser == null) { 132 mOriginalSwitchedUser = currentUser 133 } 134 user.switchTo() 135 } 136 } 137 138 /** 139 * Ensure switched to the specified user 140 */ 141 fun ensureSwitchedToUser(switchedToUser: OptionalBoolean, user: UserReference) { 142 if (switchedToUser == OptionalBoolean.TRUE) { 143 mAnnotationHasSwitchedUser = true 144 switchToUser(user) 145 } else if (switchedToUser == OptionalBoolean.FALSE) { 146 mAnnotationHasSwitchedUser = true 147 switchFromUser(user) 148 } 149 } 150 151 /** 152 * Returns the additional user specified by annotation 153 */ 154 fun additionalUser(): UserReference = checkNotNull(mAdditionalUser) { 155 "No additional user found. Ensure the correct annotations " + 156 "have been used to declare use of additional user." 157 } 158 159 /** 160 * Ensure has a user with a specified userType 161 */ 162 fun ensureHasUser( 163 userType: String, 164 installInstrumentedApp: OptionalBoolean, 165 switchedToUser: OptionalBoolean 166 ) { 167 val resolvedUserType: UserType = RequireUserSupported(userType).logic() 168 val user = users().findUsersOfType(resolvedUserType).firstOrNull { 169 // If the existing user is ephemeral, foreground and ensured not to be the current user, 170 // then we need to create a new one because it will be deleted when switched away. 171 !(it.isEphemeral && switchedToUser == OptionalBoolean.FALSE && it.isForeground) 172 } ?: createUser(resolvedUserType) 173 user.start() 174 if (installInstrumentedApp == OptionalBoolean.TRUE) { 175 packages().find(context.getPackageName()).installExisting(user) 176 } else if (installInstrumentedApp == OptionalBoolean.FALSE) { 177 packages().find(context.getPackageName()).uninstall(user) 178 } 179 ensureSwitchedToUser(switchedToUser, user) 180 mUsers[resolvedUserType] = user 181 } 182 183 /** 184 * Get a user of the given type. 185 * 186 * This should only be used to get users managed by Harrier (using either the 187 * annotations or calls to the [DeviceState] class. 188 * 189 * @throws IllegalStateException if there is no harrier-managed user of the correct type 190 */ 191 fun user(userType: String): UserReference { 192 val resolvedUserType = users().supportedType(userType) 193 ?: throw IllegalStateException(("Can not have a user of type " + userType + 194 " as they are not supported on this device")) 195 return user(resolvedUserType) 196 } 197 198 /** 199 * Get a user of the given type. 200 * 201 * This should only be used to get users managed by Harrier (using either the 202 * annotations or calls to the [DeviceState] class. 203 * 204 * @throws IllegalStateException if there is no harrier-managed user of the correct type 205 */ 206 fun user(userType: UserType): UserReference { 207 return mUsers.getOrElse(userType) { 208 throw IllegalStateException( 209 "No harrier-managed user of type $userType. This method should only be used " + 210 "when Harrier has been used to create the user." 211 ) 212 } 213 } 214 215 /** 216 * Ensure the system doesn't contain any additional user 217 */ 218 fun ensureHasNoAdditionalUser() { 219 var additionalUser = additionalUserOrNull() 220 while (additionalUser != null) { 221 if (users().instrumented() == additionalUser) { 222 throw AssumptionViolatedException( 223 "Tests with @EnsureHasNoAdditionalUser cannot run on an additional user" 224 ) 225 } 226 ensureSwitchedToUser(OptionalBoolean.FALSE, additionalUser) 227 additionalUser.remove() 228 additionalUser = additionalUserOrNull() 229 } 230 mAdditionalUser = null 231 } 232 233 private fun additionalUserOrNull(): UserReference? { 234 val users = users() 235 .findUsersOfType(users().supportedType(UserType.SECONDARY_USER_TYPE_NAME)) 236 .sortedBy { it.id() } 237 return if (users().isHeadlessSystemUserMode) { 238 users.drop(1).firstOrNull() 239 } else { 240 users.firstOrNull() 241 } 242 } 243 244 /** 245 * Ensure the system contains an additional user 246 */ 247 fun ensureHasAdditionalUser( 248 installInstrumentedApp: OptionalBoolean, 249 switchedToUser: OptionalBoolean 250 ) { 251 if (users().isHeadlessSystemUserMode()) { 252 val resolvedUserType: UserType = RequireUserSupported( 253 UserType.SECONDARY_USER_TYPE_NAME 254 ).logic() 255 val users: Collection<UserReference> = users().findUsersOfType(resolvedUserType) 256 if (users.size < 2) { 257 createUser(resolvedUserType) 258 } 259 mAdditionalUser = additionalUserOrNull() 260 if (installInstrumentedApp == OptionalBoolean.TRUE) { 261 packages().find(context.getPackageName()).installExisting(mAdditionalUser) 262 } else if (installInstrumentedApp == OptionalBoolean.FALSE) { 263 packages().find(context.getPackageName()).uninstall(mAdditionalUser) 264 } 265 ensureSwitchedToUser(switchedToUser, mAdditionalUser!!) 266 } else { 267 ensureHasUser(UserType.SECONDARY_USER_TYPE_NAME, installInstrumentedApp, switchedToUser) 268 mAdditionalUser = additionalUserOrNull() 269 } 270 } 271 272 /** 273 * Create a user with a specified userType and parent 274 */ 275 @CanIgnoreReturnValue 276 fun createUser(userType: UserType, parent: UserReference? = null): UserReference { 277 enterpriseMediator?.ensureDoesNotHaveUserRestriction( 278 UserManager.DISALLOW_ADD_USER 279 ) ?: noEnterpriseLog("ensureDoesNotHaveUserRestriction") 280 EnsureCanAddUser().logic() 281 return try { 282 val user = users().createUser() 283 .type(userType) 284 .parent(parent) 285 .createAndStart() 286 createdUsers.add(user) 287 user 288 } catch (e: NeneException) { 289 throw IllegalStateException("Error creating user of type $userType", e) 290 } 291 } 292 293 fun requireRunOnAdditionalUser(switchedToUser: OptionalBoolean) { 294 requireRunOnUser(arrayOf(UserType.SECONDARY_USER_TYPE_NAME), switchedToUser) 295 if (users().isHeadlessSystemUserMode()) { 296 if (users().instrumented() == users().initial()) { 297 throw AssumptionViolatedException( 298 "This test requires running on an additional secondary user" 299 ) 300 } 301 } 302 mAdditionalUser = additionalUserOrNull() 303 } 304 305 fun requireRunOnUser(userTypes: Array<String>, switchedToUser: OptionalBoolean) { 306 var mutableSwitchedToUser = switchedToUser 307 val instrumentedUser = users().instrumented() 308 Assume.assumeTrue( 309 "This test only runs on users of type " + userTypes.contentToString(), 310 userTypes.any { it == instrumentedUser.type().name() } 311 ) 312 mUsers[instrumentedUser.type()] = instrumentedUser 313 if (mutableSwitchedToUser == OptionalBoolean.ANY) { 314 if (instrumentedUser.isVisibleBagroundNonProfileUser()) { 315 // If the option for a visible background user is ANY, 316 // set it to FALSE to prevent user switching on the driver screen. 317 mutableSwitchedToUser = OptionalBoolean.FALSE 318 } else if (!mAnnotationHasSwitchedUser && instrumentedUser.canBeSwitchedTo()) { 319 mutableSwitchedToUser = OptionalBoolean.TRUE 320 } 321 } 322 if (mutableSwitchedToUser == OptionalBoolean.TRUE && !instrumentedUser.canBeSwitchedTo()) { 323 if (users().isHeadlessSystemUserMode() && instrumentedUser == users().system()) { 324 throw IllegalStateException( 325 "Cannot switch to system user on headless devices. " + 326 "Either add @RequireNotHeadlessSystemUserMode, or specify " + 327 "switchedToUser=ANY" 328 ) 329 } else { 330 throw IllegalStateException( 331 "Not permitted to switch to user " + 332 instrumentedUser + "(" + instrumentedUser.getSwitchToUserError() + ")" 333 ) 334 } 335 } 336 ensureSwitchedToUser(mutableSwitchedToUser, instrumentedUser) 337 } 338 339 override fun teardownShareableState() { 340 var ephemeralUser: UserReference? = null 341 val currentUser = users().current() 342 for (user in createdUsers) { 343 try { 344 if (user == currentUser) { 345 // user will be removed after switching to mOriginalSwitchedUser below. 346 user.removeWhenPossible() 347 ephemeralUser = user 348 } else { 349 user.remove() 350 } 351 } catch (e: NeneException) { 352 if (user.exists()) { 353 // Otherwise it's probably just already removed 354 throw NeneException("Could not remove user", e) 355 } 356 } 357 } 358 359 createdUsers.clear() 360 361 clearRemovedUsers() 362 mOriginalSwitchedUser?.let { originalSwitchedUser -> 363 if (!originalSwitchedUser.exists()) { 364 Log.d( 365 LOG_TAG, 366 "Could not switch back to original user " + originalSwitchedUser + 367 " as it does not exist. Switching to initial instead." 368 ) 369 users().initial().switchTo() 370 } else { 371 originalSwitchedUser.switchTo() 372 } 373 mOriginalSwitchedUser = null 374 375 // wait for ephemeral user to be removed after being switched away 376 if (ephemeralUser != null) { 377 Poll.forValue("Ephemeral user exists") { ephemeralUser.exists() } 378 .toBeEqualTo(false) 379 .timeout(Duration.ofMinutes(1)) 380 .errorOnFail() 381 .await() 382 } 383 } 384 } 385 386 private fun clearRemovedUsers() { 387 for (removedUser in mRemovedUsers) { 388 val user = removedUser.userBuilder.create() 389 if (removedUser.isRunning) { 390 user.start() 391 } 392 if (removedUser.isOriginalSwitchedToUser) { 393 mOriginalSwitchedUser = user 394 } 395 } 396 397 mRemovedUsers.clear() 398 } 399 400 override fun teardownNonShareableState() { 401 profiles.clear() 402 mUsers.clear() 403 mAnnotationHasSwitchedUser = false 404 mAdditionalUser = null 405 otherUserType = null 406 } 407 408 override fun prepareTestState() { 409 if (mOriginalSwitchedUser == null) { 410 mOriginalSwitchedUser = users().current() 411 } 412 } 413 414 /** 415 * See [OtherUser] 416 */ 417 fun handleOtherUser(userType: com.android.bedstead.harrier.UserType) { 418 otherUserType = userType 419 } 420 421 /** 422 * See [com.android.bedstead.harrier.DeviceState.otherUser] 423 */ 424 fun otherUser(): UserReference { 425 otherUserType?.let { 426 return userTypeResolver.toUser(it) 427 } ?: throw IllegalStateException("No other user specified. Use @OtherUser") 428 } 429 430 /** 431 * See [RequireRunOnProfileAnnotation] 432 */ 433 fun requireRunOnProfileWithNoProfileOwner( 434 userType: String, 435 installInstrumentedAppInParent: OptionalBoolean, 436 switchedToParentUser: OptionalBoolean 437 ) { 438 val instrumentedUser = requireRunOnProfile( 439 userType, 440 installInstrumentedAppInParent 441 ) 442 enterpriseMediator?.ensureHasNoProfileOwner(instrumentedUser) 443 ?: noEnterpriseLog("ensureHasNoProfileOwner") 444 ensureSwitchedToUser(switchedToParentUser, instrumentedUser.parent()!!) 445 } 446 447 /** 448 * Require run on the profile specified by [userType] 449 */ 450 fun requireRunOnProfile( 451 userType: String, 452 installInstrumentedAppInParent: OptionalBoolean 453 ): UserReference { 454 val instrumentedUser = users().instrumented() 455 Assume.assumeTrue( 456 "This test only runs on users of type $userType", 457 instrumentedUser.type().name() == userType 458 ) 459 saveProfile(instrumentedUser.type(), instrumentedUser.parent()!!, instrumentedUser) 460 if (installInstrumentedAppInParent == OptionalBoolean.TRUE) { 461 packages().find(context.getPackageName()).installExisting(instrumentedUser.parent()) 462 } else if (installInstrumentedAppInParent == OptionalBoolean.FALSE) { 463 packages().find(context.getPackageName()).uninstall(instrumentedUser.parent()) 464 } 465 466 return instrumentedUser 467 } 468 469 /** 470 * Get the [UserReference] of the profile of the given type for the given user. 471 * 472 * This should only be used to get profiles managed by Harrier (using either the 473 * annotations or calls to the [DeviceState] class. 474 * 475 * @throws IllegalStateException if there is no harrier-managed profile for the given user 476 */ 477 fun profile(userType: UserType, forUser: UserReference): UserReference { 478 val profile = getProfileManagedByHarrier(userType, forUser) 479 if (profile != null) { 480 return profile 481 } 482 483 val parentUser = users().instrumented().parent() 484 if (parentUser != null) { 485 val profileForParentUser = getProfileManagedByHarrier(userType, parentUser) 486 if (profileForParentUser != null) { 487 return profileForParentUser 488 } 489 } 490 491 throw IllegalStateException( 492 "No harrier-managed profile of type $userType. This method should only be used " + 493 "when Harrier has been used to create the profile." 494 ) 495 } 496 497 /** 498 * See [profile] 499 */ 500 fun profile(profileType: String, forUser: UserReference): UserReference { 501 val resolvedUserType = users().supportedType(profileType) ?: throw IllegalStateException( 502 "Can not have a profile of type $profileType as they are not supported on this device" 503 ) 504 return profile(resolvedUserType, forUser) 505 } 506 507 /** 508 * See [profile] 509 */ 510 fun profile( 511 profileType: String, 512 forUser: com.android.bedstead.harrier.UserType 513 ): UserReference = profile(profileType, userTypeResolver.toUser(forUser)) 514 515 /** 516 * See [DeviceState.tvProfile] 517 */ 518 fun tvProfile(): UserReference { 519 return tvProfile(forUser = com.android.bedstead.harrier.UserType.INSTRUMENTED_USER) 520 } 521 522 /** 523 * See [DeviceState.tvProfile] 524 */ 525 fun tvProfile(forUser: com.android.bedstead.harrier.UserType): UserReference { 526 return tvProfile(userTypeResolver.toUser(forUser)) 527 } 528 529 /** 530 * See [DeviceState.tvProfile] 531 */ 532 fun tvProfile(forUser: UserReference): UserReference { 533 return profile(TV_PROFILE_TYPE_NAME, forUser) 534 } 535 536 /** 537 * See [DeviceState.cloneProfile] 538 */ 539 fun cloneProfile(): UserReference { 540 return cloneProfile(forUser = com.android.bedstead.harrier.UserType.INITIAL_USER) 541 } 542 543 /** 544 * See [DeviceState.cloneProfile] 545 */ 546 fun cloneProfile(forUser: com.android.bedstead.harrier.UserType): UserReference { 547 return cloneProfile(userTypeResolver.toUser(forUser)) 548 } 549 550 /** 551 * See [DeviceState.cloneProfile] 552 */ 553 fun cloneProfile(forUser: UserReference): UserReference { 554 return profile(CLONE_PROFILE_TYPE_NAME, forUser) 555 } 556 557 /** 558 * See [DeviceState.privateProfile] 559 */ 560 fun privateProfile(): UserReference { 561 return privateProfile(forUser = com.android.bedstead.harrier.UserType.INITIAL_USER) 562 } 563 564 /** 565 * See [DeviceState.privateProfile] 566 */ 567 fun privateProfile(forUser: com.android.bedstead.harrier.UserType): UserReference { 568 return privateProfile(userTypeResolver.toUser(forUser)) 569 } 570 571 /** 572 * See [DeviceState.privateProfile] 573 */ 574 fun privateProfile(forUser: UserReference): UserReference { 575 return profile(PRIVATE_PROFILE_TYPE_NAME, forUser) 576 } 577 578 private fun getProfileManagedByHarrier( 579 userType: UserType, 580 forUser: UserReference 581 ) = profiles[userType]?.get(forUser) 582 583 private fun saveProfile( 584 userType: UserType, 585 forUserReference: UserReference, 586 profile: UserReference 587 ) { 588 getProfilesForType(userType)[forUserReference] = profile 589 } 590 591 private fun getProfilesForType(userType: UserType): MutableMap<UserReference, UserReference> { 592 if (!profiles.containsKey(userType)) { 593 profiles[userType] = mutableMapOf() 594 } 595 return profiles[userType]!! 596 } 597 598 /** 599 * See [EnsureHasProfileAnnotation] 600 */ 601 fun ensureHasProfileWithNoProfileOwner( 602 profileType: String, 603 installInstrumentedApp: OptionalBoolean, 604 forUser: com.android.bedstead.harrier.UserType, 605 switchedToParentUser: OptionalBoolean, 606 isQuietModeEnabled: OptionalBoolean 607 ) { 608 val forUserReference = userTypeResolver.toUser(forUser) 609 ensureHasProfile(profileType, forUserReference, isQuietModeEnabled, installInstrumentedApp) 610 ensureSwitchedToUser(switchedToParentUser, forUserReference) 611 } 612 613 /** 614 * See [EnsureHasProfileAnnotation] 615 */ 616 @CanIgnoreReturnValue 617 fun ensureHasProfile( 618 profileType: String, 619 forUserReference: UserReference, 620 isQuietModeEnabled: OptionalBoolean, 621 installInstrumentedApp: OptionalBoolean 622 ): UserReference { 623 val resolvedUserType: UserType = RequireUserSupported(profileType).logic() 624 var profile = users().findProfileOfType(resolvedUserType, forUserReference) 625 if (profile == null) { 626 if (profileType == UserType.MANAGED_PROFILE_TYPE_NAME) { 627 // TODO(b/239961027): either remove this check (once tests on UserManagerTest / 628 // MultipleUsersOnMultipleDisplaysTest uses non-work profiles) or add a unit test 629 // for it on DeviceStateTest 630 requireFeature(PackageManager.FEATURE_MANAGED_USERS, FailureMode.SKIP) 631 632 // DO + work profile isn't a valid state 633 enterpriseMediator?.ensureHasNoDeviceOwner() 634 ?: noEnterpriseLog("ensureHasNoDeviceOwner") 635 ensureDoesNotHaveUserRestriction( 636 CommonUserRestrictions.DISALLOW_ADD_MANAGED_PROFILE, 637 forUserReference 638 ) 639 } 640 profile = createProfile(resolvedUserType, forUserReference) 641 } 642 profile.start() 643 if (isQuietModeEnabled == OptionalBoolean.TRUE) { 644 profile.setQuietMode(true) 645 } else if (isQuietModeEnabled == OptionalBoolean.FALSE) { 646 profile.setQuietMode(false) 647 } 648 if (installInstrumentedApp == OptionalBoolean.TRUE) { 649 packages().find(context.getPackageName()).installExisting(profile) 650 } else if (installInstrumentedApp == OptionalBoolean.FALSE) { 651 packages().find(context.getPackageName()).uninstall(profile) 652 } 653 saveProfile(resolvedUserType, forUserReference, profile) 654 return profile 655 } 656 657 private fun requireFeature(feature: String, failureMode: FailureMode) { 658 AnnotationExecutorUtil.checkFailOrSkip( 659 "Device must have feature $feature", 660 packages().features().contains(feature), 661 failureMode 662 ) 663 } 664 665 private fun createProfile( 666 profileType: UserType, 667 parent: UserReference 668 ): UserReference { 669 EnsureCanAddUser().logic() 670 ensureCanAddProfile(parent, profileType) 671 if (profileType.name() == "android.os.usertype.profile.CLONE") { 672 // Special case - we can't create a clone profile if this is set 673 ensureDoesNotHaveUserRestriction( 674 CommonUserRestrictions.DISALLOW_ADD_CLONE_PROFILE, 675 parent 676 ) 677 } else if (profileType.name() == "android.os.usertype.profile.PRIVATE") { 678 // Special case - we can't create a private profile if this is set 679 ensureDoesNotHaveUserRestriction( 680 CommonUserRestrictions.DISALLOW_ADD_PRIVATE_PROFILE, 681 parent 682 ) 683 } 684 return try { 685 createUser(profileType, parent) 686 } catch (e: NeneException) { 687 throw IllegalStateException("Error creating profile of type $profileType", e) 688 } 689 } 690 691 private fun ensureCanAddProfile( 692 parent: UserReference, 693 userType: UserType, 694 failureMode: FailureMode = FailureMode.SKIP 695 ) { 696 AnnotationExecutorUtil.checkFailOrSkip( 697 "the device cannot add more profiles of type $userType", 698 parent.canCreateProfile(userType), 699 failureMode 700 ) 701 } 702 703 /** 704 * See [EnsureHasNoProfileAnnotation] 705 */ 706 fun ensureHasNoProfile( 707 profileType: String, 708 forUser: com.android.bedstead.harrier.UserType 709 ) { 710 val forUserReference: UserReference = userTypeResolver.toUser(forUser) 711 val resolvedProfileType = users().supportedType(profileType) 712 ?: return // These profile types don't exist so there can't be any 713 val profile = users().findProfileOfType( 714 resolvedProfileType, 715 forUserReference 716 ) 717 if (profile != null) { 718 // We can't remove an organization owned profile 719 val profileOwner = TestApis.devicePolicy().getProfileOwner(profile) 720 if (profileOwner != null && profileOwner.isOrganizationOwned()) { 721 profileOwner.setIsOrganizationOwned(false) 722 } 723 removeAndRecordUser(profile) 724 } 725 } 726 727 private fun ensureDoesNotHaveUserRestriction(restriction: String, onUser: UserReference?) { 728 enterpriseMediator?.ensureDoesNotHaveUserRestriction(restriction, onUser) 729 ?: noEnterpriseLog("ensureDoesNotHaveUserRestriction") 730 } 731 732 private fun noEnterpriseLog(methodName: String) { 733 Log.i( 734 LOG_TAG, 735 "bedstead-enterprise module is not loaded, $methodName will not be executed" 736 ) 737 } 738 739 companion object { 740 private const val LOG_TAG = "UsersComponent" 741 private const val CLONE_PROFILE_TYPE_NAME: String = "android.os.usertype.profile.CLONE" 742 private const val TV_PROFILE_TYPE_NAME: String = "com.android.tv.profile" 743 private const val PRIVATE_PROFILE_TYPE_NAME: String = "android.os.usertype.profile.PRIVATE" 744 } 745 } 746 747 private class RemovedUser( 748 val userBuilder: UserBuilder, 749 val isRunning: Boolean, 750 val isOriginalSwitchedToUser: Boolean 751 ) 752