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