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