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