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