1 /* 2 * Copyright (C) 2008 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.location.cts; 18 19 20 import android.app.PendingIntent; 21 import android.content.BroadcastReceiver; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.content.pm.PackageManager; 27 import android.location.Criteria; 28 import android.location.GpsStatus; 29 import android.location.GpsStatus.Listener; 30 import android.location.Location; 31 import android.location.LocationListener; 32 import android.location.LocationManager; 33 import android.location.LocationProvider; 34 import android.os.Bundle; 35 import android.os.HandlerThread; 36 import android.os.Looper; 37 import android.os.SystemClock; 38 import android.provider.Settings; 39 import android.test.InstrumentationTestCase; 40 import android.test.UiThreadTest; 41 42 import java.util.List; 43 44 /** 45 * Requires the permissions 46 * android.permission.ACCESS_MOCK_LOCATION to mock provider 47 * android.permission.ACCESS_COARSE_LOCATION to access network provider 48 * android.permission.ACCESS_FINE_LOCATION to access GPS provider 49 * android.permission.ACCESS_LOCATION_EXTRA_COMMANDS to send extra commands to GPS provider 50 */ 51 public class LocationManagerTest extends InstrumentationTestCase { 52 private static final long TEST_TIME_OUT = 5000; 53 54 private static final String TEST_MOCK_PROVIDER_NAME = "test_provider"; 55 56 private static final String UNKNOWN_PROVIDER_NAME = "unknown_provider"; 57 58 private static final String FUSED_PROVIDER_NAME = "fused"; 59 60 private LocationManager mManager; 61 62 private Context mContext; 63 64 private PendingIntent mPendingIntent; 65 66 private TestIntentReceiver mIntentReceiver; 67 68 @Override 69 protected void setUp() throws Exception { 70 super.setUp(); 71 mContext = getInstrumentation().getTargetContext(); 72 73 mManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); 74 75 // test that mock locations are allowed so a more descriptive error message can be logged 76 if (Settings.Secure.getInt(mContext.getContentResolver(), 77 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 0) { 78 fail("Mock locations are currently disabled in Settings - this test requires " 79 + "mock locations"); 80 } 81 82 // remove test provider if left over from an aborted run 83 LocationProvider lp = mManager.getProvider(TEST_MOCK_PROVIDER_NAME); 84 if (lp != null) { 85 mManager.removeTestProvider(TEST_MOCK_PROVIDER_NAME); 86 } 87 88 addTestProvider(TEST_MOCK_PROVIDER_NAME); 89 } 90 91 /** 92 * Helper method to add a test provider with given name. 93 */ 94 private void addTestProvider(final String providerName) { 95 mManager.addTestProvider(providerName, true, //requiresNetwork, 96 false, // requiresSatellite, 97 true, // requiresCell, 98 false, // hasMonetaryCost, 99 false, // supportsAltitude, 100 false, // supportsSpeed, 101 false, // supportsBearing, 102 Criteria.POWER_MEDIUM, // powerRequirement 103 Criteria.ACCURACY_FINE); // accuracy 104 mManager.setTestProviderEnabled(providerName, true); 105 } 106 107 @Override 108 protected void tearDown() throws Exception { 109 LocationProvider provider = mManager.getProvider(TEST_MOCK_PROVIDER_NAME); 110 if (provider != null) { 111 mManager.removeTestProvider(TEST_MOCK_PROVIDER_NAME); 112 } 113 if (mPendingIntent != null) { 114 mManager.removeProximityAlert(mPendingIntent); 115 } 116 if (mIntentReceiver != null) { 117 mContext.unregisterReceiver(mIntentReceiver); 118 } 119 super.tearDown(); 120 } 121 122 public void testRemoveTestProvider() { 123 // this test assumes TEST_MOCK_PROVIDER_NAME was created in setUp. 124 LocationProvider provider = mManager.getProvider(TEST_MOCK_PROVIDER_NAME); 125 assertNotNull(provider); 126 127 try { 128 mManager.addTestProvider(TEST_MOCK_PROVIDER_NAME, true, //requiresNetwork, 129 false, // requiresSatellite, 130 true, // requiresCell, 131 false, // hasMonetaryCost, 132 false, // supportsAltitude, 133 false, // supportsSpeed, 134 false, // supportsBearing, 135 Criteria.POWER_MEDIUM, // powerRequirement 136 Criteria.ACCURACY_FINE); // accuracy 137 fail("Should throw IllegalArgumentException when provider already exists!"); 138 } catch (IllegalArgumentException e) { 139 // expected 140 } 141 142 mManager.removeTestProvider(TEST_MOCK_PROVIDER_NAME); 143 provider = mManager.getProvider(TEST_MOCK_PROVIDER_NAME); 144 assertNull(provider); 145 146 try { 147 mManager.removeTestProvider(UNKNOWN_PROVIDER_NAME); 148 fail("Should throw IllegalArgumentException when no provider exists!"); 149 } catch (IllegalArgumentException e) { 150 // expected 151 } 152 } 153 154 public void testGetProviders() { 155 List<String> providers = mManager.getAllProviders(); 156 assertTrue(providers.size() >= 2); 157 assertTrue(hasTestProvider(providers)); 158 159 assertEquals(hasGpsFeature(), hasGpsProvider(providers)); 160 161 int oldSizeAllProviders = providers.size(); 162 163 providers = mManager.getProviders(false); 164 assertEquals(oldSizeAllProviders, providers.size()); 165 assertTrue(hasTestProvider(providers)); 166 167 providers = mManager.getProviders(true); 168 assertTrue(providers.size() >= 1); 169 assertTrue(hasTestProvider(providers)); 170 int oldSizeTrueProviders = providers.size(); 171 172 mManager.setTestProviderEnabled(TEST_MOCK_PROVIDER_NAME, false); 173 providers = mManager.getProviders(true); 174 assertEquals(oldSizeTrueProviders - 1, providers.size()); 175 assertFalse(hasTestProvider(providers)); 176 177 providers = mManager.getProviders(false); 178 assertEquals(oldSizeAllProviders, providers.size()); 179 assertTrue(hasTestProvider(providers)); 180 181 mManager.removeTestProvider(TEST_MOCK_PROVIDER_NAME); 182 providers = mManager.getAllProviders(); 183 assertEquals(oldSizeAllProviders - 1, providers.size()); 184 assertFalse(hasTestProvider(providers)); 185 } 186 187 private boolean hasTestProvider(List<String> providers) { 188 return hasProvider(providers, TEST_MOCK_PROVIDER_NAME); 189 } 190 191 private boolean hasGpsProvider(List<String> providers) { 192 return hasProvider(providers, LocationManager.GPS_PROVIDER); 193 } 194 195 private boolean hasGpsFeature() { 196 return mContext.getPackageManager().hasSystemFeature( 197 PackageManager.FEATURE_LOCATION_GPS); 198 } 199 200 private boolean hasProvider(List<String> providers, String providerName) { 201 for (String provider : providers) { 202 if (provider != null && provider.equals(providerName)) { 203 return true; 204 } 205 } 206 return false; 207 } 208 209 public void testGetProvider() { 210 LocationProvider p = mManager.getProvider(TEST_MOCK_PROVIDER_NAME); 211 assertNotNull(p); 212 assertEquals(TEST_MOCK_PROVIDER_NAME, p.getName()); 213 214 p = mManager.getProvider(LocationManager.GPS_PROVIDER); 215 if (hasGpsFeature()) { 216 assertNotNull(p); 217 assertEquals(LocationManager.GPS_PROVIDER, p.getName()); 218 } else { 219 assertNull(p); 220 } 221 222 p = mManager.getProvider(UNKNOWN_PROVIDER_NAME); 223 assertNull(p); 224 225 try { 226 mManager.getProvider(null); 227 fail("Should throw IllegalArgumentException when provider is null!"); 228 } catch (IllegalArgumentException e) { 229 // expected 230 } 231 } 232 233 public void testGetProvidersWithCriteria() { 234 Criteria criteria = new Criteria(); 235 List<String> providers = mManager.getProviders(criteria, true); 236 assertTrue(providers.size() >= 1); 237 assertTrue(hasTestProvider(providers)); 238 239 criteria = new Criteria(); 240 criteria.setPowerRequirement(Criteria.POWER_HIGH); 241 String p = mManager.getBestProvider(criteria, true); 242 if (p != null) { // we may not have any enabled providers 243 assertTrue(mManager.isProviderEnabled(p)); 244 } 245 246 criteria.setPowerRequirement(Criteria.POWER_MEDIUM); 247 p = mManager.getBestProvider(criteria, false); 248 assertNotNull(p); 249 250 criteria.setPowerRequirement(Criteria.POWER_LOW); 251 p = mManager.getBestProvider(criteria, true); 252 if (p != null) { // we may not have any enabled providers 253 assertTrue(mManager.isProviderEnabled(p)); 254 } 255 256 criteria.setPowerRequirement(Criteria.NO_REQUIREMENT); 257 p = mManager.getBestProvider(criteria, false); 258 assertNotNull(p); 259 } 260 261 /** 262 * Tests that location mode is consistent with which providers are enabled. Sadly we can only 263 * passively test whatever mode happens to be selected--actually changing the mode would require 264 * the test to be system-signed, and CTS tests aren't. Also mode changes that enable NLP require 265 * user consent. Thus we will have a manual CTS verifier test that is similar to this test but 266 * tests every location mode. This test is just a "backup" for that since verifier tests are 267 * less reliable. 268 */ 269 public void testModeAndProviderApisConsistent() { 270 ContentResolver cr = mContext.getContentResolver(); 271 272 // Find out what the settings say about which providers are enabled 273 int mode = Settings.Secure.getInt( 274 cr, Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF); 275 boolean gps = Settings.Secure.isLocationProviderEnabled(cr, LocationManager.GPS_PROVIDER); 276 boolean nlp = Settings.Secure.isLocationProviderEnabled( 277 cr, LocationManager.NETWORK_PROVIDER); 278 279 // Find out location manager's opinion on the matter, making sure we dont' get spurious 280 // results from test versions of the two providers. 281 forceRemoveTestProvider(LocationManager.GPS_PROVIDER); 282 forceRemoveTestProvider(LocationManager.NETWORK_PROVIDER); 283 boolean lmGps = mManager.isProviderEnabled(LocationManager.GPS_PROVIDER); 284 boolean lmNlp = mManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); 285 286 // Location Manager will report provider as off if it doesn't know about it 287 boolean expectedGps = gps && deviceHasProvider(LocationManager.GPS_PROVIDER); 288 boolean expectedNlp = nlp && deviceHasProvider(LocationManager.NETWORK_PROVIDER); 289 290 // Assert LocationManager returned the values from Settings.Secure (assuming the device has 291 // the appropriate hardware). 292 assertEquals("Inconsistent GPS values", expectedGps, lmGps); 293 assertEquals("Inconsistent NLP values", expectedNlp, lmNlp); 294 295 switch (mode) { 296 case Settings.Secure.LOCATION_MODE_OFF: 297 expectedGps = false; 298 expectedNlp = false; 299 break; 300 case Settings.Secure.LOCATION_MODE_SENSORS_ONLY: 301 expectedGps = true; 302 expectedNlp = false; 303 break; 304 case Settings.Secure.LOCATION_MODE_BATTERY_SAVING: 305 expectedGps = false; 306 expectedNlp = true; 307 break; 308 case Settings.Secure.LOCATION_MODE_HIGH_ACCURACY: 309 expectedGps = true; 310 expectedNlp = true; 311 break; 312 } 313 314 // Assert that isLocationProviderEnabled() values are consistent with the location mode 315 assertEquals("Bad GPS for mode " + mode, expectedGps, gps); 316 assertEquals("Bad NLP for mode " + mode, expectedNlp, nlp); 317 } 318 319 /** 320 * Returns true if the {@link LocationManager} reports that the device includes this flavor 321 * of location provider. 322 */ 323 private boolean deviceHasProvider(String provider) { 324 List<String> providers = mManager.getAllProviders(); 325 for (String aProvider : providers) { 326 if (aProvider.equals(provider)) { 327 return true; 328 } 329 } 330 return false; 331 } 332 333 /** 334 * Ensures the test provider is removed. {@link LocationManager#removeTestProvider(String)} 335 * throws an {@link java.lang.IllegalArgumentException} if there is no such test provider, 336 * so we have to add it before we clear it. 337 */ 338 private void forceRemoveTestProvider(String provider) { 339 addTestProvider(provider); 340 mManager.removeTestProvider(provider); 341 } 342 343 public void testLocationUpdatesWithLocationListener() throws InterruptedException { 344 doLocationUpdatesWithLocationListener(TEST_MOCK_PROVIDER_NAME); 345 346 try { 347 mManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, 348 (LocationListener) null); 349 fail("Should throw IllegalArgumentException if param listener is null!"); 350 } catch (IllegalArgumentException e) { 351 // expected 352 } 353 354 try { 355 mManager.requestLocationUpdates(null, 0, 0, new MockLocationListener()); 356 fail("Should throw IllegalArgumentException if param provider is null!"); 357 } catch (IllegalArgumentException e) { 358 // expected 359 } 360 361 try { 362 mManager.removeUpdates( (LocationListener) null ); 363 fail("Should throw IllegalArgumentException if listener is null!"); 364 } catch (IllegalArgumentException e) { 365 // expected 366 } 367 368 try { 369 mManager.clearTestProviderLocation(UNKNOWN_PROVIDER_NAME); 370 fail("Should throw IllegalArgumentException if provider is unknown!"); 371 } catch (IllegalArgumentException e) { 372 // expected 373 } 374 } 375 376 /** 377 * Helper method to test a location update with given mock location provider 378 * 379 * @param providerName name of provider to test. Must already exist. 380 * @throws InterruptedException 381 */ 382 private void doLocationUpdatesWithLocationListener(final String providerName) 383 throws InterruptedException { 384 final double latitude1 = 10; 385 final double longitude1 = 40; 386 final double latitude2 = 35; 387 final double longitude2 = 80; 388 final MockLocationListener listener = new MockLocationListener(); 389 390 // update location and notify listener 391 new Thread(new Runnable() { 392 public void run() { 393 Looper.prepare(); 394 mManager.requestLocationUpdates(providerName, 0, 0, listener); 395 listener.setLocationRequested(); 396 Looper.loop(); 397 } 398 }).start(); 399 // wait for location requested to be called first, otherwise setLocation can be called 400 // before there is a listener attached 401 assertTrue(listener.hasCalledLocationRequested(TEST_TIME_OUT)); 402 updateLocation(providerName, latitude1, longitude1); 403 assertTrue(listener.hasCalledOnLocationChanged(TEST_TIME_OUT)); 404 Location location = listener.getLocation(); 405 assertEquals(providerName, location.getProvider()); 406 assertEquals(latitude1, location.getLatitude()); 407 assertEquals(longitude1, location.getLongitude()); 408 assertEquals(true, location.isFromMockProvider()); 409 410 // update location without notifying listener 411 listener.reset(); 412 assertFalse(listener.hasCalledOnLocationChanged(0)); 413 mManager.removeUpdates(listener); 414 updateLocation(providerName, latitude2, longitude2); 415 assertFalse(listener.hasCalledOnLocationChanged(TEST_TIME_OUT)); 416 } 417 418 /** 419 * Verifies that all real location providers can be replaced by a mock provider. 420 * <p/> 421 * This feature is quite useful for developer automated testing. 422 * This test may fail if another unknown test provider already exists, because there is no 423 * known way to determine if a given provider is a test provider. 424 * @throws InterruptedException 425 */ 426 public void testReplaceRealProvidersWithMocks() throws InterruptedException { 427 for (String providerName : mManager.getAllProviders()) { 428 if (!providerName.equals(TEST_MOCK_PROVIDER_NAME) && 429 !providerName.equals(LocationManager.PASSIVE_PROVIDER)) { 430 addTestProvider(providerName); 431 try { 432 // run the update location test logic to ensure location updates can be injected 433 doLocationUpdatesWithLocationListener(providerName); 434 } finally { 435 mManager.removeTestProvider(providerName); 436 } 437 } 438 } 439 } 440 441 public void testLocationUpdatesWithLocationListenerAndLooper() throws InterruptedException { 442 double latitude1 = 60; 443 double longitude1 = 20; 444 double latitude2 = 40; 445 double longitude2 = 30; 446 final MockLocationListener listener = new MockLocationListener(); 447 448 // update location and notify listener 449 HandlerThread handlerThread = new HandlerThread("testLocationUpdates"); 450 handlerThread.start(); 451 mManager.requestLocationUpdates(TEST_MOCK_PROVIDER_NAME, 0, 0, listener, 452 handlerThread.getLooper()); 453 454 updateLocation(latitude1, longitude1); 455 assertTrue(listener.hasCalledOnLocationChanged(TEST_TIME_OUT)); 456 Location location = listener.getLocation(); 457 assertEquals(TEST_MOCK_PROVIDER_NAME, location.getProvider()); 458 assertEquals(latitude1, location.getLatitude()); 459 assertEquals(longitude1, location.getLongitude()); 460 assertEquals(true, location.isFromMockProvider()); 461 462 // update location without notifying listener 463 mManager.removeUpdates(listener); 464 listener.reset(); 465 updateLocation(latitude2, longitude2); 466 assertFalse(listener.hasCalledOnLocationChanged(TEST_TIME_OUT)); 467 468 try { 469 mManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, 470 (LocationListener) null, Looper.myLooper()); 471 fail("Should throw IllegalArgumentException if param listener is null!"); 472 } catch (IllegalArgumentException e) { 473 // expected 474 } 475 476 try { 477 mManager.requestLocationUpdates(null, 0, 0, listener, Looper.myLooper()); 478 fail("Should throw IllegalArgumentException if param provider is null!"); 479 } catch (IllegalArgumentException e) { 480 // expected 481 } 482 483 try { 484 mManager.removeUpdates( (LocationListener) null ); 485 fail("Should throw IllegalArgumentException if listener is null!"); 486 } catch (IllegalArgumentException e) { 487 // expected 488 } 489 } 490 491 public void testLocationUpdatesWithPendingIntent() throws InterruptedException { 492 double latitude1 = 20; 493 double longitude1 = 40; 494 double latitude2 = 30; 495 double longitude2 = 50; 496 497 // update location and receive broadcast. 498 registerIntentReceiver(); 499 mManager.requestLocationUpdates(TEST_MOCK_PROVIDER_NAME, 0, 0, mPendingIntent); 500 updateLocation(latitude1, longitude1); 501 waitForReceiveBroadcast(); 502 503 assertNotNull(mIntentReceiver.getLastReceivedIntent()); 504 Location location = mManager.getLastKnownLocation(TEST_MOCK_PROVIDER_NAME); 505 assertEquals(TEST_MOCK_PROVIDER_NAME, location.getProvider()); 506 assertEquals(latitude1, location.getLatitude()); 507 assertEquals(longitude1, location.getLongitude()); 508 assertEquals(true, location.isFromMockProvider()); 509 510 // update location without receiving broadcast. 511 mManager.removeUpdates(mPendingIntent); 512 mIntentReceiver.clearReceivedIntents(); 513 updateLocation(latitude2, longitude2); 514 waitForReceiveBroadcast(); 515 assertNull(mIntentReceiver.getLastReceivedIntent()); 516 517 try { 518 mManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, 519 (PendingIntent) null); 520 fail("Should throw IllegalArgumentException if param intent is null!"); 521 } catch (IllegalArgumentException e) { 522 // expected 523 } 524 525 try { 526 mManager.requestLocationUpdates(null, 0, 0, mPendingIntent); 527 fail("Should throw IllegalArgumentException if param provider is null!"); 528 } catch (IllegalArgumentException e) { 529 // expected 530 } 531 532 try { 533 mManager.removeUpdates( (PendingIntent) null ); 534 fail("Should throw IllegalArgumentException if intent is null!"); 535 } catch (IllegalArgumentException e) { 536 // expected 537 } 538 } 539 540 public void testAddProximityAlert() { 541 Intent i = new Intent(); 542 i.setAction("android.location.cts.TEST_GET_GPS_STATUS_ACTION"); 543 PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, PendingIntent.FLAG_ONE_SHOT); 544 545 mManager.addProximityAlert(0, 0, 1.0f, 5000, pi); 546 mManager.removeProximityAlert(pi); 547 } 548 549 public void testIsProviderEnabled() { 550 // this test assumes enabled TEST_MOCK_PROVIDER_NAME was created in setUp. 551 assertNotNull(mManager.getProvider(TEST_MOCK_PROVIDER_NAME)); 552 assertTrue(mManager.isProviderEnabled(TEST_MOCK_PROVIDER_NAME)); 553 554 mManager.clearTestProviderEnabled(TEST_MOCK_PROVIDER_NAME); 555 assertFalse(mManager.isProviderEnabled(TEST_MOCK_PROVIDER_NAME)); 556 557 mManager.setTestProviderEnabled(TEST_MOCK_PROVIDER_NAME, true); 558 assertTrue(mManager.isProviderEnabled(TEST_MOCK_PROVIDER_NAME)); 559 560 try { 561 mManager.isProviderEnabled(null); 562 fail("Should throw IllegalArgumentException if provider is null!"); 563 } catch (IllegalArgumentException e) { 564 // expected 565 } 566 567 try { 568 mManager.clearTestProviderEnabled(UNKNOWN_PROVIDER_NAME); 569 fail("Should throw IllegalArgumentException if provider is unknown!"); 570 } catch (IllegalArgumentException e) { 571 // expected 572 } 573 574 try { 575 mManager.setTestProviderEnabled(UNKNOWN_PROVIDER_NAME, false); 576 fail("Should throw IllegalArgumentException if provider is unknown!"); 577 } catch (IllegalArgumentException e) { 578 // expected 579 } 580 } 581 582 public void testGetLastKnownLocation() throws InterruptedException { 583 double latitude1 = 20; 584 double longitude1 = 40; 585 double latitude2 = 10; 586 double longitude2 = 70; 587 588 registerIntentReceiver(); 589 mManager.requestLocationUpdates(TEST_MOCK_PROVIDER_NAME, 0, 0, mPendingIntent); 590 updateLocation(latitude1, longitude1); 591 waitForReceiveBroadcast(); 592 593 assertNotNull(mIntentReceiver.getLastReceivedIntent()); 594 Location location = mManager.getLastKnownLocation(TEST_MOCK_PROVIDER_NAME); 595 assertEquals(TEST_MOCK_PROVIDER_NAME, location.getProvider()); 596 assertEquals(latitude1, location.getLatitude()); 597 assertEquals(longitude1, location.getLongitude()); 598 assertEquals(true, location.isFromMockProvider()); 599 600 mIntentReceiver.clearReceivedIntents(); 601 updateLocation(latitude2, longitude2); 602 waitForReceiveBroadcast(); 603 604 assertNotNull(mIntentReceiver.getLastReceivedIntent()); 605 location = mManager.getLastKnownLocation(TEST_MOCK_PROVIDER_NAME); 606 assertEquals(TEST_MOCK_PROVIDER_NAME, location.getProvider()); 607 assertEquals(latitude2, location.getLatitude()); 608 assertEquals(longitude2, location.getLongitude()); 609 assertEquals(true, location.isFromMockProvider()); 610 611 try { 612 mManager.getLastKnownLocation(null); 613 fail("Should throw IllegalArgumentException if provider is null!"); 614 } catch (IllegalArgumentException e) { 615 // expected 616 } 617 } 618 619 @UiThreadTest 620 public void testGpsStatusListener() { 621 MockGpsStatusListener listener = new MockGpsStatusListener(); 622 mManager.addGpsStatusListener(listener); 623 mManager.removeGpsStatusListener(listener); 624 625 mManager.addGpsStatusListener(null); 626 mManager.removeGpsStatusListener(null); 627 } 628 629 public void testGetGpsStatus() { 630 GpsStatus status = mManager.getGpsStatus(null); 631 assertNotNull(status); 632 assertSame(status, mManager.getGpsStatus(status)); 633 } 634 635 public void testSendExtraCommand() { 636 // this test assumes TEST_MOCK_PROVIDER_NAME was created in setUp. 637 assertNotNull(mManager.getProvider(TEST_MOCK_PROVIDER_NAME)); 638 // Unknown command 639 assertFalse(mManager.sendExtraCommand(TEST_MOCK_PROVIDER_NAME, "unknown", new Bundle())); 640 641 assertNull(mManager.getProvider(UNKNOWN_PROVIDER_NAME)); 642 assertFalse(mManager.sendExtraCommand(UNKNOWN_PROVIDER_NAME, "unknown", new Bundle())); 643 } 644 645 public void testSetTestProviderStatus() throws InterruptedException { 646 final int status = LocationProvider.TEMPORARILY_UNAVAILABLE; 647 final long updateTime = 1000; 648 final MockLocationListener listener = new MockLocationListener(); 649 650 HandlerThread handlerThread = new HandlerThread("testStatusUpdates"); 651 handlerThread.start(); 652 653 // set status successfully 654 mManager.requestLocationUpdates(TEST_MOCK_PROVIDER_NAME, 0, 0, listener, 655 handlerThread.getLooper()); 656 mManager.setTestProviderStatus(TEST_MOCK_PROVIDER_NAME, status, null, updateTime); 657 // setting the status alone is not sufficient to trigger a status update 658 updateLocation(10, 30); 659 assertTrue(listener.hasCalledOnStatusChanged(TEST_TIME_OUT)); 660 assertEquals(TEST_MOCK_PROVIDER_NAME, listener.getProvider()); 661 assertEquals(status, listener.getStatus()); 662 663 try { 664 mManager.setTestProviderStatus(UNKNOWN_PROVIDER_NAME, 0, null, 665 System.currentTimeMillis()); 666 fail("Should throw IllegalArgumentException if provider is unknown!"); 667 } catch (IllegalArgumentException e) { 668 // expected 669 } 670 671 try { 672 mManager.clearTestProviderStatus(UNKNOWN_PROVIDER_NAME); 673 fail("Should throw IllegalArgumentException if provider is unknown!"); 674 } catch (IllegalArgumentException e) { 675 // expected 676 } 677 } 678 679 /** 680 * Tests basic proximity alert when entering proximity 681 */ 682 public void testEnterProximity() throws Exception { 683 // need to mock the fused location provider for proximity tests 684 mockFusedLocation(); 685 686 doTestEnterProximity(10000); 687 688 unmockFusedLocation(); 689 } 690 691 /** 692 * Tests proximity alert when entering proximity, with no expiration 693 */ 694 public void testEnterProximity_noexpire() throws Exception { 695 // need to mock the fused location provider for proximity tests 696 mockFusedLocation(); 697 698 doTestEnterProximity(-1); 699 700 unmockFusedLocation(); 701 } 702 703 /** 704 * Tests basic proximity alert when exiting proximity 705 */ 706 public void testExitProximity() throws Exception { 707 // need to mock the fused location provider for proximity tests 708 mockFusedLocation(); 709 710 // first do enter proximity scenario 711 doTestEnterProximity(-1); 712 713 // now update to trigger exit proximity proximity 714 mIntentReceiver.clearReceivedIntents(); 715 updateLocationAndWait(FUSED_PROVIDER_NAME, 20, 20); 716 waitForReceiveBroadcast(); 717 assertProximityType(false); 718 719 unmockFusedLocation(); 720 } 721 722 /** 723 * Tests basic proximity alert when initially within proximity 724 */ 725 public void testInitiallyWithinProximity() throws Exception { 726 // need to mock the fused location provider for proximity tests 727 mockFusedLocation(); 728 729 updateLocationAndWait(FUSED_PROVIDER_NAME, 0, 0); 730 registerProximityListener(0, 0, 1000, 10000); 731 waitForReceiveBroadcast(); 732 assertProximityType(true); 733 734 unmockFusedLocation(); 735 } 736 737 /** 738 * Helper variant for testing enter proximity scenario 739 * TODO: add additional parameters as more scenarios are added 740 * 741 * @param expiration - expiration of proximity alert 742 */ 743 private void doTestEnterProximity(long expiration) throws Exception { 744 // update location to outside proximity range 745 updateLocationAndWait(FUSED_PROVIDER_NAME, 30, 30); 746 registerProximityListener(0, 0, 1000, expiration); 747 748 // Adding geofences is asynchronous, the return of LocationManager.addProximityAlert 749 // doesn't mean that geofences are already being monitored. Wait for a few milliseconds 750 // so that GeofenceManager is actively monitoring locations before we send the mock 751 // location to avoid flaky tests. 752 Thread.sleep(500); 753 754 updateLocationAndWait(FUSED_PROVIDER_NAME, 0, 0); 755 waitForReceiveBroadcast(); 756 assertProximityType(true); 757 } 758 759 760 private void updateLocationAndWait(String providerName, double latitude, double longitude) 761 throws InterruptedException { 762 // Register a listener for the location we are about to set. 763 MockLocationListener listener = new MockLocationListener(); 764 HandlerThread handlerThread = new HandlerThread("updateLocationAndWait"); 765 handlerThread.start(); 766 mManager.requestLocationUpdates(providerName, 0, 0, listener, handlerThread.getLooper()); 767 768 // Set the location. 769 updateLocation(providerName, latitude, longitude); 770 771 // Make sure we received the location, and it is the right one. 772 assertTrue(listener.hasCalledOnLocationChanged(TEST_TIME_OUT)); 773 Location location = listener.getLocation(); 774 assertEquals(providerName, location.getProvider()); 775 assertEquals(latitude, location.getLatitude()); 776 assertEquals(longitude, location.getLongitude()); 777 assertEquals(true, location.isFromMockProvider()); 778 779 // Remove the listener. 780 mManager.removeUpdates(listener); 781 } 782 783 private void registerIntentReceiver() { 784 String intentKey = "LocationManagerTest"; 785 Intent proximityIntent = new Intent(intentKey); 786 mPendingIntent = PendingIntent.getBroadcast(mContext, 0, proximityIntent, 787 PendingIntent.FLAG_CANCEL_CURRENT); 788 mIntentReceiver = new TestIntentReceiver(intentKey); 789 mContext.registerReceiver(mIntentReceiver, mIntentReceiver.getFilter()); 790 } 791 792 /** 793 * Registers the proximity intent receiver 794 */ 795 private void registerProximityListener(double latitude, double longitude, float radius, 796 long expiration) { 797 registerIntentReceiver(); 798 mManager.addProximityAlert(latitude, longitude, radius, expiration, mPendingIntent); 799 } 800 801 /** 802 * Blocks until receive intent notification or time out. 803 * 804 * @throws InterruptedException 805 */ 806 private void waitForReceiveBroadcast() throws InterruptedException { 807 synchronized (mIntentReceiver) { 808 mIntentReceiver.wait(TEST_TIME_OUT); 809 } 810 } 811 812 /** 813 * Asserts that the received intent had the enter proximity property set as 814 * expected 815 * 816 * @param expectedEnterProximity - true if enter proximity expected, false 817 * if exit expected 818 */ 819 private void assertProximityType(boolean expectedEnterProximity) throws Exception { 820 Intent intent = mIntentReceiver.getLastReceivedIntent(); 821 assertNotNull("Did not receive any intent", intent); 822 boolean proximityTest = intent.getBooleanExtra( 823 LocationManager.KEY_PROXIMITY_ENTERING, !expectedEnterProximity); 824 assertEquals("proximity alert not set to expected enter proximity value", 825 expectedEnterProximity, proximityTest); 826 } 827 828 private void updateLocation(final String providerName, final double latitude, 829 final double longitude) { 830 Location location = new Location(providerName); 831 location.setLatitude(latitude); 832 location.setLongitude(longitude); 833 location.setAccuracy(1.0f); 834 location.setTime(java.lang.System.currentTimeMillis()); 835 location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); 836 mManager.setTestProviderLocation(providerName, location); 837 } 838 839 private void updateLocation(final double latitude, final double longitude) { 840 updateLocation(TEST_MOCK_PROVIDER_NAME, latitude, longitude); 841 } 842 843 private void mockFusedLocation() { 844 addTestProvider(FUSED_PROVIDER_NAME); 845 } 846 847 private void unmockFusedLocation() { 848 mManager.removeTestProvider(FUSED_PROVIDER_NAME); 849 } 850 851 /** 852 * Helper class that receives a proximity intent and notifies the main class 853 * when received 854 */ 855 private static class TestIntentReceiver extends BroadcastReceiver { 856 private String mExpectedAction; 857 858 private Intent mLastReceivedIntent; 859 860 public TestIntentReceiver(String expectedAction) { 861 mExpectedAction = expectedAction; 862 mLastReceivedIntent = null; 863 } 864 865 public IntentFilter getFilter() { 866 IntentFilter filter = new IntentFilter(mExpectedAction); 867 return filter; 868 } 869 870 @Override 871 public void onReceive(Context context, Intent intent) { 872 if (intent != null && mExpectedAction.equals(intent.getAction())) { 873 synchronized (this) { 874 mLastReceivedIntent = intent; 875 notify(); 876 } 877 } 878 } 879 880 public Intent getLastReceivedIntent() { 881 return mLastReceivedIntent; 882 } 883 884 public void clearReceivedIntents() { 885 mLastReceivedIntent = null; 886 } 887 } 888 889 private static class MockLocationListener implements LocationListener { 890 private String mProvider; 891 private int mStatus; 892 private Location mLocation; 893 private Object mStatusLock = new Object(); 894 private Object mLocationLock = new Object(); 895 private Object mLocationRequestLock = new Object(); 896 897 private boolean mHasCalledOnLocationChanged; 898 899 private boolean mHasCalledOnProviderDisabled; 900 901 private boolean mHasCalledOnProviderEnabled; 902 903 private boolean mHasCalledOnStatusChanged; 904 905 private boolean mHasCalledRequestLocation; 906 907 public void reset(){ 908 mHasCalledOnLocationChanged = false; 909 mHasCalledOnProviderDisabled = false; 910 mHasCalledOnProviderEnabled = false; 911 mHasCalledOnStatusChanged = false; 912 mHasCalledRequestLocation = false; 913 mProvider = null; 914 mStatus = 0; 915 } 916 917 /** 918 * Call to inform listener that location has been updates have been requested 919 */ 920 public void setLocationRequested() { 921 synchronized (mLocationRequestLock) { 922 mHasCalledRequestLocation = true; 923 mLocationRequestLock.notify(); 924 } 925 } 926 927 public boolean hasCalledLocationRequested(long timeout) throws InterruptedException { 928 synchronized (mLocationRequestLock) { 929 if (timeout > 0 && !mHasCalledRequestLocation) { 930 mLocationRequestLock.wait(timeout); 931 } 932 } 933 return mHasCalledRequestLocation; 934 } 935 936 /** 937 * Check whether onLocationChanged() has been called. Wait up to timeout milliseconds 938 * for the callback. 939 * @param timeout Maximum time to wait for the callback, 0 to return immediately. 940 */ 941 public boolean hasCalledOnLocationChanged(long timeout) throws InterruptedException { 942 synchronized (mLocationLock) { 943 if (timeout > 0 && !mHasCalledOnLocationChanged) { 944 mLocationLock.wait(timeout); 945 } 946 } 947 return mHasCalledOnLocationChanged; 948 } 949 950 public boolean hasCalledOnProviderDisabled() { 951 return mHasCalledOnProviderDisabled; 952 } 953 954 public boolean hasCalledOnProviderEnabled() { 955 return mHasCalledOnProviderEnabled; 956 } 957 958 public boolean hasCalledOnStatusChanged(long timeout) throws InterruptedException { 959 synchronized(mStatusLock) { 960 // wait(0) would wait forever 961 if (timeout > 0 && !mHasCalledOnStatusChanged) { 962 mStatusLock.wait(timeout); 963 } 964 } 965 return mHasCalledOnStatusChanged; 966 } 967 968 public void onLocationChanged(Location location) { 969 mLocation = location; 970 synchronized (mLocationLock) { 971 mHasCalledOnLocationChanged = true; 972 mLocationLock.notify(); 973 } 974 } 975 976 public void onProviderDisabled(String provider) { 977 mHasCalledOnProviderDisabled = true; 978 } 979 980 public void onProviderEnabled(String provider) { 981 mHasCalledOnProviderEnabled = true; 982 } 983 984 public void onStatusChanged(String provider, int status, Bundle extras) { 985 mProvider = provider; 986 mStatus = status; 987 synchronized (mStatusLock) { 988 mHasCalledOnStatusChanged = true; 989 mStatusLock.notify(); 990 } 991 } 992 993 public String getProvider() { 994 return mProvider; 995 } 996 997 public int getStatus() { 998 return mStatus; 999 } 1000 1001 public Location getLocation() { 1002 return mLocation; 1003 } 1004 } 1005 1006 private static class MockGpsStatusListener implements Listener { 1007 private boolean mHasCallOnGpsStatusChanged; 1008 1009 public boolean hasCallOnGpsStatusChanged() { 1010 return mHasCallOnGpsStatusChanged; 1011 } 1012 1013 public void reset(){ 1014 mHasCallOnGpsStatusChanged = false; 1015 } 1016 1017 public void onGpsStatusChanged(int event) { 1018 mHasCallOnGpsStatusChanged = true; 1019 } 1020 } 1021 } 1022