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