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