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.RequiresPermission;
     22 import android.annotation.SystemApi;
     23 import android.annotation.TestApi;
     24 import android.app.PendingIntent;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.os.Build;
     28 import android.os.Bundle;
     29 import android.os.Handler;
     30 import android.os.Looper;
     31 import android.os.Message;
     32 import android.os.RemoteException;
     33 import android.util.Log;
     34 
     35 import java.util.ArrayList;
     36 import java.util.HashMap;
     37 import java.util.List;
     38 
     39 import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
     40 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
     41 
     42 /**
     43  * This class provides access to the system location services.  These
     44  * services allow applications to obtain periodic updates of the
     45  * device's geographical location, or to fire an application-specified
     46  * {@link Intent} when the device enters the proximity of a given
     47  * geographical location.
     48  *
     49  * <p>You do not
     50  * instantiate this class directly; instead, retrieve it through
     51  * {@link android.content.Context#getSystemService
     52  * Context.getSystemService(Context.LOCATION_SERVICE)}.
     53  *
     54  * <p class="note">Unless noted, all Location API methods require
     55  * the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or
     56  * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permissions.
     57  * If your application only has the coarse permission then it will not have
     58  * access to the GPS or passive location providers. Other providers will still
     59  * return location results, but the update rate will be throttled and the exact
     60  * location will be obfuscated to a coarse level of accuracy.
     61  */
     62 public class LocationManager {
     63     private static final String TAG = "LocationManager";
     64 
     65     private final Context mContext;
     66     private final ILocationManager mService;
     67     private final GnssMeasurementCallbackTransport mGnssMeasurementCallbackTransport;
     68     private final GnssNavigationMessageCallbackTransport mGnssNavigationMessageCallbackTransport;
     69     private final HashMap<GpsStatus.Listener, GnssStatusListenerTransport> mGpsStatusListeners =
     70             new HashMap<>();
     71     private final HashMap<GpsStatus.NmeaListener, GnssStatusListenerTransport> mGpsNmeaListeners =
     72             new HashMap<>();
     73     private final HashMap<GnssStatusCallback, GnssStatusListenerTransport>
     74             mOldGnssStatusListeners = new HashMap<>();
     75     private final HashMap<GnssStatus.Callback, GnssStatusListenerTransport> mGnssStatusListeners =
     76             new HashMap<>();
     77     private final HashMap<GnssNmeaListener, GnssStatusListenerTransport> mOldGnssNmeaListeners =
     78             new HashMap<>();
     79     private final HashMap<OnNmeaMessageListener, GnssStatusListenerTransport> mGnssNmeaListeners =
     80             new HashMap<>();
     81     private final HashMap<GnssNavigationMessageEvent.Callback, GnssNavigationMessage.Callback>
     82             mNavigationMessageBridge = new HashMap<>();
     83     private GnssStatus mGnssStatus;
     84     private int mTimeToFirstFix;
     85 
     86     /**
     87      * Name of the network location provider.
     88      * <p>This provider determines location based on
     89      * availability of cell tower and WiFi access points. Results are retrieved
     90      * by means of a network lookup.
     91      */
     92     public static final String NETWORK_PROVIDER = "network";
     93 
     94     /**
     95      * Name of the GPS location provider.
     96      *
     97      * <p>This provider determines location using
     98      * satellites. Depending on conditions, this provider may take a while to return
     99      * a location fix. Requires the permission
    100      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
    101      *
    102      * <p> The extras Bundle for the GPS location provider can contain the
    103      * following key/value pairs:
    104      * <ul>
    105      * <li> satellites - the number of satellites used to derive the fix
    106      * </ul>
    107      */
    108     public static final String GPS_PROVIDER = "gps";
    109 
    110     /**
    111      * A special location provider for receiving locations without actually initiating
    112      * a location fix.
    113      *
    114      * <p>This provider can be used to passively receive location updates
    115      * when other applications or services request them without actually requesting
    116      * the locations yourself.  This provider will return locations generated by other
    117      * providers.  You can query the {@link Location#getProvider()} method to determine
    118      * the origin of the location update. Requires the permission
    119      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}, although if the GPS is
    120      * not enabled this provider might only return coarse fixes.
    121      */
    122     public static final String PASSIVE_PROVIDER = "passive";
    123 
    124     /**
    125      * Name of the Fused location provider.
    126      *
    127      * <p>This provider combines inputs for all possible location sources
    128      * to provide the best possible Location fix. It is implicitly
    129      * used for all API's that involve the {@link LocationRequest}
    130      * object.
    131      *
    132      * @hide
    133      */
    134     public static final String FUSED_PROVIDER = "fused";
    135 
    136     /**
    137      * Key used for the Bundle extra holding a boolean indicating whether
    138      * a proximity alert is entering (true) or exiting (false)..
    139      */
    140     public static final String KEY_PROXIMITY_ENTERING = "entering";
    141 
    142     /**
    143      * Key used for a Bundle extra holding an Integer status value
    144      * when a status change is broadcast using a PendingIntent.
    145      */
    146     public static final String KEY_STATUS_CHANGED = "status";
    147 
    148     /**
    149      * Key used for a Bundle extra holding an Boolean status value
    150      * when a provider enabled/disabled event is broadcast using a PendingIntent.
    151      */
    152     public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
    153 
    154     /**
    155      * Key used for a Bundle extra holding a Location value
    156      * when a location change is broadcast using a PendingIntent.
    157      */
    158     public static final String KEY_LOCATION_CHANGED = "location";
    159 
    160     /**
    161      * Broadcast intent action indicating that the GPS has either been
    162      * enabled or disabled. An intent extra provides this state as a boolean,
    163      * where {@code true} means enabled.
    164      * @see #EXTRA_GPS_ENABLED
    165      *
    166      * @hide
    167      */
    168     public static final String GPS_ENABLED_CHANGE_ACTION =
    169         "android.location.GPS_ENABLED_CHANGE";
    170 
    171     /**
    172      * Broadcast intent action when the configured location providers
    173      * change. For use with {@link #isProviderEnabled(String)}. If you're interacting with the
    174      * {@link android.provider.Settings.Secure#LOCATION_MODE} API, use {@link #MODE_CHANGED_ACTION}
    175      * instead.
    176      */
    177     public static final String PROVIDERS_CHANGED_ACTION =
    178         "android.location.PROVIDERS_CHANGED";
    179 
    180     /**
    181      * Broadcast intent action when {@link android.provider.Settings.Secure#LOCATION_MODE} changes.
    182      * For use with the {@link android.provider.Settings.Secure#LOCATION_MODE} API.
    183      * If you're interacting with {@link #isProviderEnabled(String)}, use
    184      * {@link #PROVIDERS_CHANGED_ACTION} instead.
    185      *
    186      * In the future, there may be mode changes that do not result in
    187      * {@link #PROVIDERS_CHANGED_ACTION} broadcasts.
    188      */
    189     public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
    190 
    191     /**
    192      * Broadcast intent action indicating that the GPS has either started or
    193      * stopped receiving GPS fixes. An intent extra provides this state as a
    194      * boolean, where {@code true} means that the GPS is actively receiving fixes.
    195      * @see #EXTRA_GPS_ENABLED
    196      *
    197      * @hide
    198      */
    199     public static final String GPS_FIX_CHANGE_ACTION =
    200         "android.location.GPS_FIX_CHANGE";
    201 
    202     /**
    203      * The lookup key for a boolean that indicates whether GPS is enabled or
    204      * disabled. {@code true} means GPS is enabled. Retrieve it with
    205      * {@link android.content.Intent#getBooleanExtra(String,boolean)}.
    206      *
    207      * @hide
    208      */
    209     public static final String EXTRA_GPS_ENABLED = "enabled";
    210 
    211     /**
    212      * Broadcast intent action indicating that a high power location requests
    213      * has either started or stopped being active.  The current state of
    214      * active location requests should be read from AppOpsManager using
    215      * {@code OP_MONITOR_HIGH_POWER_LOCATION}.
    216      *
    217      * @hide
    218      */
    219     public static final String HIGH_POWER_REQUEST_CHANGE_ACTION =
    220         "android.location.HIGH_POWER_REQUEST_CHANGE";
    221 
    222     // Map from LocationListeners to their associated ListenerTransport objects
    223     private HashMap<LocationListener,ListenerTransport> mListeners =
    224         new HashMap<LocationListener,ListenerTransport>();
    225 
    226     private class ListenerTransport extends ILocationListener.Stub {
    227         private static final int TYPE_LOCATION_CHANGED = 1;
    228         private static final int TYPE_STATUS_CHANGED = 2;
    229         private static final int TYPE_PROVIDER_ENABLED = 3;
    230         private static final int TYPE_PROVIDER_DISABLED = 4;
    231 
    232         private LocationListener mListener;
    233         private final Handler mListenerHandler;
    234 
    235         ListenerTransport(LocationListener listener, Looper looper) {
    236             mListener = listener;
    237 
    238             if (looper == null) {
    239                 mListenerHandler = new Handler() {
    240                     @Override
    241                     public void handleMessage(Message msg) {
    242                         _handleMessage(msg);
    243                     }
    244                 };
    245             } else {
    246                 mListenerHandler = new Handler(looper) {
    247                     @Override
    248                     public void handleMessage(Message msg) {
    249                         _handleMessage(msg);
    250                     }
    251                 };
    252             }
    253         }
    254 
    255         @Override
    256         public void onLocationChanged(Location location) {
    257             Message msg = Message.obtain();
    258             msg.what = TYPE_LOCATION_CHANGED;
    259             msg.obj = location;
    260             mListenerHandler.sendMessage(msg);
    261         }
    262 
    263         @Override
    264         public void onStatusChanged(String provider, int status, Bundle extras) {
    265             Message msg = Message.obtain();
    266             msg.what = TYPE_STATUS_CHANGED;
    267             Bundle b = new Bundle();
    268             b.putString("provider", provider);
    269             b.putInt("status", status);
    270             if (extras != null) {
    271                 b.putBundle("extras", extras);
    272             }
    273             msg.obj = b;
    274             mListenerHandler.sendMessage(msg);
    275         }
    276 
    277         @Override
    278         public void onProviderEnabled(String provider) {
    279             Message msg = Message.obtain();
    280             msg.what = TYPE_PROVIDER_ENABLED;
    281             msg.obj = provider;
    282             mListenerHandler.sendMessage(msg);
    283         }
    284 
    285         @Override
    286         public void onProviderDisabled(String provider) {
    287             Message msg = Message.obtain();
    288             msg.what = TYPE_PROVIDER_DISABLED;
    289             msg.obj = provider;
    290             mListenerHandler.sendMessage(msg);
    291         }
    292 
    293         private void _handleMessage(Message msg) {
    294             switch (msg.what) {
    295                 case TYPE_LOCATION_CHANGED:
    296                     Location location = new Location((Location) msg.obj);
    297                     mListener.onLocationChanged(location);
    298                     break;
    299                 case TYPE_STATUS_CHANGED:
    300                     Bundle b = (Bundle) msg.obj;
    301                     String provider = b.getString("provider");
    302                     int status = b.getInt("status");
    303                     Bundle extras = b.getBundle("extras");
    304                     mListener.onStatusChanged(provider, status, extras);
    305                     break;
    306                 case TYPE_PROVIDER_ENABLED:
    307                     mListener.onProviderEnabled((String) msg.obj);
    308                     break;
    309                 case TYPE_PROVIDER_DISABLED:
    310                     mListener.onProviderDisabled((String) msg.obj);
    311                     break;
    312             }
    313             try {
    314                 mService.locationCallbackFinished(this);
    315             } catch (RemoteException e) {
    316                 throw e.rethrowFromSystemServer();
    317             }
    318         }
    319     }
    320 
    321     /**
    322      * @hide - hide this constructor because it has a parameter
    323      * of type ILocationManager, which is a system private class. The
    324      * right way to create an instance of this class is using the
    325      * factory Context.getSystemService.
    326      */
    327     public LocationManager(Context context, ILocationManager service) {
    328         mService = service;
    329         mContext = context;
    330         mGnssMeasurementCallbackTransport = new GnssMeasurementCallbackTransport(mContext, mService);
    331         mGnssNavigationMessageCallbackTransport =
    332                 new GnssNavigationMessageCallbackTransport(mContext, mService);
    333     }
    334 
    335     private LocationProvider createProvider(String name, ProviderProperties properties) {
    336         return new LocationProvider(name, properties);
    337     }
    338 
    339     /**
    340      * Returns a list of the names of all known location providers.
    341      * <p>All providers are returned, including ones that are not permitted to
    342      * be accessed by the calling activity or are currently disabled.
    343      *
    344      * @return list of Strings containing names of the provider
    345      */
    346     public List<String> getAllProviders() {
    347         try {
    348             return mService.getAllProviders();
    349         } catch (RemoteException e) {
    350             throw e.rethrowFromSystemServer();
    351         }
    352     }
    353 
    354     /**
    355      * Returns a list of the names of location providers.
    356      *
    357      * @param enabledOnly if true then only the providers which are currently
    358      * enabled are returned.
    359      * @return list of Strings containing names of the providers
    360      */
    361     public List<String> getProviders(boolean enabledOnly) {
    362         try {
    363             return mService.getProviders(null, enabledOnly);
    364         } catch (RemoteException e) {
    365             throw e.rethrowFromSystemServer();
    366         }
    367     }
    368 
    369     /**
    370      * Returns the information associated with the location provider of the
    371      * given name, or null if no provider exists by that name.
    372      *
    373      * @param name the provider name
    374      * @return a LocationProvider, or null
    375      *
    376      * @throws IllegalArgumentException if name is null or does not exist
    377      * @throws SecurityException if the caller is not permitted to access the
    378      * given provider.
    379      */
    380     public LocationProvider getProvider(String name) {
    381         checkProvider(name);
    382         try {
    383             ProviderProperties properties = mService.getProviderProperties(name);
    384             if (properties == null) {
    385                 return null;
    386             }
    387             return createProvider(name, properties);
    388         } catch (RemoteException e) {
    389             throw e.rethrowFromSystemServer();
    390         }
    391     }
    392 
    393     /**
    394      * Returns a list of the names of LocationProviders that satisfy the given
    395      * criteria, or null if none do.  Only providers that are permitted to be
    396      * accessed by the calling activity will be returned.
    397      *
    398      * @param criteria the criteria that the returned providers must match
    399      * @param enabledOnly if true then only the providers which are currently
    400      * enabled are returned.
    401      * @return list of Strings containing names of the providers
    402      */
    403     public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
    404         checkCriteria(criteria);
    405         try {
    406             return mService.getProviders(criteria, enabledOnly);
    407         } catch (RemoteException e) {
    408             throw e.rethrowFromSystemServer();
    409         }
    410     }
    411 
    412     /**
    413      * Returns the name of the provider that best meets the given criteria. Only providers
    414      * that are permitted to be accessed by the calling activity will be
    415      * returned.  If several providers meet the criteria, the one with the best
    416      * accuracy is returned.  If no provider meets the criteria,
    417      * the criteria are loosened in the following sequence:
    418      *
    419      * <ul>
    420      * <li> power requirement
    421      * <li> accuracy
    422      * <li> bearing
    423      * <li> speed
    424      * <li> altitude
    425      * </ul>
    426      *
    427      * <p> Note that the requirement on monetary cost is not removed
    428      * in this process.
    429      *
    430      * @param criteria the criteria that need to be matched
    431      * @param enabledOnly if true then only a provider that is currently enabled is returned
    432      * @return name of the provider that best matches the requirements
    433      */
    434     public String getBestProvider(Criteria criteria, boolean enabledOnly) {
    435         checkCriteria(criteria);
    436         try {
    437             return mService.getBestProvider(criteria, enabledOnly);
    438         } catch (RemoteException e) {
    439             throw e.rethrowFromSystemServer();
    440         }
    441     }
    442 
    443     /**
    444      * Register for location updates using the named provider, and a
    445      * pending intent.
    446      *
    447      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    448      * for more detail on how to use this method.
    449      *
    450      * @param provider the name of the provider with which to register
    451      * @param minTime minimum time interval between location updates, in milliseconds
    452      * @param minDistance minimum distance between location updates, in meters
    453      * @param listener a {@link LocationListener} whose
    454      * {@link LocationListener#onLocationChanged} method will be called for
    455      * each location update
    456      *
    457      * @throws IllegalArgumentException if provider is null or doesn't exist
    458      * on this device
    459      * @throws IllegalArgumentException if listener is null
    460      * @throws RuntimeException if the calling thread has no Looper
    461      * @throws SecurityException if no suitable permission is present
    462      */
    463     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
    464     public void requestLocationUpdates(String provider, long minTime, float minDistance,
    465             LocationListener listener) {
    466         checkProvider(provider);
    467         checkListener(listener);
    468 
    469         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
    470                 provider, minTime, minDistance, false);
    471         requestLocationUpdates(request, listener, null, null);
    472     }
    473 
    474     /**
    475      * Register for location updates using the named provider, and a callback on
    476      * the specified looper thread.
    477      *
    478      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    479      * for more detail on how to use this method.
    480      *
    481      * @param provider the name of the provider with which to register
    482      * @param minTime minimum time interval between location updates, in milliseconds
    483      * @param minDistance minimum distance between location updates, in meters
    484      * @param listener a {@link LocationListener} whose
    485      * {@link LocationListener#onLocationChanged} method will be called for
    486      * each location update
    487      * @param looper a Looper object whose message queue will be used to
    488      * implement the callback mechanism, or null to make callbacks on the calling
    489      * thread
    490      *
    491      * @throws IllegalArgumentException if provider is null or doesn't exist
    492      * @throws IllegalArgumentException if listener is null
    493      * @throws SecurityException if no suitable permission is present
    494      */
    495     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
    496     public void requestLocationUpdates(String provider, long minTime, float minDistance,
    497             LocationListener listener, Looper looper) {
    498         checkProvider(provider);
    499         checkListener(listener);
    500 
    501         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
    502                 provider, minTime, minDistance, false);
    503         requestLocationUpdates(request, listener, looper, null);
    504     }
    505 
    506     /**
    507      * Register for location updates using a Criteria, and a callback
    508      * on the specified looper thread.
    509      *
    510      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    511      * for more detail on how to use this method.
    512      *
    513      * @param minTime minimum time interval between location updates, in milliseconds
    514      * @param minDistance minimum distance between location updates, in meters
    515      * @param criteria contains parameters for the location manager to choose the
    516      * appropriate provider and parameters to compute the location
    517      * @param listener a {@link LocationListener} whose
    518      * {@link LocationListener#onLocationChanged} method will be called for
    519      * each location update
    520      * @param looper a Looper object whose message queue will be used to
    521      * implement the callback mechanism, or null to make callbacks on the calling
    522      * thread
    523      *
    524      * @throws IllegalArgumentException if criteria is null
    525      * @throws IllegalArgumentException if listener is null
    526      * @throws SecurityException if no suitable permission is present
    527      */
    528     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
    529     public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
    530             LocationListener listener, Looper looper) {
    531         checkCriteria(criteria);
    532         checkListener(listener);
    533 
    534         LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
    535                 criteria, minTime, minDistance, false);
    536         requestLocationUpdates(request, listener, looper, null);
    537     }
    538 
    539     /**
    540      * Register for location updates using the named provider, and a
    541      * pending intent.
    542      *
    543      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    544      * for more detail on how to use this method.
    545      *
    546      * @param provider the name of the provider with which to register
    547      * @param minTime minimum time interval between location updates, in milliseconds
    548      * @param minDistance minimum distance between location updates, in meters
    549      * @param intent a {@link PendingIntent} to be sent for each location update
    550      *
    551      * @throws IllegalArgumentException if provider is null or doesn't exist
    552      * on this device
    553      * @throws IllegalArgumentException if intent is null
    554      * @throws SecurityException if no suitable permission is present
    555      */
    556     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
    557     public void requestLocationUpdates(String provider, long minTime, float minDistance,
    558             PendingIntent intent) {
    559         checkProvider(provider);
    560         checkPendingIntent(intent);
    561 
    562         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
    563                 provider, minTime, minDistance, false);
    564         requestLocationUpdates(request, null, null, intent);
    565     }
    566 
    567     /**
    568      * Register for location updates using a Criteria and pending intent.
    569      *
    570      * <p>The <code>requestLocationUpdates()</code> and
    571      * <code>requestSingleUpdate()</code> register the current activity to be
    572      * updated periodically by the named provider, or by the provider matching
    573      * the specified {@link Criteria}, with location and status updates.
    574      *
    575      * <p> It may take a while to receive the first location update. If
    576      * an immediate location is required, applications may use the
    577      * {@link #getLastKnownLocation(String)} method.
    578      *
    579      * <p> Location updates are received either by {@link LocationListener}
    580      * callbacks, or by broadcast intents to a supplied {@link PendingIntent}.
    581      *
    582      * <p> If the caller supplied a pending intent, then location updates
    583      * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
    584      * {@link android.location.Location} value.
    585      *
    586      * <p> The location update interval can be controlled using the minTime parameter.
    587      * The elapsed time between location updates will never be less than
    588      * minTime, although it can be more depending on the Location Provider
    589      * implementation and the update interval requested by other applications.
    590      *
    591      * <p> Choosing a sensible value for minTime is important to conserve
    592      * battery life. Each location update requires power from
    593      * GPS, WIFI, Cell and other radios. Select a minTime value as high as
    594      * possible while still providing a reasonable user experience.
    595      * If your application is not in the foreground and showing
    596      * location to the user then your application should avoid using an active
    597      * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
    598      * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
    599      * or greater. If your application is in the foreground and showing
    600      * location to the user then it is appropriate to select a faster
    601      * update interval.
    602      *
    603      * <p> The minDistance parameter can also be used to control the
    604      * frequency of location updates. If it is greater than 0 then the
    605      * location provider will only send your application an update when
    606      * the location has changed by at least minDistance meters, AND
    607      * at least minTime milliseconds have passed. However it is more
    608      * difficult for location providers to save power using the minDistance
    609      * parameter, so minTime should be the primary tool to conserving battery
    610      * life.
    611      *
    612      * <p> If your application wants to passively observe location
    613      * updates triggered by other applications, but not consume
    614      * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
    615      * This provider does not actively turn on or modify active location
    616      * providers, so you do not need to be as careful about minTime and
    617      * minDistance. However if your application performs heavy work
    618      * on a location update (such as network activity) then you should
    619      * select non-zero values for minTime and/or minDistance to rate-limit
    620      * your update frequency in the case another application enables a
    621      * location provider with extremely fast updates.
    622      *
    623      * <p>In case the provider is disabled by the user, updates will stop,
    624      * and a provider availability update will be sent.
    625      * As soon as the provider is enabled again,
    626      * location updates will immediately resume and a provider availability
    627      * update sent. Providers can also send status updates, at any time,
    628      * with extra's specific to the provider. If a callback was supplied
    629      * then status and availability updates are via
    630      * {@link LocationListener#onProviderDisabled},
    631      * {@link LocationListener#onProviderEnabled} or
    632      * {@link LocationListener#onStatusChanged}. Alternately, if a
    633      * pending intent was supplied then status and availability updates
    634      * are broadcast intents with extra keys of
    635      * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED}.
    636      *
    637      * <p> If a {@link LocationListener} is used but with no Looper specified
    638      * then the calling thread must already
    639      * be a {@link android.os.Looper} thread such as the main thread of the
    640      * calling Activity. If a Looper is specified with a {@link LocationListener}
    641      * then callbacks are made on the supplied Looper thread.
    642      *
    643      * <p class="note"> Prior to Jellybean, the minTime parameter was
    644      * only a hint, and some location provider implementations ignored it.
    645      * From Jellybean and onwards it is mandatory for Android compatible
    646      * devices to observe both the minTime and minDistance parameters.
    647      *
    648      * @param minTime minimum time interval between location updates, in milliseconds
    649      * @param minDistance minimum distance between location updates, in meters
    650      * @param criteria contains parameters for the location manager to choose the
    651      * appropriate provider and parameters to compute the location
    652      * @param intent a {@link PendingIntent} to be sent for each location update
    653      *
    654      * @throws IllegalArgumentException if criteria is null
    655      * @throws IllegalArgumentException if intent is null
    656      * @throws SecurityException if no suitable permission is present
    657      */
    658     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
    659     public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
    660             PendingIntent intent) {
    661         checkCriteria(criteria);
    662         checkPendingIntent(intent);
    663 
    664         LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
    665                 criteria, minTime, minDistance, false);
    666         requestLocationUpdates(request, null, null, intent);
    667     }
    668 
    669     /**
    670      * Register for a single location update using the named provider and
    671      * a callback.
    672      *
    673      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    674      * for more detail on how to use this method.
    675      *
    676      * @param provider the name of the provider with which to register
    677      * @param listener a {@link LocationListener} whose
    678      * {@link LocationListener#onLocationChanged} method will be called when
    679      * the location update is available
    680      * @param looper a Looper object whose message queue will be used to
    681      * implement the callback mechanism, or null to make callbacks on the calling
    682      * thread
    683      *
    684      * @throws IllegalArgumentException if provider is null or doesn't exist
    685      * @throws IllegalArgumentException if listener is null
    686      * @throws SecurityException if no suitable permission is present
    687      */
    688     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
    689     public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) {
    690         checkProvider(provider);
    691         checkListener(listener);
    692 
    693         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
    694                 provider, 0, 0, true);
    695         requestLocationUpdates(request, listener, looper, null);
    696     }
    697 
    698     /**
    699      * Register for a single location update using a Criteria and
    700      * a callback.
    701      *
    702      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    703      * for more detail on how to use this method.
    704      *
    705      * @param criteria contains parameters for the location manager to choose the
    706      * appropriate provider and parameters to compute the location
    707      * @param listener a {@link LocationListener} whose
    708      * {@link LocationListener#onLocationChanged} method will be called when
    709      * the location update is available
    710      * @param looper a Looper object whose message queue will be used to
    711      * implement the callback mechanism, or null to make callbacks on the calling
    712      * thread
    713      *
    714      * @throws IllegalArgumentException if criteria is null
    715      * @throws IllegalArgumentException if listener is null
    716      * @throws SecurityException if no suitable permission is present
    717      */
    718     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
    719     public void requestSingleUpdate(Criteria criteria, LocationListener listener, Looper looper) {
    720         checkCriteria(criteria);
    721         checkListener(listener);
    722 
    723         LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
    724                 criteria, 0, 0, true);
    725         requestLocationUpdates(request, listener, looper, null);
    726     }
    727 
    728     /**
    729      * Register for a single location update using a named provider and pending intent.
    730      *
    731      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    732      * for more detail on how to use this method.
    733      *
    734      * @param provider the name of the provider with which to register
    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     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
    742     public void requestSingleUpdate(String provider, PendingIntent intent) {
    743         checkProvider(provider);
    744         checkPendingIntent(intent);
    745 
    746         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
    747                 provider, 0, 0, true);
    748         requestLocationUpdates(request, null, null, intent);
    749     }
    750 
    751     /**
    752      * Register for a single location update using a Criteria and pending intent.
    753      *
    754      * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
    755      * for more detail on how to use this method.
    756      *
    757      * @param criteria contains parameters for the location manager to choose the
    758      * appropriate provider and parameters to compute the location
    759      * @param intent a {@link PendingIntent} to be sent for the location update
    760      *
    761      * @throws IllegalArgumentException if provider is null or doesn't exist
    762      * @throws IllegalArgumentException if intent is null
    763      * @throws SecurityException if no suitable permission is present
    764      */
    765     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
    766     public void requestSingleUpdate(Criteria criteria, PendingIntent intent) {
    767         checkCriteria(criteria);
    768         checkPendingIntent(intent);
    769 
    770         LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
    771                 criteria, 0, 0, true);
    772         requestLocationUpdates(request, null, null, intent);
    773     }
    774 
    775     /**
    776      * Register for fused location updates using a LocationRequest and callback.
    777      *
    778      * <p>Upon a location update, the system delivers the new {@link Location} to the
    779      * provided {@link LocationListener}, by calling its {@link
    780      * LocationListener#onLocationChanged} method.</p>
    781      *
    782      * <p>The system will automatically select and enable the best providers
    783      * to compute a location for your application. It may use only passive
    784      * locations, or just a single location source, or it may fuse together
    785      * multiple location sources in order to produce the best possible
    786      * result, depending on the quality of service requested in the
    787      * {@link LocationRequest}.
    788      *
    789      * <p>LocationRequest can be null, in which case the system will choose
    790      * default, low power parameters for location updates. You will occasionally
    791      * receive location updates as available, without a major power impact on the
    792      * system. If your application just needs an occasional location update
    793      * without any strict demands, then pass a null LocationRequest.
    794      *
    795      * <p>Only one LocationRequest can be registered for each unique callback
    796      * or pending intent. So a subsequent request with the same callback or
    797      * pending intent will over-write the previous LocationRequest.
    798      *
    799      * <p> If a pending intent is supplied then location updates
    800      * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
    801      * {@link android.location.Location} value. If a callback is supplied
    802      * then location updates are made using the
    803      * {@link LocationListener#onLocationChanged} callback, on the specified
    804      * Looper thread. If a {@link LocationListener} is used
    805      * but with a null Looper then the calling thread must already
    806      * be a {@link android.os.Looper} thread (such as the main thread) and
    807      * callbacks will occur on this thread.
    808      *
    809      * <p> Provider status updates and availability updates are deprecated
    810      * because the system is performing provider fusion on the applications
    811      * behalf. So {@link LocationListener#onProviderDisabled},
    812      * {@link LocationListener#onProviderEnabled}, {@link LocationListener#onStatusChanged}
    813      * will not be called, and intents with extra keys of
    814      * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED} will not
    815      * be received.
    816      *
    817      * <p> To unregister for Location updates, use: {@link #removeUpdates(LocationListener)}.
    818      *
    819      * @param request quality of service required, null for default low power
    820      * @param listener a {@link LocationListener} whose
    821      * {@link LocationListener#onLocationChanged} method will be called when
    822      * the location update is available
    823      * @param looper a Looper object whose message queue will be used to
    824      * implement the callback mechanism, or null to make callbacks on the calling
    825      * thread
    826      *
    827      * @throws IllegalArgumentException if listener is null
    828      * @throws SecurityException if no suitable permission is present
    829      *
    830      * @hide
    831      */
    832     @SystemApi
    833     public void requestLocationUpdates(LocationRequest request, LocationListener listener,
    834             Looper looper) {
    835         checkListener(listener);
    836         requestLocationUpdates(request, listener, looper, null);
    837     }
    838 
    839 
    840     /**
    841      * Register for fused location updates using a LocationRequest and a pending intent.
    842      *
    843      * <p>Upon a location update, the system delivers the new {@link Location} with your provided
    844      * {@link PendingIntent}, as the value for {@link LocationManager#KEY_LOCATION_CHANGED}
    845      * in the intent's extras.</p>
    846      *
    847      * <p> To unregister for Location updates, use: {@link #removeUpdates(PendingIntent)}.
    848      *
    849      * <p> See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)}
    850      * for more detail.
    851      *
    852      * @param request quality of service required, null for default low power
    853      * @param intent a {@link PendingIntent} to be sent for the location update
    854      *
    855      * @throws IllegalArgumentException if intent is null
    856      * @throws SecurityException if no suitable permission is present
    857      *
    858      * @hide
    859      */
    860     @SystemApi
    861     public void requestLocationUpdates(LocationRequest request, PendingIntent intent) {
    862         checkPendingIntent(intent);
    863         requestLocationUpdates(request, null, null, intent);
    864     }
    865 
    866     private ListenerTransport wrapListener(LocationListener listener, Looper looper) {
    867         if (listener == null) return null;
    868         synchronized (mListeners) {
    869             ListenerTransport transport = mListeners.get(listener);
    870             if (transport == null) {
    871                 transport = new ListenerTransport(listener, looper);
    872             }
    873             mListeners.put(listener, transport);
    874             return transport;
    875         }
    876     }
    877 
    878     private void requestLocationUpdates(LocationRequest request, LocationListener listener,
    879             Looper looper, PendingIntent intent) {
    880 
    881         String packageName = mContext.getPackageName();
    882 
    883         // wrap the listener class
    884         ListenerTransport transport = wrapListener(listener, looper);
    885 
    886         try {
    887             mService.requestLocationUpdates(request, transport, intent, packageName);
    888        } catch (RemoteException e) {
    889            throw e.rethrowFromSystemServer();
    890        }
    891     }
    892 
    893     /**
    894      * Removes all location updates for the specified LocationListener.
    895      *
    896      * <p>Following this call, updates will no longer
    897      * occur for this listener.
    898      *
    899      * @param listener listener object that no longer needs location updates
    900      * @throws IllegalArgumentException if listener is null
    901      */
    902     public void removeUpdates(LocationListener listener) {
    903         checkListener(listener);
    904         String packageName = mContext.getPackageName();
    905 
    906         ListenerTransport transport;
    907         synchronized (mListeners) {
    908             transport = mListeners.remove(listener);
    909         }
    910         if (transport == null) return;
    911 
    912         try {
    913             mService.removeUpdates(transport, null, packageName);
    914         } catch (RemoteException e) {
    915             throw e.rethrowFromSystemServer();
    916         }
    917     }
    918 
    919     /**
    920      * Removes all location updates for the specified pending intent.
    921      *
    922      * <p>Following this call, updates will no longer for this pending intent.
    923      *
    924      * @param intent pending intent object that no longer needs location updates
    925      * @throws IllegalArgumentException if intent is null
    926      */
    927     public void removeUpdates(PendingIntent intent) {
    928         checkPendingIntent(intent);
    929         String packageName = mContext.getPackageName();
    930 
    931         try {
    932             mService.removeUpdates(null, intent, packageName);
    933         } catch (RemoteException e) {
    934             throw e.rethrowFromSystemServer();
    935         }
    936     }
    937 
    938     /**
    939      * Set a proximity alert for the location given by the position
    940      * (latitude, longitude) and the given radius.
    941      *
    942      * <p> When the device
    943      * detects that it has entered or exited the area surrounding the
    944      * location, the given PendingIntent will be used to create an Intent
    945      * to be fired.
    946      *
    947      * <p> The fired Intent will have a boolean extra added with key
    948      * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
    949      * entering the proximity region; if false, it is exiting.
    950      *
    951      * <p> Due to the approximate nature of position estimation, if the
    952      * device passes through the given area briefly, it is possible
    953      * that no Intent will be fired.  Similarly, an Intent could be
    954      * fired if the device passes very close to the given area but
    955      * does not actually enter it.
    956      *
    957      * <p> After the number of milliseconds given by the expiration
    958      * parameter, the location manager will delete this proximity
    959      * alert and no longer monitor it.  A value of -1 indicates that
    960      * there should be no expiration time.
    961      *
    962      * <p> Internally, this method uses both {@link #NETWORK_PROVIDER}
    963      * and {@link #GPS_PROVIDER}.
    964      *
    965      * <p>Before API version 17, this method could be used with
    966      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
    967      * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
    968      * From API version 17 and onwards, this method requires
    969      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
    970      *
    971      * @param latitude the latitude of the central point of the
    972      * alert region
    973      * @param longitude the longitude of the central point of the
    974      * alert region
    975      * @param radius the radius of the central point of the
    976      * alert region, in meters
    977      * @param expiration time for this proximity alert, in milliseconds,
    978      * or -1 to indicate no expiration
    979      * @param intent a PendingIntent that will be used to generate an Intent to
    980      * fire when entry to or exit from the alert region is detected
    981      *
    982      * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
    983      * permission is not present
    984      */
    985     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
    986     public void addProximityAlert(double latitude, double longitude, float radius, long expiration,
    987             PendingIntent intent) {
    988         checkPendingIntent(intent);
    989         if (expiration < 0) expiration = Long.MAX_VALUE;
    990 
    991         Geofence fence = Geofence.createCircle(latitude, longitude, radius);
    992         LocationRequest request = new LocationRequest().setExpireIn(expiration);
    993         try {
    994             mService.requestGeofence(request, fence, intent, mContext.getPackageName());
    995         } catch (RemoteException e) {
    996             throw e.rethrowFromSystemServer();
    997         }
    998     }
    999 
   1000     /**
   1001      * Add a geofence with the specified LocationRequest quality of service.
   1002      *
   1003      * <p> When the device
   1004      * detects that it has entered or exited the area surrounding the
   1005      * location, the given PendingIntent will be used to create an Intent
   1006      * to be fired.
   1007      *
   1008      * <p> The fired Intent will have a boolean extra added with key
   1009      * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
   1010      * entering the proximity region; if false, it is exiting.
   1011      *
   1012      * <p> The geofence engine fuses results from all location providers to
   1013      * provide the best balance between accuracy and power. Applications
   1014      * can choose the quality of service required using the
   1015      * {@link LocationRequest} object. If it is null then a default,
   1016      * low power geo-fencing implementation is used. It is possible to cross
   1017      * a geo-fence without notification, but the system will do its best
   1018      * to detect, using {@link LocationRequest} as a hint to trade-off
   1019      * accuracy and power.
   1020      *
   1021      * <p> The power required by the geofence engine can depend on many factors,
   1022      * such as quality and interval requested in {@link LocationRequest},
   1023      * distance to nearest geofence and current device velocity.
   1024      *
   1025      * @param request quality of service required, null for default low power
   1026      * @param fence a geographical description of the geofence area
   1027      * @param intent pending intent to receive geofence updates
   1028      *
   1029      * @throws IllegalArgumentException if fence is null
   1030      * @throws IllegalArgumentException if intent is null
   1031      * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
   1032      * permission is not present
   1033      *
   1034      * @hide
   1035      */
   1036     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
   1037     public void addGeofence(LocationRequest request, Geofence fence, PendingIntent intent) {
   1038         checkPendingIntent(intent);
   1039         checkGeofence(fence);
   1040 
   1041         try {
   1042             mService.requestGeofence(request, fence, intent, mContext.getPackageName());
   1043         } catch (RemoteException e) {
   1044             throw e.rethrowFromSystemServer();
   1045         }
   1046     }
   1047 
   1048     /**
   1049      * Removes the proximity alert with the given PendingIntent.
   1050      *
   1051      * <p>Before API version 17, this method could be used with
   1052      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
   1053      * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
   1054      * From API version 17 and onwards, this method requires
   1055      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
   1056      *
   1057      * @param intent the PendingIntent that no longer needs to be notified of
   1058      * proximity alerts
   1059      *
   1060      * @throws IllegalArgumentException if intent is null
   1061      * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
   1062      * permission is not present
   1063      */
   1064     public void removeProximityAlert(PendingIntent intent) {
   1065         checkPendingIntent(intent);
   1066         String packageName = mContext.getPackageName();
   1067 
   1068         try {
   1069             mService.removeGeofence(null, intent, packageName);
   1070         } catch (RemoteException e) {
   1071             throw e.rethrowFromSystemServer();
   1072         }
   1073     }
   1074 
   1075     /**
   1076      * Remove a single geofence.
   1077      *
   1078      * <p>This removes only the specified geofence associated with the
   1079      * specified pending intent. All other geofences remain unchanged.
   1080      *
   1081      * @param fence a geofence previously passed to {@link #addGeofence}
   1082      * @param intent a pending intent previously passed to {@link #addGeofence}
   1083      *
   1084      * @throws IllegalArgumentException if fence is null
   1085      * @throws IllegalArgumentException if intent is null
   1086      * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
   1087      * permission is not present
   1088      *
   1089      * @hide
   1090      */
   1091     public void removeGeofence(Geofence fence, PendingIntent intent) {
   1092         checkPendingIntent(intent);
   1093         checkGeofence(fence);
   1094         String packageName = mContext.getPackageName();
   1095 
   1096         try {
   1097             mService.removeGeofence(fence, intent, packageName);
   1098         } catch (RemoteException e) {
   1099             throw e.rethrowFromSystemServer();
   1100         }
   1101     }
   1102 
   1103     /**
   1104      * Remove all geofences registered to the specified pending intent.
   1105      *
   1106      * @param intent a pending intent previously passed to {@link #addGeofence}
   1107      *
   1108      * @throws IllegalArgumentException if intent is null
   1109      * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
   1110      * permission is not present
   1111      *
   1112      * @hide
   1113      */
   1114     public void removeAllGeofences(PendingIntent intent) {
   1115         checkPendingIntent(intent);
   1116         String packageName = mContext.getPackageName();
   1117 
   1118         try {
   1119             mService.removeGeofence(null, intent, packageName);
   1120         } catch (RemoteException e) {
   1121             throw e.rethrowFromSystemServer();
   1122         }
   1123     }
   1124 
   1125     /**
   1126      * Returns the current enabled/disabled status of the given provider.
   1127      *
   1128      * <p>If the user has enabled this provider in the Settings menu, true
   1129      * is returned otherwise false is returned
   1130      *
   1131      * <p>Callers should instead use
   1132      * {@link android.provider.Settings.Secure#LOCATION_MODE}
   1133      * unless they depend on provider-specific APIs such as
   1134      * {@link #requestLocationUpdates(String, long, float, LocationListener)}.
   1135      *
   1136      * <p>
   1137      * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this
   1138      * method would throw {@link SecurityException} if the location permissions
   1139      * were not sufficient to use the specified provider.
   1140      *
   1141      * @param provider the name of the provider
   1142      * @return true if the provider exists and is enabled
   1143      *
   1144      * @throws IllegalArgumentException if provider is null
   1145      */
   1146     public boolean isProviderEnabled(String provider) {
   1147         checkProvider(provider);
   1148 
   1149         try {
   1150             return mService.isProviderEnabled(provider);
   1151         } catch (RemoteException e) {
   1152             throw e.rethrowFromSystemServer();
   1153         }
   1154     }
   1155 
   1156     /**
   1157      * Get the last known location.
   1158      *
   1159      * <p>This location could be very old so use
   1160      * {@link Location#getElapsedRealtimeNanos} to calculate its age. It can
   1161      * also return null if no previous location is available.
   1162      *
   1163      * <p>Always returns immediately.
   1164      *
   1165      * @return The last known location, or null if not available
   1166      * @throws SecurityException if no suitable permission is present
   1167      *
   1168      * @hide
   1169      */
   1170     public Location getLastLocation() {
   1171         String packageName = mContext.getPackageName();
   1172 
   1173         try {
   1174             return mService.getLastLocation(null, packageName);
   1175         } catch (RemoteException e) {
   1176             throw e.rethrowFromSystemServer();
   1177         }
   1178     }
   1179 
   1180     /**
   1181      * Returns a Location indicating the data from the last known
   1182      * location fix obtained from the given provider.
   1183      *
   1184      * <p> This can be done
   1185      * without starting the provider.  Note that this location could
   1186      * be out-of-date, for example if the device was turned off and
   1187      * moved to another location.
   1188      *
   1189      * <p> If the provider is currently disabled, null is returned.
   1190      *
   1191      * @param provider the name of the provider
   1192      * @return the last known location for the provider, or null
   1193      *
   1194      * @throws SecurityException if no suitable permission is present
   1195      * @throws IllegalArgumentException if provider is null or doesn't exist
   1196      */
   1197     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
   1198     public Location getLastKnownLocation(String provider) {
   1199         checkProvider(provider);
   1200         String packageName = mContext.getPackageName();
   1201         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
   1202                 provider, 0, 0, true);
   1203 
   1204         try {
   1205             return mService.getLastLocation(request, packageName);
   1206         } catch (RemoteException e) {
   1207             throw e.rethrowFromSystemServer();
   1208         }
   1209     }
   1210 
   1211     // --- Mock provider support ---
   1212     // TODO: It would be fantastic to deprecate mock providers entirely, and replace
   1213     // with something closer to LocationProviderBase.java
   1214 
   1215     /**
   1216      * Creates a mock location provider and adds it to the set of active providers.
   1217      *
   1218      * @param name the provider name
   1219      *
   1220      * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
   1221      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
   1222      * allowed} for your app.
   1223      * @throws IllegalArgumentException if a provider with the given name already exists
   1224      */
   1225     public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
   1226             boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
   1227             boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
   1228         ProviderProperties properties = new ProviderProperties(requiresNetwork,
   1229                 requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
   1230                 supportsBearing, powerRequirement, accuracy);
   1231         if (name.matches(LocationProvider.BAD_CHARS_REGEX)) {
   1232             throw new IllegalArgumentException("provider name contains illegal character: " + name);
   1233         }
   1234 
   1235         try {
   1236             mService.addTestProvider(name, properties, mContext.getOpPackageName());
   1237         } catch (RemoteException e) {
   1238             throw e.rethrowFromSystemServer();
   1239         }
   1240     }
   1241 
   1242     /**
   1243      * Removes the mock location provider with the given name.
   1244      *
   1245      * @param provider the provider name
   1246      *
   1247      * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
   1248      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
   1249      * allowed} for your app.
   1250      * @throws IllegalArgumentException if no provider with the given name exists
   1251      */
   1252     public void removeTestProvider(String provider) {
   1253         try {
   1254             mService.removeTestProvider(provider, mContext.getOpPackageName());
   1255         } catch (RemoteException e) {
   1256             throw e.rethrowFromSystemServer();
   1257         }
   1258     }
   1259 
   1260     /**
   1261      * Sets a mock location for the given provider.
   1262      * <p>This location will be used in place of any actual location from the provider.
   1263      * The location object must have a minimum number of fields set to be
   1264      * considered a valid LocationProvider Location, as per documentation
   1265      * on {@link Location} class.
   1266      *
   1267      * @param provider the provider name
   1268      * @param loc the mock location
   1269      *
   1270      * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
   1271      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
   1272      * allowed} for your app.
   1273      * @throws IllegalArgumentException if no provider with the given name exists
   1274      * @throws IllegalArgumentException if the location is incomplete
   1275      */
   1276     public void setTestProviderLocation(String provider, Location loc) {
   1277         if (!loc.isComplete()) {
   1278             IllegalArgumentException e = new IllegalArgumentException(
   1279                     "Incomplete location object, missing timestamp or accuracy? " + loc);
   1280             if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) {
   1281                 // just log on old platform (for backwards compatibility)
   1282                 Log.w(TAG, e);
   1283                 loc.makeComplete();
   1284             } else {
   1285                 // really throw it!
   1286                 throw e;
   1287             }
   1288         }
   1289 
   1290         try {
   1291             mService.setTestProviderLocation(provider, loc, mContext.getOpPackageName());
   1292         } catch (RemoteException e) {
   1293             throw e.rethrowFromSystemServer();
   1294         }
   1295     }
   1296 
   1297     /**
   1298      * Removes any mock location associated with the given provider.
   1299      *
   1300      * @param provider the provider name
   1301      *
   1302      * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
   1303      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
   1304      * allowed} for your app.
   1305      * @throws IllegalArgumentException if no provider with the given name exists
   1306      */
   1307     public void clearTestProviderLocation(String provider) {
   1308         try {
   1309             mService.clearTestProviderLocation(provider, mContext.getOpPackageName());
   1310         } catch (RemoteException e) {
   1311             throw e.rethrowFromSystemServer();
   1312         }
   1313     }
   1314 
   1315     /**
   1316      * Sets a mock enabled value for the given provider.  This value will be used in place
   1317      * of any actual value from the provider.
   1318      *
   1319      * @param provider the provider name
   1320      * @param enabled the mock enabled value
   1321      *
   1322      * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
   1323      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
   1324      * allowed} for your app.
   1325      * @throws IllegalArgumentException if no provider with the given name exists
   1326      */
   1327     public void setTestProviderEnabled(String provider, boolean enabled) {
   1328         try {
   1329             mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName());
   1330         } catch (RemoteException e) {
   1331             throw e.rethrowFromSystemServer();
   1332         }
   1333     }
   1334 
   1335     /**
   1336      * Removes any mock enabled value associated with the given provider.
   1337      *
   1338      * @param provider the provider name
   1339      *
   1340      * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
   1341      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
   1342      * allowed} for your app.
   1343      * @throws IllegalArgumentException if no provider with the given name exists
   1344      */
   1345     public void clearTestProviderEnabled(String provider) {
   1346         try {
   1347             mService.clearTestProviderEnabled(provider, mContext.getOpPackageName());
   1348         } catch (RemoteException e) {
   1349             throw e.rethrowFromSystemServer();
   1350         }
   1351     }
   1352 
   1353     /**
   1354      * Sets mock status values for the given provider.  These values will be used in place
   1355      * of any actual values from the provider.
   1356      *
   1357      * @param provider the provider name
   1358      * @param status the mock status
   1359      * @param extras a Bundle containing mock extras
   1360      * @param updateTime the mock update time
   1361      *
   1362      * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
   1363      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
   1364      * allowed} for your app.
   1365      * @throws IllegalArgumentException if no provider with the given name exists
   1366      */
   1367     public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
   1368         try {
   1369             mService.setTestProviderStatus(provider, status, extras, updateTime,
   1370                     mContext.getOpPackageName());
   1371         } catch (RemoteException e) {
   1372             throw e.rethrowFromSystemServer();
   1373         }
   1374     }
   1375 
   1376     /**
   1377      * Removes any mock status values associated with the given provider.
   1378      *
   1379      * @param provider the provider name
   1380      *
   1381      * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
   1382      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
   1383      * allowed} for your app.
   1384      * @throws IllegalArgumentException if no provider with the given name exists
   1385      */
   1386     public void clearTestProviderStatus(String provider) {
   1387         try {
   1388             mService.clearTestProviderStatus(provider, mContext.getOpPackageName());
   1389         } catch (RemoteException e) {
   1390             throw e.rethrowFromSystemServer();
   1391         }
   1392     }
   1393 
   1394     // --- GPS-specific support ---
   1395 
   1396     // This class is used to send Gnss status events to the client's specific thread.
   1397     private class GnssStatusListenerTransport extends IGnssStatusListener.Stub {
   1398 
   1399         private final GpsStatus.Listener mGpsListener;
   1400         private final GpsStatus.NmeaListener mGpsNmeaListener;
   1401         private final GnssStatusCallback mOldGnssCallback;
   1402         private final GnssStatus.Callback mGnssCallback;
   1403         private final GnssNmeaListener mOldGnssNmeaListener;
   1404         private final OnNmeaMessageListener mGnssNmeaListener;
   1405 
   1406         private class GnssHandler extends Handler {
   1407             public GnssHandler(Handler handler) {
   1408                 super(handler != null ? handler.getLooper() : Looper.myLooper());
   1409             }
   1410 
   1411             @Override
   1412             public void handleMessage(Message msg) {
   1413                 switch (msg.what) {
   1414                     case NMEA_RECEIVED:
   1415                         synchronized (mNmeaBuffer) {
   1416                             int length = mNmeaBuffer.size();
   1417                             for (int i = 0; i < length; i++) {
   1418                                 Nmea nmea = mNmeaBuffer.get(i);
   1419                                 mGnssNmeaListener.onNmeaMessage(nmea.mNmea, nmea.mTimestamp);
   1420                             }
   1421                             mNmeaBuffer.clear();
   1422                         }
   1423                         break;
   1424                     case GpsStatus.GPS_EVENT_STARTED:
   1425                         mGnssCallback.onStarted();
   1426                         break;
   1427                     case GpsStatus.GPS_EVENT_STOPPED:
   1428                         mGnssCallback.onStopped();
   1429                         break;
   1430                     case GpsStatus.GPS_EVENT_FIRST_FIX:
   1431                         mGnssCallback.onFirstFix(mTimeToFirstFix);
   1432                         break;
   1433                     case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
   1434                         mGnssCallback.onSatelliteStatusChanged(mGnssStatus);
   1435                         break;
   1436                     default:
   1437                         break;
   1438                 }
   1439             }
   1440         }
   1441 
   1442         private final Handler mGnssHandler;
   1443 
   1444         // This must not equal any of the GpsStatus event IDs
   1445         private static final int NMEA_RECEIVED = 1000;
   1446 
   1447         private class Nmea {
   1448             long mTimestamp;
   1449             String mNmea;
   1450 
   1451             Nmea(long timestamp, String nmea) {
   1452                 mTimestamp = timestamp;
   1453                 mNmea = nmea;
   1454             }
   1455         }
   1456         private final ArrayList<Nmea> mNmeaBuffer;
   1457 
   1458         GnssStatusListenerTransport(GpsStatus.Listener listener) {
   1459             this(listener, null);
   1460         }
   1461 
   1462         GnssStatusListenerTransport(GpsStatus.Listener listener, Handler handler) {
   1463             mGpsListener = listener;
   1464             mGnssHandler = new GnssHandler(handler);
   1465             mGpsNmeaListener = null;
   1466             mNmeaBuffer = null;
   1467             mOldGnssCallback = null;
   1468             mGnssCallback = new GnssStatus.Callback() {
   1469                 @Override
   1470                 public void onStarted() {
   1471                     mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED);
   1472                 }
   1473 
   1474                 @Override
   1475                 public void onStopped() {
   1476                     mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STOPPED);
   1477                 }
   1478 
   1479                 @Override
   1480                 public void onFirstFix(int ttff) {
   1481                     mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_FIRST_FIX);
   1482                 }
   1483 
   1484                 @Override
   1485                 public void onSatelliteStatusChanged(GnssStatus status) {
   1486                     mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
   1487                 }
   1488             };
   1489             mOldGnssNmeaListener = null;
   1490             mGnssNmeaListener = null;
   1491         }
   1492 
   1493         GnssStatusListenerTransport(GpsStatus.NmeaListener listener) {
   1494             this(listener, null);
   1495         }
   1496 
   1497         GnssStatusListenerTransport(GpsStatus.NmeaListener listener, Handler handler) {
   1498             mGpsListener = null;
   1499             mGnssHandler = new GnssHandler(handler);
   1500             mGpsNmeaListener = listener;
   1501             mNmeaBuffer = new ArrayList<Nmea>();
   1502             mOldGnssCallback = null;
   1503             mGnssCallback = null;
   1504             mOldGnssNmeaListener = null;
   1505             mGnssNmeaListener = new OnNmeaMessageListener() {
   1506                 @Override
   1507                 public void onNmeaMessage(String nmea, long timestamp) {
   1508                     mGpsNmeaListener.onNmeaReceived(timestamp, nmea);
   1509                 }
   1510             };
   1511         }
   1512 
   1513         GnssStatusListenerTransport(GnssStatusCallback callback) {
   1514             this(callback, null);
   1515         }
   1516 
   1517         GnssStatusListenerTransport(GnssStatusCallback callback, Handler handler) {
   1518             mOldGnssCallback = callback;
   1519             mGnssCallback = new GnssStatus.Callback() {
   1520                 @Override
   1521                 public void onStarted() {
   1522                     mOldGnssCallback.onStarted();
   1523                 }
   1524 
   1525                 @Override
   1526                 public void onStopped() {
   1527                     mOldGnssCallback.onStopped();
   1528                 }
   1529 
   1530                 @Override
   1531                 public void onFirstFix(int ttff) {
   1532                     mOldGnssCallback.onFirstFix(ttff);
   1533                 }
   1534 
   1535                 @Override
   1536                 public void onSatelliteStatusChanged(GnssStatus status) {
   1537                     mOldGnssCallback.onSatelliteStatusChanged(status);
   1538                 }
   1539             };
   1540             mGnssHandler = new GnssHandler(handler);
   1541             mOldGnssNmeaListener = null;
   1542             mGnssNmeaListener = null;
   1543             mNmeaBuffer = null;
   1544             mGpsListener = null;
   1545             mGpsNmeaListener = null;
   1546         }
   1547 
   1548         GnssStatusListenerTransport(GnssStatus.Callback callback) {
   1549             this(callback, null);
   1550         }
   1551 
   1552         GnssStatusListenerTransport(GnssStatus.Callback callback, Handler handler) {
   1553             mOldGnssCallback = null;
   1554             mGnssCallback = callback;
   1555             mGnssHandler = new GnssHandler(handler);
   1556             mOldGnssNmeaListener = null;
   1557             mGnssNmeaListener = null;
   1558             mNmeaBuffer = null;
   1559             mGpsListener = null;
   1560             mGpsNmeaListener = null;
   1561         }
   1562 
   1563         GnssStatusListenerTransport(GnssNmeaListener listener) {
   1564             this(listener, null);
   1565         }
   1566 
   1567         GnssStatusListenerTransport(GnssNmeaListener listener, Handler handler) {
   1568             mGnssCallback = null;
   1569             mOldGnssCallback = null;
   1570             mGnssHandler = new GnssHandler(handler);
   1571             mOldGnssNmeaListener = listener;
   1572             mGnssNmeaListener = new OnNmeaMessageListener() {
   1573                 @Override
   1574                 public void onNmeaMessage(String message, long timestamp) {
   1575                     mOldGnssNmeaListener.onNmeaReceived(timestamp, message);
   1576                 }
   1577             };
   1578             mGpsListener = null;
   1579             mGpsNmeaListener = null;
   1580             mNmeaBuffer = new ArrayList<Nmea>();
   1581         }
   1582 
   1583         GnssStatusListenerTransport(OnNmeaMessageListener listener) {
   1584             this(listener, null);
   1585         }
   1586 
   1587         GnssStatusListenerTransport(OnNmeaMessageListener listener, Handler handler) {
   1588             mOldGnssCallback = null;
   1589             mGnssCallback = null;
   1590             mGnssHandler = new GnssHandler(handler);
   1591             mOldGnssNmeaListener = null;
   1592             mGnssNmeaListener = listener;
   1593             mGpsListener = null;
   1594             mGpsNmeaListener = null;
   1595             mNmeaBuffer = new ArrayList<Nmea>();
   1596         }
   1597 
   1598         @Override
   1599         public void onGnssStarted() {
   1600             if (mGpsListener != null) {
   1601                 Message msg = Message.obtain();
   1602                 msg.what = GpsStatus.GPS_EVENT_STARTED;
   1603                 mGnssHandler.sendMessage(msg);
   1604             }
   1605         }
   1606 
   1607         @Override
   1608         public void onGnssStopped() {
   1609             if (mGpsListener != null) {
   1610                 Message msg = Message.obtain();
   1611                 msg.what = GpsStatus.GPS_EVENT_STOPPED;
   1612                 mGnssHandler.sendMessage(msg);
   1613             }
   1614         }
   1615 
   1616         @Override
   1617         public void onFirstFix(int ttff) {
   1618             if (mGpsListener != null) {
   1619                 mTimeToFirstFix = ttff;
   1620                 Message msg = Message.obtain();
   1621                 msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
   1622                 mGnssHandler.sendMessage(msg);
   1623             }
   1624         }
   1625 
   1626         @Override
   1627         public void onSvStatusChanged(int svCount, int[] prnWithFlags,
   1628                 float[] cn0s, float[] elevations, float[] azimuths) {
   1629             if (mGnssCallback != null) {
   1630                 mGnssStatus = new GnssStatus(svCount, prnWithFlags, cn0s, elevations, azimuths);
   1631 
   1632                 Message msg = Message.obtain();
   1633                 msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
   1634                 // remove any SV status messages already in the queue
   1635                 mGnssHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
   1636                 mGnssHandler.sendMessage(msg);
   1637             }
   1638         }
   1639 
   1640         @Override
   1641         public void onNmeaReceived(long timestamp, String nmea) {
   1642             if (mGnssNmeaListener != null) {
   1643                 synchronized (mNmeaBuffer) {
   1644                     mNmeaBuffer.add(new Nmea(timestamp, nmea));
   1645                 }
   1646                 Message msg = Message.obtain();
   1647                 msg.what = NMEA_RECEIVED;
   1648                 // remove any NMEA_RECEIVED messages already in the queue
   1649                 mGnssHandler.removeMessages(NMEA_RECEIVED);
   1650                 mGnssHandler.sendMessage(msg);
   1651             }
   1652         }
   1653     }
   1654 
   1655     /**
   1656      * Adds a GPS status listener.
   1657      *
   1658      * @param listener GPS status listener object to register
   1659      *
   1660      * @return true if the listener was successfully added
   1661      *
   1662      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
   1663      * @deprecated use {@link #registerGnssStatusCallback(GnssStatus.Callback)} instead.
   1664      */
   1665     @Deprecated
   1666     @RequiresPermission(ACCESS_FINE_LOCATION)
   1667     public boolean addGpsStatusListener(GpsStatus.Listener listener) {
   1668         boolean result;
   1669 
   1670         if (mGpsStatusListeners.get(listener) != null) {
   1671             // listener is already registered
   1672             return true;
   1673         }
   1674         try {
   1675             GnssStatusListenerTransport transport = new GnssStatusListenerTransport(listener);
   1676             result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
   1677             if (result) {
   1678                 mGpsStatusListeners.put(listener, transport);
   1679             }
   1680         } catch (RemoteException e) {
   1681             throw e.rethrowFromSystemServer();
   1682         }
   1683 
   1684         return result;
   1685     }
   1686 
   1687     /**
   1688      * Removes a GPS status listener.
   1689      *
   1690      * @param listener GPS status listener object to remove
   1691      * @deprecated use {@link #unregisterGnssStatusCallback(GnssStatus.Callback)} instead.
   1692      */
   1693     @Deprecated
   1694     public void removeGpsStatusListener(GpsStatus.Listener listener) {
   1695         try {
   1696             GnssStatusListenerTransport transport = mGpsStatusListeners.remove(listener);
   1697             if (transport != null) {
   1698                 mService.unregisterGnssStatusCallback(transport);
   1699             }
   1700         } catch (RemoteException e) {
   1701             throw e.rethrowFromSystemServer();
   1702         }
   1703     }
   1704 
   1705     /**
   1706      * Registers a GNSS status listener.
   1707      *
   1708      * @param callback GNSS status listener object to register
   1709      *
   1710      * @return true if the listener was successfully added
   1711      *
   1712      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
   1713      * @removed
   1714      */
   1715     @RequiresPermission(ACCESS_FINE_LOCATION)
   1716     public boolean registerGnssStatusCallback(GnssStatusCallback callback) {
   1717         return registerGnssStatusCallback(callback, null);
   1718     }
   1719 
   1720     /**
   1721      * Registers a GNSS status listener.
   1722      *
   1723      * @param callback GNSS status listener object to register
   1724      * @param handler the handler that the callback runs on.
   1725      *
   1726      * @return true if the listener was successfully added
   1727      *
   1728      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
   1729      * @removed
   1730      */
   1731     @RequiresPermission(ACCESS_FINE_LOCATION)
   1732     public boolean registerGnssStatusCallback(GnssStatusCallback callback, Handler handler) {
   1733         boolean result;
   1734         if (mOldGnssStatusListeners.get(callback) != null) {
   1735             // listener is already registered
   1736             return true;
   1737         }
   1738         try {
   1739             GnssStatusListenerTransport transport =
   1740                     new GnssStatusListenerTransport(callback, handler);
   1741             result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
   1742             if (result) {
   1743                 mOldGnssStatusListeners.put(callback, transport);
   1744             }
   1745         } catch (RemoteException e) {
   1746             throw e.rethrowFromSystemServer();
   1747         }
   1748 
   1749         return result;
   1750     }
   1751 
   1752     /**
   1753      * Removes a GNSS status listener.
   1754      *
   1755      * @param callback GNSS status listener object to remove
   1756      * @removed
   1757      */
   1758     public void unregisterGnssStatusCallback(GnssStatusCallback callback) {
   1759         try {
   1760             GnssStatusListenerTransport transport = mOldGnssStatusListeners.remove(callback);
   1761             if (transport != null) {
   1762                 mService.unregisterGnssStatusCallback(transport);
   1763             }
   1764         } catch (RemoteException e) {
   1765             throw e.rethrowFromSystemServer();
   1766         }
   1767     }
   1768 
   1769     /**
   1770      * Registers a GNSS status listener.
   1771      *
   1772      * @param callback GNSS status listener object to register
   1773      *
   1774      * @return true if the listener was successfully added
   1775      *
   1776      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
   1777      */
   1778     @RequiresPermission(ACCESS_FINE_LOCATION)
   1779     public boolean registerGnssStatusCallback(GnssStatus.Callback callback) {
   1780         return registerGnssStatusCallback(callback, null);
   1781     }
   1782 
   1783     /**
   1784      * Registers a GNSS status listener.
   1785      *
   1786      * @param callback GNSS status listener object to register
   1787      * @param handler the handler that the callback runs on.
   1788      *
   1789      * @return true if the listener was successfully added
   1790      *
   1791      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
   1792      */
   1793     @RequiresPermission(ACCESS_FINE_LOCATION)
   1794     public boolean registerGnssStatusCallback(GnssStatus.Callback callback, Handler handler) {
   1795         boolean result;
   1796         if (mGnssStatusListeners.get(callback) != null) {
   1797             // listener is already registered
   1798             return true;
   1799         }
   1800         try {
   1801             GnssStatusListenerTransport transport =
   1802                     new GnssStatusListenerTransport(callback, handler);
   1803             result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
   1804             if (result) {
   1805                 mGnssStatusListeners.put(callback, transport);
   1806             }
   1807         } catch (RemoteException e) {
   1808             throw e.rethrowFromSystemServer();
   1809         }
   1810 
   1811         return result;
   1812     }
   1813 
   1814     /**
   1815      * Removes a GNSS status listener.
   1816      *
   1817      * @param callback GNSS status listener object to remove
   1818      */
   1819     public void unregisterGnssStatusCallback(GnssStatus.Callback callback) {
   1820         try {
   1821             GnssStatusListenerTransport transport = mGnssStatusListeners.remove(callback);
   1822             if (transport != null) {
   1823                 mService.unregisterGnssStatusCallback(transport);
   1824             }
   1825         } catch (RemoteException e) {
   1826             throw e.rethrowFromSystemServer();
   1827         }
   1828     }
   1829 
   1830     /**
   1831      * Adds an NMEA listener.
   1832      *
   1833      * @param listener a {@link GpsStatus.NmeaListener} object to register
   1834      *
   1835      * @return true if the listener was successfully added
   1836      *
   1837      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
   1838      * @deprecated use {@link #addNmeaListener(OnNmeaMessageListener)} instead.
   1839      */
   1840     @Deprecated
   1841     @RequiresPermission(ACCESS_FINE_LOCATION)
   1842     public boolean addNmeaListener(GpsStatus.NmeaListener listener) {
   1843         boolean result;
   1844 
   1845         if (mGpsNmeaListeners.get(listener) != null) {
   1846             // listener is already registered
   1847             return true;
   1848         }
   1849         try {
   1850             GnssStatusListenerTransport transport = new GnssStatusListenerTransport(listener);
   1851             result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
   1852             if (result) {
   1853                 mGpsNmeaListeners.put(listener, transport);
   1854             }
   1855         } catch (RemoteException e) {
   1856             throw e.rethrowFromSystemServer();
   1857         }
   1858 
   1859         return result;
   1860     }
   1861 
   1862     /**
   1863      * Removes an NMEA listener.
   1864      *
   1865      * @param listener a {@link GpsStatus.NmeaListener} object to remove
   1866      * @deprecated use {@link #removeNmeaListener(OnNmeaMessageListener)} instead.
   1867      */
   1868     @Deprecated
   1869     public void removeNmeaListener(GpsStatus.NmeaListener listener) {
   1870         try {
   1871             GnssStatusListenerTransport transport = mGpsNmeaListeners.remove(listener);
   1872             if (transport != null) {
   1873                 mService.unregisterGnssStatusCallback(transport);
   1874             }
   1875         } catch (RemoteException e) {
   1876             throw e.rethrowFromSystemServer();
   1877         }
   1878     }
   1879 
   1880     /**
   1881      * Adds an NMEA listener.
   1882      *
   1883      * @param listener a {@link GnssNmeaListener} object to register
   1884      *
   1885      * @return true if the listener was successfully added
   1886      *
   1887      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
   1888      * @removed
   1889      */
   1890     @RequiresPermission(ACCESS_FINE_LOCATION)
   1891     public boolean addNmeaListener(GnssNmeaListener listener) {
   1892         return addNmeaListener(listener, null);
   1893     }
   1894 
   1895     /**
   1896      * Adds an NMEA listener.
   1897      *
   1898      * @param listener a {@link GnssNmeaListener} object to register
   1899      * @param handler the handler that the listener runs on.
   1900      *
   1901      * @return true if the listener was successfully added
   1902      *
   1903      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
   1904      * @removed
   1905      */
   1906     @RequiresPermission(ACCESS_FINE_LOCATION)
   1907     public boolean addNmeaListener(GnssNmeaListener listener, Handler handler) {
   1908         boolean result;
   1909 
   1910         if (mGpsNmeaListeners.get(listener) != null) {
   1911             // listener is already registered
   1912             return true;
   1913         }
   1914         try {
   1915             GnssStatusListenerTransport transport =
   1916                     new GnssStatusListenerTransport(listener, handler);
   1917             result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
   1918             if (result) {
   1919                 mOldGnssNmeaListeners.put(listener, transport);
   1920             }
   1921         } catch (RemoteException e) {
   1922             throw e.rethrowFromSystemServer();
   1923         }
   1924 
   1925         return result;
   1926     }
   1927 
   1928     /**
   1929      * Removes an NMEA listener.
   1930      *
   1931      * @param listener a {@link GnssNmeaListener} object to remove
   1932      * @removed
   1933      */
   1934     public void removeNmeaListener(GnssNmeaListener listener) {
   1935         try {
   1936             GnssStatusListenerTransport transport = mOldGnssNmeaListeners.remove(listener);
   1937             if (transport != null) {
   1938                 mService.unregisterGnssStatusCallback(transport);
   1939             }
   1940         } catch (RemoteException e) {
   1941             throw e.rethrowFromSystemServer();
   1942         }
   1943     }
   1944 
   1945     /**
   1946      * Adds an NMEA listener.
   1947      *
   1948      * @param listener a {@link OnNmeaMessageListener} object to register
   1949      *
   1950      * @return true if the listener was successfully added
   1951      *
   1952      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
   1953      */
   1954     @RequiresPermission(ACCESS_FINE_LOCATION)
   1955     public boolean addNmeaListener(OnNmeaMessageListener listener) {
   1956         return addNmeaListener(listener, null);
   1957     }
   1958 
   1959     /**
   1960      * Adds an NMEA listener.
   1961      *
   1962      * @param listener a {@link OnNmeaMessageListener} object to register
   1963      * @param handler the handler that the listener runs on.
   1964      *
   1965      * @return true if the listener was successfully added
   1966      *
   1967      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
   1968      */
   1969     @RequiresPermission(ACCESS_FINE_LOCATION)
   1970     public boolean addNmeaListener(OnNmeaMessageListener listener, Handler handler) {
   1971         boolean result;
   1972 
   1973         if (mGpsNmeaListeners.get(listener) != null) {
   1974             // listener is already registered
   1975             return true;
   1976         }
   1977         try {
   1978             GnssStatusListenerTransport transport =
   1979                     new GnssStatusListenerTransport(listener, handler);
   1980             result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
   1981             if (result) {
   1982                 mGnssNmeaListeners.put(listener, transport);
   1983             }
   1984         } catch (RemoteException e) {
   1985             throw e.rethrowFromSystemServer();
   1986         }
   1987 
   1988         return result;
   1989     }
   1990 
   1991     /**
   1992      * Removes an NMEA listener.
   1993      *
   1994      * @param listener a {@link OnNmeaMessageListener} object to remove
   1995      */
   1996     public void removeNmeaListener(OnNmeaMessageListener listener) {
   1997         try {
   1998             GnssStatusListenerTransport transport = mGnssNmeaListeners.remove(listener);
   1999             if (transport != null) {
   2000                 mService.unregisterGnssStatusCallback(transport);
   2001             }
   2002         } catch (RemoteException e) {
   2003             throw e.rethrowFromSystemServer();
   2004         }
   2005     }
   2006 
   2007     /**
   2008      * No-op method to keep backward-compatibility.
   2009      * Don't use it. Use {@link #registerGnssMeasurementsCallback} instead.
   2010      * @hide
   2011      * @deprecated Not supported anymore.
   2012      */
   2013     @Deprecated
   2014     @SystemApi
   2015     public boolean addGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {
   2016         return false;
   2017     }
   2018 
   2019     /**
   2020      * Registers a GPS Measurement callback.
   2021      *
   2022      * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
   2023      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
   2024      */
   2025     @RequiresPermission(ACCESS_FINE_LOCATION)
   2026     public boolean registerGnssMeasurementsCallback(GnssMeasurementsEvent.Callback callback) {
   2027         return registerGnssMeasurementsCallback(callback, null);
   2028     }
   2029 
   2030     /**
   2031      * Registers a GPS Measurement callback.
   2032      *
   2033      * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
   2034      * @param handler the handler that the callback runs on.
   2035      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
   2036      */
   2037     @RequiresPermission(ACCESS_FINE_LOCATION)
   2038     public boolean registerGnssMeasurementsCallback(GnssMeasurementsEvent.Callback callback,
   2039             Handler handler) {
   2040         return mGnssMeasurementCallbackTransport.add(callback, handler);
   2041     }
   2042 
   2043     /**
   2044      * No-op method to keep backward-compatibility.
   2045      * Don't use it. Use {@link #unregisterGnssMeasurementsCallback} instead.
   2046      * @hide
   2047      * @deprecated use {@link #unregisterGnssMeasurementsCallback(GnssMeasurementsEvent.Callback)}
   2048      * instead.
   2049      */
   2050     @Deprecated
   2051     @SystemApi
   2052     public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {
   2053     }
   2054 
   2055     /**
   2056      * Unregisters a GPS Measurement callback.
   2057      *
   2058      * @param callback a {@link GnssMeasurementsEvent.Callback} object to remove.
   2059      */
   2060     public void unregisterGnssMeasurementsCallback(GnssMeasurementsEvent.Callback callback) {
   2061         mGnssMeasurementCallbackTransport.remove(callback);
   2062     }
   2063 
   2064     /**
   2065      * No-op method to keep backward-compatibility.
   2066      * Don't use it. Use {@link #registerGnssNavigationMessageCallback} instead.
   2067      * @hide
   2068      * @deprecated Not supported anymore.
   2069      */
   2070     @Deprecated
   2071     @SystemApi
   2072     public boolean addGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {
   2073         return false;
   2074     }
   2075 
   2076     /**
   2077      * No-op method to keep backward-compatibility.
   2078      * Don't use it. Use {@link #unregisterGnssNavigationMessageCallback} instead.
   2079      * @hide
   2080      * @deprecated use {@link #unregisterGnssNavigationMessageCallback(GnssMeasurements.Callback)}
   2081      * instead
   2082      */
   2083     @Deprecated
   2084     @SystemApi
   2085     public void removeGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {
   2086     }
   2087 
   2088     /**
   2089      * Registers a GNSS Navigation Message callback.
   2090      *
   2091      * @param callback a {@link GnssNavigationMessageEvent.Callback} object to register.
   2092      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
   2093      * @removed
   2094      */
   2095     public boolean registerGnssNavigationMessageCallback(
   2096             GnssNavigationMessageEvent.Callback callback) {
   2097         return registerGnssNavigationMessageCallback(callback, null);
   2098     }
   2099 
   2100     /**
   2101      * Registers a GNSS Navigation Message callback.
   2102      *
   2103      * @param callback a {@link GnssNavigationMessageEvent.Callback} object to register.
   2104      * @param handler the handler that the callback runs on.
   2105      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
   2106      * @removed
   2107      */
   2108     @RequiresPermission(ACCESS_FINE_LOCATION)
   2109     public boolean registerGnssNavigationMessageCallback(
   2110             final GnssNavigationMessageEvent.Callback callback, Handler handler) {
   2111         GnssNavigationMessage.Callback bridge = new GnssNavigationMessage.Callback() {
   2112             @Override
   2113             public void onGnssNavigationMessageReceived(GnssNavigationMessage message) {
   2114                 GnssNavigationMessageEvent event = new GnssNavigationMessageEvent(message);
   2115                 callback.onGnssNavigationMessageReceived(event);
   2116             }
   2117 
   2118             @Override
   2119             public void onStatusChanged(int status) {
   2120                 callback.onStatusChanged(status);
   2121             }
   2122         };
   2123         mNavigationMessageBridge.put(callback, bridge);
   2124         return mGnssNavigationMessageCallbackTransport.add(bridge, handler);
   2125     }
   2126 
   2127     /**
   2128      * Unregisters a GNSS Navigation Message callback.
   2129      *
   2130      * @param callback a {@link GnssNavigationMessageEvent.Callback} object to remove.
   2131      * @removed
   2132      */
   2133     public void unregisterGnssNavigationMessageCallback(
   2134             GnssNavigationMessageEvent.Callback callback) {
   2135         mGnssNavigationMessageCallbackTransport.remove(
   2136                 mNavigationMessageBridge.remove(
   2137                         callback));
   2138     }
   2139 
   2140     /**
   2141      * Registers a GNSS Navigation Message callback.
   2142      *
   2143      * @param callback a {@link GnssNavigationMessage.Callback} object to register.
   2144      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
   2145      */
   2146     public boolean registerGnssNavigationMessageCallback(
   2147             GnssNavigationMessage.Callback callback) {
   2148         return registerGnssNavigationMessageCallback(callback, null);
   2149     }
   2150 
   2151     /**
   2152      * Registers a GNSS Navigation Message callback.
   2153      *
   2154      * @param callback a {@link GnssNavigationMessage.Callback} object to register.
   2155      * @param handler the handler that the callback runs on.
   2156      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
   2157      */
   2158     @RequiresPermission(ACCESS_FINE_LOCATION)
   2159     public boolean registerGnssNavigationMessageCallback(
   2160             GnssNavigationMessage.Callback callback, Handler handler) {
   2161         return mGnssNavigationMessageCallbackTransport.add(callback, handler);
   2162     }
   2163 
   2164     /**
   2165      * Unregisters a GNSS Navigation Message callback.
   2166      *
   2167      * @param callback a {@link GnssNavigationMessage.Callback} object to remove.
   2168      */
   2169     public void unregisterGnssNavigationMessageCallback(
   2170             GnssNavigationMessage.Callback callback) {
   2171         mGnssNavigationMessageCallbackTransport.remove(callback);
   2172     }
   2173 
   2174     /**
   2175      * Retrieves information about the current status of the GPS engine.
   2176      * This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged}
   2177      * callback to ensure that the data is copied atomically.
   2178      *
   2179      * The caller may either pass in a {@link GpsStatus} object to set with the latest
   2180      * status information, or pass null to create a new {@link GpsStatus} object.
   2181      *
   2182      * @param status object containing GPS status details, or null.
   2183      * @return status object containing updated GPS status.
   2184      */
   2185     @Deprecated
   2186     @RequiresPermission(ACCESS_FINE_LOCATION)
   2187     public GpsStatus getGpsStatus(GpsStatus status) {
   2188         if (status == null) {
   2189             status = new GpsStatus();
   2190         }
   2191         // When mGnssStatus is null, that means that this method is called outside
   2192         // onGpsStatusChanged().  Return an empty status  to maintain backwards compatibility.
   2193         if (mGnssStatus != null) {
   2194             status.setStatus(mGnssStatus, mTimeToFirstFix);
   2195         }
   2196         return status;
   2197     }
   2198 
   2199     /**
   2200      * Returns the system information of the GPS hardware.
   2201      * May return 0 if GPS hardware is earlier than 2016.
   2202      * @hide
   2203      */
   2204     @TestApi
   2205     public int getGnssYearOfHardware() {
   2206         try {
   2207             return mService.getGnssYearOfHardware();
   2208         } catch (RemoteException e) {
   2209             throw e.rethrowFromSystemServer();
   2210         }
   2211     }
   2212 
   2213     /**
   2214      * Sends additional commands to a location provider.
   2215      * Can be used to support provider specific extensions to the Location Manager API
   2216      *
   2217      * @param provider name of the location provider.
   2218      * @param command name of the command to send to the provider.
   2219      * @param extras optional arguments for the command (or null).
   2220      * The provider may optionally fill the extras Bundle with results from the command.
   2221      *
   2222      * @return true if the command succeeds.
   2223      */
   2224     public boolean sendExtraCommand(String provider, String command, Bundle extras) {
   2225         try {
   2226             return mService.sendExtraCommand(provider, command, extras);
   2227         } catch (RemoteException e) {
   2228             throw e.rethrowFromSystemServer();
   2229         }
   2230     }
   2231 
   2232     /**
   2233      * Used by NetInitiatedActivity to report user response
   2234      * for network initiated GPS fix requests.
   2235      *
   2236      * @hide
   2237      */
   2238     public boolean sendNiResponse(int notifId, int userResponse) {
   2239         try {
   2240             return mService.sendNiResponse(notifId, userResponse);
   2241         } catch (RemoteException e) {
   2242             throw e.rethrowFromSystemServer();
   2243         }
   2244     }
   2245 
   2246     private static void checkProvider(String provider) {
   2247         if (provider == null) {
   2248             throw new IllegalArgumentException("invalid provider: " + provider);
   2249         }
   2250     }
   2251 
   2252     private static void checkCriteria(Criteria criteria) {
   2253         if (criteria == null) {
   2254             throw new IllegalArgumentException("invalid criteria: " + criteria);
   2255         }
   2256     }
   2257 
   2258     private static void checkListener(LocationListener listener) {
   2259         if (listener == null) {
   2260             throw new IllegalArgumentException("invalid listener: " + listener);
   2261         }
   2262     }
   2263 
   2264     private void checkPendingIntent(PendingIntent intent) {
   2265         if (intent == null) {
   2266             throw new IllegalArgumentException("invalid pending intent: " + intent);
   2267         }
   2268         if (!intent.isTargetedToPackage()) {
   2269             IllegalArgumentException e = new IllegalArgumentException(
   2270                     "pending intent must be targeted to package");
   2271             if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) {
   2272                 throw e;
   2273             } else {
   2274                 Log.w(TAG, e);
   2275             }
   2276         }
   2277     }
   2278 
   2279     private static void checkGeofence(Geofence fence) {
   2280         if (fence == null) {
   2281             throw new IllegalArgumentException("invalid geofence: " + fence);
   2282         }
   2283     }
   2284 }
   2285