Home | History | Annotate | Download | only in location
      1 /*
      2  * Copyright (C) 2007 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;
     18 
     19 import android.app.PendingIntent;
     20 import android.content.Intent;
     21 import android.os.Bundle;
     22 import android.os.Looper;
     23 import android.os.RemoteException;
     24 import android.os.Handler;
     25 import android.os.Message;
     26 import android.util.Log;
     27 
     28 import com.android.internal.location.DummyLocationProvider;
     29 
     30 import java.util.ArrayList;
     31 import java.util.Collections;
     32 import java.util.Comparator;
     33 import java.util.HashMap;
     34 import java.util.List;
     35 
     36 /**
     37  * This class provides access to the system location services.  These
     38  * services allow applications to obtain periodic updates of the
     39  * device's geographical location, or to fire an application-specified
     40  * {@link Intent} when the device enters the proximity of a given
     41  * geographical location.
     42  *
     43  * <p>You do not
     44  * instantiate this class directly; instead, retrieve it through
     45  * {@link android.content.Context#getSystemService
     46  * Context.getSystemService(Context.LOCATION_SERVICE)}.
     47  */
     48 public class LocationManager {
     49     private static final String TAG = "LocationManager";
     50     private ILocationManager mService;
     51     private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners =
     52             new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>();
     53     private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners =
     54             new HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport>();
     55     private final GpsStatus mGpsStatus = new GpsStatus();
     56 
     57     /**
     58      * Name of the network location provider.  This provider determines location based on
     59      * availability of cell tower and WiFi access points. Results are retrieved
     60      * by means of a network lookup.
     61      *
     62      * Requires either of the permissions android.permission.ACCESS_COARSE_LOCATION
     63      * or android.permission.ACCESS_FINE_LOCATION.
     64      */
     65     public static final String NETWORK_PROVIDER = "network";
     66 
     67     /**
     68      * Name of the GPS location provider. This provider determines location using
     69      * satellites. Depending on conditions, this provider may take a while to return
     70      * a location fix.
     71      *
     72      * Requires the permission android.permission.ACCESS_FINE_LOCATION.
     73      *
     74      * <p> The extras Bundle for the GPS location provider can contain the
     75      * following key/value pairs:
     76      *
     77      * <ul>
     78      * <li> satellites - the number of satellites used to derive the fix
     79      * </ul>
     80      */
     81     public static final String GPS_PROVIDER = "gps";
     82 
     83     /**
     84      * A special location provider for receiving locations without actually initiating
     85      * a location fix. This provider can be used to passively receive location updates
     86      * when other applications or services request them without actually requesting
     87      * the locations yourself.  This provider will return locations generated by other
     88      * providers.  You can query the {@link Location#getProvider()} method to determine
     89      * the origin of the location update.
     90      *
     91      * Requires the permission android.permission.ACCESS_FINE_LOCATION, although if the GPS
     92      * is not enabled this provider might only return coarse fixes.
     93      */
     94     public static final String PASSIVE_PROVIDER = "passive";
     95 
     96     /**
     97      * Key used for the Bundle extra holding a boolean indicating whether
     98      * a proximity alert is entering (true) or exiting (false)..
     99      */
    100     public static final String KEY_PROXIMITY_ENTERING = "entering";
    101 
    102     /**
    103      * Key used for a Bundle extra holding an Integer status value
    104      * when a status change is broadcast using a PendingIntent.
    105      */
    106     public static final String KEY_STATUS_CHANGED = "status";
    107 
    108     /**
    109      * Key used for a Bundle extra holding an Boolean status value
    110      * when a provider enabled/disabled event is broadcast using a PendingIntent.
    111      */
    112     public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
    113 
    114     /**
    115      * Key used for a Bundle extra holding a Location value
    116      * when a location change is broadcast using a PendingIntent.
    117      */
    118     public static final String KEY_LOCATION_CHANGED = "location";
    119 
    120     // Map from LocationListeners to their associated ListenerTransport objects
    121     private HashMap<LocationListener,ListenerTransport> mListeners =
    122         new HashMap<LocationListener,ListenerTransport>();
    123 
    124     private class ListenerTransport extends ILocationListener.Stub {
    125         private static final int TYPE_LOCATION_CHANGED = 1;
    126         private static final int TYPE_STATUS_CHANGED = 2;
    127         private static final int TYPE_PROVIDER_ENABLED = 3;
    128         private static final int TYPE_PROVIDER_DISABLED = 4;
    129 
    130         private LocationListener mListener;
    131         private final Handler mListenerHandler;
    132 
    133         ListenerTransport(LocationListener listener, Looper looper) {
    134             mListener = listener;
    135 
    136             if (looper == null) {
    137                 mListenerHandler = new Handler() {
    138                     @Override
    139                     public void handleMessage(Message msg) {
    140                         _handleMessage(msg);
    141                     }
    142                 };
    143             } else {
    144                 mListenerHandler = new Handler(looper) {
    145                     @Override
    146                     public void handleMessage(Message msg) {
    147                         _handleMessage(msg);
    148                     }
    149                 };
    150             }
    151         }
    152 
    153         public void onLocationChanged(Location location) {
    154             Message msg = Message.obtain();
    155             msg.what = TYPE_LOCATION_CHANGED;
    156             msg.obj = location;
    157             mListenerHandler.sendMessage(msg);
    158         }
    159 
    160         public void onStatusChanged(String provider, int status, Bundle extras) {
    161             Message msg = Message.obtain();
    162             msg.what = TYPE_STATUS_CHANGED;
    163             Bundle b = new Bundle();
    164             b.putString("provider", provider);
    165             b.putInt("status", status);
    166             if (extras != null) {
    167                 b.putBundle("extras", extras);
    168             }
    169             msg.obj = b;
    170             mListenerHandler.sendMessage(msg);
    171         }
    172 
    173         public void onProviderEnabled(String provider) {
    174             Message msg = Message.obtain();
    175             msg.what = TYPE_PROVIDER_ENABLED;
    176             msg.obj = provider;
    177             mListenerHandler.sendMessage(msg);
    178         }
    179 
    180         public void onProviderDisabled(String provider) {
    181             Message msg = Message.obtain();
    182             msg.what = TYPE_PROVIDER_DISABLED;
    183             msg.obj = provider;
    184             mListenerHandler.sendMessage(msg);
    185         }
    186 
    187         private void _handleMessage(Message msg) {
    188             switch (msg.what) {
    189                 case TYPE_LOCATION_CHANGED:
    190                     Location location = new Location((Location) msg.obj);
    191                     mListener.onLocationChanged(location);
    192                     break;
    193                 case TYPE_STATUS_CHANGED:
    194                     Bundle b = (Bundle) msg.obj;
    195                     String provider = b.getString("provider");
    196                     int status = b.getInt("status");
    197                     Bundle extras = b.getBundle("extras");
    198                     mListener.onStatusChanged(provider, status, extras);
    199                     break;
    200                 case TYPE_PROVIDER_ENABLED:
    201                     mListener.onProviderEnabled((String) msg.obj);
    202                     break;
    203                 case TYPE_PROVIDER_DISABLED:
    204                     mListener.onProviderDisabled((String) msg.obj);
    205                     break;
    206             }
    207             try {
    208                 mService.locationCallbackFinished(this);
    209             } catch (RemoteException e) {
    210                 Log.e(TAG, "locationCallbackFinished: RemoteException", e);
    211             }
    212         }
    213     }
    214     /**
    215      * @hide - hide this constructor because it has a parameter
    216      * of type ILocationManager, which is a system private class. The
    217      * right way to create an instance of this class is using the
    218      * factory Context.getSystemService.
    219      */
    220     public LocationManager(ILocationManager service) {
    221         if (false) {
    222             Log.d(TAG, "Constructor: service = " + service);
    223         }
    224         mService = service;
    225     }
    226 
    227     private LocationProvider createProvider(String name, Bundle info) {
    228         DummyLocationProvider provider =
    229             new DummyLocationProvider(name);
    230         provider.setRequiresNetwork(info.getBoolean("network"));
    231         provider.setRequiresSatellite(info.getBoolean("satellite"));
    232         provider.setRequiresCell(info.getBoolean("cell"));
    233         provider.setHasMonetaryCost(info.getBoolean("cost"));
    234         provider.setSupportsAltitude(info.getBoolean("altitude"));
    235         provider.setSupportsSpeed(info.getBoolean("speed"));
    236         provider.setSupportsBearing(info.getBoolean("bearing"));
    237         provider.setPowerRequirement(info.getInt("power"));
    238         provider.setAccuracy(info.getInt("accuracy"));
    239         return provider;
    240     }
    241 
    242     /**
    243      * Returns a list of the names of all known location providers.  All
    244      * providers are returned, including ones that are not permitted to be
    245      * accessed by the calling activity or are currently disabled.
    246      *
    247      * @return list of Strings containing names of the providers
    248      */
    249     public List<String> getAllProviders() {
    250         if (false) {
    251             Log.d(TAG, "getAllProviders");
    252         }
    253         try {
    254             return mService.getAllProviders();
    255         } catch (RemoteException ex) {
    256             Log.e(TAG, "getAllProviders: RemoteException", ex);
    257         }
    258         return null;
    259     }
    260 
    261     /**
    262      * Returns a list of the names of location providers.  Only providers that
    263      * are permitted to be accessed by the calling activity will be returned.
    264      *
    265      * @param enabledOnly if true then only the providers which are currently
    266      * enabled are returned.
    267      * @return list of Strings containing names of the providers
    268      */
    269     public List<String> getProviders(boolean enabledOnly) {
    270         try {
    271             return mService.getProviders(enabledOnly);
    272         } catch (RemoteException ex) {
    273             Log.e(TAG, "getProviders: RemoteException", ex);
    274         }
    275         return null;
    276     }
    277 
    278     /**
    279      * Returns the information associated with the location provider of the
    280      * given name, or null if no provider exists by that name.
    281      *
    282      * @param name the provider name
    283      * @return a LocationProvider, or null
    284      *
    285      * @throws IllegalArgumentException if name is null
    286      * @throws SecurityException if the caller is not permitted to access the
    287      * given provider.
    288      */
    289     public LocationProvider getProvider(String name) {
    290         if (name == null) {
    291             throw new IllegalArgumentException("name==null");
    292         }
    293         try {
    294             Bundle info = mService.getProviderInfo(name);
    295             if (info == null) {
    296                 return null;
    297             }
    298             return createProvider(name, info);
    299         } catch (RemoteException ex) {
    300             Log.e(TAG, "getProvider: RemoteException", ex);
    301         }
    302         return null;
    303     }
    304 
    305     /**
    306      * Returns a list of the names of LocationProviders that satisfy the given
    307      * criteria, or null if none do.  Only providers that are permitted to be
    308      * accessed by the calling activity will be returned.
    309      *
    310      * @param criteria the criteria that the returned providers must match
    311      * @param enabledOnly if true then only the providers which are currently
    312      * enabled are returned.
    313      * @return list of Strings containing names of the providers
    314      */
    315     public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
    316         List<String> goodProviders = Collections.emptyList();
    317         List<String> providers = getProviders(enabledOnly);
    318         for (String providerName : providers) {
    319             LocationProvider provider = getProvider(providerName);
    320             if (provider != null && provider.meetsCriteria(criteria)) {
    321                 if (goodProviders.isEmpty()) {
    322                     goodProviders = new ArrayList<String>();
    323                 }
    324                 goodProviders.add(providerName);
    325             }
    326         }
    327         return goodProviders;
    328     }
    329 
    330     /**
    331      * Returns the next looser power requirement, in the sequence:
    332      *
    333      * POWER_LOW -> POWER_MEDIUM -> POWER_HIGH -> NO_REQUIREMENT
    334      */
    335     private int nextPower(int power) {
    336         switch (power) {
    337         case Criteria.POWER_LOW:
    338             return Criteria.POWER_MEDIUM;
    339         case Criteria.POWER_MEDIUM:
    340             return Criteria.POWER_HIGH;
    341         case Criteria.POWER_HIGH:
    342             return Criteria.NO_REQUIREMENT;
    343         case Criteria.NO_REQUIREMENT:
    344         default:
    345             return Criteria.NO_REQUIREMENT;
    346         }
    347     }
    348 
    349     /**
    350      * Returns the next looser accuracy requirement, in the sequence:
    351      *
    352      * ACCURACY_FINE -> ACCURACY_APPROXIMATE-> NO_REQUIREMENT
    353      */
    354     private int nextAccuracy(int accuracy) {
    355         if (accuracy == Criteria.ACCURACY_FINE) {
    356             return Criteria.ACCURACY_COARSE;
    357         } else {
    358             return Criteria.NO_REQUIREMENT;
    359         }
    360     }
    361 
    362     private abstract class LpComparator implements Comparator<LocationProvider> {
    363 
    364         public int compare(int a1, int a2) {
    365             if (a1 < a2) {
    366                 return -1;
    367             } else if (a1 > a2) {
    368                 return 1;
    369             } else {
    370                 return 0;
    371             }
    372         }
    373 
    374         public int compare(float a1, float a2) {
    375             if (a1 < a2) {
    376                 return -1;
    377             } else if (a1 > a2) {
    378                 return 1;
    379             } else {
    380                 return 0;
    381             }
    382         }
    383     }
    384 
    385     private class LpPowerComparator extends LpComparator {
    386         public int compare(LocationProvider l1, LocationProvider l2) {
    387             int a1 = l1.getPowerRequirement();
    388             int a2 = l2.getPowerRequirement();
    389             return compare(a1, a2); // Smaller is better
    390          }
    391 
    392          public boolean equals(LocationProvider l1, LocationProvider l2) {
    393              int a1 = l1.getPowerRequirement();
    394              int a2 = l2.getPowerRequirement();
    395              return a1 == a2;
    396          }
    397     }
    398 
    399     private class LpAccuracyComparator extends LpComparator {
    400         public int compare(LocationProvider l1, LocationProvider l2) {
    401             int a1 = l1.getAccuracy();
    402             int a2 = l2.getAccuracy();
    403             return compare(a1, a2); // Smaller is better
    404          }
    405 
    406          public boolean equals(LocationProvider l1, LocationProvider l2) {
    407              int a1 = l1.getAccuracy();
    408              int a2 = l2.getAccuracy();
    409              return a1 == a2;
    410          }
    411     }
    412 
    413     private class LpCapabilityComparator extends LpComparator {
    414 
    415         private static final int ALTITUDE_SCORE = 4;
    416         private static final int BEARING_SCORE = 4;
    417         private static final int SPEED_SCORE = 4;
    418 
    419         private int score(LocationProvider p) {
    420             return (p.supportsAltitude() ? ALTITUDE_SCORE : 0) +
    421                 (p.supportsBearing() ? BEARING_SCORE : 0) +
    422                 (p.supportsSpeed() ? SPEED_SCORE : 0);
    423         }
    424 
    425         public int compare(LocationProvider l1, LocationProvider l2) {
    426             int a1 = score(l1);
    427             int a2 = score(l2);
    428             return compare(-a1, -a2); // Bigger is better
    429          }
    430 
    431          public boolean equals(LocationProvider l1, LocationProvider l2) {
    432              int a1 = score(l1);
    433              int a2 = score(l2);
    434              return a1 == a2;
    435          }
    436     }
    437 
    438     private LocationProvider best(List<String> providerNames) {
    439         List<LocationProvider> providers = new ArrayList<LocationProvider>(providerNames.size());
    440         for (String name : providerNames) {
    441             providers.add(getProvider(name));
    442         }
    443 
    444         if (providers.size() < 2) {
    445             return providers.get(0);
    446         }
    447 
    448         // First, sort by power requirement
    449         Collections.sort(providers, new LpPowerComparator());
    450         int power = providers.get(0).getPowerRequirement();
    451         if (power < providers.get(1).getPowerRequirement()) {
    452             return providers.get(0);
    453         }
    454 
    455         int idx, size;
    456 
    457         List<LocationProvider> tmp = new ArrayList<LocationProvider>();
    458         idx = 0;
    459         size = providers.size();
    460         while ((idx < size) && (providers.get(idx).getPowerRequirement() == power)) {
    461             tmp.add(providers.get(idx));
    462             idx++;
    463         }
    464 
    465         // Next, sort by accuracy
    466         Collections.sort(tmp, new LpAccuracyComparator());
    467         int acc = tmp.get(0).getAccuracy();
    468         if (acc < tmp.get(1).getAccuracy()) {
    469             return tmp.get(0);
    470         }
    471 
    472         List<LocationProvider> tmp2 = new ArrayList<LocationProvider>();
    473         idx = 0;
    474         size = tmp.size();
    475         while ((idx < size) && (tmp.get(idx).getAccuracy() == acc)) {
    476             tmp2.add(tmp.get(idx));
    477             idx++;
    478         }
    479 
    480         // Finally, sort by capability "score"
    481         Collections.sort(tmp2, new LpCapabilityComparator());
    482         return tmp2.get(0);
    483     }
    484 
    485     /**
    486      * Returns the name of the provider that best meets the given criteria. Only providers
    487      * that are permitted to be accessed by the calling activity will be
    488      * returned.  If several providers meet the criteria, the one with the best
    489      * accuracy is returned.  If no provider meets the criteria,
    490      * the criteria are loosened in the following sequence:
    491      *
    492      * <ul>
    493      * <li> power requirement
    494      * <li> accuracy
    495      * <li> bearing
    496      * <li> speed
    497      * <li> altitude
    498      * </ul>
    499      *
    500      * <p> Note that the requirement on monetary cost is not removed
    501      * in this process.
    502      *
    503      * @param criteria the criteria that need to be matched
    504      * @param enabledOnly if true then only a provider that is currently enabled is returned
    505      * @return name of the provider that best matches the requirements
    506      */
    507     public String getBestProvider(Criteria criteria, boolean enabledOnly) {
    508         List<String> goodProviders = getProviders(criteria, enabledOnly);
    509         if (!goodProviders.isEmpty()) {
    510             return best(goodProviders).getName();
    511         }
    512 
    513         // Make a copy of the criteria that we can modify
    514         criteria = new Criteria(criteria);
    515 
    516         // Loosen power requirement
    517         int power = criteria.getPowerRequirement();
    518         while (goodProviders.isEmpty() && (power != Criteria.NO_REQUIREMENT)) {
    519             power = nextPower(power);
    520             criteria.setPowerRequirement(power);
    521             goodProviders = getProviders(criteria, enabledOnly);
    522         }
    523         if (!goodProviders.isEmpty()) {
    524             return best(goodProviders).getName();
    525         }
    526 
    527 //        // Loosen response time requirement
    528 //        int responseTime = criteria.getPreferredResponseTime();
    529 //        while (goodProviders.isEmpty() &&
    530 //            (responseTime != Criteria.NO_REQUIREMENT)) {
    531 //            responseTime += 1000;
    532 //            if (responseTime > 60000) {
    533 //                responseTime = Criteria.NO_REQUIREMENT;
    534 //            }
    535 //            criteria.setPreferredResponseTime(responseTime);
    536 //            goodProviders = getProviders(criteria);
    537 //        }
    538 //        if (!goodProviders.isEmpty()) {
    539 //            return best(goodProviders);
    540 //        }
    541 
    542         // Loosen accuracy requirement
    543         int accuracy = criteria.getAccuracy();
    544         while (goodProviders.isEmpty() && (accuracy != Criteria.NO_REQUIREMENT)) {
    545             accuracy = nextAccuracy(accuracy);
    546             criteria.setAccuracy(accuracy);
    547             goodProviders = getProviders(criteria, enabledOnly);
    548         }
    549         if (!goodProviders.isEmpty()) {
    550             return best(goodProviders).getName();
    551         }
    552 
    553         // Remove bearing requirement
    554         criteria.setBearingRequired(false);
    555         goodProviders = getProviders(criteria, enabledOnly);
    556         if (!goodProviders.isEmpty()) {
    557             return best(goodProviders).getName();
    558         }
    559 
    560         // Remove speed requirement
    561         criteria.setSpeedRequired(false);
    562         goodProviders = getProviders(criteria, enabledOnly);
    563         if (!goodProviders.isEmpty()) {
    564             return best(goodProviders).getName();
    565         }
    566 
    567         // Remove altitude requirement
    568         criteria.setAltitudeRequired(false);
    569         goodProviders = getProviders(criteria, enabledOnly);
    570         if (!goodProviders.isEmpty()) {
    571             return best(goodProviders).getName();
    572         }
    573 
    574         return null;
    575     }
    576 
    577     /**
    578      * Registers the current activity to be notified periodically by
    579      * the named provider.  Periodically, the supplied LocationListener will
    580      * be called with the current Location or with status updates.
    581      *
    582      * <p> It may take a while to receive the most recent location. If
    583      * an immediate location is required, applications may use the
    584      * {@link #getLastKnownLocation(String)} method.
    585      *
    586      * <p> In case the provider is disabled by the user, updates will stop,
    587      * and the {@link LocationListener#onProviderDisabled(String)}
    588      * method will be called. As soon as the provider is enabled again,
    589      * the {@link LocationListener#onProviderEnabled(String)} method will
    590      * be called and location updates will start again.
    591      *
    592      * <p> The frequency of notification may be controlled using the
    593      * minTime and minDistance parameters. If minTime is greater than 0,
    594      * the LocationManager could potentially rest for minTime milliseconds
    595      * between location updates to conserve power. If minDistance is greater than 0,
    596      * a location will only be broadcasted if the device moves by minDistance meters.
    597      * To obtain notifications as frequently as possible, set both parameters to 0.
    598      *
    599      * <p> Background services should be careful about setting a sufficiently high
    600      * minTime so that the device doesn't consume too much power by keeping the
    601      * GPS or wireless radios on all the time. In particular, values under 60000ms
    602      * are not recommended.
    603      *
    604      * <p> The calling thread must be a {@link android.os.Looper} thread such as
    605      * the main thread of the calling Activity.
    606      *
    607      * @param provider the name of the provider with which to register
    608      * @param minTime the minimum time interval for notifications, in
    609      * milliseconds. This field is only used as a hint to conserve power, and actual
    610      * time between location updates may be greater or lesser than this value.
    611      * @param minDistance the minimum distance interval for notifications,
    612      * in meters
    613      * @param listener a {#link LocationListener} whose
    614      * {@link LocationListener#onLocationChanged} method will be called for
    615      * each location update
    616      *
    617      * @throws IllegalArgumentException if provider is null or doesn't exist
    618      * @throws IllegalArgumentException if listener is null
    619      * @throws RuntimeException if the calling thread has no Looper
    620      * @throws SecurityException if no suitable permission is present for the provider.
    621      */
    622     public void requestLocationUpdates(String provider,
    623         long minTime, float minDistance, LocationListener listener) {
    624         if (provider == null) {
    625             throw new IllegalArgumentException("provider==null");
    626         }
    627         if (listener == null) {
    628             throw new IllegalArgumentException("listener==null");
    629         }
    630         _requestLocationUpdates(provider, minTime, minDistance, listener, null);
    631     }
    632 
    633     /**
    634      * Registers the current activity to be notified periodically by
    635      * the named provider.  Periodically, the supplied LocationListener will
    636      * be called with the current Location or with status updates.
    637      *
    638      * <p> It may take a while to receive the most recent location. If
    639      * an immediate location is required, applications may use the
    640      * {@link #getLastKnownLocation(String)} method.
    641      *
    642      * <p> In case the provider is disabled by the user, updates will stop,
    643      * and the {@link LocationListener#onProviderDisabled(String)}
    644      * method will be called. As soon as the provider is enabled again,
    645      * the {@link LocationListener#onProviderEnabled(String)} method will
    646      * be called and location updates will start again.
    647      *
    648      * <p> The frequency of notification may be controlled using the
    649      * minTime and minDistance parameters. If minTime is greater than 0,
    650      * the LocationManager could potentially rest for minTime milliseconds
    651      * between location updates to conserve power. If minDistance is greater than 0,
    652      * a location will only be broadcasted if the device moves by minDistance meters.
    653      * To obtain notifications as frequently as possible, set both parameters to 0.
    654      *
    655      * <p> Background services should be careful about setting a sufficiently high
    656      * minTime so that the device doesn't consume too much power by keeping the
    657      * GPS or wireless radios on all the time. In particular, values under 60000ms
    658      * are not recommended.
    659      *
    660      * <p> The supplied Looper is used to implement the callback mechanism.
    661      *
    662      * @param provider the name of the provider with which to register
    663      * @param minTime the minimum time interval for notifications, in
    664      * milliseconds. This field is only used as a hint to conserve power, and actual
    665      * time between location updates may be greater or lesser than this value.
    666      * @param minDistance the minimum distance interval for notifications,
    667      * in meters
    668      * @param listener a {#link LocationListener} whose
    669      * {@link LocationListener#onLocationChanged} method will be called for
    670      * each location update
    671      * @param looper a Looper object whose message queue will be used to
    672      * implement the callback mechanism.
    673      *
    674      * @throws IllegalArgumentException if provider is null or doesn't exist
    675      * @throws IllegalArgumentException if listener is null
    676      * @throws IllegalArgumentException if looper is null
    677      * @throws SecurityException if no suitable permission is present for the provider.
    678      */
    679     public void requestLocationUpdates(String provider,
    680         long minTime, float minDistance, LocationListener listener,
    681         Looper looper) {
    682         if (provider == null) {
    683             throw new IllegalArgumentException("provider==null");
    684         }
    685         if (listener == null) {
    686             throw new IllegalArgumentException("listener==null");
    687         }
    688         if (looper == null) {
    689             throw new IllegalArgumentException("looper==null");
    690         }
    691         _requestLocationUpdates(provider, minTime, minDistance, listener, looper);
    692     }
    693 
    694     private void _requestLocationUpdates(String provider,
    695         long minTime, float minDistance, LocationListener listener,
    696         Looper looper) {
    697         if (minTime < 0L) {
    698             minTime = 0L;
    699         }
    700         if (minDistance < 0.0f) {
    701             minDistance = 0.0f;
    702         }
    703 
    704         try {
    705             synchronized (mListeners) {
    706                 ListenerTransport transport = mListeners.get(listener);
    707                 if (transport == null) {
    708                     transport = new ListenerTransport(listener, looper);
    709                 }
    710                 mListeners.put(listener, transport);
    711                 mService.requestLocationUpdates(provider, minTime, minDistance, transport);
    712             }
    713         } catch (RemoteException ex) {
    714             Log.e(TAG, "requestLocationUpdates: DeadObjectException", ex);
    715         }
    716     }
    717 
    718     /**
    719      * Registers the current activity to be notified periodically by
    720      * the named provider.  Periodically, the supplied PendingIntent will
    721      * be broadcast with the current Location or with status updates.
    722      *
    723      * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
    724      *
    725      * <p> It may take a while to receive the most recent location. If
    726      * an immediate location is required, applications may use the
    727      * {@link #getLastKnownLocation(String)} method.
    728      *
    729      * <p> The frequency of notification or new locations may be controlled using the
    730      * minTime and minDistance parameters. If minTime is greater than 0,
    731      * the LocationManager could potentially rest for minTime milliseconds
    732      * between location updates to conserve power. If minDistance is greater than 0,
    733      * a location will only be broadcast if the device moves by minDistance meters.
    734      * To obtain notifications as frequently as possible, set both parameters to 0.
    735      *
    736      * <p> Background services should be careful about setting a sufficiently high
    737      * minTime so that the device doesn't consume too much power by keeping the
    738      * GPS or wireless radios on all the time. In particular, values under 60000ms
    739      * are not recommended.
    740      *
    741      * <p> In case the provider is disabled by the user, updates will stop,
    742      * and an intent will be sent with an extra with key KEY_PROVIDER_ENABLED and a boolean value
    743      * of false.  If the provider is re-enabled, an intent will be sent with an
    744      * extra with key KEY_PROVIDER_ENABLED and a boolean value of true and location updates will
    745      * start again.
    746      *
    747      * <p> If the provider's status changes, an intent will be sent with an extra with key
    748      * KEY_STATUS_CHANGED and an integer value indicating the new status.  Any extras associated
    749      * with the status update will be sent as well.
    750      *
    751      * @param provider the name of the provider with which to register
    752      * @param minTime the minimum time interval for notifications, in
    753      * milliseconds. This field is only used as a hint to conserve power, and actual
    754      * time between location updates may be greater or lesser than this value.
    755      * @param minDistance the minimum distance interval for notifications,
    756      * in meters
    757      * @param intent a {#link PendingIntet} to be sent for each location update
    758      *
    759      * @throws IllegalArgumentException if provider is null or doesn't exist
    760      * @throws IllegalArgumentException if intent is null
    761      * @throws SecurityException if no suitable permission is present for the provider.
    762      */
    763     public void requestLocationUpdates(String provider,
    764             long minTime, float minDistance, PendingIntent intent) {
    765         if (provider == null) {
    766             throw new IllegalArgumentException("provider==null");
    767         }
    768         if (intent == null) {
    769             throw new IllegalArgumentException("intent==null");
    770         }
    771         _requestLocationUpdates(provider, minTime, minDistance, intent);
    772     }
    773 
    774     private void _requestLocationUpdates(String provider,
    775             long minTime, float minDistance, PendingIntent intent) {
    776         if (minTime < 0L) {
    777             minTime = 0L;
    778         }
    779         if (minDistance < 0.0f) {
    780             minDistance = 0.0f;
    781         }
    782 
    783         try {
    784             mService.requestLocationUpdatesPI(provider, minTime, minDistance, intent);
    785         } catch (RemoteException ex) {
    786             Log.e(TAG, "requestLocationUpdates: RemoteException", ex);
    787         }
    788     }
    789 
    790     /**
    791      * Removes any current registration for location updates of the current activity
    792      * with the given LocationListener.  Following this call, updates will no longer
    793      * occur for this listener.
    794      *
    795      * @param listener {#link LocationListener} object that no longer needs location updates
    796      * @throws IllegalArgumentException if listener is null
    797      */
    798     public void removeUpdates(LocationListener listener) {
    799         if (listener == null) {
    800             throw new IllegalArgumentException("listener==null");
    801         }
    802         if (false) {
    803             Log.d(TAG, "removeUpdates: listener = " + listener);
    804         }
    805         try {
    806             ListenerTransport transport = mListeners.remove(listener);
    807             if (transport != null) {
    808                 mService.removeUpdates(transport);
    809             }
    810         } catch (RemoteException ex) {
    811             Log.e(TAG, "removeUpdates: DeadObjectException", ex);
    812         }
    813     }
    814 
    815     /**
    816      * Removes any current registration for location updates of the current activity
    817      * with the given PendingIntent.  Following this call, updates will no longer
    818      * occur for this intent.
    819      *
    820      * @param intent {#link PendingIntent} object that no longer needs location updates
    821      * @throws IllegalArgumentException if intent is null
    822      */
    823     public void removeUpdates(PendingIntent intent) {
    824         if (intent == null) {
    825             throw new IllegalArgumentException("intent==null");
    826         }
    827         if (false) {
    828             Log.d(TAG, "removeUpdates: intent = " + intent);
    829         }
    830         try {
    831             mService.removeUpdatesPI(intent);
    832         } catch (RemoteException ex) {
    833             Log.e(TAG, "removeUpdates: RemoteException", ex);
    834         }
    835     }
    836 
    837     /**
    838      * Sets a proximity alert for the location given by the position
    839      * (latitude, longitude) and the given radius.  When the device
    840      * detects that it has entered or exited the area surrounding the
    841      * location, the given PendingIntent will be used to create an Intent
    842      * to be fired.
    843      *
    844      * <p> The fired Intent will have a boolean extra added with key
    845      * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
    846      * entering the proximity region; if false, it is exiting.
    847      *
    848      * <p> Due to the approximate nature of position estimation, if the
    849      * device passes through the given area briefly, it is possible
    850      * that no Intent will be fired.  Similarly, an Intent could be
    851      * fired if the device passes very close to the given area but
    852      * does not actually enter it.
    853      *
    854      * <p> After the number of milliseconds given by the expiration
    855      * parameter, the location manager will delete this proximity
    856      * alert and no longer monitor it.  A value of -1 indicates that
    857      * there should be no expiration time.
    858      *
    859      * <p> In case the screen goes to sleep, checks for proximity alerts
    860      * happen only once every 4 minutes. This conserves battery life by
    861      * ensuring that the device isn't perpetually awake.
    862      *
    863      * <p> Internally, this method uses both {@link #NETWORK_PROVIDER}
    864      * and {@link #GPS_PROVIDER}.
    865      *
    866      * @param latitude the latitude of the central point of the
    867      * alert region
    868      * @param longitude the longitude of the central point of the
    869      * alert region
    870      * @param radius the radius of the central point of the
    871      * alert region, in meters
    872      * @param expiration time for this proximity alert, in milliseconds,
    873      * or -1 to indicate no expiration
    874      * @param intent a PendingIntent that will be used to generate an Intent to
    875      * fire when entry to or exit from the alert region is detected
    876      *
    877      * @throws SecurityException if no permission exists for the required
    878      * providers.
    879      */
    880     public void addProximityAlert(double latitude, double longitude,
    881         float radius, long expiration, PendingIntent intent) {
    882         if (false) {
    883             Log.d(TAG, "addProximityAlert: latitude = " + latitude +
    884                 ", longitude = " + longitude + ", radius = " + radius +
    885                 ", expiration = " + expiration +
    886                 ", intent = " + intent);
    887         }
    888         try {
    889             mService.addProximityAlert(latitude, longitude, radius,
    890                                        expiration, intent);
    891         } catch (RemoteException ex) {
    892             Log.e(TAG, "addProximityAlert: RemoteException", ex);
    893         }
    894     }
    895 
    896     /**
    897      * Removes the proximity alert with the given PendingIntent.
    898      *
    899      * @param intent the PendingIntent that no longer needs to be notified of
    900      * proximity alerts
    901      */
    902     public void removeProximityAlert(PendingIntent intent) {
    903         if (false) {
    904             Log.d(TAG, "removeProximityAlert: intent = " + intent);
    905         }
    906         try {
    907             mService.removeProximityAlert(intent);
    908         } catch (RemoteException ex) {
    909             Log.e(TAG, "removeProximityAlert: RemoteException", ex);
    910         }
    911     }
    912 
    913     /**
    914      * Returns the current enabled/disabled status of the given provider. If the
    915      * user has enabled this provider in the Settings menu, true is returned
    916      * otherwise false is returned
    917      *
    918      * @param provider the name of the provider
    919      * @return true if the provider is enabled
    920      *
    921      * @throws SecurityException if no suitable permission is present for the provider.
    922      * @throws IllegalArgumentException if provider is null or doesn't exist
    923      */
    924     public boolean isProviderEnabled(String provider) {
    925         if (provider == null) {
    926             throw new IllegalArgumentException("provider==null");
    927         }
    928         try {
    929             return mService.isProviderEnabled(provider);
    930         } catch (RemoteException ex) {
    931             Log.e(TAG, "isProviderEnabled: RemoteException", ex);
    932             return false;
    933         }
    934     }
    935 
    936     /**
    937      * Returns a Location indicating the data from the last known
    938      * location fix obtained from the given provider.  This can be done
    939      * without starting the provider.  Note that this location could
    940      * be out-of-date, for example if the device was turned off and
    941      * moved to another location.
    942      *
    943      * <p> If the provider is currently disabled, null is returned.
    944      *
    945      * @param provider the name of the provider
    946      * @return the last known location for the provider, or null
    947      *
    948      * @throws SecurityException if no suitable permission is present for the provider.
    949      * @throws IllegalArgumentException if provider is null or doesn't exist
    950      */
    951     public Location getLastKnownLocation(String provider) {
    952         if (provider == null) {
    953             throw new IllegalArgumentException("provider==null");
    954         }
    955         try {
    956             return mService.getLastKnownLocation(provider);
    957         } catch (RemoteException ex) {
    958             Log.e(TAG, "getLastKnowLocation: RemoteException", ex);
    959             return null;
    960         }
    961     }
    962 
    963     // Mock provider support
    964 
    965     /**
    966      * Creates a mock location provider and adds it to the set of active providers.
    967      *
    968      * @param name the provider name
    969      * @param requiresNetwork
    970      * @param requiresSatellite
    971      * @param requiresCell
    972      * @param hasMonetaryCost
    973      * @param supportsAltitude
    974      * @param supportsSpeed
    975      * @param supportsBearing
    976      * @param powerRequirement
    977      * @param accuracy
    978      *
    979      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
    980      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
    981      * Settings.Secure.ALLOW_MOCK_LOCATION} system setting is not enabled
    982      * @throws IllegalArgumentException if a provider with the given name already exists
    983      */
    984     public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
    985         boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
    986         boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
    987         try {
    988             mService.addTestProvider(name, requiresNetwork, requiresSatellite, requiresCell,
    989                 hasMonetaryCost, supportsAltitude, supportsSpeed, supportsBearing, powerRequirement,
    990                 accuracy);
    991         } catch (RemoteException ex) {
    992             Log.e(TAG, "addTestProvider: RemoteException", ex);
    993         }
    994     }
    995 
    996     /**
    997      * Removes the mock location provider with the given name.
    998      *
    999      * @param provider the provider name
   1000      *
   1001      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
   1002      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
   1003      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
   1004      * @throws IllegalArgumentException if no provider with the given name exists
   1005      */
   1006     public void removeTestProvider(String provider) {
   1007         try {
   1008             mService.removeTestProvider(provider);
   1009         } catch (RemoteException ex) {
   1010             Log.e(TAG, "removeTestProvider: RemoteException", ex);
   1011         }
   1012     }
   1013 
   1014     /**
   1015      * Sets a mock location for the given provider.  This location will be used in place
   1016      * of any actual location from the provider.
   1017      *
   1018      * @param provider the provider name
   1019      * @param loc the mock location
   1020      *
   1021      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
   1022      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
   1023      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
   1024      * @throws IllegalArgumentException if no provider with the given name exists
   1025      */
   1026     public void setTestProviderLocation(String provider, Location loc) {
   1027         try {
   1028             mService.setTestProviderLocation(provider, loc);
   1029         } catch (RemoteException ex) {
   1030             Log.e(TAG, "setTestProviderLocation: RemoteException", ex);
   1031         }
   1032     }
   1033 
   1034     /**
   1035      * Removes any mock location associated with the given provider.
   1036      *
   1037      * @param provider the provider name
   1038      *
   1039      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
   1040      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
   1041      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
   1042      * @throws IllegalArgumentException if no provider with the given name exists
   1043      */
   1044     public void clearTestProviderLocation(String provider) {
   1045         try {
   1046             mService.clearTestProviderLocation(provider);
   1047         } catch (RemoteException ex) {
   1048             Log.e(TAG, "clearTestProviderLocation: RemoteException", ex);
   1049         }
   1050     }
   1051 
   1052     /**
   1053      * Sets a mock enabled value for the given provider.  This value will be used in place
   1054      * of any actual value from the provider.
   1055      *
   1056      * @param provider the provider name
   1057      * @param enabled the mock enabled value
   1058      *
   1059      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
   1060      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
   1061      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
   1062      * @throws IllegalArgumentException if no provider with the given name exists
   1063      */
   1064     public void setTestProviderEnabled(String provider, boolean enabled) {
   1065         try {
   1066             mService.setTestProviderEnabled(provider, enabled);
   1067         } catch (RemoteException ex) {
   1068             Log.e(TAG, "setTestProviderEnabled: RemoteException", ex);
   1069         }
   1070     }
   1071 
   1072     /**
   1073      * Removes any mock enabled value associated with the given provider.
   1074      *
   1075      * @param provider the provider name
   1076      *
   1077      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
   1078      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
   1079      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
   1080      * @throws IllegalArgumentException if no provider with the given name exists
   1081      */
   1082     public void clearTestProviderEnabled(String provider) {
   1083         try {
   1084             mService.clearTestProviderEnabled(provider);
   1085         } catch (RemoteException ex) {
   1086             Log.e(TAG, "clearTestProviderEnabled: RemoteException", ex);
   1087         }
   1088 
   1089     }
   1090 
   1091     /**
   1092      * Sets mock status values for the given provider.  These values will be used in place
   1093      * of any actual values from the provider.
   1094      *
   1095      * @param provider the provider name
   1096      * @param status the mock status
   1097      * @param extras a Bundle containing mock extras
   1098      * @param updateTime the mock update time
   1099      *
   1100      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
   1101      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
   1102      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
   1103      * @throws IllegalArgumentException if no provider with the given name exists
   1104      */
   1105     public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
   1106         try {
   1107             mService.setTestProviderStatus(provider, status, extras, updateTime);
   1108         } catch (RemoteException ex) {
   1109             Log.e(TAG, "setTestProviderStatus: RemoteException", ex);
   1110         }
   1111     }
   1112 
   1113     /**
   1114      * Removes any mock status values associated with the given provider.
   1115      *
   1116      * @param provider the provider name
   1117      *
   1118      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
   1119      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
   1120      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
   1121      * @throws IllegalArgumentException if no provider with the given name exists
   1122      */
   1123     public void clearTestProviderStatus(String provider) {
   1124         try {
   1125             mService.clearTestProviderStatus(provider);
   1126         } catch (RemoteException ex) {
   1127             Log.e(TAG, "clearTestProviderStatus: RemoteException", ex);
   1128         }
   1129     }
   1130 
   1131     // GPS-specific support
   1132 
   1133     // This class is used to send GPS status events to the client's main thread.
   1134     private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
   1135 
   1136         private final GpsStatus.Listener mListener;
   1137         private final GpsStatus.NmeaListener mNmeaListener;
   1138 
   1139         // This must not equal any of the GpsStatus event IDs
   1140         private static final int NMEA_RECEIVED = 1000;
   1141 
   1142         private class Nmea {
   1143             long mTimestamp;
   1144             String mNmea;
   1145 
   1146             Nmea(long timestamp, String nmea) {
   1147                 mTimestamp = timestamp;
   1148                 mNmea = nmea;
   1149             }
   1150         }
   1151         private ArrayList<Nmea> mNmeaBuffer;
   1152 
   1153         GpsStatusListenerTransport(GpsStatus.Listener listener) {
   1154             mListener = listener;
   1155             mNmeaListener = null;
   1156         }
   1157 
   1158         GpsStatusListenerTransport(GpsStatus.NmeaListener listener) {
   1159             mNmeaListener = listener;
   1160             mListener = null;
   1161             mNmeaBuffer = new ArrayList<Nmea>();
   1162         }
   1163 
   1164         public void onGpsStarted() {
   1165             if (mListener != null) {
   1166                 Message msg = Message.obtain();
   1167                 msg.what = GpsStatus.GPS_EVENT_STARTED;
   1168                 mGpsHandler.sendMessage(msg);
   1169             }
   1170         }
   1171 
   1172         public void onGpsStopped() {
   1173             if (mListener != null) {
   1174                 Message msg = Message.obtain();
   1175                 msg.what = GpsStatus.GPS_EVENT_STOPPED;
   1176                 mGpsHandler.sendMessage(msg);
   1177             }
   1178         }
   1179 
   1180         public void onFirstFix(int ttff) {
   1181             if (mListener != null) {
   1182                 mGpsStatus.setTimeToFirstFix(ttff);
   1183                 Message msg = Message.obtain();
   1184                 msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
   1185                 mGpsHandler.sendMessage(msg);
   1186             }
   1187         }
   1188 
   1189         public void onSvStatusChanged(int svCount, int[] prns, float[] snrs,
   1190                 float[] elevations, float[] azimuths, int ephemerisMask,
   1191                 int almanacMask, int usedInFixMask) {
   1192             if (mListener != null) {
   1193                 mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths,
   1194                         ephemerisMask, almanacMask, usedInFixMask);
   1195 
   1196                 Message msg = Message.obtain();
   1197                 msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
   1198                 // remove any SV status messages already in the queue
   1199                 mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
   1200                 mGpsHandler.sendMessage(msg);
   1201             }
   1202         }
   1203 
   1204         public void onNmeaReceived(long timestamp, String nmea) {
   1205             if (mNmeaListener != null) {
   1206                 synchronized (mNmeaBuffer) {
   1207                     mNmeaBuffer.add(new Nmea(timestamp, nmea));
   1208                 }
   1209                 Message msg = Message.obtain();
   1210                 msg.what = NMEA_RECEIVED;
   1211                 // remove any NMEA_RECEIVED messages already in the queue
   1212                 mGpsHandler.removeMessages(NMEA_RECEIVED);
   1213                 mGpsHandler.sendMessage(msg);
   1214             }
   1215         }
   1216 
   1217         private final Handler mGpsHandler = new Handler() {
   1218             @Override
   1219             public void handleMessage(Message msg) {
   1220                 if (msg.what == NMEA_RECEIVED) {
   1221                     synchronized (mNmeaBuffer) {
   1222                         int length = mNmeaBuffer.size();
   1223                         for (int i = 0; i < length; i++) {
   1224                             Nmea nmea = mNmeaBuffer.get(i);
   1225                             mNmeaListener.onNmeaReceived(nmea.mTimestamp, nmea.mNmea);
   1226                         }
   1227                         mNmeaBuffer.clear();
   1228                     }
   1229                 } else {
   1230                     // synchronize on mGpsStatus to ensure the data is copied atomically.
   1231                     synchronized(mGpsStatus) {
   1232                         mListener.onGpsStatusChanged(msg.what);
   1233                     }
   1234                 }
   1235             }
   1236         };
   1237     }
   1238 
   1239     /**
   1240      * Adds a GPS status listener.
   1241      *
   1242      * @param listener GPS status listener object to register
   1243      *
   1244      * @return true if the listener was successfully added
   1245      *
   1246      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
   1247      */
   1248     public boolean addGpsStatusListener(GpsStatus.Listener listener) {
   1249         boolean result;
   1250 
   1251         if (mGpsStatusListeners.get(listener) != null) {
   1252             // listener is already registered
   1253             return true;
   1254         }
   1255         try {
   1256             GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
   1257             result = mService.addGpsStatusListener(transport);
   1258             if (result) {
   1259                 mGpsStatusListeners.put(listener, transport);
   1260             }
   1261         } catch (RemoteException e) {
   1262             Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
   1263             result = false;
   1264         }
   1265 
   1266         return result;
   1267     }
   1268 
   1269     /**
   1270      * Removes a GPS status listener.
   1271      *
   1272      * @param listener GPS status listener object to remove
   1273      */
   1274     public void removeGpsStatusListener(GpsStatus.Listener listener) {
   1275         try {
   1276             GpsStatusListenerTransport transport = mGpsStatusListeners.remove(listener);
   1277             if (transport != null) {
   1278                 mService.removeGpsStatusListener(transport);
   1279             }
   1280         } catch (RemoteException e) {
   1281             Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
   1282         }
   1283     }
   1284 
   1285     /**
   1286      * Adds an NMEA listener.
   1287      *
   1288      * @param listener a {#link GpsStatus.NmeaListener} object to register
   1289      *
   1290      * @return true if the listener was successfully added
   1291      *
   1292      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
   1293      */
   1294     public boolean addNmeaListener(GpsStatus.NmeaListener listener) {
   1295         boolean result;
   1296 
   1297         if (mNmeaListeners.get(listener) != null) {
   1298             // listener is already registered
   1299             return true;
   1300         }
   1301         try {
   1302             GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
   1303             result = mService.addGpsStatusListener(transport);
   1304             if (result) {
   1305                 mNmeaListeners.put(listener, transport);
   1306             }
   1307         } catch (RemoteException e) {
   1308             Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
   1309             result = false;
   1310         }
   1311 
   1312         return result;
   1313     }
   1314 
   1315     /**
   1316      * Removes an NMEA listener.
   1317      *
   1318      * @param listener a {#link GpsStatus.NmeaListener} object to remove
   1319      */
   1320     public void removeNmeaListener(GpsStatus.NmeaListener listener) {
   1321         try {
   1322             GpsStatusListenerTransport transport = mNmeaListeners.remove(listener);
   1323             if (transport != null) {
   1324                 mService.removeGpsStatusListener(transport);
   1325             }
   1326         } catch (RemoteException e) {
   1327             Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
   1328         }
   1329     }
   1330 
   1331      /**
   1332      * Retrieves information about the current status of the GPS engine.
   1333      * This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged}
   1334      * callback to ensure that the data is copied atomically.
   1335      *
   1336      * The caller may either pass in a {@link GpsStatus} object to set with the latest
   1337      * status information, or pass null to create a new {@link GpsStatus} object.
   1338      *
   1339      * @param status object containing GPS status details, or null.
   1340      * @return status object containing updated GPS status.
   1341      */
   1342     public GpsStatus getGpsStatus(GpsStatus status) {
   1343         if (status == null) {
   1344             status = new GpsStatus();
   1345        }
   1346        status.setStatus(mGpsStatus);
   1347        return status;
   1348     }
   1349 
   1350     /**
   1351      * Sends additional commands to a location provider.
   1352      * Can be used to support provider specific extensions to the Location Manager API
   1353      *
   1354      * @param provider name of the location provider.
   1355      * @param command name of the command to send to the provider.
   1356      * @param extras optional arguments for the command (or null).
   1357      * The provider may optionally fill the extras Bundle with results from the command.
   1358      *
   1359      * @return true if the command succeeds.
   1360      */
   1361     public boolean sendExtraCommand(String provider, String command, Bundle extras) {
   1362         try {
   1363             return mService.sendExtraCommand(provider, command, extras);
   1364         } catch (RemoteException e) {
   1365             Log.e(TAG, "RemoteException in sendExtraCommand: ", e);
   1366             return false;
   1367         }
   1368     }
   1369 
   1370     /**
   1371      * Used by NetInitiatedActivity to report user response
   1372      * for network initiated GPS fix requests.
   1373      *
   1374      * {@hide}
   1375      */
   1376     public boolean sendNiResponse(int notifId, int userResponse) {
   1377     	try {
   1378             return mService.sendNiResponse(notifId, userResponse);
   1379         } catch (RemoteException e) {
   1380             Log.e(TAG, "RemoteException in sendNiResponse: ", e);
   1381             return false;
   1382         }
   1383     }
   1384 
   1385 }
   1386