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