blob: ed9ee7927313ca3bff30c6cf3baad45c8fa53053 [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;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080060import android.provider.Settings.SettingNotFoundException;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070061import android.telephony.CellLocation;
62import android.telephony.PhoneStateListener;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070063import 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 /**
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800148 * Mapping from listener IBinder/PendingIntent to local Listener wrappers.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700149 */
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800150 private final HashMap<Object,Receiver> mListeners =
151 new HashMap<Object,Receiver>();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700152
153 /**
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800154 * Mapping from listener IBinder/PendingIntent to a map from provider name to UpdateRecord.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700155 */
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800156 private final HashMap<Object,HashMap<String,UpdateRecord>> mLocationListeners =
157 new HashMap<Object,HashMap<String,UpdateRecord>>();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700158
159 /**
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800160 * Mapping from listener IBinder/PendingIntent to a map from provider name to last broadcast
161 * location.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700162 */
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800163 private final HashMap<Object,HashMap<String,Location>> mLastFixBroadcast =
164 new HashMap<Object,HashMap<String,Location>>();
165 private final HashMap<Object,HashMap<String,Long>> mLastStatusBroadcast =
166 new HashMap<Object,HashMap<String,Long>>();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700167
168 /**
169 * Mapping from provider name to all its UpdateRecords
170 */
171 private final HashMap<String,HashSet<UpdateRecord>> mRecordsByProvider =
172 new HashMap<String,HashSet<UpdateRecord>>();
173
174 /**
175 * Mappings from provider name to object to use for current location. Locations
176 * contained in this list may not always be valid.
177 */
178 private final HashMap<String,Location> mLocationsByProvider =
179 new HashMap<String,Location>();
180
181 // Proximity listeners
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800182 private Receiver mProximityListener = null;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700183 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
184 new HashMap<PendingIntent,ProximityAlert>();
185 private HashSet<ProximityAlert> mProximitiesEntered =
186 new HashSet<ProximityAlert>();
187
188 // Last known location for each provider
189 private HashMap<String,Location> mLastKnownLocation =
190 new HashMap<String,Location>();
191
192 // Battery status extras (from com.android.server.BatteryService)
193 private static final String BATTERY_EXTRA_SCALE = "scale";
194 private static final String BATTERY_EXTRA_LEVEL = "level";
195 private static final String BATTERY_EXTRA_PLUGGED = "plugged";
196
197 // Last known cell service state
198 private TelephonyManager mTelephonyManager;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800199 private int mSignalStrength = -1;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700200
201 // Location collector
202 private LocationCollector mCollector;
203
204 // Location MASF service
205 private LocationMasfClient mMasfClient;
206
207 // Wifi Manager
208 private WifiManager mWifiManager;
209
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800210 /**
211 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
212 * location updates.
213 */
214 private final class Receiver implements IBinder.DeathRecipient {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700215 final ILocationListener mListener;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800216 final PendingIntent mPendingIntent;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700217
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800218 Receiver(ILocationListener listener) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700219 mListener = listener;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800220 mPendingIntent = null;
221 }
222
223 Receiver(PendingIntent intent) {
224 mPendingIntent = intent;
225 mListener = null;
226 }
227
228 public Object getKey() {
229 if (mListener != null) {
230 return mListener.asBinder();
231 } else {
232 return mPendingIntent;
233 }
234 }
235
236 public boolean isListener() {
237 return mListener != null;
238 }
239
240 public boolean isPendingIntent() {
241 return mPendingIntent != null;
242 }
243
244 public ILocationListener getListener() {
245 if (mListener != null) {
246 return mListener;
247 }
248 throw new IllegalStateException("Request for non-existent listener");
249 }
250
251 public PendingIntent getPendingIntent() {
252 if (mPendingIntent != null) {
253 return mPendingIntent;
254 }
255 throw new IllegalStateException("Request for non-existent intent");
256 }
257
258 public void onStatusChanged(String provider, int status, Bundle extras)
259 throws RemoteException {
260 if (mListener != null) {
261 mListener.onStatusChanged(provider, status, extras);
262 } else {
263 Intent statusChanged = new Intent();
264 statusChanged.putExtras(extras);
265 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
266 try {
267 mPendingIntent.send(mContext, 0, statusChanged, null, null);
268 } catch (PendingIntent.CanceledException e) {
269 _removeUpdates(this);
270 }
271 }
272 }
273
274 public void onLocationChanged(Location location) throws RemoteException {
275 if (mListener != null) {
276 mListener.onLocationChanged(location);
277 } else {
278 Intent locationChanged = new Intent();
279 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
280 try {
281 mPendingIntent.send(mContext, 0, locationChanged, null, null);
282 } catch (PendingIntent.CanceledException e) {
283 _removeUpdates(this);
284 }
285 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700286 }
287
288 public void binderDied() {
289 if (Config.LOGD) {
290 Log.d(TAG, "Location listener died");
291 }
292 synchronized (mLocationListeners) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800293 _removeUpdates(this);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700294 }
295 }
296 }
297
298 private Location readLastKnownLocation(String provider) {
299 Location location = null;
300 String s = null;
301 try {
302 File f = new File(LocationManager.SYSTEM_DIR + "/location."
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800303 + provider);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700304 if (!f.exists()) {
305 return null;
306 }
307 BufferedReader reader = new BufferedReader(new FileReader(f), 256);
308 s = reader.readLine();
309 } catch (IOException e) {
310 Log.w(TAG, "Unable to read last known location", e);
311 }
312
313 if (s == null) {
314 return null;
315 }
316 try {
317 String[] tokens = PATTERN_COMMA.split(s);
318 int idx = 0;
319 long time = Long.parseLong(tokens[idx++]);
320 double latitude = Double.parseDouble(tokens[idx++]);
321 double longitude = Double.parseDouble(tokens[idx++]);
322 double altitude = Double.parseDouble(tokens[idx++]);
323 float bearing = Float.parseFloat(tokens[idx++]);
324 float speed = Float.parseFloat(tokens[idx++]);
325
326 location = new Location(provider);
327 location.setTime(time);
328 location.setLatitude(latitude);
329 location.setLongitude(longitude);
330 location.setAltitude(altitude);
331 location.setBearing(bearing);
332 location.setSpeed(speed);
333 } catch (NumberFormatException nfe) {
334 Log.e(TAG, "NumberFormatException reading last known location", nfe);
335 return null;
336 }
337
338 return location;
339 }
340
341 private void writeLastKnownLocation(String provider,
342 Location location) {
343 long now = SystemClock.elapsedRealtime();
344 Long last = mLastWriteTime.get(provider);
345 if ((last != null)
346 && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
347 return;
348 }
349 mLastWriteTime.put(provider, now);
350
351 StringBuilder sb = new StringBuilder(100);
352 sb.append(location.getTime());
353 sb.append(',');
354 sb.append(location.getLatitude());
355 sb.append(',');
356 sb.append(location.getLongitude());
357 sb.append(',');
358 sb.append(location.getAltitude());
359 sb.append(',');
360 sb.append(location.getBearing());
361 sb.append(',');
362 sb.append(location.getSpeed());
363
364 FileWriter writer = null;
365 try {
366 File d = new File(LocationManager.SYSTEM_DIR);
367 if (!d.exists()) {
368 if (!d.mkdirs()) {
369 Log.w(TAG, "Unable to create directory to write location");
370 return;
371 }
372 }
373 File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
374 writer = new FileWriter(f);
375 writer.write(sb.toString());
376 } catch (IOException e) {
377 Log.w(TAG, "Unable to write location", e);
378 } finally {
379 if (writer != null) {
380 try {
381 writer.close();
382 } catch (IOException e) {
383 Log.w(TAG, "Exception closing file", e);
384 }
385 }
386 }
387 }
388
389 /**
390 * Load providers from /data/location/<provider_name>/
391 * class
392 * kml
393 * nmea
394 * track
395 * location
396 * properties
397 */
398 private void loadProviders() {
399 synchronized (LocationManagerService.class) {
400 if (sProvidersLoaded) {
401 return;
402 }
403
404 // Load providers
405 loadProvidersNoSync();
406 sProvidersLoaded = true;
407 }
408 }
409
410 private void loadProvidersNoSync() {
411 try {
412 _loadProvidersNoSync();
413 } catch (Exception e) {
414 Log.e(TAG, "Exception loading providers:", e);
415 }
416 }
417
418 private void _loadProvidersNoSync() {
419 // Attempt to load "real" providers first
420 if (NetworkLocationProvider.isSupported()) {
421 // Create a network location provider
422 mNetworkLocationProvider = new NetworkLocationProvider(mContext, mMasfClient);
423 LocationProviderImpl.addProvider(mNetworkLocationProvider);
424 }
425
426 if (GpsLocationProvider.isSupported()) {
427 // Create a gps location provider
428 mGpsLocationProvider = new GpsLocationProvider(mContext, mCollector);
429 LocationProviderImpl.addProvider(mGpsLocationProvider);
430 }
431
432 // Load fake providers if real providers are not available
433 File f = new File(LocationManager.PROVIDER_DIR);
434 if (f.isDirectory()) {
435 File[] subdirs = f.listFiles();
436 for (int i = 0; i < subdirs.length; i++) {
437 if (!subdirs[i].isDirectory()) {
438 continue;
439 }
440
441 String name = subdirs[i].getName();
442
443 if (Config.LOGD) {
444 Log.d(TAG, "Found dir " + subdirs[i].getAbsolutePath());
445 Log.d(TAG, "name = " + name);
446 }
447
448 // Don't create a fake provider if a real provider exists
449 if (LocationProviderImpl.getProvider(name) == null) {
450 LocationProviderImpl provider = null;
451 try {
452 File classFile = new File(subdirs[i], "class");
453 // Look for a 'class' file
454 provider = LocationProviderImpl.loadFromClass(classFile);
455
456 // Look for an 'kml', 'nmea', or 'track' file
457 if (provider == null) {
458 // Load properties from 'properties' file, if present
459 File propertiesFile = new File(subdirs[i], "properties");
460
461 if (propertiesFile.exists()) {
462 provider = new TrackProvider(name);
463 ((TrackProvider)provider).readProperties(propertiesFile);
464
465 File kmlFile = new File(subdirs[i], "kml");
466 if (kmlFile.exists()) {
467 ((TrackProvider) provider).readKml(kmlFile);
468 } else {
469 File nmeaFile = new File(subdirs[i], "nmea");
470 if (nmeaFile.exists()) {
471 ((TrackProvider) provider).readNmea(name, nmeaFile);
472 } else {
473 File trackFile = new File(subdirs[i], "track");
474 if (trackFile.exists()) {
475 ((TrackProvider) provider).readTrack(trackFile);
476 }
477 }
478 }
479 }
480 }
481 if (provider != null) {
482 LocationProviderImpl.addProvider(provider);
483 }
484 // Grab the initial location of a TrackProvider and
485 // store it as the last known location for that provider
486 if (provider instanceof TrackProvider) {
487 TrackProvider tp = (TrackProvider) provider;
488 mLastKnownLocation.put(tp.getName(), tp.getInitialLocation());
489 }
490 } catch (Exception e) {
491 Log.e(TAG, "Exception loading provder " + name, e);
492 }
493 }
494 }
495 }
496
497 updateProviders();
498 }
499
500 /**
501 * @param context the context that the LocationManagerService runs in
502 */
503 public LocationManagerService(Context context) {
504 super();
505 mContext = context;
506 mLocationHandler = new LocationWorkerHandler();
507
508 if (Config.LOGD) {
509 Log.d(TAG, "Constructed LocationManager Service");
510 }
511
512 // Initialize the LocationMasfClient
513 mMasfClient = new LocationMasfClient(mContext);
514
515 // Create location collector
516 mCollector = new LocationCollector(mMasfClient);
517
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800518 // Alarm manager, needs to be done before calling loadProviders() below
519 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
520
521 // Create a wake lock, needs to be done before calling loadProviders() below
522 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
523 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
524
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700525 // Load providers
526 loadProviders();
527
528 // Listen for Radio changes
529 mTelephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
530 mTelephonyManager.listen(mPhoneStateListener,
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800531 PhoneStateListener.LISTEN_CELL_LOCATION |
532 PhoneStateListener.LISTEN_SIGNAL_STRENGTH |
533 PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700534
535 // Register for Network (Wifi or Mobile) updates
536 NetworkStateBroadcastReceiver networkReceiver = new NetworkStateBroadcastReceiver();
537 IntentFilter networkIntentFilter = new IntentFilter();
538 networkIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
539 networkIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
540 networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
541 networkIntentFilter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
542 context.registerReceiver(networkReceiver, networkIntentFilter);
543
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700544 // Register for power updates
545 PowerStateBroadcastReceiver powerStateReceiver = new PowerStateBroadcastReceiver();
546 IntentFilter intentFilter = new IntentFilter();
547 intentFilter.addAction(ALARM_INTENT);
548 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
549 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
550 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
551 context.registerReceiver(powerStateReceiver, intentFilter);
552
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700553 // Get the wifi manager
554 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
555
556 // Create a wifi lock for future use
557 mWifiLock = getWifiWakelock();
558
559 // There might be an existing wifi scan available
560 if (mWifiManager != null) {
561 List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
562 if (wifiScanResults != null && wifiScanResults.size() != 0) {
563 if (mNetworkLocationProvider != null) {
564 mNetworkLocationProvider.updateWifiScanResults(wifiScanResults);
565 }
566 }
567 }
568 }
569
570 private WifiManager.WifiLock getWifiWakelock() {
571 if (mWifiLock == null && mWifiManager != null) {
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800572 mWifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, WIFILOCK_KEY);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700573 mWifiLock.setReferenceCounted(false);
574 }
575 return mWifiLock;
576 }
577
578 private boolean isAllowedBySettings(String provider) {
579 if (mEnabledProviders.contains(provider)) {
580 return true;
581 }
582 if (mDisabledProviders.contains(provider)) {
583 return false;
584 }
585 // Use system settings
586 ContentResolver resolver = mContext.getContentResolver();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800587 String allowedProviders = Settings.Secure.getString(resolver,
588 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700589
590 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
591 }
592
593 private void checkPermissions(String provider) {
594 if (LocationManager.GPS_PROVIDER.equals(provider)
595 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
596 != PackageManager.PERMISSION_GRANTED)) {
597 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
598 }
599 if (LocationManager.NETWORK_PROVIDER.equals(provider)
600 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
601 != PackageManager.PERMISSION_GRANTED)
602 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
603 != PackageManager.PERMISSION_GRANTED)) {
604 throw new SecurityException(
605 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
606 }
607 }
608
609 private boolean isAllowedProvider(String provider) {
610 if (LocationManager.GPS_PROVIDER.equals(provider)
611 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
612 != PackageManager.PERMISSION_GRANTED)) {
613 return false;
614 }
615 if (LocationManager.NETWORK_PROVIDER.equals(provider)
616 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
617 != PackageManager.PERMISSION_GRANTED)
618 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
619 != PackageManager.PERMISSION_GRANTED)) {
620 return false;
621 }
622
623 return true;
624 }
625
626 private String[] getPackageNames() {
627 // Since a single UID may correspond to multiple packages, this can only be used as an
628 // approximation for tracking
629 return mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
630 }
631
632 public List<String> getAllProviders() {
633 try {
634 return _getAllProviders();
635 } catch (SecurityException se) {
636 throw se;
637 } catch (Exception e) {
638 Log.e(TAG, "getAllProviders got exception:", e);
639 return null;
640 }
641 }
642
643 private List<String> _getAllProviders() {
644 if (Config.LOGD) {
645 Log.d(TAG, "getAllProviders");
646 }
647 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
648 ArrayList<String> out = new ArrayList<String>(providers.size());
649
650 for (LocationProviderImpl p : providers) {
651 out.add(p.getName());
652 }
653 return out;
654 }
655
656 public List<String> getProviders(boolean enabledOnly) {
657 try {
658 return _getProviders(enabledOnly);
659 } catch (SecurityException se) {
660 throw se;
661 } catch (Exception e) {
662 Log.e(TAG, "getProviders gotString exception:", e);
663 return null;
664 }
665 }
666
667 private List<String> _getProviders(boolean enabledOnly) {
668 if (Config.LOGD) {
669 Log.d(TAG, "getProviders");
670 }
671 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
672 ArrayList<String> out = new ArrayList<String>();
673
674 for (LocationProviderImpl p : providers) {
675 String name = p.getName();
676 if (isAllowedProvider(name)) {
677 if (enabledOnly && !isAllowedBySettings(name)) {
678 continue;
679 }
680 out.add(name);
681 }
682 }
683 return out;
684 }
685
686 public void updateProviders() {
687 for (LocationProviderImpl p : LocationProviderImpl.getProviders()) {
688 boolean isEnabled = p.isEnabled();
689 String name = p.getName();
690 boolean shouldBeEnabled = isAllowedBySettings(name);
691
692 // Collection is only allowed when network provider is being used
693 if (p.getName().equals(LocationManager.NETWORK_PROVIDER)) {
694 mCollector.updateNetworkProviderStatus(shouldBeEnabled);
695 }
696
697 if (isEnabled && !shouldBeEnabled) {
698 updateProviderListeners(name, false);
699 } else if (!isEnabled && shouldBeEnabled) {
700 updateProviderListeners(name, true);
701 }
702
703 }
704 }
705
706 private void updateProviderListeners(String provider, boolean enabled) {
707 int listeners = 0;
708
709 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
710 if (p == null) {
711 return;
712 }
713
714 synchronized (mRecordsByProvider) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700715 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
716 if (records != null) {
717 for (UpdateRecord record : records) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800718 // Sends a notification message to the receiver
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700719 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800720 Receiver receiver = record.mReceiver;
721 if (receiver.isListener()) {
722 if (enabled) {
723 receiver.getListener().onProviderEnabled(provider);
724 } else {
725 receiver.getListener().onProviderDisabled(provider);
726 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700727 } else {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800728 PendingIntent intent = receiver.getPendingIntent();
729 Intent providerIntent = new Intent();
730 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
731 try {
732 receiver.getPendingIntent().send(mContext, 0,
733 providerIntent, null, null);
734 } catch (PendingIntent.CanceledException e) {
735 _removeUpdates(receiver);
736 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700737 }
738 } catch (RemoteException e) {
739 // The death link will clean this up.
740 }
741 listeners++;
742 }
743 }
744 }
745
746 if (enabled) {
747 p.enable();
748 if (listeners > 0) {
749 p.setMinTime(getMinTime(provider));
750 p.enableLocationTracking(true);
751 updateWakelockStatus(mScreenOn);
752 }
753 } else {
754 p.enableLocationTracking(false);
755 p.disable();
756 updateWakelockStatus(mScreenOn);
757 }
758
759 if (enabled && listeners > 0) {
760 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
761 Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
762 mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
763 } else {
764 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
765 }
766 }
767
768 private long getMinTime(String provider) {
769 long minTime = Long.MAX_VALUE;
770 synchronized (mRecordsByProvider) {
771 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
772 if (records != null) {
773 for (UpdateRecord r : records) {
774 minTime = Math.min(minTime, r.mMinTime);
775 }
776 }
777 }
778 return minTime;
779 }
780
781 private class UpdateRecord {
782 String mProvider;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800783 Receiver mReceiver;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700784 long mMinTime;
785 float mMinDistance;
786 String[] mPackages;
787
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800788 UpdateRecord(String provider, long minTime, float minDistance,
789 Receiver receiver, String[] packages) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700790 mProvider = provider;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800791 mReceiver = receiver;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700792 mMinTime = minTime;
793 mMinDistance = minDistance;
794 mPackages = packages;
795
796 synchronized (mRecordsByProvider) {
797 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
798 if (records == null) {
799 records = new HashSet<UpdateRecord>();
800 mRecordsByProvider.put(provider, records);
801 }
802 records.add(this);
803 }
804 }
805
806 /**
807 * Method to be called when a record will no longer be used. Calling this multiple times
808 * must have the same effect as calling it once.
809 */
810 public void dispose() {
811 synchronized (mRecordsByProvider) {
812 HashSet<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
813 records.remove(this);
814 }
815 }
816
817 /**
818 * Calls dispose().
819 */
820 @Override protected void finalize() {
821 dispose();
822 }
823 }
824
825 public void requestLocationUpdates(String provider,
826 long minTime, float minDistance, ILocationListener listener) {
827
828 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800829 _requestLocationUpdates(provider, minTime, minDistance,
830 new Receiver(listener));
831 } catch (SecurityException se) {
832 throw se;
833 } catch (Exception e) {
834 Log.e(TAG, "requestUpdates got exception:", e);
835 }
836 }
837
838 public void requestLocationUpdatesPI(String provider,
839 long minTime, float minDistance, PendingIntent intent) {
840 try {
841 _requestLocationUpdates(provider, minTime, minDistance,
842 new Receiver(intent));
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700843 } catch (SecurityException se) {
844 throw se;
845 } catch (Exception e) {
846 Log.e(TAG, "requestUpdates got exception:", e);
847 }
848 }
849
850 private void _requestLocationUpdates(String provider,
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800851 long minTime, float minDistance, Receiver receiver) {
852 Object key = receiver.getKey();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700853 if (Config.LOGD) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800854 Log.d(TAG, "_requestLocationUpdates: listener = " + key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700855 }
856
857 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
858 if (impl == null) {
859 throw new IllegalArgumentException("provider=" + provider);
860 }
861
862 checkPermissions(provider);
863
864 String[] packages = getPackageNames();
865
866 // so wakelock calls will succeed
867 long identity = Binder.clearCallingIdentity();
868 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800869 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, packages);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700870 synchronized (mLocationListeners) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800871 if (mListeners.get(key) == null) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700872 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800873 if (receiver.isListener()) {
874 receiver.getListener().asBinder().linkToDeath(receiver, 0);
875 }
876 mListeners.put(key, receiver);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700877 } catch (RemoteException e) {
878 return;
879 }
880 }
881
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800882 HashMap<String,UpdateRecord> records = mLocationListeners.get(key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700883 if (records == null) {
884 records = new HashMap<String,UpdateRecord>();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800885 mLocationListeners.put(key, records);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700886 }
887 UpdateRecord oldRecord = records.put(provider, r);
888 if (oldRecord != null) {
889 oldRecord.dispose();
890 }
891
892 if (impl instanceof NetworkLocationProvider) {
893 ((NetworkLocationProvider) impl).addListener(packages);
894 }
895
896 boolean isProviderEnabled = isAllowedBySettings(provider);
897 if (isProviderEnabled) {
898 long minTimeForProvider = getMinTime(provider);
899 impl.setMinTime(minTimeForProvider);
900 impl.enableLocationTracking(true);
901 updateWakelockStatus(mScreenOn);
902
903 // Clear heartbeats if any before starting a new one
904 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
905 Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
906 mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
907
908 } else {
909 try {
910 // Notify the listener that updates are currently disabled
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800911 if (receiver.isListener()) {
912 receiver.getListener().onProviderDisabled(provider);
913 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700914 } catch(RemoteException e) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800915 Log.w(TAG, "RemoteException calling onProviderDisabled on " +
916 receiver.getListener());
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700917 }
918 }
919 }
920 } finally {
921 Binder.restoreCallingIdentity(identity);
922 }
923 }
924
925 public void removeUpdates(ILocationListener listener) {
926 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800927 _removeUpdates(new Receiver(listener));
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700928 } catch (SecurityException se) {
929 throw se;
930 } catch (Exception e) {
931 Log.e(TAG, "removeUpdates got exception:", e);
932 }
933 }
934
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800935 public void removeUpdatesPI(PendingIntent intent) {
936 try {
937 _removeUpdates(new Receiver(intent));
938 } catch (SecurityException se) {
939 throw se;
940 } catch (Exception e) {
941 Log.e(TAG, "removeUpdates got exception:", e);
942 }
943 }
944
945 private void _removeUpdates(Receiver receiver) {
946 Object key = receiver.getKey();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700947 if (Config.LOGD) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800948 Log.d(TAG, "_removeUpdates: listener = " + key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700949 }
950
951 // so wakelock calls will succeed
952 long identity = Binder.clearCallingIdentity();
953 try {
954 synchronized (mLocationListeners) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800955 Receiver myReceiver = mListeners.remove(key);
956 if ((myReceiver != null) && (myReceiver.isListener())) {
957 myReceiver.getListener().asBinder().unlinkToDeath(myReceiver, 0);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700958 }
959
960 // Record which providers were associated with this listener
961 HashSet<String> providers = new HashSet<String>();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800962 HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700963 if (oldRecords != null) {
964 // Call dispose() on the obsolete update records.
965 for (UpdateRecord record : oldRecords.values()) {
966 if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) {
967 if (mNetworkLocationProvider != null) {
968 mNetworkLocationProvider.removeListener(record.mPackages);
969 }
970 }
971 record.dispose();
972 }
973 // Accumulate providers
974 providers.addAll(oldRecords.keySet());
975 }
976
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800977 mLocationListeners.remove(key);
978 mLastFixBroadcast.remove(key);
979 mLastStatusBroadcast.remove(key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700980
981 // See if the providers associated with this listener have any
982 // other listeners; if one does, inform it of the new smallest minTime
983 // value; if one does not, disable location tracking for it
984 for (String provider : providers) {
985 // If provider is already disabled, don't need to do anything
986 if (!isAllowedBySettings(provider)) {
987 continue;
988 }
989
990 boolean hasOtherListener = false;
991 synchronized (mRecordsByProvider) {
992 HashSet<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
993 if (recordsForProvider != null && recordsForProvider.size() > 0) {
994 hasOtherListener = true;
995 }
996 }
997
998 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
999 if (p != null) {
1000 if (hasOtherListener) {
1001 p.setMinTime(getMinTime(provider));
1002 } else {
1003 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
1004 p.enableLocationTracking(false);
1005 }
1006 }
1007 }
1008
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001009 updateWakelockStatus(mScreenOn);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001010 }
1011 } finally {
1012 Binder.restoreCallingIdentity(identity);
1013 }
1014 }
1015
1016 public boolean addGpsStatusListener(IGpsStatusListener listener) {
1017 if (mGpsLocationProvider == null) {
1018 return false;
1019 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001020 if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1021 PackageManager.PERMISSION_GRANTED) {
1022 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1023 }
1024
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001025 try {
1026 mGpsLocationProvider.addGpsStatusListener(listener);
1027 } catch (RemoteException e) {
1028 Log.w(TAG, "RemoteException in addGpsStatusListener");
1029 return false;
1030 }
1031 return true;
1032 }
1033
1034 public void removeGpsStatusListener(IGpsStatusListener listener) {
1035 mGpsLocationProvider.removeGpsStatusListener(listener);
1036 }
1037
1038 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1039 // first check for permission to the provider
1040 checkPermissions(provider);
1041 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1042 if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1043 != PackageManager.PERMISSION_GRANTED)) {
1044 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1045 }
1046
1047 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
1048 if (provider == null) {
1049 return false;
1050 }
1051
1052 return impl.sendExtraCommand(command, extras);
1053 }
1054
1055 class ProximityAlert {
1056 double mLatitude;
1057 double mLongitude;
1058 float mRadius;
1059 long mExpiration;
1060 PendingIntent mIntent;
1061 Location mLocation;
1062
1063 public ProximityAlert(double latitude, double longitude,
1064 float radius, long expiration, PendingIntent intent) {
1065 mLatitude = latitude;
1066 mLongitude = longitude;
1067 mRadius = radius;
1068 mExpiration = expiration;
1069 mIntent = intent;
1070
1071 mLocation = new Location("");
1072 mLocation.setLatitude(latitude);
1073 mLocation.setLongitude(longitude);
1074 }
1075
1076 public long getExpiration() {
1077 return mExpiration;
1078 }
1079
1080 public PendingIntent getIntent() {
1081 return mIntent;
1082 }
1083
1084 public boolean isInProximity(double latitude, double longitude) {
1085 Location loc = new Location("");
1086 loc.setLatitude(latitude);
1087 loc.setLongitude(longitude);
1088
1089 double radius = loc.distanceTo(mLocation);
1090 return radius <= mRadius;
1091 }
1092 }
1093
1094 // Listener for receiving locations to trigger proximity alerts
1095 class ProximityListener extends ILocationListener.Stub {
1096
1097 boolean isGpsAvailable = false;
1098
1099 public void onLocationChanged(Location loc) {
1100
1101 // If Gps is available, then ignore updates from NetworkLocationProvider
1102 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1103 isGpsAvailable = true;
1104 }
1105 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1106 return;
1107 }
1108
1109 // Process proximity alerts
1110 long now = System.currentTimeMillis();
1111 double latitude = loc.getLatitude();
1112 double longitude = loc.getLongitude();
1113 ArrayList<PendingIntent> intentsToRemove = null;
1114
1115 for (ProximityAlert alert : mProximityAlerts.values()) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001116 PendingIntent intent = alert.getIntent();
1117 long expiration = alert.getExpiration();
1118
1119 if ((expiration == -1) || (now <= expiration)) {
1120 boolean entered = mProximitiesEntered.contains(alert);
1121 boolean inProximity =
1122 alert.isInProximity(latitude, longitude);
1123 if (!entered && inProximity) {
1124 if (Config.LOGD) {
1125 Log.i(TAG, "Entered alert");
1126 }
1127 mProximitiesEntered.add(alert);
1128 Intent enteredIntent = new Intent();
1129 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1130 try {
1131 intent.send(mContext, 0, enteredIntent, null, null);
1132 } catch (PendingIntent.CanceledException e) {
1133 if (Config.LOGD) {
1134 Log.i(TAG, "Canceled proximity alert: " + alert, e);
1135 }
1136 if (intentsToRemove == null) {
1137 intentsToRemove = new ArrayList<PendingIntent>();
1138 }
1139 intentsToRemove.add(intent);
1140 }
1141 } else if (entered && !inProximity) {
1142 if (Config.LOGD) {
1143 Log.i(TAG, "Exited alert");
1144 }
1145 mProximitiesEntered.remove(alert);
1146 Intent exitedIntent = new Intent();
1147 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1148 try {
1149 intent.send(mContext, 0, exitedIntent, null, null);
1150 } catch (PendingIntent.CanceledException e) {
1151 if (Config.LOGD) {
1152 Log.i(TAG, "Canceled proximity alert: " + alert, e);
1153 }
1154 if (intentsToRemove == null) {
1155 intentsToRemove = new ArrayList<PendingIntent>();
1156 }
1157 intentsToRemove.add(intent);
1158 }
1159 }
1160 } else {
1161 // Mark alert for expiration
1162 if (Config.LOGD) {
1163 Log.i(TAG, "Expiring proximity alert: " + alert);
1164 }
1165 if (intentsToRemove == null) {
1166 intentsToRemove = new ArrayList<PendingIntent>();
1167 }
1168 intentsToRemove.add(alert.getIntent());
1169 }
1170 }
1171
1172 // Remove expired alerts
1173 if (intentsToRemove != null) {
1174 for (PendingIntent i : intentsToRemove) {
1175 mProximityAlerts.remove(i);
1176 ProximityAlert alert = mProximityAlerts.get(i);
1177 mProximitiesEntered.remove(alert);
1178 }
1179 }
1180
1181 }
1182
1183 public void onProviderDisabled(String provider) {
1184 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1185 isGpsAvailable = false;
1186 }
1187 }
1188
1189 public void onProviderEnabled(String provider) {
1190 // ignore
1191 }
1192
1193 public void onStatusChanged(String provider, int status, Bundle extras) {
1194 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1195 (status != LocationProvider.AVAILABLE)) {
1196 isGpsAvailable = false;
1197 }
1198 }
1199 }
1200
1201 public void addProximityAlert(double latitude, double longitude,
1202 float radius, long expiration, PendingIntent intent) {
1203 try {
1204 _addProximityAlert(latitude, longitude, radius, expiration, intent);
1205 } catch (SecurityException se) {
1206 throw se;
1207 } catch (Exception e) {
1208 Log.e(TAG, "addProximityAlert got exception:", e);
1209 }
1210 }
1211
1212 private void _addProximityAlert(double latitude, double longitude,
1213 float radius, long expiration, PendingIntent intent) {
1214 if (Config.LOGD) {
1215 Log.d(TAG, "addProximityAlert: latitude = " + latitude +
1216 ", longitude = " + longitude +
1217 ", expiration = " + expiration +
1218 ", intent = " + intent);
1219 }
1220
1221 // Require ability to access all providers for now
1222 if (!isAllowedProvider(LocationManager.GPS_PROVIDER) ||
1223 !isAllowedProvider(LocationManager.NETWORK_PROVIDER)) {
1224 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1225 }
1226
1227 if (expiration != -1) {
1228 expiration += System.currentTimeMillis();
1229 }
1230 ProximityAlert alert = new ProximityAlert(latitude, longitude, radius, expiration, intent);
1231 mProximityAlerts.put(intent, alert);
1232
1233 if (mProximityListener == null) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001234 mProximityListener = new Receiver(new ProximityListener());
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001235
1236 LocationProvider provider = LocationProviderImpl.getProvider(
1237 LocationManager.GPS_PROVIDER);
1238 if (provider != null) {
1239 _requestLocationUpdates(provider.getName(), 1000L, 1.0f, mProximityListener);
1240 }
1241
1242 provider =
1243 LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER);
1244 if (provider != null) {
1245 _requestLocationUpdates(provider.getName(), 1000L, 1.0f, mProximityListener);
1246 }
1247 }
1248 }
1249
1250 public void removeProximityAlert(PendingIntent intent) {
1251 try {
1252 _removeProximityAlert(intent);
1253 } catch (SecurityException se) {
1254 throw se;
1255 } catch (Exception e) {
1256 Log.e(TAG, "removeProximityAlert got exception:", e);
1257 }
1258 }
1259
1260 private void _removeProximityAlert(PendingIntent intent) {
1261 if (Config.LOGD) {
1262 Log.d(TAG, "removeProximityAlert: intent = " + intent);
1263 }
1264
1265 mProximityAlerts.remove(intent);
1266 if (mProximityAlerts.size() == 0) {
1267 _removeUpdates(mProximityListener);
1268 mProximityListener = null;
1269 }
1270 }
1271
1272 /**
1273 * @return null if the provider does not exits
1274 * @throw SecurityException if the provider is not allowed to be
1275 * accessed by the caller
1276 */
1277 public Bundle getProviderInfo(String provider) {
1278 try {
1279 return _getProviderInfo(provider);
1280 } catch (SecurityException se) {
1281 throw se;
1282 } catch (Exception e) {
1283 Log.e(TAG, "_getProviderInfo got exception:", e);
1284 return null;
1285 }
1286 }
1287
1288 private Bundle _getProviderInfo(String provider) {
1289 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1290 if (p == null) {
1291 return null;
1292 }
1293
1294 checkPermissions(provider);
1295
1296 Bundle b = new Bundle();
1297 b.putBoolean("network", p.requiresNetwork());
1298 b.putBoolean("satellite", p.requiresSatellite());
1299 b.putBoolean("cell", p.requiresCell());
1300 b.putBoolean("cost", p.hasMonetaryCost());
1301 b.putBoolean("altitude", p.supportsAltitude());
1302 b.putBoolean("speed", p.supportsSpeed());
1303 b.putBoolean("bearing", p.supportsBearing());
1304 b.putInt("power", p.getPowerRequirement());
1305 b.putInt("accuracy", p.getAccuracy());
1306
1307 return b;
1308 }
1309
1310 public boolean isProviderEnabled(String provider) {
1311 try {
1312 return _isProviderEnabled(provider);
1313 } catch (SecurityException se) {
1314 throw se;
1315 } catch (Exception e) {
1316 Log.e(TAG, "isProviderEnabled got exception:", e);
1317 return false;
1318 }
1319 }
1320
1321 private boolean _isProviderEnabled(String provider) {
1322 checkPermissions(provider);
1323
1324 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1325 if (p == null) {
1326 throw new IllegalArgumentException("provider=" + provider);
1327 }
1328 return isAllowedBySettings(provider);
1329 }
1330
1331 public Location getLastKnownLocation(String provider) {
1332 try {
1333 return _getLastKnownLocation(provider);
1334 } catch (SecurityException se) {
1335 throw se;
1336 } catch (Exception e) {
1337 Log.e(TAG, "getLastKnownLocation got exception:", e);
1338 return null;
1339 }
1340 }
1341
1342 private Location _getLastKnownLocation(String provider) {
1343 checkPermissions(provider);
1344
1345 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1346 if (p == null) {
1347 throw new IllegalArgumentException("provider=" + provider);
1348 }
1349
1350 if (!isAllowedBySettings(provider)) {
1351 return null;
1352 }
1353
1354 Location location = mLastKnownLocation.get(provider);
1355 if (location == null) {
1356 // Get the persistent last known location for the provider
1357 location = readLastKnownLocation(provider);
1358 if (location != null) {
1359 mLastKnownLocation.put(provider, location);
1360 }
1361 }
1362
1363 return location;
1364 }
1365
1366 private boolean shouldBroadcast(Location loc, Location lastLoc, UpdateRecord record) {
1367 // Always broadcast the first update
1368 if (lastLoc == null) {
1369 return true;
1370 }
1371
1372 // Don't broadcast same location again regardless of condition
1373 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1374 if (loc.getTime() == lastLoc.getTime()) {
1375 return false;
1376 }
1377
1378 // Check whether sufficient distance has been traveled
1379 double minDistance = record.mMinDistance;
1380 if (minDistance > 0.0) {
1381 if (loc.distanceTo(lastLoc) <= minDistance) {
1382 return false;
1383 }
1384 }
1385
1386 return true;
1387 }
1388
1389 private void handleLocationChanged(String provider) {
1390 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
1391 if (records == null || records.size() == 0) {
1392 return;
1393 }
1394
1395 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1396 if (p == null) {
1397 return;
1398 }
1399
1400 // Get location object
1401 Location loc = mLocationsByProvider.get(provider);
1402 if (loc == null) {
1403 loc = new Location(provider);
1404 mLocationsByProvider.put(provider, loc);
1405 } else {
1406 loc.reset();
1407 }
1408
1409 // Use the mock location if available
1410 Location mockLoc = mMockProviderLocation.get(provider);
1411 boolean locationValid;
1412 if (mockLoc != null) {
1413 locationValid = true;
1414 loc.set(mockLoc);
1415 } else {
1416 locationValid = p.getLocation(loc);
1417 }
1418
1419 // Update last known location for provider
1420 if (locationValid) {
1421 Location location = mLastKnownLocation.get(provider);
1422 if (location == null) {
1423 mLastKnownLocation.put(provider, new Location(loc));
1424 } else {
1425 location.set(loc);
1426 }
1427 writeLastKnownLocation(provider, loc);
1428
1429 if (p instanceof NetworkLocationProvider) {
1430 mWakeLockNetworkReceived = true;
1431 } else if (p instanceof GpsLocationProvider) {
1432 // Gps location received signal is in NetworkStateBroadcastReceiver
1433 }
1434 }
1435
1436 // Fetch latest status update time
1437 long newStatusUpdateTime = p.getStatusUpdateTime();
1438
1439 // Override real time with mock time if present
1440 Long mockStatusUpdateTime = mMockProviderStatusUpdateTime.get(provider);
1441 if (mockStatusUpdateTime != null) {
1442 newStatusUpdateTime = mockStatusUpdateTime.longValue();
1443 }
1444
1445 // Get latest status
1446 Bundle extras = new Bundle();
1447 int status = p.getStatus(extras);
1448
1449 // Override status with mock status if present
1450 Integer mockStatus = mMockProviderStatus.get(provider);
1451 if (mockStatus != null) {
1452 status = mockStatus.intValue();
1453 }
1454
1455 // Override extras with mock extras if present
1456 Bundle mockExtras = mMockProviderStatusExtras.get(provider);
1457 if (mockExtras != null) {
1458 extras.clear();
1459 extras.putAll(mockExtras);
1460 }
1461
1462 // Broadcast location or status to all listeners
1463 for (UpdateRecord r : records) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001464 Receiver receiver = r.mReceiver;
1465 Object key = receiver.getKey();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001466
1467 // Broadcast location only if it is valid
1468 if (locationValid) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001469 HashMap<String,Location> map = mLastFixBroadcast.get(key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001470 if (map == null) {
1471 map = new HashMap<String,Location>();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001472 mLastFixBroadcast.put(key, map);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001473 }
1474 Location lastLoc = map.get(provider);
1475 if ((lastLoc == null) || shouldBroadcast(loc, lastLoc, r)) {
1476 if (lastLoc == null) {
1477 lastLoc = new Location(loc);
1478 map.put(provider, lastLoc);
1479 } else {
1480 lastLoc.set(loc);
1481 }
1482 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001483 receiver.onLocationChanged(loc);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001484 } catch (RemoteException doe) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001485 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1486 _removeUpdates(receiver);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001487 }
1488 }
1489 }
1490
1491 // Broadcast status message
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001492 HashMap<String,Long> statusMap = mLastStatusBroadcast.get(key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001493 if (statusMap == null) {
1494 statusMap = new HashMap<String,Long>();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001495 mLastStatusBroadcast.put(key, statusMap);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001496 }
1497 long prevStatusUpdateTime =
1498 (statusMap.get(provider) != null) ? statusMap.get(provider) : 0;
1499
1500 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1501 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1502
1503 statusMap.put(provider, newStatusUpdateTime);
1504 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001505 receiver.onStatusChanged(provider, status, extras);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001506 } catch (RemoteException doe) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001507 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1508 _removeUpdates(receiver);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001509 }
1510 }
1511 }
1512 }
1513
1514 private class LocationWorkerHandler extends Handler {
1515
1516 @Override
1517 public void handleMessage(Message msg) {
1518 try {
1519 if (msg.what == MESSAGE_HEARTBEAT) {
1520 // log("LocationWorkerHandler: Heartbeat!");
1521
1522 synchronized (mRecordsByProvider) {
1523 String provider = (String) msg.obj;
1524 if (!isAllowedBySettings(provider)) {
1525 return;
1526 }
1527
1528 // Process the location fix if the screen is on or we're holding a wakelock
1529 if (mScreenOn || (mWakeLockAcquireTime != 0)) {
1530 handleLocationChanged(provider);
1531 }
1532
1533 // If it continues to have listeners
1534 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
1535 if (records != null && records.size() > 0) {
1536 Message m = Message.obtain(this, MESSAGE_HEARTBEAT, provider);
1537 sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
1538 }
1539 }
1540
1541 if ((mWakeLockAcquireTime != 0) &&
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001542 (SystemClock.elapsedRealtime() - mWakeLockAcquireTime
1543 > MAX_TIME_FOR_WAKE_LOCK)) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001544
1545 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1546 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1547
1548 log("LocationWorkerHandler: Exceeded max time for wake lock");
1549 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1550 sendMessageAtFrontOfQueue(m);
1551
1552 } else if (mWakeLockAcquireTime != 0 &&
1553 mWakeLockGpsReceived && mWakeLockNetworkReceived) {
1554
1555 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1556 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1557
1558 log("LocationWorkerHandler: Locations received.");
1559 mWakeLockAcquireTime = 0;
1560 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1561 sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK);
1562 }
1563
1564 } else if (msg.what == MESSAGE_ACQUIRE_WAKE_LOCK) {
1565 log("LocationWorkerHandler: Acquire");
1566 acquireWakeLock();
1567 } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) {
1568 log("LocationWorkerHandler: Release");
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001569
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001570 // Update wakelock status so the next alarm is set before releasing wakelock
1571 updateWakelockStatus(mScreenOn);
1572 releaseWakeLock();
1573 }
1574 } catch (Exception e) {
1575 // Log, don't crash!
1576 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1577 }
1578 }
1579 }
1580
1581 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001582
1583 private CellState mLastCellState = null;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001584 @Override
1585 public void onCellLocationChanged(CellLocation cellLocation) {
1586 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001587 int asu = mSignalStrength;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001588
1589 // Gets cell state
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001590 mLastCellState = new CellState(mTelephonyManager, cellLocation, asu);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001591
1592 // Notify collector
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001593 mCollector.updateCellState(mLastCellState);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001594
1595 // Updates providers
1596 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1597 for (LocationProviderImpl provider : providers) {
1598 if (provider.requiresCell()) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001599 provider.updateCellState(mLastCellState);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001600 }
1601 }
1602 } catch (Exception e) {
1603 Log.e(TAG, "Exception in PhoneStateListener.onCellLocationCahnged:", e);
1604 }
1605 }
1606
1607 @Override
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001608 public void onSignalStrengthChanged(int asu) {
1609 mSignalStrength = asu;
1610
1611 if (mLastCellState != null) {
1612 mLastCellState.updateSignalStrength(asu);
1613 }
1614 }
1615
1616 @Override
1617 public void onDataConnectionStateChanged(int state) {
1618 if (mLastCellState != null) {
1619 mLastCellState.updateRadioType(mTelephonyManager);
1620 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001621 }
1622 };
1623
1624 private class PowerStateBroadcastReceiver extends BroadcastReceiver {
1625 @Override public void onReceive(Context context, Intent intent) {
1626 String action = intent.getAction();
1627
1628 if (action.equals(ALARM_INTENT)) {
1629 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1630 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1631
1632 log("PowerStateBroadcastReceiver: Alarm received");
1633 Message m = mLocationHandler.obtainMessage(MESSAGE_ACQUIRE_WAKE_LOCK);
1634 mLocationHandler.sendMessageAtFrontOfQueue(m);
1635
1636 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1637 log("PowerStateBroadcastReceiver: Screen off");
1638 updateWakelockStatus(false);
1639
1640 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1641 log("PowerStateBroadcastReceiver: Screen on");
1642 updateWakelockStatus(true);
1643
1644 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
1645 log("PowerStateBroadcastReceiver: Battery changed");
1646 int scale = intent.getIntExtra(BATTERY_EXTRA_SCALE, 100);
1647 int level = intent.getIntExtra(BATTERY_EXTRA_LEVEL, 0);
1648 boolean plugged = intent.getIntExtra(BATTERY_EXTRA_PLUGGED, 0) != 0;
1649
1650 // Notify collector battery state
1651 mCollector.updateBatteryState(scale, level, plugged);
1652 }
1653 }
1654 }
1655
1656 private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
1657 @Override public void onReceive(Context context, Intent intent) {
1658 String action = intent.getAction();
1659
1660 if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
1661
1662 List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
1663
1664 if (wifiScanResults == null) {
1665 return;
1666 }
1667
1668 // Notify provider and collector of Wifi scan results
1669 mCollector.updateWifiScanResults(wifiScanResults);
1670 if (mNetworkLocationProvider != null) {
1671 mNetworkLocationProvider.updateWifiScanResults(wifiScanResults);
1672 }
1673
1674 } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
1675 int networkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
1676
1677 boolean noConnectivity =
1678 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1679 if (!noConnectivity) {
1680 networkState = LocationProvider.AVAILABLE;
1681 }
1682
1683 // Notify location providers of current network state
1684 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1685 for (LocationProviderImpl provider : providers) {
1686 if (provider.requiresNetwork()) {
1687 provider.updateNetworkState(networkState);
1688 }
1689 }
1690
1691 } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
1692 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
1693 WifiManager.WIFI_STATE_UNKNOWN);
1694
1695 boolean enabled;
1696 if (state == WifiManager.WIFI_STATE_ENABLED) {
1697 enabled = true;
1698 } else if (state == WifiManager.WIFI_STATE_DISABLED) {
1699 enabled = false;
1700 } else {
1701 return;
1702 }
1703
1704 // Notify network provider of current wifi enabled state
1705 if (mNetworkLocationProvider != null) {
1706 mNetworkLocationProvider.updateWifiEnabledState(enabled);
1707 }
1708
1709 } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
1710
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001711 final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED,
1712 false);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001713
1714 if (!enabled) {
1715 // When GPS is disabled, we are OK to release wake-lock
1716 mWakeLockGpsReceived = true;
1717 }
1718 }
1719
1720 }
1721 }
1722
1723 // Wake locks
1724
1725 private void updateWakelockStatus(boolean screenOn) {
1726 log("updateWakelockStatus(): " + screenOn);
1727
1728 boolean needsLock = false;
1729 long minTime = Integer.MAX_VALUE;
1730
1731 if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) {
1732 needsLock = true;
1733 minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime);
1734 }
1735
1736 if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) {
1737 needsLock = true;
1738 minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime);
1739 if (screenOn) {
1740 startGps();
1741 } else if (mScreenOn && !screenOn) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001742
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001743 // We just turned the screen off so stop navigating
1744 stopGps();
1745 }
1746 }
1747
1748 mScreenOn = screenOn;
1749
1750 PendingIntent sender =
1751 PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0);
1752
1753 // Cancel existing alarm
1754 log("Cancelling existing alarm");
1755 mAlarmManager.cancel(sender);
1756
1757 if (needsLock && !mScreenOn) {
1758 long now = SystemClock.elapsedRealtime();
1759 mAlarmManager.set(
1760 AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender);
1761 mAlarmInterval = minTime;
1762 log("Creating a new wakelock alarm with minTime = " + minTime);
1763 } else {
1764 log("No need for alarm");
1765 mAlarmInterval = -1;
1766
1767 // Clear out existing wakelocks
1768 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1769 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1770 releaseWakeLock();
1771 }
1772 }
1773
1774 private void acquireWakeLock() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001775 try {
1776 acquireWakeLockX();
1777 } catch (Exception e) {
1778 // This is to catch a runtime exception thrown when we try to release an
1779 // already released lock.
1780 Log.e(TAG, "exception in acquireWakeLock()", e);
1781 }
1782 }
1783
1784 private void acquireWakeLockX() {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001785 if (mWakeLock.isHeld()) {
1786 log("Must release wakelock before acquiring");
1787 mWakeLockAcquireTime = 0;
1788 mWakeLock.release();
1789 }
1790
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001791 boolean networkActive = (mNetworkLocationProvider != null)
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001792 && mNetworkLocationProvider.isLocationTracking();
1793 boolean gpsActive = (mGpsLocationProvider != null)
1794 && mGpsLocationProvider.isLocationTracking();
1795
1796 boolean needsLock = networkActive || gpsActive;
1797 if (!needsLock) {
1798 log("No need for Lock!");
1799 return;
1800 }
1801
1802 mWakeLockGpsReceived = !gpsActive;
1803 mWakeLockNetworkReceived = !networkActive;
1804
1805 // Acquire wake lock
1806 mWakeLock.acquire();
1807 mWakeLockAcquireTime = SystemClock.elapsedRealtime();
1808 log("Acquired wakelock");
1809
1810 // Start the gps provider
1811 startGps();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001812
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001813 // Acquire cell lock
1814 if (mCellWakeLockAcquired) {
1815 // Lock is already acquired
1816 } else if (!mWakeLockNetworkReceived) {
1817 mTelephonyManager.enableLocationUpdates();
1818 mCellWakeLockAcquired = true;
1819 } else {
1820 mCellWakeLockAcquired = false;
1821 }
1822
1823 // Notify NetworkLocationProvider
1824 if (mNetworkLocationProvider != null) {
1825 mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired);
1826 }
1827
1828 // Acquire wifi lock
1829 WifiManager.WifiLock wifiLock = getWifiWakelock();
1830 if (wifiLock != null) {
1831 if (mWifiWakeLockAcquired) {
1832 // Lock is already acquired
1833 } else if (mWifiManager.isWifiEnabled() && !mWakeLockNetworkReceived) {
1834 wifiLock.acquire();
1835 mWifiWakeLockAcquired = true;
1836 } else {
1837 mWifiWakeLockAcquired = false;
1838 Log.w(TAG, "acquireWakeLock(): Unable to get WiFi lock");
1839 }
1840 }
1841 }
1842
1843 private void startGps() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001844 boolean gpsActive = (mGpsLocationProvider != null)
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001845 && mGpsLocationProvider.isLocationTracking();
1846 if (gpsActive) {
1847 mGpsLocationProvider.startNavigating();
1848 }
1849 }
1850
1851 private void stopGps() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001852 boolean gpsActive = mGpsLocationProvider != null
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001853 && mGpsLocationProvider.isLocationTracking();
1854 if (gpsActive) {
1855 mGpsLocationProvider.stopNavigating();
1856 }
1857 }
1858
1859 private void releaseWakeLock() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001860 try {
1861 releaseWakeLockX();
1862 } catch (Exception e) {
1863 // This is to catch a runtime exception thrown when we try to release an
1864 // already released lock.
1865 Log.e(TAG, "exception in releaseWakeLock()", e);
1866 }
1867 }
1868
1869 private void releaseWakeLockX() {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001870 // Release wifi lock
1871 WifiManager.WifiLock wifiLock = getWifiWakelock();
1872 if (wifiLock != null) {
1873 if (mWifiWakeLockAcquired) {
1874 wifiLock.release();
1875 mWifiWakeLockAcquired = false;
1876 }
1877 }
1878
1879 if (!mScreenOn) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001880
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001881 // Stop the gps
1882 stopGps();
1883 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001884
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001885 // Release cell lock
1886 if (mCellWakeLockAcquired) {
1887 mTelephonyManager.disableLocationUpdates();
1888 mCellWakeLockAcquired = false;
1889 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001890
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001891 // Notify NetworkLocationProvider
1892 if (mNetworkLocationProvider != null) {
1893 mNetworkLocationProvider.updateCellLockStatus(mCellWakeLockAcquired);
1894 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001895
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001896 // Release wake lock
1897 mWakeLockAcquireTime = 0;
1898 if (mWakeLock.isHeld()) {
1899 log("Released wakelock");
1900 mWakeLock.release();
1901 } else {
1902 log("Can't release wakelock again!");
1903 }
1904 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001905
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001906 // Geocoder
1907
1908 public String getFromLocation(double latitude, double longitude, int maxResults,
1909 String language, String country, String variant, String appName, List<Address> addrs) {
1910 try {
1911 Locale locale = new Locale(language, country, variant);
1912 mMasfClient.reverseGeocode(locale, appName, latitude, longitude, maxResults, addrs);
1913 return null;
1914 } catch(IOException e) {
1915 return e.getMessage();
1916 } catch(Exception e) {
1917 Log.e(TAG, "getFromLocation got exception:", e);
1918 return null;
1919 }
1920 }
1921
1922 public String getFromLocationName(String locationName,
1923 double lowerLeftLatitude, double lowerLeftLongitude,
1924 double upperRightLatitude, double upperRightLongitude, int maxResults,
1925 String language, String country, String variant, String appName, List<Address> addrs) {
1926
1927 try {
1928 Locale locale = new Locale(language, country, variant);
1929 mMasfClient.forwardGeocode(locale, appName, locationName,
1930 lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
1931 maxResults, addrs);
1932 return null;
1933 } catch(IOException e) {
1934 return e.getMessage();
1935 } catch(Exception e) {
1936 Log.e(TAG, "getFromLocationName got exception:", e);
1937 return null;
1938 }
1939 }
1940
1941 // Mock Providers
1942
1943 class MockProvider extends LocationProviderImpl {
1944 boolean mRequiresNetwork;
1945 boolean mRequiresSatellite;
1946 boolean mRequiresCell;
1947 boolean mHasMonetaryCost;
1948 boolean mSupportsAltitude;
1949 boolean mSupportsSpeed;
1950 boolean mSupportsBearing;
1951 int mPowerRequirement;
1952 int mAccuracy;
1953
1954 public MockProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
1955 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
1956 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
1957 super(name);
1958
1959 mRequiresNetwork = requiresNetwork;
1960 mRequiresSatellite = requiresSatellite;
1961 mRequiresCell = requiresCell;
1962 mHasMonetaryCost = hasMonetaryCost;
1963 mSupportsAltitude = supportsAltitude;
1964 mSupportsBearing = supportsBearing;
1965 mSupportsSpeed = supportsSpeed;
1966 mPowerRequirement = powerRequirement;
1967 mAccuracy = accuracy;
1968 }
1969
1970 @Override
1971 public void disable() {
1972 String name = getName();
1973 mEnabledProviders.remove(name);
1974 mDisabledProviders.add(name);
1975 }
1976
1977 @Override
1978 public void enable() {
1979 String name = getName();
1980 mEnabledProviders.add(name);
1981 mDisabledProviders.remove(name);
1982 }
1983
1984 @Override
1985 public boolean getLocation(Location l) {
1986 Location loc = mMockProviderLocation.get(getName());
1987 if (loc == null) {
1988 return false;
1989 }
1990 l.set(loc);
1991 return true;
1992 }
1993
1994 @Override
1995 public int getStatus(Bundle extras) {
1996 String name = getName();
1997 Integer s = mMockProviderStatus.get(name);
1998 int status = (s == null) ? AVAILABLE : s.intValue();
1999 Bundle newExtras = mMockProviderStatusExtras.get(name);
2000 if (newExtras != null) {
2001 extras.clear();
2002 extras.putAll(newExtras);
2003 }
2004 return status;
2005 }
2006
2007 @Override
2008 public boolean isEnabled() {
2009 return mEnabledProviders.contains(getName());
2010 }
2011
2012 @Override
2013 public int getAccuracy() {
2014 return mAccuracy;
2015 }
2016
2017 @Override
2018 public int getPowerRequirement() {
2019 return mPowerRequirement;
2020 }
2021
2022 @Override
2023 public boolean hasMonetaryCost() {
2024 return mHasMonetaryCost;
2025 }
2026
2027 @Override
2028 public boolean requiresCell() {
2029 return mRequiresCell;
2030 }
2031
2032 @Override
2033 public boolean requiresNetwork() {
2034 return mRequiresNetwork;
2035 }
2036
2037 @Override
2038 public boolean requiresSatellite() {
2039 return mRequiresSatellite;
2040 }
2041
2042 @Override
2043 public boolean supportsAltitude() {
2044 return mSupportsAltitude;
2045 }
2046
2047 @Override
2048 public boolean supportsBearing() {
2049 return mSupportsBearing;
2050 }
2051
2052 @Override
2053 public boolean supportsSpeed() {
2054 return mSupportsSpeed;
2055 }
2056 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002057
2058 private void checkMockPermissions() {
2059 boolean allowMocks = false;
2060 try {
2061 allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
2062 Settings.Secure.ALLOW_MOCK_LOCATION) == 1;
2063 } catch (SettingNotFoundException e) {
2064 // Do nothing
2065 }
2066 if (!allowMocks) {
2067 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
2068 }
2069
2070 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
2071 PackageManager.PERMISSION_GRANTED) {
2072 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
2073 }
2074 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002075
2076 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
2077 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
2078 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002079 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002080
2081 MockProvider provider = new MockProvider(name, requiresNetwork, requiresSatellite,
2082 requiresCell, hasMonetaryCost, supportsAltitude,
2083 supportsSpeed, supportsBearing, powerRequirement, accuracy);
2084 if (LocationProviderImpl.getProvider(name) != null) {
2085 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
2086 }
2087 LocationProviderImpl.addProvider(provider);
2088 updateProviders();
2089 }
2090
2091 public void removeTestProvider(String provider) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002092 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002093 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
2094 if (p == null) {
2095 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2096 }
2097 LocationProviderImpl.removeProvider(p);
2098 updateProviders();
2099 }
2100
2101 public void setTestProviderLocation(String provider, Location loc) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002102 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002103 if (LocationProviderImpl.getProvider(provider) == null) {
2104 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2105 }
2106 mMockProviderLocation.put(provider, loc);
2107 }
2108
2109 public void clearTestProviderLocation(String provider) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002110 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002111 if (LocationProviderImpl.getProvider(provider) == null) {
2112 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2113 }
2114 mMockProviderLocation.remove(provider);
2115 }
2116
2117 public void setTestProviderEnabled(String provider, boolean enabled) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002118 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002119 if (LocationProviderImpl.getProvider(provider) == null) {
2120 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2121 }
2122 if (enabled) {
2123 mEnabledProviders.add(provider);
2124 mDisabledProviders.remove(provider);
2125 } else {
2126 mEnabledProviders.remove(provider);
2127 mDisabledProviders.add(provider);
2128 }
2129 updateProviders();
2130 }
2131
2132 public void clearTestProviderEnabled(String provider) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002133 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002134 if (LocationProviderImpl.getProvider(provider) == null) {
2135 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2136 }
2137 mEnabledProviders.remove(provider);
2138 mDisabledProviders.remove(provider);
2139 updateProviders();
2140 }
2141
2142 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002143 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002144 if (LocationProviderImpl.getProvider(provider) == null) {
2145 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2146 }
2147 mMockProviderStatus.put(provider, new Integer(status));
2148 mMockProviderStatusExtras.put(provider, extras);
2149 mMockProviderStatusUpdateTime.put(provider, new Long(updateTime));
2150 }
2151
2152 public void clearTestProviderStatus(String provider) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002153 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002154 if (LocationProviderImpl.getProvider(provider) == null) {
2155 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2156 }
2157 mMockProviderStatus.remove(provider);
2158 mMockProviderStatusExtras.remove(provider);
2159 mMockProviderStatusUpdateTime.remove(provider);
2160 }
2161
2162 private void log(String log) {
2163 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2164 Log.d(TAG, log);
2165 }
2166 }
2167}
2168