| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package android.packageinstaller.nopermission.cts |
| |
| import android.app.PendingIntent |
| import android.content.BroadcastReceiver |
| import android.content.Context |
| import android.content.Intent |
| import android.content.IntentFilter |
| import android.content.pm.PackageManager; |
| import android.content.pm.PackageInstaller |
| import android.content.pm.PackageInstaller.EXTRA_STATUS |
| import android.content.pm.PackageInstaller.STATUS_FAILURE_INVALID |
| import android.os.Build |
| import android.platform.test.annotations.AppModeFull |
| import androidx.test.InstrumentationRegistry |
| import androidx.test.filters.MediumTest |
| import androidx.test.runner.AndroidJUnit4 |
| import android.support.test.uiautomator.By |
| import android.support.test.uiautomator.UiDevice |
| import android.support.test.uiautomator.Until |
| import androidx.core.content.FileProvider |
| import org.junit.After |
| import org.junit.Assert.assertFalse |
| import org.junit.Assert.assertTrue |
| import org.junit.Before |
| import org.junit.Test |
| import org.junit.runner.RunWith |
| import org.junit.runners.model.Statement |
| import org.junit.runner.Description |
| import org.junit.Rule |
| import org.junit.rules.TestRule |
| import org.junit.AssumptionViolatedException |
| |
| import java.io.File |
| import java.lang.IllegalArgumentException |
| |
| private const val TEST_APK_NAME = "CtsEmptyTestApp.apk" |
| private const val TEST_APK_PACKAGE_NAME = "android.packageinstaller.emptytestapp.cts" |
| private const val TEST_APK_EXTERNAL_LOCATION = "/data/local/tmp/cts/nopermission" |
| private const val CONTENT_AUTHORITY = "android.packageinstaller.nopermission.cts.fileprovider" |
| private const val PACKAGE_INSTALLER_PACKAGE_NAME = "com.android.packageinstaller" |
| private const val INSTALL_CONFIRM_TEXT_ID = "install_confirm_question" |
| private const val WM_DISMISS_KEYGUARD_COMMAND = "wm dismiss-keyguard" |
| |
| private const val ACTION = "NoPermissionTests.install_cb" |
| |
| private const val WAIT_FOR_UI_TIMEOUT = 5000L |
| |
| @RunWith(AndroidJUnit4::class) |
| @MediumTest |
| @AppModeFull |
| class NoPermissionTests { |
| private var context = InstrumentationRegistry.getTargetContext() |
| private var pm = context.packageManager |
| private var packageName = context.packageName |
| private var apkFile = File(context.filesDir, TEST_APK_NAME) |
| private var uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) |
| private var isWatch: Boolean = pm.hasSystemFeature(PackageManager.FEATURE_WATCH) |
| |
| private val receiver = object : BroadcastReceiver() { |
| override fun onReceive(context: Context, intent: Intent) { |
| val status = intent.getIntExtra(EXTRA_STATUS, STATUS_FAILURE_INVALID) |
| |
| if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) { |
| val activityIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT) |
| activityIntent!!.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) |
| context.startActivity(activityIntent) |
| } |
| } |
| } |
| |
| @get:Rule |
| val testRule: TestRule = object :TestRule { |
| override fun apply(base: Statement, description: Description?) |
| = NewStatement(base) |
| inner class NewStatement(private val base: Statement) : Statement() { |
| @Throws(Throwable::class) |
| override fun evaluate() { |
| if (isWatch) { |
| throw AssumptionViolatedException("Install/uninstall feature is not supported on WearOs"); |
| } |
| base.evaluate() |
| } |
| } |
| } |
| |
| @Before |
| fun wakeUpScreen() { |
| if (!uiDevice.isScreenOn) { |
| uiDevice.wakeUp() |
| } |
| uiDevice.executeShellCommand(WM_DISMISS_KEYGUARD_COMMAND) |
| } |
| |
| @Before |
| fun copyTestApk() { |
| File(TEST_APK_EXTERNAL_LOCATION, TEST_APK_NAME).copyTo(target = apkFile, overwrite = true) |
| } |
| |
| @Before |
| fun registerInstallResultReceiver() { |
| context.registerReceiver(receiver, IntentFilter(ACTION)) |
| } |
| |
| @Before |
| fun waitForUIIdle() { |
| uiDevice.waitForIdle() |
| } |
| |
| private fun launchPackageInstallerViaIntent() { |
| val intent = Intent(Intent.ACTION_INSTALL_PACKAGE) |
| intent.data = FileProvider.getUriForFile(context, CONTENT_AUTHORITY, apkFile) |
| intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK |
| context.startActivity(intent) |
| } |
| |
| private fun launchPackageInstallerViaSession() { |
| val pi = pm.packageInstaller |
| |
| // Create session |
| val sessionId = pi.createSession(PackageInstaller.SessionParams( |
| PackageInstaller.SessionParams.MODE_FULL_INSTALL)) |
| val session = pi.openSession(sessionId)!! |
| |
| // Write data to session |
| File(TEST_APK_EXTERNAL_LOCATION, TEST_APK_NAME).inputStream().use { fileOnDisk -> |
| session.openWrite(TEST_APK_NAME, 0, -1).use { sessionFile -> |
| fileOnDisk.copyTo(sessionFile) |
| } |
| } |
| |
| // Commit session |
| val pendingIntent = PendingIntent.getBroadcast(context, 0, Intent(ACTION), |
| PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE) |
| session.commit(pendingIntent.intentSender) |
| } |
| |
| private fun assertInstallSucceeded(errorMessage: String) { |
| val selector = By.res(PACKAGE_INSTALLER_PACKAGE_NAME, INSTALL_CONFIRM_TEXT_ID) |
| assertTrue(errorMessage, uiDevice.wait(Until.hasObject(selector), WAIT_FOR_UI_TIMEOUT)) |
| uiDevice.pressBack() |
| } |
| |
| private fun assertInstallFailed(errorMessage: String) { |
| val selector = By.res(PACKAGE_INSTALLER_PACKAGE_NAME, INSTALL_CONFIRM_TEXT_ID) |
| assertFalse(errorMessage, uiDevice.wait(Until.hasObject(selector), WAIT_FOR_UI_TIMEOUT)) |
| uiDevice.pressBack() |
| } |
| |
| @Test |
| fun noPermissionsTestIntent() { |
| launchPackageInstallerViaIntent() |
| |
| if (pm.getPackageInfo(packageName, 0).applicationInfo.targetSdkVersion |
| >= Build.VERSION_CODES.O) { |
| assertInstallFailed("Package Installer UI should not appear") |
| } else { |
| assertInstallSucceeded("Package Installer UI should appear") |
| } |
| } |
| |
| @Test |
| fun noPermissionsTestSession() { |
| launchPackageInstallerViaSession() |
| |
| if (pm.getPackageInfo(packageName, 0).applicationInfo.targetSdkVersion |
| >= Build.VERSION_CODES.O) { |
| assertInstallFailed("Package Installer UI should not appear") |
| } else { |
| assertInstallSucceeded("Package Installer UI should appear") |
| } |
| } |
| |
| @After |
| fun unregisterInstallResultReceiver() { |
| try { |
| context.unregisterReceiver(receiver) |
| } catch (ignored: IllegalArgumentException) { |
| } |
| } |
| |
| @After |
| fun uninstallTestPackage() { |
| uiDevice.executeShellCommand("pm uninstall $TEST_APK_PACKAGE_NAME") |
| } |
| } |