blob: 75b59eef8005eaaf385d6b385cbf2c0e5e93c712 [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;
Mike Lockwooda55c3212009-04-15 11:10:11 -040047import android.location.IGeocodeProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.location.IGpsStatusListener;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070049import android.location.ILocationCollector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.location.ILocationListener;
51import android.location.ILocationManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070052import android.location.ILocationProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.location.Location;
54import android.location.LocationManager;
55import android.location.LocationProvider;
56import android.location.LocationProviderImpl;
57import android.net.ConnectivityManager;
58import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import android.net.wifi.WifiManager;
60import android.os.Binder;
61import android.os.Bundle;
62import android.os.Handler;
63import android.os.IBinder;
64import android.os.Message;
65import android.os.PowerManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070066import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import android.os.RemoteException;
68import android.os.SystemClock;
69import android.provider.Settings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070import android.telephony.TelephonyManager;
71import android.util.Config;
72import android.util.Log;
73import android.util.PrintWriterPrinter;
74import android.util.SparseIntArray;
75
76import com.android.internal.app.IBatteryStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077import com.android.internal.location.GpsLocationProvider;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070078import com.android.internal.location.LocationProviderProxy;
Mike Lockwood7ec434e2009-03-27 07:46:48 -070079import com.android.internal.location.MockProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080import com.android.internal.location.TrackProvider;
81import com.android.server.am.BatteryStatsService;
82
83/**
84 * The service class that manages LocationProviders and issues location
85 * updates and alerts.
86 *
87 * {@hide}
88 */
Mike Lockwoode932f7f2009-04-06 10:51:26 -070089public class LocationManagerService extends ILocationManager.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 private static final String TAG = "LocationManagerService";
The Android Open Source Project10592532009-03-18 17:39:46 -070091 private static final boolean LOCAL_LOGV = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092
93 // Minimum time interval between last known location writes, in milliseconds.
94 private static final long MIN_LAST_KNOWN_LOCATION_TIME = 60L * 1000L;
95
96 // Max time to hold wake lock for, in milliseconds.
97 private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L;
98
99 // Time to wait after releasing a wake lock for clients to process location update,
100 // in milliseconds.
101 private static final long TIME_AFTER_WAKE_LOCK = 2 * 1000L;
102
103 // The last time a location was written, by provider name.
104 private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
105
106 private static final Pattern PATTERN_COMMA = Pattern.compile(",");
107
108 private static final String ACCESS_FINE_LOCATION =
109 android.Manifest.permission.ACCESS_FINE_LOCATION;
110 private static final String ACCESS_COARSE_LOCATION =
111 android.Manifest.permission.ACCESS_COARSE_LOCATION;
112 private static final String ACCESS_MOCK_LOCATION =
113 android.Manifest.permission.ACCESS_MOCK_LOCATION;
114 private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
115 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
116
117 // Set of providers that are explicitly enabled
118 private final Set<String> mEnabledProviders = new HashSet<String>();
119
120 // Set of providers that are explicitly disabled
121 private final Set<String> mDisabledProviders = new HashSet<String>();
122
123 // Locations, status values, and extras for mock providers
Mike Lockwood7ec434e2009-03-27 07:46:48 -0700124 private final HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125
126 private static boolean sProvidersLoaded = false;
127
128 private final Context mContext;
129 private GpsLocationProvider mGpsLocationProvider;
130 private boolean mGpsNavigating;
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700131 private LocationProviderProxy mNetworkLocationProvider;
Mike Lockwooda55c3212009-04-15 11:10:11 -0400132 private IGeocodeProvider mGeocodeProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 private LocationWorkerHandler mLocationHandler;
134
135 // Handler messages
Mike Lockwood4e50b782009-04-03 08:24:43 -0700136 private static final int MESSAGE_LOCATION_CHANGED = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 private static final int MESSAGE_ACQUIRE_WAKE_LOCK = 2;
138 private static final int MESSAGE_RELEASE_WAKE_LOCK = 3;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139
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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 // Proximity listeners
191 private Receiver mProximityListener = null;
192 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
193 new HashMap<PendingIntent,ProximityAlert>();
194 private HashSet<ProximityAlert> mProximitiesEntered =
195 new HashSet<ProximityAlert>();
196
197 // Last known location for each provider
198 private HashMap<String,Location> mLastKnownLocation =
199 new HashMap<String,Location>();
200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 // Last known cell service state
202 private TelephonyManager mTelephonyManager;
203
204 // Location collector
205 private ILocationCollector mCollector;
206
207 // Wifi Manager
208 private WifiManager mWifiManager;
209
The Android Open Source Project4df24232009-03-05 14:34:35 -0800210 private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800211
Mike Lockwood9637d472009-04-02 21:41:57 -0700212 // for Settings change notification
213 private ContentQueryMap mSettings;
214
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 /**
216 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
217 * location updates.
218 */
219 private final class Receiver implements IBinder.DeathRecipient {
220 final ILocationListener mListener;
221 final PendingIntent mPendingIntent;
222 final int mUid;
223 final Object mKey;
224
225 Receiver(ILocationListener listener, int uid) {
226 mListener = listener;
227 mPendingIntent = null;
228 mUid = uid;
229 mKey = listener.asBinder();
230 }
231
232 Receiver(PendingIntent intent, int uid) {
233 mPendingIntent = intent;
234 mListener = null;
235 mUid = uid;
236 mKey = intent;
237 }
238
239 @Override
240 public boolean equals(Object otherObj) {
241 if (otherObj instanceof Receiver) {
242 return mKey.equals(
243 ((Receiver)otherObj).mKey);
244 }
245 return false;
246 }
247
248 @Override
249 public int hashCode() {
250 return mKey.hashCode();
251 }
252
253
254 @Override
255 public String toString() {
256 if (mListener != null) {
257 return "Receiver{"
258 + Integer.toHexString(System.identityHashCode(this))
259 + " uid " + mUid + " Listener " + mKey + "}";
260 } else {
261 return "Receiver{"
262 + Integer.toHexString(System.identityHashCode(this))
263 + " uid " + mUid + " Intent " + mKey + "}";
264 }
265 }
266
267 public boolean isListener() {
268 return mListener != null;
269 }
270
271 public boolean isPendingIntent() {
272 return mPendingIntent != null;
273 }
274
275 public ILocationListener getListener() {
276 if (mListener != null) {
277 return mListener;
278 }
279 throw new IllegalStateException("Request for non-existent listener");
280 }
281
282 public PendingIntent getPendingIntent() {
283 if (mPendingIntent != null) {
284 return mPendingIntent;
285 }
286 throw new IllegalStateException("Request for non-existent intent");
287 }
288
289 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
290 if (mListener != null) {
291 try {
292 mListener.onStatusChanged(provider, status, extras);
293 } catch (RemoteException e) {
294 return false;
295 }
296 } else {
297 Intent statusChanged = new Intent();
298 statusChanged.putExtras(extras);
299 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
300 try {
301 mPendingIntent.send(mContext, 0, statusChanged, null, null);
302 } catch (PendingIntent.CanceledException e) {
303 return false;
304 }
305 }
306 return true;
307 }
308
309 public boolean callLocationChangedLocked(Location location) {
310 if (mListener != null) {
311 try {
312 mListener.onLocationChanged(location);
313 } catch (RemoteException e) {
314 return false;
315 }
316 } else {
317 Intent locationChanged = new Intent();
318 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
319 try {
320 mPendingIntent.send(mContext, 0, locationChanged, null, null);
321 } catch (PendingIntent.CanceledException e) {
322 return false;
323 }
324 }
325 return true;
326 }
327
328 public void binderDied() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700329 if (LOCAL_LOGV) {
330 Log.v(TAG, "Location listener died");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 }
332 synchronized (mLocationListeners) {
333 removeUpdatesLocked(this);
334 }
335 }
336 }
337
Mike Lockwood9637d472009-04-02 21:41:57 -0700338 private final class SettingsObserver implements Observer {
339 public void update(Observable o, Object arg) {
340 synchronized (mLocationListeners) {
341 updateProvidersLocked();
342 }
343 }
344 }
345
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 private Location readLastKnownLocationLocked(String provider) {
347 Location location = null;
348 String s = null;
349 try {
350 File f = new File(LocationManager.SYSTEM_DIR + "/location."
351 + provider);
352 if (!f.exists()) {
353 return null;
354 }
355 BufferedReader reader = new BufferedReader(new FileReader(f), 256);
356 s = reader.readLine();
357 } catch (IOException e) {
358 Log.w(TAG, "Unable to read last known location", e);
359 }
360
361 if (s == null) {
362 return null;
363 }
364 try {
365 String[] tokens = PATTERN_COMMA.split(s);
366 int idx = 0;
367 long time = Long.parseLong(tokens[idx++]);
368 double latitude = Double.parseDouble(tokens[idx++]);
369 double longitude = Double.parseDouble(tokens[idx++]);
370 double altitude = Double.parseDouble(tokens[idx++]);
371 float bearing = Float.parseFloat(tokens[idx++]);
372 float speed = Float.parseFloat(tokens[idx++]);
373
374 location = new Location(provider);
375 location.setTime(time);
376 location.setLatitude(latitude);
377 location.setLongitude(longitude);
378 location.setAltitude(altitude);
379 location.setBearing(bearing);
380 location.setSpeed(speed);
381 } catch (NumberFormatException nfe) {
382 Log.e(TAG, "NumberFormatException reading last known location", nfe);
383 return null;
384 }
385
386 return location;
387 }
388
389 private void writeLastKnownLocationLocked(String provider,
390 Location location) {
391 long now = SystemClock.elapsedRealtime();
392 Long last = mLastWriteTime.get(provider);
393 if ((last != null)
394 && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
395 return;
396 }
397 mLastWriteTime.put(provider, now);
398
399 StringBuilder sb = new StringBuilder(100);
400 sb.append(location.getTime());
401 sb.append(',');
402 sb.append(location.getLatitude());
403 sb.append(',');
404 sb.append(location.getLongitude());
405 sb.append(',');
406 sb.append(location.getAltitude());
407 sb.append(',');
408 sb.append(location.getBearing());
409 sb.append(',');
410 sb.append(location.getSpeed());
411
412 FileWriter writer = null;
413 try {
414 File d = new File(LocationManager.SYSTEM_DIR);
415 if (!d.exists()) {
416 if (!d.mkdirs()) {
417 Log.w(TAG, "Unable to create directory to write location");
418 return;
419 }
420 }
421 File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
422 writer = new FileWriter(f);
423 writer.write(sb.toString());
424 } catch (IOException e) {
425 Log.w(TAG, "Unable to write location", e);
426 } finally {
427 if (writer != null) {
428 try {
429 writer.close();
430 } catch (IOException e) {
431 Log.w(TAG, "Exception closing file", e);
432 }
433 }
434 }
435 }
436
437 /**
438 * Load providers from /data/location/<provider_name>/
439 * class
440 * kml
441 * nmea
442 * track
443 * location
444 * properties
445 */
446 private void loadProviders() {
447 synchronized (mLocationListeners) {
448 if (sProvidersLoaded) {
449 return;
450 }
451
452 // Load providers
453 loadProvidersLocked();
454 sProvidersLoaded = true;
455 }
456 }
457
458 private void loadProvidersLocked() {
459 try {
460 _loadProvidersLocked();
461 } catch (Exception e) {
462 Log.e(TAG, "Exception loading providers:", e);
463 }
464 }
465
466 private void _loadProvidersLocked() {
467 // Attempt to load "real" providers first
468 if (GpsLocationProvider.isSupported()) {
469 // Create a gps location provider
Mike Lockwood4e50b782009-04-03 08:24:43 -0700470 mGpsLocationProvider = new GpsLocationProvider(mContext, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 LocationProviderImpl.addProvider(mGpsLocationProvider);
472 }
473
474 // Load fake providers if real providers are not available
475 File f = new File(LocationManager.PROVIDER_DIR);
476 if (f.isDirectory()) {
477 File[] subdirs = f.listFiles();
478 for (int i = 0; i < subdirs.length; i++) {
479 if (!subdirs[i].isDirectory()) {
480 continue;
481 }
482
483 String name = subdirs[i].getName();
484
The Android Open Source Project10592532009-03-18 17:39:46 -0700485 if (LOCAL_LOGV) {
486 Log.v(TAG, "Found dir " + subdirs[i].getAbsolutePath());
487 Log.v(TAG, "name = " + name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 }
489
490 // Don't create a fake provider if a real provider exists
491 if (LocationProviderImpl.getProvider(name) == null) {
492 LocationProviderImpl provider = null;
493 try {
494 File classFile = new File(subdirs[i], "class");
495 // Look for a 'class' file
496 provider = LocationProviderImpl.loadFromClass(classFile);
497
498 // Look for an 'kml', 'nmea', or 'track' file
499 if (provider == null) {
500 // Load properties from 'properties' file, if present
501 File propertiesFile = new File(subdirs[i], "properties");
502
503 if (propertiesFile.exists()) {
Mike Lockwood4e50b782009-04-03 08:24:43 -0700504 provider = new TrackProvider(name, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 ((TrackProvider)provider).readProperties(propertiesFile);
506
507 File kmlFile = new File(subdirs[i], "kml");
508 if (kmlFile.exists()) {
509 ((TrackProvider) provider).readKml(kmlFile);
510 } else {
511 File nmeaFile = new File(subdirs[i], "nmea");
512 if (nmeaFile.exists()) {
513 ((TrackProvider) provider).readNmea(name, nmeaFile);
514 } else {
515 File trackFile = new File(subdirs[i], "track");
516 if (trackFile.exists()) {
517 ((TrackProvider) provider).readTrack(trackFile);
518 }
519 }
520 }
521 }
522 }
523 if (provider != null) {
524 LocationProviderImpl.addProvider(provider);
525 }
526 // Grab the initial location of a TrackProvider and
527 // store it as the last known location for that provider
528 if (provider instanceof TrackProvider) {
529 TrackProvider tp = (TrackProvider) provider;
530 mLastKnownLocation.put(tp.getName(), tp.getInitialLocation());
531 }
532 } catch (Exception e) {
533 Log.e(TAG, "Exception loading provder " + name, e);
534 }
535 }
536 }
537 }
538
539 updateProvidersLocked();
540 }
541
542 /**
543 * @param context the context that the LocationManagerService runs in
544 */
545 public LocationManagerService(Context context) {
546 super();
547 mContext = context;
548 mLocationHandler = new LocationWorkerHandler();
549
The Android Open Source Project10592532009-03-18 17:39:46 -0700550 if (LOCAL_LOGV) {
551 Log.v(TAG, "Constructed LocationManager Service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 }
553
554 // Alarm manager, needs to be done before calling loadProviders() below
555 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
556
557 // Create a wake lock, needs to be done before calling loadProviders() below
558 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
559 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
560
561 // Battery statistics service to be notified when GPS turns on or off
562 mBatteryStats = BatteryStatsService.getService();
563
564 // Load providers
565 loadProviders();
566
567 // Listen for Radio changes
568 mTelephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569
570 // Register for Network (Wifi or Mobile) updates
571 NetworkStateBroadcastReceiver networkReceiver = new NetworkStateBroadcastReceiver();
572 IntentFilter networkIntentFilter = new IntentFilter();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
574 networkIntentFilter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
575 context.registerReceiver(networkReceiver, networkIntentFilter);
576
577 // Register for power updates
578 PowerStateBroadcastReceiver powerStateReceiver = new PowerStateBroadcastReceiver();
579 IntentFilter intentFilter = new IntentFilter();
580 intentFilter.addAction(ALARM_INTENT);
581 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
582 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800583 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
584 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
585 context.registerReceiver(powerStateReceiver, intentFilter);
586
Mike Lockwood9637d472009-04-02 21:41:57 -0700587 // listen for settings changes
588 ContentResolver resolver = mContext.getContentResolver();
589 Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
590 "(" + Settings.System.NAME + "=?)",
591 new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
592 null);
593 mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler);
594 SettingsObserver settingsObserver = new SettingsObserver();
595 mSettings.addObserver(settingsObserver);
596
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 // Get the wifi manager
598 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
599
600 // Create a wifi lock for future use
601 mWifiLock = getWifiWakelockLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 }
603
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700604 public void setNetworkLocationProvider(ILocationProvider provider) {
605 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
606 throw new SecurityException(
607 "Installing location providers outside of the system is not supported");
608 }
609
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 synchronized (mLocationListeners) {
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700611 mNetworkLocationProvider =
612 new LocationProviderProxy(LocationManager.NETWORK_PROVIDER, this, provider);
613 mNetworkLocationProvider.addListener(getPackageNames());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 LocationProviderImpl.addProvider(mNetworkLocationProvider);
615 updateProvidersLocked();
The Android Open Source Project4df24232009-03-05 14:34:35 -0800616
617 // notify NetworkLocationProvider of any events it might have missed
Mike Lockwoodf113fbe2009-04-06 05:17:28 -0700618 mNetworkLocationProvider.updateNetworkState(mNetworkState);
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700619 mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 }
621 }
622
623 public void setLocationCollector(ILocationCollector collector) {
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700624 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
625 throw new SecurityException(
626 "Installing location collectors outside of the system is not supported");
627 }
628
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 synchronized (mLocationListeners) {
630 mCollector = collector;
631 if (mGpsLocationProvider != null) {
632 mGpsLocationProvider.setLocationCollector(mCollector);
633 }
634 }
635 }
636
Mike Lockwooda55c3212009-04-15 11:10:11 -0400637 public void setGeocodeProvider(IGeocodeProvider provider) {
638 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
639 throw new SecurityException(
640 "Installing location providers outside of the system is not supported");
641 }
642
643 mGeocodeProvider = provider;
644 }
645
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646 private WifiManager.WifiLock getWifiWakelockLocked() {
647 if (mWifiLock == null && mWifiManager != null) {
648 mWifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, WIFILOCK_KEY);
649 mWifiLock.setReferenceCounted(false);
650 }
651 return mWifiLock;
652 }
653
654 private boolean isAllowedBySettingsLocked(String provider) {
655 if (mEnabledProviders.contains(provider)) {
656 return true;
657 }
658 if (mDisabledProviders.contains(provider)) {
659 return false;
660 }
661 // Use system settings
662 ContentResolver resolver = mContext.getContentResolver();
663 String allowedProviders = Settings.Secure.getString(resolver,
664 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
665
666 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
667 }
668
669 private void checkPermissionsSafe(String provider) {
670 if (LocationManager.GPS_PROVIDER.equals(provider)
671 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
672 != PackageManager.PERMISSION_GRANTED)) {
673 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
674 }
675 if (LocationManager.NETWORK_PROVIDER.equals(provider)
676 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
677 != PackageManager.PERMISSION_GRANTED)
678 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
679 != PackageManager.PERMISSION_GRANTED)) {
680 throw new SecurityException(
681 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
682 }
683 }
684
685 private boolean isAllowedProviderSafe(String provider) {
686 if (LocationManager.GPS_PROVIDER.equals(provider)
687 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
688 != PackageManager.PERMISSION_GRANTED)) {
689 return false;
690 }
691 if (LocationManager.NETWORK_PROVIDER.equals(provider)
692 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
693 != PackageManager.PERMISSION_GRANTED)
694 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
695 != PackageManager.PERMISSION_GRANTED)) {
696 return false;
697 }
698
699 return true;
700 }
701
702 private String[] getPackageNames() {
703 // Since a single UID may correspond to multiple packages, this can only be used as an
704 // approximation for tracking
705 return mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
706 }
707
708 public List<String> getAllProviders() {
709 try {
710 synchronized (mLocationListeners) {
711 return _getAllProvidersLocked();
712 }
713 } catch (SecurityException se) {
714 throw se;
715 } catch (Exception e) {
716 Log.e(TAG, "getAllProviders got exception:", e);
717 return null;
718 }
719 }
720
721 private List<String> _getAllProvidersLocked() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700722 if (LOCAL_LOGV) {
723 Log.v(TAG, "getAllProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800724 }
725 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
726 ArrayList<String> out = new ArrayList<String>(providers.size());
727
728 for (LocationProviderImpl p : providers) {
729 out.add(p.getName());
730 }
731 return out;
732 }
733
734 public List<String> getProviders(boolean enabledOnly) {
735 try {
736 synchronized (mLocationListeners) {
737 return _getProvidersLocked(enabledOnly);
738 }
739 } catch (SecurityException se) {
740 throw se;
741 } catch (Exception e) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700742 Log.e(TAG, "getProviders got exception:", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800743 return null;
744 }
745 }
746
747 private List<String> _getProvidersLocked(boolean enabledOnly) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700748 if (LOCAL_LOGV) {
749 Log.v(TAG, "getProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 }
751 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
752 ArrayList<String> out = new ArrayList<String>();
753
754 for (LocationProviderImpl p : providers) {
755 String name = p.getName();
756 if (isAllowedProviderSafe(name)) {
757 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
758 continue;
759 }
760 out.add(name);
761 }
762 }
763 return out;
764 }
765
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766 private void updateProvidersLocked() {
767 for (LocationProviderImpl p : LocationProviderImpl.getProviders()) {
768 boolean isEnabled = p.isEnabled();
769 String name = p.getName();
770 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
771
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 if (isEnabled && !shouldBeEnabled) {
773 updateProviderListenersLocked(name, false);
774 } else if (!isEnabled && shouldBeEnabled) {
775 updateProviderListenersLocked(name, true);
776 }
777
778 }
779 }
780
781 private void updateProviderListenersLocked(String provider, boolean enabled) {
782 int listeners = 0;
783
784 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
785 if (p == null) {
786 return;
787 }
788
789 ArrayList<Receiver> deadReceivers = null;
790
791 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
792 if (records != null) {
793 final int N = records.size();
794 for (int i=0; i<N; i++) {
795 UpdateRecord record = records.get(i);
796 // Sends a notification message to the receiver
797 try {
798 Receiver receiver = record.mReceiver;
799 if (receiver.isListener()) {
800 if (enabled) {
801 receiver.getListener().onProviderEnabled(provider);
802 } else {
803 receiver.getListener().onProviderDisabled(provider);
804 }
805 } else {
806 Intent providerIntent = new Intent();
807 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
808 try {
809 receiver.getPendingIntent().send(mContext, 0,
810 providerIntent, null, null);
811 } catch (PendingIntent.CanceledException e) {
812 if (deadReceivers == null) {
813 deadReceivers = new ArrayList<Receiver>();
814 deadReceivers.add(receiver);
815 }
816 }
817 }
818 } catch (RemoteException e) {
819 // The death link will clean this up.
820 }
821 listeners++;
822 }
823 }
824
825 if (deadReceivers != null) {
826 for (int i=deadReceivers.size()-1; i>=0; i--) {
827 removeUpdatesLocked(deadReceivers.get(i));
828 }
829 }
830
831 if (enabled) {
832 p.enable();
833 if (listeners > 0) {
834 p.setMinTime(getMinTimeLocked(provider));
835 p.enableLocationTracking(true);
836 updateWakelockStatusLocked(mScreenOn);
837 }
838 } else {
839 p.enableLocationTracking(false);
840 if (p == mGpsLocationProvider) {
841 mGpsNavigating = false;
842 reportStopGpsLocked();
843 }
844 p.disable();
845 updateWakelockStatusLocked(mScreenOn);
846 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 }
848
849 private long getMinTimeLocked(String provider) {
850 long minTime = Long.MAX_VALUE;
851 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
852 if (records != null) {
853 for (int i=records.size()-1; i>=0; i--) {
854 minTime = Math.min(minTime, records.get(i).mMinTime);
855 }
856 }
857 return minTime;
858 }
859
860 private class UpdateRecord {
861 final String mProvider;
862 final Receiver mReceiver;
863 final long mMinTime;
864 final float mMinDistance;
865 final int mUid;
866 final String[] mPackages;
867
868 /**
869 * Note: must be constructed with lock held.
870 */
871 UpdateRecord(String provider, long minTime, float minDistance,
872 Receiver receiver, int uid, String[] packages) {
873 mProvider = provider;
874 mReceiver = receiver;
875 mMinTime = minTime;
876 mMinDistance = minDistance;
877 mUid = uid;
878 mPackages = packages;
879
880 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
881 if (records == null) {
882 records = new ArrayList<UpdateRecord>();
883 mRecordsByProvider.put(provider, records);
884 }
885 if (!records.contains(this)) {
886 records.add(this);
887 }
888 }
889
890 /**
891 * Method to be called when a record will no longer be used. Calling this multiple times
892 * must have the same effect as calling it once.
893 */
894 void disposeLocked() {
895 ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
896 records.remove(this);
897 }
898
899 @Override
900 public String toString() {
901 return "UpdateRecord{"
902 + Integer.toHexString(System.identityHashCode(this))
903 + " " + mProvider + " " + mReceiver + "}";
904 }
905
906 void dump(PrintWriter pw, String prefix) {
907 pw.println(prefix + this);
908 pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
909 pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
910 StringBuilder sb = new StringBuilder();
911 if (mPackages != null) {
912 for (int i=0; i<mPackages.length; i++) {
913 if (i > 0) sb.append(", ");
914 sb.append(mPackages[i]);
915 }
916 }
917 pw.println(prefix + "mUid=" + mUid + " mPackages=" + sb);
918 }
919
920 /**
921 * Calls dispose().
922 */
923 @Override protected void finalize() {
924 synchronized (mLocationListeners) {
925 disposeLocked();
926 }
927 }
928 }
929
930 public void requestLocationUpdates(String provider,
931 long minTime, float minDistance, ILocationListener listener) {
932
933 try {
934 synchronized (mLocationListeners) {
935 requestLocationUpdatesLocked(provider, minTime, minDistance,
936 new Receiver(listener, Binder.getCallingUid()));
937 }
938 } catch (SecurityException se) {
939 throw se;
940 } catch (Exception e) {
941 Log.e(TAG, "requestUpdates got exception:", e);
942 }
943 }
944
945 public void requestLocationUpdatesPI(String provider,
946 long minTime, float minDistance, PendingIntent intent) {
947 try {
948 synchronized (mLocationListeners) {
949 requestLocationUpdatesLocked(provider, minTime, minDistance,
950 new Receiver(intent, Binder.getCallingUid()));
951 }
952 } catch (SecurityException se) {
953 throw se;
954 } catch (Exception e) {
955 Log.e(TAG, "requestUpdates got exception:", e);
956 }
957 }
958
959 private void requestLocationUpdatesLocked(String provider,
960 long minTime, float minDistance, Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700961 if (LOCAL_LOGV) {
962 Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963 }
964
965 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
966 if (impl == null) {
967 throw new IllegalArgumentException("provider=" + provider);
968 }
969
970 checkPermissionsSafe(provider);
971
972 String[] packages = getPackageNames();
973
974 // so wakelock calls will succeed
975 final int callingUid = Binder.getCallingUid();
976 long identity = Binder.clearCallingIdentity();
977 try {
978 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance,
979 receiver, callingUid, packages);
980 if (!mListeners.contains(receiver)) {
981 try {
982 if (receiver.isListener()) {
983 receiver.getListener().asBinder().linkToDeath(receiver, 0);
984 }
985 mListeners.add(receiver);
986 } catch (RemoteException e) {
987 return;
988 }
989 }
990
991 HashMap<String,UpdateRecord> records = mLocationListeners.get(receiver);
992 if (records == null) {
993 records = new HashMap<String,UpdateRecord>();
994 mLocationListeners.put(receiver, records);
995 }
996 UpdateRecord oldRecord = records.put(provider, r);
997 if (oldRecord != null) {
998 oldRecord.disposeLocked();
999 }
1000
1001 boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
1002 if (isProviderEnabled) {
1003 long minTimeForProvider = getMinTimeLocked(provider);
1004 impl.setMinTime(minTimeForProvider);
1005 impl.enableLocationTracking(true);
1006 updateWakelockStatusLocked(mScreenOn);
1007
1008 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1009 if (mGpsNavigating) {
1010 updateReportedGpsLocked();
1011 }
1012 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 } else {
1014 try {
1015 // Notify the listener that updates are currently disabled
1016 if (receiver.isListener()) {
1017 receiver.getListener().onProviderDisabled(provider);
1018 }
1019 } catch(RemoteException e) {
1020 Log.w(TAG, "RemoteException calling onProviderDisabled on " +
1021 receiver.getListener());
1022 }
1023 }
1024 } finally {
1025 Binder.restoreCallingIdentity(identity);
1026 }
1027 }
1028
1029 public void removeUpdates(ILocationListener listener) {
1030 try {
1031 synchronized (mLocationListeners) {
1032 removeUpdatesLocked(new Receiver(listener, Binder.getCallingUid()));
1033 }
1034 } catch (SecurityException se) {
1035 throw se;
1036 } catch (Exception e) {
1037 Log.e(TAG, "removeUpdates got exception:", e);
1038 }
1039 }
1040
1041 public void removeUpdatesPI(PendingIntent intent) {
1042 try {
1043 synchronized (mLocationListeners) {
1044 removeUpdatesLocked(new Receiver(intent, Binder.getCallingUid()));
1045 }
1046 } catch (SecurityException se) {
1047 throw se;
1048 } catch (Exception e) {
1049 Log.e(TAG, "removeUpdates got exception:", e);
1050 }
1051 }
1052
1053 private void removeUpdatesLocked(Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001054 if (LOCAL_LOGV) {
1055 Log.v(TAG, "_removeUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 }
1057
1058 // so wakelock calls will succeed
1059 final int callingUid = Binder.getCallingUid();
1060 long identity = Binder.clearCallingIdentity();
1061 try {
1062 int idx = mListeners.indexOf(receiver);
1063 if (idx >= 0) {
1064 Receiver myReceiver = mListeners.remove(idx);
1065 if (myReceiver.isListener()) {
1066 myReceiver.getListener().asBinder().unlinkToDeath(myReceiver, 0);
1067 }
1068 }
1069
1070 // Record which providers were associated with this listener
1071 HashSet<String> providers = new HashSet<String>();
1072 HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(receiver);
1073 if (oldRecords != null) {
1074 // Call dispose() on the obsolete update records.
1075 for (UpdateRecord record : oldRecords.values()) {
1076 if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) {
Mike Lockwoode932f7f2009-04-06 10:51:26 -07001077 if (mNetworkLocationProvider != null) {
1078 mNetworkLocationProvider.removeListener(record.mPackages);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001079 }
1080 }
1081 record.disposeLocked();
1082 }
1083 // Accumulate providers
1084 providers.addAll(oldRecords.keySet());
1085 }
1086
1087 mLocationListeners.remove(receiver);
1088 mLastFixBroadcast.remove(receiver);
1089 mLastStatusBroadcast.remove(receiver);
1090
1091 // See if the providers associated with this listener have any
1092 // other listeners; if one does, inform it of the new smallest minTime
1093 // value; if one does not, disable location tracking for it
1094 for (String provider : providers) {
1095 // If provider is already disabled, don't need to do anything
1096 if (!isAllowedBySettingsLocked(provider)) {
1097 continue;
1098 }
1099
1100 boolean hasOtherListener = false;
1101 ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1102 if (recordsForProvider != null && recordsForProvider.size() > 0) {
1103 hasOtherListener = true;
1104 }
1105
1106 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1107 if (p != null) {
1108 if (hasOtherListener) {
1109 p.setMinTime(getMinTimeLocked(provider));
1110 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001111 p.enableLocationTracking(false);
1112 }
1113
1114 if (p == mGpsLocationProvider && mGpsNavigating) {
1115 updateReportedGpsLocked();
1116 }
1117 }
1118 }
1119
1120 updateWakelockStatusLocked(mScreenOn);
1121 } finally {
1122 Binder.restoreCallingIdentity(identity);
1123 }
1124 }
1125
1126 public boolean addGpsStatusListener(IGpsStatusListener listener) {
1127 if (mGpsLocationProvider == null) {
1128 return false;
1129 }
1130 if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1131 PackageManager.PERMISSION_GRANTED) {
1132 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1133 }
1134
1135 try {
1136 mGpsLocationProvider.addGpsStatusListener(listener);
1137 } catch (RemoteException e) {
1138 Log.w(TAG, "RemoteException in addGpsStatusListener");
1139 return false;
1140 }
1141 return true;
1142 }
1143
1144 public void removeGpsStatusListener(IGpsStatusListener listener) {
1145 synchronized (mLocationListeners) {
1146 mGpsLocationProvider.removeGpsStatusListener(listener);
1147 }
1148 }
1149
1150 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1151 // first check for permission to the provider
1152 checkPermissionsSafe(provider);
1153 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1154 if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1155 != PackageManager.PERMISSION_GRANTED)) {
1156 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1157 }
1158
1159 synchronized (mLocationListeners) {
1160 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
1161 if (provider == null) {
1162 return false;
1163 }
1164
1165 return impl.sendExtraCommand(command, extras);
1166 }
1167 }
1168
1169 class ProximityAlert {
1170 final int mUid;
1171 final double mLatitude;
1172 final double mLongitude;
1173 final float mRadius;
1174 final long mExpiration;
1175 final PendingIntent mIntent;
1176 final Location mLocation;
1177
1178 public ProximityAlert(int uid, double latitude, double longitude,
1179 float radius, long expiration, PendingIntent intent) {
1180 mUid = uid;
1181 mLatitude = latitude;
1182 mLongitude = longitude;
1183 mRadius = radius;
1184 mExpiration = expiration;
1185 mIntent = intent;
1186
1187 mLocation = new Location("");
1188 mLocation.setLatitude(latitude);
1189 mLocation.setLongitude(longitude);
1190 }
1191
1192 long getExpiration() {
1193 return mExpiration;
1194 }
1195
1196 PendingIntent getIntent() {
1197 return mIntent;
1198 }
1199
1200 boolean isInProximity(double latitude, double longitude) {
1201 Location loc = new Location("");
1202 loc.setLatitude(latitude);
1203 loc.setLongitude(longitude);
1204
1205 double radius = loc.distanceTo(mLocation);
1206 return radius <= mRadius;
1207 }
1208
1209 @Override
1210 public String toString() {
1211 return "ProximityAlert{"
1212 + Integer.toHexString(System.identityHashCode(this))
1213 + " uid " + mUid + mIntent + "}";
1214 }
1215
1216 void dump(PrintWriter pw, String prefix) {
1217 pw.println(prefix + this);
1218 pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
1219 pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
1220 pw.println(prefix + "mIntent=" + mIntent);
1221 pw.println(prefix + "mLocation:");
1222 mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
1223 }
1224 }
1225
1226 // Listener for receiving locations to trigger proximity alerts
1227 class ProximityListener extends ILocationListener.Stub {
1228
1229 boolean isGpsAvailable = false;
1230
1231 // Note: this is called with the lock held.
1232 public void onLocationChanged(Location loc) {
1233
1234 // If Gps is available, then ignore updates from NetworkLocationProvider
1235 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1236 isGpsAvailable = true;
1237 }
1238 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1239 return;
1240 }
1241
1242 // Process proximity alerts
1243 long now = System.currentTimeMillis();
1244 double latitude = loc.getLatitude();
1245 double longitude = loc.getLongitude();
1246 ArrayList<PendingIntent> intentsToRemove = null;
1247
1248 for (ProximityAlert alert : mProximityAlerts.values()) {
1249 PendingIntent intent = alert.getIntent();
1250 long expiration = alert.getExpiration();
1251
1252 if ((expiration == -1) || (now <= expiration)) {
1253 boolean entered = mProximitiesEntered.contains(alert);
1254 boolean inProximity =
1255 alert.isInProximity(latitude, longitude);
1256 if (!entered && inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001257 if (LOCAL_LOGV) {
1258 Log.v(TAG, "Entered alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001259 }
1260 mProximitiesEntered.add(alert);
1261 Intent enteredIntent = new Intent();
1262 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1263 try {
1264 intent.send(mContext, 0, enteredIntent, null, null);
1265 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001266 if (LOCAL_LOGV) {
1267 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001268 }
1269 if (intentsToRemove == null) {
1270 intentsToRemove = new ArrayList<PendingIntent>();
1271 }
1272 intentsToRemove.add(intent);
1273 }
1274 } else if (entered && !inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001275 if (LOCAL_LOGV) {
1276 Log.v(TAG, "Exited alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001277 }
1278 mProximitiesEntered.remove(alert);
1279 Intent exitedIntent = new Intent();
1280 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1281 try {
1282 intent.send(mContext, 0, exitedIntent, null, null);
1283 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001284 if (LOCAL_LOGV) {
1285 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 }
1287 if (intentsToRemove == null) {
1288 intentsToRemove = new ArrayList<PendingIntent>();
1289 }
1290 intentsToRemove.add(intent);
1291 }
1292 }
1293 } else {
1294 // Mark alert for expiration
The Android Open Source Project10592532009-03-18 17:39:46 -07001295 if (LOCAL_LOGV) {
1296 Log.v(TAG, "Expiring proximity alert: " + alert);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001297 }
1298 if (intentsToRemove == null) {
1299 intentsToRemove = new ArrayList<PendingIntent>();
1300 }
1301 intentsToRemove.add(alert.getIntent());
1302 }
1303 }
1304
1305 // Remove expired alerts
1306 if (intentsToRemove != null) {
1307 for (PendingIntent i : intentsToRemove) {
1308 mProximityAlerts.remove(i);
1309 ProximityAlert alert = mProximityAlerts.get(i);
1310 mProximitiesEntered.remove(alert);
1311 }
1312 }
1313
1314 }
1315
1316 // Note: this is called with the lock held.
1317 public void onProviderDisabled(String provider) {
1318 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1319 isGpsAvailable = false;
1320 }
1321 }
1322
1323 // Note: this is called with the lock held.
1324 public void onProviderEnabled(String provider) {
1325 // ignore
1326 }
1327
1328 // Note: this is called with the lock held.
1329 public void onStatusChanged(String provider, int status, Bundle extras) {
1330 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1331 (status != LocationProvider.AVAILABLE)) {
1332 isGpsAvailable = false;
1333 }
1334 }
1335 }
1336
1337 public void addProximityAlert(double latitude, double longitude,
1338 float radius, long expiration, PendingIntent intent) {
1339 try {
1340 synchronized (mLocationListeners) {
1341 addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
1342 }
1343 } catch (SecurityException se) {
1344 throw se;
1345 } catch (Exception e) {
1346 Log.e(TAG, "addProximityAlert got exception:", e);
1347 }
1348 }
1349
1350 private void addProximityAlertLocked(double latitude, double longitude,
1351 float radius, long expiration, PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001352 if (LOCAL_LOGV) {
1353 Log.v(TAG, "addProximityAlert: latitude = " + latitude +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001354 ", longitude = " + longitude +
1355 ", expiration = " + expiration +
1356 ", intent = " + intent);
1357 }
1358
1359 // Require ability to access all providers for now
1360 if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
1361 !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
1362 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1363 }
1364
1365 if (expiration != -1) {
1366 expiration += System.currentTimeMillis();
1367 }
1368 ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
1369 latitude, longitude, radius, expiration, intent);
1370 mProximityAlerts.put(intent, alert);
1371
1372 if (mProximityListener == null) {
1373 mProximityListener = new Receiver(new ProximityListener(), -1);
1374
1375 LocationProvider provider = LocationProviderImpl.getProvider(
1376 LocationManager.GPS_PROVIDER);
1377 if (provider != null) {
1378 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1379 }
1380
1381 provider =
1382 LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER);
1383 if (provider != null) {
1384 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1385 }
1386 } else if (mGpsNavigating) {
1387 updateReportedGpsLocked();
1388 }
1389 }
1390
1391 public void removeProximityAlert(PendingIntent intent) {
1392 try {
1393 synchronized (mLocationListeners) {
1394 removeProximityAlertLocked(intent);
1395 }
1396 } catch (SecurityException se) {
1397 throw se;
1398 } catch (Exception e) {
1399 Log.e(TAG, "removeProximityAlert got exception:", e);
1400 }
1401 }
1402
1403 private void removeProximityAlertLocked(PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001404 if (LOCAL_LOGV) {
1405 Log.v(TAG, "removeProximityAlert: intent = " + intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001406 }
1407
1408 mProximityAlerts.remove(intent);
1409 if (mProximityAlerts.size() == 0) {
1410 removeUpdatesLocked(mProximityListener);
1411 mProximityListener = null;
1412 } else if (mGpsNavigating) {
1413 updateReportedGpsLocked();
1414 }
1415 }
1416
1417 /**
1418 * @return null if the provider does not exits
1419 * @throw SecurityException if the provider is not allowed to be
1420 * accessed by the caller
1421 */
1422 public Bundle getProviderInfo(String provider) {
1423 try {
1424 synchronized (mLocationListeners) {
1425 return _getProviderInfoLocked(provider);
1426 }
1427 } catch (SecurityException se) {
1428 throw se;
1429 } catch (Exception e) {
1430 Log.e(TAG, "_getProviderInfo got exception:", e);
1431 return null;
1432 }
1433 }
1434
1435 private Bundle _getProviderInfoLocked(String provider) {
1436 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1437 if (p == null) {
1438 return null;
1439 }
1440
1441 checkPermissionsSafe(provider);
1442
1443 Bundle b = new Bundle();
1444 b.putBoolean("network", p.requiresNetwork());
1445 b.putBoolean("satellite", p.requiresSatellite());
1446 b.putBoolean("cell", p.requiresCell());
1447 b.putBoolean("cost", p.hasMonetaryCost());
1448 b.putBoolean("altitude", p.supportsAltitude());
1449 b.putBoolean("speed", p.supportsSpeed());
1450 b.putBoolean("bearing", p.supportsBearing());
1451 b.putInt("power", p.getPowerRequirement());
1452 b.putInt("accuracy", p.getAccuracy());
1453
1454 return b;
1455 }
1456
1457 public boolean isProviderEnabled(String provider) {
1458 try {
1459 synchronized (mLocationListeners) {
1460 return _isProviderEnabledLocked(provider);
1461 }
1462 } catch (SecurityException se) {
1463 throw se;
1464 } catch (Exception e) {
1465 Log.e(TAG, "isProviderEnabled got exception:", e);
1466 return false;
1467 }
1468 }
1469
Mike Lockwood4e50b782009-04-03 08:24:43 -07001470 public void setLocation(Location location) {
1471 mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
1472 Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
1473 mLocationHandler.sendMessageAtFrontOfQueue(m);
1474 }
1475
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001476 private boolean _isProviderEnabledLocked(String provider) {
1477 checkPermissionsSafe(provider);
1478
1479 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1480 if (p == null) {
1481 throw new IllegalArgumentException("provider=" + provider);
1482 }
1483 return isAllowedBySettingsLocked(provider);
1484 }
1485
1486 public Location getLastKnownLocation(String provider) {
1487 try {
1488 synchronized (mLocationListeners) {
1489 return _getLastKnownLocationLocked(provider);
1490 }
1491 } catch (SecurityException se) {
1492 throw se;
1493 } catch (Exception e) {
1494 Log.e(TAG, "getLastKnownLocation got exception:", e);
1495 return null;
1496 }
1497 }
1498
1499 private Location _getLastKnownLocationLocked(String provider) {
1500 checkPermissionsSafe(provider);
1501
1502 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1503 if (p == null) {
1504 throw new IllegalArgumentException("provider=" + provider);
1505 }
1506
1507 if (!isAllowedBySettingsLocked(provider)) {
1508 return null;
1509 }
1510
1511 Location location = mLastKnownLocation.get(provider);
1512 if (location == null) {
1513 // Get the persistent last known location for the provider
1514 location = readLastKnownLocationLocked(provider);
1515 if (location != null) {
1516 mLastKnownLocation.put(provider, location);
1517 }
1518 }
1519
1520 return location;
1521 }
1522
1523 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1524 // Always broadcast the first update
1525 if (lastLoc == null) {
1526 return true;
1527 }
1528
1529 // Don't broadcast same location again regardless of condition
1530 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1531 if (loc.getTime() == lastLoc.getTime()) {
1532 return false;
1533 }
1534
1535 // Check whether sufficient distance has been traveled
1536 double minDistance = record.mMinDistance;
1537 if (minDistance > 0.0) {
1538 if (loc.distanceTo(lastLoc) <= minDistance) {
1539 return false;
1540 }
1541 }
1542
1543 return true;
1544 }
1545
Mike Lockwood4e50b782009-04-03 08:24:43 -07001546 private void handleLocationChangedLocked(Location location) {
1547 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001548 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1549 if (records == null || records.size() == 0) {
1550 return;
1551 }
1552
1553 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1554 if (p == null) {
1555 return;
1556 }
1557
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001558 // Update last known location for provider
Mike Lockwood4e50b782009-04-03 08:24:43 -07001559 Location lastLocation = mLastKnownLocation.get(provider);
1560 if (lastLocation == null) {
1561 mLastKnownLocation.put(provider, new Location(location));
1562 } else {
1563 lastLocation.set(location);
1564 }
1565 writeLastKnownLocationLocked(provider, location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001566
Mike Lockwoode932f7f2009-04-06 10:51:26 -07001567 if (LocationManager.NETWORK_PROVIDER.equals(p.getName())) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001568 mWakeLockNetworkReceived = true;
1569 } else if (p instanceof GpsLocationProvider) {
1570 // Gps location received signal is in NetworkStateBroadcastReceiver
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001571 }
1572
1573 // Fetch latest status update time
1574 long newStatusUpdateTime = p.getStatusUpdateTime();
1575
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001576 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001577 Bundle extras = new Bundle();
1578 int status = p.getStatus(extras);
1579
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001580 ArrayList<Receiver> deadReceivers = null;
1581
1582 // Broadcast location or status to all listeners
1583 final int N = records.size();
1584 for (int i=0; i<N; i++) {
1585 UpdateRecord r = records.get(i);
1586 Receiver receiver = r.mReceiver;
1587
Mike Lockwood4e50b782009-04-03 08:24:43 -07001588 HashMap<String,Location> map = mLastFixBroadcast.get(receiver);
1589 if (map == null) {
1590 map = new HashMap<String,Location>();
1591 mLastFixBroadcast.put(receiver, map);
1592 }
1593 Location lastLoc = map.get(provider);
1594 if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1595 if (lastLoc == null) {
1596 lastLoc = new Location(location);
1597 map.put(provider, lastLoc);
1598 } else {
1599 lastLoc.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001600 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001601 if (!receiver.callLocationChangedLocked(location)) {
1602 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1603 if (deadReceivers == null) {
1604 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001605 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001606 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001607 }
1608 }
1609
1610 // Broadcast status message
1611 HashMap<String,Long> statusMap = mLastStatusBroadcast.get(receiver);
1612 if (statusMap == null) {
1613 statusMap = new HashMap<String,Long>();
1614 mLastStatusBroadcast.put(receiver, statusMap);
1615 }
1616 long prevStatusUpdateTime =
1617 (statusMap.get(provider) != null) ? statusMap.get(provider) : 0;
1618
1619 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1620 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1621
1622 statusMap.put(provider, newStatusUpdateTime);
1623 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1624 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1625 if (deadReceivers == null) {
1626 deadReceivers = new ArrayList<Receiver>();
1627 }
1628 if (!deadReceivers.contains(receiver)) {
1629 deadReceivers.add(receiver);
1630 }
1631 }
1632 }
1633 }
1634
1635 if (deadReceivers != null) {
1636 for (int i=deadReceivers.size()-1; i>=0; i--) {
1637 removeUpdatesLocked(deadReceivers.get(i));
1638 }
1639 }
1640 }
1641
1642 private class LocationWorkerHandler extends Handler {
1643
1644 @Override
1645 public void handleMessage(Message msg) {
1646 try {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001647 if (msg.what == MESSAGE_LOCATION_CHANGED) {
1648 // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001649
1650 synchronized (mLocationListeners) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001651 Location location = (Location) msg.obj;
1652 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001653 if (!isAllowedBySettingsLocked(provider)) {
1654 return;
1655 }
1656
1657 // Process the location fix if the screen is on or we're holding a wakelock
1658 if (mScreenOn || (mWakeLockAcquireTime != 0)) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001659 handleLocationChangedLocked(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001660 }
1661
1662 if ((mWakeLockAcquireTime != 0) &&
1663 (SystemClock.elapsedRealtime() - mWakeLockAcquireTime
1664 > MAX_TIME_FOR_WAKE_LOCK)) {
1665
1666 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1667 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1668
1669 log("LocationWorkerHandler: Exceeded max time for wake lock");
1670 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1671 sendMessageAtFrontOfQueue(m);
1672
1673 } else if (mWakeLockAcquireTime != 0 &&
1674 mWakeLockGpsReceived && mWakeLockNetworkReceived) {
1675
1676 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1677 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1678
1679 log("LocationWorkerHandler: Locations received.");
1680 mWakeLockAcquireTime = 0;
1681 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1682 sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK);
1683 }
1684 }
1685
1686 } else if (msg.what == MESSAGE_ACQUIRE_WAKE_LOCK) {
1687 log("LocationWorkerHandler: Acquire");
1688 synchronized (mLocationListeners) {
1689 acquireWakeLockLocked();
1690 }
1691 } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) {
1692 log("LocationWorkerHandler: Release");
1693
1694 // Update wakelock status so the next alarm is set before releasing wakelock
1695 synchronized (mLocationListeners) {
1696 updateWakelockStatusLocked(mScreenOn);
1697 releaseWakeLockLocked();
1698 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001699 }
1700 } catch (Exception e) {
1701 // Log, don't crash!
1702 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1703 }
1704 }
1705 }
1706
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001707 private class PowerStateBroadcastReceiver extends BroadcastReceiver {
1708 @Override public void onReceive(Context context, Intent intent) {
1709 String action = intent.getAction();
1710
1711 if (action.equals(ALARM_INTENT)) {
1712 synchronized (mLocationListeners) {
1713 log("PowerStateBroadcastReceiver: Alarm received");
1714 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1715 // Have to do this immediately, rather than posting a
1716 // message, so we execute our code while the system
1717 // is holding a wake lock until the alarm broadcast
1718 // is finished.
1719 acquireWakeLockLocked();
1720 }
1721
1722 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1723 log("PowerStateBroadcastReceiver: Screen off");
1724 synchronized (mLocationListeners) {
1725 updateWakelockStatusLocked(false);
1726 }
1727
1728 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1729 log("PowerStateBroadcastReceiver: Screen on");
1730 synchronized (mLocationListeners) {
1731 updateWakelockStatusLocked(true);
1732 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
1734 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
1735 synchronized (mLocationListeners) {
1736 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1737 if (uid >= 0) {
1738 ArrayList<Receiver> removedRecs = null;
1739 for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
1740 for (int j=i.size()-1; j>=0; j--) {
1741 UpdateRecord ur = i.get(j);
1742 if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
1743 if (removedRecs == null) {
1744 removedRecs = new ArrayList<Receiver>();
1745 }
1746 if (!removedRecs.contains(ur.mReceiver)) {
1747 removedRecs.add(ur.mReceiver);
1748 }
1749 }
1750 }
1751 }
1752 ArrayList<ProximityAlert> removedAlerts = null;
1753 for (ProximityAlert i : mProximityAlerts.values()) {
1754 if (i.mUid == uid) {
1755 if (removedAlerts == null) {
1756 removedAlerts = new ArrayList<ProximityAlert>();
1757 }
1758 if (!removedAlerts.contains(i)) {
1759 removedAlerts.add(i);
1760 }
1761 }
1762 }
1763 if (removedRecs != null) {
1764 for (int i=removedRecs.size()-1; i>=0; i--) {
1765 removeUpdatesLocked(removedRecs.get(i));
1766 }
1767 }
1768 if (removedAlerts != null) {
1769 for (int i=removedAlerts.size()-1; i>=0; i--) {
1770 removeProximityAlertLocked(removedAlerts.get(i).mIntent);
1771 }
1772 }
1773 }
1774 }
1775 }
1776 }
1777 }
1778
1779 private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
1780 @Override public void onReceive(Context context, Intent intent) {
1781 String action = intent.getAction();
1782
Mike Lockwoodf113fbe2009-04-06 05:17:28 -07001783 if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001784 boolean noConnectivity =
1785 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1786 if (!noConnectivity) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001787 mNetworkState = LocationProvider.AVAILABLE;
1788 } else {
1789 mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001790 }
1791
1792 // Notify location providers of current network state
1793 synchronized (mLocationListeners) {
1794 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1795 for (LocationProviderImpl provider : providers) {
1796 if (provider.requiresNetwork()) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001797 provider.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001798 }
1799 }
1800 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001801 } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
1802
1803 final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED,
1804 false);
1805
1806 synchronized (mLocationListeners) {
1807 if (enabled) {
1808 updateReportedGpsLocked();
1809 mGpsNavigating = true;
1810 } else {
1811 reportStopGpsLocked();
1812 mGpsNavigating = false;
1813 // When GPS is disabled, we are OK to release wake-lock
1814 mWakeLockGpsReceived = true;
1815 }
1816 }
1817 }
1818
1819 }
1820 }
1821
1822 // Wake locks
1823
1824 private void updateWakelockStatusLocked(boolean screenOn) {
1825 log("updateWakelockStatus(): " + screenOn);
1826
Amith Yamasanie1ccba22009-04-02 11:40:25 -07001827 long callerId = Binder.clearCallingIdentity();
1828
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001829 boolean needsLock = false;
1830 long minTime = Integer.MAX_VALUE;
1831
1832 if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) {
1833 needsLock = true;
1834 minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime);
1835 }
1836
1837 if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) {
1838 needsLock = true;
1839 minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime);
1840 if (screenOn) {
1841 startGpsLocked();
1842 } else if (mScreenOn && !screenOn) {
1843 // We just turned the screen off so stop navigating
1844 stopGpsLocked();
1845 }
1846 }
1847
1848 mScreenOn = screenOn;
1849
1850 PendingIntent sender =
1851 PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0);
1852
1853 // Cancel existing alarm
1854 log("Cancelling existing alarm");
1855 mAlarmManager.cancel(sender);
1856
1857 if (needsLock && !mScreenOn) {
1858 long now = SystemClock.elapsedRealtime();
1859 mAlarmManager.set(
1860 AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender);
1861 mAlarmInterval = minTime;
1862 log("Creating a new wakelock alarm with minTime = " + minTime);
1863 } else {
1864 log("No need for alarm");
1865 mAlarmInterval = -1;
1866
1867 // Clear out existing wakelocks
1868 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1869 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1870 releaseWakeLockLocked();
1871 }
Amith Yamasanie1ccba22009-04-02 11:40:25 -07001872 Binder.restoreCallingIdentity(callerId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001873 }
1874
1875 private void acquireWakeLockLocked() {
1876 try {
1877 acquireWakeLockXLocked();
1878 } catch (Exception e) {
1879 // This is to catch a runtime exception thrown when we try to release an
1880 // already released lock.
1881 Log.e(TAG, "exception in acquireWakeLock()", e);
1882 }
1883 }
1884
1885 private void acquireWakeLockXLocked() {
1886 if (mWakeLock.isHeld()) {
1887 log("Must release wakelock before acquiring");
1888 mWakeLockAcquireTime = 0;
1889 mWakeLock.release();
1890 }
1891
1892 boolean networkActive = (mNetworkLocationProvider != null)
1893 && mNetworkLocationProvider.isLocationTracking();
1894 boolean gpsActive = (mGpsLocationProvider != null)
1895 && mGpsLocationProvider.isLocationTracking();
1896
1897 boolean needsLock = networkActive || gpsActive;
1898 if (!needsLock) {
1899 log("No need for Lock!");
1900 return;
1901 }
1902
1903 mWakeLockGpsReceived = !gpsActive;
1904 mWakeLockNetworkReceived = !networkActive;
1905
1906 // Acquire wake lock
1907 mWakeLock.acquire();
1908 mWakeLockAcquireTime = SystemClock.elapsedRealtime();
1909 log("Acquired wakelock");
1910
1911 // Start the gps provider
1912 startGpsLocked();
1913
1914 // Acquire cell lock
1915 if (mCellWakeLockAcquired) {
1916 // Lock is already acquired
1917 } else if (!mWakeLockNetworkReceived) {
1918 mTelephonyManager.enableLocationUpdates();
1919 mCellWakeLockAcquired = true;
1920 } else {
1921 mCellWakeLockAcquired = false;
1922 }
1923
1924 // Notify NetworkLocationProvider
Mike Lockwoode932f7f2009-04-06 10:51:26 -07001925 if (mNetworkLocationProvider != null) {
1926 mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001927 }
1928
1929 // Acquire wifi lock
1930 WifiManager.WifiLock wifiLock = getWifiWakelockLocked();
1931 if (wifiLock != null) {
1932 if (mWifiWakeLockAcquired) {
1933 // Lock is already acquired
1934 } else if (mWifiManager.isWifiEnabled() && !mWakeLockNetworkReceived) {
1935 wifiLock.acquire();
1936 mWifiWakeLockAcquired = true;
1937 } else {
1938 mWifiWakeLockAcquired = false;
1939 Log.w(TAG, "acquireWakeLock(): Unable to get WiFi lock");
1940 }
1941 }
1942 }
1943
1944 private boolean reportGpsUidLocked(int curSeq, int nextSeq, int uid) {
1945 int seq = mReportedGpsUids.get(uid, -1);
1946 if (seq == curSeq) {
1947 // Already reported; propagate to next sequence.
1948 mReportedGpsUids.put(uid, nextSeq);
1949 return true;
1950 } else if (seq != nextSeq) {
1951 try {
1952 // New UID; report it.
1953 mBatteryStats.noteStartGps(uid);
1954 mReportedGpsUids.put(uid, nextSeq);
1955 return true;
1956 } catch (RemoteException e) {
1957 }
1958 }
1959 return false;
1960 }
1961
1962 private void updateReportedGpsLocked() {
1963 if (mGpsLocationProvider == null) {
1964 return;
1965 }
1966
1967 final String name = mGpsLocationProvider.getName();
1968 final int curSeq = mReportedGpsSeq;
1969 final int nextSeq = (curSeq+1) >= 0 ? (curSeq+1) : 0;
1970 mReportedGpsSeq = nextSeq;
1971
1972 ArrayList<UpdateRecord> urs = mRecordsByProvider.get(name);
1973 int num = 0;
1974 final int N = urs.size();
1975 for (int i=0; i<N; i++) {
1976 UpdateRecord ur = urs.get(i);
1977 if (ur.mReceiver == mProximityListener) {
1978 // We don't want the system to take the blame for this one.
1979 continue;
1980 }
1981 if (reportGpsUidLocked(curSeq, nextSeq, ur.mUid)) {
1982 num++;
1983 }
1984 }
1985
1986 for (ProximityAlert pe : mProximityAlerts.values()) {
1987 if (reportGpsUidLocked(curSeq, nextSeq, pe.mUid)) {
1988 num++;
1989 }
1990 }
1991
1992 if (num != mReportedGpsUids.size()) {
1993 // The number of uids is processed is different than the
1994 // array; report any that are no longer active.
1995 for (int i=mReportedGpsUids.size()-1; i>=0; i--) {
1996 if (mReportedGpsUids.valueAt(i) != nextSeq) {
1997 try {
1998 mBatteryStats.noteStopGps(mReportedGpsUids.keyAt(i));
1999 } catch (RemoteException e) {
2000 }
2001 mReportedGpsUids.removeAt(i);
2002 }
2003 }
2004 }
2005 }
2006
2007 private void reportStopGpsLocked() {
2008 int curSeq = mReportedGpsSeq;
2009 for (int i=mReportedGpsUids.size()-1; i>=0; i--) {
2010 if (mReportedGpsUids.valueAt(i) == curSeq) {
2011 try {
2012 mBatteryStats.noteStopGps(mReportedGpsUids.keyAt(i));
2013 } catch (RemoteException e) {
2014 }
2015 }
2016 }
2017 curSeq++;
2018 if (curSeq < 0) curSeq = 0;
2019 mReportedGpsSeq = curSeq;
2020 mReportedGpsUids.clear();
2021 }
2022
2023 private void startGpsLocked() {
2024 boolean gpsActive = (mGpsLocationProvider != null)
2025 && mGpsLocationProvider.isLocationTracking();
2026 if (gpsActive) {
2027 mGpsLocationProvider.startNavigating();
2028 }
2029 }
2030
2031 private void stopGpsLocked() {
2032 boolean gpsActive = mGpsLocationProvider != null
2033 && mGpsLocationProvider.isLocationTracking();
2034 if (gpsActive) {
2035 mGpsLocationProvider.stopNavigating();
2036 }
2037 }
2038
2039 private void releaseWakeLockLocked() {
2040 try {
2041 releaseWakeLockXLocked();
2042 } catch (Exception e) {
2043 // This is to catch a runtime exception thrown when we try to release an
2044 // already released lock.
2045 Log.e(TAG, "exception in releaseWakeLock()", e);
2046 }
2047 }
2048
2049 private void releaseWakeLockXLocked() {
2050 // Release wifi lock
2051 WifiManager.WifiLock wifiLock = getWifiWakelockLocked();
2052 if (wifiLock != null) {
2053 if (mWifiWakeLockAcquired) {
2054 wifiLock.release();
2055 mWifiWakeLockAcquired = false;
2056 }
2057 }
2058
2059 if (!mScreenOn) {
2060 // Stop the gps
2061 stopGpsLocked();
2062 }
2063
2064 // Release cell lock
2065 if (mCellWakeLockAcquired) {
2066 mTelephonyManager.disableLocationUpdates();
2067 mCellWakeLockAcquired = false;
2068 }
2069
2070 // Notify NetworkLocationProvider
Mike Lockwoode932f7f2009-04-06 10:51:26 -07002071 if (mNetworkLocationProvider != null) {
2072 mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002073 }
2074
2075 // Release wake lock
2076 mWakeLockAcquireTime = 0;
2077 if (mWakeLock.isHeld()) {
2078 log("Released wakelock");
2079 mWakeLock.release();
2080 } else {
2081 log("Can't release wakelock again!");
2082 }
2083 }
2084
2085 // Geocoder
2086
2087 public String getFromLocation(double latitude, double longitude, int maxResults,
Mike Lockwooda55c3212009-04-15 11:10:11 -04002088 String language, String country, String variant, String appName, List<Address> addrs) {
2089 if (mGeocodeProvider != null) {
2090 try {
2091 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, language, country,
2092 variant, appName, addrs);
2093 } catch (RemoteException e) {
2094 Log.e(TAG, "getFromLocation failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002095 }
2096 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04002097 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002098 }
2099
Mike Lockwooda55c3212009-04-15 11:10:11 -04002100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002101 public String getFromLocationName(String locationName,
Mike Lockwooda55c3212009-04-15 11:10:11 -04002102 double lowerLeftLatitude, double lowerLeftLongitude,
2103 double upperRightLatitude, double upperRightLongitude, int maxResults,
2104 String language, String country, String variant, String appName, List<Address> addrs) {
2105
2106 if (mGeocodeProvider != null) {
2107 try {
2108 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
2109 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
2110 maxResults, language, country, variant, appName, addrs);
2111 } catch (RemoteException e) {
2112 Log.e(TAG, "getFromLocationName failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002113 }
2114 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04002115 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002116 }
2117
2118 // Mock Providers
2119
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002120 private void checkMockPermissionsSafe() {
2121 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
2122 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
2123 if (!allowMocks) {
2124 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
2125 }
2126
2127 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
2128 PackageManager.PERMISSION_GRANTED) {
2129 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
2130 }
2131 }
2132
2133 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
2134 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
2135 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
2136 checkMockPermissionsSafe();
2137
2138 synchronized (mLocationListeners) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07002139 MockProvider provider = new MockProvider(name, this,
2140 requiresNetwork, requiresSatellite,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002141 requiresCell, hasMonetaryCost, supportsAltitude,
2142 supportsSpeed, supportsBearing, powerRequirement, accuracy);
2143 if (LocationProviderImpl.getProvider(name) != null) {
2144 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
2145 }
2146 LocationProviderImpl.addProvider(provider);
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002147 mMockProviders.put(name, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002148 updateProvidersLocked();
2149 }
2150 }
2151
2152 public void removeTestProvider(String provider) {
2153 checkMockPermissionsSafe();
2154 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002155 MockProvider mockProvider = mMockProviders.get(provider);
2156 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002157 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2158 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002159 LocationProviderImpl.removeProvider(mockProvider);
2160 mMockProviders.remove(mockProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002161 updateProvidersLocked();
2162 }
2163 }
2164
2165 public void setTestProviderLocation(String provider, Location loc) {
2166 checkMockPermissionsSafe();
2167 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002168 MockProvider mockProvider = mMockProviders.get(provider);
2169 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002170 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2171 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002172 mockProvider.setLocation(loc);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002173 }
2174 }
2175
2176 public void clearTestProviderLocation(String provider) {
2177 checkMockPermissionsSafe();
2178 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002179 MockProvider mockProvider = mMockProviders.get(provider);
2180 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002181 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2182 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002183 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002184 }
2185 }
2186
2187 public void setTestProviderEnabled(String provider, boolean enabled) {
2188 checkMockPermissionsSafe();
2189 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002190 MockProvider mockProvider = mMockProviders.get(provider);
2191 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002192 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2193 }
2194 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002195 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002196 mEnabledProviders.add(provider);
2197 mDisabledProviders.remove(provider);
2198 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002199 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002200 mEnabledProviders.remove(provider);
2201 mDisabledProviders.add(provider);
2202 }
2203 updateProvidersLocked();
2204 }
2205 }
2206
2207 public void clearTestProviderEnabled(String provider) {
2208 checkMockPermissionsSafe();
2209 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002210 MockProvider mockProvider = mMockProviders.get(provider);
2211 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002212 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2213 }
2214 mEnabledProviders.remove(provider);
2215 mDisabledProviders.remove(provider);
2216 updateProvidersLocked();
2217 }
2218 }
2219
2220 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
2221 checkMockPermissionsSafe();
2222 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002223 MockProvider mockProvider = mMockProviders.get(provider);
2224 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002225 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2226 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002227 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002228 }
2229 }
2230
2231 public void clearTestProviderStatus(String provider) {
2232 checkMockPermissionsSafe();
2233 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002234 MockProvider mockProvider = mMockProviders.get(provider);
2235 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002236 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2237 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002238 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002239 }
2240 }
2241
2242 private void log(String log) {
2243 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2244 Log.d(TAG, log);
2245 }
2246 }
2247
2248 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2249 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2250 != PackageManager.PERMISSION_GRANTED) {
2251 pw.println("Permission Denial: can't dump AlarmManager from from pid="
2252 + Binder.getCallingPid()
2253 + ", uid=" + Binder.getCallingUid());
2254 return;
2255 }
2256
2257 synchronized (mLocationListeners) {
2258 pw.println("Current Location Manager state:");
2259 pw.println(" sProvidersLoaded=" + sProvidersLoaded);
2260 pw.println(" mGpsLocationProvider=" + mGpsLocationProvider);
2261 pw.println(" mGpsNavigating=" + mGpsNavigating);
2262 pw.println(" mNetworkLocationProvider=" + mNetworkLocationProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002263 pw.println(" mCollector=" + mCollector);
2264 pw.println(" mAlarmInterval=" + mAlarmInterval
2265 + " mScreenOn=" + mScreenOn
2266 + " mWakeLockAcquireTime=" + mWakeLockAcquireTime);
2267 pw.println(" mWakeLockGpsReceived=" + mWakeLockGpsReceived
2268 + " mWakeLockNetworkReceived=" + mWakeLockNetworkReceived);
2269 pw.println(" mWifiWakeLockAcquired=" + mWifiWakeLockAcquired
2270 + " mCellWakeLockAcquired=" + mCellWakeLockAcquired);
2271 pw.println(" Listeners:");
2272 int N = mListeners.size();
2273 for (int i=0; i<N; i++) {
2274 pw.println(" " + mListeners.get(i));
2275 }
2276 pw.println(" Location Listeners:");
2277 for (Map.Entry<Receiver, HashMap<String,UpdateRecord>> i
2278 : mLocationListeners.entrySet()) {
2279 pw.println(" " + i.getKey() + ":");
2280 for (Map.Entry<String,UpdateRecord> j : i.getValue().entrySet()) {
2281 pw.println(" " + j.getKey() + ":");
2282 j.getValue().dump(pw, " ");
2283 }
2284 }
2285 pw.println(" Last Fix Broadcasts:");
2286 for (Map.Entry<Receiver, HashMap<String,Location>> i
2287 : mLastFixBroadcast.entrySet()) {
2288 pw.println(" " + i.getKey() + ":");
2289 for (Map.Entry<String,Location> j : i.getValue().entrySet()) {
2290 pw.println(" " + j.getKey() + ":");
2291 j.getValue().dump(new PrintWriterPrinter(pw), " ");
2292 }
2293 }
2294 pw.println(" Last Status Broadcasts:");
2295 for (Map.Entry<Receiver, HashMap<String,Long>> i
2296 : mLastStatusBroadcast.entrySet()) {
2297 pw.println(" " + i.getKey() + ":");
2298 for (Map.Entry<String,Long> j : i.getValue().entrySet()) {
2299 pw.println(" " + j.getKey() + " -> 0x"
2300 + Long.toHexString(j.getValue()));
2301 }
2302 }
2303 pw.println(" Records by Provider:");
2304 for (Map.Entry<String, ArrayList<UpdateRecord>> i
2305 : mRecordsByProvider.entrySet()) {
2306 pw.println(" " + i.getKey() + ":");
2307 for (UpdateRecord j : i.getValue()) {
2308 pw.println(" " + j + ":");
2309 j.dump(pw, " ");
2310 }
2311 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002312 pw.println(" Last Known Locations:");
2313 for (Map.Entry<String, Location> i
2314 : mLastKnownLocation.entrySet()) {
2315 pw.println(" " + i.getKey() + ":");
2316 i.getValue().dump(new PrintWriterPrinter(pw), " ");
2317 }
2318 if (mProximityAlerts.size() > 0) {
2319 pw.println(" Proximity Alerts:");
2320 for (Map.Entry<PendingIntent, ProximityAlert> i
2321 : mProximityAlerts.entrySet()) {
2322 pw.println(" " + i.getKey() + ":");
2323 i.getValue().dump(pw, " ");
2324 }
2325 }
2326 if (mProximitiesEntered.size() > 0) {
2327 pw.println(" Proximities Entered:");
2328 for (ProximityAlert i : mProximitiesEntered) {
2329 pw.println(" " + i + ":");
2330 i.dump(pw, " ");
2331 }
2332 }
2333 pw.println(" mProximityListener=" + mProximityListener);
2334 if (mEnabledProviders.size() > 0) {
2335 pw.println(" Enabled Providers:");
2336 for (String i : mEnabledProviders) {
2337 pw.println(" " + i);
2338 }
2339
2340 }
2341 if (mDisabledProviders.size() > 0) {
2342 pw.println(" Disabled Providers:");
2343 for (String i : mDisabledProviders) {
2344 pw.println(" " + i);
2345 }
2346
2347 }
2348 if (mMockProviders.size() > 0) {
2349 pw.println(" Mock Providers:");
2350 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002351 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002352 }
2353 }
2354 pw.println(" Reported GPS UIDs @ seq " + mReportedGpsSeq + ":");
2355 N = mReportedGpsUids.size();
2356 for (int i=0; i<N; i++) {
2357 pw.println(" UID " + mReportedGpsUids.keyAt(i)
2358 + " seq=" + mReportedGpsUids.valueAt(i));
2359 }
2360 }
2361 }
2362}