blob: 1bf60d4bc5df3dd7f568b87656b17217c7a731d2 [file] [log] [blame]
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001/*
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.FileReader;
22import java.io.FileWriter;
23import java.io.IOException;
24import java.util.ArrayList;
25import java.util.HashMap;
26import java.util.HashSet;
27import java.util.List;
28import java.util.Locale;
29import java.util.Set;
30import java.util.regex.Pattern;
31
32import android.app.AlarmManager;
33import android.app.PendingIntent;
34import android.content.BroadcastReceiver;
35import android.content.ContentResolver;
36import android.content.Context;
37import android.content.Intent;
38import android.content.IntentFilter;
39import android.content.pm.PackageManager;
40import android.location.Address;
41import android.location.IGpsStatusListener;
42import android.location.ILocationListener;
43import android.location.ILocationManager;
44import android.location.Location;
45import android.location.LocationManager;
46import android.location.LocationProvider;
47import android.location.LocationProviderImpl;
48import android.net.ConnectivityManager;
49import android.net.wifi.ScanResult;
50import android.net.wifi.WifiManager;
51import android.os.Binder;
52import android.os.Bundle;
53import android.os.Handler;
54import android.os.IBinder;
55import android.os.Message;
56import android.os.PowerManager;
57import android.os.RemoteException;
58import android.os.SystemClock;
59import android.provider.Settings;
60import android.telephony.CellLocation;
61import android.telephony.PhoneStateListener;
62import android.telephony.ServiceState;
63import android.telephony.TelephonyManager;
64import android.util.Config;
65import android.util.Log;
66
67import com.android.internal.location.CellState;
68import com.android.internal.location.GpsLocationProvider;
69import com.android.internal.location.LocationCollector;
70import com.android.internal.location.LocationMasfClient;
71import com.android.internal.location.NetworkLocationProvider;
72import com.android.internal.location.TrackProvider;
73
74/**
75 * The service class that manages LocationProviders and issues location
76 * updates and alerts.
77 *
78 * {@hide}
79 */
80public class LocationManagerService extends ILocationManager.Stub {
81 private static final String TAG = "LocationManagerService";
82
83 // Minimum time interval between last known location writes, in milliseconds.
84 private static final long MIN_LAST_KNOWN_LOCATION_TIME = 60L * 1000L;
85
86 // Max time to hold wake lock for, in milliseconds.
87 private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L;
88
89 // Time to wait after releasing a wake lock for clients to process location update,
90 // in milliseconds.
91 private static final long TIME_AFTER_WAKE_LOCK = 2 * 1000L;
92
93 // The last time a location was written, by provider name.
94 private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
95
96 private static final Pattern PATTERN_COMMA = Pattern.compile(",");
97
98 private static final String ACCESS_FINE_LOCATION =
99 android.Manifest.permission.ACCESS_FINE_LOCATION;
100 private static final String ACCESS_COARSE_LOCATION =
101 android.Manifest.permission.ACCESS_COARSE_LOCATION;
102 private static final String ACCESS_MOCK_LOCATION =
103 android.Manifest.permission.ACCESS_MOCK_LOCATION;
104 private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
105 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
106
107 // Set of providers that are explicitly enabled
108 private final Set<String> mEnabledProviders = new HashSet<String>();
109
110 // Set of providers that are explicitly disabled
111 private final Set<String> mDisabledProviders = new HashSet<String>();
112
113 // Locations, status values, and extras for mock providers
114 HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
115 private final HashMap<String,Location> mMockProviderLocation = new HashMap<String,Location>();
116 private final HashMap<String,Integer> mMockProviderStatus = new HashMap<String,Integer>();
117 private final HashMap<String,Bundle> mMockProviderStatusExtras = new HashMap<String,Bundle>();
118 private final HashMap<String,Long> mMockProviderStatusUpdateTime = new HashMap<String,Long>();
119
120 private static boolean sProvidersLoaded = false;
121
122 private final Context mContext;
123 private GpsLocationProvider mGpsLocationProvider;
124 private NetworkLocationProvider mNetworkLocationProvider;
125 private LocationWorkerHandler mLocationHandler;
126
127 // Handler messages
128 private static final int MESSAGE_HEARTBEAT = 1;
129 private static final int MESSAGE_ACQUIRE_WAKE_LOCK = 2;
130 private static final int MESSAGE_RELEASE_WAKE_LOCK = 3;
131
132 // Alarm manager and wakelock variables
133 private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT";
134 private final static String WAKELOCK_KEY = "LocationManagerService";
135 private final static String WIFILOCK_KEY = "LocationManagerService";
136 private AlarmManager mAlarmManager;
137 private long mAlarmInterval = 0;
138 private boolean mScreenOn = true;
139 private PowerManager.WakeLock mWakeLock = null;
140 private WifiManager.WifiLock mWifiLock = null;
141 private long mWakeLockAcquireTime = 0;
142 private boolean mWakeLockGpsReceived = true;
143 private boolean mWakeLockNetworkReceived = true;
144 private boolean mWifiWakeLockAcquired = false;
145 private boolean mCellWakeLockAcquired = false;
146
147 /**
148 * Mapping from listener IBinder to local Listener wrappers.
149 */
150 private final HashMap<IBinder,Listener> mListeners =
151 new HashMap<IBinder,Listener>();
152
153 /**
154 * Mapping from listener IBinder to a map from provider name to UpdateRecord.
155 */
156 private final HashMap<IBinder,HashMap<String,UpdateRecord>> mLocationListeners =
157 new HashMap<IBinder,HashMap<String,UpdateRecord>>();
158
159 /**
160 * Mapping from listener IBinder to a map from provider name to last broadcast location.
161 */
162 private final HashMap<IBinder,HashMap<String,Location>> mLastFixBroadcast =
163 new HashMap<IBinder,HashMap<String,Location>>();
164 private final HashMap<IBinder,HashMap<String,Long>> mLastStatusBroadcast =
165 new HashMap<IBinder,HashMap<String,Long>>();
166
167 /**
168 * Mapping from provider name to all its UpdateRecords
169 */
170 private final HashMap<String,HashSet<UpdateRecord>> mRecordsByProvider =
171 new HashMap<String,HashSet<UpdateRecord>>();
172
173 /**
174 * Mappings from provider name to object to use for current location. Locations
175 * contained in this list may not always be valid.
176 */
177 private final HashMap<String,Location> mLocationsByProvider =
178 new HashMap<String,Location>();
179
180 // Proximity listeners
181 private ProximityListener mProximityListener = null;
182 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
183 new HashMap<PendingIntent,ProximityAlert>();
184 private HashSet<ProximityAlert> mProximitiesEntered =
185 new HashSet<ProximityAlert>();
186
187 // Last known location for each provider
188 private HashMap<String,Location> mLastKnownLocation =
189 new HashMap<String,Location>();
190
191 // Battery status extras (from com.android.server.BatteryService)
192 private static final String BATTERY_EXTRA_SCALE = "scale";
193 private static final String BATTERY_EXTRA_LEVEL = "level";
194 private static final String BATTERY_EXTRA_PLUGGED = "plugged";
195
196 // Last known cell service state
197 private TelephonyManager mTelephonyManager;
198 private ServiceState mServiceState = new ServiceState();
199
200 // Location collector
201 private LocationCollector mCollector;
202
203 // Location MASF service
204 private LocationMasfClient mMasfClient;
205
206 // Wifi Manager
207 private WifiManager mWifiManager;
208
209 private final class Listener implements IBinder.DeathRecipient {
210 final ILocationListener mListener;
211
212 Listener(ILocationListener listener) {
213 mListener = listener;
214 }
215
216 public void binderDied() {
217 if (Config.LOGD) {
218 Log.d(TAG, "Location listener died");
219 }
220 synchronized (mLocationListeners) {
221 _removeUpdates(mListener);
222 }
223 }
224 }
225
226 private Location readLastKnownLocation(String provider) {
227 Location location = null;
228 String s = null;
229 try {
230 File f = new File(LocationManager.SYSTEM_DIR + "/location."
231 + provider);
232 if (!f.exists()) {
233 return null;
234 }
235 BufferedReader reader = new BufferedReader(new FileReader(f), 256);
236 s = reader.readLine();
237 } catch (IOException e) {
238 Log.w(TAG, "Unable to read last known location", e);
239 }
240
241 if (s == null) {
242 return null;
243 }
244 try {
245 String[] tokens = PATTERN_COMMA.split(s);
246 int idx = 0;
247 long time = Long.parseLong(tokens[idx++]);
248 double latitude = Double.parseDouble(tokens[idx++]);
249 double longitude = Double.parseDouble(tokens[idx++]);
250 double altitude = Double.parseDouble(tokens[idx++]);
251 float bearing = Float.parseFloat(tokens[idx++]);
252 float speed = Float.parseFloat(tokens[idx++]);
253
254 location = new Location(provider);
255 location.setTime(time);
256 location.setLatitude(latitude);
257 location.setLongitude(longitude);
258 location.setAltitude(altitude);
259 location.setBearing(bearing);
260 location.setSpeed(speed);
261 } catch (NumberFormatException nfe) {
262 Log.e(TAG, "NumberFormatException reading last known location", nfe);
263 return null;
264 }
265
266 return location;
267 }
268
269 private void writeLastKnownLocation(String provider,
270 Location location) {
271 long now = SystemClock.elapsedRealtime();
272 Long last = mLastWriteTime.get(provider);
273 if ((last != null)
274 && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
275 return;
276 }
277 mLastWriteTime.put(provider, now);
278
279 StringBuilder sb = new StringBuilder(100);
280 sb.append(location.getTime());
281 sb.append(',');
282 sb.append(location.getLatitude());
283 sb.append(',');
284 sb.append(location.getLongitude());
285 sb.append(',');
286 sb.append(location.getAltitude());
287 sb.append(',');
288 sb.append(location.getBearing());
289 sb.append(',');
290 sb.append(location.getSpeed());
291
292 FileWriter writer = null;
293 try {
294 File d = new File(LocationManager.SYSTEM_DIR);
295 if (!d.exists()) {
296 if (!d.mkdirs()) {
297 Log.w(TAG, "Unable to create directory to write location");
298 return;
299 }
300 }
301 File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
302 writer = new FileWriter(f);
303 writer.write(sb.toString());
304 } catch (IOException e) {
305 Log.w(TAG, "Unable to write location", e);
306 } finally {
307 if (writer != null) {
308 try {
309 writer.close();
310 } catch (IOException e) {
311 Log.w(TAG, "Exception closing file", e);
312 }
313 }
314 }
315 }
316
317 /**
318 * Load providers from /data/location/<provider_name>/
319 * class
320 * kml
321 * nmea
322 * track
323 * location
324 * properties
325 */
326 private void loadProviders() {
327 synchronized (LocationManagerService.class) {
328 if (sProvidersLoaded) {
329 return;
330 }
331
332 // Load providers
333 loadProvidersNoSync();
334 sProvidersLoaded = true;
335 }
336 }
337
338 private void loadProvidersNoSync() {
339 try {
340 _loadProvidersNoSync();
341 } catch (Exception e) {
342 Log.e(TAG, "Exception loading providers:", e);
343 }
344 }
345
346 private void _loadProvidersNoSync() {
347 // Attempt to load "real" providers first
348 if (NetworkLocationProvider.isSupported()) {
349 // Create a network location provider
350 mNetworkLocationProvider = new NetworkLocationProvider(mContext, mMasfClient);
351 LocationProviderImpl.addProvider(mNetworkLocationProvider);
352 }
353
354 if (GpsLocationProvider.isSupported()) {
355 // Create a gps location provider
356 mGpsLocationProvider = new GpsLocationProvider(mContext, mCollector);
357 LocationProviderImpl.addProvider(mGpsLocationProvider);
358 }
359
360 // Load fake providers if real providers are not available
361 File f = new File(LocationManager.PROVIDER_DIR);
362 if (f.isDirectory()) {
363 File[] subdirs = f.listFiles();
364 for (int i = 0; i < subdirs.length; i++) {
365 if (!subdirs[i].isDirectory()) {
366 continue;
367 }
368
369 String name = subdirs[i].getName();
370
371 if (Config.LOGD) {
372 Log.d(TAG, "Found dir " + subdirs[i].getAbsolutePath());
373 Log.d(TAG, "name = " + name);
374 }
375
376 // Don't create a fake provider if a real provider exists
377 if (LocationProviderImpl.getProvider(name) == null) {
378 LocationProviderImpl provider = null;
379 try {
380 File classFile = new File(subdirs[i], "class");
381 // Look for a 'class' file
382 provider = LocationProviderImpl.loadFromClass(classFile);
383
384 // Look for an 'kml', 'nmea', or 'track' file
385 if (provider == null) {
386 // Load properties from 'properties' file, if present
387 File propertiesFile = new File(subdirs[i], "properties");
388
389 if (propertiesFile.exists()) {
390 provider = new TrackProvider(name);
391 ((TrackProvider)provider).readProperties(propertiesFile);
392
393 File kmlFile = new File(subdirs[i], "kml");
394 if (kmlFile.exists()) {
395 ((TrackProvider) provider).readKml(kmlFile);
396 } else {
397 File nmeaFile = new File(subdirs[i], "nmea");
398 if (nmeaFile.exists()) {
399 ((TrackProvider) provider).readNmea(name, nmeaFile);
400 } else {
401 File trackFile = new File(subdirs[i], "track");
402 if (trackFile.exists()) {
403 ((TrackProvider) provider).readTrack(trackFile);
404 }
405 }
406 }
407 }
408 }
409 if (provider != null) {
410 LocationProviderImpl.addProvider(provider);
411 }
412 // Grab the initial location of a TrackProvider and
413 // store it as the last known location for that provider
414 if (provider instanceof TrackProvider) {
415 TrackProvider tp = (TrackProvider) provider;
416 mLastKnownLocation.put(tp.getName(), tp.getInitialLocation());
417 }
418 } catch (Exception e) {
419 Log.e(TAG, "Exception loading provder " + name, e);
420 }
421 }
422 }
423 }
424
425 updateProviders();
426 }
427
428 /**
429 * @param context the context that the LocationManagerService runs in
430 */
431 public LocationManagerService(Context context) {
432 super();
433 mContext = context;
434 mLocationHandler = new LocationWorkerHandler();
435
436 if (Config.LOGD) {
437 Log.d(TAG, "Constructed LocationManager Service");
438 }
439
440 // Initialize the LocationMasfClient
441 mMasfClient = new LocationMasfClient(mContext);
442
443 // Create location collector
444 mCollector = new LocationCollector(mMasfClient);
445
446 // Load providers
447 loadProviders();
448
449 // Listen for Radio changes
450 mTelephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
451 mTelephonyManager.listen(mPhoneStateListener,
452 PhoneStateListener.LISTEN_SERVICE_STATE | PhoneStateListener.LISTEN_CELL_LOCATION);
453
454 // Register for Network (Wifi or Mobile) updates
455 NetworkStateBroadcastReceiver networkReceiver = new NetworkStateBroadcastReceiver();
456 IntentFilter networkIntentFilter = new IntentFilter();
457 networkIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
458 networkIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
459 networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
460 networkIntentFilter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
461 context.registerReceiver(networkReceiver, networkIntentFilter);
462
463 // Alarm manager
464 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
465
466 // Register for power updates
467 PowerStateBroadcastReceiver powerStateReceiver = new PowerStateBroadcastReceiver();
468 IntentFilter intentFilter = new IntentFilter();
469 intentFilter.addAction(ALARM_INTENT);
470 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
471 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
472 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
473 context.registerReceiver(powerStateReceiver, intentFilter);
474
475 // Create a wake lock
476 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
477 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
478
479 // Get the wifi manager
480 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
481
482 // Create a wifi lock for future use
483 mWifiLock = getWifiWakelock();
484
485 // There might be an existing wifi scan available
486 if (mWifiManager != null) {
487 List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
488 if (wifiScanResults != null && wifiScanResults.size() != 0) {
489 if (mNetworkLocationProvider != null) {
490 mNetworkLocationProvider.updateWifiScanResults(wifiScanResults);
491 }
492 }
493 }
494 }
495
496 private WifiManager.WifiLock getWifiWakelock() {
497 if (mWifiLock == null && mWifiManager != null) {
498 mWifiLock = mWifiManager.createWifiLock(WIFILOCK_KEY);
499 mWifiLock.setReferenceCounted(false);
500 }
501 return mWifiLock;
502 }
503
504 private boolean isAllowedBySettings(String provider) {
505 if (mEnabledProviders.contains(provider)) {
506 return true;
507 }
508 if (mDisabledProviders.contains(provider)) {
509 return false;
510 }
511 // Use system settings
512 ContentResolver resolver = mContext.getContentResolver();
513 String allowedProviders = Settings.System.getString(resolver,
514 Settings.System.LOCATION_PROVIDERS_ALLOWED);
515
516 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
517 }
518
519 private void checkPermissions(String provider) {
520 if (LocationManager.GPS_PROVIDER.equals(provider)
521 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
522 != PackageManager.PERMISSION_GRANTED)) {
523 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
524 }
525 if (LocationManager.NETWORK_PROVIDER.equals(provider)
526 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
527 != PackageManager.PERMISSION_GRANTED)
528 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
529 != PackageManager.PERMISSION_GRANTED)) {
530 throw new SecurityException(
531 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
532 }
533 }
534
535 private boolean isAllowedProvider(String provider) {
536 if (LocationManager.GPS_PROVIDER.equals(provider)
537 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
538 != PackageManager.PERMISSION_GRANTED)) {
539 return false;
540 }
541 if (LocationManager.NETWORK_PROVIDER.equals(provider)
542 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
543 != PackageManager.PERMISSION_GRANTED)
544 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
545 != PackageManager.PERMISSION_GRANTED)) {
546 return false;
547 }
548
549 return true;
550 }
551
552 private String[] getPackageNames() {
553 // Since a single UID may correspond to multiple packages, this can only be used as an
554 // approximation for tracking
555 return mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
556 }
557
558 public List<String> getAllProviders() {
559 try {
560 return _getAllProviders();
561 } catch (SecurityException se) {
562 throw se;
563 } catch (Exception e) {
564 Log.e(TAG, "getAllProviders got exception:", e);
565 return null;
566 }
567 }
568
569 private List<String> _getAllProviders() {
570 if (Config.LOGD) {
571 Log.d(TAG, "getAllProviders");
572 }
573 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
574 ArrayList<String> out = new ArrayList<String>(providers.size());
575
576 for (LocationProviderImpl p : providers) {
577 out.add(p.getName());
578 }
579 return out;
580 }
581
582 public List<String> getProviders(boolean enabledOnly) {
583 try {
584 return _getProviders(enabledOnly);
585 } catch (SecurityException se) {
586 throw se;
587 } catch (Exception e) {
588 Log.e(TAG, "getProviders gotString exception:", e);
589 return null;
590 }
591 }
592
593 private List<String> _getProviders(boolean enabledOnly) {
594 if (Config.LOGD) {
595 Log.d(TAG, "getProviders");
596 }
597 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
598 ArrayList<String> out = new ArrayList<String>();
599
600 for (LocationProviderImpl p : providers) {
601 String name = p.getName();
602 if (isAllowedProvider(name)) {
603 if (enabledOnly && !isAllowedBySettings(name)) {
604 continue;
605 }
606 out.add(name);
607 }
608 }
609 return out;
610 }
611
612 public void updateProviders() {
613 for (LocationProviderImpl p : LocationProviderImpl.getProviders()) {
614 boolean isEnabled = p.isEnabled();
615 String name = p.getName();
616 boolean shouldBeEnabled = isAllowedBySettings(name);
617
618 // Collection is only allowed when network provider is being used
619 if (p.getName().equals(LocationManager.NETWORK_PROVIDER)) {
620 mCollector.updateNetworkProviderStatus(shouldBeEnabled);
621 }
622
623 if (isEnabled && !shouldBeEnabled) {
624 updateProviderListeners(name, false);
625 } else if (!isEnabled && shouldBeEnabled) {
626 updateProviderListeners(name, true);
627 }
628
629 }
630 }
631
632 private void updateProviderListeners(String provider, boolean enabled) {
633 int listeners = 0;
634
635 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
636 if (p == null) {
637 return;
638 }
639
640 synchronized (mRecordsByProvider) {
641
642 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
643 if (records != null) {
644 for (UpdateRecord record : records) {
645 // Sends a notification message to the listener
646 try {
647 if (enabled) {
648 record.mListener.mListener.onProviderEnabled(provider);
649 } else {
650 record.mListener.mListener.onProviderDisabled(provider);
651 }
652 } catch (RemoteException e) {
653 // The death link will clean this up.
654 }
655 listeners++;
656 }
657 }
658 }
659
660 if (enabled) {
661 p.enable();
662 if (listeners > 0) {
663 p.setMinTime(getMinTime(provider));
664 p.enableLocationTracking(true);
665 updateWakelockStatus(mScreenOn);
666 }
667 } else {
668 p.enableLocationTracking(false);
669 p.disable();
670 updateWakelockStatus(mScreenOn);
671 }
672
673 if (enabled && listeners > 0) {
674 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
675 Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
676 mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
677 } else {
678 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
679 }
680 }
681
682 private long getMinTime(String provider) {
683 long minTime = Long.MAX_VALUE;
684 synchronized (mRecordsByProvider) {
685 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
686 if (records != null) {
687 for (UpdateRecord r : records) {
688 minTime = Math.min(minTime, r.mMinTime);
689 }
690 }
691 }
692 return minTime;
693 }
694
695 private class UpdateRecord {
696 String mProvider;
697 Listener mListener;
698 long mMinTime;
699 float mMinDistance;
700 String[] mPackages;
701
702 UpdateRecord(String provider, long minTime, float minDistance, Listener listener,
703 String[] packages) {
704 mProvider = provider;
705 mListener = listener;
706 mMinTime = minTime;
707 mMinDistance = minDistance;
708 mPackages = packages;
709
710 synchronized (mRecordsByProvider) {
711 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
712 if (records == null) {
713 records = new HashSet<UpdateRecord>();
714 mRecordsByProvider.put(provider, records);
715 }
716 records.add(this);
717 }
718 }
719
720 /**
721 * Method to be called when a record will no longer be used. Calling this multiple times
722 * must have the same effect as calling it once.
723 */
724 public void dispose() {
725 synchronized (mRecordsByProvider) {
726 HashSet<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
727 records.remove(this);
728 }
729 }
730
731 /**
732 * Calls dispose().
733 */
734 @Override protected void finalize() {
735 dispose();
736 }
737 }
738
739 public void requestLocationUpdates(String provider,
740 long minTime, float minDistance, ILocationListener listener) {
741
742 try {
743 _requestLocationUpdates(provider, minTime, minDistance, listener);
744 } catch (SecurityException se) {
745 throw se;
746 } catch (Exception e) {
747 Log.e(TAG, "requestUpdates got exception:", e);
748 }
749 }
750
751 private void _requestLocationUpdates(String provider,
752 long minTime, float minDistance, ILocationListener listener) {
753 if (Config.LOGD) {
754 Log.d(TAG, "_requestLocationUpdates: listener = " + listener.asBinder());
755 }
756
757 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
758 if (impl == null) {
759 throw new IllegalArgumentException("provider=" + provider);
760 }
761
762 checkPermissions(provider);
763
764 String[] packages = getPackageNames();
765
766 // so wakelock calls will succeed
767 long identity = Binder.clearCallingIdentity();
768 try {
769 Listener myListener = new Listener(listener);
770 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, myListener, packages);
771
772 synchronized (mLocationListeners) {
773 IBinder binder = listener.asBinder();
774 if (mListeners.get(binder) == null) {
775 try {
776 binder.linkToDeath(myListener, 0);
777 mListeners.put(binder, myListener);
778 } catch (RemoteException e) {
779 return;
780 }
781 }
782
783 HashMap<String,UpdateRecord> records = mLocationListeners.get(binder);
784 if (records == null) {
785 records = new HashMap<String,UpdateRecord>();
786 mLocationListeners.put(binder, records);
787 }
788 UpdateRecord oldRecord = records.put(provider, r);
789 if (oldRecord != null) {
790 oldRecord.dispose();
791 }
792
793 if (impl instanceof NetworkLocationProvider) {
794 ((NetworkLocationProvider) impl).addListener(packages);
795 }
796
797 boolean isProviderEnabled = isAllowedBySettings(provider);
798 if (isProviderEnabled) {
799 long minTimeForProvider = getMinTime(provider);
800 impl.setMinTime(minTimeForProvider);
801 impl.enableLocationTracking(true);
802 updateWakelockStatus(mScreenOn);
803
804 // Clear heartbeats if any before starting a new one
805 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
806 Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
807 mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
808
809 } else {
810 try {
811 // Notify the listener that updates are currently disabled
812 listener.onProviderDisabled(provider);
813 } catch(RemoteException e) {
814 Log.w(TAG, "RemoteException calling onProviderDisabled on " + listener);
815 }
816 }
817 }
818 } finally {
819 Binder.restoreCallingIdentity(identity);
820 }
821 }
822
823 public void removeUpdates(ILocationListener listener) {
824 try {
825 _removeUpdates(listener);
826 } catch (SecurityException se) {
827 throw se;
828 } catch (Exception e) {
829 Log.e(TAG, "removeUpdates got exception:", e);
830 }
831 }
832
833 private void _removeUpdates(ILocationListener listener) {
834 if (Config.LOGD) {
835 Log.d(TAG, "_removeUpdates: listener = " + listener.asBinder());
836 }
837
838 // so wakelock calls will succeed
839 long identity = Binder.clearCallingIdentity();
840 try {
841 synchronized (mLocationListeners) {
842 IBinder binder = listener.asBinder();
843 Listener myListener = mListeners.remove(binder);
844 if (myListener != null) {
845 binder.unlinkToDeath(myListener, 0);
846 }
847
848 // Record which providers were associated with this listener
849 HashSet<String> providers = new HashSet<String>();
850 HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(binder);
851 if (oldRecords != null) {
852 // Call dispose() on the obsolete update records.
853 for (UpdateRecord record : oldRecords.values()) {
854 if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) {
855 if (mNetworkLocationProvider != null) {
856 mNetworkLocationProvider.removeListener(record.mPackages);
857 }
858 }
859 record.dispose();
860 }
861 // Accumulate providers
862 providers.addAll(oldRecords.keySet());
863 }
864
865 mLocationListeners.remove(binder);
866 mLastFixBroadcast.remove(binder);
867 mLastStatusBroadcast.remove(binder);
868
869 // See if the providers associated with this listener have any
870 // other listeners; if one does, inform it of the new smallest minTime
871 // value; if one does not, disable location tracking for it
872 for (String provider : providers) {
873 // If provider is already disabled, don't need to do anything
874 if (!isAllowedBySettings(provider)) {
875 continue;
876 }
877
878 boolean hasOtherListener = false;
879 synchronized (mRecordsByProvider) {
880 HashSet<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
881 if (recordsForProvider != null && recordsForProvider.size() > 0) {
882 hasOtherListener = true;
883 }
884 }
885
886 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
887 if (p != null) {
888 if (hasOtherListener) {
889 p.setMinTime(getMinTime(provider));
890 } else {
891 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
892 p.enableLocationTracking(false);
893 }
894 }
895 }
896
897 updateWakelockStatus(mScreenOn);
898 }
899 } finally {
900 Binder.restoreCallingIdentity(identity);
901 }
902 }
903
904 public boolean addGpsStatusListener(IGpsStatusListener listener) {
905 if (mGpsLocationProvider == null) {
906 return false;
907 }
908 try {
909 mGpsLocationProvider.addGpsStatusListener(listener);
910 } catch (RemoteException e) {
911 Log.w(TAG, "RemoteException in addGpsStatusListener");
912 return false;
913 }
914 return true;
915 }
916
917 public void removeGpsStatusListener(IGpsStatusListener listener) {
918 mGpsLocationProvider.removeGpsStatusListener(listener);
919 }
920
921 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
922 // first check for permission to the provider
923 checkPermissions(provider);
924 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
925 if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
926 != PackageManager.PERMISSION_GRANTED)) {
927 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
928 }
929
930 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
931 if (provider == null) {
932 return false;
933 }
934
935 return impl.sendExtraCommand(command, extras);
936 }
937
938 class ProximityAlert {
939 double mLatitude;
940 double mLongitude;
941 float mRadius;
942 long mExpiration;
943 PendingIntent mIntent;
944 Location mLocation;
945
946 public ProximityAlert(double latitude, double longitude,
947 float radius, long expiration, PendingIntent intent) {
948 mLatitude = latitude;
949 mLongitude = longitude;
950 mRadius = radius;
951 mExpiration = expiration;
952 mIntent = intent;
953
954 mLocation = new Location("");
955 mLocation.setLatitude(latitude);
956 mLocation.setLongitude(longitude);
957 }
958
959 public long getExpiration() {
960 return mExpiration;
961 }
962
963 public PendingIntent getIntent() {
964 return mIntent;
965 }
966
967 public boolean isInProximity(double latitude, double longitude) {
968 Location loc = new Location("");
969 loc.setLatitude(latitude);
970 loc.setLongitude(longitude);
971
972 double radius = loc.distanceTo(mLocation);
973 return radius <= mRadius;
974 }
975 }
976
977 // Listener for receiving locations to trigger proximity alerts
978 class ProximityListener extends ILocationListener.Stub {
979
980 boolean isGpsAvailable = false;
981
982 public void onLocationChanged(Location loc) {
983
984 // If Gps is available, then ignore updates from NetworkLocationProvider
985 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
986 isGpsAvailable = true;
987 }
988 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
989 return;
990 }
991
992 // Process proximity alerts
993 long now = System.currentTimeMillis();
994 double latitude = loc.getLatitude();
995 double longitude = loc.getLongitude();
996 ArrayList<PendingIntent> intentsToRemove = null;
997
998 for (ProximityAlert alert : mProximityAlerts.values()) {
999
1000 PendingIntent intent = alert.getIntent();
1001 long expiration = alert.getExpiration();
1002
1003 if ((expiration == -1) || (now <= expiration)) {
1004 boolean entered = mProximitiesEntered.contains(alert);
1005 boolean inProximity =
1006 alert.isInProximity(latitude, longitude);
1007 if (!entered && inProximity) {
1008 if (Config.LOGD) {
1009 Log.i(TAG, "Entered alert");
1010 }
1011 mProximitiesEntered.add(alert);
1012 Intent enteredIntent = new Intent();
1013 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1014 try {
1015 intent.send(mContext, 0, enteredIntent, null, null);
1016 } catch (PendingIntent.CanceledException e) {
1017 if (Config.LOGD) {
1018 Log.i(TAG, "Canceled proximity alert: " + alert, e);
1019 }
1020 if (intentsToRemove == null) {
1021 intentsToRemove = new ArrayList<PendingIntent>();
1022 }
1023 intentsToRemove.add(intent);
1024 }
1025 } else if (entered && !inProximity) {
1026 if (Config.LOGD) {
1027 Log.i(TAG, "Exited alert");
1028 }
1029 mProximitiesEntered.remove(alert);
1030 Intent exitedIntent = new Intent();
1031 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1032 try {
1033 intent.send(mContext, 0, exitedIntent, null, null);
1034 } catch (PendingIntent.CanceledException e) {
1035 if (Config.LOGD) {
1036 Log.i(TAG, "Canceled proximity alert: " + alert, e);
1037 }
1038 if (intentsToRemove == null) {
1039 intentsToRemove = new ArrayList<PendingIntent>();
1040 }
1041 intentsToRemove.add(intent);
1042 }
1043 }
1044 } else {
1045 // Mark alert for expiration
1046 if (Config.LOGD) {
1047 Log.i(TAG, "Expiring proximity alert: " + alert);
1048 }
1049 if (intentsToRemove == null) {
1050 intentsToRemove = new ArrayList<PendingIntent>();
1051 }
1052 intentsToRemove.add(alert.getIntent());
1053 }
1054 }
1055
1056 // Remove expired alerts
1057 if (intentsToRemove != null) {
1058 for (PendingIntent i : intentsToRemove) {
1059 mProximityAlerts.remove(i);
1060 ProximityAlert alert = mProximityAlerts.get(i);
1061 mProximitiesEntered.remove(alert);
1062 }
1063 }
1064
1065 }
1066
1067 public void onProviderDisabled(String provider) {
1068 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1069 isGpsAvailable = false;
1070 }
1071 }
1072
1073 public void onProviderEnabled(String provider) {
1074 // ignore
1075 }
1076
1077 public void onStatusChanged(String provider, int status, Bundle extras) {
1078 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1079 (status != LocationProvider.AVAILABLE)) {
1080 isGpsAvailable = false;
1081 }
1082 }
1083 }
1084
1085 public void addProximityAlert(double latitude, double longitude,
1086 float radius, long expiration, PendingIntent intent) {
1087 try {
1088 _addProximityAlert(latitude, longitude, radius, expiration, intent);
1089 } catch (SecurityException se) {
1090 throw se;
1091 } catch (Exception e) {
1092 Log.e(TAG, "addProximityAlert got exception:", e);
1093 }
1094 }
1095
1096 private void _addProximityAlert(double latitude, double longitude,
1097 float radius, long expiration, PendingIntent intent) {
1098 if (Config.LOGD) {
1099 Log.d(TAG, "addProximityAlert: latitude = " + latitude +
1100 ", longitude = " + longitude +
1101 ", expiration = " + expiration +
1102 ", intent = " + intent);
1103 }
1104
1105 // Require ability to access all providers for now
1106 if (!isAllowedProvider(LocationManager.GPS_PROVIDER) ||
1107 !isAllowedProvider(LocationManager.NETWORK_PROVIDER)) {
1108 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1109 }
1110
1111 if (expiration != -1) {
1112 expiration += System.currentTimeMillis();
1113 }
1114 ProximityAlert alert = new ProximityAlert(latitude, longitude, radius, expiration, intent);
1115 mProximityAlerts.put(intent, alert);
1116
1117 if (mProximityListener == null) {
1118 mProximityListener = new ProximityListener();
1119
1120 LocationProvider provider = LocationProviderImpl.getProvider(
1121 LocationManager.GPS_PROVIDER);
1122 if (provider != null) {
1123 _requestLocationUpdates(provider.getName(), 1000L, 1.0f, mProximityListener);
1124 }
1125
1126 provider =
1127 LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER);
1128 if (provider != null) {
1129 _requestLocationUpdates(provider.getName(), 1000L, 1.0f, mProximityListener);
1130 }
1131 }
1132 }
1133
1134 public void removeProximityAlert(PendingIntent intent) {
1135 try {
1136 _removeProximityAlert(intent);
1137 } catch (SecurityException se) {
1138 throw se;
1139 } catch (Exception e) {
1140 Log.e(TAG, "removeProximityAlert got exception:", e);
1141 }
1142 }
1143
1144 private void _removeProximityAlert(PendingIntent intent) {
1145 if (Config.LOGD) {
1146 Log.d(TAG, "removeProximityAlert: intent = " + intent);
1147 }
1148
1149 mProximityAlerts.remove(intent);
1150 if (mProximityAlerts.size() == 0) {
1151 _removeUpdates(mProximityListener);
1152 mProximityListener = null;
1153 }
1154 }
1155
1156 /**
1157 * @return null if the provider does not exits
1158 * @throw SecurityException if the provider is not allowed to be
1159 * accessed by the caller
1160 */
1161 public Bundle getProviderInfo(String provider) {
1162 try {
1163 return _getProviderInfo(provider);
1164 } catch (SecurityException se) {
1165 throw se;
1166 } catch (Exception e) {
1167 Log.e(TAG, "_getProviderInfo got exception:", e);
1168 return null;
1169 }
1170 }
1171
1172 private Bundle _getProviderInfo(String provider) {
1173 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1174 if (p == null) {
1175 return null;
1176 }
1177
1178 checkPermissions(provider);
1179
1180 Bundle b = new Bundle();
1181 b.putBoolean("network", p.requiresNetwork());
1182 b.putBoolean("satellite", p.requiresSatellite());
1183 b.putBoolean("cell", p.requiresCell());
1184 b.putBoolean("cost", p.hasMonetaryCost());
1185 b.putBoolean("altitude", p.supportsAltitude());
1186 b.putBoolean("speed", p.supportsSpeed());
1187 b.putBoolean("bearing", p.supportsBearing());
1188 b.putInt("power", p.getPowerRequirement());
1189 b.putInt("accuracy", p.getAccuracy());
1190
1191 return b;
1192 }
1193
1194 public boolean isProviderEnabled(String provider) {
1195 try {
1196 return _isProviderEnabled(provider);
1197 } catch (SecurityException se) {
1198 throw se;
1199 } catch (Exception e) {
1200 Log.e(TAG, "isProviderEnabled got exception:", e);
1201 return false;
1202 }
1203 }
1204
1205 private boolean _isProviderEnabled(String provider) {
1206 checkPermissions(provider);
1207
1208 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1209 if (p == null) {
1210 throw new IllegalArgumentException("provider=" + provider);
1211 }
1212 return isAllowedBySettings(provider);
1213 }
1214
1215 public Location getLastKnownLocation(String provider) {
1216 try {
1217 return _getLastKnownLocation(provider);
1218 } catch (SecurityException se) {
1219 throw se;
1220 } catch (Exception e) {
1221 Log.e(TAG, "getLastKnownLocation got exception:", e);
1222 return null;
1223 }
1224 }
1225
1226 private Location _getLastKnownLocation(String provider) {
1227 checkPermissions(provider);
1228
1229 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1230 if (p == null) {
1231 throw new IllegalArgumentException("provider=" + provider);
1232 }
1233
1234 if (!isAllowedBySettings(provider)) {
1235 return null;
1236 }
1237
1238 Location location = mLastKnownLocation.get(provider);
1239 if (location == null) {
1240 // Get the persistent last known location for the provider
1241 location = readLastKnownLocation(provider);
1242 if (location != null) {
1243 mLastKnownLocation.put(provider, location);
1244 }
1245 }
1246
1247 return location;
1248 }
1249
1250 private boolean shouldBroadcast(Location loc, Location lastLoc, UpdateRecord record) {
1251 // Always broadcast the first update
1252 if (lastLoc == null) {
1253 return true;
1254 }
1255
1256 // Don't broadcast same location again regardless of condition
1257 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1258 if (loc.getTime() == lastLoc.getTime()) {
1259 return false;
1260 }
1261
1262 // Check whether sufficient distance has been traveled
1263 double minDistance = record.mMinDistance;
1264 if (minDistance > 0.0) {
1265 if (loc.distanceTo(lastLoc) <= minDistance) {
1266 return false;
1267 }
1268 }
1269
1270 return true;
1271 }
1272
1273 private void handleLocationChanged(String provider) {
1274 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
1275 if (records == null || records.size() == 0) {
1276 return;
1277 }
1278
1279 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1280 if (p == null) {
1281 return;
1282 }
1283
1284 // Get location object
1285 Location loc = mLocationsByProvider.get(provider);
1286 if (loc == null) {
1287 loc = new Location(provider);
1288 mLocationsByProvider.put(provider, loc);
1289 } else {
1290 loc.reset();
1291 }
1292
1293 // Use the mock location if available
1294 Location mockLoc = mMockProviderLocation.get(provider);
1295 boolean locationValid;
1296 if (mockLoc != null) {
1297 locationValid = true;
1298 loc.set(mockLoc);
1299 } else {
1300 locationValid = p.getLocation(loc);
1301 }
1302
1303 // Update last known location for provider
1304 if (locationValid) {
1305 Location location = mLastKnownLocation.get(provider);
1306 if (location == null) {
1307 mLastKnownLocation.put(provider, new Location(loc));
1308 } else {
1309 location.set(loc);
1310 }
1311 writeLastKnownLocation(provider, loc);
1312
1313 if (p instanceof NetworkLocationProvider) {
1314 mWakeLockNetworkReceived = true;
1315 } else if (p instanceof GpsLocationProvider) {
1316 // Gps location received signal is in NetworkStateBroadcastReceiver
1317 }
1318 }
1319
1320 // Fetch latest status update time
1321 long newStatusUpdateTime = p.getStatusUpdateTime();
1322
1323 // Override real time with mock time if present
1324 Long mockStatusUpdateTime = mMockProviderStatusUpdateTime.get(provider);
1325 if (mockStatusUpdateTime != null) {
1326 newStatusUpdateTime = mockStatusUpdateTime.longValue();
1327 }
1328
1329 // Get latest status
1330 Bundle extras = new Bundle();
1331 int status = p.getStatus(extras);
1332
1333 // Override status with mock status if present
1334 Integer mockStatus = mMockProviderStatus.get(provider);
1335 if (mockStatus != null) {
1336 status = mockStatus.intValue();
1337 }
1338
1339 // Override extras with mock extras if present
1340 Bundle mockExtras = mMockProviderStatusExtras.get(provider);
1341 if (mockExtras != null) {
1342 extras.clear();
1343 extras.putAll(mockExtras);
1344 }
1345
1346 // Broadcast location or status to all listeners
1347 for (UpdateRecord r : records) {
1348 ILocationListener listener = r.mListener.mListener;
1349 IBinder binder = listener.asBinder();
1350
1351 // Broadcast location only if it is valid
1352 if (locationValid) {
1353 HashMap<String,Location> map = mLastFixBroadcast.get(binder);
1354 if (map == null) {
1355 map = new HashMap<String,Location>();
1356 mLastFixBroadcast.put(binder, map);
1357 }
1358 Location lastLoc = map.get(provider);
1359 if ((lastLoc == null) || shouldBroadcast(loc, lastLoc, r)) {
1360 if (lastLoc == null) {
1361 lastLoc = new Location(loc);
1362 map.put(provider, lastLoc);
1363 } else {
1364 lastLoc.set(loc);
1365 }
1366 try {
1367 listener.onLocationChanged(loc);
1368 } catch (RemoteException doe) {
1369 Log.w(TAG, "RemoteException calling onLocationChanged on " + listener);
1370 _removeUpdates(listener);
1371 }
1372 }
1373 }
1374
1375 // Broadcast status message
1376 HashMap<String,Long> statusMap = mLastStatusBroadcast.get(binder);
1377 if (statusMap == null) {
1378 statusMap = new HashMap<String,Long>();
1379 mLastStatusBroadcast.put(binder, statusMap);
1380 }
1381 long prevStatusUpdateTime =
1382 (statusMap.get(provider) != null) ? statusMap.get(provider) : 0;
1383
1384 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1385 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1386
1387 statusMap.put(provider, newStatusUpdateTime);
1388 try {
1389 listener.onStatusChanged(provider, status, extras);
1390 } catch (RemoteException doe) {
1391 Log.w(TAG, "RemoteException calling onStatusChanged on " + listener);
1392 _removeUpdates(listener);
1393 }
1394 }
1395 }
1396 }
1397
1398 private class LocationWorkerHandler extends Handler {
1399
1400 @Override
1401 public void handleMessage(Message msg) {
1402 try {
1403 if (msg.what == MESSAGE_HEARTBEAT) {
1404 // log("LocationWorkerHandler: Heartbeat!");
1405
1406 synchronized (mRecordsByProvider) {
1407 String provider = (String) msg.obj;
1408 if (!isAllowedBySettings(provider)) {
1409 return;
1410 }
1411
1412 // Process the location fix if the screen is on or we're holding a wakelock
1413 if (mScreenOn || (mWakeLockAcquireTime != 0)) {
1414 handleLocationChanged(provider);
1415 }
1416
1417 // If it continues to have listeners
1418 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
1419 if (records != null && records.size() > 0) {
1420 Message m = Message.obtain(this, MESSAGE_HEARTBEAT, provider);
1421 sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
1422 }
1423 }
1424
1425 if ((mWakeLockAcquireTime != 0) &&
1426 (SystemClock.elapsedRealtime() - mWakeLockAcquireTime > MAX_TIME_FOR_WAKE_LOCK)) {
1427
1428 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1429 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1430
1431 log("LocationWorkerHandler: Exceeded max time for wake lock");
1432 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1433 sendMessageAtFrontOfQueue(m);
1434
1435 } else if (mWakeLockAcquireTime != 0 &&
1436 mWakeLockGpsReceived && mWakeLockNetworkReceived) {
1437
1438 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1439 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1440
1441 log("LocationWorkerHandler: Locations received.");
1442 mWakeLockAcquireTime = 0;
1443 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1444 sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK);
1445 }
1446
1447 } else if (msg.what == MESSAGE_ACQUIRE_WAKE_LOCK) {
1448 log("LocationWorkerHandler: Acquire");
1449 acquireWakeLock();
1450 } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) {
1451 log("LocationWorkerHandler: Release");
1452
1453 // Update wakelock status so the next alarm is set before releasing wakelock
1454 updateWakelockStatus(mScreenOn);
1455 releaseWakeLock();
1456 }
1457 } catch (Exception e) {
1458 // Log, don't crash!
1459 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1460 }
1461 }
1462 }
1463
1464 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
1465 @Override
1466 public void onCellLocationChanged(CellLocation cellLocation) {
1467 try {
1468 ServiceState serviceState = mServiceState;
1469
1470 // Gets cell state
1471 CellState cellState = new CellState(serviceState, cellLocation);
1472
1473 // Notify collector
1474 mCollector.updateCellState(cellState);
1475
1476 // Updates providers
1477 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1478 for (LocationProviderImpl provider : providers) {
1479 if (provider.requiresCell()) {
1480 provider.updateCellState(cellState);
1481 }
1482 }
1483 } catch (Exception e) {
1484 Log.e(TAG, "Exception in PhoneStateListener.onCellLocationCahnged:", e);
1485 }
1486 }
1487
1488 @Override
1489 public void onServiceStateChanged(ServiceState serviceState) {
1490 mServiceState = serviceState;
1491 }
1492 };
1493
1494 private class PowerStateBroadcastReceiver extends BroadcastReceiver {
1495 @Override public void onReceive(Context context, Intent intent) {
1496 String action = intent.getAction();
1497
1498 if (action.equals(ALARM_INTENT)) {
1499 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1500 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1501
1502 log("PowerStateBroadcastReceiver: Alarm received");
1503 Message m = mLocationHandler.obtainMessage(MESSAGE_ACQUIRE_WAKE_LOCK);
1504 mLocationHandler.sendMessageAtFrontOfQueue(m);
1505
1506 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1507 log("PowerStateBroadcastReceiver: Screen off");
1508 updateWakelockStatus(false);
1509
1510 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1511 log("PowerStateBroadcastReceiver: Screen on");
1512 updateWakelockStatus(true);
1513
1514 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
1515 log("PowerStateBroadcastReceiver: Battery changed");
1516 int scale = intent.getIntExtra(BATTERY_EXTRA_SCALE, 100);
1517 int level = intent.getIntExtra(BATTERY_EXTRA_LEVEL, 0);
1518 boolean plugged = intent.getIntExtra(BATTERY_EXTRA_PLUGGED, 0) != 0;
1519
1520 // Notify collector battery state
1521 mCollector.updateBatteryState(scale, level, plugged);
1522 }
1523 }
1524 }
1525
1526 private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
1527 @Override public void onReceive(Context context, Intent intent) {
1528 String action = intent.getAction();
1529
1530 if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
1531
1532 List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
1533
1534 if (wifiScanResults == null) {
1535 return;
1536 }
1537
1538 // Notify provider and collector of Wifi scan results
1539 mCollector.updateWifiScanResults(wifiScanResults);
1540 if (mNetworkLocationProvider != null) {
1541 mNetworkLocationProvider.updateWifiScanResults(wifiScanResults);
1542 }
1543
1544 } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
1545 int networkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
1546
1547 boolean noConnectivity =
1548 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1549 if (!noConnectivity) {
1550 networkState = LocationProvider.AVAILABLE;
1551 }
1552
1553 // Notify location providers of current network state
1554 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1555 for (LocationProviderImpl provider : providers) {
1556 if (provider.requiresNetwork()) {
1557 provider.updateNetworkState(networkState);
1558 }
1559 }
1560
1561 } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
1562 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
1563 WifiManager.WIFI_STATE_UNKNOWN);
1564
1565 boolean enabled;
1566 if (state == WifiManager.WIFI_STATE_ENABLED) {
1567 enabled = true;
1568 } else if (state == WifiManager.WIFI_STATE_DISABLED) {
1569 enabled = false;
1570 } else {
1571 return;
1572 }
1573
1574 // Notify network provider of current wifi enabled state
1575 if (mNetworkLocationProvider != null) {
1576 mNetworkLocationProvider.updateWifiEnabledState(enabled);
1577 }
1578
1579 } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
1580
1581 final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED, false);
1582
1583 if (!enabled) {
1584 // When GPS is disabled, we are OK to release wake-lock
1585 mWakeLockGpsReceived = true;
1586 }
1587 }
1588
1589 }
1590 }
1591
1592 // Wake locks
1593
1594 private void updateWakelockStatus(boolean screenOn) {
1595 log("updateWakelockStatus(): " + screenOn);
1596
1597 boolean needsLock = false;
1598 long minTime = Integer.MAX_VALUE;
1599
1600 if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) {
1601 needsLock = true;
1602 minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime);
1603 }
1604
1605 if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) {
1606 needsLock = true;
1607 minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime);
1608 if (screenOn) {
1609 startGps();
1610 } else if (mScreenOn && !screenOn) {
1611
1612 // We just turned the screen off so stop navigating
1613 stopGps();
1614 }
1615 }
1616
1617 mScreenOn = screenOn;
1618
1619 PendingIntent sender =
1620 PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0);
1621
1622 // Cancel existing alarm
1623 log("Cancelling existing alarm");
1624 mAlarmManager.cancel(sender);
1625
1626 if (needsLock && !mScreenOn) {
1627 long now = SystemClock.elapsedRealtime();
1628 mAlarmManager.set(
1629 AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender);
1630 mAlarmInterval = minTime;
1631 log("Creating a new wakelock alarm with minTime = " + minTime);
1632 } else {
1633 log("No need for alarm");
1634 mAlarmInterval = -1;
1635
1636 // Clear out existing wakelocks
1637 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1638 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1639 releaseWakeLock();
1640 }
1641 }
1642
1643 private void acquireWakeLock() {
1644 if (mWakeLock.isHeld()) {
1645 log("Must release wakelock before acquiring");
1646 mWakeLockAcquireTime = 0;
1647 mWakeLock.release();
1648 }
1649
1650 boolean networkActive = (mNetworkLocationProvider != null)
1651 && mNetworkLocationProvider.isLocationTracking();
1652 boolean gpsActive = (mGpsLocationProvider != null)
1653 && mGpsLocationProvider.isLocationTracking();
1654
1655 boolean needsLock = networkActive || gpsActive;
1656 if (!needsLock) {
1657 log("No need for Lock!");
1658 return;
1659 }
1660
1661 mWakeLockGpsReceived = !gpsActive;
1662 mWakeLockNetworkReceived = !networkActive;
1663
1664 // Acquire wake lock
1665 mWakeLock.acquire();
1666 mWakeLockAcquireTime = SystemClock.elapsedRealtime();
1667 log("Acquired wakelock");
1668
1669 // Start the gps provider
1670 startGps();
1671
1672 // Acquire cell lock
1673 if (mCellWakeLockAcquired) {
1674 // Lock is already acquired
1675 } else if (!mWakeLockNetworkReceived) {
1676 mTelephonyManager.enableLocationUpdates();
1677 mCellWakeLockAcquired = true;
1678 } else {
1679 mCellWakeLockAcquired = false;
1680 }
1681
1682 // Notify NetworkLocationProvider
1683 if (mNetworkLocationProvider != null) {
1684 mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired);
1685 }
1686
1687 // Acquire wifi lock
1688 WifiManager.WifiLock wifiLock = getWifiWakelock();
1689 if (wifiLock != null) {
1690 if (mWifiWakeLockAcquired) {
1691 // Lock is already acquired
1692 } else if (mWifiManager.isWifiEnabled() && !mWakeLockNetworkReceived) {
1693 wifiLock.acquire();
1694 mWifiWakeLockAcquired = true;
1695 } else {
1696 mWifiWakeLockAcquired = false;
1697 Log.w(TAG, "acquireWakeLock(): Unable to get WiFi lock");
1698 }
1699 }
1700 }
1701
1702 private void startGps() {
1703 boolean gpsActive = (mGpsLocationProvider != null)
1704 && mGpsLocationProvider.isLocationTracking();
1705 if (gpsActive) {
1706 mGpsLocationProvider.startNavigating();
1707 }
1708 }
1709
1710 private void stopGps() {
1711 boolean gpsActive = mGpsLocationProvider != null
1712 && mGpsLocationProvider.isLocationTracking();
1713 if (gpsActive) {
1714 mGpsLocationProvider.stopNavigating();
1715 }
1716 }
1717
1718 private void releaseWakeLock() {
1719 // Release wifi lock
1720 WifiManager.WifiLock wifiLock = getWifiWakelock();
1721 if (wifiLock != null) {
1722 if (mWifiWakeLockAcquired) {
1723 wifiLock.release();
1724 mWifiWakeLockAcquired = false;
1725 }
1726 }
1727
1728 if (!mScreenOn) {
1729
1730 // Stop the gps
1731 stopGps();
1732 }
1733
1734 // Release cell lock
1735 if (mCellWakeLockAcquired) {
1736 mTelephonyManager.disableLocationUpdates();
1737 mCellWakeLockAcquired = false;
1738 }
1739
1740 // Notify NetworkLocationProvider
1741 if (mNetworkLocationProvider != null) {
1742 mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired);
1743 }
1744
1745 // Release wake lock
1746 mWakeLockAcquireTime = 0;
1747 if (mWakeLock.isHeld()) {
1748 log("Released wakelock");
1749 mWakeLock.release();
1750 } else {
1751 log("Can't release wakelock again!");
1752 }
1753 }
1754
1755 // Geocoder
1756
1757 public String getFromLocation(double latitude, double longitude, int maxResults,
1758 String language, String country, String variant, String appName, List<Address> addrs) {
1759 try {
1760 Locale locale = new Locale(language, country, variant);
1761 mMasfClient.reverseGeocode(locale, appName, latitude, longitude, maxResults, addrs);
1762 return null;
1763 } catch(IOException e) {
1764 return e.getMessage();
1765 } catch(Exception e) {
1766 Log.e(TAG, "getFromLocation got exception:", e);
1767 return null;
1768 }
1769 }
1770
1771 public String getFromLocationName(String locationName,
1772 double lowerLeftLatitude, double lowerLeftLongitude,
1773 double upperRightLatitude, double upperRightLongitude, int maxResults,
1774 String language, String country, String variant, String appName, List<Address> addrs) {
1775
1776 try {
1777 Locale locale = new Locale(language, country, variant);
1778 mMasfClient.forwardGeocode(locale, appName, locationName,
1779 lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1780 maxResults, addrs);
1781 return null;
1782 } catch(IOException e) {
1783 return e.getMessage();
1784 } catch(Exception e) {
1785 Log.e(TAG, "getFromLocationName got exception:", e);
1786 return null;
1787 }
1788 }
1789
1790 // Mock Providers
1791
1792 class MockProvider extends LocationProviderImpl {
1793 boolean mRequiresNetwork;
1794 boolean mRequiresSatellite;
1795 boolean mRequiresCell;
1796 boolean mHasMonetaryCost;
1797 boolean mSupportsAltitude;
1798 boolean mSupportsSpeed;
1799 boolean mSupportsBearing;
1800 int mPowerRequirement;
1801 int mAccuracy;
1802
1803 public MockProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1804 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1805 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1806 super(name);
1807
1808 mRequiresNetwork = requiresNetwork;
1809 mRequiresSatellite = requiresSatellite;
1810 mRequiresCell = requiresCell;
1811 mHasMonetaryCost = hasMonetaryCost;
1812 mSupportsAltitude = supportsAltitude;
1813 mSupportsBearing = supportsBearing;
1814 mSupportsSpeed = supportsSpeed;
1815 mPowerRequirement = powerRequirement;
1816 mAccuracy = accuracy;
1817 }
1818
1819 @Override
1820 public void disable() {
1821 String name = getName();
1822 mEnabledProviders.remove(name);
1823 mDisabledProviders.add(name);
1824 }
1825
1826 @Override
1827 public void enable() {
1828 String name = getName();
1829 mEnabledProviders.add(name);
1830 mDisabledProviders.remove(name);
1831 }
1832
1833 @Override
1834 public boolean getLocation(Location l) {
1835 Location loc = mMockProviderLocation.get(getName());
1836 if (loc == null) {
1837 return false;
1838 }
1839 l.set(loc);
1840 return true;
1841 }
1842
1843 @Override
1844 public int getStatus(Bundle extras) {
1845 String name = getName();
1846 Integer s = mMockProviderStatus.get(name);
1847 int status = (s == null) ? AVAILABLE : s.intValue();
1848 Bundle newExtras = mMockProviderStatusExtras.get(name);
1849 if (newExtras != null) {
1850 extras.clear();
1851 extras.putAll(newExtras);
1852 }
1853 return status;
1854 }
1855
1856 @Override
1857 public boolean isEnabled() {
1858 return mEnabledProviders.contains(getName());
1859 }
1860
1861 @Override
1862 public int getAccuracy() {
1863 return mAccuracy;
1864 }
1865
1866 @Override
1867 public int getPowerRequirement() {
1868 return mPowerRequirement;
1869 }
1870
1871 @Override
1872 public boolean hasMonetaryCost() {
1873 return mHasMonetaryCost;
1874 }
1875
1876 @Override
1877 public boolean requiresCell() {
1878 return mRequiresCell;
1879 }
1880
1881 @Override
1882 public boolean requiresNetwork() {
1883 return mRequiresNetwork;
1884 }
1885
1886 @Override
1887 public boolean requiresSatellite() {
1888 return mRequiresSatellite;
1889 }
1890
1891 @Override
1892 public boolean supportsAltitude() {
1893 return mSupportsAltitude;
1894 }
1895
1896 @Override
1897 public boolean supportsBearing() {
1898 return mSupportsBearing;
1899 }
1900
1901 @Override
1902 public boolean supportsSpeed() {
1903 return mSupportsSpeed;
1904 }
1905 }
1906
1907 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1908 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1909 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1910 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1911 PackageManager.PERMISSION_GRANTED) {
1912 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1913 }
1914
1915 MockProvider provider = new MockProvider(name, requiresNetwork, requiresSatellite,
1916 requiresCell, hasMonetaryCost, supportsAltitude,
1917 supportsSpeed, supportsBearing, powerRequirement, accuracy);
1918 if (LocationProviderImpl.getProvider(name) != null) {
1919 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
1920 }
1921 LocationProviderImpl.addProvider(provider);
1922 updateProviders();
1923 }
1924
1925 public void removeTestProvider(String provider) {
1926 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1927 PackageManager.PERMISSION_GRANTED) {
1928 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1929 }
1930 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1931 if (p == null) {
1932 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1933 }
1934 LocationProviderImpl.removeProvider(p);
1935 updateProviders();
1936 }
1937
1938 public void setTestProviderLocation(String provider, Location loc) {
1939 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1940 PackageManager.PERMISSION_GRANTED) {
1941 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1942 }
1943 if (LocationProviderImpl.getProvider(provider) == null) {
1944 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1945 }
1946 mMockProviderLocation.put(provider, loc);
1947 }
1948
1949 public void clearTestProviderLocation(String provider) {
1950 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1951 PackageManager.PERMISSION_GRANTED) {
1952 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1953 }
1954 if (LocationProviderImpl.getProvider(provider) == null) {
1955 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1956 }
1957 mMockProviderLocation.remove(provider);
1958 }
1959
1960 public void setTestProviderEnabled(String provider, boolean enabled) {
1961 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1962 PackageManager.PERMISSION_GRANTED) {
1963 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1964 }
1965 if (LocationProviderImpl.getProvider(provider) == null) {
1966 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1967 }
1968 if (enabled) {
1969 mEnabledProviders.add(provider);
1970 mDisabledProviders.remove(provider);
1971 } else {
1972 mEnabledProviders.remove(provider);
1973 mDisabledProviders.add(provider);
1974 }
1975 updateProviders();
1976 }
1977
1978 public void clearTestProviderEnabled(String provider) {
1979 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1980 PackageManager.PERMISSION_GRANTED) {
1981 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1982 }
1983 if (LocationProviderImpl.getProvider(provider) == null) {
1984 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1985 }
1986 mEnabledProviders.remove(provider);
1987 mDisabledProviders.remove(provider);
1988 updateProviders();
1989 }
1990
1991 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
1992 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
1993 PackageManager.PERMISSION_GRANTED) {
1994 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
1995 }
1996 if (LocationProviderImpl.getProvider(provider) == null) {
1997 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
1998 }
1999 mMockProviderStatus.put(provider, new Integer(status));
2000 mMockProviderStatusExtras.put(provider, extras);
2001 mMockProviderStatusUpdateTime.put(provider, new Long(updateTime));
2002 }
2003
2004 public void clearTestProviderStatus(String provider) {
2005 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
2006 PackageManager.PERMISSION_GRANTED) {
2007 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
2008 }
2009 if (LocationProviderImpl.getProvider(provider) == null) {
2010 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2011 }
2012 mMockProviderStatus.remove(provider);
2013 mMockProviderStatusExtras.remove(provider);
2014 mMockProviderStatusUpdateTime.remove(provider);
2015 }
2016
2017 private void log(String log) {
2018 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2019 Log.d(TAG, log);
2020 }
2021 }
2022}
2023