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.NotificationManager
20 import android.app.role.RoleManager
21 import android.content.ComponentName
22 import android.content.Context
23 import android.content.ContextWrapper
24 import android.content.Intent
25 import android.content.pm.ApplicationInfo
26 import android.content.pm.PackageInfo
27 import android.os.Build
28 import android.os.UserHandle
29 import android.os.UserManager
30 import android.provider.DeviceConfig
31 import android.safetycenter.SafetyCenterManager
32 import android.safetycenter.SafetyEvent
33 import android.safetycenter.SafetySourceData
34 import androidx.test.core.app.ApplicationProvider
35 import androidx.test.ext.junit.runners.AndroidJUnit4
36 import androidx.test.filters.SdkSuppress
37 import androidx.test.platform.app.InstrumentationRegistry
38 import com.android.dx.mockito.inline.extended.ExtendedMockito
39 import com.android.permissioncontroller.Constants.NOTIFICATION_LISTENER_CHECK_NOTIFICATION_ID
40 import com.android.permissioncontroller.permission.utils.Utils
41 import com.android.permissioncontroller.PermissionControllerApplication
42 import com.android.permissioncontroller.privacysources.NotificationListenerPrivacySource
43 import com.android.permissioncontroller.privacysources.NotificationListenerCheckInternal
44 import com.android.permissioncontroller.privacysources.PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED
45 import com.android.permissioncontroller.privacysources.SC_NLS_SOURCE_ID
46 import com.android.permissioncontroller.privacysources.SafetyCenterReceiver
47 import org.junit.After
48 import org.junit.Before
49 import org.junit.Test
50 import org.junit.runner.RunWith
51 import org.mockito.ArgumentMatchers.any
52 import org.mockito.ArgumentMatchers.anyBoolean
53 import org.mockito.ArgumentMatchers.anyString
54 import org.mockito.ArgumentMatchers.eq
55 import org.mockito.Mock
56 import org.mockito.MockitoAnnotations
57 import org.mockito.MockitoSession
58 import org.mockito.quality.Strictness
59 import org.mockito.Mockito.never
60 import org.mockito.Mockito.`when` as whenever
61 import org.mockito.Mockito.verify
62 import org.mockito.Mockito.verifyZeroInteractions
63 
64 @RunWith(AndroidJUnit4::class)
65 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
66 class NotificationListenerPrivacySourceTest {
67     private lateinit var mockitoSession: MockitoSession
68 
69     @Mock
70     lateinit var mockSafetyCenterManager: SafetyCenterManager
71     @Mock
72     lateinit var mockNotificationManager: NotificationManager
73     @Mock
74     lateinit var mockRoleManager: RoleManager
75     @Mock
76     lateinit var mockUserManager: UserManager
77 
78     private lateinit var context: Context
79     private lateinit var notificationListenerCheck: NotificationListenerCheckInternal
80 
81     private val privacySource: NotificationListenerPrivacySource =
82         NotificationListenerPrivacySource()
83 
84     companion object {
85         private val testComponent1 = ComponentName("com.test.package", "TestClass1")
86         private val testComponent2 = ComponentName("com.test.package", "TestClass2")
87         private val enabledComponents = listOf(testComponent1, testComponent2)
88     }
89 
90     @Before
setupnull91     fun setup() {
92         MockitoAnnotations.initMocks(this)
93         context = ApplicationProvider.getApplicationContext()
94 
95         mockitoSession = ExtendedMockito.mockitoSession()
96             .mockStatic(DeviceConfig::class.java)
97             .mockStatic(PermissionControllerApplication::class.java)
98             .mockStatic(Utils::class.java)
99             .strictness(Strictness.LENIENT).startMocking()
100 
101         // Setup default flagging
102         setNotificationListenerCheckEnabled(true)
103 
104         // Setup contexts
105         whenever(Utils.getParentUserContext(any(ContextWrapper::class.java)))
106             .thenReturn(context)
107 
108         // Setup package manager and mocked NLS packages
109         val packageInfo1 = getPackageInfo()
110         val packageInfo2 = getPackageInfo()
111         whenever(Utils.getPackageInfoForComponentName(
112             any(ContextWrapper::class.java),
113             eq(testComponent1)))
114             .thenReturn(packageInfo1)
115         whenever(Utils.getPackageInfoForComponentName(
116             any(ContextWrapper::class.java),
117             eq(testComponent2)))
118             .thenReturn(packageInfo2)
119         whenever(Utils.getApplicationLabel(
120             any(ContextWrapper::class.java),
121             eq(packageInfo1.applicationInfo)))
122             .thenReturn(testComponent1.className)
123         whenever(Utils.getApplicationLabel(
124             any(ContextWrapper::class.java),
125             eq(packageInfo2.applicationInfo)))
126             .thenReturn(testComponent2.className)
127 
128         // Setup UserManager and User
129         whenever(Utils.getSystemServiceSafe(
130             any(ContextWrapper::class.java),
131             eq(UserManager::class.java)))
132             .thenReturn(mockUserManager)
133         whenever(mockUserManager.getProfileParent(any(UserHandle::class.java)))
134             .thenReturn(null)
135 
136         // Setup Notification Manager
137         whenever(Utils.getSystemServiceSafe(
138             any(ContextWrapper::class.java),
139             eq(NotificationManager::class.java)))
140             .thenReturn(mockNotificationManager)
141         whenever(mockNotificationManager.enabledNotificationListeners)
142             .thenReturn(listOf(testComponent1, testComponent2))
143 
144         whenever(Utils.getSystemServiceSafe(
145             any(ContextWrapper::class.java),
146             eq(RoleManager::class.java)))
147             .thenReturn(mockRoleManager)
148         whenever(mockRoleManager.getRoleHolders(anyString()))
149             .thenReturn(emptyList())
150 
151         // Setup Safety Center
152         whenever(Utils.getSystemServiceSafe(
153             any(ContextWrapper::class.java),
154             eq(SafetyCenterManager::class.java)))
155             .thenReturn(mockSafetyCenterManager)
156 
157         // Init NotificationListenerCheckInternal, used to quickly create expected SafetySourceData
158         notificationListenerCheck = runWithShellPermissionIdentity {
159             NotificationListenerCheckInternal(context, null)
160         }
161     }
162 
163     @After
cleanupnull164     fun cleanup() {
165         mockitoSession.finishMocking()
166     }
167 
168     @Test
safetyCenterEnabledChanged_removesNotificationsnull169     fun safetyCenterEnabledChanged_removesNotifications() {
170         runWithShellPermissionIdentity {
171             privacySource.safetyCenterEnabledChanged(context, false)
172         }
173 
174         verify(mockNotificationManager).cancel(NOTIFICATION_LISTENER_CHECK_NOTIFICATION_ID)
175     }
176 
177     @Test
safetyCenterEnabledChanged_doesNotUpdateSafetyCenterDatanull178     fun safetyCenterEnabledChanged_doesNotUpdateSafetyCenterData() {
179         runWithShellPermissionIdentity {
180             privacySource.safetyCenterEnabledChanged(context, false)
181         }
182 
183         verify(mockSafetyCenterManager, never())
184             .setSafetySourceData(
185                 eq(SC_NLS_SOURCE_ID),
186                 any(SafetySourceData::class.java),
187                 any(SafetyEvent::class.java))
188     }
189 
190     @Test
safetyCenterEnabledChanged_notificationListenerCheckDisabled_noSafetyCenterInteractionsnull191     fun safetyCenterEnabledChanged_notificationListenerCheckDisabled_noSafetyCenterInteractions() {
192         setNotificationListenerCheckEnabled(false)
193 
194         privacySource.safetyCenterEnabledChanged(context, false)
195 
196         verifyZeroInteractions(mockSafetyCenterManager)
197     }
198 
199     @Test
rescanAndPushSafetyCenterData_eventDeviceRebooted_updateSafetyCenterDatanull200     fun rescanAndPushSafetyCenterData_eventDeviceRebooted_updateSafetyCenterData() {
201         privacySource.rescanAndPushSafetyCenterData(
202             context,
203             Intent(Intent.ACTION_BOOT_COMPLETED),
204             SafetyCenterReceiver.RefreshEvent.EVENT_DEVICE_REBOOTED
205         )
206 
207         val expectedSafetySourceData: SafetySourceData = createExpectedSafetyCenterData()
208         val expectedSafetyEvent =
209             SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_REBOOTED).build()
210 
211         verify(mockSafetyCenterManager)
212             .setSafetySourceData(
213                 SC_NLS_SOURCE_ID,
214                 expectedSafetySourceData,
215                 expectedSafetyEvent
216             )
217     }
218 
219     @Test
rescanAndPushSafetyCenterData_updatesSafetyCenterEventRefreshnull220     fun rescanAndPushSafetyCenterData_updatesSafetyCenterEventRefresh() {
221         val intent = Intent(SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES)
222             .putExtra(SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID,
223                 SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID)
224         privacySource.rescanAndPushSafetyCenterData(
225             context,
226             intent,
227             SafetyCenterReceiver.RefreshEvent.EVENT_REFRESH_REQUESTED
228         )
229 
230         val expectedSafetySourceData: SafetySourceData = createExpectedSafetyCenterData()
231 
232         val refreshBroadcastId =
233             intent.getStringExtra(SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID)
234         val expectedSafetyEvent =
235             SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
236                 .setRefreshBroadcastId(refreshBroadcastId)
237                 .build()
238 
239         verify(mockSafetyCenterManager)
240             .setSafetySourceData(
241                 SC_NLS_SOURCE_ID,
242                 expectedSafetySourceData,
243                 expectedSafetyEvent
244             )
245     }
246 
247     @Test
rescanAndPushSafetyCenterData_updatesSafetyCenterEventUnknownnull248     fun rescanAndPushSafetyCenterData_updatesSafetyCenterEventUnknown() {
249         privacySource.rescanAndPushSafetyCenterData(
250             context,
251             Intent(Intent.ACTION_BOOT_COMPLETED),
252             SafetyCenterReceiver.RefreshEvent.UNKNOWN
253         )
254 
255         val expectedSafetySourceData: SafetySourceData = createExpectedSafetyCenterData()
256         val expectedSafetyEvent =
257             SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build()
258 
259         verify(mockSafetyCenterManager)
260             .setSafetySourceData(
261                 SC_NLS_SOURCE_ID,
262                 expectedSafetySourceData,
263                 expectedSafetyEvent
264             )
265     }
266 
267     @Test
rescanAndPushSafetyCenterData_notificationListenerCheckDisabled_noSafetyCenterInteractionsnull268     fun rescanAndPushSafetyCenterData_notificationListenerCheckDisabled_noSafetyCenterInteractions
269             () {
270         setNotificationListenerCheckEnabled(false)
271 
272         privacySource.rescanAndPushSafetyCenterData(
273             context,
274             Intent(Intent.ACTION_BOOT_COMPLETED),
275             SafetyCenterReceiver.RefreshEvent.UNKNOWN
276         )
277 
278         verifyZeroInteractions(mockSafetyCenterManager)
279     }
280 
setNotificationListenerCheckEnablednull281     private fun setNotificationListenerCheckEnabled(enabled: Boolean) {
282         whenever(
283             DeviceConfig.getBoolean(
284                 eq(DeviceConfig.NAMESPACE_PRIVACY),
285                 eq(PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED),
286                 anyBoolean()
287             )
288         ).thenReturn(enabled)
289     }
290 
getPackageInfonull291     private fun getPackageInfo(): PackageInfo {
292         return PackageInfo().apply {
293             applicationInfo = ApplicationInfo()
294         }
295     }
296 
createExpectedSafetyCenterDatanull297     private fun createExpectedSafetyCenterData(): SafetySourceData {
298         val pendingIssues =
299             enabledComponents.mapNotNull {
300                 notificationListenerCheck.createSafetySourceIssue(it, 0)
301             }
302         val dataBuilder = SafetySourceData.Builder()
303         pendingIssues.forEach { dataBuilder.addIssue(it) }
304         return dataBuilder.build()
305     }
306 }
307 
runWithShellPermissionIdentitynull308 private fun <R> runWithShellPermissionIdentity(block: () -> R): R {
309     val uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation()
310     uiAutomation.adoptShellPermissionIdentity()
311     try {
312         return block()
313     } finally {
314         uiAutomation.dropShellPermissionIdentity()
315     }
316 }
317