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