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