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