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.runShellCommand
48 import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
49 import com.android.modules.utils.build.SdkLevel
50 import org.junit.After
51 import org.junit.Assert
52 import org.junit.Assume
53 import org.junit.Before
54 import org.junit.ClassRule
55 import org.junit.Rule
56 import org.junit.Test
57 import org.junit.runner.RunWith
58 
59 @RunWith(AndroidJUnit4::class)
60 @AppModeFull(
61     reason =
62         "Cannot set system settings as instant app. Also we never show an accessibility " +
63             "notification for instant apps."
64 )
65 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
66 @ScreenRecordRule.ScreenRecord
67 @FlakyTest
68 class AccessibilityPrivacySourceTest {
69 
70     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
71     private val context: Context = instrumentation.targetContext
72     private val permissionControllerPackage = context.packageManager.permissionControllerPackageName
73     private val accessibilityTestService =
74         ComponentName(context, AccessibilityTestService::class.java).flattenToString()
75     private val safetyCenterIssueId = "accessibility_$accessibilityTestService"
76     private val safetyCenterManager = context.getSystemService(SafetyCenterManager::class.java)
77 
78     @get:Rule val screenRecordRule = ScreenRecordRule(false, false)
79 
80     @get:Rule
81     val mAccessibilityServiceRule =
82         InstrumentedAccessibilityServiceTestRule(AccessibilityTestService::class.java, false)
83 
84     @get:Rule
85     val deviceConfigSafetyCenterEnabled =
86         DeviceConfigStateChangerRule(
87             context,
88             DeviceConfig.NAMESPACE_PRIVACY,
89             SAFETY_CENTER_ENABLED,
90             true.toString()
91         )
92 
93     @get:Rule
94     val deviceConfigA11yListenerDisabled =
95         DeviceConfigStateChangerRule(
96             context,
97             DeviceConfig.NAMESPACE_PRIVACY,
98             ACCESSIBILITY_LISTENER_ENABLED,
99             false.toString()
100         )
101 
102     @Before
setupnull103     fun setup() {
104         Assume.assumeTrue(deviceSupportsSafetyCenter(context))
105         InstrumentedAccessibilityService.disableAllServices()
106         runShellCommand("input keyevent KEYCODE_WAKEUP")
107         resetPermissionController()
108         cancelNotifications(permissionControllerPackage)
109         assertEmptyNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
110         runWithShellPermissionIdentity { safetyCenterManager?.clearAllSafetySourceDataForTests() }
111         assertSafetyCenterIssueDoesNotExist(
112             SC_ACCESSIBILITY_SOURCE_ID,
113             safetyCenterIssueId,
114             SC_ACCESSIBILITY_ISSUE_TYPE_ID
115         )
116     }
117 
118     @After
cleanupnull119     fun cleanup() {
120         cancelNotifications(permissionControllerPackage)
121         runWithShellPermissionIdentity { safetyCenterManager?.clearAllSafetySourceDataForTests() }
122     }
123 
124     @Test
testJobSendsNotificationnull125     fun testJobSendsNotification() {
126         mAccessibilityServiceRule.enableService()
127         runJobAndWaitUntilCompleted()
128         assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
129     }
130 
131     @Test
testJobSendsNotificationOnEnablenull132     fun testJobSendsNotificationOnEnable() {
133         mAccessibilityServiceRule.enableService()
134         runJobAndWaitUntilCompleted()
135         assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
136 
137         setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, true.toString())
138         cancelNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
139         InstrumentedAccessibilityService.disableAllServices()
140         setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, false.toString())
141         setDeviceConfigPrivacyProperty(ACCESSIBILITY_JOB_INTERVAL_MILLIS, "0")
142 
143         // enable service again and verify a notification
144         try {
145             mAccessibilityServiceRule.enableService()
146             runJobAndWaitUntilCompleted()
147             assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
148         } finally {
149             deleteDeviceConfigPrivacyProperty(ACCESSIBILITY_JOB_INTERVAL_MILLIS)
150         }
151     }
152 
153     @Test
testJobSendsIssuesToSafetyCenternull154     fun testJobSendsIssuesToSafetyCenter() {
155         mAccessibilityServiceRule.enableService()
156         runJobAndWaitUntilCompleted()
157         assertSafetyCenterIssueExist(
158             SC_ACCESSIBILITY_SOURCE_ID,
159             safetyCenterIssueId,
160             SC_ACCESSIBILITY_ISSUE_TYPE_ID
161         )
162     }
163 
164     @Test
testJobDoesNotSendNotificationInSecondRunForSameServicenull165     fun testJobDoesNotSendNotificationInSecondRunForSameService() {
166         mAccessibilityServiceRule.enableService()
167         runJobAndWaitUntilCompleted()
168         assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
169 
170         cancelNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
171 
172         runJobAndWaitUntilCompleted()
173         assertEmptyNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
174     }
175 
176     @Test
testAccessibilityListenerSendsIssueToSafetyCenternull177     fun testAccessibilityListenerSendsIssueToSafetyCenter() {
178         setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, true.toString())
179         try {
180             val automation = getAutomation()
181             mAccessibilityServiceRule.enableService()
182             TestUtils.eventually(
183                 {
184                     assertSafetyCenterIssueExist(
185                         SC_ACCESSIBILITY_SOURCE_ID,
186                         safetyCenterIssueId,
187                         SC_ACCESSIBILITY_ISSUE_TYPE_ID,
188                         automation
189                     )
190                 },
191                 TIMEOUT_MILLIS
192             )
193             automation.destroy()
194         } finally {
195             setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, false.toString())
196         }
197     }
198 
199     @Test
testJobWithDisabledServiceDoesNotSendNotificationnull200     fun testJobWithDisabledServiceDoesNotSendNotification() {
201         runJobAndWaitUntilCompleted()
202         assertEmptyNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
203     }
204 
205     @Test
testJobWithDisabledServiceDoesNotSendIssueToSafetyCenternull206     fun testJobWithDisabledServiceDoesNotSendIssueToSafetyCenter() {
207         runJobAndWaitUntilCompleted()
208         assertSafetyCenterIssueDoesNotExist(
209             SC_ACCESSIBILITY_SOURCE_ID,
210             safetyCenterIssueId,
211             SC_ACCESSIBILITY_ISSUE_TYPE_ID
212         )
213     }
214 
215     @Test
testJobWithSafetyCenterDisabledDoesNotSendNotificationnull216     fun testJobWithSafetyCenterDisabledDoesNotSendNotification() {
217         setDeviceConfigPrivacyProperty(SAFETY_CENTER_ENABLED, false.toString())
218         mAccessibilityServiceRule.enableService()
219         runJobAndWaitUntilCompleted()
220         assertEmptyNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
221     }
222 
223     @Test
testJobWithSafetyCenterDisabledDoesNotSendIssueToSafetyCenternull224     fun testJobWithSafetyCenterDisabledDoesNotSendIssueToSafetyCenter() {
225         setDeviceConfigPrivacyProperty(SAFETY_CENTER_ENABLED, false.toString())
226         mAccessibilityServiceRule.enableService()
227         runJobAndWaitUntilCompleted()
228         assertSafetyCenterIssueDoesNotExist(
229             SC_ACCESSIBILITY_SOURCE_ID,
230             safetyCenterIssueId,
231             SC_ACCESSIBILITY_ISSUE_TYPE_ID
232         )
233     }
234 
235     @Test
testNotificationClickOpenSafetyCenternull236     fun testNotificationClickOpenSafetyCenter() {
237         mAccessibilityServiceRule.enableService()
238         runJobAndWaitUntilCompleted()
239         assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
240         val statusBarNotification =
241             getNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID)
242         Assert.assertNotNull(statusBarNotification)
243         val contentIntent = statusBarNotification!!.notification.contentIntent
244         if (SdkLevel.isAtLeastU()) {
245             val options =
246                 ActivityOptions.makeBasic()
247                     .setPendingIntentBackgroundActivityStartMode(
248                         ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
249                     )
250             contentIntent.send(
251                 /* context = */ null,
252                 /* code = */ 0,
253                 /* intent = */ null,
254                 /* onFinished = */ null,
255                 /* handler = */ null,
256                 /* requiredPermission = */ null,
257                 /* options = */ options.toBundle()
258             )
259         } else {
260             contentIntent.send()
261         }
262         assertSafetyCenterStarted()
263     }
264 
getAutomationnull265     private fun getAutomation(): UiAutomation {
266         return instrumentation.getUiAutomation(
267             UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES
268         )
269     }
270 
runJobAndWaitUntilCompletednull271     private fun runJobAndWaitUntilCompleted() {
272         TestUtils.runJobAndWaitUntilCompleted(
273             permissionControllerPackage,
274             ACCESSIBILITY_JOB_ID,
275             TIMEOUT_MILLIS,
276             getAutomation()
277         )
278     }
279 
280     /** Reset the permission controllers state. */
281     @Throws(Throwable::class)
resetPermissionControllernull282     private fun resetPermissionController() {
283         PermissionUtils.resetPermissionControllerJob(
284             getAutomation(),
285             permissionControllerPackage,
286             ACCESSIBILITY_JOB_ID,
287             45000,
288             ACTION_SET_UP_ACCESSIBILITY_CHECK,
289             AccessibilityOnBootReceiver
290         )
291     }
292 
293     companion object {
294         private const val SC_ACCESSIBILITY_SOURCE_ID = "AndroidAccessibility"
295         private const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
296         private const val ACCESSIBILITY_LISTENER_ENABLED = "sc_accessibility_listener_enabled"
297         private const val ACCESSIBILITY_JOB_INTERVAL_MILLIS = "sc_accessibility_job_interval_millis"
298 
299         private const val ACCESSIBILITY_JOB_ID = 6
300         private const val ACCESSIBILITY_NOTIFICATION_ID = 4
301         private const val TIMEOUT_MILLIS: Long = 10000
302 
303         private const val SC_ACCESSIBILITY_ISSUE_TYPE_ID = "accessibility_privacy_issue"
304 
305         private const val AccessibilityOnBootReceiver =
306             "com.android.permissioncontroller.privacysources.AccessibilityOnBootReceiver"
307         private const val ACTION_SET_UP_ACCESSIBILITY_CHECK =
308             "com.android.permissioncontroller.action.SET_UP_ACCESSIBILITY_CHECK"
309 
310         @get:ClassRule
311         @JvmStatic
312         val ctsNotificationListenerHelper =
313             CtsNotificationListenerHelperRule(
314                 InstrumentationRegistry.getInstrumentation().targetContext
315             )
316     }
317 }
318