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