blob: ed5ae68dd035cdb83c8380d0e1b0053f033e156b [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 private static final int MESSAGE_ACQUIRE_WAKE_LOCK = 2;
134 private static final int MESSAGE_RELEASE_WAKE_LOCK = 3;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135
136 // Alarm manager and wakelock variables
137 private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT";
138 private final static String WAKELOCK_KEY = "LocationManagerService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 private AlarmManager mAlarmManager;
140 private long mAlarmInterval = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 private PowerManager.WakeLock mWakeLock = null;
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
151 /**
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400152 * Object used internally for synchronization
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 */
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400154 private final Object mLock = new Object();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155
156 /**
157 * Mapping from provider name to all its UpdateRecords
158 */
159 private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
160 new HashMap<String,ArrayList<UpdateRecord>>();
161
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 // Proximity listeners
163 private Receiver mProximityListener = null;
164 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
165 new HashMap<PendingIntent,ProximityAlert>();
166 private HashSet<ProximityAlert> mProximitiesEntered =
167 new HashSet<ProximityAlert>();
168
169 // Last known location for each provider
170 private HashMap<String,Location> mLastKnownLocation =
171 new HashMap<String,Location>();
172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 // Location collector
174 private ILocationCollector mCollector;
175
The Android Open Source Project4df24232009-03-05 14:34:35 -0800176 private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800177
Mike Lockwood9637d472009-04-02 21:41:57 -0700178 // for Settings change notification
179 private ContentQueryMap mSettings;
180
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 /**
182 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
183 * location updates.
184 */
185 private final class Receiver implements IBinder.DeathRecipient {
186 final ILocationListener mListener;
187 final PendingIntent mPendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 final Object mKey;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400189 final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400191 Receiver(ILocationListener listener) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 mListener = listener;
193 mPendingIntent = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 mKey = listener.asBinder();
195 }
196
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400197 Receiver(PendingIntent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 mPendingIntent = intent;
199 mListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 mKey = intent;
201 }
202
203 @Override
204 public boolean equals(Object otherObj) {
205 if (otherObj instanceof Receiver) {
206 return mKey.equals(
207 ((Receiver)otherObj).mKey);
208 }
209 return false;
210 }
211
212 @Override
213 public int hashCode() {
214 return mKey.hashCode();
215 }
216
217
218 @Override
219 public String toString() {
220 if (mListener != null) {
221 return "Receiver{"
222 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400223 + " Listener " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 } else {
225 return "Receiver{"
226 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400227 + " Intent " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 }
229 }
230
231 public boolean isListener() {
232 return mListener != null;
233 }
234
235 public boolean isPendingIntent() {
236 return mPendingIntent != null;
237 }
238
239 public ILocationListener getListener() {
240 if (mListener != null) {
241 return mListener;
242 }
243 throw new IllegalStateException("Request for non-existent listener");
244 }
245
246 public PendingIntent getPendingIntent() {
247 if (mPendingIntent != null) {
248 return mPendingIntent;
249 }
250 throw new IllegalStateException("Request for non-existent intent");
251 }
252
253 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
254 if (mListener != null) {
255 try {
256 mListener.onStatusChanged(provider, status, extras);
257 } catch (RemoteException e) {
258 return false;
259 }
260 } else {
261 Intent statusChanged = new Intent();
262 statusChanged.putExtras(extras);
263 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
264 try {
265 mPendingIntent.send(mContext, 0, statusChanged, null, null);
266 } catch (PendingIntent.CanceledException e) {
267 return false;
268 }
269 }
270 return true;
271 }
272
273 public boolean callLocationChangedLocked(Location location) {
274 if (mListener != null) {
275 try {
276 mListener.onLocationChanged(location);
277 } catch (RemoteException e) {
278 return false;
279 }
280 } else {
281 Intent locationChanged = new Intent();
282 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
283 try {
284 mPendingIntent.send(mContext, 0, locationChanged, null, null);
285 } catch (PendingIntent.CanceledException e) {
286 return false;
287 }
288 }
289 return true;
290 }
291
292 public void binderDied() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700293 if (LOCAL_LOGV) {
294 Log.v(TAG, "Location listener died");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 }
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400296 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 removeUpdatesLocked(this);
298 }
299 }
300 }
301
Mike Lockwood9637d472009-04-02 21:41:57 -0700302 private final class SettingsObserver implements Observer {
303 public void update(Observable o, Object arg) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400304 synchronized (mLock) {
Mike Lockwood9637d472009-04-02 21:41:57 -0700305 updateProvidersLocked();
306 }
307 }
308 }
309
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 private Location readLastKnownLocationLocked(String provider) {
311 Location location = null;
312 String s = null;
313 try {
314 File f = new File(LocationManager.SYSTEM_DIR + "/location."
315 + provider);
316 if (!f.exists()) {
317 return null;
318 }
319 BufferedReader reader = new BufferedReader(new FileReader(f), 256);
320 s = reader.readLine();
321 } catch (IOException e) {
322 Log.w(TAG, "Unable to read last known location", e);
323 }
324
325 if (s == null) {
326 return null;
327 }
328 try {
329 String[] tokens = PATTERN_COMMA.split(s);
330 int idx = 0;
331 long time = Long.parseLong(tokens[idx++]);
332 double latitude = Double.parseDouble(tokens[idx++]);
333 double longitude = Double.parseDouble(tokens[idx++]);
334 double altitude = Double.parseDouble(tokens[idx++]);
335 float bearing = Float.parseFloat(tokens[idx++]);
336 float speed = Float.parseFloat(tokens[idx++]);
337
338 location = new Location(provider);
339 location.setTime(time);
340 location.setLatitude(latitude);
341 location.setLongitude(longitude);
342 location.setAltitude(altitude);
343 location.setBearing(bearing);
344 location.setSpeed(speed);
345 } catch (NumberFormatException nfe) {
346 Log.e(TAG, "NumberFormatException reading last known location", nfe);
347 return null;
348 }
349
350 return location;
351 }
352
353 private void writeLastKnownLocationLocked(String provider,
354 Location location) {
355 long now = SystemClock.elapsedRealtime();
356 Long last = mLastWriteTime.get(provider);
357 if ((last != null)
358 && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
359 return;
360 }
361 mLastWriteTime.put(provider, now);
362
363 StringBuilder sb = new StringBuilder(100);
364 sb.append(location.getTime());
365 sb.append(',');
366 sb.append(location.getLatitude());
367 sb.append(',');
368 sb.append(location.getLongitude());
369 sb.append(',');
370 sb.append(location.getAltitude());
371 sb.append(',');
372 sb.append(location.getBearing());
373 sb.append(',');
374 sb.append(location.getSpeed());
375
376 FileWriter writer = null;
377 try {
378 File d = new File(LocationManager.SYSTEM_DIR);
379 if (!d.exists()) {
380 if (!d.mkdirs()) {
381 Log.w(TAG, "Unable to create directory to write location");
382 return;
383 }
384 }
385 File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
386 writer = new FileWriter(f);
387 writer.write(sb.toString());
388 } catch (IOException e) {
389 Log.w(TAG, "Unable to write location", e);
390 } finally {
391 if (writer != null) {
392 try {
393 writer.close();
394 } catch (IOException e) {
395 Log.w(TAG, "Exception closing file", e);
396 }
397 }
398 }
399 }
400
401 /**
402 * Load providers from /data/location/<provider_name>/
403 * class
404 * kml
405 * nmea
406 * track
407 * location
408 * properties
409 */
410 private void loadProviders() {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400411 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 if (sProvidersLoaded) {
413 return;
414 }
415
416 // Load providers
417 loadProvidersLocked();
418 sProvidersLoaded = true;
419 }
420 }
421
422 private void loadProvidersLocked() {
423 try {
424 _loadProvidersLocked();
425 } catch (Exception e) {
426 Log.e(TAG, "Exception loading providers:", e);
427 }
428 }
429
430 private void _loadProvidersLocked() {
431 // Attempt to load "real" providers first
432 if (GpsLocationProvider.isSupported()) {
433 // Create a gps location provider
Mike Lockwood4e50b782009-04-03 08:24:43 -0700434 mGpsLocationProvider = new GpsLocationProvider(mContext, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 LocationProviderImpl.addProvider(mGpsLocationProvider);
436 }
437
438 // Load fake providers if real providers are not available
439 File f = new File(LocationManager.PROVIDER_DIR);
440 if (f.isDirectory()) {
441 File[] subdirs = f.listFiles();
442 for (int i = 0; i < subdirs.length; i++) {
443 if (!subdirs[i].isDirectory()) {
444 continue;
445 }
446
447 String name = subdirs[i].getName();
448
The Android Open Source Project10592532009-03-18 17:39:46 -0700449 if (LOCAL_LOGV) {
450 Log.v(TAG, "Found dir " + subdirs[i].getAbsolutePath());
451 Log.v(TAG, "name = " + name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 }
453
454 // Don't create a fake provider if a real provider exists
455 if (LocationProviderImpl.getProvider(name) == null) {
456 LocationProviderImpl provider = null;
457 try {
458 File classFile = new File(subdirs[i], "class");
459 // Look for a 'class' file
460 provider = LocationProviderImpl.loadFromClass(classFile);
461
462 // Look for an 'kml', 'nmea', or 'track' file
463 if (provider == null) {
464 // Load properties from 'properties' file, if present
465 File propertiesFile = new File(subdirs[i], "properties");
466
467 if (propertiesFile.exists()) {
Mike Lockwood4e50b782009-04-03 08:24:43 -0700468 provider = new TrackProvider(name, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 ((TrackProvider)provider).readProperties(propertiesFile);
470
471 File kmlFile = new File(subdirs[i], "kml");
472 if (kmlFile.exists()) {
473 ((TrackProvider) provider).readKml(kmlFile);
474 } else {
475 File nmeaFile = new File(subdirs[i], "nmea");
476 if (nmeaFile.exists()) {
477 ((TrackProvider) provider).readNmea(name, nmeaFile);
478 } else {
479 File trackFile = new File(subdirs[i], "track");
480 if (trackFile.exists()) {
481 ((TrackProvider) provider).readTrack(trackFile);
482 }
483 }
484 }
485 }
486 }
487 if (provider != null) {
488 LocationProviderImpl.addProvider(provider);
489 }
490 // Grab the initial location of a TrackProvider and
491 // store it as the last known location for that provider
492 if (provider instanceof TrackProvider) {
493 TrackProvider tp = (TrackProvider) provider;
494 mLastKnownLocation.put(tp.getName(), tp.getInitialLocation());
495 }
496 } catch (Exception e) {
497 Log.e(TAG, "Exception loading provder " + name, e);
498 }
499 }
500 }
501 }
502
503 updateProvidersLocked();
504 }
505
506 /**
507 * @param context the context that the LocationManagerService runs in
508 */
509 public LocationManagerService(Context context) {
510 super();
511 mContext = context;
512 mLocationHandler = new LocationWorkerHandler();
513
The Android Open Source Project10592532009-03-18 17:39:46 -0700514 if (LOCAL_LOGV) {
515 Log.v(TAG, "Constructed LocationManager Service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 }
517
518 // Alarm manager, needs to be done before calling loadProviders() below
519 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
520
521 // Create a wake lock, needs to be done before calling loadProviders() below
522 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
523 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
524
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 // Load providers
526 loadProviders();
527
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 // Register for Network (Wifi or Mobile) updates
529 NetworkStateBroadcastReceiver networkReceiver = new NetworkStateBroadcastReceiver();
530 IntentFilter networkIntentFilter = new IntentFilter();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
532 networkIntentFilter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
533 context.registerReceiver(networkReceiver, networkIntentFilter);
534
535 // Register for power updates
536 PowerStateBroadcastReceiver powerStateReceiver = new PowerStateBroadcastReceiver();
537 IntentFilter intentFilter = new IntentFilter();
538 intentFilter.addAction(ALARM_INTENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
540 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
541 context.registerReceiver(powerStateReceiver, intentFilter);
542
Mike Lockwood9637d472009-04-02 21:41:57 -0700543 // listen for settings changes
544 ContentResolver resolver = mContext.getContentResolver();
545 Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
546 "(" + Settings.System.NAME + "=?)",
547 new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
548 null);
549 mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler);
550 SettingsObserver settingsObserver = new SettingsObserver();
551 mSettings.addObserver(settingsObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 }
553
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700554 public void setNetworkLocationProvider(ILocationProvider provider) {
555 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
556 throw new SecurityException(
557 "Installing location providers outside of the system is not supported");
558 }
559
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400560 synchronized (mLock) {
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700561 mNetworkLocationProvider =
562 new LocationProviderProxy(LocationManager.NETWORK_PROVIDER, this, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563 LocationProviderImpl.addProvider(mNetworkLocationProvider);
564 updateProvidersLocked();
The Android Open Source Project4df24232009-03-05 14:34:35 -0800565
566 // notify NetworkLocationProvider of any events it might have missed
Mike Lockwoodf113fbe2009-04-06 05:17:28 -0700567 mNetworkLocationProvider.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 }
569 }
570
571 public void setLocationCollector(ILocationCollector collector) {
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700572 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
573 throw new SecurityException(
574 "Installing location collectors outside of the system is not supported");
575 }
576
Mike Lockwood98cb6672009-04-17 18:03:44 -0400577 mCollector = collector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 }
579
Mike Lockwooda55c3212009-04-15 11:10:11 -0400580 public void setGeocodeProvider(IGeocodeProvider provider) {
581 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
582 throw new SecurityException(
583 "Installing location providers outside of the system is not supported");
584 }
585
586 mGeocodeProvider = provider;
587 }
588
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 private boolean isAllowedBySettingsLocked(String provider) {
590 if (mEnabledProviders.contains(provider)) {
591 return true;
592 }
593 if (mDisabledProviders.contains(provider)) {
594 return false;
595 }
596 // Use system settings
597 ContentResolver resolver = mContext.getContentResolver();
598 String allowedProviders = Settings.Secure.getString(resolver,
599 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
600
601 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
602 }
603
604 private void checkPermissionsSafe(String provider) {
605 if (LocationManager.GPS_PROVIDER.equals(provider)
606 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
607 != PackageManager.PERMISSION_GRANTED)) {
608 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
609 }
610 if (LocationManager.NETWORK_PROVIDER.equals(provider)
611 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
612 != PackageManager.PERMISSION_GRANTED)
613 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
614 != PackageManager.PERMISSION_GRANTED)) {
615 throw new SecurityException(
616 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
617 }
618 }
619
620 private boolean isAllowedProviderSafe(String provider) {
621 if (LocationManager.GPS_PROVIDER.equals(provider)
622 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
623 != PackageManager.PERMISSION_GRANTED)) {
624 return false;
625 }
626 if (LocationManager.NETWORK_PROVIDER.equals(provider)
627 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
628 != PackageManager.PERMISSION_GRANTED)
629 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
630 != PackageManager.PERMISSION_GRANTED)) {
631 return false;
632 }
633
634 return true;
635 }
636
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 public List<String> getAllProviders() {
638 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400639 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800640 return _getAllProvidersLocked();
641 }
642 } catch (SecurityException se) {
643 throw se;
644 } catch (Exception e) {
645 Log.e(TAG, "getAllProviders got exception:", e);
646 return null;
647 }
648 }
649
650 private List<String> _getAllProvidersLocked() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700651 if (LOCAL_LOGV) {
652 Log.v(TAG, "getAllProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 }
654 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
655 ArrayList<String> out = new ArrayList<String>(providers.size());
656
657 for (LocationProviderImpl p : providers) {
658 out.add(p.getName());
659 }
660 return out;
661 }
662
663 public List<String> getProviders(boolean enabledOnly) {
664 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400665 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 return _getProvidersLocked(enabledOnly);
667 }
668 } catch (SecurityException se) {
669 throw se;
670 } catch (Exception e) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700671 Log.e(TAG, "getProviders got exception:", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 return null;
673 }
674 }
675
676 private List<String> _getProvidersLocked(boolean enabledOnly) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700677 if (LOCAL_LOGV) {
678 Log.v(TAG, "getProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 }
680 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
681 ArrayList<String> out = new ArrayList<String>();
682
683 for (LocationProviderImpl p : providers) {
684 String name = p.getName();
685 if (isAllowedProviderSafe(name)) {
686 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
687 continue;
688 }
689 out.add(name);
690 }
691 }
692 return out;
693 }
694
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 private void updateProvidersLocked() {
696 for (LocationProviderImpl p : LocationProviderImpl.getProviders()) {
697 boolean isEnabled = p.isEnabled();
698 String name = p.getName();
699 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
700
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 if (isEnabled && !shouldBeEnabled) {
702 updateProviderListenersLocked(name, false);
703 } else if (!isEnabled && shouldBeEnabled) {
704 updateProviderListenersLocked(name, true);
705 }
706
707 }
708 }
709
710 private void updateProviderListenersLocked(String provider, boolean enabled) {
711 int listeners = 0;
712
713 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
714 if (p == null) {
715 return;
716 }
717
718 ArrayList<Receiver> deadReceivers = null;
719
720 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
721 if (records != null) {
722 final int N = records.size();
723 for (int i=0; i<N; i++) {
724 UpdateRecord record = records.get(i);
725 // Sends a notification message to the receiver
726 try {
727 Receiver receiver = record.mReceiver;
728 if (receiver.isListener()) {
729 if (enabled) {
730 receiver.getListener().onProviderEnabled(provider);
731 } else {
732 receiver.getListener().onProviderDisabled(provider);
733 }
734 } else {
735 Intent providerIntent = new Intent();
736 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
737 try {
738 receiver.getPendingIntent().send(mContext, 0,
739 providerIntent, null, null);
740 } catch (PendingIntent.CanceledException e) {
741 if (deadReceivers == null) {
742 deadReceivers = new ArrayList<Receiver>();
743 deadReceivers.add(receiver);
744 }
745 }
746 }
747 } catch (RemoteException e) {
748 // The death link will clean this up.
749 }
750 listeners++;
751 }
752 }
753
754 if (deadReceivers != null) {
755 for (int i=deadReceivers.size()-1; i>=0; i--) {
756 removeUpdatesLocked(deadReceivers.get(i));
757 }
758 }
759
760 if (enabled) {
761 p.enable();
762 if (listeners > 0) {
763 p.setMinTime(getMinTimeLocked(provider));
764 p.enableLocationTracking(true);
Mike Lockwood61fc2862009-04-21 20:02:52 -0700765 updateWakelockStatusLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766 }
767 } else {
768 p.enableLocationTracking(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 p.disable();
Mike Lockwood61fc2862009-04-21 20:02:52 -0700770 updateWakelockStatusLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 }
773
774 private long getMinTimeLocked(String provider) {
775 long minTime = Long.MAX_VALUE;
776 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
777 if (records != null) {
778 for (int i=records.size()-1; i>=0; i--) {
779 minTime = Math.min(minTime, records.get(i).mMinTime);
780 }
781 }
782 return minTime;
783 }
784
785 private class UpdateRecord {
786 final String mProvider;
787 final Receiver mReceiver;
788 final long mMinTime;
789 final float mMinDistance;
790 final int mUid;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400791 Location mLastFixBroadcast;
792 long mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793
794 /**
795 * Note: must be constructed with lock held.
796 */
797 UpdateRecord(String provider, long minTime, float minDistance,
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400798 Receiver receiver, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 mProvider = provider;
800 mReceiver = receiver;
801 mMinTime = minTime;
802 mMinDistance = minDistance;
803 mUid = uid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804
805 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
806 if (records == null) {
807 records = new ArrayList<UpdateRecord>();
808 mRecordsByProvider.put(provider, records);
809 }
810 if (!records.contains(this)) {
811 records.add(this);
812 }
813 }
814
815 /**
816 * Method to be called when a record will no longer be used. Calling this multiple times
817 * must have the same effect as calling it once.
818 */
819 void disposeLocked() {
820 ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
821 records.remove(this);
822 }
823
824 @Override
825 public String toString() {
826 return "UpdateRecord{"
827 + Integer.toHexString(System.identityHashCode(this))
828 + " " + mProvider + " " + mReceiver + "}";
829 }
830
831 void dump(PrintWriter pw, String prefix) {
832 pw.println(prefix + this);
833 pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
834 pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400835 pw.println(prefix + "mUid=" + mUid);
836 pw.println(prefix + "mLastFixBroadcast:");
837 mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
838 pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839 }
840
841 /**
842 * Calls dispose().
843 */
844 @Override protected void finalize() {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400845 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800846 disposeLocked();
847 }
848 }
849 }
850
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400851 private Receiver getReceiver(ILocationListener listener) {
852 IBinder binder = listener.asBinder();
853 Receiver receiver = mReceivers.get(binder);
854 if (receiver == null) {
855 receiver = new Receiver(listener);
856 mReceivers.put(binder, receiver);
857
858 try {
859 if (receiver.isListener()) {
860 receiver.getListener().asBinder().linkToDeath(receiver, 0);
861 }
862 } catch (RemoteException e) {
863 Log.e(TAG, "linkToDeath failed:", e);
864 return null;
865 }
866 }
867 return receiver;
868 }
869
870 private Receiver getReceiver(PendingIntent intent) {
871 Receiver receiver = mReceivers.get(intent);
872 if (receiver == null) {
873 receiver = new Receiver(intent);
874 mReceivers.put(intent, receiver);
875 }
876 return receiver;
877 }
878
879 private boolean providerHasListener(String provider, int uid, Receiver excludedReceiver) {
880 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
881 if (records != null) {
882 for (int i = records.size() - 1; i >= 0; i--) {
883 UpdateRecord record = records.get(i);
884 if (record.mUid == uid && record.mReceiver != excludedReceiver) {
885 return true;
886 }
887 }
888 }
889 if (LocationManager.GPS_PROVIDER.equals(provider) ||
890 LocationManager.NETWORK_PROVIDER.equals(provider)) {
891 for (ProximityAlert alert : mProximityAlerts.values()) {
892 if (alert.mUid == uid) {
893 return true;
894 }
895 }
896 }
897 return false;
898 }
899
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 public void requestLocationUpdates(String provider,
901 long minTime, float minDistance, ILocationListener listener) {
902
903 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400904 synchronized (mLock) {
905 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 }
907 } catch (SecurityException se) {
908 throw se;
909 } catch (Exception e) {
910 Log.e(TAG, "requestUpdates got exception:", e);
911 }
912 }
913
914 public void requestLocationUpdatesPI(String provider,
915 long minTime, float minDistance, PendingIntent intent) {
916 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400917 synchronized (mLock) {
918 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 }
920 } catch (SecurityException se) {
921 throw se;
922 } catch (Exception e) {
923 Log.e(TAG, "requestUpdates got exception:", e);
924 }
925 }
926
927 private void requestLocationUpdatesLocked(String provider,
928 long minTime, float minDistance, Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700929 if (LOCAL_LOGV) {
930 Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 }
932
933 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
934 if (impl == null) {
935 throw new IllegalArgumentException("provider=" + provider);
936 }
937
938 checkPermissionsSafe(provider);
939
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 // so wakelock calls will succeed
941 final int callingUid = Binder.getCallingUid();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400942 boolean newUid = !providerHasListener(provider, callingUid, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800943 long identity = Binder.clearCallingIdentity();
944 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400945 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
946 UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 if (oldRecord != null) {
948 oldRecord.disposeLocked();
949 }
950
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400951 if (newUid) {
952 impl.addListener(callingUid);
953 }
954
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
956 if (isProviderEnabled) {
957 long minTimeForProvider = getMinTimeLocked(provider);
958 impl.setMinTime(minTimeForProvider);
959 impl.enableLocationTracking(true);
Mike Lockwood61fc2862009-04-21 20:02:52 -0700960 updateWakelockStatusLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800961 } else {
962 try {
963 // Notify the listener that updates are currently disabled
964 if (receiver.isListener()) {
965 receiver.getListener().onProviderDisabled(provider);
966 }
967 } catch(RemoteException e) {
968 Log.w(TAG, "RemoteException calling onProviderDisabled on " +
969 receiver.getListener());
970 }
971 }
972 } finally {
973 Binder.restoreCallingIdentity(identity);
974 }
975 }
976
977 public void removeUpdates(ILocationListener listener) {
978 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400979 synchronized (mLock) {
980 removeUpdatesLocked(getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981 }
982 } catch (SecurityException se) {
983 throw se;
984 } catch (Exception e) {
985 Log.e(TAG, "removeUpdates got exception:", e);
986 }
987 }
988
989 public void removeUpdatesPI(PendingIntent intent) {
990 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400991 synchronized (mLock) {
992 removeUpdatesLocked(getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993 }
994 } catch (SecurityException se) {
995 throw se;
996 } catch (Exception e) {
997 Log.e(TAG, "removeUpdates got exception:", e);
998 }
999 }
1000
1001 private void removeUpdatesLocked(Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001002 if (LOCAL_LOGV) {
1003 Log.v(TAG, "_removeUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 }
1005
1006 // so wakelock calls will succeed
1007 final int callingUid = Binder.getCallingUid();
1008 long identity = Binder.clearCallingIdentity();
1009 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001010 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1011 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012 }
1013
1014 // Record which providers were associated with this listener
1015 HashSet<String> providers = new HashSet<String>();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001016 HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 if (oldRecords != null) {
1018 // Call dispose() on the obsolete update records.
1019 for (UpdateRecord record : oldRecords.values()) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001020 if (!providerHasListener(record.mProvider, callingUid, receiver)) {
1021 LocationProviderImpl impl =
1022 LocationProviderImpl.getProvider(record.mProvider);
1023 if (impl != null) {
1024 impl.removeListener(callingUid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001025 }
1026 }
1027 record.disposeLocked();
1028 }
1029 // Accumulate providers
1030 providers.addAll(oldRecords.keySet());
1031 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032
1033 // See if the providers associated with this listener have any
1034 // other listeners; if one does, inform it of the new smallest minTime
1035 // value; if one does not, disable location tracking for it
1036 for (String provider : providers) {
1037 // If provider is already disabled, don't need to do anything
1038 if (!isAllowedBySettingsLocked(provider)) {
1039 continue;
1040 }
1041
1042 boolean hasOtherListener = false;
1043 ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1044 if (recordsForProvider != null && recordsForProvider.size() > 0) {
1045 hasOtherListener = true;
1046 }
1047
1048 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1049 if (p != null) {
1050 if (hasOtherListener) {
1051 p.setMinTime(getMinTimeLocked(provider));
1052 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001053 p.enableLocationTracking(false);
1054 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 }
1056 }
1057
Mike Lockwood61fc2862009-04-21 20:02:52 -07001058 updateWakelockStatusLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 } finally {
1060 Binder.restoreCallingIdentity(identity);
1061 }
1062 }
1063
1064 public boolean addGpsStatusListener(IGpsStatusListener listener) {
1065 if (mGpsLocationProvider == null) {
1066 return false;
1067 }
1068 if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1069 PackageManager.PERMISSION_GRANTED) {
1070 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1071 }
1072
1073 try {
1074 mGpsLocationProvider.addGpsStatusListener(listener);
1075 } catch (RemoteException e) {
1076 Log.w(TAG, "RemoteException in addGpsStatusListener");
1077 return false;
1078 }
1079 return true;
1080 }
1081
1082 public void removeGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001083 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 mGpsLocationProvider.removeGpsStatusListener(listener);
1085 }
1086 }
1087
1088 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1089 // first check for permission to the provider
1090 checkPermissionsSafe(provider);
1091 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1092 if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1093 != PackageManager.PERMISSION_GRANTED)) {
1094 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1095 }
1096
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001097 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001098 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
1099 if (provider == null) {
1100 return false;
1101 }
1102
1103 return impl.sendExtraCommand(command, extras);
1104 }
1105 }
1106
1107 class ProximityAlert {
1108 final int mUid;
1109 final double mLatitude;
1110 final double mLongitude;
1111 final float mRadius;
1112 final long mExpiration;
1113 final PendingIntent mIntent;
1114 final Location mLocation;
1115
1116 public ProximityAlert(int uid, double latitude, double longitude,
1117 float radius, long expiration, PendingIntent intent) {
1118 mUid = uid;
1119 mLatitude = latitude;
1120 mLongitude = longitude;
1121 mRadius = radius;
1122 mExpiration = expiration;
1123 mIntent = intent;
1124
1125 mLocation = new Location("");
1126 mLocation.setLatitude(latitude);
1127 mLocation.setLongitude(longitude);
1128 }
1129
1130 long getExpiration() {
1131 return mExpiration;
1132 }
1133
1134 PendingIntent getIntent() {
1135 return mIntent;
1136 }
1137
1138 boolean isInProximity(double latitude, double longitude) {
1139 Location loc = new Location("");
1140 loc.setLatitude(latitude);
1141 loc.setLongitude(longitude);
1142
1143 double radius = loc.distanceTo(mLocation);
1144 return radius <= mRadius;
1145 }
1146
1147 @Override
1148 public String toString() {
1149 return "ProximityAlert{"
1150 + Integer.toHexString(System.identityHashCode(this))
1151 + " uid " + mUid + mIntent + "}";
1152 }
1153
1154 void dump(PrintWriter pw, String prefix) {
1155 pw.println(prefix + this);
1156 pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
1157 pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
1158 pw.println(prefix + "mIntent=" + mIntent);
1159 pw.println(prefix + "mLocation:");
1160 mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
1161 }
1162 }
1163
1164 // Listener for receiving locations to trigger proximity alerts
1165 class ProximityListener extends ILocationListener.Stub {
1166
1167 boolean isGpsAvailable = false;
1168
1169 // Note: this is called with the lock held.
1170 public void onLocationChanged(Location loc) {
1171
1172 // If Gps is available, then ignore updates from NetworkLocationProvider
1173 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1174 isGpsAvailable = true;
1175 }
1176 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1177 return;
1178 }
1179
1180 // Process proximity alerts
1181 long now = System.currentTimeMillis();
1182 double latitude = loc.getLatitude();
1183 double longitude = loc.getLongitude();
1184 ArrayList<PendingIntent> intentsToRemove = null;
1185
1186 for (ProximityAlert alert : mProximityAlerts.values()) {
1187 PendingIntent intent = alert.getIntent();
1188 long expiration = alert.getExpiration();
1189
1190 if ((expiration == -1) || (now <= expiration)) {
1191 boolean entered = mProximitiesEntered.contains(alert);
1192 boolean inProximity =
1193 alert.isInProximity(latitude, longitude);
1194 if (!entered && inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001195 if (LOCAL_LOGV) {
1196 Log.v(TAG, "Entered alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001197 }
1198 mProximitiesEntered.add(alert);
1199 Intent enteredIntent = new Intent();
1200 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1201 try {
1202 intent.send(mContext, 0, enteredIntent, null, null);
1203 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001204 if (LOCAL_LOGV) {
1205 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001206 }
1207 if (intentsToRemove == null) {
1208 intentsToRemove = new ArrayList<PendingIntent>();
1209 }
1210 intentsToRemove.add(intent);
1211 }
1212 } else if (entered && !inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001213 if (LOCAL_LOGV) {
1214 Log.v(TAG, "Exited alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001215 }
1216 mProximitiesEntered.remove(alert);
1217 Intent exitedIntent = new Intent();
1218 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1219 try {
1220 intent.send(mContext, 0, exitedIntent, null, null);
1221 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001222 if (LOCAL_LOGV) {
1223 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001224 }
1225 if (intentsToRemove == null) {
1226 intentsToRemove = new ArrayList<PendingIntent>();
1227 }
1228 intentsToRemove.add(intent);
1229 }
1230 }
1231 } else {
1232 // Mark alert for expiration
The Android Open Source Project10592532009-03-18 17:39:46 -07001233 if (LOCAL_LOGV) {
1234 Log.v(TAG, "Expiring proximity alert: " + alert);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001235 }
1236 if (intentsToRemove == null) {
1237 intentsToRemove = new ArrayList<PendingIntent>();
1238 }
1239 intentsToRemove.add(alert.getIntent());
1240 }
1241 }
1242
1243 // Remove expired alerts
1244 if (intentsToRemove != null) {
1245 for (PendingIntent i : intentsToRemove) {
1246 mProximityAlerts.remove(i);
1247 ProximityAlert alert = mProximityAlerts.get(i);
1248 mProximitiesEntered.remove(alert);
1249 }
1250 }
1251
1252 }
1253
1254 // Note: this is called with the lock held.
1255 public void onProviderDisabled(String provider) {
1256 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1257 isGpsAvailable = false;
1258 }
1259 }
1260
1261 // Note: this is called with the lock held.
1262 public void onProviderEnabled(String provider) {
1263 // ignore
1264 }
1265
1266 // Note: this is called with the lock held.
1267 public void onStatusChanged(String provider, int status, Bundle extras) {
1268 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1269 (status != LocationProvider.AVAILABLE)) {
1270 isGpsAvailable = false;
1271 }
1272 }
1273 }
1274
1275 public void addProximityAlert(double latitude, double longitude,
1276 float radius, long expiration, PendingIntent intent) {
1277 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001278 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001279 addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
1280 }
1281 } catch (SecurityException se) {
1282 throw se;
1283 } catch (Exception e) {
1284 Log.e(TAG, "addProximityAlert got exception:", e);
1285 }
1286 }
1287
1288 private void addProximityAlertLocked(double latitude, double longitude,
1289 float radius, long expiration, PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001290 if (LOCAL_LOGV) {
1291 Log.v(TAG, "addProximityAlert: latitude = " + latitude +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 ", longitude = " + longitude +
1293 ", expiration = " + expiration +
1294 ", intent = " + intent);
1295 }
1296
1297 // Require ability to access all providers for now
1298 if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
1299 !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
1300 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1301 }
1302
1303 if (expiration != -1) {
1304 expiration += System.currentTimeMillis();
1305 }
1306 ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
1307 latitude, longitude, radius, expiration, intent);
1308 mProximityAlerts.put(intent, alert);
1309
1310 if (mProximityListener == null) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001311 mProximityListener = new Receiver(new ProximityListener());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312
1313 LocationProvider provider = LocationProviderImpl.getProvider(
1314 LocationManager.GPS_PROVIDER);
1315 if (provider != null) {
1316 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1317 }
1318
1319 provider =
1320 LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER);
1321 if (provider != null) {
1322 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1323 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324 }
1325 }
1326
1327 public void removeProximityAlert(PendingIntent intent) {
1328 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001329 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001330 removeProximityAlertLocked(intent);
1331 }
1332 } catch (SecurityException se) {
1333 throw se;
1334 } catch (Exception e) {
1335 Log.e(TAG, "removeProximityAlert got exception:", e);
1336 }
1337 }
1338
1339 private void removeProximityAlertLocked(PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001340 if (LOCAL_LOGV) {
1341 Log.v(TAG, "removeProximityAlert: intent = " + intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001342 }
1343
1344 mProximityAlerts.remove(intent);
1345 if (mProximityAlerts.size() == 0) {
1346 removeUpdatesLocked(mProximityListener);
1347 mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001348 }
1349 }
1350
1351 /**
1352 * @return null if the provider does not exits
1353 * @throw SecurityException if the provider is not allowed to be
1354 * accessed by the caller
1355 */
1356 public Bundle getProviderInfo(String provider) {
1357 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001358 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001359 return _getProviderInfoLocked(provider);
1360 }
1361 } catch (SecurityException se) {
1362 throw se;
1363 } catch (Exception e) {
1364 Log.e(TAG, "_getProviderInfo got exception:", e);
1365 return null;
1366 }
1367 }
1368
1369 private Bundle _getProviderInfoLocked(String provider) {
1370 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1371 if (p == null) {
1372 return null;
1373 }
1374
1375 checkPermissionsSafe(provider);
1376
1377 Bundle b = new Bundle();
1378 b.putBoolean("network", p.requiresNetwork());
1379 b.putBoolean("satellite", p.requiresSatellite());
1380 b.putBoolean("cell", p.requiresCell());
1381 b.putBoolean("cost", p.hasMonetaryCost());
1382 b.putBoolean("altitude", p.supportsAltitude());
1383 b.putBoolean("speed", p.supportsSpeed());
1384 b.putBoolean("bearing", p.supportsBearing());
1385 b.putInt("power", p.getPowerRequirement());
1386 b.putInt("accuracy", p.getAccuracy());
1387
1388 return b;
1389 }
1390
1391 public boolean isProviderEnabled(String provider) {
1392 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001393 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001394 return _isProviderEnabledLocked(provider);
1395 }
1396 } catch (SecurityException se) {
1397 throw se;
1398 } catch (Exception e) {
1399 Log.e(TAG, "isProviderEnabled got exception:", e);
1400 return false;
1401 }
1402 }
1403
Mike Lockwood4e50b782009-04-03 08:24:43 -07001404 public void setLocation(Location location) {
1405 mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
1406 Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
1407 mLocationHandler.sendMessageAtFrontOfQueue(m);
1408 }
1409
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001410 private boolean _isProviderEnabledLocked(String provider) {
1411 checkPermissionsSafe(provider);
1412
1413 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1414 if (p == null) {
1415 throw new IllegalArgumentException("provider=" + provider);
1416 }
1417 return isAllowedBySettingsLocked(provider);
1418 }
1419
1420 public Location getLastKnownLocation(String provider) {
1421 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001422 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001423 return _getLastKnownLocationLocked(provider);
1424 }
1425 } catch (SecurityException se) {
1426 throw se;
1427 } catch (Exception e) {
1428 Log.e(TAG, "getLastKnownLocation got exception:", e);
1429 return null;
1430 }
1431 }
1432
1433 private Location _getLastKnownLocationLocked(String provider) {
1434 checkPermissionsSafe(provider);
1435
1436 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1437 if (p == null) {
1438 throw new IllegalArgumentException("provider=" + provider);
1439 }
1440
1441 if (!isAllowedBySettingsLocked(provider)) {
1442 return null;
1443 }
1444
1445 Location location = mLastKnownLocation.get(provider);
1446 if (location == null) {
1447 // Get the persistent last known location for the provider
1448 location = readLastKnownLocationLocked(provider);
1449 if (location != null) {
1450 mLastKnownLocation.put(provider, location);
1451 }
1452 }
1453
1454 return location;
1455 }
1456
1457 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1458 // Always broadcast the first update
1459 if (lastLoc == null) {
1460 return true;
1461 }
1462
1463 // Don't broadcast same location again regardless of condition
1464 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1465 if (loc.getTime() == lastLoc.getTime()) {
1466 return false;
1467 }
1468
1469 // Check whether sufficient distance has been traveled
1470 double minDistance = record.mMinDistance;
1471 if (minDistance > 0.0) {
1472 if (loc.distanceTo(lastLoc) <= minDistance) {
1473 return false;
1474 }
1475 }
1476
1477 return true;
1478 }
1479
Mike Lockwood4e50b782009-04-03 08:24:43 -07001480 private void handleLocationChangedLocked(Location location) {
1481 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001482 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1483 if (records == null || records.size() == 0) {
1484 return;
1485 }
1486
1487 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1488 if (p == null) {
1489 return;
1490 }
1491
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001492 // Update last known location for provider
Mike Lockwood4e50b782009-04-03 08:24:43 -07001493 Location lastLocation = mLastKnownLocation.get(provider);
1494 if (lastLocation == null) {
1495 mLastKnownLocation.put(provider, new Location(location));
1496 } else {
1497 lastLocation.set(location);
1498 }
1499 writeLastKnownLocationLocked(provider, location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001500
Mike Lockwoode932f7f2009-04-06 10:51:26 -07001501 if (LocationManager.NETWORK_PROVIDER.equals(p.getName())) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001502 mWakeLockNetworkReceived = true;
1503 } else if (p instanceof GpsLocationProvider) {
1504 // Gps location received signal is in NetworkStateBroadcastReceiver
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001505 }
1506
1507 // Fetch latest status update time
1508 long newStatusUpdateTime = p.getStatusUpdateTime();
1509
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001510 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001511 Bundle extras = new Bundle();
1512 int status = p.getStatus(extras);
1513
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001514 ArrayList<Receiver> deadReceivers = null;
1515
1516 // Broadcast location or status to all listeners
1517 final int N = records.size();
1518 for (int i=0; i<N; i++) {
1519 UpdateRecord r = records.get(i);
1520 Receiver receiver = r.mReceiver;
1521
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001522 Location lastLoc = r.mLastFixBroadcast;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001523 if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1524 if (lastLoc == null) {
1525 lastLoc = new Location(location);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001526 r.mLastFixBroadcast = lastLoc;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001527 } else {
1528 lastLoc.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001529 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001530 if (!receiver.callLocationChangedLocked(location)) {
1531 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1532 if (deadReceivers == null) {
1533 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001534 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001535 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001536 }
1537 }
1538
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001539 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001540 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1541 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1542
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001543 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001544 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1545 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1546 if (deadReceivers == null) {
1547 deadReceivers = new ArrayList<Receiver>();
1548 }
1549 if (!deadReceivers.contains(receiver)) {
1550 deadReceivers.add(receiver);
1551 }
1552 }
1553 }
1554 }
1555
1556 if (deadReceivers != null) {
1557 for (int i=deadReceivers.size()-1; i>=0; i--) {
1558 removeUpdatesLocked(deadReceivers.get(i));
1559 }
1560 }
1561 }
1562
1563 private class LocationWorkerHandler extends Handler {
1564
1565 @Override
1566 public void handleMessage(Message msg) {
1567 try {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001568 if (msg.what == MESSAGE_LOCATION_CHANGED) {
1569 // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001570
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001571 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001572 Location location = (Location) msg.obj;
Mike Lockwood98cb6672009-04-17 18:03:44 -04001573
1574 if (mCollector != null &&
1575 LocationManager.GPS_PROVIDER.equals(location.getProvider())) {
1576 try {
1577 mCollector.updateLocation(location);
1578 } catch (RemoteException e) {
1579 Log.w(TAG, "mCollector.updateLocation failed");
1580 }
1581 }
1582
Mike Lockwood4e50b782009-04-03 08:24:43 -07001583 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001584 if (!isAllowedBySettingsLocked(provider)) {
1585 return;
1586 }
1587
Mike Lockwood61fc2862009-04-21 20:02:52 -07001588 // Process the location fix if we're holding a wakelock
1589 if (mWakeLockAcquireTime != 0) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001590 handleLocationChangedLocked(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001591 }
1592
1593 if ((mWakeLockAcquireTime != 0) &&
1594 (SystemClock.elapsedRealtime() - mWakeLockAcquireTime
1595 > MAX_TIME_FOR_WAKE_LOCK)) {
1596
1597 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1598 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1599
1600 log("LocationWorkerHandler: Exceeded max time for wake lock");
1601 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1602 sendMessageAtFrontOfQueue(m);
1603
1604 } else if (mWakeLockAcquireTime != 0 &&
1605 mWakeLockGpsReceived && mWakeLockNetworkReceived) {
1606
1607 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1608 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1609
1610 log("LocationWorkerHandler: Locations received.");
1611 mWakeLockAcquireTime = 0;
1612 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1613 sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK);
1614 }
1615 }
1616
1617 } else if (msg.what == MESSAGE_ACQUIRE_WAKE_LOCK) {
1618 log("LocationWorkerHandler: Acquire");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001619 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001620 acquireWakeLockLocked();
1621 }
1622 } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) {
1623 log("LocationWorkerHandler: Release");
1624
1625 // Update wakelock status so the next alarm is set before releasing wakelock
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001626 synchronized (mLock) {
Mike Lockwood61fc2862009-04-21 20:02:52 -07001627 updateWakelockStatusLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001628 releaseWakeLockLocked();
1629 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001630 }
1631 } catch (Exception e) {
1632 // Log, don't crash!
1633 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1634 }
1635 }
1636 }
1637
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001638 private class PowerStateBroadcastReceiver extends BroadcastReceiver {
1639 @Override public void onReceive(Context context, Intent intent) {
1640 String action = intent.getAction();
1641
1642 if (action.equals(ALARM_INTENT)) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001643 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001644 log("PowerStateBroadcastReceiver: Alarm received");
1645 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1646 // Have to do this immediately, rather than posting a
1647 // message, so we execute our code while the system
1648 // is holding a wake lock until the alarm broadcast
1649 // is finished.
1650 acquireWakeLockLocked();
1651 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001652 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
1653 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001654 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001655 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1656 if (uid >= 0) {
1657 ArrayList<Receiver> removedRecs = null;
1658 for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
1659 for (int j=i.size()-1; j>=0; j--) {
1660 UpdateRecord ur = i.get(j);
1661 if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
1662 if (removedRecs == null) {
1663 removedRecs = new ArrayList<Receiver>();
1664 }
1665 if (!removedRecs.contains(ur.mReceiver)) {
1666 removedRecs.add(ur.mReceiver);
1667 }
1668 }
1669 }
1670 }
1671 ArrayList<ProximityAlert> removedAlerts = null;
1672 for (ProximityAlert i : mProximityAlerts.values()) {
1673 if (i.mUid == uid) {
1674 if (removedAlerts == null) {
1675 removedAlerts = new ArrayList<ProximityAlert>();
1676 }
1677 if (!removedAlerts.contains(i)) {
1678 removedAlerts.add(i);
1679 }
1680 }
1681 }
1682 if (removedRecs != null) {
1683 for (int i=removedRecs.size()-1; i>=0; i--) {
1684 removeUpdatesLocked(removedRecs.get(i));
1685 }
1686 }
1687 if (removedAlerts != null) {
1688 for (int i=removedAlerts.size()-1; i>=0; i--) {
1689 removeProximityAlertLocked(removedAlerts.get(i).mIntent);
1690 }
1691 }
1692 }
1693 }
1694 }
1695 }
1696 }
1697
1698 private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
1699 @Override public void onReceive(Context context, Intent intent) {
1700 String action = intent.getAction();
1701
Mike Lockwoodf113fbe2009-04-06 05:17:28 -07001702 if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001703 boolean noConnectivity =
1704 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1705 if (!noConnectivity) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001706 mNetworkState = LocationProvider.AVAILABLE;
1707 } else {
1708 mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001709 }
1710
1711 // Notify location providers of current network state
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001712 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001713 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1714 for (LocationProviderImpl provider : providers) {
1715 if (provider.requiresNetwork()) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001716 provider.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001717 }
1718 }
1719 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001720 } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
1721
1722 final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED,
1723 false);
1724
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001725 synchronized (mLock) {
Mike Lockwood98cb6672009-04-17 18:03:44 -04001726 if (!enabled) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001727 // When GPS is disabled, we are OK to release wake-lock
1728 mWakeLockGpsReceived = true;
1729 }
1730 }
1731 }
1732
1733 }
1734 }
1735
1736 // Wake locks
1737
Mike Lockwood61fc2862009-04-21 20:02:52 -07001738 private void updateWakelockStatusLocked() {
1739 log("updateWakelockStatus()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001740
Amith Yamasanie1ccba22009-04-02 11:40:25 -07001741 long callerId = Binder.clearCallingIdentity();
1742
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001743 boolean needsLock = false;
1744 long minTime = Integer.MAX_VALUE;
1745
1746 if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) {
1747 needsLock = true;
1748 minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime);
1749 }
1750
1751 if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) {
1752 needsLock = true;
1753 minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001754 }
1755
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001756 PendingIntent sender =
1757 PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0);
1758
1759 // Cancel existing alarm
1760 log("Cancelling existing alarm");
1761 mAlarmManager.cancel(sender);
1762
Mike Lockwood61fc2862009-04-21 20:02:52 -07001763 if (needsLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001764 long now = SystemClock.elapsedRealtime();
1765 mAlarmManager.set(
1766 AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender);
1767 mAlarmInterval = minTime;
1768 log("Creating a new wakelock alarm with minTime = " + minTime);
1769 } else {
1770 log("No need for alarm");
1771 mAlarmInterval = -1;
1772
1773 // Clear out existing wakelocks
1774 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1775 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1776 releaseWakeLockLocked();
1777 }
Amith Yamasanie1ccba22009-04-02 11:40:25 -07001778 Binder.restoreCallingIdentity(callerId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001779 }
1780
1781 private void acquireWakeLockLocked() {
1782 try {
1783 acquireWakeLockXLocked();
1784 } catch (Exception e) {
1785 // This is to catch a runtime exception thrown when we try to release an
1786 // already released lock.
1787 Log.e(TAG, "exception in acquireWakeLock()", e);
1788 }
1789 }
1790
1791 private void acquireWakeLockXLocked() {
1792 if (mWakeLock.isHeld()) {
1793 log("Must release wakelock before acquiring");
1794 mWakeLockAcquireTime = 0;
1795 mWakeLock.release();
1796 }
1797
1798 boolean networkActive = (mNetworkLocationProvider != null)
1799 && mNetworkLocationProvider.isLocationTracking();
1800 boolean gpsActive = (mGpsLocationProvider != null)
1801 && mGpsLocationProvider.isLocationTracking();
1802
1803 boolean needsLock = networkActive || gpsActive;
1804 if (!needsLock) {
1805 log("No need for Lock!");
1806 return;
1807 }
1808
1809 mWakeLockGpsReceived = !gpsActive;
1810 mWakeLockNetworkReceived = !networkActive;
1811
1812 // Acquire wake lock
1813 mWakeLock.acquire();
1814 mWakeLockAcquireTime = SystemClock.elapsedRealtime();
1815 log("Acquired wakelock");
1816
Mike Lockwood6de31542009-04-21 12:13:35 -07001817 if (mNetworkLocationProvider != null) {
1818 mNetworkLocationProvider.wakeLockAcquired();
1819 }
1820 if (mGpsLocationProvider != null) {
1821 mGpsLocationProvider.wakeLockAcquired();
1822 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001823 }
1824
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001825 private void releaseWakeLockLocked() {
1826 try {
1827 releaseWakeLockXLocked();
1828 } catch (Exception e) {
1829 // This is to catch a runtime exception thrown when we try to release an
1830 // already released lock.
1831 Log.e(TAG, "exception in releaseWakeLock()", e);
1832 }
1833 }
1834
1835 private void releaseWakeLockXLocked() {
Mike Lockwood6de31542009-04-21 12:13:35 -07001836 if (mNetworkLocationProvider != null) {
1837 mNetworkLocationProvider.wakeLockReleased();
1838 }
1839 if (mGpsLocationProvider != null) {
1840 mGpsLocationProvider.wakeLockReleased();
1841 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001842
1843 // Release wake lock
1844 mWakeLockAcquireTime = 0;
1845 if (mWakeLock.isHeld()) {
1846 log("Released wakelock");
1847 mWakeLock.release();
1848 } else {
1849 log("Can't release wakelock again!");
1850 }
1851 }
1852
1853 // Geocoder
1854
1855 public String getFromLocation(double latitude, double longitude, int maxResults,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001856 String language, String country, String variant, String appName, List<Address> addrs) {
1857 if (mGeocodeProvider != null) {
1858 try {
1859 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, language, country,
1860 variant, appName, addrs);
1861 } catch (RemoteException e) {
1862 Log.e(TAG, "getFromLocation failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001863 }
1864 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001865 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001866 }
1867
Mike Lockwooda55c3212009-04-15 11:10:11 -04001868
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001869 public String getFromLocationName(String locationName,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001870 double lowerLeftLatitude, double lowerLeftLongitude,
1871 double upperRightLatitude, double upperRightLongitude, int maxResults,
1872 String language, String country, String variant, String appName, List<Address> addrs) {
1873
1874 if (mGeocodeProvider != null) {
1875 try {
1876 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1877 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1878 maxResults, language, country, variant, appName, addrs);
1879 } catch (RemoteException e) {
1880 Log.e(TAG, "getFromLocationName failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001881 }
1882 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001883 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001884 }
1885
1886 // Mock Providers
1887
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001888 private void checkMockPermissionsSafe() {
1889 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1890 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1891 if (!allowMocks) {
1892 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1893 }
1894
1895 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1896 PackageManager.PERMISSION_GRANTED) {
1897 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1898 }
1899 }
1900
1901 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1902 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1903 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1904 checkMockPermissionsSafe();
1905
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001906 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001907 MockProvider provider = new MockProvider(name, this,
1908 requiresNetwork, requiresSatellite,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001909 requiresCell, hasMonetaryCost, supportsAltitude,
1910 supportsSpeed, supportsBearing, powerRequirement, accuracy);
1911 if (LocationProviderImpl.getProvider(name) != null) {
1912 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1913 }
1914 LocationProviderImpl.addProvider(provider);
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001915 mMockProviders.put(name, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001916 updateProvidersLocked();
1917 }
1918 }
1919
1920 public void removeTestProvider(String provider) {
1921 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001922 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001923 MockProvider mockProvider = mMockProviders.get(provider);
1924 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001925 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1926 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001927 LocationProviderImpl.removeProvider(mockProvider);
1928 mMockProviders.remove(mockProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001929 updateProvidersLocked();
1930 }
1931 }
1932
1933 public void setTestProviderLocation(String provider, Location loc) {
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.setLocation(loc);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001941 }
1942 }
1943
1944 public void clearTestProviderLocation(String provider) {
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 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001951 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001952 }
1953 }
1954
1955 public void setTestProviderEnabled(String provider, boolean enabled) {
1956 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001957 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001958 MockProvider mockProvider = mMockProviders.get(provider);
1959 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001960 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1961 }
1962 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001963 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001964 mEnabledProviders.add(provider);
1965 mDisabledProviders.remove(provider);
1966 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001967 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001968 mEnabledProviders.remove(provider);
1969 mDisabledProviders.add(provider);
1970 }
1971 updateProvidersLocked();
1972 }
1973 }
1974
1975 public void clearTestProviderEnabled(String provider) {
1976 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001977 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001978 MockProvider mockProvider = mMockProviders.get(provider);
1979 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001980 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1981 }
1982 mEnabledProviders.remove(provider);
1983 mDisabledProviders.remove(provider);
1984 updateProvidersLocked();
1985 }
1986 }
1987
1988 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
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.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001996 }
1997 }
1998
1999 public void clearTestProviderStatus(String provider) {
2000 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002001 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002002 MockProvider mockProvider = mMockProviders.get(provider);
2003 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002004 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2005 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002006 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002007 }
2008 }
2009
2010 private void log(String log) {
2011 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2012 Log.d(TAG, log);
2013 }
2014 }
2015
2016 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2017 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2018 != PackageManager.PERMISSION_GRANTED) {
2019 pw.println("Permission Denial: can't dump AlarmManager from from pid="
2020 + Binder.getCallingPid()
2021 + ", uid=" + Binder.getCallingUid());
2022 return;
2023 }
2024
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002025 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002026 pw.println("Current Location Manager state:");
2027 pw.println(" sProvidersLoaded=" + sProvidersLoaded);
2028 pw.println(" mGpsLocationProvider=" + mGpsLocationProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002029 pw.println(" mNetworkLocationProvider=" + mNetworkLocationProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002030 pw.println(" mCollector=" + mCollector);
2031 pw.println(" mAlarmInterval=" + mAlarmInterval
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002032 + " mWakeLockAcquireTime=" + mWakeLockAcquireTime);
2033 pw.println(" mWakeLockGpsReceived=" + mWakeLockGpsReceived
2034 + " mWakeLockNetworkReceived=" + mWakeLockNetworkReceived);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002035 pw.println(" Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002036 int N = mReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002037 for (int i=0; i<N; i++) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002038 pw.println(" " + mReceivers.get(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002039 }
2040 pw.println(" Location Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002041 for (Receiver i : mReceivers.values()) {
2042 pw.println(" " + i + ":");
2043 for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002044 pw.println(" " + j.getKey() + ":");
2045 j.getValue().dump(pw, " ");
2046 }
2047 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002048 pw.println(" Records by Provider:");
2049 for (Map.Entry<String, ArrayList<UpdateRecord>> i
2050 : mRecordsByProvider.entrySet()) {
2051 pw.println(" " + i.getKey() + ":");
2052 for (UpdateRecord j : i.getValue()) {
2053 pw.println(" " + j + ":");
2054 j.dump(pw, " ");
2055 }
2056 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002057 pw.println(" Last Known Locations:");
2058 for (Map.Entry<String, Location> i
2059 : mLastKnownLocation.entrySet()) {
2060 pw.println(" " + i.getKey() + ":");
2061 i.getValue().dump(new PrintWriterPrinter(pw), " ");
2062 }
2063 if (mProximityAlerts.size() > 0) {
2064 pw.println(" Proximity Alerts:");
2065 for (Map.Entry<PendingIntent, ProximityAlert> i
2066 : mProximityAlerts.entrySet()) {
2067 pw.println(" " + i.getKey() + ":");
2068 i.getValue().dump(pw, " ");
2069 }
2070 }
2071 if (mProximitiesEntered.size() > 0) {
2072 pw.println(" Proximities Entered:");
2073 for (ProximityAlert i : mProximitiesEntered) {
2074 pw.println(" " + i + ":");
2075 i.dump(pw, " ");
2076 }
2077 }
2078 pw.println(" mProximityListener=" + mProximityListener);
2079 if (mEnabledProviders.size() > 0) {
2080 pw.println(" Enabled Providers:");
2081 for (String i : mEnabledProviders) {
2082 pw.println(" " + i);
2083 }
2084
2085 }
2086 if (mDisabledProviders.size() > 0) {
2087 pw.println(" Disabled Providers:");
2088 for (String i : mDisabledProviders) {
2089 pw.println(" " + i);
2090 }
2091
2092 }
2093 if (mMockProviders.size() > 0) {
2094 pw.println(" Mock Providers:");
2095 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002096 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002097 }
2098 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002099 }
2100 }
2101}