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