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