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