• 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.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), eq(SafetyCenterManager::class.java))
99         }
100 
101         notificationListenerCheck = runWithShellPermissionIdentity {
102             NotificationListenerCheckInternal(context) { shouldCancel }
103         }
104 
105         // ensure tests start with clean sharedPrefs
106         clearSharedPrefState()
107     }
108 
109     @After
cleanupnull110     fun cleanup() {
111         clearSharedPrefState()
112         shouldCancel = false
113         mockitoSession.finishMocking()
114     }
115 
116     @Test
getEnabledNotificationListenersAndNotifyIfNeeded_shouldCancel_finishJob_reschedulenull117     fun getEnabledNotificationListenersAndNotifyIfNeeded_shouldCancel_finishJob_reschedule() {
118         shouldCancel = true
119         val jobParameters = mock(JobParameters::class.java)
120 
121         runWithShellPermissionIdentity {
122             runBlocking {
123                 notificationListenerCheck.getEnabledNotificationListenersAndNotifyIfNeeded(
124                     jobParameters, mockNotificationListenerCheckJobService)
125             }
126         }
127 
128         verify(mockNotificationListenerCheckJobService).jobFinished(jobParameters, true)
129     }
130 
131     @Test
getEnabledNotificationListenersAndNotifyIfNeeded_finishJobnull132     fun getEnabledNotificationListenersAndNotifyIfNeeded_finishJob() {
133         val jobParameters = mock(JobParameters::class.java)
134 
135         runWithShellPermissionIdentity {
136             runBlocking {
137                 notificationListenerCheck.getEnabledNotificationListenersAndNotifyIfNeeded(
138                     jobParameters, mockNotificationListenerCheckJobService)
139             }
140         }
141 
142         verify(mockNotificationListenerCheckJobService).jobFinished(jobParameters, false)
143     }
144 
145     @Test
getEnabledNotificationListenersAndNotifyIfNeeded_sendsDataToSafetyCenternull146     fun getEnabledNotificationListenersAndNotifyIfNeeded_sendsDataToSafetyCenter() {
147         val jobParameters = mock(JobParameters::class.java)
148 
149         runWithShellPermissionIdentity {
150             runBlocking {
151                 notificationListenerCheck.getEnabledNotificationListenersAndNotifyIfNeeded(
152                     jobParameters, mockNotificationListenerCheckJobService)
153             }
154         }
155 
156         verify(mockSafetyCenterManager)
157             .setSafetySourceData(
158                 eq(SC_NLS_SOURCE_ID),
159                 any(SafetySourceData::class.java),
160                 any(SafetyEvent::class.java))
161     }
162 
163     @Test
removeDisabledComponentsFromNotifiedComponentsnull164     fun removeDisabledComponentsFromNotifiedComponents() {
165         val testComponent = ComponentName("com.test.package", "TestClass")
166         val testComponent2 = ComponentName("com.test.package2", "TestClass2")
167         val initialEnabledComponents = listOf(testComponent, testComponent2)
168         val updatedEnabledComponents = listOf(testComponent2)
169 
170         // Mark all components as notified, and get the resulting list of ComponentNames
171         val initialNlsComponents = runBlocking {
172             initialEnabledComponents.forEach {
173                 notificationListenerCheck.markComponentAsNotified(it)
174             }
175             getNotifiedComponents()
176         }
177 
178         // Verify expected components are present
179         assertThat(initialNlsComponents).isNotNull()
180         assertThat(initialNlsComponents.size).isEqualTo(initialEnabledComponents.size)
181         initialEnabledComponents.forEach { assertThat(initialNlsComponents.contains(it)).isTrue() }
182 
183         // Forget about test package, and get the resulting list of ComponentNames
184         // Filter to the component that match the test component
185         val updatedNlsComponents = runWithShellPermissionIdentity {
186             runBlocking {
187                 notificationListenerCheck.removeDisabledComponentsFromNotifiedComponents(
188                     updatedEnabledComponents)
189                 getNotifiedComponents()
190             }
191         }
192 
193         // Verify expected components are present
194         assertThat(updatedNlsComponents).isNotNull()
195         assertThat(updatedNlsComponents.size).isEqualTo(updatedEnabledComponents.size)
196         updatedEnabledComponents.forEach { assertThat(updatedNlsComponents.contains(it)).isTrue() }
197     }
198 
199     @Test
markAsNotifiednull200     fun markAsNotified() {
201         val testComponent = ComponentName("com.test.package", "TestClass")
202 
203         // Mark as notified, and get the resulting list of ComponentName
204         // Filter to the component that match the test component
205         // Ensure size is equal to one (not empty)
206         runBlocking {
207                 notificationListenerCheck.markComponentAsNotified(testComponent)
208                 getNotifiedComponents()
209             }
210             .filter { it == testComponent }
211             .also { assertThat(it.size).isEqualTo(1) }
212     }
213 
214     @Test
markAsNotified_notifySecondComponentnull215     fun markAsNotified_notifySecondComponent() {
216         val testComponent = ComponentName("com.test.package", "TestClass")
217         val testComponent2 = ComponentName("com.test.package2", "TestClass2")
218 
219         // Mark as notified, and get the resulting list of ComponentNames
220         var nlsComponents = runBlocking {
221             notificationListenerCheck.markComponentAsNotified(testComponent)
222             getNotifiedComponents()
223         }
224         // Expected # components is 1
225         assertThat(nlsComponents.size).isEqualTo(1)
226 
227         // Filter to the component that match the test component
228         // Ensure size is equal to one (not empty)
229         nlsComponents.filter { it == testComponent }.also { assertThat(it.size).isEqualTo(1) }
230 
231         // Mark second component as notified, and get the resulting list of ComponentNames
232         nlsComponents = runBlocking {
233             notificationListenerCheck.markComponentAsNotified(testComponent2)
234             getNotifiedComponents()
235         }
236         // Expected # components is 2
237         assertThat(nlsComponents.size).isEqualTo(2)
238 
239         // Filter to the component that match the test component
240         // Ensure size is equal to one (not empty)
241         nlsComponents.filter { it == testComponent2 }.also { assertThat(it.size).isEqualTo(1) }
242     }
243 
244     @Test
markAsNotified_notifySecondComponent_ensureFirstComponentNotModifiednull245     fun markAsNotified_notifySecondComponent_ensureFirstComponentNotModified() {
246         val testComponent = ComponentName("com.test.package", "TestClass")
247         val testComponent2 = ComponentName("com.test.package2", "TestClass2")
248 
249         // Mark as notified, and get the resulting list of ComponentNames
250         var nlsComponents = runBlocking {
251             notificationListenerCheck.markComponentAsNotified(testComponent)
252             getNotifiedComponents()
253         }
254         // Expected # components is 1
255         assertThat(nlsComponents.size).isEqualTo(1)
256 
257         // Filter to the component that match the test component
258         // Ensure size is equal to one (not empty)
259         // Get the component
260         val firstComponent =
261             nlsComponents
262                 .filter { it == testComponent }
263                 .also { assertThat(it.size).isEqualTo(1) }[0]
264 
265         // Mark second component as notified, and get the resulting list of ComponentNames
266         nlsComponents = runBlocking {
267             notificationListenerCheck.markComponentAsNotified(testComponent2)
268             getNotifiedComponents()
269         }
270         // Expected # components is 2
271         assertThat(nlsComponents.size).isEqualTo(2)
272 
273         // Verify first notified component still present
274         assertThat(nlsComponents.contains(firstComponent)).isTrue()
275     }
276 
277     @Test
removeFromNotifiedComponentsnull278     fun removeFromNotifiedComponents() {
279         val testComponent = ComponentName("com.test.package", "TestClass")
280         val testComponent2 = ComponentName("com.test.package2", "TestClass2")
281         val testComponents = listOf(testComponent, testComponent2)
282 
283         // Mark all components as notified, and get the resulting list of ComponentNames
284         val initialNlsComponents = runBlocking {
285             testComponents.forEach { notificationListenerCheck.markComponentAsNotified(it) }
286             getNotifiedComponents()
287         }
288 
289         // Verify expected components are present
290         assertThat(initialNlsComponents).isNotNull()
291         assertThat(initialNlsComponents.size).isEqualTo(testComponents.size)
292         testComponents.forEach { assertThat(initialNlsComponents.contains(it)).isTrue() }
293 
294         // Forget about test package, and get the resulting list of ComponentNames
295         // Filter to the component that match the test component
296         val updatedNlsComponents = runWithShellPermissionIdentity {
297             runBlocking {
298                 notificationListenerCheck.removeFromNotifiedComponents(testComponent.packageName)
299                 getNotifiedComponents()
300             }
301         }
302 
303         // Verify expected components are present
304         assertThat(updatedNlsComponents).isNotNull()
305         assertThat(updatedNlsComponents.size).isEqualTo(testComponents.size - 1)
306         assertThat(updatedNlsComponents.contains(testComponent)).isFalse()
307         assertThat(updatedNlsComponents.contains(testComponent2)).isTrue()
308     }
309 
310     @Test
removeFromNotifiedComponents_multipleNlsPerPackagenull311     fun removeFromNotifiedComponents_multipleNlsPerPackage() {
312         val testComponent = ComponentName("com.test.package", "TestClass")
313         val testComponent2 = ComponentName("com.test.package", "TestClass2")
314         val testComponents = listOf(testComponent, testComponent2)
315 
316         // Mark all components as notified, and get the resulting list of ComponentNames
317         val initialNlsComponents = runBlocking {
318             testComponents.forEach { notificationListenerCheck.markComponentAsNotified(it) }
319             getNotifiedComponents()
320         }
321 
322         // Verify expected components are present
323         assertThat(initialNlsComponents).isNotNull()
324         assertThat(initialNlsComponents.size).isEqualTo(testComponents.size)
325         testComponents.forEach { assertThat(initialNlsComponents.contains(it)).isTrue() }
326 
327         // Forget about test package, and get the resulting list of ComponentNames
328         // Filter to the component that match the test component
329         val updatedNlsComponents = runWithShellPermissionIdentity {
330             runBlocking {
331                 notificationListenerCheck.removeFromNotifiedComponents(testComponent.packageName)
332                 getNotifiedComponents()
333             }
334         }
335 
336         // Ensure empty
337         assertThat(updatedNlsComponents).isEmpty()
338     }
339 
340     @Test
removeFromNotifiedComponents_noPreviouslyNotifiedPackagenull341     fun removeFromNotifiedComponents_noPreviouslyNotifiedPackage() {
342         val testComponent = ComponentName("com.test.package", "TestClass")
343 
344         // Forget about test package, and get the resulting list of ComponentNames
345         // Filter to the component that match the test component
346         val updatedNlsComponents = runWithShellPermissionIdentity {
347             runBlocking {
348                 // Verify this should not fail!
349                 notificationListenerCheck.removeFromNotifiedComponents(testComponent.packageName)
350                 getNotifiedComponents()
351             }
352         }
353 
354         // Verify no components are present
355         assertThat(updatedNlsComponents).isEmpty()
356     }
357 
358     @Test
removeFromNotifiedComponents_componentNamenull359     fun removeFromNotifiedComponents_componentName() {
360         val testComponent = ComponentName("com.test.package", "TestClass")
361         val testComponent2 = ComponentName("com.test.package2", "TestClass2")
362         val testComponents = listOf(testComponent, testComponent2)
363 
364         // Mark all components as notified, and get the resulting list of ComponentNames
365         val initialNlsComponents = runBlocking {
366             testComponents.forEach { notificationListenerCheck.markComponentAsNotified(it) }
367             getNotifiedComponents()
368         }
369 
370         // Verify expected components are present
371         assertThat(initialNlsComponents).isNotNull()
372         assertThat(initialNlsComponents.size).isEqualTo(testComponents.size)
373         testComponents.forEach { assertThat(initialNlsComponents.contains(it)).isTrue() }
374 
375         // Forget about test component, and get the resulting list of ComponentNames
376         // Filter to the component that match the test component
377         val updatedNlsComponents = runWithShellPermissionIdentity {
378             runBlocking {
379                 notificationListenerCheck.removeFromNotifiedComponents(testComponent)
380                 getNotifiedComponents()
381             }
382         }
383 
384         // Verify expected components are present
385         assertThat(updatedNlsComponents).isNotNull()
386         assertThat(updatedNlsComponents.size).isEqualTo(testComponents.size - 1)
387         assertThat(updatedNlsComponents.contains(testComponent)).isFalse()
388         assertThat(updatedNlsComponents.contains(testComponent2)).isTrue()
389     }
390 
391     @Test
removeFromNotifiedComponents_componentName_multipleNlsPerPackagenull392     fun removeFromNotifiedComponents_componentName_multipleNlsPerPackage() {
393         val testComponent = ComponentName("com.test.package", "TestClass")
394         val testComponent2 = ComponentName("com.test.package", "TestClass2")
395         val testComponents = listOf(testComponent, testComponent2)
396 
397         // Mark all components as notified, and get the resulting list of ComponentNames
398         val initialNlsComponents = runBlocking {
399             testComponents.forEach { notificationListenerCheck.markComponentAsNotified(it) }
400             getNotifiedComponents()
401         }
402 
403         // Verify expected components are present
404         assertThat(initialNlsComponents).isNotNull()
405         assertThat(initialNlsComponents.size).isEqualTo(testComponents.size)
406         testComponents.forEach { assertThat(initialNlsComponents.contains(it)).isTrue() }
407 
408         // Forget about test component, and get the resulting list of ComponentNames
409         // Filter to the component that match the test component
410         val updatedNlsComponents = runWithShellPermissionIdentity {
411             runBlocking {
412                 notificationListenerCheck.removeFromNotifiedComponents(testComponent)
413                 getNotifiedComponents()
414             }
415         }
416 
417         // Verify expected components are present
418         assertThat(updatedNlsComponents).isNotNull()
419         assertThat(updatedNlsComponents.size).isEqualTo(testComponents.size - 1)
420         assertThat(updatedNlsComponents.contains(testComponent)).isFalse()
421         assertThat(updatedNlsComponents.contains(testComponent2)).isTrue()
422     }
423 
424     @Test
removeFromNotifiedComponents_componentName_noPreviouslyNotifiedPackagenull425     fun removeFromNotifiedComponents_componentName_noPreviouslyNotifiedPackage() {
426         val testComponent = ComponentName("com.test.package", "TestClass")
427 
428         // Forget about test component, and get the resulting list of ComponentNames
429         // Filter to the component that match the test component
430         val updatedNlsComponents = runWithShellPermissionIdentity {
431             runBlocking {
432                 // Verify this should not fail!
433                 notificationListenerCheck.removeFromNotifiedComponents(testComponent)
434                 getNotifiedComponents()
435             }
436         }
437 
438         // Verify no components are present
439         assertThat(updatedNlsComponents).isEmpty()
440     }
441 
442     @Test
createSafetySourceIssuenull443     fun createSafetySourceIssue() {
444         val testComponent = ComponentName("com.test.package", "TestClass")
445         val testAppLabel = "TestApp Label"
446         doReturn(PackageInfo().apply { applicationInfo = ApplicationInfo() }).`when` {
447             Utils.getPackageInfoForComponentName(
448                 any(Context::class.java), any(ComponentName::class.java))
449         }
450         doReturn(testAppLabel).`when` {
451             Utils.getApplicationLabel(any(Context::class.java), any(ApplicationInfo::class.java))
452         }
453 
454         val safetySourceIssue =
455             Preconditions.checkNotNull(
456                 notificationListenerCheck.createSafetySourceIssue(testComponent, 0))
457 
458         val expectedId = "notification_listener_${testComponent.flattenToString()}"
459         val expectedTitle =
460             context.getString(R.string.notification_listener_reminder_notification_title)
461         val expectedSubtitle: String = testAppLabel.toString()
462         val expectedSummary = context.getString(R.string.notification_listener_warning_card_content)
463         val expectedSeverityLevel = SafetySourceData.SEVERITY_LEVEL_INFORMATION
464         val expectedIssueTypeId = NotificationListenerCheckInternal.SC_NLS_ISSUE_TYPE_ID
465         val expectedDismissIntent =
466             Intent(context, NotificationListenerActionCardDismissalReceiver::class.java).apply {
467                 putExtra(Intent.EXTRA_COMPONENT_NAME, testComponent)
468                 flags = Intent.FLAG_RECEIVER_FOREGROUND
469                 identifier = testComponent.flattenToString()
470             }
471         val expectedDismissPendingIntent =
472             PendingIntent.getBroadcast(
473                 context, 0, expectedDismissIntent, PendingIntent.FLAG_IMMUTABLE)
474         val expectedAction1 =
475             SafetySourceIssue.Action.Builder(
476                     SC_NLS_DISABLE_ACTION_ID,
477                     context.getString(R.string.notification_listener_remove_access_button_label),
478                     getDisableNlsPendingIntent(context, expectedId, testComponent))
479                 .setWillResolve(true)
480                 .setSuccessMessage(
481                     context.getString(R.string.notification_listener_remove_access_success_label))
482                 .build()
483         val expectedAction2 =
484             SafetySourceIssue.Action.Builder(
485                     NotificationListenerCheckInternal.SC_SHOW_NLS_SETTINGS_ACTION_ID,
486                     context.getString(R.string.notification_listener_review_app_button_label),
487                     getNotificationListenerSettingsPendingIntent(context, testComponent))
488                 .build()
489 
490         assertThat(safetySourceIssue.id).isEqualTo(expectedId)
491         assertThat(safetySourceIssue.title).isEqualTo(expectedTitle)
492         assertThat(safetySourceIssue.subtitle).isEqualTo(expectedSubtitle)
493         assertThat(safetySourceIssue.summary).isEqualTo(expectedSummary)
494         assertThat(safetySourceIssue.severityLevel).isEqualTo(expectedSeverityLevel)
495         assertThat(safetySourceIssue.issueTypeId).isEqualTo(expectedIssueTypeId)
496         assertThat(safetySourceIssue.onDismissPendingIntent).isEqualTo(expectedDismissPendingIntent)
497         assertThat(safetySourceIssue.actions.size).isEqualTo(2)
498         assertThat(safetySourceIssue.actions).containsExactly(expectedAction2, expectedAction1)
499     }
500 
501     @Test
exemptPackagesNotInitializedUntilUsednull502     fun exemptPackagesNotInitializedUntilUsed() {
503         assertThat(notificationListenerCheck.exemptPackagesDelegate.isInitialized()).isFalse()
504         runWithShellPermissionIdentity {
505             notificationListenerCheck.exemptPackages
506         }
507         assertThat(notificationListenerCheck.exemptPackagesDelegate.isInitialized()).isTrue()
508     }
509 
<lambda>null510     private fun getNotifiedComponents(): Set<ComponentName> = runBlocking {
511         notificationListenerCheck
512             .getNotifiedComponents()
513             .mapNotNull { ComponentName.unflattenFromString(it) }
514             .toSet()
515     }
516 
517     /** @return [PendingIntent] for remove access button on the warning card. */
getDisableNlsPendingIntentnull518     private fun getDisableNlsPendingIntent(
519         context: Context,
520         safetySourceIssueId: String,
521         componentName: ComponentName
522     ): PendingIntent {
523         val intent =
524             Intent(context, DisableNotificationListenerComponentHandler::class.java).apply {
525                 putExtra(SafetyCenterManager.EXTRA_SAFETY_SOURCE_ISSUE_ID, safetySourceIssueId)
526                 putExtra(Intent.EXTRA_COMPONENT_NAME, componentName)
527                 flags = Intent.FLAG_RECEIVER_FOREGROUND
528                 identifier = componentName.flattenToString()
529             }
530 
531         return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
532     }
533 
534     /** @return [PendingIntent] to Notification Listener Settings page */
getNotificationListenerSettingsPendingIntentnull535     private fun getNotificationListenerSettingsPendingIntent(
536         context: Context,
537         componentName: ComponentName
538     ): PendingIntent {
539         val intent =
540             Intent(Settings.ACTION_NOTIFICATION_LISTENER_DETAIL_SETTINGS).apply {
541                 flags = Intent.FLAG_ACTIVITY_NEW_TASK
542                 identifier = componentName.flattenToString()
543                 putExtra(
544                     Settings.EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME,
545                     componentName.flattenToString())
546             }
547         return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
548     }
549 
clearSharedPrefStatenull550     private fun clearSharedPrefState() {
551         context
552             .getSharedPreferences(NLS_PREFERENCE_FILE, Context.MODE_PRIVATE)
553             .edit()
554             .clear()
555             .apply()
556     }
557 
runWithShellPermissionIdentitynull558     private fun <R> runWithShellPermissionIdentity(block: () -> R): R {
559         val uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation()
560         uiAutomation.adoptShellPermissionIdentity()
561         try {
562             return block()
563         } finally {
564             uiAutomation.dropShellPermissionIdentity()
565         }
566     }
567 }
568