• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2021 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 
17 package com.android.server.pm.test.verify.domain
18 
19 import android.content.Context
20 import android.content.Intent
21 import android.content.pm.PackageManager
22 import android.content.pm.SigningDetails
23 import android.content.pm.verify.domain.DomainVerificationManager
24 import android.content.pm.verify.domain.DomainVerificationState
25 import android.os.Build
26 import android.os.Process
27 import android.util.ArraySet
28 import android.util.IndentingPrintWriter
29 import android.util.SparseArray
30 import androidx.test.platform.app.InstrumentationRegistry
31 import com.android.server.pm.Computer
32 import com.android.server.pm.parsing.pkg.AndroidPackageInternal
33 import com.android.server.pm.pkg.AndroidPackage
34 import com.android.server.pm.pkg.PackageStateInternal
35 import com.android.server.pm.pkg.PackageUserStateInternal
36 import com.android.server.pm.pkg.component.ParsedActivityImpl
37 import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
38 import com.android.server.pm.verify.domain.DomainVerificationEnforcer
39 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
40 import com.android.server.pm.verify.domain.DomainVerificationService
41 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
42 import com.android.server.testutils.mockThrowOnUnmocked
43 import com.android.server.testutils.whenever
44 import org.junit.Test
45 import org.junit.runner.RunWith
46 import org.junit.runners.Parameterized
47 import org.mockito.Mockito.any
48 import org.mockito.Mockito.anyInt
49 import org.mockito.Mockito.anyLong
50 import org.mockito.Mockito.anyString
51 import org.mockito.Mockito.eq
52 import org.mockito.Mockito.mock
53 import org.mockito.Mockito.verifyNoMoreInteractions
54 import java.util.UUID
55 import java.util.concurrent.atomic.AtomicBoolean
56 import java.util.concurrent.atomic.AtomicInteger
57 import kotlin.test.assertFailsWith
58 import kotlin.test.fail
59 
60 @RunWith(Parameterized::class)
61 class DomainVerificationEnforcerTest {
62 
63     val context: Context = InstrumentationRegistry.getInstrumentation().context
64 
65     companion object {
66         private val INTERNAL_UIDS = listOf(Process.ROOT_UID, Process.SHELL_UID, Process.SYSTEM_UID)
67         private const val VERIFIER_UID = Process.FIRST_APPLICATION_UID + 1
68         private const val NON_VERIFIER_UID = Process.FIRST_APPLICATION_UID + 2
69 
70         private const val VISIBLE_PKG = "com.test.visible"
71         private val VISIBLE_UUID = UUID.fromString("8db01272-270d-4606-a3db-bb35228ff9a2")
72         private const val INVISIBLE_PKG = "com.test.invisible"
73         private val INVISIBLE_UUID = UUID.fromString("16dcb029-d96c-4a19-833a-4c9d72e2ebc3")
74 
75         @JvmStatic
76         @Parameterized.Parameters(name = "{0}")
77         fun parameters(): Array<Any> {
78             val visiblePkg = mockPkg(VISIBLE_PKG)
79             val visiblePkgState = mockPkgState(VISIBLE_PKG, VISIBLE_UUID)
80             val invisiblePkg = mockPkg(INVISIBLE_PKG)
81             val invisiblePkgState = mockPkgState(INVISIBLE_PKG, INVISIBLE_UUID)
82 
83             val makeEnforcer: (Context) -> DomainVerificationEnforcer = {
84                 DomainVerificationEnforcer(it).apply {
85                     setCallback(mockThrowOnUnmocked {
86                         whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
87                         whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
88                             true
89                         }
90                         whenever(doesUserExist(anyInt())) { (arguments[0] as Int) <= 1 }
91                     })
92                 }
93             }
94 
95             val makeService: (Context) -> Triple<AtomicInteger, AtomicInteger,
96                     DomainVerificationService> = {
97                 val callingUidInt = AtomicInteger(-1)
98                 val callingUserIdInt = AtomicInteger(-1)
99 
100                 val connection: DomainVerificationManagerInternal.Connection =
101                     mockThrowOnUnmocked {
102                         whenever(callingUid) { callingUidInt.get() }
103                         whenever(callingUserId) { callingUserIdInt.get() }
104                         whenever(snapshot()) {
105                             mockThrowOnUnmocked {
106                                 whenever(getPackageStateInternal(anyString())) {
107                                     when (getArgument<String>(0)) {
108                                         VISIBLE_PKG -> visiblePkgState
109                                         INVISIBLE_PKG -> invisiblePkgState
110                                         else -> null
111                                     }
112                                 }
113                             }
114                         }
115                         whenever(schedule(anyInt(), any()))
116                         whenever(scheduleWriteSettings())
117                         whenever(filterAppAccess(eq(VISIBLE_PKG), anyInt(), anyInt())) { false }
118                         whenever(filterAppAccess(eq(INVISIBLE_PKG), anyInt(), anyInt())) {
119                             true
120                         }
121                         whenever(doesUserExist(anyInt())) { (arguments[0] as Int) <= 1 }
122                     }
123                 val service = DomainVerificationService(
124                     it,
125                     mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
126                     mockThrowOnUnmocked {
127                         whenever(isChangeEnabledInternalNoLogging(anyLong(), any())) { true }
128                     }).apply {
129                     setConnection(connection)
130                 }
131 
132                 Triple(callingUidInt, callingUserIdInt, service)
133             }
134 
135             fun enforcer(
136                 type: Type,
137                 name: String,
138                 block: DomainVerificationEnforcer.(Params.Input<DomainVerificationEnforcer>) -> Any?
139             ) = Params(type, makeEnforcer, name) {
140                 it.target.block(it)
141             }
142 
143             fun service(
144                 type: Type,
145                 name: String,
146                 block: DomainVerificationService.(Params.Input<Triple<AtomicInteger, AtomicInteger, DomainVerificationService>>) -> Any?
147             ) = Params(type, makeService, name) {
148                 val (callingUidInt, callingUserIdInt, service) = it.target
149                 callingUidInt.set(it.callingUid)
150                 callingUserIdInt.set(it.callingUserId)
151                 service.proxy = it.proxy
152                 service.addPackage(visiblePkgState)
153                 service.addPackage(invisiblePkgState)
154                 service.block(it)
155             }
156 
157             return arrayOf(
158                 enforcer(Type.INTERNAL, "internal") {
159                     assertInternal(it.callingUid)
160                 },
161                 enforcer(Type.QUERENT, "approvedQuerent") {
162                     assertApprovedQuerent(it.callingUid, it.proxy)
163                 },
164                 enforcer(Type.VERIFIER, "approvedVerifier") {
165                     assertApprovedVerifier(it.callingUid, it.proxy)
166                 },
167                 enforcer(
168                     Type.SELECTION_QUERENT,
169                     "approvedUserStateQuerent"
170                 ) {
171                     assertApprovedUserStateQuerent(
172                         it.callingUid, it.callingUserId,
173                         it.targetPackageName, it.userId
174                     )
175                 },
176                 enforcer(
177                     Type.SELECTOR,
178                     "approvedUserSelector"
179                 ) {
180                     assertApprovedUserSelector(
181                         it.callingUid, it.callingUserId,
182                         it.targetPackageName, it.userId
183                     )
184                 },
185                 service(Type.INTERNAL, "setStatusInternalPackageName") {
186                     setDomainVerificationStatusInternal(
187                         it.targetPackageName,
188                         DomainVerificationState.STATE_SUCCESS,
189                         ArraySet(setOf("example.com"))
190                     )
191                 },
192                 service(Type.INTERNAL, "setUserStateInternal") {
193                     setDomainVerificationUserSelectionInternal(
194                         it.userId,
195                         it.targetPackageName,
196                         false,
197                         ArraySet(setOf("example.com"))
198                     )
199                 },
200                 service(Type.INTERNAL, "verifyPackages") {
201                     verifyPackages(listOf(it.targetPackageName), true)
202                 },
203                 service(Type.INTERNAL, "clearState") {
204                     clearDomainVerificationState(listOf(it.targetPackageName))
205                 },
206                 service(Type.INTERNAL, "clearUserStates") {
207                     clearUserStates(listOf(it.targetPackageName), it.userId)
208                 },
209                 service(Type.VERIFIER, "queryValidPackageNames") {
210                     queryValidVerificationPackageNames()
211                 },
212                 service(Type.QUERENT, "getInfo") {
213                     getDomainVerificationInfo(it.targetPackageName)
214                 },
215                 service(Type.QUERENT, "printState") {
216                     printState(mock(IndentingPrintWriter::class.java), null, null)
217                 },
218                 service(Type.QUERENT, "printStateInternal") {
219                     printState(mock(Computer::class.java), mock(IndentingPrintWriter::class.java),
220                         null, null)
221                 },
222                 service(Type.QUERENT, "printOwnersForPackage") {
223                     printOwnersForPackage(
224                         mock(IndentingPrintWriter::class.java),
225                         it.targetPackageName,
226                         it.userId
227                     )
228                 },
229                 service(Type.QUERENT, "printOwnersForDomains") {
230                     printOwnersForDomains(
231                         mock(IndentingPrintWriter::class.java),
232                         listOf("example.com"),
233                         it.userId
234                     )
235                 },
236                 service(Type.VERIFIER, "setStatus") {
237                     setDomainVerificationStatus(
238                         it.targetDomainSetId,
239                         setOf("example.com"),
240                         DomainVerificationState.STATE_SUCCESS
241                     )
242                 },
243                 service(Type.VERIFIER, "setStatusInternalUid") {
244                     setDomainVerificationStatusInternal(
245                         it.callingUid,
246                         it.targetDomainSetId,
247                         setOf("example.com"),
248                         DomainVerificationState.STATE_SUCCESS
249                     )
250                 },
251                 service(Type.SELECTOR_USER, "setLinkHandlingAllowedUserId") {
252                     setDomainVerificationLinkHandlingAllowed(it.targetPackageName, true, it.userId)
253                 },
254                 service(Type.SELECTION_QUERENT, "getUserStateUserId") {
255                     getDomainVerificationUserState(it.targetPackageName, it.userId)
256                 },
257                 service(Type.SELECTOR_USER, "setUserStateUserId") {
258                     setDomainVerificationUserSelection(
259                         it.targetDomainSetId,
260                         setOf("example.com"),
261                         true,
262                         it.userId
263                     )
264                 },
265                 service(Type.LEGACY_SELECTOR, "setLegacyUserState") {
266                     setLegacyUserState(
267                         it.targetPackageName, it.userId,
268                         PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER
269                     )
270                 },
271                 service(Type.LEGACY_QUERENT, "getLegacyUserState") {
272                     getLegacyState(it.targetPackageName, it.userId)
273                 },
274                 service(Type.OWNER_QUERENT_USER, "getOwnersForDomainUserId") {
275                     // Re-use package name, since the result itself isn't relevant
276                     getOwnersForDomain(it.targetPackageName, it.userId)
277                 },
278             )
279         }
280 
281         data class Params<T : Any>(
282             val type: Type,
283             val construct: (context: Context) -> T,
284             val name: String,
285             private val method: (Input<T>) -> Any?
286         ) {
287             override fun toString() = "${type}_$name"
288 
289             fun runMethod(
290                 target: Any,
291                 callingUid: Int,
292                 callingUserId: Int,
293                 userId: Int,
294                 targetPackageName: String,
295                 targetDomainSetId: UUID,
296                 proxy: DomainVerificationProxy
297             ): Any? = method(
298                 Input(
299                     @Suppress("UNCHECKED_CAST")
300                     target as T,
301                     callingUid,
302                     callingUserId,
303                     userId,
304                     targetPackageName,
305                     targetDomainSetId,
306                     proxy
307                 )
308             )
309 
310             data class Input<T>(
311                 val target: T,
312                 val callingUid: Int,
313                 val callingUserId: Int,
314                 val userId: Int,
315                 val targetPackageName: String,
316                 val targetDomainSetId: UUID,
317                 val proxy: DomainVerificationProxy
318             )
319         }
320 
321         fun mockPkg(packageName: String) = mockThrowOnUnmocked<AndroidPackageInternal> {
322             whenever(this.packageName) { packageName }
323             whenever(targetSdkVersion) { Build.VERSION_CODES.S }
324             whenever(isEnabled) { true }
325             whenever(activities) {
326                 listOf(
327                     ParsedActivityImpl().apply {
328                         addIntent(
329                             ParsedIntentInfoImpl()
330                                 .apply {
331                                 intentFilter.apply {
332                                     autoVerify = true
333                                     addAction(Intent.ACTION_VIEW)
334                                     addCategory(Intent.CATEGORY_BROWSABLE)
335                                     addCategory(Intent.CATEGORY_DEFAULT)
336                                     addDataScheme("https")
337                                     addDataAuthority("example.com", null)
338                                 }
339                             }
340                         )
341                     }
342                 )
343             }
344             whenever(signingDetails) { SigningDetails.UNKNOWN }
345         }
346 
347         fun mockPkgState(packageName: String, domainSetId: UUID) =
348             mockThrowOnUnmocked<PackageStateInternal> {
349                 whenever(this.packageName) { packageName }
350                 whenever(pkg) { mockPkg(packageName) }
351                 whenever(this.domainSetId) { domainSetId }
352                 whenever(getUserStateOrDefault(0)) { PackageUserStateInternal.DEFAULT }
353                 whenever(getUserStateOrDefault(1)) { PackageUserStateInternal.DEFAULT }
354                 whenever(userStates) {
355                     SparseArray<PackageUserStateInternal>().apply {
356                         this[0] = PackageUserStateInternal.DEFAULT
357                         this[1] = PackageUserStateInternal.DEFAULT
358                     }
359                 }
360                 whenever(isSystem) { false }
361                 whenever(signingDetails) { SigningDetails.UNKNOWN }
362             }
363     }
364 
365     @Parameterized.Parameter(0)
366     lateinit var params: Params<*>
367 
368     private val proxy: DomainVerificationProxy = mockThrowOnUnmocked {
369         whenever(isCallerVerifier(VERIFIER_UID)) { true }
370         whenever(isCallerVerifier(NON_VERIFIER_UID)) { false }
371         whenever(sendBroadcastForPackages(any()))
372     }
373 
374     @Test
375     fun verify() {
376         when (params.type) {
377             Type.INTERNAL -> internal()
378             Type.QUERENT -> approvedQuerent()
379             Type.VERIFIER -> approvedVerifier()
380             Type.SELECTION_QUERENT -> approvedUserStateQuerent(verifyCrossUser = true)
381             Type.SELECTOR -> approvedUserSelector(verifyCrossUser = false)
382             Type.SELECTOR_USER -> approvedUserSelector(verifyCrossUser = true)
383             Type.LEGACY_QUERENT -> legacyQuerent()
384             Type.LEGACY_SELECTOR -> legacyUserSelector()
385             Type.OWNER_QUERENT -> ownerQuerent(verifyCrossUser = false)
386             Type.OWNER_QUERENT_USER -> ownerQuerent(verifyCrossUser = true)
387         }.run { /*exhaust*/ }
388     }
389 
390     private fun internal() {
391         val context: Context = mockThrowOnUnmocked()
392         val target = params.construct(context)
393 
394         // Internal doesn't care about visibility
395         listOf(true, false).forEach { visible ->
396             INTERNAL_UIDS.forEach { runMethod(target, it, visible) }
397             assertFails { runMethod(target, VERIFIER_UID, visible) }
398             assertFails {
399                 runMethod(target, NON_VERIFIER_UID, visible)
400             }
401         }
402     }
403 
404     private fun approvedQuerent() {
405         val allowUserState = AtomicBoolean(false)
406         val allowPreferredApps = AtomicBoolean(false)
407         val allowQueryAll = AtomicBoolean(false)
408         val allowDump = AtomicBoolean(false)
409         val context: Context = mockThrowOnUnmocked {
410             initPermission(
411                 allowUserState,
412                 android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
413             )
414             initPermission(
415                 allowPreferredApps,
416                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS
417             )
418             initPermission(allowQueryAll, android.Manifest.permission.QUERY_ALL_PACKAGES)
419             initPermission(allowDump, android.Manifest.permission.DUMP)
420         }
421         val target = params.construct(context)
422 
423         INTERNAL_UIDS.forEach { runMethod(target, it) }
424 
425         verifyNoMoreInteractions(context)
426 
427         assertFails { runMethod(target, VERIFIER_UID) }
428         assertFails { runMethod(target, NON_VERIFIER_UID) }
429 
430         // Check that the verifier only needs QUERY_ALL to pass
431         allowQueryAll.set(true)
432         runMethod(target, VERIFIER_UID)
433         allowQueryAll.set(false)
434 
435         allowPreferredApps.set(true)
436 
437         assertFails { runMethod(target, NON_VERIFIER_UID) }
438 
439         allowUserState.set(true)
440 
441         assertFails { runMethod(target, NON_VERIFIER_UID) }
442 
443         allowQueryAll.set(true)
444 
445         assertFails { runMethod(target, NON_VERIFIER_UID) }
446 
447         allowDump.set(true)
448 
449         runMethod(target, NON_VERIFIER_UID)
450     }
451 
452     private fun approvedVerifier() {
453         val allowDomainVerificationAgent = AtomicBoolean(false)
454         val allowIntentVerificationAgent = AtomicBoolean(false)
455         val allowQueryAll = AtomicBoolean(false)
456         val context: Context = mockThrowOnUnmocked {
457             initPermission(
458                 allowDomainVerificationAgent,
459                 android.Manifest.permission.DOMAIN_VERIFICATION_AGENT
460             )
461             initPermission(
462                 allowIntentVerificationAgent,
463                 android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT
464             )
465             initPermission(allowQueryAll, android.Manifest.permission.QUERY_ALL_PACKAGES)
466         }
467         val target = params.construct(context)
468 
469         INTERNAL_UIDS.forEach { runMethod(target, it) }
470 
471         verifyNoMoreInteractions(context)
472 
473         assertFails { runMethod(target, VERIFIER_UID) }
474         assertFails { runMethod(target, NON_VERIFIER_UID) }
475 
476         allowDomainVerificationAgent.set(true)
477 
478         assertFails { runMethod(target, VERIFIER_UID) }
479         assertFails { runMethod(target, NON_VERIFIER_UID) }
480 
481         allowQueryAll.set(true)
482 
483         runMethod(target, VERIFIER_UID)
484         assertFails { runMethod(target, NON_VERIFIER_UID) }
485 
486         // Check that v1 verifiers are also allowed through
487         allowDomainVerificationAgent.set(false)
488         allowIntentVerificationAgent.set(true)
489 
490         runMethod(target, VERIFIER_UID)
491         assertFails { runMethod(target, NON_VERIFIER_UID) }
492     }
493 
494     private fun approvedUserStateQuerent(verifyCrossUser: Boolean) {
495         val allowInteractAcrossUsers = AtomicBoolean(false)
496         val context: Context = mockThrowOnUnmocked {
497             initPermission(
498                 allowInteractAcrossUsers,
499                 android.Manifest.permission.INTERACT_ACROSS_USERS
500             )
501         }
502         val target = params.construct(context)
503 
504         fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
505             // User selector makes no distinction by UID
506             val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
507             runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws)
508         }
509 
510         val callingUserId = 0
511         val notCallingUserId = 1
512 
513         runTestCases(callingUserId, callingUserId, throws = false)
514         if (verifyCrossUser) {
515             runTestCases(callingUserId, notCallingUserId, throws = true)
516         }
517 
518         allowInteractAcrossUsers.set(true)
519 
520         runTestCases(callingUserId, callingUserId, throws = false)
521         if (verifyCrossUser) {
522             runTestCases(callingUserId, notCallingUserId, throws = false)
523         }
524     }
525 
526     private fun approvedUserSelector(verifyCrossUser: Boolean) {
527         val allowUserState = AtomicBoolean(false)
528         val allowInteractAcrossUsers = AtomicBoolean(false)
529         val context: Context = mockThrowOnUnmocked {
530             initPermission(
531                 allowUserState,
532                 android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
533             )
534             initPermission(
535                 allowInteractAcrossUsers,
536                 android.Manifest.permission.INTERACT_ACROSS_USERS
537             )
538         }
539         val target = params.construct(context)
540 
541         fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
542             // User selector makes no distinction by UID
543             val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
544             runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws)
545         }
546 
547         val callingUserId = 0
548         val notCallingUserId = 1
549 
550         runTestCases(callingUserId, callingUserId, throws = true)
551         if (verifyCrossUser) {
552             runTestCases(callingUserId, notCallingUserId, throws = true)
553         }
554 
555         allowUserState.set(true)
556 
557         runTestCases(callingUserId, callingUserId, throws = false)
558         if (verifyCrossUser) {
559             runTestCases(callingUserId, notCallingUserId, throws = true)
560         }
561 
562         allowInteractAcrossUsers.set(true)
563 
564         runTestCases(callingUserId, callingUserId, throws = false)
565         if (verifyCrossUser) {
566             runTestCases(callingUserId, notCallingUserId, throws = false)
567         }
568     }
569 
570     private fun legacyUserSelector() {
571         val allowInteractAcrossUsers = AtomicBoolean(false)
572         val allowPreferredApps = AtomicBoolean(false)
573         val context: Context = mockThrowOnUnmocked {
574             initPermission(
575                 allowInteractAcrossUsers,
576                 android.Manifest.permission.INTERACT_ACROSS_USERS
577             )
578             initPermission(
579                 allowPreferredApps,
580                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS
581             )
582         }
583         val target = params.construct(context)
584 
585         fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
586             // Legacy makes no distinction by UID
587             val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
588             // The legacy selector does a silent failure when the user IDs don't match, so it
589             // cannot verify the non-existent user ID check, as it will not throw an Exception.
590             runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws,
591                 verifyUserIdCheck = false)
592         }
593 
594         val callingUserId = 0
595         val notCallingUserId = 1
596 
597         runTestCases(callingUserId, callingUserId, throws = true)
598         runTestCases(callingUserId, notCallingUserId, throws = true)
599 
600         allowPreferredApps.set(true)
601 
602         runTestCases(callingUserId, callingUserId, throws = false)
603         runTestCases(callingUserId, notCallingUserId, throws = true)
604 
605         allowInteractAcrossUsers.set(true)
606 
607         runTestCases(callingUserId, callingUserId, throws = false)
608         runTestCases(callingUserId, notCallingUserId, throws = false)
609     }
610 
611     private fun legacyQuerent() {
612         val allowInteractAcrossUsers = AtomicBoolean(false)
613         val allowInteractAcrossUsersFull = AtomicBoolean(false)
614         val context: Context = mockThrowOnUnmocked {
615             initPermission(
616                 allowInteractAcrossUsers,
617                 android.Manifest.permission.INTERACT_ACROSS_USERS
618             )
619             initPermission(
620                 allowInteractAcrossUsersFull,
621                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
622             )
623         }
624         val target = params.construct(context)
625 
626         // Legacy code can return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
627         // as an error code. This is distinct from the class level assertFails as unfortunately
628         // the same number, 0, is used in opposite contexts, where it does represent a failure
629         // for this legacy case, but not for the modern APIs.
630         fun assertFailsLegacy(block: () -> Any?) {
631             try {
632                 val value = block()
633                 if ((value as? Int)
634                         != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
635                 ) {
636                     throw AssertionError("Expected call to return false, was $value")
637                 }
638             } catch (e: SecurityException) {
639             } catch (e: PackageManager.NameNotFoundException) {
640                 // Any of these 2 exceptions are considered failures, which is expected
641             }
642         }
643 
644         fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
645             // Legacy makes no distinction by UID
646             val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
647             runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws,
648                     assertFailsMethod = ::assertFailsLegacy)
649         }
650 
651         val callingUserId = 0
652         val notCallingUserId = 1
653 
654         runTestCases(callingUserId, callingUserId, throws = false)
655         runTestCases(callingUserId, notCallingUserId, throws = true)
656 
657         // Legacy requires the _FULL permission, so this should continue to fail
658         allowInteractAcrossUsers.set(true)
659         runTestCases(callingUserId, callingUserId, throws = false)
660         runTestCases(callingUserId, notCallingUserId, throws = true)
661 
662         allowInteractAcrossUsersFull.set(true)
663         runTestCases(callingUserId, callingUserId, throws = false)
664         runTestCases(callingUserId, notCallingUserId, throws = false)
665     }
666 
667     private fun ownerQuerent(verifyCrossUser: Boolean) {
668         val allowQueryAll = AtomicBoolean(false)
669         val allowUserState = AtomicBoolean(false)
670         val allowInteractAcrossUsers = AtomicBoolean(false)
671         val context: Context = mockThrowOnUnmocked {
672             initPermission(
673                 allowQueryAll,
674                 android.Manifest.permission.QUERY_ALL_PACKAGES
675             )
676             initPermission(
677                 allowUserState,
678                 android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
679             )
680             initPermission(
681                 allowInteractAcrossUsers,
682                 android.Manifest.permission.INTERACT_ACROSS_USERS
683             )
684         }
685         val target = params.construct(context)
686 
687         fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
688             // Owner querent makes no distinction by UID
689             val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
690             runCrossUserMethod(allUids, target, callingUserId, targetUserId, throws,
691                 verifyInvisiblePkg = false)
692         }
693 
694         val callingUserId = 0
695         val notCallingUserId = 1
696 
697         runTestCases(callingUserId, callingUserId, throws = true)
698         if (verifyCrossUser) {
699             runTestCases(callingUserId, notCallingUserId, throws = true)
700         }
701 
702         allowQueryAll.set(true)
703 
704         runTestCases(callingUserId, callingUserId, throws = true)
705         if (verifyCrossUser) {
706             runTestCases(callingUserId, notCallingUserId, throws = true)
707         }
708 
709         allowUserState.set(true)
710 
711         runTestCases(callingUserId, callingUserId, throws = false)
712         if (verifyCrossUser) {
713             runTestCases(callingUserId, notCallingUserId, throws = true)
714         }
715 
716         allowQueryAll.set(false)
717 
718         runTestCases(callingUserId, callingUserId, throws = true)
719         if (verifyCrossUser) {
720             runTestCases(callingUserId, notCallingUserId, throws = true)
721         }
722 
723         allowQueryAll.set(true)
724         allowInteractAcrossUsers.set(true)
725 
726         runTestCases(callingUserId, callingUserId, throws = false)
727         if (verifyCrossUser) {
728             runTestCases(callingUserId, notCallingUserId, throws = false)
729         }
730     }
731 
732     private fun Context.initPermission(boolean: AtomicBoolean, permission: String) {
733         whenever(enforcePermission(eq(permission), anyInt(), anyInt(), anyString())) {
734             if (!boolean.get()) {
735                 throw SecurityException()
736             }
737         }
738         whenever(checkPermission(eq(permission), anyInt(), anyInt())) {
739             if (boolean.get()) {
740                 PackageManager.PERMISSION_GRANTED
741             } else {
742                 PackageManager.PERMISSION_DENIED
743             }
744         }
745     }
746 
747     private fun runMethod(
748         target: Any,
749         callingUid: Int,
750         visible: Boolean = true,
751         callingUserId: Int = 0,
752         userId: Int = 0
753     ): Any? {
754         val packageName = if (visible) VISIBLE_PKG else INVISIBLE_PKG
755         val uuid = if (visible) VISIBLE_UUID else INVISIBLE_UUID
756         return params.runMethod(target, callingUid, callingUserId, userId, packageName, uuid, proxy)
757     }
758 
759     private fun runCrossUserMethod(
760         allUids: Iterable<Int>,
761         target: Any,
762         callingUserId: Int,
763         targetUserId: Int,
764         throws: Boolean,
765         verifyUserIdCheck: Boolean = true,
766         verifyInvisiblePkg: Boolean = true,
767         assertFailsMethod: (() -> Any?) -> Unit = ::assertFails,
768     ) {
769         if (throws) {
770             allUids.forEach {
771                 assertFailsMethod {
772                     // When testing a non-user ID failure, send an invalid user ID.
773                     // This ensures the failure occurs before the user ID check is run.
774                     try {
775                         runMethod(target, it, visible = true, callingUserId, 100)
776                     } catch (e: SecurityException) {
777                         if (verifyUserIdCheck) {
778                             e.message?.let {
779                                 if (it.contains("user ID", ignoreCase = true)
780                                     || it.contains("100")) {
781                                     fail(
782                                         "Method should not check user existence before permissions"
783                                     )
784                                 }
785                             }
786                         }
787 
788                         // Rethrow to allow normal fail checking logic to run
789                         throw e
790                     }
791                 }
792             }
793         } else {
794             allUids.forEach {
795                 runMethod(target, it, visible = true, callingUserId, targetUserId)
796             }
797         }
798 
799         if (verifyInvisiblePkg) {
800             allUids.forEach {
801                 assertFailsMethod {
802                     runMethod(target, it, visible = false, callingUserId, targetUserId)
803                 }
804             }
805         }
806 
807         if (verifyUserIdCheck) {
808             // An invalid target user ID should always fail
809             allUids.forEach {
810                 assertFailsWith(SecurityException::class) {
811                     runMethod(target, it, visible = true, callingUserId, 100)
812                 }
813             }
814 
815             // An invalid calling user ID should always fail, although this cannot happen in prod
816             allUids.forEach {
817                 assertFailsWith(SecurityException::class) {
818                     runMethod(target, it, visible = true, 100, targetUserId)
819                 }
820             }
821         }
822     }
823 
824     private fun assertFails(block: () -> Any?) {
825         try {
826             val value = block()
827             // Some methods return false or an error rather than throwing, so check that as well
828             val valueAsBoolean = value as? Boolean
829             if (valueAsBoolean == false) {
830                 // Expected failure, do not throw
831                 return
832             }
833 
834             val valueAsInt = value as? Int
835             if (valueAsInt != null) {
836                 if (valueAsInt == DomainVerificationManager.STATUS_OK) {
837                     throw AssertionError("Expected call to return false, was $value")
838                 }
839             } else {
840                 throw AssertionError("Expected call to fail")
841             }
842         } catch (e: SecurityException) {
843         } catch (e: PackageManager.NameNotFoundException) {
844             // Any of these 2 exceptions are considered failures, which is expected
845         }
846     }
847 
848     enum class Type {
849         // System/shell only
850         INTERNAL,
851 
852         // INTERNAL || non-legacy domain verification agent || DUMP permission
853         QUERENT,
854 
855         // INTERNAL || domain verification agent
856         VERIFIER,
857 
858         // No permissions, allows all apps to view domain state for visible packages
859         SELECTION_QUERENT,
860 
861         // Holding the user setting permission
862         SELECTOR,
863 
864         // Holding the user setting permission, but targeting cross user
865         SELECTOR_USER,
866 
867         // Legacy required no permissions except when cross-user
868         LEGACY_QUERENT,
869 
870         // Holding the legacy preferred apps permission
871         LEGACY_SELECTOR,
872 
873         // Holding user setting permission, but not targeting a package
874         OWNER_QUERENT,
875 
876         // Holding user setting permission, but not targeting a package, but targeting cross user
877         OWNER_QUERENT_USER,
878     }
879 }
880