1 /* 2 * Copyright (C) 2018 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 android.packageinstaller.install.cts 17 18 import android.Manifest 19 import android.app.Activity.RESULT_CANCELED 20 import android.app.Activity.RESULT_OK 21 import android.content.Intent 22 import android.content.pm.InstallSourceInfo 23 import android.net.Uri 24 import android.platform.test.annotations.AppModeFull 25 import android.platform.test.annotations.RequiresFlagsDisabled 26 import android.platform.test.flag.junit.CheckFlagsRule 27 import android.platform.test.flag.junit.DeviceFlagsValueProvider 28 import android.platform.test.rule.ScreenRecordRule.ScreenRecord 29 import androidx.test.runner.AndroidJUnit4 30 import androidx.test.uiautomator.By 31 import androidx.test.uiautomator.Until 32 import com.android.bedstead.harrier.DeviceState 33 import com.android.bedstead.nene.TestApis 34 import com.android.bedstead.nene.userrestrictions.CommonUserRestrictions.DISALLOW_INSTALL_APPS 35 import com.android.compatibility.common.util.SystemUtil 36 import com.android.xts.root.annotations.RequireAdbRoot 37 import java.util.concurrent.TimeUnit 38 import org.junit.After 39 import org.junit.Assert.assertEquals 40 import org.junit.Assert.assertNotNull 41 import org.junit.Assert.assertNull 42 import org.junit.ClassRule 43 import org.junit.Ignore 44 import org.junit.Rule 45 import org.junit.Test 46 import org.junit.runner.RunWith 47 48 @RunWith(AndroidJUnit4::class) 49 @AppModeFull(reason = "Instant apps cannot install packages") 50 @ScreenRecord 51 class IntentTest : PackageInstallerTestBase() { 52 53 @get:Rule 54 val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() 55 56 companion object { 57 // An invalid package name that exceeds the maximum file name length. 58 const val LONG_PACKAGE_NAME = "android.packageinstaller.install.cts.invalidname." + 59 "27jEBRNRG3ozwBsGr1sVIM9U0bVTI2TdyIyeRkZgW4JrJefwNIBAmCg4AzqXiCvG6JjqA0u" + 60 "TCWSFu2YqAVxVdiRKAay19k5VFlSaM7QW9uhvlrLQqsTW01ofFzxNDbp2QfIFHZR6rebKzK" + 61 "Bz6byQFM0DYQnYMwFWXjWkMPNdqkRLykoFLyBup53G68k2n8wl27jEBRNRG3ozwBsGr" 62 const val NO_INSTALL_APPS_RESTRICTION_TEXT = "This user is not allowed to install apps" 63 const val DISABLED_LAUNCHER_ACTIVITY_PKG_NAME = 64 "android.packageinstaller.disabledlauncheractivity.cts" 65 const val INSTALL_SUCCESS_TEXT = "App installed." 66 67 @JvmField 68 @ClassRule 69 @Rule 70 val deviceState = DeviceState() 71 } 72 73 @After disableSecureFrpnull74 fun disableSecureFrp() { 75 setSecureFrp(false) 76 } 77 78 /** 79 * Check that we can install an app via a package-installer intent 80 */ 81 @Test confirmInstallationnull82 fun confirmInstallation() { 83 val installation = startInstallationViaIntent() 84 clickInstallerUIButton(INSTALL_BUTTON_ID) 85 86 // Install should have succeeded 87 assertEquals(RESULT_OK, installation.get(GLOBAL_TIMEOUT, TimeUnit.MILLISECONDS)) 88 assertInstalled() 89 var originatingPackageName: String? = null 90 SystemUtil.runWithShellPermissionIdentity( 91 { originatingPackageName = getInstallSourceInfo().originatingPackageName }, 92 Manifest.permission.INSTALL_PACKAGES 93 ) 94 assertNotNull(originatingPackageName) 95 assertEquals(context.packageName, originatingPackageName) 96 } 97 98 /** 99 * Install an app via a package-installer intent, but then cancel it when the package installer 100 * pops open. 101 */ 102 @Test cancelInstallationnull103 fun cancelInstallation() { 104 val installation = startInstallationViaIntent() 105 clickInstallerUIButton(CANCEL_BUTTON_ID) 106 107 // Install should have been aborted 108 assertEquals(RESULT_CANCELED, installation.get(GLOBAL_TIMEOUT, TimeUnit.MILLISECONDS)) 109 assertNotInstalled() 110 } 111 112 /** 113 * Install an app via a package-installer intent, and assign itself as the installer. 114 */ 115 @Test installWithCallingInstallerPackageNamenull116 fun installWithCallingInstallerPackageName() { 117 val intent = getInstallationIntent() 118 intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, context.opPackageName) 119 val installation = startInstallationViaIntent(intent) 120 clickInstallerUIButton(INSTALL_BUTTON_ID) 121 122 // Install should have succeeded, and system will use the given installer package name 123 // in EXTRA_INSTALLER_PACKAGE_NAME as the installer. 124 assertEquals(RESULT_OK, installation.get(GLOBAL_TIMEOUT, TimeUnit.MILLISECONDS)) 125 assertEquals(context.opPackageName, getInstallSourceInfo().installingPackageName) 126 } 127 128 /** 129 * Install an app via a package-installer intent, but assign another package as installer 130 * package name. 131 */ 132 @Ignore("b/317736655") 133 @Test installWithAnotherInstallerPackageNamenull134 fun installWithAnotherInstallerPackageName() { 135 val intent = getInstallationIntent() 136 intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, context.opPackageName + ".another") 137 val installation = startInstallationViaIntent(intent) 138 clickInstallerUIButton(INSTALL_BUTTON_ID) 139 140 // Install should have succeeded, but system won't use the given installer package name 141 // in EXTRA_INSTALLER_PACKAGE_NAME as the installer. 142 assertEquals(RESULT_OK, installation.get(GLOBAL_TIMEOUT, TimeUnit.MILLISECONDS)) 143 assertEquals( 144 getInstallSourceInfo().initiatingPackageName, 145 getInstallSourceInfo().installingPackageName 146 ) 147 } 148 149 /** 150 * Install an app via a package-installer intent, but assign an invalid installer 151 * package name which exceeds the maximum file name length. 152 */ 153 @Test installWithLongInstallerPackageNamenull154 fun installWithLongInstallerPackageName() { 155 val intent = getInstallationIntent() 156 intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, LONG_PACKAGE_NAME) 157 val installation = startInstallationViaIntent(intent) 158 clickInstallerUIButton(INSTALL_BUTTON_ID) 159 160 // Install should have succeeded, but system won't use the given installer package name 161 // in EXTRA_INSTALLER_PACKAGE_NAME as the installer. 162 assertEquals(RESULT_OK, installation.get(GLOBAL_TIMEOUT, TimeUnit.MILLISECONDS)) 163 assertEquals( 164 getInstallSourceInfo().initiatingPackageName, 165 getInstallSourceInfo().installingPackageName 166 ) 167 } 168 169 /** 170 * Make sure that an already installed app can be reinstalled via a "package" uri 171 */ 172 @Test reinstallViaPackageUrinull173 fun reinstallViaPackageUri() { 174 // Regular install 175 confirmInstallation() 176 177 // Reinstall 178 val intent = Intent(Intent.ACTION_INSTALL_PACKAGE) 179 intent.data = Uri.fromParts("package", TEST_APK_PACKAGE_NAME, null) 180 intent.putExtra(Intent.EXTRA_RETURN_RESULT, true) 181 intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION 182 183 val reinstall = installDialogStarter.activity.startActivityForResult(intent) 184 185 clickInstallerUIButton(INSTALL_BUTTON_ID) 186 187 // Install should have succeeded 188 assertEquals(RESULT_OK, reinstall.get(GLOBAL_TIMEOUT, TimeUnit.MILLISECONDS)) 189 assertInstalled() 190 } 191 192 /** 193 * Check that we can't install an app via a package-installer intent if Secure FRP is enabled 194 */ 195 @Test 196 @RequiresFlagsDisabled(android.security.Flags.FLAG_FRP_ENFORCEMENT) packageNotInstalledSecureFrpnull197 fun packageNotInstalledSecureFrp() { 198 setSecureFrp(true) 199 try { 200 val installation = startInstallationViaIntent() 201 clickInstallerUIButton(INSTALL_BUTTON_ID) 202 203 // Install should not have succeeded 204 assertNotInstalled() 205 } finally { 206 setSecureFrp(false) 207 } 208 } 209 210 @Test 211 @RequireAdbRoot(reason = "b/322830652 Required for TestApis to set user restriction") disallowInstallApps_installFailsnull212 fun disallowInstallApps_installFails() { 213 try { 214 TestApis.devicePolicy().userRestrictions().set(DISALLOW_INSTALL_APPS, true) 215 216 val installation = startInstallationViaIntent() 217 218 assertNotNull( 219 "Error dialog not shown", 220 uiDevice.wait( 221 Until.findObject(By.text(NO_INSTALL_APPS_RESTRICTION_TEXT)), 222 GLOBAL_TIMEOUT 223 ) 224 ) 225 clickInstallerUIButton(INSTALL_BUTTON_ID) 226 227 assertEquals(RESULT_CANCELED, installation.get(GLOBAL_TIMEOUT, TimeUnit.MILLISECONDS)) 228 } finally { 229 TestApis.devicePolicy().userRestrictions().set(DISALLOW_INSTALL_APPS, false) 230 } 231 } 232 233 @Test launcherActivityDisabled_cannotLaunchAppnull234 fun launcherActivityDisabled_cannotLaunchApp() { 235 val intent = Intent(Intent.ACTION_INSTALL_PACKAGE) 236 intent.data = Uri.fromParts("package", DISABLED_LAUNCHER_ACTIVITY_PKG_NAME, null) 237 intent.putExtra(Intent.EXTRA_RETURN_RESULT, false) 238 intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION 239 240 startInstallationViaIntent(intent) 241 clickInstallerUIButton(INSTALL_BUTTON_ID) 242 243 // Wait for success dialog 244 assertNotNull( 245 "Success dialog not shown", 246 uiDevice.wait(Until.findObject(By.text(INSTALL_SUCCESS_TEXT)), GLOBAL_TIMEOUT) 247 ) 248 249 // Since the dialog is already visible, no need to wait for long for the "Open" button. 250 assertNull( 251 "Open button should not be shown", 252 uiDevice.wait(Until.findObject(getBySelector(INSTALL_BUTTON_ID)), 5000) 253 ) 254 } 255 256 @Test installLowTargetSdkApp_installFailedVisiblenull257 fun installLowTargetSdkApp_installFailedVisible() { 258 // We want the InstallFailed dialog to be visible. Thus, pass EXTRA_RETURN_RESULT as false 259 val installIntent = getInstallationIntent(TEST_LOW_TARGET_SDK_APK_NAME) 260 installIntent.putExtra(Intent.EXTRA_RETURN_RESULT, false) 261 262 val installation = startInstallationViaIntent(installIntent) 263 clickInstallerUIButton(INSTALL_BUTTON_ID) 264 265 // GPP dialog should be shown. Clicking on "Got it" should cancel the installation 266 val gppDefaultBtn = uiDevice.wait(Until.findObject(By.text("Got it")), FIND_OBJECT_TIMEOUT) 267 if (gppDefaultBtn != null) { 268 gppDefaultBtn.click() 269 } 270 271 // Click the positive button on the InstallFailed dialog 272 clickInstallerUIButton(INSTALL_BUTTON_ID) 273 274 assertEquals(RESULT_CANCELED, installation.get(GLOBAL_TIMEOUT, TimeUnit.MILLISECONDS)) 275 assertNotInstalled(TEST_LOW_TARGET_SDK_APK_PACKAGE_NAME) 276 } 277 getInstallSourceInfonull278 private fun getInstallSourceInfo(): InstallSourceInfo { 279 return pm.getInstallSourceInfo(TEST_APK_PACKAGE_NAME) 280 } 281 } 282