Home | History | Annotate | Download | only in cts
      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