blob: 9af729efd3c190d82613170008dcbdb39ecc3c5c [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import java.io.BufferedReader;
20import java.io.File;
21import java.io.FileDescriptor;
22import java.io.FileReader;
23import java.io.FileWriter;
24import java.io.IOException;
25import java.io.PrintWriter;
26import java.util.ArrayList;
27import java.util.HashMap;
28import java.util.HashSet;
29import java.util.List;
30import java.util.Map;
Mike Lockwood9637d472009-04-02 21:41:57 -070031import java.util.Observable;
32import java.util.Observer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import java.util.Set;
34import java.util.regex.Pattern;
35
36import android.app.AlarmManager;
37import android.app.PendingIntent;
38import android.content.BroadcastReceiver;
Mike Lockwood9637d472009-04-02 21:41:57 -070039import android.content.ContentQueryMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.content.ContentResolver;
41import android.content.Context;
42import android.content.Intent;
43import android.content.IntentFilter;
44import android.content.pm.PackageManager;
Mike Lockwood9637d472009-04-02 21:41:57 -070045import android.database.Cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.location.Address;
Mike Lockwooda55c3212009-04-15 11:10:11 -040047import android.location.IGeocodeProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.location.IGpsStatusListener;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -040049import android.location.IGpsStatusProvider;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070050import android.location.ILocationCollector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.location.ILocationListener;
52import android.location.ILocationManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070053import android.location.ILocationProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.location.Location;
55import android.location.LocationManager;
56import android.location.LocationProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.net.ConnectivityManager;
58import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import android.os.Binder;
60import android.os.Bundle;
61import android.os.Handler;
62import android.os.IBinder;
Mike Lockwood3d12b512009-04-21 23:25:35 -070063import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import android.os.Message;
65import android.os.PowerManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070066import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import android.os.RemoteException;
68import android.os.SystemClock;
69import android.provider.Settings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070import android.util.Config;
71import android.util.Log;
72import android.util.PrintWriterPrinter;
73import android.util.SparseIntArray;
74
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075import com.android.internal.location.GpsLocationProvider;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070076import com.android.internal.location.LocationProviderProxy;
Mike Lockwood7ec434e2009-03-27 07:46:48 -070077import com.android.internal.location.MockProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078import com.android.server.am.BatteryStatsService;
79
80/**
81 * The service class that manages LocationProviders and issues location
82 * updates and alerts.
83 *
84 * {@hide}
85 */
Mike Lockwood3d12b512009-04-21 23:25:35 -070086public class LocationManagerService extends ILocationManager.Stub implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 private static final String TAG = "LocationManagerService";
The Android Open Source Project10592532009-03-18 17:39:46 -070088 private static final boolean LOCAL_LOGV = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089
90 // Minimum time interval between last known location writes, in milliseconds.
91 private static final long MIN_LAST_KNOWN_LOCATION_TIME = 60L * 1000L;
92
93 // Max time to hold wake lock for, in milliseconds.
94 private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L;
95
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 // The last time a location was written, by provider name.
97 private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
98
99 private static final Pattern PATTERN_COMMA = Pattern.compile(",");
100
101 private static final String ACCESS_FINE_LOCATION =
102 android.Manifest.permission.ACCESS_FINE_LOCATION;
103 private static final String ACCESS_COARSE_LOCATION =
104 android.Manifest.permission.ACCESS_COARSE_LOCATION;
105 private static final String ACCESS_MOCK_LOCATION =
106 android.Manifest.permission.ACCESS_MOCK_LOCATION;
107 private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
108 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
Mike Lockwood275555c2009-05-01 11:30:34 -0400109 private static final String INSTALL_LOCATION_PROVIDER =
110 android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
111 private static final String INSTALL_LOCATION_COLLECTOR =
112 android.Manifest.permission.INSTALL_LOCATION_COLLECTOR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113
114 // Set of providers that are explicitly enabled
115 private final Set<String> mEnabledProviders = new HashSet<String>();
116
117 // Set of providers that are explicitly disabled
118 private final Set<String> mDisabledProviders = new HashSet<String>();
119
120 // Locations, status values, and extras for mock providers
Mike Lockwood7ec434e2009-03-27 07:46:48 -0700121 private final HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122
123 private static boolean sProvidersLoaded = false;
124
125 private final Context mContext;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400126 private LocationProviderProxy mGpsLocationProvider;
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700127 private LocationProviderProxy mNetworkLocationProvider;
Mike Lockwooda55c3212009-04-15 11:10:11 -0400128 private IGeocodeProvider mGeocodeProvider;
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400129 private IGpsStatusProvider mGpsStatusProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 private LocationWorkerHandler mLocationHandler;
131
132 // Handler messages
Mike Lockwood4e50b782009-04-03 08:24:43 -0700133 private static final int MESSAGE_LOCATION_CHANGED = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134
135 // Alarm manager and wakelock variables
136 private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT";
137 private final static String WAKELOCK_KEY = "LocationManagerService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 private AlarmManager mAlarmManager;
139 private long mAlarmInterval = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 private PowerManager.WakeLock mWakeLock = null;
Mike Lockwood48f17512009-04-23 09:12:08 -0700141 private int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142 private long mWakeLockAcquireTime = 0;
143 private boolean mWakeLockGpsReceived = true;
144 private boolean mWakeLockNetworkReceived = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 /**
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400147 * List of all receivers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 */
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400149 private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400151
152 /**
153 * List of location providers.
154 */
155 private final ArrayList<LocationProviderProxy> mProviders =
156 new ArrayList<LocationProviderProxy>();
157 private final HashMap<String, LocationProviderProxy> mProvidersByName
158 = new HashMap<String, LocationProviderProxy>();
159
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 /**
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400161 * Object used internally for synchronization
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 */
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400163 private final Object mLock = new Object();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164
165 /**
166 * Mapping from provider name to all its UpdateRecords
167 */
168 private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
169 new HashMap<String,ArrayList<UpdateRecord>>();
170
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 // Proximity listeners
Mike Lockwood48f17512009-04-23 09:12:08 -0700172 private Receiver mProximityReceiver = null;
173 private ILocationListener mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
175 new HashMap<PendingIntent,ProximityAlert>();
176 private HashSet<ProximityAlert> mProximitiesEntered =
177 new HashSet<ProximityAlert>();
178
179 // Last known location for each provider
180 private HashMap<String,Location> mLastKnownLocation =
181 new HashMap<String,Location>();
182
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 // Location collector
184 private ILocationCollector mCollector;
185
The Android Open Source Project4df24232009-03-05 14:34:35 -0800186 private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800187
Mike Lockwood9637d472009-04-02 21:41:57 -0700188 // for Settings change notification
189 private ContentQueryMap mSettings;
190
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 /**
192 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
193 * location updates.
194 */
Mike Lockwood48f17512009-04-23 09:12:08 -0700195 private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 final ILocationListener mListener;
197 final PendingIntent mPendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 final Object mKey;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400199 final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
Mike Lockwood48f17512009-04-23 09:12:08 -0700200 int mPendingBroadcasts;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400202 Receiver(ILocationListener listener) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 mListener = listener;
204 mPendingIntent = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 mKey = listener.asBinder();
206 }
207
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400208 Receiver(PendingIntent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 mPendingIntent = intent;
210 mListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 mKey = intent;
212 }
213
214 @Override
215 public boolean equals(Object otherObj) {
216 if (otherObj instanceof Receiver) {
217 return mKey.equals(
218 ((Receiver)otherObj).mKey);
219 }
220 return false;
221 }
222
223 @Override
224 public int hashCode() {
225 return mKey.hashCode();
226 }
227
228
229 @Override
230 public String toString() {
231 if (mListener != null) {
232 return "Receiver{"
233 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400234 + " Listener " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 } else {
236 return "Receiver{"
237 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400238 + " Intent " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239 }
240 }
241
242 public boolean isListener() {
243 return mListener != null;
244 }
245
246 public boolean isPendingIntent() {
247 return mPendingIntent != null;
248 }
249
250 public ILocationListener getListener() {
251 if (mListener != null) {
252 return mListener;
253 }
254 throw new IllegalStateException("Request for non-existent listener");
255 }
256
257 public PendingIntent getPendingIntent() {
258 if (mPendingIntent != null) {
259 return mPendingIntent;
260 }
261 throw new IllegalStateException("Request for non-existent intent");
262 }
263
264 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
265 if (mListener != null) {
266 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700267 synchronized (this) {
268 // synchronize to ensure incrementPendingBroadcastsLocked()
269 // is called before decrementPendingBroadcasts()
270 mListener.onStatusChanged(provider, status, extras);
271 if (mListener != mProximityListener) {
272 // call this after broadcasting so we do not increment
273 // if we throw an exeption.
274 incrementPendingBroadcastsLocked();
275 }
276 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 } catch (RemoteException e) {
278 return false;
279 }
280 } else {
281 Intent statusChanged = new Intent();
282 statusChanged.putExtras(extras);
283 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
284 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700285 synchronized (this) {
286 // synchronize to ensure incrementPendingBroadcastsLocked()
287 // is called before decrementPendingBroadcasts()
288 mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler);
289 // call this after broadcasting so we do not increment
290 // if we throw an exeption.
291 incrementPendingBroadcastsLocked();
292 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 } catch (PendingIntent.CanceledException e) {
294 return false;
295 }
296 }
297 return true;
298 }
299
300 public boolean callLocationChangedLocked(Location location) {
301 if (mListener != null) {
302 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700303 synchronized (this) {
304 // synchronize to ensure incrementPendingBroadcastsLocked()
305 // is called before decrementPendingBroadcasts()
306 mListener.onLocationChanged(location);
307 if (mListener != mProximityListener) {
308 // call this after broadcasting so we do not increment
309 // if we throw an exeption.
310 incrementPendingBroadcastsLocked();
311 }
312 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 } catch (RemoteException e) {
314 return false;
315 }
316 } else {
317 Intent locationChanged = new Intent();
318 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
319 try {
Mike Lockwood48f17512009-04-23 09:12:08 -0700320 synchronized (this) {
321 // synchronize to ensure incrementPendingBroadcastsLocked()
322 // is called before decrementPendingBroadcasts()
323 mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler);
324 // call this after broadcasting so we do not increment
325 // if we throw an exeption.
326 incrementPendingBroadcastsLocked();
327 }
328 } catch (PendingIntent.CanceledException e) {
329 return false;
330 }
331 }
332 return true;
333 }
334
335 public boolean callProviderEnabledLocked(String provider, boolean enabled) {
336 if (mListener != null) {
337 try {
338 synchronized (this) {
339 // synchronize to ensure incrementPendingBroadcastsLocked()
340 // is called before decrementPendingBroadcasts()
341 if (enabled) {
342 mListener.onProviderEnabled(provider);
343 } else {
344 mListener.onProviderDisabled(provider);
345 }
346 if (mListener != mProximityListener) {
347 // call this after broadcasting so we do not increment
348 // if we throw an exeption.
349 incrementPendingBroadcastsLocked();
350 }
351 }
352 } catch (RemoteException e) {
353 return false;
354 }
355 } else {
356 Intent providerIntent = new Intent();
357 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
358 try {
359 synchronized (this) {
360 // synchronize to ensure incrementPendingBroadcastsLocked()
361 // is called before decrementPendingBroadcasts()
362 mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler);
363 // call this after broadcasting so we do not increment
364 // if we throw an exeption.
365 incrementPendingBroadcastsLocked();
366 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 } catch (PendingIntent.CanceledException e) {
368 return false;
369 }
370 }
371 return true;
372 }
373
374 public void binderDied() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700375 if (LOCAL_LOGV) {
376 Log.v(TAG, "Location listener died");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 }
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400378 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 removeUpdatesLocked(this);
380 }
Mike Lockwood48f17512009-04-23 09:12:08 -0700381 synchronized (this) {
382 if (mPendingBroadcasts > 0) {
383 LocationManagerService.this.decrementPendingBroadcasts();
384 mPendingBroadcasts = 0;
385 }
386 }
387 }
388
389 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
390 int resultCode, String resultData, Bundle resultExtras) {
391 decrementPendingBroadcasts();
392 }
393
394 // this must be called while synchronized by callerin a synchronized block
395 // containing the sending of the broadcaset
396 private void incrementPendingBroadcastsLocked() {
397 if (mPendingBroadcasts++ == 0) {
398 synchronized (mLock) {
399 LocationManagerService.this.incrementPendingBroadcastsLocked();
400 }
401 }
402 }
403
404 private void decrementPendingBroadcasts() {
405 synchronized (this) {
406 if (--mPendingBroadcasts == 0) {
407 LocationManagerService.this.decrementPendingBroadcasts();
408 }
409 }
410 }
411 }
412
413 public void locationCallbackFinished(ILocationListener listener) {
414 Receiver receiver = getReceiver(listener);
415 if (receiver != null) {
416 receiver.decrementPendingBroadcasts();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 }
418 }
419
Mike Lockwood9637d472009-04-02 21:41:57 -0700420 private final class SettingsObserver implements Observer {
421 public void update(Observable o, Object arg) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400422 synchronized (mLock) {
Mike Lockwood9637d472009-04-02 21:41:57 -0700423 updateProvidersLocked();
424 }
425 }
426 }
427
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 private Location readLastKnownLocationLocked(String provider) {
429 Location location = null;
430 String s = null;
431 try {
432 File f = new File(LocationManager.SYSTEM_DIR + "/location."
433 + provider);
434 if (!f.exists()) {
435 return null;
436 }
437 BufferedReader reader = new BufferedReader(new FileReader(f), 256);
438 s = reader.readLine();
439 } catch (IOException e) {
440 Log.w(TAG, "Unable to read last known location", e);
441 }
442
443 if (s == null) {
444 return null;
445 }
446 try {
447 String[] tokens = PATTERN_COMMA.split(s);
448 int idx = 0;
449 long time = Long.parseLong(tokens[idx++]);
450 double latitude = Double.parseDouble(tokens[idx++]);
451 double longitude = Double.parseDouble(tokens[idx++]);
452 double altitude = Double.parseDouble(tokens[idx++]);
453 float bearing = Float.parseFloat(tokens[idx++]);
454 float speed = Float.parseFloat(tokens[idx++]);
455
456 location = new Location(provider);
457 location.setTime(time);
458 location.setLatitude(latitude);
459 location.setLongitude(longitude);
460 location.setAltitude(altitude);
461 location.setBearing(bearing);
462 location.setSpeed(speed);
463 } catch (NumberFormatException nfe) {
464 Log.e(TAG, "NumberFormatException reading last known location", nfe);
465 return null;
466 }
467
468 return location;
469 }
470
471 private void writeLastKnownLocationLocked(String provider,
472 Location location) {
473 long now = SystemClock.elapsedRealtime();
474 Long last = mLastWriteTime.get(provider);
475 if ((last != null)
476 && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
477 return;
478 }
479 mLastWriteTime.put(provider, now);
480
481 StringBuilder sb = new StringBuilder(100);
482 sb.append(location.getTime());
483 sb.append(',');
484 sb.append(location.getLatitude());
485 sb.append(',');
486 sb.append(location.getLongitude());
487 sb.append(',');
488 sb.append(location.getAltitude());
489 sb.append(',');
490 sb.append(location.getBearing());
491 sb.append(',');
492 sb.append(location.getSpeed());
493
494 FileWriter writer = null;
495 try {
496 File d = new File(LocationManager.SYSTEM_DIR);
497 if (!d.exists()) {
498 if (!d.mkdirs()) {
499 Log.w(TAG, "Unable to create directory to write location");
500 return;
501 }
502 }
503 File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
504 writer = new FileWriter(f);
505 writer.write(sb.toString());
506 } catch (IOException e) {
507 Log.w(TAG, "Unable to write location", e);
508 } finally {
509 if (writer != null) {
510 try {
511 writer.close();
512 } catch (IOException e) {
513 Log.w(TAG, "Exception closing file", e);
514 }
515 }
516 }
517 }
518
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400519 private void addProvider(LocationProviderProxy provider) {
520 mProviders.add(provider);
521 mProvidersByName.put(provider.getName(), provider);
522 }
523
524 private void removeProvider(LocationProviderProxy provider) {
525 mProviders.remove(provider);
526 mProvidersByName.remove(provider.getName());
527 }
528
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 /**
530 * Load providers from /data/location/<provider_name>/
531 * class
532 * kml
533 * nmea
534 * track
535 * location
536 * properties
537 */
538 private void loadProviders() {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400539 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540 if (sProvidersLoaded) {
541 return;
542 }
543
544 // Load providers
545 loadProvidersLocked();
546 sProvidersLoaded = true;
547 }
548 }
549
550 private void loadProvidersLocked() {
551 try {
552 _loadProvidersLocked();
553 } catch (Exception e) {
554 Log.e(TAG, "Exception loading providers:", e);
555 }
556 }
557
558 private void _loadProvidersLocked() {
559 // Attempt to load "real" providers first
560 if (GpsLocationProvider.isSupported()) {
561 // Create a gps location provider
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400562 GpsLocationProvider provider = new GpsLocationProvider(mContext, this);
563 mGpsStatusProvider = provider.getGpsStatusProvider();
564 mGpsLocationProvider =
565 new LocationProviderProxy(LocationManager.GPS_PROVIDER, provider);
566 addProvider(mGpsLocationProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 }
568
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 updateProvidersLocked();
570 }
571
572 /**
573 * @param context the context that the LocationManagerService runs in
574 */
575 public LocationManagerService(Context context) {
576 super();
577 mContext = context;
Mike Lockwood3d12b512009-04-21 23:25:35 -0700578
579 Thread thread = new Thread(null, this, "LocationManagerService");
580 thread.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581
The Android Open Source Project10592532009-03-18 17:39:46 -0700582 if (LOCAL_LOGV) {
583 Log.v(TAG, "Constructed LocationManager Service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 }
Mike Lockwood3d12b512009-04-21 23:25:35 -0700585 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586
Mike Lockwood3d12b512009-04-21 23:25:35 -0700587 private void initialize() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 // Alarm manager, needs to be done before calling loadProviders() below
Mike Lockwood3d12b512009-04-21 23:25:35 -0700589 mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590
591 // Create a wake lock, needs to be done before calling loadProviders() below
592 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
593 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
594
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 // Load providers
596 loadProviders();
597
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800598 // Register for Network (Wifi or Mobile) updates
599 NetworkStateBroadcastReceiver networkReceiver = new NetworkStateBroadcastReceiver();
600 IntentFilter networkIntentFilter = new IntentFilter();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
602 networkIntentFilter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
Mike Lockwood3d12b512009-04-21 23:25:35 -0700603 mContext.registerReceiver(networkReceiver, networkIntentFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604
605 // Register for power updates
606 PowerStateBroadcastReceiver powerStateReceiver = new PowerStateBroadcastReceiver();
607 IntentFilter intentFilter = new IntentFilter();
608 intentFilter.addAction(ALARM_INTENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
610 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
Mike Lockwood3d12b512009-04-21 23:25:35 -0700611 mContext.registerReceiver(powerStateReceiver, intentFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612
Mike Lockwood9637d472009-04-02 21:41:57 -0700613 // listen for settings changes
614 ContentResolver resolver = mContext.getContentResolver();
615 Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
616 "(" + Settings.System.NAME + "=?)",
617 new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
618 null);
619 mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler);
620 SettingsObserver settingsObserver = new SettingsObserver();
621 mSettings.addObserver(settingsObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 }
623
Mike Lockwood3d12b512009-04-21 23:25:35 -0700624 public void run()
625 {
626 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
627 Looper.prepare();
628 mLocationHandler = new LocationWorkerHandler();
629 initialize();
630 Looper.loop();
631 }
632
Mike Lockwood275555c2009-05-01 11:30:34 -0400633 public void installLocationProvider(String name, ILocationProvider provider) {
634 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
635 != PackageManager.PERMISSION_GRANTED) {
636 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700637 }
638
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400639 synchronized (mLock) {
Mike Lockwood275555c2009-05-01 11:30:34 -0400640 // FIXME - only network location provider supported for now
641 if (LocationManager.NETWORK_PROVIDER.equals(name)) {
642 mNetworkLocationProvider = new LocationProviderProxy(name, provider);
643 addProvider(mNetworkLocationProvider);
644 updateProvidersLocked();
645
646 // notify NetworkLocationProvider of any events it might have missed
647 mNetworkLocationProvider.updateNetworkState(mNetworkState);
648 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649 }
650 }
651
Mike Lockwood275555c2009-05-01 11:30:34 -0400652 public void installLocationCollector(ILocationCollector collector) {
653 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_COLLECTOR)
654 != PackageManager.PERMISSION_GRANTED) {
655 throw new SecurityException("Requires INSTALL_LOCATION_COLLECTOR permission");
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700656 }
657
Mike Lockwood275555c2009-05-01 11:30:34 -0400658 // FIXME - only support one collector
Mike Lockwood98cb6672009-04-17 18:03:44 -0400659 mCollector = collector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800660 }
661
Mike Lockwood275555c2009-05-01 11:30:34 -0400662 public void installGeocodeProvider(IGeocodeProvider provider) {
663 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
664 != PackageManager.PERMISSION_GRANTED) {
665 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
Mike Lockwooda55c3212009-04-15 11:10:11 -0400666 }
667
668 mGeocodeProvider = provider;
669 }
670
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 private boolean isAllowedBySettingsLocked(String provider) {
672 if (mEnabledProviders.contains(provider)) {
673 return true;
674 }
675 if (mDisabledProviders.contains(provider)) {
676 return false;
677 }
678 // Use system settings
679 ContentResolver resolver = mContext.getContentResolver();
680 String allowedProviders = Settings.Secure.getString(resolver,
681 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
682
683 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
684 }
685
686 private void checkPermissionsSafe(String provider) {
687 if (LocationManager.GPS_PROVIDER.equals(provider)
688 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
689 != PackageManager.PERMISSION_GRANTED)) {
690 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
691 }
692 if (LocationManager.NETWORK_PROVIDER.equals(provider)
693 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
694 != PackageManager.PERMISSION_GRANTED)
695 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
696 != PackageManager.PERMISSION_GRANTED)) {
697 throw new SecurityException(
698 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
699 }
700 }
701
702 private boolean isAllowedProviderSafe(String provider) {
703 if (LocationManager.GPS_PROVIDER.equals(provider)
704 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
705 != PackageManager.PERMISSION_GRANTED)) {
706 return false;
707 }
708 if (LocationManager.NETWORK_PROVIDER.equals(provider)
709 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
710 != PackageManager.PERMISSION_GRANTED)
711 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
712 != PackageManager.PERMISSION_GRANTED)) {
713 return false;
714 }
715
716 return true;
717 }
718
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800719 public List<String> getAllProviders() {
720 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400721 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722 return _getAllProvidersLocked();
723 }
724 } catch (SecurityException se) {
725 throw se;
726 } catch (Exception e) {
727 Log.e(TAG, "getAllProviders got exception:", e);
728 return null;
729 }
730 }
731
732 private List<String> _getAllProvidersLocked() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700733 if (LOCAL_LOGV) {
734 Log.v(TAG, "getAllProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400736 ArrayList<String> out = new ArrayList<String>(mProviders.size());
737 for (int i = mProviders.size() - 1; i >= 0; i--) {
738 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800739 out.add(p.getName());
740 }
741 return out;
742 }
743
744 public List<String> getProviders(boolean enabledOnly) {
745 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400746 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747 return _getProvidersLocked(enabledOnly);
748 }
749 } catch (SecurityException se) {
750 throw se;
751 } catch (Exception e) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700752 Log.e(TAG, "getProviders got exception:", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753 return null;
754 }
755 }
756
757 private List<String> _getProvidersLocked(boolean enabledOnly) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700758 if (LOCAL_LOGV) {
759 Log.v(TAG, "getProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400761 ArrayList<String> out = new ArrayList<String>(mProviders.size());
762 for (int i = mProviders.size() - 1; i >= 0; i--) {
763 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 String name = p.getName();
765 if (isAllowedProviderSafe(name)) {
766 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
767 continue;
768 }
769 out.add(name);
770 }
771 }
772 return out;
773 }
774
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 private void updateProvidersLocked() {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400776 for (int i = mProviders.size() - 1; i >= 0; i--) {
777 LocationProviderProxy p = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 boolean isEnabled = p.isEnabled();
779 String name = p.getName();
780 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
781
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 if (isEnabled && !shouldBeEnabled) {
783 updateProviderListenersLocked(name, false);
784 } else if (!isEnabled && shouldBeEnabled) {
785 updateProviderListenersLocked(name, true);
786 }
787
788 }
789 }
790
791 private void updateProviderListenersLocked(String provider, boolean enabled) {
792 int listeners = 0;
793
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400794 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 if (p == null) {
796 return;
797 }
798
799 ArrayList<Receiver> deadReceivers = null;
800
801 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
802 if (records != null) {
803 final int N = records.size();
804 for (int i=0; i<N; i++) {
805 UpdateRecord record = records.get(i);
806 // Sends a notification message to the receiver
Mike Lockwood48f17512009-04-23 09:12:08 -0700807 if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
808 if (deadReceivers == null) {
809 deadReceivers = new ArrayList<Receiver>();
810 deadReceivers.add(record.mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 }
813 listeners++;
814 }
815 }
816
817 if (deadReceivers != null) {
818 for (int i=deadReceivers.size()-1; i>=0; i--) {
819 removeUpdatesLocked(deadReceivers.get(i));
820 }
821 }
822
823 if (enabled) {
824 p.enable();
825 if (listeners > 0) {
826 p.setMinTime(getMinTimeLocked(provider));
827 p.enableLocationTracking(true);
Mike Lockwood61fc2862009-04-21 20:02:52 -0700828 updateWakelockStatusLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800829 }
830 } else {
831 p.enableLocationTracking(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 p.disable();
Mike Lockwood61fc2862009-04-21 20:02:52 -0700833 updateWakelockStatusLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800835 }
836
837 private long getMinTimeLocked(String provider) {
838 long minTime = Long.MAX_VALUE;
839 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
840 if (records != null) {
841 for (int i=records.size()-1; i>=0; i--) {
842 minTime = Math.min(minTime, records.get(i).mMinTime);
843 }
844 }
845 return minTime;
846 }
847
848 private class UpdateRecord {
849 final String mProvider;
850 final Receiver mReceiver;
851 final long mMinTime;
852 final float mMinDistance;
853 final int mUid;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400854 Location mLastFixBroadcast;
855 long mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856
857 /**
858 * Note: must be constructed with lock held.
859 */
860 UpdateRecord(String provider, long minTime, float minDistance,
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400861 Receiver receiver, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 mProvider = provider;
863 mReceiver = receiver;
864 mMinTime = minTime;
865 mMinDistance = minDistance;
866 mUid = uid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867
868 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
869 if (records == null) {
870 records = new ArrayList<UpdateRecord>();
871 mRecordsByProvider.put(provider, records);
872 }
873 if (!records.contains(this)) {
874 records.add(this);
875 }
876 }
877
878 /**
879 * Method to be called when a record will no longer be used. Calling this multiple times
880 * must have the same effect as calling it once.
881 */
882 void disposeLocked() {
883 ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
884 records.remove(this);
885 }
886
887 @Override
888 public String toString() {
889 return "UpdateRecord{"
890 + Integer.toHexString(System.identityHashCode(this))
891 + " " + mProvider + " " + mReceiver + "}";
892 }
893
894 void dump(PrintWriter pw, String prefix) {
895 pw.println(prefix + this);
896 pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
897 pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400898 pw.println(prefix + "mUid=" + mUid);
899 pw.println(prefix + "mLastFixBroadcast:");
900 mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
901 pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800902 }
903
904 /**
905 * Calls dispose().
906 */
907 @Override protected void finalize() {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400908 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 disposeLocked();
910 }
911 }
912 }
913
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400914 private Receiver getReceiver(ILocationListener listener) {
915 IBinder binder = listener.asBinder();
916 Receiver receiver = mReceivers.get(binder);
917 if (receiver == null) {
918 receiver = new Receiver(listener);
919 mReceivers.put(binder, receiver);
920
921 try {
922 if (receiver.isListener()) {
923 receiver.getListener().asBinder().linkToDeath(receiver, 0);
924 }
925 } catch (RemoteException e) {
926 Log.e(TAG, "linkToDeath failed:", e);
927 return null;
928 }
929 }
930 return receiver;
931 }
932
933 private Receiver getReceiver(PendingIntent intent) {
934 Receiver receiver = mReceivers.get(intent);
935 if (receiver == null) {
936 receiver = new Receiver(intent);
937 mReceivers.put(intent, receiver);
938 }
939 return receiver;
940 }
941
942 private boolean providerHasListener(String provider, int uid, Receiver excludedReceiver) {
943 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
944 if (records != null) {
945 for (int i = records.size() - 1; i >= 0; i--) {
946 UpdateRecord record = records.get(i);
947 if (record.mUid == uid && record.mReceiver != excludedReceiver) {
948 return true;
949 }
950 }
951 }
952 if (LocationManager.GPS_PROVIDER.equals(provider) ||
953 LocationManager.NETWORK_PROVIDER.equals(provider)) {
954 for (ProximityAlert alert : mProximityAlerts.values()) {
955 if (alert.mUid == uid) {
956 return true;
957 }
958 }
959 }
960 return false;
961 }
962
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963 public void requestLocationUpdates(String provider,
964 long minTime, float minDistance, ILocationListener listener) {
965
966 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400967 synchronized (mLock) {
968 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800969 }
970 } catch (SecurityException se) {
971 throw se;
972 } catch (Exception e) {
973 Log.e(TAG, "requestUpdates got exception:", e);
974 }
975 }
976
977 public void requestLocationUpdatesPI(String provider,
978 long minTime, float minDistance, PendingIntent intent) {
979 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400980 synchronized (mLock) {
981 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800982 }
983 } catch (SecurityException se) {
984 throw se;
985 } catch (Exception e) {
986 Log.e(TAG, "requestUpdates got exception:", e);
987 }
988 }
989
990 private void requestLocationUpdatesLocked(String provider,
991 long minTime, float minDistance, Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700992 if (LOCAL_LOGV) {
993 Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 }
995
Mike Lockwood15e3d0f2009-05-01 07:53:28 -0400996 LocationProviderProxy proxy = mProvidersByName.get(provider);
997 if (proxy == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 throw new IllegalArgumentException("provider=" + provider);
999 }
1000
1001 checkPermissionsSafe(provider);
1002
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001003 // so wakelock calls will succeed
1004 final int callingUid = Binder.getCallingUid();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001005 boolean newUid = !providerHasListener(provider, callingUid, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001006 long identity = Binder.clearCallingIdentity();
1007 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001008 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
1009 UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001010 if (oldRecord != null) {
1011 oldRecord.disposeLocked();
1012 }
1013
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001014 if (newUid) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001015 proxy.addListener(callingUid);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001016 }
1017
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018 boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
1019 if (isProviderEnabled) {
1020 long minTimeForProvider = getMinTimeLocked(provider);
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001021 proxy.setMinTime(minTimeForProvider);
1022 proxy.enableLocationTracking(true);
Mike Lockwood61fc2862009-04-21 20:02:52 -07001023 updateWakelockStatusLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024 } else {
Mike Lockwood48f17512009-04-23 09:12:08 -07001025 // Notify the listener that updates are currently disabled
1026 receiver.callProviderEnabledLocked(provider, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027 }
1028 } finally {
1029 Binder.restoreCallingIdentity(identity);
1030 }
1031 }
1032
1033 public void removeUpdates(ILocationListener listener) {
1034 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001035 synchronized (mLock) {
1036 removeUpdatesLocked(getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 }
1038 } catch (SecurityException se) {
1039 throw se;
1040 } catch (Exception e) {
1041 Log.e(TAG, "removeUpdates got exception:", e);
1042 }
1043 }
1044
1045 public void removeUpdatesPI(PendingIntent intent) {
1046 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001047 synchronized (mLock) {
1048 removeUpdatesLocked(getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001049 }
1050 } catch (SecurityException se) {
1051 throw se;
1052 } catch (Exception e) {
1053 Log.e(TAG, "removeUpdates got exception:", e);
1054 }
1055 }
1056
1057 private void removeUpdatesLocked(Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001058 if (LOCAL_LOGV) {
1059 Log.v(TAG, "_removeUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060 }
1061
1062 // so wakelock calls will succeed
1063 final int callingUid = Binder.getCallingUid();
1064 long identity = Binder.clearCallingIdentity();
1065 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001066 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1067 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001068 }
1069
1070 // Record which providers were associated with this listener
1071 HashSet<String> providers = new HashSet<String>();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001072 HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001073 if (oldRecords != null) {
1074 // Call dispose() on the obsolete update records.
1075 for (UpdateRecord record : oldRecords.values()) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001076 if (!providerHasListener(record.mProvider, callingUid, receiver)) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001077 LocationProviderProxy proxy = mProvidersByName.get(record.mProvider);
1078 if (proxy != null) {
1079 proxy.removeListener(callingUid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080 }
1081 }
1082 record.disposeLocked();
1083 }
1084 // Accumulate providers
1085 providers.addAll(oldRecords.keySet());
1086 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087
1088 // See if the providers associated with this listener have any
1089 // other listeners; if one does, inform it of the new smallest minTime
1090 // value; if one does not, disable location tracking for it
1091 for (String provider : providers) {
1092 // If provider is already disabled, don't need to do anything
1093 if (!isAllowedBySettingsLocked(provider)) {
1094 continue;
1095 }
1096
1097 boolean hasOtherListener = false;
1098 ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1099 if (recordsForProvider != null && recordsForProvider.size() > 0) {
1100 hasOtherListener = true;
1101 }
1102
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001103 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 if (p != null) {
1105 if (hasOtherListener) {
1106 p.setMinTime(getMinTimeLocked(provider));
1107 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001108 p.enableLocationTracking(false);
1109 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110 }
1111 }
1112
Mike Lockwood61fc2862009-04-21 20:02:52 -07001113 updateWakelockStatusLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 } finally {
1115 Binder.restoreCallingIdentity(identity);
1116 }
1117 }
1118
1119 public boolean addGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001120 if (mGpsStatusProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001121 return false;
1122 }
1123 if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1124 PackageManager.PERMISSION_GRANTED) {
1125 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1126 }
1127
1128 try {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001129 mGpsStatusProvider.addGpsStatusListener(listener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001130 } catch (RemoteException e) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001131 Log.e(TAG, "mGpsStatusProvider.addGpsStatusListener failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 return false;
1133 }
1134 return true;
1135 }
1136
1137 public void removeGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001138 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001139 try {
1140 mGpsStatusProvider.removeGpsStatusListener(listener);
1141 } catch (Exception e) {
1142 Log.e(TAG, "mGpsStatusProvider.removeGpsStatusListener failed", e);
1143 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001144 }
1145 }
1146
1147 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1148 // first check for permission to the provider
1149 checkPermissionsSafe(provider);
1150 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1151 if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1152 != PackageManager.PERMISSION_GRANTED)) {
1153 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1154 }
1155
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001156 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001157 LocationProviderProxy proxy = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001158 if (provider == null) {
1159 return false;
1160 }
1161
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001162 return proxy.sendExtraCommand(command, extras);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001163 }
1164 }
1165
1166 class ProximityAlert {
1167 final int mUid;
1168 final double mLatitude;
1169 final double mLongitude;
1170 final float mRadius;
1171 final long mExpiration;
1172 final PendingIntent mIntent;
1173 final Location mLocation;
1174
1175 public ProximityAlert(int uid, double latitude, double longitude,
1176 float radius, long expiration, PendingIntent intent) {
1177 mUid = uid;
1178 mLatitude = latitude;
1179 mLongitude = longitude;
1180 mRadius = radius;
1181 mExpiration = expiration;
1182 mIntent = intent;
1183
1184 mLocation = new Location("");
1185 mLocation.setLatitude(latitude);
1186 mLocation.setLongitude(longitude);
1187 }
1188
1189 long getExpiration() {
1190 return mExpiration;
1191 }
1192
1193 PendingIntent getIntent() {
1194 return mIntent;
1195 }
1196
1197 boolean isInProximity(double latitude, double longitude) {
1198 Location loc = new Location("");
1199 loc.setLatitude(latitude);
1200 loc.setLongitude(longitude);
1201
1202 double radius = loc.distanceTo(mLocation);
1203 return radius <= mRadius;
1204 }
1205
1206 @Override
1207 public String toString() {
1208 return "ProximityAlert{"
1209 + Integer.toHexString(System.identityHashCode(this))
1210 + " uid " + mUid + mIntent + "}";
1211 }
1212
1213 void dump(PrintWriter pw, String prefix) {
1214 pw.println(prefix + this);
1215 pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
1216 pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
1217 pw.println(prefix + "mIntent=" + mIntent);
1218 pw.println(prefix + "mLocation:");
1219 mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
1220 }
1221 }
1222
1223 // Listener for receiving locations to trigger proximity alerts
Mike Lockwood48f17512009-04-23 09:12:08 -07001224 class ProximityListener extends ILocationListener.Stub implements PendingIntent.OnFinished {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001225
1226 boolean isGpsAvailable = false;
1227
1228 // Note: this is called with the lock held.
1229 public void onLocationChanged(Location loc) {
1230
1231 // If Gps is available, then ignore updates from NetworkLocationProvider
1232 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1233 isGpsAvailable = true;
1234 }
1235 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1236 return;
1237 }
1238
1239 // Process proximity alerts
1240 long now = System.currentTimeMillis();
1241 double latitude = loc.getLatitude();
1242 double longitude = loc.getLongitude();
1243 ArrayList<PendingIntent> intentsToRemove = null;
1244
1245 for (ProximityAlert alert : mProximityAlerts.values()) {
1246 PendingIntent intent = alert.getIntent();
1247 long expiration = alert.getExpiration();
1248
1249 if ((expiration == -1) || (now <= expiration)) {
1250 boolean entered = mProximitiesEntered.contains(alert);
1251 boolean inProximity =
1252 alert.isInProximity(latitude, longitude);
1253 if (!entered && inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001254 if (LOCAL_LOGV) {
1255 Log.v(TAG, "Entered alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001256 }
1257 mProximitiesEntered.add(alert);
1258 Intent enteredIntent = new Intent();
1259 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1260 try {
Mike Lockwood48f17512009-04-23 09:12:08 -07001261 synchronized (mLock) {
1262 // synchronize to ensure incrementPendingBroadcastsLocked()
1263 // is called before decrementPendingBroadcasts()
1264 intent.send(mContext, 0, enteredIntent, this, mLocationHandler);
1265 // call this after broadcasting so we do not increment
1266 // if we throw an exeption.
1267 incrementPendingBroadcastsLocked();
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 } else if (entered && !inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001279 if (LOCAL_LOGV) {
1280 Log.v(TAG, "Exited alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001281 }
1282 mProximitiesEntered.remove(alert);
1283 Intent exitedIntent = new Intent();
1284 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1285 try {
Mike Lockwood48f17512009-04-23 09:12:08 -07001286 synchronized (mLock) {
1287 // synchronize to ensure incrementPendingBroadcastsLocked()
1288 // is called before decrementPendingBroadcasts()
1289 intent.send(mContext, 0, exitedIntent, this, mLocationHandler);
1290 // call this after broadcasting so we do not increment
1291 // if we throw an exeption.
1292 incrementPendingBroadcastsLocked();
1293 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001294 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001295 if (LOCAL_LOGV) {
1296 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001297 }
1298 if (intentsToRemove == null) {
1299 intentsToRemove = new ArrayList<PendingIntent>();
1300 }
1301 intentsToRemove.add(intent);
1302 }
1303 }
1304 } else {
1305 // Mark alert for expiration
The Android Open Source Project10592532009-03-18 17:39:46 -07001306 if (LOCAL_LOGV) {
1307 Log.v(TAG, "Expiring proximity alert: " + alert);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001308 }
1309 if (intentsToRemove == null) {
1310 intentsToRemove = new ArrayList<PendingIntent>();
1311 }
1312 intentsToRemove.add(alert.getIntent());
1313 }
1314 }
1315
1316 // Remove expired alerts
1317 if (intentsToRemove != null) {
1318 for (PendingIntent i : intentsToRemove) {
1319 mProximityAlerts.remove(i);
1320 ProximityAlert alert = mProximityAlerts.get(i);
1321 mProximitiesEntered.remove(alert);
1322 }
1323 }
1324
1325 }
1326
1327 // Note: this is called with the lock held.
1328 public void onProviderDisabled(String provider) {
1329 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1330 isGpsAvailable = false;
1331 }
1332 }
1333
1334 // Note: this is called with the lock held.
1335 public void onProviderEnabled(String provider) {
1336 // ignore
1337 }
1338
1339 // Note: this is called with the lock held.
1340 public void onStatusChanged(String provider, int status, Bundle extras) {
1341 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1342 (status != LocationProvider.AVAILABLE)) {
1343 isGpsAvailable = false;
1344 }
1345 }
Mike Lockwood48f17512009-04-23 09:12:08 -07001346
1347 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
1348 int resultCode, String resultData, Bundle resultExtras) {
1349 decrementPendingBroadcasts();
1350 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001351 }
1352
1353 public void addProximityAlert(double latitude, double longitude,
1354 float radius, long expiration, PendingIntent intent) {
1355 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001356 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001357 addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
1358 }
1359 } catch (SecurityException se) {
1360 throw se;
1361 } catch (Exception e) {
1362 Log.e(TAG, "addProximityAlert got exception:", e);
1363 }
1364 }
1365
1366 private void addProximityAlertLocked(double latitude, double longitude,
1367 float radius, long expiration, PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001368 if (LOCAL_LOGV) {
1369 Log.v(TAG, "addProximityAlert: latitude = " + latitude +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001370 ", longitude = " + longitude +
1371 ", expiration = " + expiration +
1372 ", intent = " + intent);
1373 }
1374
1375 // Require ability to access all providers for now
1376 if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
1377 !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
1378 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1379 }
1380
1381 if (expiration != -1) {
1382 expiration += System.currentTimeMillis();
1383 }
1384 ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
1385 latitude, longitude, radius, expiration, intent);
1386 mProximityAlerts.put(intent, alert);
1387
Mike Lockwood48f17512009-04-23 09:12:08 -07001388 if (mProximityReceiver == null) {
1389 mProximityListener = new ProximityListener();
1390 mProximityReceiver = new Receiver(mProximityListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001391
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001392 LocationProviderProxy provider = mProvidersByName.get(LocationManager.GPS_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393 if (provider != null) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001394 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001395 }
1396
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001397 provider = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001398 if (provider != null) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001399 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001401 }
1402 }
1403
1404 public void removeProximityAlert(PendingIntent intent) {
1405 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001406 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001407 removeProximityAlertLocked(intent);
1408 }
1409 } catch (SecurityException se) {
1410 throw se;
1411 } catch (Exception e) {
1412 Log.e(TAG, "removeProximityAlert got exception:", e);
1413 }
1414 }
1415
1416 private void removeProximityAlertLocked(PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001417 if (LOCAL_LOGV) {
1418 Log.v(TAG, "removeProximityAlert: intent = " + intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001419 }
1420
1421 mProximityAlerts.remove(intent);
1422 if (mProximityAlerts.size() == 0) {
Mike Lockwood48f17512009-04-23 09:12:08 -07001423 removeUpdatesLocked(mProximityReceiver);
1424 mProximityReceiver = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001425 mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001426 }
1427 }
1428
1429 /**
1430 * @return null if the provider does not exits
1431 * @throw SecurityException if the provider is not allowed to be
1432 * accessed by the caller
1433 */
1434 public Bundle getProviderInfo(String provider) {
1435 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001436 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001437 return _getProviderInfoLocked(provider);
1438 }
1439 } catch (SecurityException se) {
1440 throw se;
1441 } catch (Exception e) {
1442 Log.e(TAG, "_getProviderInfo got exception:", e);
1443 return null;
1444 }
1445 }
1446
1447 private Bundle _getProviderInfoLocked(String provider) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001448 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 if (p == null) {
1450 return null;
1451 }
1452
1453 checkPermissionsSafe(provider);
1454
1455 Bundle b = new Bundle();
1456 b.putBoolean("network", p.requiresNetwork());
1457 b.putBoolean("satellite", p.requiresSatellite());
1458 b.putBoolean("cell", p.requiresCell());
1459 b.putBoolean("cost", p.hasMonetaryCost());
1460 b.putBoolean("altitude", p.supportsAltitude());
1461 b.putBoolean("speed", p.supportsSpeed());
1462 b.putBoolean("bearing", p.supportsBearing());
1463 b.putInt("power", p.getPowerRequirement());
1464 b.putInt("accuracy", p.getAccuracy());
1465
1466 return b;
1467 }
1468
1469 public boolean isProviderEnabled(String provider) {
1470 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001471 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001472 return _isProviderEnabledLocked(provider);
1473 }
1474 } catch (SecurityException se) {
1475 throw se;
1476 } catch (Exception e) {
1477 Log.e(TAG, "isProviderEnabled got exception:", e);
1478 return false;
1479 }
1480 }
1481
Mike Lockwood275555c2009-05-01 11:30:34 -04001482 public void reportLocation(Location location) {
1483 if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
1484 != PackageManager.PERMISSION_GRANTED) {
1485 throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
1486 }
1487
Mike Lockwood4e50b782009-04-03 08:24:43 -07001488 mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
1489 Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
1490 mLocationHandler.sendMessageAtFrontOfQueue(m);
1491 }
1492
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001493 private boolean _isProviderEnabledLocked(String provider) {
1494 checkPermissionsSafe(provider);
1495
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001496 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001497 if (p == null) {
1498 throw new IllegalArgumentException("provider=" + provider);
1499 }
1500 return isAllowedBySettingsLocked(provider);
1501 }
1502
1503 public Location getLastKnownLocation(String provider) {
1504 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001505 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001506 return _getLastKnownLocationLocked(provider);
1507 }
1508 } catch (SecurityException se) {
1509 throw se;
1510 } catch (Exception e) {
1511 Log.e(TAG, "getLastKnownLocation got exception:", e);
1512 return null;
1513 }
1514 }
1515
1516 private Location _getLastKnownLocationLocked(String provider) {
1517 checkPermissionsSafe(provider);
1518
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001519 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001520 if (p == null) {
1521 throw new IllegalArgumentException("provider=" + provider);
1522 }
1523
1524 if (!isAllowedBySettingsLocked(provider)) {
1525 return null;
1526 }
1527
1528 Location location = mLastKnownLocation.get(provider);
1529 if (location == null) {
1530 // Get the persistent last known location for the provider
1531 location = readLastKnownLocationLocked(provider);
1532 if (location != null) {
1533 mLastKnownLocation.put(provider, location);
1534 }
1535 }
1536
1537 return location;
1538 }
1539
1540 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1541 // Always broadcast the first update
1542 if (lastLoc == null) {
1543 return true;
1544 }
1545
1546 // Don't broadcast same location again regardless of condition
1547 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1548 if (loc.getTime() == lastLoc.getTime()) {
1549 return false;
1550 }
1551
1552 // Check whether sufficient distance has been traveled
1553 double minDistance = record.mMinDistance;
1554 if (minDistance > 0.0) {
1555 if (loc.distanceTo(lastLoc) <= minDistance) {
1556 return false;
1557 }
1558 }
1559
1560 return true;
1561 }
1562
Mike Lockwood4e50b782009-04-03 08:24:43 -07001563 private void handleLocationChangedLocked(Location location) {
1564 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001565 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1566 if (records == null || records.size() == 0) {
1567 return;
1568 }
1569
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001570 LocationProviderProxy p = mProvidersByName.get(provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001571 if (p == null) {
1572 return;
1573 }
1574
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001575 // Update last known location for provider
Mike Lockwood4e50b782009-04-03 08:24:43 -07001576 Location lastLocation = mLastKnownLocation.get(provider);
1577 if (lastLocation == null) {
1578 mLastKnownLocation.put(provider, new Location(location));
1579 } else {
1580 lastLocation.set(location);
1581 }
1582 writeLastKnownLocationLocked(provider, location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001583
Mike Lockwoode932f7f2009-04-06 10:51:26 -07001584 if (LocationManager.NETWORK_PROVIDER.equals(p.getName())) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001585 mWakeLockNetworkReceived = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001586 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001587 // Gps location received signal is in NetworkStateBroadcastReceiver
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001588
1589 // Fetch latest status update time
1590 long newStatusUpdateTime = p.getStatusUpdateTime();
1591
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001592 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001593 Bundle extras = new Bundle();
1594 int status = p.getStatus(extras);
1595
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001596 ArrayList<Receiver> deadReceivers = null;
1597
1598 // Broadcast location or status to all listeners
1599 final int N = records.size();
1600 for (int i=0; i<N; i++) {
1601 UpdateRecord r = records.get(i);
1602 Receiver receiver = r.mReceiver;
1603
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001604 Location lastLoc = r.mLastFixBroadcast;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001605 if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1606 if (lastLoc == null) {
1607 lastLoc = new Location(location);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001608 r.mLastFixBroadcast = lastLoc;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001609 } else {
1610 lastLoc.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001611 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001612 if (!receiver.callLocationChangedLocked(location)) {
1613 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1614 if (deadReceivers == null) {
1615 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001616 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001617 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001618 }
1619 }
1620
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001621 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001622 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1623 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1624
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001625 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001626 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1627 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1628 if (deadReceivers == null) {
1629 deadReceivers = new ArrayList<Receiver>();
1630 }
1631 if (!deadReceivers.contains(receiver)) {
1632 deadReceivers.add(receiver);
1633 }
1634 }
1635 }
1636 }
1637
1638 if (deadReceivers != null) {
1639 for (int i=deadReceivers.size()-1; i>=0; i--) {
1640 removeUpdatesLocked(deadReceivers.get(i));
1641 }
1642 }
1643 }
1644
1645 private class LocationWorkerHandler extends Handler {
1646
1647 @Override
1648 public void handleMessage(Message msg) {
1649 try {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001650 if (msg.what == MESSAGE_LOCATION_CHANGED) {
1651 // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001652
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001653 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001654 Location location = (Location) msg.obj;
Mike Lockwood98cb6672009-04-17 18:03:44 -04001655
1656 if (mCollector != null &&
1657 LocationManager.GPS_PROVIDER.equals(location.getProvider())) {
1658 try {
1659 mCollector.updateLocation(location);
1660 } catch (RemoteException e) {
1661 Log.w(TAG, "mCollector.updateLocation failed");
1662 }
1663 }
1664
Mike Lockwood4e50b782009-04-03 08:24:43 -07001665 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001666 if (!isAllowedBySettingsLocked(provider)) {
1667 return;
1668 }
1669
Mike Lockwooda0e3cd32009-04-21 21:27:33 -07001670 handleLocationChangedLocked(location);
Mike Lockwood61fc2862009-04-21 20:02:52 -07001671 updateWakelockStatusLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001672 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001673 }
1674 } catch (Exception e) {
1675 // Log, don't crash!
1676 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1677 }
1678 }
1679 }
1680
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001681 private class PowerStateBroadcastReceiver extends BroadcastReceiver {
1682 @Override public void onReceive(Context context, Intent intent) {
1683 String action = intent.getAction();
1684
1685 if (action.equals(ALARM_INTENT)) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001686 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001687 log("PowerStateBroadcastReceiver: Alarm received");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001688 // Have to do this immediately, rather than posting a
1689 // message, so we execute our code while the system
1690 // is holding a wake lock until the alarm broadcast
1691 // is finished.
1692 acquireWakeLockLocked();
1693 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001694 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
1695 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001696 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001697 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1698 if (uid >= 0) {
1699 ArrayList<Receiver> removedRecs = null;
1700 for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
1701 for (int j=i.size()-1; j>=0; j--) {
1702 UpdateRecord ur = i.get(j);
1703 if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
1704 if (removedRecs == null) {
1705 removedRecs = new ArrayList<Receiver>();
1706 }
1707 if (!removedRecs.contains(ur.mReceiver)) {
1708 removedRecs.add(ur.mReceiver);
1709 }
1710 }
1711 }
1712 }
1713 ArrayList<ProximityAlert> removedAlerts = null;
1714 for (ProximityAlert i : mProximityAlerts.values()) {
1715 if (i.mUid == uid) {
1716 if (removedAlerts == null) {
1717 removedAlerts = new ArrayList<ProximityAlert>();
1718 }
1719 if (!removedAlerts.contains(i)) {
1720 removedAlerts.add(i);
1721 }
1722 }
1723 }
1724 if (removedRecs != null) {
1725 for (int i=removedRecs.size()-1; i>=0; i--) {
1726 removeUpdatesLocked(removedRecs.get(i));
1727 }
1728 }
1729 if (removedAlerts != null) {
1730 for (int i=removedAlerts.size()-1; i>=0; i--) {
1731 removeProximityAlertLocked(removedAlerts.get(i).mIntent);
1732 }
1733 }
1734 }
1735 }
1736 }
1737 }
1738 }
1739
1740 private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
1741 @Override public void onReceive(Context context, Intent intent) {
1742 String action = intent.getAction();
1743
Mike Lockwoodf113fbe2009-04-06 05:17:28 -07001744 if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001745 boolean noConnectivity =
1746 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1747 if (!noConnectivity) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001748 mNetworkState = LocationProvider.AVAILABLE;
1749 } else {
1750 mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001751 }
1752
1753 // Notify location providers of current network state
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001754 synchronized (mLock) {
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001755 for (int i = mProviders.size() - 1; i >= 0; i--) {
1756 LocationProviderProxy provider = mProviders.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001757 if (provider.requiresNetwork()) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001758 provider.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001759 }
1760 }
1761 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001762 } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
1763
1764 final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED,
1765 false);
1766
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001767 synchronized (mLock) {
Mike Lockwood98cb6672009-04-17 18:03:44 -04001768 if (!enabled) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001769 // When GPS is disabled, we are OK to release wake-lock
1770 mWakeLockGpsReceived = true;
1771 }
1772 }
1773 }
1774
1775 }
1776 }
1777
1778 // Wake locks
1779
Mike Lockwood61fc2862009-04-21 20:02:52 -07001780 private void updateWakelockStatusLocked() {
1781 log("updateWakelockStatus()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782
Amith Yamasanie1ccba22009-04-02 11:40:25 -07001783 long callerId = Binder.clearCallingIdentity();
1784
Mike Lockwood48f17512009-04-23 09:12:08 -07001785 boolean needsLock = (mPendingBroadcasts > 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001786 long minTime = Integer.MAX_VALUE;
1787
1788 if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) {
1789 needsLock = true;
1790 minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime);
1791 }
1792
1793 if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) {
1794 needsLock = true;
1795 minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001796 }
1797
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001798 PendingIntent sender =
1799 PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0);
1800
1801 // Cancel existing alarm
1802 log("Cancelling existing alarm");
1803 mAlarmManager.cancel(sender);
1804
Mike Lockwood61fc2862009-04-21 20:02:52 -07001805 if (needsLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001806 long now = SystemClock.elapsedRealtime();
1807 mAlarmManager.set(
1808 AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender);
1809 mAlarmInterval = minTime;
1810 log("Creating a new wakelock alarm with minTime = " + minTime);
1811 } else {
1812 log("No need for alarm");
1813 mAlarmInterval = -1;
1814
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001815 releaseWakeLockLocked();
1816 }
Amith Yamasanie1ccba22009-04-02 11:40:25 -07001817 Binder.restoreCallingIdentity(callerId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001818 }
1819
1820 private void acquireWakeLockLocked() {
1821 try {
1822 acquireWakeLockXLocked();
1823 } catch (Exception e) {
1824 // This is to catch a runtime exception thrown when we try to release an
1825 // already released lock.
1826 Log.e(TAG, "exception in acquireWakeLock()", e);
1827 }
1828 }
1829
1830 private void acquireWakeLockXLocked() {
1831 if (mWakeLock.isHeld()) {
1832 log("Must release wakelock before acquiring");
1833 mWakeLockAcquireTime = 0;
1834 mWakeLock.release();
1835 }
1836
1837 boolean networkActive = (mNetworkLocationProvider != null)
1838 && mNetworkLocationProvider.isLocationTracking();
1839 boolean gpsActive = (mGpsLocationProvider != null)
1840 && mGpsLocationProvider.isLocationTracking();
1841
1842 boolean needsLock = networkActive || gpsActive;
1843 if (!needsLock) {
1844 log("No need for Lock!");
1845 return;
1846 }
1847
1848 mWakeLockGpsReceived = !gpsActive;
1849 mWakeLockNetworkReceived = !networkActive;
1850
1851 // Acquire wake lock
1852 mWakeLock.acquire();
1853 mWakeLockAcquireTime = SystemClock.elapsedRealtime();
1854 log("Acquired wakelock");
1855
Mike Lockwood6de31542009-04-21 12:13:35 -07001856 if (mNetworkLocationProvider != null) {
1857 mNetworkLocationProvider.wakeLockAcquired();
1858 }
1859 if (mGpsLocationProvider != null) {
1860 mGpsLocationProvider.wakeLockAcquired();
1861 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001862 }
1863
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001864 private void releaseWakeLockLocked() {
1865 try {
1866 releaseWakeLockXLocked();
1867 } catch (Exception e) {
1868 // This is to catch a runtime exception thrown when we try to release an
1869 // already released lock.
1870 Log.e(TAG, "exception in releaseWakeLock()", e);
1871 }
1872 }
1873
1874 private void releaseWakeLockXLocked() {
Mike Lockwood6de31542009-04-21 12:13:35 -07001875 if (mNetworkLocationProvider != null) {
1876 mNetworkLocationProvider.wakeLockReleased();
1877 }
1878 if (mGpsLocationProvider != null) {
1879 mGpsLocationProvider.wakeLockReleased();
1880 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001881
1882 // Release wake lock
1883 mWakeLockAcquireTime = 0;
1884 if (mWakeLock.isHeld()) {
1885 log("Released wakelock");
1886 mWakeLock.release();
1887 } else {
1888 log("Can't release wakelock again!");
1889 }
1890 }
1891
Mike Lockwood48f17512009-04-23 09:12:08 -07001892 private void incrementPendingBroadcastsLocked() {
1893 if (mPendingBroadcasts++ == 0) {
1894 updateWakelockStatusLocked();
1895 }
1896 }
1897
1898 private void decrementPendingBroadcasts() {
1899 synchronized (mLock) {
1900 if (--mPendingBroadcasts == 0) {
1901 updateWakelockStatusLocked();
1902 }
1903 }
1904 }
1905
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001906 // Geocoder
1907
1908 public String getFromLocation(double latitude, double longitude, int maxResults,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001909 String language, String country, String variant, String appName, List<Address> addrs) {
1910 if (mGeocodeProvider != null) {
1911 try {
1912 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, language, country,
1913 variant, appName, addrs);
1914 } catch (RemoteException e) {
1915 Log.e(TAG, "getFromLocation failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001916 }
1917 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001918 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001919 }
1920
Mike Lockwooda55c3212009-04-15 11:10:11 -04001921
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001922 public String getFromLocationName(String locationName,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001923 double lowerLeftLatitude, double lowerLeftLongitude,
1924 double upperRightLatitude, double upperRightLongitude, int maxResults,
1925 String language, String country, String variant, String appName, List<Address> addrs) {
1926
1927 if (mGeocodeProvider != null) {
1928 try {
1929 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1930 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1931 maxResults, language, country, variant, appName, addrs);
1932 } catch (RemoteException e) {
1933 Log.e(TAG, "getFromLocationName failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001934 }
1935 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001936 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001937 }
1938
1939 // Mock Providers
1940
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001941 private void checkMockPermissionsSafe() {
1942 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1943 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1944 if (!allowMocks) {
1945 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1946 }
1947
1948 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1949 PackageManager.PERMISSION_GRANTED) {
1950 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1951 }
1952 }
1953
1954 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1955 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1956 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1957 checkMockPermissionsSafe();
1958
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001959 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001960 MockProvider provider = new MockProvider(name, this,
1961 requiresNetwork, requiresSatellite,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001962 requiresCell, hasMonetaryCost, supportsAltitude,
1963 supportsSpeed, supportsBearing, powerRequirement, accuracy);
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001964 if (mProvidersByName.get(name) != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001965 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1966 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001967 addProvider(new LocationProviderProxy(name, provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001968 mMockProviders.put(name, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001969 updateProvidersLocked();
1970 }
1971 }
1972
1973 public void removeTestProvider(String provider) {
1974 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001975 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001976 MockProvider mockProvider = mMockProviders.get(provider);
1977 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001978 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1979 }
Mike Lockwood15e3d0f2009-05-01 07:53:28 -04001980 removeProvider(mProvidersByName.get(provider));
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001981 mMockProviders.remove(mockProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001982 updateProvidersLocked();
1983 }
1984 }
1985
1986 public void setTestProviderLocation(String provider, Location loc) {
1987 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001988 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001989 MockProvider mockProvider = mMockProviders.get(provider);
1990 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001991 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1992 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001993 mockProvider.setLocation(loc);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001994 }
1995 }
1996
1997 public void clearTestProviderLocation(String provider) {
1998 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001999 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002000 MockProvider mockProvider = mMockProviders.get(provider);
2001 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002002 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2003 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002004 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002005 }
2006 }
2007
2008 public void setTestProviderEnabled(String provider, boolean enabled) {
2009 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002010 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002011 MockProvider mockProvider = mMockProviders.get(provider);
2012 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002013 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2014 }
2015 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002016 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002017 mEnabledProviders.add(provider);
2018 mDisabledProviders.remove(provider);
2019 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002020 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002021 mEnabledProviders.remove(provider);
2022 mDisabledProviders.add(provider);
2023 }
2024 updateProvidersLocked();
2025 }
2026 }
2027
2028 public void clearTestProviderEnabled(String provider) {
2029 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002030 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002031 MockProvider mockProvider = mMockProviders.get(provider);
2032 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002033 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2034 }
2035 mEnabledProviders.remove(provider);
2036 mDisabledProviders.remove(provider);
2037 updateProvidersLocked();
2038 }
2039 }
2040
2041 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
2042 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002043 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002044 MockProvider mockProvider = mMockProviders.get(provider);
2045 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002046 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2047 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002048 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002049 }
2050 }
2051
2052 public void clearTestProviderStatus(String provider) {
2053 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002054 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002055 MockProvider mockProvider = mMockProviders.get(provider);
2056 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002057 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2058 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002059 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002060 }
2061 }
2062
2063 private void log(String log) {
2064 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2065 Log.d(TAG, log);
2066 }
2067 }
2068
2069 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2070 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2071 != PackageManager.PERMISSION_GRANTED) {
2072 pw.println("Permission Denial: can't dump AlarmManager from from pid="
2073 + Binder.getCallingPid()
2074 + ", uid=" + Binder.getCallingUid());
2075 return;
2076 }
2077
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002078 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002079 pw.println("Current Location Manager state:");
2080 pw.println(" sProvidersLoaded=" + sProvidersLoaded);
2081 pw.println(" mGpsLocationProvider=" + mGpsLocationProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002082 pw.println(" mNetworkLocationProvider=" + mNetworkLocationProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002083 pw.println(" mCollector=" + mCollector);
2084 pw.println(" mAlarmInterval=" + mAlarmInterval
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002085 + " mWakeLockAcquireTime=" + mWakeLockAcquireTime);
2086 pw.println(" mWakeLockGpsReceived=" + mWakeLockGpsReceived
2087 + " mWakeLockNetworkReceived=" + mWakeLockNetworkReceived);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002088 pw.println(" Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002089 int N = mReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002090 for (int i=0; i<N; i++) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002091 pw.println(" " + mReceivers.get(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002092 }
2093 pw.println(" Location Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002094 for (Receiver i : mReceivers.values()) {
2095 pw.println(" " + i + ":");
2096 for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002097 pw.println(" " + j.getKey() + ":");
2098 j.getValue().dump(pw, " ");
2099 }
2100 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002101 pw.println(" Records by Provider:");
2102 for (Map.Entry<String, ArrayList<UpdateRecord>> i
2103 : mRecordsByProvider.entrySet()) {
2104 pw.println(" " + i.getKey() + ":");
2105 for (UpdateRecord j : i.getValue()) {
2106 pw.println(" " + j + ":");
2107 j.dump(pw, " ");
2108 }
2109 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002110 pw.println(" Last Known Locations:");
2111 for (Map.Entry<String, Location> i
2112 : mLastKnownLocation.entrySet()) {
2113 pw.println(" " + i.getKey() + ":");
2114 i.getValue().dump(new PrintWriterPrinter(pw), " ");
2115 }
2116 if (mProximityAlerts.size() > 0) {
2117 pw.println(" Proximity Alerts:");
2118 for (Map.Entry<PendingIntent, ProximityAlert> i
2119 : mProximityAlerts.entrySet()) {
2120 pw.println(" " + i.getKey() + ":");
2121 i.getValue().dump(pw, " ");
2122 }
2123 }
2124 if (mProximitiesEntered.size() > 0) {
2125 pw.println(" Proximities Entered:");
2126 for (ProximityAlert i : mProximitiesEntered) {
2127 pw.println(" " + i + ":");
2128 i.dump(pw, " ");
2129 }
2130 }
Mike Lockwood48f17512009-04-23 09:12:08 -07002131 pw.println(" mProximityReceiver=" + mProximityReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002132 pw.println(" mProximityListener=" + mProximityListener);
2133 if (mEnabledProviders.size() > 0) {
2134 pw.println(" Enabled Providers:");
2135 for (String i : mEnabledProviders) {
2136 pw.println(" " + i);
2137 }
2138
2139 }
2140 if (mDisabledProviders.size() > 0) {
2141 pw.println(" Disabled Providers:");
2142 for (String i : mDisabledProviders) {
2143 pw.println(" " + i);
2144 }
2145
2146 }
2147 if (mMockProviders.size() > 0) {
2148 pw.println(" Mock Providers:");
2149 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002150 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002151 }
2152 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002153 }
2154 }
2155}