blob: 9750d4d390a1fb9174d392616a92b37bd2afe0d1 [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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.app.PendingIntent;
37import android.content.BroadcastReceiver;
Mike Lockwood9637d472009-04-02 21:41:57 -070038import android.content.ContentQueryMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.content.ContentResolver;
40import android.content.Context;
41import android.content.Intent;
42import android.content.IntentFilter;
43import android.content.pm.PackageManager;
Mike Lockwood9637d472009-04-02 21:41:57 -070044import android.database.Cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.location.Address;
Mike Lockwooda55c3212009-04-15 11:10:11 -040046import android.location.IGeocodeProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.location.IGpsStatusListener;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -040048import android.location.IGpsStatusProvider;
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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.net.ConnectivityManager;
57import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.os.Binder;
59import android.os.Bundle;
60import android.os.Handler;
61import android.os.IBinder;
Mike Lockwood3d12b512009-04-21 23:25:35 -070062import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.os.Message;
64import android.os.PowerManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070065import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066import android.os.RemoteException;
67import android.os.SystemClock;
68import android.provider.Settings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import android.util.Config;
70import android.util.Log;
71import android.util.PrintWriterPrinter;
72import android.util.SparseIntArray;
73
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074import com.android.internal.location.GpsLocationProvider;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070075import com.android.internal.location.LocationProviderProxy;
Mike Lockwood7ec434e2009-03-27 07:46:48 -070076import com.android.internal.location.MockProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077import com.android.server.am.BatteryStatsService;
78
79/**
80 * The service class that manages LocationProviders and issues location
81 * updates and alerts.
82 *
83 * {@hide}
84 */
Mike Lockwood3d12b512009-04-21 23:25:35 -070085public class LocationManagerService extends ILocationManager.Stub implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 private static final String TAG = "LocationManagerService";
The Android Open Source Project10592532009-03-18 17:39:46 -070087 private static final boolean LOCAL_LOGV = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088
89 // Minimum time interval between last known location writes, in milliseconds.
90 private static final long MIN_LAST_KNOWN_LOCATION_TIME = 60L * 1000L;
91
92 // Max time to hold wake lock for, in milliseconds.
93 private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L;
94
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 // The last time a location was written, by provider name.
96 private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
97
98 private static final Pattern PATTERN_COMMA = Pattern.compile(",");
99
100 private static final String ACCESS_FINE_LOCATION =
101 android.Manifest.permission.ACCESS_FINE_LOCATION;
102 private static final String ACCESS_COARSE_LOCATION =
103 android.Manifest.permission.ACCESS_COARSE_LOCATION;
104 private static final String ACCESS_MOCK_LOCATION =
105 android.Manifest.permission.ACCESS_MOCK_LOCATION;
106 private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
107 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
Mike Lockwood275555c2009-05-01 11:30:34 -0400108 private static final String INSTALL_LOCATION_PROVIDER =
109 android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
110 private static final String INSTALL_LOCATION_COLLECTOR =
111 android.Manifest.permission.INSTALL_LOCATION_COLLECTOR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112
113 // Set of providers that are explicitly enabled
114 private final Set<String> mEnabledProviders = new HashSet<String>();
115
116 // Set of providers that are explicitly disabled
117 private final Set<String> mDisabledProviders = new HashSet<String>();
118
119 // Locations, status values, and extras for mock providers
Mike Lockwood7ec434e2009-03-27 07:46:48 -0700120 private final HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121
122 private static boolean sProvidersLoaded = false;
123
124 private final Context mContext;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400125 private LocationProviderProxy mGpsLocationProvider;
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700126 private LocationProviderProxy mNetworkLocationProvider;
Mike Lockwooda55c3212009-04-15 11:10:11 -0400127 private IGeocodeProvider mGeocodeProvider;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400128 private IGpsStatusProvider mGpsStatusProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 private LocationWorkerHandler mLocationHandler;
130
131 // Handler messages
Mike Lockwood4e50b782009-04-03 08:24:43 -0700132 private static final int MESSAGE_LOCATION_CHANGED = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400134 // wakelock variables
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 private final static String WAKELOCK_KEY = "LocationManagerService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 private PowerManager.WakeLock mWakeLock = null;
Mike Lockwood48f17512009-04-23 09:12:08 -0700137 private int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 /**
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400140 * List of all receivers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 */
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400142 private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400144
145 /**
146 * List of location providers.
147 */
148 private final ArrayList<LocationProviderProxy> mProviders =
149 new ArrayList<LocationProviderProxy>();
150 private final HashMap<String, LocationProviderProxy> mProvidersByName
151 = new HashMap<String, LocationProviderProxy>();
152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 /**
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400154 * Object used internally for synchronization
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 */
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400156 private final Object mLock = new Object();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157
158 /**
159 * Mapping from provider name to all its UpdateRecords
160 */
161 private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
162 new HashMap<String,ArrayList<UpdateRecord>>();
163
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 // Proximity listeners
Mike Lockwood48f17512009-04-23 09:12:08 -0700165 private Receiver mProximityReceiver = null;
166 private ILocationListener mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
168 new HashMap<PendingIntent,ProximityAlert>();
169 private HashSet<ProximityAlert> mProximitiesEntered =
170 new HashSet<ProximityAlert>();
171
172 // Last known location for each provider
173 private HashMap<String,Location> mLastKnownLocation =
174 new HashMap<String,Location>();
175
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 // Location collector
177 private ILocationCollector mCollector;
178
The Android Open Source Project4df24232009-03-05 14:34:35 -0800179 private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800180
Mike Lockwood9637d472009-04-02 21:41:57 -0700181 // for Settings change notification
182 private ContentQueryMap mSettings;
183
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 /**
185 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
186 * location updates.
187 */
Mike Lockwood48f17512009-04-23 09:12:08 -0700188 private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 final ILocationListener mListener;
190 final PendingIntent mPendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 final Object mKey;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400192 final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
Mike Lockwood48f17512009-04-23 09:12:08 -0700193 int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400195 Receiver(ILocationListener listener) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 mListener = listener;
197 mPendingIntent = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 mKey = listener.asBinder();
199 }
200
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400201 Receiver(PendingIntent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 mPendingIntent = intent;
203 mListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 mKey = intent;
205 }
206
207 @Override
208 public boolean equals(Object otherObj) {
209 if (otherObj instanceof Receiver) {
210 return mKey.equals(
211 ((Receiver)otherObj).mKey);
212 }
213 return false;
214 }
215
216 @Override
217 public int hashCode() {
218 return mKey.hashCode();
219 }
220
221
222 @Override
223 public String toString() {
224 if (mListener != null) {
225 return "Receiver{"
226 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400227 + " Listener " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 } else {
229 return "Receiver{"
230 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400231 + " Intent " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 }
233 }
234
235 public boolean isListener() {
236 return mListener != null;
237 }
238
239 public boolean isPendingIntent() {
240 return mPendingIntent != null;
241 }
242
243 public ILocationListener getListener() {
244 if (mListener != null) {
245 return mListener;
246 }
247 throw new IllegalStateException("Request for non-existent listener");
248 }
249
250 public PendingIntent getPendingIntent() {
251 if (mPendingIntent != null) {
252 return mPendingIntent;
253 }
254 throw new IllegalStateException("Request for non-existent intent");
255 }
256
257 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
258 if (mListener != null) {
259 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700260 synchronized (this) {
261 // synchronize to ensure incrementPendingBroadcastsLocked()
262 // is called before decrementPendingBroadcasts()
263 mListener.onStatusChanged(provider, status, extras);
264 if (mListener != mProximityListener) {
265 // call this after broadcasting so we do not increment
266 // if we throw an exeption.
267 incrementPendingBroadcastsLocked();
268 }
269 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 } catch (RemoteException e) {
271 return false;
272 }
273 } else {
274 Intent statusChanged = new Intent();
275 statusChanged.putExtras(extras);
276 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
277 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700278 synchronized (this) {
279 // synchronize to ensure incrementPendingBroadcastsLocked()
280 // is called before decrementPendingBroadcasts()
281 mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler);
282 // call this after broadcasting so we do not increment
283 // if we throw an exeption.
284 incrementPendingBroadcastsLocked();
285 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 } catch (PendingIntent.CanceledException e) {
287 return false;
288 }
289 }
290 return true;
291 }
292
293 public boolean callLocationChangedLocked(Location location) {
294 if (mListener != null) {
295 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700296 synchronized (this) {
297 // synchronize to ensure incrementPendingBroadcastsLocked()
298 // is called before decrementPendingBroadcasts()
299 mListener.onLocationChanged(location);
300 if (mListener != mProximityListener) {
301 // call this after broadcasting so we do not increment
302 // if we throw an exeption.
303 incrementPendingBroadcastsLocked();
304 }
305 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 } catch (RemoteException e) {
307 return false;
308 }
309 } else {
310 Intent locationChanged = new Intent();
311 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
312 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700313 synchronized (this) {
314 // synchronize to ensure incrementPendingBroadcastsLocked()
315 // is called before decrementPendingBroadcasts()
316 mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler);
317 // call this after broadcasting so we do not increment
318 // if we throw an exeption.
319 incrementPendingBroadcastsLocked();
320 }
321 } catch (PendingIntent.CanceledException e) {
322 return false;
323 }
324 }
325 return true;
326 }
327
328 public boolean callProviderEnabledLocked(String provider, boolean enabled) {
329 if (mListener != null) {
330 try {
331 synchronized (this) {
332 // synchronize to ensure incrementPendingBroadcastsLocked()
333 // is called before decrementPendingBroadcasts()
334 if (enabled) {
335 mListener.onProviderEnabled(provider);
336 } else {
337 mListener.onProviderDisabled(provider);
338 }
339 if (mListener != mProximityListener) {
340 // call this after broadcasting so we do not increment
341 // if we throw an exeption.
342 incrementPendingBroadcastsLocked();
343 }
344 }
345 } catch (RemoteException e) {
346 return false;
347 }
348 } else {
349 Intent providerIntent = new Intent();
350 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
351 try {
352 synchronized (this) {
353 // synchronize to ensure incrementPendingBroadcastsLocked()
354 // is called before decrementPendingBroadcasts()
355 mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler);
356 // call this after broadcasting so we do not increment
357 // if we throw an exeption.
358 incrementPendingBroadcastsLocked();
359 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 } catch (PendingIntent.CanceledException e) {
361 return false;
362 }
363 }
364 return true;
365 }
366
367 public void binderDied() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700368 if (LOCAL_LOGV) {
369 Log.v(TAG, "Location listener died");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 }
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400371 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 removeUpdatesLocked(this);
373 }
Mike Lockwood48f17512009-04-23 09:12:08 -0700374 synchronized (this) {
375 if (mPendingBroadcasts > 0) {
376 LocationManagerService.this.decrementPendingBroadcasts();
377 mPendingBroadcasts = 0;
378 }
379 }
380 }
381
382 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
383 int resultCode, String resultData, Bundle resultExtras) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400384 synchronized (this) {
385 decrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700386 }
387 }
388
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400389 // this must be called while synchronized by caller in a synchronized block
390 // containing the sending of the broadcaset
391 private void incrementPendingBroadcastsLocked() {
392 if (mPendingBroadcasts++ == 0) {
393 LocationManagerService.this.incrementPendingBroadcasts();
394 }
395 }
396
397 private void decrementPendingBroadcastsLocked() {
398 if (--mPendingBroadcasts == 0) {
399 LocationManagerService.this.decrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -0700400 }
401 }
402 }
403
404 public void locationCallbackFinished(ILocationListener listener) {
405 Receiver receiver = getReceiver(listener);
406 if (receiver != null) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400407 synchronized (receiver) {
408 // so wakelock calls will succeed
409 long identity = Binder.clearCallingIdentity();
410 receiver.decrementPendingBroadcastsLocked();
411 Binder.restoreCallingIdentity(identity);
412 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 }
414 }
415
Mike Lockwood9637d472009-04-02 21:41:57 -0700416 private final class SettingsObserver implements Observer {
417 public void update(Observable o, Object arg) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400418 synchronized (mLock) {
Mike Lockwood9637d472009-04-02 21:41:57 -0700419 updateProvidersLocked();
420 }
421 }
422 }
423
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 private Location readLastKnownLocationLocked(String provider) {
425 Location location = null;
426 String s = null;
427 try {
428 File f = new File(LocationManager.SYSTEM_DIR + "/location."
429 + provider);
430 if (!f.exists()) {
431 return null;
432 }
433 BufferedReader reader = new BufferedReader(new FileReader(f), 256);
434 s = reader.readLine();
435 } catch (IOException e) {
436 Log.w(TAG, "Unable to read last known location", e);
437 }
438
439 if (s == null) {
440 return null;
441 }
442 try {
443 String[] tokens = PATTERN_COMMA.split(s);
444 int idx = 0;
445 long time = Long.parseLong(tokens[idx++]);
446 double latitude = Double.parseDouble(tokens[idx++]);
447 double longitude = Double.parseDouble(tokens[idx++]);
448 double altitude = Double.parseDouble(tokens[idx++]);
449 float bearing = Float.parseFloat(tokens[idx++]);
450 float speed = Float.parseFloat(tokens[idx++]);
451
452 location = new Location(provider);
453 location.setTime(time);
454 location.setLatitude(latitude);
455 location.setLongitude(longitude);
456 location.setAltitude(altitude);
457 location.setBearing(bearing);
458 location.setSpeed(speed);
459 } catch (NumberFormatException nfe) {
460 Log.e(TAG, "NumberFormatException reading last known location", nfe);
461 return null;
462 }
463
464 return location;
465 }
466
467 private void writeLastKnownLocationLocked(String provider,
468 Location location) {
469 long now = SystemClock.elapsedRealtime();
470 Long last = mLastWriteTime.get(provider);
471 if ((last != null)
472 && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
473 return;
474 }
475 mLastWriteTime.put(provider, now);
476
477 StringBuilder sb = new StringBuilder(100);
478 sb.append(location.getTime());
479 sb.append(',');
480 sb.append(location.getLatitude());
481 sb.append(',');
482 sb.append(location.getLongitude());
483 sb.append(',');
484 sb.append(location.getAltitude());
485 sb.append(',');
486 sb.append(location.getBearing());
487 sb.append(',');
488 sb.append(location.getSpeed());
489
490 FileWriter writer = null;
491 try {
492 File d = new File(LocationManager.SYSTEM_DIR);
493 if (!d.exists()) {
494 if (!d.mkdirs()) {
495 Log.w(TAG, "Unable to create directory to write location");
496 return;
497 }
498 }
499 File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
500 writer = new FileWriter(f);
501 writer.write(sb.toString());
502 } catch (IOException e) {
503 Log.w(TAG, "Unable to write location", e);
504 } finally {
505 if (writer != null) {
506 try {
507 writer.close();
508 } catch (IOException e) {
509 Log.w(TAG, "Exception closing file", e);
510 }
511 }
512 }
513 }
514
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400515 private void addProvider(LocationProviderProxy provider) {
516 mProviders.add(provider);
517 mProvidersByName.put(provider.getName(), provider);
518 }
519
520 private void removeProvider(LocationProviderProxy provider) {
521 mProviders.remove(provider);
522 mProvidersByName.remove(provider.getName());
523 }
524
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 private void loadProviders() {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400526 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 if (sProvidersLoaded) {
528 return;
529 }
530
531 // Load providers
532 loadProvidersLocked();
533 sProvidersLoaded = true;
534 }
535 }
536
537 private void loadProvidersLocked() {
538 try {
539 _loadProvidersLocked();
540 } catch (Exception e) {
541 Log.e(TAG, "Exception loading providers:", e);
542 }
543 }
544
545 private void _loadProvidersLocked() {
546 // Attempt to load "real" providers first
547 if (GpsLocationProvider.isSupported()) {
548 // Create a gps location provider
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400549 GpsLocationProvider provider = new GpsLocationProvider(mContext, this);
550 mGpsStatusProvider = provider.getGpsStatusProvider();
551 mGpsLocationProvider =
552 new LocationProviderProxy(LocationManager.GPS_PROVIDER, provider);
553 addProvider(mGpsLocationProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 }
555
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556 updateProvidersLocked();
557 }
558
559 /**
560 * @param context the context that the LocationManagerService runs in
561 */
562 public LocationManagerService(Context context) {
563 super();
564 mContext = context;
Mike Lockwood3d12b512009-04-21 23:25:35 -0700565
566 Thread thread = new Thread(null, this, "LocationManagerService");
567 thread.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568
The Android Open Source Project10592532009-03-18 17:39:46 -0700569 if (LOCAL_LOGV) {
570 Log.v(TAG, "Constructed LocationManager Service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 }
Mike Lockwood3d12b512009-04-21 23:25:35 -0700572 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573
Mike Lockwood3d12b512009-04-21 23:25:35 -0700574 private void initialize() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 // Create a wake lock, needs to be done before calling loadProviders() below
576 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
577 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400578
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 // Load providers
580 loadProviders();
581
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 // Register for Network (Wifi or Mobile) updates
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800583 IntentFilter intentFilter = new IntentFilter();
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400584 intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
585 // Register for Package Manager updates
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
587 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400588 mContext.registerReceiver(mBroadcastReceiver, intentFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589
Mike Lockwood9637d472009-04-02 21:41:57 -0700590 // listen for settings changes
591 ContentResolver resolver = mContext.getContentResolver();
592 Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
593 "(" + Settings.System.NAME + "=?)",
594 new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
595 null);
596 mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler);
597 SettingsObserver settingsObserver = new SettingsObserver();
598 mSettings.addObserver(settingsObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 }
600
Mike Lockwood3d12b512009-04-21 23:25:35 -0700601 public void run()
602 {
603 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
604 Looper.prepare();
605 mLocationHandler = new LocationWorkerHandler();
606 initialize();
607 Looper.loop();
608 }
609
Mike Lockwood275555c2009-05-01 11:30:34 -0400610 public void installLocationProvider(String name, ILocationProvider provider) {
611 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
612 != PackageManager.PERMISSION_GRANTED) {
613 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700614 }
615
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400616 synchronized (mLock) {
Mike Lockwood275555c2009-05-01 11:30:34 -0400617 // FIXME - only network location provider supported for now
618 if (LocationManager.NETWORK_PROVIDER.equals(name)) {
619 mNetworkLocationProvider = new LocationProviderProxy(name, provider);
620 addProvider(mNetworkLocationProvider);
621 updateProvidersLocked();
622
623 // notify NetworkLocationProvider of any events it might have missed
624 mNetworkLocationProvider.updateNetworkState(mNetworkState);
625 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800626 }
627 }
628
Mike Lockwood275555c2009-05-01 11:30:34 -0400629 public void installLocationCollector(ILocationCollector collector) {
630 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_COLLECTOR)
631 != PackageManager.PERMISSION_GRANTED) {
632 throw new SecurityException("Requires INSTALL_LOCATION_COLLECTOR permission");
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700633 }
634
Mike Lockwood275555c2009-05-01 11:30:34 -0400635 // FIXME - only support one collector
Mike Lockwood98cb6672009-04-17 18:03:44 -0400636 mCollector = collector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 }
638
Mike Lockwood275555c2009-05-01 11:30:34 -0400639 public void installGeocodeProvider(IGeocodeProvider provider) {
640 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
641 != PackageManager.PERMISSION_GRANTED) {
642 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
Mike Lockwooda55c3212009-04-15 11:10:11 -0400643 }
644
645 mGeocodeProvider = provider;
646 }
647
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800648 private boolean isAllowedBySettingsLocked(String provider) {
649 if (mEnabledProviders.contains(provider)) {
650 return true;
651 }
652 if (mDisabledProviders.contains(provider)) {
653 return false;
654 }
655 // Use system settings
656 ContentResolver resolver = mContext.getContentResolver();
657 String allowedProviders = Settings.Secure.getString(resolver,
658 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
659
660 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
661 }
662
663 private void checkPermissionsSafe(String provider) {
664 if (LocationManager.GPS_PROVIDER.equals(provider)
665 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
666 != PackageManager.PERMISSION_GRANTED)) {
667 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
668 }
669 if (LocationManager.NETWORK_PROVIDER.equals(provider)
670 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
671 != PackageManager.PERMISSION_GRANTED)
672 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
673 != PackageManager.PERMISSION_GRANTED)) {
674 throw new SecurityException(
675 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
676 }
677 }
678
679 private boolean isAllowedProviderSafe(String provider) {
680 if (LocationManager.GPS_PROVIDER.equals(provider)
681 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
682 != PackageManager.PERMISSION_GRANTED)) {
683 return false;
684 }
685 if (LocationManager.NETWORK_PROVIDER.equals(provider)
686 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
687 != PackageManager.PERMISSION_GRANTED)
688 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
689 != PackageManager.PERMISSION_GRANTED)) {
690 return false;
691 }
692
693 return true;
694 }
695
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 public List<String> getAllProviders() {
697 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400698 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 return _getAllProvidersLocked();
700 }
701 } catch (SecurityException se) {
702 throw se;
703 } catch (Exception e) {
704 Log.e(TAG, "getAllProviders got exception:", e);
705 return null;
706 }
707 }
708
709 private List<String> _getAllProvidersLocked() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700710 if (LOCAL_LOGV) {
711 Log.v(TAG, "getAllProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400713 ArrayList<String> out = new ArrayList<String>(mProviders.size());
714 for (int i = mProviders.size() - 1; i >= 0; i--) {
715 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 out.add(p.getName());
717 }
718 return out;
719 }
720
721 public List<String> getProviders(boolean enabledOnly) {
722 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400723 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800724 return _getProvidersLocked(enabledOnly);
725 }
726 } catch (SecurityException se) {
727 throw se;
728 } catch (Exception e) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700729 Log.e(TAG, "getProviders got exception:", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800730 return null;
731 }
732 }
733
734 private List<String> _getProvidersLocked(boolean enabledOnly) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700735 if (LOCAL_LOGV) {
736 Log.v(TAG, "getProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800737 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400738 ArrayList<String> out = new ArrayList<String>(mProviders.size());
739 for (int i = mProviders.size() - 1; i >= 0; i--) {
740 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 String name = p.getName();
742 if (isAllowedProviderSafe(name)) {
743 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
744 continue;
745 }
746 out.add(name);
747 }
748 }
749 return out;
750 }
751
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 private void updateProvidersLocked() {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400753 for (int i = mProviders.size() - 1; i >= 0; i--) {
754 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 boolean isEnabled = p.isEnabled();
756 String name = p.getName();
757 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
758
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 if (isEnabled && !shouldBeEnabled) {
760 updateProviderListenersLocked(name, false);
761 } else if (!isEnabled && shouldBeEnabled) {
762 updateProviderListenersLocked(name, true);
763 }
764
765 }
766 }
767
768 private void updateProviderListenersLocked(String provider, boolean enabled) {
769 int listeners = 0;
770
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400771 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 if (p == null) {
773 return;
774 }
775
776 ArrayList<Receiver> deadReceivers = null;
777
778 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
779 if (records != null) {
780 final int N = records.size();
781 for (int i=0; i<N; i++) {
782 UpdateRecord record = records.get(i);
783 // Sends a notification message to the receiver
Mike Lockwood48f17512009-04-23 09:12:08 -0700784 if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
785 if (deadReceivers == null) {
786 deadReceivers = new ArrayList<Receiver>();
787 deadReceivers.add(record.mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 }
790 listeners++;
791 }
792 }
793
794 if (deadReceivers != null) {
795 for (int i=deadReceivers.size()-1; i>=0; i--) {
796 removeUpdatesLocked(deadReceivers.get(i));
797 }
798 }
799
800 if (enabled) {
801 p.enable();
802 if (listeners > 0) {
803 p.setMinTime(getMinTimeLocked(provider));
804 p.enableLocationTracking(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800805 }
806 } else {
807 p.enableLocationTracking(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 p.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 }
811
812 private long getMinTimeLocked(String provider) {
813 long minTime = Long.MAX_VALUE;
814 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
815 if (records != null) {
816 for (int i=records.size()-1; i>=0; i--) {
817 minTime = Math.min(minTime, records.get(i).mMinTime);
818 }
819 }
820 return minTime;
821 }
822
823 private class UpdateRecord {
824 final String mProvider;
825 final Receiver mReceiver;
826 final long mMinTime;
827 final float mMinDistance;
828 final int mUid;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400829 Location mLastFixBroadcast;
830 long mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831
832 /**
833 * Note: must be constructed with lock held.
834 */
835 UpdateRecord(String provider, long minTime, float minDistance,
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400836 Receiver receiver, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800837 mProvider = provider;
838 mReceiver = receiver;
839 mMinTime = minTime;
840 mMinDistance = minDistance;
841 mUid = uid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800842
843 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
844 if (records == null) {
845 records = new ArrayList<UpdateRecord>();
846 mRecordsByProvider.put(provider, records);
847 }
848 if (!records.contains(this)) {
849 records.add(this);
850 }
851 }
852
853 /**
854 * Method to be called when a record will no longer be used. Calling this multiple times
855 * must have the same effect as calling it once.
856 */
857 void disposeLocked() {
858 ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
859 records.remove(this);
860 }
861
862 @Override
863 public String toString() {
864 return "UpdateRecord{"
865 + Integer.toHexString(System.identityHashCode(this))
866 + " " + mProvider + " " + mReceiver + "}";
867 }
868
869 void dump(PrintWriter pw, String prefix) {
870 pw.println(prefix + this);
871 pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
872 pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400873 pw.println(prefix + "mUid=" + mUid);
874 pw.println(prefix + "mLastFixBroadcast:");
875 mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
876 pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877 }
878
879 /**
880 * Calls dispose().
881 */
882 @Override protected void finalize() {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400883 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884 disposeLocked();
885 }
886 }
887 }
888
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400889 private Receiver getReceiver(ILocationListener listener) {
890 IBinder binder = listener.asBinder();
891 Receiver receiver = mReceivers.get(binder);
892 if (receiver == null) {
893 receiver = new Receiver(listener);
894 mReceivers.put(binder, receiver);
895
896 try {
897 if (receiver.isListener()) {
898 receiver.getListener().asBinder().linkToDeath(receiver, 0);
899 }
900 } catch (RemoteException e) {
901 Log.e(TAG, "linkToDeath failed:", e);
902 return null;
903 }
904 }
905 return receiver;
906 }
907
908 private Receiver getReceiver(PendingIntent intent) {
909 Receiver receiver = mReceivers.get(intent);
910 if (receiver == null) {
911 receiver = new Receiver(intent);
912 mReceivers.put(intent, receiver);
913 }
914 return receiver;
915 }
916
917 private boolean providerHasListener(String provider, int uid, Receiver excludedReceiver) {
918 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
919 if (records != null) {
920 for (int i = records.size() - 1; i >= 0; i--) {
921 UpdateRecord record = records.get(i);
922 if (record.mUid == uid && record.mReceiver != excludedReceiver) {
923 return true;
924 }
925 }
926 }
927 if (LocationManager.GPS_PROVIDER.equals(provider) ||
928 LocationManager.NETWORK_PROVIDER.equals(provider)) {
929 for (ProximityAlert alert : mProximityAlerts.values()) {
930 if (alert.mUid == uid) {
931 return true;
932 }
933 }
934 }
935 return false;
936 }
937
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800938 public void requestLocationUpdates(String provider,
939 long minTime, float minDistance, ILocationListener listener) {
940
941 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400942 synchronized (mLock) {
943 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944 }
945 } catch (SecurityException se) {
946 throw se;
947 } catch (Exception e) {
948 Log.e(TAG, "requestUpdates got exception:", e);
949 }
950 }
951
952 public void requestLocationUpdatesPI(String provider,
953 long minTime, float minDistance, PendingIntent intent) {
954 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400955 synchronized (mLock) {
956 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 }
958 } catch (SecurityException se) {
959 throw se;
960 } catch (Exception e) {
961 Log.e(TAG, "requestUpdates got exception:", e);
962 }
963 }
964
965 private void requestLocationUpdatesLocked(String provider,
966 long minTime, float minDistance, Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700967 if (LOCAL_LOGV) {
968 Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800969 }
970
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400971 LocationProviderProxy proxy = mProvidersByName.get(provider);
972 if (proxy == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 throw new IllegalArgumentException("provider=" + provider);
974 }
975
976 checkPermissionsSafe(provider);
977
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 // so wakelock calls will succeed
979 final int callingUid = Binder.getCallingUid();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400980 boolean newUid = !providerHasListener(provider, callingUid, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981 long identity = Binder.clearCallingIdentity();
982 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400983 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
984 UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800985 if (oldRecord != null) {
986 oldRecord.disposeLocked();
987 }
988
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400989 if (newUid) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400990 proxy.addListener(callingUid);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400991 }
992
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993 boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
994 if (isProviderEnabled) {
995 long minTimeForProvider = getMinTimeLocked(provider);
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400996 proxy.setMinTime(minTimeForProvider);
997 proxy.enableLocationTracking(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 } else {
Mike Lockwood48f17512009-04-23 09:12:08 -0700999 // Notify the listener that updates are currently disabled
1000 receiver.callProviderEnabledLocked(provider, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001 }
1002 } finally {
1003 Binder.restoreCallingIdentity(identity);
1004 }
1005 }
1006
1007 public void removeUpdates(ILocationListener listener) {
1008 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001009 synchronized (mLock) {
1010 removeUpdatesLocked(getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001011 }
1012 } catch (SecurityException se) {
1013 throw se;
1014 } catch (Exception e) {
1015 Log.e(TAG, "removeUpdates got exception:", e);
1016 }
1017 }
1018
1019 public void removeUpdatesPI(PendingIntent intent) {
1020 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001021 synchronized (mLock) {
1022 removeUpdatesLocked(getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023 }
1024 } catch (SecurityException se) {
1025 throw se;
1026 } catch (Exception e) {
1027 Log.e(TAG, "removeUpdates got exception:", e);
1028 }
1029 }
1030
1031 private void removeUpdatesLocked(Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001032 if (LOCAL_LOGV) {
1033 Log.v(TAG, "_removeUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 }
1035
1036 // so wakelock calls will succeed
1037 final int callingUid = Binder.getCallingUid();
1038 long identity = Binder.clearCallingIdentity();
1039 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001040 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1041 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 }
1043
1044 // Record which providers were associated with this listener
1045 HashSet<String> providers = new HashSet<String>();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001046 HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001047 if (oldRecords != null) {
1048 // Call dispose() on the obsolete update records.
1049 for (UpdateRecord record : oldRecords.values()) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001050 if (!providerHasListener(record.mProvider, callingUid, receiver)) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001051 LocationProviderProxy proxy = mProvidersByName.get(record.mProvider);
1052 if (proxy != null) {
1053 proxy.removeListener(callingUid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054 }
1055 }
1056 record.disposeLocked();
1057 }
1058 // Accumulate providers
1059 providers.addAll(oldRecords.keySet());
1060 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001061
1062 // See if the providers associated with this listener have any
1063 // other listeners; if one does, inform it of the new smallest minTime
1064 // value; if one does not, disable location tracking for it
1065 for (String provider : providers) {
1066 // If provider is already disabled, don't need to do anything
1067 if (!isAllowedBySettingsLocked(provider)) {
1068 continue;
1069 }
1070
1071 boolean hasOtherListener = false;
1072 ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1073 if (recordsForProvider != null && recordsForProvider.size() > 0) {
1074 hasOtherListener = true;
1075 }
1076
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001077 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001078 if (p != null) {
1079 if (hasOtherListener) {
1080 p.setMinTime(getMinTimeLocked(provider));
1081 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001082 p.enableLocationTracking(false);
1083 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 }
1085 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 } finally {
1087 Binder.restoreCallingIdentity(identity);
1088 }
1089 }
1090
1091 public boolean addGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001092 if (mGpsStatusProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001093 return false;
1094 }
1095 if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1096 PackageManager.PERMISSION_GRANTED) {
1097 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1098 }
1099
1100 try {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001101 mGpsStatusProvider.addGpsStatusListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 } catch (RemoteException e) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001103 Log.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 return false;
1105 }
1106 return true;
1107 }
1108
1109 public void removeGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001110 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001111 try {
1112 mGpsStatusProvider.removeGpsStatusListener(listener);
1113 } catch (Exception e) {
1114 Log.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
1115 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001116 }
1117 }
1118
1119 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1120 // first check for permission to the provider
1121 checkPermissionsSafe(provider);
1122 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1123 if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1124 != PackageManager.PERMISSION_GRANTED)) {
1125 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1126 }
1127
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001128 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001129 LocationProviderProxy proxy = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001130 if (provider == null) {
1131 return false;
1132 }
1133
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001134 return proxy.sendExtraCommand(command, extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001135 }
1136 }
1137
1138 class ProximityAlert {
1139 final int mUid;
1140 final double mLatitude;
1141 final double mLongitude;
1142 final float mRadius;
1143 final long mExpiration;
1144 final PendingIntent mIntent;
1145 final Location mLocation;
1146
1147 public ProximityAlert(int uid, double latitude, double longitude,
1148 float radius, long expiration, PendingIntent intent) {
1149 mUid = uid;
1150 mLatitude = latitude;
1151 mLongitude = longitude;
1152 mRadius = radius;
1153 mExpiration = expiration;
1154 mIntent = intent;
1155
1156 mLocation = new Location("");
1157 mLocation.setLatitude(latitude);
1158 mLocation.setLongitude(longitude);
1159 }
1160
1161 long getExpiration() {
1162 return mExpiration;
1163 }
1164
1165 PendingIntent getIntent() {
1166 return mIntent;
1167 }
1168
1169 boolean isInProximity(double latitude, double longitude) {
1170 Location loc = new Location("");
1171 loc.setLatitude(latitude);
1172 loc.setLongitude(longitude);
1173
1174 double radius = loc.distanceTo(mLocation);
1175 return radius <= mRadius;
1176 }
1177
1178 @Override
1179 public String toString() {
1180 return "ProximityAlert{"
1181 + Integer.toHexString(System.identityHashCode(this))
1182 + " uid " + mUid + mIntent + "}";
1183 }
1184
1185 void dump(PrintWriter pw, String prefix) {
1186 pw.println(prefix + this);
1187 pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
1188 pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
1189 pw.println(prefix + "mIntent=" + mIntent);
1190 pw.println(prefix + "mLocation:");
1191 mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
1192 }
1193 }
1194
1195 // Listener for receiving locations to trigger proximity alerts
Mike Lockwood48f17512009-04-23 09:12:08 -07001196 class ProximityListener extends ILocationListener.Stub implements PendingIntent.OnFinished {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001197
1198 boolean isGpsAvailable = false;
1199
1200 // Note: this is called with the lock held.
1201 public void onLocationChanged(Location loc) {
1202
1203 // If Gps is available, then ignore updates from NetworkLocationProvider
1204 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1205 isGpsAvailable = true;
1206 }
1207 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1208 return;
1209 }
1210
1211 // Process proximity alerts
1212 long now = System.currentTimeMillis();
1213 double latitude = loc.getLatitude();
1214 double longitude = loc.getLongitude();
1215 ArrayList<PendingIntent> intentsToRemove = null;
1216
1217 for (ProximityAlert alert : mProximityAlerts.values()) {
1218 PendingIntent intent = alert.getIntent();
1219 long expiration = alert.getExpiration();
1220
1221 if ((expiration == -1) || (now <= expiration)) {
1222 boolean entered = mProximitiesEntered.contains(alert);
1223 boolean inProximity =
1224 alert.isInProximity(latitude, longitude);
1225 if (!entered && inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001226 if (LOCAL_LOGV) {
1227 Log.v(TAG, "Entered alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228 }
1229 mProximitiesEntered.add(alert);
1230 Intent enteredIntent = new Intent();
1231 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1232 try {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001233 synchronized (this) {
1234 // synchronize to ensure incrementPendingBroadcasts()
Mike Lockwood48f17512009-04-23 09:12:08 -07001235 // is called before decrementPendingBroadcasts()
1236 intent.send(mContext, 0, enteredIntent, this, mLocationHandler);
1237 // call this after broadcasting so we do not increment
1238 // if we throw an exeption.
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001239 incrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -07001240 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001241 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001242 if (LOCAL_LOGV) {
1243 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001244 }
1245 if (intentsToRemove == null) {
1246 intentsToRemove = new ArrayList<PendingIntent>();
1247 }
1248 intentsToRemove.add(intent);
1249 }
1250 } else if (entered && !inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001251 if (LOCAL_LOGV) {
1252 Log.v(TAG, "Exited alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001253 }
1254 mProximitiesEntered.remove(alert);
1255 Intent exitedIntent = new Intent();
1256 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1257 try {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001258 synchronized (this) {
1259 // synchronize to ensure incrementPendingBroadcasts()
Mike Lockwood48f17512009-04-23 09:12:08 -07001260 // is called before decrementPendingBroadcasts()
1261 intent.send(mContext, 0, exitedIntent, this, mLocationHandler);
1262 // call this after broadcasting so we do not increment
1263 // if we throw an exeption.
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001264 incrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -07001265 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001266 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001267 if (LOCAL_LOGV) {
1268 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001269 }
1270 if (intentsToRemove == null) {
1271 intentsToRemove = new ArrayList<PendingIntent>();
1272 }
1273 intentsToRemove.add(intent);
1274 }
1275 }
1276 } else {
1277 // Mark alert for expiration
The Android Open Source Project10592532009-03-18 17:39:46 -07001278 if (LOCAL_LOGV) {
1279 Log.v(TAG, "Expiring proximity alert: " + alert);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001280 }
1281 if (intentsToRemove == null) {
1282 intentsToRemove = new ArrayList<PendingIntent>();
1283 }
1284 intentsToRemove.add(alert.getIntent());
1285 }
1286 }
1287
1288 // Remove expired alerts
1289 if (intentsToRemove != null) {
1290 for (PendingIntent i : intentsToRemove) {
1291 mProximityAlerts.remove(i);
1292 ProximityAlert alert = mProximityAlerts.get(i);
1293 mProximitiesEntered.remove(alert);
1294 }
1295 }
1296
1297 }
1298
1299 // Note: this is called with the lock held.
1300 public void onProviderDisabled(String provider) {
1301 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1302 isGpsAvailable = false;
1303 }
1304 }
1305
1306 // Note: this is called with the lock held.
1307 public void onProviderEnabled(String provider) {
1308 // ignore
1309 }
1310
1311 // Note: this is called with the lock held.
1312 public void onStatusChanged(String provider, int status, Bundle extras) {
1313 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1314 (status != LocationProvider.AVAILABLE)) {
1315 isGpsAvailable = false;
1316 }
1317 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001318
1319 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
1320 int resultCode, String resultData, Bundle resultExtras) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001321 // synchronize to ensure incrementPendingBroadcasts()
1322 // is called before decrementPendingBroadcasts()
1323 synchronized (this) {
1324 decrementPendingBroadcasts();
1325 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001326 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 }
1328
1329 public void addProximityAlert(double latitude, double longitude,
1330 float radius, long expiration, PendingIntent intent) {
1331 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001332 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001333 addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
1334 }
1335 } catch (SecurityException se) {
1336 throw se;
1337 } catch (Exception e) {
1338 Log.e(TAG, "addProximityAlert got exception:", e);
1339 }
1340 }
1341
1342 private void addProximityAlertLocked(double latitude, double longitude,
1343 float radius, long expiration, PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001344 if (LOCAL_LOGV) {
1345 Log.v(TAG, "addProximityAlert: latitude = " + latitude +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001346 ", longitude = " + longitude +
1347 ", expiration = " + expiration +
1348 ", intent = " + intent);
1349 }
1350
1351 // Require ability to access all providers for now
1352 if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
1353 !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
1354 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1355 }
1356
1357 if (expiration != -1) {
1358 expiration += System.currentTimeMillis();
1359 }
1360 ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
1361 latitude, longitude, radius, expiration, intent);
1362 mProximityAlerts.put(intent, alert);
1363
Mike Lockwood48f17512009-04-23 09:12:08 -07001364 if (mProximityReceiver == null) {
1365 mProximityListener = new ProximityListener();
1366 mProximityReceiver = new Receiver(mProximityListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001367
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001368 LocationProviderProxy provider = mProvidersByName.get(LocationManager.GPS_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001369 if (provider != null) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001370 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001371 }
1372
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001373 provider = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001374 if (provider != null) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001375 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001376 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001377 }
1378 }
1379
1380 public void removeProximityAlert(PendingIntent intent) {
1381 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001382 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001383 removeProximityAlertLocked(intent);
1384 }
1385 } catch (SecurityException se) {
1386 throw se;
1387 } catch (Exception e) {
1388 Log.e(TAG, "removeProximityAlert got exception:", e);
1389 }
1390 }
1391
1392 private void removeProximityAlertLocked(PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001393 if (LOCAL_LOGV) {
1394 Log.v(TAG, "removeProximityAlert: intent = " + intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001395 }
1396
1397 mProximityAlerts.remove(intent);
1398 if (mProximityAlerts.size() == 0) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001399 removeUpdatesLocked(mProximityReceiver);
1400 mProximityReceiver = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001401 mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001402 }
1403 }
1404
1405 /**
1406 * @return null if the provider does not exits
1407 * @throw SecurityException if the provider is not allowed to be
1408 * accessed by the caller
1409 */
1410 public Bundle getProviderInfo(String provider) {
1411 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001412 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001413 return _getProviderInfoLocked(provider);
1414 }
1415 } catch (SecurityException se) {
1416 throw se;
1417 } catch (Exception e) {
1418 Log.e(TAG, "_getProviderInfo got exception:", e);
1419 return null;
1420 }
1421 }
1422
1423 private Bundle _getProviderInfoLocked(String provider) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001424 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001425 if (p == null) {
1426 return null;
1427 }
1428
1429 checkPermissionsSafe(provider);
1430
1431 Bundle b = new Bundle();
1432 b.putBoolean("network", p.requiresNetwork());
1433 b.putBoolean("satellite", p.requiresSatellite());
1434 b.putBoolean("cell", p.requiresCell());
1435 b.putBoolean("cost", p.hasMonetaryCost());
1436 b.putBoolean("altitude", p.supportsAltitude());
1437 b.putBoolean("speed", p.supportsSpeed());
1438 b.putBoolean("bearing", p.supportsBearing());
1439 b.putInt("power", p.getPowerRequirement());
1440 b.putInt("accuracy", p.getAccuracy());
1441
1442 return b;
1443 }
1444
1445 public boolean isProviderEnabled(String provider) {
1446 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001447 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001448 return _isProviderEnabledLocked(provider);
1449 }
1450 } catch (SecurityException se) {
1451 throw se;
1452 } catch (Exception e) {
1453 Log.e(TAG, "isProviderEnabled got exception:", e);
1454 return false;
1455 }
1456 }
1457
Mike Lockwood275555c2009-05-01 11:30:34 -04001458 public void reportLocation(Location location) {
1459 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
1460 != PackageManager.PERMISSION_GRANTED) {
1461 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
1462 }
1463
Mike Lockwood4e50b782009-04-03 08:24:43 -07001464 mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
1465 Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
1466 mLocationHandler.sendMessageAtFrontOfQueue(m);
1467 }
1468
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001469 private boolean _isProviderEnabledLocked(String provider) {
1470 checkPermissionsSafe(provider);
1471
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001472 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001473 if (p == null) {
1474 throw new IllegalArgumentException("provider=" + provider);
1475 }
1476 return isAllowedBySettingsLocked(provider);
1477 }
1478
1479 public Location getLastKnownLocation(String provider) {
1480 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001481 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001482 return _getLastKnownLocationLocked(provider);
1483 }
1484 } catch (SecurityException se) {
1485 throw se;
1486 } catch (Exception e) {
1487 Log.e(TAG, "getLastKnownLocation got exception:", e);
1488 return null;
1489 }
1490 }
1491
1492 private Location _getLastKnownLocationLocked(String provider) {
1493 checkPermissionsSafe(provider);
1494
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001495 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001496 if (p == null) {
1497 throw new IllegalArgumentException("provider=" + provider);
1498 }
1499
1500 if (!isAllowedBySettingsLocked(provider)) {
1501 return null;
1502 }
1503
1504 Location location = mLastKnownLocation.get(provider);
1505 if (location == null) {
1506 // Get the persistent last known location for the provider
1507 location = readLastKnownLocationLocked(provider);
1508 if (location != null) {
1509 mLastKnownLocation.put(provider, location);
1510 }
1511 }
1512
1513 return location;
1514 }
1515
1516 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1517 // Always broadcast the first update
1518 if (lastLoc == null) {
1519 return true;
1520 }
1521
1522 // Don't broadcast same location again regardless of condition
1523 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1524 if (loc.getTime() == lastLoc.getTime()) {
1525 return false;
1526 }
1527
1528 // Check whether sufficient distance has been traveled
1529 double minDistance = record.mMinDistance;
1530 if (minDistance > 0.0) {
1531 if (loc.distanceTo(lastLoc) <= minDistance) {
1532 return false;
1533 }
1534 }
1535
1536 return true;
1537 }
1538
Mike Lockwood4e50b782009-04-03 08:24:43 -07001539 private void handleLocationChangedLocked(Location location) {
1540 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001541 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1542 if (records == null || records.size() == 0) {
1543 return;
1544 }
1545
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001546 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001547 if (p == null) {
1548 return;
1549 }
1550
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001551 // Update last known location for provider
Mike Lockwood4e50b782009-04-03 08:24:43 -07001552 Location lastLocation = mLastKnownLocation.get(provider);
1553 if (lastLocation == null) {
1554 mLastKnownLocation.put(provider, new Location(location));
1555 } else {
1556 lastLocation.set(location);
1557 }
1558 writeLastKnownLocationLocked(provider, location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001559
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001560 // Fetch latest status update time
1561 long newStatusUpdateTime = p.getStatusUpdateTime();
1562
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001563 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001564 Bundle extras = new Bundle();
1565 int status = p.getStatus(extras);
1566
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001567 ArrayList<Receiver> deadReceivers = null;
1568
1569 // Broadcast location or status to all listeners
1570 final int N = records.size();
1571 for (int i=0; i<N; i++) {
1572 UpdateRecord r = records.get(i);
1573 Receiver receiver = r.mReceiver;
1574
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001575 Location lastLoc = r.mLastFixBroadcast;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001576 if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1577 if (lastLoc == null) {
1578 lastLoc = new Location(location);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001579 r.mLastFixBroadcast = lastLoc;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001580 } else {
1581 lastLoc.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001582 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001583 if (!receiver.callLocationChangedLocked(location)) {
1584 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1585 if (deadReceivers == null) {
1586 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001587 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001588 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001589 }
1590 }
1591
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001592 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001593 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1594 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1595
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001596 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001597 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1598 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1599 if (deadReceivers == null) {
1600 deadReceivers = new ArrayList<Receiver>();
1601 }
1602 if (!deadReceivers.contains(receiver)) {
1603 deadReceivers.add(receiver);
1604 }
1605 }
1606 }
1607 }
1608
1609 if (deadReceivers != null) {
1610 for (int i=deadReceivers.size()-1; i>=0; i--) {
1611 removeUpdatesLocked(deadReceivers.get(i));
1612 }
1613 }
1614 }
1615
1616 private class LocationWorkerHandler extends Handler {
1617
1618 @Override
1619 public void handleMessage(Message msg) {
1620 try {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001621 if (msg.what == MESSAGE_LOCATION_CHANGED) {
1622 // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001623
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001624 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001625 Location location = (Location) msg.obj;
Mike Lockwood98cb6672009-04-17 18:03:44 -04001626
1627 if (mCollector != null &&
1628 LocationManager.GPS_PROVIDER.equals(location.getProvider())) {
1629 try {
1630 mCollector.updateLocation(location);
1631 } catch (RemoteException e) {
1632 Log.w(TAG, "mCollector.updateLocation failed");
1633 }
1634 }
1635
Mike Lockwood4e50b782009-04-03 08:24:43 -07001636 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001637 if (!isAllowedBySettingsLocked(provider)) {
1638 return;
1639 }
1640
Mike Lockwooda0e3cd32009-04-21 21:27:33 -07001641 handleLocationChangedLocked(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001642 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001643 }
1644 } catch (Exception e) {
1645 // Log, don't crash!
1646 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1647 }
1648 }
1649 }
1650
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001651 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1652 @Override
1653 public void onReceive(Context context, Intent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001654 String action = intent.getAction();
1655
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001656 if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001657 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001658 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001659 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1660 if (uid >= 0) {
1661 ArrayList<Receiver> removedRecs = null;
1662 for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
1663 for (int j=i.size()-1; j>=0; j--) {
1664 UpdateRecord ur = i.get(j);
1665 if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
1666 if (removedRecs == null) {
1667 removedRecs = new ArrayList<Receiver>();
1668 }
1669 if (!removedRecs.contains(ur.mReceiver)) {
1670 removedRecs.add(ur.mReceiver);
1671 }
1672 }
1673 }
1674 }
1675 ArrayList<ProximityAlert> removedAlerts = null;
1676 for (ProximityAlert i : mProximityAlerts.values()) {
1677 if (i.mUid == uid) {
1678 if (removedAlerts == null) {
1679 removedAlerts = new ArrayList<ProximityAlert>();
1680 }
1681 if (!removedAlerts.contains(i)) {
1682 removedAlerts.add(i);
1683 }
1684 }
1685 }
1686 if (removedRecs != null) {
1687 for (int i=removedRecs.size()-1; i>=0; i--) {
1688 removeUpdatesLocked(removedRecs.get(i));
1689 }
1690 }
1691 if (removedAlerts != null) {
1692 for (int i=removedAlerts.size()-1; i>=0; i--) {
1693 removeProximityAlertLocked(removedAlerts.get(i).mIntent);
1694 }
1695 }
1696 }
1697 }
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001698 } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001699 boolean noConnectivity =
1700 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1701 if (!noConnectivity) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001702 mNetworkState = LocationProvider.AVAILABLE;
1703 } else {
1704 mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001705 }
1706
1707 // Notify location providers of current network state
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001708 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001709 for (int i = mProviders.size() - 1; i >= 0; i--) {
1710 LocationProviderProxy provider = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001711 if (provider.requiresNetwork()) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001712 provider.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001713 }
1714 }
1715 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001716 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001717 }
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001718 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001719
1720 // Wake locks
1721
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001722 private void incrementPendingBroadcasts() {
1723 synchronized (mWakeLock) {
1724 if (mPendingBroadcasts++ == 0) {
1725 try {
1726 mWakeLock.acquire();
1727 log("Acquired wakelock");
1728 } catch (Exception e) {
1729 // This is to catch a runtime exception thrown when we try to release an
1730 // already released lock.
1731 Log.e(TAG, "exception in acquireWakeLock()", e);
1732 }
1733 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001734 }
1735 }
1736
1737 private void decrementPendingBroadcasts() {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001738 synchronized (mWakeLock) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001739 if (--mPendingBroadcasts == 0) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001740 try {
1741 // Release wake lock
1742 if (mWakeLock.isHeld()) {
1743 mWakeLock.release();
1744 log("Released wakelock");
1745 } else {
1746 log("Can't release wakelock again!");
1747 }
1748 } catch (Exception e) {
1749 // This is to catch a runtime exception thrown when we try to release an
1750 // already released lock.
1751 Log.e(TAG, "exception in releaseWakeLock()", e);
1752 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001753 }
1754 }
1755 }
1756
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001757 // Geocoder
1758
1759 public String getFromLocation(double latitude, double longitude, int maxResults,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001760 String language, String country, String variant, String appName, List<Address> addrs) {
1761 if (mGeocodeProvider != null) {
1762 try {
1763 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, language, country,
1764 variant, appName, addrs);
1765 } catch (RemoteException e) {
1766 Log.e(TAG, "getFromLocation failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001767 }
1768 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001769 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001770 }
1771
Mike Lockwooda55c3212009-04-15 11:10:11 -04001772
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001773 public String getFromLocationName(String locationName,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001774 double lowerLeftLatitude, double lowerLeftLongitude,
1775 double upperRightLatitude, double upperRightLongitude, int maxResults,
1776 String language, String country, String variant, String appName, List<Address> addrs) {
1777
1778 if (mGeocodeProvider != null) {
1779 try {
1780 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1781 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1782 maxResults, language, country, variant, appName, addrs);
1783 } catch (RemoteException e) {
1784 Log.e(TAG, "getFromLocationName failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001785 }
1786 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001787 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001788 }
1789
1790 // Mock Providers
1791
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001792 private void checkMockPermissionsSafe() {
1793 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1794 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1795 if (!allowMocks) {
1796 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1797 }
1798
1799 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1800 PackageManager.PERMISSION_GRANTED) {
1801 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1802 }
1803 }
1804
1805 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1806 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1807 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1808 checkMockPermissionsSafe();
1809
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001810 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001811 MockProvider provider = new MockProvider(name, this,
1812 requiresNetwork, requiresSatellite,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001813 requiresCell, hasMonetaryCost, supportsAltitude,
1814 supportsSpeed, supportsBearing, powerRequirement, accuracy);
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001815 if (mProvidersByName.get(name) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001816 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1817 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001818 addProvider(new LocationProviderProxy(name, provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001819 mMockProviders.put(name, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001820 updateProvidersLocked();
1821 }
1822 }
1823
1824 public void removeTestProvider(String provider) {
1825 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001826 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001827 MockProvider mockProvider = mMockProviders.get(provider);
1828 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001829 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1830 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001831 removeProvider(mProvidersByName.get(provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001832 mMockProviders.remove(mockProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001833 updateProvidersLocked();
1834 }
1835 }
1836
1837 public void setTestProviderLocation(String provider, Location loc) {
1838 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001839 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001840 MockProvider mockProvider = mMockProviders.get(provider);
1841 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001842 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1843 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001844 mockProvider.setLocation(loc);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001845 }
1846 }
1847
1848 public void clearTestProviderLocation(String provider) {
1849 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001850 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001851 MockProvider mockProvider = mMockProviders.get(provider);
1852 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001853 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1854 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001855 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001856 }
1857 }
1858
1859 public void setTestProviderEnabled(String provider, boolean enabled) {
1860 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001861 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001862 MockProvider mockProvider = mMockProviders.get(provider);
1863 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001864 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1865 }
1866 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001867 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001868 mEnabledProviders.add(provider);
1869 mDisabledProviders.remove(provider);
1870 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001871 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001872 mEnabledProviders.remove(provider);
1873 mDisabledProviders.add(provider);
1874 }
1875 updateProvidersLocked();
1876 }
1877 }
1878
1879 public void clearTestProviderEnabled(String provider) {
1880 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001881 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001882 MockProvider mockProvider = mMockProviders.get(provider);
1883 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001884 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1885 }
1886 mEnabledProviders.remove(provider);
1887 mDisabledProviders.remove(provider);
1888 updateProvidersLocked();
1889 }
1890 }
1891
1892 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1893 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001894 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001895 MockProvider mockProvider = mMockProviders.get(provider);
1896 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001897 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1898 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001899 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001900 }
1901 }
1902
1903 public void clearTestProviderStatus(String provider) {
1904 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001905 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001906 MockProvider mockProvider = mMockProviders.get(provider);
1907 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001908 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1909 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001910 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001911 }
1912 }
1913
1914 private void log(String log) {
1915 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1916 Log.d(TAG, log);
1917 }
1918 }
1919
1920 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1921 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1922 != PackageManager.PERMISSION_GRANTED) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001923 pw.println("Permission Denial: can't dump LocationManagerService from from pid="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001924 + Binder.getCallingPid()
1925 + ", uid=" + Binder.getCallingUid());
1926 return;
1927 }
1928
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001929 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001930 pw.println("Current Location Manager state:");
1931 pw.println(" sProvidersLoaded=" + sProvidersLoaded);
1932 pw.println(" mGpsLocationProvider=" + mGpsLocationProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001933 pw.println(" mNetworkLocationProvider=" + mNetworkLocationProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001934 pw.println(" mCollector=" + mCollector);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001935 pw.println(" Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001936 int N = mReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001937 for (int i=0; i<N; i++) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001938 pw.println(" " + mReceivers.get(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001939 }
1940 pw.println(" Location Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001941 for (Receiver i : mReceivers.values()) {
1942 pw.println(" " + i + ":");
1943 for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001944 pw.println(" " + j.getKey() + ":");
1945 j.getValue().dump(pw, " ");
1946 }
1947 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001948 pw.println(" Records by Provider:");
1949 for (Map.Entry<String, ArrayList<UpdateRecord>> i
1950 : mRecordsByProvider.entrySet()) {
1951 pw.println(" " + i.getKey() + ":");
1952 for (UpdateRecord j : i.getValue()) {
1953 pw.println(" " + j + ":");
1954 j.dump(pw, " ");
1955 }
1956 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001957 pw.println(" Last Known Locations:");
1958 for (Map.Entry<String, Location> i
1959 : mLastKnownLocation.entrySet()) {
1960 pw.println(" " + i.getKey() + ":");
1961 i.getValue().dump(new PrintWriterPrinter(pw), " ");
1962 }
1963 if (mProximityAlerts.size() > 0) {
1964 pw.println(" Proximity Alerts:");
1965 for (Map.Entry<PendingIntent, ProximityAlert> i
1966 : mProximityAlerts.entrySet()) {
1967 pw.println(" " + i.getKey() + ":");
1968 i.getValue().dump(pw, " ");
1969 }
1970 }
1971 if (mProximitiesEntered.size() > 0) {
1972 pw.println(" Proximities Entered:");
1973 for (ProximityAlert i : mProximitiesEntered) {
1974 pw.println(" " + i + ":");
1975 i.dump(pw, " ");
1976 }
1977 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001978 pw.println(" mProximityReceiver=" + mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001979 pw.println(" mProximityListener=" + mProximityListener);
1980 if (mEnabledProviders.size() > 0) {
1981 pw.println(" Enabled Providers:");
1982 for (String i : mEnabledProviders) {
1983 pw.println(" " + i);
1984 }
1985
1986 }
1987 if (mDisabledProviders.size() > 0) {
1988 pw.println(" Disabled Providers:");
1989 for (String i : mDisabledProviders) {
1990 pw.println(" " + i);
1991 }
1992
1993 }
1994 if (mMockProviders.size() > 0) {
1995 pw.println(" Mock Providers:");
1996 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001997 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001998 }
1999 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002000 }
2001 }
2002}