LocationManagerService.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 com.android.server; 18 19import java.io.BufferedReader; 20import java.io.File; 21import java.io.FileReader; 22import java.io.FileWriter; 23import java.io.IOException; 24import java.util.ArrayList; 25import java.util.HashMap; 26import java.util.HashSet; 27import java.util.List; 28import java.util.Locale; 29import java.util.Set; 30import java.util.regex.Pattern; 31 32import android.app.AlarmManager; 33import android.app.PendingIntent; 34import android.content.BroadcastReceiver; 35import android.content.ContentResolver; 36import android.content.Context; 37import android.content.Intent; 38import android.content.IntentFilter; 39import android.content.pm.PackageManager; 40import android.location.Address; 41import android.location.IGpsStatusListener; 42import android.location.ILocationListener; 43import android.location.ILocationManager; 44import android.location.Location; 45import android.location.LocationManager; 46import android.location.LocationProvider; 47import android.location.LocationProviderImpl; 48import android.net.ConnectivityManager; 49import android.net.wifi.ScanResult; 50import android.net.wifi.WifiManager; 51import android.os.Binder; 52import android.os.Bundle; 53import android.os.Handler; 54import android.os.IBinder; 55import android.os.Message; 56import android.os.PowerManager; 57import android.os.RemoteException; 58import android.os.SystemClock; 59import android.provider.Settings; 60import android.telephony.CellLocation; 61import android.telephony.PhoneStateListener; 62import android.telephony.ServiceState; 63import android.telephony.TelephonyManager; 64import android.util.Config; 65import android.util.Log; 66 67import com.android.internal.location.CellState; 68import com.android.internal.location.GpsLocationProvider; 69import com.android.internal.location.LocationCollector; 70import com.android.internal.location.LocationMasfClient; 71import com.android.internal.location.NetworkLocationProvider; 72import com.android.internal.location.TrackProvider; 73 74/** 75 * The service class that manages LocationProviders and issues location 76 * updates and alerts. 77 * 78 * {@hide} 79 */ 80public class LocationManagerService extends ILocationManager.Stub { 81 private static final String TAG = "LocationManagerService"; 82 83 // Minimum time interval between last known location writes, in milliseconds. 84 private static final long MIN_LAST_KNOWN_LOCATION_TIME = 60L * 1000L; 85 86 // Max time to hold wake lock for, in milliseconds. 87 private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L; 88 89 // Time to wait after releasing a wake lock for clients to process location update, 90 // in milliseconds. 91 private static final long TIME_AFTER_WAKE_LOCK = 2 * 1000L; 92 93 // The last time a location was written, by provider name. 94 private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>(); 95 96 private static final Pattern PATTERN_COMMA = Pattern.compile(","); 97 98 private static final String ACCESS_FINE_LOCATION = 99 android.Manifest.permission.ACCESS_FINE_LOCATION; 100 private static final String ACCESS_COARSE_LOCATION = 101 android.Manifest.permission.ACCESS_COARSE_LOCATION; 102 private static final String ACCESS_MOCK_LOCATION = 103 android.Manifest.permission.ACCESS_MOCK_LOCATION; 104 private static final String ACCESS_LOCATION_EXTRA_COMMANDS = 105 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS; 106 107 // Set of providers that are explicitly enabled 108 private final Set<String> mEnabledProviders = new HashSet<String>(); 109 110 // Set of providers that are explicitly disabled 111 private final Set<String> mDisabledProviders = new HashSet<String>(); 112 113 // Locations, status values, and extras for mock providers 114 HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>(); 115 private final HashMap<String,Location> mMockProviderLocation = new HashMap<String,Location>(); 116 private final HashMap<String,Integer> mMockProviderStatus = new HashMap<String,Integer>(); 117 private final HashMap<String,Bundle> mMockProviderStatusExtras = new HashMap<String,Bundle>(); 118 private final HashMap<String,Long> mMockProviderStatusUpdateTime = new HashMap<String,Long>(); 119 120 private static boolean sProvidersLoaded = false; 121 122 private final Context mContext; 123 private GpsLocationProvider mGpsLocationProvider; 124 private NetworkLocationProvider mNetworkLocationProvider; 125 private LocationWorkerHandler mLocationHandler; 126 127 // Handler messages 128 private static final int MESSAGE_HEARTBEAT = 1; 129 private static final int MESSAGE_ACQUIRE_WAKE_LOCK = 2; 130 private static final int MESSAGE_RELEASE_WAKE_LOCK = 3; 131 132 // Alarm manager and wakelock variables 133 private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT"; 134 private final static String WAKELOCK_KEY = "LocationManagerService"; 135 private final static String WIFILOCK_KEY = "LocationManagerService"; 136 private AlarmManager mAlarmManager; 137 private long mAlarmInterval = 0; 138 private boolean mScreenOn = true; 139 private PowerManager.WakeLock mWakeLock = null; 140 private WifiManager.WifiLock mWifiLock = null; 141 private long mWakeLockAcquireTime = 0; 142 private boolean mWakeLockGpsReceived = true; 143 private boolean mWakeLockNetworkReceived = true; 144 private boolean mWifiWakeLockAcquired = false; 145 private boolean mCellWakeLockAcquired = false; 146 147 /** 148 * Mapping from listener IBinder to local Listener wrappers. 149 */ 150 private final HashMap<IBinder,Listener> mListeners = 151 new HashMap<IBinder,Listener>(); 152 153 /** 154 * Mapping from listener IBinder to a map from provider name to UpdateRecord. 155 */ 156 private final HashMap<IBinder,HashMap<String,UpdateRecord>> mLocationListeners = 157 new HashMap<IBinder,HashMap<String,UpdateRecord>>(); 158 159 /** 160 * Mapping from listener IBinder to a map from provider name to last broadcast location. 161 */ 162 private final HashMap<IBinder,HashMap<String,Location>> mLastFixBroadcast = 163 new HashMap<IBinder,HashMap<String,Location>>(); 164 private final HashMap<IBinder,HashMap<String,Long>> mLastStatusBroadcast = 165 new HashMap<IBinder,HashMap<String,Long>>(); 166 167 /** 168 * Mapping from provider name to all its UpdateRecords 169 */ 170 private final HashMap<String,HashSet<UpdateRecord>> mRecordsByProvider = 171 new HashMap<String,HashSet<UpdateRecord>>(); 172 173 /** 174 * Mappings from provider name to object to use for current location. Locations 175 * contained in this list may not always be valid. 176 */ 177 private final HashMap<String,Location> mLocationsByProvider = 178 new HashMap<String,Location>(); 179 180 // Proximity listeners 181 private ProximityListener mProximityListener = null; 182 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts = 183 new HashMap<PendingIntent,ProximityAlert>(); 184 private HashSet<ProximityAlert> mProximitiesEntered = 185 new HashSet<ProximityAlert>(); 186 187 // Last known location for each provider 188 private HashMap<String,Location> mLastKnownLocation = 189 new HashMap<String,Location>(); 190 191 // Battery status extras (from com.android.server.BatteryService) 192 private static final String BATTERY_EXTRA_SCALE = "scale"; 193 private static final String BATTERY_EXTRA_LEVEL = "level"; 194 private static final String BATTERY_EXTRA_PLUGGED = "plugged"; 195 196 // Last known cell service state 197 private TelephonyManager mTelephonyManager; 198 private ServiceState mServiceState = new ServiceState(); 199 200 // Location collector 201 private LocationCollector mCollector; 202 203 // Location MASF service 204 private LocationMasfClient mMasfClient; 205 206 // Wifi Manager 207 private WifiManager mWifiManager; 208 209 private final class Listener implements IBinder.DeathRecipient { 210 final ILocationListener mListener; 211 212 Listener(ILocationListener listener) { 213 mListener = listener; 214 } 215 216 public void binderDied() { 217 if (Config.LOGD) { 218 Log.d(TAG, "Location listener died"); 219 } 220 synchronized (mLocationListeners) { 221 _removeUpdates(mListener); 222 } 223 } 224 } 225 226 private Location readLastKnownLocation(String provider) { 227 Location location = null; 228 String s = null; 229 try { 230 File f = new File(LocationManager.SYSTEM_DIR + "/location." 231 + provider); 232 if (!f.exists()) { 233 return null; 234 } 235 BufferedReader reader = new BufferedReader(new FileReader(f), 256); 236 s = reader.readLine(); 237 } catch (IOException e) { 238 Log.w(TAG, "Unable to read last known location", e); 239 } 240 241 if (s == null) { 242 return null; 243 } 244 try { 245 String[] tokens = PATTERN_COMMA.split(s); 246 int idx = 0; 247 long time = Long.parseLong(tokens[idx++]); 248 double latitude = Double.parseDouble(tokens[idx++]); 249 double longitude = Double.parseDouble(tokens[idx++]); 250 double altitude = Double.parseDouble(tokens[idx++]); 251 float bearing = Float.parseFloat(tokens[idx++]); 252 float speed = Float.parseFloat(tokens[idx++]); 253 254 location = new Location(provider); 255 location.setTime(time); 256 location.setLatitude(latitude); 257 location.setLongitude(longitude); 258 location.setAltitude(altitude); 259 location.setBearing(bearing); 260 location.setSpeed(speed); 261 } catch (NumberFormatException nfe) { 262 Log.e(TAG, "NumberFormatException reading last known location", nfe); 263 return null; 264 } 265 266 return location; 267 } 268 269 private void writeLastKnownLocation(String provider, 270 Location location) { 271 long now = SystemClock.elapsedRealtime(); 272 Long last = mLastWriteTime.get(provider); 273 if ((last != null) 274 && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) { 275 return; 276 } 277 mLastWriteTime.put(provider, now); 278 279 StringBuilder sb = new StringBuilder(100); 280 sb.append(location.getTime()); 281 sb.append(','); 282 sb.append(location.getLatitude()); 283 sb.append(','); 284 sb.append(location.getLongitude()); 285 sb.append(','); 286 sb.append(location.getAltitude()); 287 sb.append(','); 288 sb.append(location.getBearing()); 289 sb.append(','); 290 sb.append(location.getSpeed()); 291 292 FileWriter writer = null; 293 try { 294 File d = new File(LocationManager.SYSTEM_DIR); 295 if (!d.exists()) { 296 if (!d.mkdirs()) { 297 Log.w(TAG, "Unable to create directory to write location"); 298 return; 299 } 300 } 301 File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider); 302 writer = new FileWriter(f); 303 writer.write(sb.toString()); 304 } catch (IOException e) { 305 Log.w(TAG, "Unable to write location", e); 306 } finally { 307 if (writer != null) { 308 try { 309 writer.close(); 310 } catch (IOException e) { 311 Log.w(TAG, "Exception closing file", e); 312 } 313 } 314 } 315 } 316 317 /** 318 * Load providers from /data/location/<provider_name>/ 319 * class 320 * kml 321 * nmea 322 * track 323 * location 324 * properties 325 */ 326 private void loadProviders() { 327 synchronized (LocationManagerService.class) { 328 if (sProvidersLoaded) { 329 return; 330 } 331 332 // Load providers 333 loadProvidersNoSync(); 334 sProvidersLoaded = true; 335 } 336 } 337 338 private void loadProvidersNoSync() { 339 try { 340 _loadProvidersNoSync(); 341 } catch (Exception e) { 342 Log.e(TAG, "Exception loading providers:", e); 343 } 344 } 345 346 private void _loadProvidersNoSync() { 347 // Attempt to load "real" providers first 348 if (NetworkLocationProvider.isSupported()) { 349 // Create a network location provider 350 mNetworkLocationProvider = new NetworkLocationProvider(mContext, mMasfClient); 351 LocationProviderImpl.addProvider(mNetworkLocationProvider); 352 } 353 354 if (GpsLocationProvider.isSupported()) { 355 // Create a gps location provider 356 mGpsLocationProvider = new GpsLocationProvider(mContext, mCollector); 357 LocationProviderImpl.addProvider(mGpsLocationProvider); 358 } 359 360 // Load fake providers if real providers are not available 361 File f = new File(LocationManager.PROVIDER_DIR); 362 if (f.isDirectory()) { 363 File[] subdirs = f.listFiles(); 364 for (int i = 0; i < subdirs.length; i++) { 365 if (!subdirs[i].isDirectory()) { 366 continue; 367 } 368 369 String name = subdirs[i].getName(); 370 371 if (Config.LOGD) { 372 Log.d(TAG, "Found dir " + subdirs[i].getAbsolutePath()); 373 Log.d(TAG, "name = " + name); 374 } 375 376 // Don't create a fake provider if a real provider exists 377 if (LocationProviderImpl.getProvider(name) == null) { 378 LocationProviderImpl provider = null; 379 try { 380 File classFile = new File(subdirs[i], "class"); 381 // Look for a 'class' file 382 provider = LocationProviderImpl.loadFromClass(classFile); 383 384 // Look for an 'kml', 'nmea', or 'track' file 385 if (provider == null) { 386 // Load properties from 'properties' file, if present 387 File propertiesFile = new File(subdirs[i], "properties"); 388 389 if (propertiesFile.exists()) { 390 provider = new TrackProvider(name); 391 ((TrackProvider)provider).readProperties(propertiesFile); 392 393 File kmlFile = new File(subdirs[i], "kml"); 394 if (kmlFile.exists()) { 395 ((TrackProvider) provider).readKml(kmlFile); 396 } else { 397 File nmeaFile = new File(subdirs[i], "nmea"); 398 if (nmeaFile.exists()) { 399 ((TrackProvider) provider).readNmea(name, nmeaFile); 400 } else { 401 File trackFile = new File(subdirs[i], "track"); 402 if (trackFile.exists()) { 403 ((TrackProvider) provider).readTrack(trackFile); 404 } 405 } 406 } 407 } 408 } 409 if (provider != null) { 410 LocationProviderImpl.addProvider(provider); 411 } 412 // Grab the initial location of a TrackProvider and 413 // store it as the last known location for that provider 414 if (provider instanceof TrackProvider) { 415 TrackProvider tp = (TrackProvider) provider; 416 mLastKnownLocation.put(tp.getName(), tp.getInitialLocation()); 417 } 418 } catch (Exception e) { 419 Log.e(TAG, "Exception loading provder " + name, e); 420 } 421 } 422 } 423 } 424 425 updateProviders(); 426 } 427 428 /** 429 * @param context the context that the LocationManagerService runs in 430 */ 431 public LocationManagerService(Context context) { 432 super(); 433 mContext = context; 434 mLocationHandler = new LocationWorkerHandler(); 435 436 if (Config.LOGD) { 437 Log.d(TAG, "Constructed LocationManager Service"); 438 } 439 440 // Initialize the LocationMasfClient 441 mMasfClient = new LocationMasfClient(mContext); 442 443 // Create location collector 444 mCollector = new LocationCollector(mMasfClient); 445 446 // Load providers 447 loadProviders(); 448 449 // Listen for Radio changes 450 mTelephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); 451 mTelephonyManager.listen(mPhoneStateListener, 452 PhoneStateListener.LISTEN_SERVICE_STATE | PhoneStateListener.LISTEN_CELL_LOCATION); 453 454 // Register for Network (Wifi or Mobile) updates 455 NetworkStateBroadcastReceiver networkReceiver = new NetworkStateBroadcastReceiver(); 456 IntentFilter networkIntentFilter = new IntentFilter(); 457 networkIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 458 networkIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 459 networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); 460 networkIntentFilter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION); 461 context.registerReceiver(networkReceiver, networkIntentFilter); 462 463 // Alarm manager 464 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 465 466 // Register for power updates 467 PowerStateBroadcastReceiver powerStateReceiver = new PowerStateBroadcastReceiver(); 468 IntentFilter intentFilter = new IntentFilter(); 469 intentFilter.addAction(ALARM_INTENT); 470 intentFilter.addAction(Intent.ACTION_SCREEN_OFF); 471 intentFilter.addAction(Intent.ACTION_SCREEN_ON); 472 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); 473 context.registerReceiver(powerStateReceiver, intentFilter); 474 475 // Create a wake lock 476 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 477 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY); 478 479 // Get the wifi manager 480 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 481 482 // Create a wifi lock for future use 483 mWifiLock = getWifiWakelock(); 484 485 // There might be an existing wifi scan available 486 if (mWifiManager != null) { 487 List<ScanResult> wifiScanResults = mWifiManager.getScanResults(); 488 if (wifiScanResults != null && wifiScanResults.size() != 0) { 489 if (mNetworkLocationProvider != null) { 490 mNetworkLocationProvider.updateWifiScanResults(wifiScanResults); 491 } 492 } 493 } 494 } 495 496 private WifiManager.WifiLock getWifiWakelock() { 497 if (mWifiLock == null && mWifiManager != null) { 498 mWifiLock = mWifiManager.createWifiLock(WIFILOCK_KEY); 499 mWifiLock.setReferenceCounted(false); 500 } 501 return mWifiLock; 502 } 503 504 private boolean isAllowedBySettings(String provider) { 505 if (mEnabledProviders.contains(provider)) { 506 return true; 507 } 508 if (mDisabledProviders.contains(provider)) { 509 return false; 510 } 511 // Use system settings 512 ContentResolver resolver = mContext.getContentResolver(); 513 String allowedProviders = Settings.System.getString(resolver, 514 Settings.System.LOCATION_PROVIDERS_ALLOWED); 515 516 return ((allowedProviders != null) && (allowedProviders.contains(provider))); 517 } 518 519 private void checkPermissions(String provider) { 520 if (LocationManager.GPS_PROVIDER.equals(provider) 521 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) 522 != PackageManager.PERMISSION_GRANTED)) { 523 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission"); 524 } 525 if (LocationManager.NETWORK_PROVIDER.equals(provider) 526 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) 527 != PackageManager.PERMISSION_GRANTED) 528 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION) 529 != PackageManager.PERMISSION_GRANTED)) { 530 throw new SecurityException( 531 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission"); 532 } 533 } 534 535 private boolean isAllowedProvider(String provider) { 536 if (LocationManager.GPS_PROVIDER.equals(provider) 537 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) 538 != PackageManager.PERMISSION_GRANTED)) { 539 return false; 540 } 541 if (LocationManager.NETWORK_PROVIDER.equals(provider) 542 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) 543 != PackageManager.PERMISSION_GRANTED) 544 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION) 545 != PackageManager.PERMISSION_GRANTED)) { 546 return false; 547 } 548 549 return true; 550 } 551 552 private String[] getPackageNames() { 553 // Since a single UID may correspond to multiple packages, this can only be used as an 554 // approximation for tracking 555 return mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid()); 556 } 557 558 public List<String> getAllProviders() { 559 try { 560 return _getAllProviders(); 561 } catch (SecurityException se) { 562 throw se; 563 } catch (Exception e) { 564 Log.e(TAG, "getAllProviders got exception:", e); 565 return null; 566 } 567 } 568 569 private List<String> _getAllProviders() { 570 if (Config.LOGD) { 571 Log.d(TAG, "getAllProviders"); 572 } 573 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders(); 574 ArrayList<String> out = new ArrayList<String>(providers.size()); 575 576 for (LocationProviderImpl p : providers) { 577 out.add(p.getName()); 578 } 579 return out; 580 } 581 582 public List<String> getProviders(boolean enabledOnly) { 583 try { 584 return _getProviders(enabledOnly); 585 } catch (SecurityException se) { 586 throw se; 587 } catch (Exception e) { 588 Log.e(TAG, "getProviders gotString exception:", e); 589 return null; 590 } 591 } 592 593 private List<String> _getProviders(boolean enabledOnly) { 594 if (Config.LOGD) { 595 Log.d(TAG, "getProviders"); 596 } 597 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders(); 598 ArrayList<String> out = new ArrayList<String>(); 599 600 for (LocationProviderImpl p : providers) { 601 String name = p.getName(); 602 if (isAllowedProvider(name)) { 603 if (enabledOnly && !isAllowedBySettings(name)) { 604 continue; 605 } 606 out.add(name); 607 } 608 } 609 return out; 610 } 611 612 public void updateProviders() { 613 for (LocationProviderImpl p : LocationProviderImpl.getProviders()) { 614 boolean isEnabled = p.isEnabled(); 615 String name = p.getName(); 616 boolean shouldBeEnabled = isAllowedBySettings(name); 617 618 // Collection is only allowed when network provider is being used 619 if (p.getName().equals(LocationManager.NETWORK_PROVIDER)) { 620 mCollector.updateNetworkProviderStatus(shouldBeEnabled); 621 } 622 623 if (isEnabled && !shouldBeEnabled) { 624 updateProviderListeners(name, false); 625 } else if (!isEnabled && shouldBeEnabled) { 626 updateProviderListeners(name, true); 627 } 628 629 } 630 } 631 632 private void updateProviderListeners(String provider, boolean enabled) { 633 int listeners = 0; 634 635 LocationProviderImpl p = LocationProviderImpl.getProvider(provider); 636 if (p == null) { 637 return; 638 } 639 640 synchronized (mRecordsByProvider) { 641 642 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider); 643 if (records != null) { 644 for (UpdateRecord record : records) { 645 // Sends a notification message to the listener 646 try { 647 if (enabled) { 648 record.mListener.mListener.onProviderEnabled(provider); 649 } else { 650 record.mListener.mListener.onProviderDisabled(provider); 651 } 652 } catch (RemoteException e) { 653 // The death link will clean this up. 654 } 655 listeners++; 656 } 657 } 658 } 659 660 if (enabled) { 661 p.enable(); 662 if (listeners > 0) { 663 p.setMinTime(getMinTime(provider)); 664 p.enableLocationTracking(true); 665 updateWakelockStatus(mScreenOn); 666 } 667 } else { 668 p.enableLocationTracking(false); 669 p.disable(); 670 updateWakelockStatus(mScreenOn); 671 } 672 673 if (enabled && listeners > 0) { 674 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider); 675 Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider); 676 mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000); 677 } else { 678 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider); 679 } 680 } 681 682 private long getMinTime(String provider) { 683 long minTime = Long.MAX_VALUE; 684 synchronized (mRecordsByProvider) { 685 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider); 686 if (records != null) { 687 for (UpdateRecord r : records) { 688 minTime = Math.min(minTime, r.mMinTime); 689 } 690 } 691 } 692 return minTime; 693 } 694 695 private class UpdateRecord { 696 String mProvider; 697 Listener mListener; 698 long mMinTime; 699 float mMinDistance; 700 String[] mPackages; 701 702 UpdateRecord(String provider, long minTime, float minDistance, Listener listener, 703 String[] packages) { 704 mProvider = provider; 705 mListener = listener; 706 mMinTime = minTime; 707 mMinDistance = minDistance; 708 mPackages = packages; 709 710 synchronized (mRecordsByProvider) { 711 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider); 712 if (records == null) { 713 records = new HashSet<UpdateRecord>(); 714 mRecordsByProvider.put(provider, records); 715 } 716 records.add(this); 717 } 718 } 719 720 /** 721 * Method to be called when a record will no longer be used. Calling this multiple times 722 * must have the same effect as calling it once. 723 */ 724 public void dispose() { 725 synchronized (mRecordsByProvider) { 726 HashSet<UpdateRecord> records = mRecordsByProvider.get(this.mProvider); 727 records.remove(this); 728 } 729 } 730 731 /** 732 * Calls dispose(). 733 */ 734 @Override protected void finalize() { 735 dispose(); 736 } 737 } 738 739 public void requestLocationUpdates(String provider, 740 long minTime, float minDistance, ILocationListener listener) { 741 742 try { 743 _requestLocationUpdates(provider, minTime, minDistance, listener); 744 } catch (SecurityException se) { 745 throw se; 746 } catch (Exception e) { 747 Log.e(TAG, "requestUpdates got exception:", e); 748 } 749 } 750 751 private void _requestLocationUpdates(String provider, 752 long minTime, float minDistance, ILocationListener listener) { 753 if (Config.LOGD) { 754 Log.d(TAG, "_requestLocationUpdates: listener = " + listener.asBinder()); 755 } 756 757 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider); 758 if (impl == null) { 759 throw new IllegalArgumentException("provider=" + provider); 760 } 761 762 checkPermissions(provider); 763 764 String[] packages = getPackageNames(); 765 766 // so wakelock calls will succeed 767 long identity = Binder.clearCallingIdentity(); 768 try { 769 Listener myListener = new Listener(listener); 770 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, myListener, packages); 771 772 synchronized (mLocationListeners) { 773 IBinder binder = listener.asBinder(); 774 if (mListeners.get(binder) == null) { 775 try { 776 binder.linkToDeath(myListener, 0); 777 mListeners.put(binder, myListener); 778 } catch (RemoteException e) { 779 return; 780 } 781 } 782 783 HashMap<String,UpdateRecord> records = mLocationListeners.get(binder); 784 if (records == null) { 785 records = new HashMap<String,UpdateRecord>(); 786 mLocationListeners.put(binder, records); 787 } 788 UpdateRecord oldRecord = records.put(provider, r); 789 if (oldRecord != null) { 790 oldRecord.dispose(); 791 } 792 793 if (impl instanceof NetworkLocationProvider) { 794 ((NetworkLocationProvider) impl).addListener(packages); 795 } 796 797 boolean isProviderEnabled = isAllowedBySettings(provider); 798 if (isProviderEnabled) { 799 long minTimeForProvider = getMinTime(provider); 800 impl.setMinTime(minTimeForProvider); 801 impl.enableLocationTracking(true); 802 updateWakelockStatus(mScreenOn); 803 804 // Clear heartbeats if any before starting a new one 805 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider); 806 Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider); 807 mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000); 808 809 } else { 810 try { 811 // Notify the listener that updates are currently disabled 812 listener.onProviderDisabled(provider); 813 } catch(RemoteException e) { 814 Log.w(TAG, "RemoteException calling onProviderDisabled on " + listener); 815 } 816 } 817 } 818 } finally { 819 Binder.restoreCallingIdentity(identity); 820 } 821 } 822 823 public void removeUpdates(ILocationListener listener) { 824 try { 825 _removeUpdates(listener); 826 } catch (SecurityException se) { 827 throw se; 828 } catch (Exception e) { 829 Log.e(TAG, "removeUpdates got exception:", e); 830 } 831 } 832 833 private void _removeUpdates(ILocationListener listener) { 834 if (Config.LOGD) { 835 Log.d(TAG, "_removeUpdates: listener = " + listener.asBinder()); 836 } 837 838 // so wakelock calls will succeed 839 long identity = Binder.clearCallingIdentity(); 840 try { 841 synchronized (mLocationListeners) { 842 IBinder binder = listener.asBinder(); 843 Listener myListener = mListeners.remove(binder); 844 if (myListener != null) { 845 binder.unlinkToDeath(myListener, 0); 846 } 847 848 // Record which providers were associated with this listener 849 HashSet<String> providers = new HashSet<String>(); 850 HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(binder); 851 if (oldRecords != null) { 852 // Call dispose() on the obsolete update records. 853 for (UpdateRecord record : oldRecords.values()) { 854 if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) { 855 if (mNetworkLocationProvider != null) { 856 mNetworkLocationProvider.removeListener(record.mPackages); 857 } 858 } 859 record.dispose(); 860 } 861 // Accumulate providers 862 providers.addAll(oldRecords.keySet()); 863 } 864 865 mLocationListeners.remove(binder); 866 mLastFixBroadcast.remove(binder); 867 mLastStatusBroadcast.remove(binder); 868 869 // See if the providers associated with this listener have any 870 // other listeners; if one does, inform it of the new smallest minTime 871 // value; if one does not, disable location tracking for it 872 for (String provider : providers) { 873 // If provider is already disabled, don't need to do anything 874 if (!isAllowedBySettings(provider)) { 875 continue; 876 } 877 878 boolean hasOtherListener = false; 879 synchronized (mRecordsByProvider) { 880 HashSet<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider); 881 if (recordsForProvider != null && recordsForProvider.size() > 0) { 882 hasOtherListener = true; 883 } 884 } 885 886 LocationProviderImpl p = LocationProviderImpl.getProvider(provider); 887 if (p != null) { 888 if (hasOtherListener) { 889 p.setMinTime(getMinTime(provider)); 890 } else { 891 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider); 892 p.enableLocationTracking(false); 893 } 894 } 895 } 896 897 updateWakelockStatus(mScreenOn); 898 } 899 } finally { 900 Binder.restoreCallingIdentity(identity); 901 } 902 } 903 904 public boolean addGpsStatusListener(IGpsStatusListener listener) { 905 if (mGpsLocationProvider == null) { 906 return false; 907 } 908 try { 909 mGpsLocationProvider.addGpsStatusListener(listener); 910 } catch (RemoteException e) { 911 Log.w(TAG, "RemoteException in addGpsStatusListener"); 912 return false; 913 } 914 return true; 915 } 916 917 public void removeGpsStatusListener(IGpsStatusListener listener) { 918 mGpsLocationProvider.removeGpsStatusListener(listener); 919 } 920 921 public boolean sendExtraCommand(String provider, String command, Bundle extras) { 922 // first check for permission to the provider 923 checkPermissions(provider); 924 // and check for ACCESS_LOCATION_EXTRA_COMMANDS 925 if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS) 926 != PackageManager.PERMISSION_GRANTED)) { 927 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission"); 928 } 929 930 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider); 931 if (provider == null) { 932 return false; 933 } 934 935 return impl.sendExtraCommand(command, extras); 936 } 937 938 class ProximityAlert { 939 double mLatitude; 940 double mLongitude; 941 float mRadius; 942 long mExpiration; 943 PendingIntent mIntent; 944 Location mLocation; 945 946 public ProximityAlert(double latitude, double longitude, 947 float radius, long expiration, PendingIntent intent) { 948 mLatitude = latitude; 949 mLongitude = longitude; 950 mRadius = radius; 951 mExpiration = expiration; 952 mIntent = intent; 953 954 mLocation = new Location(""); 955 mLocation.setLatitude(latitude); 956 mLocation.setLongitude(longitude); 957 } 958 959 public long getExpiration() { 960 return mExpiration; 961 } 962 963 public PendingIntent getIntent() { 964 return mIntent; 965 } 966 967 public boolean isInProximity(double latitude, double longitude) { 968 Location loc = new Location(""); 969 loc.setLatitude(latitude); 970 loc.setLongitude(longitude); 971 972 double radius = loc.distanceTo(mLocation); 973 return radius <= mRadius; 974 } 975 } 976 977 // Listener for receiving locations to trigger proximity alerts 978 class ProximityListener extends ILocationListener.Stub { 979 980 boolean isGpsAvailable = false; 981 982 public void onLocationChanged(Location loc) { 983 984 // If Gps is available, then ignore updates from NetworkLocationProvider 985 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) { 986 isGpsAvailable = true; 987 } 988 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) { 989 return; 990 } 991 992 // Process proximity alerts 993 long now = System.currentTimeMillis(); 994 double latitude = loc.getLatitude(); 995 double longitude = loc.getLongitude(); 996 ArrayList<PendingIntent> intentsToRemove = null; 997 998 for (ProximityAlert alert : mProximityAlerts.values()) { 999 1000 PendingIntent intent = alert.getIntent(); 1001 long expiration = alert.getExpiration(); 1002 1003 if ((expiration == -1) || (now <= expiration)) { 1004 boolean entered = mProximitiesEntered.contains(alert); 1005 boolean inProximity = 1006 alert.isInProximity(latitude, longitude); 1007 if (!entered && inProximity) { 1008 if (Config.LOGD) { 1009 Log.i(TAG, "Entered alert"); 1010 } 1011 mProximitiesEntered.add(alert); 1012 Intent enteredIntent = new Intent(); 1013 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true); 1014 try { 1015 intent.send(mContext, 0, enteredIntent, null, null); 1016 } catch (PendingIntent.CanceledException e) { 1017 if (Config.LOGD) { 1018 Log.i(TAG, "Canceled proximity alert: " + alert, e); 1019 } 1020 if (intentsToRemove == null) { 1021 intentsToRemove = new ArrayList<PendingIntent>(); 1022 } 1023 intentsToRemove.add(intent); 1024 } 1025 } else if (entered && !inProximity) { 1026 if (Config.LOGD) { 1027 Log.i(TAG, "Exited alert"); 1028 } 1029 mProximitiesEntered.remove(alert); 1030 Intent exitedIntent = new Intent(); 1031 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false); 1032 try { 1033 intent.send(mContext, 0, exitedIntent, null, null); 1034 } catch (PendingIntent.CanceledException e) { 1035 if (Config.LOGD) { 1036 Log.i(TAG, "Canceled proximity alert: " + alert, e); 1037 } 1038 if (intentsToRemove == null) { 1039 intentsToRemove = new ArrayList<PendingIntent>(); 1040 } 1041 intentsToRemove.add(intent); 1042 } 1043 } 1044 } else { 1045 // Mark alert for expiration 1046 if (Config.LOGD) { 1047 Log.i(TAG, "Expiring proximity alert: " + alert); 1048 } 1049 if (intentsToRemove == null) { 1050 intentsToRemove = new ArrayList<PendingIntent>(); 1051 } 1052 intentsToRemove.add(alert.getIntent()); 1053 } 1054 } 1055 1056 // Remove expired alerts 1057 if (intentsToRemove != null) { 1058 for (PendingIntent i : intentsToRemove) { 1059 mProximityAlerts.remove(i); 1060 ProximityAlert alert = mProximityAlerts.get(i); 1061 mProximitiesEntered.remove(alert); 1062 } 1063 } 1064 1065 } 1066 1067 public void onProviderDisabled(String provider) { 1068 if (provider.equals(LocationManager.GPS_PROVIDER)) { 1069 isGpsAvailable = false; 1070 } 1071 } 1072 1073 public void onProviderEnabled(String provider) { 1074 // ignore 1075 } 1076 1077 public void onStatusChanged(String provider, int status, Bundle extras) { 1078 if ((provider.equals(LocationManager.GPS_PROVIDER)) && 1079 (status != LocationProvider.AVAILABLE)) { 1080 isGpsAvailable = false; 1081 } 1082 } 1083 } 1084 1085 public void addProximityAlert(double latitude, double longitude, 1086 float radius, long expiration, PendingIntent intent) { 1087 try { 1088 _addProximityAlert(latitude, longitude, radius, expiration, intent); 1089 } catch (SecurityException se) { 1090 throw se; 1091 } catch (Exception e) { 1092 Log.e(TAG, "addProximityAlert got exception:", e); 1093 } 1094 } 1095 1096 private void _addProximityAlert(double latitude, double longitude, 1097 float radius, long expiration, PendingIntent intent) { 1098 if (Config.LOGD) { 1099 Log.d(TAG, "addProximityAlert: latitude = " + latitude + 1100 ", longitude = " + longitude + 1101 ", expiration = " + expiration + 1102 ", intent = " + intent); 1103 } 1104 1105 // Require ability to access all providers for now 1106 if (!isAllowedProvider(LocationManager.GPS_PROVIDER) || 1107 !isAllowedProvider(LocationManager.NETWORK_PROVIDER)) { 1108 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission"); 1109 } 1110 1111 if (expiration != -1) { 1112 expiration += System.currentTimeMillis(); 1113 } 1114 ProximityAlert alert = new ProximityAlert(latitude, longitude, radius, expiration, intent); 1115 mProximityAlerts.put(intent, alert); 1116 1117 if (mProximityListener == null) { 1118 mProximityListener = new ProximityListener(); 1119 1120 LocationProvider provider = LocationProviderImpl.getProvider( 1121 LocationManager.GPS_PROVIDER); 1122 if (provider != null) { 1123 _requestLocationUpdates(provider.getName(), 1000L, 1.0f, mProximityListener); 1124 } 1125 1126 provider = 1127 LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER); 1128 if (provider != null) { 1129 _requestLocationUpdates(provider.getName(), 1000L, 1.0f, mProximityListener); 1130 } 1131 } 1132 } 1133 1134 public void removeProximityAlert(PendingIntent intent) { 1135 try { 1136 _removeProximityAlert(intent); 1137 } catch (SecurityException se) { 1138 throw se; 1139 } catch (Exception e) { 1140 Log.e(TAG, "removeProximityAlert got exception:", e); 1141 } 1142 } 1143 1144 private void _removeProximityAlert(PendingIntent intent) { 1145 if (Config.LOGD) { 1146 Log.d(TAG, "removeProximityAlert: intent = " + intent); 1147 } 1148 1149 mProximityAlerts.remove(intent); 1150 if (mProximityAlerts.size() == 0) { 1151 _removeUpdates(mProximityListener); 1152 mProximityListener = null; 1153 } 1154 } 1155 1156 /** 1157 * @return null if the provider does not exits 1158 * @throw SecurityException if the provider is not allowed to be 1159 * accessed by the caller 1160 */ 1161 public Bundle getProviderInfo(String provider) { 1162 try { 1163 return _getProviderInfo(provider); 1164 } catch (SecurityException se) { 1165 throw se; 1166 } catch (Exception e) { 1167 Log.e(TAG, "_getProviderInfo got exception:", e); 1168 return null; 1169 } 1170 } 1171 1172 private Bundle _getProviderInfo(String provider) { 1173 LocationProviderImpl p = LocationProviderImpl.getProvider(provider); 1174 if (p == null) { 1175 return null; 1176 } 1177 1178 checkPermissions(provider); 1179 1180 Bundle b = new Bundle(); 1181 b.putBoolean("network", p.requiresNetwork()); 1182 b.putBoolean("satellite", p.requiresSatellite()); 1183 b.putBoolean("cell", p.requiresCell()); 1184 b.putBoolean("cost", p.hasMonetaryCost()); 1185 b.putBoolean("altitude", p.supportsAltitude()); 1186 b.putBoolean("speed", p.supportsSpeed()); 1187 b.putBoolean("bearing", p.supportsBearing()); 1188 b.putInt("power", p.getPowerRequirement()); 1189 b.putInt("accuracy", p.getAccuracy()); 1190 1191 return b; 1192 } 1193 1194 public boolean isProviderEnabled(String provider) { 1195 try { 1196 return _isProviderEnabled(provider); 1197 } catch (SecurityException se) { 1198 throw se; 1199 } catch (Exception e) { 1200 Log.e(TAG, "isProviderEnabled got exception:", e); 1201 return false; 1202 } 1203 } 1204 1205 private boolean _isProviderEnabled(String provider) { 1206 checkPermissions(provider); 1207 1208 LocationProviderImpl p = LocationProviderImpl.getProvider(provider); 1209 if (p == null) { 1210 throw new IllegalArgumentException("provider=" + provider); 1211 } 1212 return isAllowedBySettings(provider); 1213 } 1214 1215 public Location getLastKnownLocation(String provider) { 1216 try { 1217 return _getLastKnownLocation(provider); 1218 } catch (SecurityException se) { 1219 throw se; 1220 } catch (Exception e) { 1221 Log.e(TAG, "getLastKnownLocation got exception:", e); 1222 return null; 1223 } 1224 } 1225 1226 private Location _getLastKnownLocation(String provider) { 1227 checkPermissions(provider); 1228 1229 LocationProviderImpl p = LocationProviderImpl.getProvider(provider); 1230 if (p == null) { 1231 throw new IllegalArgumentException("provider=" + provider); 1232 } 1233 1234 if (!isAllowedBySettings(provider)) { 1235 return null; 1236 } 1237 1238 Location location = mLastKnownLocation.get(provider); 1239 if (location == null) { 1240 // Get the persistent last known location for the provider 1241 location = readLastKnownLocation(provider); 1242 if (location != null) { 1243 mLastKnownLocation.put(provider, location); 1244 } 1245 } 1246 1247 return location; 1248 } 1249 1250 private boolean shouldBroadcast(Location loc, Location lastLoc, UpdateRecord record) { 1251 // Always broadcast the first update 1252 if (lastLoc == null) { 1253 return true; 1254 } 1255 1256 // Don't broadcast same location again regardless of condition 1257 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0 1258 if (loc.getTime() == lastLoc.getTime()) { 1259 return false; 1260 } 1261 1262 // Check whether sufficient distance has been traveled 1263 double minDistance = record.mMinDistance; 1264 if (minDistance > 0.0) { 1265 if (loc.distanceTo(lastLoc) <= minDistance) { 1266 return false; 1267 } 1268 } 1269 1270 return true; 1271 } 1272 1273 private void handleLocationChanged(String provider) { 1274 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider); 1275 if (records == null || records.size() == 0) { 1276 return; 1277 } 1278 1279 LocationProviderImpl p = LocationProviderImpl.getProvider(provider); 1280 if (p == null) { 1281 return; 1282 } 1283 1284 // Get location object 1285 Location loc = mLocationsByProvider.get(provider); 1286 if (loc == null) { 1287 loc = new Location(provider); 1288 mLocationsByProvider.put(provider, loc); 1289 } else { 1290 loc.reset(); 1291 } 1292 1293 // Use the mock location if available 1294 Location mockLoc = mMockProviderLocation.get(provider); 1295 boolean locationValid; 1296 if (mockLoc != null) { 1297 locationValid = true; 1298 loc.set(mockLoc); 1299 } else { 1300 locationValid = p.getLocation(loc); 1301 } 1302 1303 // Update last known location for provider 1304 if (locationValid) { 1305 Location location = mLastKnownLocation.get(provider); 1306 if (location == null) { 1307 mLastKnownLocation.put(provider, new Location(loc)); 1308 } else { 1309 location.set(loc); 1310 } 1311 writeLastKnownLocation(provider, loc); 1312 1313 if (p instanceof NetworkLocationProvider) { 1314 mWakeLockNetworkReceived = true; 1315 } else if (p instanceof GpsLocationProvider) { 1316 // Gps location received signal is in NetworkStateBroadcastReceiver 1317 } 1318 } 1319 1320 // Fetch latest status update time 1321 long newStatusUpdateTime = p.getStatusUpdateTime(); 1322 1323 // Override real time with mock time if present 1324 Long mockStatusUpdateTime = mMockProviderStatusUpdateTime.get(provider); 1325 if (mockStatusUpdateTime != null) { 1326 newStatusUpdateTime = mockStatusUpdateTime.longValue(); 1327 } 1328 1329 // Get latest status 1330 Bundle extras = new Bundle(); 1331 int status = p.getStatus(extras); 1332 1333 // Override status with mock status if present 1334 Integer mockStatus = mMockProviderStatus.get(provider); 1335 if (mockStatus != null) { 1336 status = mockStatus.intValue(); 1337 } 1338 1339 // Override extras with mock extras if present 1340 Bundle mockExtras = mMockProviderStatusExtras.get(provider); 1341 if (mockExtras != null) { 1342 extras.clear(); 1343 extras.putAll(mockExtras); 1344 } 1345 1346 // Broadcast location or status to all listeners 1347 for (UpdateRecord r : records) { 1348 ILocationListener listener = r.mListener.mListener; 1349 IBinder binder = listener.asBinder(); 1350 1351 // Broadcast location only if it is valid 1352 if (locationValid) { 1353 HashMap<String,Location> map = mLastFixBroadcast.get(binder); 1354 if (map == null) { 1355 map = new HashMap<String,Location>(); 1356 mLastFixBroadcast.put(binder, map); 1357 } 1358 Location lastLoc = map.get(provider); 1359 if ((lastLoc == null) || shouldBroadcast(loc, lastLoc, r)) { 1360 if (lastLoc == null) { 1361 lastLoc = new Location(loc); 1362 map.put(provider, lastLoc); 1363 } else { 1364 lastLoc.set(loc); 1365 } 1366 try { 1367 listener.onLocationChanged(loc); 1368 } catch (RemoteException doe) { 1369 Log.w(TAG, "RemoteException calling onLocationChanged on " + listener); 1370 _removeUpdates(listener); 1371 } 1372 } 1373 } 1374 1375 // Broadcast status message 1376 HashMap<String,Long> statusMap = mLastStatusBroadcast.get(binder); 1377 if (statusMap == null) { 1378 statusMap = new HashMap<String,Long>(); 1379 mLastStatusBroadcast.put(binder, statusMap); 1380 } 1381 long prevStatusUpdateTime = 1382 (statusMap.get(provider) != null) ? statusMap.get(provider) : 0; 1383 1384 if ((newStatusUpdateTime > prevStatusUpdateTime) && 1385 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) { 1386 1387 statusMap.put(provider, newStatusUpdateTime); 1388 try { 1389 listener.onStatusChanged(provider, status, extras); 1390 } catch (RemoteException doe) { 1391 Log.w(TAG, "RemoteException calling onStatusChanged on " + listener); 1392 _removeUpdates(listener); 1393 } 1394 } 1395 } 1396 } 1397 1398 private class LocationWorkerHandler extends Handler { 1399 1400 @Override 1401 public void handleMessage(Message msg) { 1402 try { 1403 if (msg.what == MESSAGE_HEARTBEAT) { 1404 // log("LocationWorkerHandler: Heartbeat!"); 1405 1406 synchronized (mRecordsByProvider) { 1407 String provider = (String) msg.obj; 1408 if (!isAllowedBySettings(provider)) { 1409 return; 1410 } 1411 1412 // Process the location fix if the screen is on or we're holding a wakelock 1413 if (mScreenOn || (mWakeLockAcquireTime != 0)) { 1414 handleLocationChanged(provider); 1415 } 1416 1417 // If it continues to have listeners 1418 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider); 1419 if (records != null && records.size() > 0) { 1420 Message m = Message.obtain(this, MESSAGE_HEARTBEAT, provider); 1421 sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000); 1422 } 1423 } 1424 1425 if ((mWakeLockAcquireTime != 0) && 1426 (SystemClock.elapsedRealtime() - mWakeLockAcquireTime > MAX_TIME_FOR_WAKE_LOCK)) { 1427 1428 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK); 1429 removeMessages(MESSAGE_RELEASE_WAKE_LOCK); 1430 1431 log("LocationWorkerHandler: Exceeded max time for wake lock"); 1432 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK); 1433 sendMessageAtFrontOfQueue(m); 1434 1435 } else if (mWakeLockAcquireTime != 0 && 1436 mWakeLockGpsReceived && mWakeLockNetworkReceived) { 1437 1438 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK); 1439 removeMessages(MESSAGE_RELEASE_WAKE_LOCK); 1440 1441 log("LocationWorkerHandler: Locations received."); 1442 mWakeLockAcquireTime = 0; 1443 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK); 1444 sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK); 1445 } 1446 1447 } else if (msg.what == MESSAGE_ACQUIRE_WAKE_LOCK) { 1448 log("LocationWorkerHandler: Acquire"); 1449 acquireWakeLock(); 1450 } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) { 1451 log("LocationWorkerHandler: Release"); 1452 1453 // Update wakelock status so the next alarm is set before releasing wakelock 1454 updateWakelockStatus(mScreenOn); 1455 releaseWakeLock(); 1456 } 1457 } catch (Exception e) { 1458 // Log, don't crash! 1459 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e); 1460 } 1461 } 1462 } 1463 1464 PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 1465 @Override 1466 public void onCellLocationChanged(CellLocation cellLocation) { 1467 try { 1468 ServiceState serviceState = mServiceState; 1469 1470 // Gets cell state 1471 CellState cellState = new CellState(serviceState, cellLocation); 1472 1473 // Notify collector 1474 mCollector.updateCellState(cellState); 1475 1476 // Updates providers 1477 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders(); 1478 for (LocationProviderImpl provider : providers) { 1479 if (provider.requiresCell()) { 1480 provider.updateCellState(cellState); 1481 } 1482 } 1483 } catch (Exception e) { 1484 Log.e(TAG, "Exception in PhoneStateListener.onCellLocationCahnged:", e); 1485 } 1486 } 1487 1488 @Override 1489 public void onServiceStateChanged(ServiceState serviceState) { 1490 mServiceState = serviceState; 1491 } 1492 }; 1493 1494 private class PowerStateBroadcastReceiver extends BroadcastReceiver { 1495 @Override public void onReceive(Context context, Intent intent) { 1496 String action = intent.getAction(); 1497 1498 if (action.equals(ALARM_INTENT)) { 1499 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK); 1500 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK); 1501 1502 log("PowerStateBroadcastReceiver: Alarm received"); 1503 Message m = mLocationHandler.obtainMessage(MESSAGE_ACQUIRE_WAKE_LOCK); 1504 mLocationHandler.sendMessageAtFrontOfQueue(m); 1505 1506 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 1507 log("PowerStateBroadcastReceiver: Screen off"); 1508 updateWakelockStatus(false); 1509 1510 } else if (action.equals(Intent.ACTION_SCREEN_ON)) { 1511 log("PowerStateBroadcastReceiver: Screen on"); 1512 updateWakelockStatus(true); 1513 1514 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { 1515 log("PowerStateBroadcastReceiver: Battery changed"); 1516 int scale = intent.getIntExtra(BATTERY_EXTRA_SCALE, 100); 1517 int level = intent.getIntExtra(BATTERY_EXTRA_LEVEL, 0); 1518 boolean plugged = intent.getIntExtra(BATTERY_EXTRA_PLUGGED, 0) != 0; 1519 1520 // Notify collector battery state 1521 mCollector.updateBatteryState(scale, level, plugged); 1522 } 1523 } 1524 } 1525 1526 private class NetworkStateBroadcastReceiver extends BroadcastReceiver { 1527 @Override public void onReceive(Context context, Intent intent) { 1528 String action = intent.getAction(); 1529 1530 if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { 1531 1532 List<ScanResult> wifiScanResults = mWifiManager.getScanResults(); 1533 1534 if (wifiScanResults == null) { 1535 return; 1536 } 1537 1538 // Notify provider and collector of Wifi scan results 1539 mCollector.updateWifiScanResults(wifiScanResults); 1540 if (mNetworkLocationProvider != null) { 1541 mNetworkLocationProvider.updateWifiScanResults(wifiScanResults); 1542 } 1543 1544 } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { 1545 int networkState = LocationProvider.TEMPORARILY_UNAVAILABLE; 1546 1547 boolean noConnectivity = 1548 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); 1549 if (!noConnectivity) { 1550 networkState = LocationProvider.AVAILABLE; 1551 } 1552 1553 // Notify location providers of current network state 1554 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders(); 1555 for (LocationProviderImpl provider : providers) { 1556 if (provider.requiresNetwork()) { 1557 provider.updateNetworkState(networkState); 1558 } 1559 } 1560 1561 } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { 1562 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 1563 WifiManager.WIFI_STATE_UNKNOWN); 1564 1565 boolean enabled; 1566 if (state == WifiManager.WIFI_STATE_ENABLED) { 1567 enabled = true; 1568 } else if (state == WifiManager.WIFI_STATE_DISABLED) { 1569 enabled = false; 1570 } else { 1571 return; 1572 } 1573 1574 // Notify network provider of current wifi enabled state 1575 if (mNetworkLocationProvider != null) { 1576 mNetworkLocationProvider.updateWifiEnabledState(enabled); 1577 } 1578 1579 } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) { 1580 1581 final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED, false); 1582 1583 if (!enabled) { 1584 // When GPS is disabled, we are OK to release wake-lock 1585 mWakeLockGpsReceived = true; 1586 } 1587 } 1588 1589 } 1590 } 1591 1592 // Wake locks 1593 1594 private void updateWakelockStatus(boolean screenOn) { 1595 log("updateWakelockStatus(): " + screenOn); 1596 1597 boolean needsLock = false; 1598 long minTime = Integer.MAX_VALUE; 1599 1600 if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) { 1601 needsLock = true; 1602 minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime); 1603 } 1604 1605 if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) { 1606 needsLock = true; 1607 minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime); 1608 if (screenOn) { 1609 startGps(); 1610 } else if (mScreenOn && !screenOn) { 1611 1612 // We just turned the screen off so stop navigating 1613 stopGps(); 1614 } 1615 } 1616 1617 mScreenOn = screenOn; 1618 1619 PendingIntent sender = 1620 PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0); 1621 1622 // Cancel existing alarm 1623 log("Cancelling existing alarm"); 1624 mAlarmManager.cancel(sender); 1625 1626 if (needsLock && !mScreenOn) { 1627 long now = SystemClock.elapsedRealtime(); 1628 mAlarmManager.set( 1629 AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender); 1630 mAlarmInterval = minTime; 1631 log("Creating a new wakelock alarm with minTime = " + minTime); 1632 } else { 1633 log("No need for alarm"); 1634 mAlarmInterval = -1; 1635 1636 // Clear out existing wakelocks 1637 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK); 1638 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK); 1639 releaseWakeLock(); 1640 } 1641 } 1642 1643 private void acquireWakeLock() { 1644 if (mWakeLock.isHeld()) { 1645 log("Must release wakelock before acquiring"); 1646 mWakeLockAcquireTime = 0; 1647 mWakeLock.release(); 1648 } 1649 1650 boolean networkActive = (mNetworkLocationProvider != null) 1651 && mNetworkLocationProvider.isLocationTracking(); 1652 boolean gpsActive = (mGpsLocationProvider != null) 1653 && mGpsLocationProvider.isLocationTracking(); 1654 1655 boolean needsLock = networkActive || gpsActive; 1656 if (!needsLock) { 1657 log("No need for Lock!"); 1658 return; 1659 } 1660 1661 mWakeLockGpsReceived = !gpsActive; 1662 mWakeLockNetworkReceived = !networkActive; 1663 1664 // Acquire wake lock 1665 mWakeLock.acquire(); 1666 mWakeLockAcquireTime = SystemClock.elapsedRealtime(); 1667 log("Acquired wakelock"); 1668 1669 // Start the gps provider 1670 startGps(); 1671 1672 // Acquire cell lock 1673 if (mCellWakeLockAcquired) { 1674 // Lock is already acquired 1675 } else if (!mWakeLockNetworkReceived) { 1676 mTelephonyManager.enableLocationUpdates(); 1677 mCellWakeLockAcquired = true; 1678 } else { 1679 mCellWakeLockAcquired = false; 1680 } 1681 1682 // Notify NetworkLocationProvider 1683 if (mNetworkLocationProvider != null) { 1684 mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired); 1685 } 1686 1687 // Acquire wifi lock 1688 WifiManager.WifiLock wifiLock = getWifiWakelock(); 1689 if (wifiLock != null) { 1690 if (mWifiWakeLockAcquired) { 1691 // Lock is already acquired 1692 } else if (mWifiManager.isWifiEnabled() && !mWakeLockNetworkReceived) { 1693 wifiLock.acquire(); 1694 mWifiWakeLockAcquired = true; 1695 } else { 1696 mWifiWakeLockAcquired = false; 1697 Log.w(TAG, "acquireWakeLock(): Unable to get WiFi lock"); 1698 } 1699 } 1700 } 1701 1702 private void startGps() { 1703 boolean gpsActive = (mGpsLocationProvider != null) 1704 && mGpsLocationProvider.isLocationTracking(); 1705 if (gpsActive) { 1706 mGpsLocationProvider.startNavigating(); 1707 } 1708 } 1709 1710 private void stopGps() { 1711 boolean gpsActive = mGpsLocationProvider != null 1712 && mGpsLocationProvider.isLocationTracking(); 1713 if (gpsActive) { 1714 mGpsLocationProvider.stopNavigating(); 1715 } 1716 } 1717 1718 private void releaseWakeLock() { 1719 // Release wifi lock 1720 WifiManager.WifiLock wifiLock = getWifiWakelock(); 1721 if (wifiLock != null) { 1722 if (mWifiWakeLockAcquired) { 1723 wifiLock.release(); 1724 mWifiWakeLockAcquired = false; 1725 } 1726 } 1727 1728 if (!mScreenOn) { 1729 1730 // Stop the gps 1731 stopGps(); 1732 } 1733 1734 // Release cell lock 1735 if (mCellWakeLockAcquired) { 1736 mTelephonyManager.disableLocationUpdates(); 1737 mCellWakeLockAcquired = false; 1738 } 1739 1740 // Notify NetworkLocationProvider 1741 if (mNetworkLocationProvider != null) { 1742 mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired); 1743 } 1744 1745 // Release wake lock 1746 mWakeLockAcquireTime = 0; 1747 if (mWakeLock.isHeld()) { 1748 log("Released wakelock"); 1749 mWakeLock.release(); 1750 } else { 1751 log("Can't release wakelock again!"); 1752 } 1753 } 1754 1755 // Geocoder 1756 1757 public String getFromLocation(double latitude, double longitude, int maxResults, 1758 String language, String country, String variant, String appName, List<Address> addrs) { 1759 try { 1760 Locale locale = new Locale(language, country, variant); 1761 mMasfClient.reverseGeocode(locale, appName, latitude, longitude, maxResults, addrs); 1762 return null; 1763 } catch(IOException e) { 1764 return e.getMessage(); 1765 } catch(Exception e) { 1766 Log.e(TAG, "getFromLocation got exception:", e); 1767 return null; 1768 } 1769 } 1770 1771 public String getFromLocationName(String locationName, 1772 double lowerLeftLatitude, double lowerLeftLongitude, 1773 double upperRightLatitude, double upperRightLongitude, int maxResults, 1774 String language, String country, String variant, String appName, List<Address> addrs) { 1775 1776 try { 1777 Locale locale = new Locale(language, country, variant); 1778 mMasfClient.forwardGeocode(locale, appName, locationName, 1779 lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude, 1780 maxResults, addrs); 1781 return null; 1782 } catch(IOException e) { 1783 return e.getMessage(); 1784 } catch(Exception e) { 1785 Log.e(TAG, "getFromLocationName got exception:", e); 1786 return null; 1787 } 1788 } 1789 1790 // Mock Providers 1791 1792 class MockProvider extends LocationProviderImpl { 1793 boolean mRequiresNetwork; 1794 boolean mRequiresSatellite; 1795 boolean mRequiresCell; 1796 boolean mHasMonetaryCost; 1797 boolean mSupportsAltitude; 1798 boolean mSupportsSpeed; 1799 boolean mSupportsBearing; 1800 int mPowerRequirement; 1801 int mAccuracy; 1802 1803 public MockProvider(String name, boolean requiresNetwork, boolean requiresSatellite, 1804 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude, 1805 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) { 1806 super(name); 1807 1808 mRequiresNetwork = requiresNetwork; 1809 mRequiresSatellite = requiresSatellite; 1810 mRequiresCell = requiresCell; 1811 mHasMonetaryCost = hasMonetaryCost; 1812 mSupportsAltitude = supportsAltitude; 1813 mSupportsBearing = supportsBearing; 1814 mSupportsSpeed = supportsSpeed; 1815 mPowerRequirement = powerRequirement; 1816 mAccuracy = accuracy; 1817 } 1818 1819 @Override 1820 public void disable() { 1821 String name = getName(); 1822 mEnabledProviders.remove(name); 1823 mDisabledProviders.add(name); 1824 } 1825 1826 @Override 1827 public void enable() { 1828 String name = getName(); 1829 mEnabledProviders.add(name); 1830 mDisabledProviders.remove(name); 1831 } 1832 1833 @Override 1834 public boolean getLocation(Location l) { 1835 Location loc = mMockProviderLocation.get(getName()); 1836 if (loc == null) { 1837 return false; 1838 } 1839 l.set(loc); 1840 return true; 1841 } 1842 1843 @Override 1844 public int getStatus(Bundle extras) { 1845 String name = getName(); 1846 Integer s = mMockProviderStatus.get(name); 1847 int status = (s == null) ? AVAILABLE : s.intValue(); 1848 Bundle newExtras = mMockProviderStatusExtras.get(name); 1849 if (newExtras != null) { 1850 extras.clear(); 1851 extras.putAll(newExtras); 1852 } 1853 return status; 1854 } 1855 1856 @Override 1857 public boolean isEnabled() { 1858 return mEnabledProviders.contains(getName()); 1859 } 1860 1861 @Override 1862 public int getAccuracy() { 1863 return mAccuracy; 1864 } 1865 1866 @Override 1867 public int getPowerRequirement() { 1868 return mPowerRequirement; 1869 } 1870 1871 @Override 1872 public boolean hasMonetaryCost() { 1873 return mHasMonetaryCost; 1874 } 1875 1876 @Override 1877 public boolean requiresCell() { 1878 return mRequiresCell; 1879 } 1880 1881 @Override 1882 public boolean requiresNetwork() { 1883 return mRequiresNetwork; 1884 } 1885 1886 @Override 1887 public boolean requiresSatellite() { 1888 return mRequiresSatellite; 1889 } 1890 1891 @Override 1892 public boolean supportsAltitude() { 1893 return mSupportsAltitude; 1894 } 1895 1896 @Override 1897 public boolean supportsBearing() { 1898 return mSupportsBearing; 1899 } 1900 1901 @Override 1902 public boolean supportsSpeed() { 1903 return mSupportsSpeed; 1904 } 1905 } 1906 1907 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite, 1908 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude, 1909 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) { 1910 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != 1911 PackageManager.PERMISSION_GRANTED) { 1912 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); 1913 } 1914 1915 MockProvider provider = new MockProvider(name, requiresNetwork, requiresSatellite, 1916 requiresCell, hasMonetaryCost, supportsAltitude, 1917 supportsSpeed, supportsBearing, powerRequirement, accuracy); 1918 if (LocationProviderImpl.getProvider(name) != null) { 1919 throw new IllegalArgumentException("Provider \"" + name + "\" already exists"); 1920 } 1921 LocationProviderImpl.addProvider(provider); 1922 updateProviders(); 1923 } 1924 1925 public void removeTestProvider(String provider) { 1926 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != 1927 PackageManager.PERMISSION_GRANTED) { 1928 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); 1929 } 1930 LocationProviderImpl p = LocationProviderImpl.getProvider(provider); 1931 if (p == null) { 1932 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); 1933 } 1934 LocationProviderImpl.removeProvider(p); 1935 updateProviders(); 1936 } 1937 1938 public void setTestProviderLocation(String provider, Location loc) { 1939 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != 1940 PackageManager.PERMISSION_GRANTED) { 1941 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); 1942 } 1943 if (LocationProviderImpl.getProvider(provider) == null) { 1944 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); 1945 } 1946 mMockProviderLocation.put(provider, loc); 1947 } 1948 1949 public void clearTestProviderLocation(String provider) { 1950 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != 1951 PackageManager.PERMISSION_GRANTED) { 1952 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); 1953 } 1954 if (LocationProviderImpl.getProvider(provider) == null) { 1955 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); 1956 } 1957 mMockProviderLocation.remove(provider); 1958 } 1959 1960 public void setTestProviderEnabled(String provider, boolean enabled) { 1961 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != 1962 PackageManager.PERMISSION_GRANTED) { 1963 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); 1964 } 1965 if (LocationProviderImpl.getProvider(provider) == null) { 1966 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); 1967 } 1968 if (enabled) { 1969 mEnabledProviders.add(provider); 1970 mDisabledProviders.remove(provider); 1971 } else { 1972 mEnabledProviders.remove(provider); 1973 mDisabledProviders.add(provider); 1974 } 1975 updateProviders(); 1976 } 1977 1978 public void clearTestProviderEnabled(String provider) { 1979 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != 1980 PackageManager.PERMISSION_GRANTED) { 1981 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); 1982 } 1983 if (LocationProviderImpl.getProvider(provider) == null) { 1984 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); 1985 } 1986 mEnabledProviders.remove(provider); 1987 mDisabledProviders.remove(provider); 1988 updateProviders(); 1989 } 1990 1991 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) { 1992 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != 1993 PackageManager.PERMISSION_GRANTED) { 1994 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); 1995 } 1996 if (LocationProviderImpl.getProvider(provider) == null) { 1997 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); 1998 } 1999 mMockProviderStatus.put(provider, new Integer(status)); 2000 mMockProviderStatusExtras.put(provider, extras); 2001 mMockProviderStatusUpdateTime.put(provider, new Long(updateTime)); 2002 } 2003 2004 public void clearTestProviderStatus(String provider) { 2005 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) != 2006 PackageManager.PERMISSION_GRANTED) { 2007 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission"); 2008 } 2009 if (LocationProviderImpl.getProvider(provider) == null) { 2010 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown"); 2011 } 2012 mMockProviderStatus.remove(provider); 2013 mMockProviderStatusExtras.remove(provider); 2014 mMockProviderStatusUpdateTime.remove(provider); 2015 } 2016 2017 private void log(String log) { 2018 if (Log.isLoggable(TAG, Log.VERBOSE)) { 2019 Log.d(TAG, log); 2020 } 2021 } 2022} 2023 2024