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