• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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