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