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