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