blob: 99e6125a0d7fbf74372e7cdc1d237ac648425600 [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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -070044import android.database.Cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.location.Address;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -070046import android.location.IGeocodeProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.location.IGpsStatusListener;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -070048import android.location.IGpsStatusProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.location.ILocationListener;
50import android.location.ILocationManager;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -070061import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.os.Message;
63import android.os.PowerManager;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -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.Log;
69import android.util.PrintWriterPrinter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071import com.android.internal.location.GpsLocationProvider;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -070072import com.android.internal.location.LocationProviderProxy;
73import com.android.internal.location.MockProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074
75/**
76 * The service class that manages LocationProviders and issues location
77 * updates and alerts.
78 *
79 * {@hide}
80 */
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -070081public class LocationManagerService extends ILocationManager.Stub implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 private static final String TAG = "LocationManagerService";
The Android Open Source Project10592532009-03-18 17:39:46 -070083 private static final boolean LOCAL_LOGV = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084
85 // Minimum time interval between last known location writes, in milliseconds.
86 private static final long MIN_LAST_KNOWN_LOCATION_TIME = 60L * 1000L;
87
88 // Max time to hold wake lock for, in milliseconds.
89 private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L;
90
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 // The last time a location was written, by provider name.
92 private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
93
94 private static final Pattern PATTERN_COMMA = Pattern.compile(",");
95
96 private static final String ACCESS_FINE_LOCATION =
97 android.Manifest.permission.ACCESS_FINE_LOCATION;
98 private static final String ACCESS_COARSE_LOCATION =
99 android.Manifest.permission.ACCESS_COARSE_LOCATION;
100 private static final String ACCESS_MOCK_LOCATION =
101 android.Manifest.permission.ACCESS_MOCK_LOCATION;
102 private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
103 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700104 private static final String INSTALL_LOCATION_PROVIDER =
105 android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106
107 // Set of providers that are explicitly enabled
108 private final Set<String> mEnabledProviders = new HashSet<String>();
109
110 // Set of providers that are explicitly disabled
111 private final Set<String> mDisabledProviders = new HashSet<String>();
112
113 // Locations, status values, and extras for mock providers
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700114 private final HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115
116 private static boolean sProvidersLoaded = false;
117
118 private final Context mContext;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700119 private IGeocodeProvider mGeocodeProvider;
120 private IGpsStatusProvider mGpsStatusProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 private LocationWorkerHandler mLocationHandler;
122
Jean-Baptiste Queru72b1f372009-08-31 09:17:57 -0700123 // Cache the real providers for use in addTestProvider() and removeTestProvider()
124 LocationProviderProxy mNetworkLocationProvider;
125 LocationProviderProxy mGpsLocationProvider;
126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 // Handler messages
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700128 private static final int MESSAGE_LOCATION_CHANGED = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700130 // wakelock variables
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 private final static String WAKELOCK_KEY = "LocationManagerService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 private PowerManager.WakeLock mWakeLock = null;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700133 private int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134
135 /**
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700136 * List of all receivers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 */
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700138 private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
139
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140
141 /**
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700142 * List of location providers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 */
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700144 private final ArrayList<LocationProviderProxy> mProviders =
145 new ArrayList<LocationProviderProxy>();
146 private final HashMap<String, LocationProviderProxy> mProvidersByName
147 = new HashMap<String, LocationProviderProxy>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148
149 /**
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700150 * Object used internally for synchronization
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 */
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700152 private final Object mLock = new Object();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153
154 /**
155 * Mapping from provider name to all its UpdateRecords
156 */
157 private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
158 new HashMap<String,ArrayList<UpdateRecord>>();
159
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 // Proximity listeners
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700161 private Receiver mProximityReceiver = null;
162 private ILocationListener mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
164 new HashMap<PendingIntent,ProximityAlert>();
165 private HashSet<ProximityAlert> mProximitiesEntered =
166 new HashSet<ProximityAlert>();
167
168 // Last known location for each provider
169 private HashMap<String,Location> mLastKnownLocation =
170 new HashMap<String,Location>();
171
The Android Open Source Project4df24232009-03-05 14:34:35 -0800172 private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700173
174 // for Settings change notification
175 private ContentQueryMap mSettings;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800176
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 /**
178 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
179 * location updates.
180 */
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700181 private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 final ILocationListener mListener;
183 final PendingIntent mPendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 final Object mKey;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700185 final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
186 int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700188 Receiver(ILocationListener listener) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 mListener = listener;
190 mPendingIntent = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 mKey = listener.asBinder();
192 }
193
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700194 Receiver(PendingIntent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 mPendingIntent = intent;
196 mListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 mKey = intent;
198 }
199
200 @Override
201 public boolean equals(Object otherObj) {
202 if (otherObj instanceof Receiver) {
203 return mKey.equals(
204 ((Receiver)otherObj).mKey);
205 }
206 return false;
207 }
208
209 @Override
210 public int hashCode() {
211 return mKey.hashCode();
212 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700213
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 @Override
215 public String toString() {
216 if (mListener != null) {
217 return "Receiver{"
218 + Integer.toHexString(System.identityHashCode(this))
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700219 + " Listener " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 } else {
221 return "Receiver{"
222 + Integer.toHexString(System.identityHashCode(this))
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700223 + " Intent " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 }
225 }
226
227 public boolean isListener() {
228 return mListener != null;
229 }
230
231 public boolean isPendingIntent() {
232 return mPendingIntent != null;
233 }
234
235 public ILocationListener getListener() {
236 if (mListener != null) {
237 return mListener;
238 }
239 throw new IllegalStateException("Request for non-existent listener");
240 }
241
242 public PendingIntent getPendingIntent() {
243 if (mPendingIntent != null) {
244 return mPendingIntent;
245 }
246 throw new IllegalStateException("Request for non-existent intent");
247 }
248
249 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
250 if (mListener != null) {
251 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700252 synchronized (this) {
253 // synchronize to ensure incrementPendingBroadcastsLocked()
254 // is called before decrementPendingBroadcasts()
255 mListener.onStatusChanged(provider, status, extras);
256 if (mListener != mProximityListener) {
257 // call this after broadcasting so we do not increment
258 // if we throw an exeption.
259 incrementPendingBroadcastsLocked();
260 }
261 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 } catch (RemoteException e) {
263 return false;
264 }
265 } else {
266 Intent statusChanged = new Intent();
267 statusChanged.putExtras(extras);
268 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
269 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700270 synchronized (this) {
271 // synchronize to ensure incrementPendingBroadcastsLocked()
272 // is called before decrementPendingBroadcasts()
273 mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler);
274 // call this after broadcasting so we do not increment
275 // if we throw an exeption.
276 incrementPendingBroadcastsLocked();
277 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 } catch (PendingIntent.CanceledException e) {
279 return false;
280 }
281 }
282 return true;
283 }
284
285 public boolean callLocationChangedLocked(Location location) {
286 if (mListener != null) {
287 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700288 synchronized (this) {
289 // synchronize to ensure incrementPendingBroadcastsLocked()
290 // is called before decrementPendingBroadcasts()
291 mListener.onLocationChanged(location);
292 if (mListener != mProximityListener) {
293 // call this after broadcasting so we do not increment
294 // if we throw an exeption.
295 incrementPendingBroadcastsLocked();
296 }
297 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 } catch (RemoteException e) {
299 return false;
300 }
301 } else {
302 Intent locationChanged = new Intent();
303 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
304 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700305 synchronized (this) {
306 // synchronize to ensure incrementPendingBroadcastsLocked()
307 // is called before decrementPendingBroadcasts()
308 mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler);
309 // call this after broadcasting so we do not increment
310 // if we throw an exeption.
311 incrementPendingBroadcastsLocked();
312 }
313 } catch (PendingIntent.CanceledException e) {
314 return false;
315 }
316 }
317 return true;
318 }
319
320 public boolean callProviderEnabledLocked(String provider, boolean enabled) {
321 if (mListener != null) {
322 try {
323 synchronized (this) {
324 // synchronize to ensure incrementPendingBroadcastsLocked()
325 // is called before decrementPendingBroadcasts()
326 if (enabled) {
327 mListener.onProviderEnabled(provider);
328 } else {
329 mListener.onProviderDisabled(provider);
330 }
331 if (mListener != mProximityListener) {
332 // call this after broadcasting so we do not increment
333 // if we throw an exeption.
334 incrementPendingBroadcastsLocked();
335 }
336 }
337 } catch (RemoteException e) {
338 return false;
339 }
340 } else {
341 Intent providerIntent = new Intent();
342 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
343 try {
344 synchronized (this) {
345 // synchronize to ensure incrementPendingBroadcastsLocked()
346 // is called before decrementPendingBroadcasts()
347 mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler);
348 // call this after broadcasting so we do not increment
349 // if we throw an exeption.
350 incrementPendingBroadcastsLocked();
351 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 } catch (PendingIntent.CanceledException e) {
353 return false;
354 }
355 }
356 return true;
357 }
358
359 public void binderDied() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700360 if (LOCAL_LOGV) {
361 Log.v(TAG, "Location listener died");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700363 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 removeUpdatesLocked(this);
365 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700366 synchronized (this) {
367 if (mPendingBroadcasts > 0) {
368 LocationManagerService.this.decrementPendingBroadcasts();
369 mPendingBroadcasts = 0;
370 }
371 }
372 }
373
374 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
375 int resultCode, String resultData, Bundle resultExtras) {
376 synchronized (this) {
377 decrementPendingBroadcastsLocked();
378 }
379 }
380
381 // this must be called while synchronized by caller in a synchronized block
382 // containing the sending of the broadcaset
383 private void incrementPendingBroadcastsLocked() {
384 if (mPendingBroadcasts++ == 0) {
385 LocationManagerService.this.incrementPendingBroadcasts();
386 }
387 }
388
389 private void decrementPendingBroadcastsLocked() {
390 if (--mPendingBroadcasts == 0) {
391 LocationManagerService.this.decrementPendingBroadcasts();
392 }
393 }
394 }
395
396 public void locationCallbackFinished(ILocationListener listener) {
Joshua Bartel9ff67a52009-10-05 12:44:46 -0400397 //Do not use getReceiver here as that will add the ILocationListener to
398 //the receiver list if it is not found. If it is not found then the
399 //LocationListener was removed when it had a pending broadcast and should
400 //not be added back.
401 IBinder binder = listener.asBinder();
402 Receiver receiver = mReceivers.get(binder);
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700403 if (receiver != null) {
404 synchronized (receiver) {
405 // so wakelock calls will succeed
406 long identity = Binder.clearCallingIdentity();
407 receiver.decrementPendingBroadcastsLocked();
408 Binder.restoreCallingIdentity(identity);
409 }
410 }
411 }
412
413 private final class SettingsObserver implements Observer {
414 public void update(Observable o, Object arg) {
415 synchronized (mLock) {
416 updateProvidersLocked();
417 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418 }
419 }
420
421 private Location readLastKnownLocationLocked(String provider) {
422 Location location = null;
423 String s = null;
424 try {
425 File f = new File(LocationManager.SYSTEM_DIR + "/location."
426 + provider);
427 if (!f.exists()) {
428 return null;
429 }
430 BufferedReader reader = new BufferedReader(new FileReader(f), 256);
431 s = reader.readLine();
432 } catch (IOException e) {
433 Log.w(TAG, "Unable to read last known location", e);
434 }
435
436 if (s == null) {
437 return null;
438 }
439 try {
440 String[] tokens = PATTERN_COMMA.split(s);
441 int idx = 0;
442 long time = Long.parseLong(tokens[idx++]);
443 double latitude = Double.parseDouble(tokens[idx++]);
444 double longitude = Double.parseDouble(tokens[idx++]);
445 double altitude = Double.parseDouble(tokens[idx++]);
446 float bearing = Float.parseFloat(tokens[idx++]);
447 float speed = Float.parseFloat(tokens[idx++]);
448
449 location = new Location(provider);
450 location.setTime(time);
451 location.setLatitude(latitude);
452 location.setLongitude(longitude);
453 location.setAltitude(altitude);
454 location.setBearing(bearing);
455 location.setSpeed(speed);
456 } catch (NumberFormatException nfe) {
457 Log.e(TAG, "NumberFormatException reading last known location", nfe);
458 return null;
459 }
460
461 return location;
462 }
463
464 private void writeLastKnownLocationLocked(String provider,
465 Location location) {
466 long now = SystemClock.elapsedRealtime();
467 Long last = mLastWriteTime.get(provider);
468 if ((last != null)
469 && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
470 return;
471 }
472 mLastWriteTime.put(provider, now);
473
474 StringBuilder sb = new StringBuilder(100);
475 sb.append(location.getTime());
476 sb.append(',');
477 sb.append(location.getLatitude());
478 sb.append(',');
479 sb.append(location.getLongitude());
480 sb.append(',');
481 sb.append(location.getAltitude());
482 sb.append(',');
483 sb.append(location.getBearing());
484 sb.append(',');
485 sb.append(location.getSpeed());
486
487 FileWriter writer = null;
488 try {
489 File d = new File(LocationManager.SYSTEM_DIR);
490 if (!d.exists()) {
491 if (!d.mkdirs()) {
492 Log.w(TAG, "Unable to create directory to write location");
493 return;
494 }
495 }
496 File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
497 writer = new FileWriter(f);
498 writer.write(sb.toString());
499 } catch (IOException e) {
500 Log.w(TAG, "Unable to write location", e);
501 } finally {
502 if (writer != null) {
503 try {
504 writer.close();
505 } catch (IOException e) {
506 Log.w(TAG, "Exception closing file", e);
507 }
508 }
509 }
510 }
511
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700512 private void addProvider(LocationProviderProxy provider) {
513 mProviders.add(provider);
514 mProvidersByName.put(provider.getName(), provider);
515 }
516
517 private void removeProvider(LocationProviderProxy provider) {
518 mProviders.remove(provider);
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700519 provider.unlinkProvider();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700520 mProvidersByName.remove(provider.getName());
521 }
522
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 private void loadProviders() {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700524 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 if (sProvidersLoaded) {
526 return;
527 }
528
529 // Load providers
530 loadProvidersLocked();
531 sProvidersLoaded = true;
532 }
533 }
534
535 private void loadProvidersLocked() {
536 try {
537 _loadProvidersLocked();
538 } catch (Exception e) {
539 Log.e(TAG, "Exception loading providers:", e);
540 }
541 }
542
543 private void _loadProvidersLocked() {
544 // Attempt to load "real" providers first
545 if (GpsLocationProvider.isSupported()) {
546 // Create a gps location provider
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700547 GpsLocationProvider provider = new GpsLocationProvider(mContext, this);
548 mGpsStatusProvider = provider.getGpsStatusProvider();
549 LocationProviderProxy proxy = new LocationProviderProxy(LocationManager.GPS_PROVIDER, provider);
550 addProvider(proxy);
Jean-Baptiste Queru72b1f372009-08-31 09:17:57 -0700551 mGpsLocationProvider = proxy;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 }
553
554 updateProvidersLocked();
555 }
556
557 /**
558 * @param context the context that the LocationManagerService runs in
559 */
560 public LocationManagerService(Context context) {
561 super();
562 mContext = context;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700563
564 Thread thread = new Thread(null, this, "LocationManagerService");
565 thread.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566
The Android Open Source Project10592532009-03-18 17:39:46 -0700567 if (LOCAL_LOGV) {
568 Log.v(TAG, "Constructed LocationManager Service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700570 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700572 private void initialize() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 // Create a wake lock, needs to be done before calling loadProviders() below
574 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
575 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576
577 // Load providers
578 loadProviders();
579
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 // Register for Network (Wifi or Mobile) updates
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 IntentFilter intentFilter = new IntentFilter();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700582 intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
583 // Register for Package Manager updates
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
585 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700586 mContext.registerReceiver(mBroadcastReceiver, intentFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700588 // listen for settings changes
589 ContentResolver resolver = mContext.getContentResolver();
590 Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
591 "(" + Settings.System.NAME + "=?)",
592 new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
593 null);
594 mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler);
595 SettingsObserver settingsObserver = new SettingsObserver();
596 mSettings.addObserver(settingsObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 }
598
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700599 public void run()
600 {
601 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
602 Looper.prepare();
603 mLocationHandler = new LocationWorkerHandler();
604 initialize();
605 Looper.loop();
606 }
607
608 public void installLocationProvider(String name, ILocationProvider provider) {
609 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
610 != PackageManager.PERMISSION_GRANTED) {
611 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700614 synchronized (mLock) {
615 // check to see if we are reinstalling a dead provider
616 LocationProviderProxy oldProvider = mProvidersByName.get(name);
617 if (oldProvider != null) {
618 if (oldProvider.isDead()) {
619 Log.d(TAG, "replacing dead provider");
620 removeProvider(oldProvider);
621 } else {
622 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
623 }
624 }
625
626 LocationProviderProxy proxy = new LocationProviderProxy(name, provider);
627 addProvider(proxy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 updateProvidersLocked();
Jean-Baptiste Queru72b1f372009-08-31 09:17:57 -0700629 if (LocationManager.NETWORK_PROVIDER.equals(name)) {
630 mNetworkLocationProvider = proxy;
631 }
The Android Open Source Project4df24232009-03-05 14:34:35 -0800632
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700633 // notify provider of current network state
634 proxy.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800635 }
636 }
637
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700638 public void installGeocodeProvider(IGeocodeProvider provider) {
639 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
640 != PackageManager.PERMISSION_GRANTED) {
641 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700643
644 mGeocodeProvider = provider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 }
646
647 private boolean isAllowedBySettingsLocked(String provider) {
648 if (mEnabledProviders.contains(provider)) {
649 return true;
650 }
651 if (mDisabledProviders.contains(provider)) {
652 return false;
653 }
654 // Use system settings
655 ContentResolver resolver = mContext.getContentResolver();
656 String allowedProviders = Settings.Secure.getString(resolver,
657 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
658
659 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
660 }
661
662 private void checkPermissionsSafe(String provider) {
663 if (LocationManager.GPS_PROVIDER.equals(provider)
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700664 && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 != PackageManager.PERMISSION_GRANTED)) {
666 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
667 }
668 if (LocationManager.NETWORK_PROVIDER.equals(provider)
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700669 && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 != PackageManager.PERMISSION_GRANTED)
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700671 && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 != PackageManager.PERMISSION_GRANTED)) {
673 throw new SecurityException(
674 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
675 }
676 }
677
678 private boolean isAllowedProviderSafe(String provider) {
679 if (LocationManager.GPS_PROVIDER.equals(provider)
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700680 && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681 != PackageManager.PERMISSION_GRANTED)) {
682 return false;
683 }
684 if (LocationManager.NETWORK_PROVIDER.equals(provider)
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700685 && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 != PackageManager.PERMISSION_GRANTED)
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700687 && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 != PackageManager.PERMISSION_GRANTED)) {
689 return false;
690 }
691
692 return true;
693 }
694
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 public List<String> getAllProviders() {
696 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700697 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 return _getAllProvidersLocked();
699 }
700 } catch (SecurityException se) {
701 throw se;
702 } catch (Exception e) {
703 Log.e(TAG, "getAllProviders got exception:", e);
704 return null;
705 }
706 }
707
708 private List<String> _getAllProvidersLocked() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700709 if (LOCAL_LOGV) {
710 Log.v(TAG, "getAllProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800711 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700712 ArrayList<String> out = new ArrayList<String>(mProviders.size());
713 for (int i = mProviders.size() - 1; i >= 0; i--) {
714 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 out.add(p.getName());
716 }
717 return out;
718 }
719
720 public List<String> getProviders(boolean enabledOnly) {
721 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700722 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800723 return _getProvidersLocked(enabledOnly);
724 }
725 } catch (SecurityException se) {
726 throw se;
727 } catch (Exception e) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700728 Log.e(TAG, "getProviders got exception:", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729 return null;
730 }
731 }
732
733 private List<String> _getProvidersLocked(boolean enabledOnly) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700734 if (LOCAL_LOGV) {
735 Log.v(TAG, "getProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700737 ArrayList<String> out = new ArrayList<String>(mProviders.size());
738 for (int i = mProviders.size() - 1; i >= 0; i--) {
739 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740 String name = p.getName();
741 if (isAllowedProviderSafe(name)) {
742 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
743 continue;
744 }
745 out.add(name);
746 }
747 }
748 return out;
749 }
750
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 private void updateProvidersLocked() {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700752 for (int i = mProviders.size() - 1; i >= 0; i--) {
753 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 boolean isEnabled = p.isEnabled();
755 String name = p.getName();
756 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
757
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758 if (isEnabled && !shouldBeEnabled) {
759 updateProviderListenersLocked(name, false);
760 } else if (!isEnabled && shouldBeEnabled) {
761 updateProviderListenersLocked(name, true);
762 }
763
764 }
765 }
766
767 private void updateProviderListenersLocked(String provider, boolean enabled) {
768 int listeners = 0;
769
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700770 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 if (p == null) {
772 return;
773 }
774
775 ArrayList<Receiver> deadReceivers = null;
776
777 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
778 if (records != null) {
779 final int N = records.size();
780 for (int i=0; i<N; i++) {
781 UpdateRecord record = records.get(i);
782 // Sends a notification message to the receiver
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700783 if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
784 if (deadReceivers == null) {
785 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 }
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700787 deadReceivers.add(record.mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 }
789 listeners++;
790 }
791 }
792
793 if (deadReceivers != null) {
794 for (int i=deadReceivers.size()-1; i>=0; i--) {
795 removeUpdatesLocked(deadReceivers.get(i));
796 }
797 }
798
799 if (enabled) {
800 p.enable();
801 if (listeners > 0) {
802 p.setMinTime(getMinTimeLocked(provider));
803 p.enableLocationTracking(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 }
805 } else {
806 p.enableLocationTracking(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 p.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 }
809 }
810
811 private long getMinTimeLocked(String provider) {
812 long minTime = Long.MAX_VALUE;
813 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
814 if (records != null) {
815 for (int i=records.size()-1; i>=0; i--) {
816 minTime = Math.min(minTime, records.get(i).mMinTime);
817 }
818 }
819 return minTime;
820 }
821
822 private class UpdateRecord {
823 final String mProvider;
824 final Receiver mReceiver;
825 final long mMinTime;
826 final float mMinDistance;
827 final int mUid;
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700828 Location mLastFixBroadcast;
829 long mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830
831 /**
832 * Note: must be constructed with lock held.
833 */
834 UpdateRecord(String provider, long minTime, float minDistance,
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700835 Receiver receiver, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 mProvider = provider;
837 mReceiver = receiver;
838 mMinTime = minTime;
839 mMinDistance = minDistance;
840 mUid = uid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841
842 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
843 if (records == null) {
844 records = new ArrayList<UpdateRecord>();
845 mRecordsByProvider.put(provider, records);
846 }
847 if (!records.contains(this)) {
848 records.add(this);
849 }
850 }
851
852 /**
853 * Method to be called when a record will no longer be used. Calling this multiple times
854 * must have the same effect as calling it once.
855 */
856 void disposeLocked() {
857 ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
858 records.remove(this);
859 }
860
861 @Override
862 public String toString() {
863 return "UpdateRecord{"
864 + Integer.toHexString(System.identityHashCode(this))
865 + " " + mProvider + " " + mReceiver + "}";
866 }
867
868 void dump(PrintWriter pw, String prefix) {
869 pw.println(prefix + this);
870 pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
871 pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700872 pw.println(prefix + "mUid=" + mUid);
873 pw.println(prefix + "mLastFixBroadcast:");
874 mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
875 pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 }
877
878 /**
879 * Calls dispose().
880 */
881 @Override protected void finalize() {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700882 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800883 disposeLocked();
884 }
885 }
886 }
887
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700888 private Receiver getReceiver(ILocationListener listener) {
889 IBinder binder = listener.asBinder();
890 Receiver receiver = mReceivers.get(binder);
891 if (receiver == null) {
892 receiver = new Receiver(listener);
893 mReceivers.put(binder, receiver);
894
895 try {
896 if (receiver.isListener()) {
897 receiver.getListener().asBinder().linkToDeath(receiver, 0);
898 }
899 } catch (RemoteException e) {
900 Log.e(TAG, "linkToDeath failed:", e);
901 return null;
902 }
903 }
904 return receiver;
905 }
906
907 private Receiver getReceiver(PendingIntent intent) {
908 Receiver receiver = mReceivers.get(intent);
909 if (receiver == null) {
910 receiver = new Receiver(intent);
911 mReceivers.put(intent, receiver);
912 }
913 return receiver;
914 }
915
916 private boolean providerHasListener(String provider, int uid, Receiver excludedReceiver) {
917 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
918 if (records != null) {
919 for (int i = records.size() - 1; i >= 0; i--) {
920 UpdateRecord record = records.get(i);
921 if (record.mUid == uid && record.mReceiver != excludedReceiver) {
922 return true;
923 }
924 }
925 }
926 for (ProximityAlert alert : mProximityAlerts.values()) {
927 if (alert.mUid == uid) {
928 return true;
929 }
930 }
931 return false;
932 }
933
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 public void requestLocationUpdates(String provider,
935 long minTime, float minDistance, ILocationListener listener) {
936
937 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700938 synchronized (mLock) {
939 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 }
941 } catch (SecurityException se) {
942 throw se;
943 } catch (Exception e) {
944 Log.e(TAG, "requestUpdates got exception:", e);
945 }
946 }
947
948 public void requestLocationUpdatesPI(String provider,
949 long minTime, float minDistance, PendingIntent intent) {
950 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700951 synchronized (mLock) {
952 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800953 }
954 } catch (SecurityException se) {
955 throw se;
956 } catch (Exception e) {
957 Log.e(TAG, "requestUpdates got exception:", e);
958 }
959 }
960
961 private void requestLocationUpdatesLocked(String provider,
962 long minTime, float minDistance, Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700963 if (LOCAL_LOGV) {
964 Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800965 }
966
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700967 LocationProviderProxy proxy = mProvidersByName.get(provider);
968 if (proxy == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800969 throw new IllegalArgumentException("provider=" + provider);
970 }
971
972 checkPermissionsSafe(provider);
973
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800974 // so wakelock calls will succeed
975 final int callingUid = Binder.getCallingUid();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700976 boolean newUid = !providerHasListener(provider, callingUid, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800977 long identity = Binder.clearCallingIdentity();
978 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700979 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
980 UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981 if (oldRecord != null) {
982 oldRecord.disposeLocked();
983 }
984
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700985 if (newUid) {
986 proxy.addListener(callingUid);
987 }
988
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800989 boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
990 if (isProviderEnabled) {
991 long minTimeForProvider = getMinTimeLocked(provider);
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700992 proxy.setMinTime(minTimeForProvider);
993 proxy.enableLocationTracking(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 } else {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -0700995 // Notify the listener that updates are currently disabled
996 receiver.callProviderEnabledLocked(provider, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 }
998 } finally {
999 Binder.restoreCallingIdentity(identity);
1000 }
1001 }
1002
1003 public void removeUpdates(ILocationListener listener) {
1004 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001005 synchronized (mLock) {
1006 removeUpdatesLocked(getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001007 }
1008 } catch (SecurityException se) {
1009 throw se;
1010 } catch (Exception e) {
1011 Log.e(TAG, "removeUpdates got exception:", e);
1012 }
1013 }
1014
1015 public void removeUpdatesPI(PendingIntent intent) {
1016 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001017 synchronized (mLock) {
1018 removeUpdatesLocked(getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001019 }
1020 } catch (SecurityException se) {
1021 throw se;
1022 } catch (Exception e) {
1023 Log.e(TAG, "removeUpdates got exception:", e);
1024 }
1025 }
1026
1027 private void removeUpdatesLocked(Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001028 if (LOCAL_LOGV) {
1029 Log.v(TAG, "_removeUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030 }
1031
1032 // so wakelock calls will succeed
1033 final int callingUid = Binder.getCallingUid();
1034 long identity = Binder.clearCallingIdentity();
1035 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001036 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1037 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
Joshua Bartel9ff67a52009-10-05 12:44:46 -04001038 synchronized(receiver) {
1039 if(receiver.mPendingBroadcasts > 0) {
1040 decrementPendingBroadcasts();
1041 receiver.mPendingBroadcasts = 0;
1042 }
1043 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 }
1045
1046 // Record which providers were associated with this listener
1047 HashSet<String> providers = new HashSet<String>();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001048 HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001049 if (oldRecords != null) {
1050 // Call dispose() on the obsolete update records.
1051 for (UpdateRecord record : oldRecords.values()) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001052 if (!providerHasListener(record.mProvider, callingUid, receiver)) {
1053 LocationProviderProxy proxy = mProvidersByName.get(record.mProvider);
1054 if (proxy != null) {
1055 proxy.removeListener(callingUid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 }
1057 }
1058 record.disposeLocked();
1059 }
1060 // Accumulate providers
1061 providers.addAll(oldRecords.keySet());
1062 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063
1064 // See if the providers associated with this listener have any
1065 // other listeners; if one does, inform it of the new smallest minTime
1066 // value; if one does not, disable location tracking for it
1067 for (String provider : providers) {
1068 // If provider is already disabled, don't need to do anything
1069 if (!isAllowedBySettingsLocked(provider)) {
1070 continue;
1071 }
1072
1073 boolean hasOtherListener = false;
1074 ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1075 if (recordsForProvider != null && recordsForProvider.size() > 0) {
1076 hasOtherListener = true;
1077 }
1078
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001079 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080 if (p != null) {
1081 if (hasOtherListener) {
1082 p.setMinTime(getMinTimeLocked(provider));
1083 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 p.enableLocationTracking(false);
1085 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 }
1087 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088 } finally {
1089 Binder.restoreCallingIdentity(identity);
1090 }
1091 }
1092
1093 public boolean addGpsStatusListener(IGpsStatusListener listener) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001094 if (mGpsStatusProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001095 return false;
1096 }
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -07001097 if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001098 PackageManager.PERMISSION_GRANTED) {
1099 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1100 }
1101
1102 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001103 mGpsStatusProvider.addGpsStatusListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 } catch (RemoteException e) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001105 Log.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106 return false;
1107 }
1108 return true;
1109 }
1110
1111 public void removeGpsStatusListener(IGpsStatusListener listener) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001112 synchronized (mLock) {
1113 try {
1114 mGpsStatusProvider.removeGpsStatusListener(listener);
1115 } catch (Exception e) {
1116 Log.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
1117 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 }
1119 }
1120
1121 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1122 // first check for permission to the provider
1123 checkPermissionsSafe(provider);
1124 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -07001125 if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 != PackageManager.PERMISSION_GRANTED)) {
1127 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1128 }
1129
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001130 synchronized (mLock) {
1131 LocationProviderProxy proxy = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 if (provider == null) {
1133 return false;
1134 }
1135
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001136 return proxy.sendExtraCommand(command, extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 }
1138 }
1139
1140 class ProximityAlert {
1141 final int mUid;
1142 final double mLatitude;
1143 final double mLongitude;
1144 final float mRadius;
1145 final long mExpiration;
1146 final PendingIntent mIntent;
1147 final Location mLocation;
1148
1149 public ProximityAlert(int uid, double latitude, double longitude,
1150 float radius, long expiration, PendingIntent intent) {
1151 mUid = uid;
1152 mLatitude = latitude;
1153 mLongitude = longitude;
1154 mRadius = radius;
1155 mExpiration = expiration;
1156 mIntent = intent;
1157
1158 mLocation = new Location("");
1159 mLocation.setLatitude(latitude);
1160 mLocation.setLongitude(longitude);
1161 }
1162
1163 long getExpiration() {
1164 return mExpiration;
1165 }
1166
1167 PendingIntent getIntent() {
1168 return mIntent;
1169 }
1170
Yusuf T. Mobile5550ef42009-07-14 15:46:26 -07001171 boolean isInProximity(double latitude, double longitude, float accuracy) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172 Location loc = new Location("");
1173 loc.setLatitude(latitude);
1174 loc.setLongitude(longitude);
1175
1176 double radius = loc.distanceTo(mLocation);
Yusuf T. Mobile5550ef42009-07-14 15:46:26 -07001177 return radius <= Math.max(mRadius,accuracy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001178 }
1179
1180 @Override
1181 public String toString() {
1182 return "ProximityAlert{"
1183 + Integer.toHexString(System.identityHashCode(this))
1184 + " uid " + mUid + mIntent + "}";
1185 }
1186
1187 void dump(PrintWriter pw, String prefix) {
1188 pw.println(prefix + this);
1189 pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
1190 pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
1191 pw.println(prefix + "mIntent=" + mIntent);
1192 pw.println(prefix + "mLocation:");
1193 mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
1194 }
1195 }
1196
1197 // Listener for receiving locations to trigger proximity alerts
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001198 class ProximityListener extends ILocationListener.Stub implements PendingIntent.OnFinished {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001199
1200 boolean isGpsAvailable = false;
1201
1202 // Note: this is called with the lock held.
1203 public void onLocationChanged(Location loc) {
1204
1205 // If Gps is available, then ignore updates from NetworkLocationProvider
1206 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1207 isGpsAvailable = true;
1208 }
1209 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1210 return;
1211 }
1212
1213 // Process proximity alerts
1214 long now = System.currentTimeMillis();
1215 double latitude = loc.getLatitude();
1216 double longitude = loc.getLongitude();
Yusuf T. Mobile5550ef42009-07-14 15:46:26 -07001217 float accuracy = loc.getAccuracy();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001218 ArrayList<PendingIntent> intentsToRemove = null;
1219
1220 for (ProximityAlert alert : mProximityAlerts.values()) {
1221 PendingIntent intent = alert.getIntent();
1222 long expiration = alert.getExpiration();
1223
1224 if ((expiration == -1) || (now <= expiration)) {
1225 boolean entered = mProximitiesEntered.contains(alert);
1226 boolean inProximity =
Yusuf T. Mobile5550ef42009-07-14 15:46:26 -07001227 alert.isInProximity(latitude, longitude, accuracy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228 if (!entered && inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001229 if (LOCAL_LOGV) {
1230 Log.v(TAG, "Entered alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001231 }
1232 mProximitiesEntered.add(alert);
1233 Intent enteredIntent = new Intent();
1234 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1235 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001236 synchronized (this) {
1237 // synchronize to ensure incrementPendingBroadcasts()
1238 // is called before decrementPendingBroadcasts()
1239 intent.send(mContext, 0, enteredIntent, this, mLocationHandler);
1240 // call this after broadcasting so we do not increment
1241 // if we throw an exeption.
1242 incrementPendingBroadcasts();
1243 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001244 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001245 if (LOCAL_LOGV) {
1246 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 }
1248 if (intentsToRemove == null) {
1249 intentsToRemove = new ArrayList<PendingIntent>();
1250 }
1251 intentsToRemove.add(intent);
1252 }
1253 } else if (entered && !inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001254 if (LOCAL_LOGV) {
1255 Log.v(TAG, "Exited alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001256 }
1257 mProximitiesEntered.remove(alert);
1258 Intent exitedIntent = new Intent();
1259 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1260 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001261 synchronized (this) {
1262 // synchronize to ensure incrementPendingBroadcasts()
1263 // is called before decrementPendingBroadcasts()
1264 intent.send(mContext, 0, exitedIntent, this, mLocationHandler);
1265 // call this after broadcasting so we do not increment
1266 // if we throw an exeption.
1267 incrementPendingBroadcasts();
1268 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001269 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001270 if (LOCAL_LOGV) {
1271 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001272 }
1273 if (intentsToRemove == null) {
1274 intentsToRemove = new ArrayList<PendingIntent>();
1275 }
1276 intentsToRemove.add(intent);
1277 }
1278 }
1279 } else {
1280 // Mark alert for expiration
The Android Open Source Project10592532009-03-18 17:39:46 -07001281 if (LOCAL_LOGV) {
1282 Log.v(TAG, "Expiring proximity alert: " + alert);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283 }
1284 if (intentsToRemove == null) {
1285 intentsToRemove = new ArrayList<PendingIntent>();
1286 }
1287 intentsToRemove.add(alert.getIntent());
1288 }
1289 }
1290
1291 // Remove expired alerts
1292 if (intentsToRemove != null) {
1293 for (PendingIntent i : intentsToRemove) {
1294 mProximityAlerts.remove(i);
1295 ProximityAlert alert = mProximityAlerts.get(i);
1296 mProximitiesEntered.remove(alert);
1297 }
1298 }
1299
1300 }
1301
1302 // Note: this is called with the lock held.
1303 public void onProviderDisabled(String provider) {
1304 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1305 isGpsAvailable = false;
1306 }
1307 }
1308
1309 // Note: this is called with the lock held.
1310 public void onProviderEnabled(String provider) {
1311 // ignore
1312 }
1313
1314 // Note: this is called with the lock held.
1315 public void onStatusChanged(String provider, int status, Bundle extras) {
1316 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1317 (status != LocationProvider.AVAILABLE)) {
1318 isGpsAvailable = false;
1319 }
1320 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001321
1322 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
1323 int resultCode, String resultData, Bundle resultExtras) {
1324 // synchronize to ensure incrementPendingBroadcasts()
1325 // is called before decrementPendingBroadcasts()
1326 synchronized (this) {
1327 decrementPendingBroadcasts();
1328 }
1329 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001330 }
1331
1332 public void addProximityAlert(double latitude, double longitude,
1333 float radius, long expiration, PendingIntent intent) {
1334 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001335 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001336 addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
1337 }
1338 } catch (SecurityException se) {
1339 throw se;
1340 } catch (Exception e) {
1341 Log.e(TAG, "addProximityAlert got exception:", e);
1342 }
1343 }
1344
1345 private void addProximityAlertLocked(double latitude, double longitude,
1346 float radius, long expiration, PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001347 if (LOCAL_LOGV) {
1348 Log.v(TAG, "addProximityAlert: latitude = " + latitude +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001349 ", longitude = " + longitude +
1350 ", expiration = " + expiration +
1351 ", intent = " + intent);
1352 }
1353
1354 // Require ability to access all providers for now
1355 if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
1356 !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
1357 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1358 }
1359
1360 if (expiration != -1) {
1361 expiration += System.currentTimeMillis();
1362 }
1363 ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
1364 latitude, longitude, radius, expiration, intent);
1365 mProximityAlerts.put(intent, alert);
1366
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001367 if (mProximityReceiver == null) {
1368 mProximityListener = new ProximityListener();
1369 mProximityReceiver = new Receiver(mProximityListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001370
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001371 for (int i = mProviders.size() - 1; i >= 0; i--) {
1372 LocationProviderProxy provider = mProviders.get(i);
1373 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001374 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001375 }
1376 }
1377
1378 public void removeProximityAlert(PendingIntent intent) {
1379 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001380 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001381 removeProximityAlertLocked(intent);
1382 }
1383 } catch (SecurityException se) {
1384 throw se;
1385 } catch (Exception e) {
1386 Log.e(TAG, "removeProximityAlert got exception:", e);
1387 }
1388 }
1389
1390 private void removeProximityAlertLocked(PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001391 if (LOCAL_LOGV) {
1392 Log.v(TAG, "removeProximityAlert: intent = " + intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393 }
1394
1395 mProximityAlerts.remove(intent);
1396 if (mProximityAlerts.size() == 0) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001397 removeUpdatesLocked(mProximityReceiver);
1398 mProximityReceiver = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001399 mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400 }
1401 }
1402
1403 /**
1404 * @return null if the provider does not exits
Alexey Tarasovf2db9fb2009-09-01 02:37:07 +11001405 * @throws SecurityException if the provider is not allowed to be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001406 * accessed by the caller
1407 */
1408 public Bundle getProviderInfo(String provider) {
1409 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001410 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001411 return _getProviderInfoLocked(provider);
1412 }
1413 } catch (SecurityException se) {
1414 throw se;
1415 } catch (Exception e) {
1416 Log.e(TAG, "_getProviderInfo got exception:", e);
1417 return null;
1418 }
1419 }
1420
1421 private Bundle _getProviderInfoLocked(String provider) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001422 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001423 if (p == null) {
1424 return null;
1425 }
1426
1427 checkPermissionsSafe(provider);
1428
1429 Bundle b = new Bundle();
1430 b.putBoolean("network", p.requiresNetwork());
1431 b.putBoolean("satellite", p.requiresSatellite());
1432 b.putBoolean("cell", p.requiresCell());
1433 b.putBoolean("cost", p.hasMonetaryCost());
1434 b.putBoolean("altitude", p.supportsAltitude());
1435 b.putBoolean("speed", p.supportsSpeed());
1436 b.putBoolean("bearing", p.supportsBearing());
1437 b.putInt("power", p.getPowerRequirement());
1438 b.putInt("accuracy", p.getAccuracy());
1439
1440 return b;
1441 }
1442
1443 public boolean isProviderEnabled(String provider) {
1444 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001445 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001446 return _isProviderEnabledLocked(provider);
1447 }
1448 } catch (SecurityException se) {
1449 throw se;
1450 } catch (Exception e) {
1451 Log.e(TAG, "isProviderEnabled got exception:", e);
1452 return false;
1453 }
1454 }
1455
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001456 public void reportLocation(Location location) {
1457 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
1458 != PackageManager.PERMISSION_GRANTED) {
1459 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
1460 }
1461
1462 mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
1463 Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
1464 mLocationHandler.sendMessageAtFrontOfQueue(m);
1465 }
1466
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001467 private boolean _isProviderEnabledLocked(String provider) {
1468 checkPermissionsSafe(provider);
1469
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001470 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001471 if (p == null) {
1472 throw new IllegalArgumentException("provider=" + provider);
1473 }
1474 return isAllowedBySettingsLocked(provider);
1475 }
1476
1477 public Location getLastKnownLocation(String provider) {
1478 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001479 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001480 return _getLastKnownLocationLocked(provider);
1481 }
1482 } catch (SecurityException se) {
1483 throw se;
1484 } catch (Exception e) {
1485 Log.e(TAG, "getLastKnownLocation got exception:", e);
1486 return null;
1487 }
1488 }
1489
1490 private Location _getLastKnownLocationLocked(String provider) {
1491 checkPermissionsSafe(provider);
1492
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001493 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001494 if (p == null) {
1495 throw new IllegalArgumentException("provider=" + provider);
1496 }
1497
1498 if (!isAllowedBySettingsLocked(provider)) {
1499 return null;
1500 }
1501
1502 Location location = mLastKnownLocation.get(provider);
1503 if (location == null) {
1504 // Get the persistent last known location for the provider
1505 location = readLastKnownLocationLocked(provider);
1506 if (location != null) {
1507 mLastKnownLocation.put(provider, location);
1508 }
1509 }
1510
1511 return location;
1512 }
1513
1514 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1515 // Always broadcast the first update
1516 if (lastLoc == null) {
1517 return true;
1518 }
1519
1520 // Don't broadcast same location again regardless of condition
1521 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1522 if (loc.getTime() == lastLoc.getTime()) {
1523 return false;
1524 }
1525
1526 // Check whether sufficient distance has been traveled
1527 double minDistance = record.mMinDistance;
1528 if (minDistance > 0.0) {
1529 if (loc.distanceTo(lastLoc) <= minDistance) {
1530 return false;
1531 }
1532 }
1533
1534 return true;
1535 }
1536
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001537 private void handleLocationChangedLocked(Location location) {
1538 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1540 if (records == null || records.size() == 0) {
1541 return;
1542 }
1543
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001544 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001545 if (p == null) {
1546 return;
1547 }
1548
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001549 // Update last known location for provider
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001550 Location lastLocation = mLastKnownLocation.get(provider);
1551 if (lastLocation == null) {
1552 mLastKnownLocation.put(provider, new Location(location));
1553 } else {
1554 lastLocation.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001555 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001556 writeLastKnownLocationLocked(provider, location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001557
1558 // Fetch latest status update time
1559 long newStatusUpdateTime = p.getStatusUpdateTime();
1560
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001561 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001562 Bundle extras = new Bundle();
1563 int status = p.getStatus(extras);
1564
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001565 ArrayList<Receiver> deadReceivers = null;
1566
1567 // Broadcast location or status to all listeners
1568 final int N = records.size();
1569 for (int i=0; i<N; i++) {
1570 UpdateRecord r = records.get(i);
1571 Receiver receiver = r.mReceiver;
1572
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001573 Location lastLoc = r.mLastFixBroadcast;
1574 if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1575 if (lastLoc == null) {
1576 lastLoc = new Location(location);
1577 r.mLastFixBroadcast = lastLoc;
1578 } else {
1579 lastLoc.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001580 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001581 if (!receiver.callLocationChangedLocked(location)) {
1582 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1583 if (deadReceivers == null) {
1584 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001585 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001586 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001587 }
1588 }
1589
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001590 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001591 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1592 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1593
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001594 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001595 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1596 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1597 if (deadReceivers == null) {
1598 deadReceivers = new ArrayList<Receiver>();
1599 }
1600 if (!deadReceivers.contains(receiver)) {
1601 deadReceivers.add(receiver);
1602 }
1603 }
1604 }
1605 }
1606
1607 if (deadReceivers != null) {
1608 for (int i=deadReceivers.size()-1; i>=0; i--) {
1609 removeUpdatesLocked(deadReceivers.get(i));
1610 }
1611 }
1612 }
1613
1614 private class LocationWorkerHandler extends Handler {
1615
1616 @Override
1617 public void handleMessage(Message msg) {
1618 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001619 if (msg.what == MESSAGE_LOCATION_CHANGED) {
1620 // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001621
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001622 synchronized (mLock) {
1623 Location location = (Location) msg.obj;
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -07001624 String provider = location.getProvider();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001625
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -07001626 // notify other providers of the new location
1627 for (int i = mProviders.size() - 1; i >= 0; i--) {
1628 LocationProviderProxy proxy = mProviders.get(i);
1629 if (!provider.equals(proxy.getName())) {
1630 proxy.updateLocation(location);
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001631 }
1632 }
1633
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -07001634 if (isAllowedBySettingsLocked(provider)) {
1635 handleLocationChangedLocked(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001636 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001637 }
1638 }
1639 } catch (Exception e) {
1640 // Log, don't crash!
1641 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1642 }
1643 }
1644 }
1645
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001646 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001647 @Override
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001648 public void onReceive(Context context, Intent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001649 String action = intent.getAction();
1650
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001651 if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001652 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001653 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001654 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1655 if (uid >= 0) {
1656 ArrayList<Receiver> removedRecs = null;
1657 for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
1658 for (int j=i.size()-1; j>=0; j--) {
1659 UpdateRecord ur = i.get(j);
1660 if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
1661 if (removedRecs == null) {
1662 removedRecs = new ArrayList<Receiver>();
1663 }
1664 if (!removedRecs.contains(ur.mReceiver)) {
1665 removedRecs.add(ur.mReceiver);
1666 }
1667 }
1668 }
1669 }
1670 ArrayList<ProximityAlert> removedAlerts = null;
1671 for (ProximityAlert i : mProximityAlerts.values()) {
1672 if (i.mUid == uid) {
1673 if (removedAlerts == null) {
1674 removedAlerts = new ArrayList<ProximityAlert>();
1675 }
1676 if (!removedAlerts.contains(i)) {
1677 removedAlerts.add(i);
1678 }
1679 }
1680 }
1681 if (removedRecs != null) {
1682 for (int i=removedRecs.size()-1; i>=0; i--) {
1683 removeUpdatesLocked(removedRecs.get(i));
1684 }
1685 }
1686 if (removedAlerts != null) {
1687 for (int i=removedAlerts.size()-1; i>=0; i--) {
1688 removeProximityAlertLocked(removedAlerts.get(i).mIntent);
1689 }
1690 }
1691 }
1692 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001693 } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001694 boolean noConnectivity =
1695 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1696 if (!noConnectivity) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001697 mNetworkState = LocationProvider.AVAILABLE;
1698 } else {
1699 mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001700 }
1701
1702 // Notify location providers of current network state
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001703 synchronized (mLock) {
1704 for (int i = mProviders.size() - 1; i >= 0; i--) {
1705 LocationProviderProxy provider = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001706 if (provider.requiresNetwork()) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001707 provider.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001708 }
1709 }
1710 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001711 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001712 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001713 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001714
1715 // Wake locks
1716
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001717 private void incrementPendingBroadcasts() {
1718 synchronized (mWakeLock) {
1719 if (mPendingBroadcasts++ == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001720 try {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001721 mWakeLock.acquire();
1722 log("Acquired wakelock");
1723 } catch (Exception e) {
1724 // This is to catch a runtime exception thrown when we try to release an
1725 // already released lock.
1726 Log.e(TAG, "exception in acquireWakeLock()", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001727 }
1728 }
1729 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001730 }
1731
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001732 private void decrementPendingBroadcasts() {
1733 synchronized (mWakeLock) {
1734 if (--mPendingBroadcasts == 0) {
1735 try {
1736 // Release wake lock
1737 if (mWakeLock.isHeld()) {
1738 mWakeLock.release();
1739 log("Released wakelock");
1740 } else {
1741 log("Can't release wakelock again!");
1742 }
1743 } catch (Exception e) {
1744 // This is to catch a runtime exception thrown when we try to release an
1745 // already released lock.
1746 Log.e(TAG, "exception in releaseWakeLock()", e);
1747 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001748 }
1749 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001750 }
1751
1752 // Geocoder
1753
1754 public String getFromLocation(double latitude, double longitude, int maxResults,
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001755 String language, String country, String variant, String appName, List<Address> addrs) {
1756 if (mGeocodeProvider != null) {
1757 try {
1758 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, language, country,
1759 variant, appName, addrs);
1760 } catch (RemoteException e) {
1761 Log.e(TAG, "getFromLocation failed", e);
1762 mGeocodeProvider = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001763 }
1764 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001765 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001766 }
1767
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001768
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001769 public String getFromLocationName(String locationName,
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001770 double lowerLeftLatitude, double lowerLeftLongitude,
1771 double upperRightLatitude, double upperRightLongitude, int maxResults,
1772 String language, String country, String variant, String appName, List<Address> addrs) {
1773
1774 if (mGeocodeProvider != null) {
1775 try {
1776 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1777 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1778 maxResults, language, country, variant, appName, addrs);
1779 } catch (RemoteException e) {
1780 Log.e(TAG, "getFromLocationName failed", e);
1781 mGeocodeProvider = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782 }
1783 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001784 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001785 }
1786
1787 // Mock Providers
1788
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001789 private void checkMockPermissionsSafe() {
1790 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1791 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1792 if (!allowMocks) {
1793 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1794 }
1795
1796 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1797 PackageManager.PERMISSION_GRANTED) {
1798 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1799 }
1800 }
1801
1802 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1803 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1804 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1805 checkMockPermissionsSafe();
1806
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001807 synchronized (mLock) {
1808 MockProvider provider = new MockProvider(name, this,
1809 requiresNetwork, requiresSatellite,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001810 requiresCell, hasMonetaryCost, supportsAltitude,
1811 supportsSpeed, supportsBearing, powerRequirement, accuracy);
Jean-Baptiste Queru72b1f372009-08-31 09:17:57 -07001812 // remove the real provider if we are replacing GPS or network provider
1813 if (LocationManager.GPS_PROVIDER.equals(name)
1814 || LocationManager.NETWORK_PROVIDER.equals(name)) {
1815 LocationProviderProxy proxy = mProvidersByName.get(name);
1816 if (proxy != null) {
1817 proxy.enableLocationTracking(false);
1818 removeProvider(proxy);
1819 }
1820 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001821 if (mProvidersByName.get(name) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001822 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1823 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001824 addProvider(new LocationProviderProxy(name, provider));
1825 mMockProviders.put(name, provider);
Jean-Baptiste Queru72b1f372009-08-31 09:17:57 -07001826 mLastKnownLocation.put(name, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001827 updateProvidersLocked();
1828 }
1829 }
1830
1831 public void removeTestProvider(String provider) {
1832 checkMockPermissionsSafe();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001833 synchronized (mLock) {
1834 MockProvider mockProvider = mMockProviders.get(provider);
1835 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001836 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1837 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001838 removeProvider(mProvidersByName.get(provider));
1839 mMockProviders.remove(mockProvider);
Jean-Baptiste Queru72b1f372009-08-31 09:17:57 -07001840 // reinstall real provider if we were mocking GPS or network provider
1841 if (LocationManager.GPS_PROVIDER.equals(provider) &&
1842 mGpsLocationProvider != null) {
1843 addProvider(mGpsLocationProvider);
1844 } else if (LocationManager.NETWORK_PROVIDER.equals(provider) &&
1845 mNetworkLocationProvider != null) {
1846 addProvider(mNetworkLocationProvider);
1847 }
1848 mLastKnownLocation.put(provider, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001849 updateProvidersLocked();
1850 }
1851 }
1852
1853 public void setTestProviderLocation(String provider, Location loc) {
1854 checkMockPermissionsSafe();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001855 synchronized (mLock) {
1856 MockProvider mockProvider = mMockProviders.get(provider);
1857 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001858 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1859 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001860 // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
1861 long identity = Binder.clearCallingIdentity();
1862 mockProvider.setLocation(loc);
1863 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001864 }
1865 }
1866
1867 public void clearTestProviderLocation(String provider) {
1868 checkMockPermissionsSafe();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001869 synchronized (mLock) {
1870 MockProvider mockProvider = mMockProviders.get(provider);
1871 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001872 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1873 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001874 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001875 }
1876 }
1877
1878 public void setTestProviderEnabled(String provider, boolean enabled) {
1879 checkMockPermissionsSafe();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001880 synchronized (mLock) {
1881 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 }
1885 if (enabled) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001886 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001887 mEnabledProviders.add(provider);
1888 mDisabledProviders.remove(provider);
1889 } else {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001890 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001891 mEnabledProviders.remove(provider);
1892 mDisabledProviders.add(provider);
1893 }
1894 updateProvidersLocked();
1895 }
1896 }
1897
1898 public void clearTestProviderEnabled(String provider) {
1899 checkMockPermissionsSafe();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001900 synchronized (mLock) {
1901 MockProvider mockProvider = mMockProviders.get(provider);
1902 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001903 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1904 }
1905 mEnabledProviders.remove(provider);
1906 mDisabledProviders.remove(provider);
1907 updateProvidersLocked();
1908 }
1909 }
1910
1911 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1912 checkMockPermissionsSafe();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001913 synchronized (mLock) {
1914 MockProvider mockProvider = mMockProviders.get(provider);
1915 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001916 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1917 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001918 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001919 }
1920 }
1921
1922 public void clearTestProviderStatus(String provider) {
1923 checkMockPermissionsSafe();
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001924 synchronized (mLock) {
1925 MockProvider mockProvider = mMockProviders.get(provider);
1926 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001927 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1928 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001929 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001930 }
1931 }
1932
1933 private void log(String log) {
1934 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1935 Log.d(TAG, log);
1936 }
1937 }
1938
1939 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1940 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1941 != PackageManager.PERMISSION_GRANTED) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001942 pw.println("Permission Denial: can't dump LocationManagerService from from pid="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001943 + Binder.getCallingPid()
1944 + ", uid=" + Binder.getCallingUid());
1945 return;
1946 }
1947
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001948 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001949 pw.println("Current Location Manager state:");
1950 pw.println(" sProvidersLoaded=" + sProvidersLoaded);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001951 pw.println(" Listeners:");
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001952 int N = mReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001953 for (int i=0; i<N; i++) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001954 pw.println(" " + mReceivers.get(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001955 }
1956 pw.println(" Location Listeners:");
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001957 for (Receiver i : mReceivers.values()) {
1958 pw.println(" " + i + ":");
1959 for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001960 pw.println(" " + j.getKey() + ":");
1961 j.getValue().dump(pw, " ");
1962 }
1963 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001964 pw.println(" Records by Provider:");
1965 for (Map.Entry<String, ArrayList<UpdateRecord>> i
1966 : mRecordsByProvider.entrySet()) {
1967 pw.println(" " + i.getKey() + ":");
1968 for (UpdateRecord j : i.getValue()) {
1969 pw.println(" " + j + ":");
1970 j.dump(pw, " ");
1971 }
1972 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001973 pw.println(" Last Known Locations:");
1974 for (Map.Entry<String, Location> i
1975 : mLastKnownLocation.entrySet()) {
1976 pw.println(" " + i.getKey() + ":");
1977 i.getValue().dump(new PrintWriterPrinter(pw), " ");
1978 }
1979 if (mProximityAlerts.size() > 0) {
1980 pw.println(" Proximity Alerts:");
1981 for (Map.Entry<PendingIntent, ProximityAlert> i
1982 : mProximityAlerts.entrySet()) {
1983 pw.println(" " + i.getKey() + ":");
1984 i.getValue().dump(pw, " ");
1985 }
1986 }
1987 if (mProximitiesEntered.size() > 0) {
1988 pw.println(" Proximities Entered:");
1989 for (ProximityAlert i : mProximitiesEntered) {
1990 pw.println(" " + i + ":");
1991 i.dump(pw, " ");
1992 }
1993 }
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07001994 pw.println(" mProximityReceiver=" + mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001995 pw.println(" mProximityListener=" + mProximityListener);
1996 if (mEnabledProviders.size() > 0) {
1997 pw.println(" Enabled Providers:");
1998 for (String i : mEnabledProviders) {
1999 pw.println(" " + i);
2000 }
2001
2002 }
2003 if (mDisabledProviders.size() > 0) {
2004 pw.println(" Disabled Providers:");
2005 for (String i : mDisabledProviders) {
2006 pw.println(" " + i);
2007 }
2008
2009 }
2010 if (mMockProviders.size() > 0) {
2011 pw.println(" Mock Providers:");
2012 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Jean-Baptiste Queru843ef362009-05-20 11:28:04 -07002013 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002014 }
2015 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002016 }
2017 }
2018}