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