• 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.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