• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 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.content.pm.PackageInfo
20 import android.content.pm.PackageInstaller
21 import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DEFAULT
22 import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DENIED
23 import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED
24 import android.content.pm.PackageManager
25 import android.content.pm.PermissionInfo
26 import android.Manifest
27 import android.content.AttributionSource
28 import android.permission.PermissionManager
29 import android.platform.test.annotations.AppModeFull
30 import com.android.compatibility.common.util.SystemUtil
31 import com.google.common.truth.Truth.assertThat
32 import com.google.common.truth.Truth.assertWithMessage
33 import java.io.File
34 import kotlin.test.assertFailsWith
35 import org.junit.BeforeClass
36 import org.junit.Before
37 import org.junit.Test
38 import org.junit.runner.RunWith
39 import org.junit.runners.Parameterized
40 
41 @RunWith(Parameterized::class)
42 @AppModeFull(reason = "Instant apps cannot create installer sessions")
43 class SessionParamsPermissionStateTest : PackageInstallerTestBase() {
44 
45     companion object {
46         private const val FULL_SCREEN_INTENT_APK = "CtsEmptyTestApp_FullScreenIntent.apk"
47         private const val NON_EXISTENT_PERMISSION = "android.cts.NON_EXISTENT_PERMISSION"
48         private val GET_PERMISSIONS_FLAGS =
49             PackageManager.PackageInfoFlags.of(PackageManager.GET_PERMISSIONS.toLong())
50 
51         private val permissionManager = context.getSystemService(PermissionManager::class.java)!!
52 
53         private val isFsiDefaultGranted by lazy {
54             context.packageManager
55                 .getPermissionInfo(Manifest.permission.USE_FULL_SCREEN_INTENT, 0)
56                 .protection == PermissionInfo.PROTECTION_NORMAL
57         }
58 
59         @JvmStatic
60         @BeforeClass
61         fun copySubclassTestApk() {
62             File(TEST_APK_LOCATION, FULL_SCREEN_INTENT_APK).copyTo(
63                 target = File(context.filesDir, FULL_SCREEN_INTENT_APK),
64                 overwrite = true
65             )
66         }
67 
68         @JvmStatic
69         @BeforeClass
70         fun verifyNoGrantRuntimePermission() {
71             // Ensure the test doesn't have the grant runtime permission
72             assertThat(
73                 context.checkSelfPermission(
74                     Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS
75                 )
76             ).isEqualTo(PackageManager.PERMISSION_DENIED)
77         }
78 
79         @JvmStatic
80         @Parameterized.Parameters(name = "{0}")
81         fun parameters() = listOf(
82             // Check that installer is allowed to explicitly grant FSI
83             Params(
84                 name = "fullScreenIntentGranted",
85                 finalPermissionState = mapOf(Manifest.permission.USE_FULL_SCREEN_INTENT to true)
86             ) {
87                 setFinalState(
88                     Manifest.permission.USE_FULL_SCREEN_INTENT,
89                     PERMISSION_STATE_GRANTED
90                 )
91             },
92 
93             // Check that installer is allowed to explicitly deny FSI
94             Params(
95                 name = "fullScreenIntentDenied",
96                 finalPermissionState = mapOf(Manifest.permission.USE_FULL_SCREEN_INTENT to false)
97             ) {
98                 setFinalState(
99                     Manifest.permission.USE_FULL_SCREEN_INTENT,
100                     PERMISSION_STATE_DENIED
101                 )
102             },
103 
104             // Check that a vanilla session automatically grants/denies FSI to an app declaring it
105             Params(
106                 name = "fullScreenIntentDefault",
107                 finalPermissionState = mapOf(
108                     Manifest.permission.USE_FULL_SCREEN_INTENT to isFsiDefaultGranted,
109                 ),
110             ) {
111                 setFinalState(
112                     Manifest.permission.USE_FULL_SCREEN_INTENT,
113                     PERMISSION_STATE_DEFAULT
114                 )
115             },
116 
117             // Check that the installer doesn't affect an app that doesn't declare FSI
118             listOf(
119                 PERMISSION_STATE_GRANTED,
120                 PERMISSION_STATE_DENIED,
121                 PERMISSION_STATE_DEFAULT,
122             ).map {
123                 Params(
124                     name = "fullScreenIntentWithoutAppDeclaration${stateToName(it)}",
125                     success = true,
126                     testApkName = TEST_APK_NAME,
127                     finalPermissionState = mapOf(Manifest.permission.USE_FULL_SCREEN_INTENT to null)
128                 ) { setFinalState(Manifest.permission.USE_FULL_SCREEN_INTENT, it) }
129             },
130 
131             // Check that granting/denying a real runtime permission isn't allowed
132             listOf(
133                 PERMISSION_STATE_GRANTED,
134                 PERMISSION_STATE_DENIED,
135             ).map {
136                 Params(
137                     name = "runtimePermission${stateToName(it)}",
138                     success = false,
139                 ) { setFinalState(Manifest.permission.READ_CALENDAR, it) }
140             },
141 
142             // Check that setting a runtime permission to default is ignored (and thus succeeds)
143             Params(
144                 name = "runtimePermissionDefault",
145                 finalPermissionState = mapOf(
146                     Manifest.permission.USE_FULL_SCREEN_INTENT to isFsiDefaultGranted,
147                     Manifest.permission.READ_CALENDAR to false,
148                 ),
149             ) { setFinalState(Manifest.permission.READ_CALENDAR, PERMISSION_STATE_DEFAULT) },
150 
151             // Check that setting a permission not known to the system isn't allowed
152             listOf(
153                 PERMISSION_STATE_GRANTED,
154                 PERMISSION_STATE_DENIED,
155             ).map {
156                 Params(
157                     name = "unknownPermission${stateToName(it)}",
158                     success = false,
159                 ) { setFinalState(NON_EXISTENT_PERMISSION, it) }
160             },
161 
162             // Check that setting an unknown permission to default is ignored (and thus succeeds)
163             Params(
164                 name = "unknownPermissionDefault",
165                 finalPermissionState = mapOf(
166                     Manifest.permission.USE_FULL_SCREEN_INTENT to isFsiDefaultGranted,
167                 ),
168             ) { setFinalState(NON_EXISTENT_PERMISSION, PERMISSION_STATE_DEFAULT) },
169 
170             // Check that setting a runtime/unknown permission with the right permission is allowed
171             Params(
172                 name = "runtimePermissionGranted",
173                 withInstallGrantRuntimePermissions = true,
174                 finalPermissionState = mapOf(
175                     Manifest.permission.USE_FULL_SCREEN_INTENT to isFsiDefaultGranted,
176                     Manifest.permission.READ_CALENDAR to true,
177                     NON_EXISTENT_PERMISSION to null,
178                 ),
179             ) {
180                 setFinalState(Manifest.permission.READ_CALENDAR, PERMISSION_STATE_GRANTED)
181                     .setFinalState(NON_EXISTENT_PERMISSION, PERMISSION_STATE_GRANTED)
182             },
183         ).flatMap { if (it is Collection<*>) it else listOf(it) }
184 
185         data class Params(
186             val name: String,
187             var success: Boolean = true,
188             val testApkName: String = FULL_SCREEN_INTENT_APK,
189             val withInstallGrantRuntimePermissions: Boolean = false,
190             val finalPermissionState: Map<String, Boolean?> = emptyMap(),
191             val paramsBlock: PackageInstaller.SessionParams.() -> Unit = {},
192         ) {
193             override fun toString() = "${name}_${if (success) "Success" else "Failure"}"
194         }
195 
196         private fun stateToName(state: Int) = when (state) {
197             PERMISSION_STATE_GRANTED -> "Granted"
198             PERMISSION_STATE_DENIED -> "Denied"
199             PERMISSION_STATE_DEFAULT -> "Default"
200             else -> throw IllegalArgumentException("Unknown state: $state")
201         }
202 
203         /** Cycles through all of the states to make sure only latest is kept */
204         private fun PackageInstaller.SessionParams.setFinalState(
205             permissionName: String,
206             state: Int
207         ) = setPermissionState(permissionName, PERMISSION_STATE_GRANTED)
208             .setPermissionState(permissionName, PERMISSION_STATE_DENIED)
209             .setPermissionState(permissionName, PERMISSION_STATE_DEFAULT)
210             .setPermissionState(permissionName, state)
211     }
212 
213     @Parameterized.Parameter(0)
214     lateinit var params: Params
215 
216     @Before
217     fun validateParams() {
218         if (!params.success) {
219             // Ensure that a test case expecting failure has no permission state to assert
220             assertThat(params.finalPermissionState).isEmpty()
221         }
222     }
223 
224     @Test
225     fun checkInstall() {
226         val block = {
227             startInstallationViaSession(
228                 apkName = params.testApkName,
229                 paramsBlock = params.paramsBlock,
230             )
231         }
232 
233         if (!params.success) {
234             assertFailsWith(SecurityException::class) { block() }
235             return
236         } else if (params.withInstallGrantRuntimePermissions) {
237             SystemUtil.callWithShellPermissionIdentity(
238                 { block() },
239                 Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS
240             )
241         } else {
242             block()
243         }
244 
245         clickInstallerUIButton(INSTALL_BUTTON_ID)
246 
247         val result = getInstallSessionResult()
248         assertWithMessage(result.message)
249             .that(result.status)
250             .isEqualTo(PackageInstaller.STATUS_SUCCESS)
251 
252         val packageInfo = assertInstalled(GET_PERMISSIONS_FLAGS)
253         params.finalPermissionState.forEach { (permission, granted) ->
254             assertPermission(packageInfo, permission, granted)
255         }
256     }
257 
258     private fun assertPermission(packageInfo: PackageInfo, name: String, granted: Boolean?) {
259         val permissionIndex = packageInfo.requestedPermissions.indexOfFirst { it == name }
260 
261         if (granted == null) {
262             assertThat(permissionIndex).isEqualTo(-1)
263         } else {
264             val appInfo = pm.getApplicationInfo(
265                 TEST_APK_PACKAGE_NAME,
266                 PackageManager.ApplicationInfoFlags.of(0),
267             )
268 
269             permissionManager.checkPermissionForPreflight(
270                 name,
271                 AttributionSource.Builder(appInfo.uid)
272                     .setPackageName(TEST_APK_PACKAGE_NAME)
273                     .build(),
274             ).let(::assertThat)
275                 .run {
276                     if (granted) {
277                         isEqualTo(PermissionManager.PERMISSION_GRANTED)
278                     } else {
279                         isNotEqualTo(PermissionManager.PERMISSION_GRANTED)
280                     }
281                 }
282         }
283     }
284 }
285