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 17 package android.location; 18 19 import com.android.internal.location.ProviderProperties; 20 21 import android.annotation.RequiresPermission; 22 import android.annotation.SystemApi; 23 import android.app.PendingIntent; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.os.Build; 27 import android.os.Bundle; 28 import android.os.Handler; 29 import android.os.Looper; 30 import android.os.Message; 31 import android.os.RemoteException; 32 import android.util.Log; 33 34 import java.util.ArrayList; 35 import java.util.HashMap; 36 import java.util.List; 37 38 import static android.Manifest.permission.ACCESS_COARSE_LOCATION; 39 import static android.Manifest.permission.ACCESS_FINE_LOCATION; 40 41 /** 42 * This class provides access to the system location services. These 43 * services allow applications to obtain periodic updates of the 44 * device's geographical location, or to fire an application-specified 45 * {@link Intent} when the device enters the proximity of a given 46 * geographical location. 47 * 48 * <p>You do not 49 * instantiate this class directly; instead, retrieve it through 50 * {@link android.content.Context#getSystemService 51 * Context.getSystemService(Context.LOCATION_SERVICE)}. 52 * 53 * <p class="note">Unless noted, all Location API methods require 54 * the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or 55 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permissions. 56 * If your application only has the coarse permission then it will not have 57 * access to the GPS or passive location providers. Other providers will still 58 * return location results, but the update rate will be throttled and the exact 59 * location will be obfuscated to a coarse level of accuracy. 60 */ 61 public class LocationManager { 62 private static final String TAG = "LocationManager"; 63 64 private final Context mContext; 65 private final ILocationManager mService; 66 private final GpsMeasurementListenerTransport mGpsMeasurementListenerTransport; 67 private final GpsNavigationMessageListenerTransport mGpsNavigationMessageListenerTransport; 68 private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners = 69 new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>(); 70 private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners = 71 new HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport>(); 72 private final GpsStatus mGpsStatus = new GpsStatus(); 73 74 /** 75 * Name of the network location provider. 76 * <p>This provider determines location based on 77 * availability of cell tower and WiFi access points. Results are retrieved 78 * by means of a network lookup. 79 */ 80 public static final String NETWORK_PROVIDER = "network"; 81 82 /** 83 * Name of the GPS location provider. 84 * 85 * <p>This provider determines location using 86 * satellites. Depending on conditions, this provider may take a while to return 87 * a location fix. Requires the permission 88 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. 89 * 90 * <p> The extras Bundle for the GPS location provider can contain the 91 * following key/value pairs: 92 * <ul> 93 * <li> satellites - the number of satellites used to derive the fix 94 * </ul> 95 */ 96 public static final String GPS_PROVIDER = "gps"; 97 98 /** 99 * A special location provider for receiving locations without actually initiating 100 * a location fix. 101 * 102 * <p>This provider can be used to passively receive location updates 103 * when other applications or services request them without actually requesting 104 * the locations yourself. This provider will return locations generated by other 105 * providers. You can query the {@link Location#getProvider()} method to determine 106 * the origin of the location update. Requires the permission 107 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}, although if the GPS is 108 * not enabled this provider might only return coarse fixes. 109 */ 110 public static final String PASSIVE_PROVIDER = "passive"; 111 112 /** 113 * Name of the Fused location provider. 114 * 115 * <p>This provider combines inputs for all possible location sources 116 * to provide the best possible Location fix. It is implicitly 117 * used for all API's that involve the {@link LocationRequest} 118 * object. 119 * 120 * @hide 121 */ 122 public static final String FUSED_PROVIDER = "fused"; 123 124 /** 125 * Key used for the Bundle extra holding a boolean indicating whether 126 * a proximity alert is entering (true) or exiting (false).. 127 */ 128 public static final String KEY_PROXIMITY_ENTERING = "entering"; 129 130 /** 131 * Key used for a Bundle extra holding an Integer status value 132 * when a status change is broadcast using a PendingIntent. 133 */ 134 public static final String KEY_STATUS_CHANGED = "status"; 135 136 /** 137 * Key used for a Bundle extra holding an Boolean status value 138 * when a provider enabled/disabled event is broadcast using a PendingIntent. 139 */ 140 public static final String KEY_PROVIDER_ENABLED = "providerEnabled"; 141 142 /** 143 * Key used for a Bundle extra holding a Location value 144 * when a location change is broadcast using a PendingIntent. 145 */ 146 public static final String KEY_LOCATION_CHANGED = "location"; 147 148 /** 149 * Broadcast intent action indicating that the GPS has either been 150 * enabled or disabled. An intent extra provides this state as a boolean, 151 * where {@code true} means enabled. 152 * @see #EXTRA_GPS_ENABLED 153 * 154 * @hide 155 */ 156 public static final String GPS_ENABLED_CHANGE_ACTION = 157 "android.location.GPS_ENABLED_CHANGE"; 158 159 /** 160 * Broadcast intent action when the configured location providers 161 * change. For use with {@link #isProviderEnabled(String)}. If you're interacting with the 162 * {@link android.provider.Settings.Secure#LOCATION_MODE} API, use {@link #MODE_CHANGED_ACTION} 163 * instead. 164 */ 165 public static final String PROVIDERS_CHANGED_ACTION = 166 "android.location.PROVIDERS_CHANGED"; 167 168 /** 169 * Broadcast intent action when {@link android.provider.Settings.Secure#LOCATION_MODE} changes. 170 * For use with the {@link android.provider.Settings.Secure#LOCATION_MODE} API. 171 * If you're interacting with {@link #isProviderEnabled(String)}, use 172 * {@link #PROVIDERS_CHANGED_ACTION} instead. 173 * 174 * In the future, there may be mode changes that do not result in 175 * {@link #PROVIDERS_CHANGED_ACTION} broadcasts. 176 */ 177 public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED"; 178 179 /** 180 * Broadcast intent action indicating that the GPS has either started or 181 * stopped receiving GPS fixes. An intent extra provides this state as a 182 * boolean, where {@code true} means that the GPS is actively receiving fixes. 183 * @see #EXTRA_GPS_ENABLED 184 * 185 * @hide 186 */ 187 public static final String GPS_FIX_CHANGE_ACTION = 188 "android.location.GPS_FIX_CHANGE"; 189 190 /** 191 * The lookup key for a boolean that indicates whether GPS is enabled or 192 * disabled. {@code true} means GPS is enabled. Retrieve it with 193 * {@link android.content.Intent#getBooleanExtra(String,boolean)}. 194 * 195 * @hide 196 */ 197 public static final String EXTRA_GPS_ENABLED = "enabled"; 198 199 /** 200 * Broadcast intent action indicating that a high power location requests 201 * has either started or stopped being active. The current state of 202 * active location requests should be read from AppOpsManager using 203 * {@code OP_MONITOR_HIGH_POWER_LOCATION}. 204 * 205 * @hide 206 */ 207 public static final String HIGH_POWER_REQUEST_CHANGE_ACTION = 208 "android.location.HIGH_POWER_REQUEST_CHANGE"; 209 210 // Map from LocationListeners to their associated ListenerTransport objects 211 private HashMap<LocationListener,ListenerTransport> mListeners = 212 new HashMap<LocationListener,ListenerTransport>(); 213 214 private class ListenerTransport extends ILocationListener.Stub { 215 private static final int TYPE_LOCATION_CHANGED = 1; 216 private static final int TYPE_STATUS_CHANGED = 2; 217 private static final int TYPE_PROVIDER_ENABLED = 3; 218 private static final int TYPE_PROVIDER_DISABLED = 4; 219 220 private LocationListener mListener; 221 private final Handler mListenerHandler; 222 ListenerTransport(LocationListener listener, Looper looper)223 ListenerTransport(LocationListener listener, Looper looper) { 224 mListener = listener; 225 226 if (looper == null) { 227 mListenerHandler = new Handler() { 228 @Override 229 public void handleMessage(Message msg) { 230 _handleMessage(msg); 231 } 232 }; 233 } else { 234 mListenerHandler = new Handler(looper) { 235 @Override 236 public void handleMessage(Message msg) { 237 _handleMessage(msg); 238 } 239 }; 240 } 241 } 242 243 @Override onLocationChanged(Location location)244 public void onLocationChanged(Location location) { 245 Message msg = Message.obtain(); 246 msg.what = TYPE_LOCATION_CHANGED; 247 msg.obj = location; 248 mListenerHandler.sendMessage(msg); 249 } 250 251 @Override onStatusChanged(String provider, int status, Bundle extras)252 public void onStatusChanged(String provider, int status, Bundle extras) { 253 Message msg = Message.obtain(); 254 msg.what = TYPE_STATUS_CHANGED; 255 Bundle b = new Bundle(); 256 b.putString("provider", provider); 257 b.putInt("status", status); 258 if (extras != null) { 259 b.putBundle("extras", extras); 260 } 261 msg.obj = b; 262 mListenerHandler.sendMessage(msg); 263 } 264 265 @Override onProviderEnabled(String provider)266 public void onProviderEnabled(String provider) { 267 Message msg = Message.obtain(); 268 msg.what = TYPE_PROVIDER_ENABLED; 269 msg.obj = provider; 270 mListenerHandler.sendMessage(msg); 271 } 272 273 @Override onProviderDisabled(String provider)274 public void onProviderDisabled(String provider) { 275 Message msg = Message.obtain(); 276 msg.what = TYPE_PROVIDER_DISABLED; 277 msg.obj = provider; 278 mListenerHandler.sendMessage(msg); 279 } 280 _handleMessage(Message msg)281 private void _handleMessage(Message msg) { 282 switch (msg.what) { 283 case TYPE_LOCATION_CHANGED: 284 Location location = new Location((Location) msg.obj); 285 mListener.onLocationChanged(location); 286 break; 287 case TYPE_STATUS_CHANGED: 288 Bundle b = (Bundle) msg.obj; 289 String provider = b.getString("provider"); 290 int status = b.getInt("status"); 291 Bundle extras = b.getBundle("extras"); 292 mListener.onStatusChanged(provider, status, extras); 293 break; 294 case TYPE_PROVIDER_ENABLED: 295 mListener.onProviderEnabled((String) msg.obj); 296 break; 297 case TYPE_PROVIDER_DISABLED: 298 mListener.onProviderDisabled((String) msg.obj); 299 break; 300 } 301 try { 302 mService.locationCallbackFinished(this); 303 } catch (RemoteException e) { 304 Log.e(TAG, "locationCallbackFinished: RemoteException", e); 305 } 306 } 307 } 308 309 /** 310 * @hide - hide this constructor because it has a parameter 311 * of type ILocationManager, which is a system private class. The 312 * right way to create an instance of this class is using the 313 * factory Context.getSystemService. 314 */ LocationManager(Context context, ILocationManager service)315 public LocationManager(Context context, ILocationManager service) { 316 mService = service; 317 mContext = context; 318 mGpsMeasurementListenerTransport = new GpsMeasurementListenerTransport(mContext, mService); 319 mGpsNavigationMessageListenerTransport = 320 new GpsNavigationMessageListenerTransport(mContext, mService); 321 } 322 createProvider(String name, ProviderProperties properties)323 private LocationProvider createProvider(String name, ProviderProperties properties) { 324 return new LocationProvider(name, properties); 325 } 326 327 /** 328 * Returns a list of the names of all known location providers. 329 * <p>All providers are returned, including ones that are not permitted to 330 * be accessed by the calling activity or are currently disabled. 331 * 332 * @return list of Strings containing names of the provider 333 */ getAllProviders()334 public List<String> getAllProviders() { 335 try { 336 return mService.getAllProviders(); 337 } catch (RemoteException e) { 338 Log.e(TAG, "RemoteException", e); 339 } 340 return null; 341 } 342 343 /** 344 * Returns a list of the names of location providers. 345 * 346 * @param enabledOnly if true then only the providers which are currently 347 * enabled are returned. 348 * @return list of Strings containing names of the providers 349 */ getProviders(boolean enabledOnly)350 public List<String> getProviders(boolean enabledOnly) { 351 try { 352 return mService.getProviders(null, enabledOnly); 353 } catch (RemoteException e) { 354 Log.e(TAG, "RemoteException", e); 355 } 356 return null; 357 } 358 359 /** 360 * Returns the information associated with the location provider of the 361 * given name, or null if no provider exists by that name. 362 * 363 * @param name the provider name 364 * @return a LocationProvider, or null 365 * 366 * @throws IllegalArgumentException if name is null or does not exist 367 * @throws SecurityException if the caller is not permitted to access the 368 * given provider. 369 */ getProvider(String name)370 public LocationProvider getProvider(String name) { 371 checkProvider(name); 372 try { 373 ProviderProperties properties = mService.getProviderProperties(name); 374 if (properties == null) { 375 return null; 376 } 377 return createProvider(name, properties); 378 } catch (RemoteException e) { 379 Log.e(TAG, "RemoteException", e); 380 } 381 return null; 382 } 383 384 /** 385 * Returns a list of the names of LocationProviders that satisfy the given 386 * criteria, or null if none do. Only providers that are permitted to be 387 * accessed by the calling activity will be returned. 388 * 389 * @param criteria the criteria that the returned providers must match 390 * @param enabledOnly if true then only the providers which are currently 391 * enabled are returned. 392 * @return list of Strings containing names of the providers 393 */ getProviders(Criteria criteria, boolean enabledOnly)394 public List<String> getProviders(Criteria criteria, boolean enabledOnly) { 395 checkCriteria(criteria); 396 try { 397 return mService.getProviders(criteria, enabledOnly); 398 } catch (RemoteException e) { 399 Log.e(TAG, "RemoteException", e); 400 } 401 return null; 402 } 403 404 /** 405 * Returns the name of the provider that best meets the given criteria. Only providers 406 * that are permitted to be accessed by the calling activity will be 407 * returned. If several providers meet the criteria, the one with the best 408 * accuracy is returned. If no provider meets the criteria, 409 * the criteria are loosened in the following sequence: 410 * 411 * <ul> 412 * <li> power requirement 413 * <li> accuracy 414 * <li> bearing 415 * <li> speed 416 * <li> altitude 417 * </ul> 418 * 419 * <p> Note that the requirement on monetary cost is not removed 420 * in this process. 421 * 422 * @param criteria the criteria that need to be matched 423 * @param enabledOnly if true then only a provider that is currently enabled is returned 424 * @return name of the provider that best matches the requirements 425 */ getBestProvider(Criteria criteria, boolean enabledOnly)426 public String getBestProvider(Criteria criteria, boolean enabledOnly) { 427 checkCriteria(criteria); 428 try { 429 return mService.getBestProvider(criteria, enabledOnly); 430 } catch (RemoteException e) { 431 Log.e(TAG, "RemoteException", e); 432 } 433 return null; 434 } 435 436 /** 437 * Register for location updates using the named provider, and a 438 * pending intent. 439 * 440 * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} 441 * for more detail on how to use this method. 442 * 443 * @param provider the name of the provider with which to register 444 * @param minTime minimum time interval between location updates, in milliseconds 445 * @param minDistance minimum distance between location updates, in meters 446 * @param listener a {@link LocationListener} whose 447 * {@link LocationListener#onLocationChanged} method will be called for 448 * each location update 449 * 450 * @throws IllegalArgumentException if provider is null or doesn't exist 451 * on this device 452 * @throws IllegalArgumentException if listener is null 453 * @throws RuntimeException if the calling thread has no Looper 454 * @throws SecurityException if no suitable permission is present 455 */ 456 @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener)457 public void requestLocationUpdates(String provider, long minTime, float minDistance, 458 LocationListener listener) { 459 checkProvider(provider); 460 checkListener(listener); 461 462 LocationRequest request = LocationRequest.createFromDeprecatedProvider( 463 provider, minTime, minDistance, false); 464 requestLocationUpdates(request, listener, null, null); 465 } 466 467 /** 468 * Register for location updates using the named provider, and a callback on 469 * the specified looper thread. 470 * 471 * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} 472 * for more detail on how to use this method. 473 * 474 * @param provider the name of the provider with which to register 475 * @param minTime minimum time interval between location updates, in milliseconds 476 * @param minDistance minimum distance between location updates, in meters 477 * @param listener a {@link LocationListener} whose 478 * {@link LocationListener#onLocationChanged} method will be called for 479 * each location update 480 * @param looper a Looper object whose message queue will be used to 481 * implement the callback mechanism, or null to make callbacks on the calling 482 * thread 483 * 484 * @throws IllegalArgumentException if provider is null or doesn't exist 485 * @throws IllegalArgumentException if listener is null 486 * @throws SecurityException if no suitable permission is present 487 */ 488 @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener, Looper looper)489 public void requestLocationUpdates(String provider, long minTime, float minDistance, 490 LocationListener listener, Looper looper) { 491 checkProvider(provider); 492 checkListener(listener); 493 494 LocationRequest request = LocationRequest.createFromDeprecatedProvider( 495 provider, minTime, minDistance, false); 496 requestLocationUpdates(request, listener, looper, null); 497 } 498 499 /** 500 * Register for location updates using a Criteria, and a callback 501 * on the specified looper thread. 502 * 503 * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} 504 * for more detail on how to use this method. 505 * 506 * @param minTime minimum time interval between location updates, in milliseconds 507 * @param minDistance minimum distance between location updates, in meters 508 * @param criteria contains parameters for the location manager to choose the 509 * appropriate provider and parameters to compute the location 510 * @param listener a {@link LocationListener} whose 511 * {@link LocationListener#onLocationChanged} method will be called for 512 * each location update 513 * @param looper a Looper object whose message queue will be used to 514 * implement the callback mechanism, or null to make callbacks on the calling 515 * thread 516 * 517 * @throws IllegalArgumentException if criteria is null 518 * @throws IllegalArgumentException if listener is null 519 * @throws SecurityException if no suitable permission is present 520 */ 521 @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) requestLocationUpdates(long minTime, float minDistance, Criteria criteria, LocationListener listener, Looper looper)522 public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria, 523 LocationListener listener, Looper looper) { 524 checkCriteria(criteria); 525 checkListener(listener); 526 527 LocationRequest request = LocationRequest.createFromDeprecatedCriteria( 528 criteria, minTime, minDistance, false); 529 requestLocationUpdates(request, listener, looper, null); 530 } 531 532 /** 533 * Register for location updates using the named provider, and a 534 * pending intent. 535 * 536 * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} 537 * for more detail on how to use this method. 538 * 539 * @param provider the name of the provider with which to register 540 * @param minTime minimum time interval between location updates, in milliseconds 541 * @param minDistance minimum distance between location updates, in meters 542 * @param intent a {@link PendingIntent} to be sent for each location update 543 * 544 * @throws IllegalArgumentException if provider is null or doesn't exist 545 * on this device 546 * @throws IllegalArgumentException if intent is null 547 * @throws SecurityException if no suitable permission is present 548 */ 549 @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) requestLocationUpdates(String provider, long minTime, float minDistance, PendingIntent intent)550 public void requestLocationUpdates(String provider, long minTime, float minDistance, 551 PendingIntent intent) { 552 checkProvider(provider); 553 checkPendingIntent(intent); 554 555 LocationRequest request = LocationRequest.createFromDeprecatedProvider( 556 provider, minTime, minDistance, false); 557 requestLocationUpdates(request, null, null, intent); 558 } 559 560 /** 561 * Register for location updates using a Criteria and pending intent. 562 * 563 * <p>The <code>requestLocationUpdates()</code> and 564 * <code>requestSingleUpdate()</code> register the current activity to be 565 * updated periodically by the named provider, or by the provider matching 566 * the specified {@link Criteria}, with location and status updates. 567 * 568 * <p> It may take a while to receive the first location update. If 569 * an immediate location is required, applications may use the 570 * {@link #getLastKnownLocation(String)} method. 571 * 572 * <p> Location updates are received either by {@link LocationListener} 573 * callbacks, or by broadcast intents to a supplied {@link PendingIntent}. 574 * 575 * <p> If the caller supplied a pending intent, then location updates 576 * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a 577 * {@link android.location.Location} value. 578 * 579 * <p> The location update interval can be controlled using the minTime parameter. 580 * The elapsed time between location updates will never be less than 581 * minTime, although it can be more depending on the Location Provider 582 * implementation and the update interval requested by other applications. 583 * 584 * <p> Choosing a sensible value for minTime is important to conserve 585 * battery life. Each location update requires power from 586 * GPS, WIFI, Cell and other radios. Select a minTime value as high as 587 * possible while still providing a reasonable user experience. 588 * If your application is not in the foreground and showing 589 * location to the user then your application should avoid using an active 590 * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}), 591 * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes) 592 * or greater. If your application is in the foreground and showing 593 * location to the user then it is appropriate to select a faster 594 * update interval. 595 * 596 * <p> The minDistance parameter can also be used to control the 597 * frequency of location updates. If it is greater than 0 then the 598 * location provider will only send your application an update when 599 * the location has changed by at least minDistance meters, AND 600 * at least minTime milliseconds have passed. However it is more 601 * difficult for location providers to save power using the minDistance 602 * parameter, so minTime should be the primary tool to conserving battery 603 * life. 604 * 605 * <p> If your application wants to passively observe location 606 * updates triggered by other applications, but not consume 607 * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER} 608 * This provider does not actively turn on or modify active location 609 * providers, so you do not need to be as careful about minTime and 610 * minDistance. However if your application performs heavy work 611 * on a location update (such as network activity) then you should 612 * select non-zero values for minTime and/or minDistance to rate-limit 613 * your update frequency in the case another application enables a 614 * location provider with extremely fast updates. 615 * 616 * <p>In case the provider is disabled by the user, updates will stop, 617 * and a provider availability update will be sent. 618 * As soon as the provider is enabled again, 619 * location updates will immediately resume and a provider availability 620 * update sent. Providers can also send status updates, at any time, 621 * with extra's specific to the provider. If a callback was supplied 622 * then status and availability updates are via 623 * {@link LocationListener#onProviderDisabled}, 624 * {@link LocationListener#onProviderEnabled} or 625 * {@link LocationListener#onStatusChanged}. Alternately, if a 626 * pending intent was supplied then status and availability updates 627 * are broadcast intents with extra keys of 628 * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED}. 629 * 630 * <p> If a {@link LocationListener} is used but with no Looper specified 631 * then the calling thread must already 632 * be a {@link android.os.Looper} thread such as the main thread of the 633 * calling Activity. If a Looper is specified with a {@link LocationListener} 634 * then callbacks are made on the supplied Looper thread. 635 * 636 * <p class="note"> Prior to Jellybean, the minTime parameter was 637 * only a hint, and some location provider implementations ignored it. 638 * From Jellybean and onwards it is mandatory for Android compatible 639 * devices to observe both the minTime and minDistance parameters. 640 * 641 * @param minTime minimum time interval between location updates, in milliseconds 642 * @param minDistance minimum distance between location updates, in meters 643 * @param criteria contains parameters for the location manager to choose the 644 * appropriate provider and parameters to compute the location 645 * @param intent a {@link PendingIntent} to be sent for each location update 646 * 647 * @throws IllegalArgumentException if criteria is null 648 * @throws IllegalArgumentException if intent is null 649 * @throws SecurityException if no suitable permission is present 650 */ 651 @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) requestLocationUpdates(long minTime, float minDistance, Criteria criteria, PendingIntent intent)652 public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria, 653 PendingIntent intent) { 654 checkCriteria(criteria); 655 checkPendingIntent(intent); 656 657 LocationRequest request = LocationRequest.createFromDeprecatedCriteria( 658 criteria, minTime, minDistance, false); 659 requestLocationUpdates(request, null, null, intent); 660 } 661 662 /** 663 * Register for a single location update using the named provider and 664 * a callback. 665 * 666 * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} 667 * for more detail on how to use this method. 668 * 669 * @param provider the name of the provider with which to register 670 * @param listener a {@link LocationListener} whose 671 * {@link LocationListener#onLocationChanged} method will be called when 672 * the location update is available 673 * @param looper a Looper object whose message queue will be used to 674 * implement the callback mechanism, or null to make callbacks on the calling 675 * thread 676 * 677 * @throws IllegalArgumentException if provider is null or doesn't exist 678 * @throws IllegalArgumentException if listener is null 679 * @throws SecurityException if no suitable permission is present 680 */ 681 @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) requestSingleUpdate(String provider, LocationListener listener, Looper looper)682 public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) { 683 checkProvider(provider); 684 checkListener(listener); 685 686 LocationRequest request = LocationRequest.createFromDeprecatedProvider( 687 provider, 0, 0, true); 688 requestLocationUpdates(request, listener, looper, null); 689 } 690 691 /** 692 * Register for a single location update using a Criteria and 693 * a callback. 694 * 695 * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} 696 * for more detail on how to use this method. 697 * 698 * @param criteria contains parameters for the location manager to choose the 699 * appropriate provider and parameters to compute the location 700 * @param listener a {@link LocationListener} whose 701 * {@link LocationListener#onLocationChanged} method will be called when 702 * the location update is available 703 * @param looper a Looper object whose message queue will be used to 704 * implement the callback mechanism, or null to make callbacks on the calling 705 * thread 706 * 707 * @throws IllegalArgumentException if criteria is null 708 * @throws IllegalArgumentException if listener is null 709 * @throws SecurityException if no suitable permission is present 710 */ 711 @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) requestSingleUpdate(Criteria criteria, LocationListener listener, Looper looper)712 public void requestSingleUpdate(Criteria criteria, LocationListener listener, Looper looper) { 713 checkCriteria(criteria); 714 checkListener(listener); 715 716 LocationRequest request = LocationRequest.createFromDeprecatedCriteria( 717 criteria, 0, 0, true); 718 requestLocationUpdates(request, listener, looper, null); 719 } 720 721 /** 722 * Register for a single location update using a named provider and pending intent. 723 * 724 * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} 725 * for more detail on how to use this method. 726 * 727 * @param provider the name of the provider with which to register 728 * @param intent a {@link PendingIntent} to be sent for the location update 729 * 730 * @throws IllegalArgumentException if provider is null or doesn't exist 731 * @throws IllegalArgumentException if intent is null 732 * @throws SecurityException if no suitable permission is present 733 */ 734 @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) requestSingleUpdate(String provider, PendingIntent intent)735 public void requestSingleUpdate(String provider, PendingIntent intent) { 736 checkProvider(provider); 737 checkPendingIntent(intent); 738 739 LocationRequest request = LocationRequest.createFromDeprecatedProvider( 740 provider, 0, 0, true); 741 requestLocationUpdates(request, null, null, intent); 742 } 743 744 /** 745 * Register for a single location update using a Criteria and pending intent. 746 * 747 * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} 748 * for more detail on how to use this method. 749 * 750 * @param criteria contains parameters for the location manager to choose the 751 * appropriate provider and parameters to compute the location 752 * @param intent a {@link PendingIntent} to be sent for the location update 753 * 754 * @throws IllegalArgumentException if provider is null or doesn't exist 755 * @throws IllegalArgumentException if intent is null 756 * @throws SecurityException if no suitable permission is present 757 */ 758 @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) requestSingleUpdate(Criteria criteria, PendingIntent intent)759 public void requestSingleUpdate(Criteria criteria, PendingIntent intent) { 760 checkCriteria(criteria); 761 checkPendingIntent(intent); 762 763 LocationRequest request = LocationRequest.createFromDeprecatedCriteria( 764 criteria, 0, 0, true); 765 requestLocationUpdates(request, null, null, intent); 766 } 767 768 /** 769 * Register for fused location updates using a LocationRequest and callback. 770 * 771 * <p>Upon a location update, the system delivers the new {@link Location} to the 772 * provided {@link LocationListener}, by calling its {@link 773 * LocationListener#onLocationChanged} method.</p> 774 * 775 * <p>The system will automatically select and enable the best providers 776 * to compute a location for your application. It may use only passive 777 * locations, or just a single location source, or it may fuse together 778 * multiple location sources in order to produce the best possible 779 * result, depending on the quality of service requested in the 780 * {@link LocationRequest}. 781 * 782 * <p>LocationRequest can be null, in which case the system will choose 783 * default, low power parameters for location updates. You will occasionally 784 * receive location updates as available, without a major power impact on the 785 * system. If your application just needs an occasional location update 786 * without any strict demands, then pass a null LocationRequest. 787 * 788 * <p>Only one LocationRequest can be registered for each unique callback 789 * or pending intent. So a subsequent request with the same callback or 790 * pending intent will over-write the previous LocationRequest. 791 * 792 * <p> If a pending intent is supplied then location updates 793 * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a 794 * {@link android.location.Location} value. If a callback is supplied 795 * then location updates are made using the 796 * {@link LocationListener#onLocationChanged} callback, on the specified 797 * Looper thread. If a {@link LocationListener} is used 798 * but with a null Looper then the calling thread must already 799 * be a {@link android.os.Looper} thread (such as the main thread) and 800 * callbacks will occur on this thread. 801 * 802 * <p> Provider status updates and availability updates are deprecated 803 * because the system is performing provider fusion on the applications 804 * behalf. So {@link LocationListener#onProviderDisabled}, 805 * {@link LocationListener#onProviderEnabled}, {@link LocationListener#onStatusChanged} 806 * will not be called, and intents with extra keys of 807 * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED} will not 808 * be received. 809 * 810 * <p> To unregister for Location updates, use: {@link #removeUpdates(LocationListener)}. 811 * 812 * @param request quality of service required, null for default low power 813 * @param listener a {@link LocationListener} whose 814 * {@link LocationListener#onLocationChanged} method will be called when 815 * the location update is available 816 * @param looper a Looper object whose message queue will be used to 817 * implement the callback mechanism, or null to make callbacks on the calling 818 * thread 819 * 820 * @throws IllegalArgumentException if listener is null 821 * @throws SecurityException if no suitable permission is present 822 * 823 * @hide 824 */ 825 @SystemApi requestLocationUpdates(LocationRequest request, LocationListener listener, Looper looper)826 public void requestLocationUpdates(LocationRequest request, LocationListener listener, 827 Looper looper) { 828 checkListener(listener); 829 requestLocationUpdates(request, listener, looper, null); 830 } 831 832 833 /** 834 * Register for fused location updates using a LocationRequest and a pending intent. 835 * 836 * <p>Upon a location update, the system delivers the new {@link Location} with your provided 837 * {@link PendingIntent}, as the value for {@link LocationManager#KEY_LOCATION_CHANGED} 838 * in the intent's extras.</p> 839 * 840 * <p> To unregister for Location updates, use: {@link #removeUpdates(PendingIntent)}. 841 * 842 * <p> See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)} 843 * for more detail. 844 * 845 * @param request quality of service required, null for default low power 846 * @param intent a {@link PendingIntent} to be sent for the location update 847 * 848 * @throws IllegalArgumentException if intent is null 849 * @throws SecurityException if no suitable permission is present 850 * 851 * @hide 852 */ 853 @SystemApi requestLocationUpdates(LocationRequest request, PendingIntent intent)854 public void requestLocationUpdates(LocationRequest request, PendingIntent intent) { 855 checkPendingIntent(intent); 856 requestLocationUpdates(request, null, null, intent); 857 } 858 wrapListener(LocationListener listener, Looper looper)859 private ListenerTransport wrapListener(LocationListener listener, Looper looper) { 860 if (listener == null) return null; 861 synchronized (mListeners) { 862 ListenerTransport transport = mListeners.get(listener); 863 if (transport == null) { 864 transport = new ListenerTransport(listener, looper); 865 } 866 mListeners.put(listener, transport); 867 return transport; 868 } 869 } 870 requestLocationUpdates(LocationRequest request, LocationListener listener, Looper looper, PendingIntent intent)871 private void requestLocationUpdates(LocationRequest request, LocationListener listener, 872 Looper looper, PendingIntent intent) { 873 874 String packageName = mContext.getPackageName(); 875 876 // wrap the listener class 877 ListenerTransport transport = wrapListener(listener, looper); 878 879 try { 880 mService.requestLocationUpdates(request, transport, intent, packageName); 881 } catch (RemoteException e) { 882 Log.e(TAG, "RemoteException", e); 883 } 884 } 885 886 /** 887 * Removes all location updates for the specified LocationListener. 888 * 889 * <p>Following this call, updates will no longer 890 * occur for this listener. 891 * 892 * @param listener listener object that no longer needs location updates 893 * @throws IllegalArgumentException if listener is null 894 */ removeUpdates(LocationListener listener)895 public void removeUpdates(LocationListener listener) { 896 checkListener(listener); 897 String packageName = mContext.getPackageName(); 898 899 ListenerTransport transport; 900 synchronized (mListeners) { 901 transport = mListeners.remove(listener); 902 } 903 if (transport == null) return; 904 905 try { 906 mService.removeUpdates(transport, null, packageName); 907 } catch (RemoteException e) { 908 Log.e(TAG, "RemoteException", e); 909 } 910 } 911 912 /** 913 * Removes all location updates for the specified pending intent. 914 * 915 * <p>Following this call, updates will no longer for this pending intent. 916 * 917 * @param intent pending intent object that no longer needs location updates 918 * @throws IllegalArgumentException if intent is null 919 */ removeUpdates(PendingIntent intent)920 public void removeUpdates(PendingIntent intent) { 921 checkPendingIntent(intent); 922 String packageName = mContext.getPackageName(); 923 924 try { 925 mService.removeUpdates(null, intent, packageName); 926 } catch (RemoteException e) { 927 Log.e(TAG, "RemoteException", e); 928 } 929 } 930 931 /** 932 * Set a proximity alert for the location given by the position 933 * (latitude, longitude) and the given radius. 934 * 935 * <p> When the device 936 * detects that it has entered or exited the area surrounding the 937 * location, the given PendingIntent will be used to create an Intent 938 * to be fired. 939 * 940 * <p> The fired Intent will have a boolean extra added with key 941 * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is 942 * entering the proximity region; if false, it is exiting. 943 * 944 * <p> Due to the approximate nature of position estimation, if the 945 * device passes through the given area briefly, it is possible 946 * that no Intent will be fired. Similarly, an Intent could be 947 * fired if the device passes very close to the given area but 948 * does not actually enter it. 949 * 950 * <p> After the number of milliseconds given by the expiration 951 * parameter, the location manager will delete this proximity 952 * alert and no longer monitor it. A value of -1 indicates that 953 * there should be no expiration time. 954 * 955 * <p> Internally, this method uses both {@link #NETWORK_PROVIDER} 956 * and {@link #GPS_PROVIDER}. 957 * 958 * <p>Before API version 17, this method could be used with 959 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or 960 * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. 961 * From API version 17 and onwards, this method requires 962 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission. 963 * 964 * @param latitude the latitude of the central point of the 965 * alert region 966 * @param longitude the longitude of the central point of the 967 * alert region 968 * @param radius the radius of the central point of the 969 * alert region, in meters 970 * @param expiration time for this proximity alert, in milliseconds, 971 * or -1 to indicate no expiration 972 * @param intent a PendingIntent that will be used to generate an Intent to 973 * fire when entry to or exit from the alert region is detected 974 * 975 * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION} 976 * permission is not present 977 */ 978 @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) addProximityAlert(double latitude, double longitude, float radius, long expiration, PendingIntent intent)979 public void addProximityAlert(double latitude, double longitude, float radius, long expiration, 980 PendingIntent intent) { 981 checkPendingIntent(intent); 982 if (expiration < 0) expiration = Long.MAX_VALUE; 983 984 Geofence fence = Geofence.createCircle(latitude, longitude, radius); 985 LocationRequest request = new LocationRequest().setExpireIn(expiration); 986 try { 987 mService.requestGeofence(request, fence, intent, mContext.getPackageName()); 988 } catch (RemoteException e) { 989 Log.e(TAG, "RemoteException", e); 990 } 991 } 992 993 /** 994 * Add a geofence with the specified LocationRequest quality of service. 995 * 996 * <p> When the device 997 * detects that it has entered or exited the area surrounding the 998 * location, the given PendingIntent will be used to create an Intent 999 * to be fired. 1000 * 1001 * <p> The fired Intent will have a boolean extra added with key 1002 * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is 1003 * entering the proximity region; if false, it is exiting. 1004 * 1005 * <p> The geofence engine fuses results from all location providers to 1006 * provide the best balance between accuracy and power. Applications 1007 * can choose the quality of service required using the 1008 * {@link LocationRequest} object. If it is null then a default, 1009 * low power geo-fencing implementation is used. It is possible to cross 1010 * a geo-fence without notification, but the system will do its best 1011 * to detect, using {@link LocationRequest} as a hint to trade-off 1012 * accuracy and power. 1013 * 1014 * <p> The power required by the geofence engine can depend on many factors, 1015 * such as quality and interval requested in {@link LocationRequest}, 1016 * distance to nearest geofence and current device velocity. 1017 * 1018 * @param request quality of service required, null for default low power 1019 * @param fence a geographical description of the geofence area 1020 * @param intent pending intent to receive geofence updates 1021 * 1022 * @throws IllegalArgumentException if fence is null 1023 * @throws IllegalArgumentException if intent is null 1024 * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION} 1025 * permission is not present 1026 * 1027 * @hide 1028 */ 1029 @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) addGeofence(LocationRequest request, Geofence fence, PendingIntent intent)1030 public void addGeofence(LocationRequest request, Geofence fence, PendingIntent intent) { 1031 checkPendingIntent(intent); 1032 checkGeofence(fence); 1033 1034 try { 1035 mService.requestGeofence(request, fence, intent, mContext.getPackageName()); 1036 } catch (RemoteException e) { 1037 Log.e(TAG, "RemoteException", e); 1038 } 1039 } 1040 1041 /** 1042 * Removes the proximity alert with the given PendingIntent. 1043 * 1044 * <p>Before API version 17, this method could be used with 1045 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or 1046 * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. 1047 * From API version 17 and onwards, this method requires 1048 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission. 1049 * 1050 * @param intent the PendingIntent that no longer needs to be notified of 1051 * proximity alerts 1052 * 1053 * @throws IllegalArgumentException if intent is null 1054 * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION} 1055 * permission is not present 1056 */ removeProximityAlert(PendingIntent intent)1057 public void removeProximityAlert(PendingIntent intent) { 1058 checkPendingIntent(intent); 1059 String packageName = mContext.getPackageName(); 1060 1061 try { 1062 mService.removeGeofence(null, intent, packageName); 1063 } catch (RemoteException e) { 1064 Log.e(TAG, "RemoteException", e); 1065 } 1066 } 1067 1068 /** 1069 * Remove a single geofence. 1070 * 1071 * <p>This removes only the specified geofence associated with the 1072 * specified pending intent. All other geofences remain unchanged. 1073 * 1074 * @param fence a geofence previously passed to {@link #addGeofence} 1075 * @param intent a pending intent previously passed to {@link #addGeofence} 1076 * 1077 * @throws IllegalArgumentException if fence is null 1078 * @throws IllegalArgumentException if intent is null 1079 * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION} 1080 * permission is not present 1081 * 1082 * @hide 1083 */ removeGeofence(Geofence fence, PendingIntent intent)1084 public void removeGeofence(Geofence fence, PendingIntent intent) { 1085 checkPendingIntent(intent); 1086 checkGeofence(fence); 1087 String packageName = mContext.getPackageName(); 1088 1089 try { 1090 mService.removeGeofence(fence, intent, packageName); 1091 } catch (RemoteException e) { 1092 Log.e(TAG, "RemoteException", e); 1093 } 1094 } 1095 1096 /** 1097 * Remove all geofences registered to the specified pending intent. 1098 * 1099 * @param intent a pending intent previously passed to {@link #addGeofence} 1100 * 1101 * @throws IllegalArgumentException if intent is null 1102 * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION} 1103 * permission is not present 1104 * 1105 * @hide 1106 */ removeAllGeofences(PendingIntent intent)1107 public void removeAllGeofences(PendingIntent intent) { 1108 checkPendingIntent(intent); 1109 String packageName = mContext.getPackageName(); 1110 1111 try { 1112 mService.removeGeofence(null, intent, packageName); 1113 } catch (RemoteException e) { 1114 Log.e(TAG, "RemoteException", e); 1115 } 1116 } 1117 1118 /** 1119 * Returns the current enabled/disabled status of the given provider. 1120 * 1121 * <p>If the user has enabled this provider in the Settings menu, true 1122 * is returned otherwise false is returned 1123 * 1124 * <p>Callers should instead use 1125 * {@link android.provider.Settings.Secure#LOCATION_MODE} 1126 * unless they depend on provider-specific APIs such as 1127 * {@link #requestLocationUpdates(String, long, float, LocationListener)}. 1128 * 1129 * <p> 1130 * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this 1131 * method would throw {@link SecurityException} if the location permissions 1132 * were not sufficient to use the specified provider. 1133 * 1134 * @param provider the name of the provider 1135 * @return true if the provider exists and is enabled 1136 * 1137 * @throws IllegalArgumentException if provider is null 1138 */ isProviderEnabled(String provider)1139 public boolean isProviderEnabled(String provider) { 1140 checkProvider(provider); 1141 1142 try { 1143 return mService.isProviderEnabled(provider); 1144 } catch (RemoteException e) { 1145 Log.e(TAG, "RemoteException", e); 1146 return false; 1147 } 1148 } 1149 1150 /** 1151 * Get the last known location. 1152 * 1153 * <p>This location could be very old so use 1154 * {@link Location#getElapsedRealtimeNanos} to calculate its age. It can 1155 * also return null if no previous location is available. 1156 * 1157 * <p>Always returns immediately. 1158 * 1159 * @return The last known location, or null if not available 1160 * @throws SecurityException if no suitable permission is present 1161 * 1162 * @hide 1163 */ getLastLocation()1164 public Location getLastLocation() { 1165 String packageName = mContext.getPackageName(); 1166 1167 try { 1168 return mService.getLastLocation(null, packageName); 1169 } catch (RemoteException e) { 1170 Log.e(TAG, "RemoteException", e); 1171 return null; 1172 } 1173 } 1174 1175 /** 1176 * Returns a Location indicating the data from the last known 1177 * location fix obtained from the given provider. 1178 * 1179 * <p> This can be done 1180 * without starting the provider. Note that this location could 1181 * be out-of-date, for example if the device was turned off and 1182 * moved to another location. 1183 * 1184 * <p> If the provider is currently disabled, null is returned. 1185 * 1186 * @param provider the name of the provider 1187 * @return the last known location for the provider, or null 1188 * 1189 * @throws SecurityException if no suitable permission is present 1190 * @throws IllegalArgumentException if provider is null or doesn't exist 1191 */ 1192 @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) getLastKnownLocation(String provider)1193 public Location getLastKnownLocation(String provider) { 1194 checkProvider(provider); 1195 String packageName = mContext.getPackageName(); 1196 LocationRequest request = LocationRequest.createFromDeprecatedProvider( 1197 provider, 0, 0, true); 1198 1199 try { 1200 return mService.getLastLocation(request, packageName); 1201 } catch (RemoteException e) { 1202 Log.e(TAG, "RemoteException", e); 1203 return null; 1204 } 1205 } 1206 1207 // --- Mock provider support --- 1208 // TODO: It would be fantastic to deprecate mock providers entirely, and replace 1209 // with something closer to LocationProviderBase.java 1210 1211 /** 1212 * Creates a mock location provider and adds it to the set of active providers. 1213 * 1214 * @param name the provider name 1215 * 1216 * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION 1217 * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED 1218 * allowed} for your app. 1219 * @throws IllegalArgumentException if a provider with the given name already exists 1220 */ addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite, boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude, boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy)1221 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite, 1222 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude, 1223 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) { 1224 ProviderProperties properties = new ProviderProperties(requiresNetwork, 1225 requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed, 1226 supportsBearing, powerRequirement, accuracy); 1227 if (name.matches(LocationProvider.BAD_CHARS_REGEX)) { 1228 throw new IllegalArgumentException("provider name contains illegal character: " + name); 1229 } 1230 1231 try { 1232 mService.addTestProvider(name, properties, mContext.getOpPackageName()); 1233 } catch (RemoteException e) { 1234 Log.e(TAG, "RemoteException", e); 1235 } 1236 } 1237 1238 /** 1239 * Removes the mock location provider with the given name. 1240 * 1241 * @param provider the provider name 1242 * 1243 * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION 1244 * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED 1245 * allowed} for your app. 1246 * @throws IllegalArgumentException if no provider with the given name exists 1247 */ removeTestProvider(String provider)1248 public void removeTestProvider(String provider) { 1249 try { 1250 mService.removeTestProvider(provider, mContext.getOpPackageName()); 1251 } catch (RemoteException e) { 1252 Log.e(TAG, "RemoteException", e); 1253 } 1254 } 1255 1256 /** 1257 * Sets a mock location for the given provider. 1258 * <p>This location will be used in place of any actual location from the provider. 1259 * The location object must have a minimum number of fields set to be 1260 * considered a valid LocationProvider Location, as per documentation 1261 * on {@link Location} class. 1262 * 1263 * @param provider the provider name 1264 * @param loc the mock location 1265 * 1266 * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION 1267 * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED 1268 * allowed} for your app. 1269 * @throws IllegalArgumentException if no provider with the given name exists 1270 * @throws IllegalArgumentException if the location is incomplete 1271 */ setTestProviderLocation(String provider, Location loc)1272 public void setTestProviderLocation(String provider, Location loc) { 1273 if (!loc.isComplete()) { 1274 IllegalArgumentException e = new IllegalArgumentException( 1275 "Incomplete location object, missing timestamp or accuracy? " + loc); 1276 if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) { 1277 // just log on old platform (for backwards compatibility) 1278 Log.w(TAG, e); 1279 loc.makeComplete(); 1280 } else { 1281 // really throw it! 1282 throw e; 1283 } 1284 } 1285 1286 try { 1287 mService.setTestProviderLocation(provider, loc, mContext.getOpPackageName()); 1288 } catch (RemoteException e) { 1289 Log.e(TAG, "RemoteException", e); 1290 } 1291 } 1292 1293 /** 1294 * Removes any mock location associated with the given provider. 1295 * 1296 * @param provider the provider name 1297 * 1298 * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION 1299 * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED 1300 * allowed} for your app. 1301 * @throws IllegalArgumentException if no provider with the given name exists 1302 */ clearTestProviderLocation(String provider)1303 public void clearTestProviderLocation(String provider) { 1304 try { 1305 mService.clearTestProviderLocation(provider, mContext.getOpPackageName()); 1306 } catch (RemoteException e) { 1307 Log.e(TAG, "RemoteException", e); 1308 } 1309 } 1310 1311 /** 1312 * Sets a mock enabled value for the given provider. This value will be used in place 1313 * of any actual value from the provider. 1314 * 1315 * @param provider the provider name 1316 * @param enabled the mock enabled value 1317 * 1318 * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION 1319 * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED 1320 * allowed} for your app. 1321 * @throws IllegalArgumentException if no provider with the given name exists 1322 */ setTestProviderEnabled(String provider, boolean enabled)1323 public void setTestProviderEnabled(String provider, boolean enabled) { 1324 try { 1325 mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName()); 1326 } catch (RemoteException e) { 1327 Log.e(TAG, "RemoteException", e); 1328 } 1329 } 1330 1331 /** 1332 * Removes any mock enabled value associated with the given provider. 1333 * 1334 * @param provider the provider name 1335 * 1336 * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION 1337 * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED 1338 * allowed} for your app. 1339 * @throws IllegalArgumentException if no provider with the given name exists 1340 */ clearTestProviderEnabled(String provider)1341 public void clearTestProviderEnabled(String provider) { 1342 try { 1343 mService.clearTestProviderEnabled(provider, mContext.getOpPackageName()); 1344 } catch (RemoteException e) { 1345 Log.e(TAG, "RemoteException", e); 1346 } 1347 } 1348 1349 /** 1350 * Sets mock status values for the given provider. These values will be used in place 1351 * of any actual values from the provider. 1352 * 1353 * @param provider the provider name 1354 * @param status the mock status 1355 * @param extras a Bundle containing mock extras 1356 * @param updateTime the mock update time 1357 * 1358 * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION 1359 * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED 1360 * allowed} for your app. 1361 * @throws IllegalArgumentException if no provider with the given name exists 1362 */ setTestProviderStatus(String provider, int status, Bundle extras, long updateTime)1363 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) { 1364 try { 1365 mService.setTestProviderStatus(provider, status, extras, updateTime, 1366 mContext.getOpPackageName()); 1367 } catch (RemoteException e) { 1368 Log.e(TAG, "RemoteException", e); 1369 } 1370 } 1371 1372 /** 1373 * Removes any mock status values associated with the given provider. 1374 * 1375 * @param provider the provider name 1376 * 1377 * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION 1378 * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED 1379 * allowed} for your app. 1380 * @throws IllegalArgumentException if no provider with the given name exists 1381 */ clearTestProviderStatus(String provider)1382 public void clearTestProviderStatus(String provider) { 1383 try { 1384 mService.clearTestProviderStatus(provider, mContext.getOpPackageName()); 1385 } catch (RemoteException e) { 1386 Log.e(TAG, "RemoteException", e); 1387 } 1388 } 1389 1390 // --- GPS-specific support --- 1391 1392 // This class is used to send GPS status events to the client's main thread. 1393 private class GpsStatusListenerTransport extends IGpsStatusListener.Stub { 1394 1395 private final GpsStatus.Listener mListener; 1396 private final GpsStatus.NmeaListener mNmeaListener; 1397 1398 // This must not equal any of the GpsStatus event IDs 1399 private static final int NMEA_RECEIVED = 1000; 1400 1401 private class Nmea { 1402 long mTimestamp; 1403 String mNmea; 1404 Nmea(long timestamp, String nmea)1405 Nmea(long timestamp, String nmea) { 1406 mTimestamp = timestamp; 1407 mNmea = nmea; 1408 } 1409 } 1410 private ArrayList<Nmea> mNmeaBuffer; 1411 GpsStatusListenerTransport(GpsStatus.Listener listener)1412 GpsStatusListenerTransport(GpsStatus.Listener listener) { 1413 mListener = listener; 1414 mNmeaListener = null; 1415 } 1416 GpsStatusListenerTransport(GpsStatus.NmeaListener listener)1417 GpsStatusListenerTransport(GpsStatus.NmeaListener listener) { 1418 mNmeaListener = listener; 1419 mListener = null; 1420 mNmeaBuffer = new ArrayList<Nmea>(); 1421 } 1422 1423 @Override onGpsStarted()1424 public void onGpsStarted() { 1425 if (mListener != null) { 1426 Message msg = Message.obtain(); 1427 msg.what = GpsStatus.GPS_EVENT_STARTED; 1428 mGpsHandler.sendMessage(msg); 1429 } 1430 } 1431 1432 @Override onGpsStopped()1433 public void onGpsStopped() { 1434 if (mListener != null) { 1435 Message msg = Message.obtain(); 1436 msg.what = GpsStatus.GPS_EVENT_STOPPED; 1437 mGpsHandler.sendMessage(msg); 1438 } 1439 } 1440 1441 @Override onFirstFix(int ttff)1442 public void onFirstFix(int ttff) { 1443 if (mListener != null) { 1444 mGpsStatus.setTimeToFirstFix(ttff); 1445 Message msg = Message.obtain(); 1446 msg.what = GpsStatus.GPS_EVENT_FIRST_FIX; 1447 mGpsHandler.sendMessage(msg); 1448 } 1449 } 1450 1451 @Override onSvStatusChanged(int svCount, int[] prns, float[] snrs, float[] elevations, float[] azimuths, int ephemerisMask, int almanacMask, int usedInFixMask)1452 public void onSvStatusChanged(int svCount, int[] prns, float[] snrs, 1453 float[] elevations, float[] azimuths, int ephemerisMask, 1454 int almanacMask, int usedInFixMask) { 1455 if (mListener != null) { 1456 mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths, 1457 ephemerisMask, almanacMask, usedInFixMask); 1458 1459 Message msg = Message.obtain(); 1460 msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS; 1461 // remove any SV status messages already in the queue 1462 mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS); 1463 mGpsHandler.sendMessage(msg); 1464 } 1465 } 1466 1467 @Override onNmeaReceived(long timestamp, String nmea)1468 public void onNmeaReceived(long timestamp, String nmea) { 1469 if (mNmeaListener != null) { 1470 synchronized (mNmeaBuffer) { 1471 mNmeaBuffer.add(new Nmea(timestamp, nmea)); 1472 } 1473 Message msg = Message.obtain(); 1474 msg.what = NMEA_RECEIVED; 1475 // remove any NMEA_RECEIVED messages already in the queue 1476 mGpsHandler.removeMessages(NMEA_RECEIVED); 1477 mGpsHandler.sendMessage(msg); 1478 } 1479 } 1480 1481 private final Handler mGpsHandler = new Handler() { 1482 @Override 1483 public void handleMessage(Message msg) { 1484 if (msg.what == NMEA_RECEIVED) { 1485 synchronized (mNmeaBuffer) { 1486 int length = mNmeaBuffer.size(); 1487 for (int i = 0; i < length; i++) { 1488 Nmea nmea = mNmeaBuffer.get(i); 1489 mNmeaListener.onNmeaReceived(nmea.mTimestamp, nmea.mNmea); 1490 } 1491 mNmeaBuffer.clear(); 1492 } 1493 } else { 1494 // synchronize on mGpsStatus to ensure the data is copied atomically. 1495 synchronized(mGpsStatus) { 1496 mListener.onGpsStatusChanged(msg.what); 1497 } 1498 } 1499 } 1500 }; 1501 } 1502 1503 /** 1504 * Adds a GPS status listener. 1505 * 1506 * @param listener GPS status listener object to register 1507 * 1508 * @return true if the listener was successfully added 1509 * 1510 * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present 1511 */ 1512 @RequiresPermission(ACCESS_FINE_LOCATION) addGpsStatusListener(GpsStatus.Listener listener)1513 public boolean addGpsStatusListener(GpsStatus.Listener listener) { 1514 boolean result; 1515 1516 if (mGpsStatusListeners.get(listener) != null) { 1517 // listener is already registered 1518 return true; 1519 } 1520 try { 1521 GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener); 1522 result = mService.addGpsStatusListener(transport, mContext.getPackageName()); 1523 if (result) { 1524 mGpsStatusListeners.put(listener, transport); 1525 } 1526 } catch (RemoteException e) { 1527 Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e); 1528 result = false; 1529 } 1530 1531 return result; 1532 } 1533 1534 /** 1535 * Removes a GPS status listener. 1536 * 1537 * @param listener GPS status listener object to remove 1538 */ removeGpsStatusListener(GpsStatus.Listener listener)1539 public void removeGpsStatusListener(GpsStatus.Listener listener) { 1540 try { 1541 GpsStatusListenerTransport transport = mGpsStatusListeners.remove(listener); 1542 if (transport != null) { 1543 mService.removeGpsStatusListener(transport); 1544 } 1545 } catch (RemoteException e) { 1546 Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e); 1547 } 1548 } 1549 1550 /** 1551 * Adds an NMEA listener. 1552 * 1553 * @param listener a {@link GpsStatus.NmeaListener} object to register 1554 * 1555 * @return true if the listener was successfully added 1556 * 1557 * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present 1558 */ 1559 @RequiresPermission(ACCESS_FINE_LOCATION) addNmeaListener(GpsStatus.NmeaListener listener)1560 public boolean addNmeaListener(GpsStatus.NmeaListener listener) { 1561 boolean result; 1562 1563 if (mNmeaListeners.get(listener) != null) { 1564 // listener is already registered 1565 return true; 1566 } 1567 try { 1568 GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener); 1569 result = mService.addGpsStatusListener(transport, mContext.getPackageName()); 1570 if (result) { 1571 mNmeaListeners.put(listener, transport); 1572 } 1573 } catch (RemoteException e) { 1574 Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e); 1575 result = false; 1576 } 1577 1578 return result; 1579 } 1580 1581 /** 1582 * Removes an NMEA listener. 1583 * 1584 * @param listener a {@link GpsStatus.NmeaListener} object to remove 1585 */ removeNmeaListener(GpsStatus.NmeaListener listener)1586 public void removeNmeaListener(GpsStatus.NmeaListener listener) { 1587 try { 1588 GpsStatusListenerTransport transport = mNmeaListeners.remove(listener); 1589 if (transport != null) { 1590 mService.removeGpsStatusListener(transport); 1591 } 1592 } catch (RemoteException e) { 1593 Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e); 1594 } 1595 } 1596 1597 /** 1598 * Adds a GPS Measurement listener. 1599 * 1600 * @param listener a {@link GpsMeasurementsEvent.Listener} object to register. 1601 * @return {@code true} if the listener was added successfully, {@code false} otherwise. 1602 * 1603 * @hide 1604 */ 1605 @SystemApi addGpsMeasurementListener(GpsMeasurementsEvent.Listener listener)1606 public boolean addGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) { 1607 return mGpsMeasurementListenerTransport.add(listener); 1608 } 1609 1610 /** 1611 * Removes a GPS Measurement listener. 1612 * 1613 * @param listener a {@link GpsMeasurementsEvent.Listener} object to remove. 1614 * 1615 * @hide 1616 */ 1617 @SystemApi removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener)1618 public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) { 1619 mGpsMeasurementListenerTransport.remove(listener); 1620 } 1621 1622 /** 1623 * Adds a GPS Navigation Message listener. 1624 * 1625 * @param listener a {@link GpsNavigationMessageEvent.Listener} object to register. 1626 * @return {@code true} if the listener was added successfully, {@code false} otherwise. 1627 * 1628 * @hide 1629 */ 1630 @SystemApi addGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener)1631 public boolean addGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) { 1632 return mGpsNavigationMessageListenerTransport.add(listener); 1633 } 1634 1635 /** 1636 * Removes a GPS Navigation Message listener. 1637 * 1638 * @param listener a {@link GpsNavigationMessageEvent.Listener} object to remove. 1639 * 1640 * @hide 1641 */ 1642 @SystemApi removeGpsNavigationMessageListener( GpsNavigationMessageEvent.Listener listener)1643 public void removeGpsNavigationMessageListener( 1644 GpsNavigationMessageEvent.Listener listener) { 1645 mGpsNavigationMessageListenerTransport.remove(listener); 1646 } 1647 1648 /** 1649 * Retrieves information about the current status of the GPS engine. 1650 * This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged} 1651 * callback to ensure that the data is copied atomically. 1652 * 1653 * The caller may either pass in a {@link GpsStatus} object to set with the latest 1654 * status information, or pass null to create a new {@link GpsStatus} object. 1655 * 1656 * @param status object containing GPS status details, or null. 1657 * @return status object containing updated GPS status. 1658 */ getGpsStatus(GpsStatus status)1659 public GpsStatus getGpsStatus(GpsStatus status) { 1660 if (status == null) { 1661 status = new GpsStatus(); 1662 } 1663 status.setStatus(mGpsStatus); 1664 return status; 1665 } 1666 1667 /** 1668 * Sends additional commands to a location provider. 1669 * Can be used to support provider specific extensions to the Location Manager API 1670 * 1671 * @param provider name of the location provider. 1672 * @param command name of the command to send to the provider. 1673 * @param extras optional arguments for the command (or null). 1674 * The provider may optionally fill the extras Bundle with results from the command. 1675 * 1676 * @return true if the command succeeds. 1677 */ sendExtraCommand(String provider, String command, Bundle extras)1678 public boolean sendExtraCommand(String provider, String command, Bundle extras) { 1679 try { 1680 return mService.sendExtraCommand(provider, command, extras); 1681 } catch (RemoteException e) { 1682 Log.e(TAG, "RemoteException in sendExtraCommand: ", e); 1683 return false; 1684 } 1685 } 1686 1687 /** 1688 * Used by NetInitiatedActivity to report user response 1689 * for network initiated GPS fix requests. 1690 * 1691 * @hide 1692 */ sendNiResponse(int notifId, int userResponse)1693 public boolean sendNiResponse(int notifId, int userResponse) { 1694 try { 1695 return mService.sendNiResponse(notifId, userResponse); 1696 } catch (RemoteException e) { 1697 Log.e(TAG, "RemoteException in sendNiResponse: ", e); 1698 return false; 1699 } 1700 } 1701 checkProvider(String provider)1702 private static void checkProvider(String provider) { 1703 if (provider == null) { 1704 throw new IllegalArgumentException("invalid provider: " + provider); 1705 } 1706 } 1707 checkCriteria(Criteria criteria)1708 private static void checkCriteria(Criteria criteria) { 1709 if (criteria == null) { 1710 throw new IllegalArgumentException("invalid criteria: " + criteria); 1711 } 1712 } 1713 checkListener(LocationListener listener)1714 private static void checkListener(LocationListener listener) { 1715 if (listener == null) { 1716 throw new IllegalArgumentException("invalid listener: " + listener); 1717 } 1718 } 1719 checkPendingIntent(PendingIntent intent)1720 private void checkPendingIntent(PendingIntent intent) { 1721 if (intent == null) { 1722 throw new IllegalArgumentException("invalid pending intent: " + intent); 1723 } 1724 if (!intent.isTargetedToPackage()) { 1725 IllegalArgumentException e = new IllegalArgumentException( 1726 "pending intent must be targeted to package"); 1727 if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) { 1728 throw e; 1729 } else { 1730 Log.w(TAG, e); 1731 } 1732 } 1733 } 1734 checkGeofence(Geofence fence)1735 private static void checkGeofence(Geofence fence) { 1736 if (fence == null) { 1737 throw new IllegalArgumentException("invalid geofence: " + fence); 1738 } 1739 } 1740 } 1741