blob: 5238c58d990d34567d9e0f0c9ecb99b71718d718 [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;
141 private boolean mScreenOn = true;
142 private PowerManager.WakeLock mWakeLock = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 private long mWakeLockAcquireTime = 0;
144 private boolean mWakeLockGpsReceived = true;
145 private boolean mWakeLockNetworkReceived = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 /**
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400148 * List of all receivers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 */
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400150 private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151
152 /**
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400153 * Object used internally for synchronization
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 */
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400155 private final Object mLock = new Object();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156
157 /**
158 * Mapping from provider name to all its UpdateRecords
159 */
160 private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
161 new HashMap<String,ArrayList<UpdateRecord>>();
162
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 // Proximity listeners
164 private Receiver mProximityListener = null;
165 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
166 new HashMap<PendingIntent,ProximityAlert>();
167 private HashSet<ProximityAlert> mProximitiesEntered =
168 new HashSet<ProximityAlert>();
169
170 // Last known location for each provider
171 private HashMap<String,Location> mLastKnownLocation =
172 new HashMap<String,Location>();
173
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 // Location collector
175 private ILocationCollector mCollector;
176
The Android Open Source Project4df24232009-03-05 14:34:35 -0800177 private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800178
Mike Lockwood9637d472009-04-02 21:41:57 -0700179 // for Settings change notification
180 private ContentQueryMap mSettings;
181
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 /**
183 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
184 * location updates.
185 */
186 private final class Receiver implements IBinder.DeathRecipient {
187 final ILocationListener mListener;
188 final PendingIntent mPendingIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 final Object mKey;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400190 final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400192 Receiver(ILocationListener listener) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 mListener = listener;
194 mPendingIntent = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 mKey = listener.asBinder();
196 }
197
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400198 Receiver(PendingIntent intent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 mPendingIntent = intent;
200 mListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 mKey = intent;
202 }
203
204 @Override
205 public boolean equals(Object otherObj) {
206 if (otherObj instanceof Receiver) {
207 return mKey.equals(
208 ((Receiver)otherObj).mKey);
209 }
210 return false;
211 }
212
213 @Override
214 public int hashCode() {
215 return mKey.hashCode();
216 }
217
218
219 @Override
220 public String toString() {
221 if (mListener != null) {
222 return "Receiver{"
223 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400224 + " Listener " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 } else {
226 return "Receiver{"
227 + Integer.toHexString(System.identityHashCode(this))
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400228 + " Intent " + mKey + "}";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 }
230 }
231
232 public boolean isListener() {
233 return mListener != null;
234 }
235
236 public boolean isPendingIntent() {
237 return mPendingIntent != null;
238 }
239
240 public ILocationListener getListener() {
241 if (mListener != null) {
242 return mListener;
243 }
244 throw new IllegalStateException("Request for non-existent listener");
245 }
246
247 public PendingIntent getPendingIntent() {
248 if (mPendingIntent != null) {
249 return mPendingIntent;
250 }
251 throw new IllegalStateException("Request for non-existent intent");
252 }
253
254 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
255 if (mListener != null) {
256 try {
257 mListener.onStatusChanged(provider, status, extras);
258 } catch (RemoteException e) {
259 return false;
260 }
261 } else {
262 Intent statusChanged = new Intent();
263 statusChanged.putExtras(extras);
264 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
265 try {
266 mPendingIntent.send(mContext, 0, statusChanged, null, null);
267 } catch (PendingIntent.CanceledException e) {
268 return false;
269 }
270 }
271 return true;
272 }
273
274 public boolean callLocationChangedLocked(Location location) {
275 if (mListener != null) {
276 try {
277 mListener.onLocationChanged(location);
278 } catch (RemoteException e) {
279 return false;
280 }
281 } else {
282 Intent locationChanged = new Intent();
283 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
284 try {
285 mPendingIntent.send(mContext, 0, locationChanged, null, null);
286 } catch (PendingIntent.CanceledException e) {
287 return false;
288 }
289 }
290 return true;
291 }
292
293 public void binderDied() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700294 if (LOCAL_LOGV) {
295 Log.v(TAG, "Location listener died");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 }
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400297 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 removeUpdatesLocked(this);
299 }
300 }
301 }
302
Mike Lockwood9637d472009-04-02 21:41:57 -0700303 private final class SettingsObserver implements Observer {
304 public void update(Observable o, Object arg) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400305 synchronized (mLock) {
Mike Lockwood9637d472009-04-02 21:41:57 -0700306 updateProvidersLocked();
307 }
308 }
309 }
310
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 private Location readLastKnownLocationLocked(String provider) {
312 Location location = null;
313 String s = null;
314 try {
315 File f = new File(LocationManager.SYSTEM_DIR + "/location."
316 + provider);
317 if (!f.exists()) {
318 return null;
319 }
320 BufferedReader reader = new BufferedReader(new FileReader(f), 256);
321 s = reader.readLine();
322 } catch (IOException e) {
323 Log.w(TAG, "Unable to read last known location", e);
324 }
325
326 if (s == null) {
327 return null;
328 }
329 try {
330 String[] tokens = PATTERN_COMMA.split(s);
331 int idx = 0;
332 long time = Long.parseLong(tokens[idx++]);
333 double latitude = Double.parseDouble(tokens[idx++]);
334 double longitude = Double.parseDouble(tokens[idx++]);
335 double altitude = Double.parseDouble(tokens[idx++]);
336 float bearing = Float.parseFloat(tokens[idx++]);
337 float speed = Float.parseFloat(tokens[idx++]);
338
339 location = new Location(provider);
340 location.setTime(time);
341 location.setLatitude(latitude);
342 location.setLongitude(longitude);
343 location.setAltitude(altitude);
344 location.setBearing(bearing);
345 location.setSpeed(speed);
346 } catch (NumberFormatException nfe) {
347 Log.e(TAG, "NumberFormatException reading last known location", nfe);
348 return null;
349 }
350
351 return location;
352 }
353
354 private void writeLastKnownLocationLocked(String provider,
355 Location location) {
356 long now = SystemClock.elapsedRealtime();
357 Long last = mLastWriteTime.get(provider);
358 if ((last != null)
359 && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
360 return;
361 }
362 mLastWriteTime.put(provider, now);
363
364 StringBuilder sb = new StringBuilder(100);
365 sb.append(location.getTime());
366 sb.append(',');
367 sb.append(location.getLatitude());
368 sb.append(',');
369 sb.append(location.getLongitude());
370 sb.append(',');
371 sb.append(location.getAltitude());
372 sb.append(',');
373 sb.append(location.getBearing());
374 sb.append(',');
375 sb.append(location.getSpeed());
376
377 FileWriter writer = null;
378 try {
379 File d = new File(LocationManager.SYSTEM_DIR);
380 if (!d.exists()) {
381 if (!d.mkdirs()) {
382 Log.w(TAG, "Unable to create directory to write location");
383 return;
384 }
385 }
386 File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
387 writer = new FileWriter(f);
388 writer.write(sb.toString());
389 } catch (IOException e) {
390 Log.w(TAG, "Unable to write location", e);
391 } finally {
392 if (writer != null) {
393 try {
394 writer.close();
395 } catch (IOException e) {
396 Log.w(TAG, "Exception closing file", e);
397 }
398 }
399 }
400 }
401
402 /**
403 * Load providers from /data/location/<provider_name>/
404 * class
405 * kml
406 * nmea
407 * track
408 * location
409 * properties
410 */
411 private void loadProviders() {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400412 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 if (sProvidersLoaded) {
414 return;
415 }
416
417 // Load providers
418 loadProvidersLocked();
419 sProvidersLoaded = true;
420 }
421 }
422
423 private void loadProvidersLocked() {
424 try {
425 _loadProvidersLocked();
426 } catch (Exception e) {
427 Log.e(TAG, "Exception loading providers:", e);
428 }
429 }
430
431 private void _loadProvidersLocked() {
432 // Attempt to load "real" providers first
433 if (GpsLocationProvider.isSupported()) {
434 // Create a gps location provider
Mike Lockwood4e50b782009-04-03 08:24:43 -0700435 mGpsLocationProvider = new GpsLocationProvider(mContext, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 LocationProviderImpl.addProvider(mGpsLocationProvider);
437 }
438
439 // Load fake providers if real providers are not available
440 File f = new File(LocationManager.PROVIDER_DIR);
441 if (f.isDirectory()) {
442 File[] subdirs = f.listFiles();
443 for (int i = 0; i < subdirs.length; i++) {
444 if (!subdirs[i].isDirectory()) {
445 continue;
446 }
447
448 String name = subdirs[i].getName();
449
The Android Open Source Project10592532009-03-18 17:39:46 -0700450 if (LOCAL_LOGV) {
451 Log.v(TAG, "Found dir " + subdirs[i].getAbsolutePath());
452 Log.v(TAG, "name = " + name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 }
454
455 // Don't create a fake provider if a real provider exists
456 if (LocationProviderImpl.getProvider(name) == null) {
457 LocationProviderImpl provider = null;
458 try {
459 File classFile = new File(subdirs[i], "class");
460 // Look for a 'class' file
461 provider = LocationProviderImpl.loadFromClass(classFile);
462
463 // Look for an 'kml', 'nmea', or 'track' file
464 if (provider == null) {
465 // Load properties from 'properties' file, if present
466 File propertiesFile = new File(subdirs[i], "properties");
467
468 if (propertiesFile.exists()) {
Mike Lockwood4e50b782009-04-03 08:24:43 -0700469 provider = new TrackProvider(name, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 ((TrackProvider)provider).readProperties(propertiesFile);
471
472 File kmlFile = new File(subdirs[i], "kml");
473 if (kmlFile.exists()) {
474 ((TrackProvider) provider).readKml(kmlFile);
475 } else {
476 File nmeaFile = new File(subdirs[i], "nmea");
477 if (nmeaFile.exists()) {
478 ((TrackProvider) provider).readNmea(name, nmeaFile);
479 } else {
480 File trackFile = new File(subdirs[i], "track");
481 if (trackFile.exists()) {
482 ((TrackProvider) provider).readTrack(trackFile);
483 }
484 }
485 }
486 }
487 }
488 if (provider != null) {
489 LocationProviderImpl.addProvider(provider);
490 }
491 // Grab the initial location of a TrackProvider and
492 // store it as the last known location for that provider
493 if (provider instanceof TrackProvider) {
494 TrackProvider tp = (TrackProvider) provider;
495 mLastKnownLocation.put(tp.getName(), tp.getInitialLocation());
496 }
497 } catch (Exception e) {
498 Log.e(TAG, "Exception loading provder " + name, e);
499 }
500 }
501 }
502 }
503
504 updateProvidersLocked();
505 }
506
507 /**
508 * @param context the context that the LocationManagerService runs in
509 */
510 public LocationManagerService(Context context) {
511 super();
512 mContext = context;
513 mLocationHandler = new LocationWorkerHandler();
514
The Android Open Source Project10592532009-03-18 17:39:46 -0700515 if (LOCAL_LOGV) {
516 Log.v(TAG, "Constructed LocationManager Service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 }
518
519 // Alarm manager, needs to be done before calling loadProviders() below
520 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
521
522 // Create a wake lock, needs to be done before calling loadProviders() below
523 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
524 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
525
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526 // Load providers
527 loadProviders();
528
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 // Register for Network (Wifi or Mobile) updates
530 NetworkStateBroadcastReceiver networkReceiver = new NetworkStateBroadcastReceiver();
531 IntentFilter networkIntentFilter = new IntentFilter();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
533 networkIntentFilter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
534 context.registerReceiver(networkReceiver, networkIntentFilter);
535
536 // Register for power updates
537 PowerStateBroadcastReceiver powerStateReceiver = new PowerStateBroadcastReceiver();
538 IntentFilter intentFilter = new IntentFilter();
539 intentFilter.addAction(ALARM_INTENT);
540 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
541 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
543 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
544 context.registerReceiver(powerStateReceiver, intentFilter);
545
Mike Lockwood9637d472009-04-02 21:41:57 -0700546 // listen for settings changes
547 ContentResolver resolver = mContext.getContentResolver();
548 Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
549 "(" + Settings.System.NAME + "=?)",
550 new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
551 null);
552 mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler);
553 SettingsObserver settingsObserver = new SettingsObserver();
554 mSettings.addObserver(settingsObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 }
556
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700557 public void setNetworkLocationProvider(ILocationProvider provider) {
558 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
559 throw new SecurityException(
560 "Installing location providers outside of the system is not supported");
561 }
562
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400563 synchronized (mLock) {
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700564 mNetworkLocationProvider =
565 new LocationProviderProxy(LocationManager.NETWORK_PROVIDER, this, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 LocationProviderImpl.addProvider(mNetworkLocationProvider);
567 updateProvidersLocked();
The Android Open Source Project4df24232009-03-05 14:34:35 -0800568
569 // notify NetworkLocationProvider of any events it might have missed
Mike Lockwoodf113fbe2009-04-06 05:17:28 -0700570 mNetworkLocationProvider.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 }
572 }
573
574 public void setLocationCollector(ILocationCollector collector) {
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700575 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
576 throw new SecurityException(
577 "Installing location collectors outside of the system is not supported");
578 }
579
Mike Lockwood98cb6672009-04-17 18:03:44 -0400580 mCollector = collector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 }
582
Mike Lockwooda55c3212009-04-15 11:10:11 -0400583 public void setGeocodeProvider(IGeocodeProvider provider) {
584 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
585 throw new SecurityException(
586 "Installing location providers outside of the system is not supported");
587 }
588
589 mGeocodeProvider = provider;
590 }
591
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592 private boolean isAllowedBySettingsLocked(String provider) {
593 if (mEnabledProviders.contains(provider)) {
594 return true;
595 }
596 if (mDisabledProviders.contains(provider)) {
597 return false;
598 }
599 // Use system settings
600 ContentResolver resolver = mContext.getContentResolver();
601 String allowedProviders = Settings.Secure.getString(resolver,
602 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
603
604 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
605 }
606
607 private void checkPermissionsSafe(String provider) {
608 if (LocationManager.GPS_PROVIDER.equals(provider)
609 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
610 != PackageManager.PERMISSION_GRANTED)) {
611 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
612 }
613 if (LocationManager.NETWORK_PROVIDER.equals(provider)
614 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
615 != PackageManager.PERMISSION_GRANTED)
616 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
617 != PackageManager.PERMISSION_GRANTED)) {
618 throw new SecurityException(
619 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
620 }
621 }
622
623 private boolean isAllowedProviderSafe(String provider) {
624 if (LocationManager.GPS_PROVIDER.equals(provider)
625 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
626 != PackageManager.PERMISSION_GRANTED)) {
627 return false;
628 }
629 if (LocationManager.NETWORK_PROVIDER.equals(provider)
630 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
631 != PackageManager.PERMISSION_GRANTED)
632 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
633 != PackageManager.PERMISSION_GRANTED)) {
634 return false;
635 }
636
637 return true;
638 }
639
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800640 public List<String> getAllProviders() {
641 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400642 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800643 return _getAllProvidersLocked();
644 }
645 } catch (SecurityException se) {
646 throw se;
647 } catch (Exception e) {
648 Log.e(TAG, "getAllProviders got exception:", e);
649 return null;
650 }
651 }
652
653 private List<String> _getAllProvidersLocked() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700654 if (LOCAL_LOGV) {
655 Log.v(TAG, "getAllProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656 }
657 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
658 ArrayList<String> out = new ArrayList<String>(providers.size());
659
660 for (LocationProviderImpl p : providers) {
661 out.add(p.getName());
662 }
663 return out;
664 }
665
666 public List<String> getProviders(boolean enabledOnly) {
667 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400668 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 return _getProvidersLocked(enabledOnly);
670 }
671 } catch (SecurityException se) {
672 throw se;
673 } catch (Exception e) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700674 Log.e(TAG, "getProviders got exception:", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675 return null;
676 }
677 }
678
679 private List<String> _getProvidersLocked(boolean enabledOnly) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700680 if (LOCAL_LOGV) {
681 Log.v(TAG, "getProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 }
683 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
684 ArrayList<String> out = new ArrayList<String>();
685
686 for (LocationProviderImpl p : providers) {
687 String name = p.getName();
688 if (isAllowedProviderSafe(name)) {
689 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
690 continue;
691 }
692 out.add(name);
693 }
694 }
695 return out;
696 }
697
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 private void updateProvidersLocked() {
699 for (LocationProviderImpl p : LocationProviderImpl.getProviders()) {
700 boolean isEnabled = p.isEnabled();
701 String name = p.getName();
702 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
703
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800704 if (isEnabled && !shouldBeEnabled) {
705 updateProviderListenersLocked(name, false);
706 } else if (!isEnabled && shouldBeEnabled) {
707 updateProviderListenersLocked(name, true);
708 }
709
710 }
711 }
712
713 private void updateProviderListenersLocked(String provider, boolean enabled) {
714 int listeners = 0;
715
716 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
717 if (p == null) {
718 return;
719 }
720
721 ArrayList<Receiver> deadReceivers = null;
722
723 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
724 if (records != null) {
725 final int N = records.size();
726 for (int i=0; i<N; i++) {
727 UpdateRecord record = records.get(i);
728 // Sends a notification message to the receiver
729 try {
730 Receiver receiver = record.mReceiver;
731 if (receiver.isListener()) {
732 if (enabled) {
733 receiver.getListener().onProviderEnabled(provider);
734 } else {
735 receiver.getListener().onProviderDisabled(provider);
736 }
737 } else {
738 Intent providerIntent = new Intent();
739 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
740 try {
741 receiver.getPendingIntent().send(mContext, 0,
742 providerIntent, null, null);
743 } catch (PendingIntent.CanceledException e) {
744 if (deadReceivers == null) {
745 deadReceivers = new ArrayList<Receiver>();
746 deadReceivers.add(receiver);
747 }
748 }
749 }
750 } catch (RemoteException e) {
751 // The death link will clean this up.
752 }
753 listeners++;
754 }
755 }
756
757 if (deadReceivers != null) {
758 for (int i=deadReceivers.size()-1; i>=0; i--) {
759 removeUpdatesLocked(deadReceivers.get(i));
760 }
761 }
762
763 if (enabled) {
764 p.enable();
765 if (listeners > 0) {
766 p.setMinTime(getMinTimeLocked(provider));
767 p.enableLocationTracking(true);
768 updateWakelockStatusLocked(mScreenOn);
769 }
770 } else {
771 p.enableLocationTracking(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 p.disable();
773 updateWakelockStatusLocked(mScreenOn);
774 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 }
776
777 private long getMinTimeLocked(String provider) {
778 long minTime = Long.MAX_VALUE;
779 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
780 if (records != null) {
781 for (int i=records.size()-1; i>=0; i--) {
782 minTime = Math.min(minTime, records.get(i).mMinTime);
783 }
784 }
785 return minTime;
786 }
787
788 private class UpdateRecord {
789 final String mProvider;
790 final Receiver mReceiver;
791 final long mMinTime;
792 final float mMinDistance;
793 final int mUid;
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400794 Location mLastFixBroadcast;
795 long mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796
797 /**
798 * Note: must be constructed with lock held.
799 */
800 UpdateRecord(String provider, long minTime, float minDistance,
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400801 Receiver receiver, int uid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800802 mProvider = provider;
803 mReceiver = receiver;
804 mMinTime = minTime;
805 mMinDistance = minDistance;
806 mUid = uid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807
808 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
809 if (records == null) {
810 records = new ArrayList<UpdateRecord>();
811 mRecordsByProvider.put(provider, records);
812 }
813 if (!records.contains(this)) {
814 records.add(this);
815 }
816 }
817
818 /**
819 * Method to be called when a record will no longer be used. Calling this multiple times
820 * must have the same effect as calling it once.
821 */
822 void disposeLocked() {
823 ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
824 records.remove(this);
825 }
826
827 @Override
828 public String toString() {
829 return "UpdateRecord{"
830 + Integer.toHexString(System.identityHashCode(this))
831 + " " + mProvider + " " + mReceiver + "}";
832 }
833
834 void dump(PrintWriter pw, String prefix) {
835 pw.println(prefix + this);
836 pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
837 pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400838 pw.println(prefix + "mUid=" + mUid);
839 pw.println(prefix + "mLastFixBroadcast:");
840 mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
841 pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800842 }
843
844 /**
845 * Calls dispose().
846 */
847 @Override protected void finalize() {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400848 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800849 disposeLocked();
850 }
851 }
852 }
853
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400854 private Receiver getReceiver(ILocationListener listener) {
855 IBinder binder = listener.asBinder();
856 Receiver receiver = mReceivers.get(binder);
857 if (receiver == null) {
858 receiver = new Receiver(listener);
859 mReceivers.put(binder, receiver);
860
861 try {
862 if (receiver.isListener()) {
863 receiver.getListener().asBinder().linkToDeath(receiver, 0);
864 }
865 } catch (RemoteException e) {
866 Log.e(TAG, "linkToDeath failed:", e);
867 return null;
868 }
869 }
870 return receiver;
871 }
872
873 private Receiver getReceiver(PendingIntent intent) {
874 Receiver receiver = mReceivers.get(intent);
875 if (receiver == null) {
876 receiver = new Receiver(intent);
877 mReceivers.put(intent, receiver);
878 }
879 return receiver;
880 }
881
882 private boolean providerHasListener(String provider, int uid, Receiver excludedReceiver) {
883 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
884 if (records != null) {
885 for (int i = records.size() - 1; i >= 0; i--) {
886 UpdateRecord record = records.get(i);
887 if (record.mUid == uid && record.mReceiver != excludedReceiver) {
888 return true;
889 }
890 }
891 }
892 if (LocationManager.GPS_PROVIDER.equals(provider) ||
893 LocationManager.NETWORK_PROVIDER.equals(provider)) {
894 for (ProximityAlert alert : mProximityAlerts.values()) {
895 if (alert.mUid == uid) {
896 return true;
897 }
898 }
899 }
900 return false;
901 }
902
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 public void requestLocationUpdates(String provider,
904 long minTime, float minDistance, ILocationListener listener) {
905
906 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400907 synchronized (mLock) {
908 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 }
910 } catch (SecurityException se) {
911 throw se;
912 } catch (Exception e) {
913 Log.e(TAG, "requestUpdates got exception:", e);
914 }
915 }
916
917 public void requestLocationUpdatesPI(String provider,
918 long minTime, float minDistance, PendingIntent intent) {
919 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400920 synchronized (mLock) {
921 requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 }
923 } catch (SecurityException se) {
924 throw se;
925 } catch (Exception e) {
926 Log.e(TAG, "requestUpdates got exception:", e);
927 }
928 }
929
930 private void requestLocationUpdatesLocked(String provider,
931 long minTime, float minDistance, Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700932 if (LOCAL_LOGV) {
933 Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 }
935
936 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
937 if (impl == null) {
938 throw new IllegalArgumentException("provider=" + provider);
939 }
940
941 checkPermissionsSafe(provider);
942
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800943 // so wakelock calls will succeed
944 final int callingUid = Binder.getCallingUid();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400945 boolean newUid = !providerHasListener(provider, callingUid, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800946 long identity = Binder.clearCallingIdentity();
947 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400948 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
949 UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950 if (oldRecord != null) {
951 oldRecord.disposeLocked();
952 }
953
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400954 if (newUid) {
955 impl.addListener(callingUid);
956 }
957
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800958 boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
959 if (isProviderEnabled) {
960 long minTimeForProvider = getMinTimeLocked(provider);
961 impl.setMinTime(minTimeForProvider);
962 impl.enableLocationTracking(true);
963 updateWakelockStatusLocked(mScreenOn);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 } else {
965 try {
966 // Notify the listener that updates are currently disabled
967 if (receiver.isListener()) {
968 receiver.getListener().onProviderDisabled(provider);
969 }
970 } catch(RemoteException e) {
971 Log.w(TAG, "RemoteException calling onProviderDisabled on " +
972 receiver.getListener());
973 }
974 }
975 } finally {
976 Binder.restoreCallingIdentity(identity);
977 }
978 }
979
980 public void removeUpdates(ILocationListener listener) {
981 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400982 synchronized (mLock) {
983 removeUpdatesLocked(getReceiver(listener));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800984 }
985 } catch (SecurityException se) {
986 throw se;
987 } catch (Exception e) {
988 Log.e(TAG, "removeUpdates got exception:", e);
989 }
990 }
991
992 public void removeUpdatesPI(PendingIntent intent) {
993 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -0400994 synchronized (mLock) {
995 removeUpdatesLocked(getReceiver(intent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 }
997 } catch (SecurityException se) {
998 throw se;
999 } catch (Exception e) {
1000 Log.e(TAG, "removeUpdates got exception:", e);
1001 }
1002 }
1003
1004 private void removeUpdatesLocked(Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001005 if (LOCAL_LOGV) {
1006 Log.v(TAG, "_removeUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001007 }
1008
1009 // so wakelock calls will succeed
1010 final int callingUid = Binder.getCallingUid();
1011 long identity = Binder.clearCallingIdentity();
1012 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001013 if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
1014 receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 }
1016
1017 // Record which providers were associated with this listener
1018 HashSet<String> providers = new HashSet<String>();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001019 HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 if (oldRecords != null) {
1021 // Call dispose() on the obsolete update records.
1022 for (UpdateRecord record : oldRecords.values()) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001023 if (!providerHasListener(record.mProvider, callingUid, receiver)) {
1024 LocationProviderImpl impl =
1025 LocationProviderImpl.getProvider(record.mProvider);
1026 if (impl != null) {
1027 impl.removeListener(callingUid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028 }
1029 }
1030 record.disposeLocked();
1031 }
1032 // Accumulate providers
1033 providers.addAll(oldRecords.keySet());
1034 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001035
1036 // See if the providers associated with this listener have any
1037 // other listeners; if one does, inform it of the new smallest minTime
1038 // value; if one does not, disable location tracking for it
1039 for (String provider : providers) {
1040 // If provider is already disabled, don't need to do anything
1041 if (!isAllowedBySettingsLocked(provider)) {
1042 continue;
1043 }
1044
1045 boolean hasOtherListener = false;
1046 ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1047 if (recordsForProvider != null && recordsForProvider.size() > 0) {
1048 hasOtherListener = true;
1049 }
1050
1051 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1052 if (p != null) {
1053 if (hasOtherListener) {
1054 p.setMinTime(getMinTimeLocked(provider));
1055 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 p.enableLocationTracking(false);
1057 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 }
1059 }
1060
1061 updateWakelockStatusLocked(mScreenOn);
1062 } finally {
1063 Binder.restoreCallingIdentity(identity);
1064 }
1065 }
1066
1067 public boolean addGpsStatusListener(IGpsStatusListener listener) {
1068 if (mGpsLocationProvider == null) {
1069 return false;
1070 }
1071 if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1072 PackageManager.PERMISSION_GRANTED) {
1073 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1074 }
1075
1076 try {
1077 mGpsLocationProvider.addGpsStatusListener(listener);
1078 } catch (RemoteException e) {
1079 Log.w(TAG, "RemoteException in addGpsStatusListener");
1080 return false;
1081 }
1082 return true;
1083 }
1084
1085 public void removeGpsStatusListener(IGpsStatusListener listener) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001086 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087 mGpsLocationProvider.removeGpsStatusListener(listener);
1088 }
1089 }
1090
1091 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1092 // first check for permission to the provider
1093 checkPermissionsSafe(provider);
1094 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1095 if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1096 != PackageManager.PERMISSION_GRANTED)) {
1097 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1098 }
1099
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001100 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001101 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
1102 if (provider == null) {
1103 return false;
1104 }
1105
1106 return impl.sendExtraCommand(command, extras);
1107 }
1108 }
1109
1110 class ProximityAlert {
1111 final int mUid;
1112 final double mLatitude;
1113 final double mLongitude;
1114 final float mRadius;
1115 final long mExpiration;
1116 final PendingIntent mIntent;
1117 final Location mLocation;
1118
1119 public ProximityAlert(int uid, double latitude, double longitude,
1120 float radius, long expiration, PendingIntent intent) {
1121 mUid = uid;
1122 mLatitude = latitude;
1123 mLongitude = longitude;
1124 mRadius = radius;
1125 mExpiration = expiration;
1126 mIntent = intent;
1127
1128 mLocation = new Location("");
1129 mLocation.setLatitude(latitude);
1130 mLocation.setLongitude(longitude);
1131 }
1132
1133 long getExpiration() {
1134 return mExpiration;
1135 }
1136
1137 PendingIntent getIntent() {
1138 return mIntent;
1139 }
1140
1141 boolean isInProximity(double latitude, double longitude) {
1142 Location loc = new Location("");
1143 loc.setLatitude(latitude);
1144 loc.setLongitude(longitude);
1145
1146 double radius = loc.distanceTo(mLocation);
1147 return radius <= mRadius;
1148 }
1149
1150 @Override
1151 public String toString() {
1152 return "ProximityAlert{"
1153 + Integer.toHexString(System.identityHashCode(this))
1154 + " uid " + mUid + mIntent + "}";
1155 }
1156
1157 void dump(PrintWriter pw, String prefix) {
1158 pw.println(prefix + this);
1159 pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
1160 pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
1161 pw.println(prefix + "mIntent=" + mIntent);
1162 pw.println(prefix + "mLocation:");
1163 mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
1164 }
1165 }
1166
1167 // Listener for receiving locations to trigger proximity alerts
1168 class ProximityListener extends ILocationListener.Stub {
1169
1170 boolean isGpsAvailable = false;
1171
1172 // Note: this is called with the lock held.
1173 public void onLocationChanged(Location loc) {
1174
1175 // If Gps is available, then ignore updates from NetworkLocationProvider
1176 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1177 isGpsAvailable = true;
1178 }
1179 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1180 return;
1181 }
1182
1183 // Process proximity alerts
1184 long now = System.currentTimeMillis();
1185 double latitude = loc.getLatitude();
1186 double longitude = loc.getLongitude();
1187 ArrayList<PendingIntent> intentsToRemove = null;
1188
1189 for (ProximityAlert alert : mProximityAlerts.values()) {
1190 PendingIntent intent = alert.getIntent();
1191 long expiration = alert.getExpiration();
1192
1193 if ((expiration == -1) || (now <= expiration)) {
1194 boolean entered = mProximitiesEntered.contains(alert);
1195 boolean inProximity =
1196 alert.isInProximity(latitude, longitude);
1197 if (!entered && inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001198 if (LOCAL_LOGV) {
1199 Log.v(TAG, "Entered alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 }
1201 mProximitiesEntered.add(alert);
1202 Intent enteredIntent = new Intent();
1203 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1204 try {
1205 intent.send(mContext, 0, enteredIntent, null, null);
1206 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001207 if (LOCAL_LOGV) {
1208 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001209 }
1210 if (intentsToRemove == null) {
1211 intentsToRemove = new ArrayList<PendingIntent>();
1212 }
1213 intentsToRemove.add(intent);
1214 }
1215 } else if (entered && !inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001216 if (LOCAL_LOGV) {
1217 Log.v(TAG, "Exited alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001218 }
1219 mProximitiesEntered.remove(alert);
1220 Intent exitedIntent = new Intent();
1221 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1222 try {
1223 intent.send(mContext, 0, exitedIntent, null, null);
1224 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001225 if (LOCAL_LOGV) {
1226 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001227 }
1228 if (intentsToRemove == null) {
1229 intentsToRemove = new ArrayList<PendingIntent>();
1230 }
1231 intentsToRemove.add(intent);
1232 }
1233 }
1234 } else {
1235 // Mark alert for expiration
The Android Open Source Project10592532009-03-18 17:39:46 -07001236 if (LOCAL_LOGV) {
1237 Log.v(TAG, "Expiring proximity alert: " + alert);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001238 }
1239 if (intentsToRemove == null) {
1240 intentsToRemove = new ArrayList<PendingIntent>();
1241 }
1242 intentsToRemove.add(alert.getIntent());
1243 }
1244 }
1245
1246 // Remove expired alerts
1247 if (intentsToRemove != null) {
1248 for (PendingIntent i : intentsToRemove) {
1249 mProximityAlerts.remove(i);
1250 ProximityAlert alert = mProximityAlerts.get(i);
1251 mProximitiesEntered.remove(alert);
1252 }
1253 }
1254
1255 }
1256
1257 // Note: this is called with the lock held.
1258 public void onProviderDisabled(String provider) {
1259 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1260 isGpsAvailable = false;
1261 }
1262 }
1263
1264 // Note: this is called with the lock held.
1265 public void onProviderEnabled(String provider) {
1266 // ignore
1267 }
1268
1269 // Note: this is called with the lock held.
1270 public void onStatusChanged(String provider, int status, Bundle extras) {
1271 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1272 (status != LocationProvider.AVAILABLE)) {
1273 isGpsAvailable = false;
1274 }
1275 }
1276 }
1277
1278 public void addProximityAlert(double latitude, double longitude,
1279 float radius, long expiration, PendingIntent intent) {
1280 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001281 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001282 addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
1283 }
1284 } catch (SecurityException se) {
1285 throw se;
1286 } catch (Exception e) {
1287 Log.e(TAG, "addProximityAlert got exception:", e);
1288 }
1289 }
1290
1291 private void addProximityAlertLocked(double latitude, double longitude,
1292 float radius, long expiration, PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001293 if (LOCAL_LOGV) {
1294 Log.v(TAG, "addProximityAlert: latitude = " + latitude +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 ", longitude = " + longitude +
1296 ", expiration = " + expiration +
1297 ", intent = " + intent);
1298 }
1299
1300 // Require ability to access all providers for now
1301 if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
1302 !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
1303 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1304 }
1305
1306 if (expiration != -1) {
1307 expiration += System.currentTimeMillis();
1308 }
1309 ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
1310 latitude, longitude, radius, expiration, intent);
1311 mProximityAlerts.put(intent, alert);
1312
1313 if (mProximityListener == null) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001314 mProximityListener = new Receiver(new ProximityListener());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001315
1316 LocationProvider provider = LocationProviderImpl.getProvider(
1317 LocationManager.GPS_PROVIDER);
1318 if (provider != null) {
1319 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1320 }
1321
1322 provider =
1323 LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER);
1324 if (provider != null) {
1325 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1326 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 }
1328 }
1329
1330 public void removeProximityAlert(PendingIntent intent) {
1331 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001332 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001333 removeProximityAlertLocked(intent);
1334 }
1335 } catch (SecurityException se) {
1336 throw se;
1337 } catch (Exception e) {
1338 Log.e(TAG, "removeProximityAlert got exception:", e);
1339 }
1340 }
1341
1342 private void removeProximityAlertLocked(PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001343 if (LOCAL_LOGV) {
1344 Log.v(TAG, "removeProximityAlert: intent = " + intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001345 }
1346
1347 mProximityAlerts.remove(intent);
1348 if (mProximityAlerts.size() == 0) {
1349 removeUpdatesLocked(mProximityListener);
1350 mProximityListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001351 }
1352 }
1353
1354 /**
1355 * @return null if the provider does not exits
1356 * @throw SecurityException if the provider is not allowed to be
1357 * accessed by the caller
1358 */
1359 public Bundle getProviderInfo(String provider) {
1360 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001361 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001362 return _getProviderInfoLocked(provider);
1363 }
1364 } catch (SecurityException se) {
1365 throw se;
1366 } catch (Exception e) {
1367 Log.e(TAG, "_getProviderInfo got exception:", e);
1368 return null;
1369 }
1370 }
1371
1372 private Bundle _getProviderInfoLocked(String provider) {
1373 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1374 if (p == null) {
1375 return null;
1376 }
1377
1378 checkPermissionsSafe(provider);
1379
1380 Bundle b = new Bundle();
1381 b.putBoolean("network", p.requiresNetwork());
1382 b.putBoolean("satellite", p.requiresSatellite());
1383 b.putBoolean("cell", p.requiresCell());
1384 b.putBoolean("cost", p.hasMonetaryCost());
1385 b.putBoolean("altitude", p.supportsAltitude());
1386 b.putBoolean("speed", p.supportsSpeed());
1387 b.putBoolean("bearing", p.supportsBearing());
1388 b.putInt("power", p.getPowerRequirement());
1389 b.putInt("accuracy", p.getAccuracy());
1390
1391 return b;
1392 }
1393
1394 public boolean isProviderEnabled(String provider) {
1395 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001396 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001397 return _isProviderEnabledLocked(provider);
1398 }
1399 } catch (SecurityException se) {
1400 throw se;
1401 } catch (Exception e) {
1402 Log.e(TAG, "isProviderEnabled got exception:", e);
1403 return false;
1404 }
1405 }
1406
Mike Lockwood4e50b782009-04-03 08:24:43 -07001407 public void setLocation(Location location) {
1408 mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
1409 Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
1410 mLocationHandler.sendMessageAtFrontOfQueue(m);
1411 }
1412
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001413 private boolean _isProviderEnabledLocked(String provider) {
1414 checkPermissionsSafe(provider);
1415
1416 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1417 if (p == null) {
1418 throw new IllegalArgumentException("provider=" + provider);
1419 }
1420 return isAllowedBySettingsLocked(provider);
1421 }
1422
1423 public Location getLastKnownLocation(String provider) {
1424 try {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001425 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001426 return _getLastKnownLocationLocked(provider);
1427 }
1428 } catch (SecurityException se) {
1429 throw se;
1430 } catch (Exception e) {
1431 Log.e(TAG, "getLastKnownLocation got exception:", e);
1432 return null;
1433 }
1434 }
1435
1436 private Location _getLastKnownLocationLocked(String provider) {
1437 checkPermissionsSafe(provider);
1438
1439 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1440 if (p == null) {
1441 throw new IllegalArgumentException("provider=" + provider);
1442 }
1443
1444 if (!isAllowedBySettingsLocked(provider)) {
1445 return null;
1446 }
1447
1448 Location location = mLastKnownLocation.get(provider);
1449 if (location == null) {
1450 // Get the persistent last known location for the provider
1451 location = readLastKnownLocationLocked(provider);
1452 if (location != null) {
1453 mLastKnownLocation.put(provider, location);
1454 }
1455 }
1456
1457 return location;
1458 }
1459
1460 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1461 // Always broadcast the first update
1462 if (lastLoc == null) {
1463 return true;
1464 }
1465
1466 // Don't broadcast same location again regardless of condition
1467 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1468 if (loc.getTime() == lastLoc.getTime()) {
1469 return false;
1470 }
1471
1472 // Check whether sufficient distance has been traveled
1473 double minDistance = record.mMinDistance;
1474 if (minDistance > 0.0) {
1475 if (loc.distanceTo(lastLoc) <= minDistance) {
1476 return false;
1477 }
1478 }
1479
1480 return true;
1481 }
1482
Mike Lockwood4e50b782009-04-03 08:24:43 -07001483 private void handleLocationChangedLocked(Location location) {
1484 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1486 if (records == null || records.size() == 0) {
1487 return;
1488 }
1489
1490 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1491 if (p == null) {
1492 return;
1493 }
1494
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001495 // Update last known location for provider
Mike Lockwood4e50b782009-04-03 08:24:43 -07001496 Location lastLocation = mLastKnownLocation.get(provider);
1497 if (lastLocation == null) {
1498 mLastKnownLocation.put(provider, new Location(location));
1499 } else {
1500 lastLocation.set(location);
1501 }
1502 writeLastKnownLocationLocked(provider, location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001503
Mike Lockwoode932f7f2009-04-06 10:51:26 -07001504 if (LocationManager.NETWORK_PROVIDER.equals(p.getName())) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001505 mWakeLockNetworkReceived = true;
1506 } else if (p instanceof GpsLocationProvider) {
1507 // Gps location received signal is in NetworkStateBroadcastReceiver
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001508 }
1509
1510 // Fetch latest status update time
1511 long newStatusUpdateTime = p.getStatusUpdateTime();
1512
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001513 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001514 Bundle extras = new Bundle();
1515 int status = p.getStatus(extras);
1516
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001517 ArrayList<Receiver> deadReceivers = null;
1518
1519 // Broadcast location or status to all listeners
1520 final int N = records.size();
1521 for (int i=0; i<N; i++) {
1522 UpdateRecord r = records.get(i);
1523 Receiver receiver = r.mReceiver;
1524
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001525 Location lastLoc = r.mLastFixBroadcast;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001526 if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1527 if (lastLoc == null) {
1528 lastLoc = new Location(location);
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001529 r.mLastFixBroadcast = lastLoc;
Mike Lockwood4e50b782009-04-03 08:24:43 -07001530 } else {
1531 lastLoc.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001532 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001533 if (!receiver.callLocationChangedLocked(location)) {
1534 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1535 if (deadReceivers == null) {
1536 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001537 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001538 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539 }
1540 }
1541
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001542 long prevStatusUpdateTime = r.mLastStatusBroadcast;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001543 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1544 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1545
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001546 r.mLastStatusBroadcast = newStatusUpdateTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001547 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1548 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1549 if (deadReceivers == null) {
1550 deadReceivers = new ArrayList<Receiver>();
1551 }
1552 if (!deadReceivers.contains(receiver)) {
1553 deadReceivers.add(receiver);
1554 }
1555 }
1556 }
1557 }
1558
1559 if (deadReceivers != null) {
1560 for (int i=deadReceivers.size()-1; i>=0; i--) {
1561 removeUpdatesLocked(deadReceivers.get(i));
1562 }
1563 }
1564 }
1565
1566 private class LocationWorkerHandler extends Handler {
1567
1568 @Override
1569 public void handleMessage(Message msg) {
1570 try {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001571 if (msg.what == MESSAGE_LOCATION_CHANGED) {
1572 // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001573
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001574 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001575 Location location = (Location) msg.obj;
Mike Lockwood98cb6672009-04-17 18:03:44 -04001576
1577 if (mCollector != null &&
1578 LocationManager.GPS_PROVIDER.equals(location.getProvider())) {
1579 try {
1580 mCollector.updateLocation(location);
1581 } catch (RemoteException e) {
1582 Log.w(TAG, "mCollector.updateLocation failed");
1583 }
1584 }
1585
Mike Lockwood4e50b782009-04-03 08:24:43 -07001586 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001587 if (!isAllowedBySettingsLocked(provider)) {
1588 return;
1589 }
1590
1591 // Process the location fix if the screen is on or we're holding a wakelock
1592 if (mScreenOn || (mWakeLockAcquireTime != 0)) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001593 handleLocationChangedLocked(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001594 }
1595
1596 if ((mWakeLockAcquireTime != 0) &&
1597 (SystemClock.elapsedRealtime() - mWakeLockAcquireTime
1598 > MAX_TIME_FOR_WAKE_LOCK)) {
1599
1600 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1601 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1602
1603 log("LocationWorkerHandler: Exceeded max time for wake lock");
1604 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1605 sendMessageAtFrontOfQueue(m);
1606
1607 } else if (mWakeLockAcquireTime != 0 &&
1608 mWakeLockGpsReceived && mWakeLockNetworkReceived) {
1609
1610 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1611 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1612
1613 log("LocationWorkerHandler: Locations received.");
1614 mWakeLockAcquireTime = 0;
1615 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1616 sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK);
1617 }
1618 }
1619
1620 } else if (msg.what == MESSAGE_ACQUIRE_WAKE_LOCK) {
1621 log("LocationWorkerHandler: Acquire");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001622 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001623 acquireWakeLockLocked();
1624 }
1625 } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) {
1626 log("LocationWorkerHandler: Release");
1627
1628 // Update wakelock status so the next alarm is set before releasing wakelock
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001629 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001630 updateWakelockStatusLocked(mScreenOn);
1631 releaseWakeLockLocked();
1632 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001633 }
1634 } catch (Exception e) {
1635 // Log, don't crash!
1636 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1637 }
1638 }
1639 }
1640
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001641 private class PowerStateBroadcastReceiver extends BroadcastReceiver {
1642 @Override public void onReceive(Context context, Intent intent) {
1643 String action = intent.getAction();
1644
1645 if (action.equals(ALARM_INTENT)) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001646 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001647 log("PowerStateBroadcastReceiver: Alarm received");
1648 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1649 // Have to do this immediately, rather than posting a
1650 // message, so we execute our code while the system
1651 // is holding a wake lock until the alarm broadcast
1652 // is finished.
1653 acquireWakeLockLocked();
1654 }
1655
1656 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1657 log("PowerStateBroadcastReceiver: Screen off");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001658 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001659 updateWakelockStatusLocked(false);
1660 }
1661
1662 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1663 log("PowerStateBroadcastReceiver: Screen on");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001664 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001665 updateWakelockStatusLocked(true);
1666 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001667 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
1668 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001669 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001670 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1671 if (uid >= 0) {
1672 ArrayList<Receiver> removedRecs = null;
1673 for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
1674 for (int j=i.size()-1; j>=0; j--) {
1675 UpdateRecord ur = i.get(j);
1676 if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
1677 if (removedRecs == null) {
1678 removedRecs = new ArrayList<Receiver>();
1679 }
1680 if (!removedRecs.contains(ur.mReceiver)) {
1681 removedRecs.add(ur.mReceiver);
1682 }
1683 }
1684 }
1685 }
1686 ArrayList<ProximityAlert> removedAlerts = null;
1687 for (ProximityAlert i : mProximityAlerts.values()) {
1688 if (i.mUid == uid) {
1689 if (removedAlerts == null) {
1690 removedAlerts = new ArrayList<ProximityAlert>();
1691 }
1692 if (!removedAlerts.contains(i)) {
1693 removedAlerts.add(i);
1694 }
1695 }
1696 }
1697 if (removedRecs != null) {
1698 for (int i=removedRecs.size()-1; i>=0; i--) {
1699 removeUpdatesLocked(removedRecs.get(i));
1700 }
1701 }
1702 if (removedAlerts != null) {
1703 for (int i=removedAlerts.size()-1; i>=0; i--) {
1704 removeProximityAlertLocked(removedAlerts.get(i).mIntent);
1705 }
1706 }
1707 }
1708 }
1709 }
1710 }
1711 }
1712
1713 private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
1714 @Override public void onReceive(Context context, Intent intent) {
1715 String action = intent.getAction();
1716
Mike Lockwoodf113fbe2009-04-06 05:17:28 -07001717 if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001718 boolean noConnectivity =
1719 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1720 if (!noConnectivity) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001721 mNetworkState = LocationProvider.AVAILABLE;
1722 } else {
1723 mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001724 }
1725
1726 // Notify location providers of current network state
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001727 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001728 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1729 for (LocationProviderImpl provider : providers) {
1730 if (provider.requiresNetwork()) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001731 provider.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001732 }
1733 }
1734 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001735 } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
1736
1737 final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED,
1738 false);
1739
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001740 synchronized (mLock) {
Mike Lockwood98cb6672009-04-17 18:03:44 -04001741 if (!enabled) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001742 // When GPS is disabled, we are OK to release wake-lock
1743 mWakeLockGpsReceived = true;
1744 }
1745 }
1746 }
1747
1748 }
1749 }
1750
1751 // Wake locks
1752
1753 private void updateWakelockStatusLocked(boolean screenOn) {
1754 log("updateWakelockStatus(): " + screenOn);
1755
Amith Yamasanie1ccba22009-04-02 11:40:25 -07001756 long callerId = Binder.clearCallingIdentity();
1757
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001758 boolean needsLock = false;
1759 long minTime = Integer.MAX_VALUE;
1760
1761 if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) {
1762 needsLock = true;
1763 minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime);
1764 }
1765
1766 if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) {
1767 needsLock = true;
1768 minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001769 }
1770
1771 mScreenOn = screenOn;
1772
1773 PendingIntent sender =
1774 PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0);
1775
1776 // Cancel existing alarm
1777 log("Cancelling existing alarm");
1778 mAlarmManager.cancel(sender);
1779
1780 if (needsLock && !mScreenOn) {
1781 long now = SystemClock.elapsedRealtime();
1782 mAlarmManager.set(
1783 AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender);
1784 mAlarmInterval = minTime;
1785 log("Creating a new wakelock alarm with minTime = " + minTime);
1786 } else {
1787 log("No need for alarm");
1788 mAlarmInterval = -1;
1789
1790 // Clear out existing wakelocks
1791 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1792 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1793 releaseWakeLockLocked();
1794 }
Amith Yamasanie1ccba22009-04-02 11:40:25 -07001795 Binder.restoreCallingIdentity(callerId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001796 }
1797
1798 private void acquireWakeLockLocked() {
1799 try {
1800 acquireWakeLockXLocked();
1801 } catch (Exception e) {
1802 // This is to catch a runtime exception thrown when we try to release an
1803 // already released lock.
1804 Log.e(TAG, "exception in acquireWakeLock()", e);
1805 }
1806 }
1807
1808 private void acquireWakeLockXLocked() {
1809 if (mWakeLock.isHeld()) {
1810 log("Must release wakelock before acquiring");
1811 mWakeLockAcquireTime = 0;
1812 mWakeLock.release();
1813 }
1814
1815 boolean networkActive = (mNetworkLocationProvider != null)
1816 && mNetworkLocationProvider.isLocationTracking();
1817 boolean gpsActive = (mGpsLocationProvider != null)
1818 && mGpsLocationProvider.isLocationTracking();
1819
1820 boolean needsLock = networkActive || gpsActive;
1821 if (!needsLock) {
1822 log("No need for Lock!");
1823 return;
1824 }
1825
1826 mWakeLockGpsReceived = !gpsActive;
1827 mWakeLockNetworkReceived = !networkActive;
1828
1829 // Acquire wake lock
1830 mWakeLock.acquire();
1831 mWakeLockAcquireTime = SystemClock.elapsedRealtime();
1832 log("Acquired wakelock");
1833
Mike Lockwood6de31542009-04-21 12:13:35 -07001834 if (mNetworkLocationProvider != null) {
1835 mNetworkLocationProvider.wakeLockAcquired();
1836 }
1837 if (mGpsLocationProvider != null) {
1838 mGpsLocationProvider.wakeLockAcquired();
1839 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001840 }
1841
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001842 private void releaseWakeLockLocked() {
1843 try {
1844 releaseWakeLockXLocked();
1845 } catch (Exception e) {
1846 // This is to catch a runtime exception thrown when we try to release an
1847 // already released lock.
1848 Log.e(TAG, "exception in releaseWakeLock()", e);
1849 }
1850 }
1851
1852 private void releaseWakeLockXLocked() {
Mike Lockwood6de31542009-04-21 12:13:35 -07001853 if (mNetworkLocationProvider != null) {
1854 mNetworkLocationProvider.wakeLockReleased();
1855 }
1856 if (mGpsLocationProvider != null) {
1857 mGpsLocationProvider.wakeLockReleased();
1858 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001859
1860 // Release wake lock
1861 mWakeLockAcquireTime = 0;
1862 if (mWakeLock.isHeld()) {
1863 log("Released wakelock");
1864 mWakeLock.release();
1865 } else {
1866 log("Can't release wakelock again!");
1867 }
1868 }
1869
1870 // Geocoder
1871
1872 public String getFromLocation(double latitude, double longitude, int maxResults,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001873 String language, String country, String variant, String appName, List<Address> addrs) {
1874 if (mGeocodeProvider != null) {
1875 try {
1876 return mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, language, country,
1877 variant, appName, addrs);
1878 } catch (RemoteException e) {
1879 Log.e(TAG, "getFromLocation failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001880 }
1881 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001882 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001883 }
1884
Mike Lockwooda55c3212009-04-15 11:10:11 -04001885
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001886 public String getFromLocationName(String locationName,
Mike Lockwooda55c3212009-04-15 11:10:11 -04001887 double lowerLeftLatitude, double lowerLeftLongitude,
1888 double upperRightLatitude, double upperRightLongitude, int maxResults,
1889 String language, String country, String variant, String appName, List<Address> addrs) {
1890
1891 if (mGeocodeProvider != null) {
1892 try {
1893 return mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
1894 lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1895 maxResults, language, country, variant, appName, addrs);
1896 } catch (RemoteException e) {
1897 Log.e(TAG, "getFromLocationName failed", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001898 }
1899 }
Mike Lockwooda55c3212009-04-15 11:10:11 -04001900 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001901 }
1902
1903 // Mock Providers
1904
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001905 private void checkMockPermissionsSafe() {
1906 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
1907 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
1908 if (!allowMocks) {
1909 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
1910 }
1911
1912 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1913 PackageManager.PERMISSION_GRANTED) {
1914 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1915 }
1916 }
1917
1918 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1919 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1920 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1921 checkMockPermissionsSafe();
1922
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001923 synchronized (mLock) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001924 MockProvider provider = new MockProvider(name, this,
1925 requiresNetwork, requiresSatellite,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001926 requiresCell, hasMonetaryCost, supportsAltitude,
1927 supportsSpeed, supportsBearing, powerRequirement, accuracy);
1928 if (LocationProviderImpl.getProvider(name) != null) {
1929 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1930 }
1931 LocationProviderImpl.addProvider(provider);
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001932 mMockProviders.put(name, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001933 updateProvidersLocked();
1934 }
1935 }
1936
1937 public void removeTestProvider(String provider) {
1938 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001939 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001940 MockProvider mockProvider = mMockProviders.get(provider);
1941 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001942 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1943 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001944 LocationProviderImpl.removeProvider(mockProvider);
1945 mMockProviders.remove(mockProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001946 updateProvidersLocked();
1947 }
1948 }
1949
1950 public void setTestProviderLocation(String provider, Location loc) {
1951 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001952 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001953 MockProvider mockProvider = mMockProviders.get(provider);
1954 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001955 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1956 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001957 mockProvider.setLocation(loc);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001958 }
1959 }
1960
1961 public void clearTestProviderLocation(String provider) {
1962 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001963 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001964 MockProvider mockProvider = mMockProviders.get(provider);
1965 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001966 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1967 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001968 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001969 }
1970 }
1971
1972 public void setTestProviderEnabled(String provider, boolean enabled) {
1973 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001974 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001975 MockProvider mockProvider = mMockProviders.get(provider);
1976 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001977 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1978 }
1979 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001980 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001981 mEnabledProviders.add(provider);
1982 mDisabledProviders.remove(provider);
1983 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001984 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001985 mEnabledProviders.remove(provider);
1986 mDisabledProviders.add(provider);
1987 }
1988 updateProvidersLocked();
1989 }
1990 }
1991
1992 public void clearTestProviderEnabled(String provider) {
1993 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04001994 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001995 MockProvider mockProvider = mMockProviders.get(provider);
1996 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001997 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1998 }
1999 mEnabledProviders.remove(provider);
2000 mDisabledProviders.remove(provider);
2001 updateProvidersLocked();
2002 }
2003 }
2004
2005 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
2006 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002007 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002008 MockProvider mockProvider = mMockProviders.get(provider);
2009 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002010 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2011 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002012 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002013 }
2014 }
2015
2016 public void clearTestProviderStatus(String provider) {
2017 checkMockPermissionsSafe();
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002018 synchronized (mLock) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002019 MockProvider mockProvider = mMockProviders.get(provider);
2020 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002021 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2022 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002023 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002024 }
2025 }
2026
2027 private void log(String log) {
2028 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2029 Log.d(TAG, log);
2030 }
2031 }
2032
2033 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2034 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2035 != PackageManager.PERMISSION_GRANTED) {
2036 pw.println("Permission Denial: can't dump AlarmManager from from pid="
2037 + Binder.getCallingPid()
2038 + ", uid=" + Binder.getCallingUid());
2039 return;
2040 }
2041
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002042 synchronized (mLock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002043 pw.println("Current Location Manager state:");
2044 pw.println(" sProvidersLoaded=" + sProvidersLoaded);
2045 pw.println(" mGpsLocationProvider=" + mGpsLocationProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002046 pw.println(" mNetworkLocationProvider=" + mNetworkLocationProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002047 pw.println(" mCollector=" + mCollector);
2048 pw.println(" mAlarmInterval=" + mAlarmInterval
2049 + " mScreenOn=" + mScreenOn
2050 + " mWakeLockAcquireTime=" + mWakeLockAcquireTime);
2051 pw.println(" mWakeLockGpsReceived=" + mWakeLockGpsReceived
2052 + " mWakeLockNetworkReceived=" + mWakeLockNetworkReceived);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002053 pw.println(" Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002054 int N = mReceivers.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002055 for (int i=0; i<N; i++) {
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002056 pw.println(" " + mReceivers.get(i));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002057 }
2058 pw.println(" Location Listeners:");
Mike Lockwood2f82c4e2009-04-17 08:24:10 -04002059 for (Receiver i : mReceivers.values()) {
2060 pw.println(" " + i + ":");
2061 for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002062 pw.println(" " + j.getKey() + ":");
2063 j.getValue().dump(pw, " ");
2064 }
2065 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002066 pw.println(" Records by Provider:");
2067 for (Map.Entry<String, ArrayList<UpdateRecord>> i
2068 : mRecordsByProvider.entrySet()) {
2069 pw.println(" " + i.getKey() + ":");
2070 for (UpdateRecord j : i.getValue()) {
2071 pw.println(" " + j + ":");
2072 j.dump(pw, " ");
2073 }
2074 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002075 pw.println(" Last Known Locations:");
2076 for (Map.Entry<String, Location> i
2077 : mLastKnownLocation.entrySet()) {
2078 pw.println(" " + i.getKey() + ":");
2079 i.getValue().dump(new PrintWriterPrinter(pw), " ");
2080 }
2081 if (mProximityAlerts.size() > 0) {
2082 pw.println(" Proximity Alerts:");
2083 for (Map.Entry<PendingIntent, ProximityAlert> i
2084 : mProximityAlerts.entrySet()) {
2085 pw.println(" " + i.getKey() + ":");
2086 i.getValue().dump(pw, " ");
2087 }
2088 }
2089 if (mProximitiesEntered.size() > 0) {
2090 pw.println(" Proximities Entered:");
2091 for (ProximityAlert i : mProximitiesEntered) {
2092 pw.println(" " + i + ":");
2093 i.dump(pw, " ");
2094 }
2095 }
2096 pw.println(" mProximityListener=" + mProximityListener);
2097 if (mEnabledProviders.size() > 0) {
2098 pw.println(" Enabled Providers:");
2099 for (String i : mEnabledProviders) {
2100 pw.println(" " + i);
2101 }
2102
2103 }
2104 if (mDisabledProviders.size() > 0) {
2105 pw.println(" Disabled Providers:");
2106 for (String i : mDisabledProviders) {
2107 pw.println(" " + i);
2108 }
2109
2110 }
2111 if (mMockProviders.size() > 0) {
2112 pw.println(" Mock Providers:");
2113 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002114 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002115 }
2116 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002117 }
2118 }
2119}