LocationManager.java revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
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 * Propagates the enabled/disabled state of the providers from the system 317 * settings to the providers themselves. 318 * 319 * {@hide} 320 */ 321 public void updateProviders() { 322 try { 323 mService.updateProviders(); 324 } catch (RemoteException ex) { 325 Log.e(TAG, "updateProviders: RemoteException", ex); 326 } 327 } 328 329 /** 330 * Returns the next looser power requirement, in the sequence: 331 * 332 * POWER_LOW -> POWER_MEDIUM -> POWER_HIGH -> NO_REQUIREMENT 333 */ 334 private int nextPower(int power) { 335 switch (power) { 336 case Criteria.POWER_LOW: 337 return Criteria.POWER_MEDIUM; 338 case Criteria.POWER_MEDIUM: 339 return Criteria.POWER_HIGH; 340 case Criteria.POWER_HIGH: 341 return Criteria.NO_REQUIREMENT; 342 case Criteria.NO_REQUIREMENT: 343 default: 344 return Criteria.NO_REQUIREMENT; 345 } 346 } 347 348 /** 349 * Returns the next looser accuracy requirement, in the sequence: 350 * 351 * ACCURACY_FINE -> ACCURACY_APPROXIMATE-> NO_REQUIREMENT 352 */ 353 private int nextAccuracy(int accuracy) { 354 if (accuracy == Criteria.ACCURACY_FINE) { 355 return Criteria.ACCURACY_COARSE; 356 } else { 357 return Criteria.NO_REQUIREMENT; 358 } 359 } 360 361 private abstract class LpComparator implements Comparator<LocationProvider> { 362 363 public int compare(int a1, int a2) { 364 if (a1 < a2) { 365 return -1; 366 } else if (a1 > a2) { 367 return 1; 368 } else { 369 return 0; 370 } 371 } 372 373 public int compare(float a1, float a2) { 374 if (a1 < a2) { 375 return -1; 376 } else if (a1 > a2) { 377 return 1; 378 } else { 379 return 0; 380 } 381 } 382 } 383 384 private class LpPowerComparator extends LpComparator { 385 public int compare(LocationProvider l1, LocationProvider l2) { 386 int a1 = l1.getPowerRequirement(); 387 int a2 = l2.getPowerRequirement(); 388 return compare(a1, a2); // Smaller is better 389 } 390 391 public boolean equals(LocationProvider l1, LocationProvider l2) { 392 int a1 = l1.getPowerRequirement(); 393 int a2 = l2.getPowerRequirement(); 394 return a1 == a2; 395 } 396 } 397 398 private class LpAccuracyComparator extends LpComparator { 399 public int compare(LocationProvider l1, LocationProvider l2) { 400 int a1 = l1.getAccuracy(); 401 int a2 = l2.getAccuracy(); 402 return compare(a1, a2); // Smaller is better 403 } 404 405 public boolean equals(LocationProvider l1, LocationProvider l2) { 406 int a1 = l1.getAccuracy(); 407 int a2 = l2.getAccuracy(); 408 return a1 == a2; 409 } 410 } 411 412 private class LpCapabilityComparator extends LpComparator { 413 414 private static final int ALTITUDE_SCORE = 4; 415 private static final int BEARING_SCORE = 4; 416 private static final int SPEED_SCORE = 4; 417 418 private int score(LocationProvider p) { 419 return (p.supportsAltitude() ? ALTITUDE_SCORE : 0) + 420 (p.supportsBearing() ? BEARING_SCORE : 0) + 421 (p.supportsSpeed() ? SPEED_SCORE : 0); 422 } 423 424 public int compare(LocationProvider l1, LocationProvider l2) { 425 int a1 = score(l1); 426 int a2 = score(l2); 427 return compare(-a1, -a2); // Bigger is better 428 } 429 430 public boolean equals(LocationProvider l1, LocationProvider l2) { 431 int a1 = score(l1); 432 int a2 = score(l2); 433 return a1 == a2; 434 } 435 } 436 437 private LocationProvider best(List<String> providerNames) { 438 List<LocationProvider> providers = new ArrayList<LocationProvider>(providerNames.size()); 439 for (String name : providerNames) { 440 providers.add(getProvider(name)); 441 } 442 443 if (providers.size() < 2) { 444 return providers.get(0); 445 } 446 447 // First, sort by power requirement 448 Collections.sort(providers, new LpPowerComparator()); 449 int power = providers.get(0).getPowerRequirement(); 450 if (power < providers.get(1).getPowerRequirement()) { 451 return providers.get(0); 452 } 453 454 int idx, size; 455 456 List<LocationProvider> tmp = new ArrayList<LocationProvider>(); 457 idx = 0; 458 size = providers.size(); 459 while ((idx < size) && (providers.get(idx).getPowerRequirement() == power)) { 460 tmp.add(providers.get(idx)); 461 idx++; 462 } 463 464 // Next, sort by accuracy 465 Collections.sort(tmp, new LpAccuracyComparator()); 466 int acc = tmp.get(0).getAccuracy(); 467 if (acc < tmp.get(1).getAccuracy()) { 468 return tmp.get(0); 469 } 470 471 List<LocationProvider> tmp2 = new ArrayList<LocationProvider>(); 472 idx = 0; 473 size = tmp.size(); 474 while ((idx < size) && (tmp.get(idx).getAccuracy() == acc)) { 475 tmp2.add(tmp.get(idx)); 476 idx++; 477 } 478 479 // Finally, sort by capability "score" 480 Collections.sort(tmp2, new LpCapabilityComparator()); 481 return tmp2.get(0); 482 } 483 484 /** 485 * Returns the name of the provider that best meets the given criteria. Only providers 486 * that are permitted to be accessed by the calling activity will be 487 * returned. If several providers meet the criteria, the one with the best 488 * accuracy is returned. If no provider meets the criteria, 489 * the criteria are loosened in the following sequence: 490 * 491 * <ul> 492 * <li> power requirement 493 * <li> accuracy 494 * <li> bearing 495 * <li> speed 496 * <li> altitude 497 * </ul> 498 * 499 * <p> Note that the requirement on monetary cost is not removed 500 * in this process. 501 * 502 * @param criteria the criteria that need to be matched 503 * @param enabledOnly if true then only a provider that is currently enabled is returned 504 * @return name of the provider that best matches the requirements 505 */ 506 public String getBestProvider(Criteria criteria, boolean enabledOnly) { 507 List<String> goodProviders = getProviders(criteria, enabledOnly); 508 if (!goodProviders.isEmpty()) { 509 return best(goodProviders).getName(); 510 } 511 512 // Make a copy of the criteria that we can modify 513 criteria = new Criteria(criteria); 514 515 // Loosen power requirement 516 int power = criteria.getPowerRequirement(); 517 while (goodProviders.isEmpty() && (power != Criteria.NO_REQUIREMENT)) { 518 power = nextPower(power); 519 criteria.setPowerRequirement(power); 520 goodProviders = getProviders(criteria, enabledOnly); 521 } 522 if (!goodProviders.isEmpty()) { 523 return best(goodProviders).getName(); 524 } 525 526// // Loosen response time requirement 527// int responseTime = criteria.getPreferredResponseTime(); 528// while (goodProviders.isEmpty() && 529// (responseTime != Criteria.NO_REQUIREMENT)) { 530// responseTime += 1000; 531// if (responseTime > 60000) { 532// responseTime = Criteria.NO_REQUIREMENT; 533// } 534// criteria.setPreferredResponseTime(responseTime); 535// goodProviders = getProviders(criteria); 536// } 537// if (!goodProviders.isEmpty()) { 538// return best(goodProviders); 539// } 540 541 // Loosen accuracy requirement 542 int accuracy = criteria.getAccuracy(); 543 while (goodProviders.isEmpty() && (accuracy != Criteria.NO_REQUIREMENT)) { 544 accuracy = nextAccuracy(accuracy); 545 criteria.setAccuracy(accuracy); 546 goodProviders = getProviders(criteria, enabledOnly); 547 } 548 if (!goodProviders.isEmpty()) { 549 return best(goodProviders).getName(); 550 } 551 552 // Remove bearing requirement 553 criteria.setBearingRequired(false); 554 goodProviders = getProviders(criteria, enabledOnly); 555 if (!goodProviders.isEmpty()) { 556 return best(goodProviders).getName(); 557 } 558 559 // Remove speed requirement 560 criteria.setSpeedRequired(false); 561 goodProviders = getProviders(criteria, enabledOnly); 562 if (!goodProviders.isEmpty()) { 563 return best(goodProviders).getName(); 564 } 565 566 // Remove altitude requirement 567 criteria.setAltitudeRequired(false); 568 goodProviders = getProviders(criteria, enabledOnly); 569 if (!goodProviders.isEmpty()) { 570 return best(goodProviders).getName(); 571 } 572 573 return null; 574 } 575 576 /** 577 * Registers the current activity to be notified periodically by 578 * the named provider. Periodically, the supplied LocationListener will 579 * be called with the current Location or with status updates. 580 * 581 * <p> It may take a while to receive the most recent location. If 582 * an immediate location is required, applications may use the 583 * {@link #getLastKnownLocation(String)} method. 584 * 585 * <p> In case the provider is disabled by the user, updates will stop, 586 * and the {@link LocationListener#onProviderDisabled(String)} 587 * method will be called. As soon as the provider is enabled again, 588 * the {@link LocationListener#onProviderEnabled(String)} method will 589 * be called and location updates will start again. 590 * 591 * <p> The frequency of notification may be controlled using the 592 * minTime and minDistance parameters. If minTime is greater than 0, 593 * the LocationManager could potentially rest for minTime milliseconds 594 * between location updates to conserve power. If minDistance is greater than 0, 595 * a location will only be broadcasted if the device moves by minDistance meters. 596 * To obtain notifications as frequently as possible, set both parameters to 0. 597 * 598 * <p> Background services should be careful about setting a sufficiently high 599 * minTime so that the device doesn't consume too much power by keeping the 600 * GPS or wireless radios on all the time. In particular, values under 60000ms 601 * are not recommended. 602 * 603 * <p> The calling thread must be a {@link android.os.Looper} thread such as 604 * the main thread of the calling Activity. 605 * 606 * @param provider the name of the provider with which to register 607 * @param minTime the minimum time interval for notifications, in 608 * milliseconds. This field is only used as a hint to conserve power, and actual 609 * time between location updates may be greater or lesser than this value. 610 * @param minDistance the minimum distance interval for notifications, 611 * in meters 612 * @param listener a {#link LocationListener} whose 613 * {@link LocationListener#onLocationChanged} method will be called for 614 * each location update 615 * 616 * @throws IllegalArgumentException if provider is null or doesn't exist 617 * @throws IllegalArgumentException if listener is null 618 * @throws RuntimeException if the calling thread has no Looper 619 * @throws SecurityException if no suitable permission is present for the provider. 620 */ 621 public void requestLocationUpdates(String provider, 622 long minTime, float minDistance, LocationListener listener) { 623 if (provider == null) { 624 throw new IllegalArgumentException("provider==null"); 625 } 626 if (listener == null) { 627 throw new IllegalArgumentException("listener==null"); 628 } 629 _requestLocationUpdates(provider, minTime, minDistance, listener, null); 630 } 631 632 /** 633 * Registers the current activity to be notified periodically by 634 * the named provider. Periodically, the supplied LocationListener will 635 * be called with the current Location or with status updates. 636 * 637 * <p> It may take a while to receive the most recent location. If 638 * an immediate location is required, applications may use the 639 * {@link #getLastKnownLocation(String)} method. 640 * 641 * <p> In case the provider is disabled by the user, updates will stop, 642 * and the {@link LocationListener#onProviderDisabled(String)} 643 * method will be called. As soon as the provider is enabled again, 644 * the {@link LocationListener#onProviderEnabled(String)} method will 645 * be called and location updates will start again. 646 * 647 * <p> The frequency of notification may be controlled using the 648 * minTime and minDistance parameters. If minTime is greater than 0, 649 * the LocationManager could potentially rest for minTime milliseconds 650 * between location updates to conserve power. If minDistance is greater than 0, 651 * a location will only be broadcasted if the device moves by minDistance meters. 652 * To obtain notifications as frequently as possible, set both parameters to 0. 653 * 654 * <p> Background services should be careful about setting a sufficiently high 655 * minTime so that the device doesn't consume too much power by keeping the 656 * GPS or wireless radios on all the time. In particular, values under 60000ms 657 * are not recommended. 658 * 659 * <p> The supplied Looper is used to implement the callback mechanism. 660 * 661 * @param provider the name of the provider with which to register 662 * @param minTime the minimum time interval for notifications, in 663 * milliseconds. This field is only used as a hint to conserve power, and actual 664 * time between location updates may be greater or lesser than this value. 665 * @param minDistance the minimum distance interval for notifications, 666 * in meters 667 * @param listener a {#link LocationListener} whose 668 * {@link LocationListener#onLocationChanged} method will be called for 669 * each location update 670 * @param looper a Looper object whose message queue will be used to 671 * implement the callback mechanism. 672 * 673 * @throws IllegalArgumentException if provider is null or doesn't exist 674 * @throws IllegalArgumentException if listener is null 675 * @throws IllegalArgumentException if looper is null 676 * @throws SecurityException if no suitable permission is present for the provider. 677 */ 678 public void requestLocationUpdates(String provider, 679 long minTime, float minDistance, LocationListener listener, 680 Looper looper) { 681 if (provider == null) { 682 throw new IllegalArgumentException("provider==null"); 683 } 684 if (listener == null) { 685 throw new IllegalArgumentException("listener==null"); 686 } 687 if (looper == null) { 688 throw new IllegalArgumentException("looper==null"); 689 } 690 _requestLocationUpdates(provider, minTime, minDistance, listener, looper); 691 } 692 693 private void _requestLocationUpdates(String provider, 694 long minTime, float minDistance, LocationListener listener, 695 Looper looper) { 696 if (minTime < 0L) { 697 minTime = 0L; 698 } 699 if (minDistance < 0.0f) { 700 minDistance = 0.0f; 701 } 702 703 try { 704 synchronized (mListeners) { 705 ListenerTransport transport = mListeners.get(listener); 706 if (transport == null) { 707 transport = new ListenerTransport(listener, looper); 708 } 709 mListeners.put(listener, transport); 710 mService.requestLocationUpdates(provider, minTime, minDistance, transport); 711 } 712 } catch (RemoteException ex) { 713 Log.e(TAG, "requestLocationUpdates: DeadObjectException", ex); 714 } 715 } 716 717 /** 718 * Registers the current activity to be notified periodically by 719 * the named provider. Periodically, the supplied PendingIntent will 720 * be broadcast with the current Location or with status updates. 721 * 722 * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value. 723 * 724 * <p> It may take a while to receive the most recent location. If 725 * an immediate location is required, applications may use the 726 * {@link #getLastKnownLocation(String)} method. 727 * 728 * <p> The frequency of notification or new locations may be controlled using the 729 * minTime and minDistance parameters. If minTime is greater than 0, 730 * the LocationManager could potentially rest for minTime milliseconds 731 * between location updates to conserve power. If minDistance is greater than 0, 732 * a location will only be broadcast if the device moves by minDistance meters. 733 * To obtain notifications as frequently as possible, set both parameters to 0. 734 * 735 * <p> Background services should be careful about setting a sufficiently high 736 * minTime so that the device doesn't consume too much power by keeping the 737 * GPS or wireless radios on all the time. In particular, values under 60000ms 738 * are not recommended. 739 * 740 * <p> In case the provider is disabled by the user, updates will stop, 741 * and an intent will be sent with an extra with key KEY_PROVIDER_ENABLED and a boolean value 742 * of false. If the provider is re-enabled, an intent will be sent with an 743 * extra with key KEY_PROVIDER_ENABLED and a boolean value of true and location updates will 744 * start again. 745 * 746 * <p> If the provider's status changes, an intent will be sent with an extra with key 747 * KEY_STATUS_CHANGED and an integer value indicating the new status. Any extras associated 748 * with the status update will be sent as well. 749 * 750 * @param provider the name of the provider with which to register 751 * @param minTime the minimum time interval for notifications, in 752 * milliseconds. This field is only used as a hint to conserve power, and actual 753 * time between location updates may be greater or lesser than this value. 754 * @param minDistance the minimum distance interval for notifications, 755 * in meters 756 * @param intent a {#link PendingIntet} to be sent for each location update 757 * 758 * @throws IllegalArgumentException if provider is null or doesn't exist 759 * @throws IllegalArgumentException if intent is null 760 * @throws SecurityException if no suitable permission is present for the provider. 761 */ 762 public void requestLocationUpdates(String provider, 763 long minTime, float minDistance, PendingIntent intent) { 764 if (provider == null) { 765 throw new IllegalArgumentException("provider==null"); 766 } 767 if (intent == null) { 768 throw new IllegalArgumentException("intent==null"); 769 } 770 _requestLocationUpdates(provider, minTime, minDistance, intent); 771 } 772 773 private void _requestLocationUpdates(String provider, 774 long minTime, float minDistance, PendingIntent intent) { 775 if (minTime < 0L) { 776 minTime = 0L; 777 } 778 if (minDistance < 0.0f) { 779 minDistance = 0.0f; 780 } 781 782 try { 783 mService.requestLocationUpdatesPI(provider, minTime, minDistance, intent); 784 } catch (RemoteException ex) { 785 Log.e(TAG, "requestLocationUpdates: RemoteException", ex); 786 } 787 } 788 789 /** 790 * Removes any current registration for location updates of the current activity 791 * with the given LocationListener. Following this call, updates will no longer 792 * occur for this listener. 793 * 794 * @param listener {#link LocationListener} object that no longer needs location updates 795 * @throws IllegalArgumentException if listener is null 796 */ 797 public void removeUpdates(LocationListener listener) { 798 if (listener == null) { 799 throw new IllegalArgumentException("listener==null"); 800 } 801 if (Config.LOGD) { 802 Log.d(TAG, "removeUpdates: listener = " + listener); 803 } 804 try { 805 ListenerTransport transport = mListeners.remove(listener); 806 if (transport != null) { 807 mService.removeUpdates(transport); 808 } 809 } catch (RemoteException ex) { 810 Log.e(TAG, "removeUpdates: DeadObjectException", ex); 811 } 812 } 813 814 /** 815 * Removes any current registration for location updates of the current activity 816 * with the given PendingIntent. Following this call, updates will no longer 817 * occur for this intent. 818 * 819 * @param intent {#link PendingIntent} object that no longer needs location updates 820 * @throws IllegalArgumentException if intent is null 821 */ 822 public void removeUpdates(PendingIntent intent) { 823 if (intent == null) { 824 throw new IllegalArgumentException("intent==null"); 825 } 826 if (Config.LOGD) { 827 Log.d(TAG, "removeUpdates: intent = " + intent); 828 } 829 try { 830 mService.removeUpdatesPI(intent); 831 } catch (RemoteException ex) { 832 Log.e(TAG, "removeUpdates: RemoteException", ex); 833 } 834 } 835 836 /** 837 * Sets a proximity alert for the location given by the position 838 * (latitude, longitude) and the given radius. When the device 839 * detects that it has entered or exited the area surrounding the 840 * location, the given PendingIntent will be used to create an Intent 841 * to be fired. 842 * 843 * <p> The fired Intent will have a boolean extra added with key 844 * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is 845 * entering the proximity region; if false, it is exiting. 846 * 847 * <p> Due to the approximate nature of position estimation, if the 848 * device passes through the given area briefly, it is possible 849 * that no Intent will be fired. Similarly, an Intent could be 850 * fired if the device passes very close to the given area but 851 * does not actually enter it. 852 * 853 * <p> After the number of milliseconds given by the expiration 854 * parameter, the location manager will delete this proximity 855 * alert and no longer monitor it. A value of -1 indicates that 856 * there should be no expiration time. 857 * 858 * <p> In case the screen goes to sleep, checks for proximity alerts 859 * happen only once every 4 minutes. This conserves battery life by 860 * ensuring that the device isn't perpetually awake. 861 * 862 * <p> Internally, this method uses both {@link #NETWORK_PROVIDER} 863 * and {@link #GPS_PROVIDER}. 864 * 865 * @param latitude the latitude of the central point of the 866 * alert region 867 * @param longitude the longitude of the central point of the 868 * alert region 869 * @param radius the radius of the central point of the 870 * alert region, in meters 871 * @param expiration time for this proximity alert, in milliseconds, 872 * or -1 to indicate no expiration 873 * @param intent a PendingIntent that will be used to generate an Intent to 874 * fire when entry to or exit from the alert region is detected 875 * 876 * @throws SecurityException if no permission exists for the required 877 * providers. 878 */ 879 public void addProximityAlert(double latitude, double longitude, 880 float radius, long expiration, PendingIntent intent) { 881 if (Config.LOGD) { 882 Log.d(TAG, "addProximityAlert: latitude = " + latitude + 883 ", longitude = " + longitude + ", radius = " + radius + 884 ", expiration = " + expiration + 885 ", intent = " + intent); 886 } 887 try { 888 mService.addProximityAlert(latitude, longitude, radius, 889 expiration, intent); 890 } catch (RemoteException ex) { 891 Log.e(TAG, "addProximityAlert: RemoteException", ex); 892 } 893 } 894 895 /** 896 * Removes the proximity alert with the given PendingIntent. 897 * 898 * @param intent the PendingIntent that no longer needs to be notified of 899 * proximity alerts 900 */ 901 public void removeProximityAlert(PendingIntent intent) { 902 if (Config.LOGD) { 903 Log.d(TAG, "removeProximityAlert: intent = " + intent); 904 } 905 try { 906 mService.removeProximityAlert(intent); 907 } catch (RemoteException ex) { 908 Log.e(TAG, "removeProximityAlert: RemoteException", ex); 909 } 910 } 911 912 /** 913 * Returns the current enabled/disabled status of the given provider. If the 914 * user has enabled this provider in the Settings menu, true is returned 915 * otherwise false is returned 916 * 917 * @param provider the name of the provider 918 * @return true if the provider is enabled 919 * 920 * @throws SecurityException if no suitable permission is present for the provider. 921 * @throws IllegalArgumentException if provider is null or doesn't exist 922 */ 923 public boolean isProviderEnabled(String provider) { 924 if (provider == null) { 925 throw new IllegalArgumentException("provider==null"); 926 } 927 try { 928 return mService.isProviderEnabled(provider); 929 } catch (RemoteException ex) { 930 Log.e(TAG, "isProviderEnabled: RemoteException", ex); 931 return false; 932 } 933 } 934 935 /** 936 * Returns a Location indicating the data from the last known 937 * location fix obtained from the given provider. This can be done 938 * without starting the provider. Note that this location could 939 * be out-of-date, for example if the device was turned off and 940 * moved to another location. 941 * 942 * <p> If the provider is currently disabled, null is returned. 943 * 944 * @param provider the name of the provider 945 * @return the last known location for the provider, or null 946 * 947 * @throws SecurityException if no suitable permission is present for the provider. 948 * @throws IllegalArgumentException if provider is null or doesn't exist 949 */ 950 public Location getLastKnownLocation(String provider) { 951 if (provider == null) { 952 throw new IllegalArgumentException("provider==null"); 953 } 954 try { 955 return mService.getLastKnownLocation(provider); 956 } catch (RemoteException ex) { 957 Log.e(TAG, "getLastKnowLocation: RemoteException", ex); 958 return null; 959 } 960 } 961 962 // Mock provider support 963 964 /** 965 * Creates a mock location provider and adds it to the set of active providers. 966 * 967 * @param name the provider name 968 * @param requiresNetwork 969 * @param requiresSatellite 970 * @param requiresCell 971 * @param hasMonetaryCost 972 * @param supportsAltitude 973 * @param supportsSpeed 974 * @param supportsBearing 975 * @param powerRequirement 976 * @param accuracy 977 * 978 * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present 979 * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION 980 * Settings.Secure.ALLOW_MOCK_LOCATION} system setting is not enabled 981 * @throws IllegalArgumentException if a provider with the given name already exists 982 */ 983 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite, 984 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude, 985 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) { 986 try { 987 mService.addTestProvider(name, requiresNetwork, requiresSatellite, requiresCell, 988 hasMonetaryCost, supportsAltitude, supportsSpeed, supportsBearing, powerRequirement, 989 accuracy); 990 } catch (RemoteException ex) { 991 Log.e(TAG, "addTestProvider: RemoteException", ex); 992 } 993 } 994 995 /** 996 * Removes the mock location provider with the given name. 997 * 998 * @param provider the provider name 999 * 1000 * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present 1001 * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION 1002 * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled 1003 * @throws IllegalArgumentException if no provider with the given name exists 1004 */ 1005 public void removeTestProvider(String provider) { 1006 try { 1007 mService.removeTestProvider(provider); 1008 } catch (RemoteException ex) { 1009 Log.e(TAG, "removeTestProvider: RemoteException", ex); 1010 } 1011 } 1012 1013 /** 1014 * Sets a mock location for the given provider. This location will be used in place 1015 * of any actual location from the provider. 1016 * 1017 * @param provider the provider name 1018 * @param loc the mock location 1019 * 1020 * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present 1021 * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION 1022 * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled 1023 * @throws IllegalArgumentException if no provider with the given name exists 1024 */ 1025 public void setTestProviderLocation(String provider, Location loc) { 1026 try { 1027 mService.setTestProviderLocation(provider, loc); 1028 } catch (RemoteException ex) { 1029 Log.e(TAG, "setTestProviderLocation: RemoteException", ex); 1030 } 1031 } 1032 1033 /** 1034 * Removes any mock location associated with the given provider. 1035 * 1036 * @param provider the provider name 1037 * 1038 * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present 1039 * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION 1040 * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled 1041 * @throws IllegalArgumentException if no provider with the given name exists 1042 */ 1043 public void clearTestProviderLocation(String provider) { 1044 try { 1045 mService.clearTestProviderLocation(provider); 1046 } catch (RemoteException ex) { 1047 Log.e(TAG, "clearTestProviderLocation: RemoteException", ex); 1048 } 1049 } 1050 1051 /** 1052 * Sets a mock enabled value for the given provider. This value will be used in place 1053 * of any actual value from the provider. 1054 * 1055 * @param provider the provider name 1056 * @param enabled the mock enabled value 1057 * 1058 * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present 1059 * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION 1060 * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled 1061 * @throws IllegalArgumentException if no provider with the given name exists 1062 */ 1063 public void setTestProviderEnabled(String provider, boolean enabled) { 1064 try { 1065 mService.setTestProviderEnabled(provider, enabled); 1066 } catch (RemoteException ex) { 1067 Log.e(TAG, "setTestProviderEnabled: RemoteException", ex); 1068 } 1069 } 1070 1071 /** 1072 * Removes any mock enabled value associated with the given provider. 1073 * 1074 * @param provider the provider name 1075 * 1076 * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present 1077 * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION 1078 * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled 1079 * @throws IllegalArgumentException if no provider with the given name exists 1080 */ 1081 public void clearTestProviderEnabled(String provider) { 1082 try { 1083 mService.clearTestProviderEnabled(provider); 1084 } catch (RemoteException ex) { 1085 Log.e(TAG, "clearTestProviderEnabled: RemoteException", ex); 1086 } 1087 1088 } 1089 1090 /** 1091 * Sets mock status values for the given provider. These values will be used in place 1092 * of any actual values from the provider. 1093 * 1094 * @param provider the provider name 1095 * @param status the mock status 1096 * @param extras a Bundle containing mock extras 1097 * @param updateTime the mock update time 1098 * 1099 * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present 1100 * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION 1101 * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled 1102 * @throws IllegalArgumentException if no provider with the given name exists 1103 */ 1104 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) { 1105 try { 1106 mService.setTestProviderStatus(provider, status, extras, updateTime); 1107 } catch (RemoteException ex) { 1108 Log.e(TAG, "setTestProviderStatus: RemoteException", ex); 1109 } 1110 } 1111 1112 /** 1113 * Removes any mock status values associated with the given provider. 1114 * 1115 * @param provider the provider name 1116 * 1117 * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present 1118 * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION 1119 * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled 1120 * @throws IllegalArgumentException if no provider with the given name exists 1121 */ 1122 public void clearTestProviderStatus(String provider) { 1123 try { 1124 mService.clearTestProviderStatus(provider); 1125 } catch (RemoteException ex) { 1126 Log.e(TAG, "clearTestProviderStatus: RemoteException", ex); 1127 } 1128 } 1129 1130 // GPS-specific support 1131 1132 // This class is used to send GPS status events to the client's main thread. 1133 private class GpsStatusListenerTransport extends IGpsStatusListener.Stub { 1134 1135 private final GpsStatus.Listener mListener; 1136 1137 GpsStatusListenerTransport(GpsStatus.Listener listener) { 1138 mListener = listener; 1139 } 1140 1141 public void onGpsStarted() { 1142 Message msg = Message.obtain(); 1143 msg.what = GpsStatus.GPS_EVENT_STARTED; 1144 mGpsHandler.sendMessage(msg); 1145 } 1146 1147 public void onGpsStopped() { 1148 Message msg = Message.obtain(); 1149 msg.what = GpsStatus.GPS_EVENT_STOPPED; 1150 mGpsHandler.sendMessage(msg); 1151 } 1152 1153 public void onFirstFix(int ttff) { 1154 mGpsStatus.setTimeToFirstFix(ttff); 1155 Message msg = Message.obtain(); 1156 msg.what = GpsStatus.GPS_EVENT_FIRST_FIX; 1157 mGpsHandler.sendMessage(msg); 1158 } 1159 1160 public void onSvStatusChanged(int svCount, int[] prns, float[] snrs, 1161 float[] elevations, float[] azimuths, int ephemerisMask, 1162 int almanacMask, int usedInFixMask) { 1163 mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths, 1164 ephemerisMask, almanacMask, usedInFixMask); 1165 1166 Message msg = Message.obtain(); 1167 msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS; 1168 // remove any SV status messages already in the queue 1169 mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS); 1170 mGpsHandler.sendMessage(msg); 1171 } 1172 1173 private final Handler mGpsHandler = new Handler() { 1174 @Override 1175 public void handleMessage(Message msg) { 1176 // synchronize on mGpsStatus to ensure the data is copied atomically. 1177 synchronized(mGpsStatus) { 1178 mListener.onGpsStatusChanged(msg.what); 1179 } 1180 } 1181 }; 1182 } 1183 1184 /** 1185 * Adds a GPS status listener. 1186 * 1187 * @param listener GPS status listener object to register 1188 * 1189 * @return true if the listener was successfully added 1190 * 1191 * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present 1192 */ 1193 public boolean addGpsStatusListener(GpsStatus.Listener listener) { 1194 boolean result; 1195 1196 if (mGpsStatusListeners.get(listener) != null) { 1197 // listener is already registered 1198 return true; 1199 } 1200 try { 1201 GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener); 1202 result = mService.addGpsStatusListener(transport); 1203 if (result) { 1204 mGpsStatusListeners.put(listener, transport); 1205 } 1206 } catch (RemoteException e) { 1207 Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e); 1208 result = false; 1209 } 1210 1211 return result; 1212 } 1213 1214 /** 1215 * Removes a GPS status listener. 1216 * 1217 * @param listener GPS status listener object to remove 1218 */ 1219 public void removeGpsStatusListener(GpsStatus.Listener listener) { 1220 try { 1221 GpsStatusListenerTransport transport = mGpsStatusListeners.remove(listener); 1222 if (transport != null) { 1223 mService.removeGpsStatusListener(transport); 1224 } 1225 } catch (RemoteException e) { 1226 Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e); 1227 } 1228 } 1229 1230 /** 1231 * Retrieves information about the current status of the GPS engine. 1232 * This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged} 1233 * callback to ensure that the data is copied atomically. 1234 * 1235 * The caller may either pass in a {@link GpsStatus} object to set with the latest 1236 * status information, or pass null to create a new {@link GpsStatus} object. 1237 * 1238 * @param status object containing GPS status details, or null. 1239 * @return status object containing updated GPS status. 1240 */ 1241 public GpsStatus getGpsStatus(GpsStatus status) { 1242 if (status == null) { 1243 status = new GpsStatus(); 1244 } 1245 status.setStatus(mGpsStatus); 1246 return status; 1247 } 1248 1249 /** 1250 * Sends additional commands to a location provider. 1251 * Can be used to support provider specific extensions to the Location Manager API 1252 * 1253 * @param provider name of the location provider. 1254 * @param command name of the command to send to the provider. 1255 * @param extras optional arguments for the command (or null). 1256 * The provider may optionally fill the extras Bundle with results from the command. 1257 * 1258 * @return true if the command succeeds. 1259 */ 1260 public boolean sendExtraCommand(String provider, String command, Bundle extras) { 1261 try { 1262 return mService.sendExtraCommand(provider, command, extras); 1263 } catch (RemoteException e) { 1264 Log.e(TAG, "RemoteException in sendExtraCommand: ", e); 1265 return false; 1266 } 1267 } 1268} 1269