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