1 /* <lambda>null2 * 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 17 package android.packageinstaller.install.cts 18 19 import android.app.PendingIntent 20 import android.app.PendingIntent.FLAG_MUTABLE 21 import android.app.PendingIntent.FLAG_UPDATE_CURRENT 22 import android.content.BroadcastReceiver 23 import android.content.Context 24 import android.content.Intent 25 import android.content.Intent.EXTRA_INTENT 26 import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK 27 import android.content.Intent.FLAG_ACTIVITY_NEW_TASK 28 import android.content.IntentFilter 29 import android.content.pm.PackageInstaller 30 import android.content.pm.PackageInstaller.EXTRA_STATUS 31 import android.content.pm.PackageInstaller.STATUS_FAILURE_INVALID 32 import android.content.pm.PackageInstaller.STATUS_PENDING_USER_ACTION 33 import android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL 34 import android.content.pm.PackageManager 35 import android.support.test.uiautomator.By 36 import android.support.test.uiautomator.UiDevice 37 import android.support.test.uiautomator.Until 38 import androidx.core.content.FileProvider 39 import androidx.test.InstrumentationRegistry 40 import androidx.test.rule.ActivityTestRule 41 import com.android.compatibility.common.util.FutureResultActivity 42 import org.junit.After 43 import org.junit.Assert 44 import org.junit.Assume.assumeFalse 45 import org.junit.Assume.assumeTrue 46 import org.junit.Before 47 import org.junit.Rule 48 import java.io.File 49 import java.util.concurrent.CompletableFuture 50 import java.util.concurrent.LinkedBlockingQueue 51 import java.util.concurrent.TimeUnit 52 53 const val TEST_APK_NAME = "CtsEmptyTestApp.apk" 54 const val TEST_APK_PACKAGE_NAME = "android.packageinstaller.emptytestapp.cts" 55 const val TEST_APK_EXTERNAL_LOCATION = "/data/local/tmp/cts/packageinstaller" 56 const val INSTALL_ACTION_CB = "PackageInstallerTestBase.install_cb" 57 58 const val CONTENT_AUTHORITY = "android.packageinstaller.install.cts.fileprovider" 59 60 const val PACKAGE_INSTALLER_PACKAGE_NAME = "com.android.packageinstaller" 61 const val SYSTEM_PACKAGE_NAME = "android" 62 63 const val TIMEOUT = 60000L 64 const val APP_OP_STR = "REQUEST_INSTALL_PACKAGES" 65 66 const val INSTALL_INSTANT_APP = 0x00000800 67 68 open class PackageInstallerTestBase { 69 @get:Rule 70 val installDialogStarter = ActivityTestRule(FutureResultActivity::class.java) 71 72 private val context = InstrumentationRegistry.getTargetContext() 73 private val pm = context.packageManager 74 private val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) 75 private val apkFile = File(context.filesDir, TEST_APK_NAME) 76 77 /** If a status was received the value of the status, otherwise null */ 78 private var installSessionResult = LinkedBlockingQueue<Int>() 79 80 private val receiver = object : BroadcastReceiver() { 81 override fun onReceive(context: Context, intent: Intent) { 82 val status = intent.getIntExtra(EXTRA_STATUS, STATUS_FAILURE_INVALID) 83 84 if (status == STATUS_PENDING_USER_ACTION) { 85 val activityIntent = intent.getParcelableExtra<Intent>(EXTRA_INTENT) 86 activityIntent!!.addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) 87 installDialogStarter.activity.startActivityForResult(activityIntent) 88 } 89 90 installSessionResult.offer(status) 91 } 92 } 93 94 @Before 95 fun copyTestApk() { 96 File(TEST_APK_EXTERNAL_LOCATION, TEST_APK_NAME).copyTo(target = apkFile, overwrite = true) 97 } 98 99 @Before 100 fun wakeUpScreen() { 101 if (!uiDevice.isScreenOn) { 102 uiDevice.wakeUp() 103 } 104 uiDevice.executeShellCommand("wm dismiss-keyguard") 105 } 106 107 @Before 108 fun assertTestPackageNotInstalled() { 109 try { 110 context.packageManager.getPackageInfo(TEST_APK_PACKAGE_NAME, 0) 111 Assert.fail("Package should not be installed") 112 } catch (expected: PackageManager.NameNotFoundException) { 113 } 114 } 115 116 @Before 117 fun registerInstallResultReceiver() { 118 context.registerReceiver(receiver, IntentFilter(INSTALL_ACTION_CB)) 119 } 120 121 @Before 122 fun waitForUIIdle() { 123 uiDevice.waitForIdle() 124 } 125 126 /** 127 * Wait for session's install result and return it 128 */ 129 protected fun getInstallSessionResult(timeout: Long = TIMEOUT): Int? { 130 return installSessionResult.poll(timeout, TimeUnit.MILLISECONDS) 131 } 132 133 /** 134 * Start an installation via a session 135 */ 136 protected fun startInstallationViaSession(): CompletableFuture<Int> { 137 return startInstallationViaSession(0 /* installFlags */) 138 } 139 140 protected fun startInstallationViaSession(installFlags: Int): CompletableFuture<Int> { 141 val pi = pm.packageInstaller 142 143 // Create session 144 val sessionParam = PackageInstaller.SessionParams(MODE_FULL_INSTALL) 145 // Handle additional install flags 146 if (installFlags and INSTALL_INSTANT_APP != 0) { 147 sessionParam.setInstallAsInstantApp(true) 148 } 149 150 val sessionId = pi.createSession(sessionParam) 151 val session = pi.openSession(sessionId)!! 152 153 // Write data to session 154 apkFile.inputStream().use { fileOnDisk -> 155 session.openWrite(TEST_APK_NAME, 0, -1).use { sessionFile -> 156 fileOnDisk.copyTo(sessionFile) 157 } 158 } 159 160 // Commit session 161 val dialog = FutureResultActivity.doAndAwaitStart { 162 val pendingIntent = PendingIntent.getBroadcast(context, 0, Intent(INSTALL_ACTION_CB), 163 FLAG_UPDATE_CURRENT or FLAG_MUTABLE) 164 session.commit(pendingIntent.intentSender) 165 } 166 167 // The system should have asked us to launch the installer 168 Assert.assertEquals(STATUS_PENDING_USER_ACTION, getInstallSessionResult()) 169 170 return dialog 171 } 172 173 /** 174 * Start an installation via a session 175 */ 176 protected fun startInstallationViaIntent(): CompletableFuture<Int> { 177 val intent = Intent(Intent.ACTION_INSTALL_PACKAGE) 178 intent.data = FileProvider.getUriForFile(context, CONTENT_AUTHORITY, apkFile) 179 intent.putExtra(Intent.EXTRA_RETURN_RESULT, true) 180 intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION 181 182 return installDialogStarter.activity.startActivityForResult(intent) 183 } 184 185 fun assertInstalled() { 186 // Throws exception if package is not installed. 187 pm.getPackageInfo(TEST_APK_PACKAGE_NAME, 0) 188 } 189 190 fun assertNotInstalled() { 191 try { 192 pm.getPackageInfo(TEST_APK_PACKAGE_NAME, 0) 193 Assert.fail("Package should not be installed") 194 } catch (expected: PackageManager.NameNotFoundException) { 195 } 196 } 197 198 /** 199 * Click a button in the UI of the installer app 200 * 201 * @param resId The resource ID of the button to click 202 */ 203 fun clickInstallerUIButton(resId: String) { 204 uiDevice.wait(Until.findObject(By.res(SYSTEM_PACKAGE_NAME, resId)), TIMEOUT) 205 .click() 206 } 207 208 /** 209 * Sets the given secure setting to the provided value. 210 */ 211 fun setSecureSetting(secureSetting: String, value: Int) { 212 uiDevice.executeShellCommand("settings put secure $secureSetting $value") 213 } 214 215 fun setSecureFrp(secureFrp: Boolean) { 216 uiDevice.executeShellCommand("settings --user 0 " + 217 "put secure secure_frp_mode ${if (secureFrp) 1 else 0}") 218 } 219 220 @After 221 fun unregisterInstallResultReceiver() { 222 try { 223 context.unregisterReceiver(receiver) 224 } catch (ignored: IllegalArgumentException) { 225 } 226 } 227 228 @After 229 fun uninstallTestPackage() { 230 uiDevice.executeShellCommand("pm uninstall $TEST_APK_PACKAGE_NAME") 231 } 232 233 fun assumeWatch() { 234 assumeTrue("Test only valid for watch", hasFeatureWatch()) 235 } 236 237 fun assumeNotWatch() { 238 assumeFalse("Installing APKs not supported on watch", hasFeatureWatch()) 239 } 240 241 private fun hasFeatureWatch(): Boolean { 242 return pm.hasSystemFeature(PackageManager.FEATURE_WATCH) 243 } 244 } 245