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