1 /* <lambda>null2 * Copyright (C) 2020 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.permission.cts 18 19 import android.Manifest.permission.ACCESS_MEDIA_LOCATION 20 import android.Manifest.permission.READ_EXTERNAL_STORAGE 21 import android.Manifest.permission.WRITE_EXTERNAL_STORAGE 22 import android.app.Instrumentation 23 import android.app.UiAutomation 24 import android.content.Context 25 import android.content.pm.PackageManager 26 import android.os.Process 27 import android.os.UserHandle 28 import android.platform.test.annotations.AppModeFull 29 import androidx.test.platform.app.InstrumentationRegistry 30 import com.android.compatibility.common.util.SystemUtil 31 import org.junit.After 32 import org.junit.Assert 33 import org.junit.Assert.assertTrue 34 import org.junit.Assume.assumeNoException 35 import org.junit.Before 36 import org.junit.Test 37 38 @AppModeFull 39 class StorageEscalationTest { 40 companion object { 41 private const val APK_DIRECTORY = "/data/local/tmp/cts/permissions" 42 const val APP_APK_PATH_28 = "$APK_DIRECTORY/CtsStorageEscalationApp28.apk" 43 const val APP_APK_PATH_29_SCOPED = "$APK_DIRECTORY/CtsStorageEscalationApp29Scoped.apk" 44 const val APP_APK_PATH_29_FULL = "$APK_DIRECTORY/CtsStorageEscalationApp29Full.apk" 45 const val APP_PACKAGE_NAME = "android.permission3.cts.storageescalation" 46 const val DELAY_TIME_MS: Long = 200 47 val permissions = listOf<String>(READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE, 48 ACCESS_MEDIA_LOCATION) 49 } 50 51 private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() 52 private val context: Context = instrumentation.context 53 private val uiAutomation: UiAutomation = instrumentation.uiAutomation 54 private var secondaryUserId: Int? = null 55 56 @Before 57 @After 58 fun uninstallApp() { 59 SystemUtil.runShellCommand("pm uninstall $APP_PACKAGE_NAME --user ALL") 60 } 61 62 private fun installPackage(apk: String) { 63 var userString = "" 64 secondaryUserId?.let { userId -> 65 userString = " --user $userId" 66 } 67 val result = SystemUtil.runShellCommand("pm install -r$userString $apk") 68 assertTrue("Expected output to contain \"Success\", but was \"$result\"", 69 result.contains("Success")) 70 } 71 72 private fun createSecondaryUser() { 73 val createUserOutput: String = SystemUtil.runShellCommand("pm create-user secondary") 74 var formatException: Exception? = null 75 val userId = try { 76 createUserOutput.split(" id ".toRegex())[1].trim { it <= ' ' }.toInt() 77 } catch (e: Exception) { 78 formatException = e 79 -1 80 } 81 assumeNoException("Failed to parse userId from $createUserOutput", formatException) 82 SystemUtil.runShellCommand("am start-user -w $userId") 83 secondaryUserId = userId 84 } 85 86 @After 87 fun removeSecondaryUser() { 88 secondaryUserId?.let { userId -> 89 SystemUtil.runShellCommand("pm remove-user $userId") 90 secondaryUserId = null 91 } 92 } 93 94 private fun grantStoragePermissions() { 95 for (permName in permissions) { 96 var user = Process.myUserHandle() 97 secondaryUserId?.let { 98 user = UserHandle.of(it) 99 } 100 uiAutomation.grantRuntimePermissionAsUser(APP_PACKAGE_NAME, permName, user) 101 } 102 } 103 104 private fun assertStoragePermissionState(granted: Boolean) { 105 for (permName in permissions) { 106 var userContext = context 107 secondaryUserId?.let { userId -> 108 SystemUtil.runWithShellPermissionIdentity { 109 userContext = context.createPackageContextAsUser( 110 APP_PACKAGE_NAME, 0, UserHandle.of(userId)) 111 } 112 } 113 Assert.assertEquals(granted, userContext.packageManager.checkPermission(permName, 114 APP_PACKAGE_NAME) == PackageManager.PERMISSION_GRANTED) 115 } 116 } 117 118 @Test 119 fun testCannotEscalateWithSdkDowngrade() { 120 runStorageEscalationTest(APP_APK_PATH_29_SCOPED, APP_APK_PATH_28) 121 } 122 123 @Test 124 fun testCannotEscalateWithNewManifestLegacyRequest() { 125 runStorageEscalationTest(APP_APK_PATH_29_SCOPED, APP_APK_PATH_29_FULL) 126 } 127 128 @Test 129 fun testCannotEscalateWithSdkDowngradeSecondary() { 130 createSecondaryUser() 131 runStorageEscalationTest(APP_APK_PATH_29_SCOPED, APP_APK_PATH_28) 132 } 133 134 @Test 135 fun testCannotEscalateWithNewManifestLegacyRequestSecondary() { 136 createSecondaryUser() 137 runStorageEscalationTest(APP_APK_PATH_29_SCOPED, APP_APK_PATH_29_FULL) 138 } 139 140 private fun runStorageEscalationTest(startPackageApk: String, finishPackageApk: String) { 141 installPackage(startPackageApk) 142 grantStoragePermissions() 143 assertStoragePermissionState(granted = true) 144 installPackage(finishPackageApk) 145 // permission revoke is async, so wait a short period 146 Thread.sleep(DELAY_TIME_MS) 147 assertStoragePermissionState(granted = false) 148 } 149 } 150