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