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
17package android.location;
18
19import com.android.internal.location.ProviderProperties;
20
21import android.Manifest;
22import android.annotation.RequiresPermission;
23import android.annotation.SuppressLint;
24import android.annotation.SystemApi;
25import android.annotation.SystemService;
26import android.annotation.TestApi;
27import android.app.PendingIntent;
28import android.content.Context;
29import android.content.Intent;
30import android.os.Build;
31import android.os.Bundle;
32import android.os.Handler;
33import android.os.Looper;
34import android.os.Message;
35import android.os.RemoteException;
36import android.util.Log;
37
38import java.util.ArrayList;
39import java.util.HashMap;
40import java.util.List;
41
42import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
43import 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)
61public 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
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
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
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
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
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
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     */
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
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     */
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     */
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     */
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     */
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     */
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})
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})
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})
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})
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})
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})
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})
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})
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})
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})
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})
861    public void requestLocationUpdates(LocationRequest request, PendingIntent intent) {
862        checkPendingIntent(intent);
863        requestLocationUpdates(request, null, null, intent);
864    }
865
866    private ListenerTransport wrapListener(LocationListener listener, Looper looper) {
867        if (listener == null) return null;
868        synchronized (mListeners) {
869            ListenerTransport transport = mListeners.get(listener);
870            if (transport == null) {
871                transport = new ListenerTransport(listener, looper);
872            }
873            mListeners.put(listener, transport);
874            return transport;
875        }
876    }
877
878    private void requestLocationUpdates(LocationRequest request, LocationListener listener,
879            Looper looper, PendingIntent intent) {
880
881        String packageName = mContext.getPackageName();
882
883        // wrap the listener class
884        ListenerTransport transport = wrapListener(listener, looper);
885
886        try {
887            mService.requestLocationUpdates(request, transport, intent, packageName);
888       } catch (RemoteException e) {
889           throw e.rethrowFromSystemServer();
890       }
891    }
892
893    /**
894     * Removes all location updates for the specified LocationListener.
895     *
896     * <p>Following this call, updates will no longer
897     * occur for this listener.
898     *
899     * @param listener listener object that no longer needs location updates
900     * @throws IllegalArgumentException if listener is null
901     */
902    public void removeUpdates(LocationListener listener) {
903        checkListener(listener);
904        String packageName = mContext.getPackageName();
905
906        ListenerTransport transport;
907        synchronized (mListeners) {
908            transport = mListeners.remove(listener);
909        }
910        if (transport == null) return;
911
912        try {
913            mService.removeUpdates(transport, null, packageName);
914        } catch (RemoteException e) {
915            throw e.rethrowFromSystemServer();
916        }
917    }
918
919    /**
920     * Removes all location updates for the specified pending intent.
921     *
922     * <p>Following this call, updates will no longer for this pending intent.
923     *
924     * @param intent pending intent object that no longer needs location updates
925     * @throws IllegalArgumentException if intent is null
926     */
927    public void removeUpdates(PendingIntent intent) {
928        checkPendingIntent(intent);
929        String packageName = mContext.getPackageName();
930
931        try {
932            mService.removeUpdates(null, intent, packageName);
933        } catch (RemoteException e) {
934            throw e.rethrowFromSystemServer();
935        }
936    }
937
938    /**
939     * Set a proximity alert for the location given by the position
940     * (latitude, longitude) and the given radius.
941     *
942     * <p> When the device
943     * detects that it has entered or exited the area surrounding the
944     * location, the given PendingIntent will be used to create an Intent
945     * to be fired.
946     *
947     * <p> The fired Intent will have a boolean extra added with key
948     * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
949     * entering the proximity region; if false, it is exiting.
950     *
951     * <p> Due to the approximate nature of position estimation, if the
952     * device passes through the given area briefly, it is possible
953     * that no Intent will be fired.  Similarly, an Intent could be
954     * fired if the device passes very close to the given area but
955     * does not actually enter it.
956     *
957     * <p> After the number of milliseconds given by the expiration
958     * parameter, the location manager will delete this proximity
959     * alert and no longer monitor it.  A value of -1 indicates that
960     * there should be no expiration time.
961     *
962     * <p> Internally, this method uses both {@link #NETWORK_PROVIDER}
963     * and {@link #GPS_PROVIDER}.
964     *
965     * <p>Before API version 17, this method could be used with
966     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
967     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
968     * From API version 17 and onwards, this method requires
969     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
970     *
971     * @param latitude the latitude of the central point of the
972     * alert region
973     * @param longitude the longitude of the central point of the
974     * alert region
975     * @param radius the radius of the central point of the
976     * alert region, in meters
977     * @param expiration time for this proximity alert, in milliseconds,
978     * or -1 to indicate no expiration
979     * @param intent a PendingIntent that will be used to generate an Intent to
980     * fire when entry to or exit from the alert region is detected
981     *
982     * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
983     * permission is not present
984     */
985    @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
986    public void addProximityAlert(double latitude, double longitude, float radius, long expiration,
987            PendingIntent intent) {
988        checkPendingIntent(intent);
989        if (expiration < 0) expiration = Long.MAX_VALUE;
990
991        Geofence fence = Geofence.createCircle(latitude, longitude, radius);
992        LocationRequest request = new LocationRequest().setExpireIn(expiration);
993        try {
994            mService.requestGeofence(request, fence, intent, mContext.getPackageName());
995        } catch (RemoteException e) {
996            throw e.rethrowFromSystemServer();
997        }
998    }
999
1000    /**
1001     * Add a geofence with the specified LocationRequest quality of service.
1002     *
1003     * <p> When the device
1004     * detects that it has entered or exited the area surrounding the
1005     * location, the given PendingIntent will be used to create an Intent
1006     * to be fired.
1007     *
1008     * <p> The fired Intent will have a boolean extra added with key
1009     * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
1010     * entering the proximity region; if false, it is exiting.
1011     *
1012     * <p> The geofence engine fuses results from all location providers to
1013     * provide the best balance between accuracy and power. Applications
1014     * can choose the quality of service required using the
1015     * {@link LocationRequest} object. If it is null then a default,
1016     * low power geo-fencing implementation is used. It is possible to cross
1017     * a geo-fence without notification, but the system will do its best
1018     * to detect, using {@link LocationRequest} as a hint to trade-off
1019     * accuracy and power.
1020     *
1021     * <p> The power required by the geofence engine can depend on many factors,
1022     * such as quality and interval requested in {@link LocationRequest},
1023     * distance to nearest geofence and current device velocity.
1024     *
1025     * @param request quality of service required, null for default low power
1026     * @param fence a geographical description of the geofence area
1027     * @param intent pending intent to receive geofence updates
1028     *
1029     * @throws IllegalArgumentException if fence is null
1030     * @throws IllegalArgumentException if intent is null
1031     * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
1032     * permission is not present
1033     *
1034     * @hide
1035     */
1036    @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
1037    public void addGeofence(LocationRequest request, Geofence fence, PendingIntent intent) {
1038        checkPendingIntent(intent);
1039        checkGeofence(fence);
1040
1041        try {
1042            mService.requestGeofence(request, fence, intent, mContext.getPackageName());
1043        } catch (RemoteException e) {
1044            throw e.rethrowFromSystemServer();
1045        }
1046    }
1047
1048    /**
1049     * Removes the proximity alert with the given PendingIntent.
1050     *
1051     * <p>Before API version 17, this method could be used with
1052     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
1053     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
1054     * From API version 17 and onwards, this method requires
1055     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
1056     *
1057     * @param intent the PendingIntent that no longer needs to be notified of
1058     * proximity alerts
1059     *
1060     * @throws IllegalArgumentException if intent is null
1061     * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
1062     * permission is not present
1063     */
1064    public void removeProximityAlert(PendingIntent intent) {
1065        checkPendingIntent(intent);
1066        String packageName = mContext.getPackageName();
1067
1068        try {
1069            mService.removeGeofence(null, intent, packageName);
1070        } catch (RemoteException e) {
1071            throw e.rethrowFromSystemServer();
1072        }
1073    }
1074
1075    /**
1076     * Remove a single geofence.
1077     *
1078     * <p>This removes only the specified geofence associated with the
1079     * specified pending intent. All other geofences remain unchanged.
1080     *
1081     * @param fence a geofence previously passed to {@link #addGeofence}
1082     * @param intent a pending intent previously passed to {@link #addGeofence}
1083     *
1084     * @throws IllegalArgumentException if fence is null
1085     * @throws IllegalArgumentException if intent is null
1086     * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
1087     * permission is not present
1088     *
1089     * @hide
1090     */
1091    public void removeGeofence(Geofence fence, PendingIntent intent) {
1092        checkPendingIntent(intent);
1093        checkGeofence(fence);
1094        String packageName = mContext.getPackageName();
1095
1096        try {
1097            mService.removeGeofence(fence, intent, packageName);
1098        } catch (RemoteException e) {
1099            throw e.rethrowFromSystemServer();
1100        }
1101    }
1102
1103    /**
1104     * Remove all geofences registered to the specified pending intent.
1105     *
1106     * @param intent a pending intent previously passed to {@link #addGeofence}
1107     *
1108     * @throws IllegalArgumentException if intent is null
1109     * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
1110     * permission is not present
1111     *
1112     * @hide
1113     */
1114    public void removeAllGeofences(PendingIntent intent) {
1115        checkPendingIntent(intent);
1116        String packageName = mContext.getPackageName();
1117
1118        try {
1119            mService.removeGeofence(null, intent, packageName);
1120        } catch (RemoteException e) {
1121            throw e.rethrowFromSystemServer();
1122        }
1123    }
1124
1125    /**
1126     * Returns the current enabled/disabled status of the given provider.
1127     *
1128     * <p>If the user has enabled this provider in the Settings menu, true
1129     * is returned otherwise false is returned
1130     *
1131     * <p>Callers should instead use
1132     * {@link android.provider.Settings.Secure#LOCATION_MODE}
1133     * unless they depend on provider-specific APIs such as
1134     * {@link #requestLocationUpdates(String, long, float, LocationListener)}.
1135     *
1136     * <p>
1137     * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this
1138     * method would throw {@link SecurityException} if the location permissions
1139     * were not sufficient to use the specified provider.
1140     *
1141     * @param provider the name of the provider
1142     * @return true if the provider exists and is enabled
1143     *
1144     * @throws IllegalArgumentException if provider is null
1145     */
1146    public boolean isProviderEnabled(String provider) {
1147        checkProvider(provider);
1148
1149        try {
1150            return mService.isProviderEnabled(provider);
1151        } catch (RemoteException e) {
1152            throw e.rethrowFromSystemServer();
1153        }
1154    }
1155
1156    /**
1157     * Get the last known location.
1158     *
1159     * <p>This location could be very old so use
1160     * {@link Location#getElapsedRealtimeNanos} to calculate its age. It can
1161     * also return null if no previous location is available.
1162     *
1163     * <p>Always returns immediately.
1164     *
1165     * @return The last known location, or null if not available
1166     * @throws SecurityException if no suitable permission is present
1167     *
1168     * @hide
1169     */
1170    public Location getLastLocation() {
1171        String packageName = mContext.getPackageName();
1172
1173        try {
1174            return mService.getLastLocation(null, packageName);
1175        } catch (RemoteException e) {
1176            throw e.rethrowFromSystemServer();
1177        }
1178    }
1179
1180    /**
1181     * Returns a Location indicating the data from the last known
1182     * location fix obtained from the given provider.
1183     *
1184     * <p> This can be done
1185     * without starting the provider.  Note that this location could
1186     * be out-of-date, for example if the device was turned off and
1187     * moved to another location.
1188     *
1189     * <p> If the provider is currently disabled, null is returned.
1190     *
1191     * @param provider the name of the provider
1192     * @return the last known location for the provider, or null
1193     *
1194     * @throws SecurityException if no suitable permission is present
1195     * @throws IllegalArgumentException if provider is null or doesn't exist
1196     */
1197    @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
1198    public Location getLastKnownLocation(String provider) {
1199        checkProvider(provider);
1200        String packageName = mContext.getPackageName();
1201        LocationRequest request = LocationRequest.createFromDeprecatedProvider(
1202                provider, 0, 0, true);
1203
1204        try {
1205            return mService.getLastLocation(request, packageName);
1206        } catch (RemoteException e) {
1207            throw e.rethrowFromSystemServer();
1208        }
1209    }
1210
1211    // --- Mock provider support ---
1212    // TODO: It would be fantastic to deprecate mock providers entirely, and replace
1213    // with something closer to LocationProviderBase.java
1214
1215    /**
1216     * Creates a mock location provider and adds it to the set of active providers.
1217     *
1218     * @param name the provider name
1219     *
1220     * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
1221     * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
1222     * allowed} for your app.
1223     * @throws IllegalArgumentException if a provider with the given name already exists
1224     */
1225    public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1226            boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1227            boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1228        ProviderProperties properties = new ProviderProperties(requiresNetwork,
1229                requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
1230                supportsBearing, powerRequirement, accuracy);
1231        if (name.matches(LocationProvider.BAD_CHARS_REGEX)) {
1232            throw new IllegalArgumentException("provider name contains illegal character: " + name);
1233        }
1234
1235        try {
1236            mService.addTestProvider(name, properties, mContext.getOpPackageName());
1237        } catch (RemoteException e) {
1238            throw e.rethrowFromSystemServer();
1239        }
1240    }
1241
1242    /**
1243     * Removes the mock location provider with the given name.
1244     *
1245     * @param provider the provider name
1246     *
1247     * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
1248     * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
1249     * allowed} for your app.
1250     * @throws IllegalArgumentException if no provider with the given name exists
1251     */
1252    public void removeTestProvider(String provider) {
1253        try {
1254            mService.removeTestProvider(provider, mContext.getOpPackageName());
1255        } catch (RemoteException e) {
1256            throw e.rethrowFromSystemServer();
1257        }
1258    }
1259
1260    /**
1261     * Sets a mock location for the given provider.
1262     * <p>This location will be used in place of any actual location from the provider.
1263     * The location object must have a minimum number of fields set to be
1264     * considered a valid LocationProvider Location, as per documentation
1265     * on {@link Location} class.
1266     *
1267     * @param provider the provider name
1268     * @param loc the mock location
1269     *
1270     * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
1271     * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
1272     * allowed} for your app.
1273     * @throws IllegalArgumentException if no provider with the given name exists
1274     * @throws IllegalArgumentException if the location is incomplete
1275     */
1276    public void setTestProviderLocation(String provider, Location loc) {
1277        if (!loc.isComplete()) {
1278            IllegalArgumentException e = new IllegalArgumentException(
1279                    "Incomplete location object, missing timestamp or accuracy? " + loc);
1280            if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) {
1281                // just log on old platform (for backwards compatibility)
1282                Log.w(TAG, e);
1283                loc.makeComplete();
1284            } else {
1285                // really throw it!
1286                throw e;
1287            }
1288        }
1289
1290        try {
1291            mService.setTestProviderLocation(provider, loc, mContext.getOpPackageName());
1292        } catch (RemoteException e) {
1293            throw e.rethrowFromSystemServer();
1294        }
1295    }
1296
1297    /**
1298     * Removes any mock location associated with the given provider.
1299     *
1300     * @param provider the provider name
1301     *
1302     * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
1303     * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
1304     * allowed} for your app.
1305     * @throws IllegalArgumentException if no provider with the given name exists
1306     */
1307    public void clearTestProviderLocation(String provider) {
1308        try {
1309            mService.clearTestProviderLocation(provider, mContext.getOpPackageName());
1310        } catch (RemoteException e) {
1311            throw e.rethrowFromSystemServer();
1312        }
1313    }
1314
1315    /**
1316     * Sets a mock enabled value for the given provider.  This value will be used in place
1317     * of any actual value from the provider.
1318     *
1319     * @param provider the provider name
1320     * @param enabled the mock enabled value
1321     *
1322     * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
1323     * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
1324     * allowed} for your app.
1325     * @throws IllegalArgumentException if no provider with the given name exists
1326     */
1327    public void setTestProviderEnabled(String provider, boolean enabled) {
1328        try {
1329            mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName());
1330        } catch (RemoteException e) {
1331            throw e.rethrowFromSystemServer();
1332        }
1333    }
1334
1335    /**
1336     * Removes any mock enabled value associated with the given provider.
1337     *
1338     * @param provider the provider name
1339     *
1340     * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
1341     * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
1342     * allowed} for your app.
1343     * @throws IllegalArgumentException if no provider with the given name exists
1344     */
1345    public void clearTestProviderEnabled(String provider) {
1346        try {
1347            mService.clearTestProviderEnabled(provider, mContext.getOpPackageName());
1348        } catch (RemoteException e) {
1349            throw e.rethrowFromSystemServer();
1350        }
1351    }
1352
1353    /**
1354     * Sets mock status values for the given provider.  These values will be used in place
1355     * of any actual values from the provider.
1356     *
1357     * @param provider the provider name
1358     * @param status the mock status
1359     * @param extras a Bundle containing mock extras
1360     * @param updateTime the mock update time
1361     *
1362     * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
1363     * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
1364     * allowed} for your app.
1365     * @throws IllegalArgumentException if no provider with the given name exists
1366     */
1367    public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1368        try {
1369            mService.setTestProviderStatus(provider, status, extras, updateTime,
1370                    mContext.getOpPackageName());
1371        } catch (RemoteException e) {
1372            throw e.rethrowFromSystemServer();
1373        }
1374    }
1375
1376    /**
1377     * Removes any mock status values associated with the given provider.
1378     *
1379     * @param provider the provider name
1380     *
1381     * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
1382     * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
1383     * allowed} for your app.
1384     * @throws IllegalArgumentException if no provider with the given name exists
1385     */
1386    public void clearTestProviderStatus(String provider) {
1387        try {
1388            mService.clearTestProviderStatus(provider, mContext.getOpPackageName());
1389        } catch (RemoteException e) {
1390            throw e.rethrowFromSystemServer();
1391        }
1392    }
1393
1394    // --- GPS-specific support ---
1395
1396    // This class is used to send Gnss status events to the client's specific thread.
1397    private class GnssStatusListenerTransport extends IGnssStatusListener.Stub {
1398
1399        private final GpsStatus.Listener mGpsListener;
1400        private final GpsStatus.NmeaListener mGpsNmeaListener;
1401        private final GnssStatus.Callback mGnssCallback;
1402        private final OnNmeaMessageListener mGnssNmeaListener;
1403
1404        private class GnssHandler extends Handler {
1405            public GnssHandler(Handler handler) {
1406                super(handler != null ? handler.getLooper() : Looper.myLooper());
1407            }
1408
1409            @Override
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
1449            Nmea(long timestamp, String nmea) {
1450                mTimestamp = timestamp;
1451                mNmea = nmea;
1452            }
1453        }
1454        private final ArrayList<Nmea> mNmeaBuffer;
1455
1456        GnssStatusListenerTransport(GpsStatus.Listener listener) {
1457            this(listener, null);
1458        }
1459
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
1489        GnssStatusListenerTransport(GpsStatus.NmeaListener listener) {
1490            this(listener, null);
1491        }
1492
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
1507        GnssStatusListenerTransport(GnssStatus.Callback callback) {
1508            this(callback, null);
1509        }
1510
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
1520        GnssStatusListenerTransport(OnNmeaMessageListener listener) {
1521            this(listener, null);
1522        }
1523
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
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
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
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
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
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)
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
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)
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)
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     */
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)
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
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)
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)
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     */
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")
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)
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)
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")
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     */
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")
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")
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     */
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)
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     */
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)
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
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)
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)
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)
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)
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     */
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     */
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
2096    private static void checkProvider(String provider) {
2097        if (provider == null) {
2098            throw new IllegalArgumentException("invalid provider: " + provider);
2099        }
2100    }
2101
2102    private static void checkCriteria(Criteria criteria) {
2103        if (criteria == null) {
2104            throw new IllegalArgumentException("invalid criteria: " + criteria);
2105        }
2106    }
2107
2108    private static void checkListener(LocationListener listener) {
2109        if (listener == null) {
2110            throw new IllegalArgumentException("invalid listener: " + listener);
2111        }
2112    }
2113
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
2129    private static void checkGeofence(Geofence fence) {
2130        if (fence == null) {
2131            throw new IllegalArgumentException("invalid geofence: " + fence);
2132        }
2133    }
2134}
2135