• 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.accounts
17 
18 import android.app.admin.DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED
19 import android.app.admin.DevicePolicyManager.ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED
20 import com.android.bedstead.accounts.annotations.EnsureHasAccount
21 import com.android.bedstead.accounts.annotations.EnsureHasAccountAuthenticator
22 import com.android.bedstead.accounts.annotations.EnsureHasAccounts
23 import com.android.bedstead.accounts.annotations.EnsureHasNoAccounts
24 import com.android.bedstead.harrier.AnnotationExecutorUtil.failOrSkip
25 import com.android.bedstead.harrier.BedsteadServiceLocator
26 import com.android.bedstead.harrier.DeviceState
27 import com.android.bedstead.harrier.DeviceStateComponent
28 import com.android.bedstead.harrier.UserType
29 import com.android.bedstead.harrier.annotations.FailureMode
30 import com.android.bedstead.harrier.components.UserTypeResolver
31 import com.android.bedstead.nene.TestApis
32 import com.android.bedstead.nene.TestApis.devicePolicy
33 import com.android.bedstead.nene.TestApis.users
34 import com.android.bedstead.nene.accounts.AccountReference
35 import com.android.bedstead.nene.users.UserReference
36 import com.android.bedstead.remoteaccountauthenticator.RemoteAccountAuthenticator
37 import com.android.bedstead.remoteaccountauthenticator.RemoteAccountAuthenticator.REMOTE_ACCOUNT_AUTHENTICATOR_TEST_APP
38 import com.android.bedstead.testapps.TestAppsComponent
39 import com.google.errorprone.annotations.CanIgnoreReturnValue
40 
41 /**
42  * Contains logic specific to accounts for Bedstead tests using [DeviceState] rule
43  *
44  * @param locator provides access to other dependencies.
45  */
46 class AccountsComponent(locator: BedsteadServiceLocator) : DeviceStateComponent {
47 
48     private val createdAccounts: MutableSet<AccountReference> = mutableSetOf()
49     private val accounts: MutableMap<String, AccountReference> = mutableMapOf()
50     private val accountAuthenticators:
51             MutableMap<UserReference, RemoteAccountAuthenticator> = mutableMapOf()
52     private val testAppsComponent: TestAppsComponent by locator
53     private val userTypeResolver: UserTypeResolver by locator
54 
55     /**
56      * Get the default account defined with [EnsureHasAccount].
57      */
58     fun account(): AccountReference {
59         return account(EnsureHasAccount.DEFAULT_ACCOUNT_KEY)
60     }
61 
62     /**
63      * Get the account defined with [EnsureHasAccount] with a given key.
64      */
65     fun account(key: String): AccountReference {
66         return accounts[key] ?: throw IllegalStateException("No account for key $key")
67     }
68 
69     /**
70      * Access harrier-managed accounts on the instrumented user.
71      */
72     fun accounts(): RemoteAccountAuthenticator {
73         return accounts(users().instrumented())
74     }
75 
76     /**
77      * Access harrier-managed accounts on the given user.
78      */
79     fun accounts(user: UserType): RemoteAccountAuthenticator {
80         return accounts(userTypeResolver.toUser(user))
81     }
82 
83     /**
84      * Access harrier-managed accounts on the given user.
85      */
86     fun accounts(user: UserReference): RemoteAccountAuthenticator {
87         return accountAuthenticators[user] ?: throw IllegalStateException(
88             "No Harrier-Managed account authenticator on user $user. " +
89                     "Did you use @EnsureHasAccountAuthenticator or @EnsureHasAccount?"
90         )
91     }
92 
93     /**
94      * See [EnsureHasAccountAuthenticator]
95      */
96     fun ensureHasAccountAuthenticator(onUser: UserType) {
97         val user: UserReference = userTypeResolver.toUser(onUser)
98         // We don't use .install() so we can rely on the default testapp sharing/uninstall logic
99         testAppsComponent.ensureTestAppInstalled(
100             REMOTE_ACCOUNT_AUTHENTICATOR_TEST_APP,
101             user
102         )
103 
104         accountAuthenticators[user] = RemoteAccountAuthenticator.install(user)
105     }
106 
107     /**
108      * See [EnsureHasAccounts]
109      */
110     fun ensureHasAccounts(accounts: Array<EnsureHasAccount>) {
111         val ignoredAccounts: MutableSet<AccountReference> = mutableSetOf()
112 
113         accounts.forEach {
114             ignoredAccounts.add(
115                 ensureHasAccount(it.onUser, it.key, it.features, ignoredAccounts)
116             )
117         }
118     }
119 
120     /**
121      * See [EnsureHasAccount]
122      */
123     @CanIgnoreReturnValue
124     fun ensureHasAccount(
125         onUser: UserType,
126         key: String,
127         features: Array<String>,
128         ignoredAccounts: Set<AccountReference> = emptySet()
129     ): AccountReference {
130         ensureHasAccountAuthenticator(onUser)
131 
132         val account = accounts(onUser).allAccounts().firstOrNull {
133             !ignoredAccounts.contains(it)
134         }
135 
136         if (account != null) {
137             accounts(onUser).setFeatures(account, features.toSet())
138             accounts[key] = account
139             devicePolicy().calculateHasIncompatibleAccounts()
140             return account
141         }
142 
143         val createdAccount = accounts(onUser).addAccount()
144             .features(features.toSet())
145             .add()
146         createdAccounts.add(createdAccount)
147         accounts[key] = createdAccount
148         devicePolicy().calculateHasIncompatibleAccounts()
149         return createdAccount
150     }
151 
152     /**
153      * See [EnsureHasNoAccounts]
154      */
155     fun ensureHasNoAccounts(
156         userType: UserType,
157         allowPreCreatedAccounts: Boolean,
158         failureMode: FailureMode
159     ) {
160         if (userType == UserType.ANY) {
161             users().all().forEach { user ->
162                 ensureHasNoAccounts(user, allowPreCreatedAccounts, failureMode)
163             }
164         } else {
165             ensureHasNoAccounts(
166                 userTypeResolver.toUser(userType),
167                 allowPreCreatedAccounts,
168                 failureMode
169             )
170         }
171     }
172 
173     /**
174      * See [EnsureHasNoAccounts]
175      */
176     fun ensureHasNoAccounts(
177         user: UserReference,
178         allowPreCreatedAccounts: Boolean,
179         failureMode: FailureMode
180     ) {
181         if (REMOTE_ACCOUNT_AUTHENTICATOR_TEST_APP.pkg().installedOnUser(user)) {
182             user.start() // The user has to be started to remove accounts
183             RemoteAccountAuthenticator.install(user).allAccounts().forEach {
184                 it.remove()
185             }
186         }
187 
188         var accounts = TestApis.accounts().all(user)
189 
190         // If allowPreCreatedAccounts is enabled, that means it's okay to have
191         // pre created accounts on the device.
192         // Now to EnsureHasNoAccounts we will only check that there are no non-pre created accounts.
193         // Non pre created accounts either have ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED
194         // or do not have ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED
195         if (allowPreCreatedAccounts) {
196             accounts = accounts.filter {
197                 !it.hasFeature(ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED) ||
198                         it.hasFeature(ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED)
199             }.toSet()
200         }
201 
202         if (accounts.isNotEmpty()) {
203             failOrSkip(
204                 "Expected no user created accounts on user $user" +
205                         " but there was some that could not be removed.",
206                 failureMode
207             )
208         }
209 
210         devicePolicy().calculateHasIncompatibleAccounts()
211     }
212 
213     override fun teardownNonShareableState() {
214         accounts.clear()
215         accountAuthenticators.clear()
216     }
217 
218     override fun teardownShareableState() {
219         if (createdAccounts.isNotEmpty()) {
220             createdAccounts.forEach {
221                 it.remove()
222             }
223             devicePolicy().calculateHasIncompatibleAccounts()
224         }
225     }
226 }
227