• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package android.packageinstaller.nopermission.cts
17 
18 import android.app.AppOpsManager.MODE_ALLOWED
19 import android.app.PendingIntent
20 import android.content.BroadcastReceiver
21 import android.content.Context
22 import android.content.Intent
23 import android.content.IntentFilter
24 import android.content.pm.PackageInstaller
25 import android.content.pm.PackageInstaller.EXTRA_STATUS
26 import android.content.pm.PackageInstaller.STATUS_FAILURE_INVALID
27 import android.os.Build
28 import android.platform.test.annotations.AppModeFull
29 import androidx.test.InstrumentationRegistry
30 import androidx.test.filters.MediumTest
31 import androidx.test.runner.AndroidJUnit4
32 import android.support.test.uiautomator.By
33 import android.support.test.uiautomator.UiDevice
34 import android.support.test.uiautomator.Until
35 import androidx.core.content.FileProvider
36 import com.android.compatibility.common.util.AppOpsUtils
37 import org.junit.After
38 import org.junit.Assert.assertFalse
39 import org.junit.Assert.assertTrue
40 import org.junit.Before
41 import org.junit.Test
42 import org.junit.runner.RunWith
43 import java.io.File
44 import java.lang.IllegalArgumentException
45 
46 private const val TEST_APK_NAME = "CtsEmptyTestApp.apk"
47 private const val TEST_APK_PACKAGE_NAME = "android.packageinstaller.emptytestapp.cts"
48 private const val TEST_APK_EXTERNAL_LOCATION = "/data/local/tmp/cts/nopermission"
49 private const val CONTENT_AUTHORITY = "android.packageinstaller.nopermission.cts.fileprovider"
50 private const val PACKAGE_INSTALLER_PACKAGE_NAME = "com.android.packageinstaller"
51 private const val INSTALL_CONFIRM_TEXT_ID = "install_confirm_question"
52 private const val WM_DISMISS_KEYGUARD_COMMAND = "wm dismiss-keyguard"
53 
54 private const val ACTION = "NoPermissionTests.install_cb"
55 
56 private const val WAIT_FOR_UI_TIMEOUT = 5000L
57 private const val APP_OP_STR = "REQUEST_INSTALL_PACKAGES"
58 
59 @RunWith(AndroidJUnit4::class)
60 @MediumTest
61 @AppModeFull
62 class NoPermissionTests {
63     private var context = InstrumentationRegistry.getTargetContext()
64     private var pm = context.packageManager
65     private var packageName = context.packageName
66     private var apkFile = File(context.filesDir, TEST_APK_NAME)
67     private var uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
68 
69     private val receiver = object : BroadcastReceiver() {
70         override fun onReceive(context: Context, intent: Intent) {
71             val status = intent.getIntExtra(EXTRA_STATUS, STATUS_FAILURE_INVALID)
72 
73             if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
74                 val activityIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
75                 activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
76                 context.startActivity(activityIntent)
77             }
78         }
79     }
80 
81     @Before
82     fun wakeUpScreen() {
83         if (!uiDevice.isScreenOn) {
84             uiDevice.wakeUp()
85         }
86         uiDevice.executeShellCommand(WM_DISMISS_KEYGUARD_COMMAND)
87     }
88 
89     @Before
90     fun copyTestApk() {
91         File(TEST_APK_EXTERNAL_LOCATION, TEST_APK_NAME).copyTo(target = apkFile, overwrite = true)
92     }
93 
94     @Before
95     fun allowToInstallPackages() {
96         // To make sure no other blocking dialogs appear
97         AppOpsUtils.setOpMode(context.packageName, APP_OP_STR, MODE_ALLOWED)
98     }
99 
100     @Before
101     fun registerInstallResultReceiver() {
102         context.registerReceiver(receiver, IntentFilter(ACTION))
103     }
104 
105     @Before
106     fun waitForUIIdle() {
107         uiDevice.waitForIdle()
108     }
109 
110     private fun launchPackageInstallerViaIntent() {
111         val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
112         intent.data = FileProvider.getUriForFile(context, CONTENT_AUTHORITY, apkFile)
113         intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK
114         context.startActivity(intent)
115     }
116 
117     private fun launchPackageInstallerViaSession() {
118         val pi = pm.packageInstaller
119 
120         // Create session
121         val sessionId = pi.createSession(PackageInstaller.SessionParams(
122                 PackageInstaller.SessionParams.MODE_FULL_INSTALL))
123         val session = pi.openSession(sessionId)!!
124 
125         // Write data to session
126         File(TEST_APK_EXTERNAL_LOCATION, TEST_APK_NAME).inputStream().use { fileOnDisk ->
127             session.openWrite(TEST_APK_NAME, 0, -1).use { sessionFile ->
128                 fileOnDisk.copyTo(sessionFile)
129             }
130         }
131 
132         // Commit session
133         val pendingIntent = PendingIntent.getBroadcast(context, 0, Intent(ACTION),
134                 PendingIntent.FLAG_UPDATE_CURRENT)
135         session.commit(pendingIntent.intentSender)
136     }
137 
138     private fun assertInstallSucceeded(errorMessage: String) {
139         val selector = By.res(PACKAGE_INSTALLER_PACKAGE_NAME, INSTALL_CONFIRM_TEXT_ID)
140         assertTrue(errorMessage, uiDevice.wait(Until.hasObject(selector), WAIT_FOR_UI_TIMEOUT))
141         uiDevice.pressBack()
142     }
143 
144     private fun assertInstallFailed(errorMessage: String) {
145         val selector = By.res(PACKAGE_INSTALLER_PACKAGE_NAME, INSTALL_CONFIRM_TEXT_ID)
146         assertFalse(errorMessage, uiDevice.wait(Until.hasObject(selector), WAIT_FOR_UI_TIMEOUT))
147         uiDevice.pressBack()
148     }
149 
150     @Test
151     fun noPermissionsTestIntent() {
152         launchPackageInstallerViaIntent()
153 
154         if (pm.getPackageInfo(packageName, 0).applicationInfo.targetSdkVersion
155                 >= Build.VERSION_CODES.O) {
156             assertInstallFailed("Package Installer UI should not appear")
157         } else {
158             assertInstallSucceeded("Package Installer UI should appear")
159         }
160     }
161 
162     @Test
163     fun noPermissionsTestSession() {
164         launchPackageInstallerViaSession()
165 
166         if (pm.getPackageInfo(packageName, 0).applicationInfo.targetSdkVersion
167                 >= Build.VERSION_CODES.O) {
168             assertInstallFailed("Package Installer UI should not appear")
169         } else {
170             assertInstallSucceeded("Package Installer UI should appear")
171         }
172     }
173 
174     @After
175     fun unregisterInstallResultReceiver() {
176         try {
177             context.unregisterReceiver(receiver)
178         } catch (ignored: IllegalArgumentException) {
179         }
180     }
181 
182     @After
183     fun uninstallTestPackage() {
184         uiDevice.executeShellCommand("pm uninstall $TEST_APK_PACKAGE_NAME")
185     }
186 }
187