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.Intent;
21import android.os.Bundle;
22import android.os.Looper;
23import android.os.RemoteException;
24import android.os.Handler;
25import android.os.Message;
26import android.util.Log;
27
28import com.android.internal.location.DummyLocationProvider;
29
30import java.util.ArrayList;
31import java.util.HashMap;
32import java.util.List;
33
34/**
35 * This class provides access to the system location services.  These
36 * services allow applications to obtain periodic updates of the
37 * device's geographical location, or to fire an application-specified
38 * {@link Intent} when the device enters the proximity of a given
39 * geographical location.
40 *
41 * <p>You do not
42 * instantiate this class directly; instead, retrieve it through
43 * {@link android.content.Context#getSystemService
44 * Context.getSystemService(Context.LOCATION_SERVICE)}.
45 */
46public class LocationManager {
47    private static final String TAG = "LocationManager";
48    private ILocationManager mService;
49    private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners =
50            new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>();
51    private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners =
52            new HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport>();
53    private final GpsStatus mGpsStatus = new GpsStatus();
54
55    /**
56     * Name of the network location provider.  This provider determines location based on
57     * availability of cell tower and WiFi access points. Results are retrieved
58     * by means of a network lookup.
59     *
60     * Requires either of the permissions android.permission.ACCESS_COARSE_LOCATION
61     * or android.permission.ACCESS_FINE_LOCATION.
62     */
63    public static final String NETWORK_PROVIDER = "network";
64
65    /**
66     * Name of the GPS location provider. This provider determines location using
67     * satellites. Depending on conditions, this provider may take a while to return
68     * a location fix.
69     *
70     * Requires the permission android.permission.ACCESS_FINE_LOCATION.
71     *
72     * <p> The extras Bundle for the GPS location provider can contain the
73     * following key/value pairs:
74     *
75     * <ul>
76     * <li> satellites - the number of satellites used to derive the fix
77     * </ul>
78     */
79    public static final String GPS_PROVIDER = "gps";
80
81    /**
82     * A special location provider for receiving locations without actually initiating
83     * a location fix. This provider can be used to passively receive location updates
84     * when other applications or services request them without actually requesting
85     * the locations yourself.  This provider will return locations generated by other
86     * providers.  You can query the {@link Location#getProvider()} method to determine
87     * the origin of the location update.
88     *
89     * Requires the permission android.permission.ACCESS_FINE_LOCATION, although if the GPS
90     * is not enabled this provider might only return coarse fixes.
91     */
92    public static final String PASSIVE_PROVIDER = "passive";
93
94    /**
95     * Key used for the Bundle extra holding a boolean indicating whether
96     * a proximity alert is entering (true) or exiting (false)..
97     */
98    public static final String KEY_PROXIMITY_ENTERING = "entering";
99
100    /**
101     * Key used for a Bundle extra holding an Integer status value
102     * when a status change is broadcast using a PendingIntent.
103     */
104    public static final String KEY_STATUS_CHANGED = "status";
105
106    /**
107     * Key used for a Bundle extra holding an Boolean status value
108     * when a provider enabled/disabled event is broadcast using a PendingIntent.
109     */
110    public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
111
112    /**
113     * Key used for a Bundle extra holding a Location value
114     * when a location change is broadcast using a PendingIntent.
115     */
116    public static final String KEY_LOCATION_CHANGED = "location";
117
118    /**
119     * Broadcast intent action indicating that the GPS has either been
120     * enabled or disabled. An intent extra provides this state as a boolean,
121     * where {@code true} means enabled.
122     * @see #EXTRA_GPS_ENABLED
123     *
124     * {@hide}
125     */
126    public static final String GPS_ENABLED_CHANGE_ACTION =
127        "android.location.GPS_ENABLED_CHANGE";
128
129    /**
130     * Broadcast intent action when the configured location providers
131     * change.
132     */
133    public static final String PROVIDERS_CHANGED_ACTION =
134        "android.location.PROVIDERS_CHANGED";
135
136    /**
137     * Broadcast intent action indicating that the GPS has either started or
138     * stopped receiving GPS fixes. An intent extra provides this state as a
139     * boolean, where {@code true} means that the GPS is actively receiving fixes.
140     * @see #EXTRA_GPS_ENABLED
141     *
142     * {@hide}
143     */
144    public static final String GPS_FIX_CHANGE_ACTION =
145        "android.location.GPS_FIX_CHANGE";
146
147    /**
148     * The lookup key for a boolean that indicates whether GPS is enabled or
149     * disabled. {@code true} means GPS is enabled. Retrieve it with
150     * {@link android.content.Intent#getBooleanExtra(String,boolean)}.
151     *
152     * {@hide}
153     */
154    public static final String EXTRA_GPS_ENABLED = "enabled";
155
156    // Map from LocationListeners to their associated ListenerTransport objects
157    private HashMap<LocationListener,ListenerTransport> mListeners =
158        new HashMap<LocationListener,ListenerTransport>();
159
160    private class ListenerTransport extends ILocationListener.Stub {
161        private static final int TYPE_LOCATION_CHANGED = 1;
162        private static final int TYPE_STATUS_CHANGED = 2;
163        private static final int TYPE_PROVIDER_ENABLED = 3;
164        private static final int TYPE_PROVIDER_DISABLED = 4;
165
166        private LocationListener mListener;
167        private final Handler mListenerHandler;
168
169        ListenerTransport(LocationListener listener, Looper looper) {
170            mListener = listener;
171
172            if (looper == null) {
173                mListenerHandler = new Handler() {
174                    @Override
175                    public void handleMessage(Message msg) {
176                        _handleMessage(msg);
177                    }
178                };
179            } else {
180                mListenerHandler = new Handler(looper) {
181                    @Override
182                    public void handleMessage(Message msg) {
183                        _handleMessage(msg);
184                    }
185                };
186            }
187        }
188
189        public void onLocationChanged(Location location) {
190            Message msg = Message.obtain();
191            msg.what = TYPE_LOCATION_CHANGED;
192            msg.obj = location;
193            mListenerHandler.sendMessage(msg);
194        }
195
196        public void onStatusChanged(String provider, int status, Bundle extras) {
197            Message msg = Message.obtain();
198            msg.what = TYPE_STATUS_CHANGED;
199            Bundle b = new Bundle();
200            b.putString("provider", provider);
201            b.putInt("status", status);
202            if (extras != null) {
203                b.putBundle("extras", extras);
204            }
205            msg.obj = b;
206            mListenerHandler.sendMessage(msg);
207        }
208
209        public void onProviderEnabled(String provider) {
210            Message msg = Message.obtain();
211            msg.what = TYPE_PROVIDER_ENABLED;
212            msg.obj = provider;
213            mListenerHandler.sendMessage(msg);
214        }
215
216        public void onProviderDisabled(String provider) {
217            Message msg = Message.obtain();
218            msg.what = TYPE_PROVIDER_DISABLED;
219            msg.obj = provider;
220            mListenerHandler.sendMessage(msg);
221        }
222
223        private void _handleMessage(Message msg) {
224            switch (msg.what) {
225                case TYPE_LOCATION_CHANGED:
226                    Location location = new Location((Location) msg.obj);
227                    mListener.onLocationChanged(location);
228                    break;
229                case TYPE_STATUS_CHANGED:
230                    Bundle b = (Bundle) msg.obj;
231                    String provider = b.getString("provider");
232                    int status = b.getInt("status");
233                    Bundle extras = b.getBundle("extras");
234                    mListener.onStatusChanged(provider, status, extras);
235                    break;
236                case TYPE_PROVIDER_ENABLED:
237                    mListener.onProviderEnabled((String) msg.obj);
238                    break;
239                case TYPE_PROVIDER_DISABLED:
240                    mListener.onProviderDisabled((String) msg.obj);
241                    break;
242            }
243            try {
244                mService.locationCallbackFinished(this);
245            } catch (RemoteException e) {
246                Log.e(TAG, "locationCallbackFinished: RemoteException", e);
247            }
248        }
249    }
250    /**
251     * @hide - hide this constructor because it has a parameter
252     * of type ILocationManager, which is a system private class. The
253     * right way to create an instance of this class is using the
254     * factory Context.getSystemService.
255     */
256    public LocationManager(ILocationManager service) {
257        mService = service;
258    }
259
260    private LocationProvider createProvider(String name, Bundle info) {
261        DummyLocationProvider provider =
262            new DummyLocationProvider(name, mService);
263        provider.setRequiresNetwork(info.getBoolean("network"));
264        provider.setRequiresSatellite(info.getBoolean("satellite"));
265        provider.setRequiresCell(info.getBoolean("cell"));
266        provider.setHasMonetaryCost(info.getBoolean("cost"));
267        provider.setSupportsAltitude(info.getBoolean("altitude"));
268        provider.setSupportsSpeed(info.getBoolean("speed"));
269        provider.setSupportsBearing(info.getBoolean("bearing"));
270        provider.setPowerRequirement(info.getInt("power"));
271        provider.setAccuracy(info.getInt("accuracy"));
272        return provider;
273    }
274
275    /**
276     * Returns a list of the names of all known location providers.  All
277     * providers are returned, including ones that are not permitted to be
278     * accessed by the calling activity or are currently disabled.
279     *
280     * @return list of Strings containing names of the providers
281     */
282    public List<String> getAllProviders() {
283        if (false) {
284            Log.d(TAG, "getAllProviders");
285        }
286        try {
287            return mService.getAllProviders();
288        } catch (RemoteException ex) {
289            Log.e(TAG, "getAllProviders: RemoteException", ex);
290        }
291        return null;
292    }
293
294    /**
295     * Returns a list of the names of location providers.  Only providers that
296     * are permitted to be accessed by the calling activity will be returned.
297     *
298     * @param enabledOnly if true then only the providers which are currently
299     * enabled are returned.
300     * @return list of Strings containing names of the providers
301     */
302    public List<String> getProviders(boolean enabledOnly) {
303        try {
304            return mService.getProviders(null, enabledOnly);
305        } catch (RemoteException ex) {
306            Log.e(TAG, "getProviders: RemoteException", ex);
307        }
308        return null;
309    }
310
311    /**
312     * Returns the information associated with the location provider of the
313     * given name, or null if no provider exists by that name.
314     *
315     * @param name the provider name
316     * @return a LocationProvider, or null
317     *
318     * @throws IllegalArgumentException if name is null
319     * @throws SecurityException if the caller is not permitted to access the
320     * given provider.
321     */
322    public LocationProvider getProvider(String name) {
323        if (name == null) {
324            throw new IllegalArgumentException("name==null");
325        }
326        try {
327            Bundle info = mService.getProviderInfo(name);
328            if (info == null) {
329                return null;
330            }
331            return createProvider(name, info);
332        } catch (RemoteException ex) {
333            Log.e(TAG, "getProvider: RemoteException", ex);
334        }
335        return null;
336    }
337
338    /**
339     * Returns a list of the names of LocationProviders that satisfy the given
340     * criteria, or null if none do.  Only providers that are permitted to be
341     * accessed by the calling activity will be returned.
342     *
343     * @param criteria the criteria that the returned providers must match
344     * @param enabledOnly if true then only the providers which are currently
345     * enabled are returned.
346     * @return list of Strings containing names of the providers
347     */
348    public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
349        if (criteria == null) {
350            throw new IllegalArgumentException("criteria==null");
351        }
352        try {
353            return mService.getProviders(criteria, enabledOnly);
354        } catch (RemoteException ex) {
355            Log.e(TAG, "getProviders: RemoteException", ex);
356        }
357        return null;
358    }
359
360    /**
361     * Returns the name of the provider that best meets the given criteria. Only providers
362     * that are permitted to be accessed by the calling activity will be
363     * returned.  If several providers meet the criteria, the one with the best
364     * accuracy is returned.  If no provider meets the criteria,
365     * the criteria are loosened in the following sequence:
366     *
367     * <ul>
368     * <li> power requirement
369     * <li> accuracy
370     * <li> bearing
371     * <li> speed
372     * <li> altitude
373     * </ul>
374     *
375     * <p> Note that the requirement on monetary cost is not removed
376     * in this process.
377     *
378     * @param criteria the criteria that need to be matched
379     * @param enabledOnly if true then only a provider that is currently enabled is returned
380     * @return name of the provider that best matches the requirements
381     */
382    public String getBestProvider(Criteria criteria, boolean enabledOnly) {
383        if (criteria == null) {
384            throw new IllegalArgumentException("criteria==null");
385        }
386        try {
387            return mService.getBestProvider(criteria, enabledOnly);
388        } catch (RemoteException ex) {
389            Log.e(TAG, "getBestProvider: RemoteException", ex);
390        }
391        return null;
392    }
393
394    /**
395     * Registers the current activity to be notified periodically by
396     * the named provider.  Periodically, the supplied LocationListener will
397     * be called with the current Location or with status updates.
398     *
399     * <p> It may take a while to receive the most recent location. If
400     * an immediate location is required, applications may use the
401     * {@link #getLastKnownLocation(String)} method.
402     *
403     * <p> In case the provider is disabled by the user, updates will stop,
404     * and the {@link LocationListener#onProviderDisabled(String)}
405     * method will be called. As soon as the provider is enabled again,
406     * the {@link LocationListener#onProviderEnabled(String)} method will
407     * be called and location updates will start again.
408     *
409     * <p> The frequency of notification may be controlled using the
410     * minTime and minDistance parameters. If minTime is greater than 0,
411     * the LocationManager could potentially rest for minTime milliseconds
412     * between location updates to conserve power. If minDistance is greater than 0,
413     * a location will only be broadcasted if the device moves by minDistance meters.
414     * To obtain notifications as frequently as possible, set both parameters to 0.
415     *
416     * <p> Background services should be careful about setting a sufficiently high
417     * minTime so that the device doesn't consume too much power by keeping the
418     * GPS or wireless radios on all the time. In particular, values under 60000ms
419     * are not recommended.
420     *
421     * <p> The calling thread must be a {@link android.os.Looper} thread such as
422     * the main thread of the calling Activity.
423     *
424     * @param provider the name of the provider with which to register
425     * @param minTime the minimum time interval for notifications, in
426     * milliseconds. This field is only used as a hint to conserve power, and actual
427     * time between location updates may be greater or lesser than this value.
428     * @param minDistance the minimum distance interval for notifications,
429     * in meters
430     * @param listener a {#link LocationListener} whose
431     * {@link LocationListener#onLocationChanged} method will be called for
432     * each location update
433     *
434     * @throws IllegalArgumentException if provider or listener is null
435     * @throws RuntimeException if the calling thread has no Looper
436     * @throws SecurityException if no suitable permission is present for the provider.
437     */
438    public void requestLocationUpdates(String provider,
439        long minTime, float minDistance, LocationListener listener) {
440        if (provider == null) {
441            throw new IllegalArgumentException("provider==null");
442        }
443        if (listener == null) {
444            throw new IllegalArgumentException("listener==null");
445        }
446        _requestLocationUpdates(provider, null, minTime, minDistance, false, listener, null);
447    }
448
449    /**
450     * Registers the current activity to be notified periodically by
451     * the named provider.  Periodically, the supplied LocationListener will
452     * be called with the current Location or with status updates.
453     *
454     * <p> It may take a while to receive the most recent location. If
455     * an immediate location is required, applications may use the
456     * {@link #getLastKnownLocation(String)} method.
457     *
458     * <p> In case the provider is disabled by the user, updates will stop,
459     * and the {@link LocationListener#onProviderDisabled(String)}
460     * method will be called. As soon as the provider is enabled again,
461     * the {@link LocationListener#onProviderEnabled(String)} method will
462     * be called and location updates will start again.
463     *
464     * <p> The frequency of notification may be controlled using the
465     * minTime and minDistance parameters. If minTime is greater than 0,
466     * the LocationManager could potentially rest for minTime milliseconds
467     * between location updates to conserve power. If minDistance is greater than 0,
468     * a location will only be broadcasted if the device moves by minDistance meters.
469     * To obtain notifications as frequently as possible, set both parameters to 0.
470     *
471     * <p> Background services should be careful about setting a sufficiently high
472     * minTime so that the device doesn't consume too much power by keeping the
473     * GPS or wireless radios on all the time. In particular, values under 60000ms
474     * are not recommended.
475     *
476     * <p> The supplied Looper is used to implement the callback mechanism.
477     *
478     * @param provider the name of the provider with which to register
479     * @param minTime the minimum time interval for notifications, in
480     * milliseconds. This field is only used as a hint to conserve power, and actual
481     * time between location updates may be greater or lesser than this value.
482     * @param minDistance the minimum distance interval for notifications,
483     * in meters
484     * @param listener a {#link LocationListener} whose
485     * {@link LocationListener#onLocationChanged} method will be called for
486     * each location update
487     * @param looper a Looper object whose message queue will be used to
488     * implement the callback mechanism.
489     * If looper is null then the callbacks will be called on the main thread.
490     *
491     * @throws IllegalArgumentException if provider is null or doesn't exist
492     * @throws IllegalArgumentException if listener is null
493     * @throws SecurityException if no suitable permission is present for the provider.
494     */
495    public void requestLocationUpdates(String provider,
496        long minTime, float minDistance, LocationListener listener,
497        Looper looper) {
498        if (provider == null) {
499            throw new IllegalArgumentException("provider==null");
500        }
501        if (listener == null) {
502            throw new IllegalArgumentException("listener==null");
503        }
504        _requestLocationUpdates(provider, null, minTime, minDistance, false, listener, looper);
505    }
506
507    /**
508     * Registers the current activity to be notified periodically based on
509     * the specified criteria.  Periodically, the supplied LocationListener will
510     * be called with the current Location or with status updates.
511     *
512     * <p> It may take a while to receive the most recent location. If
513     * an immediate location is required, applications may use the
514     * {@link #getLastKnownLocation(String)} method.
515     *
516     * <p> In case the provider is disabled by the user, updates will stop,
517     * and the {@link LocationListener#onProviderDisabled(String)}
518     * method will be called. As soon as the provider is enabled again,
519     * the {@link LocationListener#onProviderEnabled(String)} method will
520     * be called and location updates will start again.
521     *
522     * <p> The frequency of notification may be controlled using the
523     * minTime and minDistance parameters. If minTime is greater than 0,
524     * the LocationManager could potentially rest for minTime milliseconds
525     * between location updates to conserve power. If minDistance is greater than 0,
526     * a location will only be broadcasted if the device moves by minDistance meters.
527     * To obtain notifications as frequently as possible, set both parameters to 0.
528     *
529     * <p> Background services should be careful about setting a sufficiently high
530     * minTime so that the device doesn't consume too much power by keeping the
531     * GPS or wireless radios on all the time. In particular, values under 60000ms
532     * are not recommended.
533     *
534     * <p> The supplied Looper is used to implement the callback mechanism.
535     *
536     * @param minTime the minimum time interval for notifications, in
537     * milliseconds. This field is only used as a hint to conserve power, and actual
538     * time between location updates may be greater or lesser than this value.
539     * @param minDistance the minimum distance interval for notifications,
540     * in meters
541     * @param criteria contains parameters for the location manager to choose the
542     * appropriate provider and parameters to compute the location
543     * @param listener a {#link LocationListener} whose
544     * {@link LocationListener#onLocationChanged} method will be called for
545     * each location update
546     * @param looper a Looper object whose message queue will be used to
547     * implement the callback mechanism.
548     * If looper is null then the callbacks will be called on the main thread.
549     *
550     * @throws IllegalArgumentException if criteria is null
551     * @throws IllegalArgumentException if listener is null
552     * @throws SecurityException if no suitable permission is present to access
553     * the location services.
554     */
555    public void requestLocationUpdates(long minTime, float minDistance,
556            Criteria criteria, LocationListener listener, Looper looper) {
557        if (criteria == null) {
558            throw new IllegalArgumentException("criteria==null");
559        }
560        if (listener == null) {
561            throw new IllegalArgumentException("listener==null");
562        }
563        _requestLocationUpdates(null, criteria, minTime, minDistance, false, listener, looper);
564    }
565
566    private void _requestLocationUpdates(String provider, Criteria criteria, long minTime,
567            float minDistance, boolean singleShot, LocationListener listener, Looper looper) {
568        if (minTime < 0L) {
569            minTime = 0L;
570        }
571        if (minDistance < 0.0f) {
572            minDistance = 0.0f;
573        }
574
575        try {
576            synchronized (mListeners) {
577                ListenerTransport transport = mListeners.get(listener);
578                if (transport == null) {
579                    transport = new ListenerTransport(listener, looper);
580                }
581                mListeners.put(listener, transport);
582                mService.requestLocationUpdates(provider, criteria, minTime, minDistance, singleShot, transport);
583            }
584        } catch (RemoteException ex) {
585            Log.e(TAG, "requestLocationUpdates: DeadObjectException", ex);
586        }
587    }
588
589    /**
590     * Registers the current activity to be notified periodically by
591     * the named provider.  Periodically, the supplied PendingIntent will
592     * be broadcast with the current Location or with status updates.
593     *
594     * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
595     *
596     * <p> It may take a while to receive the most recent location. If
597     * an immediate location is required, applications may use the
598     * {@link #getLastKnownLocation(String)} method.
599     *
600     * <p> The frequency of notification or new locations may be controlled using the
601     * minTime and minDistance parameters. If minTime is greater than 0,
602     * the LocationManager could potentially rest for minTime milliseconds
603     * between location updates to conserve power. If minDistance is greater than 0,
604     * a location will only be broadcast if the device moves by minDistance meters.
605     * To obtain notifications as frequently as possible, set both parameters to 0.
606     *
607     * <p> Background services should be careful about setting a sufficiently high
608     * minTime so that the device doesn't consume too much power by keeping the
609     * GPS or wireless radios on all the time. In particular, values under 60000ms
610     * are not recommended.
611     *
612     * <p> In case the provider is disabled by the user, updates will stop,
613     * and an intent will be sent with an extra with key KEY_PROVIDER_ENABLED and a boolean value
614     * of false.  If the provider is re-enabled, an intent will be sent with an
615     * extra with key KEY_PROVIDER_ENABLED and a boolean value of true and location updates will
616     * start again.
617     *
618     * <p> If the provider's status changes, an intent will be sent with an extra with key
619     * KEY_STATUS_CHANGED and an integer value indicating the new status.  Any extras associated
620     * with the status update will be sent as well.
621     *
622     * @param provider the name of the provider with which to register
623     * @param minTime the minimum time interval for notifications, in
624     * milliseconds. This field is only used as a hint to conserve power, and actual
625     * time between location updates may be greater or lesser than this value.
626     * @param minDistance the minimum distance interval for notifications,
627     * in meters
628     * @param intent a {#link PendingIntent} to be sent for each location update
629     *
630     * @throws IllegalArgumentException if provider is null or doesn't exist
631     * @throws IllegalArgumentException if intent is null
632     * @throws SecurityException if no suitable permission is present for the provider.
633     */
634    public void requestLocationUpdates(String provider,
635            long minTime, float minDistance, PendingIntent intent) {
636        if (provider == null) {
637            throw new IllegalArgumentException("provider==null");
638        }
639        if (intent == null) {
640            throw new IllegalArgumentException("intent==null");
641        }
642        _requestLocationUpdates(provider, null, minTime, minDistance, false, intent);
643    }
644
645    /**
646     * Registers the current activity to be notified periodically based on
647     * the specified criteria.  Periodically, the supplied PendingIntent will
648     * be broadcast with the current Location or with status updates.
649     *
650     * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
651     *
652     * <p> It may take a while to receive the most recent location. If
653     * an immediate location is required, applications may use the
654     * {@link #getLastKnownLocation(String)} method.
655     *
656     * <p> The frequency of notification or new locations may be controlled using the
657     * minTime and minDistance parameters. If minTime is greater than 0,
658     * the LocationManager could potentially rest for minTime milliseconds
659     * between location updates to conserve power. If minDistance is greater than 0,
660     * a location will only be broadcast if the device moves by minDistance meters.
661     * To obtain notifications as frequently as possible, set both parameters to 0.
662     *
663     * <p> Background services should be careful about setting a sufficiently high
664     * minTime so that the device doesn't consume too much power by keeping the
665     * GPS or wireless radios on all the time. In particular, values under 60000ms
666     * are not recommended.
667     *
668     * <p> In case the provider is disabled by the user, updates will stop,
669     * and an intent will be sent with an extra with key KEY_PROVIDER_ENABLED and a boolean value
670     * of false.  If the provider is re-enabled, an intent will be sent with an
671     * extra with key KEY_PROVIDER_ENABLED and a boolean value of true and location updates will
672     * start again.
673     *
674     * <p> If the provider's status changes, an intent will be sent with an extra with key
675     * KEY_STATUS_CHANGED and an integer value indicating the new status.  Any extras associated
676     * with the status update will be sent as well.
677     *
678     * @param minTime the minimum time interval for notifications, in
679     * milliseconds. This field is only used as a hint to conserve power, and actual
680     * time between location updates may be greater or lesser than this value.
681     * @param minDistance the minimum distance interval for notifications,
682     * in meters
683     * @param criteria contains parameters for the location manager to choose the
684     * appropriate provider and parameters to compute the location
685     * @param intent a {#link PendingIntent} to be sent for each location update
686     *
687     * @throws IllegalArgumentException if provider is null or doesn't exist
688     * @throws IllegalArgumentException if intent is null
689     * @throws SecurityException if no suitable permission is present for the provider.
690     */
691    public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria, PendingIntent intent) {
692        if (criteria == null) {
693            throw new IllegalArgumentException("criteria==null");
694        }
695        if (intent == null) {
696            throw new IllegalArgumentException("intent==null");
697        }
698        _requestLocationUpdates(null, criteria, minTime, minDistance, false, intent);
699    }
700
701    private void _requestLocationUpdates(String provider, Criteria criteria,
702            long minTime, float minDistance, boolean singleShot, PendingIntent intent) {
703        if (minTime < 0L) {
704            minTime = 0L;
705        }
706        if (minDistance < 0.0f) {
707            minDistance = 0.0f;
708        }
709
710        try {
711            mService.requestLocationUpdatesPI(provider, criteria, minTime, minDistance, singleShot, intent);
712        } catch (RemoteException ex) {
713            Log.e(TAG, "requestLocationUpdates: RemoteException", ex);
714        }
715    }
716
717    /**
718     * Requests a single location update from the named provider.
719     *
720     * <p> It may take a while to receive the most recent location. If
721     * an immediate location is required, applications may use the
722     * {@link #getLastKnownLocation(String)} method.
723     *
724     * <p> In case the provider is disabled by the user, the update will not be received,
725     * and the {@link LocationListener#onProviderDisabled(String)}
726     * method will be called. As soon as the provider is enabled again,
727     * the {@link LocationListener#onProviderEnabled(String)} method will
728     * be called and location updates will start again.
729     *
730     * <p> The supplied Looper is used to implement the callback mechanism.
731     *
732     * @param provider the name of the provider with which to register
733     * @param listener a {#link LocationListener} whose
734     * {@link LocationListener#onLocationChanged} method will be called when
735     * the location update is available
736     * @param looper a Looper object whose message queue will be used to
737     * implement the callback mechanism.
738     * If looper is null then the callbacks will be called on the main thread.
739     *
740     * @throws IllegalArgumentException if provider is null or doesn't exist
741     * @throws IllegalArgumentException if listener is null
742     * @throws SecurityException if no suitable permission is present for the provider.
743     */
744    public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) {
745        if (provider == null) {
746            throw new IllegalArgumentException("provider==null");
747        }
748        if (listener == null) {
749            throw new IllegalArgumentException("listener==null");
750        }
751        _requestLocationUpdates(provider, null, 0L, 0.0f, true, listener, looper);
752    }
753
754    /**
755     * Requests a single location update based on the specified criteria.
756     *
757     * <p> It may take a while to receive the most recent location. If
758     * an immediate location is required, applications may use the
759     * {@link #getLastKnownLocation(String)} method.
760     *
761     * <p> In case the provider is disabled by the user, the update will not be received,
762     * and the {@link LocationListener#onProviderDisabled(String)}
763     * method will be called. As soon as the provider is enabled again,
764     * the {@link LocationListener#onProviderEnabled(String)} method will
765     * be called and location updates will start again.
766     *
767     * <p> The supplied Looper is used to implement the callback mechanism.
768     *
769     * @param criteria contains parameters for the location manager to choose the
770     * appropriate provider and parameters to compute the location
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.
776     * If looper is null then the callbacks will be called on the current thread.
777     *
778     * @throws IllegalArgumentException if criteria is null
779     * @throws IllegalArgumentException if listener is null
780     * @throws SecurityException if no suitable permission is present to access
781     * the location services.
782     */
783    public void requestSingleUpdate(Criteria criteria, LocationListener listener, Looper looper) {
784        if (criteria == null) {
785            throw new IllegalArgumentException("criteria==null");
786        }
787        if (listener == null) {
788            throw new IllegalArgumentException("listener==null");
789        }
790        _requestLocationUpdates(null, criteria, 0L, 0.0f, true, listener, looper);
791    }
792
793    /**
794     * Requests a single location update from the named provider.
795     *
796     * <p> It may take a while to receive the most recent location. If
797     * an immediate location is required, applications may use the
798     * {@link #getLastKnownLocation(String)} method.
799     *
800     * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
801     *
802     * <p> In case the provider is disabled by the user, the update will not be received,
803     * and the {@link LocationListener#onProviderDisabled(String)}
804     * method will be called. As soon as the provider is enabled again,
805     * the {@link LocationListener#onProviderEnabled(String)} method will
806     * be called and location updates will start again.
807     *
808     * @param provider the name of the provider with which to register
809     * @param intent a {#link PendingIntent} to be sent for the location update
810     *
811     * @throws IllegalArgumentException if provider is null or doesn't exist
812     * @throws IllegalArgumentException if intent is null
813     * @throws SecurityException if no suitable permission is present for the provider.
814     */
815    public void requestSingleUpdate(String provider, PendingIntent intent) {
816        if (provider == null) {
817            throw new IllegalArgumentException("provider==null");
818        }
819        if (intent == null) {
820            throw new IllegalArgumentException("intent==null");
821        }
822        _requestLocationUpdates(provider, null, 0L, 0.0f, true, intent);
823    }
824
825    /**
826     * Requests a single location update based on the specified criteria.
827     *
828     * <p> It may take a while to receive the most recent location. If
829     * an immediate location is required, applications may use the
830     * {@link #getLastKnownLocation(String)} method.
831     *
832     * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
833     *
834     * <p> In case the provider is disabled by the user, the update will not be received,
835     * and the {@link LocationListener#onProviderDisabled(String)}
836     * method will be called. As soon as the provider is enabled again,
837     * the {@link LocationListener#onProviderEnabled(String)} method will
838     * be called and location updates will start again.
839     *
840     * @param criteria contains parameters for the location manager to choose the
841     * appropriate provider and parameters to compute the location
842     * @param intent a {#link PendingIntent} to be sent for the location update
843     *
844     * @throws IllegalArgumentException if provider is null or doesn't exist
845     * @throws IllegalArgumentException if intent is null
846     * @throws SecurityException if no suitable permission is present for the provider.
847     */
848    public void requestSingleUpdate(Criteria criteria, PendingIntent intent) {
849        if (criteria == null) {
850            throw new IllegalArgumentException("criteria==null");
851        }
852        if (intent == null) {
853            throw new IllegalArgumentException("intent==null");
854        }
855        _requestLocationUpdates(null, criteria, 0L, 0.0f, true, intent);
856    }
857
858    /**
859     * Removes any current registration for location updates of the current activity
860     * with the given LocationListener.  Following this call, updates will no longer
861     * occur for this listener.
862     *
863     * @param listener {#link LocationListener} object that no longer needs location updates
864     * @throws IllegalArgumentException if listener is null
865     */
866    public void removeUpdates(LocationListener listener) {
867        if (listener == null) {
868            throw new IllegalArgumentException("listener==null");
869        }
870        if (false) {
871            Log.d(TAG, "removeUpdates: listener = " + listener);
872        }
873        try {
874            ListenerTransport transport = mListeners.remove(listener);
875            if (transport != null) {
876                mService.removeUpdates(transport);
877            }
878        } catch (RemoteException ex) {
879            Log.e(TAG, "removeUpdates: DeadObjectException", ex);
880        }
881    }
882
883    /**
884     * Removes any current registration for location updates of the current activity
885     * with the given PendingIntent.  Following this call, updates will no longer
886     * occur for this intent.
887     *
888     * @param intent {#link PendingIntent} object that no longer needs location updates
889     * @throws IllegalArgumentException if intent is null
890     */
891    public void removeUpdates(PendingIntent intent) {
892        if (intent == null) {
893            throw new IllegalArgumentException("intent==null");
894        }
895        if (false) {
896            Log.d(TAG, "removeUpdates: intent = " + intent);
897        }
898        try {
899            mService.removeUpdatesPI(intent);
900        } catch (RemoteException ex) {
901            Log.e(TAG, "removeUpdates: RemoteException", ex);
902        }
903    }
904
905    /**
906     * Sets a proximity alert for the location given by the position
907     * (latitude, longitude) and the given radius.  When the device
908     * detects that it has entered or exited the area surrounding the
909     * location, the given PendingIntent will be used to create an Intent
910     * to be fired.
911     *
912     * <p> The fired Intent will have a boolean extra added with key
913     * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
914     * entering the proximity region; if false, it is exiting.
915     *
916     * <p> Due to the approximate nature of position estimation, if the
917     * device passes through the given area briefly, it is possible
918     * that no Intent will be fired.  Similarly, an Intent could be
919     * fired if the device passes very close to the given area but
920     * does not actually enter it.
921     *
922     * <p> After the number of milliseconds given by the expiration
923     * parameter, the location manager will delete this proximity
924     * alert and no longer monitor it.  A value of -1 indicates that
925     * there should be no expiration time.
926     *
927     * <p> In case the screen goes to sleep, checks for proximity alerts
928     * happen only once every 4 minutes. This conserves battery life by
929     * ensuring that the device isn't perpetually awake.
930     *
931     * <p> Internally, this method uses both {@link #NETWORK_PROVIDER}
932     * and {@link #GPS_PROVIDER}.
933     *
934     * @param latitude the latitude of the central point of the
935     * alert region
936     * @param longitude the longitude of the central point of the
937     * alert region
938     * @param radius the radius of the central point of the
939     * alert region, in meters
940     * @param expiration time for this proximity alert, in milliseconds,
941     * or -1 to indicate no expiration
942     * @param intent a PendingIntent that will be used to generate an Intent to
943     * fire when entry to or exit from the alert region is detected
944     *
945     * @throws SecurityException if no permission exists for the required
946     * providers.
947     */
948    public void addProximityAlert(double latitude, double longitude,
949        float radius, long expiration, PendingIntent intent) {
950        if (false) {
951            Log.d(TAG, "addProximityAlert: latitude = " + latitude +
952                ", longitude = " + longitude + ", radius = " + radius +
953                ", expiration = " + expiration +
954                ", intent = " + intent);
955        }
956        try {
957            mService.addProximityAlert(latitude, longitude, radius,
958                                       expiration, intent);
959        } catch (RemoteException ex) {
960            Log.e(TAG, "addProximityAlert: RemoteException", ex);
961        }
962    }
963
964    /**
965     * Removes the proximity alert with the given PendingIntent.
966     *
967     * @param intent the PendingIntent that no longer needs to be notified of
968     * proximity alerts
969     */
970    public void removeProximityAlert(PendingIntent intent) {
971        if (false) {
972            Log.d(TAG, "removeProximityAlert: intent = " + intent);
973        }
974        try {
975            mService.removeProximityAlert(intent);
976        } catch (RemoteException ex) {
977            Log.e(TAG, "removeProximityAlert: RemoteException", ex);
978        }
979    }
980
981    /**
982     * Returns the current enabled/disabled status of the given provider. If the
983     * user has enabled this provider in the Settings menu, true is returned
984     * otherwise false is returned
985     *
986     * @param provider the name of the provider
987     * @return true if the provider is enabled
988     *
989     * @throws SecurityException if no suitable permission is present for the provider.
990     * @throws IllegalArgumentException if provider is null
991     */
992    public boolean isProviderEnabled(String provider) {
993        if (provider == null) {
994            throw new IllegalArgumentException("provider==null");
995        }
996        try {
997            return mService.isProviderEnabled(provider);
998        } catch (RemoteException ex) {
999            Log.e(TAG, "isProviderEnabled: RemoteException", ex);
1000            return false;
1001        }
1002    }
1003
1004    /**
1005     * Returns a Location indicating the data from the last known
1006     * location fix obtained from the given provider.  This can be done
1007     * without starting the provider.  Note that this location could
1008     * be out-of-date, for example if the device was turned off and
1009     * moved to another location.
1010     *
1011     * <p> If the provider is currently disabled, null is returned.
1012     *
1013     * @param provider the name of the provider
1014     * @return the last known location for the provider, or null
1015     *
1016     * @throws SecurityException if no suitable permission is present for the provider.
1017     * @throws IllegalArgumentException if provider is null or doesn't exist
1018     */
1019    public Location getLastKnownLocation(String provider) {
1020        if (provider == null) {
1021            throw new IllegalArgumentException("provider==null");
1022        }
1023        try {
1024            return mService.getLastKnownLocation(provider);
1025        } catch (RemoteException ex) {
1026            Log.e(TAG, "getLastKnowLocation: RemoteException", ex);
1027            return null;
1028        }
1029    }
1030
1031    // Mock provider support
1032
1033    /**
1034     * Creates a mock location provider and adds it to the set of active providers.
1035     *
1036     * @param name the provider name
1037     * @param requiresNetwork
1038     * @param requiresSatellite
1039     * @param requiresCell
1040     * @param hasMonetaryCost
1041     * @param supportsAltitude
1042     * @param supportsSpeed
1043     * @param supportsBearing
1044     * @param powerRequirement
1045     * @param accuracy
1046     *
1047     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1048     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1049     * Settings.Secure.ALLOW_MOCK_LOCATION} system setting is not enabled
1050     * @throws IllegalArgumentException if a provider with the given name already exists
1051     */
1052    public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1053        boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1054        boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1055        try {
1056            mService.addTestProvider(name, requiresNetwork, requiresSatellite, requiresCell,
1057                hasMonetaryCost, supportsAltitude, supportsSpeed, supportsBearing, powerRequirement,
1058                accuracy);
1059        } catch (RemoteException ex) {
1060            Log.e(TAG, "addTestProvider: RemoteException", ex);
1061        }
1062    }
1063
1064    /**
1065     * Removes the mock location provider with the given name.
1066     *
1067     * @param provider the provider name
1068     *
1069     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1070     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1071     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1072     * @throws IllegalArgumentException if no provider with the given name exists
1073     */
1074    public void removeTestProvider(String provider) {
1075        try {
1076            mService.removeTestProvider(provider);
1077        } catch (RemoteException ex) {
1078            Log.e(TAG, "removeTestProvider: RemoteException", ex);
1079        }
1080    }
1081
1082    /**
1083     * Sets a mock location for the given provider.  This location will be used in place
1084     * of any actual location from the provider.
1085     *
1086     * @param provider the provider name
1087     * @param loc the mock location
1088     *
1089     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1090     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1091     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1092     * @throws IllegalArgumentException if no provider with the given name exists
1093     */
1094    public void setTestProviderLocation(String provider, Location loc) {
1095        try {
1096            mService.setTestProviderLocation(provider, loc);
1097        } catch (RemoteException ex) {
1098            Log.e(TAG, "setTestProviderLocation: RemoteException", ex);
1099        }
1100    }
1101
1102    /**
1103     * Removes any mock location associated with the given provider.
1104     *
1105     * @param provider the provider name
1106     *
1107     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1108     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1109     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1110     * @throws IllegalArgumentException if no provider with the given name exists
1111     */
1112    public void clearTestProviderLocation(String provider) {
1113        try {
1114            mService.clearTestProviderLocation(provider);
1115        } catch (RemoteException ex) {
1116            Log.e(TAG, "clearTestProviderLocation: RemoteException", ex);
1117        }
1118    }
1119
1120    /**
1121     * Sets a mock enabled value for the given provider.  This value will be used in place
1122     * of any actual value from the provider.
1123     *
1124     * @param provider the provider name
1125     * @param enabled the mock enabled value
1126     *
1127     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1128     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1129     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1130     * @throws IllegalArgumentException if no provider with the given name exists
1131     */
1132    public void setTestProviderEnabled(String provider, boolean enabled) {
1133        try {
1134            mService.setTestProviderEnabled(provider, enabled);
1135        } catch (RemoteException ex) {
1136            Log.e(TAG, "setTestProviderEnabled: RemoteException", ex);
1137        }
1138    }
1139
1140    /**
1141     * Removes any mock enabled value associated with the given provider.
1142     *
1143     * @param provider the provider name
1144     *
1145     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1146     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1147     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1148     * @throws IllegalArgumentException if no provider with the given name exists
1149     */
1150    public void clearTestProviderEnabled(String provider) {
1151        try {
1152            mService.clearTestProviderEnabled(provider);
1153        } catch (RemoteException ex) {
1154            Log.e(TAG, "clearTestProviderEnabled: RemoteException", ex);
1155        }
1156
1157    }
1158
1159    /**
1160     * Sets mock status values for the given provider.  These values will be used in place
1161     * of any actual values from the provider.
1162     *
1163     * @param provider the provider name
1164     * @param status the mock status
1165     * @param extras a Bundle containing mock extras
1166     * @param updateTime the mock update time
1167     *
1168     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1169     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1170     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1171     * @throws IllegalArgumentException if no provider with the given name exists
1172     */
1173    public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1174        try {
1175            mService.setTestProviderStatus(provider, status, extras, updateTime);
1176        } catch (RemoteException ex) {
1177            Log.e(TAG, "setTestProviderStatus: RemoteException", ex);
1178        }
1179    }
1180
1181    /**
1182     * Removes any mock status values associated with the given provider.
1183     *
1184     * @param provider the provider name
1185     *
1186     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1187     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1188     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1189     * @throws IllegalArgumentException if no provider with the given name exists
1190     */
1191    public void clearTestProviderStatus(String provider) {
1192        try {
1193            mService.clearTestProviderStatus(provider);
1194        } catch (RemoteException ex) {
1195            Log.e(TAG, "clearTestProviderStatus: RemoteException", ex);
1196        }
1197    }
1198
1199    // GPS-specific support
1200
1201    // This class is used to send GPS status events to the client's main thread.
1202    private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
1203
1204        private final GpsStatus.Listener mListener;
1205        private final GpsStatus.NmeaListener mNmeaListener;
1206
1207        // This must not equal any of the GpsStatus event IDs
1208        private static final int NMEA_RECEIVED = 1000;
1209
1210        private class Nmea {
1211            long mTimestamp;
1212            String mNmea;
1213
1214            Nmea(long timestamp, String nmea) {
1215                mTimestamp = timestamp;
1216                mNmea = nmea;
1217            }
1218        }
1219        private ArrayList<Nmea> mNmeaBuffer;
1220
1221        GpsStatusListenerTransport(GpsStatus.Listener listener) {
1222            mListener = listener;
1223            mNmeaListener = null;
1224        }
1225
1226        GpsStatusListenerTransport(GpsStatus.NmeaListener listener) {
1227            mNmeaListener = listener;
1228            mListener = null;
1229            mNmeaBuffer = new ArrayList<Nmea>();
1230        }
1231
1232        public void onGpsStarted() {
1233            if (mListener != null) {
1234                Message msg = Message.obtain();
1235                msg.what = GpsStatus.GPS_EVENT_STARTED;
1236                mGpsHandler.sendMessage(msg);
1237            }
1238        }
1239
1240        public void onGpsStopped() {
1241            if (mListener != null) {
1242                Message msg = Message.obtain();
1243                msg.what = GpsStatus.GPS_EVENT_STOPPED;
1244                mGpsHandler.sendMessage(msg);
1245            }
1246        }
1247
1248        public void onFirstFix(int ttff) {
1249            if (mListener != null) {
1250                mGpsStatus.setTimeToFirstFix(ttff);
1251                Message msg = Message.obtain();
1252                msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
1253                mGpsHandler.sendMessage(msg);
1254            }
1255        }
1256
1257        public void onSvStatusChanged(int svCount, int[] prns, float[] snrs,
1258                float[] elevations, float[] azimuths, int ephemerisMask,
1259                int almanacMask, int usedInFixMask) {
1260            if (mListener != null) {
1261                mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths,
1262                        ephemerisMask, almanacMask, usedInFixMask);
1263
1264                Message msg = Message.obtain();
1265                msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
1266                // remove any SV status messages already in the queue
1267                mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
1268                mGpsHandler.sendMessage(msg);
1269            }
1270        }
1271
1272        public void onNmeaReceived(long timestamp, String nmea) {
1273            if (mNmeaListener != null) {
1274                synchronized (mNmeaBuffer) {
1275                    mNmeaBuffer.add(new Nmea(timestamp, nmea));
1276                }
1277                Message msg = Message.obtain();
1278                msg.what = NMEA_RECEIVED;
1279                // remove any NMEA_RECEIVED messages already in the queue
1280                mGpsHandler.removeMessages(NMEA_RECEIVED);
1281                mGpsHandler.sendMessage(msg);
1282            }
1283        }
1284
1285        private final Handler mGpsHandler = new Handler() {
1286            @Override
1287            public void handleMessage(Message msg) {
1288                if (msg.what == NMEA_RECEIVED) {
1289                    synchronized (mNmeaBuffer) {
1290                        int length = mNmeaBuffer.size();
1291                        for (int i = 0; i < length; i++) {
1292                            Nmea nmea = mNmeaBuffer.get(i);
1293                            mNmeaListener.onNmeaReceived(nmea.mTimestamp, nmea.mNmea);
1294                        }
1295                        mNmeaBuffer.clear();
1296                    }
1297                } else {
1298                    // synchronize on mGpsStatus to ensure the data is copied atomically.
1299                    synchronized(mGpsStatus) {
1300                        mListener.onGpsStatusChanged(msg.what);
1301                    }
1302                }
1303            }
1304        };
1305    }
1306
1307    /**
1308     * Adds a GPS status listener.
1309     *
1310     * @param listener GPS status listener object to register
1311     *
1312     * @return true if the listener was successfully added
1313     *
1314     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
1315     */
1316    public boolean addGpsStatusListener(GpsStatus.Listener listener) {
1317        boolean result;
1318
1319        if (mGpsStatusListeners.get(listener) != null) {
1320            // listener is already registered
1321            return true;
1322        }
1323        try {
1324            GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
1325            result = mService.addGpsStatusListener(transport);
1326            if (result) {
1327                mGpsStatusListeners.put(listener, transport);
1328            }
1329        } catch (RemoteException e) {
1330            Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
1331            result = false;
1332        }
1333
1334        return result;
1335    }
1336
1337    /**
1338     * Removes a GPS status listener.
1339     *
1340     * @param listener GPS status listener object to remove
1341     */
1342    public void removeGpsStatusListener(GpsStatus.Listener listener) {
1343        try {
1344            GpsStatusListenerTransport transport = mGpsStatusListeners.remove(listener);
1345            if (transport != null) {
1346                mService.removeGpsStatusListener(transport);
1347            }
1348        } catch (RemoteException e) {
1349            Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
1350        }
1351    }
1352
1353    /**
1354     * Adds an NMEA listener.
1355     *
1356     * @param listener a {#link GpsStatus.NmeaListener} object to register
1357     *
1358     * @return true if the listener was successfully added
1359     *
1360     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
1361     */
1362    public boolean addNmeaListener(GpsStatus.NmeaListener listener) {
1363        boolean result;
1364
1365        if (mNmeaListeners.get(listener) != null) {
1366            // listener is already registered
1367            return true;
1368        }
1369        try {
1370            GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
1371            result = mService.addGpsStatusListener(transport);
1372            if (result) {
1373                mNmeaListeners.put(listener, transport);
1374            }
1375        } catch (RemoteException e) {
1376            Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
1377            result = false;
1378        }
1379
1380        return result;
1381    }
1382
1383    /**
1384     * Removes an NMEA listener.
1385     *
1386     * @param listener a {#link GpsStatus.NmeaListener} object to remove
1387     */
1388    public void removeNmeaListener(GpsStatus.NmeaListener listener) {
1389        try {
1390            GpsStatusListenerTransport transport = mNmeaListeners.remove(listener);
1391            if (transport != null) {
1392                mService.removeGpsStatusListener(transport);
1393            }
1394        } catch (RemoteException e) {
1395            Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
1396        }
1397    }
1398
1399     /**
1400     * Retrieves information about the current status of the GPS engine.
1401     * This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged}
1402     * callback to ensure that the data is copied atomically.
1403     *
1404     * The caller may either pass in a {@link GpsStatus} object to set with the latest
1405     * status information, or pass null to create a new {@link GpsStatus} object.
1406     *
1407     * @param status object containing GPS status details, or null.
1408     * @return status object containing updated GPS status.
1409     */
1410    public GpsStatus getGpsStatus(GpsStatus status) {
1411        if (status == null) {
1412            status = new GpsStatus();
1413       }
1414       status.setStatus(mGpsStatus);
1415       return status;
1416    }
1417
1418    /**
1419     * Sends additional commands to a location provider.
1420     * Can be used to support provider specific extensions to the Location Manager API
1421     *
1422     * @param provider name of the location provider.
1423     * @param command name of the command to send to the provider.
1424     * @param extras optional arguments for the command (or null).
1425     * The provider may optionally fill the extras Bundle with results from the command.
1426     *
1427     * @return true if the command succeeds.
1428     */
1429    public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1430        try {
1431            return mService.sendExtraCommand(provider, command, extras);
1432        } catch (RemoteException e) {
1433            Log.e(TAG, "RemoteException in sendExtraCommand: ", e);
1434            return false;
1435        }
1436    }
1437
1438    /**
1439     * Used by NetInitiatedActivity to report user response
1440     * for network initiated GPS fix requests.
1441     *
1442     * {@hide}
1443     */
1444    public boolean sendNiResponse(int notifId, int userResponse) {
1445    	try {
1446            return mService.sendNiResponse(notifId, userResponse);
1447        } catch (RemoteException e) {
1448            Log.e(TAG, "RemoteException in sendNiResponse: ", e);
1449            return false;
1450        }
1451    }
1452
1453}
1454