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