blob: 630c84b65e2c24a5deb3bd677e0ae2bfb37e3b0d [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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.Set;
32import java.util.regex.Pattern;
33
34import android.app.AlarmManager;
35import android.app.PendingIntent;
36import android.content.BroadcastReceiver;
37import android.content.ContentResolver;
38import android.content.Context;
39import android.content.Intent;
40import android.content.IntentFilter;
41import android.content.pm.PackageManager;
42import android.location.Address;
43import android.location.IGpsStatusListener;
44import android.location.ILocationListener;
45import android.location.ILocationManager;
46import android.location.Location;
47import android.location.LocationManager;
48import android.location.LocationProvider;
49import android.location.LocationProviderImpl;
50import android.net.ConnectivityManager;
51import android.net.Uri;
52import android.net.wifi.ScanResult;
53import android.net.wifi.WifiManager;
54import android.os.Binder;
55import android.os.Bundle;
56import android.os.Handler;
57import android.os.IBinder;
58import android.os.Message;
59import android.os.PowerManager;
60import android.os.RemoteException;
61import android.os.SystemClock;
62import android.provider.Settings;
63import android.telephony.CellLocation;
64import android.telephony.PhoneStateListener;
65import android.telephony.TelephonyManager;
66import android.util.Config;
67import android.util.Log;
68import android.util.PrintWriterPrinter;
69import android.util.SparseIntArray;
70
71import com.android.internal.app.IBatteryStats;
72import com.android.internal.location.CellState;
73import com.android.internal.location.GpsLocationProvider;
74import com.android.internal.location.ILocationCollector;
75import com.android.internal.location.INetworkLocationManager;
76import com.android.internal.location.INetworkLocationProvider;
Mike Lockwood7ec434e2009-03-27 07:46:48 -070077import com.android.internal.location.MockProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078import com.android.internal.location.TrackProvider;
79import com.android.server.am.BatteryStatsService;
80
81/**
82 * The service class that manages LocationProviders and issues location
83 * updates and alerts.
84 *
85 * {@hide}
86 */
87public class LocationManagerService extends ILocationManager.Stub
88 implements INetworkLocationManager {
89 private static final String TAG = "LocationManagerService";
The Android Open Source Project10592532009-03-18 17:39:46 -070090 private static final boolean LOCAL_LOGV = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091
92 // Minimum time interval between last known location writes, in milliseconds.
93 private static final long MIN_LAST_KNOWN_LOCATION_TIME = 60L * 1000L;
94
95 // Max time to hold wake lock for, in milliseconds.
96 private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L;
97
98 // Time to wait after releasing a wake lock for clients to process location update,
99 // in milliseconds.
100 private static final long TIME_AFTER_WAKE_LOCK = 2 * 1000L;
101
102 // The last time a location was written, by provider name.
103 private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
104
105 private static final Pattern PATTERN_COMMA = Pattern.compile(",");
106
107 private static final String ACCESS_FINE_LOCATION =
108 android.Manifest.permission.ACCESS_FINE_LOCATION;
109 private static final String ACCESS_COARSE_LOCATION =
110 android.Manifest.permission.ACCESS_COARSE_LOCATION;
111 private static final String ACCESS_MOCK_LOCATION =
112 android.Manifest.permission.ACCESS_MOCK_LOCATION;
113 private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
114 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
115
116 // Set of providers that are explicitly enabled
117 private final Set<String> mEnabledProviders = new HashSet<String>();
118
119 // Set of providers that are explicitly disabled
120 private final Set<String> mDisabledProviders = new HashSet<String>();
121
122 // Locations, status values, and extras for mock providers
Mike Lockwood7ec434e2009-03-27 07:46:48 -0700123 private final HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124
125 private static boolean sProvidersLoaded = false;
126
127 private final Context mContext;
128 private GpsLocationProvider mGpsLocationProvider;
129 private boolean mGpsNavigating;
130 private LocationProviderImpl mNetworkLocationProvider;
131 private INetworkLocationProvider mNetworkLocationInterface;
132 private LocationWorkerHandler mLocationHandler;
133
134 // Handler messages
135 private static final int MESSAGE_HEARTBEAT = 1;
136 private static final int MESSAGE_ACQUIRE_WAKE_LOCK = 2;
137 private static final int MESSAGE_RELEASE_WAKE_LOCK = 3;
138 private static final int MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER = 4;
139
140 // Alarm manager and wakelock variables
141 private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT";
142 private final static String WAKELOCK_KEY = "LocationManagerService";
143 private final static String WIFILOCK_KEY = "LocationManagerService";
144 private AlarmManager mAlarmManager;
145 private long mAlarmInterval = 0;
146 private boolean mScreenOn = true;
147 private PowerManager.WakeLock mWakeLock = null;
148 private WifiManager.WifiLock mWifiLock = null;
149 private long mWakeLockAcquireTime = 0;
150 private boolean mWakeLockGpsReceived = true;
151 private boolean mWakeLockNetworkReceived = true;
152 private boolean mWifiWakeLockAcquired = false;
153 private boolean mCellWakeLockAcquired = false;
154
155 private final IBatteryStats mBatteryStats;
156
157 /**
158 * Mapping from listener IBinder/PendingIntent to local Listener wrappers.
159 */
160 private final ArrayList<Receiver> mListeners = new ArrayList<Receiver>();
161
162 /**
163 * Used for reporting which UIDs are causing the GPS to run.
164 */
165 private final SparseIntArray mReportedGpsUids = new SparseIntArray();
166 private int mReportedGpsSeq = 0;
167
168 /**
169 * Mapping from listener IBinder/PendingIntent to a map from provider name to UpdateRecord.
170 * This also serves as the lock for our state.
171 */
172 private final HashMap<Receiver,HashMap<String,UpdateRecord>> mLocationListeners =
173 new HashMap<Receiver,HashMap<String,UpdateRecord>>();
174
175 /**
176 * Mapping from listener IBinder/PendingIntent to a map from provider name to last broadcast
177 * location.
178 */
179 private final HashMap<Receiver,HashMap<String,Location>> mLastFixBroadcast =
180 new HashMap<Receiver,HashMap<String,Location>>();
181 private final HashMap<Receiver,HashMap<String,Long>> mLastStatusBroadcast =
182 new HashMap<Receiver,HashMap<String,Long>>();
183
184 /**
185 * Mapping from provider name to all its UpdateRecords
186 */
187 private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
188 new HashMap<String,ArrayList<UpdateRecord>>();
189
190 /**
191 * Mappings from provider name to object to use for current location. Locations
192 * contained in this list may not always be valid.
193 */
194 private final HashMap<String,Location> mLocationsByProvider =
195 new HashMap<String,Location>();
196
197 // Proximity listeners
198 private Receiver mProximityListener = null;
199 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
200 new HashMap<PendingIntent,ProximityAlert>();
201 private HashSet<ProximityAlert> mProximitiesEntered =
202 new HashSet<ProximityAlert>();
203
204 // Last known location for each provider
205 private HashMap<String,Location> mLastKnownLocation =
206 new HashMap<String,Location>();
207
208 // Battery status extras (from com.android.server.BatteryService)
209 private static final String BATTERY_EXTRA_SCALE = "scale";
210 private static final String BATTERY_EXTRA_LEVEL = "level";
211 private static final String BATTERY_EXTRA_PLUGGED = "plugged";
212
213 // Last known cell service state
214 private TelephonyManager mTelephonyManager;
215
216 // Location collector
217 private ILocationCollector mCollector;
218
219 // Wifi Manager
220 private WifiManager mWifiManager;
221
The Android Open Source Project4df24232009-03-05 14:34:35 -0800222 private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
223 private boolean mWifiEnabled = false;
224
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 /**
226 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
227 * location updates.
228 */
229 private final class Receiver implements IBinder.DeathRecipient {
230 final ILocationListener mListener;
231 final PendingIntent mPendingIntent;
232 final int mUid;
233 final Object mKey;
234
235 Receiver(ILocationListener listener, int uid) {
236 mListener = listener;
237 mPendingIntent = null;
238 mUid = uid;
239 mKey = listener.asBinder();
240 }
241
242 Receiver(PendingIntent intent, int uid) {
243 mPendingIntent = intent;
244 mListener = null;
245 mUid = uid;
246 mKey = intent;
247 }
248
249 @Override
250 public boolean equals(Object otherObj) {
251 if (otherObj instanceof Receiver) {
252 return mKey.equals(
253 ((Receiver)otherObj).mKey);
254 }
255 return false;
256 }
257
258 @Override
259 public int hashCode() {
260 return mKey.hashCode();
261 }
262
263
264 @Override
265 public String toString() {
266 if (mListener != null) {
267 return "Receiver{"
268 + Integer.toHexString(System.identityHashCode(this))
269 + " uid " + mUid + " Listener " + mKey + "}";
270 } else {
271 return "Receiver{"
272 + Integer.toHexString(System.identityHashCode(this))
273 + " uid " + mUid + " Intent " + mKey + "}";
274 }
275 }
276
277 public boolean isListener() {
278 return mListener != null;
279 }
280
281 public boolean isPendingIntent() {
282 return mPendingIntent != null;
283 }
284
285 public ILocationListener getListener() {
286 if (mListener != null) {
287 return mListener;
288 }
289 throw new IllegalStateException("Request for non-existent listener");
290 }
291
292 public PendingIntent getPendingIntent() {
293 if (mPendingIntent != null) {
294 return mPendingIntent;
295 }
296 throw new IllegalStateException("Request for non-existent intent");
297 }
298
299 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
300 if (mListener != null) {
301 try {
302 mListener.onStatusChanged(provider, status, extras);
303 } catch (RemoteException e) {
304 return false;
305 }
306 } else {
307 Intent statusChanged = new Intent();
308 statusChanged.putExtras(extras);
309 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
310 try {
311 mPendingIntent.send(mContext, 0, statusChanged, null, null);
312 } catch (PendingIntent.CanceledException e) {
313 return false;
314 }
315 }
316 return true;
317 }
318
319 public boolean callLocationChangedLocked(Location location) {
320 if (mListener != null) {
321 try {
322 mListener.onLocationChanged(location);
323 } catch (RemoteException e) {
324 return false;
325 }
326 } else {
327 Intent locationChanged = new Intent();
328 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
329 try {
330 mPendingIntent.send(mContext, 0, locationChanged, null, null);
331 } catch (PendingIntent.CanceledException e) {
332 return false;
333 }
334 }
335 return true;
336 }
337
338 public void binderDied() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700339 if (LOCAL_LOGV) {
340 Log.v(TAG, "Location listener died");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 }
342 synchronized (mLocationListeners) {
343 removeUpdatesLocked(this);
344 }
345 }
346 }
347
348 private Location readLastKnownLocationLocked(String provider) {
349 Location location = null;
350 String s = null;
351 try {
352 File f = new File(LocationManager.SYSTEM_DIR + "/location."
353 + provider);
354 if (!f.exists()) {
355 return null;
356 }
357 BufferedReader reader = new BufferedReader(new FileReader(f), 256);
358 s = reader.readLine();
359 } catch (IOException e) {
360 Log.w(TAG, "Unable to read last known location", e);
361 }
362
363 if (s == null) {
364 return null;
365 }
366 try {
367 String[] tokens = PATTERN_COMMA.split(s);
368 int idx = 0;
369 long time = Long.parseLong(tokens[idx++]);
370 double latitude = Double.parseDouble(tokens[idx++]);
371 double longitude = Double.parseDouble(tokens[idx++]);
372 double altitude = Double.parseDouble(tokens[idx++]);
373 float bearing = Float.parseFloat(tokens[idx++]);
374 float speed = Float.parseFloat(tokens[idx++]);
375
376 location = new Location(provider);
377 location.setTime(time);
378 location.setLatitude(latitude);
379 location.setLongitude(longitude);
380 location.setAltitude(altitude);
381 location.setBearing(bearing);
382 location.setSpeed(speed);
383 } catch (NumberFormatException nfe) {
384 Log.e(TAG, "NumberFormatException reading last known location", nfe);
385 return null;
386 }
387
388 return location;
389 }
390
391 private void writeLastKnownLocationLocked(String provider,
392 Location location) {
393 long now = SystemClock.elapsedRealtime();
394 Long last = mLastWriteTime.get(provider);
395 if ((last != null)
396 && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
397 return;
398 }
399 mLastWriteTime.put(provider, now);
400
401 StringBuilder sb = new StringBuilder(100);
402 sb.append(location.getTime());
403 sb.append(',');
404 sb.append(location.getLatitude());
405 sb.append(',');
406 sb.append(location.getLongitude());
407 sb.append(',');
408 sb.append(location.getAltitude());
409 sb.append(',');
410 sb.append(location.getBearing());
411 sb.append(',');
412 sb.append(location.getSpeed());
413
414 FileWriter writer = null;
415 try {
416 File d = new File(LocationManager.SYSTEM_DIR);
417 if (!d.exists()) {
418 if (!d.mkdirs()) {
419 Log.w(TAG, "Unable to create directory to write location");
420 return;
421 }
422 }
423 File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
424 writer = new FileWriter(f);
425 writer.write(sb.toString());
426 } catch (IOException e) {
427 Log.w(TAG, "Unable to write location", e);
428 } finally {
429 if (writer != null) {
430 try {
431 writer.close();
432 } catch (IOException e) {
433 Log.w(TAG, "Exception closing file", e);
434 }
435 }
436 }
437 }
438
439 /**
440 * Load providers from /data/location/<provider_name>/
441 * class
442 * kml
443 * nmea
444 * track
445 * location
446 * properties
447 */
448 private void loadProviders() {
449 synchronized (mLocationListeners) {
450 if (sProvidersLoaded) {
451 return;
452 }
453
454 // Load providers
455 loadProvidersLocked();
456 sProvidersLoaded = true;
457 }
458 }
459
460 private void loadProvidersLocked() {
461 try {
462 _loadProvidersLocked();
463 } catch (Exception e) {
464 Log.e(TAG, "Exception loading providers:", e);
465 }
466 }
467
468 private void _loadProvidersLocked() {
469 // Attempt to load "real" providers first
470 if (GpsLocationProvider.isSupported()) {
471 // Create a gps location provider
472 mGpsLocationProvider = new GpsLocationProvider(mContext);
473 LocationProviderImpl.addProvider(mGpsLocationProvider);
474 }
475
476 // Load fake providers if real providers are not available
477 File f = new File(LocationManager.PROVIDER_DIR);
478 if (f.isDirectory()) {
479 File[] subdirs = f.listFiles();
480 for (int i = 0; i < subdirs.length; i++) {
481 if (!subdirs[i].isDirectory()) {
482 continue;
483 }
484
485 String name = subdirs[i].getName();
486
The Android Open Source Project10592532009-03-18 17:39:46 -0700487 if (LOCAL_LOGV) {
488 Log.v(TAG, "Found dir " + subdirs[i].getAbsolutePath());
489 Log.v(TAG, "name = " + name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 }
491
492 // Don't create a fake provider if a real provider exists
493 if (LocationProviderImpl.getProvider(name) == null) {
494 LocationProviderImpl provider = null;
495 try {
496 File classFile = new File(subdirs[i], "class");
497 // Look for a 'class' file
498 provider = LocationProviderImpl.loadFromClass(classFile);
499
500 // Look for an 'kml', 'nmea', or 'track' file
501 if (provider == null) {
502 // Load properties from 'properties' file, if present
503 File propertiesFile = new File(subdirs[i], "properties");
504
505 if (propertiesFile.exists()) {
506 provider = new TrackProvider(name);
507 ((TrackProvider)provider).readProperties(propertiesFile);
508
509 File kmlFile = new File(subdirs[i], "kml");
510 if (kmlFile.exists()) {
511 ((TrackProvider) provider).readKml(kmlFile);
512 } else {
513 File nmeaFile = new File(subdirs[i], "nmea");
514 if (nmeaFile.exists()) {
515 ((TrackProvider) provider).readNmea(name, nmeaFile);
516 } else {
517 File trackFile = new File(subdirs[i], "track");
518 if (trackFile.exists()) {
519 ((TrackProvider) provider).readTrack(trackFile);
520 }
521 }
522 }
523 }
524 }
525 if (provider != null) {
526 LocationProviderImpl.addProvider(provider);
527 }
528 // Grab the initial location of a TrackProvider and
529 // store it as the last known location for that provider
530 if (provider instanceof TrackProvider) {
531 TrackProvider tp = (TrackProvider) provider;
532 mLastKnownLocation.put(tp.getName(), tp.getInitialLocation());
533 }
534 } catch (Exception e) {
535 Log.e(TAG, "Exception loading provder " + name, e);
536 }
537 }
538 }
539 }
540
541 updateProvidersLocked();
542 }
543
544 /**
545 * @param context the context that the LocationManagerService runs in
546 */
547 public LocationManagerService(Context context) {
548 super();
549 mContext = context;
550 mLocationHandler = new LocationWorkerHandler();
551
The Android Open Source Project10592532009-03-18 17:39:46 -0700552 if (LOCAL_LOGV) {
553 Log.v(TAG, "Constructed LocationManager Service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 }
555
556 // Alarm manager, needs to be done before calling loadProviders() below
557 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
558
559 // Create a wake lock, needs to be done before calling loadProviders() below
560 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
561 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
562
563 // Battery statistics service to be notified when GPS turns on or off
564 mBatteryStats = BatteryStatsService.getService();
565
566 // Load providers
567 loadProviders();
568
569 // Listen for Radio changes
570 mTelephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
571 mTelephonyManager.listen(mPhoneStateListener,
572 PhoneStateListener.LISTEN_CELL_LOCATION |
573 PhoneStateListener.LISTEN_SIGNAL_STRENGTH |
574 PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
575
576 // Register for Network (Wifi or Mobile) updates
577 NetworkStateBroadcastReceiver networkReceiver = new NetworkStateBroadcastReceiver();
578 IntentFilter networkIntentFilter = new IntentFilter();
579 networkIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
580 networkIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
581 networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
582 networkIntentFilter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
583 context.registerReceiver(networkReceiver, networkIntentFilter);
584
585 // Register for power updates
586 PowerStateBroadcastReceiver powerStateReceiver = new PowerStateBroadcastReceiver();
587 IntentFilter intentFilter = new IntentFilter();
588 intentFilter.addAction(ALARM_INTENT);
589 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
590 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
591 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
592 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
593 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
594 context.registerReceiver(powerStateReceiver, intentFilter);
595
596 // Get the wifi manager
597 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
598
599 // Create a wifi lock for future use
600 mWifiLock = getWifiWakelockLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 }
602
603 public void setInstallCallback(InstallCallback callback) {
604 synchronized (mLocationListeners) {
605 mLocationHandler.removeMessages(MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER);
606 Message m = Message.obtain(mLocationHandler,
607 MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER, callback);
608 mLocationHandler.sendMessageAtFrontOfQueue(m);
609 }
610 }
611
612 public void setNetworkLocationProvider(INetworkLocationProvider provider) {
613 synchronized (mLocationListeners) {
614 mNetworkLocationInterface = provider;
615 provider.addListener(getPackageNames());
616 mNetworkLocationProvider = (LocationProviderImpl)provider;
617 LocationProviderImpl.addProvider(mNetworkLocationProvider);
618 updateProvidersLocked();
The Android Open Source Project4df24232009-03-05 14:34:35 -0800619
620 // notify NetworkLocationProvider of any events it might have missed
621 synchronized (mLocationListeners) {
622 mNetworkLocationProvider.updateNetworkState(mNetworkState);
623 mNetworkLocationInterface.updateWifiEnabledState(mWifiEnabled);
624 mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
625
626 if (mLastCellState != null) {
627 if (mCollector != null) {
628 mCollector.updateCellState(mLastCellState);
629 }
630 mNetworkLocationProvider.updateCellState(mLastCellState);
631 }
632
633 // There might be an existing wifi scan available
634 if (mWifiManager != null) {
635 List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
636 if (wifiScanResults != null && wifiScanResults.size() != 0) {
637 mNetworkLocationInterface.updateWifiScanResults(wifiScanResults);
638 if (mCollector != null) {
639 mCollector.updateWifiScanResults(wifiScanResults);
640 }
641 }
642 }
643 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 }
645 }
646
647 public void setLocationCollector(ILocationCollector collector) {
648 synchronized (mLocationListeners) {
649 mCollector = collector;
650 if (mGpsLocationProvider != null) {
651 mGpsLocationProvider.setLocationCollector(mCollector);
652 }
653 }
654 }
655
656 private WifiManager.WifiLock getWifiWakelockLocked() {
657 if (mWifiLock == null && mWifiManager != null) {
658 mWifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, WIFILOCK_KEY);
659 mWifiLock.setReferenceCounted(false);
660 }
661 return mWifiLock;
662 }
663
664 private boolean isAllowedBySettingsLocked(String provider) {
665 if (mEnabledProviders.contains(provider)) {
666 return true;
667 }
668 if (mDisabledProviders.contains(provider)) {
669 return false;
670 }
671 // Use system settings
672 ContentResolver resolver = mContext.getContentResolver();
673 String allowedProviders = Settings.Secure.getString(resolver,
674 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
675
676 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
677 }
678
679 private void checkPermissionsSafe(String provider) {
680 if (LocationManager.GPS_PROVIDER.equals(provider)
681 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
682 != PackageManager.PERMISSION_GRANTED)) {
683 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
684 }
685 if (LocationManager.NETWORK_PROVIDER.equals(provider)
686 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
687 != PackageManager.PERMISSION_GRANTED)
688 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
689 != PackageManager.PERMISSION_GRANTED)) {
690 throw new SecurityException(
691 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
692 }
693 }
694
695 private boolean isAllowedProviderSafe(String provider) {
696 if (LocationManager.GPS_PROVIDER.equals(provider)
697 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
698 != PackageManager.PERMISSION_GRANTED)) {
699 return false;
700 }
701 if (LocationManager.NETWORK_PROVIDER.equals(provider)
702 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
703 != PackageManager.PERMISSION_GRANTED)
704 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
705 != PackageManager.PERMISSION_GRANTED)) {
706 return false;
707 }
708
709 return true;
710 }
711
712 private String[] getPackageNames() {
713 // Since a single UID may correspond to multiple packages, this can only be used as an
714 // approximation for tracking
715 return mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
716 }
717
718 public List<String> getAllProviders() {
719 try {
720 synchronized (mLocationListeners) {
721 return _getAllProvidersLocked();
722 }
723 } catch (SecurityException se) {
724 throw se;
725 } catch (Exception e) {
726 Log.e(TAG, "getAllProviders got exception:", e);
727 return null;
728 }
729 }
730
731 private List<String> _getAllProvidersLocked() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700732 if (LOCAL_LOGV) {
733 Log.v(TAG, "getAllProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 }
735 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
736 ArrayList<String> out = new ArrayList<String>(providers.size());
737
738 for (LocationProviderImpl p : providers) {
739 out.add(p.getName());
740 }
741 return out;
742 }
743
744 public List<String> getProviders(boolean enabledOnly) {
745 try {
746 synchronized (mLocationListeners) {
747 return _getProvidersLocked(enabledOnly);
748 }
749 } catch (SecurityException se) {
750 throw se;
751 } catch (Exception e) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700752 Log.e(TAG, "getProviders got exception:", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753 return null;
754 }
755 }
756
757 private List<String> _getProvidersLocked(boolean enabledOnly) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700758 if (LOCAL_LOGV) {
759 Log.v(TAG, "getProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 }
761 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
762 ArrayList<String> out = new ArrayList<String>();
763
764 for (LocationProviderImpl p : providers) {
765 String name = p.getName();
766 if (isAllowedProviderSafe(name)) {
767 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
768 continue;
769 }
770 out.add(name);
771 }
772 }
773 return out;
774 }
775
776 public void updateProviders() {
777 synchronized (mLocationListeners) {
778 updateProvidersLocked();
779 }
780 }
781
782 private void updateProvidersLocked() {
783 for (LocationProviderImpl p : LocationProviderImpl.getProviders()) {
784 boolean isEnabled = p.isEnabled();
785 String name = p.getName();
786 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
787
788 // Collection is only allowed when network provider is being used
789 if (mCollector != null &&
790 p.getName().equals(LocationManager.NETWORK_PROVIDER)) {
791 mCollector.updateNetworkProviderStatus(shouldBeEnabled);
792 }
793
794 if (isEnabled && !shouldBeEnabled) {
795 updateProviderListenersLocked(name, false);
796 } else if (!isEnabled && shouldBeEnabled) {
797 updateProviderListenersLocked(name, true);
798 }
799
800 }
801 }
802
803 private void updateProviderListenersLocked(String provider, boolean enabled) {
804 int listeners = 0;
805
806 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
807 if (p == null) {
808 return;
809 }
810
811 ArrayList<Receiver> deadReceivers = null;
812
813 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
814 if (records != null) {
815 final int N = records.size();
816 for (int i=0; i<N; i++) {
817 UpdateRecord record = records.get(i);
818 // Sends a notification message to the receiver
819 try {
820 Receiver receiver = record.mReceiver;
821 if (receiver.isListener()) {
822 if (enabled) {
823 receiver.getListener().onProviderEnabled(provider);
824 } else {
825 receiver.getListener().onProviderDisabled(provider);
826 }
827 } else {
828 Intent providerIntent = new Intent();
829 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
830 try {
831 receiver.getPendingIntent().send(mContext, 0,
832 providerIntent, null, null);
833 } catch (PendingIntent.CanceledException e) {
834 if (deadReceivers == null) {
835 deadReceivers = new ArrayList<Receiver>();
836 deadReceivers.add(receiver);
837 }
838 }
839 }
840 } catch (RemoteException e) {
841 // The death link will clean this up.
842 }
843 listeners++;
844 }
845 }
846
847 if (deadReceivers != null) {
848 for (int i=deadReceivers.size()-1; i>=0; i--) {
849 removeUpdatesLocked(deadReceivers.get(i));
850 }
851 }
852
853 if (enabled) {
854 p.enable();
855 if (listeners > 0) {
856 p.setMinTime(getMinTimeLocked(provider));
857 p.enableLocationTracking(true);
858 updateWakelockStatusLocked(mScreenOn);
859 }
860 } else {
861 p.enableLocationTracking(false);
862 if (p == mGpsLocationProvider) {
863 mGpsNavigating = false;
864 reportStopGpsLocked();
865 }
866 p.disable();
867 updateWakelockStatusLocked(mScreenOn);
868 }
869
870 if (enabled && listeners > 0) {
871 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
872 Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
873 mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
874 } else {
875 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
876 }
877 }
878
879 private long getMinTimeLocked(String provider) {
880 long minTime = Long.MAX_VALUE;
881 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
882 if (records != null) {
883 for (int i=records.size()-1; i>=0; i--) {
884 minTime = Math.min(minTime, records.get(i).mMinTime);
885 }
886 }
887 return minTime;
888 }
889
890 private class UpdateRecord {
891 final String mProvider;
892 final Receiver mReceiver;
893 final long mMinTime;
894 final float mMinDistance;
895 final int mUid;
896 final String[] mPackages;
897
898 /**
899 * Note: must be constructed with lock held.
900 */
901 UpdateRecord(String provider, long minTime, float minDistance,
902 Receiver receiver, int uid, String[] packages) {
903 mProvider = provider;
904 mReceiver = receiver;
905 mMinTime = minTime;
906 mMinDistance = minDistance;
907 mUid = uid;
908 mPackages = packages;
909
910 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
911 if (records == null) {
912 records = new ArrayList<UpdateRecord>();
913 mRecordsByProvider.put(provider, records);
914 }
915 if (!records.contains(this)) {
916 records.add(this);
917 }
918 }
919
920 /**
921 * Method to be called when a record will no longer be used. Calling this multiple times
922 * must have the same effect as calling it once.
923 */
924 void disposeLocked() {
925 ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
926 records.remove(this);
927 }
928
929 @Override
930 public String toString() {
931 return "UpdateRecord{"
932 + Integer.toHexString(System.identityHashCode(this))
933 + " " + mProvider + " " + mReceiver + "}";
934 }
935
936 void dump(PrintWriter pw, String prefix) {
937 pw.println(prefix + this);
938 pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
939 pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
940 StringBuilder sb = new StringBuilder();
941 if (mPackages != null) {
942 for (int i=0; i<mPackages.length; i++) {
943 if (i > 0) sb.append(", ");
944 sb.append(mPackages[i]);
945 }
946 }
947 pw.println(prefix + "mUid=" + mUid + " mPackages=" + sb);
948 }
949
950 /**
951 * Calls dispose().
952 */
953 @Override protected void finalize() {
954 synchronized (mLocationListeners) {
955 disposeLocked();
956 }
957 }
958 }
959
960 public void requestLocationUpdates(String provider,
961 long minTime, float minDistance, ILocationListener listener) {
962
963 try {
964 synchronized (mLocationListeners) {
965 requestLocationUpdatesLocked(provider, minTime, minDistance,
966 new Receiver(listener, Binder.getCallingUid()));
967 }
968 } catch (SecurityException se) {
969 throw se;
970 } catch (Exception e) {
971 Log.e(TAG, "requestUpdates got exception:", e);
972 }
973 }
974
975 public void requestLocationUpdatesPI(String provider,
976 long minTime, float minDistance, PendingIntent intent) {
977 try {
978 synchronized (mLocationListeners) {
979 requestLocationUpdatesLocked(provider, minTime, minDistance,
980 new Receiver(intent, Binder.getCallingUid()));
981 }
982 } catch (SecurityException se) {
983 throw se;
984 } catch (Exception e) {
985 Log.e(TAG, "requestUpdates got exception:", e);
986 }
987 }
988
989 private void requestLocationUpdatesLocked(String provider,
990 long minTime, float minDistance, Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700991 if (LOCAL_LOGV) {
992 Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993 }
994
995 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
996 if (impl == null) {
997 throw new IllegalArgumentException("provider=" + provider);
998 }
999
1000 checkPermissionsSafe(provider);
1001
1002 String[] packages = getPackageNames();
1003
1004 // so wakelock calls will succeed
1005 final int callingUid = Binder.getCallingUid();
1006 long identity = Binder.clearCallingIdentity();
1007 try {
1008 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance,
1009 receiver, callingUid, packages);
1010 if (!mListeners.contains(receiver)) {
1011 try {
1012 if (receiver.isListener()) {
1013 receiver.getListener().asBinder().linkToDeath(receiver, 0);
1014 }
1015 mListeners.add(receiver);
1016 } catch (RemoteException e) {
1017 return;
1018 }
1019 }
1020
1021 HashMap<String,UpdateRecord> records = mLocationListeners.get(receiver);
1022 if (records == null) {
1023 records = new HashMap<String,UpdateRecord>();
1024 mLocationListeners.put(receiver, records);
1025 }
1026 UpdateRecord oldRecord = records.put(provider, r);
1027 if (oldRecord != null) {
1028 oldRecord.disposeLocked();
1029 }
1030
1031 boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
1032 if (isProviderEnabled) {
1033 long minTimeForProvider = getMinTimeLocked(provider);
1034 impl.setMinTime(minTimeForProvider);
1035 impl.enableLocationTracking(true);
1036 updateWakelockStatusLocked(mScreenOn);
1037
1038 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1039 if (mGpsNavigating) {
1040 updateReportedGpsLocked();
1041 }
1042 }
1043
1044 // Clear heartbeats if any before starting a new one
1045 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
1046 Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
1047 mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
1048 } else {
1049 try {
1050 // Notify the listener that updates are currently disabled
1051 if (receiver.isListener()) {
1052 receiver.getListener().onProviderDisabled(provider);
1053 }
1054 } catch(RemoteException e) {
1055 Log.w(TAG, "RemoteException calling onProviderDisabled on " +
1056 receiver.getListener());
1057 }
1058 }
1059 } finally {
1060 Binder.restoreCallingIdentity(identity);
1061 }
1062 }
1063
1064 public void removeUpdates(ILocationListener listener) {
1065 try {
1066 synchronized (mLocationListeners) {
1067 removeUpdatesLocked(new Receiver(listener, Binder.getCallingUid()));
1068 }
1069 } catch (SecurityException se) {
1070 throw se;
1071 } catch (Exception e) {
1072 Log.e(TAG, "removeUpdates got exception:", e);
1073 }
1074 }
1075
1076 public void removeUpdatesPI(PendingIntent intent) {
1077 try {
1078 synchronized (mLocationListeners) {
1079 removeUpdatesLocked(new Receiver(intent, Binder.getCallingUid()));
1080 }
1081 } catch (SecurityException se) {
1082 throw se;
1083 } catch (Exception e) {
1084 Log.e(TAG, "removeUpdates got exception:", e);
1085 }
1086 }
1087
1088 private void removeUpdatesLocked(Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001089 if (LOCAL_LOGV) {
1090 Log.v(TAG, "_removeUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 }
1092
1093 // so wakelock calls will succeed
1094 final int callingUid = Binder.getCallingUid();
1095 long identity = Binder.clearCallingIdentity();
1096 try {
1097 int idx = mListeners.indexOf(receiver);
1098 if (idx >= 0) {
1099 Receiver myReceiver = mListeners.remove(idx);
1100 if (myReceiver.isListener()) {
1101 myReceiver.getListener().asBinder().unlinkToDeath(myReceiver, 0);
1102 }
1103 }
1104
1105 // Record which providers were associated with this listener
1106 HashSet<String> providers = new HashSet<String>();
1107 HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(receiver);
1108 if (oldRecords != null) {
1109 // Call dispose() on the obsolete update records.
1110 for (UpdateRecord record : oldRecords.values()) {
1111 if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) {
1112 if (mNetworkLocationInterface != null) {
1113 mNetworkLocationInterface.removeListener(record.mPackages);
1114 }
1115 }
1116 record.disposeLocked();
1117 }
1118 // Accumulate providers
1119 providers.addAll(oldRecords.keySet());
1120 }
1121
1122 mLocationListeners.remove(receiver);
1123 mLastFixBroadcast.remove(receiver);
1124 mLastStatusBroadcast.remove(receiver);
1125
1126 // See if the providers associated with this listener have any
1127 // other listeners; if one does, inform it of the new smallest minTime
1128 // value; if one does not, disable location tracking for it
1129 for (String provider : providers) {
1130 // If provider is already disabled, don't need to do anything
1131 if (!isAllowedBySettingsLocked(provider)) {
1132 continue;
1133 }
1134
1135 boolean hasOtherListener = false;
1136 ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1137 if (recordsForProvider != null && recordsForProvider.size() > 0) {
1138 hasOtherListener = true;
1139 }
1140
1141 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1142 if (p != null) {
1143 if (hasOtherListener) {
1144 p.setMinTime(getMinTimeLocked(provider));
1145 } else {
1146 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
1147 p.enableLocationTracking(false);
1148 }
1149
1150 if (p == mGpsLocationProvider && mGpsNavigating) {
1151 updateReportedGpsLocked();
1152 }
1153 }
1154 }
1155
1156 updateWakelockStatusLocked(mScreenOn);
1157 } finally {
1158 Binder.restoreCallingIdentity(identity);
1159 }
1160 }
1161
1162 public boolean addGpsStatusListener(IGpsStatusListener listener) {
1163 if (mGpsLocationProvider == null) {
1164 return false;
1165 }
1166 if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1167 PackageManager.PERMISSION_GRANTED) {
1168 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1169 }
1170
1171 try {
1172 mGpsLocationProvider.addGpsStatusListener(listener);
1173 } catch (RemoteException e) {
1174 Log.w(TAG, "RemoteException in addGpsStatusListener");
1175 return false;
1176 }
1177 return true;
1178 }
1179
1180 public void removeGpsStatusListener(IGpsStatusListener listener) {
1181 synchronized (mLocationListeners) {
1182 mGpsLocationProvider.removeGpsStatusListener(listener);
1183 }
1184 }
1185
1186 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1187 // first check for permission to the provider
1188 checkPermissionsSafe(provider);
1189 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1190 if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1191 != PackageManager.PERMISSION_GRANTED)) {
1192 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1193 }
1194
1195 synchronized (mLocationListeners) {
1196 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
1197 if (provider == null) {
1198 return false;
1199 }
1200
1201 return impl.sendExtraCommand(command, extras);
1202 }
1203 }
1204
1205 class ProximityAlert {
1206 final int mUid;
1207 final double mLatitude;
1208 final double mLongitude;
1209 final float mRadius;
1210 final long mExpiration;
1211 final PendingIntent mIntent;
1212 final Location mLocation;
1213
1214 public ProximityAlert(int uid, double latitude, double longitude,
1215 float radius, long expiration, PendingIntent intent) {
1216 mUid = uid;
1217 mLatitude = latitude;
1218 mLongitude = longitude;
1219 mRadius = radius;
1220 mExpiration = expiration;
1221 mIntent = intent;
1222
1223 mLocation = new Location("");
1224 mLocation.setLatitude(latitude);
1225 mLocation.setLongitude(longitude);
1226 }
1227
1228 long getExpiration() {
1229 return mExpiration;
1230 }
1231
1232 PendingIntent getIntent() {
1233 return mIntent;
1234 }
1235
1236 boolean isInProximity(double latitude, double longitude) {
1237 Location loc = new Location("");
1238 loc.setLatitude(latitude);
1239 loc.setLongitude(longitude);
1240
1241 double radius = loc.distanceTo(mLocation);
1242 return radius <= mRadius;
1243 }
1244
1245 @Override
1246 public String toString() {
1247 return "ProximityAlert{"
1248 + Integer.toHexString(System.identityHashCode(this))
1249 + " uid " + mUid + mIntent + "}";
1250 }
1251
1252 void dump(PrintWriter pw, String prefix) {
1253 pw.println(prefix + this);
1254 pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
1255 pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
1256 pw.println(prefix + "mIntent=" + mIntent);
1257 pw.println(prefix + "mLocation:");
1258 mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
1259 }
1260 }
1261
1262 // Listener for receiving locations to trigger proximity alerts
1263 class ProximityListener extends ILocationListener.Stub {
1264
1265 boolean isGpsAvailable = false;
1266
1267 // Note: this is called with the lock held.
1268 public void onLocationChanged(Location loc) {
1269
1270 // If Gps is available, then ignore updates from NetworkLocationProvider
1271 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1272 isGpsAvailable = true;
1273 }
1274 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1275 return;
1276 }
1277
1278 // Process proximity alerts
1279 long now = System.currentTimeMillis();
1280 double latitude = loc.getLatitude();
1281 double longitude = loc.getLongitude();
1282 ArrayList<PendingIntent> intentsToRemove = null;
1283
1284 for (ProximityAlert alert : mProximityAlerts.values()) {
1285 PendingIntent intent = alert.getIntent();
1286 long expiration = alert.getExpiration();
1287
1288 if ((expiration == -1) || (now <= expiration)) {
1289 boolean entered = mProximitiesEntered.contains(alert);
1290 boolean inProximity =
1291 alert.isInProximity(latitude, longitude);
1292 if (!entered && inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001293 if (LOCAL_LOGV) {
1294 Log.v(TAG, "Entered alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 }
1296 mProximitiesEntered.add(alert);
1297 Intent enteredIntent = new Intent();
1298 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1299 try {
1300 intent.send(mContext, 0, enteredIntent, null, null);
1301 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001302 if (LOCAL_LOGV) {
1303 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001304 }
1305 if (intentsToRemove == null) {
1306 intentsToRemove = new ArrayList<PendingIntent>();
1307 }
1308 intentsToRemove.add(intent);
1309 }
1310 } else if (entered && !inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001311 if (LOCAL_LOGV) {
1312 Log.v(TAG, "Exited alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001313 }
1314 mProximitiesEntered.remove(alert);
1315 Intent exitedIntent = new Intent();
1316 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1317 try {
1318 intent.send(mContext, 0, exitedIntent, null, null);
1319 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001320 if (LOCAL_LOGV) {
1321 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 }
1323 if (intentsToRemove == null) {
1324 intentsToRemove = new ArrayList<PendingIntent>();
1325 }
1326 intentsToRemove.add(intent);
1327 }
1328 }
1329 } else {
1330 // Mark alert for expiration
The Android Open Source Project10592532009-03-18 17:39:46 -07001331 if (LOCAL_LOGV) {
1332 Log.v(TAG, "Expiring proximity alert: " + alert);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001333 }
1334 if (intentsToRemove == null) {
1335 intentsToRemove = new ArrayList<PendingIntent>();
1336 }
1337 intentsToRemove.add(alert.getIntent());
1338 }
1339 }
1340
1341 // Remove expired alerts
1342 if (intentsToRemove != null) {
1343 for (PendingIntent i : intentsToRemove) {
1344 mProximityAlerts.remove(i);
1345 ProximityAlert alert = mProximityAlerts.get(i);
1346 mProximitiesEntered.remove(alert);
1347 }
1348 }
1349
1350 }
1351
1352 // Note: this is called with the lock held.
1353 public void onProviderDisabled(String provider) {
1354 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1355 isGpsAvailable = false;
1356 }
1357 }
1358
1359 // Note: this is called with the lock held.
1360 public void onProviderEnabled(String provider) {
1361 // ignore
1362 }
1363
1364 // Note: this is called with the lock held.
1365 public void onStatusChanged(String provider, int status, Bundle extras) {
1366 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1367 (status != LocationProvider.AVAILABLE)) {
1368 isGpsAvailable = false;
1369 }
1370 }
1371 }
1372
1373 public void addProximityAlert(double latitude, double longitude,
1374 float radius, long expiration, PendingIntent intent) {
1375 try {
1376 synchronized (mLocationListeners) {
1377 addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
1378 }
1379 } catch (SecurityException se) {
1380 throw se;
1381 } catch (Exception e) {
1382 Log.e(TAG, "addProximityAlert got exception:", e);
1383 }
1384 }
1385
1386 private void addProximityAlertLocked(double latitude, double longitude,
1387 float radius, long expiration, PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001388 if (LOCAL_LOGV) {
1389 Log.v(TAG, "addProximityAlert: latitude = " + latitude +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001390 ", longitude = " + longitude +
1391 ", expiration = " + expiration +
1392 ", intent = " + intent);
1393 }
1394
1395 // Require ability to access all providers for now
1396 if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
1397 !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
1398 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1399 }
1400
1401 if (expiration != -1) {
1402 expiration += System.currentTimeMillis();
1403 }
1404 ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
1405 latitude, longitude, radius, expiration, intent);
1406 mProximityAlerts.put(intent, alert);
1407
1408 if (mProximityListener == null) {
1409 mProximityListener = new Receiver(new ProximityListener(), -1);
1410
1411 LocationProvider provider = LocationProviderImpl.getProvider(
1412 LocationManager.GPS_PROVIDER);
1413 if (provider != null) {
1414 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1415 }
1416
1417 provider =
1418 LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER);
1419 if (provider != null) {
1420 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1421 }
1422 } else if (mGpsNavigating) {
1423 updateReportedGpsLocked();
1424 }
1425 }
1426
1427 public void removeProximityAlert(PendingIntent intent) {
1428 try {
1429 synchronized (mLocationListeners) {
1430 removeProximityAlertLocked(intent);
1431 }
1432 } catch (SecurityException se) {
1433 throw se;
1434 } catch (Exception e) {
1435 Log.e(TAG, "removeProximityAlert got exception:", e);
1436 }
1437 }
1438
1439 private void removeProximityAlertLocked(PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001440 if (LOCAL_LOGV) {
1441 Log.v(TAG, "removeProximityAlert: intent = " + intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442 }
1443
1444 mProximityAlerts.remove(intent);
1445 if (mProximityAlerts.size() == 0) {
1446 removeUpdatesLocked(mProximityListener);
1447 mProximityListener = null;
1448 } else if (mGpsNavigating) {
1449 updateReportedGpsLocked();
1450 }
1451 }
1452
1453 /**
1454 * @return null if the provider does not exits
1455 * @throw SecurityException if the provider is not allowed to be
1456 * accessed by the caller
1457 */
1458 public Bundle getProviderInfo(String provider) {
1459 try {
1460 synchronized (mLocationListeners) {
1461 return _getProviderInfoLocked(provider);
1462 }
1463 } catch (SecurityException se) {
1464 throw se;
1465 } catch (Exception e) {
1466 Log.e(TAG, "_getProviderInfo got exception:", e);
1467 return null;
1468 }
1469 }
1470
1471 private Bundle _getProviderInfoLocked(String provider) {
1472 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1473 if (p == null) {
1474 return null;
1475 }
1476
1477 checkPermissionsSafe(provider);
1478
1479 Bundle b = new Bundle();
1480 b.putBoolean("network", p.requiresNetwork());
1481 b.putBoolean("satellite", p.requiresSatellite());
1482 b.putBoolean("cell", p.requiresCell());
1483 b.putBoolean("cost", p.hasMonetaryCost());
1484 b.putBoolean("altitude", p.supportsAltitude());
1485 b.putBoolean("speed", p.supportsSpeed());
1486 b.putBoolean("bearing", p.supportsBearing());
1487 b.putInt("power", p.getPowerRequirement());
1488 b.putInt("accuracy", p.getAccuracy());
1489
1490 return b;
1491 }
1492
1493 public boolean isProviderEnabled(String provider) {
1494 try {
1495 synchronized (mLocationListeners) {
1496 return _isProviderEnabledLocked(provider);
1497 }
1498 } catch (SecurityException se) {
1499 throw se;
1500 } catch (Exception e) {
1501 Log.e(TAG, "isProviderEnabled got exception:", e);
1502 return false;
1503 }
1504 }
1505
1506 private boolean _isProviderEnabledLocked(String provider) {
1507 checkPermissionsSafe(provider);
1508
1509 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1510 if (p == null) {
1511 throw new IllegalArgumentException("provider=" + provider);
1512 }
1513 return isAllowedBySettingsLocked(provider);
1514 }
1515
1516 public Location getLastKnownLocation(String provider) {
1517 try {
1518 synchronized (mLocationListeners) {
1519 return _getLastKnownLocationLocked(provider);
1520 }
1521 } catch (SecurityException se) {
1522 throw se;
1523 } catch (Exception e) {
1524 Log.e(TAG, "getLastKnownLocation got exception:", e);
1525 return null;
1526 }
1527 }
1528
1529 private Location _getLastKnownLocationLocked(String provider) {
1530 checkPermissionsSafe(provider);
1531
1532 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1533 if (p == null) {
1534 throw new IllegalArgumentException("provider=" + provider);
1535 }
1536
1537 if (!isAllowedBySettingsLocked(provider)) {
1538 return null;
1539 }
1540
1541 Location location = mLastKnownLocation.get(provider);
1542 if (location == null) {
1543 // Get the persistent last known location for the provider
1544 location = readLastKnownLocationLocked(provider);
1545 if (location != null) {
1546 mLastKnownLocation.put(provider, location);
1547 }
1548 }
1549
1550 return location;
1551 }
1552
1553 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1554 // Always broadcast the first update
1555 if (lastLoc == null) {
1556 return true;
1557 }
1558
1559 // Don't broadcast same location again regardless of condition
1560 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1561 if (loc.getTime() == lastLoc.getTime()) {
1562 return false;
1563 }
1564
1565 // Check whether sufficient distance has been traveled
1566 double minDistance = record.mMinDistance;
1567 if (minDistance > 0.0) {
1568 if (loc.distanceTo(lastLoc) <= minDistance) {
1569 return false;
1570 }
1571 }
1572
1573 return true;
1574 }
1575
1576 private void handleLocationChangedLocked(String provider) {
1577 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1578 if (records == null || records.size() == 0) {
1579 return;
1580 }
1581
1582 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1583 if (p == null) {
1584 return;
1585 }
1586
1587 // Get location object
1588 Location loc = mLocationsByProvider.get(provider);
1589 if (loc == null) {
1590 loc = new Location(provider);
1591 mLocationsByProvider.put(provider, loc);
1592 } else {
1593 loc.reset();
1594 }
1595
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001596 boolean locationValid;
1597
1598 // Use the mock location if available
1599 MockProvider mockProvider = mMockProviders.get(provider);
1600 if (mockProvider != null && mockProvider.getLocation(loc)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001601 locationValid = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001602 } else {
1603 locationValid = p.getLocation(loc);
1604 }
1605
1606 // Update last known location for provider
1607 if (locationValid) {
1608 Location location = mLastKnownLocation.get(provider);
1609 if (location == null) {
1610 mLastKnownLocation.put(provider, new Location(loc));
1611 } else {
1612 location.set(loc);
1613 }
1614 writeLastKnownLocationLocked(provider, loc);
1615
1616 if (p instanceof INetworkLocationProvider) {
1617 mWakeLockNetworkReceived = true;
1618 } else if (p instanceof GpsLocationProvider) {
1619 // Gps location received signal is in NetworkStateBroadcastReceiver
1620 }
1621 }
1622
1623 // Fetch latest status update time
1624 long newStatusUpdateTime = p.getStatusUpdateTime();
1625
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001626 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001627 Bundle extras = new Bundle();
1628 int status = p.getStatus(extras);
1629
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001630 // Override with mock values if mock provider is present
1631 if (mockProvider != null) {
1632 status = mockProvider.overrideStatus(status);
1633 newStatusUpdateTime = mockProvider.overrideStatusUpdateTime(newStatusUpdateTime);
1634 mockProvider.overrideExtras(extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001635 }
1636
1637 ArrayList<Receiver> deadReceivers = null;
1638
1639 // Broadcast location or status to all listeners
1640 final int N = records.size();
1641 for (int i=0; i<N; i++) {
1642 UpdateRecord r = records.get(i);
1643 Receiver receiver = r.mReceiver;
1644
1645 // Broadcast location only if it is valid
1646 if (locationValid) {
1647 HashMap<String,Location> map = mLastFixBroadcast.get(receiver);
1648 if (map == null) {
1649 map = new HashMap<String,Location>();
1650 mLastFixBroadcast.put(receiver, map);
1651 }
1652 Location lastLoc = map.get(provider);
1653 if ((lastLoc == null) || shouldBroadcastSafe(loc, lastLoc, r)) {
1654 if (lastLoc == null) {
1655 lastLoc = new Location(loc);
1656 map.put(provider, lastLoc);
1657 } else {
1658 lastLoc.set(loc);
1659 }
1660 if (!receiver.callLocationChangedLocked(loc)) {
1661 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1662 if (deadReceivers == null) {
1663 deadReceivers = new ArrayList<Receiver>();
1664 }
1665 deadReceivers.add(receiver);
1666 }
1667 }
1668 }
1669
1670 // Broadcast status message
1671 HashMap<String,Long> statusMap = mLastStatusBroadcast.get(receiver);
1672 if (statusMap == null) {
1673 statusMap = new HashMap<String,Long>();
1674 mLastStatusBroadcast.put(receiver, statusMap);
1675 }
1676 long prevStatusUpdateTime =
1677 (statusMap.get(provider) != null) ? statusMap.get(provider) : 0;
1678
1679 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1680 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1681
1682 statusMap.put(provider, newStatusUpdateTime);
1683 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1684 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1685 if (deadReceivers == null) {
1686 deadReceivers = new ArrayList<Receiver>();
1687 }
1688 if (!deadReceivers.contains(receiver)) {
1689 deadReceivers.add(receiver);
1690 }
1691 }
1692 }
1693 }
1694
1695 if (deadReceivers != null) {
1696 for (int i=deadReceivers.size()-1; i>=0; i--) {
1697 removeUpdatesLocked(deadReceivers.get(i));
1698 }
1699 }
1700 }
1701
1702 private class LocationWorkerHandler extends Handler {
1703
1704 @Override
1705 public void handleMessage(Message msg) {
1706 try {
1707 if (msg.what == MESSAGE_HEARTBEAT) {
1708 // log("LocationWorkerHandler: Heartbeat!");
1709
1710 synchronized (mLocationListeners) {
1711 String provider = (String) msg.obj;
1712 if (!isAllowedBySettingsLocked(provider)) {
1713 return;
1714 }
1715
1716 // Process the location fix if the screen is on or we're holding a wakelock
1717 if (mScreenOn || (mWakeLockAcquireTime != 0)) {
1718 handleLocationChangedLocked(provider);
1719 }
1720
1721 // If it continues to have listeners
1722 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1723 if (records != null && records.size() > 0) {
1724 Message m = Message.obtain(this, MESSAGE_HEARTBEAT, provider);
1725 sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
1726 }
1727
1728 if ((mWakeLockAcquireTime != 0) &&
1729 (SystemClock.elapsedRealtime() - mWakeLockAcquireTime
1730 > MAX_TIME_FOR_WAKE_LOCK)) {
1731
1732 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1733 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1734
1735 log("LocationWorkerHandler: Exceeded max time for wake lock");
1736 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1737 sendMessageAtFrontOfQueue(m);
1738
1739 } else if (mWakeLockAcquireTime != 0 &&
1740 mWakeLockGpsReceived && mWakeLockNetworkReceived) {
1741
1742 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1743 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1744
1745 log("LocationWorkerHandler: Locations received.");
1746 mWakeLockAcquireTime = 0;
1747 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1748 sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK);
1749 }
1750 }
1751
1752 } else if (msg.what == MESSAGE_ACQUIRE_WAKE_LOCK) {
1753 log("LocationWorkerHandler: Acquire");
1754 synchronized (mLocationListeners) {
1755 acquireWakeLockLocked();
1756 }
1757 } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) {
1758 log("LocationWorkerHandler: Release");
1759
1760 // Update wakelock status so the next alarm is set before releasing wakelock
1761 synchronized (mLocationListeners) {
1762 updateWakelockStatusLocked(mScreenOn);
1763 releaseWakeLockLocked();
1764 }
1765 } else if (msg.what == MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER) {
1766 synchronized (mLocationListeners) {
1767 Log.d(TAG, "installing network location provider");
1768 INetworkLocationManager.InstallCallback callback =
1769 (INetworkLocationManager.InstallCallback)msg.obj;
1770 callback.installNetworkLocationProvider(LocationManagerService.this);
1771 }
1772 }
1773 } catch (Exception e) {
1774 // Log, don't crash!
1775 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1776 }
1777 }
1778 }
1779
1780 class CellLocationUpdater extends Thread {
1781 CellLocation mNextLocation;
1782
1783 CellLocationUpdater() {
1784 super("CellLocationUpdater");
1785 }
1786
1787 @Override
1788 public void run() {
1789 int curAsu = -1;
1790 CellLocation curLocation = null;
1791
1792 while (true) {
1793 // See if there is more work to do...
1794 synchronized (mLocationListeners) {
1795 if (curLocation == mNextLocation) {
1796 mCellLocationUpdater = null;
1797 break;
1798 }
1799
1800 curLocation = mNextLocation;
1801 if (curLocation == null) {
1802 mCellLocationUpdater = null;
1803 break;
1804 }
1805
1806 curAsu = mLastSignalStrength;
1807
1808 mNextLocation = null;
1809 }
1810
1811 try {
1812 // Gets cell state. This can block so must be done without
1813 // locks held.
1814 CellState cs = new CellState(mTelephonyManager, curLocation, curAsu);
1815
1816 synchronized (mLocationListeners) {
1817 mLastCellState = cs;
1818
1819 cs.updateSignalStrength(mLastSignalStrength);
1820 cs.updateRadioType(mLastRadioType);
1821
1822 // Notify collector
1823 if (mCollector != null) {
1824 mCollector.updateCellState(cs);
1825 }
1826
1827 // Updates providers
1828 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1829 for (LocationProviderImpl provider : providers) {
1830 if (provider.requiresCell()) {
1831 provider.updateCellState(cs);
1832 }
1833 }
1834 }
1835 } catch (RuntimeException e) {
1836 Log.e(TAG, "Exception in PhoneStateListener.onCellLocationChanged:", e);
1837 }
1838 }
1839 }
1840 }
1841
1842 CellLocationUpdater mCellLocationUpdater = null;
1843 CellState mLastCellState = null;
1844 int mLastSignalStrength = -1;
1845 int mLastRadioType = -1;
1846
1847 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
1848
1849 @Override
1850 public void onCellLocationChanged(CellLocation cellLocation) {
1851 synchronized (mLocationListeners) {
1852 if (mCellLocationUpdater == null) {
1853 mCellLocationUpdater = new CellLocationUpdater();
1854 mCellLocationUpdater.start();
1855 }
1856 mCellLocationUpdater.mNextLocation = cellLocation;
1857 }
1858 }
1859
1860 @Override
1861 public void onSignalStrengthChanged(int asu) {
1862 synchronized (mLocationListeners) {
1863 mLastSignalStrength = asu;
1864
1865 if (mLastCellState != null) {
1866 mLastCellState.updateSignalStrength(asu);
1867 }
1868 }
1869 }
1870
1871 @Override
1872 public void onDataConnectionStateChanged(int state) {
1873 synchronized (mLocationListeners) {
1874 // Get radio type
1875 int radioType = mTelephonyManager.getNetworkType();
1876 if (radioType == TelephonyManager.NETWORK_TYPE_GPRS ||
1877 radioType == TelephonyManager.NETWORK_TYPE_EDGE) {
1878 radioType = CellState.RADIO_TYPE_GPRS;
1879 } else if (radioType == TelephonyManager.NETWORK_TYPE_UMTS) {
1880 radioType = CellState.RADIO_TYPE_WCDMA;
1881 }
1882 mLastRadioType = radioType;
1883
1884 if (mLastCellState != null) {
1885 mLastCellState.updateRadioType(radioType);
1886 }
1887 }
1888 }
1889 };
1890
1891 private class PowerStateBroadcastReceiver extends BroadcastReceiver {
1892 @Override public void onReceive(Context context, Intent intent) {
1893 String action = intent.getAction();
1894
1895 if (action.equals(ALARM_INTENT)) {
1896 synchronized (mLocationListeners) {
1897 log("PowerStateBroadcastReceiver: Alarm received");
1898 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1899 // Have to do this immediately, rather than posting a
1900 // message, so we execute our code while the system
1901 // is holding a wake lock until the alarm broadcast
1902 // is finished.
1903 acquireWakeLockLocked();
1904 }
1905
1906 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1907 log("PowerStateBroadcastReceiver: Screen off");
1908 synchronized (mLocationListeners) {
1909 updateWakelockStatusLocked(false);
1910 }
1911
1912 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1913 log("PowerStateBroadcastReceiver: Screen on");
1914 synchronized (mLocationListeners) {
1915 updateWakelockStatusLocked(true);
1916 }
1917
1918 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
1919 log("PowerStateBroadcastReceiver: Battery changed");
1920 synchronized (mLocationListeners) {
1921 int scale = intent.getIntExtra(BATTERY_EXTRA_SCALE, 100);
1922 int level = intent.getIntExtra(BATTERY_EXTRA_LEVEL, 0);
1923 boolean plugged = intent.getIntExtra(BATTERY_EXTRA_PLUGGED, 0) != 0;
1924
1925 // Notify collector battery state
1926 if (mCollector != null) {
1927 mCollector.updateBatteryState(scale, level, plugged);
1928 }
1929 }
1930 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
1931 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
1932 synchronized (mLocationListeners) {
1933 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1934 if (uid >= 0) {
1935 ArrayList<Receiver> removedRecs = null;
1936 for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
1937 for (int j=i.size()-1; j>=0; j--) {
1938 UpdateRecord ur = i.get(j);
1939 if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
1940 if (removedRecs == null) {
1941 removedRecs = new ArrayList<Receiver>();
1942 }
1943 if (!removedRecs.contains(ur.mReceiver)) {
1944 removedRecs.add(ur.mReceiver);
1945 }
1946 }
1947 }
1948 }
1949 ArrayList<ProximityAlert> removedAlerts = null;
1950 for (ProximityAlert i : mProximityAlerts.values()) {
1951 if (i.mUid == uid) {
1952 if (removedAlerts == null) {
1953 removedAlerts = new ArrayList<ProximityAlert>();
1954 }
1955 if (!removedAlerts.contains(i)) {
1956 removedAlerts.add(i);
1957 }
1958 }
1959 }
1960 if (removedRecs != null) {
1961 for (int i=removedRecs.size()-1; i>=0; i--) {
1962 removeUpdatesLocked(removedRecs.get(i));
1963 }
1964 }
1965 if (removedAlerts != null) {
1966 for (int i=removedAlerts.size()-1; i>=0; i--) {
1967 removeProximityAlertLocked(removedAlerts.get(i).mIntent);
1968 }
1969 }
1970 }
1971 }
1972 }
1973 }
1974 }
1975
1976 private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
1977 @Override public void onReceive(Context context, Intent intent) {
1978 String action = intent.getAction();
1979
1980 if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
1981
1982 List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
1983
1984 if (wifiScanResults == null) {
1985 return;
1986 }
1987
1988 // Notify provider and collector of Wifi scan results
1989 synchronized (mLocationListeners) {
1990 if (mCollector != null) {
1991 mCollector.updateWifiScanResults(wifiScanResults);
1992 }
1993 if (mNetworkLocationInterface != null) {
1994 mNetworkLocationInterface.updateWifiScanResults(wifiScanResults);
1995 }
1996 }
1997
1998 } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001999 boolean noConnectivity =
2000 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
2001 if (!noConnectivity) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08002002 mNetworkState = LocationProvider.AVAILABLE;
2003 } else {
2004 mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002005 }
2006
2007 // Notify location providers of current network state
2008 synchronized (mLocationListeners) {
2009 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
2010 for (LocationProviderImpl provider : providers) {
2011 if (provider.requiresNetwork()) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08002012 provider.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002013 }
2014 }
2015 }
2016
2017 } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
2018 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
2019 WifiManager.WIFI_STATE_UNKNOWN);
2020
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002021 if (state == WifiManager.WIFI_STATE_ENABLED) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08002022 mWifiEnabled = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002023 } else if (state == WifiManager.WIFI_STATE_DISABLED) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08002024 mWifiEnabled = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002025 } else {
2026 return;
2027 }
2028
2029 // Notify network provider of current wifi enabled state
2030 synchronized (mLocationListeners) {
2031 if (mNetworkLocationInterface != null) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08002032 mNetworkLocationInterface.updateWifiEnabledState(mWifiEnabled);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002033 }
2034 }
2035
2036 } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
2037
2038 final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED,
2039 false);
2040
2041 synchronized (mLocationListeners) {
2042 if (enabled) {
2043 updateReportedGpsLocked();
2044 mGpsNavigating = true;
2045 } else {
2046 reportStopGpsLocked();
2047 mGpsNavigating = false;
2048 // When GPS is disabled, we are OK to release wake-lock
2049 mWakeLockGpsReceived = true;
2050 }
2051 }
2052 }
2053
2054 }
2055 }
2056
2057 // Wake locks
2058
2059 private void updateWakelockStatusLocked(boolean screenOn) {
2060 log("updateWakelockStatus(): " + screenOn);
2061
2062 boolean needsLock = false;
2063 long minTime = Integer.MAX_VALUE;
2064
2065 if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) {
2066 needsLock = true;
2067 minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime);
2068 }
2069
2070 if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) {
2071 needsLock = true;
2072 minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime);
2073 if (screenOn) {
2074 startGpsLocked();
2075 } else if (mScreenOn && !screenOn) {
2076 // We just turned the screen off so stop navigating
2077 stopGpsLocked();
2078 }
2079 }
2080
2081 mScreenOn = screenOn;
2082
2083 PendingIntent sender =
2084 PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0);
2085
2086 // Cancel existing alarm
2087 log("Cancelling existing alarm");
2088 mAlarmManager.cancel(sender);
2089
2090 if (needsLock && !mScreenOn) {
2091 long now = SystemClock.elapsedRealtime();
2092 mAlarmManager.set(
2093 AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender);
2094 mAlarmInterval = minTime;
2095 log("Creating a new wakelock alarm with minTime = " + minTime);
2096 } else {
2097 log("No need for alarm");
2098 mAlarmInterval = -1;
2099
2100 // Clear out existing wakelocks
2101 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
2102 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
2103 releaseWakeLockLocked();
2104 }
2105 }
2106
2107 private void acquireWakeLockLocked() {
2108 try {
2109 acquireWakeLockXLocked();
2110 } catch (Exception e) {
2111 // This is to catch a runtime exception thrown when we try to release an
2112 // already released lock.
2113 Log.e(TAG, "exception in acquireWakeLock()", e);
2114 }
2115 }
2116
2117 private void acquireWakeLockXLocked() {
2118 if (mWakeLock.isHeld()) {
2119 log("Must release wakelock before acquiring");
2120 mWakeLockAcquireTime = 0;
2121 mWakeLock.release();
2122 }
2123
2124 boolean networkActive = (mNetworkLocationProvider != null)
2125 && mNetworkLocationProvider.isLocationTracking();
2126 boolean gpsActive = (mGpsLocationProvider != null)
2127 && mGpsLocationProvider.isLocationTracking();
2128
2129 boolean needsLock = networkActive || gpsActive;
2130 if (!needsLock) {
2131 log("No need for Lock!");
2132 return;
2133 }
2134
2135 mWakeLockGpsReceived = !gpsActive;
2136 mWakeLockNetworkReceived = !networkActive;
2137
2138 // Acquire wake lock
2139 mWakeLock.acquire();
2140 mWakeLockAcquireTime = SystemClock.elapsedRealtime();
2141 log("Acquired wakelock");
2142
2143 // Start the gps provider
2144 startGpsLocked();
2145
2146 // Acquire cell lock
2147 if (mCellWakeLockAcquired) {
2148 // Lock is already acquired
2149 } else if (!mWakeLockNetworkReceived) {
2150 mTelephonyManager.enableLocationUpdates();
2151 mCellWakeLockAcquired = true;
2152 } else {
2153 mCellWakeLockAcquired = false;
2154 }
2155
2156 // Notify NetworkLocationProvider
2157 if (mNetworkLocationInterface != null) {
2158 mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
2159 }
2160
2161 // Acquire wifi lock
2162 WifiManager.WifiLock wifiLock = getWifiWakelockLocked();
2163 if (wifiLock != null) {
2164 if (mWifiWakeLockAcquired) {
2165 // Lock is already acquired
2166 } else if (mWifiManager.isWifiEnabled() && !mWakeLockNetworkReceived) {
2167 wifiLock.acquire();
2168 mWifiWakeLockAcquired = true;
2169 } else {
2170 mWifiWakeLockAcquired = false;
2171 Log.w(TAG, "acquireWakeLock(): Unable to get WiFi lock");
2172 }
2173 }
2174 }
2175
2176 private boolean reportGpsUidLocked(int curSeq, int nextSeq, int uid) {
2177 int seq = mReportedGpsUids.get(uid, -1);
2178 if (seq == curSeq) {
2179 // Already reported; propagate to next sequence.
2180 mReportedGpsUids.put(uid, nextSeq);
2181 return true;
2182 } else if (seq != nextSeq) {
2183 try {
2184 // New UID; report it.
2185 mBatteryStats.noteStartGps(uid);
2186 mReportedGpsUids.put(uid, nextSeq);
2187 return true;
2188 } catch (RemoteException e) {
2189 }
2190 }
2191 return false;
2192 }
2193
2194 private void updateReportedGpsLocked() {
2195 if (mGpsLocationProvider == null) {
2196 return;
2197 }
2198
2199 final String name = mGpsLocationProvider.getName();
2200 final int curSeq = mReportedGpsSeq;
2201 final int nextSeq = (curSeq+1) >= 0 ? (curSeq+1) : 0;
2202 mReportedGpsSeq = nextSeq;
2203
2204 ArrayList<UpdateRecord> urs = mRecordsByProvider.get(name);
2205 int num = 0;
2206 final int N = urs.size();
2207 for (int i=0; i<N; i++) {
2208 UpdateRecord ur = urs.get(i);
2209 if (ur.mReceiver == mProximityListener) {
2210 // We don't want the system to take the blame for this one.
2211 continue;
2212 }
2213 if (reportGpsUidLocked(curSeq, nextSeq, ur.mUid)) {
2214 num++;
2215 }
2216 }
2217
2218 for (ProximityAlert pe : mProximityAlerts.values()) {
2219 if (reportGpsUidLocked(curSeq, nextSeq, pe.mUid)) {
2220 num++;
2221 }
2222 }
2223
2224 if (num != mReportedGpsUids.size()) {
2225 // The number of uids is processed is different than the
2226 // array; report any that are no longer active.
2227 for (int i=mReportedGpsUids.size()-1; i>=0; i--) {
2228 if (mReportedGpsUids.valueAt(i) != nextSeq) {
2229 try {
2230 mBatteryStats.noteStopGps(mReportedGpsUids.keyAt(i));
2231 } catch (RemoteException e) {
2232 }
2233 mReportedGpsUids.removeAt(i);
2234 }
2235 }
2236 }
2237 }
2238
2239 private void reportStopGpsLocked() {
2240 int curSeq = mReportedGpsSeq;
2241 for (int i=mReportedGpsUids.size()-1; i>=0; i--) {
2242 if (mReportedGpsUids.valueAt(i) == curSeq) {
2243 try {
2244 mBatteryStats.noteStopGps(mReportedGpsUids.keyAt(i));
2245 } catch (RemoteException e) {
2246 }
2247 }
2248 }
2249 curSeq++;
2250 if (curSeq < 0) curSeq = 0;
2251 mReportedGpsSeq = curSeq;
2252 mReportedGpsUids.clear();
2253 }
2254
2255 private void startGpsLocked() {
2256 boolean gpsActive = (mGpsLocationProvider != null)
2257 && mGpsLocationProvider.isLocationTracking();
2258 if (gpsActive) {
2259 mGpsLocationProvider.startNavigating();
2260 }
2261 }
2262
2263 private void stopGpsLocked() {
2264 boolean gpsActive = mGpsLocationProvider != null
2265 && mGpsLocationProvider.isLocationTracking();
2266 if (gpsActive) {
2267 mGpsLocationProvider.stopNavigating();
2268 }
2269 }
2270
2271 private void releaseWakeLockLocked() {
2272 try {
2273 releaseWakeLockXLocked();
2274 } catch (Exception e) {
2275 // This is to catch a runtime exception thrown when we try to release an
2276 // already released lock.
2277 Log.e(TAG, "exception in releaseWakeLock()", e);
2278 }
2279 }
2280
2281 private void releaseWakeLockXLocked() {
2282 // Release wifi lock
2283 WifiManager.WifiLock wifiLock = getWifiWakelockLocked();
2284 if (wifiLock != null) {
2285 if (mWifiWakeLockAcquired) {
2286 wifiLock.release();
2287 mWifiWakeLockAcquired = false;
2288 }
2289 }
2290
2291 if (!mScreenOn) {
2292 // Stop the gps
2293 stopGpsLocked();
2294 }
2295
2296 // Release cell lock
2297 if (mCellWakeLockAcquired) {
2298 mTelephonyManager.disableLocationUpdates();
2299 mCellWakeLockAcquired = false;
2300 }
2301
2302 // Notify NetworkLocationProvider
2303 if (mNetworkLocationInterface != null) {
2304 mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
2305 }
2306
2307 // Release wake lock
2308 mWakeLockAcquireTime = 0;
2309 if (mWakeLock.isHeld()) {
2310 log("Released wakelock");
2311 mWakeLock.release();
2312 } else {
2313 log("Can't release wakelock again!");
2314 }
2315 }
2316
2317 // Geocoder
2318
2319 public String getFromLocation(double latitude, double longitude, int maxResults,
2320 String language, String country, String variant, String appName, List<Address> addrs) {
2321 synchronized (mLocationListeners) {
2322 if (mNetworkLocationInterface != null) {
2323 return mNetworkLocationInterface.getFromLocation(latitude, longitude, maxResults,
2324 language, country, variant, appName, addrs);
2325 } else {
2326 return null;
2327 }
2328 }
2329 }
2330
2331 public String getFromLocationName(String locationName,
2332 double lowerLeftLatitude, double lowerLeftLongitude,
2333 double upperRightLatitude, double upperRightLongitude, int maxResults,
2334 String language, String country, String variant, String appName, List<Address> addrs) {
2335 synchronized (mLocationListeners) {
2336 if (mNetworkLocationInterface != null) {
2337 return mNetworkLocationInterface.getFromLocationName(locationName, lowerLeftLatitude,
2338 lowerLeftLongitude, upperRightLatitude, upperRightLongitude, maxResults,
2339 language, country, variant, appName, addrs);
2340 } else {
2341 return null;
2342 }
2343 }
2344 }
2345
2346 // Mock Providers
2347
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002348 private void checkMockPermissionsSafe() {
2349 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
2350 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
2351 if (!allowMocks) {
2352 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
2353 }
2354
2355 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
2356 PackageManager.PERMISSION_GRANTED) {
2357 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
2358 }
2359 }
2360
2361 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
2362 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
2363 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
2364 checkMockPermissionsSafe();
2365
2366 synchronized (mLocationListeners) {
2367 MockProvider provider = new MockProvider(name, requiresNetwork, requiresSatellite,
2368 requiresCell, hasMonetaryCost, supportsAltitude,
2369 supportsSpeed, supportsBearing, powerRequirement, accuracy);
2370 if (LocationProviderImpl.getProvider(name) != null) {
2371 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
2372 }
2373 LocationProviderImpl.addProvider(provider);
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002374 mMockProviders.put(name, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002375 updateProvidersLocked();
2376 }
2377 }
2378
2379 public void removeTestProvider(String provider) {
2380 checkMockPermissionsSafe();
2381 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002382 MockProvider mockProvider = mMockProviders.get(provider);
2383 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002384 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2385 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002386 LocationProviderImpl.removeProvider(mockProvider);
2387 mMockProviders.remove(mockProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002388 updateProvidersLocked();
2389 }
2390 }
2391
2392 public void setTestProviderLocation(String provider, Location loc) {
2393 checkMockPermissionsSafe();
2394 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002395 MockProvider mockProvider = mMockProviders.get(provider);
2396 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002397 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2398 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002399 mockProvider.setLocation(loc);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002400 }
2401 }
2402
2403 public void clearTestProviderLocation(String provider) {
2404 checkMockPermissionsSafe();
2405 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002406 MockProvider mockProvider = mMockProviders.get(provider);
2407 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002408 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2409 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002410 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002411 }
2412 }
2413
2414 public void setTestProviderEnabled(String provider, boolean enabled) {
2415 checkMockPermissionsSafe();
2416 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002417 MockProvider mockProvider = mMockProviders.get(provider);
2418 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002419 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2420 }
2421 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002422 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002423 mEnabledProviders.add(provider);
2424 mDisabledProviders.remove(provider);
2425 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002426 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002427 mEnabledProviders.remove(provider);
2428 mDisabledProviders.add(provider);
2429 }
2430 updateProvidersLocked();
2431 }
2432 }
2433
2434 public void clearTestProviderEnabled(String provider) {
2435 checkMockPermissionsSafe();
2436 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002437 MockProvider mockProvider = mMockProviders.get(provider);
2438 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002439 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2440 }
2441 mEnabledProviders.remove(provider);
2442 mDisabledProviders.remove(provider);
2443 updateProvidersLocked();
2444 }
2445 }
2446
2447 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
2448 checkMockPermissionsSafe();
2449 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002450 MockProvider mockProvider = mMockProviders.get(provider);
2451 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002452 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2453 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002454 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002455 }
2456 }
2457
2458 public void clearTestProviderStatus(String provider) {
2459 checkMockPermissionsSafe();
2460 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002461 MockProvider mockProvider = mMockProviders.get(provider);
2462 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002463 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2464 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002465 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002466 }
2467 }
2468
2469 private void log(String log) {
2470 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2471 Log.d(TAG, log);
2472 }
2473 }
2474
2475 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2476 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2477 != PackageManager.PERMISSION_GRANTED) {
2478 pw.println("Permission Denial: can't dump AlarmManager from from pid="
2479 + Binder.getCallingPid()
2480 + ", uid=" + Binder.getCallingUid());
2481 return;
2482 }
2483
2484 synchronized (mLocationListeners) {
2485 pw.println("Current Location Manager state:");
2486 pw.println(" sProvidersLoaded=" + sProvidersLoaded);
2487 pw.println(" mGpsLocationProvider=" + mGpsLocationProvider);
2488 pw.println(" mGpsNavigating=" + mGpsNavigating);
2489 pw.println(" mNetworkLocationProvider=" + mNetworkLocationProvider);
2490 pw.println(" mNetworkLocationInterface=" + mNetworkLocationInterface);
2491 pw.println(" mLastSignalStrength=" + mLastSignalStrength
2492 + " mLastRadioType=" + mLastRadioType);
2493 pw.println(" mCellLocationUpdater=" + mCellLocationUpdater);
2494 pw.println(" mLastCellState=" + mLastCellState);
2495 pw.println(" mCollector=" + mCollector);
2496 pw.println(" mAlarmInterval=" + mAlarmInterval
2497 + " mScreenOn=" + mScreenOn
2498 + " mWakeLockAcquireTime=" + mWakeLockAcquireTime);
2499 pw.println(" mWakeLockGpsReceived=" + mWakeLockGpsReceived
2500 + " mWakeLockNetworkReceived=" + mWakeLockNetworkReceived);
2501 pw.println(" mWifiWakeLockAcquired=" + mWifiWakeLockAcquired
2502 + " mCellWakeLockAcquired=" + mCellWakeLockAcquired);
2503 pw.println(" Listeners:");
2504 int N = mListeners.size();
2505 for (int i=0; i<N; i++) {
2506 pw.println(" " + mListeners.get(i));
2507 }
2508 pw.println(" Location Listeners:");
2509 for (Map.Entry<Receiver, HashMap<String,UpdateRecord>> i
2510 : mLocationListeners.entrySet()) {
2511 pw.println(" " + i.getKey() + ":");
2512 for (Map.Entry<String,UpdateRecord> j : i.getValue().entrySet()) {
2513 pw.println(" " + j.getKey() + ":");
2514 j.getValue().dump(pw, " ");
2515 }
2516 }
2517 pw.println(" Last Fix Broadcasts:");
2518 for (Map.Entry<Receiver, HashMap<String,Location>> i
2519 : mLastFixBroadcast.entrySet()) {
2520 pw.println(" " + i.getKey() + ":");
2521 for (Map.Entry<String,Location> j : i.getValue().entrySet()) {
2522 pw.println(" " + j.getKey() + ":");
2523 j.getValue().dump(new PrintWriterPrinter(pw), " ");
2524 }
2525 }
2526 pw.println(" Last Status Broadcasts:");
2527 for (Map.Entry<Receiver, HashMap<String,Long>> i
2528 : mLastStatusBroadcast.entrySet()) {
2529 pw.println(" " + i.getKey() + ":");
2530 for (Map.Entry<String,Long> j : i.getValue().entrySet()) {
2531 pw.println(" " + j.getKey() + " -> 0x"
2532 + Long.toHexString(j.getValue()));
2533 }
2534 }
2535 pw.println(" Records by Provider:");
2536 for (Map.Entry<String, ArrayList<UpdateRecord>> i
2537 : mRecordsByProvider.entrySet()) {
2538 pw.println(" " + i.getKey() + ":");
2539 for (UpdateRecord j : i.getValue()) {
2540 pw.println(" " + j + ":");
2541 j.dump(pw, " ");
2542 }
2543 }
2544 pw.println(" Locations by Provider:");
2545 for (Map.Entry<String, Location> i
2546 : mLocationsByProvider.entrySet()) {
2547 pw.println(" " + i.getKey() + ":");
2548 i.getValue().dump(new PrintWriterPrinter(pw), " ");
2549 }
2550 pw.println(" Last Known Locations:");
2551 for (Map.Entry<String, Location> i
2552 : mLastKnownLocation.entrySet()) {
2553 pw.println(" " + i.getKey() + ":");
2554 i.getValue().dump(new PrintWriterPrinter(pw), " ");
2555 }
2556 if (mProximityAlerts.size() > 0) {
2557 pw.println(" Proximity Alerts:");
2558 for (Map.Entry<PendingIntent, ProximityAlert> i
2559 : mProximityAlerts.entrySet()) {
2560 pw.println(" " + i.getKey() + ":");
2561 i.getValue().dump(pw, " ");
2562 }
2563 }
2564 if (mProximitiesEntered.size() > 0) {
2565 pw.println(" Proximities Entered:");
2566 for (ProximityAlert i : mProximitiesEntered) {
2567 pw.println(" " + i + ":");
2568 i.dump(pw, " ");
2569 }
2570 }
2571 pw.println(" mProximityListener=" + mProximityListener);
2572 if (mEnabledProviders.size() > 0) {
2573 pw.println(" Enabled Providers:");
2574 for (String i : mEnabledProviders) {
2575 pw.println(" " + i);
2576 }
2577
2578 }
2579 if (mDisabledProviders.size() > 0) {
2580 pw.println(" Disabled Providers:");
2581 for (String i : mDisabledProviders) {
2582 pw.println(" " + i);
2583 }
2584
2585 }
2586 if (mMockProviders.size() > 0) {
2587 pw.println(" Mock Providers:");
2588 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002589 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002590 }
2591 }
2592 pw.println(" Reported GPS UIDs @ seq " + mReportedGpsSeq + ":");
2593 N = mReportedGpsUids.size();
2594 for (int i=0; i<N; i++) {
2595 pw.println(" UID " + mReportedGpsUids.keyAt(i)
2596 + " seq=" + mReportedGpsUids.valueAt(i));
2597 }
2598 }
2599 }
2600}