• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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