blob: 938a43d1f83ad0dabbaba19561b74dcc77cb48fb [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;
47import android.location.IGpsStatusListener;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070048import android.location.ILocationCollector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.location.ILocationListener;
50import android.location.ILocationManager;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070051import android.location.ILocationProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.location.Location;
53import android.location.LocationManager;
54import android.location.LocationProvider;
55import android.location.LocationProviderImpl;
56import android.net.ConnectivityManager;
57import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.net.wifi.WifiManager;
59import 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.telephony.TelephonyManager;
70import android.util.Config;
71import android.util.Log;
72import android.util.PrintWriterPrinter;
73import android.util.SparseIntArray;
74
75import com.android.internal.app.IBatteryStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076import com.android.internal.location.GpsLocationProvider;
Mike Lockwoode932f7f2009-04-06 10:51:26 -070077import com.android.internal.location.LocationProviderProxy;
Mike Lockwood7ec434e2009-03-27 07:46:48 -070078import com.android.internal.location.MockProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079import com.android.internal.location.TrackProvider;
80import com.android.server.am.BatteryStatsService;
81
82/**
83 * The service class that manages LocationProviders and issues location
84 * updates and alerts.
85 *
86 * {@hide}
87 */
Mike Lockwoode932f7f2009-04-06 10:51:26 -070088public class LocationManagerService extends ILocationManager.Stub {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 private static final String TAG = "LocationManagerService";
The Android Open Source Project10592532009-03-18 17:39:46 -070090 private static final boolean LOCAL_LOGV = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091
92 // Minimum time interval between last known location writes, in milliseconds.
93 private static final long MIN_LAST_KNOWN_LOCATION_TIME = 60L * 1000L;
94
95 // Max time to hold wake lock for, in milliseconds.
96 private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L;
97
98 // Time to wait after releasing a wake lock for clients to process location update,
99 // in milliseconds.
100 private static final long TIME_AFTER_WAKE_LOCK = 2 * 1000L;
101
102 // The last time a location was written, by provider name.
103 private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
104
105 private static final Pattern PATTERN_COMMA = Pattern.compile(",");
106
107 private static final String ACCESS_FINE_LOCATION =
108 android.Manifest.permission.ACCESS_FINE_LOCATION;
109 private static final String ACCESS_COARSE_LOCATION =
110 android.Manifest.permission.ACCESS_COARSE_LOCATION;
111 private static final String ACCESS_MOCK_LOCATION =
112 android.Manifest.permission.ACCESS_MOCK_LOCATION;
113 private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
114 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
115
116 // Set of providers that are explicitly enabled
117 private final Set<String> mEnabledProviders = new HashSet<String>();
118
119 // Set of providers that are explicitly disabled
120 private final Set<String> mDisabledProviders = new HashSet<String>();
121
122 // Locations, status values, and extras for mock providers
Mike Lockwood7ec434e2009-03-27 07:46:48 -0700123 private final HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124
125 private static boolean sProvidersLoaded = false;
126
127 private final Context mContext;
128 private GpsLocationProvider mGpsLocationProvider;
129 private boolean mGpsNavigating;
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700130 private LocationProviderProxy mNetworkLocationProvider;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 private LocationWorkerHandler mLocationHandler;
132
133 // Handler messages
Mike Lockwood4e50b782009-04-03 08:24:43 -0700134 private static final int MESSAGE_LOCATION_CHANGED = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 private static final int MESSAGE_ACQUIRE_WAKE_LOCK = 2;
136 private static final int MESSAGE_RELEASE_WAKE_LOCK = 3;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137
138 // Alarm manager and wakelock variables
139 private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT";
140 private final static String WAKELOCK_KEY = "LocationManagerService";
141 private final static String WIFILOCK_KEY = "LocationManagerService";
142 private AlarmManager mAlarmManager;
143 private long mAlarmInterval = 0;
144 private boolean mScreenOn = true;
145 private PowerManager.WakeLock mWakeLock = null;
146 private WifiManager.WifiLock mWifiLock = null;
147 private long mWakeLockAcquireTime = 0;
148 private boolean mWakeLockGpsReceived = true;
149 private boolean mWakeLockNetworkReceived = true;
150 private boolean mWifiWakeLockAcquired = false;
151 private boolean mCellWakeLockAcquired = false;
152
153 private final IBatteryStats mBatteryStats;
154
155 /**
156 * Mapping from listener IBinder/PendingIntent to local Listener wrappers.
157 */
158 private final ArrayList<Receiver> mListeners = new ArrayList<Receiver>();
159
160 /**
161 * Used for reporting which UIDs are causing the GPS to run.
162 */
163 private final SparseIntArray mReportedGpsUids = new SparseIntArray();
164 private int mReportedGpsSeq = 0;
165
166 /**
167 * Mapping from listener IBinder/PendingIntent to a map from provider name to UpdateRecord.
168 * This also serves as the lock for our state.
169 */
170 private final HashMap<Receiver,HashMap<String,UpdateRecord>> mLocationListeners =
171 new HashMap<Receiver,HashMap<String,UpdateRecord>>();
172
173 /**
174 * Mapping from listener IBinder/PendingIntent to a map from provider name to last broadcast
175 * location.
176 */
177 private final HashMap<Receiver,HashMap<String,Location>> mLastFixBroadcast =
178 new HashMap<Receiver,HashMap<String,Location>>();
179 private final HashMap<Receiver,HashMap<String,Long>> mLastStatusBroadcast =
180 new HashMap<Receiver,HashMap<String,Long>>();
181
182 /**
183 * Mapping from provider name to all its UpdateRecords
184 */
185 private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
186 new HashMap<String,ArrayList<UpdateRecord>>();
187
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 // Proximity listeners
189 private Receiver mProximityListener = null;
190 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
191 new HashMap<PendingIntent,ProximityAlert>();
192 private HashSet<ProximityAlert> mProximitiesEntered =
193 new HashSet<ProximityAlert>();
194
195 // Last known location for each provider
196 private HashMap<String,Location> mLastKnownLocation =
197 new HashMap<String,Location>();
198
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 // Last known cell service state
200 private TelephonyManager mTelephonyManager;
201
202 // Location collector
203 private ILocationCollector mCollector;
204
205 // Wifi Manager
206 private WifiManager mWifiManager;
207
The Android Open Source Project4df24232009-03-05 14:34:35 -0800208 private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project4df24232009-03-05 14:34:35 -0800209
Mike Lockwood9637d472009-04-02 21:41:57 -0700210 // for Settings change notification
211 private ContentQueryMap mSettings;
212
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 /**
214 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
215 * location updates.
216 */
217 private final class Receiver implements IBinder.DeathRecipient {
218 final ILocationListener mListener;
219 final PendingIntent mPendingIntent;
220 final int mUid;
221 final Object mKey;
222
223 Receiver(ILocationListener listener, int uid) {
224 mListener = listener;
225 mPendingIntent = null;
226 mUid = uid;
227 mKey = listener.asBinder();
228 }
229
230 Receiver(PendingIntent intent, int uid) {
231 mPendingIntent = intent;
232 mListener = null;
233 mUid = uid;
234 mKey = intent;
235 }
236
237 @Override
238 public boolean equals(Object otherObj) {
239 if (otherObj instanceof Receiver) {
240 return mKey.equals(
241 ((Receiver)otherObj).mKey);
242 }
243 return false;
244 }
245
246 @Override
247 public int hashCode() {
248 return mKey.hashCode();
249 }
250
251
252 @Override
253 public String toString() {
254 if (mListener != null) {
255 return "Receiver{"
256 + Integer.toHexString(System.identityHashCode(this))
257 + " uid " + mUid + " Listener " + mKey + "}";
258 } else {
259 return "Receiver{"
260 + Integer.toHexString(System.identityHashCode(this))
261 + " uid " + mUid + " Intent " + mKey + "}";
262 }
263 }
264
265 public boolean isListener() {
266 return mListener != null;
267 }
268
269 public boolean isPendingIntent() {
270 return mPendingIntent != null;
271 }
272
273 public ILocationListener getListener() {
274 if (mListener != null) {
275 return mListener;
276 }
277 throw new IllegalStateException("Request for non-existent listener");
278 }
279
280 public PendingIntent getPendingIntent() {
281 if (mPendingIntent != null) {
282 return mPendingIntent;
283 }
284 throw new IllegalStateException("Request for non-existent intent");
285 }
286
287 public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
288 if (mListener != null) {
289 try {
290 mListener.onStatusChanged(provider, status, extras);
291 } catch (RemoteException e) {
292 return false;
293 }
294 } else {
295 Intent statusChanged = new Intent();
296 statusChanged.putExtras(extras);
297 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
298 try {
299 mPendingIntent.send(mContext, 0, statusChanged, null, null);
300 } catch (PendingIntent.CanceledException e) {
301 return false;
302 }
303 }
304 return true;
305 }
306
307 public boolean callLocationChangedLocked(Location location) {
308 if (mListener != null) {
309 try {
310 mListener.onLocationChanged(location);
311 } catch (RemoteException e) {
312 return false;
313 }
314 } else {
315 Intent locationChanged = new Intent();
316 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
317 try {
318 mPendingIntent.send(mContext, 0, locationChanged, null, null);
319 } catch (PendingIntent.CanceledException e) {
320 return false;
321 }
322 }
323 return true;
324 }
325
326 public void binderDied() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700327 if (LOCAL_LOGV) {
328 Log.v(TAG, "Location listener died");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 }
330 synchronized (mLocationListeners) {
331 removeUpdatesLocked(this);
332 }
333 }
334 }
335
Mike Lockwood9637d472009-04-02 21:41:57 -0700336 private final class SettingsObserver implements Observer {
337 public void update(Observable o, Object arg) {
338 synchronized (mLocationListeners) {
339 updateProvidersLocked();
340 }
341 }
342 }
343
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 private Location readLastKnownLocationLocked(String provider) {
345 Location location = null;
346 String s = null;
347 try {
348 File f = new File(LocationManager.SYSTEM_DIR + "/location."
349 + provider);
350 if (!f.exists()) {
351 return null;
352 }
353 BufferedReader reader = new BufferedReader(new FileReader(f), 256);
354 s = reader.readLine();
355 } catch (IOException e) {
356 Log.w(TAG, "Unable to read last known location", e);
357 }
358
359 if (s == null) {
360 return null;
361 }
362 try {
363 String[] tokens = PATTERN_COMMA.split(s);
364 int idx = 0;
365 long time = Long.parseLong(tokens[idx++]);
366 double latitude = Double.parseDouble(tokens[idx++]);
367 double longitude = Double.parseDouble(tokens[idx++]);
368 double altitude = Double.parseDouble(tokens[idx++]);
369 float bearing = Float.parseFloat(tokens[idx++]);
370 float speed = Float.parseFloat(tokens[idx++]);
371
372 location = new Location(provider);
373 location.setTime(time);
374 location.setLatitude(latitude);
375 location.setLongitude(longitude);
376 location.setAltitude(altitude);
377 location.setBearing(bearing);
378 location.setSpeed(speed);
379 } catch (NumberFormatException nfe) {
380 Log.e(TAG, "NumberFormatException reading last known location", nfe);
381 return null;
382 }
383
384 return location;
385 }
386
387 private void writeLastKnownLocationLocked(String provider,
388 Location location) {
389 long now = SystemClock.elapsedRealtime();
390 Long last = mLastWriteTime.get(provider);
391 if ((last != null)
392 && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
393 return;
394 }
395 mLastWriteTime.put(provider, now);
396
397 StringBuilder sb = new StringBuilder(100);
398 sb.append(location.getTime());
399 sb.append(',');
400 sb.append(location.getLatitude());
401 sb.append(',');
402 sb.append(location.getLongitude());
403 sb.append(',');
404 sb.append(location.getAltitude());
405 sb.append(',');
406 sb.append(location.getBearing());
407 sb.append(',');
408 sb.append(location.getSpeed());
409
410 FileWriter writer = null;
411 try {
412 File d = new File(LocationManager.SYSTEM_DIR);
413 if (!d.exists()) {
414 if (!d.mkdirs()) {
415 Log.w(TAG, "Unable to create directory to write location");
416 return;
417 }
418 }
419 File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
420 writer = new FileWriter(f);
421 writer.write(sb.toString());
422 } catch (IOException e) {
423 Log.w(TAG, "Unable to write location", e);
424 } finally {
425 if (writer != null) {
426 try {
427 writer.close();
428 } catch (IOException e) {
429 Log.w(TAG, "Exception closing file", e);
430 }
431 }
432 }
433 }
434
435 /**
436 * Load providers from /data/location/<provider_name>/
437 * class
438 * kml
439 * nmea
440 * track
441 * location
442 * properties
443 */
444 private void loadProviders() {
445 synchronized (mLocationListeners) {
446 if (sProvidersLoaded) {
447 return;
448 }
449
450 // Load providers
451 loadProvidersLocked();
452 sProvidersLoaded = true;
453 }
454 }
455
456 private void loadProvidersLocked() {
457 try {
458 _loadProvidersLocked();
459 } catch (Exception e) {
460 Log.e(TAG, "Exception loading providers:", e);
461 }
462 }
463
464 private void _loadProvidersLocked() {
465 // Attempt to load "real" providers first
466 if (GpsLocationProvider.isSupported()) {
467 // Create a gps location provider
Mike Lockwood4e50b782009-04-03 08:24:43 -0700468 mGpsLocationProvider = new GpsLocationProvider(mContext, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 LocationProviderImpl.addProvider(mGpsLocationProvider);
470 }
471
472 // Load fake providers if real providers are not available
473 File f = new File(LocationManager.PROVIDER_DIR);
474 if (f.isDirectory()) {
475 File[] subdirs = f.listFiles();
476 for (int i = 0; i < subdirs.length; i++) {
477 if (!subdirs[i].isDirectory()) {
478 continue;
479 }
480
481 String name = subdirs[i].getName();
482
The Android Open Source Project10592532009-03-18 17:39:46 -0700483 if (LOCAL_LOGV) {
484 Log.v(TAG, "Found dir " + subdirs[i].getAbsolutePath());
485 Log.v(TAG, "name = " + name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 }
487
488 // Don't create a fake provider if a real provider exists
489 if (LocationProviderImpl.getProvider(name) == null) {
490 LocationProviderImpl provider = null;
491 try {
492 File classFile = new File(subdirs[i], "class");
493 // Look for a 'class' file
494 provider = LocationProviderImpl.loadFromClass(classFile);
495
496 // Look for an 'kml', 'nmea', or 'track' file
497 if (provider == null) {
498 // Load properties from 'properties' file, if present
499 File propertiesFile = new File(subdirs[i], "properties");
500
501 if (propertiesFile.exists()) {
Mike Lockwood4e50b782009-04-03 08:24:43 -0700502 provider = new TrackProvider(name, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 ((TrackProvider)provider).readProperties(propertiesFile);
504
505 File kmlFile = new File(subdirs[i], "kml");
506 if (kmlFile.exists()) {
507 ((TrackProvider) provider).readKml(kmlFile);
508 } else {
509 File nmeaFile = new File(subdirs[i], "nmea");
510 if (nmeaFile.exists()) {
511 ((TrackProvider) provider).readNmea(name, nmeaFile);
512 } else {
513 File trackFile = new File(subdirs[i], "track");
514 if (trackFile.exists()) {
515 ((TrackProvider) provider).readTrack(trackFile);
516 }
517 }
518 }
519 }
520 }
521 if (provider != null) {
522 LocationProviderImpl.addProvider(provider);
523 }
524 // Grab the initial location of a TrackProvider and
525 // store it as the last known location for that provider
526 if (provider instanceof TrackProvider) {
527 TrackProvider tp = (TrackProvider) provider;
528 mLastKnownLocation.put(tp.getName(), tp.getInitialLocation());
529 }
530 } catch (Exception e) {
531 Log.e(TAG, "Exception loading provder " + name, e);
532 }
533 }
534 }
535 }
536
537 updateProvidersLocked();
538 }
539
540 /**
541 * @param context the context that the LocationManagerService runs in
542 */
543 public LocationManagerService(Context context) {
544 super();
545 mContext = context;
546 mLocationHandler = new LocationWorkerHandler();
547
The Android Open Source Project10592532009-03-18 17:39:46 -0700548 if (LOCAL_LOGV) {
549 Log.v(TAG, "Constructed LocationManager Service");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 }
551
552 // Alarm manager, needs to be done before calling loadProviders() below
553 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
554
555 // Create a wake lock, needs to be done before calling loadProviders() below
556 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
557 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
558
559 // Battery statistics service to be notified when GPS turns on or off
560 mBatteryStats = BatteryStatsService.getService();
561
562 // Load providers
563 loadProviders();
564
565 // Listen for Radio changes
566 mTelephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567
568 // Register for Network (Wifi or Mobile) updates
569 NetworkStateBroadcastReceiver networkReceiver = new NetworkStateBroadcastReceiver();
570 IntentFilter networkIntentFilter = new IntentFilter();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
572 networkIntentFilter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
573 context.registerReceiver(networkReceiver, networkIntentFilter);
574
575 // Register for power updates
576 PowerStateBroadcastReceiver powerStateReceiver = new PowerStateBroadcastReceiver();
577 IntentFilter intentFilter = new IntentFilter();
578 intentFilter.addAction(ALARM_INTENT);
579 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
580 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
582 intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
583 context.registerReceiver(powerStateReceiver, intentFilter);
584
Mike Lockwood9637d472009-04-02 21:41:57 -0700585 // listen for settings changes
586 ContentResolver resolver = mContext.getContentResolver();
587 Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
588 "(" + Settings.System.NAME + "=?)",
589 new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
590 null);
591 mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler);
592 SettingsObserver settingsObserver = new SettingsObserver();
593 mSettings.addObserver(settingsObserver);
594
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 // Get the wifi manager
596 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
597
598 // Create a wifi lock for future use
599 mWifiLock = getWifiWakelockLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 }
601
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700602 public void setNetworkLocationProvider(ILocationProvider provider) {
603 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
604 throw new SecurityException(
605 "Installing location providers outside of the system is not supported");
606 }
607
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 synchronized (mLocationListeners) {
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700609 mNetworkLocationProvider =
610 new LocationProviderProxy(LocationManager.NETWORK_PROVIDER, this, provider);
611 mNetworkLocationProvider.addListener(getPackageNames());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 LocationProviderImpl.addProvider(mNetworkLocationProvider);
613 updateProvidersLocked();
The Android Open Source Project4df24232009-03-05 14:34:35 -0800614
615 // notify NetworkLocationProvider of any events it might have missed
Mike Lockwoodf113fbe2009-04-06 05:17:28 -0700616 mNetworkLocationProvider.updateNetworkState(mNetworkState);
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700617 mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 }
619 }
620
621 public void setLocationCollector(ILocationCollector collector) {
Mike Lockwoode932f7f2009-04-06 10:51:26 -0700622 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
623 throw new SecurityException(
624 "Installing location collectors outside of the system is not supported");
625 }
626
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 synchronized (mLocationListeners) {
628 mCollector = collector;
629 if (mGpsLocationProvider != null) {
630 mGpsLocationProvider.setLocationCollector(mCollector);
631 }
632 }
633 }
634
635 private WifiManager.WifiLock getWifiWakelockLocked() {
636 if (mWifiLock == null && mWifiManager != null) {
637 mWifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, WIFILOCK_KEY);
638 mWifiLock.setReferenceCounted(false);
639 }
640 return mWifiLock;
641 }
642
643 private boolean isAllowedBySettingsLocked(String provider) {
644 if (mEnabledProviders.contains(provider)) {
645 return true;
646 }
647 if (mDisabledProviders.contains(provider)) {
648 return false;
649 }
650 // Use system settings
651 ContentResolver resolver = mContext.getContentResolver();
652 String allowedProviders = Settings.Secure.getString(resolver,
653 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
654
655 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
656 }
657
658 private void checkPermissionsSafe(String provider) {
659 if (LocationManager.GPS_PROVIDER.equals(provider)
660 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
661 != PackageManager.PERMISSION_GRANTED)) {
662 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
663 }
664 if (LocationManager.NETWORK_PROVIDER.equals(provider)
665 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
666 != PackageManager.PERMISSION_GRANTED)
667 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
668 != PackageManager.PERMISSION_GRANTED)) {
669 throw new SecurityException(
670 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
671 }
672 }
673
674 private boolean isAllowedProviderSafe(String provider) {
675 if (LocationManager.GPS_PROVIDER.equals(provider)
676 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
677 != PackageManager.PERMISSION_GRANTED)) {
678 return false;
679 }
680 if (LocationManager.NETWORK_PROVIDER.equals(provider)
681 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
682 != PackageManager.PERMISSION_GRANTED)
683 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
684 != PackageManager.PERMISSION_GRANTED)) {
685 return false;
686 }
687
688 return true;
689 }
690
691 private String[] getPackageNames() {
692 // Since a single UID may correspond to multiple packages, this can only be used as an
693 // approximation for tracking
694 return mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
695 }
696
697 public List<String> getAllProviders() {
698 try {
699 synchronized (mLocationListeners) {
700 return _getAllProvidersLocked();
701 }
702 } catch (SecurityException se) {
703 throw se;
704 } catch (Exception e) {
705 Log.e(TAG, "getAllProviders got exception:", e);
706 return null;
707 }
708 }
709
710 private List<String> _getAllProvidersLocked() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700711 if (LOCAL_LOGV) {
712 Log.v(TAG, "getAllProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800713 }
714 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
715 ArrayList<String> out = new ArrayList<String>(providers.size());
716
717 for (LocationProviderImpl p : providers) {
718 out.add(p.getName());
719 }
720 return out;
721 }
722
723 public List<String> getProviders(boolean enabledOnly) {
724 try {
725 synchronized (mLocationListeners) {
726 return _getProvidersLocked(enabledOnly);
727 }
728 } catch (SecurityException se) {
729 throw se;
730 } catch (Exception e) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700731 Log.e(TAG, "getProviders got exception:", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732 return null;
733 }
734 }
735
736 private List<String> _getProvidersLocked(boolean enabledOnly) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700737 if (LOCAL_LOGV) {
738 Log.v(TAG, "getProviders");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800739 }
740 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
741 ArrayList<String> out = new ArrayList<String>();
742
743 for (LocationProviderImpl p : providers) {
744 String name = p.getName();
745 if (isAllowedProviderSafe(name)) {
746 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
747 continue;
748 }
749 out.add(name);
750 }
751 }
752 return out;
753 }
754
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 private void updateProvidersLocked() {
756 for (LocationProviderImpl p : LocationProviderImpl.getProviders()) {
757 boolean isEnabled = p.isEnabled();
758 String name = p.getName();
759 boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
760
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761 if (isEnabled && !shouldBeEnabled) {
762 updateProviderListenersLocked(name, false);
763 } else if (!isEnabled && shouldBeEnabled) {
764 updateProviderListenersLocked(name, true);
765 }
766
767 }
768 }
769
770 private void updateProviderListenersLocked(String provider, boolean enabled) {
771 int listeners = 0;
772
773 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
774 if (p == null) {
775 return;
776 }
777
778 ArrayList<Receiver> deadReceivers = null;
779
780 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
781 if (records != null) {
782 final int N = records.size();
783 for (int i=0; i<N; i++) {
784 UpdateRecord record = records.get(i);
785 // Sends a notification message to the receiver
786 try {
787 Receiver receiver = record.mReceiver;
788 if (receiver.isListener()) {
789 if (enabled) {
790 receiver.getListener().onProviderEnabled(provider);
791 } else {
792 receiver.getListener().onProviderDisabled(provider);
793 }
794 } else {
795 Intent providerIntent = new Intent();
796 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
797 try {
798 receiver.getPendingIntent().send(mContext, 0,
799 providerIntent, null, null);
800 } catch (PendingIntent.CanceledException e) {
801 if (deadReceivers == null) {
802 deadReceivers = new ArrayList<Receiver>();
803 deadReceivers.add(receiver);
804 }
805 }
806 }
807 } catch (RemoteException e) {
808 // The death link will clean this up.
809 }
810 listeners++;
811 }
812 }
813
814 if (deadReceivers != null) {
815 for (int i=deadReceivers.size()-1; i>=0; i--) {
816 removeUpdatesLocked(deadReceivers.get(i));
817 }
818 }
819
820 if (enabled) {
821 p.enable();
822 if (listeners > 0) {
823 p.setMinTime(getMinTimeLocked(provider));
824 p.enableLocationTracking(true);
825 updateWakelockStatusLocked(mScreenOn);
826 }
827 } else {
828 p.enableLocationTracking(false);
829 if (p == mGpsLocationProvider) {
830 mGpsNavigating = false;
831 reportStopGpsLocked();
832 }
833 p.disable();
834 updateWakelockStatusLocked(mScreenOn);
835 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 }
837
838 private long getMinTimeLocked(String provider) {
839 long minTime = Long.MAX_VALUE;
840 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
841 if (records != null) {
842 for (int i=records.size()-1; i>=0; i--) {
843 minTime = Math.min(minTime, records.get(i).mMinTime);
844 }
845 }
846 return minTime;
847 }
848
849 private class UpdateRecord {
850 final String mProvider;
851 final Receiver mReceiver;
852 final long mMinTime;
853 final float mMinDistance;
854 final int mUid;
855 final String[] mPackages;
856
857 /**
858 * Note: must be constructed with lock held.
859 */
860 UpdateRecord(String provider, long minTime, float minDistance,
861 Receiver receiver, int uid, String[] packages) {
862 mProvider = provider;
863 mReceiver = receiver;
864 mMinTime = minTime;
865 mMinDistance = minDistance;
866 mUid = uid;
867 mPackages = packages;
868
869 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
870 if (records == null) {
871 records = new ArrayList<UpdateRecord>();
872 mRecordsByProvider.put(provider, records);
873 }
874 if (!records.contains(this)) {
875 records.add(this);
876 }
877 }
878
879 /**
880 * Method to be called when a record will no longer be used. Calling this multiple times
881 * must have the same effect as calling it once.
882 */
883 void disposeLocked() {
884 ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
885 records.remove(this);
886 }
887
888 @Override
889 public String toString() {
890 return "UpdateRecord{"
891 + Integer.toHexString(System.identityHashCode(this))
892 + " " + mProvider + " " + mReceiver + "}";
893 }
894
895 void dump(PrintWriter pw, String prefix) {
896 pw.println(prefix + this);
897 pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
898 pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
899 StringBuilder sb = new StringBuilder();
900 if (mPackages != null) {
901 for (int i=0; i<mPackages.length; i++) {
902 if (i > 0) sb.append(", ");
903 sb.append(mPackages[i]);
904 }
905 }
906 pw.println(prefix + "mUid=" + mUid + " mPackages=" + sb);
907 }
908
909 /**
910 * Calls dispose().
911 */
912 @Override protected void finalize() {
913 synchronized (mLocationListeners) {
914 disposeLocked();
915 }
916 }
917 }
918
919 public void requestLocationUpdates(String provider,
920 long minTime, float minDistance, ILocationListener listener) {
921
922 try {
923 synchronized (mLocationListeners) {
924 requestLocationUpdatesLocked(provider, minTime, minDistance,
925 new Receiver(listener, Binder.getCallingUid()));
926 }
927 } catch (SecurityException se) {
928 throw se;
929 } catch (Exception e) {
930 Log.e(TAG, "requestUpdates got exception:", e);
931 }
932 }
933
934 public void requestLocationUpdatesPI(String provider,
935 long minTime, float minDistance, PendingIntent intent) {
936 try {
937 synchronized (mLocationListeners) {
938 requestLocationUpdatesLocked(provider, minTime, minDistance,
939 new Receiver(intent, Binder.getCallingUid()));
940 }
941 } catch (SecurityException se) {
942 throw se;
943 } catch (Exception e) {
944 Log.e(TAG, "requestUpdates got exception:", e);
945 }
946 }
947
948 private void requestLocationUpdatesLocked(String provider,
949 long minTime, float minDistance, Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700950 if (LOCAL_LOGV) {
951 Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 }
953
954 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
955 if (impl == null) {
956 throw new IllegalArgumentException("provider=" + provider);
957 }
958
959 checkPermissionsSafe(provider);
960
961 String[] packages = getPackageNames();
962
963 // so wakelock calls will succeed
964 final int callingUid = Binder.getCallingUid();
965 long identity = Binder.clearCallingIdentity();
966 try {
967 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance,
968 receiver, callingUid, packages);
969 if (!mListeners.contains(receiver)) {
970 try {
971 if (receiver.isListener()) {
972 receiver.getListener().asBinder().linkToDeath(receiver, 0);
973 }
974 mListeners.add(receiver);
975 } catch (RemoteException e) {
976 return;
977 }
978 }
979
980 HashMap<String,UpdateRecord> records = mLocationListeners.get(receiver);
981 if (records == null) {
982 records = new HashMap<String,UpdateRecord>();
983 mLocationListeners.put(receiver, records);
984 }
985 UpdateRecord oldRecord = records.put(provider, r);
986 if (oldRecord != null) {
987 oldRecord.disposeLocked();
988 }
989
990 boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
991 if (isProviderEnabled) {
992 long minTimeForProvider = getMinTimeLocked(provider);
993 impl.setMinTime(minTimeForProvider);
994 impl.enableLocationTracking(true);
995 updateWakelockStatusLocked(mScreenOn);
996
997 if (provider.equals(LocationManager.GPS_PROVIDER)) {
998 if (mGpsNavigating) {
999 updateReportedGpsLocked();
1000 }
1001 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 } else {
1003 try {
1004 // Notify the listener that updates are currently disabled
1005 if (receiver.isListener()) {
1006 receiver.getListener().onProviderDisabled(provider);
1007 }
1008 } catch(RemoteException e) {
1009 Log.w(TAG, "RemoteException calling onProviderDisabled on " +
1010 receiver.getListener());
1011 }
1012 }
1013 } finally {
1014 Binder.restoreCallingIdentity(identity);
1015 }
1016 }
1017
1018 public void removeUpdates(ILocationListener listener) {
1019 try {
1020 synchronized (mLocationListeners) {
1021 removeUpdatesLocked(new Receiver(listener, Binder.getCallingUid()));
1022 }
1023 } catch (SecurityException se) {
1024 throw se;
1025 } catch (Exception e) {
1026 Log.e(TAG, "removeUpdates got exception:", e);
1027 }
1028 }
1029
1030 public void removeUpdatesPI(PendingIntent intent) {
1031 try {
1032 synchronized (mLocationListeners) {
1033 removeUpdatesLocked(new Receiver(intent, Binder.getCallingUid()));
1034 }
1035 } catch (SecurityException se) {
1036 throw se;
1037 } catch (Exception e) {
1038 Log.e(TAG, "removeUpdates got exception:", e);
1039 }
1040 }
1041
1042 private void removeUpdatesLocked(Receiver receiver) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001043 if (LOCAL_LOGV) {
1044 Log.v(TAG, "_removeUpdates: listener = " + receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 }
1046
1047 // so wakelock calls will succeed
1048 final int callingUid = Binder.getCallingUid();
1049 long identity = Binder.clearCallingIdentity();
1050 try {
1051 int idx = mListeners.indexOf(receiver);
1052 if (idx >= 0) {
1053 Receiver myReceiver = mListeners.remove(idx);
1054 if (myReceiver.isListener()) {
1055 myReceiver.getListener().asBinder().unlinkToDeath(myReceiver, 0);
1056 }
1057 }
1058
1059 // Record which providers were associated with this listener
1060 HashSet<String> providers = new HashSet<String>();
1061 HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(receiver);
1062 if (oldRecords != null) {
1063 // Call dispose() on the obsolete update records.
1064 for (UpdateRecord record : oldRecords.values()) {
1065 if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) {
Mike Lockwoode932f7f2009-04-06 10:51:26 -07001066 if (mNetworkLocationProvider != null) {
1067 mNetworkLocationProvider.removeListener(record.mPackages);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001068 }
1069 }
1070 record.disposeLocked();
1071 }
1072 // Accumulate providers
1073 providers.addAll(oldRecords.keySet());
1074 }
1075
1076 mLocationListeners.remove(receiver);
1077 mLastFixBroadcast.remove(receiver);
1078 mLastStatusBroadcast.remove(receiver);
1079
1080 // See if the providers associated with this listener have any
1081 // other listeners; if one does, inform it of the new smallest minTime
1082 // value; if one does not, disable location tracking for it
1083 for (String provider : providers) {
1084 // If provider is already disabled, don't need to do anything
1085 if (!isAllowedBySettingsLocked(provider)) {
1086 continue;
1087 }
1088
1089 boolean hasOtherListener = false;
1090 ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1091 if (recordsForProvider != null && recordsForProvider.size() > 0) {
1092 hasOtherListener = true;
1093 }
1094
1095 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1096 if (p != null) {
1097 if (hasOtherListener) {
1098 p.setMinTime(getMinTimeLocked(provider));
1099 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001100 p.enableLocationTracking(false);
1101 }
1102
1103 if (p == mGpsLocationProvider && mGpsNavigating) {
1104 updateReportedGpsLocked();
1105 }
1106 }
1107 }
1108
1109 updateWakelockStatusLocked(mScreenOn);
1110 } finally {
1111 Binder.restoreCallingIdentity(identity);
1112 }
1113 }
1114
1115 public boolean addGpsStatusListener(IGpsStatusListener listener) {
1116 if (mGpsLocationProvider == null) {
1117 return false;
1118 }
1119 if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1120 PackageManager.PERMISSION_GRANTED) {
1121 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1122 }
1123
1124 try {
1125 mGpsLocationProvider.addGpsStatusListener(listener);
1126 } catch (RemoteException e) {
1127 Log.w(TAG, "RemoteException in addGpsStatusListener");
1128 return false;
1129 }
1130 return true;
1131 }
1132
1133 public void removeGpsStatusListener(IGpsStatusListener listener) {
1134 synchronized (mLocationListeners) {
1135 mGpsLocationProvider.removeGpsStatusListener(listener);
1136 }
1137 }
1138
1139 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1140 // first check for permission to the provider
1141 checkPermissionsSafe(provider);
1142 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1143 if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1144 != PackageManager.PERMISSION_GRANTED)) {
1145 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1146 }
1147
1148 synchronized (mLocationListeners) {
1149 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
1150 if (provider == null) {
1151 return false;
1152 }
1153
1154 return impl.sendExtraCommand(command, extras);
1155 }
1156 }
1157
1158 class ProximityAlert {
1159 final int mUid;
1160 final double mLatitude;
1161 final double mLongitude;
1162 final float mRadius;
1163 final long mExpiration;
1164 final PendingIntent mIntent;
1165 final Location mLocation;
1166
1167 public ProximityAlert(int uid, double latitude, double longitude,
1168 float radius, long expiration, PendingIntent intent) {
1169 mUid = uid;
1170 mLatitude = latitude;
1171 mLongitude = longitude;
1172 mRadius = radius;
1173 mExpiration = expiration;
1174 mIntent = intent;
1175
1176 mLocation = new Location("");
1177 mLocation.setLatitude(latitude);
1178 mLocation.setLongitude(longitude);
1179 }
1180
1181 long getExpiration() {
1182 return mExpiration;
1183 }
1184
1185 PendingIntent getIntent() {
1186 return mIntent;
1187 }
1188
1189 boolean isInProximity(double latitude, double longitude) {
1190 Location loc = new Location("");
1191 loc.setLatitude(latitude);
1192 loc.setLongitude(longitude);
1193
1194 double radius = loc.distanceTo(mLocation);
1195 return radius <= mRadius;
1196 }
1197
1198 @Override
1199 public String toString() {
1200 return "ProximityAlert{"
1201 + Integer.toHexString(System.identityHashCode(this))
1202 + " uid " + mUid + mIntent + "}";
1203 }
1204
1205 void dump(PrintWriter pw, String prefix) {
1206 pw.println(prefix + this);
1207 pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
1208 pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
1209 pw.println(prefix + "mIntent=" + mIntent);
1210 pw.println(prefix + "mLocation:");
1211 mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
1212 }
1213 }
1214
1215 // Listener for receiving locations to trigger proximity alerts
1216 class ProximityListener extends ILocationListener.Stub {
1217
1218 boolean isGpsAvailable = false;
1219
1220 // Note: this is called with the lock held.
1221 public void onLocationChanged(Location loc) {
1222
1223 // If Gps is available, then ignore updates from NetworkLocationProvider
1224 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1225 isGpsAvailable = true;
1226 }
1227 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1228 return;
1229 }
1230
1231 // Process proximity alerts
1232 long now = System.currentTimeMillis();
1233 double latitude = loc.getLatitude();
1234 double longitude = loc.getLongitude();
1235 ArrayList<PendingIntent> intentsToRemove = null;
1236
1237 for (ProximityAlert alert : mProximityAlerts.values()) {
1238 PendingIntent intent = alert.getIntent();
1239 long expiration = alert.getExpiration();
1240
1241 if ((expiration == -1) || (now <= expiration)) {
1242 boolean entered = mProximitiesEntered.contains(alert);
1243 boolean inProximity =
1244 alert.isInProximity(latitude, longitude);
1245 if (!entered && inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001246 if (LOCAL_LOGV) {
1247 Log.v(TAG, "Entered alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001248 }
1249 mProximitiesEntered.add(alert);
1250 Intent enteredIntent = new Intent();
1251 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1252 try {
1253 intent.send(mContext, 0, enteredIntent, null, null);
1254 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001255 if (LOCAL_LOGV) {
1256 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001257 }
1258 if (intentsToRemove == null) {
1259 intentsToRemove = new ArrayList<PendingIntent>();
1260 }
1261 intentsToRemove.add(intent);
1262 }
1263 } else if (entered && !inProximity) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001264 if (LOCAL_LOGV) {
1265 Log.v(TAG, "Exited alert");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001266 }
1267 mProximitiesEntered.remove(alert);
1268 Intent exitedIntent = new Intent();
1269 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1270 try {
1271 intent.send(mContext, 0, exitedIntent, null, null);
1272 } catch (PendingIntent.CanceledException e) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001273 if (LOCAL_LOGV) {
1274 Log.v(TAG, "Canceled proximity alert: " + alert, e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001275 }
1276 if (intentsToRemove == null) {
1277 intentsToRemove = new ArrayList<PendingIntent>();
1278 }
1279 intentsToRemove.add(intent);
1280 }
1281 }
1282 } else {
1283 // Mark alert for expiration
The Android Open Source Project10592532009-03-18 17:39:46 -07001284 if (LOCAL_LOGV) {
1285 Log.v(TAG, "Expiring proximity alert: " + alert);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 }
1287 if (intentsToRemove == null) {
1288 intentsToRemove = new ArrayList<PendingIntent>();
1289 }
1290 intentsToRemove.add(alert.getIntent());
1291 }
1292 }
1293
1294 // Remove expired alerts
1295 if (intentsToRemove != null) {
1296 for (PendingIntent i : intentsToRemove) {
1297 mProximityAlerts.remove(i);
1298 ProximityAlert alert = mProximityAlerts.get(i);
1299 mProximitiesEntered.remove(alert);
1300 }
1301 }
1302
1303 }
1304
1305 // Note: this is called with the lock held.
1306 public void onProviderDisabled(String provider) {
1307 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1308 isGpsAvailable = false;
1309 }
1310 }
1311
1312 // Note: this is called with the lock held.
1313 public void onProviderEnabled(String provider) {
1314 // ignore
1315 }
1316
1317 // Note: this is called with the lock held.
1318 public void onStatusChanged(String provider, int status, Bundle extras) {
1319 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1320 (status != LocationProvider.AVAILABLE)) {
1321 isGpsAvailable = false;
1322 }
1323 }
1324 }
1325
1326 public void addProximityAlert(double latitude, double longitude,
1327 float radius, long expiration, PendingIntent intent) {
1328 try {
1329 synchronized (mLocationListeners) {
1330 addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
1331 }
1332 } catch (SecurityException se) {
1333 throw se;
1334 } catch (Exception e) {
1335 Log.e(TAG, "addProximityAlert got exception:", e);
1336 }
1337 }
1338
1339 private void addProximityAlertLocked(double latitude, double longitude,
1340 float radius, long expiration, PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001341 if (LOCAL_LOGV) {
1342 Log.v(TAG, "addProximityAlert: latitude = " + latitude +
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343 ", longitude = " + longitude +
1344 ", expiration = " + expiration +
1345 ", intent = " + intent);
1346 }
1347
1348 // Require ability to access all providers for now
1349 if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
1350 !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
1351 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1352 }
1353
1354 if (expiration != -1) {
1355 expiration += System.currentTimeMillis();
1356 }
1357 ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
1358 latitude, longitude, radius, expiration, intent);
1359 mProximityAlerts.put(intent, alert);
1360
1361 if (mProximityListener == null) {
1362 mProximityListener = new Receiver(new ProximityListener(), -1);
1363
1364 LocationProvider provider = LocationProviderImpl.getProvider(
1365 LocationManager.GPS_PROVIDER);
1366 if (provider != null) {
1367 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1368 }
1369
1370 provider =
1371 LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER);
1372 if (provider != null) {
1373 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
1374 }
1375 } else if (mGpsNavigating) {
1376 updateReportedGpsLocked();
1377 }
1378 }
1379
1380 public void removeProximityAlert(PendingIntent intent) {
1381 try {
1382 synchronized (mLocationListeners) {
1383 removeProximityAlertLocked(intent);
1384 }
1385 } catch (SecurityException se) {
1386 throw se;
1387 } catch (Exception e) {
1388 Log.e(TAG, "removeProximityAlert got exception:", e);
1389 }
1390 }
1391
1392 private void removeProximityAlertLocked(PendingIntent intent) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001393 if (LOCAL_LOGV) {
1394 Log.v(TAG, "removeProximityAlert: intent = " + intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001395 }
1396
1397 mProximityAlerts.remove(intent);
1398 if (mProximityAlerts.size() == 0) {
1399 removeUpdatesLocked(mProximityListener);
1400 mProximityListener = null;
1401 } else if (mGpsNavigating) {
1402 updateReportedGpsLocked();
1403 }
1404 }
1405
1406 /**
1407 * @return null if the provider does not exits
1408 * @throw SecurityException if the provider is not allowed to be
1409 * accessed by the caller
1410 */
1411 public Bundle getProviderInfo(String provider) {
1412 try {
1413 synchronized (mLocationListeners) {
1414 return _getProviderInfoLocked(provider);
1415 }
1416 } catch (SecurityException se) {
1417 throw se;
1418 } catch (Exception e) {
1419 Log.e(TAG, "_getProviderInfo got exception:", e);
1420 return null;
1421 }
1422 }
1423
1424 private Bundle _getProviderInfoLocked(String provider) {
1425 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1426 if (p == null) {
1427 return null;
1428 }
1429
1430 checkPermissionsSafe(provider);
1431
1432 Bundle b = new Bundle();
1433 b.putBoolean("network", p.requiresNetwork());
1434 b.putBoolean("satellite", p.requiresSatellite());
1435 b.putBoolean("cell", p.requiresCell());
1436 b.putBoolean("cost", p.hasMonetaryCost());
1437 b.putBoolean("altitude", p.supportsAltitude());
1438 b.putBoolean("speed", p.supportsSpeed());
1439 b.putBoolean("bearing", p.supportsBearing());
1440 b.putInt("power", p.getPowerRequirement());
1441 b.putInt("accuracy", p.getAccuracy());
1442
1443 return b;
1444 }
1445
1446 public boolean isProviderEnabled(String provider) {
1447 try {
1448 synchronized (mLocationListeners) {
1449 return _isProviderEnabledLocked(provider);
1450 }
1451 } catch (SecurityException se) {
1452 throw se;
1453 } catch (Exception e) {
1454 Log.e(TAG, "isProviderEnabled got exception:", e);
1455 return false;
1456 }
1457 }
1458
Mike Lockwood4e50b782009-04-03 08:24:43 -07001459 public void setLocation(Location location) {
1460 mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
1461 Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
1462 mLocationHandler.sendMessageAtFrontOfQueue(m);
1463 }
1464
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001465 private boolean _isProviderEnabledLocked(String provider) {
1466 checkPermissionsSafe(provider);
1467
1468 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1469 if (p == null) {
1470 throw new IllegalArgumentException("provider=" + provider);
1471 }
1472 return isAllowedBySettingsLocked(provider);
1473 }
1474
1475 public Location getLastKnownLocation(String provider) {
1476 try {
1477 synchronized (mLocationListeners) {
1478 return _getLastKnownLocationLocked(provider);
1479 }
1480 } catch (SecurityException se) {
1481 throw se;
1482 } catch (Exception e) {
1483 Log.e(TAG, "getLastKnownLocation got exception:", e);
1484 return null;
1485 }
1486 }
1487
1488 private Location _getLastKnownLocationLocked(String provider) {
1489 checkPermissionsSafe(provider);
1490
1491 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1492 if (p == null) {
1493 throw new IllegalArgumentException("provider=" + provider);
1494 }
1495
1496 if (!isAllowedBySettingsLocked(provider)) {
1497 return null;
1498 }
1499
1500 Location location = mLastKnownLocation.get(provider);
1501 if (location == null) {
1502 // Get the persistent last known location for the provider
1503 location = readLastKnownLocationLocked(provider);
1504 if (location != null) {
1505 mLastKnownLocation.put(provider, location);
1506 }
1507 }
1508
1509 return location;
1510 }
1511
1512 private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
1513 // Always broadcast the first update
1514 if (lastLoc == null) {
1515 return true;
1516 }
1517
1518 // Don't broadcast same location again regardless of condition
1519 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1520 if (loc.getTime() == lastLoc.getTime()) {
1521 return false;
1522 }
1523
1524 // Check whether sufficient distance has been traveled
1525 double minDistance = record.mMinDistance;
1526 if (minDistance > 0.0) {
1527 if (loc.distanceTo(lastLoc) <= minDistance) {
1528 return false;
1529 }
1530 }
1531
1532 return true;
1533 }
1534
Mike Lockwood4e50b782009-04-03 08:24:43 -07001535 private void handleLocationChangedLocked(Location location) {
1536 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001537 ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
1538 if (records == null || records.size() == 0) {
1539 return;
1540 }
1541
1542 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1543 if (p == null) {
1544 return;
1545 }
1546
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001547 // Update last known location for provider
Mike Lockwood4e50b782009-04-03 08:24:43 -07001548 Location lastLocation = mLastKnownLocation.get(provider);
1549 if (lastLocation == null) {
1550 mLastKnownLocation.put(provider, new Location(location));
1551 } else {
1552 lastLocation.set(location);
1553 }
1554 writeLastKnownLocationLocked(provider, location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001555
Mike Lockwoode932f7f2009-04-06 10:51:26 -07001556 if (LocationManager.NETWORK_PROVIDER.equals(p.getName())) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001557 mWakeLockNetworkReceived = true;
1558 } else if (p instanceof GpsLocationProvider) {
1559 // Gps location received signal is in NetworkStateBroadcastReceiver
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001560 }
1561
1562 // Fetch latest status update time
1563 long newStatusUpdateTime = p.getStatusUpdateTime();
1564
Mike Lockwood7ec434e2009-03-27 07:46:48 -07001565 // Get latest status
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001566 Bundle extras = new Bundle();
1567 int status = p.getStatus(extras);
1568
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001569 ArrayList<Receiver> deadReceivers = null;
1570
1571 // Broadcast location or status to all listeners
1572 final int N = records.size();
1573 for (int i=0; i<N; i++) {
1574 UpdateRecord r = records.get(i);
1575 Receiver receiver = r.mReceiver;
1576
Mike Lockwood4e50b782009-04-03 08:24:43 -07001577 HashMap<String,Location> map = mLastFixBroadcast.get(receiver);
1578 if (map == null) {
1579 map = new HashMap<String,Location>();
1580 mLastFixBroadcast.put(receiver, map);
1581 }
1582 Location lastLoc = map.get(provider);
1583 if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
1584 if (lastLoc == null) {
1585 lastLoc = new Location(location);
1586 map.put(provider, lastLoc);
1587 } else {
1588 lastLoc.set(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001589 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001590 if (!receiver.callLocationChangedLocked(location)) {
1591 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1592 if (deadReceivers == null) {
1593 deadReceivers = new ArrayList<Receiver>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001594 }
Mike Lockwood4e50b782009-04-03 08:24:43 -07001595 deadReceivers.add(receiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001596 }
1597 }
1598
1599 // Broadcast status message
1600 HashMap<String,Long> statusMap = mLastStatusBroadcast.get(receiver);
1601 if (statusMap == null) {
1602 statusMap = new HashMap<String,Long>();
1603 mLastStatusBroadcast.put(receiver, statusMap);
1604 }
1605 long prevStatusUpdateTime =
1606 (statusMap.get(provider) != null) ? statusMap.get(provider) : 0;
1607
1608 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1609 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1610
1611 statusMap.put(provider, newStatusUpdateTime);
1612 if (!receiver.callStatusChangedLocked(provider, status, extras)) {
1613 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1614 if (deadReceivers == null) {
1615 deadReceivers = new ArrayList<Receiver>();
1616 }
1617 if (!deadReceivers.contains(receiver)) {
1618 deadReceivers.add(receiver);
1619 }
1620 }
1621 }
1622 }
1623
1624 if (deadReceivers != null) {
1625 for (int i=deadReceivers.size()-1; i>=0; i--) {
1626 removeUpdatesLocked(deadReceivers.get(i));
1627 }
1628 }
1629 }
1630
1631 private class LocationWorkerHandler extends Handler {
1632
1633 @Override
1634 public void handleMessage(Message msg) {
1635 try {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001636 if (msg.what == MESSAGE_LOCATION_CHANGED) {
1637 // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001638
1639 synchronized (mLocationListeners) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001640 Location location = (Location) msg.obj;
1641 String provider = location.getProvider();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001642 if (!isAllowedBySettingsLocked(provider)) {
1643 return;
1644 }
1645
1646 // Process the location fix if the screen is on or we're holding a wakelock
1647 if (mScreenOn || (mWakeLockAcquireTime != 0)) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07001648 handleLocationChangedLocked(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001649 }
1650
1651 if ((mWakeLockAcquireTime != 0) &&
1652 (SystemClock.elapsedRealtime() - mWakeLockAcquireTime
1653 > MAX_TIME_FOR_WAKE_LOCK)) {
1654
1655 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1656 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1657
1658 log("LocationWorkerHandler: Exceeded max time for wake lock");
1659 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1660 sendMessageAtFrontOfQueue(m);
1661
1662 } else if (mWakeLockAcquireTime != 0 &&
1663 mWakeLockGpsReceived && mWakeLockNetworkReceived) {
1664
1665 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1666 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1667
1668 log("LocationWorkerHandler: Locations received.");
1669 mWakeLockAcquireTime = 0;
1670 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1671 sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK);
1672 }
1673 }
1674
1675 } else if (msg.what == MESSAGE_ACQUIRE_WAKE_LOCK) {
1676 log("LocationWorkerHandler: Acquire");
1677 synchronized (mLocationListeners) {
1678 acquireWakeLockLocked();
1679 }
1680 } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) {
1681 log("LocationWorkerHandler: Release");
1682
1683 // Update wakelock status so the next alarm is set before releasing wakelock
1684 synchronized (mLocationListeners) {
1685 updateWakelockStatusLocked(mScreenOn);
1686 releaseWakeLockLocked();
1687 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001688 }
1689 } catch (Exception e) {
1690 // Log, don't crash!
1691 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1692 }
1693 }
1694 }
1695
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001696 private class PowerStateBroadcastReceiver extends BroadcastReceiver {
1697 @Override public void onReceive(Context context, Intent intent) {
1698 String action = intent.getAction();
1699
1700 if (action.equals(ALARM_INTENT)) {
1701 synchronized (mLocationListeners) {
1702 log("PowerStateBroadcastReceiver: Alarm received");
1703 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1704 // Have to do this immediately, rather than posting a
1705 // message, so we execute our code while the system
1706 // is holding a wake lock until the alarm broadcast
1707 // is finished.
1708 acquireWakeLockLocked();
1709 }
1710
1711 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1712 log("PowerStateBroadcastReceiver: Screen off");
1713 synchronized (mLocationListeners) {
1714 updateWakelockStatusLocked(false);
1715 }
1716
1717 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1718 log("PowerStateBroadcastReceiver: Screen on");
1719 synchronized (mLocationListeners) {
1720 updateWakelockStatusLocked(true);
1721 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001722 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
1723 || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
1724 synchronized (mLocationListeners) {
1725 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1726 if (uid >= 0) {
1727 ArrayList<Receiver> removedRecs = null;
1728 for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
1729 for (int j=i.size()-1; j>=0; j--) {
1730 UpdateRecord ur = i.get(j);
1731 if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
1732 if (removedRecs == null) {
1733 removedRecs = new ArrayList<Receiver>();
1734 }
1735 if (!removedRecs.contains(ur.mReceiver)) {
1736 removedRecs.add(ur.mReceiver);
1737 }
1738 }
1739 }
1740 }
1741 ArrayList<ProximityAlert> removedAlerts = null;
1742 for (ProximityAlert i : mProximityAlerts.values()) {
1743 if (i.mUid == uid) {
1744 if (removedAlerts == null) {
1745 removedAlerts = new ArrayList<ProximityAlert>();
1746 }
1747 if (!removedAlerts.contains(i)) {
1748 removedAlerts.add(i);
1749 }
1750 }
1751 }
1752 if (removedRecs != null) {
1753 for (int i=removedRecs.size()-1; i>=0; i--) {
1754 removeUpdatesLocked(removedRecs.get(i));
1755 }
1756 }
1757 if (removedAlerts != null) {
1758 for (int i=removedAlerts.size()-1; i>=0; i--) {
1759 removeProximityAlertLocked(removedAlerts.get(i).mIntent);
1760 }
1761 }
1762 }
1763 }
1764 }
1765 }
1766 }
1767
1768 private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
1769 @Override public void onReceive(Context context, Intent intent) {
1770 String action = intent.getAction();
1771
Mike Lockwoodf113fbe2009-04-06 05:17:28 -07001772 if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001773 boolean noConnectivity =
1774 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1775 if (!noConnectivity) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001776 mNetworkState = LocationProvider.AVAILABLE;
1777 } else {
1778 mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001779 }
1780
1781 // Notify location providers of current network state
1782 synchronized (mLocationListeners) {
1783 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1784 for (LocationProviderImpl provider : providers) {
1785 if (provider.requiresNetwork()) {
The Android Open Source Project4df24232009-03-05 14:34:35 -08001786 provider.updateNetworkState(mNetworkState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001787 }
1788 }
1789 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001790 } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
1791
1792 final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED,
1793 false);
1794
1795 synchronized (mLocationListeners) {
1796 if (enabled) {
1797 updateReportedGpsLocked();
1798 mGpsNavigating = true;
1799 } else {
1800 reportStopGpsLocked();
1801 mGpsNavigating = false;
1802 // When GPS is disabled, we are OK to release wake-lock
1803 mWakeLockGpsReceived = true;
1804 }
1805 }
1806 }
1807
1808 }
1809 }
1810
1811 // Wake locks
1812
1813 private void updateWakelockStatusLocked(boolean screenOn) {
1814 log("updateWakelockStatus(): " + screenOn);
1815
Amith Yamasanie1ccba22009-04-02 11:40:25 -07001816 long callerId = Binder.clearCallingIdentity();
1817
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001818 boolean needsLock = false;
1819 long minTime = Integer.MAX_VALUE;
1820
1821 if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) {
1822 needsLock = true;
1823 minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime);
1824 }
1825
1826 if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) {
1827 needsLock = true;
1828 minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime);
1829 if (screenOn) {
1830 startGpsLocked();
1831 } else if (mScreenOn && !screenOn) {
1832 // We just turned the screen off so stop navigating
1833 stopGpsLocked();
1834 }
1835 }
1836
1837 mScreenOn = screenOn;
1838
1839 PendingIntent sender =
1840 PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0);
1841
1842 // Cancel existing alarm
1843 log("Cancelling existing alarm");
1844 mAlarmManager.cancel(sender);
1845
1846 if (needsLock && !mScreenOn) {
1847 long now = SystemClock.elapsedRealtime();
1848 mAlarmManager.set(
1849 AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender);
1850 mAlarmInterval = minTime;
1851 log("Creating a new wakelock alarm with minTime = " + minTime);
1852 } else {
1853 log("No need for alarm");
1854 mAlarmInterval = -1;
1855
1856 // Clear out existing wakelocks
1857 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1858 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1859 releaseWakeLockLocked();
1860 }
Amith Yamasanie1ccba22009-04-02 11:40:25 -07001861 Binder.restoreCallingIdentity(callerId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001862 }
1863
1864 private void acquireWakeLockLocked() {
1865 try {
1866 acquireWakeLockXLocked();
1867 } catch (Exception e) {
1868 // This is to catch a runtime exception thrown when we try to release an
1869 // already released lock.
1870 Log.e(TAG, "exception in acquireWakeLock()", e);
1871 }
1872 }
1873
1874 private void acquireWakeLockXLocked() {
1875 if (mWakeLock.isHeld()) {
1876 log("Must release wakelock before acquiring");
1877 mWakeLockAcquireTime = 0;
1878 mWakeLock.release();
1879 }
1880
1881 boolean networkActive = (mNetworkLocationProvider != null)
1882 && mNetworkLocationProvider.isLocationTracking();
1883 boolean gpsActive = (mGpsLocationProvider != null)
1884 && mGpsLocationProvider.isLocationTracking();
1885
1886 boolean needsLock = networkActive || gpsActive;
1887 if (!needsLock) {
1888 log("No need for Lock!");
1889 return;
1890 }
1891
1892 mWakeLockGpsReceived = !gpsActive;
1893 mWakeLockNetworkReceived = !networkActive;
1894
1895 // Acquire wake lock
1896 mWakeLock.acquire();
1897 mWakeLockAcquireTime = SystemClock.elapsedRealtime();
1898 log("Acquired wakelock");
1899
1900 // Start the gps provider
1901 startGpsLocked();
1902
1903 // Acquire cell lock
1904 if (mCellWakeLockAcquired) {
1905 // Lock is already acquired
1906 } else if (!mWakeLockNetworkReceived) {
1907 mTelephonyManager.enableLocationUpdates();
1908 mCellWakeLockAcquired = true;
1909 } else {
1910 mCellWakeLockAcquired = false;
1911 }
1912
1913 // Notify NetworkLocationProvider
Mike Lockwoode932f7f2009-04-06 10:51:26 -07001914 if (mNetworkLocationProvider != null) {
1915 mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001916 }
1917
1918 // Acquire wifi lock
1919 WifiManager.WifiLock wifiLock = getWifiWakelockLocked();
1920 if (wifiLock != null) {
1921 if (mWifiWakeLockAcquired) {
1922 // Lock is already acquired
1923 } else if (mWifiManager.isWifiEnabled() && !mWakeLockNetworkReceived) {
1924 wifiLock.acquire();
1925 mWifiWakeLockAcquired = true;
1926 } else {
1927 mWifiWakeLockAcquired = false;
1928 Log.w(TAG, "acquireWakeLock(): Unable to get WiFi lock");
1929 }
1930 }
1931 }
1932
1933 private boolean reportGpsUidLocked(int curSeq, int nextSeq, int uid) {
1934 int seq = mReportedGpsUids.get(uid, -1);
1935 if (seq == curSeq) {
1936 // Already reported; propagate to next sequence.
1937 mReportedGpsUids.put(uid, nextSeq);
1938 return true;
1939 } else if (seq != nextSeq) {
1940 try {
1941 // New UID; report it.
1942 mBatteryStats.noteStartGps(uid);
1943 mReportedGpsUids.put(uid, nextSeq);
1944 return true;
1945 } catch (RemoteException e) {
1946 }
1947 }
1948 return false;
1949 }
1950
1951 private void updateReportedGpsLocked() {
1952 if (mGpsLocationProvider == null) {
1953 return;
1954 }
1955
1956 final String name = mGpsLocationProvider.getName();
1957 final int curSeq = mReportedGpsSeq;
1958 final int nextSeq = (curSeq+1) >= 0 ? (curSeq+1) : 0;
1959 mReportedGpsSeq = nextSeq;
1960
1961 ArrayList<UpdateRecord> urs = mRecordsByProvider.get(name);
1962 int num = 0;
1963 final int N = urs.size();
1964 for (int i=0; i<N; i++) {
1965 UpdateRecord ur = urs.get(i);
1966 if (ur.mReceiver == mProximityListener) {
1967 // We don't want the system to take the blame for this one.
1968 continue;
1969 }
1970 if (reportGpsUidLocked(curSeq, nextSeq, ur.mUid)) {
1971 num++;
1972 }
1973 }
1974
1975 for (ProximityAlert pe : mProximityAlerts.values()) {
1976 if (reportGpsUidLocked(curSeq, nextSeq, pe.mUid)) {
1977 num++;
1978 }
1979 }
1980
1981 if (num != mReportedGpsUids.size()) {
1982 // The number of uids is processed is different than the
1983 // array; report any that are no longer active.
1984 for (int i=mReportedGpsUids.size()-1; i>=0; i--) {
1985 if (mReportedGpsUids.valueAt(i) != nextSeq) {
1986 try {
1987 mBatteryStats.noteStopGps(mReportedGpsUids.keyAt(i));
1988 } catch (RemoteException e) {
1989 }
1990 mReportedGpsUids.removeAt(i);
1991 }
1992 }
1993 }
1994 }
1995
1996 private void reportStopGpsLocked() {
1997 int curSeq = mReportedGpsSeq;
1998 for (int i=mReportedGpsUids.size()-1; i>=0; i--) {
1999 if (mReportedGpsUids.valueAt(i) == curSeq) {
2000 try {
2001 mBatteryStats.noteStopGps(mReportedGpsUids.keyAt(i));
2002 } catch (RemoteException e) {
2003 }
2004 }
2005 }
2006 curSeq++;
2007 if (curSeq < 0) curSeq = 0;
2008 mReportedGpsSeq = curSeq;
2009 mReportedGpsUids.clear();
2010 }
2011
2012 private void startGpsLocked() {
2013 boolean gpsActive = (mGpsLocationProvider != null)
2014 && mGpsLocationProvider.isLocationTracking();
2015 if (gpsActive) {
2016 mGpsLocationProvider.startNavigating();
2017 }
2018 }
2019
2020 private void stopGpsLocked() {
2021 boolean gpsActive = mGpsLocationProvider != null
2022 && mGpsLocationProvider.isLocationTracking();
2023 if (gpsActive) {
2024 mGpsLocationProvider.stopNavigating();
2025 }
2026 }
2027
2028 private void releaseWakeLockLocked() {
2029 try {
2030 releaseWakeLockXLocked();
2031 } catch (Exception e) {
2032 // This is to catch a runtime exception thrown when we try to release an
2033 // already released lock.
2034 Log.e(TAG, "exception in releaseWakeLock()", e);
2035 }
2036 }
2037
2038 private void releaseWakeLockXLocked() {
2039 // Release wifi lock
2040 WifiManager.WifiLock wifiLock = getWifiWakelockLocked();
2041 if (wifiLock != null) {
2042 if (mWifiWakeLockAcquired) {
2043 wifiLock.release();
2044 mWifiWakeLockAcquired = false;
2045 }
2046 }
2047
2048 if (!mScreenOn) {
2049 // Stop the gps
2050 stopGpsLocked();
2051 }
2052
2053 // Release cell lock
2054 if (mCellWakeLockAcquired) {
2055 mTelephonyManager.disableLocationUpdates();
2056 mCellWakeLockAcquired = false;
2057 }
2058
2059 // Notify NetworkLocationProvider
Mike Lockwoode932f7f2009-04-06 10:51:26 -07002060 if (mNetworkLocationProvider != null) {
2061 mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002062 }
2063
2064 // Release wake lock
2065 mWakeLockAcquireTime = 0;
2066 if (mWakeLock.isHeld()) {
2067 log("Released wakelock");
2068 mWakeLock.release();
2069 } else {
2070 log("Can't release wakelock again!");
2071 }
2072 }
2073
2074 // Geocoder
2075
2076 public String getFromLocation(double latitude, double longitude, int maxResults,
2077 String language, String country, String variant, String appName, List<Address> addrs) {
2078 synchronized (mLocationListeners) {
Mike Lockwoode932f7f2009-04-06 10:51:26 -07002079 if (mNetworkLocationProvider != null) {
2080 return mNetworkLocationProvider.getFromLocation(latitude, longitude, maxResults,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002081 language, country, variant, appName, addrs);
2082 } else {
2083 return null;
2084 }
2085 }
2086 }
2087
2088 public String getFromLocationName(String locationName,
2089 double lowerLeftLatitude, double lowerLeftLongitude,
2090 double upperRightLatitude, double upperRightLongitude, int maxResults,
2091 String language, String country, String variant, String appName, List<Address> addrs) {
2092 synchronized (mLocationListeners) {
Mike Lockwoode932f7f2009-04-06 10:51:26 -07002093 if (mNetworkLocationProvider != null) {
2094 return mNetworkLocationProvider.getFromLocationName(locationName, lowerLeftLatitude,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002095 lowerLeftLongitude, upperRightLatitude, upperRightLongitude, maxResults,
2096 language, country, variant, appName, addrs);
2097 } else {
2098 return null;
2099 }
2100 }
2101 }
2102
2103 // Mock Providers
2104
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002105 private void checkMockPermissionsSafe() {
2106 boolean allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
2107 Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 1;
2108 if (!allowMocks) {
2109 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
2110 }
2111
2112 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
2113 PackageManager.PERMISSION_GRANTED) {
2114 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
2115 }
2116 }
2117
2118 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
2119 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
2120 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
2121 checkMockPermissionsSafe();
2122
2123 synchronized (mLocationListeners) {
Mike Lockwood4e50b782009-04-03 08:24:43 -07002124 MockProvider provider = new MockProvider(name, this,
2125 requiresNetwork, requiresSatellite,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002126 requiresCell, hasMonetaryCost, supportsAltitude,
2127 supportsSpeed, supportsBearing, powerRequirement, accuracy);
2128 if (LocationProviderImpl.getProvider(name) != null) {
2129 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
2130 }
2131 LocationProviderImpl.addProvider(provider);
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002132 mMockProviders.put(name, provider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002133 updateProvidersLocked();
2134 }
2135 }
2136
2137 public void removeTestProvider(String provider) {
2138 checkMockPermissionsSafe();
2139 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002140 MockProvider mockProvider = mMockProviders.get(provider);
2141 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002142 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2143 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002144 LocationProviderImpl.removeProvider(mockProvider);
2145 mMockProviders.remove(mockProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002146 updateProvidersLocked();
2147 }
2148 }
2149
2150 public void setTestProviderLocation(String provider, Location loc) {
2151 checkMockPermissionsSafe();
2152 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002153 MockProvider mockProvider = mMockProviders.get(provider);
2154 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002155 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2156 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002157 mockProvider.setLocation(loc);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002158 }
2159 }
2160
2161 public void clearTestProviderLocation(String provider) {
2162 checkMockPermissionsSafe();
2163 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002164 MockProvider mockProvider = mMockProviders.get(provider);
2165 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002166 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2167 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002168 mockProvider.clearLocation();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002169 }
2170 }
2171
2172 public void setTestProviderEnabled(String provider, boolean enabled) {
2173 checkMockPermissionsSafe();
2174 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002175 MockProvider mockProvider = mMockProviders.get(provider);
2176 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002177 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2178 }
2179 if (enabled) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002180 mockProvider.enable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002181 mEnabledProviders.add(provider);
2182 mDisabledProviders.remove(provider);
2183 } else {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002184 mockProvider.disable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002185 mEnabledProviders.remove(provider);
2186 mDisabledProviders.add(provider);
2187 }
2188 updateProvidersLocked();
2189 }
2190 }
2191
2192 public void clearTestProviderEnabled(String provider) {
2193 checkMockPermissionsSafe();
2194 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002195 MockProvider mockProvider = mMockProviders.get(provider);
2196 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002197 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2198 }
2199 mEnabledProviders.remove(provider);
2200 mDisabledProviders.remove(provider);
2201 updateProvidersLocked();
2202 }
2203 }
2204
2205 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
2206 checkMockPermissionsSafe();
2207 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002208 MockProvider mockProvider = mMockProviders.get(provider);
2209 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002210 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2211 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002212 mockProvider.setStatus(status, extras, updateTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002213 }
2214 }
2215
2216 public void clearTestProviderStatus(String provider) {
2217 checkMockPermissionsSafe();
2218 synchronized (mLocationListeners) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002219 MockProvider mockProvider = mMockProviders.get(provider);
2220 if (mockProvider == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002221 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2222 }
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002223 mockProvider.clearStatus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002224 }
2225 }
2226
2227 private void log(String log) {
2228 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2229 Log.d(TAG, log);
2230 }
2231 }
2232
2233 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2234 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
2235 != PackageManager.PERMISSION_GRANTED) {
2236 pw.println("Permission Denial: can't dump AlarmManager from from pid="
2237 + Binder.getCallingPid()
2238 + ", uid=" + Binder.getCallingUid());
2239 return;
2240 }
2241
2242 synchronized (mLocationListeners) {
2243 pw.println("Current Location Manager state:");
2244 pw.println(" sProvidersLoaded=" + sProvidersLoaded);
2245 pw.println(" mGpsLocationProvider=" + mGpsLocationProvider);
2246 pw.println(" mGpsNavigating=" + mGpsNavigating);
2247 pw.println(" mNetworkLocationProvider=" + mNetworkLocationProvider);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002248 pw.println(" mCollector=" + mCollector);
2249 pw.println(" mAlarmInterval=" + mAlarmInterval
2250 + " mScreenOn=" + mScreenOn
2251 + " mWakeLockAcquireTime=" + mWakeLockAcquireTime);
2252 pw.println(" mWakeLockGpsReceived=" + mWakeLockGpsReceived
2253 + " mWakeLockNetworkReceived=" + mWakeLockNetworkReceived);
2254 pw.println(" mWifiWakeLockAcquired=" + mWifiWakeLockAcquired
2255 + " mCellWakeLockAcquired=" + mCellWakeLockAcquired);
2256 pw.println(" Listeners:");
2257 int N = mListeners.size();
2258 for (int i=0; i<N; i++) {
2259 pw.println(" " + mListeners.get(i));
2260 }
2261 pw.println(" Location Listeners:");
2262 for (Map.Entry<Receiver, HashMap<String,UpdateRecord>> i
2263 : mLocationListeners.entrySet()) {
2264 pw.println(" " + i.getKey() + ":");
2265 for (Map.Entry<String,UpdateRecord> j : i.getValue().entrySet()) {
2266 pw.println(" " + j.getKey() + ":");
2267 j.getValue().dump(pw, " ");
2268 }
2269 }
2270 pw.println(" Last Fix Broadcasts:");
2271 for (Map.Entry<Receiver, HashMap<String,Location>> i
2272 : mLastFixBroadcast.entrySet()) {
2273 pw.println(" " + i.getKey() + ":");
2274 for (Map.Entry<String,Location> j : i.getValue().entrySet()) {
2275 pw.println(" " + j.getKey() + ":");
2276 j.getValue().dump(new PrintWriterPrinter(pw), " ");
2277 }
2278 }
2279 pw.println(" Last Status Broadcasts:");
2280 for (Map.Entry<Receiver, HashMap<String,Long>> i
2281 : mLastStatusBroadcast.entrySet()) {
2282 pw.println(" " + i.getKey() + ":");
2283 for (Map.Entry<String,Long> j : i.getValue().entrySet()) {
2284 pw.println(" " + j.getKey() + " -> 0x"
2285 + Long.toHexString(j.getValue()));
2286 }
2287 }
2288 pw.println(" Records by Provider:");
2289 for (Map.Entry<String, ArrayList<UpdateRecord>> i
2290 : mRecordsByProvider.entrySet()) {
2291 pw.println(" " + i.getKey() + ":");
2292 for (UpdateRecord j : i.getValue()) {
2293 pw.println(" " + j + ":");
2294 j.dump(pw, " ");
2295 }
2296 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002297 pw.println(" Last Known Locations:");
2298 for (Map.Entry<String, Location> i
2299 : mLastKnownLocation.entrySet()) {
2300 pw.println(" " + i.getKey() + ":");
2301 i.getValue().dump(new PrintWriterPrinter(pw), " ");
2302 }
2303 if (mProximityAlerts.size() > 0) {
2304 pw.println(" Proximity Alerts:");
2305 for (Map.Entry<PendingIntent, ProximityAlert> i
2306 : mProximityAlerts.entrySet()) {
2307 pw.println(" " + i.getKey() + ":");
2308 i.getValue().dump(pw, " ");
2309 }
2310 }
2311 if (mProximitiesEntered.size() > 0) {
2312 pw.println(" Proximities Entered:");
2313 for (ProximityAlert i : mProximitiesEntered) {
2314 pw.println(" " + i + ":");
2315 i.dump(pw, " ");
2316 }
2317 }
2318 pw.println(" mProximityListener=" + mProximityListener);
2319 if (mEnabledProviders.size() > 0) {
2320 pw.println(" Enabled Providers:");
2321 for (String i : mEnabledProviders) {
2322 pw.println(" " + i);
2323 }
2324
2325 }
2326 if (mDisabledProviders.size() > 0) {
2327 pw.println(" Disabled Providers:");
2328 for (String i : mDisabledProviders) {
2329 pw.println(" " + i);
2330 }
2331
2332 }
2333 if (mMockProviders.size() > 0) {
2334 pw.println(" Mock Providers:");
2335 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
Mike Lockwood7ec434e2009-03-27 07:46:48 -07002336 i.getValue().dump(pw, " ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002337 }
2338 }
2339 pw.println(" Reported GPS UIDs @ seq " + mReportedGpsSeq + ":");
2340 N = mReportedGpsUids.size();
2341 for (int i=0; i<N; i++) {
2342 pw.println(" UID " + mReportedGpsUids.keyAt(i)
2343 + " seq=" + mReportedGpsUids.valueAt(i));
2344 }
2345 }
2346 }
2347}