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