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