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.permissionmultidevice.cts 18 19 import android.Manifest 20 import android.app.Instrumentation 21 import android.companion.virtual.VirtualDeviceManager 22 import android.companion.virtual.VirtualDeviceParams 23 import android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM 24 import android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA 25 import android.content.ComponentName 26 import android.content.Intent 27 import android.content.Intent.EXTRA_RESULT_RECEIVER 28 import android.content.pm.PackageManager 29 import android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS 30 import android.hardware.display.DisplayManager 31 import android.hardware.display.VirtualDisplay 32 import android.os.Build 33 import android.os.Bundle 34 import android.os.RemoteCallback 35 import android.permission.flags.Flags 36 import android.permissionmultidevice.cts.PackageManagementUtils.installPackage 37 import android.permissionmultidevice.cts.PackageManagementUtils.uninstallPackage 38 import android.permissionmultidevice.cts.UiAutomatorUtils.click 39 import android.permissionmultidevice.cts.UiAutomatorUtils.findTextForView 40 import android.permissionmultidevice.cts.UiAutomatorUtils.waitFindObject 41 import android.platform.test.annotations.AppModeFull 42 import android.platform.test.annotations.RequiresFlagsEnabled 43 import android.view.Display 44 import android.virtualdevice.cts.common.VirtualDeviceRule 45 import androidx.test.ext.junit.runners.AndroidJUnit4 46 import androidx.test.filters.SdkSuppress 47 import androidx.test.platform.app.InstrumentationRegistry 48 import androidx.test.uiautomator.By 49 import com.android.compatibility.common.util.SystemUtil 50 import com.google.common.truth.Truth 51 import com.google.common.truth.Truth.assertThat 52 import java.util.concurrent.CompletableFuture 53 import java.util.concurrent.TimeUnit 54 import org.junit.After 55 import org.junit.Assert 56 import org.junit.Assume.assumeFalse 57 import org.junit.Before 58 import org.junit.Rule 59 import org.junit.Test 60 import org.junit.runner.RunWith 61 62 @RunWith(AndroidJUnit4::class) 63 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = "VanillaIceCream") 64 @AppModeFull(reason = "VirtualDeviceManager cannot be accessed by instant apps") 65 class DeviceAwarePermissionGrantTest { 66 private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() 67 private val defaultDeviceContext = instrumentation.targetContext 68 private lateinit var virtualDeviceManager: VirtualDeviceManager 69 private lateinit var virtualDevice: VirtualDeviceManager.VirtualDevice 70 private lateinit var virtualDisplay: VirtualDisplay 71 private lateinit var deviceDisplayName: String 72 73 @get:Rule var virtualDeviceRule = VirtualDeviceRule.createDefault() 74 75 @Before 76 fun setup() { 77 assumeFalse(PermissionUtils.isAutomotive(defaultDeviceContext)) 78 assumeFalse(PermissionUtils.isTv(defaultDeviceContext)) 79 assumeFalse(PermissionUtils.isWatch(defaultDeviceContext)) 80 81 installPackage(APP_APK_PATH_STREAMING) 82 virtualDeviceManager = 83 defaultDeviceContext.getSystemService(VirtualDeviceManager::class.java)!! 84 virtualDevice = 85 virtualDeviceRule.createManagedVirtualDevice( 86 VirtualDeviceParams.Builder() 87 .setDevicePolicy(POLICY_TYPE_CAMERA, DEVICE_POLICY_CUSTOM) 88 .build() 89 ) 90 91 val displayConfig = 92 VirtualDeviceRule.createDefaultVirtualDisplayConfigBuilder( 93 DISPLAY_WIDTH, 94 DISPLAY_HEIGHT 95 ) 96 .setFlags( 97 DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC or 98 DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED or 99 DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY 100 ) 101 .build() 102 103 virtualDisplay = 104 virtualDeviceRule.createManagedVirtualDisplay(virtualDevice, displayConfig)!! 105 deviceDisplayName = 106 virtualDeviceManager.getVirtualDevice(virtualDevice.deviceId)!!.displayName.toString() 107 } 108 109 @After 110 fun cleanup() { 111 uninstallPackage(APP_PACKAGE_NAME, requireSuccess = false) 112 } 113 114 @RequiresFlagsEnabled( 115 Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED, 116 Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED 117 ) 118 @Test 119 fun onHostDevice_requestPermissionForHostDevice_shouldGrantPermission() { 120 testGrantPermissionForDevice( 121 Display.DEFAULT_DISPLAY, 122 DEVICE_ID_DEFAULT, 123 false, 124 "", 125 expectPermissionGrantedOnDefaultDevice = true, 126 expectPermissionGrantedOnRemoteDevice = false 127 ) 128 } 129 130 @RequiresFlagsEnabled( 131 Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED, 132 Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED 133 ) 134 @Test 135 fun onHostDevice_requestPermissionForRemoteDevice_shouldGrantPermission() { 136 testGrantPermissionForDevice( 137 Display.DEFAULT_DISPLAY, 138 virtualDevice.deviceId, 139 true, 140 deviceDisplayName, 141 expectPermissionGrantedOnDefaultDevice = false, 142 expectPermissionGrantedOnRemoteDevice = true 143 ) 144 } 145 146 @RequiresFlagsEnabled( 147 Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED, 148 Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED 149 ) 150 @Test 151 fun onRemoteDevice_requestPermissionForHostDevice_shouldShowWarningDialog() { 152 requestPermissionOnDevice(virtualDisplay.display.displayId, DEVICE_ID_DEFAULT) 153 154 val displayId = virtualDisplay.display.displayId 155 waitFindObject(By.displayId(displayId).textContains("Permission request suppressed")) 156 } 157 158 @RequiresFlagsEnabled( 159 Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED, 160 Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED 161 ) 162 @Test 163 fun onRemoteDevice_requestPermissionForRemoteDevice_shouldGrantPermission() { 164 testGrantPermissionForDevice( 165 virtualDisplay.display.displayId, 166 virtualDevice.deviceId, 167 true, 168 deviceDisplayName, 169 expectPermissionGrantedOnDefaultDevice = false, 170 expectPermissionGrantedOnRemoteDevice = true 171 ) 172 } 173 174 private fun testGrantPermissionForDevice( 175 displayId: Int, 176 targetDeviceId: Int, 177 showDeviceName: Boolean, 178 expectedDeviceNameInDialog: String, 179 expectPermissionGrantedOnDefaultDevice: Boolean, 180 expectPermissionGrantedOnRemoteDevice: Boolean 181 ) { 182 // Assert no permission granted to either default device or virtual device at the beginning 183 assertAppHasPermissionForDevice(DEVICE_ID_DEFAULT, false) 184 assertAppHasPermissionForDevice(virtualDevice.deviceId, false) 185 186 val future = requestPermissionOnDevice(displayId, targetDeviceId) 187 virtualDeviceRule.waitAndAssertActivityResumed(getPermissionDialogComponentName()) 188 189 if (showDeviceName) { 190 assertPermissionMessageContainsDeviceName(displayId, expectedDeviceNameInDialog) 191 } 192 193 // Click the allow button in the dialog to grant permission 194 SystemUtil.eventually { click(By.displayId(displayId).res(ALLOW_BUTTON)) } 195 196 // Validate permission grant result returned from callback 197 val grantPermissionResult = future.get(TIMEOUT, TimeUnit.MILLISECONDS) 198 assertThat( 199 grantPermissionResult.getStringArray( 200 TestConstants.PERMISSION_RESULT_KEY_PERMISSIONS 201 ) 202 ) 203 .isEqualTo(arrayOf(DEVICE_AWARE_PERMISSION)) 204 assertThat( 205 grantPermissionResult.getIntArray(TestConstants.PERMISSION_RESULT_KEY_GRANT_RESULTS) 206 ) 207 .isEqualTo(arrayOf(PackageManager.PERMISSION_GRANTED).toIntArray()) 208 assertThat(grantPermissionResult.getInt(TestConstants.PERMISSION_RESULT_KEY_DEVICE_ID)) 209 .isEqualTo(targetDeviceId) 210 211 // Validate whether permission is granted as expected 212 assertAppHasPermissionForDevice(DEVICE_ID_DEFAULT, expectPermissionGrantedOnDefaultDevice) 213 assertAppHasPermissionForDevice( 214 virtualDevice.deviceId, 215 expectPermissionGrantedOnRemoteDevice 216 ) 217 } 218 219 private fun requestPermissionOnDevice( 220 displayId: Int, 221 targetDeviceId: Int 222 ): CompletableFuture<Bundle> { 223 val future = CompletableFuture<Bundle>() 224 val callback = RemoteCallback { result: Bundle? -> future.complete(result) } 225 val intent = 226 Intent() 227 .setComponent( 228 ComponentName(APP_PACKAGE_NAME, "$APP_PACKAGE_NAME.RequestPermissionActivity") 229 ) 230 .putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_DEVICE_ID, targetDeviceId) 231 .putExtra(EXTRA_RESULT_RECEIVER, callback) 232 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) 233 virtualDeviceRule.sendIntentToDisplay(intent, displayId) 234 235 return future 236 } 237 238 private fun assertPermissionMessageContainsDeviceName(displayId: Int, deviceName: String) { 239 waitFindObject(By.displayId(displayId).res(PERMISSION_MESSAGE_ID)) 240 val text = findTextForView(By.displayId(displayId).res(PERMISSION_MESSAGE_ID)) 241 Truth.assertThat(text).contains(deviceName) 242 } 243 244 private fun assertAppHasPermissionForDevice(deviceId: Int, expectPermissionGranted: Boolean) { 245 val checkPermissionResult = 246 defaultDeviceContext 247 .createDeviceContext(deviceId) 248 .packageManager 249 .checkPermission(DEVICE_AWARE_PERMISSION, APP_PACKAGE_NAME) 250 251 if (expectPermissionGranted) { 252 Assert.assertEquals(PackageManager.PERMISSION_GRANTED, checkPermissionResult) 253 } else { 254 Assert.assertEquals(PackageManager.PERMISSION_DENIED, checkPermissionResult) 255 } 256 } 257 258 private fun getPermissionDialogComponentName(): ComponentName { 259 val intent = Intent(ACTION_REQUEST_PERMISSIONS) 260 intent.setPackage(defaultDeviceContext.packageManager.getPermissionControllerPackageName()) 261 return intent.resolveActivity(defaultDeviceContext.packageManager) 262 } 263 264 companion object { 265 const val APK_DIRECTORY = "/data/local/tmp/cts-permissionmultidevice" 266 const val APP_APK_PATH_STREAMING = "${APK_DIRECTORY}/CtsAccessRemoteDeviceCamera.apk" 267 const val APP_PACKAGE_NAME = "android.permissionmultidevice.cts.accessremotedevicecamera" 268 const val PERMISSION_MESSAGE_ID = "com.android.permissioncontroller:id/permission_message" 269 const val ALLOW_BUTTON = 270 "com.android.permissioncontroller:id/permission_allow_foreground_only_button" 271 const val DEVICE_ID_DEFAULT = 0 272 const val PERSISTENT_DEVICE_ID_DEFAULT = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT 273 const val DEVICE_AWARE_PERMISSION = Manifest.permission.CAMERA 274 const val TIMEOUT = 5000L 275 private const val DISPLAY_HEIGHT = 1920 276 private const val DISPLAY_WIDTH = 1080 277 } 278 } 279