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