1 /* 2 * Copyright (C) 2018 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 android.permission.cts; 18 19 import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION; 20 import static android.Manifest.permission.ACCESS_FINE_LOCATION; 21 import static android.app.AppOpsManager.OPSTR_FINE_LOCATION; 22 import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED; 23 import static android.content.Context.BIND_AUTO_CREATE; 24 import static android.content.Context.BIND_NOT_FOREGROUND; 25 import static android.location.Criteria.ACCURACY_FINE; 26 import static android.os.Process.myUserHandle; 27 import static android.provider.Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS; 28 import static android.provider.Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS; 29 30 import static com.android.compatibility.common.util.SystemUtil.runShellCommand; 31 import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow; 32 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; 33 import static com.android.compatibility.common.util.SystemUtil.waitForBroadcasts; 34 35 import static org.junit.Assert.assertNotNull; 36 import static org.junit.Assert.assertNull; 37 import static org.junit.Assert.assertTrue; 38 import static org.junit.Assume.assumeFalse; 39 import static org.junit.Assume.assumeTrue; 40 41 import static java.util.concurrent.TimeUnit.MILLISECONDS; 42 43 import android.app.ActivityManager; 44 import android.app.ActivityOptions; 45 import android.app.AppOpsManager; 46 import android.app.PendingIntent; 47 import android.app.UiAutomation; 48 import android.content.ComponentName; 49 import android.content.ContentResolver; 50 import android.content.Context; 51 import android.content.Intent; 52 import android.content.ServiceConnection; 53 import android.content.pm.PackageManager; 54 import android.location.Criteria; 55 import android.location.Location; 56 import android.location.LocationListener; 57 import android.location.LocationManager; 58 import android.os.Build; 59 import android.os.Bundle; 60 import android.os.IBinder; 61 import android.os.Looper; 62 import android.os.Process; 63 import android.permission.cts.appthataccesseslocation.IAccessLocationOnCommand; 64 import android.platform.test.annotations.AppModeFull; 65 import android.platform.test.annotations.AsbSecurityTest; 66 import android.platform.test.annotations.SystemUserOnly; 67 import android.platform.test.rule.ScreenRecordRule; 68 import android.provider.DeviceConfig; 69 import android.provider.Settings; 70 import android.service.notification.StatusBarNotification; 71 import android.util.Log; 72 73 import androidx.annotation.NonNull; 74 import androidx.test.InstrumentationRegistry; 75 import androidx.test.filters.FlakyTest; 76 import androidx.test.filters.SdkSuppress; 77 import androidx.test.runner.AndroidJUnit4; 78 79 import com.android.compatibility.common.util.DeviceConfigStateChangerRule; 80 import com.android.compatibility.common.util.UserHelper; 81 import com.android.compatibility.common.util.mainline.MainlineModule; 82 import com.android.compatibility.common.util.mainline.ModuleDetector; 83 import com.android.modules.utils.build.SdkLevel; 84 85 import org.junit.After; 86 import org.junit.AfterClass; 87 import org.junit.Before; 88 import org.junit.BeforeClass; 89 import org.junit.Rule; 90 import org.junit.Test; 91 import org.junit.runner.RunWith; 92 93 import java.util.List; 94 import java.util.concurrent.CountDownLatch; 95 96 /** 97 * Tests the {@code LocationAccessCheck} in permission controller. 98 */ 99 @RunWith(AndroidJUnit4.class) 100 @AppModeFull(reason = "Cannot set system settings as instant app. Also we never show a location " 101 + "access check notification for instant apps.") 102 @ScreenRecordRule.ScreenRecord 103 @FlakyTest 104 public class LocationAccessCheckTest { 105 106 private static final String LOG_TAG = LocationAccessCheckTest.class.getSimpleName(); 107 108 private static final String TEST_APP_PKG = "android.permission.cts.appthataccesseslocation"; 109 private static final String TEST_APP_LABEL = "CtsLocationAccess"; 110 private static final String TEST_APP_SERVICE = TEST_APP_PKG + ".AccessLocationOnCommand"; 111 private static final String TEST_APP_LOCATION_BG_ACCESS_APK = 112 "/data/local/tmp/cts-permission/CtsAppThatAccessesLocationOnCommand.apk"; 113 private static final String TEST_APP_LOCATION_FG_ACCESS_APK = 114 "/data/local/tmp/cts-permission/AppThatDoesNotHaveBgLocationAccess.apk"; 115 private static final String ACTION_SET_UP_LOCATION_ACCESS_CHECK = 116 "com.android.permissioncontroller.action.SET_UP_LOCATION_ACCESS_CHECK"; 117 private static final int LOCATION_ACCESS_CHECK_JOB_ID = 0; 118 private static final int LOCATION_ACCESS_CHECK_NOTIFICATION_ID = 0; 119 120 private static final String PROPERTY_LOCATION_ACCESS_CHECK_DELAY_MILLIS = 121 "location_access_check_delay_millis"; 122 private static final String PROPERTY_LOCATION_ACCESS_PERIODIC_INTERVAL_MILLIS = 123 "location_access_check_periodic_interval_millis"; 124 private static final String PROPERTY_BG_LOCATION_CHECK_ENABLED = "bg_location_check_is_enabled"; 125 126 private static final long UNEXPECTED_TIMEOUT_MILLIS = 10000; 127 private static final long EXPECTED_TIMEOUT_MILLIS = 15000; 128 private static final long LOCATION_ACCESS_TIMEOUT_MILLIS = 15000; 129 130 private static final Context sContext = InstrumentationRegistry.getTargetContext(); 131 private static final ActivityManager sActivityManager = 132 sContext.getSystemService(ActivityManager.class); 133 private static final PackageManager sPackageManager = sContext.getPackageManager(); 134 private static final AppOpsManager sAppOpsManager = 135 sContext.getSystemService(AppOpsManager.class); 136 private static final LocationManager sLocationManager = 137 sContext.getSystemService(LocationManager.class); 138 private static final UiAutomation sUiAutomation = InstrumentationRegistry.getInstrumentation() 139 .getUiAutomation(); 140 141 private static final String PERMISSION_CONTROLLER_PKG = sContext.getPackageManager() 142 .getPermissionControllerPackageName(); 143 private static final String LocationAccessCheckOnBootReceiver = 144 "com.android.permissioncontroller.permission.service" 145 + ".LocationAccessCheck$SetupPeriodicBackgroundLocationAccessCheck"; 146 147 148 /** 149 * The result of {@link #assumeCanGetFineLocation()}, so we don't have to run it over and over 150 * again. 151 */ 152 private static Boolean sCanAccessFineLocation = null; 153 154 private static ServiceConnection sConnection; 155 private static IAccessLocationOnCommand sLocationAccessor; 156 assumeNotPlayManaged()157 private static void assumeNotPlayManaged() throws Exception { 158 assumeFalse(ModuleDetector.moduleIsPlayManaged( 159 sContext.getPackageManager(), MainlineModule.PERMISSION_CONTROLLER)); 160 } 161 162 @Rule 163 public final ScreenRecordRule mScreenRecordRule = new ScreenRecordRule(false, false); 164 165 // Override SafetyCenter enabled flag 166 @Rule 167 public DeviceConfigStateChangerRule sPrivacyDeviceConfigSafetyCenterEnabled = 168 new DeviceConfigStateChangerRule(sContext, 169 DeviceConfig.NAMESPACE_PRIVACY, 170 SafetyCenterUtils.PROPERTY_SAFETY_CENTER_ENABLED, 171 Boolean.toString(true)); 172 173 // Override BG location enabled flag 174 @Rule 175 public DeviceConfigStateChangerRule sPrivacyDeviceConfigBgLocationCheckEnabled = 176 new DeviceConfigStateChangerRule(sContext, 177 DeviceConfig.NAMESPACE_PRIVACY, 178 PROPERTY_BG_LOCATION_CHECK_ENABLED, 179 Boolean.toString(true)); 180 181 // Override general notification interval 182 @Rule 183 public DeviceConfigStateChangerRule sPrivacyDeviceConfigBgCheckIntervalMillis = 184 new DeviceConfigStateChangerRule(sContext, 185 DeviceConfig.NAMESPACE_PRIVACY, 186 PROPERTY_LOCATION_ACCESS_PERIODIC_INTERVAL_MILLIS, 187 "100"); 188 189 // Override general delay interval 190 @Rule 191 public DeviceConfigStateChangerRule sPrivacyDeviceConfigBgCheckDelayMillis = 192 new DeviceConfigStateChangerRule(sContext, 193 DeviceConfig.NAMESPACE_PRIVACY, 194 PROPERTY_LOCATION_ACCESS_CHECK_DELAY_MILLIS, 195 "50"); 196 197 @Rule 198 public CtsNotificationListenerHelperRule ctsNotificationListenerHelper = 199 new CtsNotificationListenerHelperRule(sContext); 200 201 private static boolean sWasLocationEnabled = true; 202 203 private UserHelper mUserHelper = new UserHelper(sContext); 204 205 @BeforeClass beforeClassSetup()206 public static void beforeClassSetup() throws Exception { 207 reduceDelays(); 208 installBackgroundAccessApp(); 209 runWithShellPermissionIdentity(() -> { 210 sWasLocationEnabled = sLocationManager.isLocationEnabled(); 211 if (!sWasLocationEnabled) { 212 sLocationManager.setLocationEnabledForUser(true, Process.myUserHandle()); 213 } 214 }); 215 } 216 217 /** 218 * Change settings so that permission controller can show location access notifications more 219 * often. 220 */ reduceDelays()221 public static void reduceDelays() { 222 runWithShellPermissionIdentity(() -> { 223 ContentResolver cr = sContext.getContentResolver(); 224 // New settings will be applied in when permission controller is reset 225 Settings.Secure.putLong(cr, LOCATION_ACCESS_CHECK_INTERVAL_MILLIS, 100); 226 Settings.Secure.putLong(cr, LOCATION_ACCESS_CHECK_DELAY_MILLIS, 50); 227 }); 228 } 229 230 @AfterClass cleanupAfterClass()231 public static void cleanupAfterClass() throws Throwable { 232 resetDelays(); 233 uninstallTestApp(); 234 runWithShellPermissionIdentity(() -> { 235 if (!sWasLocationEnabled) { 236 sLocationManager.setLocationEnabledForUser(false, Process.myUserHandle()); 237 } 238 }); 239 } 240 241 /** 242 * Reset settings so that permission controller runs normally. 243 */ resetDelays()244 public static void resetDelays() throws Throwable { 245 runWithShellPermissionIdentity(() -> { 246 ContentResolver cr = sContext.getContentResolver(); 247 Settings.Secure.resetToDefaults(cr, LOCATION_ACCESS_CHECK_INTERVAL_MILLIS); 248 Settings.Secure.resetToDefaults(cr, LOCATION_ACCESS_CHECK_DELAY_MILLIS); 249 }); 250 } 251 252 /** 253 * Connected to {@value #TEST_APP_PKG} and make it access the location in the background 254 */ accessLocation()255 private void accessLocation() throws Throwable { 256 if (sConnection == null || sLocationAccessor == null) { 257 bindService(); 258 } 259 260 long beforeAccess = System.currentTimeMillis(); 261 // Wait a little to avoid raciness in timing between threads 262 Thread.sleep(1000); 263 264 // Try again until binder call goes though. It might not go through if the sLocationAccessor 265 // is not bound yet 266 eventually(() -> { 267 assertNotNull(sLocationAccessor); 268 sLocationAccessor.accessLocation(); 269 }, EXPECTED_TIMEOUT_MILLIS); 270 271 // Wait until the access is recorded 272 eventually(() -> { 273 List<AppOpsManager.PackageOps> ops = runWithShellPermissionIdentity( 274 () -> sAppOpsManager.getOpsForPackage( 275 sPackageManager.getPackageUid(TEST_APP_PKG, 0), TEST_APP_PKG, 276 OPSTR_FINE_LOCATION)); 277 278 // Background access must have happened after "beforeAccess" 279 assertTrue(ops.get(0).getOps().get(0).getLastAccessBackgroundTime(OP_FLAGS_ALL_TRUSTED) 280 >= beforeAccess); 281 }, EXPECTED_TIMEOUT_MILLIS); 282 } 283 284 /** 285 * A {@link java.util.concurrent.Callable} that can throw a {@link Throwable} 286 */ 287 private interface ThrowingCallable<T> { call()288 T call() throws Throwable; 289 } 290 291 /** 292 * A {@link Runnable} that can throw a {@link Throwable} 293 */ 294 private interface ThrowingRunnable { run()295 void run() throws Throwable; 296 } 297 298 /** 299 * Make sure that a {@link ThrowingRunnable} eventually finishes without throwing a {@link 300 * Exception}. 301 * 302 * @param r The {@link ThrowingRunnable} to run. 303 * @param timeout the maximum time to wait 304 */ eventually(@onNull ThrowingRunnable r, long timeout)305 public static void eventually(@NonNull ThrowingRunnable r, long timeout) throws Throwable { 306 eventually(() -> { 307 r.run(); 308 return 0; 309 }, timeout); 310 } 311 312 /** 313 * Make sure that a {@link ThrowingCallable} eventually finishes without throwing a {@link 314 * Exception}. 315 * 316 * @param r The {@link ThrowingCallable} to run. 317 * @param timeout the maximum time to wait 318 * @return the return value from the callable 319 * @throws NullPointerException If the return value never becomes non-null 320 */ eventually(@onNull ThrowingCallable<T> r, long timeout)321 public static <T> T eventually(@NonNull ThrowingCallable<T> r, long timeout) throws Throwable { 322 long start = System.currentTimeMillis(); 323 324 while (true) { 325 try { 326 T res = r.call(); 327 if (res == null) { 328 throw new NullPointerException("No result"); 329 } 330 331 return res; 332 } catch (Throwable e) { 333 if (System.currentTimeMillis() - start < timeout) { 334 Log.d(LOG_TAG, "Ignoring exception", e); 335 336 Thread.sleep(500); 337 } else { 338 throw e; 339 } 340 } 341 } 342 } 343 344 /** 345 * Clear all data of a package including permissions and files. 346 * 347 * @param pkg The name of the package to be cleared 348 */ clearPackageData(@onNull String pkg)349 private static void clearPackageData(@NonNull String pkg) { 350 unbindService(); 351 runShellCommand("pm clear --user -2 " + pkg); 352 } 353 isJobReady()354 private static boolean isJobReady() { 355 String jobStatus = runShellCommand("cmd jobscheduler get-job-state -u " 356 + Process.myUserHandle().getIdentifier() + " " + PERMISSION_CONTROLLER_PKG 357 + " " + LOCATION_ACCESS_CHECK_JOB_ID); 358 return jobStatus.contains("waiting"); 359 } 360 361 /** 362 * Force a run of the location check. 363 */ runLocationCheck()364 private static void runLocationCheck() throws Throwable { 365 if (!isJobReady()) { 366 PermissionUtils.scheduleJob(sUiAutomation, PERMISSION_CONTROLLER_PKG, 367 LOCATION_ACCESS_CHECK_JOB_ID, EXPECTED_TIMEOUT_MILLIS, 368 ACTION_SET_UP_LOCATION_ACCESS_CHECK, LocationAccessCheckOnBootReceiver); 369 } 370 371 TestUtils.awaitJobUntilRequestedState( 372 PERMISSION_CONTROLLER_PKG, 373 LOCATION_ACCESS_CHECK_JOB_ID, 374 EXPECTED_TIMEOUT_MILLIS, 375 sUiAutomation, 376 "waiting" 377 ); 378 379 TestUtils.runJobAndWaitUntilCompleted( 380 PERMISSION_CONTROLLER_PKG, 381 LOCATION_ACCESS_CHECK_JOB_ID, 382 EXPECTED_TIMEOUT_MILLIS, 383 sUiAutomation 384 ); 385 } 386 387 /** 388 * Get a location access notification that is currently visible. 389 * 390 * @param cancelNotification if {@code true} the notification is canceled inside this method 391 * @return The notification or {@code null} if there is none 392 */ getNotification(boolean cancelNotification)393 private StatusBarNotification getNotification(boolean cancelNotification) throws Throwable { 394 return CtsNotificationListenerServiceUtils.getNotificationForPackageAndId( 395 PERMISSION_CONTROLLER_PKG, LOCATION_ACCESS_CHECK_NOTIFICATION_ID, 396 cancelNotification); 397 } 398 399 /** 400 * Grant a permission to the {@value #TEST_APP_PKG}. 401 * 402 * @param permission The permission to grant 403 */ grantPermissionToTestApp(@onNull String permission)404 private void grantPermissionToTestApp(@NonNull String permission) { 405 sUiAutomation.grantRuntimePermission(TEST_APP_PKG, permission); 406 } 407 installBackgroundAccessApp()408 public static void installBackgroundAccessApp() throws Exception { 409 String output = 410 runShellCommandOrThrow("pm install -r -g " + TEST_APP_LOCATION_BG_ACCESS_APK); 411 assertTrue(output.contains("Success")); 412 // Wait for user sensitive to be updated, which is checked by LocationAccessCheck. 413 Thread.sleep(5000); 414 } 415 uninstallTestApp()416 public static void uninstallTestApp() { 417 unbindService(); 418 runShellCommand("pm uninstall " + TEST_APP_PKG); 419 } 420 unbindService()421 private static void unbindService() { 422 if (sConnection != null) { 423 sContext.unbindService(sConnection); 424 } 425 sConnection = null; 426 sLocationAccessor = null; 427 } 428 installForegroundAccessApp()429 private static void installForegroundAccessApp() throws Exception { 430 unbindService(); 431 runShellCommandOrThrow("pm install -r -g " + TEST_APP_LOCATION_FG_ACCESS_APK); 432 // Wait for user sensitive to be updated, which is checked by LocationAccessCheck. 433 Thread.sleep(5000); 434 } 435 436 /** 437 * Skip each test for low ram device 438 */ assumeIsNotLowRamDevice()439 public void assumeIsNotLowRamDevice() { 440 assumeFalse(sActivityManager.isLowRamDevice()); 441 } 442 wakeUpAndDismissKeyguard()443 public void wakeUpAndDismissKeyguard() { 444 runShellCommand("input keyevent KEYCODE_WAKEUP"); 445 runShellCommand("wm dismiss-keyguard"); 446 } 447 bindService()448 public void bindService() { 449 sConnection = new ServiceConnection() { 450 @Override 451 public void onServiceConnected(ComponentName name, IBinder service) { 452 sLocationAccessor = IAccessLocationOnCommand.Stub.asInterface(service); 453 } 454 455 @Override 456 public void onServiceDisconnected(ComponentName name) { 457 sConnection = null; 458 sLocationAccessor = null; 459 } 460 }; 461 462 Intent testAppService = new Intent(); 463 testAppService.setComponent(new ComponentName(TEST_APP_PKG, TEST_APP_SERVICE)); 464 465 sContext.bindService(testAppService, sConnection, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND); 466 } 467 468 @Before beforeEachTestSetup()469 public void beforeEachTestSetup() throws Throwable { 470 assumeIsNotLowRamDevice(); 471 472 // TODO(b/380297485): Remove this assumption once NotificationListeners are supported on 473 // visible background users. 474 // Skipping each test for visible background users as all test cases depend on 475 // NotificationListeners. 476 assumeFalse("NotificationListeners are not yet supported on visible background users", 477 mUserHelper.isVisibleBackgroundUser()); 478 479 wakeUpAndDismissKeyguard(); 480 bindService(); 481 resetPermissionControllerBeforeEachTest(); 482 assumeCanGetFineLocation(); 483 } 484 485 /** 486 * Reset the permission controllers state before each test 487 */ resetPermissionControllerBeforeEachTest()488 public void resetPermissionControllerBeforeEachTest() throws Throwable { 489 // Has to be before resetPermissionController to make sure enablement time is the reset time 490 // of permission controller 491 runLocationCheck(); 492 493 resetPermissionController(); 494 495 eventually(() -> assertNull(getNotification(false)), UNEXPECTED_TIMEOUT_MILLIS); 496 497 // Reset job scheduler stats (to allow more jobs to be run) 498 runShellCommand( 499 "cmd jobscheduler reset-execution-quota -u " + myUserHandle().getIdentifier() + " " 500 + PERMISSION_CONTROLLER_PKG); 501 runShellCommand("cmd jobscheduler reset-schedule-quota"); 502 } 503 504 /** 505 * Make sure fine location can be accessed at all. 506 */ assumeCanGetFineLocation()507 public void assumeCanGetFineLocation() { 508 if (sCanAccessFineLocation == null) { 509 Criteria crit = new Criteria(); 510 crit.setAccuracy(ACCURACY_FINE); 511 512 CountDownLatch locationCounter = new CountDownLatch(1); 513 sContext.getSystemService(LocationManager.class).requestSingleUpdate(crit, 514 new LocationListener() { 515 @Override 516 public void onLocationChanged(Location location) { 517 locationCounter.countDown(); 518 } 519 520 @Override 521 public void onStatusChanged(String provider, int status, Bundle extras) { 522 } 523 524 @Override 525 public void onProviderEnabled(String provider) { 526 } 527 528 @Override 529 public void onProviderDisabled(String provider) { 530 } 531 }, Looper.getMainLooper()); 532 533 534 try { 535 sCanAccessFineLocation = locationCounter.await(LOCATION_ACCESS_TIMEOUT_MILLIS, 536 MILLISECONDS); 537 } catch (InterruptedException ignored) { 538 } 539 } 540 541 assumeTrue(sCanAccessFineLocation); 542 } 543 544 /** 545 * Reset the permission controllers state. 546 */ resetPermissionController()547 private static void resetPermissionController() throws Throwable { 548 unbindService(); 549 PermissionUtils.resetPermissionControllerJob(sUiAutomation, PERMISSION_CONTROLLER_PKG, 550 LOCATION_ACCESS_CHECK_JOB_ID, 45000, 551 ACTION_SET_UP_LOCATION_ACCESS_CHECK, LocationAccessCheckOnBootReceiver); 552 } 553 554 @After cleanupAfterEachTest()555 public void cleanupAfterEachTest() throws Throwable { 556 resetPrivacyConfig(); 557 locationUnbind(); 558 } 559 560 /** 561 * Reset location access check 562 */ resetPrivacyConfig()563 public void resetPrivacyConfig() throws Throwable { 564 // Run a location access check to update enabled state inside permission controller 565 runLocationCheck(); 566 } 567 locationUnbind()568 public void locationUnbind() throws Throwable { 569 unbindService(); 570 } 571 572 @Test notificationIsShown()573 public void notificationIsShown() throws Throwable { 574 accessLocation(); 575 runLocationCheck(); 576 eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS); 577 } 578 579 @Test 580 @AsbSecurityTest(cveBugId = 141028068) notificationIsShownOnlyOnce()581 public void notificationIsShownOnlyOnce() throws Throwable { 582 assumeNotPlayManaged(); 583 584 accessLocation(); 585 runLocationCheck(); 586 587 eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS); 588 589 accessLocation(); 590 runLocationCheck(); 591 592 assertNull(getNotification(true)); 593 } 594 595 @SystemUserOnly(reason = "b/172259935") 596 @Test 597 @AsbSecurityTest(cveBugId = 141028068) notificationIsShownAgainAfterClear()598 public void notificationIsShownAgainAfterClear() throws Throwable { 599 assumeNotPlayManaged(); 600 accessLocation(); 601 runLocationCheck(); 602 603 eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS); 604 605 clearPackageData(TEST_APP_PKG); 606 607 // Wait until package is cleared and permission controller has cleared the state 608 Thread.sleep(10000); 609 waitForBroadcasts(); 610 611 // Clearing removed the permissions, hence grant them again 612 grantPermissionToTestApp(ACCESS_FINE_LOCATION); 613 grantPermissionToTestApp(ACCESS_BACKGROUND_LOCATION); 614 615 accessLocation(); 616 runLocationCheck(); 617 618 eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS); 619 } 620 621 @SystemUserOnly(reason = "b/172259935") 622 @Test notificationIsShownAgainAfterUninstallAndReinstall()623 public void notificationIsShownAgainAfterUninstallAndReinstall() throws Throwable { 624 accessLocation(); 625 runLocationCheck(); 626 627 eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS); 628 629 uninstallTestApp(); 630 631 // Wait until package permission controller has cleared the state 632 Thread.sleep(2000); 633 634 installBackgroundAccessApp(); 635 waitForBroadcasts(); 636 accessLocation(); 637 runLocationCheck(); 638 639 eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS); 640 } 641 642 @Test 643 @AsbSecurityTest(cveBugId = 141028068) removeNotificationOnUninstall()644 public void removeNotificationOnUninstall() throws Throwable { 645 assumeNotPlayManaged(); 646 647 accessLocation(); 648 runLocationCheck(); 649 650 eventually(() -> assertNotNull(getNotification(false)), EXPECTED_TIMEOUT_MILLIS); 651 652 uninstallTestApp(); 653 // wait for permission controller (broadcast receiver) to clean up things 654 Thread.sleep(5000); 655 waitForBroadcasts(); 656 657 try { 658 eventually(() -> assertNull(getNotification(false)), UNEXPECTED_TIMEOUT_MILLIS); 659 } finally { 660 installBackgroundAccessApp(); 661 } 662 } 663 664 @Test notificationIsNotShownAfterAppDoesNotRequestLocationAnymore()665 public void notificationIsNotShownAfterAppDoesNotRequestLocationAnymore() throws Throwable { 666 accessLocation(); 667 runLocationCheck(); 668 669 eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS); 670 671 // Update to app to a version that does not request permission anymore 672 installForegroundAccessApp(); 673 674 try { 675 resetPermissionController(); 676 677 runLocationCheck(); 678 679 // We don't expect a notification, but try to trigger one anyway 680 assertNull(getNotification(false)); 681 } finally { 682 installBackgroundAccessApp(); 683 } 684 } 685 686 @Test 687 @AsbSecurityTest(cveBugId = 141028068) noNotificationIfBlamerNotSystemOrLocationProvider()688 public void noNotificationIfBlamerNotSystemOrLocationProvider() throws Throwable { 689 assumeNotPlayManaged(); 690 691 // Blame the app for access from an untrusted for notification purposes package. 692 runWithShellPermissionIdentity(() -> { 693 AppOpsManager appOpsManager = sContext.getSystemService(AppOpsManager.class); 694 appOpsManager.noteProxyOpNoThrow(OPSTR_FINE_LOCATION, TEST_APP_PKG, 695 sContext.getPackageManager().getPackageUid(TEST_APP_PKG, 0)); 696 }); 697 runLocationCheck(); 698 699 assertNull(getNotification(false)); 700 } 701 702 @Test 703 // Mark as flaky until b/286874765 is fixed 704 @FlakyTest 705 @MtsIgnore 706 @AsbSecurityTest(cveBugId = 141028068) testOpeningLocationSettingsDoesNotTriggerAccess()707 public void testOpeningLocationSettingsDoesNotTriggerAccess() throws Throwable { 708 assumeNotPlayManaged(); 709 710 Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); 711 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 712 sContext.startActivity(intent); 713 714 runLocationCheck(); 715 assertNull(getNotification(false)); 716 } 717 718 @Test 719 @AsbSecurityTest(cveBugId = 141028068) noNotificationWhenLocationNeverAccessed()720 public void noNotificationWhenLocationNeverAccessed() throws Throwable { 721 assumeNotPlayManaged(); 722 723 // Reset to clear property location_access_check_enabled_time has been already happened 724 // when resetPermissionController() invoked from before test method 725 726 runLocationCheck(); 727 728 // Not expecting notification as location is not accessed and previously set 729 // LOCATION_ACCESS_CHECK_ENABLED_TIME if any is cleaned up 730 assertNull(getNotification(false)); 731 } 732 733 @Test 734 @AsbSecurityTest(cveBugId = 141028068) notificationWhenLocationAccessed()735 public void notificationWhenLocationAccessed() throws Throwable { 736 assumeNotPlayManaged(); 737 738 // Reset to clear property location_access_check_enabled_time has been already happened 739 // when resetPermissionController() invoked from before test method 740 741 accessLocation(); 742 runLocationCheck(); 743 744 // Expecting notification as accessing the location causes 745 // LOCATION_ACCESS_CHECK_ENABLED_TIME to be set 746 eventually(() -> assertNotNull(getNotification(true)), EXPECTED_TIMEOUT_MILLIS); 747 } 748 749 @Test 750 @AsbSecurityTest(cveBugId = 141028068) noNotificationWhenLocationAccessedPriorToEnableTime()751 public void noNotificationWhenLocationAccessedPriorToEnableTime() throws Throwable { 752 assumeNotPlayManaged(); 753 754 accessLocation(); 755 756 // Reset to clear the property location_access_check_enabled_time 757 resetPermissionController(); 758 759 runLocationCheck(); 760 761 // Not expecting the notification as the location 762 // access was prior to LOCATION_ACCESS_CHECK_ENABLED_TIME (No notification for prior events) 763 assertNull(getNotification(false)); 764 } 765 766 @Test 767 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") notificationOnClickOpensSafetyCenter()768 public void notificationOnClickOpensSafetyCenter() throws Throwable { 769 assumeTrue(SafetyCenterUtils.deviceSupportsSafetyCenter(sContext)); 770 accessLocation(); 771 runLocationCheck(); 772 773 StatusBarNotification currentNotification = eventually(() -> { 774 StatusBarNotification notification = getNotification(false); 775 assertNotNull(notification); 776 return notification; 777 }, EXPECTED_TIMEOUT_MILLIS); 778 779 // Verify content intent 780 PendingIntent contentIntent = currentNotification.getNotification().contentIntent; 781 if (SdkLevel.isAtLeastU()) { 782 contentIntent.send(null, 0, null, null, null, null, 783 ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode( 784 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle()); 785 } else { 786 contentIntent.send(); 787 } 788 789 SafetyCenterUtils.assertSafetyCenterStarted(); 790 } 791 } 792