• 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.PendingIntent
20 import android.app.job.JobParameters
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.provider.Settings
29 import android.safetycenter.SafetyCenterManager
30 import android.safetycenter.SafetyEvent
31 import android.safetycenter.SafetySourceData
32 import android.safetycenter.SafetySourceIssue
33 import androidx.test.core.app.ApplicationProvider
34 import androidx.test.ext.junit.runners.AndroidJUnit4
35 import androidx.test.filters.SdkSuppress
36 import androidx.test.platform.app.InstrumentationRegistry
37 import com.android.dx.mockito.inline.extended.ExtendedMockito
38 import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
39 import com.android.permissioncontroller.R
40 import com.android.permissioncontroller.permission.utils.Utils
41 import com.android.permissioncontroller.privacysources.DisableNotificationListenerComponentHandler
42 import com.android.permissioncontroller.privacysources.NotificationListenerActionCardDismissalReceiver
43 import com.android.permissioncontroller.privacysources.NotificationListenerCheckInternal
44 import com.android.permissioncontroller.privacysources.NotificationListenerCheckInternal.Companion.NLS_PREFERENCE_FILE
45 import com.android.permissioncontroller.privacysources.NotificationListenerCheckJobService
46 import com.android.permissioncontroller.privacysources.SC_NLS_DISABLE_ACTION_ID
47 import com.android.permissioncontroller.privacysources.SC_NLS_SOURCE_ID
48 import com.google.common.truth.Truth.assertThat
49 import kotlinx.coroutines.runBlocking
50 import org.junit.After
51 import org.junit.Before
52 import org.junit.Test
53 import org.junit.runner.RunWith
54 import org.mockito.ArgumentMatchers.any
55 import org.mockito.ArgumentMatchers.eq
56 import org.mockito.Mock
57 import org.mockito.Mockito.mock
58 import org.mockito.Mockito.verify
59 import org.mockito.MockitoAnnotations
60 import org.mockito.MockitoSession
61 import org.mockito.quality.Strictness
62 
63 /**
64  * Unit tests for [NotificationListenerCheckInternal]
65  *
66  * <p> Does not test notification as there are conflicts with being able to mock NotificationManager
67  * and PendintIntent.getBroadcast requiring a valid context. Notifications are tested in the CTS
68  * integration tests
69  */
70 @RunWith(AndroidJUnit4::class)
71 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
72 class NotificationListenerCheckInternalTest {
73 
74     @Mock lateinit var mockNotificationListenerCheckJobService: NotificationListenerCheckJobService
75     @Mock lateinit var mockSafetyCenterManager: SafetyCenterManager
76 
77     private lateinit var context: Context
78     private lateinit var mockitoSession: MockitoSession
79     private lateinit var notificationListenerCheck: NotificationListenerCheckInternal
80 
81     private var shouldCancel = false
82 
83     @Before
setupnull84     fun setup() {
85         MockitoAnnotations.initMocks(this)
86         context = ApplicationProvider.getApplicationContext()
87 
88         mockitoSession =
89             ExtendedMockito.mockitoSession()
90                 .spyStatic(Utils::class.java)
91                 .strictness(Strictness.LENIENT)
92                 .startMocking()
93 
94         // Setup Safety Center
95         doReturn(mockSafetyCenterManager).`when` {
96             Utils.getSystemServiceSafe(
97                 any(ContextWrapper::class.java),
98                 eq(SafetyCenterManager::class.java)
99             )
100         }
101 
102         notificationListenerCheck = runWithShellPermissionIdentity {
103             NotificationListenerCheckInternal(context) { shouldCancel }
104         }
105 
106         // ensure tests start with clean sharedPrefs
107         clearSharedPrefState()
108     }
109 
110     @After
cleanupnull111     fun cleanup() {
112         clearSharedPrefState()
113         shouldCancel = false
114         mockitoSession.finishMocking()
115     }
116 
117     @Test
getEnabledNotificationListenersAndNotifyIfNeeded_shouldCancel_finishJob_reschedulenull118     fun getEnabledNotificationListenersAndNotifyIfNeeded_shouldCancel_finishJob_reschedule() {
119         shouldCancel = true
120         val jobParameters = mock(JobParameters::class.java)
121 
122         runWithShellPermissionIdentity {
123             runBlocking {
124                 notificationListenerCheck.getEnabledNotificationListenersAndNotifyIfNeeded(
125                     jobParameters,
126                     mockNotificationListenerCheckJobService
127                 )
128             }
129         }
130 
131         verify(mockNotificationListenerCheckJobService).jobFinished(jobParameters, true)
132     }
133 
134     @Test
getEnabledNotificationListenersAndNotifyIfNeeded_finishJobnull135     fun getEnabledNotificationListenersAndNotifyIfNeeded_finishJob() {
136         val jobParameters = mock(JobParameters::class.java)
137 
138         runWithShellPermissionIdentity {
139             runBlocking {
140                 notificationListenerCheck.getEnabledNotificationListenersAndNotifyIfNeeded(
141                     jobParameters,
142                     mockNotificationListenerCheckJobService
143                 )
144             }
145         }
146 
147         verify(mockNotificationListenerCheckJobService).jobFinished(jobParameters, false)
148     }
149 
150     @Test
getEnabledNotificationListenersAndNotifyIfNeeded_sendsDataToSafetyCenternull151     fun getEnabledNotificationListenersAndNotifyIfNeeded_sendsDataToSafetyCenter() {
152         val jobParameters = mock(JobParameters::class.java)
153 
154         runWithShellPermissionIdentity {
155             runBlocking {
156                 notificationListenerCheck.getEnabledNotificationListenersAndNotifyIfNeeded(
157                     jobParameters,
158                     mockNotificationListenerCheckJobService
159                 )
160             }
161         }
162 
163         verify(mockSafetyCenterManager)
164             .setSafetySourceData(
165                 eq(SC_NLS_SOURCE_ID),
166                 any(SafetySourceData::class.java),
167                 any(SafetyEvent::class.java)
168             )
169     }
170 
171     @Test
removeDisabledComponentsFromNotifiedComponentsnull172     fun removeDisabledComponentsFromNotifiedComponents() {
173         val testComponent = ComponentName("com.test.package", "TestClass")
174         val testComponent2 = ComponentName("com.test.package2", "TestClass2")
175         val initialEnabledComponents = listOf(testComponent, testComponent2)
176         val updatedEnabledComponents = listOf(testComponent2)
177 
178         // Mark all components as notified, and get the resulting list of ComponentNames
179         val initialNlsComponents = runBlocking {
180             initialEnabledComponents.forEach {
181                 notificationListenerCheck.markComponentAsNotified(it)
182             }
183             getNotifiedComponents()
184         }
185 
186         // Verify expected components are present
187         assertThat(initialNlsComponents).isNotNull()
188         assertThat(initialNlsComponents.size).isEqualTo(initialEnabledComponents.size)
189         initialEnabledComponents.forEach { assertThat(initialNlsComponents.contains(it)).isTrue() }
190 
191         // Forget about test package, and get the resulting list of ComponentNames
192         // Filter to the component that match the test component
193         val updatedNlsComponents = runWithShellPermissionIdentity {
194             runBlocking {
195                 notificationListenerCheck.removeDisabledComponentsFromNotifiedComponents(
196                     updatedEnabledComponents
197                 )
198                 getNotifiedComponents()
199             }
200         }
201 
202         // Verify expected components are present
203         assertThat(updatedNlsComponents).isNotNull()
204         assertThat(updatedNlsComponents.size).isEqualTo(updatedEnabledComponents.size)
205         updatedEnabledComponents.forEach { assertThat(updatedNlsComponents.contains(it)).isTrue() }
206     }
207 
208     @Test
markAsNotifiednull209     fun markAsNotified() {
210         val testComponent = ComponentName("com.test.package", "TestClass")
211 
212         // Mark as notified, and get the resulting list of ComponentName
213         // Filter to the component that match the test component
214         // Ensure size is equal to one (not empty)
215         runBlocking {
216                 notificationListenerCheck.markComponentAsNotified(testComponent)
217                 getNotifiedComponents()
218             }
219             .filter { it == testComponent }
220             .also { assertThat(it.size).isEqualTo(1) }
221     }
222 
223     @Test
markAsNotified_notifySecondComponentnull224     fun markAsNotified_notifySecondComponent() {
225         val testComponent = ComponentName("com.test.package", "TestClass")
226         val testComponent2 = ComponentName("com.test.package2", "TestClass2")
227 
228         // Mark as notified, and get the resulting list of ComponentNames
229         var nlsComponents = runBlocking {
230             notificationListenerCheck.markComponentAsNotified(testComponent)
231             getNotifiedComponents()
232         }
233         // Expected # components is 1
234         assertThat(nlsComponents.size).isEqualTo(1)
235 
236         // Filter to the component that match the test component
237         // Ensure size is equal to one (not empty)
238         nlsComponents.filter { it == testComponent }.also { assertThat(it.size).isEqualTo(1) }
239 
240         // Mark second component as notified, and get the resulting list of ComponentNames
241         nlsComponents = runBlocking {
242             notificationListenerCheck.markComponentAsNotified(testComponent2)
243             getNotifiedComponents()
244         }
245         // Expected # components is 2
246         assertThat(nlsComponents.size).isEqualTo(2)
247 
248         // Filter to the component that match the test component
249         // Ensure size is equal to one (not empty)
250         nlsComponents.filter { it == testComponent2 }.also { assertThat(it.size).isEqualTo(1) }
251     }
252 
253     @Test
markAsNotified_notifySecondComponent_ensureFirstComponentNotModifiednull254     fun markAsNotified_notifySecondComponent_ensureFirstComponentNotModified() {
255         val testComponent = ComponentName("com.test.package", "TestClass")
256         val testComponent2 = ComponentName("com.test.package2", "TestClass2")
257 
258         // Mark as notified, and get the resulting list of ComponentNames
259         var nlsComponents = runBlocking {
260             notificationListenerCheck.markComponentAsNotified(testComponent)
261             getNotifiedComponents()
262         }
263         // Expected # components is 1
264         assertThat(nlsComponents.size).isEqualTo(1)
265 
266         // Filter to the component that match the test component
267         // Ensure size is equal to one (not empty)
268         // Get the component
269         val firstComponent =
270             nlsComponents
271                 .filter { it == testComponent }
272                 .also { assertThat(it.size).isEqualTo(1) }[0]
273 
274         // Mark second component as notified, and get the resulting list of ComponentNames
275         nlsComponents = runBlocking {
276             notificationListenerCheck.markComponentAsNotified(testComponent2)
277             getNotifiedComponents()
278         }
279         // Expected # components is 2
280         assertThat(nlsComponents.size).isEqualTo(2)
281 
282         // Verify first notified component still present
283         assertThat(nlsComponents.contains(firstComponent)).isTrue()
284     }
285 
286     @Test
removeFromNotifiedComponentsnull287     fun removeFromNotifiedComponents() {
288         val testComponent = ComponentName("com.test.package", "TestClass")
289         val testComponent2 = ComponentName("com.test.package2", "TestClass2")
290         val testComponents = listOf(testComponent, testComponent2)
291 
292         // Mark all components as notified, and get the resulting list of ComponentNames
293         val initialNlsComponents = runBlocking {
294             testComponents.forEach { notificationListenerCheck.markComponentAsNotified(it) }
295             getNotifiedComponents()
296         }
297 
298         // Verify expected components are present
299         assertThat(initialNlsComponents).isNotNull()
300         assertThat(initialNlsComponents.size).isEqualTo(testComponents.size)
301         testComponents.forEach { assertThat(initialNlsComponents.contains(it)).isTrue() }
302 
303         // Forget about test package, and get the resulting list of ComponentNames
304         // Filter to the component that match the test component
305         val updatedNlsComponents = runWithShellPermissionIdentity {
306             runBlocking {
307                 notificationListenerCheck.removeFromNotifiedComponents(testComponent.packageName)
308                 getNotifiedComponents()
309             }
310         }
311 
312         // Verify expected components are present
313         assertThat(updatedNlsComponents).isNotNull()
314         assertThat(updatedNlsComponents.size).isEqualTo(testComponents.size - 1)
315         assertThat(updatedNlsComponents.contains(testComponent)).isFalse()
316         assertThat(updatedNlsComponents.contains(testComponent2)).isTrue()
317     }
318 
319     @Test
removeFromNotifiedComponents_multipleNlsPerPackagenull320     fun removeFromNotifiedComponents_multipleNlsPerPackage() {
321         val testComponent = ComponentName("com.test.package", "TestClass")
322         val testComponent2 = ComponentName("com.test.package", "TestClass2")
323         val testComponents = listOf(testComponent, testComponent2)
324 
325         // Mark all components as notified, and get the resulting list of ComponentNames
326         val initialNlsComponents = runBlocking {
327             testComponents.forEach { notificationListenerCheck.markComponentAsNotified(it) }
328             getNotifiedComponents()
329         }
330 
331         // Verify expected components are present
332         assertThat(initialNlsComponents).isNotNull()
333         assertThat(initialNlsComponents.size).isEqualTo(testComponents.size)
334         testComponents.forEach { assertThat(initialNlsComponents.contains(it)).isTrue() }
335 
336         // Forget about test package, and get the resulting list of ComponentNames
337         // Filter to the component that match the test component
338         val updatedNlsComponents = runWithShellPermissionIdentity {
339             runBlocking {
340                 notificationListenerCheck.removeFromNotifiedComponents(testComponent.packageName)
341                 getNotifiedComponents()
342             }
343         }
344 
345         // Ensure empty
346         assertThat(updatedNlsComponents).isEmpty()
347     }
348 
349     @Test
removeFromNotifiedComponents_noPreviouslyNotifiedPackagenull350     fun removeFromNotifiedComponents_noPreviouslyNotifiedPackage() {
351         val testComponent = ComponentName("com.test.package", "TestClass")
352 
353         // Forget about test package, and get the resulting list of ComponentNames
354         // Filter to the component that match the test component
355         val updatedNlsComponents = runWithShellPermissionIdentity {
356             runBlocking {
357                 // Verify this should not fail!
358                 notificationListenerCheck.removeFromNotifiedComponents(testComponent.packageName)
359                 getNotifiedComponents()
360             }
361         }
362 
363         // Verify no components are present
364         assertThat(updatedNlsComponents).isEmpty()
365     }
366 
367     @Test
removeFromNotifiedComponents_componentNamenull368     fun removeFromNotifiedComponents_componentName() {
369         val testComponent = ComponentName("com.test.package", "TestClass")
370         val testComponent2 = ComponentName("com.test.package2", "TestClass2")
371         val testComponents = listOf(testComponent, testComponent2)
372 
373         // Mark all components as notified, and get the resulting list of ComponentNames
374         val initialNlsComponents = runBlocking {
375             testComponents.forEach { notificationListenerCheck.markComponentAsNotified(it) }
376             getNotifiedComponents()
377         }
378 
379         // Verify expected components are present
380         assertThat(initialNlsComponents).isNotNull()
381         assertThat(initialNlsComponents.size).isEqualTo(testComponents.size)
382         testComponents.forEach { assertThat(initialNlsComponents.contains(it)).isTrue() }
383 
384         // Forget about test component, and get the resulting list of ComponentNames
385         // Filter to the component that match the test component
386         val updatedNlsComponents = runWithShellPermissionIdentity {
387             runBlocking {
388                 notificationListenerCheck.removeFromNotifiedComponents(testComponent)
389                 getNotifiedComponents()
390             }
391         }
392 
393         // Verify expected components are present
394         assertThat(updatedNlsComponents).isNotNull()
395         assertThat(updatedNlsComponents.size).isEqualTo(testComponents.size - 1)
396         assertThat(updatedNlsComponents.contains(testComponent)).isFalse()
397         assertThat(updatedNlsComponents.contains(testComponent2)).isTrue()
398     }
399 
400     @Test
removeFromNotifiedComponents_componentName_multipleNlsPerPackagenull401     fun removeFromNotifiedComponents_componentName_multipleNlsPerPackage() {
402         val testComponent = ComponentName("com.test.package", "TestClass")
403         val testComponent2 = ComponentName("com.test.package", "TestClass2")
404         val testComponents = listOf(testComponent, testComponent2)
405 
406         // Mark all components as notified, and get the resulting list of ComponentNames
407         val initialNlsComponents = runBlocking {
408             testComponents.forEach { notificationListenerCheck.markComponentAsNotified(it) }
409             getNotifiedComponents()
410         }
411 
412         // Verify expected components are present
413         assertThat(initialNlsComponents).isNotNull()
414         assertThat(initialNlsComponents.size).isEqualTo(testComponents.size)
415         testComponents.forEach { assertThat(initialNlsComponents.contains(it)).isTrue() }
416 
417         // Forget about test component, and get the resulting list of ComponentNames
418         // Filter to the component that match the test component
419         val updatedNlsComponents = runWithShellPermissionIdentity {
420             runBlocking {
421                 notificationListenerCheck.removeFromNotifiedComponents(testComponent)
422                 getNotifiedComponents()
423             }
424         }
425 
426         // Verify expected components are present
427         assertThat(updatedNlsComponents).isNotNull()
428         assertThat(updatedNlsComponents.size).isEqualTo(testComponents.size - 1)
429         assertThat(updatedNlsComponents.contains(testComponent)).isFalse()
430         assertThat(updatedNlsComponents.contains(testComponent2)).isTrue()
431     }
432 
433     @Test
removeFromNotifiedComponents_componentName_noPreviouslyNotifiedPackagenull434     fun removeFromNotifiedComponents_componentName_noPreviouslyNotifiedPackage() {
435         val testComponent = ComponentName("com.test.package", "TestClass")
436 
437         // Forget about test component, and get the resulting list of ComponentNames
438         // Filter to the component that match the test component
439         val updatedNlsComponents = runWithShellPermissionIdentity {
440             runBlocking {
441                 // Verify this should not fail!
442                 notificationListenerCheck.removeFromNotifiedComponents(testComponent)
443                 getNotifiedComponents()
444             }
445         }
446 
447         // Verify no components are present
448         assertThat(updatedNlsComponents).isEmpty()
449     }
450 
451     @Test
createSafetySourceIssuenull452     fun createSafetySourceIssue() {
453         val testComponent = ComponentName("com.test.package", "TestClass")
454         val testAppLabel = "TestApp Label"
455         doReturn(PackageInfo().apply { applicationInfo = ApplicationInfo() }).`when` {
456             Utils.getPackageInfoForComponentName(
457                 any(Context::class.java),
458                 any(ComponentName::class.java)
459             )
460         }
461         doReturn(testAppLabel).`when` {
462             Utils.getApplicationLabel(any(Context::class.java), any(ApplicationInfo::class.java))
463         }
464 
465         val safetySourceIssue =
466             checkNotNull(notificationListenerCheck.createSafetySourceIssue(testComponent, 0))
467 
468         val expectedId = "notification_listener_${testComponent.flattenToString()}"
469         val expectedTitle =
470             context.getString(R.string.notification_listener_reminder_notification_title)
471         val expectedSubtitle: String = testAppLabel.toString()
472         val expectedSummary = context.getString(R.string.notification_listener_warning_card_content)
473         val expectedSeverityLevel = SafetySourceData.SEVERITY_LEVEL_INFORMATION
474         val expectedIssueTypeId = NotificationListenerCheckInternal.SC_NLS_ISSUE_TYPE_ID
475         val expectedDismissIntent =
476             Intent(context, NotificationListenerActionCardDismissalReceiver::class.java).apply {
477                 putExtra(Intent.EXTRA_COMPONENT_NAME, testComponent)
478                 flags = Intent.FLAG_RECEIVER_FOREGROUND
479                 identifier = testComponent.flattenToString()
480             }
481         val expectedDismissPendingIntent =
482             PendingIntent.getBroadcast(
483                 context,
484                 0,
485                 expectedDismissIntent,
486                 PendingIntent.FLAG_IMMUTABLE
487             )
488         val expectedAction1 =
489             SafetySourceIssue.Action.Builder(
490                     SC_NLS_DISABLE_ACTION_ID,
491                     context.getString(R.string.notification_listener_remove_access_button_label),
492                     getDisableNlsPendingIntent(context, expectedId, testComponent)
493                 )
494                 .setWillResolve(true)
495                 .setSuccessMessage(
496                     context.getString(R.string.notification_listener_remove_access_success_label)
497                 )
498                 .build()
499         val expectedAction2 =
500             SafetySourceIssue.Action.Builder(
501                     NotificationListenerCheckInternal.SC_SHOW_NLS_SETTINGS_ACTION_ID,
502                     context.getString(R.string.notification_listener_review_app_button_label),
503                     getNotificationListenerSettingsPendingIntent(context, testComponent)
504                 )
505                 .build()
506 
507         assertThat(safetySourceIssue.id).isEqualTo(expectedId)
508         assertThat(safetySourceIssue.title).isEqualTo(expectedTitle)
509         assertThat(safetySourceIssue.subtitle).isEqualTo(expectedSubtitle)
510         assertThat(safetySourceIssue.summary).isEqualTo(expectedSummary)
511         assertThat(safetySourceIssue.severityLevel).isEqualTo(expectedSeverityLevel)
512         assertThat(safetySourceIssue.issueTypeId).isEqualTo(expectedIssueTypeId)
513         assertThat(safetySourceIssue.onDismissPendingIntent).isEqualTo(expectedDismissPendingIntent)
514         assertThat(safetySourceIssue.actions.size).isEqualTo(2)
515         assertThat(safetySourceIssue.actions).containsExactly(expectedAction2, expectedAction1)
516     }
517 
518     @Test
exemptPackagesNotInitializedUntilUsednull519     fun exemptPackagesNotInitializedUntilUsed() {
520         assertThat(notificationListenerCheck.exemptPackagesDelegate.isInitialized()).isFalse()
521         runWithShellPermissionIdentity { notificationListenerCheck.exemptPackages }
522         assertThat(notificationListenerCheck.exemptPackagesDelegate.isInitialized()).isTrue()
523     }
524 
<lambda>null525     private fun getNotifiedComponents(): Set<ComponentName> = runBlocking {
526         notificationListenerCheck
527             .getNotifiedComponents()
528             .mapNotNull { ComponentName.unflattenFromString(it) }
529             .toSet()
530     }
531 
532     /** @return [PendingIntent] for remove access button on the warning card. */
getDisableNlsPendingIntentnull533     private fun getDisableNlsPendingIntent(
534         context: Context,
535         safetySourceIssueId: String,
536         componentName: ComponentName
537     ): PendingIntent {
538         val intent =
539             Intent(context, DisableNotificationListenerComponentHandler::class.java).apply {
540                 putExtra(SafetyCenterManager.EXTRA_SAFETY_SOURCE_ISSUE_ID, safetySourceIssueId)
541                 putExtra(Intent.EXTRA_COMPONENT_NAME, componentName)
542                 flags = Intent.FLAG_RECEIVER_FOREGROUND
543                 identifier = componentName.flattenToString()
544             }
545 
546         return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
547     }
548 
549     /** @return [PendingIntent] to Notification Listener Settings page */
getNotificationListenerSettingsPendingIntentnull550     private fun getNotificationListenerSettingsPendingIntent(
551         context: Context,
552         componentName: ComponentName
553     ): PendingIntent {
554         val intent =
555             Intent(Settings.ACTION_NOTIFICATION_LISTENER_DETAIL_SETTINGS).apply {
556                 flags = Intent.FLAG_ACTIVITY_NEW_TASK
557                 identifier = componentName.flattenToString()
558                 putExtra(
559                     Settings.EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME,
560                     componentName.flattenToString()
561                 )
562             }
563         return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
564     }
565 
clearSharedPrefStatenull566     private fun clearSharedPrefState() {
567         context
568             .getSharedPreferences(NLS_PREFERENCE_FILE, Context.MODE_PRIVATE)
569             .edit()
570             .clear()
571             .apply()
572     }
573 
runWithShellPermissionIdentitynull574     private fun <R> runWithShellPermissionIdentity(block: () -> R): R {
575         val uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation()
576         uiAutomation.adoptShellPermissionIdentity()
577         try {
578             return block()
579         } finally {
580             uiAutomation.dropShellPermissionIdentity()
581         }
582     }
583 }
584