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