blob: 83108f87c12e75973732ffc56e4b71a63c87f981 [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 Lockwoode932f7f2009-04-06 10:51:26 -070049import android.location.ILocationCollector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.location.ILocationListener;
51import android.location.ILocationManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070052import android.location.ILocationProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.location.Location;
54import android.location.LocationManager;
55import android.location.LocationProvider;
56import android.location.LocationProviderImpl;
57import 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;
63import android.os.Message;
64import android.os.PowerManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070065import android.os.Process;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066import android.os.RemoteException;
67import android.os.SystemClock;
68import android.provider.Settings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import android.util.Config;
70import android.util.Log;
71import android.util.PrintWriterPrinter;
72import android.util.SparseIntArray;
73
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074import com.android.internal.location.GpsLocationProvider;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070075import com.android.internal.location.LocationProviderProxy;
Mike Lockwood7ec434e2009-03-27 07:46:48 -070076import com.android.internal.location.MockProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077import com.android.internal.location.TrackProvider;
78import 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 Lockwoode932f7f2009-04-06 10:51:26 -070086public class LocationManagerService extends ILocationManager.Stub {
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
96 // Time to wait after releasing a wake lock for clients to process location update,
97 // in milliseconds.
98 private static final long TIME_AFTER_WAKE_LOCK = 2 * 1000L;
99
100 // The last time a location was written, by provider name.
101 private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
102
103 private static final Pattern PATTERN_COMMA = Pattern.compile(",");
104
105 private static final String ACCESS_FINE_LOCATION =
106 android.Manifest.permission.ACCESS_FINE_LOCATION;
107 private static final String ACCESS_COARSE_LOCATION =
108 android.Manifest.permission.ACCESS_COARSE_LOCATION;
109 private static final String ACCESS_MOCK_LOCATION =
110 android.Manifest.permission.ACCESS_MOCK_LOCATION;
111 private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
112 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
113
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;
126 private GpsLocationProvider mGpsLocationProvider;
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700127 private LocationProviderProxy mNetworkLocationProvider;
Mike Lockwooda55c3212009-04-15 11:10:11 -0400128 private IGeocodeProvider mGeocodeProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 private LocationWorkerHandler mLocationHandler;
130
131 // Handler messages
Mike Lockwood4e50b782009-04-03 08:24:43 -0700132 private static final int MESSAGE_LOCATION_CHANGED = 1;
Mike Lockwood9ce72172009-04-21 20:18:41 -0700133 private static final int MESSAGE_RELEASE_WAKE_LOCK = 2;
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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 private long mWakeLockAcquireTime = 0;
142 private boolean mWakeLockGpsReceived = true;
143 private boolean mWakeLockNetworkReceived = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 /**
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400146 * List of all receivers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 */
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400148 private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149
150 /**
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400151 * Object used internally for synchronization
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 */
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400153 private final Object mLock = new Object();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154
155 /**
156 * Mapping from provider name to all its UpdateRecords
157 */
158 private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
159 new HashMap<String,ArrayList<UpdateRecord>>();
160
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 // Proximity listeners
162 private Receiver mProximityListener = null;
163 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
164 new HashMap<PendingIntent,ProximityAlert>();
165 private HashSet<ProximityAlert> mProximitiesEntered =
166 new HashSet<ProximityAlert>();
167
168 // Last known location for each provider
169 private HashMap<String,Location> mLastKnownLocation =
170 new HashMap<String,Location>();
171
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 // Location collector
173 private ILocationCollector mCollector;
174
The Android Open Source Project4df24232009-03-05 14:34:35 -0800175 private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800176
Mike Lockwood9637d472009-04-02 21:41:57 -0700177 // for Settings change notification
178 private ContentQueryMap mSettings;
179
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 /**
181 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
182 * location updates.
183 */
184 private final class Receiver implements IBinder.DeathRecipient {
185 final ILocationListener mListener;
186 final PendingIntent mPendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 final Object mKey;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400188 final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400190 Receiver(ILocationListener listener) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 mListener = listener;
192 mPendingIntent = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 mKey = listener.asBinder();
194 }
195
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400196 Receiver(PendingIntent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 mPendingIntent = intent;
198 mListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 mKey = intent;
200 }
201
202 @Override
203 public boolean equals(Object otherObj) {
204 if (otherObj instanceof Receiver) {
205 return mKey.equals(
206 ((Receiver)otherObj).mKey);
207 }
208 return false;
209 }
210
211 @Override
212 public int hashCode() {
213 return mKey.hashCode();
214 }
215
216
217 @Override
218 public String toString() {
219 if (mListener != null) {
220 return "Receiver{"
221 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400222 + " Listener " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 } else {
224 return "Receiver{"
225 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400226 + " Intent " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 }
228 }
229
230 public boolean isListener() {
231 return mListener != null;
232 }
233
234 public boolean isPendingIntent() {
235 return mPendingIntent != null;
236 }
237
238 public ILocationListener getListener() {
239 if (mListener != null) {
240 return mListener;
241 }
242 throw new IllegalStateException("Request for non-existent listener");
243 }
244
245 public PendingIntent getPendingIntent() {
246 if (mPendingIntent != null) {
247 return mPendingIntent;
248 }
249 throw new IllegalStateException("Request for non-existent intent");
250 }
251
252 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
253 if (mListener != null) {
254 try {
255 mListener.onStatusChanged(provider, status, extras);
256 } catch (RemoteException e) {
257 return false;
258 }
259 } else {
260 Intent statusChanged = new Intent();
261 statusChanged.putExtras(extras);
262 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
263 try {
264 mPendingIntent.send(mContext, 0, statusChanged, null, null);
265 } catch (PendingIntent.CanceledException e) {
266 return false;
267 }
268 }
269 return true;
270 }
271
272 public boolean callLocationChangedLocked(Location location) {
273 if (mListener != null) {
274 try {
275 mListener.onLocationChanged(location);
276 } catch (RemoteException e) {
277 return false;
278 }
279 } else {
280 Intent locationChanged = new Intent();
281 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
282 try {
283 mPendingIntent.send(mContext, 0, locationChanged, null, null);
284 } catch (PendingIntent.CanceledException e) {
285 return false;
286 }
287 }
288 return true;
289 }
290
291 public void binderDied() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700292 if (LOCAL_LOGV) {
293 Log.v(TAG, "Location listener died");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 }
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400295 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 removeUpdatesLocked(this);
297 }
298 }
299 }
300
Mike Lockwood9637d472009-04-02 21:41:57 -0700301 private final class SettingsObserver implements Observer {
302 public void update(Observable o, Object arg) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400303 synchronized (mLock) {
Mike Lockwood9637d472009-04-02 21:41:57 -0700304 updateProvidersLocked();
305 }
306 }
307 }
308
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 private Location readLastKnownLocationLocked(String provider) {
310 Location location = null;
311 String s = null;
312 try {
313 File f = new File(LocationManager.SYSTEM_DIR + "/location."
314 + provider);
315 if (!f.exists()) {
316 return null;
317 }
318 BufferedReader reader = new BufferedReader(new FileReader(f), 256);
319 s = reader.readLine();
320 } catch (IOException e) {
321 Log.w(TAG, "Unable to read last known location", e);
322 }
323
324 if (s == null) {
325 return null;
326 }
327 try {
328 String[] tokens = PATTERN_COMMA.split(s);
329 int idx = 0;
330 long time = Long.parseLong(tokens[idx++]);
331 double latitude = Double.parseDouble(tokens[idx++]);
332 double longitude = Double.parseDouble(tokens[idx++]);
333 double altitude = Double.parseDouble(tokens[idx++]);
334 float bearing = Float.parseFloat(tokens[idx++]);
335 float speed = Float.parseFloat(tokens[idx++]);
336
337 location = new Location(provider);
338 location.setTime(time);
339 location.setLatitude(latitude);
340 location.setLongitude(longitude);
341 location.setAltitude(altitude);
342 location.setBearing(bearing);
343 location.setSpeed(speed);
344 } catch (NumberFormatException nfe) {
345 Log.e(TAG, "NumberFormatException reading last known location", nfe);
346 return null;
347 }
348
349 return location;
350 }
351
352 private void writeLastKnownLocationLocked(String provider,
353 Location location) {
354 long now = SystemClock.elapsedRealtime();
355 Long last = mLastWriteTime.get(provider);
356 if ((last != null)
357 && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
358 return;
359 }
360 mLastWriteTime.put(provider, now);
361
362 StringBuilder sb = new StringBuilder(100);
363 sb.append(location.getTime());
364 sb.append(',');
365 sb.append(location.getLatitude());
366 sb.append(',');
367 sb.append(location.getLongitude());
368 sb.append(',');
369 sb.append(location.getAltitude());
370 sb.append(',');
371 sb.append(location.getBearing());
372 sb.append(',');
373 sb.append(location.getSpeed());
374
375 FileWriter writer = null;
376 try {
377 File d = new File(LocationManager.SYSTEM_DIR);
378 if (!d.exists()) {
379 if (!d.mkdirs()) {
380 Log.w(TAG, "Unable to create directory to write location");
381 return;
382 }
383 }
384 File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
385 writer = new FileWriter(f);
386 writer.write(sb.toString());
387 } catch (IOException e) {
388 Log.w(TAG, "Unable to write location", e);
389 } finally {
390 if (writer != null) {
391 try {
392 writer.close();
393 } catch (IOException e) {
394 Log.w(TAG, "Exception closing file", e);
395 }
396 }
397 }
398 }
399
400 /**
401 * Load providers from /data/location/<provider_name>/
402 * class
403 * kml
404 * nmea
405 * track
406 * location
407 * properties
408 */
409 private void loadProviders() {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400410 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 if (sProvidersLoaded) {
412 return;
413 }
414
415 // Load providers
416 loadProvidersLocked();
417 sProvidersLoaded = true;
418 }
419 }
420
421 private void loadProvidersLocked() {
422 try {
423 _loadProvidersLocked();
424 } catch (Exception e) {
425 Log.e(TAG, "Exception loading providers:", e);
426 }
427 }
428
429 private void _loadProvidersLocked() {
430 // Attempt to load "real" providers first
431 if (GpsLocationProvider.isSupported()) {
432 // Create a gps location provider
Mike Lockwood4e50b782009-04-03 08:24:43 -0700433 mGpsLocationProvider = new GpsLocationProvider(mContext, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 LocationProviderImpl.addProvider(mGpsLocationProvider);
435 }
436
437 // Load fake providers if real providers are not available
438 File f = new File(LocationManager.PROVIDER_DIR);
439 if (f.isDirectory()) {
440 File[] subdirs = f.listFiles();
441 for (int i = 0; i < subdirs.length; i++) {
442 if (!subdirs[i].isDirectory()) {
443 continue;
444 }
445
446 String name = subdirs[i].getName();
447
The Android Open Source Project10592532009-03-18 17:39:46 -0700448 if (LOCAL_LOGV) {
449 Log.v(TAG, "Found dir " + subdirs[i].getAbsolutePath());
450 Log.v(TAG, "name = " + name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 }
452
453 // Don't create a fake provider if a real provider exists
454 if (LocationProviderImpl.getProvider(name) == null) {
455 LocationProviderImpl provider = null;
456 try {
457 File classFile = new File(subdirs[i], "class");
458 // Look for a 'class' file
459 provider = LocationProviderImpl.loadFromClass(classFile);
460
461 // Look for an 'kml', 'nmea', or 'track' file
462 if (provider == null) {
463 // Load properties from 'properties' file, if present
464 File propertiesFile = new File(subdirs[i], "properties");
465
466 if (propertiesFile.exists()) {
Mike Lockwood4e50b782009-04-03 08:24:43 -0700467 provider = new TrackProvider(name, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 ((TrackProvider)provider).readProperties(propertiesFile);
469
470 File kmlFile = new File(subdirs[i], "kml");
471 if (kmlFile.exists()) {
472 ((TrackProvider) provider).readKml(kmlFile);
473 } else {
474 File nmeaFile = new File(subdirs[i], "nmea");
475 if (nmeaFile.exists()) {
476 ((TrackProvider) provider).readNmea(name, nmeaFile);
477 } else {
478 File trackFile = new File(subdirs[i], "track");
479 if (trackFile.exists()) {
480 ((TrackProvider) provider).readTrack(trackFile);
481 }
482 }
483 }
484 }
485 }
486 if (provider != null) {
487 LocationProviderImpl.addProvider(provider);
488 }
489 // Grab the initial location of a TrackProvider and
490 // store it as the last known location for that provider
491 if (provider instanceof TrackProvider) {
492 TrackProvider tp = (TrackProvider) provider;
493 mLastKnownLocation.put(tp.getName(), tp.getInitialLocation());
494 }
495 } catch (Exception e) {
496 Log.e(TAG, "Exception loading provder " + name, e);
497 }
498 }
499 }
500 }
501
502 updateProvidersLocked();
503 }
504
505 /**
506 * @param context the context that the LocationManagerService runs in
507 */
508 public LocationManagerService(Context context) {
509 super();
510 mContext = context;
511 mLocationHandler = new LocationWorkerHandler();
512
The Android Open Source Project10592532009-03-18 17:39:46 -0700513 if (LOCAL_LOGV) {
514 Log.v(TAG, "Constructed LocationManager Service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 }
516
517 // Alarm manager, needs to be done before calling loadProviders() below
518 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
519
520 // Create a wake lock, needs to be done before calling loadProviders() below
521 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
522 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
523
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 // Load providers
525 loadProviders();
526
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 // Register for Network (Wifi or Mobile) updates
528 NetworkStateBroadcastReceiver networkReceiver = new NetworkStateBroadcastReceiver();
529 IntentFilter networkIntentFilter = new IntentFilter();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
531 networkIntentFilter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
532 context.registerReceiver(networkReceiver, networkIntentFilter);
533
534 // Register for power updates
535 PowerStateBroadcastReceiver powerStateReceiver = new PowerStateBroadcastReceiver();
536 IntentFilter intentFilter = new IntentFilter();
537 intentFilter.addAction(ALARM_INTENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
539 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
540 context.registerReceiver(powerStateReceiver, intentFilter);
541
Mike Lockwood9637d472009-04-02 21:41:57 -0700542 // listen for settings changes
543 ContentResolver resolver = mContext.getContentResolver();
544 Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
545 "(" + Settings.System.NAME + "=?)",
546 new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
547 null);
548 mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler);
549 SettingsObserver settingsObserver = new SettingsObserver();
550 mSettings.addObserver(settingsObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 }
552
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700553 public void setNetworkLocationProvider(ILocationProvider provider) {
554 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
555 throw new SecurityException(
556 "Installing location providers outside of the system is not supported");
557 }
558
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400559 synchronized (mLock) {
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700560 mNetworkLocationProvider =
561 new LocationProviderProxy(LocationManager.NETWORK_PROVIDER, this, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 LocationProviderImpl.addProvider(mNetworkLocationProvider);
563 updateProvidersLocked();
The Android Open Source Project4df24232009-03-05 14:34:35 -0800564
565 // notify NetworkLocationProvider of any events it might have missed
Mike Lockwoodf113fbe2009-04-06 05:17:28 -0700566 mNetworkLocationProvider.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567 }
568 }
569
570 public void setLocationCollector(ILocationCollector collector) {
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700571 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
572 throw new SecurityException(
573 "Installing location collectors outside of the system is not supported");
574 }
575
Mike Lockwood98cb6672009-04-17 18:03:44 -0400576 mCollector = collector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 }
578
Mike Lockwooda55c3212009-04-15 11:10:11 -0400579 public void setGeocodeProvider(IGeocodeProvider provider) {
580 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
581 throw new SecurityException(
582 "Installing location providers outside of the system is not supported");
583 }
584
585 mGeocodeProvider = provider;
586 }
587
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 private boolean isAllowedBySettingsLocked(String provider) {
589 if (mEnabledProviders.contains(provider)) {
590 return true;
591 }
592 if (mDisabledProviders.contains(provider)) {
593 return false;
594 }
595 // Use system settings
596 ContentResolver resolver = mContext.getContentResolver();
597 String allowedProviders = Settings.Secure.getString(resolver,
598 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
599
600 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
601 }
602
603 private void checkPermissionsSafe(String provider) {
604 if (LocationManager.GPS_PROVIDER.equals(provider)
605 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
606 != PackageManager.PERMISSION_GRANTED)) {
607 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
608 }
609 if (LocationManager.NETWORK_PROVIDER.equals(provider)
610 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
611 != PackageManager.PERMISSION_GRANTED)
612 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
613 != PackageManager.PERMISSION_GRANTED)) {
614 throw new SecurityException(
615 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
616 }
617 }
618
619 private boolean isAllowedProviderSafe(String provider) {
620 if (LocationManager.GPS_PROVIDER.equals(provider)
621 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
622 != PackageManager.PERMISSION_GRANTED)) {
623 return false;
624 }
625 if (LocationManager.NETWORK_PROVIDER.equals(provider)
626 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
627 != PackageManager.PERMISSION_GRANTED)
628 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
629 != PackageManager.PERMISSION_GRANTED)) {
630 return false;
631 }
632
633 return true;
634 }
635
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800636 public List<String> getAllProviders() {
637 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400638 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 return _getAllProvidersLocked();
640 }
641 } catch (SecurityException se) {
642 throw se;
643 } catch (Exception e) {
644 Log.e(TAG, "getAllProviders got exception:", e);
645 return null;
646 }
647 }
648
649 private List<String> _getAllProvidersLocked() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700650 if (LOCAL_LOGV) {
651 Log.v(TAG, "getAllProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800652 }
653 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
654 ArrayList<String> out = new ArrayList<String>(providers.size());
655
656 for (LocationProviderImpl p : providers) {
657 out.add(p.getName());
658 }
659 return out;
660 }
661
662 public List<String> getProviders(boolean enabledOnly) {
663 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400664 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 return _getProvidersLocked(enabledOnly);
666 }
667 } catch (SecurityException se) {
668 throw se;
669 } catch (Exception e) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700670 Log.e(TAG, "getProviders got exception:", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 return null;
672 }
673 }
674
675 private List<String> _getProvidersLocked(boolean enabledOnly) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700676 if (LOCAL_LOGV) {
677 Log.v(TAG, "getProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800678 }
679 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
680 ArrayList<String> out = new ArrayList<String>();
681
682 for (LocationProviderImpl p : providers) {
683 String name = p.getName();
684 if (isAllowedProviderSafe(name)) {
685 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
686 continue;
687 }
688 out.add(name);
689 }
690 }
691 return out;
692 }
693
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800694 private void updateProvidersLocked() {
695 for (LocationProviderImpl p : LocationProviderImpl.getProviders()) {
696 boolean isEnabled = p.isEnabled();
697 String name = p.getName();
698 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
699
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700 if (isEnabled && !shouldBeEnabled) {
701 updateProviderListenersLocked(name, false);
702 } else if (!isEnabled && shouldBeEnabled) {
703 updateProviderListenersLocked(name, true);
704 }
705
706 }
707 }
708
709 private void updateProviderListenersLocked(String provider, boolean enabled) {
710 int listeners = 0;
711
712 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
713 if (p == null) {
714 return;
715 }
716
717 ArrayList<Receiver> deadReceivers = null;
718
719 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
720 if (records != null) {
721 final int N = records.size();
722 for (int i=0; i<N; i++) {
723 UpdateRecord record = records.get(i);
724 // Sends a notification message to the receiver
725 try {
726 Receiver receiver = record.mReceiver;
727 if (receiver.isListener()) {
728 if (enabled) {
729 receiver.getListener().onProviderEnabled(provider);
730 } else {
731 receiver.getListener().onProviderDisabled(provider);
732 }
733 } else {
734 Intent providerIntent = new Intent();
735 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
736 try {
737 receiver.getPendingIntent().send(mContext, 0,
738 providerIntent, null, null);
739 } catch (PendingIntent.CanceledException e) {
740 if (deadReceivers == null) {
741 deadReceivers = new ArrayList<Receiver>();
742 deadReceivers.add(receiver);
743 }
744 }
745 }
746 } catch (RemoteException e) {
747 // The death link will clean this up.
748 }
749 listeners++;
750 }
751 }
752
753 if (deadReceivers != null) {
754 for (int i=deadReceivers.size()-1; i>=0; i--) {
755 removeUpdatesLocked(deadReceivers.get(i));
756 }
757 }
758
759 if (enabled) {
760 p.enable();
761 if (listeners > 0) {
762 p.setMinTime(getMinTimeLocked(provider));
763 p.enableLocationTracking(true);
Mike Lockwood61fc2862009-04-21 20:02:52 -0700764 updateWakelockStatusLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800765 }
766 } else {
767 p.enableLocationTracking(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 p.disable();
Mike Lockwood61fc2862009-04-21 20:02:52 -0700769 updateWakelockStatusLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 }
772
773 private long getMinTimeLocked(String provider) {
774 long minTime = Long.MAX_VALUE;
775 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
776 if (records != null) {
777 for (int i=records.size()-1; i>=0; i--) {
778 minTime = Math.min(minTime, records.get(i).mMinTime);
779 }
780 }
781 return minTime;
782 }
783
784 private class UpdateRecord {
785 final String mProvider;
786 final Receiver mReceiver;
787 final long mMinTime;
788 final float mMinDistance;
789 final int mUid;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400790 Location mLastFixBroadcast;
791 long mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792
793 /**
794 * Note: must be constructed with lock held.
795 */
796 UpdateRecord(String provider, long minTime, float minDistance,
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400797 Receiver receiver, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 mProvider = provider;
799 mReceiver = receiver;
800 mMinTime = minTime;
801 mMinDistance = minDistance;
802 mUid = uid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803
804 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
805 if (records == null) {
806 records = new ArrayList<UpdateRecord>();
807 mRecordsByProvider.put(provider, records);
808 }
809 if (!records.contains(this)) {
810 records.add(this);
811 }
812 }
813
814 /**
815 * Method to be called when a record will no longer be used. Calling this multiple times
816 * must have the same effect as calling it once.
817 */
818 void disposeLocked() {
819 ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
820 records.remove(this);
821 }
822
823 @Override
824 public String toString() {
825 return "UpdateRecord{"
826 + Integer.toHexString(System.identityHashCode(this))
827 + " " + mProvider + " " + mReceiver + "}";
828 }
829
830 void dump(PrintWriter pw, String prefix) {
831 pw.println(prefix + this);
832 pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
833 pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400834 pw.println(prefix + "mUid=" + mUid);
835 pw.println(prefix + "mLastFixBroadcast:");
836 mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
837 pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 }
839
840 /**
841 * Calls dispose().
842 */
843 @Override protected void finalize() {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400844 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800845 disposeLocked();
846 }
847 }
848 }
849
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400850 private Receiver getReceiver(ILocationListener listener) {
851 IBinder binder = listener.asBinder();
852 Receiver receiver = mReceivers.get(binder);
853 if (receiver == null) {
854 receiver = new Receiver(listener);
855 mReceivers.put(binder, receiver);
856
857 try {
858 if (receiver.isListener()) {
859 receiver.getListener().asBinder().linkToDeath(receiver, 0);
860 }
861 } catch (RemoteException e) {
862 Log.e(TAG, "linkToDeath failed:", e);
863 return null;
864 }
865 }
866 return receiver;
867 }
868
869 private Receiver getReceiver(PendingIntent intent) {
870 Receiver receiver = mReceivers.get(intent);
871 if (receiver == null) {
872 receiver = new Receiver(intent);
873 mReceivers.put(intent, receiver);
874 }
875 return receiver;
876 }
877
878 private boolean providerHasListener(String provider, int uid, Receiver excludedReceiver) {
879 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
880 if (records != null) {
881 for (int i = records.size() - 1; i >= 0; i--) {
882 UpdateRecord record = records.get(i);
883 if (record.mUid == uid && record.mReceiver != excludedReceiver) {
884 return true;
885 }
886 }
887 }
888 if (LocationManager.GPS_PROVIDER.equals(provider) ||
889 LocationManager.NETWORK_PROVIDER.equals(provider)) {
890 for (ProximityAlert alert : mProximityAlerts.values()) {
891 if (alert.mUid == uid) {
892 return true;
893 }
894 }
895 }
896 return false;
897 }
898
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 public void requestLocationUpdates(String provider,
900 long minTime, float minDistance, ILocationListener listener) {
901
902 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400903 synchronized (mLock) {
904 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 }
906 } catch (SecurityException se) {
907 throw se;
908 } catch (Exception e) {
909 Log.e(TAG, "requestUpdates got exception:", e);
910 }
911 }
912
913 public void requestLocationUpdatesPI(String provider,
914 long minTime, float minDistance, PendingIntent intent) {
915 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400916 synchronized (mLock) {
917 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 }
919 } catch (SecurityException se) {
920 throw se;
921 } catch (Exception e) {
922 Log.e(TAG, "requestUpdates got exception:", e);
923 }
924 }
925
926 private void requestLocationUpdatesLocked(String provider,
927 long minTime, float minDistance, Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700928 if (LOCAL_LOGV) {
929 Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800930 }
931
932 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
933 if (impl == null) {
934 throw new IllegalArgumentException("provider=" + provider);
935 }
936
937 checkPermissionsSafe(provider);
938
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939 // so wakelock calls will succeed
940 final int callingUid = Binder.getCallingUid();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400941 boolean newUid = !providerHasListener(provider, callingUid, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800942 long identity = Binder.clearCallingIdentity();
943 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400944 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
945 UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800946 if (oldRecord != null) {
947 oldRecord.disposeLocked();
948 }
949
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400950 if (newUid) {
951 impl.addListener(callingUid);
952 }
953
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
955 if (isProviderEnabled) {
956 long minTimeForProvider = getMinTimeLocked(provider);
957 impl.setMinTime(minTimeForProvider);
958 impl.enableLocationTracking(true);
Mike Lockwood61fc2862009-04-21 20:02:52 -0700959 updateWakelockStatusLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 } else {
961 try {
962 // Notify the listener that updates are currently disabled
963 if (receiver.isListener()) {
964 receiver.getListener().onProviderDisabled(provider);
965 }
966 } catch(RemoteException e) {
967 Log.w(TAG, "RemoteException calling onProviderDisabled on " +
968 receiver.getListener());
969 }
970 }
971 } finally {
972 Binder.restoreCallingIdentity(identity);
973 }
974 }
975
976 public void removeUpdates(ILocationListener listener) {
977 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400978 synchronized (mLock) {
979 removeUpdatesLocked(getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800980 }
981 } catch (SecurityException se) {
982 throw se;
983 } catch (Exception e) {
984 Log.e(TAG, "removeUpdates got exception:", e);
985 }
986 }
987
988 public void removeUpdatesPI(PendingIntent intent) {
989 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400990 synchronized (mLock) {
991 removeUpdatesLocked(getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800992 }
993 } catch (SecurityException se) {
994 throw se;
995 } catch (Exception e) {
996 Log.e(TAG, "removeUpdates got exception:", e);
997 }
998 }
999
1000 private void removeUpdatesLocked(Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001001 if (LOCAL_LOGV) {
1002 Log.v(TAG, "_removeUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001003 }
1004
1005 // so wakelock calls will succeed
1006 final int callingUid = Binder.getCallingUid();
1007 long identity = Binder.clearCallingIdentity();
1008 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001009 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1010 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001011 }
1012
1013 // Record which providers were associated with this listener
1014 HashSet<String> providers = new HashSet<String>();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001015 HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001016 if (oldRecords != null) {
1017 // Call dispose() on the obsolete update records.
1018 for (UpdateRecord record : oldRecords.values()) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001019 if (!providerHasListener(record.mProvider, callingUid, receiver)) {
1020 LocationProviderImpl impl =
1021 LocationProviderImpl.getProvider(record.mProvider);
1022 if (impl != null) {
1023 impl.removeListener(callingUid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024 }
1025 }
1026 record.disposeLocked();
1027 }
1028 // Accumulate providers
1029 providers.addAll(oldRecords.keySet());
1030 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001031
1032 // See if the providers associated with this listener have any
1033 // other listeners; if one does, inform it of the new smallest minTime
1034 // value; if one does not, disable location tracking for it
1035 for (String provider : providers) {
1036 // If provider is already disabled, don't need to do anything
1037 if (!isAllowedBySettingsLocked(provider)) {
1038 continue;
1039 }
1040
1041 boolean hasOtherListener = false;
1042 ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1043 if (recordsForProvider != null && recordsForProvider.size() > 0) {
1044 hasOtherListener = true;
1045 }
1046
1047 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1048 if (p != null) {
1049 if (hasOtherListener) {
1050 p.setMinTime(getMinTimeLocked(provider));
1051 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 p.enableLocationTracking(false);
1053 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054 }
1055 }
1056
Mike Lockwood61fc2862009-04-21 20:02:52 -07001057 updateWakelockStatusLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 } finally {
1059 Binder.restoreCallingIdentity(identity);
1060 }
1061 }
1062
1063 public boolean addGpsStatusListener(IGpsStatusListener listener) {
1064 if (mGpsLocationProvider == null) {
1065 return false;
1066 }
1067 if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1068 PackageManager.PERMISSION_GRANTED) {
1069 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1070 }
1071
1072 try {
1073 mGpsLocationProvider.addGpsStatusListener(listener);
1074 } catch (RemoteException e) {
1075 Log.w(TAG, "RemoteException in addGpsStatusListener");
1076 return false;
1077 }
1078 return true;
1079 }
1080
1081 public void removeGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001082 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001083 mGpsLocationProvider.removeGpsStatusListener(listener);
1084 }
1085 }
1086
1087 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1088 // first check for permission to the provider
1089 checkPermissionsSafe(provider);
1090 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1091 if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1092 != PackageManager.PERMISSION_GRANTED)) {
1093 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1094 }
1095
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001096 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
1098 if (provider == null) {
1099 return false;
1100 }
1101
1102 return impl.sendExtraCommand(command, extras);
1103 }
1104 }
1105
1106 class ProximityAlert {
1107 final int mUid;
1108 final double mLatitude;
1109 final double mLongitude;
1110 final float mRadius;
1111 final long mExpiration;
1112 final PendingIntent mIntent;
1113 final Location mLocation;
1114
1115 public ProximityAlert(int uid, double latitude, double longitude,
1116 float radius, long expiration, PendingIntent intent) {
1117 mUid = uid;
1118 mLatitude = latitude;
1119 mLongitude = longitude;
1120 mRadius = radius;
1121 mExpiration = expiration;
1122 mIntent = intent;
1123
1124 mLocation = new Location("");
1125 mLocation.setLatitude(latitude);
1126 mLocation.setLongitude(longitude);
1127 }
1128
1129 long getExpiration() {
1130 return mExpiration;
1131 }
1132
1133 PendingIntent getIntent() {
1134 return mIntent;
1135 }
1136
1137 boolean isInProximity(double latitude, double longitude) {
1138 Location loc = new Location("");
1139 loc.setLatitude(latitude);
1140 loc.setLongitude(longitude);
1141
1142 double radius = loc.distanceTo(mLocation);
1143 return radius <= mRadius;
1144 }
1145
1146 @Override
1147 public String toString() {
1148 return "ProximityAlert{"
1149 + Integer.toHexString(System.identityHashCode(this))
1150 + " uid " + mUid + mIntent + "}";
1151 }
1152
1153 void dump(PrintWriter pw, String prefix) {
1154 pw.println(prefix + this);
1155 pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
1156 pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
1157 pw.println(prefix + "mIntent=" + mIntent);
1158 pw.println(prefix + "mLocation:");
1159 mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
1160 }
1161 }
1162
1163 // Listener for receiving locations to trigger proximity alerts
1164 class ProximityListener extends ILocationListener.Stub {
1165
1166 boolean isGpsAvailable = false;
1167
1168 // Note: this is called with the lock held.
1169 public void onLocationChanged(Location loc) {
1170
1171 // If Gps is available, then ignore updates from NetworkLocationProvider
1172 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1173 isGpsAvailable = true;
1174 }
1175 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1176 return;
1177 }
1178
1179 // Process proximity alerts
1180 long now = System.currentTimeMillis();
1181 double latitude = loc.getLatitude();
1182 double longitude = loc.getLongitude();
1183 ArrayList<PendingIntent> intentsToRemove = null;
1184
1185 for (ProximityAlert alert : mProximityAlerts.values()) {
1186 PendingIntent intent = alert.getIntent();
1187 long expiration = alert.getExpiration();
1188
1189 if ((expiration == -1) || (now <= expiration)) {
1190 boolean entered = mProximitiesEntered.contains(alert);
1191 boolean inProximity =
1192 alert.isInProximity(latitude, longitude);
1193 if (!entered && inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001194 if (LOCAL_LOGV) {
1195 Log.v(TAG, "Entered alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196 }
1197 mProximitiesEntered.add(alert);
1198 Intent enteredIntent = new Intent();
1199 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1200 try {
1201 intent.send(mContext, 0, enteredIntent, null, null);
1202 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001203 if (LOCAL_LOGV) {
1204 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001205 }
1206 if (intentsToRemove == null) {
1207 intentsToRemove = new ArrayList<PendingIntent>();
1208 }
1209 intentsToRemove.add(intent);
1210 }
1211 } else if (entered && !inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001212 if (LOCAL_LOGV) {
1213 Log.v(TAG, "Exited alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214 }
1215 mProximitiesEntered.remove(alert);
1216 Intent exitedIntent = new Intent();
1217 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1218 try {
1219 intent.send(mContext, 0, exitedIntent, null, null);
1220 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001221 if (LOCAL_LOGV) {
1222 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001223 }
1224 if (intentsToRemove == null) {
1225 intentsToRemove = new ArrayList<PendingIntent>();
1226 }
1227 intentsToRemove.add(intent);
1228 }
1229 }
1230 } else {
1231 // Mark alert for expiration
The Android Open Source Project10592532009-03-18 17:39:46 -07001232 if (LOCAL_LOGV) {
1233 Log.v(TAG, "Expiring proximity alert: " + alert);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001234 }
1235 if (intentsToRemove == null) {
1236 intentsToRemove = new ArrayList<PendingIntent>();
1237 }
1238 intentsToRemove.add(alert.getIntent());
1239 }
1240 }
1241
1242 // Remove expired alerts
1243 if (intentsToRemove != null) {
1244 for (PendingIntent i : intentsToRemove) {
1245 mProximityAlerts.remove(i);
1246 ProximityAlert alert = mProximityAlerts.get(i);
1247 mProximitiesEntered.remove(alert);
1248 }
1249 }
1250
1251 }
1252
1253 // Note: this is called with the lock held.
1254 public void onProviderDisabled(String provider) {
1255 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1256 isGpsAvailable = false;
1257 }
1258 }
1259
1260 // Note: this is called with the lock held.
1261 public void onProviderEnabled(String provider) {
1262 // ignore
1263 }
1264
1265 // Note: this is called with the lock held.
1266 public void onStatusChanged(String provider, int status, Bundle extras) {
1267 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1268 (status != LocationProvider.AVAILABLE)) {
1269 isGpsAvailable = false;
1270 }
1271 }
1272 }
1273
1274 public void addProximityAlert(double latitude, double longitude,
1275 float radius, long expiration, PendingIntent intent) {
1276 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001277 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001278 addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
1279 }
1280 } catch (SecurityException se) {
1281 throw se;
1282 } catch (Exception e) {
1283 Log.e(TAG, "addProximityAlert got exception:", e);
1284 }
1285 }
1286
1287 private void addProximityAlertLocked(double latitude, double longitude,
1288 float radius, long expiration, PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001289 if (LOCAL_LOGV) {
1290 Log.v(TAG, "addProximityAlert: latitude = " + latitude +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001291 ", longitude = " + longitude +
1292 ", expiration = " + expiration +
1293 ", intent = " + intent);
1294 }
1295
1296 // Require ability to access all providers for now
1297 if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
1298 !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
1299 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1300 }
1301
1302 if (expiration != -1) {
1303 expiration += System.currentTimeMillis();
1304 }
1305 ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
1306 latitude, longitude, radius, expiration, intent);
1307 mProximityAlerts.put(intent, alert);
1308
1309 if (mProximityListener == null) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001310 mProximityListener = new Receiver(new ProximityListener());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001311
1312 LocationProvider provider = LocationProviderImpl.getProvider(
1313 LocationManager.GPS_PROVIDER);
1314 if (provider != null) {
1315 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1316 }
1317
1318 provider =
1319 LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER);
1320 if (provider != null) {
1321 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1322 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001323 }
1324 }
1325
1326 public void removeProximityAlert(PendingIntent intent) {
1327 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001328 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001329 removeProximityAlertLocked(intent);
1330 }
1331 } catch (SecurityException se) {
1332 throw se;
1333 } catch (Exception e) {
1334 Log.e(TAG, "removeProximityAlert got exception:", e);
1335 }
1336 }
1337
1338 private void removeProximityAlertLocked(PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001339 if (LOCAL_LOGV) {
1340 Log.v(TAG, "removeProximityAlert: intent = " + intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001341 }
1342
1343 mProximityAlerts.remove(intent);
1344 if (mProximityAlerts.size() == 0) {
1345 removeUpdatesLocked(mProximityListener);
1346 mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001347 }
1348 }
1349
1350 /**
1351 * @return null if the provider does not exits
1352 * @throw SecurityException if the provider is not allowed to be
1353 * accessed by the caller
1354 */
1355 public Bundle getProviderInfo(String provider) {
1356 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001357 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001358 return _getProviderInfoLocked(provider);
1359 }
1360 } catch (SecurityException se) {
1361 throw se;
1362 } catch (Exception e) {
1363 Log.e(TAG, "_getProviderInfo got exception:", e);
1364 return null;
1365 }
1366 }
1367
1368 private Bundle _getProviderInfoLocked(String provider) {
1369 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1370 if (p == null) {
1371 return null;
1372 }
1373
1374 checkPermissionsSafe(provider);
1375
1376 Bundle b = new Bundle();
1377 b.putBoolean("network", p.requiresNetwork());
1378 b.putBoolean("satellite", p.requiresSatellite());
1379 b.putBoolean("cell", p.requiresCell());
1380 b.putBoolean("cost", p.hasMonetaryCost());
1381 b.putBoolean("altitude", p.supportsAltitude());
1382 b.putBoolean("speed", p.supportsSpeed());
1383 b.putBoolean("bearing", p.supportsBearing());
1384 b.putInt("power", p.getPowerRequirement());
1385 b.putInt("accuracy", p.getAccuracy());
1386
1387 return b;
1388 }
1389
1390 public boolean isProviderEnabled(String provider) {
1391 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001392 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393 return _isProviderEnabledLocked(provider);
1394 }
1395 } catch (SecurityException se) {
1396 throw se;
1397 } catch (Exception e) {
1398 Log.e(TAG, "isProviderEnabled got exception:", e);
1399 return false;
1400 }
1401 }
1402
Mike Lockwood4e50b782009-04-03 08:24:43 -07001403 public void setLocation(Location location) {
1404 mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
1405 Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
1406 mLocationHandler.sendMessageAtFrontOfQueue(m);
1407 }
1408
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001409 private boolean _isProviderEnabledLocked(String provider) {
1410 checkPermissionsSafe(provider);
1411
1412 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1413 if (p == null) {
1414 throw new IllegalArgumentException("provider=" + provider);
1415 }
1416 return isAllowedBySettingsLocked(provider);
1417 }
1418
1419 public Location getLastKnownLocation(String provider) {
1420 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001421 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001422 return _getLastKnownLocationLocked(provider);
1423 }
1424 } catch (SecurityException se) {
1425 throw se;
1426 } catch (Exception e) {
1427 Log.e(TAG, "getLastKnownLocation got exception:", e);
1428 return null;
1429 }
1430 }
1431
1432 private Location _getLastKnownLocationLocked(String provider) {
1433 checkPermissionsSafe(provider);
1434
1435 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1436 if (p == null) {
1437 throw new IllegalArgumentException("provider=" + provider);
1438 }
1439
1440 if (!isAllowedBySettingsLocked(provider)) {
1441 return null;
1442 }
1443
1444 Location location = mLastKnownLocation.get(provider);
1445 if (location == null) {
1446 // Get the persistent last known location for the provider
1447 location = readLastKnownLocationLocked(provider);
1448 if (location != null) {
1449 mLastKnownLocation.put(provider, location);
1450 }
1451 }
1452
1453 return location;
1454 }
1455
1456 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1457 // Always broadcast the first update
1458 if (lastLoc == null) {
1459 return true;
1460 }
1461
1462 // Don't broadcast same location again regardless of condition
1463 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1464 if (loc.getTime() == lastLoc.getTime()) {
1465 return false;
1466 }
1467
1468 // Check whether sufficient distance has been traveled
1469 double minDistance = record.mMinDistance;
1470 if (minDistance > 0.0) {
1471 if (loc.distanceTo(lastLoc) <= minDistance) {
1472 return false;
1473 }
1474 }
1475
1476 return true;
1477 }
1478
Mike Lockwood4e50b782009-04-03 08:24:43 -07001479 private void handleLocationChangedLocked(Location location) {
1480 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001481 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1482 if (records == null || records.size() == 0) {
1483 return;
1484 }
1485
1486 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1487 if (p == null) {
1488 return;
1489 }
1490
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001491 // Update last known location for provider
Mike Lockwood4e50b782009-04-03 08:24:43 -07001492 Location lastLocation = mLastKnownLocation.get(provider);
1493 if (lastLocation == null) {
1494 mLastKnownLocation.put(provider, new Location(location));
1495 } else {
1496 lastLocation.set(location);
1497 }
1498 writeLastKnownLocationLocked(provider, location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001499
Mike Lockwoode932f7f2009-04-06 10:51:26 -07001500 if (LocationManager.NETWORK_PROVIDER.equals(p.getName())) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001501 mWakeLockNetworkReceived = true;
1502 } else if (p instanceof GpsLocationProvider) {
1503 // Gps location received signal is in NetworkStateBroadcastReceiver
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001504 }
1505
1506 // Fetch latest status update time
1507 long newStatusUpdateTime = p.getStatusUpdateTime();
1508
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001509 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001510 Bundle extras = new Bundle();
1511 int status = p.getStatus(extras);
1512
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001513 ArrayList<Receiver> deadReceivers = null;
1514
1515 // Broadcast location or status to all listeners
1516 final int N = records.size();
1517 for (int i=0; i<N; i++) {
1518 UpdateRecord r = records.get(i);
1519 Receiver receiver = r.mReceiver;
1520
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001521 Location lastLoc = r.mLastFixBroadcast;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001522 if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1523 if (lastLoc == null) {
1524 lastLoc = new Location(location);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001525 r.mLastFixBroadcast = lastLoc;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001526 } else {
1527 lastLoc.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001528 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001529 if (!receiver.callLocationChangedLocked(location)) {
1530 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1531 if (deadReceivers == null) {
1532 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001533 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001534 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001535 }
1536 }
1537
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001538 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1540 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1541
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001542 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001543 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1544 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1545 if (deadReceivers == null) {
1546 deadReceivers = new ArrayList<Receiver>();
1547 }
1548 if (!deadReceivers.contains(receiver)) {
1549 deadReceivers.add(receiver);
1550 }
1551 }
1552 }
1553 }
1554
1555 if (deadReceivers != null) {
1556 for (int i=deadReceivers.size()-1; i>=0; i--) {
1557 removeUpdatesLocked(deadReceivers.get(i));
1558 }
1559 }
1560 }
1561
1562 private class LocationWorkerHandler extends Handler {
1563
1564 @Override
1565 public void handleMessage(Message msg) {
1566 try {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001567 if (msg.what == MESSAGE_LOCATION_CHANGED) {
1568 // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001569
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001570 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001571 Location location = (Location) msg.obj;
Mike Lockwood98cb6672009-04-17 18:03:44 -04001572
1573 if (mCollector != null &&
1574 LocationManager.GPS_PROVIDER.equals(location.getProvider())) {
1575 try {
1576 mCollector.updateLocation(location);
1577 } catch (RemoteException e) {
1578 Log.w(TAG, "mCollector.updateLocation failed");
1579 }
1580 }
1581
Mike Lockwood4e50b782009-04-03 08:24:43 -07001582 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001583 if (!isAllowedBySettingsLocked(provider)) {
1584 return;
1585 }
1586
Mike Lockwood61fc2862009-04-21 20:02:52 -07001587 // Process the location fix if we're holding a wakelock
1588 if (mWakeLockAcquireTime != 0) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001589 handleLocationChangedLocked(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001590 }
1591
1592 if ((mWakeLockAcquireTime != 0) &&
1593 (SystemClock.elapsedRealtime() - mWakeLockAcquireTime
1594 > MAX_TIME_FOR_WAKE_LOCK)) {
1595
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001596 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1597
1598 log("LocationWorkerHandler: Exceeded max time for wake lock");
1599 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1600 sendMessageAtFrontOfQueue(m);
1601
1602 } else if (mWakeLockAcquireTime != 0 &&
1603 mWakeLockGpsReceived && mWakeLockNetworkReceived) {
1604
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001605 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1606
1607 log("LocationWorkerHandler: Locations received.");
1608 mWakeLockAcquireTime = 0;
1609 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1610 sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK);
1611 }
1612 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001613 } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) {
1614 log("LocationWorkerHandler: Release");
1615
1616 // Update wakelock status so the next alarm is set before releasing wakelock
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001617 synchronized (mLock) {
Mike Lockwood61fc2862009-04-21 20:02:52 -07001618 updateWakelockStatusLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001619 releaseWakeLockLocked();
1620 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001621 }
1622 } catch (Exception e) {
1623 // Log, don't crash!
1624 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1625 }
1626 }
1627 }
1628
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 private class PowerStateBroadcastReceiver extends BroadcastReceiver {
1630 @Override public void onReceive(Context context, Intent intent) {
1631 String action = intent.getAction();
1632
1633 if (action.equals(ALARM_INTENT)) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001634 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001635 log("PowerStateBroadcastReceiver: Alarm received");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001636 // Have to do this immediately, rather than posting a
1637 // message, so we execute our code while the system
1638 // is holding a wake lock until the alarm broadcast
1639 // is finished.
1640 acquireWakeLockLocked();
1641 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001642 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
1643 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001644 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001645 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1646 if (uid >= 0) {
1647 ArrayList<Receiver> removedRecs = null;
1648 for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
1649 for (int j=i.size()-1; j>=0; j--) {
1650 UpdateRecord ur = i.get(j);
1651 if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
1652 if (removedRecs == null) {
1653 removedRecs = new ArrayList<Receiver>();
1654 }
1655 if (!removedRecs.contains(ur.mReceiver)) {
1656 removedRecs.add(ur.mReceiver);
1657 }
1658 }
1659 }
1660 }
1661 ArrayList<ProximityAlert> removedAlerts = null;
1662 for (ProximityAlert i : mProximityAlerts.values()) {
1663 if (i.mUid == uid) {
1664 if (removedAlerts == null) {
1665 removedAlerts = new ArrayList<ProximityAlert>();
1666 }
1667 if (!removedAlerts.contains(i)) {
1668 removedAlerts.add(i);
1669 }
1670 }
1671 }
1672 if (removedRecs != null) {
1673 for (int i=removedRecs.size()-1; i>=0; i--) {
1674 removeUpdatesLocked(removedRecs.get(i));
1675 }
1676 }
1677 if (removedAlerts != null) {
1678 for (int i=removedAlerts.size()-1; i>=0; i--) {
1679 removeProximityAlertLocked(removedAlerts.get(i).mIntent);
1680 }
1681 }
1682 }
1683 }
1684 }
1685 }
1686 }
1687
1688 private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
1689 @Override public void onReceive(Context context, Intent intent) {
1690 String action = intent.getAction();
1691
Mike Lockwoodf113fbe2009-04-06 05:17:28 -07001692 if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001693 boolean noConnectivity =
1694 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1695 if (!noConnectivity) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001696 mNetworkState = LocationProvider.AVAILABLE;
1697 } else {
1698 mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001699 }
1700
1701 // Notify location providers of current network state
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001702 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001703 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1704 for (LocationProviderImpl provider : providers) {
1705 if (provider.requiresNetwork()) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001706 provider.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001707 }
1708 }
1709 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001710 } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
1711
1712 final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED,
1713 false);
1714
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001715 synchronized (mLock) {
Mike Lockwood98cb6672009-04-17 18:03:44 -04001716 if (!enabled) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001717 // When GPS is disabled, we are OK to release wake-lock
1718 mWakeLockGpsReceived = true;
1719 }
1720 }
1721 }
1722
1723 }
1724 }
1725
1726 // Wake locks
1727
Mike Lockwood61fc2862009-04-21 20:02:52 -07001728 private void updateWakelockStatusLocked() {
1729 log("updateWakelockStatus()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001730
Amith Yamasanie1ccba22009-04-02 11:40:25 -07001731 long callerId = Binder.clearCallingIdentity();
1732
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733 boolean needsLock = false;
1734 long minTime = Integer.MAX_VALUE;
1735
1736 if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) {
1737 needsLock = true;
1738 minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime);
1739 }
1740
1741 if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) {
1742 needsLock = true;
1743 minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001744 }
1745
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001746 PendingIntent sender =
1747 PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0);
1748
1749 // Cancel existing alarm
1750 log("Cancelling existing alarm");
1751 mAlarmManager.cancel(sender);
1752
Mike Lockwood61fc2862009-04-21 20:02:52 -07001753 if (needsLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001754 long now = SystemClock.elapsedRealtime();
1755 mAlarmManager.set(
1756 AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender);
1757 mAlarmInterval = minTime;
1758 log("Creating a new wakelock alarm with minTime = " + minTime);
1759 } else {
1760 log("No need for alarm");
1761 mAlarmInterval = -1;
1762
1763 // Clear out existing wakelocks
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001764 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1765 releaseWakeLockLocked();
1766 }
Amith Yamasanie1ccba22009-04-02 11:40:25 -07001767 Binder.restoreCallingIdentity(callerId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001768 }
1769
1770 private void acquireWakeLockLocked() {
1771 try {
1772 acquireWakeLockXLocked();
1773 } catch (Exception e) {
1774 // This is to catch a runtime exception thrown when we try to release an
1775 // already released lock.
1776 Log.e(TAG, "exception in acquireWakeLock()", e);
1777 }
1778 }
1779
1780 private void acquireWakeLockXLocked() {
1781 if (mWakeLock.isHeld()) {
1782 log("Must release wakelock before acquiring");
1783 mWakeLockAcquireTime = 0;
1784 mWakeLock.release();
1785 }
1786
1787 boolean networkActive = (mNetworkLocationProvider != null)
1788 && mNetworkLocationProvider.isLocationTracking();
1789 boolean gpsActive = (mGpsLocationProvider != null)
1790 && mGpsLocationProvider.isLocationTracking();
1791
1792 boolean needsLock = networkActive || gpsActive;
1793 if (!needsLock) {
1794 log("No need for Lock!");
1795 return;
1796 }
1797
1798 mWakeLockGpsReceived = !gpsActive;
1799 mWakeLockNetworkReceived = !networkActive;
1800
1801 // Acquire wake lock
1802 mWakeLock.acquire();
1803 mWakeLockAcquireTime = SystemClock.elapsedRealtime();
1804 log("Acquired wakelock");
1805
Mike Lockwood6de31542009-04-21 12:13:35 -07001806 if (mNetworkLocationProvider != null) {
1807 mNetworkLocationProvider.wakeLockAcquired();
1808 }
1809 if (mGpsLocationProvider != null) {
1810 mGpsLocationProvider.wakeLockAcquired();
1811 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001812 }
1813
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001814 private void releaseWakeLockLocked() {
1815 try {
1816 releaseWakeLockXLocked();
1817 } catch (Exception e) {
1818 // This is to catch a runtime exception thrown when we try to release an
1819 // already released lock.
1820 Log.e(TAG, "exception in releaseWakeLock()", e);
1821 }
1822 }
1823
1824 private void releaseWakeLockXLocked() {
Mike Lockwood6de31542009-04-21 12:13:35 -07001825 if (mNetworkLocationProvider != null) {
1826 mNetworkLocationProvider.wakeLockReleased();
1827 }
1828 if (mGpsLocationProvider != null) {
1829 mGpsLocationProvider.wakeLockReleased();
1830 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001831
1832 // Release wake lock
1833 mWakeLockAcquireTime = 0;
1834 if (mWakeLock.isHeld()) {
1835 log("Released wakelock");
1836 mWakeLock.release();
1837 } else {
1838 log("Can't release wakelock again!");
1839 }
1840 }
1841
1842 // Geocoder
1843
1844 public String getFromLocation(double latitude, double longitude, int maxResults,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001845 String language, String country, String variant, String appName, List<Address> addrs) {
1846 if (mGeocodeProvider != null) {
1847 try {
1848 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, language, country,
1849 variant, appName, addrs);
1850 } catch (RemoteException e) {
1851 Log.e(TAG, "getFromLocation failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001852 }
1853 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001854 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001855 }
1856
Mike Lockwooda55c3212009-04-15 11:10:11 -04001857
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001858 public String getFromLocationName(String locationName,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001859 double lowerLeftLatitude, double lowerLeftLongitude,
1860 double upperRightLatitude, double upperRightLongitude, int maxResults,
1861 String language, String country, String variant, String appName, List<Address> addrs) {
1862
1863 if (mGeocodeProvider != null) {
1864 try {
1865 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1866 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1867 maxResults, language, country, variant, appName, addrs);
1868 } catch (RemoteException e) {
1869 Log.e(TAG, "getFromLocationName failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001870 }
1871 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001872 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001873 }
1874
1875 // Mock Providers
1876
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001877 private void checkMockPermissionsSafe() {
1878 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1879 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1880 if (!allowMocks) {
1881 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1882 }
1883
1884 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1885 PackageManager.PERMISSION_GRANTED) {
1886 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1887 }
1888 }
1889
1890 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1891 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1892 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1893 checkMockPermissionsSafe();
1894
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001895 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001896 MockProvider provider = new MockProvider(name, this,
1897 requiresNetwork, requiresSatellite,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001898 requiresCell, hasMonetaryCost, supportsAltitude,
1899 supportsSpeed, supportsBearing, powerRequirement, accuracy);
1900 if (LocationProviderImpl.getProvider(name) != null) {
1901 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1902 }
1903 LocationProviderImpl.addProvider(provider);
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001904 mMockProviders.put(name, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001905 updateProvidersLocked();
1906 }
1907 }
1908
1909 public void removeTestProvider(String provider) {
1910 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001911 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001912 MockProvider mockProvider = mMockProviders.get(provider);
1913 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001914 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1915 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001916 LocationProviderImpl.removeProvider(mockProvider);
1917 mMockProviders.remove(mockProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001918 updateProvidersLocked();
1919 }
1920 }
1921
1922 public void setTestProviderLocation(String provider, Location loc) {
1923 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001924 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001925 MockProvider mockProvider = mMockProviders.get(provider);
1926 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001927 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1928 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001929 mockProvider.setLocation(loc);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001930 }
1931 }
1932
1933 public void clearTestProviderLocation(String provider) {
1934 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001935 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001936 MockProvider mockProvider = mMockProviders.get(provider);
1937 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001938 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1939 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001940 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001941 }
1942 }
1943
1944 public void setTestProviderEnabled(String provider, boolean enabled) {
1945 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001946 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001947 MockProvider mockProvider = mMockProviders.get(provider);
1948 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001949 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1950 }
1951 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001952 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001953 mEnabledProviders.add(provider);
1954 mDisabledProviders.remove(provider);
1955 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001956 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001957 mEnabledProviders.remove(provider);
1958 mDisabledProviders.add(provider);
1959 }
1960 updateProvidersLocked();
1961 }
1962 }
1963
1964 public void clearTestProviderEnabled(String provider) {
1965 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001966 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001967 MockProvider mockProvider = mMockProviders.get(provider);
1968 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001969 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1970 }
1971 mEnabledProviders.remove(provider);
1972 mDisabledProviders.remove(provider);
1973 updateProvidersLocked();
1974 }
1975 }
1976
1977 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1978 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001979 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001980 MockProvider mockProvider = mMockProviders.get(provider);
1981 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001982 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1983 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001984 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001985 }
1986 }
1987
1988 public void clearTestProviderStatus(String provider) {
1989 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001990 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001991 MockProvider mockProvider = mMockProviders.get(provider);
1992 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001993 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1994 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001995 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001996 }
1997 }
1998
1999 private void log(String log) {
2000 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2001 Log.d(TAG, log);
2002 }
2003 }
2004
2005 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2006 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2007 != PackageManager.PERMISSION_GRANTED) {
2008 pw.println("Permission Denial: can't dump AlarmManager from from pid="
2009 + Binder.getCallingPid()
2010 + ", uid=" + Binder.getCallingUid());
2011 return;
2012 }
2013
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002014 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002015 pw.println("Current Location Manager state:");
2016 pw.println(" sProvidersLoaded=" + sProvidersLoaded);
2017 pw.println(" mGpsLocationProvider=" + mGpsLocationProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002018 pw.println(" mNetworkLocationProvider=" + mNetworkLocationProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002019 pw.println(" mCollector=" + mCollector);
2020 pw.println(" mAlarmInterval=" + mAlarmInterval
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002021 + " mWakeLockAcquireTime=" + mWakeLockAcquireTime);
2022 pw.println(" mWakeLockGpsReceived=" + mWakeLockGpsReceived
2023 + " mWakeLockNetworkReceived=" + mWakeLockNetworkReceived);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002024 pw.println(" Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002025 int N = mReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002026 for (int i=0; i<N; i++) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002027 pw.println(" " + mReceivers.get(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002028 }
2029 pw.println(" Location Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002030 for (Receiver i : mReceivers.values()) {
2031 pw.println(" " + i + ":");
2032 for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002033 pw.println(" " + j.getKey() + ":");
2034 j.getValue().dump(pw, " ");
2035 }
2036 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002037 pw.println(" Records by Provider:");
2038 for (Map.Entry<String, ArrayList<UpdateRecord>> i
2039 : mRecordsByProvider.entrySet()) {
2040 pw.println(" " + i.getKey() + ":");
2041 for (UpdateRecord j : i.getValue()) {
2042 pw.println(" " + j + ":");
2043 j.dump(pw, " ");
2044 }
2045 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002046 pw.println(" Last Known Locations:");
2047 for (Map.Entry<String, Location> i
2048 : mLastKnownLocation.entrySet()) {
2049 pw.println(" " + i.getKey() + ":");
2050 i.getValue().dump(new PrintWriterPrinter(pw), " ");
2051 }
2052 if (mProximityAlerts.size() > 0) {
2053 pw.println(" Proximity Alerts:");
2054 for (Map.Entry<PendingIntent, ProximityAlert> i
2055 : mProximityAlerts.entrySet()) {
2056 pw.println(" " + i.getKey() + ":");
2057 i.getValue().dump(pw, " ");
2058 }
2059 }
2060 if (mProximitiesEntered.size() > 0) {
2061 pw.println(" Proximities Entered:");
2062 for (ProximityAlert i : mProximitiesEntered) {
2063 pw.println(" " + i + ":");
2064 i.dump(pw, " ");
2065 }
2066 }
2067 pw.println(" mProximityListener=" + mProximityListener);
2068 if (mEnabledProviders.size() > 0) {
2069 pw.println(" Enabled Providers:");
2070 for (String i : mEnabledProviders) {
2071 pw.println(" " + i);
2072 }
2073
2074 }
2075 if (mDisabledProviders.size() > 0) {
2076 pw.println(" Disabled Providers:");
2077 for (String i : mDisabledProviders) {
2078 pw.println(" " + i);
2079 }
2080
2081 }
2082 if (mMockProviders.size() > 0) {
2083 pw.println(" Mock Providers:");
2084 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002085 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002086 }
2087 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002088 }
2089 }
2090}