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