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.enterprise 17 18 import android.util.Log 19 import com.android.bedstead.enterprise.annotations.EnsureDoesNotHaveUserRestriction 20 import com.android.bedstead.enterprise.annotations.EnsureHasUserRestriction 21 import com.android.bedstead.harrier.BedsteadServiceLocator 22 import com.android.bedstead.harrier.DeviceState 23 import com.android.bedstead.harrier.DeviceStateComponent 24 import com.android.bedstead.harrier.UserType 25 import com.android.bedstead.harrier.components.UserTypeResolver 26 import com.android.bedstead.nene.TestApis.devicePolicy 27 import com.android.bedstead.nene.TestApis.users 28 import com.android.bedstead.nene.exceptions.AdbException 29 import com.android.bedstead.nene.exceptions.NeneException 30 import com.android.bedstead.nene.userrestrictions.CommonUserRestrictions 31 import com.android.bedstead.nene.users.UserReference 32 import com.android.bedstead.nene.utils.Tags.hasTag 33 import com.android.bedstead.remotedpc.RemotePolicyManager 34 import com.google.errorprone.annotations.CanIgnoreReturnValue 35 import org.junit.AssumptionViolatedException 36 37 /** 38 * Contains logic specific to user restrictions for Bedstead tests using [DeviceState] rule 39 * 40 * @param locator provides access to other dependencies. 41 */ 42 class UserRestrictionsComponent(locator: BedsteadServiceLocator) : DeviceStateComponent { 43 44 private val userTypeResolver: UserTypeResolver by locator 45 private val mProfileOwnersComponent: ProfileOwnersComponent by locator 46 private val mDeviceOwnerComponent: DeviceOwnerComponent by locator 47 private val mAddedUserRestrictions: MutableMap<UserReference, MutableSet<String>> = 48 mutableMapOf() 49 private val mRemovedUserRestrictions: MutableMap<UserReference, MutableSet<String>> = 50 mutableMapOf() 51 52 /** 53 * See [EnsureHasUserRestriction] 54 */ 55 fun ensureHasUserRestriction(restriction: String, onUser: UserType) { 56 ensureHasUserRestriction(restriction, userTypeResolver.toUser(onUser)) 57 } 58 59 private fun ensureHasUserRestriction(restriction: String, onUser: UserReference) { 60 if (devicePolicy().userRestrictions(onUser).isSet(restriction)) { 61 return 62 } 63 64 // TODO use TestApis.root().testUsesAdbRoot when this is modularised 65 val shouldRunAsRoot = hasTag("adb-root") 66 if (shouldRunAsRoot) { 67 Log.i(LOG_TAG, "Trying to set user restriction as root.") 68 try { 69 devicePolicy().userRestrictions(onUser)[restriction] = true 70 } catch (e: AdbException) { 71 Log.i( 72 LOG_TAG, 73 "Unable to set user restriction as root, trying to set using heuristics." 74 ) 75 trySetUserRestriction(onUser, restriction) 76 } 77 } else { 78 trySetUserRestriction(onUser, restriction) 79 } 80 if (!devicePolicy().userRestrictions(onUser).isSet(restriction)) { 81 val message = "Infra cannot set user restriction $restriction" + 82 if (!shouldRunAsRoot) { 83 ". Please add RequireAdbRoot to enable root capabilities." 84 } else { 85 "" 86 } 87 throw AssumptionViolatedException(message) 88 } 89 if (mRemovedUserRestrictions[onUser]?.remove(restriction) != true) { 90 if (!mAddedUserRestrictions.containsKey(onUser)) { 91 mAddedUserRestrictions[onUser] = HashSet() 92 } 93 mAddedUserRestrictions[onUser]!!.add(restriction) 94 } 95 if (!devicePolicy().userRestrictions(onUser).isSet(restriction)) { 96 throw NeneException("Error setting user restriction $restriction") 97 } 98 } 99 100 private fun trySetUserRestriction(onUser: UserReference, restriction: String) { 101 Log.i(LOG_TAG, "Trying to set user restriction using heuristics.") 102 var hasSet = false 103 if (onUser == users().system()) { 104 hasSet = trySetUserRestrictionWithDeviceOwner(restriction) 105 } 106 if (!hasSet) { 107 hasSet = trySetUserRestrictionWithProfileOwner(onUser, restriction) 108 } 109 if (!hasSet && onUser != users().system()) { 110 trySetUserRestrictionWithDeviceOwner(restriction) 111 } 112 } 113 114 @CanIgnoreReturnValue 115 private fun trySetUserRestrictionWithDeviceOwner(restriction: String): Boolean { 116 mDeviceOwnerComponent.ensureHasDeviceOwner() 117 val dpc: RemotePolicyManager = mDeviceOwnerComponent.deviceOwner() 118 try { 119 dpc.devicePolicyManager().addUserRestriction(dpc.componentName(), restriction) 120 } catch (e: SecurityException) { 121 if (e.message!!.contains("cannot set user restriction")) { 122 return false 123 } 124 throw e 125 } 126 return true 127 } 128 129 @CanIgnoreReturnValue 130 private fun trySetUserRestrictionWithProfileOwner( 131 onUser: UserReference, 132 restriction: String 133 ): Boolean { 134 mProfileOwnersComponent.ensureHasProfileOwner(user = onUser) 135 val dpc: RemotePolicyManager = mProfileOwnersComponent.profileOwner(onUser) 136 try { 137 dpc.devicePolicyManager().addUserRestriction(dpc.componentName(), restriction) 138 } catch (e: SecurityException) { 139 if (e.message!!.contains("cannot set user restriction")) { 140 return false 141 } 142 throw e 143 } 144 return true 145 } 146 147 /** 148 * See [EnsureDoesNotHaveUserRestriction] 149 */ 150 fun ensureDoesNotHaveUserRestriction(restriction: String, onUser: UserReference?) { 151 if (!devicePolicy().userRestrictions(onUser!!).isSet(restriction)) { 152 return 153 } 154 155 // TODO use TestApis.root().testUsesAdbRoot when this is modularised 156 val shouldRunAsRoot = hasTag("adb-root") 157 if (shouldRunAsRoot) { 158 Log.i(LOG_TAG, "Trying to clear user restriction as root.") 159 try { 160 devicePolicy().userRestrictions(onUser)[restriction] = false 161 } catch (e: AdbException) { 162 Log.i( 163 LOG_TAG, 164 "Unable to clear user restriction as root, trying to clear using" + 165 " heuristics.", 166 e 167 ) 168 tryClearUserRestriction(onUser, restriction) 169 } 170 } else { 171 tryClearUserRestriction(onUser, restriction) 172 } 173 if (devicePolicy().userRestrictions(onUser).isSet(restriction)) { 174 val message = 175 "Infra cannot remove user restriction $restriction" + 176 if (shouldRunAsRoot) { 177 "" 178 } else { 179 ". If this test requires capabilities only available on devices " + 180 "where adb has root, add @RequireAdbRoot to the test." 181 } 182 throw AssumptionViolatedException(message) 183 } 184 if (mAddedUserRestrictions[onUser]?.remove(restriction) != true) { 185 if (!mRemovedUserRestrictions.containsKey(onUser)) { 186 mRemovedUserRestrictions[onUser] = mutableSetOf() 187 } 188 mRemovedUserRestrictions[onUser]!!.add(restriction) 189 } 190 } 191 192 private fun tryClearUserRestriction(onUser: UserReference, restriction: String) { 193 if (restriction == CommonUserRestrictions.DISALLOW_ADD_MANAGED_PROFILE) { 194 // Special case - set by the system whenever there is a Device Owner 195 mDeviceOwnerComponent.ensureHasNoDeviceOwner() 196 } else if (restriction == CommonUserRestrictions.DISALLOW_ADD_CLONE_PROFILE) { 197 // Special case - set by the system whenever there is a Device Owner 198 mDeviceOwnerComponent.ensureHasNoDeviceOwner() 199 } else if (restriction == CommonUserRestrictions.DISALLOW_ADD_PRIVATE_PROFILE) { 200 // Special case - set by the system whenever there is a Device Owner 201 mDeviceOwnerComponent.ensureHasNoDeviceOwner() 202 } else if (restriction == CommonUserRestrictions.DISALLOW_ADD_USER) { 203 // Special case - set by the system whenever there is a Device Owner or 204 // organization-owned profile owner 205 mDeviceOwnerComponent.ensureHasNoDeviceOwner() 206 val orgOwnedProfileOwner = devicePolicy().organizationOwnedProfileOwner 207 if (orgOwnedProfileOwner != null) { 208 mProfileOwnersComponent.ensureHasNoProfileOwner(orgOwnedProfileOwner.user()) 209 return 210 } 211 } 212 var hasCleared = !devicePolicy().userRestrictions(onUser).isSet(restriction) 213 if (!hasCleared && onUser == users().system()) { 214 hasCleared = tryClearUserRestrictionWithDeviceOwner(restriction) 215 } 216 if (!hasCleared) { 217 hasCleared = tryClearUserRestrictionWithProfileOwner(onUser, restriction) 218 } 219 if (!hasCleared && onUser != users().system()) { 220 tryClearUserRestrictionWithDeviceOwner(restriction) 221 } 222 } 223 224 override fun teardownNonShareableState() { 225 mAddedUserRestrictions.toMap().forEach { 226 it.value.toList().forEach { restriction -> 227 // below function modifies both collections that we iterate over 228 ensureDoesNotHaveUserRestriction(restriction, it.key) 229 } 230 } 231 232 mRemovedUserRestrictions.toMap().forEach { 233 it.value.toList().forEach { restriction -> 234 // below function modifies both collections that we iterate over 235 ensureHasUserRestriction(restriction, it.key) 236 } 237 } 238 239 mAddedUserRestrictions.clear() 240 mRemovedUserRestrictions.clear() 241 } 242 243 @CanIgnoreReturnValue 244 private fun tryClearUserRestrictionWithDeviceOwner(restriction: String): Boolean { 245 mDeviceOwnerComponent.ensureHasDeviceOwner() 246 val dpc: RemotePolicyManager = mDeviceOwnerComponent.deviceOwner() 247 try { 248 dpc.devicePolicyManager().clearUserRestriction(dpc.componentName(), restriction) 249 } catch (e: SecurityException) { 250 if (e.message!!.contains("cannot set user restriction")) { 251 return false 252 } 253 throw e 254 } 255 return true 256 } 257 258 @CanIgnoreReturnValue 259 private fun tryClearUserRestrictionWithProfileOwner( 260 onUser: UserReference, 261 restriction: String 262 ): Boolean { 263 mProfileOwnersComponent.ensureHasProfileOwner(user = onUser) 264 val dpc: RemotePolicyManager = mProfileOwnersComponent.profileOwner(onUser) 265 try { 266 dpc.devicePolicyManager().clearUserRestriction(dpc.componentName(), restriction) 267 } catch (e: SecurityException) { 268 if (e.message!!.contains("cannot set user restriction")) { 269 return false 270 } 271 throw e 272 } 273 return true 274 } 275 276 /** 277 * See [EnsureDoesNotHaveUserRestriction] 278 */ 279 fun ensureDoesNotHaveUserRestriction(restriction: String, onUser: UserType = UserType.ANY) { 280 if (onUser == UserType.ANY) { 281 for (userReference in users().all()) { 282 ensureDoesNotHaveUserRestriction(restriction, userReference) 283 } 284 return 285 } 286 ensureDoesNotHaveUserRestriction(restriction, userTypeResolver.toUser(onUser)) 287 } 288 289 companion object { 290 private const val LOG_TAG = "UserRestrictionsComponent" 291 } 292 } 293