LocationManager.java revision 628fd6d9c11ed9806abebf34bc986247c106328f
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.location;
18
19import android.app.PendingIntent;
20import android.content.Intent;
21import android.os.Bundle;
22import android.os.Looper;
23import android.os.RemoteException;
24import android.os.Handler;
25import android.os.Message;
26import android.util.Log;
27
28import com.android.internal.location.DummyLocationProvider;
29
30import java.util.ArrayList;
31import java.util.Collections;
32import java.util.Comparator;
33import java.util.HashMap;
34import java.util.List;
35
36/**
37 * This class provides access to the system location services.  These
38 * services allow applications to obtain periodic updates of the
39 * device's geographical location, or to fire an application-specified
40 * {@link Intent} when the device enters the proximity of a given
41 * geographical location.
42 *
43 * <p>You do not
44 * instantiate this class directly; instead, retrieve it through
45 * {@link android.content.Context#getSystemService
46 * Context.getSystemService(Context.LOCATION_SERVICE)}.
47 */
48public class LocationManager {
49    private static final String TAG = "LocationManager";
50    private ILocationManager mService;
51    private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners =
52            new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>();
53    private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners =
54            new HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport>();
55    private final GpsStatus mGpsStatus = new GpsStatus();
56
57    /**
58     * Name of the network location provider.  This provider determines location based on
59     * availability of cell tower and WiFi access points. Results are retrieved
60     * by means of a network lookup.
61     *
62     * Requires either of the permissions android.permission.ACCESS_COARSE_LOCATION
63     * or android.permission.ACCESS_FINE_LOCATION.
64     */
65    public static final String NETWORK_PROVIDER = "network";
66
67    /**
68     * Name of the GPS location provider. This provider determines location using
69     * satellites. Depending on conditions, this provider may take a while to return
70     * a location fix.
71     *
72     * Requires the permission android.permission.ACCESS_FINE_LOCATION.
73     *
74     * <p> The extras Bundle for the GPS location provider can contain the
75     * following key/value pairs:
76     *
77     * <ul>
78     * <li> satellites - the number of satellites used to derive the fix
79     * </ul>
80     */
81    public static final String GPS_PROVIDER = "gps";
82
83    /**
84     * 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}
1373