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