blob: 05888e03d4eaaf69b3dfcb2985b7335dad1415d1 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import java.io.BufferedReader;
20import java.io.File;
21import java.io.FileDescriptor;
22import java.io.FileReader;
23import java.io.FileWriter;
24import java.io.IOException;
25import java.io.PrintWriter;
26import java.util.ArrayList;
27import java.util.HashMap;
28import java.util.HashSet;
29import java.util.List;
30import java.util.Map;
Mike Lockwood9637d472009-04-02 21:41:57 -070031import java.util.Observable;
32import java.util.Observer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import java.util.Set;
34import java.util.regex.Pattern;
35
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.app.PendingIntent;
37import android.content.BroadcastReceiver;
Mike Lockwood9637d472009-04-02 21:41:57 -070038import android.content.ContentQueryMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.content.ContentResolver;
40import android.content.Context;
41import android.content.Intent;
42import android.content.IntentFilter;
43import android.content.pm.PackageManager;
Mike Lockwood9637d472009-04-02 21:41:57 -070044import android.database.Cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.location.Address;
Mike Lockwooda55c3212009-04-15 11:10:11 -040046import android.location.IGeocodeProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.location.IGpsStatusListener;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -040048import android.location.IGpsStatusProvider;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070049import android.location.ILocationCollector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.location.ILocationListener;
51import android.location.ILocationManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070052import android.location.ILocationProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.location.Location;
54import android.location.LocationManager;
55import android.location.LocationProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.net.ConnectivityManager;
57import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.os.Binder;
59import android.os.Bundle;
60import android.os.Handler;
61import android.os.IBinder;
Mike Lockwood3d12b512009-04-21 23:25:35 -070062import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.os.Message;
64import android.os.PowerManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070065import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066import android.os.RemoteException;
67import android.os.SystemClock;
68import android.provider.Settings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import android.util.Config;
70import android.util.Log;
71import android.util.PrintWriterPrinter;
72import android.util.SparseIntArray;
73
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074import com.android.internal.location.GpsLocationProvider;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070075import com.android.internal.location.LocationProviderProxy;
Mike Lockwood7ec434e2009-03-27 07:46:48 -070076import com.android.internal.location.MockProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077import com.android.server.am.BatteryStatsService;
78
79/**
80 * The service class that manages LocationProviders and issues location
81 * updates and alerts.
82 *
83 * {@hide}
84 */
Mike Lockwood3d12b512009-04-21 23:25:35 -070085public class LocationManagerService extends ILocationManager.Stub implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 private static final String TAG = "LocationManagerService";
The Android Open Source Project10592532009-03-18 17:39:46 -070087 private static final boolean LOCAL_LOGV = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088
89 // Minimum time interval between last known location writes, in milliseconds.
90 private static final long MIN_LAST_KNOWN_LOCATION_TIME = 60L * 1000L;
91
92 // Max time to hold wake lock for, in milliseconds.
93 private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L;
94
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 // The last time a location was written, by provider name.
96 private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
97
98 private static final Pattern PATTERN_COMMA = Pattern.compile(",");
99
100 private static final String ACCESS_FINE_LOCATION =
101 android.Manifest.permission.ACCESS_FINE_LOCATION;
102 private static final String ACCESS_COARSE_LOCATION =
103 android.Manifest.permission.ACCESS_COARSE_LOCATION;
104 private static final String ACCESS_MOCK_LOCATION =
105 android.Manifest.permission.ACCESS_MOCK_LOCATION;
106 private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
107 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
Mike Lockwood275555c2009-05-01 11:30:34 -0400108 private static final String INSTALL_LOCATION_PROVIDER =
109 android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
110 private static final String INSTALL_LOCATION_COLLECTOR =
111 android.Manifest.permission.INSTALL_LOCATION_COLLECTOR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112
113 // Set of providers that are explicitly enabled
114 private final Set<String> mEnabledProviders = new HashSet<String>();
115
116 // Set of providers that are explicitly disabled
117 private final Set<String> mDisabledProviders = new HashSet<String>();
118
119 // Locations, status values, and extras for mock providers
Mike Lockwood7ec434e2009-03-27 07:46:48 -0700120 private final HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121
122 private static boolean sProvidersLoaded = false;
123
124 private final Context mContext;
Mike Lockwooda55c3212009-04-15 11:10:11 -0400125 private IGeocodeProvider mGeocodeProvider;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400126 private IGpsStatusProvider mGpsStatusProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 private LocationWorkerHandler mLocationHandler;
128
129 // Handler messages
Mike Lockwood4e50b782009-04-03 08:24:43 -0700130 private static final int MESSAGE_LOCATION_CHANGED = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400132 // wakelock variables
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 private final static String WAKELOCK_KEY = "LocationManagerService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 private PowerManager.WakeLock mWakeLock = null;
Mike Lockwood48f17512009-04-23 09:12:08 -0700135 private int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 /**
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400138 * List of all receivers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 */
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400140 private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400142
143 /**
144 * List of location providers.
145 */
146 private final ArrayList<LocationProviderProxy> mProviders =
147 new ArrayList<LocationProviderProxy>();
148 private final HashMap<String, LocationProviderProxy> mProvidersByName
149 = new HashMap<String, LocationProviderProxy>();
150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 /**
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400152 * Object used internally for synchronization
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 */
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400154 private final Object mLock = new Object();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155
156 /**
157 * Mapping from provider name to all its UpdateRecords
158 */
159 private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
160 new HashMap<String,ArrayList<UpdateRecord>>();
161
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 // Proximity listeners
Mike Lockwood48f17512009-04-23 09:12:08 -0700163 private Receiver mProximityReceiver = null;
164 private ILocationListener mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
166 new HashMap<PendingIntent,ProximityAlert>();
167 private HashSet<ProximityAlert> mProximitiesEntered =
168 new HashSet<ProximityAlert>();
169
170 // Last known location for each provider
171 private HashMap<String,Location> mLastKnownLocation =
172 new HashMap<String,Location>();
173
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 // Location collector
175 private ILocationCollector mCollector;
176
The Android Open Source Project4df24232009-03-05 14:34:35 -0800177 private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800178
Mike Lockwood9637d472009-04-02 21:41:57 -0700179 // for Settings change notification
180 private ContentQueryMap mSettings;
181
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 /**
183 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
184 * location updates.
185 */
Mike Lockwood48f17512009-04-23 09:12:08 -0700186 private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 final ILocationListener mListener;
188 final PendingIntent mPendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 final Object mKey;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400190 final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
Mike Lockwood48f17512009-04-23 09:12:08 -0700191 int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400193 Receiver(ILocationListener listener) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 mListener = listener;
195 mPendingIntent = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 mKey = listener.asBinder();
197 }
198
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400199 Receiver(PendingIntent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 mPendingIntent = intent;
201 mListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 mKey = intent;
203 }
204
205 @Override
206 public boolean equals(Object otherObj) {
207 if (otherObj instanceof Receiver) {
208 return mKey.equals(
209 ((Receiver)otherObj).mKey);
210 }
211 return false;
212 }
213
214 @Override
215 public int hashCode() {
216 return mKey.hashCode();
217 }
Mike Lockwood3681f262009-05-12 10:52:03 -0400218
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 @Override
220 public String toString() {
221 if (mListener != null) {
222 return "Receiver{"
223 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400224 + " Listener " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 } else {
226 return "Receiver{"
227 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400228 + " Intent " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 }
230 }
231
232 public boolean isListener() {
233 return mListener != null;
234 }
235
236 public boolean isPendingIntent() {
237 return mPendingIntent != null;
238 }
239
240 public ILocationListener getListener() {
241 if (mListener != null) {
242 return mListener;
243 }
244 throw new IllegalStateException("Request for non-existent listener");
245 }
246
247 public PendingIntent getPendingIntent() {
248 if (mPendingIntent != null) {
249 return mPendingIntent;
250 }
251 throw new IllegalStateException("Request for non-existent intent");
252 }
253
254 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
255 if (mListener != null) {
256 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700257 synchronized (this) {
258 // synchronize to ensure incrementPendingBroadcastsLocked()
259 // is called before decrementPendingBroadcasts()
260 mListener.onStatusChanged(provider, status, extras);
261 if (mListener != mProximityListener) {
262 // call this after broadcasting so we do not increment
263 // if we throw an exeption.
264 incrementPendingBroadcastsLocked();
265 }
266 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 } catch (RemoteException e) {
268 return false;
269 }
270 } else {
271 Intent statusChanged = new Intent();
272 statusChanged.putExtras(extras);
273 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
274 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700275 synchronized (this) {
276 // synchronize to ensure incrementPendingBroadcastsLocked()
277 // is called before decrementPendingBroadcasts()
278 mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler);
279 // call this after broadcasting so we do not increment
280 // if we throw an exeption.
281 incrementPendingBroadcastsLocked();
282 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 } catch (PendingIntent.CanceledException e) {
284 return false;
285 }
286 }
287 return true;
288 }
289
290 public boolean callLocationChangedLocked(Location location) {
291 if (mListener != null) {
292 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700293 synchronized (this) {
294 // synchronize to ensure incrementPendingBroadcastsLocked()
295 // is called before decrementPendingBroadcasts()
296 mListener.onLocationChanged(location);
297 if (mListener != mProximityListener) {
298 // call this after broadcasting so we do not increment
299 // if we throw an exeption.
300 incrementPendingBroadcastsLocked();
301 }
302 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 } catch (RemoteException e) {
304 return false;
305 }
306 } else {
307 Intent locationChanged = new Intent();
308 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
309 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700310 synchronized (this) {
311 // synchronize to ensure incrementPendingBroadcastsLocked()
312 // is called before decrementPendingBroadcasts()
313 mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler);
314 // call this after broadcasting so we do not increment
315 // if we throw an exeption.
316 incrementPendingBroadcastsLocked();
317 }
318 } catch (PendingIntent.CanceledException e) {
319 return false;
320 }
321 }
322 return true;
323 }
324
325 public boolean callProviderEnabledLocked(String provider, boolean enabled) {
326 if (mListener != null) {
327 try {
328 synchronized (this) {
329 // synchronize to ensure incrementPendingBroadcastsLocked()
330 // is called before decrementPendingBroadcasts()
331 if (enabled) {
332 mListener.onProviderEnabled(provider);
333 } else {
334 mListener.onProviderDisabled(provider);
335 }
336 if (mListener != mProximityListener) {
337 // call this after broadcasting so we do not increment
338 // if we throw an exeption.
339 incrementPendingBroadcastsLocked();
340 }
341 }
342 } catch (RemoteException e) {
343 return false;
344 }
345 } else {
346 Intent providerIntent = new Intent();
347 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
348 try {
349 synchronized (this) {
350 // synchronize to ensure incrementPendingBroadcastsLocked()
351 // is called before decrementPendingBroadcasts()
352 mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler);
353 // call this after broadcasting so we do not increment
354 // if we throw an exeption.
355 incrementPendingBroadcastsLocked();
356 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 } catch (PendingIntent.CanceledException e) {
358 return false;
359 }
360 }
361 return true;
362 }
363
364 public void binderDied() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700365 if (LOCAL_LOGV) {
366 Log.v(TAG, "Location listener died");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 }
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400368 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 removeUpdatesLocked(this);
370 }
Mike Lockwood48f17512009-04-23 09:12:08 -0700371 synchronized (this) {
372 if (mPendingBroadcasts > 0) {
373 LocationManagerService.this.decrementPendingBroadcasts();
374 mPendingBroadcasts = 0;
375 }
376 }
377 }
378
379 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
380 int resultCode, String resultData, Bundle resultExtras) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400381 synchronized (this) {
382 decrementPendingBroadcastsLocked();
Mike Lockwood48f17512009-04-23 09:12:08 -0700383 }
384 }
385
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400386 // this must be called while synchronized by caller in a synchronized block
387 // containing the sending of the broadcaset
388 private void incrementPendingBroadcastsLocked() {
389 if (mPendingBroadcasts++ == 0) {
390 LocationManagerService.this.incrementPendingBroadcasts();
391 }
392 }
393
394 private void decrementPendingBroadcastsLocked() {
395 if (--mPendingBroadcasts == 0) {
396 LocationManagerService.this.decrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -0700397 }
398 }
399 }
400
401 public void locationCallbackFinished(ILocationListener listener) {
402 Receiver receiver = getReceiver(listener);
403 if (receiver != null) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400404 synchronized (receiver) {
405 // so wakelock calls will succeed
406 long identity = Binder.clearCallingIdentity();
407 receiver.decrementPendingBroadcastsLocked();
408 Binder.restoreCallingIdentity(identity);
409 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 }
411 }
412
Mike Lockwood9637d472009-04-02 21:41:57 -0700413 private final class SettingsObserver implements Observer {
414 public void update(Observable o, Object arg) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400415 synchronized (mLock) {
Mike Lockwood9637d472009-04-02 21:41:57 -0700416 updateProvidersLocked();
417 }
418 }
419 }
420
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 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
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400512 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);
519 mProvidersByName.remove(provider.getName());
520 }
521
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 private void loadProviders() {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400523 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 if (sProvidersLoaded) {
525 return;
526 }
527
528 // Load providers
529 loadProvidersLocked();
530 sProvidersLoaded = true;
531 }
532 }
533
534 private void loadProvidersLocked() {
535 try {
536 _loadProvidersLocked();
537 } catch (Exception e) {
538 Log.e(TAG, "Exception loading providers:", e);
539 }
540 }
541
542 private void _loadProvidersLocked() {
543 // Attempt to load "real" providers first
544 if (GpsLocationProvider.isSupported()) {
545 // Create a gps location provider
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400546 GpsLocationProvider provider = new GpsLocationProvider(mContext, this);
547 mGpsStatusProvider = provider.getGpsStatusProvider();
Mike Lockwood8dfe5d82009-05-07 11:49:01 -0400548 LocationProviderProxy proxy = new LocationProviderProxy(LocationManager.GPS_PROVIDER, provider);
549 addProvider(proxy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 }
551
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 updateProvidersLocked();
553 }
554
555 /**
556 * @param context the context that the LocationManagerService runs in
557 */
558 public LocationManagerService(Context context) {
559 super();
560 mContext = context;
Mike Lockwood3d12b512009-04-21 23:25:35 -0700561
562 Thread thread = new Thread(null, this, "LocationManagerService");
563 thread.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564
The Android Open Source Project10592532009-03-18 17:39:46 -0700565 if (LOCAL_LOGV) {
566 Log.v(TAG, "Constructed LocationManager Service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 }
Mike Lockwood3d12b512009-04-21 23:25:35 -0700568 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569
Mike Lockwood3d12b512009-04-21 23:25:35 -0700570 private void initialize() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 // Create a wake lock, needs to be done before calling loadProviders() below
572 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
573 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400574
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 // Load providers
576 loadProviders();
577
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 // Register for Network (Wifi or Mobile) updates
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 IntentFilter intentFilter = new IntentFilter();
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400580 intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
581 // Register for Package Manager updates
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
583 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
Mike Lockwood0528b9b2009-05-07 10:12:54 -0400584 mContext.registerReceiver(mBroadcastReceiver, intentFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585
Mike Lockwood9637d472009-04-02 21:41:57 -0700586 // listen for settings changes
587 ContentResolver resolver = mContext.getContentResolver();
588 Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
589 "(" + Settings.System.NAME + "=?)",
590 new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
591 null);
592 mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler);
593 SettingsObserver settingsObserver = new SettingsObserver();
594 mSettings.addObserver(settingsObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 }
596
Mike Lockwood3d12b512009-04-21 23:25:35 -0700597 public void run()
598 {
599 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
600 Looper.prepare();
601 mLocationHandler = new LocationWorkerHandler();
602 initialize();
603 Looper.loop();
604 }
605
Mike Lockwood275555c2009-05-01 11:30:34 -0400606 public void installLocationProvider(String name, ILocationProvider provider) {
607 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
608 != PackageManager.PERMISSION_GRANTED) {
609 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700610 }
611
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400612 synchronized (mLock) {
Mike Lockwood3681f262009-05-12 10:52:03 -0400613 // check to see if we are reinstalling a dead provider
614 LocationProviderProxy oldProvider = mProvidersByName.get(name);
615 if (oldProvider != null) {
616 if (oldProvider.isDead()) {
617 Log.d(TAG, "replacing dead provider");
618 removeProvider(oldProvider);
619 } else {
620 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
621 }
622 }
623
Mike Lockwood8dfe5d82009-05-07 11:49:01 -0400624 LocationProviderProxy proxy = new LocationProviderProxy(name, provider);
625 addProvider(proxy);
626 updateProvidersLocked();
Mike Lockwood275555c2009-05-01 11:30:34 -0400627
Mike Lockwood8dfe5d82009-05-07 11:49:01 -0400628 // notify provider of current network state
629 proxy.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630 }
631 }
632
Mike Lockwood275555c2009-05-01 11:30:34 -0400633 public void installLocationCollector(ILocationCollector collector) {
634 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_COLLECTOR)
635 != PackageManager.PERMISSION_GRANTED) {
636 throw new SecurityException("Requires INSTALL_LOCATION_COLLECTOR permission");
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700637 }
638
Mike Lockwood275555c2009-05-01 11:30:34 -0400639 // FIXME - only support one collector
Mike Lockwood98cb6672009-04-17 18:03:44 -0400640 mCollector = collector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 }
642
Mike Lockwood275555c2009-05-01 11:30:34 -0400643 public void installGeocodeProvider(IGeocodeProvider provider) {
644 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
645 != PackageManager.PERMISSION_GRANTED) {
646 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
Mike Lockwooda55c3212009-04-15 11:10:11 -0400647 }
648
649 mGeocodeProvider = provider;
650 }
651
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800652 private boolean isAllowedBySettingsLocked(String provider) {
653 if (mEnabledProviders.contains(provider)) {
654 return true;
655 }
656 if (mDisabledProviders.contains(provider)) {
657 return false;
658 }
659 // Use system settings
660 ContentResolver resolver = mContext.getContentResolver();
661 String allowedProviders = Settings.Secure.getString(resolver,
662 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
663
664 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
665 }
666
667 private void checkPermissionsSafe(String provider) {
668 if (LocationManager.GPS_PROVIDER.equals(provider)
669 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
670 != PackageManager.PERMISSION_GRANTED)) {
671 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
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 throw new SecurityException(
679 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
680 }
681 }
682
683 private boolean isAllowedProviderSafe(String provider) {
684 if (LocationManager.GPS_PROVIDER.equals(provider)
685 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
686 != PackageManager.PERMISSION_GRANTED)) {
687 return false;
688 }
689 if (LocationManager.NETWORK_PROVIDER.equals(provider)
690 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
691 != PackageManager.PERMISSION_GRANTED)
692 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
693 != PackageManager.PERMISSION_GRANTED)) {
694 return false;
695 }
696
697 return true;
698 }
699
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700 public List<String> getAllProviders() {
701 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400702 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 return _getAllProvidersLocked();
704 }
705 } catch (SecurityException se) {
706 throw se;
707 } catch (Exception e) {
708 Log.e(TAG, "getAllProviders got exception:", e);
709 return null;
710 }
711 }
712
713 private List<String> _getAllProvidersLocked() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700714 if (LOCAL_LOGV) {
715 Log.v(TAG, "getAllProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400717 ArrayList<String> out = new ArrayList<String>(mProviders.size());
718 for (int i = mProviders.size() - 1; i >= 0; i--) {
719 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800720 out.add(p.getName());
721 }
722 return out;
723 }
724
725 public List<String> getProviders(boolean enabledOnly) {
726 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400727 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728 return _getProvidersLocked(enabledOnly);
729 }
730 } catch (SecurityException se) {
731 throw se;
732 } catch (Exception e) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700733 Log.e(TAG, "getProviders got exception:", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 return null;
735 }
736 }
737
738 private List<String> _getProvidersLocked(boolean enabledOnly) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700739 if (LOCAL_LOGV) {
740 Log.v(TAG, "getProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400742 ArrayList<String> out = new ArrayList<String>(mProviders.size());
743 for (int i = mProviders.size() - 1; i >= 0; i--) {
744 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800745 String name = p.getName();
746 if (isAllowedProviderSafe(name)) {
747 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
748 continue;
749 }
750 out.add(name);
751 }
752 }
753 return out;
754 }
755
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 private void updateProvidersLocked() {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400757 for (int i = mProviders.size() - 1; i >= 0; i--) {
758 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 boolean isEnabled = p.isEnabled();
760 String name = p.getName();
761 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
762
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800763 if (isEnabled && !shouldBeEnabled) {
764 updateProviderListenersLocked(name, false);
765 } else if (!isEnabled && shouldBeEnabled) {
766 updateProviderListenersLocked(name, true);
767 }
768
769 }
770 }
771
772 private void updateProviderListenersLocked(String provider, boolean enabled) {
773 int listeners = 0;
774
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400775 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776 if (p == null) {
777 return;
778 }
779
780 ArrayList<Receiver> deadReceivers = null;
781
782 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
783 if (records != null) {
784 final int N = records.size();
785 for (int i=0; i<N; i++) {
786 UpdateRecord record = records.get(i);
787 // Sends a notification message to the receiver
Mike Lockwood48f17512009-04-23 09:12:08 -0700788 if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
789 if (deadReceivers == null) {
790 deadReceivers = new ArrayList<Receiver>();
791 deadReceivers.add(record.mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 }
794 listeners++;
795 }
796 }
797
798 if (deadReceivers != null) {
799 for (int i=deadReceivers.size()-1; i>=0; i--) {
800 removeUpdatesLocked(deadReceivers.get(i));
801 }
802 }
803
804 if (enabled) {
805 p.enable();
806 if (listeners > 0) {
807 p.setMinTime(getMinTimeLocked(provider));
808 p.enableLocationTracking(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 }
810 } else {
811 p.enableLocationTracking(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 p.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800814 }
815
816 private long getMinTimeLocked(String provider) {
817 long minTime = Long.MAX_VALUE;
818 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
819 if (records != null) {
820 for (int i=records.size()-1; i>=0; i--) {
821 minTime = Math.min(minTime, records.get(i).mMinTime);
822 }
823 }
824 return minTime;
825 }
826
827 private class UpdateRecord {
828 final String mProvider;
829 final Receiver mReceiver;
830 final long mMinTime;
831 final float mMinDistance;
832 final int mUid;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400833 Location mLastFixBroadcast;
834 long mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800835
836 /**
837 * Note: must be constructed with lock held.
838 */
839 UpdateRecord(String provider, long minTime, float minDistance,
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400840 Receiver receiver, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841 mProvider = provider;
842 mReceiver = receiver;
843 mMinTime = minTime;
844 mMinDistance = minDistance;
845 mUid = uid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800846
847 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
848 if (records == null) {
849 records = new ArrayList<UpdateRecord>();
850 mRecordsByProvider.put(provider, records);
851 }
852 if (!records.contains(this)) {
853 records.add(this);
854 }
855 }
856
857 /**
858 * Method to be called when a record will no longer be used. Calling this multiple times
859 * must have the same effect as calling it once.
860 */
861 void disposeLocked() {
862 ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
863 records.remove(this);
864 }
865
866 @Override
867 public String toString() {
868 return "UpdateRecord{"
869 + Integer.toHexString(System.identityHashCode(this))
870 + " " + mProvider + " " + mReceiver + "}";
871 }
872
873 void dump(PrintWriter pw, String prefix) {
874 pw.println(prefix + this);
875 pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
876 pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400877 pw.println(prefix + "mUid=" + mUid);
878 pw.println(prefix + "mLastFixBroadcast:");
879 mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
880 pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 }
882
883 /**
884 * Calls dispose().
885 */
886 @Override protected void finalize() {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400887 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800888 disposeLocked();
889 }
890 }
891 }
892
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400893 private Receiver getReceiver(ILocationListener listener) {
894 IBinder binder = listener.asBinder();
895 Receiver receiver = mReceivers.get(binder);
896 if (receiver == null) {
897 receiver = new Receiver(listener);
898 mReceivers.put(binder, receiver);
899
900 try {
901 if (receiver.isListener()) {
902 receiver.getListener().asBinder().linkToDeath(receiver, 0);
903 }
904 } catch (RemoteException e) {
905 Log.e(TAG, "linkToDeath failed:", e);
906 return null;
907 }
908 }
909 return receiver;
910 }
911
912 private Receiver getReceiver(PendingIntent intent) {
913 Receiver receiver = mReceivers.get(intent);
914 if (receiver == null) {
915 receiver = new Receiver(intent);
916 mReceivers.put(intent, receiver);
917 }
918 return receiver;
919 }
920
921 private boolean providerHasListener(String provider, int uid, Receiver excludedReceiver) {
922 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
923 if (records != null) {
924 for (int i = records.size() - 1; i >= 0; i--) {
925 UpdateRecord record = records.get(i);
926 if (record.mUid == uid && record.mReceiver != excludedReceiver) {
927 return true;
928 }
929 }
930 }
Mike Lockwood95427cd2009-05-07 13:27:54 -0400931 for (ProximityAlert alert : mProximityAlerts.values()) {
932 if (alert.mUid == uid) {
933 return true;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400934 }
935 }
936 return false;
937 }
938
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939 public void requestLocationUpdates(String provider,
940 long minTime, float minDistance, ILocationListener listener) {
941
942 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400943 synchronized (mLock) {
944 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800945 }
946 } catch (SecurityException se) {
947 throw se;
948 } catch (Exception e) {
949 Log.e(TAG, "requestUpdates got exception:", e);
950 }
951 }
952
953 public void requestLocationUpdatesPI(String provider,
954 long minTime, float minDistance, PendingIntent intent) {
955 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400956 synchronized (mLock) {
957 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800958 }
959 } catch (SecurityException se) {
960 throw se;
961 } catch (Exception e) {
962 Log.e(TAG, "requestUpdates got exception:", e);
963 }
964 }
965
966 private void requestLocationUpdatesLocked(String provider,
967 long minTime, float minDistance, Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700968 if (LOCAL_LOGV) {
969 Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800970 }
971
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400972 LocationProviderProxy proxy = mProvidersByName.get(provider);
973 if (proxy == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800974 throw new IllegalArgumentException("provider=" + provider);
975 }
976
977 checkPermissionsSafe(provider);
978
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 // so wakelock calls will succeed
980 final int callingUid = Binder.getCallingUid();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400981 boolean newUid = !providerHasListener(provider, callingUid, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800982 long identity = Binder.clearCallingIdentity();
983 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400984 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
985 UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986 if (oldRecord != null) {
987 oldRecord.disposeLocked();
988 }
989
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400990 if (newUid) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400991 proxy.addListener(callingUid);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400992 }
993
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
995 if (isProviderEnabled) {
996 long minTimeForProvider = getMinTimeLocked(provider);
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400997 proxy.setMinTime(minTimeForProvider);
998 proxy.enableLocationTracking(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999 } else {
Mike Lockwood48f17512009-04-23 09:12:08 -07001000 // Notify the listener that updates are currently disabled
1001 receiver.callProviderEnabledLocked(provider, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 }
1003 } finally {
1004 Binder.restoreCallingIdentity(identity);
1005 }
1006 }
1007
1008 public void removeUpdates(ILocationListener listener) {
1009 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001010 synchronized (mLock) {
1011 removeUpdatesLocked(getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012 }
1013 } catch (SecurityException se) {
1014 throw se;
1015 } catch (Exception e) {
1016 Log.e(TAG, "removeUpdates got exception:", e);
1017 }
1018 }
1019
1020 public void removeUpdatesPI(PendingIntent intent) {
1021 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001022 synchronized (mLock) {
1023 removeUpdatesLocked(getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024 }
1025 } catch (SecurityException se) {
1026 throw se;
1027 } catch (Exception e) {
1028 Log.e(TAG, "removeUpdates got exception:", e);
1029 }
1030 }
1031
1032 private void removeUpdatesLocked(Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001033 if (LOCAL_LOGV) {
1034 Log.v(TAG, "_removeUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001035 }
1036
1037 // so wakelock calls will succeed
1038 final int callingUid = Binder.getCallingUid();
1039 long identity = Binder.clearCallingIdentity();
1040 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001041 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1042 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001043 }
1044
1045 // Record which providers were associated with this listener
1046 HashSet<String> providers = new HashSet<String>();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001047 HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048 if (oldRecords != null) {
1049 // Call dispose() on the obsolete update records.
1050 for (UpdateRecord record : oldRecords.values()) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001051 if (!providerHasListener(record.mProvider, callingUid, receiver)) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001052 LocationProviderProxy proxy = mProvidersByName.get(record.mProvider);
1053 if (proxy != null) {
1054 proxy.removeListener(callingUid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 }
1056 }
1057 record.disposeLocked();
1058 }
1059 // Accumulate providers
1060 providers.addAll(oldRecords.keySet());
1061 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001062
1063 // See if the providers associated with this listener have any
1064 // other listeners; if one does, inform it of the new smallest minTime
1065 // value; if one does not, disable location tracking for it
1066 for (String provider : providers) {
1067 // If provider is already disabled, don't need to do anything
1068 if (!isAllowedBySettingsLocked(provider)) {
1069 continue;
1070 }
1071
1072 boolean hasOtherListener = false;
1073 ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1074 if (recordsForProvider != null && recordsForProvider.size() > 0) {
1075 hasOtherListener = true;
1076 }
1077
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001078 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001079 if (p != null) {
1080 if (hasOtherListener) {
1081 p.setMinTime(getMinTimeLocked(provider));
1082 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001083 p.enableLocationTracking(false);
1084 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001085 }
1086 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087 } finally {
1088 Binder.restoreCallingIdentity(identity);
1089 }
1090 }
1091
1092 public boolean addGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001093 if (mGpsStatusProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001094 return false;
1095 }
1096 if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1097 PackageManager.PERMISSION_GRANTED) {
1098 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1099 }
1100
1101 try {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001102 mGpsStatusProvider.addGpsStatusListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001103 } catch (RemoteException e) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001104 Log.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001105 return false;
1106 }
1107 return true;
1108 }
1109
1110 public void removeGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001111 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001112 try {
1113 mGpsStatusProvider.removeGpsStatusListener(listener);
1114 } catch (Exception e) {
1115 Log.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
1116 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001117 }
1118 }
1119
1120 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1121 // first check for permission to the provider
1122 checkPermissionsSafe(provider);
1123 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1124 if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1125 != PackageManager.PERMISSION_GRANTED)) {
1126 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1127 }
1128
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001129 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001130 LocationProviderProxy proxy = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001131 if (provider == null) {
1132 return false;
1133 }
1134
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001135 return proxy.sendExtraCommand(command, extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001136 }
1137 }
1138
1139 class ProximityAlert {
1140 final int mUid;
1141 final double mLatitude;
1142 final double mLongitude;
1143 final float mRadius;
1144 final long mExpiration;
1145 final PendingIntent mIntent;
1146 final Location mLocation;
1147
1148 public ProximityAlert(int uid, double latitude, double longitude,
1149 float radius, long expiration, PendingIntent intent) {
1150 mUid = uid;
1151 mLatitude = latitude;
1152 mLongitude = longitude;
1153 mRadius = radius;
1154 mExpiration = expiration;
1155 mIntent = intent;
1156
1157 mLocation = new Location("");
1158 mLocation.setLatitude(latitude);
1159 mLocation.setLongitude(longitude);
1160 }
1161
1162 long getExpiration() {
1163 return mExpiration;
1164 }
1165
1166 PendingIntent getIntent() {
1167 return mIntent;
1168 }
1169
1170 boolean isInProximity(double latitude, double longitude) {
1171 Location loc = new Location("");
1172 loc.setLatitude(latitude);
1173 loc.setLongitude(longitude);
1174
1175 double radius = loc.distanceTo(mLocation);
1176 return radius <= mRadius;
1177 }
1178
1179 @Override
1180 public String toString() {
1181 return "ProximityAlert{"
1182 + Integer.toHexString(System.identityHashCode(this))
1183 + " uid " + mUid + mIntent + "}";
1184 }
1185
1186 void dump(PrintWriter pw, String prefix) {
1187 pw.println(prefix + this);
1188 pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
1189 pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
1190 pw.println(prefix + "mIntent=" + mIntent);
1191 pw.println(prefix + "mLocation:");
1192 mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
1193 }
1194 }
1195
1196 // Listener for receiving locations to trigger proximity alerts
Mike Lockwood48f17512009-04-23 09:12:08 -07001197 class ProximityListener extends ILocationListener.Stub implements PendingIntent.OnFinished {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001198
1199 boolean isGpsAvailable = false;
1200
1201 // Note: this is called with the lock held.
1202 public void onLocationChanged(Location loc) {
1203
1204 // If Gps is available, then ignore updates from NetworkLocationProvider
1205 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1206 isGpsAvailable = true;
1207 }
1208 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1209 return;
1210 }
1211
1212 // Process proximity alerts
1213 long now = System.currentTimeMillis();
1214 double latitude = loc.getLatitude();
1215 double longitude = loc.getLongitude();
1216 ArrayList<PendingIntent> intentsToRemove = null;
1217
1218 for (ProximityAlert alert : mProximityAlerts.values()) {
1219 PendingIntent intent = alert.getIntent();
1220 long expiration = alert.getExpiration();
1221
1222 if ((expiration == -1) || (now <= expiration)) {
1223 boolean entered = mProximitiesEntered.contains(alert);
1224 boolean inProximity =
1225 alert.isInProximity(latitude, longitude);
1226 if (!entered && inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001227 if (LOCAL_LOGV) {
1228 Log.v(TAG, "Entered alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001229 }
1230 mProximitiesEntered.add(alert);
1231 Intent enteredIntent = new Intent();
1232 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1233 try {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001234 synchronized (this) {
1235 // synchronize to ensure incrementPendingBroadcasts()
Mike Lockwood48f17512009-04-23 09:12:08 -07001236 // is called before decrementPendingBroadcasts()
1237 intent.send(mContext, 0, enteredIntent, this, mLocationHandler);
1238 // call this after broadcasting so we do not increment
1239 // if we throw an exeption.
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001240 incrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -07001241 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001242 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001243 if (LOCAL_LOGV) {
1244 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001245 }
1246 if (intentsToRemove == null) {
1247 intentsToRemove = new ArrayList<PendingIntent>();
1248 }
1249 intentsToRemove.add(intent);
1250 }
1251 } else if (entered && !inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001252 if (LOCAL_LOGV) {
1253 Log.v(TAG, "Exited alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001254 }
1255 mProximitiesEntered.remove(alert);
1256 Intent exitedIntent = new Intent();
1257 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1258 try {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001259 synchronized (this) {
1260 // synchronize to ensure incrementPendingBroadcasts()
Mike Lockwood48f17512009-04-23 09:12:08 -07001261 // is called before decrementPendingBroadcasts()
1262 intent.send(mContext, 0, exitedIntent, this, mLocationHandler);
1263 // call this after broadcasting so we do not increment
1264 // if we throw an exeption.
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001265 incrementPendingBroadcasts();
Mike Lockwood48f17512009-04-23 09:12:08 -07001266 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001267 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001268 if (LOCAL_LOGV) {
1269 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 }
1271 if (intentsToRemove == null) {
1272 intentsToRemove = new ArrayList<PendingIntent>();
1273 }
1274 intentsToRemove.add(intent);
1275 }
1276 }
1277 } else {
1278 // Mark alert for expiration
The Android Open Source Project10592532009-03-18 17:39:46 -07001279 if (LOCAL_LOGV) {
1280 Log.v(TAG, "Expiring proximity alert: " + alert);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001281 }
1282 if (intentsToRemove == null) {
1283 intentsToRemove = new ArrayList<PendingIntent>();
1284 }
1285 intentsToRemove.add(alert.getIntent());
1286 }
1287 }
1288
1289 // Remove expired alerts
1290 if (intentsToRemove != null) {
1291 for (PendingIntent i : intentsToRemove) {
1292 mProximityAlerts.remove(i);
1293 ProximityAlert alert = mProximityAlerts.get(i);
1294 mProximitiesEntered.remove(alert);
1295 }
1296 }
1297
1298 }
1299
1300 // Note: this is called with the lock held.
1301 public void onProviderDisabled(String provider) {
1302 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1303 isGpsAvailable = false;
1304 }
1305 }
1306
1307 // Note: this is called with the lock held.
1308 public void onProviderEnabled(String provider) {
1309 // ignore
1310 }
1311
1312 // Note: this is called with the lock held.
1313 public void onStatusChanged(String provider, int status, Bundle extras) {
1314 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1315 (status != LocationProvider.AVAILABLE)) {
1316 isGpsAvailable = false;
1317 }
1318 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001319
1320 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
1321 int resultCode, String resultData, Bundle resultExtras) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001322 // synchronize to ensure incrementPendingBroadcasts()
1323 // is called before decrementPendingBroadcasts()
1324 synchronized (this) {
1325 decrementPendingBroadcasts();
1326 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001327 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001328 }
1329
1330 public void addProximityAlert(double latitude, double longitude,
1331 float radius, long expiration, PendingIntent intent) {
1332 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001333 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001334 addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
1335 }
1336 } catch (SecurityException se) {
1337 throw se;
1338 } catch (Exception e) {
1339 Log.e(TAG, "addProximityAlert got exception:", e);
1340 }
1341 }
1342
1343 private void addProximityAlertLocked(double latitude, double longitude,
1344 float radius, long expiration, PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001345 if (LOCAL_LOGV) {
1346 Log.v(TAG, "addProximityAlert: latitude = " + latitude +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001347 ", longitude = " + longitude +
1348 ", expiration = " + expiration +
1349 ", intent = " + intent);
1350 }
1351
1352 // Require ability to access all providers for now
1353 if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
1354 !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
1355 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1356 }
1357
1358 if (expiration != -1) {
1359 expiration += System.currentTimeMillis();
1360 }
1361 ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
1362 latitude, longitude, radius, expiration, intent);
1363 mProximityAlerts.put(intent, alert);
1364
Mike Lockwood48f17512009-04-23 09:12:08 -07001365 if (mProximityReceiver == null) {
1366 mProximityListener = new ProximityListener();
1367 mProximityReceiver = new Receiver(mProximityListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368
Mike Lockwood95427cd2009-05-07 13:27:54 -04001369 for (int i = mProviders.size() - 1; i >= 0; i--) {
1370 LocationProviderProxy provider = mProviders.get(i);
Mike Lockwood48f17512009-04-23 09:12:08 -07001371 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001372 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373 }
1374 }
1375
1376 public void removeProximityAlert(PendingIntent intent) {
1377 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001378 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001379 removeProximityAlertLocked(intent);
1380 }
1381 } catch (SecurityException se) {
1382 throw se;
1383 } catch (Exception e) {
1384 Log.e(TAG, "removeProximityAlert got exception:", e);
1385 }
1386 }
1387
1388 private void removeProximityAlertLocked(PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001389 if (LOCAL_LOGV) {
1390 Log.v(TAG, "removeProximityAlert: intent = " + intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001391 }
1392
1393 mProximityAlerts.remove(intent);
1394 if (mProximityAlerts.size() == 0) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001395 removeUpdatesLocked(mProximityReceiver);
1396 mProximityReceiver = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001397 mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001398 }
1399 }
1400
1401 /**
1402 * @return null if the provider does not exits
1403 * @throw SecurityException if the provider is not allowed to be
1404 * accessed by the caller
1405 */
1406 public Bundle getProviderInfo(String provider) {
1407 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001408 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001409 return _getProviderInfoLocked(provider);
1410 }
1411 } catch (SecurityException se) {
1412 throw se;
1413 } catch (Exception e) {
1414 Log.e(TAG, "_getProviderInfo got exception:", e);
1415 return null;
1416 }
1417 }
1418
1419 private Bundle _getProviderInfoLocked(String provider) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001420 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001421 if (p == null) {
1422 return null;
1423 }
1424
1425 checkPermissionsSafe(provider);
1426
1427 Bundle b = new Bundle();
1428 b.putBoolean("network", p.requiresNetwork());
1429 b.putBoolean("satellite", p.requiresSatellite());
1430 b.putBoolean("cell", p.requiresCell());
1431 b.putBoolean("cost", p.hasMonetaryCost());
1432 b.putBoolean("altitude", p.supportsAltitude());
1433 b.putBoolean("speed", p.supportsSpeed());
1434 b.putBoolean("bearing", p.supportsBearing());
1435 b.putInt("power", p.getPowerRequirement());
1436 b.putInt("accuracy", p.getAccuracy());
1437
1438 return b;
1439 }
1440
1441 public boolean isProviderEnabled(String provider) {
1442 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001443 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001444 return _isProviderEnabledLocked(provider);
1445 }
1446 } catch (SecurityException se) {
1447 throw se;
1448 } catch (Exception e) {
1449 Log.e(TAG, "isProviderEnabled got exception:", e);
1450 return false;
1451 }
1452 }
1453
Mike Lockwood275555c2009-05-01 11:30:34 -04001454 public void reportLocation(Location location) {
1455 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
1456 != PackageManager.PERMISSION_GRANTED) {
1457 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
1458 }
1459
Mike Lockwood4e50b782009-04-03 08:24:43 -07001460 mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
1461 Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
1462 mLocationHandler.sendMessageAtFrontOfQueue(m);
1463 }
1464
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001465 private boolean _isProviderEnabledLocked(String provider) {
1466 checkPermissionsSafe(provider);
1467
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001468 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001469 if (p == null) {
1470 throw new IllegalArgumentException("provider=" + provider);
1471 }
1472 return isAllowedBySettingsLocked(provider);
1473 }
1474
1475 public Location getLastKnownLocation(String provider) {
1476 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001477 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478 return _getLastKnownLocationLocked(provider);
1479 }
1480 } catch (SecurityException se) {
1481 throw se;
1482 } catch (Exception e) {
1483 Log.e(TAG, "getLastKnownLocation got exception:", e);
1484 return null;
1485 }
1486 }
1487
1488 private Location _getLastKnownLocationLocked(String provider) {
1489 checkPermissionsSafe(provider);
1490
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001491 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001492 if (p == null) {
1493 throw new IllegalArgumentException("provider=" + provider);
1494 }
1495
1496 if (!isAllowedBySettingsLocked(provider)) {
1497 return null;
1498 }
1499
1500 Location location = mLastKnownLocation.get(provider);
1501 if (location == null) {
1502 // Get the persistent last known location for the provider
1503 location = readLastKnownLocationLocked(provider);
1504 if (location != null) {
1505 mLastKnownLocation.put(provider, location);
1506 }
1507 }
1508
1509 return location;
1510 }
1511
1512 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1513 // Always broadcast the first update
1514 if (lastLoc == null) {
1515 return true;
1516 }
1517
1518 // Don't broadcast same location again regardless of condition
1519 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1520 if (loc.getTime() == lastLoc.getTime()) {
1521 return false;
1522 }
1523
1524 // Check whether sufficient distance has been traveled
1525 double minDistance = record.mMinDistance;
1526 if (minDistance > 0.0) {
1527 if (loc.distanceTo(lastLoc) <= minDistance) {
1528 return false;
1529 }
1530 }
1531
1532 return true;
1533 }
1534
Mike Lockwood4e50b782009-04-03 08:24:43 -07001535 private void handleLocationChangedLocked(Location location) {
1536 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001537 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1538 if (records == null || records.size() == 0) {
1539 return;
1540 }
1541
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001542 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001543 if (p == null) {
1544 return;
1545 }
1546
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001547 // Update last known location for provider
Mike Lockwood4e50b782009-04-03 08:24:43 -07001548 Location lastLocation = mLastKnownLocation.get(provider);
1549 if (lastLocation == null) {
1550 mLastKnownLocation.put(provider, new Location(location));
1551 } else {
1552 lastLocation.set(location);
1553 }
1554 writeLastKnownLocationLocked(provider, location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001555
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001556 // Fetch latest status update time
1557 long newStatusUpdateTime = p.getStatusUpdateTime();
1558
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001559 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001560 Bundle extras = new Bundle();
1561 int status = p.getStatus(extras);
1562
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001563 ArrayList<Receiver> deadReceivers = null;
1564
1565 // Broadcast location or status to all listeners
1566 final int N = records.size();
1567 for (int i=0; i<N; i++) {
1568 UpdateRecord r = records.get(i);
1569 Receiver receiver = r.mReceiver;
1570
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001571 Location lastLoc = r.mLastFixBroadcast;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001572 if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1573 if (lastLoc == null) {
1574 lastLoc = new Location(location);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001575 r.mLastFixBroadcast = lastLoc;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001576 } else {
1577 lastLoc.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001578 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001579 if (!receiver.callLocationChangedLocked(location)) {
1580 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1581 if (deadReceivers == null) {
1582 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001583 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001584 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001585 }
1586 }
1587
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001588 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001589 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1590 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1591
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001592 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001593 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1594 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1595 if (deadReceivers == null) {
1596 deadReceivers = new ArrayList<Receiver>();
1597 }
1598 if (!deadReceivers.contains(receiver)) {
1599 deadReceivers.add(receiver);
1600 }
1601 }
1602 }
1603 }
1604
1605 if (deadReceivers != null) {
1606 for (int i=deadReceivers.size()-1; i>=0; i--) {
1607 removeUpdatesLocked(deadReceivers.get(i));
1608 }
1609 }
1610 }
1611
1612 private class LocationWorkerHandler extends Handler {
1613
1614 @Override
1615 public void handleMessage(Message msg) {
1616 try {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001617 if (msg.what == MESSAGE_LOCATION_CHANGED) {
1618 // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001619
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001620 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001621 Location location = (Location) msg.obj;
Mike Lockwood98cb6672009-04-17 18:03:44 -04001622
1623 if (mCollector != null &&
1624 LocationManager.GPS_PROVIDER.equals(location.getProvider())) {
1625 try {
1626 mCollector.updateLocation(location);
1627 } catch (RemoteException e) {
1628 Log.w(TAG, "mCollector.updateLocation failed");
Mike Lockwood3681f262009-05-12 10:52:03 -04001629 mCollector = null;
Mike Lockwood98cb6672009-04-17 18:03:44 -04001630 }
1631 }
1632
Mike Lockwood4e50b782009-04-03 08:24:43 -07001633 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001634 if (!isAllowedBySettingsLocked(provider)) {
1635 return;
1636 }
1637
Mike Lockwooda0e3cd32009-04-21 21:27:33 -07001638 handleLocationChangedLocked(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001639 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001640 }
1641 } catch (Exception e) {
1642 // Log, don't crash!
1643 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1644 }
1645 }
1646 }
1647
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001648 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1649 @Override
1650 public void onReceive(Context context, Intent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001651 String action = intent.getAction();
1652
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001653 if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001654 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001655 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001656 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1657 if (uid >= 0) {
1658 ArrayList<Receiver> removedRecs = null;
1659 for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
1660 for (int j=i.size()-1; j>=0; j--) {
1661 UpdateRecord ur = i.get(j);
1662 if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
1663 if (removedRecs == null) {
1664 removedRecs = new ArrayList<Receiver>();
1665 }
1666 if (!removedRecs.contains(ur.mReceiver)) {
1667 removedRecs.add(ur.mReceiver);
1668 }
1669 }
1670 }
1671 }
1672 ArrayList<ProximityAlert> removedAlerts = null;
1673 for (ProximityAlert i : mProximityAlerts.values()) {
1674 if (i.mUid == uid) {
1675 if (removedAlerts == null) {
1676 removedAlerts = new ArrayList<ProximityAlert>();
1677 }
1678 if (!removedAlerts.contains(i)) {
1679 removedAlerts.add(i);
1680 }
1681 }
1682 }
1683 if (removedRecs != null) {
1684 for (int i=removedRecs.size()-1; i>=0; i--) {
1685 removeUpdatesLocked(removedRecs.get(i));
1686 }
1687 }
1688 if (removedAlerts != null) {
1689 for (int i=removedAlerts.size()-1; i>=0; i--) {
1690 removeProximityAlertLocked(removedAlerts.get(i).mIntent);
1691 }
1692 }
1693 }
1694 }
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001695 } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001696 boolean noConnectivity =
1697 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1698 if (!noConnectivity) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001699 mNetworkState = LocationProvider.AVAILABLE;
1700 } else {
1701 mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001702 }
1703
1704 // Notify location providers of current network state
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001705 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001706 for (int i = mProviders.size() - 1; i >= 0; i--) {
1707 LocationProviderProxy provider = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001708 if (provider.requiresNetwork()) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001709 provider.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001710 }
1711 }
1712 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001713 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001714 }
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001715 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001716
1717 // Wake locks
1718
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001719 private void incrementPendingBroadcasts() {
1720 synchronized (mWakeLock) {
1721 if (mPendingBroadcasts++ == 0) {
1722 try {
1723 mWakeLock.acquire();
1724 log("Acquired wakelock");
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 acquireWakeLock()", e);
1729 }
1730 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001731 }
1732 }
1733
1734 private void decrementPendingBroadcasts() {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001735 synchronized (mWakeLock) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001736 if (--mPendingBroadcasts == 0) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001737 try {
1738 // Release wake lock
1739 if (mWakeLock.isHeld()) {
1740 mWakeLock.release();
1741 log("Released wakelock");
1742 } else {
1743 log("Can't release wakelock again!");
1744 }
1745 } catch (Exception e) {
1746 // This is to catch a runtime exception thrown when we try to release an
1747 // already released lock.
1748 Log.e(TAG, "exception in releaseWakeLock()", e);
1749 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001750 }
1751 }
1752 }
1753
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001754 // Geocoder
1755
1756 public String getFromLocation(double latitude, double longitude, int maxResults,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001757 String language, String country, String variant, String appName, List<Address> addrs) {
1758 if (mGeocodeProvider != null) {
1759 try {
1760 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, language, country,
1761 variant, appName, addrs);
1762 } catch (RemoteException e) {
1763 Log.e(TAG, "getFromLocation failed", e);
Mike Lockwood3681f262009-05-12 10:52:03 -04001764 mGeocodeProvider = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001765 }
1766 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001767 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001768 }
1769
Mike Lockwooda55c3212009-04-15 11:10:11 -04001770
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001771 public String getFromLocationName(String locationName,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001772 double lowerLeftLatitude, double lowerLeftLongitude,
1773 double upperRightLatitude, double upperRightLongitude, int maxResults,
1774 String language, String country, String variant, String appName, List<Address> addrs) {
1775
1776 if (mGeocodeProvider != null) {
1777 try {
1778 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1779 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1780 maxResults, language, country, variant, appName, addrs);
1781 } catch (RemoteException e) {
1782 Log.e(TAG, "getFromLocationName failed", e);
Mike Lockwood3681f262009-05-12 10:52:03 -04001783 mGeocodeProvider = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001784 }
1785 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001786 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001787 }
1788
1789 // Mock Providers
1790
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001791 private void checkMockPermissionsSafe() {
1792 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1793 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1794 if (!allowMocks) {
1795 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1796 }
1797
1798 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1799 PackageManager.PERMISSION_GRANTED) {
1800 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1801 }
1802 }
1803
1804 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1805 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1806 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1807 checkMockPermissionsSafe();
1808
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001809 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001810 MockProvider provider = new MockProvider(name, this,
1811 requiresNetwork, requiresSatellite,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001812 requiresCell, hasMonetaryCost, supportsAltitude,
1813 supportsSpeed, supportsBearing, powerRequirement, accuracy);
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001814 if (mProvidersByName.get(name) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001815 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1816 }
Mike Lockwood95427cd2009-05-07 13:27:54 -04001817
1818 // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
1819 long identity = Binder.clearCallingIdentity();
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001820 addProvider(new LocationProviderProxy(name, provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001821 mMockProviders.put(name, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001822 updateProvidersLocked();
Mike Lockwood95427cd2009-05-07 13:27:54 -04001823 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001824 }
1825 }
1826
1827 public void removeTestProvider(String provider) {
1828 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001829 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001830 MockProvider mockProvider = mMockProviders.get(provider);
1831 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001832 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1833 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001834 removeProvider(mProvidersByName.get(provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001835 mMockProviders.remove(mockProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001836 updateProvidersLocked();
1837 }
1838 }
1839
1840 public void setTestProviderLocation(String provider, Location loc) {
1841 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001842 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001843 MockProvider mockProvider = mMockProviders.get(provider);
1844 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001845 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1846 }
Mike Lockwood95427cd2009-05-07 13:27:54 -04001847 // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
1848 long identity = Binder.clearCallingIdentity();
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001849 mockProvider.setLocation(loc);
Mike Lockwood95427cd2009-05-07 13:27:54 -04001850 Binder.restoreCallingIdentity(identity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001851 }
1852 }
1853
1854 public void clearTestProviderLocation(String provider) {
1855 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001856 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001857 MockProvider mockProvider = mMockProviders.get(provider);
1858 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001859 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1860 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001861 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001862 }
1863 }
1864
1865 public void setTestProviderEnabled(String provider, boolean enabled) {
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 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001873 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001874 mEnabledProviders.add(provider);
1875 mDisabledProviders.remove(provider);
1876 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001877 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001878 mEnabledProviders.remove(provider);
1879 mDisabledProviders.add(provider);
1880 }
1881 updateProvidersLocked();
1882 }
1883 }
1884
1885 public void clearTestProviderEnabled(String provider) {
1886 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001887 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001888 MockProvider mockProvider = mMockProviders.get(provider);
1889 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001890 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1891 }
1892 mEnabledProviders.remove(provider);
1893 mDisabledProviders.remove(provider);
1894 updateProvidersLocked();
1895 }
1896 }
1897
1898 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1899 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001900 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001901 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 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001905 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001906 }
1907 }
1908
1909 public void clearTestProviderStatus(String provider) {
1910 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001911 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001912 MockProvider mockProvider = mMockProviders.get(provider);
1913 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001914 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1915 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001916 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001917 }
1918 }
1919
1920 private void log(String log) {
1921 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1922 Log.d(TAG, log);
1923 }
1924 }
1925
1926 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1927 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1928 != PackageManager.PERMISSION_GRANTED) {
Mike Lockwood0528b9b2009-05-07 10:12:54 -04001929 pw.println("Permission Denial: can't dump LocationManagerService from from pid="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001930 + Binder.getCallingPid()
1931 + ", uid=" + Binder.getCallingUid());
1932 return;
1933 }
1934
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001935 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001936 pw.println("Current Location Manager state:");
1937 pw.println(" sProvidersLoaded=" + sProvidersLoaded);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001938 pw.println(" mCollector=" + mCollector);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001939 pw.println(" Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001940 int N = mReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001941 for (int i=0; i<N; i++) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001942 pw.println(" " + mReceivers.get(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001943 }
1944 pw.println(" Location Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001945 for (Receiver i : mReceivers.values()) {
1946 pw.println(" " + i + ":");
1947 for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001948 pw.println(" " + j.getKey() + ":");
1949 j.getValue().dump(pw, " ");
1950 }
1951 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001952 pw.println(" Records by Provider:");
1953 for (Map.Entry<String, ArrayList<UpdateRecord>> i
1954 : mRecordsByProvider.entrySet()) {
1955 pw.println(" " + i.getKey() + ":");
1956 for (UpdateRecord j : i.getValue()) {
1957 pw.println(" " + j + ":");
1958 j.dump(pw, " ");
1959 }
1960 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001961 pw.println(" Last Known Locations:");
1962 for (Map.Entry<String, Location> i
1963 : mLastKnownLocation.entrySet()) {
1964 pw.println(" " + i.getKey() + ":");
1965 i.getValue().dump(new PrintWriterPrinter(pw), " ");
1966 }
1967 if (mProximityAlerts.size() > 0) {
1968 pw.println(" Proximity Alerts:");
1969 for (Map.Entry<PendingIntent, ProximityAlert> i
1970 : mProximityAlerts.entrySet()) {
1971 pw.println(" " + i.getKey() + ":");
1972 i.getValue().dump(pw, " ");
1973 }
1974 }
1975 if (mProximitiesEntered.size() > 0) {
1976 pw.println(" Proximities Entered:");
1977 for (ProximityAlert i : mProximitiesEntered) {
1978 pw.println(" " + i + ":");
1979 i.dump(pw, " ");
1980 }
1981 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001982 pw.println(" mProximityReceiver=" + mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001983 pw.println(" mProximityListener=" + mProximityListener);
1984 if (mEnabledProviders.size() > 0) {
1985 pw.println(" Enabled Providers:");
1986 for (String i : mEnabledProviders) {
1987 pw.println(" " + i);
1988 }
1989
1990 }
1991 if (mDisabledProviders.size() > 0) {
1992 pw.println(" Disabled Providers:");
1993 for (String i : mDisabledProviders) {
1994 pw.println(" " + i);
1995 }
1996
1997 }
1998 if (mMockProviders.size() > 0) {
1999 pw.println(" Mock Providers:");
2000 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002001 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002002 }
2003 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002004 }
2005 }
2006}