OSDN Git Service

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