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