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