• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.permissioncontroller.tests.mocking.privacysources
18 
19 import android.app.job.JobParameters
20 import android.content.ComponentName
21 import android.content.Context
22 import android.content.SharedPreferences
23 import android.os.Build
24 import android.provider.DeviceConfig
25 import androidx.test.core.app.ApplicationProvider
26 import androidx.test.ext.junit.runners.AndroidJUnit4
27 import androidx.test.filters.SdkSuppress
28 import androidx.test.platform.app.InstrumentationRegistry
29 import com.android.dx.mockito.inline.extended.ExtendedMockito
30 import com.android.permissioncontroller.privacysources.AccessibilityJobService
31 import com.android.permissioncontroller.privacysources.AccessibilitySourceService
32 import com.google.common.truth.Truth.assertThat
33 import kotlinx.coroutines.runBlocking
34 import org.junit.After
35 import org.junit.Before
36 import org.junit.runner.RunWith
37 import org.junit.Test
38 import org.mockito.Mock
39 import org.mockito.Mockito.mock
40 import org.mockito.Mockito.verify
41 import org.mockito.MockitoAnnotations
42 import org.mockito.MockitoSession
43 import org.mockito.quality.Strictness
44 
45 /**
46  * Unit tests for internal [AccessibilitySourceService]
47  *
48  * <p> Does not test notification as there are conflicts with being able to mock NotificationManager
49  * and PendingIntent.getBroadcast requiring a valid context. Notifications are tested in the CTS
50  * integration tests
51  */
52 @RunWith(AndroidJUnit4::class)
53 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
54 class AccessibilitySourceServiceTest {
55 
56     @Mock
57     lateinit var jobService: AccessibilityJobService
58     private lateinit var context: Context
59     private lateinit var mockitoSession: MockitoSession
60     private lateinit var accessibilitySourceService: AccessibilitySourceService
61     private var shouldCancel = false
62     private lateinit var sharedPref: SharedPreferences
63 
64     @Before
setupnull65     fun setup() {
66         MockitoAnnotations.initMocks(this)
67         context = ApplicationProvider.getApplicationContext()
68 
69         mockitoSession = ExtendedMockito.mockitoSession()
70             .mockStatic(DeviceConfig::class.java)
71             .strictness(Strictness.LENIENT).startMocking()
72 
73         accessibilitySourceService = runWithShellPermissionIdentity {
74             AccessibilitySourceService(context)
75         }
76         sharedPref = accessibilitySourceService.getSharedPreference()
77         sharedPref.edit().clear().apply()
78     }
79 
80     @After
cleanupnull81     fun cleanup() {
82         shouldCancel = false
83         mockitoSession.finishMocking()
84         sharedPref.edit().clear().apply()
85     }
86 
87     @Test
processAccessibilityJobWithCancellationnull88     fun processAccessibilityJobWithCancellation() {
89         shouldCancel = true
90         val jobParameters = mock(JobParameters::class.java)
91 
92         runWithShellPermissionIdentity {
93             runBlocking {
94                 accessibilitySourceService.processAccessibilityJob(
95                     jobParameters,
96                     jobService
97                 ) { shouldCancel }
98             }
99         }
100         verify(jobService).jobFinished(jobParameters, true)
101     }
102 
103     @Test
markServiceAsNotifiednull104     fun markServiceAsNotified() {
105         val a11yService = ComponentName("com.test.package", "AccessibilityService")
106         runBlocking {
107             accessibilitySourceService.markServiceAsNotified(a11yService)
108         }
109 
110         val storedServices = getNotifiedServices()
111         assertThat(storedServices.size).isEqualTo(1)
112         assertThat(storedServices.iterator().next()).isEqualTo(a11yService.flattenToShortString())
113     }
114 
115     @Test
markAsNotifiedWithSecondComponentnull116     fun markAsNotifiedWithSecondComponent() {
117         val testComponent = ComponentName("com.test.package", "TestClass")
118         val testComponent2 = ComponentName("com.test.package2", "TestClass2")
119 
120         var notifiedServices = runBlocking {
121             accessibilitySourceService.markServiceAsNotified(testComponent)
122             getNotifiedServices()
123         }
124         assertThat(notifiedServices.size).isEqualTo(1)
125         assertThat(notifiedServices.iterator().next())
126             .isEqualTo(testComponent.flattenToShortString())
127 
128         notifiedServices = runBlocking {
129             accessibilitySourceService.markServiceAsNotified(testComponent2)
130             getNotifiedServices()
131         }
132         assertThat(notifiedServices.size).isEqualTo(2)
133         val expectedServices = listOf(testComponent, testComponent2)
134         expectedServices.forEach {
135             assertThat(notifiedServices.contains(it.flattenToShortString())).isTrue()
136         }
137     }
138     @Test
removeNotifiedServicenull139     fun removeNotifiedService() {
140         val a11yService = ComponentName("com.test.package", "AccessibilityService")
141         val a11yService2 = ComponentName("com.test.package", "AccessibilityService2")
142         val a11yService3 = ComponentName("com.test.package", "AccessibilityService3")
143         val allServices = listOf(a11yService, a11yService2, a11yService3)
144 
145         val notifiedServices = runBlocking {
146             allServices.forEach {
147                 accessibilitySourceService.markServiceAsNotified(it)
148             }
149             accessibilitySourceService.removeFromNotifiedServices(a11yService2)
150             getNotifiedServices()
151         }
152         val expectedServices = listOf(a11yService, a11yService3)
153         assertThat(notifiedServices.size).isEqualTo(2)
154         expectedServices.forEach {
155             assertThat(notifiedServices.contains(it.flattenToShortString())).isTrue()
156         }
157     }
158 
159     @Test
removePackageStatenull160     fun removePackageState() {
161         val testComponent = ComponentName("com.test.package", "TestClass")
162         val testComponent2 = ComponentName("com.test.package", "TestClass2")
163         val testComponent3 = ComponentName("com.test.package2", "TestClass3")
164         val testComponents = listOf(testComponent, testComponent2, testComponent3)
165 
166         val notifiedServices = runBlocking {
167             testComponents.forEach {
168                 accessibilitySourceService.markServiceAsNotified(it)
169             }
170             accessibilitySourceService.removePackageState(testComponent.packageName)
171             getNotifiedServices()
172         }
173 
174         assertThat(notifiedServices.size).isEqualTo(1)
175         assertThat(notifiedServices.contains(testComponent3.flattenToShortString())).isTrue()
176     }
177 
178     @Test
removePackageStateWithMultipleServicePerPackagenull179     fun removePackageStateWithMultipleServicePerPackage() {
180         val testComponent = ComponentName("com.test.package", "TestClass")
181         val testComponent2 = ComponentName("com.test.package", "TestClass2")
182         val testComponents = listOf(testComponent, testComponent2)
183 
184         val notifiedServices = runBlocking {
185             testComponents.forEach {
186                 accessibilitySourceService.markServiceAsNotified(it)
187             }
188             accessibilitySourceService.removePackageState(testComponent.packageName)
189             getNotifiedServices()
190         }
191 
192         assertThat(notifiedServices).isEmpty()
193     }
194 
195     @Test
removePackageState_noPreviouslyNotifiedPackagenull196     fun removePackageState_noPreviouslyNotifiedPackage() {
197         val testComponent = ComponentName("com.test.package", "TestClass")
198 
199         // Get the initial list of Components
200         val initialComponents = getNotifiedServices()
201 
202         // Verify no components are present
203         assertThat(initialComponents).isEmpty()
204 
205         // Forget about test package, and get the resulting list of Components
206         // Filter to the component that match the test component
207         val updatedComponents = runWithShellPermissionIdentity {
208             runBlocking {
209                 // Verify this should not fail!
210                 accessibilitySourceService.removePackageState(testComponent.packageName)
211                 getNotifiedServices()
212             }
213         }
214 
215         // Verify no components are present
216         assertThat(updatedComponents).isEmpty()
217     }
218 
getNotifiedServicesnull219     private fun getNotifiedServices(): MutableSet<String> {
220         return sharedPref.getStringSet(
221             AccessibilitySourceService.KEY_ALREADY_NOTIFIED_SERVICES,
222             mutableSetOf<String>()
223         )!!
224     }
225 
runWithShellPermissionIdentitynull226     private fun <R> runWithShellPermissionIdentity(block: () -> R): R {
227         val uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation()
228         uiAutomation.adoptShellPermissionIdentity()
229         try {
230             return block()
231         } finally {
232             uiAutomation.dropShellPermissionIdentity()
233         }
234     }
235 }
236