LocationManager.java revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
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     * Propagates the enabled/disabled state of the providers from the system
317     * settings to the providers themselves.
318     *
319     * {@hide}
320     */
321    public void updateProviders() {
322        try {
323            mService.updateProviders();
324        } catch (RemoteException ex) {
325            Log.e(TAG, "updateProviders: RemoteException", ex);
326        }
327    }
328
329    /**
330     * Returns the next looser power requirement, in the sequence:
331     *
332     * POWER_LOW -> POWER_MEDIUM -> POWER_HIGH -> NO_REQUIREMENT
333     */
334    private int nextPower(int power) {
335        switch (power) {
336        case Criteria.POWER_LOW:
337            return Criteria.POWER_MEDIUM;
338        case Criteria.POWER_MEDIUM:
339            return Criteria.POWER_HIGH;
340        case Criteria.POWER_HIGH:
341            return Criteria.NO_REQUIREMENT;
342        case Criteria.NO_REQUIREMENT:
343        default:
344            return Criteria.NO_REQUIREMENT;
345        }
346    }
347
348    /**
349     * Returns the next looser accuracy requirement, in the sequence:
350     *
351     * ACCURACY_FINE -> ACCURACY_APPROXIMATE-> NO_REQUIREMENT
352     */
353    private int nextAccuracy(int accuracy) {
354        if (accuracy == Criteria.ACCURACY_FINE) {
355            return Criteria.ACCURACY_COARSE;
356        } else {
357            return Criteria.NO_REQUIREMENT;
358        }
359    }
360
361    private abstract class LpComparator implements Comparator<LocationProvider> {
362
363        public int compare(int a1, int a2) {
364            if (a1 < a2) {
365                return -1;
366            } else if (a1 > a2) {
367                return 1;
368            } else {
369                return 0;
370            }
371        }
372
373        public int compare(float a1, float a2) {
374            if (a1 < a2) {
375                return -1;
376            } else if (a1 > a2) {
377                return 1;
378            } else {
379                return 0;
380            }
381        }
382    }
383
384    private class LpPowerComparator extends LpComparator {
385        public int compare(LocationProvider l1, LocationProvider l2) {
386            int a1 = l1.getPowerRequirement();
387            int a2 = l2.getPowerRequirement();
388            return compare(a1, a2); // Smaller is better
389         }
390
391         public boolean equals(LocationProvider l1, LocationProvider l2) {
392             int a1 = l1.getPowerRequirement();
393             int a2 = l2.getPowerRequirement();
394             return a1 == a2;
395         }
396    }
397
398    private class LpAccuracyComparator extends LpComparator {
399        public int compare(LocationProvider l1, LocationProvider l2) {
400            int a1 = l1.getAccuracy();
401            int a2 = l2.getAccuracy();
402            return compare(a1, a2); // Smaller is better
403         }
404
405         public boolean equals(LocationProvider l1, LocationProvider l2) {
406             int a1 = l1.getAccuracy();
407             int a2 = l2.getAccuracy();
408             return a1 == a2;
409         }
410    }
411
412    private class LpCapabilityComparator extends LpComparator {
413
414        private static final int ALTITUDE_SCORE = 4;
415        private static final int BEARING_SCORE = 4;
416        private static final int SPEED_SCORE = 4;
417
418        private int score(LocationProvider p) {
419            return (p.supportsAltitude() ? ALTITUDE_SCORE : 0) +
420                (p.supportsBearing() ? BEARING_SCORE : 0) +
421                (p.supportsSpeed() ? SPEED_SCORE : 0);
422        }
423
424        public int compare(LocationProvider l1, LocationProvider l2) {
425            int a1 = score(l1);
426            int a2 = score(l2);
427            return compare(-a1, -a2); // Bigger is better
428         }
429
430         public boolean equals(LocationProvider l1, LocationProvider l2) {
431             int a1 = score(l1);
432             int a2 = score(l2);
433             return a1 == a2;
434         }
435    }
436
437    private LocationProvider best(List<String> providerNames) {
438        List<LocationProvider> providers = new ArrayList<LocationProvider>(providerNames.size());
439        for (String name : providerNames) {
440            providers.add(getProvider(name));
441        }
442
443        if (providers.size() < 2) {
444            return providers.get(0);
445        }
446
447        // First, sort by power requirement
448        Collections.sort(providers, new LpPowerComparator());
449        int power = providers.get(0).getPowerRequirement();
450        if (power < providers.get(1).getPowerRequirement()) {
451            return providers.get(0);
452        }
453
454        int idx, size;
455
456        List<LocationProvider> tmp = new ArrayList<LocationProvider>();
457        idx = 0;
458        size = providers.size();
459        while ((idx < size) && (providers.get(idx).getPowerRequirement() == power)) {
460            tmp.add(providers.get(idx));
461            idx++;
462        }
463
464        // Next, sort by accuracy
465        Collections.sort(tmp, new LpAccuracyComparator());
466        int acc = tmp.get(0).getAccuracy();
467        if (acc < tmp.get(1).getAccuracy()) {
468            return tmp.get(0);
469        }
470
471        List<LocationProvider> tmp2 = new ArrayList<LocationProvider>();
472        idx = 0;
473        size = tmp.size();
474        while ((idx < size) && (tmp.get(idx).getAccuracy() == acc)) {
475            tmp2.add(tmp.get(idx));
476            idx++;
477        }
478
479        // Finally, sort by capability "score"
480        Collections.sort(tmp2, new LpCapabilityComparator());
481        return tmp2.get(0);
482    }
483
484    /**
485     * Returns the name of the provider that best meets the given criteria. Only providers
486     * that are permitted to be accessed by the calling activity will be
487     * returned.  If several providers meet the criteria, the one with the best
488     * accuracy is returned.  If no provider meets the criteria,
489     * the criteria are loosened in the following sequence:
490     *
491     * <ul>
492     * <li> power requirement
493     * <li> accuracy
494     * <li> bearing
495     * <li> speed
496     * <li> altitude
497     * </ul>
498     *
499     * <p> Note that the requirement on monetary cost is not removed
500     * in this process.
501     *
502     * @param criteria the criteria that need to be matched
503     * @param enabledOnly if true then only a provider that is currently enabled is returned
504     * @return name of the provider that best matches the requirements
505     */
506    public String getBestProvider(Criteria criteria, boolean enabledOnly) {
507        List<String> goodProviders = getProviders(criteria, enabledOnly);
508        if (!goodProviders.isEmpty()) {
509            return best(goodProviders).getName();
510        }
511
512        // Make a copy of the criteria that we can modify
513        criteria = new Criteria(criteria);
514
515        // Loosen power requirement
516        int power = criteria.getPowerRequirement();
517        while (goodProviders.isEmpty() && (power != Criteria.NO_REQUIREMENT)) {
518            power = nextPower(power);
519            criteria.setPowerRequirement(power);
520            goodProviders = getProviders(criteria, enabledOnly);
521        }
522        if (!goodProviders.isEmpty()) {
523            return best(goodProviders).getName();
524        }
525
526//        // Loosen response time requirement
527//        int responseTime = criteria.getPreferredResponseTime();
528//        while (goodProviders.isEmpty() &&
529//            (responseTime != Criteria.NO_REQUIREMENT)) {
530//            responseTime += 1000;
531//            if (responseTime > 60000) {
532//                responseTime = Criteria.NO_REQUIREMENT;
533//            }
534//            criteria.setPreferredResponseTime(responseTime);
535//            goodProviders = getProviders(criteria);
536//        }
537//        if (!goodProviders.isEmpty()) {
538//            return best(goodProviders);
539//        }
540
541        // Loosen accuracy requirement
542        int accuracy = criteria.getAccuracy();
543        while (goodProviders.isEmpty() && (accuracy != Criteria.NO_REQUIREMENT)) {
544            accuracy = nextAccuracy(accuracy);
545            criteria.setAccuracy(accuracy);
546            goodProviders = getProviders(criteria, enabledOnly);
547        }
548        if (!goodProviders.isEmpty()) {
549            return best(goodProviders).getName();
550        }
551
552        // Remove bearing requirement
553        criteria.setBearingRequired(false);
554        goodProviders = getProviders(criteria, enabledOnly);
555        if (!goodProviders.isEmpty()) {
556            return best(goodProviders).getName();
557        }
558
559        // Remove speed requirement
560        criteria.setSpeedRequired(false);
561        goodProviders = getProviders(criteria, enabledOnly);
562        if (!goodProviders.isEmpty()) {
563            return best(goodProviders).getName();
564        }
565
566        // Remove altitude requirement
567        criteria.setAltitudeRequired(false);
568        goodProviders = getProviders(criteria, enabledOnly);
569        if (!goodProviders.isEmpty()) {
570            return best(goodProviders).getName();
571        }
572
573        return null;
574    }
575
576    /**
577     * Registers the current activity to be notified periodically by
578     * the named provider.  Periodically, the supplied LocationListener will
579     * be called with the current Location or with status updates.
580     *
581     * <p> It may take a while to receive the most recent location. If
582     * an immediate location is required, applications may use the
583     * {@link #getLastKnownLocation(String)} method.
584     *
585     * <p> In case the provider is disabled by the user, updates will stop,
586     * and the {@link LocationListener#onProviderDisabled(String)}
587     * method will be called. As soon as the provider is enabled again,
588     * the {@link LocationListener#onProviderEnabled(String)} method will
589     * be called and location updates will start again.
590     *
591     * <p> The frequency of notification may be controlled using the
592     * minTime and minDistance parameters. If minTime is greater than 0,
593     * the LocationManager could potentially rest for minTime milliseconds
594     * between location updates to conserve power. If minDistance is greater than 0,
595     * a location will only be broadcasted if the device moves by minDistance meters.
596     * To obtain notifications as frequently as possible, set both parameters to 0.
597     *
598     * <p> Background services should be careful about setting a sufficiently high
599     * minTime so that the device doesn't consume too much power by keeping the
600     * GPS or wireless radios on all the time. In particular, values under 60000ms
601     * are not recommended.
602     *
603     * <p> The calling thread must be a {@link android.os.Looper} thread such as
604     * the main thread of the calling Activity.
605     *
606     * @param provider the name of the provider with which to register
607     * @param minTime the minimum time interval for notifications, in
608     * milliseconds. This field is only used as a hint to conserve power, and actual
609     * time between location updates may be greater or lesser than this value.
610     * @param minDistance the minimum distance interval for notifications,
611     * in meters
612     * @param listener a {#link LocationListener} whose
613     * {@link LocationListener#onLocationChanged} method will be called for
614     * each location update
615     *
616     * @throws IllegalArgumentException if provider is null or doesn't exist
617     * @throws IllegalArgumentException if listener is null
618     * @throws RuntimeException if the calling thread has no Looper
619     * @throws SecurityException if no suitable permission is present for the provider.
620     */
621    public void requestLocationUpdates(String provider,
622        long minTime, float minDistance, LocationListener listener) {
623        if (provider == null) {
624            throw new IllegalArgumentException("provider==null");
625        }
626        if (listener == null) {
627            throw new IllegalArgumentException("listener==null");
628        }
629        _requestLocationUpdates(provider, minTime, minDistance, listener, null);
630    }
631
632    /**
633     * Registers the current activity to be notified periodically by
634     * the named provider.  Periodically, the supplied LocationListener will
635     * be called with the current Location or with status updates.
636     *
637     * <p> It may take a while to receive the most recent location. If
638     * an immediate location is required, applications may use the
639     * {@link #getLastKnownLocation(String)} method.
640     *
641     * <p> In case the provider is disabled by the user, updates will stop,
642     * and the {@link LocationListener#onProviderDisabled(String)}
643     * method will be called. As soon as the provider is enabled again,
644     * the {@link LocationListener#onProviderEnabled(String)} method will
645     * be called and location updates will start again.
646     *
647     * <p> The frequency of notification may be controlled using the
648     * minTime and minDistance parameters. If minTime is greater than 0,
649     * the LocationManager could potentially rest for minTime milliseconds
650     * between location updates to conserve power. If minDistance is greater than 0,
651     * a location will only be broadcasted if the device moves by minDistance meters.
652     * To obtain notifications as frequently as possible, set both parameters to 0.
653     *
654     * <p> Background services should be careful about setting a sufficiently high
655     * minTime so that the device doesn't consume too much power by keeping the
656     * GPS or wireless radios on all the time. In particular, values under 60000ms
657     * are not recommended.
658     *
659     * <p> The supplied Looper is used to implement the callback mechanism.
660     *
661     * @param provider the name of the provider with which to register
662     * @param minTime the minimum time interval for notifications, in
663     * milliseconds. This field is only used as a hint to conserve power, and actual
664     * time between location updates may be greater or lesser than this value.
665     * @param minDistance the minimum distance interval for notifications,
666     * in meters
667     * @param listener a {#link LocationListener} whose
668     * {@link LocationListener#onLocationChanged} method will be called for
669     * each location update
670     * @param looper a Looper object whose message queue will be used to
671     * implement the callback mechanism.
672     *
673     * @throws IllegalArgumentException if provider is null or doesn't exist
674     * @throws IllegalArgumentException if listener is null
675     * @throws IllegalArgumentException if looper is null
676     * @throws SecurityException if no suitable permission is present for the provider.
677     */
678    public void requestLocationUpdates(String provider,
679        long minTime, float minDistance, LocationListener listener,
680        Looper looper) {
681        if (provider == null) {
682            throw new IllegalArgumentException("provider==null");
683        }
684        if (listener == null) {
685            throw new IllegalArgumentException("listener==null");
686        }
687        if (looper == null) {
688            throw new IllegalArgumentException("looper==null");
689        }
690        _requestLocationUpdates(provider, minTime, minDistance, listener, looper);
691    }
692
693    private void _requestLocationUpdates(String provider,
694        long minTime, float minDistance, LocationListener listener,
695        Looper looper) {
696        if (minTime < 0L) {
697            minTime = 0L;
698        }
699        if (minDistance < 0.0f) {
700            minDistance = 0.0f;
701        }
702
703        try {
704            synchronized (mListeners) {
705                ListenerTransport transport = mListeners.get(listener);
706                if (transport == null) {
707                    transport = new ListenerTransport(listener, looper);
708                }
709                mListeners.put(listener, transport);
710                mService.requestLocationUpdates(provider, minTime, minDistance, transport);
711            }
712        } catch (RemoteException ex) {
713            Log.e(TAG, "requestLocationUpdates: DeadObjectException", ex);
714        }
715    }
716
717    /**
718     * Registers the current activity to be notified periodically by
719     * the named provider.  Periodically, the supplied PendingIntent will
720     * be broadcast with the current Location or with status updates.
721     *
722     * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
723     *
724     * <p> It may take a while to receive the most recent location. If
725     * an immediate location is required, applications may use the
726     * {@link #getLastKnownLocation(String)} method.
727     *
728     * <p> The frequency of notification or new locations may be controlled using the
729     * minTime and minDistance parameters. If minTime is greater than 0,
730     * the LocationManager could potentially rest for minTime milliseconds
731     * between location updates to conserve power. If minDistance is greater than 0,
732     * a location will only be broadcast if the device moves by minDistance meters.
733     * To obtain notifications as frequently as possible, set both parameters to 0.
734     *
735     * <p> Background services should be careful about setting a sufficiently high
736     * minTime so that the device doesn't consume too much power by keeping the
737     * GPS or wireless radios on all the time. In particular, values under 60000ms
738     * are not recommended.
739     *
740     * <p> In case the provider is disabled by the user, updates will stop,
741     * and an intent will be sent with an extra with key KEY_PROVIDER_ENABLED and a boolean value
742     * of false.  If the provider is re-enabled, an intent will be sent with an
743     * extra with key KEY_PROVIDER_ENABLED and a boolean value of true and location updates will
744     * start again.
745     *
746     * <p> If the provider's status changes, an intent will be sent with an extra with key
747     * KEY_STATUS_CHANGED and an integer value indicating the new status.  Any extras associated
748     * with the status update will be sent as well.
749     *
750     * @param provider the name of the provider with which to register
751     * @param minTime the minimum time interval for notifications, in
752     * milliseconds. This field is only used as a hint to conserve power, and actual
753     * time between location updates may be greater or lesser than this value.
754     * @param minDistance the minimum distance interval for notifications,
755     * in meters
756     * @param intent a {#link PendingIntet} to be sent for each location update
757     *
758     * @throws IllegalArgumentException if provider is null or doesn't exist
759     * @throws IllegalArgumentException if intent is null
760     * @throws SecurityException if no suitable permission is present for the provider.
761     */
762    public void requestLocationUpdates(String provider,
763            long minTime, float minDistance, PendingIntent intent) {
764        if (provider == null) {
765            throw new IllegalArgumentException("provider==null");
766        }
767        if (intent == null) {
768            throw new IllegalArgumentException("intent==null");
769        }
770        _requestLocationUpdates(provider, minTime, minDistance, intent);
771    }
772
773    private void _requestLocationUpdates(String provider,
774            long minTime, float minDistance, PendingIntent intent) {
775        if (minTime < 0L) {
776            minTime = 0L;
777        }
778        if (minDistance < 0.0f) {
779            minDistance = 0.0f;
780        }
781
782        try {
783            mService.requestLocationUpdatesPI(provider, minTime, minDistance, intent);
784        } catch (RemoteException ex) {
785            Log.e(TAG, "requestLocationUpdates: RemoteException", ex);
786        }
787    }
788
789    /**
790     * Removes any current registration for location updates of the current activity
791     * with the given LocationListener.  Following this call, updates will no longer
792     * occur for this listener.
793     *
794     * @param listener {#link LocationListener} object that no longer needs location updates
795     * @throws IllegalArgumentException if listener is null
796     */
797    public void removeUpdates(LocationListener listener) {
798        if (listener == null) {
799            throw new IllegalArgumentException("listener==null");
800        }
801        if (Config.LOGD) {
802            Log.d(TAG, "removeUpdates: listener = " + listener);
803        }
804        try {
805            ListenerTransport transport = mListeners.remove(listener);
806            if (transport != null) {
807                mService.removeUpdates(transport);
808            }
809        } catch (RemoteException ex) {
810            Log.e(TAG, "removeUpdates: DeadObjectException", ex);
811        }
812    }
813
814    /**
815     * Removes any current registration for location updates of the current activity
816     * with the given PendingIntent.  Following this call, updates will no longer
817     * occur for this intent.
818     *
819     * @param intent {#link PendingIntent} object that no longer needs location updates
820     * @throws IllegalArgumentException if intent is null
821     */
822    public void removeUpdates(PendingIntent intent) {
823        if (intent == null) {
824            throw new IllegalArgumentException("intent==null");
825        }
826        if (Config.LOGD) {
827            Log.d(TAG, "removeUpdates: intent = " + intent);
828        }
829        try {
830            mService.removeUpdatesPI(intent);
831        } catch (RemoteException ex) {
832            Log.e(TAG, "removeUpdates: RemoteException", ex);
833        }
834    }
835
836    /**
837     * Sets a proximity alert for the location given by the position
838     * (latitude, longitude) and the given radius.  When the device
839     * detects that it has entered or exited the area surrounding the
840     * location, the given PendingIntent will be used to create an Intent
841     * to be fired.
842     *
843     * <p> The fired Intent will have a boolean extra added with key
844     * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
845     * entering the proximity region; if false, it is exiting.
846     *
847     * <p> Due to the approximate nature of position estimation, if the
848     * device passes through the given area briefly, it is possible
849     * that no Intent will be fired.  Similarly, an Intent could be
850     * fired if the device passes very close to the given area but
851     * does not actually enter it.
852     *
853     * <p> After the number of milliseconds given by the expiration
854     * parameter, the location manager will delete this proximity
855     * alert and no longer monitor it.  A value of -1 indicates that
856     * there should be no expiration time.
857     *
858     * <p> In case the screen goes to sleep, checks for proximity alerts
859     * happen only once every 4 minutes. This conserves battery life by
860     * ensuring that the device isn't perpetually awake.
861     *
862     * <p> Internally, this method uses both {@link #NETWORK_PROVIDER}
863     * and {@link #GPS_PROVIDER}.
864     *
865     * @param latitude the latitude of the central point of the
866     * alert region
867     * @param longitude the longitude of the central point of the
868     * alert region
869     * @param radius the radius of the central point of the
870     * alert region, in meters
871     * @param expiration time for this proximity alert, in milliseconds,
872     * or -1 to indicate no expiration
873     * @param intent a PendingIntent that will be used to generate an Intent to
874     * fire when entry to or exit from the alert region is detected
875     *
876     * @throws SecurityException if no permission exists for the required
877     * providers.
878     */
879    public void addProximityAlert(double latitude, double longitude,
880        float radius, long expiration, PendingIntent intent) {
881        if (Config.LOGD) {
882            Log.d(TAG, "addProximityAlert: latitude = " + latitude +
883                ", longitude = " + longitude + ", radius = " + radius +
884                ", expiration = " + expiration +
885                ", intent = " + intent);
886        }
887        try {
888            mService.addProximityAlert(latitude, longitude, radius,
889                                       expiration, intent);
890        } catch (RemoteException ex) {
891            Log.e(TAG, "addProximityAlert: RemoteException", ex);
892        }
893    }
894
895    /**
896     * Removes the proximity alert with the given PendingIntent.
897     *
898     * @param intent the PendingIntent that no longer needs to be notified of
899     * proximity alerts
900     */
901    public void removeProximityAlert(PendingIntent intent) {
902        if (Config.LOGD) {
903            Log.d(TAG, "removeProximityAlert: intent = " + intent);
904        }
905        try {
906            mService.removeProximityAlert(intent);
907        } catch (RemoteException ex) {
908            Log.e(TAG, "removeProximityAlert: RemoteException", ex);
909        }
910    }
911
912    /**
913     * Returns the current enabled/disabled status of the given provider. If the
914     * user has enabled this provider in the Settings menu, true is returned
915     * otherwise false is returned
916     *
917     * @param provider the name of the provider
918     * @return true if the provider is enabled
919     *
920     * @throws SecurityException if no suitable permission is present for the provider.
921     * @throws IllegalArgumentException if provider is null or doesn't exist
922     */
923    public boolean isProviderEnabled(String provider) {
924        if (provider == null) {
925            throw new IllegalArgumentException("provider==null");
926        }
927        try {
928            return mService.isProviderEnabled(provider);
929        } catch (RemoteException ex) {
930            Log.e(TAG, "isProviderEnabled: RemoteException", ex);
931            return false;
932        }
933    }
934
935    /**
936     * Returns a Location indicating the data from the last known
937     * location fix obtained from the given provider.  This can be done
938     * without starting the provider.  Note that this location could
939     * be out-of-date, for example if the device was turned off and
940     * moved to another location.
941     *
942     * <p> If the provider is currently disabled, null is returned.
943     *
944     * @param provider the name of the provider
945     * @return the last known location for the provider, or null
946     *
947     * @throws SecurityException if no suitable permission is present for the provider.
948     * @throws IllegalArgumentException if provider is null or doesn't exist
949     */
950    public Location getLastKnownLocation(String provider) {
951        if (provider == null) {
952            throw new IllegalArgumentException("provider==null");
953        }
954        try {
955            return mService.getLastKnownLocation(provider);
956        } catch (RemoteException ex) {
957            Log.e(TAG, "getLastKnowLocation: RemoteException", ex);
958            return null;
959        }
960    }
961
962    // Mock provider support
963
964    /**
965     * Creates a mock location provider and adds it to the set of active providers.
966     *
967     * @param name the provider name
968     * @param requiresNetwork
969     * @param requiresSatellite
970     * @param requiresCell
971     * @param hasMonetaryCost
972     * @param supportsAltitude
973     * @param supportsSpeed
974     * @param supportsBearing
975     * @param powerRequirement
976     * @param accuracy
977     *
978     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
979     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
980     * Settings.Secure.ALLOW_MOCK_LOCATION} system setting is not enabled
981     * @throws IllegalArgumentException if a provider with the given name already exists
982     */
983    public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
984        boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
985        boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
986        try {
987            mService.addTestProvider(name, requiresNetwork, requiresSatellite, requiresCell,
988                hasMonetaryCost, supportsAltitude, supportsSpeed, supportsBearing, powerRequirement,
989                accuracy);
990        } catch (RemoteException ex) {
991            Log.e(TAG, "addTestProvider: RemoteException", ex);
992        }
993    }
994
995    /**
996     * Removes the mock location provider with the given name.
997     *
998     * @param provider the provider name
999     *
1000     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1001     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1002     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1003     * @throws IllegalArgumentException if no provider with the given name exists
1004     */
1005    public void removeTestProvider(String provider) {
1006        try {
1007            mService.removeTestProvider(provider);
1008        } catch (RemoteException ex) {
1009            Log.e(TAG, "removeTestProvider: RemoteException", ex);
1010        }
1011    }
1012
1013    /**
1014     * Sets a mock location for the given provider.  This location will be used in place
1015     * of any actual location from the provider.
1016     *
1017     * @param provider the provider name
1018     * @param loc the mock location
1019     *
1020     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1021     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1022     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1023     * @throws IllegalArgumentException if no provider with the given name exists
1024     */
1025    public void setTestProviderLocation(String provider, Location loc) {
1026        try {
1027            mService.setTestProviderLocation(provider, loc);
1028        } catch (RemoteException ex) {
1029            Log.e(TAG, "setTestProviderLocation: RemoteException", ex);
1030        }
1031    }
1032
1033    /**
1034     * Removes any mock location associated with the given provider.
1035     *
1036     * @param provider the provider name
1037     *
1038     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1039     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1040     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1041     * @throws IllegalArgumentException if no provider with the given name exists
1042     */
1043    public void clearTestProviderLocation(String provider) {
1044        try {
1045            mService.clearTestProviderLocation(provider);
1046        } catch (RemoteException ex) {
1047            Log.e(TAG, "clearTestProviderLocation: RemoteException", ex);
1048        }
1049    }
1050
1051    /**
1052     * Sets a mock enabled value for the given provider.  This value will be used in place
1053     * of any actual value from the provider.
1054     *
1055     * @param provider the provider name
1056     * @param enabled the mock enabled value
1057     *
1058     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1059     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1060     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1061     * @throws IllegalArgumentException if no provider with the given name exists
1062     */
1063    public void setTestProviderEnabled(String provider, boolean enabled) {
1064        try {
1065            mService.setTestProviderEnabled(provider, enabled);
1066        } catch (RemoteException ex) {
1067            Log.e(TAG, "setTestProviderEnabled: RemoteException", ex);
1068        }
1069    }
1070
1071    /**
1072     * Removes any mock enabled value associated with the given provider.
1073     *
1074     * @param provider the provider name
1075     *
1076     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1077     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1078     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1079     * @throws IllegalArgumentException if no provider with the given name exists
1080     */
1081    public void clearTestProviderEnabled(String provider) {
1082        try {
1083            mService.clearTestProviderEnabled(provider);
1084        } catch (RemoteException ex) {
1085            Log.e(TAG, "clearTestProviderEnabled: RemoteException", ex);
1086        }
1087
1088    }
1089
1090    /**
1091     * Sets mock status values for the given provider.  These values will be used in place
1092     * of any actual values from the provider.
1093     *
1094     * @param provider the provider name
1095     * @param status the mock status
1096     * @param extras a Bundle containing mock extras
1097     * @param updateTime the mock update time
1098     *
1099     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1100     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1101     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1102     * @throws IllegalArgumentException if no provider with the given name exists
1103     */
1104    public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1105        try {
1106            mService.setTestProviderStatus(provider, status, extras, updateTime);
1107        } catch (RemoteException ex) {
1108            Log.e(TAG, "setTestProviderStatus: RemoteException", ex);
1109        }
1110    }
1111
1112    /**
1113     * Removes any mock status values associated with the given provider.
1114     *
1115     * @param provider the provider name
1116     *
1117     * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
1118     * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
1119     * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
1120     * @throws IllegalArgumentException if no provider with the given name exists
1121     */
1122    public void clearTestProviderStatus(String provider) {
1123        try {
1124            mService.clearTestProviderStatus(provider);
1125        } catch (RemoteException ex) {
1126            Log.e(TAG, "clearTestProviderStatus: RemoteException", ex);
1127        }
1128    }
1129
1130    // GPS-specific support
1131
1132    // This class is used to send GPS status events to the client's main thread.
1133    private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
1134
1135        private final GpsStatus.Listener mListener;
1136
1137        GpsStatusListenerTransport(GpsStatus.Listener listener) {
1138            mListener = listener;
1139        }
1140
1141        public void onGpsStarted() {
1142            Message msg = Message.obtain();
1143            msg.what = GpsStatus.GPS_EVENT_STARTED;
1144            mGpsHandler.sendMessage(msg);
1145        }
1146
1147        public void onGpsStopped() {
1148            Message msg = Message.obtain();
1149            msg.what = GpsStatus.GPS_EVENT_STOPPED;
1150            mGpsHandler.sendMessage(msg);
1151        }
1152
1153        public void onFirstFix(int ttff) {
1154            mGpsStatus.setTimeToFirstFix(ttff);
1155            Message msg = Message.obtain();
1156            msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
1157            mGpsHandler.sendMessage(msg);
1158        }
1159
1160        public void onSvStatusChanged(int svCount, int[] prns, float[] snrs,
1161                float[] elevations, float[] azimuths, int ephemerisMask,
1162                int almanacMask, int usedInFixMask) {
1163            mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths,
1164                    ephemerisMask, almanacMask, usedInFixMask);
1165
1166            Message msg = Message.obtain();
1167            msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
1168            // remove any SV status messages already in the queue
1169            mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
1170            mGpsHandler.sendMessage(msg);
1171        }
1172
1173        private final Handler mGpsHandler = new Handler() {
1174            @Override
1175            public void handleMessage(Message msg) {
1176                // synchronize on mGpsStatus to ensure the data is copied atomically.
1177                synchronized(mGpsStatus) {
1178                    mListener.onGpsStatusChanged(msg.what);
1179                }
1180            }
1181        };
1182    }
1183
1184    /**
1185     * Adds a GPS status listener.
1186     *
1187     * @param listener GPS status listener object to register
1188     *
1189     * @return true if the listener was successfully added
1190     *
1191     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
1192     */
1193    public boolean addGpsStatusListener(GpsStatus.Listener listener) {
1194        boolean result;
1195
1196        if (mGpsStatusListeners.get(listener) != null) {
1197            // listener is already registered
1198            return true;
1199        }
1200        try {
1201            GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
1202            result = mService.addGpsStatusListener(transport);
1203            if (result) {
1204                mGpsStatusListeners.put(listener, transport);
1205            }
1206        } catch (RemoteException e) {
1207            Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
1208            result = false;
1209        }
1210
1211        return result;
1212    }
1213
1214    /**
1215     * Removes a GPS status listener.
1216     *
1217     * @param listener GPS status listener object to remove
1218     */
1219    public void removeGpsStatusListener(GpsStatus.Listener listener) {
1220        try {
1221            GpsStatusListenerTransport transport = mGpsStatusListeners.remove(listener);
1222            if (transport != null) {
1223                mService.removeGpsStatusListener(transport);
1224            }
1225        } catch (RemoteException e) {
1226            Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
1227        }
1228    }
1229
1230     /**
1231     * Retrieves information about the current status of the GPS engine.
1232     * This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged}
1233     * callback to ensure that the data is copied atomically.
1234     *
1235     * The caller may either pass in a {@link GpsStatus} object to set with the latest
1236     * status information, or pass null to create a new {@link GpsStatus} object.
1237     *
1238     * @param status object containing GPS status details, or null.
1239     * @return status object containing updated GPS status.
1240     */
1241    public GpsStatus getGpsStatus(GpsStatus status) {
1242        if (status == null) {
1243            status = new GpsStatus();
1244       }
1245       status.setStatus(mGpsStatus);
1246       return status;
1247    }
1248
1249    /**
1250     * Sends additional commands to a location provider.
1251     * Can be used to support provider specific extensions to the Location Manager API
1252     *
1253     * @param provider name of the location provider.
1254     * @param command name of the command to send to the provider.
1255     * @param extras optional arguments for the command (or null).
1256     * The provider may optionally fill the extras Bundle with results from the command.
1257     *
1258     * @return true if the command succeeds.
1259     */
1260    public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1261        try {
1262            return mService.sendExtraCommand(provider, command, extras);
1263        } catch (RemoteException e) {
1264            Log.e(TAG, "RemoteException in sendExtraCommand: ", e);
1265            return false;
1266        }
1267    }
1268}
1269