1 /* 2 * Copyright (C) 2022 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.accessibility.cts.common.InstrumentedAccessibilityService 20 import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule 21 import android.app.ActivityOptions 22 import android.app.Instrumentation 23 import android.app.UiAutomation 24 import android.content.ComponentName 25 import android.content.Context 26 import android.os.Build 27 import android.permission.cts.CtsNotificationListenerServiceUtils.assertEmptyNotification 28 import android.permission.cts.CtsNotificationListenerServiceUtils.assertNotificationExist 29 import android.permission.cts.CtsNotificationListenerServiceUtils.cancelNotification 30 import android.permission.cts.CtsNotificationListenerServiceUtils.cancelNotifications 31 import android.permission.cts.CtsNotificationListenerServiceUtils.getNotification 32 import android.permission.cts.SafetyCenterUtils.assertSafetyCenterIssueDoesNotExist 33 import android.permission.cts.SafetyCenterUtils.assertSafetyCenterIssueExist 34 import android.permission.cts.SafetyCenterUtils.assertSafetyCenterStarted 35 import android.permission.cts.SafetyCenterUtils.deleteDeviceConfigPrivacyProperty 36 import android.permission.cts.SafetyCenterUtils.deviceSupportsSafetyCenter 37 import android.permission.cts.SafetyCenterUtils.setDeviceConfigPrivacyProperty 38 import android.platform.test.annotations.AppModeFull 39 import android.platform.test.rule.ScreenRecordRule 40 import android.provider.DeviceConfig 41 import android.safetycenter.SafetyCenterManager 42 import androidx.test.filters.FlakyTest 43 import androidx.test.filters.SdkSuppress 44 import androidx.test.platform.app.InstrumentationRegistry 45 import androidx.test.runner.AndroidJUnit4 46 import com.android.compatibility.common.util.DeviceConfigStateChangerRule 47 import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity 48 import com.android.compatibility.common.util.SystemUtil.runShellCommand 49 import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity 50 import com.android.modules.utils.build.SdkLevel 51 import org.junit.After 52 import org.junit.Assert 53 import org.junit.Assume 54 import org.junit.Assume.assumeFalse 55 import org.junit.Before 56 import org.junit.ClassRule 57 import org.junit.Rule 58 import org.junit.Test 59 import org.junit.runner.RunWith 60 61 @RunWith(AndroidJUnit4::class) 62 @AppModeFull( 63 reason = 64 "Cannot set system settings as instant app. Also we never show an accessibility " + 65 "notification for instant apps." 66 ) 67 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") 68 @ScreenRecordRule.ScreenRecord 69 @FlakyTest 70 class AccessibilityPrivacySourceTest { 71 72 private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() 73 private val context: Context = instrumentation.targetContext 74 private val permissionControllerPackage = context.packageManager.permissionControllerPackageName 75 private val accessibilityTestService = 76 ComponentName(context, AccessibilityTestService::class.java).flattenToString() 77 private val safetyCenterIssueId = "accessibility_$accessibilityTestService" 78 private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java) 79 80 @get:Rule val screenRecordRule = ScreenRecordRule(false, false) 81 82 @get:Rule 83 val mAccessibilityServiceRule = 84 InstrumentedAccessibilityServiceTestRule(AccessibilityTestService::class.java, false) 85 86 @get:Rule 87 val deviceConfigSafetyCenterEnabled = 88 DeviceConfigStateChangerRule( 89 context, 90 DeviceConfig.NAMESPACE_PRIVACY, 91 SAFETY_CENTER_ENABLED, 92 true.toString() 93 ) 94 95 @get:Rule 96 val deviceConfigA11yListenerDisabled = 97 DeviceConfigStateChangerRule( 98 context, 99 DeviceConfig.NAMESPACE_PRIVACY, 100 ACCESSIBILITY_LISTENER_ENABLED, 101 false.toString() 102 ) 103 104 @Before setupnull105 fun setup() { 106 Assume.assumeTrue(deviceSupportsSafetyCenter(context)) 107 InstrumentedAccessibilityService.disableAllServices() 108 runShellCommand("input keyevent KEYCODE_WAKEUP") 109 resetPermissionController() 110 cancelNotifications(permissionControllerPackage) 111 assertEmptyNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) 112 runWithShellPermissionIdentity { safetyCenterManager?.clearAllSafetySourceDataForTests() } 113 assertSafetyCenterIssueDoesNotExist( 114 SC_ACCESSIBILITY_SOURCE_ID, 115 safetyCenterIssueId, 116 SC_ACCESSIBILITY_ISSUE_TYPE_ID 117 ) 118 } 119 120 @After cleanupnull121 fun cleanup() { 122 cancelNotifications(permissionControllerPackage) 123 runWithShellPermissionIdentity { safetyCenterManager?.clearAllSafetySourceDataForTests() } 124 } 125 126 @Test testJobSendsNotificationnull127 fun testJobSendsNotification() { 128 mAccessibilityServiceRule.enableService() 129 runJobAndWaitUntilCompleted() 130 assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) 131 } 132 133 @Test testJobSendsNotificationOnEnablenull134 fun testJobSendsNotificationOnEnable() { 135 mAccessibilityServiceRule.enableService() 136 runJobAndWaitUntilCompleted() 137 assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) 138 139 setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, true.toString()) 140 cancelNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) 141 InstrumentedAccessibilityService.disableAllServices() 142 setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, false.toString()) 143 setDeviceConfigPrivacyProperty(ACCESSIBILITY_JOB_INTERVAL_MILLIS, "0") 144 145 // enable service again and verify a notification 146 try { 147 mAccessibilityServiceRule.enableService() 148 runJobAndWaitUntilCompleted() 149 assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) 150 } finally { 151 deleteDeviceConfigPrivacyProperty(ACCESSIBILITY_JOB_INTERVAL_MILLIS) 152 } 153 } 154 155 @Test testJobSendsIssuesToSafetyCenternull156 fun testJobSendsIssuesToSafetyCenter() { 157 mAccessibilityServiceRule.enableService() 158 runJobAndWaitUntilCompleted() 159 assertSafetyCenterIssueExist( 160 SC_ACCESSIBILITY_SOURCE_ID, 161 safetyCenterIssueId, 162 SC_ACCESSIBILITY_ISSUE_TYPE_ID 163 ) 164 } 165 166 @Test testJobDoesNotSendNotificationInSecondRunForSameServicenull167 fun testJobDoesNotSendNotificationInSecondRunForSameService() { 168 mAccessibilityServiceRule.enableService() 169 runJobAndWaitUntilCompleted() 170 assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) 171 172 cancelNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) 173 174 runJobAndWaitUntilCompleted() 175 assertEmptyNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) 176 } 177 178 @Test testAccessibilityListenerSendsIssueToSafetyCenternull179 fun testAccessibilityListenerSendsIssueToSafetyCenter() { 180 setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, true.toString()) 181 try { 182 val automation = getAutomation() 183 mAccessibilityServiceRule.enableService() 184 TestUtils.eventually( 185 { 186 assertSafetyCenterIssueExist( 187 SC_ACCESSIBILITY_SOURCE_ID, 188 safetyCenterIssueId, 189 SC_ACCESSIBILITY_ISSUE_TYPE_ID, 190 automation 191 ) 192 }, 193 TIMEOUT_MILLIS 194 ) 195 automation.destroy() 196 } finally { 197 setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, false.toString()) 198 } 199 } 200 201 @Test testJobWithDisabledServiceDoesNotSendNotificationnull202 fun testJobWithDisabledServiceDoesNotSendNotification() { 203 runJobAndWaitUntilCompleted() 204 assertEmptyNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) 205 } 206 207 @Test testJobWithDisabledServiceDoesNotSendIssueToSafetyCenternull208 fun testJobWithDisabledServiceDoesNotSendIssueToSafetyCenter() { 209 runJobAndWaitUntilCompleted() 210 assertSafetyCenterIssueDoesNotExist( 211 SC_ACCESSIBILITY_SOURCE_ID, 212 safetyCenterIssueId, 213 SC_ACCESSIBILITY_ISSUE_TYPE_ID 214 ) 215 } 216 217 @Test testJobWithSafetyCenterDisabledDoesNotSendNotificationnull218 fun testJobWithSafetyCenterDisabledDoesNotSendNotification() { 219 setDeviceConfigPrivacyProperty(SAFETY_CENTER_ENABLED, false.toString()) 220 // Safety Center cannot be disabled at runtime on UDC+. 221 assumeFalse( 222 "Safety Center cannot be disabled", 223 callWithShellPermissionIdentity { 224 safetyCenterManager?.isSafetyCenterEnabled() ?: false 225 } 226 ) 227 mAccessibilityServiceRule.enableService() 228 runJobAndWaitUntilCompleted() 229 assertEmptyNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) 230 } 231 232 @Test testJobWithSafetyCenterDisabledDoesNotSendIssueToSafetyCenternull233 fun testJobWithSafetyCenterDisabledDoesNotSendIssueToSafetyCenter() { 234 setDeviceConfigPrivacyProperty(SAFETY_CENTER_ENABLED, false.toString()) 235 // Safety Center cannot be disabled at runtime on UDC+. 236 assumeFalse( 237 "Safety Center cannot be disabled", 238 callWithShellPermissionIdentity { 239 safetyCenterManager?.isSafetyCenterEnabled() ?: false 240 } 241 ) 242 mAccessibilityServiceRule.enableService() 243 runJobAndWaitUntilCompleted() 244 assertSafetyCenterIssueDoesNotExist( 245 SC_ACCESSIBILITY_SOURCE_ID, 246 safetyCenterIssueId, 247 SC_ACCESSIBILITY_ISSUE_TYPE_ID 248 ) 249 } 250 251 @Test testNotificationClickOpenSafetyCenternull252 fun testNotificationClickOpenSafetyCenter() { 253 mAccessibilityServiceRule.enableService() 254 runJobAndWaitUntilCompleted() 255 assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) 256 val statusBarNotification = 257 getNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) 258 Assert.assertNotNull(statusBarNotification) 259 val contentIntent = statusBarNotification!!.notification.contentIntent 260 if (SdkLevel.isAtLeastU()) { 261 val options = 262 ActivityOptions.makeBasic() 263 .setPendingIntentBackgroundActivityStartMode( 264 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED 265 ) 266 contentIntent.send( 267 /* context = */ null, 268 /* code = */ 0, 269 /* intent = */ null, 270 /* onFinished = */ null, 271 /* handler = */ null, 272 /* requiredPermission = */ null, 273 /* options = */ options.toBundle() 274 ) 275 } else { 276 contentIntent.send() 277 } 278 assertSafetyCenterStarted() 279 } 280 getAutomationnull281 private fun getAutomation(): UiAutomation { 282 return instrumentation.getUiAutomation( 283 UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES 284 ) 285 } 286 runJobAndWaitUntilCompletednull287 private fun runJobAndWaitUntilCompleted() { 288 TestUtils.runJobAndWaitUntilCompleted( 289 permissionControllerPackage, 290 ACCESSIBILITY_JOB_ID, 291 TIMEOUT_MILLIS, 292 getAutomation() 293 ) 294 } 295 296 /** Reset the permission controllers state. */ 297 @Throws(Throwable::class) resetPermissionControllernull298 private fun resetPermissionController() { 299 PermissionUtils.resetPermissionControllerJob( 300 getAutomation(), 301 permissionControllerPackage, 302 ACCESSIBILITY_JOB_ID, 303 45000, 304 ACTION_SET_UP_ACCESSIBILITY_CHECK, 305 AccessibilityOnBootReceiver 306 ) 307 } 308 309 companion object { 310 private const val SC_ACCESSIBILITY_SOURCE_ID = "AndroidAccessibility" 311 private const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled" 312 private const val ACCESSIBILITY_LISTENER_ENABLED = "sc_accessibility_listener_enabled" 313 private const val ACCESSIBILITY_JOB_INTERVAL_MILLIS = "sc_accessibility_job_interval_millis" 314 315 private const val ACCESSIBILITY_JOB_ID = 6 316 private const val ACCESSIBILITY_NOTIFICATION_ID = 4 317 private const val TIMEOUT_MILLIS: Long = 10000 318 319 private const val SC_ACCESSIBILITY_ISSUE_TYPE_ID = "accessibility_privacy_issue" 320 321 private const val AccessibilityOnBootReceiver = 322 "com.android.permissioncontroller.privacysources.AccessibilityOnBootReceiver" 323 private const val ACTION_SET_UP_ACCESSIBILITY_CHECK = 324 "com.android.permissioncontroller.action.SET_UP_ACCESSIBILITY_CHECK" 325 326 @get:ClassRule 327 @JvmStatic 328 val ctsNotificationListenerHelper = 329 CtsNotificationListenerHelperRule( 330 InstrumentationRegistry.getInstrumentation().targetContext 331 ) 332 } 333 } 334