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 com.android.internal.location.ProviderProperties;
     20 
     21 import android.annotation.SystemApi;
     22 import android.app.PendingIntent;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.os.Build;
     26 import android.os.Bundle;
     27 import android.os.Handler;
     28 import android.os.Looper;
     29 import android.os.Message;
     30 import android.os.RemoteException;
     31 import android.util.Log;
     32 
     33 import java.util.ArrayList;
     34 import java.util.HashMap;
     35 import java.util.List;
     36 
     37 /**
     38  * This class provides access to the system location services.  These
     39  * services allow applications to obtain periodic updates of the
     40  * device's geographical location, or to fire an application-specified
     41  * {@link Intent} when the device enters the proximity of a given
     42  * geographical location.
     43  *
     44  * <p>You do not
     45  * instantiate this class directly; instead, retrieve it through
     46  * {@link android.content.Context#getSystemService
     47  * Context.getSystemService(Context.LOCATION_SERVICE)}.
     48  *
     49  * <p class="note">Unless noted, all Location API methods require
     50  * the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or
     51  * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permissions.
     52  * If your application only has the coarse permission then it will not have
     53  * access to the GPS or passive location providers. Other providers will still
     54  * return location results, but the update rate will be throttled and the exact
     55  * location will be obfuscated to a coarse level of accuracy.
     56  */
     57 public class LocationManager {
     58     private static final String TAG = "LocationManager";
     59 
     60     private final Context mContext;
     61     private final ILocationManager mService;
     62     private final GpsMeasurementListenerTransport mGpsMeasurementListenerTransport;
     63     private final GpsNavigationMessageListenerTransport mGpsNavigationMessageListenerTransport;
     64     private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners =
     65             new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>();
     66     private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners =
     67             new HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport>();
     68     private final GpsStatus mGpsStatus = new GpsStatus();
     69 
     70     /**
     71      * Name of the network location provider.
     72      * <p>This provider determines location based on
     73      * availability of cell tower and WiFi access points. Results are retrieved
     74      * by means of a network lookup.
     75      */
     76     public static final String NETWORK_PROVIDER = "network";
     77 
     78     /**
     79      * Name of the GPS location provider.
     80      *
     81      * <p>This provider determines location using
     82      * satellites. Depending on conditions, this provider may take a while to return
     83      * a location fix. Requires the permission
     84      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
     85      *
     86      * <p> The extras Bundle for the GPS location provider can contain the
     87      * following key/value pairs:
     88      * <ul>
     89      * <li> satellites - the number of satellites used to derive the fix
     90      * </ul>
     91      */
     92     public static final String GPS_PROVIDER = "gps";
     93 
     94     /**
     95      * A special location provider for receiving locations without actually initiating
     96      * a location fix.
     97      *
     98      * <p>This provider can be used to passively receive location updates
     99      * when other applications or services request them without actually requesting
    100      * the locations yourself.  This provider will return locations generated by other
    101      * providers.  You can query the {@link Location#getProvider()} method to determine
    102      * the origin of the location update. Requires the permission
    103      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}, although if the GPS is
    104      * not enabled this provider might only return coarse fixes.
    105      */
    106     public static final String PASSIVE_PROVIDER = "passive";
    107 
    108     /**
    109      * Name of the Fused location provider.
    110      *
    111      * <p>This provider combines inputs for all possible location sources
    112      * to provide the best possible Location fix. It is implicitly
    113      * used for all API's that involve the {@link LocationRequest}
    114      * object.
    115      *
    116      * @hide
    117      */
    118     public static final String FUSED_PROVIDER = "fused";
    119 
    120     /**
    121      * Key used for the Bundle extra holding a boolean indicating whether
    122      * a proximity alert is entering (true) or exiting (false)..
    123      */
    124     public static final String KEY_PROXIMITY_ENTERING = "entering";
    125 
    126     /**
    127      * Key used for a Bundle extra holding an Integer status value
    128      * when a status change is broadcast using a PendingIntent.
    129      */
    130     public static final String KEY_STATUS_CHANGED = "status";
    131 
    132     /**
    133      * Key used for a Bundle extra holding an Boolean status value
    134      * when a provider enabled/disabled event is broadcast using a PendingIntent.
    135      */
    136     public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
    137 
    138     /**
    139      * Key used for a Bundle extra holding a Location value
    140      * when a location change is broadcast using a PendingIntent.
    141      */
    142     public static final String KEY_LOCATION_CHANGED = "location";
    143 
    144     /**
    145      * Broadcast intent action indicating that the GPS has either been
    146      * enabled or disabled. An intent extra provides this state as a boolean,
    147      * where {@code true} means enabled.
    148      * @see #EXTRA_GPS_ENABLED
    149      *
    150      * @hide
    151      */
    152     public static final String GPS_ENABLED_CHANGE_ACTION =
    153         "android.location.GPS_ENABLED_CHANGE";
    154 
    155     /**
    156      * Broadcast intent action when the configured location providers
    157      * change. For use with {@link #isProviderEnabled(String)}. If you're interacting with the
    158      * {@link android.provider.Settings.Secure#LOCATION_MODE} API, use {@link #MODE_CHANGED_ACTION}
    159      * instead.
    160      */
    161     public static final String PROVIDERS_CHANGED_ACTION =
    162         "android.location.PROVIDERS_CHANGED";
    163 
    164     /**
    165      * Broadcast intent action when {@link android.provider.Settings.Secure#LOCATION_MODE} changes.
    166      * For use with the {@link android.provider.Settings.Secure#LOCATION_MODE} API.
    167      * If you're interacting with {@link #isProviderEnabled(String)}, use
    168      * {@link #PROVIDERS_CHANGED_ACTION} instead.
    169      *
    170      * In the future, there may be mode changes that do not result in
    171      * {@link #PROVIDERS_CHANGED_ACTION} broadcasts.
    172      */
    173     public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
    174 
    175     /**
    176      * Broadcast intent action indicating that the GPS has either started or
    177      * stopped receiving GPS fixes. An intent extra provides this state as a
    178      * boolean, where {@code true} means that the GPS is actively receiving fixes.
    179      * @see #EXTRA_GPS_ENABLED
    180      *
    181      * @hide
    182      */
    183     public static final String GPS_FIX_CHANGE_ACTION =
    184         "android.location.GPS_FIX_CHANGE";
    185 
    186     /**
    187      * The lookup key for a boolean that indicates whether GPS is enabled or
    188      * disabled. {@code true} means GPS is enabled. Retrieve it with
    189      * {@link android.content.Intent#getBooleanExtra(String,boolean)}.
    190      *
    191      * @hide
    192      */
    193     public static final String EXTRA_GPS_ENABLED = "enabled";
    194 
    195     /**
    196      * Broadcast intent action indicating that a high power location requests
    197      * has either started or stopped being active.  The current state of
    198      * active location requests should be read from AppOpsManager using
    199      * {@code OP_MONITOR_HIGH_POWER_LOCATION}.
    200      *
    201      * @hide
    202      */
    203     public static final String HIGH_POWER_REQUEST_CHANGE_ACTION =
    204         "android.location.HIGH_POWER_REQUEST_CHANGE";
    205 
    206     // Map from LocationListeners to their associated ListenerTransport objects
    207     private HashMap<LocationListener,ListenerTransport> mListeners =
    208         new HashMap<LocationListener,ListenerTransport>();
    209 
    210     private class ListenerTransport extends ILocationListener.Stub {
    211         private static final int TYPE_LOCATION_CHANGED = 1;
    212         private static final int TYPE_STATUS_CHANGED = 2;
    213         private static final int TYPE_PROVIDER_ENABLED = 3;
    214         private static final int TYPE_PROVIDER_DISABLED = 4;
    215 
    216         private LocationListener mListener;
    217         private final Handler mListenerHandler;
    218 
    219         ListenerTransport(LocationListener listener, Looper looper) {
    220             mListener = listener;
    221 
    222             if (looper == null) {
    223                 mListenerHandler = new Handler() {
    224                     @Override
    225                     public void handleMessage(Message msg) {
    226                         _handleMessage(msg);
    227                     }
    228                 };
    229             } else {
    230                 mListenerHandler = new Handler(looper) {
    231                     @Override
    232                     public void handleMessage(Message msg) {
    233                         _handleMessage(msg);
    234                     }
    235                 };
    236             }
    237         }
    238 
    239         @Override
    240         public void onLocationChanged(Location location) {
    241             Message msg = Message.obtain();
    242             msg.what = TYPE_LOCATION_CHANGED;
    243             msg.obj = location;
    244             mListenerHandler.sendMessage(msg);
    245         }
    246 
    247         @Override
    248         public void onStatusChanged(String provider, int status, Bundle extras) {
    249             Message msg = Message.obtain();
    250             msg.what = TYPE_STATUS_CHANGED;
    251             Bundle b = new Bundle();
    252             b.putString("provider", provider);
    253             b.putInt("status", status);
    254             if (extras != null) {
    255                 b.putBundle("extras", extras);
    256             }
    257             msg.obj = b;
    258             mListenerHandler.sendMessage(msg);
    259         }
    260 
    261         @Override
    262         public void onProviderEnabled(String provider) {
    263             Message msg = Message.obtain();
    264             msg.what = TYPE_PROVIDER_ENABLED;
    265             msg.obj = provider;
    266             mListenerHandler.sendMessage(msg);
    267         }
    268 
    269         @Override
    270         public void onProviderDisabled(String provider) {
    271             Message msg = Message.obtain();
    272             msg.what = TYPE_PROVIDER_DISABLED;
    273             msg.obj = provider;
    274             mListenerHandler.sendMessage(msg);
    275         }
    276 
    277         private void _handleMessage(Message msg) {
    278             switch (msg.what) {
    279                 case TYPE_LOCATION_CHANGED:
    280                     Location location = new Location((Location) msg.obj);
    281                     mListener.onLocationChanged(location);
    282                     break;
    283                 case TYPE_STATUS_CHANGED:
    284                     Bundle b = (Bundle) msg.obj;
    285                     String provider = b.getString("provider");
    286                     int status = b.getInt("status");
    287                     Bundle extras = b.getBundle("extras");
    288                     mListener.onStatusChanged(provider, status, extras);
    289                     break;
    290                 case TYPE_PROVIDER_ENABLED:
    291                     mListener.onProviderEnabled((String) msg.obj);
    292                     break;
    293                 case TYPE_PROVIDER_DISABLED:
    294                     mListener.onProviderDisabled((String) msg.obj);
    295                     break;
    296             }
    297             try {
    298                 mService.locationCallbackFinished(this);
    299             } catch (RemoteException e) {
    300                 Log.e(TAG, "locationCallbackFinished: RemoteException", e);
    301             }
    302         }
    303     }
    304 
    305     /**
    306      * @hide - hide this constructor because it has a parameter
    307      * of type ILocationManager, which is a system private class. The
    308      * right way to create an instance of this class is using the
    309      * factory Context.getSystemService.
    310      */
    311     public LocationManager(Context context, ILocationManager service) {
    312         mService = service;
    313         mContext = context;
    314         mGpsMeasurementListenerTransport = new GpsMeasurementListenerTransport(mContext, mService);
    315         mGpsNavigationMessageListenerTransport =
    316                 new GpsNavigationMessageListenerTransport(mContext, mService);
    317     }
    318 
    319     private LocationProvider createProvider(String name, ProviderProperties properties) {
    320         return new LocationProvider(name, properties);
    321     }
    322 
    323     /**
    324      * Returns a list of the names of all known location providers.
    325      * <p>All providers are returned, including ones that are not permitted to
    326      * be accessed by the calling activity or are currently disabled.
    327      *
    328      * @return list of Strings containing names of the provider
    329      */
    330     public List<String> getAllProviders() {
    331         try {
    332             return mService.getAllProviders();
    333         } catch (RemoteException e) {
    334             Log.e(TAG, "RemoteException", e);
    335         }
    336         return null;
    337     }
    338 
    339     /**
    340      * Returns a list of the names of location providers.
    341      *
    342      * @param enabledOnly if true then only the providers which are currently
    343      * enabled are returned.
    344      * @return list of Strings containing names of the providers
    345      */
    346     public List<String> getProviders(boolean enabledOnly) {
    347         try {
    348             return mService.getProviders(null, enabledOnly);
    349         } catch (RemoteException e) {
    350             Log.e(TAG, "RemoteException", e);
    351         }
    352         return null;
    353     }
    354 
    355     /**
    356      * Returns the information associated with the location provider of the
    357      * given name, or null if no provider exists by that name.
    358      *
    359      * @param name the provider name
    360      * @return a LocationProvider, or null
    361      *
    362      * @throws IllegalArgumentException if name is null or does not exist
    363      * @throws SecurityException if the caller is not permitted to access the
    364      * given provider.
    365      */
    366     public LocationProvider getProvider(String name) {
    367         checkProvider(name);
    368         try {
    369             ProviderProperties properties = mService.getProviderProperties(name);
    370             if (properties == null) {
    371                 return null;
    372             }
    373             return createProvider(name, properties);
    374         } catch (RemoteException e) {
    375             Log.e(TAG, "RemoteException", e);
    376         }
    377         return null;
    378     }
    379 
    380     /**
    381      * Returns a list of the names of LocationProviders that satisfy the given
    382      * criteria, or null if none do.  Only providers that are permitted to be
    383      * accessed by the calling activity will be returned.
    384      *
    385      * @param criteria the criteria that the returned providers must match
    386      * @param enabledOnly if true then only the providers which are currently
    387      * enabled are returned.
    388      * @return list of Strings containing names of the providers
    389      */
    390     public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
    391         checkCriteria(criteria);
    392         try {
    393             return mService.getProviders(criteria, enabledOnly);
    394         } catch (RemoteException e) {
    395             Log.e(TAG, "RemoteException", e);
    396         }
    397         return null;
    398     }
    399 
    400     /**
    401      * Returns the name of the provider that best meets the given criteria. Only providers
    402      * that are permitted to be accessed by the calling activity will be
    403      * returned.  If several providers meet the criteria, the one with the best
    404      * accuracy is returned.  If no provider meets the criteria,
    405      * the criteria are loosened in the following sequence:
    406      *
    407      * <ul>
    408      * <li> power requirement
    409      * <li> accuracy
    410      * <li> bearing
    411      * <li> speed
    412      * <li> altitude
    413      * </ul>
    414      *
    415      * <p> Note that the requirement on monetary cost is not removed
    416      * in this process.
    417      *
    418      * @param criteria the criteria that need to be matched
    419      * @param enabledOnly if true then only a provider that is currently enabled is returned
    420      * @return name of the provider that best matches the requirements
    421      */
    422     public String getBestProvider(Criteria criteria, boolean enabledOnly) {
    423         checkCriteria(criteria);
    424         try {
    425             return mService.getBestProvider(criteria, enabledOnly);
    426         } catch (RemoteException e) {
    427             Log.e(TAG, "RemoteException", e);
    428         }
    429         return null;
    430     }
    431 
    432     /**
    433      * Register for location updates using the named provider, and a
    434      * pending intent.
    435      *
    436      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    437      * for more detail on how to use this method.
    438      *
    439      * @param provider the name of the provider with which to register
    440      * @param minTime minimum time interval between location updates, in milliseconds
    441      * @param minDistance minimum distance between location updates, in meters
    442      * @param listener a {@link LocationListener} whose
    443      * {@link LocationListener#onLocationChanged} method will be called for
    444      * each location update
    445      *
    446      * @throws IllegalArgumentException if provider is null or doesn't exist
    447      * on this device
    448      * @throws IllegalArgumentException if listener is null
    449      * @throws RuntimeException if the calling thread has no Looper
    450      * @throws SecurityException if no suitable permission is present
    451      */
    452     public void requestLocationUpdates(String provider, long minTime, float minDistance,
    453             LocationListener listener) {
    454         checkProvider(provider);
    455         checkListener(listener);
    456 
    457         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
    458                 provider, minTime, minDistance, false);
    459         requestLocationUpdates(request, listener, null, null);
    460     }
    461 
    462     /**
    463      * Register for location updates using the named provider, and a callback on
    464      * the specified looper thread.
    465      *
    466      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    467      * for more detail on how to use this method.
    468      *
    469      * @param provider the name of the provider with which to register
    470      * @param minTime minimum time interval between location updates, in milliseconds
    471      * @param minDistance minimum distance between location updates, in meters
    472      * @param listener a {@link LocationListener} whose
    473      * {@link LocationListener#onLocationChanged} method will be called for
    474      * each location update
    475      * @param looper a Looper object whose message queue will be used to
    476      * implement the callback mechanism, or null to make callbacks on the calling
    477      * thread
    478      *
    479      * @throws IllegalArgumentException if provider is null or doesn't exist
    480      * @throws IllegalArgumentException if listener is null
    481      * @throws SecurityException if no suitable permission is present
    482      */
    483     public void requestLocationUpdates(String provider, long minTime, float minDistance,
    484             LocationListener listener, Looper looper) {
    485         checkProvider(provider);
    486         checkListener(listener);
    487 
    488         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
    489                 provider, minTime, minDistance, false);
    490         requestLocationUpdates(request, listener, looper, null);
    491     }
    492 
    493     /**
    494      * Register for location updates using a Criteria, and a callback
    495      * on the specified looper thread.
    496      *
    497      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    498      * for more detail on how to use this method.
    499      *
    500      * @param minTime minimum time interval between location updates, in milliseconds
    501      * @param minDistance minimum distance between location updates, in meters
    502      * @param criteria contains parameters for the location manager to choose the
    503      * appropriate provider and parameters to compute the location
    504      * @param listener a {@link LocationListener} whose
    505      * {@link LocationListener#onLocationChanged} method will be called for
    506      * each location update
    507      * @param looper a Looper object whose message queue will be used to
    508      * implement the callback mechanism, or null to make callbacks on the calling
    509      * thread
    510      *
    511      * @throws IllegalArgumentException if criteria is null
    512      * @throws IllegalArgumentException if listener is null
    513      * @throws SecurityException if no suitable permission is present
    514      */
    515     public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
    516             LocationListener listener, Looper looper) {
    517         checkCriteria(criteria);
    518         checkListener(listener);
    519 
    520         LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
    521                 criteria, minTime, minDistance, false);
    522         requestLocationUpdates(request, listener, looper, null);
    523     }
    524 
    525     /**
    526      * Register for location updates using the named provider, and a
    527      * pending intent.
    528      *
    529      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    530      * for more detail on how to use this method.
    531      *
    532      * @param provider the name of the provider with which to register
    533      * @param minTime minimum time interval between location updates, in milliseconds
    534      * @param minDistance minimum distance between location updates, in meters
    535      * @param intent a {@link PendingIntent} to be sent for each location update
    536      *
    537      * @throws IllegalArgumentException if provider is null or doesn't exist
    538      * on this device
    539      * @throws IllegalArgumentException if intent is null
    540      * @throws SecurityException if no suitable permission is present
    541      */
    542     public void requestLocationUpdates(String provider, long minTime, float minDistance,
    543             PendingIntent intent) {
    544         checkProvider(provider);
    545         checkPendingIntent(intent);
    546 
    547         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
    548                 provider, minTime, minDistance, false);
    549         requestLocationUpdates(request, null, null, intent);
    550     }
    551 
    552     /**
    553      * Register for location updates using a Criteria and pending intent.
    554      *
    555      * <p>The <code>requestLocationUpdates()</code> and
    556      * <code>requestSingleUpdate()</code> register the current activity to be
    557      * updated periodically by the named provider, or by the provider matching
    558      * the specified {@link Criteria}, with location and status updates.
    559      *
    560      * <p> It may take a while to receive the first location update. If
    561      * an immediate location is required, applications may use the
    562      * {@link #getLastKnownLocation(String)} method.
    563      *
    564      * <p> Location updates are received either by {@link LocationListener}
    565      * callbacks, or by broadcast intents to a supplied {@link PendingIntent}.
    566      *
    567      * <p> If the caller supplied a pending intent, then location updates
    568      * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
    569      * {@link android.location.Location} value.
    570      *
    571      * <p> The location update interval can be controlled using the minTime parameter.
    572      * The elapsed time between location updates will never be less than
    573      * minTime, although it can be more depending on the Location Provider
    574      * implementation and the update interval requested by other applications.
    575      *
    576      * <p> Choosing a sensible value for minTime is important to conserve
    577      * battery life. Each location update requires power from
    578      * GPS, WIFI, Cell and other radios. Select a minTime value as high as
    579      * possible while still providing a reasonable user experience.
    580      * If your application is not in the foreground and showing
    581      * location to the user then your application should avoid using an active
    582      * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
    583      * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
    584      * or greater. If your application is in the foreground and showing
    585      * location to the user then it is appropriate to select a faster
    586      * update interval.
    587      *
    588      * <p> The minDistance parameter can also be used to control the
    589      * frequency of location updates. If it is greater than 0 then the
    590      * location provider will only send your application an update when
    591      * the location has changed by at least minDistance meters, AND
    592      * at least minTime milliseconds have passed. However it is more
    593      * difficult for location providers to save power using the minDistance
    594      * parameter, so minTime should be the primary tool to conserving battery
    595      * life.
    596      *
    597      * <p> If your application wants to passively observe location
    598      * updates triggered by other applications, but not consume
    599      * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
    600      * This provider does not actively turn on or modify active location
    601      * providers, so you do not need to be as careful about minTime and
    602      * minDistance. However if your application performs heavy work
    603      * on a location update (such as network activity) then you should
    604      * select non-zero values for minTime and/or minDistance to rate-limit
    605      * your update frequency in the case another application enables a
    606      * location provider with extremely fast updates.
    607      *
    608      * <p>In case the provider is disabled by the user, updates will stop,
    609      * and a provider availability update will be sent.
    610      * As soon as the provider is enabled again,
    611      * location updates will immediately resume and a provider availability
    612      * update sent. Providers can also send status updates, at any time,
    613      * with extra's specific to the provider. If a callback was supplied
    614      * then status and availability updates are via
    615      * {@link LocationListener#onProviderDisabled},
    616      * {@link LocationListener#onProviderEnabled} or
    617      * {@link LocationListener#onStatusChanged}. Alternately, if a
    618      * pending intent was supplied then status and availability updates
    619      * are broadcast intents with extra keys of
    620      * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED}.
    621      *
    622      * <p> If a {@link LocationListener} is used but with no Looper specified
    623      * then the calling thread must already
    624      * be a {@link android.os.Looper} thread such as the main thread of the
    625      * calling Activity. If a Looper is specified with a {@link LocationListener}
    626      * then callbacks are made on the supplied Looper thread.
    627      *
    628      * <p class="note"> Prior to Jellybean, the minTime parameter was
    629      * only a hint, and some location provider implementations ignored it.
    630      * From Jellybean and onwards it is mandatory for Android compatible
    631      * devices to observe both the minTime and minDistance parameters.
    632      *
    633      * @param minTime minimum time interval between location updates, in milliseconds
    634      * @param minDistance minimum distance between location updates, in meters
    635      * @param criteria contains parameters for the location manager to choose the
    636      * appropriate provider and parameters to compute the location
    637      * @param intent a {@link PendingIntent} to be sent for each location update
    638      *
    639      * @throws IllegalArgumentException if criteria is null
    640      * @throws IllegalArgumentException if intent is null
    641      * @throws SecurityException if no suitable permission is present
    642      */
    643     public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
    644             PendingIntent intent) {
    645         checkCriteria(criteria);
    646         checkPendingIntent(intent);
    647 
    648         LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
    649                 criteria, minTime, minDistance, false);
    650         requestLocationUpdates(request, null, null, intent);
    651     }
    652 
    653     /**
    654      * Register for a single location update using the named provider and
    655      * a callback.
    656      *
    657      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    658      * for more detail on how to use this method.
    659      *
    660      * @param provider the name of the provider with which to register
    661      * @param listener a {@link LocationListener} whose
    662      * {@link LocationListener#onLocationChanged} method will be called when
    663      * the location update is available
    664      * @param looper a Looper object whose message queue will be used to
    665      * implement the callback mechanism, or null to make callbacks on the calling
    666      * thread
    667      *
    668      * @throws IllegalArgumentException if provider is null or doesn't exist
    669      * @throws IllegalArgumentException if listener is null
    670      * @throws SecurityException if no suitable permission is present
    671      */
    672     public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) {
    673         checkProvider(provider);
    674         checkListener(listener);
    675 
    676         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
    677                 provider, 0, 0, true);
    678         requestLocationUpdates(request, listener, looper, null);
    679     }
    680 
    681     /**
    682      * Register for a single location update using a Criteria and
    683      * a callback.
    684      *
    685      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    686      * for more detail on how to use this method.
    687      *
    688      * @param criteria contains parameters for the location manager to choose the
    689      * appropriate provider and parameters to compute the location
    690      * @param listener a {@link LocationListener} whose
    691      * {@link LocationListener#onLocationChanged} method will be called when
    692      * the location update is available
    693      * @param looper a Looper object whose message queue will be used to
    694      * implement the callback mechanism, or null to make callbacks on the calling
    695      * thread
    696      *
    697      * @throws IllegalArgumentException if criteria is null
    698      * @throws IllegalArgumentException if listener is null
    699      * @throws SecurityException if no suitable permission is present
    700      */
    701     public void requestSingleUpdate(Criteria criteria, LocationListener listener, Looper looper) {
    702         checkCriteria(criteria);
    703         checkListener(listener);
    704 
    705         LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
    706                 criteria, 0, 0, true);
    707         requestLocationUpdates(request, listener, looper, null);
    708     }
    709 
    710     /**
    711      * Register for a single location update using a named provider and pending intent.
    712      *
    713      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    714      * for more detail on how to use this method.
    715      *
    716      * @param provider the name of the provider with which to register
    717      * @param intent a {@link PendingIntent} to be sent for the location update
    718      *
    719      * @throws IllegalArgumentException if provider is null or doesn't exist
    720      * @throws IllegalArgumentException if intent is null
    721      * @throws SecurityException if no suitable permission is present
    722      */
    723     public void requestSingleUpdate(String provider, PendingIntent intent) {
    724         checkProvider(provider);
    725         checkPendingIntent(intent);
    726 
    727         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
    728                 provider, 0, 0, true);
    729         requestLocationUpdates(request, null, null, intent);
    730     }
    731 
    732     /**
    733      * Register for a single location update using a Criteria and pending intent.
    734      *
    735      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    736      * for more detail on how to use this method.
    737      *
    738      * @param criteria contains parameters for the location manager to choose the
    739      * appropriate provider and parameters to compute the location
    740      * @param intent a {@link PendingIntent} to be sent for the location update
    741      *
    742      * @throws IllegalArgumentException if provider is null or doesn't exist
    743      * @throws IllegalArgumentException if intent is null
    744      * @throws SecurityException if no suitable permission is present
    745      */
    746     public void requestSingleUpdate(Criteria criteria, PendingIntent intent) {
    747         checkCriteria(criteria);
    748         checkPendingIntent(intent);
    749 
    750         LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
    751                 criteria, 0, 0, true);
    752         requestLocationUpdates(request, null, null, intent);
    753     }
    754 
    755     /**
    756      * Register for fused location updates using a LocationRequest and callback.
    757      *
    758      * <p>Upon a location update, the system delivers the new {@link Location} to the
    759      * provided {@link LocationListener}, by calling its {@link
    760      * LocationListener#onLocationChanged} method.</p>
    761      *
    762      * <p>The system will automatically select and enable the best providers
    763      * to compute a location for your application. It may use only passive
    764      * locations, or just a single location source, or it may fuse together
    765      * multiple location sources in order to produce the best possible
    766      * result, depending on the quality of service requested in the
    767      * {@link LocationRequest}.
    768      *
    769      * <p>LocationRequest can be null, in which case the system will choose
    770      * default, low power parameters for location updates. You will occasionally
    771      * receive location updates as available, without a major power impact on the
    772      * system. If your application just needs an occasional location update
    773      * without any strict demands, then pass a null LocationRequest.
    774      *
    775      * <p>Only one LocationRequest can be registered for each unique callback
    776      * or pending intent. So a subsequent request with the same callback or
    777      * pending intent will over-write the previous LocationRequest.
    778      *
    779      * <p> If a pending intent is supplied then location updates
    780      * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
    781      * {@link android.location.Location} value. If a callback is supplied
    782      * then location updates are made using the
    783      * {@link LocationListener#onLocationChanged} callback, on the specified
    784      * Looper thread. If a {@link LocationListener} is used
    785      * but with a null Looper then the calling thread must already
    786      * be a {@link android.os.Looper} thread (such as the main thread) and
    787      * callbacks will occur on this thread.
    788      *
    789      * <p> Provider status updates and availability updates are deprecated
    790      * because the system is performing provider fusion on the applications
    791      * behalf. So {@link LocationListener#onProviderDisabled},
    792      * {@link LocationListener#onProviderEnabled}, {@link LocationListener#onStatusChanged}
    793      * will not be called, and intents with extra keys of
    794      * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED} will not
    795      * be received.
    796      *
    797      * <p> To unregister for Location updates, use: {@link #removeUpdates(LocationListener)}.
    798      *
    799      * @param request quality of service required, null for default low power
    800      * @param listener a {@link LocationListener} whose
    801      * {@link LocationListener#onLocationChanged} method will be called when
    802      * the location update is available
    803      * @param looper a Looper object whose message queue will be used to
    804      * implement the callback mechanism, or null to make callbacks on the calling
    805      * thread
    806      *
    807      * @throws IllegalArgumentException if listener is null
    808      * @throws SecurityException if no suitable permission is present
    809      *
    810      * @hide
    811      */
    812     @SystemApi
    813     public void requestLocationUpdates(LocationRequest request, LocationListener listener,
    814             Looper looper) {
    815         checkListener(listener);
    816         requestLocationUpdates(request, listener, looper, null);
    817     }
    818 
    819 
    820     /**
    821      * Register for fused location updates using a LocationRequest and a pending intent.
    822      *
    823      * <p>Upon a location update, the system delivers the new {@link Location} with your provided
    824      * {@link PendingIntent}, as the value for {@link LocationManager#KEY_LOCATION_CHANGED}
    825      * in the intent's extras.</p>
    826      *
    827      * <p> To unregister for Location updates, use: {@link #removeUpdates(PendingIntent)}.
    828      *
    829      * <p> See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)}
    830      * for more detail.
    831      *
    832      * @param request quality of service required, null for default low power
    833      * @param intent a {@link PendingIntent} to be sent for the location update
    834      *
    835      * @throws IllegalArgumentException if intent is null
    836      * @throws SecurityException if no suitable permission is present
    837      *
    838      * @hide
    839      */
    840     @SystemApi
    841     public void requestLocationUpdates(LocationRequest request, PendingIntent intent) {
    842         checkPendingIntent(intent);
    843         requestLocationUpdates(request, null, null, intent);
    844     }
    845 
    846     private ListenerTransport wrapListener(LocationListener listener, Looper looper) {
    847         if (listener == null) return null;
    848         synchronized (mListeners) {
    849             ListenerTransport transport = mListeners.get(listener);
    850             if (transport == null) {
    851                 transport = new ListenerTransport(listener, looper);
    852             }
    853             mListeners.put(listener, transport);
    854             return transport;
    855         }
    856     }
    857 
    858     private void requestLocationUpdates(LocationRequest request, LocationListener listener,
    859             Looper looper, PendingIntent intent) {
    860 
    861         String packageName = mContext.getPackageName();
    862 
    863         // wrap the listener class
    864         ListenerTransport transport = wrapListener(listener, looper);
    865 
    866         try {
    867             mService.requestLocationUpdates(request, transport, intent, packageName);
    868        } catch (RemoteException e) {
    869            Log.e(TAG, "RemoteException", e);
    870        }
    871     }
    872 
    873     /**
    874      * Removes all location updates for the specified LocationListener.
    875      *
    876      * <p>Following this call, updates will no longer
    877      * occur for this listener.
    878      *
    879      * @param listener listener object that no longer needs location updates
    880      * @throws IllegalArgumentException if listener is null
    881      */
    882     public void removeUpdates(LocationListener listener) {
    883         checkListener(listener);
    884         String packageName = mContext.getPackageName();
    885 
    886         ListenerTransport transport;
    887         synchronized (mListeners) {
    888             transport = mListeners.remove(listener);
    889         }
    890         if (transport == null) return;
    891 
    892         try {
    893             mService.removeUpdates(transport, null, packageName);
    894         } catch (RemoteException e) {
    895             Log.e(TAG, "RemoteException", e);
    896         }
    897     }
    898 
    899     /**
    900      * Removes all location updates for the specified pending intent.
    901      *
    902      * <p>Following this call, updates will no longer for this pending intent.
    903      *
    904      * @param intent pending intent object that no longer needs location updates
    905      * @throws IllegalArgumentException if intent is null
    906      */
    907     public void removeUpdates(PendingIntent intent) {
    908         checkPendingIntent(intent);
    909         String packageName = mContext.getPackageName();
    910 
    911         try {
    912             mService.removeUpdates(null, intent, packageName);
    913         } catch (RemoteException e) {
    914             Log.e(TAG, "RemoteException", e);
    915         }
    916     }
    917 
    918     /**
    919      * Set a proximity alert for the location given by the position
    920      * (latitude, longitude) and the given radius.
    921      *
    922      * <p> When the device
    923      * detects that it has entered or exited the area surrounding the
    924      * location, the given PendingIntent will be used to create an Intent
    925      * to be fired.
    926      *
    927      * <p> The fired Intent will have a boolean extra added with key
    928      * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
    929      * entering the proximity region; if false, it is exiting.
    930      *
    931      * <p> Due to the approximate nature of position estimation, if the
    932      * device passes through the given area briefly, it is possible
    933      * that no Intent will be fired.  Similarly, an Intent could be
    934      * fired if the device passes very close to the given area but
    935      * does not actually enter it.
    936      *
    937      * <p> After the number of milliseconds given by the expiration
    938      * parameter, the location manager will delete this proximity
    939      * alert and no longer monitor it.  A value of -1 indicates that
    940      * there should be no expiration time.
    941      *
    942      * <p> Internally, this method uses both {@link #NETWORK_PROVIDER}
    943      * and {@link #GPS_PROVIDER}.
    944      *
    945      * <p>Before API version 17, this method could be used with
    946      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
    947      * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
    948      * From API version 17 and onwards, this method requires
    949      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
    950      *
    951      * @param latitude the latitude of the central point of the
    952      * alert region
    953      * @param longitude the longitude of the central point of the
    954      * alert region
    955      * @param radius the radius of the central point of the
    956      * alert region, in meters
    957      * @param expiration time for this proximity alert, in milliseconds,
    958      * or -1 to indicate no expiration
    959      * @param intent a PendingIntent that will be used to generate an Intent to
    960      * fire when entry to or exit from the alert region is detected
    961      *
    962      * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
    963      * permission is not present
    964      */
    965     public void addProximityAlert(double latitude, double longitude, float radius, long expiration,
    966             PendingIntent intent) {
    967         checkPendingIntent(intent);
    968         if (expiration < 0) expiration = Long.MAX_VALUE;
    969 
    970         Geofence fence = Geofence.createCircle(latitude, longitude, radius);
    971         LocationRequest request = new LocationRequest().setExpireIn(expiration);
    972         try {
    973             mService.requestGeofence(request, fence, intent, mContext.getPackageName());
    974         } catch (RemoteException e) {
    975             Log.e(TAG, "RemoteException", e);
    976         }
    977     }
    978 
    979     /**
    980      * Add a geofence with the specified LocationRequest quality of service.
    981      *
    982      * <p> When the device
    983      * detects that it has entered or exited the area surrounding the
    984      * location, the given PendingIntent will be used to create an Intent
    985      * to be fired.
    986      *
    987      * <p> The fired Intent will have a boolean extra added with key
    988      * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
    989      * entering the proximity region; if false, it is exiting.
    990      *
    991      * <p> The geofence engine fuses results from all location providers to
    992      * provide the best balance between accuracy and power. Applications
    993      * can choose the quality of service required using the
    994      * {@link LocationRequest} object. If it is null then a default,
    995      * low power geo-fencing implementation is used. It is possible to cross
    996      * a geo-fence without notification, but the system will do its best
    997      * to detect, using {@link LocationRequest} as a hint to trade-off
    998      * accuracy and power.
    999      *
   1000      * <p> The power required by the geofence engine can depend on many factors,
   1001      * such as quality and interval requested in {@link LocationRequest},
   1002      * distance to nearest geofence and current device velocity.
   1003      *
   1004      * @param request quality of service required, null for default low power
   1005      * @param fence a geographical description of the geofence area
   1006      * @param intent pending intent to receive geofence updates
   1007      *
   1008      * @throws IllegalArgumentException if fence is null
   1009      * @throws IllegalArgumentException if intent is null
   1010      * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
   1011      * permission is not present
   1012      *
   1013      * @hide
   1014      */
   1015     public void addGeofence(LocationRequest request, Geofence fence, PendingIntent intent) {
   1016         checkPendingIntent(intent);
   1017         checkGeofence(fence);
   1018 
   1019         try {
   1020             mService.requestGeofence(request, fence, intent, mContext.getPackageName());
   1021         } catch (RemoteException e) {
   1022             Log.e(TAG, "RemoteException", e);
   1023         }
   1024     }
   1025 
   1026     /**
   1027      * Removes the proximity alert with the given PendingIntent.
   1028      *
   1029      * <p>Before API version 17, this method could be used with
   1030      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
   1031      * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
   1032      * From API version 17 and onwards, this method requires
   1033      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
   1034      *
   1035      * @param intent the PendingIntent that no longer needs to be notified of
   1036      * proximity alerts
   1037      *
   1038      * @throws IllegalArgumentException if intent is null
   1039      * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
   1040      * permission is not present
   1041      */
   1042     public void removeProximityAlert(PendingIntent intent) {
   1043         checkPendingIntent(intent);
   1044         String packageName = mContext.getPackageName();
   1045 
   1046         try {
   1047             mService.removeGeofence(null, intent, packageName);
   1048         } catch (RemoteException e) {
   1049             Log.e(TAG, "RemoteException", e);
   1050         }
   1051     }
   1052 
   1053     /**
   1054      * Remove a single geofence.
   1055      *
   1056      * <p>This removes only the specified geofence associated with the
   1057      * specified pending intent. All other geofences remain unchanged.
   1058      *
   1059      * @param fence a geofence previously passed to {@link #addGeofence}
   1060      * @param intent a pending intent previously passed to {@link #addGeofence}
   1061      *
   1062      * @throws IllegalArgumentException if fence is null
   1063      * @throws IllegalArgumentException if intent is null
   1064      * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
   1065      * permission is not present
   1066      *
   1067      * @hide
   1068      */
   1069     public void removeGeofence(Geofence fence, PendingIntent intent) {
   1070         checkPendingIntent(intent);
   1071         checkGeofence(fence);
   1072         String packageName = mContext.getPackageName();
   1073 
   1074         try {
   1075             mService.removeGeofence(fence, intent, packageName);
   1076         } catch (RemoteException e) {
   1077             Log.e(TAG, "RemoteException", e);
   1078         }
   1079     }
   1080 
   1081     /**
   1082      * Remove all geofences registered to the specified pending intent.
   1083      *
   1084      * @param intent a pending intent previously passed to {@link #addGeofence}
   1085      *
   1086      * @throws IllegalArgumentException if intent is null
   1087      * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
   1088      * permission is not present
   1089      *
   1090      * @hide
   1091      */
   1092     public void removeAllGeofences(PendingIntent intent) {
   1093         checkPendingIntent(intent);
   1094         String packageName = mContext.getPackageName();
   1095 
   1096         try {
   1097             mService.removeGeofence(null, intent, packageName);
   1098         } catch (RemoteException e) {
   1099             Log.e(TAG, "RemoteException", e);
   1100         }
   1101     }
   1102 
   1103     /**
   1104      * Returns the current enabled/disabled status of the given provider.
   1105      *
   1106      * <p>If the user has enabled this provider in the Settings menu, true
   1107      * is returned otherwise false is returned
   1108      *
   1109      * <p>Callers should instead use
   1110      * {@link android.provider.Settings.Secure#LOCATION_MODE}
   1111      * unless they depend on provider-specific APIs such as
   1112      * {@link #requestLocationUpdates(String, long, float, LocationListener)}.
   1113      *
   1114      * <p>
   1115      * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this
   1116      * method would throw {@link SecurityException} if the location permissions
   1117      * were not sufficient to use the specified provider.
   1118      *
   1119      * @param provider the name of the provider
   1120      * @return true if the provider exists and is enabled
   1121      *
   1122      * @throws IllegalArgumentException if provider is null
   1123      */
   1124     public boolean isProviderEnabled(String provider) {
   1125         checkProvider(provider);
   1126 
   1127         try {
   1128             return mService.isProviderEnabled(provider);
   1129         } catch (RemoteException e) {
   1130             Log.e(TAG, "RemoteException", e);
   1131             return false;
   1132         }
   1133     }
   1134 
   1135     /**
   1136      * Get the last known location.
   1137      *
   1138      * <p>This location could be very old so use
   1139      * {@link Location#getElapsedRealtimeNanos} to calculate its age. It can
   1140      * also return null if no previous location is available.
   1141      *
   1142      * <p>Always returns immediately.
   1143      *
   1144      * @return The last known location, or null if not available
   1145      * @throws SecurityException if no suitable permission is present
   1146      *
   1147      * @hide
   1148      */
   1149     public Location getLastLocation() {
   1150         String packageName = mContext.getPackageName();
   1151 
   1152         try {
   1153             return mService.getLastLocation(null, packageName);
   1154         } catch (RemoteException e) {
   1155             Log.e(TAG, "RemoteException", e);
   1156             return null;
   1157         }
   1158     }
   1159 
   1160     /**
   1161      * Returns a Location indicating the data from the last known
   1162      * location fix obtained from the given provider.
   1163      *
   1164      * <p> This can be done
   1165      * without starting the provider.  Note that this location could
   1166      * be out-of-date, for example if the device was turned off and
   1167      * moved to another location.
   1168      *
   1169      * <p> If the provider is currently disabled, null is returned.
   1170      *
   1171      * @param provider the name of the provider
   1172      * @return the last known location for the provider, or null
   1173      *
   1174      * @throws SecurityException if no suitable permission is present
   1175      * @throws IllegalArgumentException if provider is null or doesn't exist
   1176      */
   1177     public Location getLastKnownLocation(String provider) {
   1178         checkProvider(provider);
   1179         String packageName = mContext.getPackageName();
   1180         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
   1181                 provider, 0, 0, true);
   1182 
   1183         try {
   1184             return mService.getLastLocation(request, packageName);
   1185         } catch (RemoteException e) {
   1186             Log.e(TAG, "RemoteException", e);
   1187             return null;
   1188         }
   1189     }
   1190 
   1191     // --- Mock provider support ---
   1192     // TODO: It would be fantastic to deprecate mock providers entirely, and replace
   1193     // with something closer to LocationProviderBase.java
   1194 
   1195     /**
   1196      * Creates a mock location provider and adds it to the set of active providers.
   1197      *
   1198      * @param name the provider name
   1199      *
   1200      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
   1201      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
   1202      * Settings.Secure.ALLOW_MOCK_LOCATION} system setting is not enabled
   1203      * @throws IllegalArgumentException if a provider with the given name already exists
   1204      */
   1205     public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
   1206             boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
   1207             boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
   1208         ProviderProperties properties = new ProviderProperties(requiresNetwork,
   1209                 requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
   1210                 supportsBearing, powerRequirement, accuracy);
   1211         if (name.matches(LocationProvider.BAD_CHARS_REGEX)) {
   1212             throw new IllegalArgumentException("provider name contains illegal character: " + name);
   1213         }
   1214 
   1215         try {
   1216             mService.addTestProvider(name, properties);
   1217         } catch (RemoteException e) {
   1218             Log.e(TAG, "RemoteException", e);
   1219         }
   1220     }
   1221 
   1222     /**
   1223      * Removes the mock location provider with the given name.
   1224      *
   1225      * @param provider the provider name
   1226      *
   1227      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
   1228      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
   1229      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
   1230      * @throws IllegalArgumentException if no provider with the given name exists
   1231      */
   1232     public void removeTestProvider(String provider) {
   1233         try {
   1234             mService.removeTestProvider(provider);
   1235         } catch (RemoteException e) {
   1236             Log.e(TAG, "RemoteException", e);
   1237         }
   1238     }
   1239 
   1240     /**
   1241      * Sets a mock location for the given provider.
   1242      * <p>This location will be used in place of any actual location from the provider.
   1243      * The location object must have a minimum number of fields set to be
   1244      * considered a valid LocationProvider Location, as per documentation
   1245      * on {@link Location} class.
   1246      *
   1247      * @param provider the provider name
   1248      * @param loc the mock location
   1249      *
   1250      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
   1251      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
   1252      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
   1253      * @throws IllegalArgumentException if no provider with the given name exists
   1254      * @throws IllegalArgumentException if the location is incomplete
   1255      */
   1256     public void setTestProviderLocation(String provider, Location loc) {
   1257         if (!loc.isComplete()) {
   1258             IllegalArgumentException e = new IllegalArgumentException(
   1259                     "Incomplete location object, missing timestamp or accuracy? " + loc);
   1260             if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) {
   1261                 // just log on old platform (for backwards compatibility)
   1262                 Log.w(TAG, e);
   1263                 loc.makeComplete();
   1264             } else {
   1265                 // really throw it!
   1266                 throw e;
   1267             }
   1268         }
   1269 
   1270         try {
   1271             mService.setTestProviderLocation(provider, loc);
   1272         } catch (RemoteException e) {
   1273             Log.e(TAG, "RemoteException", e);
   1274         }
   1275     }
   1276 
   1277     /**
   1278      * Removes any mock location associated with the given provider.
   1279      *
   1280      * @param provider the provider name
   1281      *
   1282      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
   1283      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
   1284      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
   1285      * @throws IllegalArgumentException if no provider with the given name exists
   1286      */
   1287     public void clearTestProviderLocation(String provider) {
   1288         try {
   1289             mService.clearTestProviderLocation(provider);
   1290         } catch (RemoteException e) {
   1291             Log.e(TAG, "RemoteException", e);
   1292         }
   1293     }
   1294 
   1295     /**
   1296      * Sets a mock enabled value for the given provider.  This value will be used in place
   1297      * of any actual value from the provider.
   1298      *
   1299      * @param provider the provider name
   1300      * @param enabled the mock enabled value
   1301      *
   1302      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
   1303      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
   1304      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
   1305      * @throws IllegalArgumentException if no provider with the given name exists
   1306      */
   1307     public void setTestProviderEnabled(String provider, boolean enabled) {
   1308         try {
   1309             mService.setTestProviderEnabled(provider, enabled);
   1310         } catch (RemoteException e) {
   1311             Log.e(TAG, "RemoteException", e);
   1312         }
   1313     }
   1314 
   1315     /**
   1316      * Removes any mock enabled value associated with the given provider.
   1317      *
   1318      * @param provider the provider name
   1319      *
   1320      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
   1321      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
   1322      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
   1323      * @throws IllegalArgumentException if no provider with the given name exists
   1324      */
   1325     public void clearTestProviderEnabled(String provider) {
   1326         try {
   1327             mService.clearTestProviderEnabled(provider);
   1328         } catch (RemoteException e) {
   1329             Log.e(TAG, "RemoteException", e);
   1330         }
   1331     }
   1332 
   1333     /**
   1334      * Sets mock status values for the given provider.  These values will be used in place
   1335      * of any actual values from the provider.
   1336      *
   1337      * @param provider the provider name
   1338      * @param status the mock status
   1339      * @param extras a Bundle containing mock extras
   1340      * @param updateTime the mock update time
   1341      *
   1342      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
   1343      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
   1344      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
   1345      * @throws IllegalArgumentException if no provider with the given name exists
   1346      */
   1347     public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
   1348         try {
   1349             mService.setTestProviderStatus(provider, status, extras, updateTime);
   1350         } catch (RemoteException e) {
   1351             Log.e(TAG, "RemoteException", e);
   1352         }
   1353     }
   1354 
   1355     /**
   1356      * Removes any mock status values associated with the given provider.
   1357      *
   1358      * @param provider the provider name
   1359      *
   1360      * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
   1361      * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
   1362      * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
   1363      * @throws IllegalArgumentException if no provider with the given name exists
   1364      */
   1365     public void clearTestProviderStatus(String provider) {
   1366         try {
   1367             mService.clearTestProviderStatus(provider);
   1368         } catch (RemoteException e) {
   1369             Log.e(TAG, "RemoteException", e);
   1370         }
   1371     }
   1372 
   1373     // --- GPS-specific support ---
   1374 
   1375     // This class is used to send GPS status events to the client's main thread.
   1376     private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
   1377 
   1378         private final GpsStatus.Listener mListener;
   1379         private final GpsStatus.NmeaListener mNmeaListener;
   1380 
   1381         // This must not equal any of the GpsStatus event IDs
   1382         private static final int NMEA_RECEIVED = 1000;
   1383 
   1384         private class Nmea {
   1385             long mTimestamp;
   1386             String mNmea;
   1387 
   1388             Nmea(long timestamp, String nmea) {
   1389                 mTimestamp = timestamp;
   1390                 mNmea = nmea;
   1391             }
   1392         }
   1393         private ArrayList<Nmea> mNmeaBuffer;
   1394 
   1395         GpsStatusListenerTransport(GpsStatus.Listener listener) {
   1396             mListener = listener;
   1397             mNmeaListener = null;
   1398         }
   1399 
   1400         GpsStatusListenerTransport(GpsStatus.NmeaListener listener) {
   1401             mNmeaListener = listener;
   1402             mListener = null;
   1403             mNmeaBuffer = new ArrayList<Nmea>();
   1404         }
   1405 
   1406         @Override
   1407         public void onGpsStarted() {
   1408             if (mListener != null) {
   1409                 Message msg = Message.obtain();
   1410                 msg.what = GpsStatus.GPS_EVENT_STARTED;
   1411                 mGpsHandler.sendMessage(msg);
   1412             }
   1413         }
   1414 
   1415         @Override
   1416         public void onGpsStopped() {
   1417             if (mListener != null) {
   1418                 Message msg = Message.obtain();
   1419                 msg.what = GpsStatus.GPS_EVENT_STOPPED;
   1420                 mGpsHandler.sendMessage(msg);
   1421             }
   1422         }
   1423 
   1424         @Override
   1425         public void onFirstFix(int ttff) {
   1426             if (mListener != null) {
   1427                 mGpsStatus.setTimeToFirstFix(ttff);
   1428                 Message msg = Message.obtain();
   1429                 msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
   1430                 mGpsHandler.sendMessage(msg);
   1431             }
   1432         }
   1433 
   1434         @Override
   1435         public void onSvStatusChanged(int svCount, int[] prns, float[] snrs,
   1436                 float[] elevations, float[] azimuths, int ephemerisMask,
   1437                 int almanacMask, int usedInFixMask) {
   1438             if (mListener != null) {
   1439                 mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths,
   1440                         ephemerisMask, almanacMask, usedInFixMask);
   1441 
   1442                 Message msg = Message.obtain();
   1443                 msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
   1444                 // remove any SV status messages already in the queue
   1445                 mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
   1446                 mGpsHandler.sendMessage(msg);
   1447             }
   1448         }
   1449 
   1450         @Override
   1451         public void onNmeaReceived(long timestamp, String nmea) {
   1452             if (mNmeaListener != null) {
   1453                 synchronized (mNmeaBuffer) {
   1454                     mNmeaBuffer.add(new Nmea(timestamp, nmea));
   1455                 }
   1456                 Message msg = Message.obtain();
   1457                 msg.what = NMEA_RECEIVED;
   1458                 // remove any NMEA_RECEIVED messages already in the queue
   1459                 mGpsHandler.removeMessages(NMEA_RECEIVED);
   1460                 mGpsHandler.sendMessage(msg);
   1461             }
   1462         }
   1463 
   1464         private final Handler mGpsHandler = new Handler() {
   1465             @Override
   1466             public void handleMessage(Message msg) {
   1467                 if (msg.what == NMEA_RECEIVED) {
   1468                     synchronized (mNmeaBuffer) {
   1469                         int length = mNmeaBuffer.size();
   1470                         for (int i = 0; i < length; i++) {
   1471                             Nmea nmea = mNmeaBuffer.get(i);
   1472                             mNmeaListener.onNmeaReceived(nmea.mTimestamp, nmea.mNmea);
   1473                         }
   1474                         mNmeaBuffer.clear();
   1475                     }
   1476                 } else {
   1477                     // synchronize on mGpsStatus to ensure the data is copied atomically.
   1478                     synchronized(mGpsStatus) {
   1479                         mListener.onGpsStatusChanged(msg.what);
   1480                     }
   1481                 }
   1482             }
   1483         };
   1484     }
   1485 
   1486     /**
   1487      * Adds a GPS status listener.
   1488      *
   1489      * @param listener GPS status listener object to register
   1490      *
   1491      * @return true if the listener was successfully added
   1492      *
   1493      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
   1494      */
   1495     public boolean addGpsStatusListener(GpsStatus.Listener listener) {
   1496         boolean result;
   1497 
   1498         if (mGpsStatusListeners.get(listener) != null) {
   1499             // listener is already registered
   1500             return true;
   1501         }
   1502         try {
   1503             GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
   1504             result = mService.addGpsStatusListener(transport, mContext.getPackageName());
   1505             if (result) {
   1506                 mGpsStatusListeners.put(listener, transport);
   1507             }
   1508         } catch (RemoteException e) {
   1509             Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
   1510             result = false;
   1511         }
   1512 
   1513         return result;
   1514     }
   1515 
   1516     /**
   1517      * Removes a GPS status listener.
   1518      *
   1519      * @param listener GPS status listener object to remove
   1520      */
   1521     public void removeGpsStatusListener(GpsStatus.Listener listener) {
   1522         try {
   1523             GpsStatusListenerTransport transport = mGpsStatusListeners.remove(listener);
   1524             if (transport != null) {
   1525                 mService.removeGpsStatusListener(transport);
   1526             }
   1527         } catch (RemoteException e) {
   1528             Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
   1529         }
   1530     }
   1531 
   1532     /**
   1533      * Adds an NMEA listener.
   1534      *
   1535      * @param listener a {@link GpsStatus.NmeaListener} object to register
   1536      *
   1537      * @return true if the listener was successfully added
   1538      *
   1539      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
   1540      */
   1541     public boolean addNmeaListener(GpsStatus.NmeaListener listener) {
   1542         boolean result;
   1543 
   1544         if (mNmeaListeners.get(listener) != null) {
   1545             // listener is already registered
   1546             return true;
   1547         }
   1548         try {
   1549             GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
   1550             result = mService.addGpsStatusListener(transport, mContext.getPackageName());
   1551             if (result) {
   1552                 mNmeaListeners.put(listener, transport);
   1553             }
   1554         } catch (RemoteException e) {
   1555             Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
   1556             result = false;
   1557         }
   1558 
   1559         return result;
   1560     }
   1561 
   1562     /**
   1563      * Removes an NMEA listener.
   1564      *
   1565      * @param listener a {@link GpsStatus.NmeaListener} object to remove
   1566      */
   1567     public void removeNmeaListener(GpsStatus.NmeaListener listener) {
   1568         try {
   1569             GpsStatusListenerTransport transport = mNmeaListeners.remove(listener);
   1570             if (transport != null) {
   1571                 mService.removeGpsStatusListener(transport);
   1572             }
   1573         } catch (RemoteException e) {
   1574             Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
   1575         }
   1576     }
   1577 
   1578     /**
   1579      * Adds a GPS Measurement listener.
   1580      *
   1581      * @param listener a {@link GpsMeasurementsEvent.Listener} object to register.
   1582      * @return {@code true} if the listener was successfully registered, {@code false} otherwise.
   1583      *
   1584      * @hide
   1585      */
   1586     public boolean addGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {
   1587         return mGpsMeasurementListenerTransport.add(listener);
   1588     }
   1589 
   1590     /**
   1591      * Removes a GPS Measurement listener.
   1592      *
   1593      * @param listener a {@link GpsMeasurementsEvent.Listener} object to remove.
   1594      *
   1595      * @hide
   1596      */
   1597     public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {
   1598         mGpsMeasurementListenerTransport.remove(listener);
   1599     }
   1600 
   1601     /**
   1602      * Adds a GPS Navigation Message listener.
   1603      *
   1604      * @param listener a {@link GpsNavigationMessageEvent.Listener} object to register.
   1605      * @return {@code true} if the listener was successfully registered, {@code false} otherwise.
   1606      *
   1607      * @hide
   1608      */
   1609     public boolean addGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {
   1610         return mGpsNavigationMessageListenerTransport.add(listener);
   1611     }
   1612 
   1613     /**
   1614      * Removes a GPS Navigation Message listener.
   1615      *
   1616      * @param listener a {@link GpsNavigationMessageEvent.Listener} object to remove.
   1617      *
   1618      * @hide
   1619      */
   1620     public void removeGpsNavigationMessageListener(
   1621             GpsNavigationMessageEvent.Listener listener) {
   1622         mGpsNavigationMessageListenerTransport.remove(listener);
   1623     }
   1624 
   1625      /**
   1626      * Retrieves information about the current status of the GPS engine.
   1627      * This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged}
   1628      * callback to ensure that the data is copied atomically.
   1629      *
   1630      * The caller may either pass in a {@link GpsStatus} object to set with the latest
   1631      * status information, or pass null to create a new {@link GpsStatus} object.
   1632      *
   1633      * @param status object containing GPS status details, or null.
   1634      * @return status object containing updated GPS status.
   1635      */
   1636     public GpsStatus getGpsStatus(GpsStatus status) {
   1637         if (status == null) {
   1638             status = new GpsStatus();
   1639         }
   1640         status.setStatus(mGpsStatus);
   1641         return status;
   1642     }
   1643 
   1644     /**
   1645      * Sends additional commands to a location provider.
   1646      * Can be used to support provider specific extensions to the Location Manager API
   1647      *
   1648      * @param provider name of the location provider.
   1649      * @param command name of the command to send to the provider.
   1650      * @param extras optional arguments for the command (or null).
   1651      * The provider may optionally fill the extras Bundle with results from the command.
   1652      *
   1653      * @return true if the command succeeds.
   1654      */
   1655     public boolean sendExtraCommand(String provider, String command, Bundle extras) {
   1656         try {
   1657             return mService.sendExtraCommand(provider, command, extras);
   1658         } catch (RemoteException e) {
   1659             Log.e(TAG, "RemoteException in sendExtraCommand: ", e);
   1660             return false;
   1661         }
   1662     }
   1663 
   1664     /**
   1665      * Used by NetInitiatedActivity to report user response
   1666      * for network initiated GPS fix requests.
   1667      *
   1668      * @hide
   1669      */
   1670     public boolean sendNiResponse(int notifId, int userResponse) {
   1671         try {
   1672             return mService.sendNiResponse(notifId, userResponse);
   1673         } catch (RemoteException e) {
   1674             Log.e(TAG, "RemoteException in sendNiResponse: ", e);
   1675             return false;
   1676         }
   1677     }
   1678 
   1679     private static void checkProvider(String provider) {
   1680         if (provider == null) {
   1681             throw new IllegalArgumentException("invalid provider: " + provider);
   1682         }
   1683     }
   1684 
   1685     private static void checkCriteria(Criteria criteria) {
   1686         if (criteria == null) {
   1687             throw new IllegalArgumentException("invalid criteria: " + criteria);
   1688         }
   1689     }
   1690 
   1691     private static void checkListener(LocationListener listener) {
   1692         if (listener == null) {
   1693             throw new IllegalArgumentException("invalid listener: " + listener);
   1694         }
   1695     }
   1696 
   1697     private void checkPendingIntent(PendingIntent intent) {
   1698         if (intent == null) {
   1699             throw new IllegalArgumentException("invalid pending intent: " + intent);
   1700         }
   1701         if (!intent.isTargetedToPackage()) {
   1702             IllegalArgumentException e = new IllegalArgumentException(
   1703                     "pending intent must be targeted to package");
   1704             if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) {
   1705                 throw e;
   1706             } else {
   1707                 Log.w(TAG, e);
   1708             }
   1709         }
   1710     }
   1711 
   1712     private static void checkGeofence(Geofence fence) {
   1713         if (fence == null) {
   1714             throw new IllegalArgumentException("invalid geofence: " + fence);
   1715         }
   1716     }
   1717 }
   1718