OSDN Git Service

Merge "Increase the sleep time to fix testTakePicture."
[android-x86/frameworks-base.git] / location / java / android / location / LocationManager.java
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
17 package android.location;
18
19 import android.app.PendingIntent;
20 import android.content.Intent;
21 import android.os.Bundle;
22 import android.os.Looper;
23 import android.os.RemoteException;
24 import android.os.Handler;
25 import android.os.Message;
26 import android.util.Log;
27
28 import com.android.internal.location.DummyLocationProvider;
29
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.Comparator;
33 import java.util.HashMap;
34 import 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  */
48 public 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 }