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.cts.packagemanager.verify.domain.android
18 
19 import android.app.Instrumentation
20 import android.content.ComponentName
21 import android.content.Intent
22 import android.content.pm.PackageManager
23 import android.content.pm.verify.domain.DomainVerificationManager
24 import android.net.Uri
25 import androidx.test.platform.app.InstrumentationRegistry
26 import com.android.compatibility.common.util.ShellUtils
27 import com.android.cts.packagemanager.verify.domain.android.DomainUtils.DECLARING_PKG_1_COMPONENT
28 import com.android.cts.packagemanager.verify.domain.android.DomainUtils.DECLARING_PKG_2_COMPONENT
29 import com.android.cts.packagemanager.verify.domain.java.DomainUtils
30 import com.android.cts.packagemanager.verify.domain.java.DomainUtils.DECLARING_PKG_NAME_1
31 import com.android.cts.packagemanager.verify.domain.java.DomainUtils.DECLARING_PKG_NAME_2
32 import com.android.cts.packagemanager.verify.domain.java.DomainUtils.DOMAIN_UNHANDLED
33 import com.google.common.truth.Truth.assertThat
34 import com.google.common.truth.Truth.assertWithMessage
35 import org.junit.After
36 import org.junit.Assume.assumeTrue
37 import org.junit.Before
38 import org.junit.runner.RunWith
39 import org.junit.runners.Parameterized
40 
41 @RunWith(Parameterized::class)
42 abstract class DomainVerificationIntentTestBase(
43     private val domain: String,
44     private val assertResolvesToBrowsersInBefore: Boolean = true,
45     private val resetEnable: Boolean = false,
46 ) {
47 
48     companion object {
49 
50         @Parameterized.Parameters(name = "{0}")
51         @JvmStatic
parametersnull52         fun parameters() = IntentVariant.values()
53     }
54 
55     @Parameterized.Parameter(0)
56     lateinit var intentVariant: IntentVariant
57 
58     protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
59     protected val context = instrumentation.targetContext
60     protected val packageManager = context.packageManager
61     protected val userId = context.userId
62     protected val manager = context.getSystemService(DomainVerificationManager::class.java)!!
63 
64     protected lateinit var browsers: List<ComponentName>
65     protected lateinit var allResults: List<ComponentName>
66 
67     @Before
68     fun findBrowsers() {
69         SharedVerifications.reset(context, resetEnable)
70 
71         browsers = Intent(Intent.ACTION_VIEW, Uri.parse("https://$DOMAIN_UNHANDLED"))
72             .applyIntentVariant(intentVariant)
73             .let { context.packageManager.queryIntentActivities(it, 0) }
74             .map { it.activityInfo }
75             .map { ComponentName(it.packageName, it.name) }
76             .also { assumeTrue(it.isNotEmpty()) }
77 
78         val allResults = browsers.toMutableList()
79         try {
80             packageManager.getPackageInfo(DECLARING_PKG_NAME_1, 0)
81             allResults += DECLARING_PKG_1_COMPONENT
82         } catch (ignored: PackageManager.NameNotFoundException) {
83         }
84         try {
85             packageManager.getPackageInfo(DECLARING_PKG_NAME_2, 0)
86             allResults += DECLARING_PKG_2_COMPONENT
87         } catch (ignored: PackageManager.NameNotFoundException) {
88         }
89 
90         this.allResults = allResults
91 
92         if (assertResolvesToBrowsersInBefore) {
93             assertResolvesTo(browsers)
94         }
95     }
96 
97     @After
resetnull98     fun reset() {
99         SharedVerifications.reset(context, resetEnable)
100     }
101 
runShellCommandnull102     protected fun runShellCommand(vararg commands: String) = commands.forEach {
103         assertThat(ShellUtils.runShellCommand(it)).isEmpty()
104     }
105 
assertResolvesTonull106     protected fun assertResolvesTo(result: ComponentName, domain: String = this.domain) =
107         assertResolvesTo(listOf(result), domain)
108 
109     protected fun assertResolvesTo(
110         components: Collection<ComponentName>,
111         domain: String = this.domain,
112     ) {
113         val message = ShellUtils.runShellCommand(
114             "pm get-app-links --user ${context.userId} $DECLARING_PKG_NAME_1"
115         ) + "\n" + ShellUtils.runShellCommand(
116             "pm get-app-links --user ${context.userId} $DECLARING_PKG_NAME_2"
117         )
118 
119         val intent = Intent(Intent.ACTION_VIEW, Uri.parse("http://$domain"))
120             .addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION)
121             .applyIntentVariant(intentVariant)
122 
123         // Pass MATCH_DEFAULT_ONLY to mirror startActivity resolution
124         assertWithMessage(message)
125             .that(packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
126                 .map { it.activityInfo }
127                 .map { ComponentName(it.packageName, it.name) })
128             .containsExactlyElementsIn(components)
129 
130         if (intent.hasCategory(Intent.CATEGORY_DEFAULT)) {
131             // Verify explicit DEFAULT mirrors MATCH_DEFAULT_ONLY
132             assertWithMessage(message)
133                 .that(packageManager.queryIntentActivities(intent, 0)
134                 .map { it.activityInfo }
135                 .map { ComponentName(it.packageName, it.name) })
136                 .containsExactlyElementsIn(components)
137         } else {
138             val expected = allResults.filter {
139                 browsers.contains(it) || (isComponentEnabled(
140                     packageManager.getApplicationEnabledSetting(
141                         it.packageName
142                     )
143                 ) && isComponentEnabled(packageManager.getComponentEnabledSetting(it)))
144             }
145 
146             // Verify that non-DEFAULT match returns all results
147             assertWithMessage(message)
148                 .that(packageManager.queryIntentActivities(intent, 0)
149                     .map { it.activityInfo }
150                     .map { ComponentName(it.packageName, it.name) })
151                 .containsExactlyElementsIn(expected)
152         }
153     }
154 
isComponentEnablednull155     private fun isComponentEnabled(enabledSetting: Int) = when (enabledSetting) {
156         PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
157         PackageManager.COMPONENT_ENABLED_STATE_ENABLED -> true
158 
159         else -> false
160     }
161 
resetAppLinksnull162     fun resetAppLinks(packageName: String) {
163         runShellCommand(DomainUtils.resetAppLinks(packageName))
164     }
165 
setAppLinksnull166     fun setAppLinks(packageName: String, enabled: Boolean, vararg domains: String) {
167         val state = "STATE_APPROVED".takeIf { enabled } ?: "STATE_DENIED"
168         runShellCommand(DomainUtils.setAppLinks(packageName, state, *domains))
169     }
170 
setAppLinksAllowednull171     fun setAppLinksAllowed(packageName: String, userId: Int, enabled: Boolean) {
172         runShellCommand(DomainUtils.setAppLinksAllowed(packageName, userId, enabled))
173     }
174 
setAppLinksUserSelectionnull175     fun setAppLinksUserSelection(
176         packageName: String,
177         userId: Int,
178         enabled: Boolean,
179         vararg domains: String
180     ) {
181         runShellCommand(
182             DomainUtils.setAppLinksUserSelection(packageName, userId, enabled, *domains)
183         )
184     }
185 }
186