blob: 147a0851cab88e0bc0b45c83f53b27308a863a91 [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.location.ILocationListener;
50import android.location.ILocationManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070051import android.location.ILocationProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.location.Location;
53import android.location.LocationManager;
54import android.location.LocationProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.net.ConnectivityManager;
56import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.os.Binder;
58import android.os.Bundle;
59import android.os.Handler;
60import android.os.IBinder;
Mike Lockwood3d12b512009-04-21 23:25:35 -070061import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.os.Message;
63import android.os.PowerManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070064import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065import android.os.RemoteException;
66import android.os.SystemClock;
67import android.provider.Settings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.util.Config;
69import android.util.Log;
70import android.util.PrintWriterPrinter;
71import android.util.SparseIntArray;
72
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073import com.android.internal.location.GpsLocationProvider;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070074import com.android.internal.location.LocationProviderProxy;
Mike Lockwood7ec434e2009-03-27 07:46:48 -070075import com.android.internal.location.MockProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076import com.android.server.am.BatteryStatsService;
77
78/**
79 * The service class that manages LocationProviders and issues location
80 * updates and alerts.
81 *
82 * {@hide}
83 */
Mike Lockwood3d12b512009-04-21 23:25:35 -070084public class LocationManagerService extends ILocationManager.Stub implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 private static final String TAG = "LocationManagerService";
The Android Open Source Project10592532009-03-18 17:39:46 -070086 private static final boolean LOCAL_LOGV = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087
88 // Minimum time interval between last known location writes, in milliseconds.
89 private static final long MIN_LAST_KNOWN_LOCATION_TIME = 60L * 1000L;
90
91 // Max time to hold wake lock for, in milliseconds.
92 private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L;
93
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 // The last time a location was written, by provider name.
95 private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
96
97 private static final Pattern PATTERN_COMMA = Pattern.compile(",");
98
99 private static final String ACCESS_FINE_LOCATION =
100 android.Manifest.permission.ACCESS_FINE_LOCATION;
101 private static final String ACCESS_COARSE_LOCATION =
102 android.Manifest.permission.ACCESS_COARSE_LOCATION;
103 private static final String ACCESS_MOCK_LOCATION =
104 android.Manifest.permission.ACCESS_MOCK_LOCATION;
105 private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
106 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
Mike Lockwood275555c2009-05-01 11:30:34 -0400107 private static final String INSTALL_LOCATION_PROVIDER =
108 android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109
110 // Set of providers that are explicitly enabled
111 private final Set<String> mEnabledProviders = new HashSet<String>();
112
113 // Set of providers that are explicitly disabled
114 private final Set<String> mDisabledProviders = new HashSet<String>();
115
116 // Locations, status values, and extras for mock providers
Mike Lockwood7ec434e2009-03-27 07:46:48 -0700117 private final HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118
119 private static boolean sProvidersLoaded = false;
120
121 private final Context mContext;
Mike Lockwooda55c3212009-04-15 11:10:11 -0400122 private IGeocodeProvider mGeocodeProvider;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400123 private IGpsStatusProvider mGpsStatusProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 private LocationWorkerHandler mLocationHandler;
125
126 // Handler messages
Mike Lockwood4e50b782009-04-03 08:24:43 -0700127 private static final int MESSAGE_LOCATION_CHANGED = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400129 // wakelock variables
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 private final static String WAKELOCK_KEY = "LocationManagerService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 private PowerManager.WakeLock mWakeLock = null;
Mike Lockwood48f17512009-04-23 09:12:08 -0700132 private int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 /**
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400135 * List of all receivers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 */
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400137 private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400139
140 /**
141 * List of location providers.
142 */
143 private final ArrayList<LocationProviderProxy> mProviders =
144 new ArrayList<LocationProviderProxy>();
145 private final HashMap<String, LocationProviderProxy> mProvidersByName
146 = new HashMap<String, LocationProviderProxy>();
147
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 /**
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400149 * Object used internally for synchronization
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 */
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400151 private final Object mLock = new Object();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152
153 /**
154 * Mapping from provider name to all its UpdateRecords
155 */
156 private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
157 new HashMap<String,ArrayList<UpdateRecord>>();
158
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 // Proximity listeners
Mike Lockwood48f17512009-04-23 09:12:08 -0700160 private Receiver mProximityReceiver = null;
161 private ILocationListener mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
163 new HashMap<PendingIntent,ProximityAlert>();
164 private HashSet<ProximityAlert> mProximitiesEntered =
165 new HashSet<ProximityAlert>();
166
167 // Last known location for each provider
168 private HashMap<String,Location> mLastKnownLocation =
169 new HashMap<String,Location>();
170
The Android Open Source Project4df24232009-03-05 14:34:35 -0800171 private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800172
Mike Lockwood9637d472009-04-02 21:41:57 -0700173 // for Settings change notification
174 private ContentQueryMap mSettings;
175
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 /**
177 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
178 * location updates.
179 */
Mike Lockwood48f17512009-04-23 09:12:08 -0700180 private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 final ILocationListener mListener;
182 final PendingIntent mPendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 final Object mKey;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400184 final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
Mike Lockwood48f17512009-04-23 09:12:08 -0700185 int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400187 Receiver(ILocationListener listener) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 mListener = listener;
189 mPendingIntent = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 mKey = listener.asBinder();
191 }
192
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400193 Receiver(PendingIntent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 mPendingIntent = intent;
195 mListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 mKey = intent;
197 }
198
199 @Override
200 public boolean equals(Object otherObj) {
201 if (otherObj instanceof Receiver) {
202 return mKey.equals(
203 ((Receiver)otherObj).mKey);
204 }
205 return false;
206 }
207
208 @Override
209 public int hashCode() {
210 return mKey.hashCode();
211 }
Mike Lockwood3681f262009-05-12 10:52:03 -0400212
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 @Override
214 public String toString() {
215 if (mListener != null) {
216 return "Receiver{"
217 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400218 + " Listener " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 } else {
220 return "Receiver{"
221 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400222 + " Intent " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 }
224 }
225
226 public boolean isListener() {
227 return mListener != null;
228 }
229
230 public boolean isPendingIntent() {
231 return mPendingIntent != null;
232 }
233
234 public ILocationListener getListener() {
235 if (mListener != null) {
236 return mListener;
237 }
238 throw new IllegalStateException("Request for non-existent listener");
239 }
240
241 public PendingIntent getPendingIntent() {
242 if (mPendingIntent != null) {
243 return mPendingIntent;
244 }
245 throw new IllegalStateException("Request for non-existent intent");
246 }
247
248 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
249 if (mListener != null) {
250 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700251 synchronized (this) {
252 // synchronize to ensure incrementPendingBroadcastsLocked()
253 // is called before decrementPendingBroadcasts()
254 mListener.onStatusChanged(provider, status, extras);
255 if (mListener != mProximityListener) {
256 // call this after broadcasting so we do not increment
257 // if we throw an exeption.
258 incrementPendingBroadcastsLocked();
259 }
260 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261 } catch (RemoteException e) {
262 return false;
263 }
264 } else {
265 Intent statusChanged = new Intent();
266 statusChanged.putExtras(extras);
267 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
268 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700269 synchronized (this) {
270 // synchronize to ensure incrementPendingBroadcastsLocked()
271 // is called before decrementPendingBroadcasts()
272 mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler);
273 // call this after broadcasting so we do not increment
274 // if we throw an exeption.
275 incrementPendingBroadcastsLocked();
276 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 } catch (PendingIntent.CanceledException e) {
278 return false;
279 }
280 }
281 return true;
282 }
283
284 public boolean callLocationChangedLocked(Location location) {
285 if (mListener != null) {
286 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700287 synchronized (this) {
288 // synchronize to ensure incrementPendingBroadcastsLocked()
289 // is called before decrementPendingBroadcasts()
290 mListener.onLocationChanged(location);
291 if (mListener != mProximityListener) {
292 // call this after broadcasting so we do not increment
293 // if we throw an exeption.
294 incrementPendingBroadcastsLocked();
295 }
296 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 } catch (RemoteException e) {
298 return false;
299 }
300 } else {
301 Intent locationChanged = new Intent();
302 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
303 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700304 synchronized (this) {
305 // synchronize to ensure incrementPendingBroadcastsLocked()
306 // is called before decrementPendingBroadcasts()
307 mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler);
308 // call this after broadcasting so we do not increment
309 // if we throw an exeption.
310 incrementPendingBroadcastsLocked();
311 }
312 } catch (PendingIntent.CanceledException e) {
313 return false;
314 }
315 }
316 return true;
317 }
318
319 public boolean callProviderEnabledLocked(String provider, boolean enabled) {
320 if (mListener != null) {
321 try {
322 synchronized (this) {
323 // synchronize to ensure incrementPendingBroadcastsLocked()
324 // is called before decrementPendingBroadcasts()
325 if (enabled) {
326 mListener.onProviderEnabled(provider);
327 } else {
328 mListener.onProviderDisabled(provider);
329 }
330 if (mListener != mProximityListener) {
331 // call this after broadcasting so we do not increment
332 // if we throw an exeption.
333 incrementPendingBroadcastsLocked();
334 }
335 }
336 } catch (RemoteException e) {
337 return false;
338 }
339 } else {
340 Intent providerIntent = new Intent();
341 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
342 try {
343 synchronized (this) {
344 // synchronize to ensure incrementPendingBroadcastsLocked()
345 // is called before decrementPendingBroadcasts()
346 mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler);
347 // call this after broadcasting so we do not increment
348 // if we throw an exeption.
349 incrementPendingBroadcastsLocked();
350 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 } catch (PendingIntent.CanceledException e) {
352 return false;
353 }
354 }
355 return true;
356 }
357
358 public void binderDied() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700359 if (LOCAL_LOGV) {
360 Log.v(TAG, "Location listener died");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 }
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400362 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 removeUpdatesLocked(this);
364 }
Mike Lockwood48f17512009-04-23 09:12:08 -0700365 synchronized (this) {
366 if (mPendingBroadcasts > 0) {
367 LocationManagerService.this.decrementPendingBroadcasts();
368 mPendingBroadcasts = 0;
369 }
370 }
371 }
372
373 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
374 int resultCode, String resultData, Bundle resultExtras) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400375 synchronized (this) {
376 decrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700377 }
378 }
379
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400380 // this must be called while synchronized by caller in a synchronized block
381 // containing the sending of the broadcaset
382 private void incrementPendingBroadcastsLocked() {
383 if (mPendingBroadcasts++ == 0) {
384 LocationManagerService.this.incrementPendingBroadcasts();
385 }
386 }
387
388 private void decrementPendingBroadcastsLocked() {
389 if (--mPendingBroadcasts == 0) {
390 LocationManagerService.this.decrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -0700391 }
392 }
393 }
394
395 public void locationCallbackFinished(ILocationListener listener) {
396 Receiver receiver = getReceiver(listener);
397 if (receiver != null) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400398 synchronized (receiver) {
399 // so wakelock calls will succeed
400 long identity = Binder.clearCallingIdentity();
401 receiver.decrementPendingBroadcastsLocked();
402 Binder.restoreCallingIdentity(identity);
403 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 }
405 }
406
Mike Lockwood9637d472009-04-02 21:41:57 -0700407 private final class SettingsObserver implements Observer {
408 public void update(Observable o, Object arg) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400409 synchronized (mLock) {
Mike Lockwood9637d472009-04-02 21:41:57 -0700410 updateProvidersLocked();
411 }
412 }
413 }
414
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 private Location readLastKnownLocationLocked(String provider) {
416 Location location = null;
417 String s = null;
418 try {
419 File f = new File(LocationManager.SYSTEM_DIR + "/location."
420 + provider);
421 if (!f.exists()) {
422 return null;
423 }
424 BufferedReader reader = new BufferedReader(new FileReader(f), 256);
425 s = reader.readLine();
426 } catch (IOException e) {
427 Log.w(TAG, "Unable to read last known location", e);
428 }
429
430 if (s == null) {
431 return null;
432 }
433 try {
434 String[] tokens = PATTERN_COMMA.split(s);
435 int idx = 0;
436 long time = Long.parseLong(tokens[idx++]);
437 double latitude = Double.parseDouble(tokens[idx++]);
438 double longitude = Double.parseDouble(tokens[idx++]);
439 double altitude = Double.parseDouble(tokens[idx++]);
440 float bearing = Float.parseFloat(tokens[idx++]);
441 float speed = Float.parseFloat(tokens[idx++]);
442
443 location = new Location(provider);
444 location.setTime(time);
445 location.setLatitude(latitude);
446 location.setLongitude(longitude);
447 location.setAltitude(altitude);
448 location.setBearing(bearing);
449 location.setSpeed(speed);
450 } catch (NumberFormatException nfe) {
451 Log.e(TAG, "NumberFormatException reading last known location", nfe);
452 return null;
453 }
454
455 return location;
456 }
457
458 private void writeLastKnownLocationLocked(String provider,
459 Location location) {
460 long now = SystemClock.elapsedRealtime();
461 Long last = mLastWriteTime.get(provider);
462 if ((last != null)
463 && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
464 return;
465 }
466 mLastWriteTime.put(provider, now);
467
468 StringBuilder sb = new StringBuilder(100);
469 sb.append(location.getTime());
470 sb.append(',');
471 sb.append(location.getLatitude());
472 sb.append(',');
473 sb.append(location.getLongitude());
474 sb.append(',');
475 sb.append(location.getAltitude());
476 sb.append(',');
477 sb.append(location.getBearing());
478 sb.append(',');
479 sb.append(location.getSpeed());
480
481 FileWriter writer = null;
482 try {
483 File d = new File(LocationManager.SYSTEM_DIR);
484 if (!d.exists()) {
485 if (!d.mkdirs()) {
486 Log.w(TAG, "Unable to create directory to write location");
487 return;
488 }
489 }
490 File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
491 writer = new FileWriter(f);
492 writer.write(sb.toString());
493 } catch (IOException e) {
494 Log.w(TAG, "Unable to write location", e);
495 } finally {
496 if (writer != null) {
497 try {
498 writer.close();
499 } catch (IOException e) {
500 Log.w(TAG, "Exception closing file", e);
501 }
502 }
503 }
504 }
505
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400506 private void addProvider(LocationProviderProxy provider) {
507 mProviders.add(provider);
508 mProvidersByName.put(provider.getName(), provider);
509 }
510
511 private void removeProvider(LocationProviderProxy provider) {
512 mProviders.remove(provider);
513 mProvidersByName.remove(provider.getName());
514 }
515
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 private void loadProviders() {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400517 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800518 if (sProvidersLoaded) {
519 return;
520 }
521
522 // Load providers
523 loadProvidersLocked();
524 sProvidersLoaded = true;
525 }
526 }
527
528 private void loadProvidersLocked() {
529 try {
530 _loadProvidersLocked();
531 } catch (Exception e) {
532 Log.e(TAG, "Exception loading providers:", e);
533 }
534 }
535
536 private void _loadProvidersLocked() {
537 // Attempt to load "real" providers first
538 if (GpsLocationProvider.isSupported()) {
539 // Create a gps location provider
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400540 GpsLocationProvider provider = new GpsLocationProvider(mContext, this);
541 mGpsStatusProvider = provider.getGpsStatusProvider();
Mike Lockwood8dfe5d82009-05-07 11:49:01 -0400542 LocationProviderProxy proxy = new LocationProviderProxy(LocationManager.GPS_PROVIDER, provider);
543 addProvider(proxy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 }
545
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 updateProvidersLocked();
547 }
548
549 /**
550 * @param context the context that the LocationManagerService runs in
551 */
552 public LocationManagerService(Context context) {
553 super();
554 mContext = context;
Mike Lockwood3d12b512009-04-21 23:25:35 -0700555
556 Thread thread = new Thread(null, this, "LocationManagerService");
557 thread.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558
The Android Open Source Project10592532009-03-18 17:39:46 -0700559 if (LOCAL_LOGV) {
560 Log.v(TAG, "Constructed LocationManager Service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 }
Mike Lockwood3d12b512009-04-21 23:25:35 -0700562 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563
Mike Lockwood3d12b512009-04-21 23:25:35 -0700564 private void initialize() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 // Create a wake lock, needs to be done before calling loadProviders() below
566 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
567 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400568
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 // Load providers
570 loadProviders();
571
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572 // Register for Network (Wifi or Mobile) updates
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 IntentFilter intentFilter = new IntentFilter();
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400574 intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
575 // Register for Package Manager updates
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
577 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400578 mContext.registerReceiver(mBroadcastReceiver, intentFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579
Mike Lockwood9637d472009-04-02 21:41:57 -0700580 // listen for settings changes
581 ContentResolver resolver = mContext.getContentResolver();
582 Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
583 "(" + Settings.System.NAME + "=?)",
584 new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
585 null);
586 mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler);
587 SettingsObserver settingsObserver = new SettingsObserver();
588 mSettings.addObserver(settingsObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 }
590
Mike Lockwood3d12b512009-04-21 23:25:35 -0700591 public void run()
592 {
593 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
594 Looper.prepare();
595 mLocationHandler = new LocationWorkerHandler();
596 initialize();
597 Looper.loop();
598 }
599
Mike Lockwood275555c2009-05-01 11:30:34 -0400600 public void installLocationProvider(String name, ILocationProvider provider) {
601 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
602 != PackageManager.PERMISSION_GRANTED) {
603 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700604 }
605
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400606 synchronized (mLock) {
Mike Lockwood3681f262009-05-12 10:52:03 -0400607 // check to see if we are reinstalling a dead provider
608 LocationProviderProxy oldProvider = mProvidersByName.get(name);
609 if (oldProvider != null) {
610 if (oldProvider.isDead()) {
611 Log.d(TAG, "replacing dead provider");
612 removeProvider(oldProvider);
613 } else {
614 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
615 }
616 }
617
Mike Lockwood8dfe5d82009-05-07 11:49:01 -0400618 LocationProviderProxy proxy = new LocationProviderProxy(name, provider);
619 addProvider(proxy);
620 updateProvidersLocked();
Mike Lockwood275555c2009-05-01 11:30:34 -0400621
Mike Lockwood8dfe5d82009-05-07 11:49:01 -0400622 // notify provider of current network state
623 proxy.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 }
625 }
626
Mike Lockwood275555c2009-05-01 11:30:34 -0400627 public void installGeocodeProvider(IGeocodeProvider provider) {
628 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
629 != PackageManager.PERMISSION_GRANTED) {
630 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
Mike Lockwooda55c3212009-04-15 11:10:11 -0400631 }
632
633 mGeocodeProvider = provider;
634 }
635
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800636 private boolean isAllowedBySettingsLocked(String provider) {
637 if (mEnabledProviders.contains(provider)) {
638 return true;
639 }
640 if (mDisabledProviders.contains(provider)) {
641 return false;
642 }
643 // Use system settings
644 ContentResolver resolver = mContext.getContentResolver();
645 String allowedProviders = Settings.Secure.getString(resolver,
646 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
647
648 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
649 }
650
651 private void checkPermissionsSafe(String provider) {
652 if (LocationManager.GPS_PROVIDER.equals(provider)
653 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
654 != PackageManager.PERMISSION_GRANTED)) {
655 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
656 }
657 if (LocationManager.NETWORK_PROVIDER.equals(provider)
658 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
659 != PackageManager.PERMISSION_GRANTED)
660 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
661 != PackageManager.PERMISSION_GRANTED)) {
662 throw new SecurityException(
663 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
664 }
665 }
666
667 private boolean isAllowedProviderSafe(String provider) {
668 if (LocationManager.GPS_PROVIDER.equals(provider)
669 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
670 != PackageManager.PERMISSION_GRANTED)) {
671 return false;
672 }
673 if (LocationManager.NETWORK_PROVIDER.equals(provider)
674 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
675 != PackageManager.PERMISSION_GRANTED)
676 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
677 != PackageManager.PERMISSION_GRANTED)) {
678 return false;
679 }
680
681 return true;
682 }
683
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684 public List<String> getAllProviders() {
685 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400686 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687 return _getAllProvidersLocked();
688 }
689 } catch (SecurityException se) {
690 throw se;
691 } catch (Exception e) {
692 Log.e(TAG, "getAllProviders got exception:", e);
693 return null;
694 }
695 }
696
697 private List<String> _getAllProvidersLocked() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700698 if (LOCAL_LOGV) {
699 Log.v(TAG, "getAllProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400701 ArrayList<String> out = new ArrayList<String>(mProviders.size());
702 for (int i = mProviders.size() - 1; i >= 0; i--) {
703 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800704 out.add(p.getName());
705 }
706 return out;
707 }
708
709 public List<String> getProviders(boolean enabledOnly) {
710 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400711 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 return _getProvidersLocked(enabledOnly);
713 }
714 } catch (SecurityException se) {
715 throw se;
716 } catch (Exception e) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700717 Log.e(TAG, "getProviders got exception:", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 return null;
719 }
720 }
721
722 private List<String> _getProvidersLocked(boolean enabledOnly) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700723 if (LOCAL_LOGV) {
724 Log.v(TAG, "getProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400726 ArrayList<String> out = new ArrayList<String>(mProviders.size());
727 for (int i = mProviders.size() - 1; i >= 0; i--) {
728 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729 String name = p.getName();
730 if (isAllowedProviderSafe(name)) {
731 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
732 continue;
733 }
734 out.add(name);
735 }
736 }
737 return out;
738 }
739
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740 private void updateProvidersLocked() {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400741 for (int i = mProviders.size() - 1; i >= 0; i--) {
742 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800743 boolean isEnabled = p.isEnabled();
744 String name = p.getName();
745 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
746
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747 if (isEnabled && !shouldBeEnabled) {
748 updateProviderListenersLocked(name, false);
749 } else if (!isEnabled && shouldBeEnabled) {
750 updateProviderListenersLocked(name, true);
751 }
752
753 }
754 }
755
756 private void updateProviderListenersLocked(String provider, boolean enabled) {
757 int listeners = 0;
758
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400759 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 if (p == null) {
761 return;
762 }
763
764 ArrayList<Receiver> deadReceivers = null;
765
766 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
767 if (records != null) {
768 final int N = records.size();
769 for (int i=0; i<N; i++) {
770 UpdateRecord record = records.get(i);
771 // Sends a notification message to the receiver
Mike Lockwood48f17512009-04-23 09:12:08 -0700772 if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
773 if (deadReceivers == null) {
774 deadReceivers = new ArrayList<Receiver>();
775 deadReceivers.add(record.mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 }
778 listeners++;
779 }
780 }
781
782 if (deadReceivers != null) {
783 for (int i=deadReceivers.size()-1; i>=0; i--) {
784 removeUpdatesLocked(deadReceivers.get(i));
785 }
786 }
787
788 if (enabled) {
789 p.enable();
790 if (listeners > 0) {
791 p.setMinTime(getMinTimeLocked(provider));
792 p.enableLocationTracking(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 }
794 } else {
795 p.enableLocationTracking(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 p.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 }
799
800 private long getMinTimeLocked(String provider) {
801 long minTime = Long.MAX_VALUE;
802 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
803 if (records != null) {
804 for (int i=records.size()-1; i>=0; i--) {
805 minTime = Math.min(minTime, records.get(i).mMinTime);
806 }
807 }
808 return minTime;
809 }
810
811 private class UpdateRecord {
812 final String mProvider;
813 final Receiver mReceiver;
814 final long mMinTime;
815 final float mMinDistance;
816 final int mUid;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400817 Location mLastFixBroadcast;
818 long mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819
820 /**
821 * Note: must be constructed with lock held.
822 */
823 UpdateRecord(String provider, long minTime, float minDistance,
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400824 Receiver receiver, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 mProvider = provider;
826 mReceiver = receiver;
827 mMinTime = minTime;
828 mMinDistance = minDistance;
829 mUid = uid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830
831 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
832 if (records == null) {
833 records = new ArrayList<UpdateRecord>();
834 mRecordsByProvider.put(provider, records);
835 }
836 if (!records.contains(this)) {
837 records.add(this);
838 }
839 }
840
841 /**
842 * Method to be called when a record will no longer be used. Calling this multiple times
843 * must have the same effect as calling it once.
844 */
845 void disposeLocked() {
846 ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
847 records.remove(this);
848 }
849
850 @Override
851 public String toString() {
852 return "UpdateRecord{"
853 + Integer.toHexString(System.identityHashCode(this))
854 + " " + mProvider + " " + mReceiver + "}";
855 }
856
857 void dump(PrintWriter pw, String prefix) {
858 pw.println(prefix + this);
859 pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
860 pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400861 pw.println(prefix + "mUid=" + mUid);
862 pw.println(prefix + "mLastFixBroadcast:");
863 mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
864 pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865 }
866
867 /**
868 * Calls dispose().
869 */
870 @Override protected void finalize() {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400871 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 disposeLocked();
873 }
874 }
875 }
876
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400877 private Receiver getReceiver(ILocationListener listener) {
878 IBinder binder = listener.asBinder();
879 Receiver receiver = mReceivers.get(binder);
880 if (receiver == null) {
881 receiver = new Receiver(listener);
882 mReceivers.put(binder, receiver);
883
884 try {
885 if (receiver.isListener()) {
886 receiver.getListener().asBinder().linkToDeath(receiver, 0);
887 }
888 } catch (RemoteException e) {
889 Log.e(TAG, "linkToDeath failed:", e);
890 return null;
891 }
892 }
893 return receiver;
894 }
895
896 private Receiver getReceiver(PendingIntent intent) {
897 Receiver receiver = mReceivers.get(intent);
898 if (receiver == null) {
899 receiver = new Receiver(intent);
900 mReceivers.put(intent, receiver);
901 }
902 return receiver;
903 }
904
905 private boolean providerHasListener(String provider, int uid, Receiver excludedReceiver) {
906 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
907 if (records != null) {
908 for (int i = records.size() - 1; i >= 0; i--) {
909 UpdateRecord record = records.get(i);
910 if (record.mUid == uid && record.mReceiver != excludedReceiver) {
911 return true;
912 }
913 }
914 }
Mike Lockwood95427cd2009-05-07 13:27:54 -0400915 for (ProximityAlert alert : mProximityAlerts.values()) {
916 if (alert.mUid == uid) {
917 return true;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400918 }
919 }
920 return false;
921 }
922
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923 public void requestLocationUpdates(String provider,
924 long minTime, float minDistance, ILocationListener listener) {
925
926 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400927 synchronized (mLock) {
928 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 }
930 } catch (SecurityException se) {
931 throw se;
932 } catch (Exception e) {
933 Log.e(TAG, "requestUpdates got exception:", e);
934 }
935 }
936
937 public void requestLocationUpdatesPI(String provider,
938 long minTime, float minDistance, PendingIntent intent) {
939 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400940 synchronized (mLock) {
941 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800942 }
943 } catch (SecurityException se) {
944 throw se;
945 } catch (Exception e) {
946 Log.e(TAG, "requestUpdates got exception:", e);
947 }
948 }
949
950 private void requestLocationUpdatesLocked(String provider,
951 long minTime, float minDistance, Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700952 if (LOCAL_LOGV) {
953 Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 }
955
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400956 LocationProviderProxy proxy = mProvidersByName.get(provider);
957 if (proxy == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800958 throw new IllegalArgumentException("provider=" + provider);
959 }
960
961 checkPermissionsSafe(provider);
962
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963 // so wakelock calls will succeed
964 final int callingUid = Binder.getCallingUid();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400965 boolean newUid = !providerHasListener(provider, callingUid, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800966 long identity = Binder.clearCallingIdentity();
967 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400968 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
969 UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800970 if (oldRecord != null) {
971 oldRecord.disposeLocked();
972 }
973
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400974 if (newUid) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400975 proxy.addListener(callingUid);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400976 }
977
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
979 if (isProviderEnabled) {
980 long minTimeForProvider = getMinTimeLocked(provider);
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400981 proxy.setMinTime(minTimeForProvider);
982 proxy.enableLocationTracking(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800983 } else {
Mike Lockwood48f17512009-04-23 09:12:08 -0700984 // Notify the listener that updates are currently disabled
985 receiver.callProviderEnabledLocked(provider, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986 }
987 } finally {
988 Binder.restoreCallingIdentity(identity);
989 }
990 }
991
992 public void removeUpdates(ILocationListener listener) {
993 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400994 synchronized (mLock) {
995 removeUpdatesLocked(getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 }
997 } catch (SecurityException se) {
998 throw se;
999 } catch (Exception e) {
1000 Log.e(TAG, "removeUpdates got exception:", e);
1001 }
1002 }
1003
1004 public void removeUpdatesPI(PendingIntent intent) {
1005 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001006 synchronized (mLock) {
1007 removeUpdatesLocked(getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001008 }
1009 } catch (SecurityException se) {
1010 throw se;
1011 } catch (Exception e) {
1012 Log.e(TAG, "removeUpdates got exception:", e);
1013 }
1014 }
1015
1016 private void removeUpdatesLocked(Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001017 if (LOCAL_LOGV) {
1018 Log.v(TAG, "_removeUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 }
1020
1021 // so wakelock calls will succeed
1022 final int callingUid = Binder.getCallingUid();
1023 long identity = Binder.clearCallingIdentity();
1024 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001025 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1026 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027 }
1028
1029 // Record which providers were associated with this listener
1030 HashSet<String> providers = new HashSet<String>();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001031 HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032 if (oldRecords != null) {
1033 // Call dispose() on the obsolete update records.
1034 for (UpdateRecord record : oldRecords.values()) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001035 if (!providerHasListener(record.mProvider, callingUid, receiver)) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001036 LocationProviderProxy proxy = mProvidersByName.get(record.mProvider);
1037 if (proxy != null) {
1038 proxy.removeListener(callingUid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 }
1040 }
1041 record.disposeLocked();
1042 }
1043 // Accumulate providers
1044 providers.addAll(oldRecords.keySet());
1045 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001046
1047 // See if the providers associated with this listener have any
1048 // other listeners; if one does, inform it of the new smallest minTime
1049 // value; if one does not, disable location tracking for it
1050 for (String provider : providers) {
1051 // If provider is already disabled, don't need to do anything
1052 if (!isAllowedBySettingsLocked(provider)) {
1053 continue;
1054 }
1055
1056 boolean hasOtherListener = false;
1057 ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1058 if (recordsForProvider != null && recordsForProvider.size() > 0) {
1059 hasOtherListener = true;
1060 }
1061
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001062 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063 if (p != null) {
1064 if (hasOtherListener) {
1065 p.setMinTime(getMinTimeLocked(provider));
1066 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067 p.enableLocationTracking(false);
1068 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001069 }
1070 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001071 } finally {
1072 Binder.restoreCallingIdentity(identity);
1073 }
1074 }
1075
1076 public boolean addGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001077 if (mGpsStatusProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001078 return false;
1079 }
1080 if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1081 PackageManager.PERMISSION_GRANTED) {
1082 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1083 }
1084
1085 try {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001086 mGpsStatusProvider.addGpsStatusListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087 } catch (RemoteException e) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001088 Log.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 return false;
1090 }
1091 return true;
1092 }
1093
1094 public void removeGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001095 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001096 try {
1097 mGpsStatusProvider.removeGpsStatusListener(listener);
1098 } catch (Exception e) {
1099 Log.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
1100 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001101 }
1102 }
1103
1104 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1105 // first check for permission to the provider
1106 checkPermissionsSafe(provider);
1107 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1108 if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1109 != PackageManager.PERMISSION_GRANTED)) {
1110 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1111 }
1112
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001113 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001114 LocationProviderProxy proxy = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001115 if (provider == null) {
1116 return false;
1117 }
1118
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001119 return proxy.sendExtraCommand(command, extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001120 }
1121 }
1122
1123 class ProximityAlert {
1124 final int mUid;
1125 final double mLatitude;
1126 final double mLongitude;
1127 final float mRadius;
1128 final long mExpiration;
1129 final PendingIntent mIntent;
1130 final Location mLocation;
1131
1132 public ProximityAlert(int uid, double latitude, double longitude,
1133 float radius, long expiration, PendingIntent intent) {
1134 mUid = uid;
1135 mLatitude = latitude;
1136 mLongitude = longitude;
1137 mRadius = radius;
1138 mExpiration = expiration;
1139 mIntent = intent;
1140
1141 mLocation = new Location("");
1142 mLocation.setLatitude(latitude);
1143 mLocation.setLongitude(longitude);
1144 }
1145
1146 long getExpiration() {
1147 return mExpiration;
1148 }
1149
1150 PendingIntent getIntent() {
1151 return mIntent;
1152 }
1153
1154 boolean isInProximity(double latitude, double longitude) {
1155 Location loc = new Location("");
1156 loc.setLatitude(latitude);
1157 loc.setLongitude(longitude);
1158
1159 double radius = loc.distanceTo(mLocation);
1160 return radius <= mRadius;
1161 }
1162
1163 @Override
1164 public String toString() {
1165 return "ProximityAlert{"
1166 + Integer.toHexString(System.identityHashCode(this))
1167 + " uid " + mUid + mIntent + "}";
1168 }
1169
1170 void dump(PrintWriter pw, String prefix) {
1171 pw.println(prefix + this);
1172 pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
1173 pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
1174 pw.println(prefix + "mIntent=" + mIntent);
1175 pw.println(prefix + "mLocation:");
1176 mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
1177 }
1178 }
1179
1180 // Listener for receiving locations to trigger proximity alerts
Mike Lockwood48f17512009-04-23 09:12:08 -07001181 class ProximityListener extends ILocationListener.Stub implements PendingIntent.OnFinished {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182
1183 boolean isGpsAvailable = false;
1184
1185 // Note: this is called with the lock held.
1186 public void onLocationChanged(Location loc) {
1187
1188 // If Gps is available, then ignore updates from NetworkLocationProvider
1189 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1190 isGpsAvailable = true;
1191 }
1192 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1193 return;
1194 }
1195
1196 // Process proximity alerts
1197 long now = System.currentTimeMillis();
1198 double latitude = loc.getLatitude();
1199 double longitude = loc.getLongitude();
1200 ArrayList<PendingIntent> intentsToRemove = null;
1201
1202 for (ProximityAlert alert : mProximityAlerts.values()) {
1203 PendingIntent intent = alert.getIntent();
1204 long expiration = alert.getExpiration();
1205
1206 if ((expiration == -1) || (now <= expiration)) {
1207 boolean entered = mProximitiesEntered.contains(alert);
1208 boolean inProximity =
1209 alert.isInProximity(latitude, longitude);
1210 if (!entered && inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001211 if (LOCAL_LOGV) {
1212 Log.v(TAG, "Entered alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001213 }
1214 mProximitiesEntered.add(alert);
1215 Intent enteredIntent = new Intent();
1216 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1217 try {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001218 synchronized (this) {
1219 // synchronize to ensure incrementPendingBroadcasts()
Mike Lockwood48f17512009-04-23 09:12:08 -07001220 // is called before decrementPendingBroadcasts()
1221 intent.send(mContext, 0, enteredIntent, this, mLocationHandler);
1222 // call this after broadcasting so we do not increment
1223 // if we throw an exeption.
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001224 incrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -07001225 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001226 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001227 if (LOCAL_LOGV) {
1228 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001229 }
1230 if (intentsToRemove == null) {
1231 intentsToRemove = new ArrayList<PendingIntent>();
1232 }
1233 intentsToRemove.add(intent);
1234 }
1235 } else if (entered && !inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001236 if (LOCAL_LOGV) {
1237 Log.v(TAG, "Exited alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001238 }
1239 mProximitiesEntered.remove(alert);
1240 Intent exitedIntent = new Intent();
1241 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1242 try {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001243 synchronized (this) {
1244 // synchronize to ensure incrementPendingBroadcasts()
Mike Lockwood48f17512009-04-23 09:12:08 -07001245 // is called before decrementPendingBroadcasts()
1246 intent.send(mContext, 0, exitedIntent, this, mLocationHandler);
1247 // call this after broadcasting so we do not increment
1248 // if we throw an exeption.
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001249 incrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -07001250 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001251 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001252 if (LOCAL_LOGV) {
1253 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001254 }
1255 if (intentsToRemove == null) {
1256 intentsToRemove = new ArrayList<PendingIntent>();
1257 }
1258 intentsToRemove.add(intent);
1259 }
1260 }
1261 } else {
1262 // Mark alert for expiration
The Android Open Source Project10592532009-03-18 17:39:46 -07001263 if (LOCAL_LOGV) {
1264 Log.v(TAG, "Expiring proximity alert: " + alert);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001265 }
1266 if (intentsToRemove == null) {
1267 intentsToRemove = new ArrayList<PendingIntent>();
1268 }
1269 intentsToRemove.add(alert.getIntent());
1270 }
1271 }
1272
1273 // Remove expired alerts
1274 if (intentsToRemove != null) {
1275 for (PendingIntent i : intentsToRemove) {
1276 mProximityAlerts.remove(i);
1277 ProximityAlert alert = mProximityAlerts.get(i);
1278 mProximitiesEntered.remove(alert);
1279 }
1280 }
1281
1282 }
1283
1284 // Note: this is called with the lock held.
1285 public void onProviderDisabled(String provider) {
1286 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1287 isGpsAvailable = false;
1288 }
1289 }
1290
1291 // Note: this is called with the lock held.
1292 public void onProviderEnabled(String provider) {
1293 // ignore
1294 }
1295
1296 // Note: this is called with the lock held.
1297 public void onStatusChanged(String provider, int status, Bundle extras) {
1298 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1299 (status != LocationProvider.AVAILABLE)) {
1300 isGpsAvailable = false;
1301 }
1302 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001303
1304 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
1305 int resultCode, String resultData, Bundle resultExtras) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001306 // synchronize to ensure incrementPendingBroadcasts()
1307 // is called before decrementPendingBroadcasts()
1308 synchronized (this) {
1309 decrementPendingBroadcasts();
1310 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001311 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312 }
1313
1314 public void addProximityAlert(double latitude, double longitude,
1315 float radius, long expiration, PendingIntent intent) {
1316 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001317 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001318 addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
1319 }
1320 } catch (SecurityException se) {
1321 throw se;
1322 } catch (Exception e) {
1323 Log.e(TAG, "addProximityAlert got exception:", e);
1324 }
1325 }
1326
1327 private void addProximityAlertLocked(double latitude, double longitude,
1328 float radius, long expiration, PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001329 if (LOCAL_LOGV) {
1330 Log.v(TAG, "addProximityAlert: latitude = " + latitude +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001331 ", longitude = " + longitude +
1332 ", expiration = " + expiration +
1333 ", intent = " + intent);
1334 }
1335
1336 // Require ability to access all providers for now
1337 if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
1338 !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
1339 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1340 }
1341
1342 if (expiration != -1) {
1343 expiration += System.currentTimeMillis();
1344 }
1345 ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
1346 latitude, longitude, radius, expiration, intent);
1347 mProximityAlerts.put(intent, alert);
1348
Mike Lockwood48f17512009-04-23 09:12:08 -07001349 if (mProximityReceiver == null) {
1350 mProximityListener = new ProximityListener();
1351 mProximityReceiver = new Receiver(mProximityListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001352
Mike Lockwood95427cd2009-05-07 13:27:54 -04001353 for (int i = mProviders.size() - 1; i >= 0; i--) {
1354 LocationProviderProxy provider = mProviders.get(i);
Mike Lockwood48f17512009-04-23 09:12:08 -07001355 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001356 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001357 }
1358 }
1359
1360 public void removeProximityAlert(PendingIntent intent) {
1361 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001362 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001363 removeProximityAlertLocked(intent);
1364 }
1365 } catch (SecurityException se) {
1366 throw se;
1367 } catch (Exception e) {
1368 Log.e(TAG, "removeProximityAlert got exception:", e);
1369 }
1370 }
1371
1372 private void removeProximityAlertLocked(PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001373 if (LOCAL_LOGV) {
1374 Log.v(TAG, "removeProximityAlert: intent = " + intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001375 }
1376
1377 mProximityAlerts.remove(intent);
1378 if (mProximityAlerts.size() == 0) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001379 removeUpdatesLocked(mProximityReceiver);
1380 mProximityReceiver = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001381 mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001382 }
1383 }
1384
1385 /**
1386 * @return null if the provider does not exits
1387 * @throw SecurityException if the provider is not allowed to be
1388 * accessed by the caller
1389 */
1390 public Bundle getProviderInfo(String provider) {
1391 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001392 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393 return _getProviderInfoLocked(provider);
1394 }
1395 } catch (SecurityException se) {
1396 throw se;
1397 } catch (Exception e) {
1398 Log.e(TAG, "_getProviderInfo got exception:", e);
1399 return null;
1400 }
1401 }
1402
1403 private Bundle _getProviderInfoLocked(String provider) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001404 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001405 if (p == null) {
1406 return null;
1407 }
1408
1409 checkPermissionsSafe(provider);
1410
1411 Bundle b = new Bundle();
1412 b.putBoolean("network", p.requiresNetwork());
1413 b.putBoolean("satellite", p.requiresSatellite());
1414 b.putBoolean("cell", p.requiresCell());
1415 b.putBoolean("cost", p.hasMonetaryCost());
1416 b.putBoolean("altitude", p.supportsAltitude());
1417 b.putBoolean("speed", p.supportsSpeed());
1418 b.putBoolean("bearing", p.supportsBearing());
1419 b.putInt("power", p.getPowerRequirement());
1420 b.putInt("accuracy", p.getAccuracy());
1421
1422 return b;
1423 }
1424
1425 public boolean isProviderEnabled(String provider) {
1426 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001427 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001428 return _isProviderEnabledLocked(provider);
1429 }
1430 } catch (SecurityException se) {
1431 throw se;
1432 } catch (Exception e) {
1433 Log.e(TAG, "isProviderEnabled got exception:", e);
1434 return false;
1435 }
1436 }
1437
Mike Lockwood275555c2009-05-01 11:30:34 -04001438 public void reportLocation(Location location) {
1439 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
1440 != PackageManager.PERMISSION_GRANTED) {
1441 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
1442 }
1443
Mike Lockwood4e50b782009-04-03 08:24:43 -07001444 mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
1445 Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
1446 mLocationHandler.sendMessageAtFrontOfQueue(m);
1447 }
1448
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 private boolean _isProviderEnabledLocked(String provider) {
1450 checkPermissionsSafe(provider);
1451
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001452 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 if (p == null) {
1454 throw new IllegalArgumentException("provider=" + provider);
1455 }
1456 return isAllowedBySettingsLocked(provider);
1457 }
1458
1459 public Location getLastKnownLocation(String provider) {
1460 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001461 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001462 return _getLastKnownLocationLocked(provider);
1463 }
1464 } catch (SecurityException se) {
1465 throw se;
1466 } catch (Exception e) {
1467 Log.e(TAG, "getLastKnownLocation got exception:", e);
1468 return null;
1469 }
1470 }
1471
1472 private Location _getLastKnownLocationLocked(String provider) {
1473 checkPermissionsSafe(provider);
1474
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001475 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001476 if (p == null) {
1477 throw new IllegalArgumentException("provider=" + provider);
1478 }
1479
1480 if (!isAllowedBySettingsLocked(provider)) {
1481 return null;
1482 }
1483
1484 Location location = mLastKnownLocation.get(provider);
1485 if (location == null) {
1486 // Get the persistent last known location for the provider
1487 location = readLastKnownLocationLocked(provider);
1488 if (location != null) {
1489 mLastKnownLocation.put(provider, location);
1490 }
1491 }
1492
1493 return location;
1494 }
1495
1496 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1497 // Always broadcast the first update
1498 if (lastLoc == null) {
1499 return true;
1500 }
1501
1502 // Don't broadcast same location again regardless of condition
1503 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1504 if (loc.getTime() == lastLoc.getTime()) {
1505 return false;
1506 }
1507
1508 // Check whether sufficient distance has been traveled
1509 double minDistance = record.mMinDistance;
1510 if (minDistance > 0.0) {
1511 if (loc.distanceTo(lastLoc) <= minDistance) {
1512 return false;
1513 }
1514 }
1515
1516 return true;
1517 }
1518
Mike Lockwood4e50b782009-04-03 08:24:43 -07001519 private void handleLocationChangedLocked(Location location) {
1520 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001521 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1522 if (records == null || records.size() == 0) {
1523 return;
1524 }
1525
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001526 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001527 if (p == null) {
1528 return;
1529 }
1530
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001531 // Update last known location for provider
Mike Lockwood4e50b782009-04-03 08:24:43 -07001532 Location lastLocation = mLastKnownLocation.get(provider);
1533 if (lastLocation == null) {
1534 mLastKnownLocation.put(provider, new Location(location));
1535 } else {
1536 lastLocation.set(location);
1537 }
1538 writeLastKnownLocationLocked(provider, location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001540 // Fetch latest status update time
1541 long newStatusUpdateTime = p.getStatusUpdateTime();
1542
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001543 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001544 Bundle extras = new Bundle();
1545 int status = p.getStatus(extras);
1546
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001547 ArrayList<Receiver> deadReceivers = null;
1548
1549 // Broadcast location or status to all listeners
1550 final int N = records.size();
1551 for (int i=0; i<N; i++) {
1552 UpdateRecord r = records.get(i);
1553 Receiver receiver = r.mReceiver;
1554
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001555 Location lastLoc = r.mLastFixBroadcast;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001556 if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1557 if (lastLoc == null) {
1558 lastLoc = new Location(location);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001559 r.mLastFixBroadcast = lastLoc;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001560 } else {
1561 lastLoc.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001562 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001563 if (!receiver.callLocationChangedLocked(location)) {
1564 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1565 if (deadReceivers == null) {
1566 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001567 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001568 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001569 }
1570 }
1571
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001572 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001573 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1574 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1575
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001576 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001577 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1578 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1579 if (deadReceivers == null) {
1580 deadReceivers = new ArrayList<Receiver>();
1581 }
1582 if (!deadReceivers.contains(receiver)) {
1583 deadReceivers.add(receiver);
1584 }
1585 }
1586 }
1587 }
1588
1589 if (deadReceivers != null) {
1590 for (int i=deadReceivers.size()-1; i>=0; i--) {
1591 removeUpdatesLocked(deadReceivers.get(i));
1592 }
1593 }
1594 }
1595
1596 private class LocationWorkerHandler extends Handler {
1597
1598 @Override
1599 public void handleMessage(Message msg) {
1600 try {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001601 if (msg.what == MESSAGE_LOCATION_CHANGED) {
1602 // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001603
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001604 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001605 Location location = (Location) msg.obj;
Mike Lockwoodfd6e5f02009-05-21 11:28:20 -04001606 String provider = location.getProvider();
Mike Lockwood98cb6672009-04-17 18:03:44 -04001607
Mike Lockwoodfd6e5f02009-05-21 11:28:20 -04001608 // notify other providers of the new location
1609 for (int i = mProviders.size() - 1; i >= 0; i--) {
1610 LocationProviderProxy proxy = mProviders.get(i);
1611 if (!provider.equals(proxy.getName())) {
1612 proxy.updateLocation(location);
Mike Lockwood98cb6672009-04-17 18:03:44 -04001613 }
1614 }
1615
Mike Lockwoodfd6e5f02009-05-21 11:28:20 -04001616 if (isAllowedBySettingsLocked(provider)) {
1617 handleLocationChangedLocked(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001618 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001619 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001620 }
1621 } catch (Exception e) {
1622 // Log, don't crash!
1623 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1624 }
1625 }
1626 }
1627
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001628 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1629 @Override
1630 public void onReceive(Context context, Intent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001631 String action = intent.getAction();
1632
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001633 if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001634 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001635 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001636 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1637 if (uid >= 0) {
1638 ArrayList<Receiver> removedRecs = null;
1639 for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
1640 for (int j=i.size()-1; j>=0; j--) {
1641 UpdateRecord ur = i.get(j);
1642 if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
1643 if (removedRecs == null) {
1644 removedRecs = new ArrayList<Receiver>();
1645 }
1646 if (!removedRecs.contains(ur.mReceiver)) {
1647 removedRecs.add(ur.mReceiver);
1648 }
1649 }
1650 }
1651 }
1652 ArrayList<ProximityAlert> removedAlerts = null;
1653 for (ProximityAlert i : mProximityAlerts.values()) {
1654 if (i.mUid == uid) {
1655 if (removedAlerts == null) {
1656 removedAlerts = new ArrayList<ProximityAlert>();
1657 }
1658 if (!removedAlerts.contains(i)) {
1659 removedAlerts.add(i);
1660 }
1661 }
1662 }
1663 if (removedRecs != null) {
1664 for (int i=removedRecs.size()-1; i>=0; i--) {
1665 removeUpdatesLocked(removedRecs.get(i));
1666 }
1667 }
1668 if (removedAlerts != null) {
1669 for (int i=removedAlerts.size()-1; i>=0; i--) {
1670 removeProximityAlertLocked(removedAlerts.get(i).mIntent);
1671 }
1672 }
1673 }
1674 }
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001675 } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001676 boolean noConnectivity =
1677 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1678 if (!noConnectivity) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001679 mNetworkState = LocationProvider.AVAILABLE;
1680 } else {
1681 mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001682 }
1683
1684 // Notify location providers of current network state
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001685 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001686 for (int i = mProviders.size() - 1; i >= 0; i--) {
1687 LocationProviderProxy provider = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001688 if (provider.requiresNetwork()) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001689 provider.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001690 }
1691 }
1692 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001693 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001694 }
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001695 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001696
1697 // Wake locks
1698
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001699 private void incrementPendingBroadcasts() {
1700 synchronized (mWakeLock) {
1701 if (mPendingBroadcasts++ == 0) {
1702 try {
1703 mWakeLock.acquire();
1704 log("Acquired wakelock");
1705 } catch (Exception e) {
1706 // This is to catch a runtime exception thrown when we try to release an
1707 // already released lock.
1708 Log.e(TAG, "exception in acquireWakeLock()", e);
1709 }
1710 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001711 }
1712 }
1713
1714 private void decrementPendingBroadcasts() {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001715 synchronized (mWakeLock) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001716 if (--mPendingBroadcasts == 0) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001717 try {
1718 // Release wake lock
1719 if (mWakeLock.isHeld()) {
1720 mWakeLock.release();
1721 log("Released wakelock");
1722 } else {
1723 log("Can't release wakelock again!");
1724 }
1725 } catch (Exception e) {
1726 // This is to catch a runtime exception thrown when we try to release an
1727 // already released lock.
1728 Log.e(TAG, "exception in releaseWakeLock()", e);
1729 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001730 }
1731 }
1732 }
1733
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001734 // Geocoder
1735
1736 public String getFromLocation(double latitude, double longitude, int maxResults,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001737 String language, String country, String variant, String appName, List<Address> addrs) {
1738 if (mGeocodeProvider != null) {
1739 try {
1740 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, language, country,
1741 variant, appName, addrs);
1742 } catch (RemoteException e) {
1743 Log.e(TAG, "getFromLocation failed", e);
Mike Lockwood3681f262009-05-12 10:52:03 -04001744 mGeocodeProvider = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001745 }
1746 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001747 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001748 }
1749
Mike Lockwooda55c3212009-04-15 11:10:11 -04001750
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001751 public String getFromLocationName(String locationName,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001752 double lowerLeftLatitude, double lowerLeftLongitude,
1753 double upperRightLatitude, double upperRightLongitude, int maxResults,
1754 String language, String country, String variant, String appName, List<Address> addrs) {
1755
1756 if (mGeocodeProvider != null) {
1757 try {
1758 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1759 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1760 maxResults, language, country, variant, appName, addrs);
1761 } catch (RemoteException e) {
1762 Log.e(TAG, "getFromLocationName failed", e);
Mike Lockwood3681f262009-05-12 10:52:03 -04001763 mGeocodeProvider = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001764 }
1765 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001766 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001767 }
1768
1769 // Mock Providers
1770
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001771 private void checkMockPermissionsSafe() {
1772 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1773 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1774 if (!allowMocks) {
1775 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1776 }
1777
1778 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1779 PackageManager.PERMISSION_GRANTED) {
1780 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1781 }
1782 }
1783
1784 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1785 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1786 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1787 checkMockPermissionsSafe();
1788
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001789 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001790 MockProvider provider = new MockProvider(name, this,
1791 requiresNetwork, requiresSatellite,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001792 requiresCell, hasMonetaryCost, supportsAltitude,
1793 supportsSpeed, supportsBearing, powerRequirement, accuracy);
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001794 if (mProvidersByName.get(name) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001795 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1796 }
Mike Lockwood95427cd2009-05-07 13:27:54 -04001797
1798 // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
1799 long identity = Binder.clearCallingIdentity();
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001800 addProvider(new LocationProviderProxy(name, provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001801 mMockProviders.put(name, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001802 updateProvidersLocked();
Mike Lockwood95427cd2009-05-07 13:27:54 -04001803 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001804 }
1805 }
1806
1807 public void removeTestProvider(String provider) {
1808 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001809 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001810 MockProvider mockProvider = mMockProviders.get(provider);
1811 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001812 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1813 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001814 removeProvider(mProvidersByName.get(provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001815 mMockProviders.remove(mockProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001816 updateProvidersLocked();
1817 }
1818 }
1819
1820 public void setTestProviderLocation(String provider, Location loc) {
1821 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001822 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001823 MockProvider mockProvider = mMockProviders.get(provider);
1824 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001825 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1826 }
Mike Lockwood95427cd2009-05-07 13:27:54 -04001827 // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
1828 long identity = Binder.clearCallingIdentity();
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001829 mockProvider.setLocation(loc);
Mike Lockwood95427cd2009-05-07 13:27:54 -04001830 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001831 }
1832 }
1833
1834 public void clearTestProviderLocation(String provider) {
1835 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001836 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001837 MockProvider mockProvider = mMockProviders.get(provider);
1838 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001839 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1840 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001841 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001842 }
1843 }
1844
1845 public void setTestProviderEnabled(String provider, boolean enabled) {
1846 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001847 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001848 MockProvider mockProvider = mMockProviders.get(provider);
1849 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001850 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1851 }
1852 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001853 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001854 mEnabledProviders.add(provider);
1855 mDisabledProviders.remove(provider);
1856 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001857 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001858 mEnabledProviders.remove(provider);
1859 mDisabledProviders.add(provider);
1860 }
1861 updateProvidersLocked();
1862 }
1863 }
1864
1865 public void clearTestProviderEnabled(String provider) {
1866 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001867 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001868 MockProvider mockProvider = mMockProviders.get(provider);
1869 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001870 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1871 }
1872 mEnabledProviders.remove(provider);
1873 mDisabledProviders.remove(provider);
1874 updateProvidersLocked();
1875 }
1876 }
1877
1878 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1879 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001880 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001881 MockProvider mockProvider = mMockProviders.get(provider);
1882 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001883 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1884 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001885 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001886 }
1887 }
1888
1889 public void clearTestProviderStatus(String provider) {
1890 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001891 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001892 MockProvider mockProvider = mMockProviders.get(provider);
1893 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001894 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1895 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001896 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001897 }
1898 }
1899
1900 private void log(String log) {
1901 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1902 Log.d(TAG, log);
1903 }
1904 }
1905
1906 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1907 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1908 != PackageManager.PERMISSION_GRANTED) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001909 pw.println("Permission Denial: can't dump LocationManagerService from from pid="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001910 + Binder.getCallingPid()
1911 + ", uid=" + Binder.getCallingUid());
1912 return;
1913 }
1914
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001915 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001916 pw.println("Current Location Manager state:");
1917 pw.println(" sProvidersLoaded=" + sProvidersLoaded);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001918 pw.println(" Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001919 int N = mReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001920 for (int i=0; i<N; i++) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001921 pw.println(" " + mReceivers.get(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001922 }
1923 pw.println(" Location Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001924 for (Receiver i : mReceivers.values()) {
1925 pw.println(" " + i + ":");
1926 for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001927 pw.println(" " + j.getKey() + ":");
1928 j.getValue().dump(pw, " ");
1929 }
1930 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001931 pw.println(" Records by Provider:");
1932 for (Map.Entry<String, ArrayList<UpdateRecord>> i
1933 : mRecordsByProvider.entrySet()) {
1934 pw.println(" " + i.getKey() + ":");
1935 for (UpdateRecord j : i.getValue()) {
1936 pw.println(" " + j + ":");
1937 j.dump(pw, " ");
1938 }
1939 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001940 pw.println(" Last Known Locations:");
1941 for (Map.Entry<String, Location> i
1942 : mLastKnownLocation.entrySet()) {
1943 pw.println(" " + i.getKey() + ":");
1944 i.getValue().dump(new PrintWriterPrinter(pw), " ");
1945 }
1946 if (mProximityAlerts.size() > 0) {
1947 pw.println(" Proximity Alerts:");
1948 for (Map.Entry<PendingIntent, ProximityAlert> i
1949 : mProximityAlerts.entrySet()) {
1950 pw.println(" " + i.getKey() + ":");
1951 i.getValue().dump(pw, " ");
1952 }
1953 }
1954 if (mProximitiesEntered.size() > 0) {
1955 pw.println(" Proximities Entered:");
1956 for (ProximityAlert i : mProximitiesEntered) {
1957 pw.println(" " + i + ":");
1958 i.dump(pw, " ");
1959 }
1960 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001961 pw.println(" mProximityReceiver=" + mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001962 pw.println(" mProximityListener=" + mProximityListener);
1963 if (mEnabledProviders.size() > 0) {
1964 pw.println(" Enabled Providers:");
1965 for (String i : mEnabledProviders) {
1966 pw.println(" " + i);
1967 }
1968
1969 }
1970 if (mDisabledProviders.size() > 0) {
1971 pw.println(" Disabled Providers:");
1972 for (String i : mDisabledProviders) {
1973 pw.println(" " + i);
1974 }
1975
1976 }
1977 if (mMockProviders.size() > 0) {
1978 pw.println(" Mock Providers:");
1979 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001980 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001981 }
1982 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001983 }
1984 }
1985}