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