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