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.server.pm.test.preverifieddomains 17 18 import android.app.UiAutomation 19 import android.content.ComponentName 20 import android.content.Context 21 import android.content.pm.Flags 22 import android.content.pm.PackageInstaller 23 import android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL 24 import android.content.pm.PackageManager 25 import android.os.Build 26 import android.platform.test.annotations.RequiresFlagsEnabled 27 import android.platform.test.flag.junit.CheckFlagsRule 28 import android.platform.test.flag.junit.DeviceFlagsValueProvider 29 import android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE 30 import androidx.test.ext.junit.runners.AndroidJUnit4 31 import androidx.test.platform.app.InstrumentationRegistry 32 import com.android.compatibility.common.util.DeviceConfigStateManager 33 import com.google.common.truth.Truth.assertThat 34 import org.junit.After 35 import org.junit.AfterClass 36 import org.junit.Assert.assertThrows 37 import org.junit.Assume.assumeTrue 38 import org.junit.Before 39 import org.junit.BeforeClass 40 import org.junit.Rule 41 import org.junit.Test 42 import org.junit.function.ThrowingRunnable 43 import org.junit.runner.RunWith 44 45 /** 46 * Pre-verified domains API tests. These tests require the device's default instant app 47 * installer to be disabled temporarily and is only able to run on ENG builds. 48 */ 49 @RunWith(AndroidJUnit4::class) 50 class PreVerifiedDomainsTests { 51 companion object { 52 private const val PROPERTY_PRE_VERIFIED_DOMAINS_COUNT_LIMIT = 53 "pre_verified_domains_count_limit" 54 private const val PROPERTY_PRE_VERIFIED_DOMAIN_LENGTH_LIMIT = 55 "pre_verified_domain_length_limit" 56 private const val TEMP_COUNT_LIMIT = 10 57 private const val TEMP_LENGTH_LIMIT = 15 58 private val testDomains = setOf("com.foo", "com.bar") 59 60 private val uiAutomation: UiAutomation = 61 InstrumentationRegistry.getInstrumentation().getUiAutomation() 62 private lateinit var packageManager: PackageManager 63 private var defaultInstantAppInstaller: ComponentName? = null 64 private lateinit var fakeInstantAppInstaller: ComponentName 65 66 @JvmStatic 67 @BeforeClass setupBeforeClassnull68 fun setupBeforeClass() { 69 val context = InstrumentationRegistry.getInstrumentation().getContext() 70 packageManager = context.packageManager 71 defaultInstantAppInstaller = packageManager.getInstantAppInstallerComponent() 72 fakeInstantAppInstaller = ComponentName( 73 context.packageName, 74 context.packageName + ".FakeInstantAppInstallerActivity") 75 // By disabling the original instant app installer, this test app becomes the instant 76 // app installer 77 uiAutomation.adoptShellPermissionIdentity() 78 try { 79 // Enable the fake instant app installer before disabling the default one 80 packageManager.setComponentEnabledSetting( 81 fakeInstantAppInstaller, 82 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 83 PackageManager.DONT_KILL_APP 84 ) 85 if (defaultInstantAppInstaller != null) { 86 packageManager.setComponentEnabledSetting( 87 defaultInstantAppInstaller!!, 88 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 89 0 90 ) 91 } 92 } finally { 93 uiAutomation.dropShellPermissionIdentity() 94 } 95 assertThat(fakeInstantAppInstaller).isEqualTo( 96 packageManager.getInstantAppInstallerComponent()) 97 } 98 99 @JvmStatic 100 @AfterClass restoreInstantAppInstallernull101 fun restoreInstantAppInstaller() { 102 uiAutomation.adoptShellPermissionIdentity() 103 try { 104 // Enable the original instant app installer before disabling the temporary one, so 105 // there won't be a time when the device doesn't have a valid instant app installer 106 if (defaultInstantAppInstaller != null) { 107 packageManager.setComponentEnabledSetting( 108 defaultInstantAppInstaller!!, 109 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 110 0 111 ) 112 } 113 // Be careful not to let this test process killed, or the test will be considered 114 // as failed 115 packageManager.setComponentEnabledSetting( 116 fakeInstantAppInstaller, 117 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 118 PackageManager.DONT_KILL_APP 119 ) 120 } finally { 121 uiAutomation.dropShellPermissionIdentity() 122 } 123 } 124 } 125 126 private lateinit var packageInstaller: PackageInstaller 127 private lateinit var context: Context 128 private lateinit var packageManager: PackageManager 129 private var mDefaultCountLimit: String? = null 130 private var mDefaultLengthLimit: String? = null 131 132 @JvmField 133 @Rule 134 val mCheckFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() 135 136 @Before setUpnull137 fun setUp() { 138 context = InstrumentationRegistry.getInstrumentation().getContext() 139 packageManager = context.packageManager 140 packageInstaller = packageManager.packageInstaller 141 mDefaultCountLimit = getLimitFromDeviceConfig(PROPERTY_PRE_VERIFIED_DOMAINS_COUNT_LIMIT) 142 mDefaultLengthLimit = getLimitFromDeviceConfig(PROPERTY_PRE_VERIFIED_DOMAIN_LENGTH_LIMIT) 143 } 144 145 @After cleanUpnull146 fun cleanUp() { 147 setLimitInDeviceConfig(PROPERTY_PRE_VERIFIED_DOMAINS_COUNT_LIMIT, mDefaultCountLimit) 148 setLimitInDeviceConfig(PROPERTY_PRE_VERIFIED_DOMAIN_LENGTH_LIMIT, mDefaultLengthLimit) 149 } 150 151 @RequiresFlagsEnabled(Flags.FLAG_SET_PRE_VERIFIED_DOMAINS) 152 @Test testSetPreVerifiedDomainsExceedsCountLimitnull153 fun testSetPreVerifiedDomainsExceedsCountLimit() { 154 // Temporarily change the count limit to a much smaller number so the test can exceed it 155 setLimitInDeviceConfig( 156 PROPERTY_PRE_VERIFIED_DOMAINS_COUNT_LIMIT, 157 TEMP_COUNT_LIMIT.toString() 158 ) 159 val domains = mutableSetOf<String>() 160 for (i in 0 until(TEMP_COUNT_LIMIT + 1)) { 161 domains.add("domain$i") 162 } 163 164 uiAutomation.adoptShellPermissionIdentity(android.Manifest.permission.ACCESS_INSTANT_APPS) 165 try { 166 assertThrows( 167 IllegalArgumentException::class.java, 168 ThrowingRunnable { 169 createSessionWithPreVerifiedDomains(domains) 170 } 171 ) 172 } finally { 173 uiAutomation.dropShellPermissionIdentity() 174 } 175 } 176 177 @RequiresFlagsEnabled(Flags.FLAG_SET_PRE_VERIFIED_DOMAINS) 178 @Test testSetPreVerifiedDomainsExceedsLengthLimitnull179 fun testSetPreVerifiedDomainsExceedsLengthLimit() { 180 // Temporarily change the count limit to a much smaller number so the test can exceed it 181 setLimitInDeviceConfig( 182 PROPERTY_PRE_VERIFIED_DOMAIN_LENGTH_LIMIT, 183 TEMP_LENGTH_LIMIT.toString() 184 ) 185 val invalidDomain = "a".repeat(TEMP_LENGTH_LIMIT + 1) 186 187 uiAutomation.adoptShellPermissionIdentity(android.Manifest.permission.ACCESS_INSTANT_APPS) 188 try { 189 assertThrows( 190 "Pre-verified domain: [" + 191 invalidDomain + " ] exceeds maximum length allowed: " + 192 TEMP_LENGTH_LIMIT, 193 IllegalArgumentException::class.java, 194 ThrowingRunnable { 195 createSessionWithPreVerifiedDomains(setOf(invalidDomain)) 196 } 197 ) 198 } finally { 199 uiAutomation.dropShellPermissionIdentity() 200 } 201 } 202 203 @RequiresFlagsEnabled(Flags.FLAG_SET_PRE_VERIFIED_DOMAINS) 204 @Test testSetAndGetPreVerifiedDomainsnull205 fun testSetAndGetPreVerifiedDomains() { 206 // Fake instant app installers can only work on ENG builds 207 assumeTrue("eng" == Build.TYPE) 208 var session: PackageInstaller.Session? = null 209 uiAutomation.adoptShellPermissionIdentity(android.Manifest.permission.ACCESS_INSTANT_APPS) 210 try { 211 val sessionId = createSessionWithPreVerifiedDomains(testDomains) 212 session = packageInstaller.openSession(sessionId) 213 assertThat(session.getPreVerifiedDomains()).isEqualTo(testDomains) 214 } finally { 215 uiAutomation.dropShellPermissionIdentity() 216 session?.abandon() 217 } 218 } 219 createSessionWithPreVerifiedDomainsnull220 private fun createSessionWithPreVerifiedDomains(domains: Set<String>): Int { 221 val sessionParam = PackageInstaller.SessionParams(MODE_FULL_INSTALL) 222 val sessionId = packageInstaller.createSession(sessionParam) 223 val session = packageInstaller.openSession(sessionId) 224 try { 225 session.setPreVerifiedDomains(domains) 226 } catch (e: Exception) { 227 session.abandon() 228 throw e 229 } 230 return sessionId 231 } 232 getLimitFromDeviceConfignull233 private fun getLimitFromDeviceConfig(propertyName: String): String? { 234 val stateManager = DeviceConfigStateManager( 235 context, 236 NAMESPACE_PACKAGE_MANAGER_SERVICE, 237 propertyName 238 ) 239 return stateManager.get() 240 } 241 setLimitInDeviceConfignull242 private fun setLimitInDeviceConfig(propertyName: String, value: String?) { 243 val stateManager = DeviceConfigStateManager( 244 context, 245 NAMESPACE_PACKAGE_MANAGER_SERVICE, 246 propertyName 247 ) 248 val currentValue = stateManager.get() 249 if (currentValue != value) { 250 // Only change the value if the current value is different 251 stateManager.set(value) 252 } 253 } 254 } 255