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