1 /* 2 * 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.enterprise 17 18 import android.os.Build 19 import com.android.bedstead.accounts.AccountsComponent 20 import com.android.bedstead.enterprise.annotations.EnsureHasDeviceOwner 21 import com.android.bedstead.enterprise.annotations.EnsureHasNoDeviceOwner 22 import com.android.bedstead.harrier.AnnotationExecutorUtil 23 import com.android.bedstead.harrier.BedsteadServiceLocator 24 import com.android.bedstead.harrier.DeviceStateComponent 25 import com.android.bedstead.harrier.UserType 26 import com.android.bedstead.harrier.annotations.FailureMode 27 import com.android.bedstead.multiuser.UsersComponent 28 import com.android.bedstead.nene.TestApis.devicePolicy 29 import com.android.bedstead.nene.TestApis.users 30 import com.android.bedstead.nene.devicepolicy.DeviceOwner 31 import com.android.bedstead.nene.devicepolicy.DeviceOwnerType 32 import com.android.bedstead.nene.devicepolicy.DevicePolicyController 33 import com.android.bedstead.nene.exceptions.NeneException 34 import com.android.bedstead.nene.users.UserReference 35 import com.android.bedstead.nene.utils.Versions 36 import com.android.bedstead.remotedpc.RemoteDpc 37 import com.android.bedstead.testapp.TestAppProvider 38 import com.android.bedstead.testapp.TestAppQueryBuilder 39 import com.android.bedstead.testapps.TestAppsComponent 40 import java.util.stream.Collectors 41 import org.junit.AssumptionViolatedException 42 43 /** 44 * Manages device owner for device state tests. 45 * 46 * @param locator provides access to other dependencies. 47 */ 48 class DeviceOwnerComponent(locator: BedsteadServiceLocator) : DeviceStateComponent { 49 50 private val accountsComponent: AccountsComponent by locator 51 private val usersComponent: UsersComponent by locator 52 private val enterpriseComponent: EnterpriseComponent by locator 53 private val profileOwnersComponent: ProfileOwnersComponent by locator 54 private val testAppComponent: TestAppsComponent by locator 55 private var hasChangedDeviceOwner = false 56 private var originalDeviceOwner: DevicePolicyController? = null 57 private var originalDeviceOwnerType: Int? = null 58 private var hasChangedDeviceOwnerType = false 59 private var deviceOwner: DevicePolicyController? = null 60 61 /** 62 * Gets currently saved device owner 63 */ getDeviceOwnernull64 fun getDeviceOwner() = deviceOwner 65 66 private fun recordDeviceOwner() { 67 originalDeviceOwner = devicePolicy().getDeviceOwner() 68 originalDeviceOwnerType = (originalDeviceOwner as? DeviceOwner)?.getType() 69 } 70 71 /** 72 * See [EnsureHasNoDeviceOwner] 73 */ ensureHasNoDeviceOwnernull74 fun ensureHasNoDeviceOwner() { 75 val currentDeviceOwner = devicePolicy().getDeviceOwner() ?: return 76 if (!hasChangedDeviceOwner) { 77 recordDeviceOwner() 78 hasChangedDeviceOwner = true 79 hasChangedDeviceOwnerType = true 80 } 81 deviceOwner = null 82 currentDeviceOwner.remove() 83 } 84 85 /** 86 * See [EnsureHasDeviceOwner] 87 */ ensureHasDeviceOwnernull88 fun ensureHasDeviceOwner( 89 failureMode: FailureMode = FailureMode.FAIL, 90 isPrimary: Boolean = false, 91 headlessDeviceOwnerType: EnsureHasDeviceOwner.HeadlessDeviceOwnerType = 92 EnsureHasDeviceOwner.HeadlessDeviceOwnerType.NONE, 93 affiliationIds: MutableSet<String> = mutableSetOf(), 94 type: Int = DeviceOwnerType.DEFAULT, 95 key: String = EnsureHasDeviceOwner.DEFAULT_KEY, 96 dpcQuery: TestAppQueryBuilder = TestAppProvider().query() 97 ) { 98 // TODO(scottjonathan): Should support non-remotedpc device owner (default to remotedpc) 99 var dpcQueryMutable = dpcQuery 100 dpcQueryMutable.applyAnnotation( 101 testAppComponent.additionalQueryParameters.getOrDefault(key, null) 102 ) 103 if (dpcQueryMutable.isEmptyQuery) { 104 dpcQueryMutable = TestAppProvider() 105 .query() 106 .wherePackageName() 107 .isEqualTo(RemoteDpc.REMOTE_DPC_APP_PACKAGE_NAME_OR_PREFIX) 108 } 109 if (headlessDeviceOwnerType == EnsureHasDeviceOwner.HeadlessDeviceOwnerType.AFFILIATED && 110 users().isHeadlessSystemUserMode()) { 111 affiliationIds.add("DEFAULT_AFFILIATED") // To ensure headless PO + DO are affiliated 112 } 113 val deviceOwnerUser: UserReference? = 114 if ( 115 headlessDeviceOwnerType == EnsureHasDeviceOwner.HeadlessDeviceOwnerType.SINGLE_USER 116 ) { 117 users().main() 118 } else { 119 users().system() 120 } 121 if (isPrimary && enterpriseComponent.primaryPolicyManager != null && 122 deviceOwnerUser != enterpriseComponent.primaryPolicyManager?.user() 123 ) { 124 throw IllegalStateException( 125 "Only one DPC can be marked as primary per test " + 126 "(current primary is $enterpriseComponent.mPrimaryPolicyManager)" 127 ) 128 } 129 val currentDeviceOwner = devicePolicy().getDeviceOwner() 130 131 // if current device owner matches query, keep it as it is 132 if (RemoteDpc.matchesRemoteDpcQuery(currentDeviceOwner, dpcQueryMutable)) { 133 deviceOwner = currentDeviceOwner 134 } else { 135 // if there is no device owner, or current device owner is not a remote dpc 136 val instrumentedUser = users().instrumented() 137 if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) { 138 // Prior to S we can't set device owner if there are other users on the device 139 if (instrumentedUser.id() != 0) { 140 // If we're not on the system user we can't reach the required state 141 throw AssumptionViolatedException( 142 "Can't set Device Owner when running on non-system-user" + 143 " on this version of Android" 144 ) 145 } 146 for (u: UserReference in users().all()) { 147 if ((u == instrumentedUser)) { 148 // Can't remove the user we're running on 149 continue 150 } 151 try { 152 usersComponent.removeAndRecordUser(u) 153 } catch (e: NeneException) { 154 AnnotationExecutorUtil.failOrSkip( 155 "Error removing user to prepare for DeviceOwner: $e", 156 failureMode 157 ) 158 } 159 } 160 } 161 removeAllNonTestUsers(failureMode) 162 163 if (Versions.meetsMinimumSdkVersionRequirement(Versions.U)) { 164 accountsComponent.ensureHasNoAccounts( 165 UserType.ANY, 166 allowPreCreatedAccounts = true, 167 FailureMode.FAIL 168 ) 169 } else { 170 // Prior to U this only checked the system user 171 accountsComponent.ensureHasNoAccounts( 172 UserType.SYSTEM_USER, 173 allowPreCreatedAccounts = true, 174 FailureMode.FAIL 175 ) 176 } 177 if (deviceOwnerUser != null) { 178 profileOwnersComponent.ensureHasNoProfileOwner(deviceOwnerUser) 179 } 180 if (!hasChangedDeviceOwner) { 181 recordDeviceOwner() 182 hasChangedDeviceOwner = true 183 hasChangedDeviceOwnerType = true 184 } 185 deviceOwner = RemoteDpc.setAsDeviceOwner(dpcQueryMutable, 186 deviceOwnerUser).devicePolicyController() 187 } 188 if (isPrimary) { 189 enterpriseComponent.primaryPolicyManager = 190 RemoteDpc.forDevicePolicyController(deviceOwner) 191 } 192 val deviceOwnerType = (deviceOwner as DeviceOwner).getType() 193 if (deviceOwnerType != type) { 194 if (!hasChangedDeviceOwnerType) { 195 originalDeviceOwnerType = deviceOwnerType 196 hasChangedDeviceOwnerType = true 197 } 198 (deviceOwner as DeviceOwner).setType(type) 199 } 200 if (type != DeviceOwnerType.FINANCED) { 201 // API is not allowed to be called by a financed device owner. 202 val remoteDpcForDeviceOwner = 203 RemoteDpc.forDevicePolicyController(deviceOwner) 204 remoteDpcForDeviceOwner.devicePolicyManager().setAffiliationIds( 205 remoteDpcForDeviceOwner.componentName(), 206 affiliationIds 207 ) 208 } 209 if ((headlessDeviceOwnerType == EnsureHasDeviceOwner.HeadlessDeviceOwnerType.AFFILIATED && 210 users().isHeadlessSystemUserMode())) { 211 // To simulate "affiliated" headless mode - we must also set the profile owner on the 212 // initial user 213 profileOwnersComponent.ensureHasProfileOwner( 214 users().initial(), 215 isPrimary = false, 216 useParentInstance = false, 217 affiliationIds = affiliationIds, 218 key = key, 219 dpcQuery = dpcQueryMutable, 220 resolvedDpcTestApp = RemoteDpc 221 .forDevicePolicyController(deviceOwner) 222 .testApp() 223 ) 224 } 225 } 226 removeAllNonTestUsersnull227 private fun removeAllNonTestUsers(failureMode: FailureMode) { 228 // We must remove all non-test users on all devices though 229 // (except for the first 1 if headless and always the system user) 230 var allowedNonTestUsers = if (users().isHeadlessSystemUserMode()) 1 else 0 231 val instrumented = users().instrumented() 232 for (u: UserReference in users() 233 .all() 234 .stream() 235 .sorted(Comparator.comparing { u: Any -> (u == instrumented) }.reversed()) 236 .collect(Collectors.toList()) 237 ) { 238 if (u.isSystem) { 239 continue 240 } 241 if (u.isForTesting()) { 242 continue 243 } 244 if (allowedNonTestUsers > 0) { 245 allowedNonTestUsers-- 246 continue 247 } 248 if ((u == instrumented)) { 249 // From U+ we limit non-for-testing users when setting device owner. 250 if (Versions.meetsMinimumSdkVersionRequirement(Versions.U)) { 251 throw IllegalStateException( 252 "Cannot set Device Owner when running on a " + 253 "non-for-testing secondary user ($u)" 254 ) 255 } else { 256 continue 257 } 258 } 259 try { 260 usersComponent.removeAndRecordUser(u) 261 } catch (e: NeneException) { 262 AnnotationExecutorUtil.failOrSkip( 263 "Error removing user to prepare for DeviceOwner: $e", 264 failureMode 265 ) 266 } 267 } 268 } 269 teardownShareableStatenull270 override fun teardownShareableState() { 271 if (hasChangedDeviceOwner) { 272 val originalDeviceOwnerCopy = originalDeviceOwner 273 if (originalDeviceOwnerCopy == null) { 274 deviceOwner?.remove() 275 } else if (originalDeviceOwner != deviceOwner) { 276 deviceOwner?.remove() 277 profileOwnersComponent.ensureHasNoProfileOwner(users().system()) 278 devicePolicy().setDeviceOwner(originalDeviceOwnerCopy.componentName()) 279 } 280 originalDeviceOwnerType?.let { 281 if (originalDeviceOwnerCopy != null) { 282 (originalDeviceOwnerCopy as DeviceOwner).setType(it) 283 } 284 } 285 286 hasChangedDeviceOwner = false 287 originalDeviceOwner = null 288 hasChangedDeviceOwnerType = false 289 originalDeviceOwnerType = null 290 } else { 291 // Device owner type changed but the device owner is the same. 292 if (hasChangedDeviceOwnerType) { 293 originalDeviceOwnerType?.let { 294 (deviceOwner as DeviceOwner).setType(it) 295 } 296 hasChangedDeviceOwnerType = false 297 originalDeviceOwnerType = null 298 } 299 } 300 } 301 302 /** 303 * See [com.android.bedstead.harrier.DeviceState.deviceOwner] 304 */ deviceOwnernull305 fun deviceOwner(): RemoteDpc { 306 checkNotNull(deviceOwner) { 307 ("No Harrier-managed device owner. This method should " + 308 "only be used when Harrier was used to set the Device Owner.") 309 } 310 check(RemoteDpc.isRemoteDpc(deviceOwner)) { 311 ("The device owner is not a RemoteDPC." + 312 " You must use Nene to query for this device owner.") 313 } 314 315 return RemoteDpc.forDevicePolicyController(deviceOwner) 316 } 317 } 318