blob: bc6fd71850d7b6d537e68496407706f011dec1c2 [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
The Android Open Source Projectd24b8182009-02-10 15:44:00 -080067import com.android.internal.app.IBatteryStats;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070068import com.android.internal.location.CellState;
69import com.android.internal.location.GpsLocationProvider;
The Android Open Source Projectd24b8182009-02-10 15:44:00 -080070import com.android.internal.location.ILocationCollector;
71import com.android.internal.location.INetworkLocationManager;
72import com.android.internal.location.INetworkLocationProvider;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070073import com.android.internal.location.TrackProvider;
The Android Open Source Projectd24b8182009-02-10 15:44:00 -080074import com.android.server.am.BatteryStatsService;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070075
76/**
77 * The service class that manages LocationProviders and issues location
78 * updates and alerts.
79 *
80 * {@hide}
81 */
The Android Open Source Projectd24b8182009-02-10 15:44:00 -080082public class LocationManagerService extends ILocationManager.Stub
83 implements INetworkLocationManager {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -070084 private static final String TAG = "LocationManagerService";
85
86 // Minimum time interval between last known location writes, in milliseconds.
87 private static final long MIN_LAST_KNOWN_LOCATION_TIME = 60L * 1000L;
88
89 // Max time to hold wake lock for, in milliseconds.
90 private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L;
91
92 // Time to wait after releasing a wake lock for clients to process location update,
93 // in milliseconds.
94 private static final long TIME_AFTER_WAKE_LOCK = 2 * 1000L;
95
96 // The last time a location was written, by provider name.
97 private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
98
99 private static final Pattern PATTERN_COMMA = Pattern.compile(",");
100
101 private static final String ACCESS_FINE_LOCATION =
102 android.Manifest.permission.ACCESS_FINE_LOCATION;
103 private static final String ACCESS_COARSE_LOCATION =
104 android.Manifest.permission.ACCESS_COARSE_LOCATION;
105 private static final String ACCESS_MOCK_LOCATION =
106 android.Manifest.permission.ACCESS_MOCK_LOCATION;
107 private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
108 android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
109
110 // Set of providers that are explicitly enabled
111 private final Set<String> mEnabledProviders = new HashSet<String>();
112
113 // Set of providers that are explicitly disabled
114 private final Set<String> mDisabledProviders = new HashSet<String>();
115
116 // Locations, status values, and extras for mock providers
117 HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
118 private final HashMap<String,Location> mMockProviderLocation = new HashMap<String,Location>();
119 private final HashMap<String,Integer> mMockProviderStatus = new HashMap<String,Integer>();
120 private final HashMap<String,Bundle> mMockProviderStatusExtras = new HashMap<String,Bundle>();
121 private final HashMap<String,Long> mMockProviderStatusUpdateTime = new HashMap<String,Long>();
122
123 private static boolean sProvidersLoaded = false;
124
125 private final Context mContext;
126 private GpsLocationProvider mGpsLocationProvider;
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800127 private LocationProviderImpl mNetworkLocationProvider;
128 private INetworkLocationProvider mNetworkLocationInterface;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700129 private LocationWorkerHandler mLocationHandler;
130
131 // Handler messages
132 private static final int MESSAGE_HEARTBEAT = 1;
133 private static final int MESSAGE_ACQUIRE_WAKE_LOCK = 2;
134 private static final int MESSAGE_RELEASE_WAKE_LOCK = 3;
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800135 private static final int MESSAGE_SET_NETWORK_LOCATION_PROVIDER = 4;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700136
137 // Alarm manager and wakelock variables
138 private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT";
139 private final static String WAKELOCK_KEY = "LocationManagerService";
140 private final static String WIFILOCK_KEY = "LocationManagerService";
141 private AlarmManager mAlarmManager;
142 private long mAlarmInterval = 0;
143 private boolean mScreenOn = true;
144 private PowerManager.WakeLock mWakeLock = null;
145 private WifiManager.WifiLock mWifiLock = null;
146 private long mWakeLockAcquireTime = 0;
147 private boolean mWakeLockGpsReceived = true;
148 private boolean mWakeLockNetworkReceived = true;
149 private boolean mWifiWakeLockAcquired = false;
150 private boolean mCellWakeLockAcquired = false;
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800151
152 private final IBatteryStats mBatteryStats;
153
154 // The calling UID when we are in a clearCallingIdentity/restoreCallingIdentity block, or -1
155 private int mCallingUid = -1;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700156
157 /**
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800158 * Mapping from listener IBinder/PendingIntent to local Listener wrappers.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700159 */
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800160 private final HashMap<Object,Receiver> mListeners =
161 new HashMap<Object,Receiver>();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700162
163 /**
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800164 * Mapping from listener IBinder/PendingIntent to a map from provider name to UpdateRecord.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700165 */
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800166 private final HashMap<Object,HashMap<String,UpdateRecord>> mLocationListeners =
167 new HashMap<Object,HashMap<String,UpdateRecord>>();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700168
169 /**
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800170 * Mapping from listener IBinder/PendingIntent to a map from provider name to last broadcast
171 * location.
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700172 */
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800173 private final HashMap<Object,HashMap<String,Location>> mLastFixBroadcast =
174 new HashMap<Object,HashMap<String,Location>>();
175 private final HashMap<Object,HashMap<String,Long>> mLastStatusBroadcast =
176 new HashMap<Object,HashMap<String,Long>>();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700177
178 /**
179 * Mapping from provider name to all its UpdateRecords
180 */
181 private final HashMap<String,HashSet<UpdateRecord>> mRecordsByProvider =
182 new HashMap<String,HashSet<UpdateRecord>>();
183
184 /**
185 * Mappings from provider name to object to use for current location. Locations
186 * contained in this list may not always be valid.
187 */
188 private final HashMap<String,Location> mLocationsByProvider =
189 new HashMap<String,Location>();
190
191 // Proximity listeners
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800192 private Receiver mProximityListener = null;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700193 private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
194 new HashMap<PendingIntent,ProximityAlert>();
195 private HashSet<ProximityAlert> mProximitiesEntered =
196 new HashSet<ProximityAlert>();
197
198 // Last known location for each provider
199 private HashMap<String,Location> mLastKnownLocation =
200 new HashMap<String,Location>();
201
202 // Battery status extras (from com.android.server.BatteryService)
203 private static final String BATTERY_EXTRA_SCALE = "scale";
204 private static final String BATTERY_EXTRA_LEVEL = "level";
205 private static final String BATTERY_EXTRA_PLUGGED = "plugged";
206
207 // Last known cell service state
208 private TelephonyManager mTelephonyManager;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800209 private int mSignalStrength = -1;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700210
211 // Location collector
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800212 private ILocationCollector mCollector;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700213
214 // Wifi Manager
215 private WifiManager mWifiManager;
216
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800217 /**
218 * A wrapper class holding either an ILocationListener or a PendingIntent to receive
219 * location updates.
220 */
221 private final class Receiver implements IBinder.DeathRecipient {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700222 final ILocationListener mListener;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800223 final PendingIntent mPendingIntent;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700224
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800225 Receiver(ILocationListener listener) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700226 mListener = listener;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800227 mPendingIntent = null;
228 }
229
230 Receiver(PendingIntent intent) {
231 mPendingIntent = intent;
232 mListener = null;
233 }
234
235 public Object getKey() {
236 if (mListener != null) {
237 return mListener.asBinder();
238 } else {
239 return mPendingIntent;
240 }
241 }
242
243 public boolean isListener() {
244 return mListener != null;
245 }
246
247 public boolean isPendingIntent() {
248 return mPendingIntent != null;
249 }
250
251 public ILocationListener getListener() {
252 if (mListener != null) {
253 return mListener;
254 }
255 throw new IllegalStateException("Request for non-existent listener");
256 }
257
258 public PendingIntent getPendingIntent() {
259 if (mPendingIntent != null) {
260 return mPendingIntent;
261 }
262 throw new IllegalStateException("Request for non-existent intent");
263 }
264
265 public void onStatusChanged(String provider, int status, Bundle extras)
266 throws RemoteException {
267 if (mListener != null) {
268 mListener.onStatusChanged(provider, status, extras);
269 } else {
270 Intent statusChanged = new Intent();
271 statusChanged.putExtras(extras);
272 statusChanged.putExtra(LocationManager.KEY_STATUS_CHANGED, status);
273 try {
274 mPendingIntent.send(mContext, 0, statusChanged, null, null);
275 } catch (PendingIntent.CanceledException e) {
276 _removeUpdates(this);
277 }
278 }
279 }
280
281 public void onLocationChanged(Location location) throws RemoteException {
282 if (mListener != null) {
283 mListener.onLocationChanged(location);
284 } else {
285 Intent locationChanged = new Intent();
286 locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
287 try {
288 mPendingIntent.send(mContext, 0, locationChanged, null, null);
289 } catch (PendingIntent.CanceledException e) {
290 _removeUpdates(this);
291 }
292 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700293 }
294
295 public void binderDied() {
296 if (Config.LOGD) {
297 Log.d(TAG, "Location listener died");
298 }
299 synchronized (mLocationListeners) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800300 _removeUpdates(this);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700301 }
302 }
303 }
304
305 private Location readLastKnownLocation(String provider) {
306 Location location = null;
307 String s = null;
308 try {
309 File f = new File(LocationManager.SYSTEM_DIR + "/location."
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800310 + provider);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700311 if (!f.exists()) {
312 return null;
313 }
314 BufferedReader reader = new BufferedReader(new FileReader(f), 256);
315 s = reader.readLine();
316 } catch (IOException e) {
317 Log.w(TAG, "Unable to read last known location", e);
318 }
319
320 if (s == null) {
321 return null;
322 }
323 try {
324 String[] tokens = PATTERN_COMMA.split(s);
325 int idx = 0;
326 long time = Long.parseLong(tokens[idx++]);
327 double latitude = Double.parseDouble(tokens[idx++]);
328 double longitude = Double.parseDouble(tokens[idx++]);
329 double altitude = Double.parseDouble(tokens[idx++]);
330 float bearing = Float.parseFloat(tokens[idx++]);
331 float speed = Float.parseFloat(tokens[idx++]);
332
333 location = new Location(provider);
334 location.setTime(time);
335 location.setLatitude(latitude);
336 location.setLongitude(longitude);
337 location.setAltitude(altitude);
338 location.setBearing(bearing);
339 location.setSpeed(speed);
340 } catch (NumberFormatException nfe) {
341 Log.e(TAG, "NumberFormatException reading last known location", nfe);
342 return null;
343 }
344
345 return location;
346 }
347
348 private void writeLastKnownLocation(String provider,
349 Location location) {
350 long now = SystemClock.elapsedRealtime();
351 Long last = mLastWriteTime.get(provider);
352 if ((last != null)
353 && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
354 return;
355 }
356 mLastWriteTime.put(provider, now);
357
358 StringBuilder sb = new StringBuilder(100);
359 sb.append(location.getTime());
360 sb.append(',');
361 sb.append(location.getLatitude());
362 sb.append(',');
363 sb.append(location.getLongitude());
364 sb.append(',');
365 sb.append(location.getAltitude());
366 sb.append(',');
367 sb.append(location.getBearing());
368 sb.append(',');
369 sb.append(location.getSpeed());
370
371 FileWriter writer = null;
372 try {
373 File d = new File(LocationManager.SYSTEM_DIR);
374 if (!d.exists()) {
375 if (!d.mkdirs()) {
376 Log.w(TAG, "Unable to create directory to write location");
377 return;
378 }
379 }
380 File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
381 writer = new FileWriter(f);
382 writer.write(sb.toString());
383 } catch (IOException e) {
384 Log.w(TAG, "Unable to write location", e);
385 } finally {
386 if (writer != null) {
387 try {
388 writer.close();
389 } catch (IOException e) {
390 Log.w(TAG, "Exception closing file", e);
391 }
392 }
393 }
394 }
395
396 /**
397 * Load providers from /data/location/<provider_name>/
398 * class
399 * kml
400 * nmea
401 * track
402 * location
403 * properties
404 */
405 private void loadProviders() {
406 synchronized (LocationManagerService.class) {
407 if (sProvidersLoaded) {
408 return;
409 }
410
411 // Load providers
412 loadProvidersNoSync();
413 sProvidersLoaded = true;
414 }
415 }
416
417 private void loadProvidersNoSync() {
418 try {
419 _loadProvidersNoSync();
420 } catch (Exception e) {
421 Log.e(TAG, "Exception loading providers:", e);
422 }
423 }
424
425 private void _loadProvidersNoSync() {
426 // Attempt to load "real" providers first
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700427 if (GpsLocationProvider.isSupported()) {
428 // Create a gps location provider
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800429 mGpsLocationProvider = new GpsLocationProvider(mContext);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700430 LocationProviderImpl.addProvider(mGpsLocationProvider);
431 }
432
433 // Load fake providers if real providers are not available
434 File f = new File(LocationManager.PROVIDER_DIR);
435 if (f.isDirectory()) {
436 File[] subdirs = f.listFiles();
437 for (int i = 0; i < subdirs.length; i++) {
438 if (!subdirs[i].isDirectory()) {
439 continue;
440 }
441
442 String name = subdirs[i].getName();
443
444 if (Config.LOGD) {
445 Log.d(TAG, "Found dir " + subdirs[i].getAbsolutePath());
446 Log.d(TAG, "name = " + name);
447 }
448
449 // Don't create a fake provider if a real provider exists
450 if (LocationProviderImpl.getProvider(name) == null) {
451 LocationProviderImpl provider = null;
452 try {
453 File classFile = new File(subdirs[i], "class");
454 // Look for a 'class' file
455 provider = LocationProviderImpl.loadFromClass(classFile);
456
457 // Look for an 'kml', 'nmea', or 'track' file
458 if (provider == null) {
459 // Load properties from 'properties' file, if present
460 File propertiesFile = new File(subdirs[i], "properties");
461
462 if (propertiesFile.exists()) {
463 provider = new TrackProvider(name);
464 ((TrackProvider)provider).readProperties(propertiesFile);
465
466 File kmlFile = new File(subdirs[i], "kml");
467 if (kmlFile.exists()) {
468 ((TrackProvider) provider).readKml(kmlFile);
469 } else {
470 File nmeaFile = new File(subdirs[i], "nmea");
471 if (nmeaFile.exists()) {
472 ((TrackProvider) provider).readNmea(name, nmeaFile);
473 } else {
474 File trackFile = new File(subdirs[i], "track");
475 if (trackFile.exists()) {
476 ((TrackProvider) provider).readTrack(trackFile);
477 }
478 }
479 }
480 }
481 }
482 if (provider != null) {
483 LocationProviderImpl.addProvider(provider);
484 }
485 // Grab the initial location of a TrackProvider and
486 // store it as the last known location for that provider
487 if (provider instanceof TrackProvider) {
488 TrackProvider tp = (TrackProvider) provider;
489 mLastKnownLocation.put(tp.getName(), tp.getInitialLocation());
490 }
491 } catch (Exception e) {
492 Log.e(TAG, "Exception loading provder " + name, e);
493 }
494 }
495 }
496 }
497
498 updateProviders();
499 }
500
501 /**
502 * @param context the context that the LocationManagerService runs in
503 */
504 public LocationManagerService(Context context) {
505 super();
506 mContext = context;
507 mLocationHandler = new LocationWorkerHandler();
508
509 if (Config.LOGD) {
510 Log.d(TAG, "Constructed LocationManager Service");
511 }
512
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800513 // Alarm manager, needs to be done before calling loadProviders() below
514 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
515
516 // Create a wake lock, needs to be done before calling loadProviders() below
517 PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
518 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800519
520 // Battery statistics service to be notified when GPS turns on or off
521 mBatteryStats = BatteryStatsService.getService();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800522
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700523 // Load providers
524 loadProviders();
525
526 // Listen for Radio changes
527 mTelephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
528 mTelephonyManager.listen(mPhoneStateListener,
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800529 PhoneStateListener.LISTEN_CELL_LOCATION |
530 PhoneStateListener.LISTEN_SIGNAL_STRENGTH |
531 PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700532
533 // Register for Network (Wifi or Mobile) updates
534 NetworkStateBroadcastReceiver networkReceiver = new NetworkStateBroadcastReceiver();
535 IntentFilter networkIntentFilter = new IntentFilter();
536 networkIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
537 networkIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
538 networkIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
539 networkIntentFilter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
540 context.registerReceiver(networkReceiver, networkIntentFilter);
541
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700542 // Register for power updates
543 PowerStateBroadcastReceiver powerStateReceiver = new PowerStateBroadcastReceiver();
544 IntentFilter intentFilter = new IntentFilter();
545 intentFilter.addAction(ALARM_INTENT);
546 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
547 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
548 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
549 context.registerReceiver(powerStateReceiver, intentFilter);
550
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700551 // Get the wifi manager
552 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
553
554 // Create a wifi lock for future use
555 mWifiLock = getWifiWakelock();
556
557 // There might be an existing wifi scan available
558 if (mWifiManager != null) {
559 List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
560 if (wifiScanResults != null && wifiScanResults.size() != 0) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800561 if (mNetworkLocationInterface != null) {
562 mNetworkLocationInterface.updateWifiScanResults(wifiScanResults);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700563 }
564 }
565 }
566 }
567
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800568 public void setNetworkLocationProvider(INetworkLocationProvider provider) {
569 mLocationHandler.removeMessages(MESSAGE_SET_NETWORK_LOCATION_PROVIDER);
570 Message m = Message.obtain(mLocationHandler,
571 MESSAGE_SET_NETWORK_LOCATION_PROVIDER, provider);
572 mLocationHandler.sendMessageAtFrontOfQueue(m);
573 }
574
575 public void setLocationCollector(ILocationCollector collector) {
576 mCollector = collector;
577 if (mGpsLocationProvider != null) {
578 mGpsLocationProvider.setLocationCollector(mCollector);
579 }
580 }
581
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700582 private WifiManager.WifiLock getWifiWakelock() {
583 if (mWifiLock == null && mWifiManager != null) {
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800584 mWifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, WIFILOCK_KEY);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700585 mWifiLock.setReferenceCounted(false);
586 }
587 return mWifiLock;
588 }
589
590 private boolean isAllowedBySettings(String provider) {
591 if (mEnabledProviders.contains(provider)) {
592 return true;
593 }
594 if (mDisabledProviders.contains(provider)) {
595 return false;
596 }
597 // Use system settings
598 ContentResolver resolver = mContext.getContentResolver();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800599 String allowedProviders = Settings.Secure.getString(resolver,
600 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700601
602 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
603 }
604
605 private void checkPermissions(String provider) {
606 if (LocationManager.GPS_PROVIDER.equals(provider)
607 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
608 != PackageManager.PERMISSION_GRANTED)) {
609 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
610 }
611 if (LocationManager.NETWORK_PROVIDER.equals(provider)
612 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
613 != PackageManager.PERMISSION_GRANTED)
614 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
615 != PackageManager.PERMISSION_GRANTED)) {
616 throw new SecurityException(
617 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
618 }
619 }
620
621 private boolean isAllowedProvider(String provider) {
622 if (LocationManager.GPS_PROVIDER.equals(provider)
623 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
624 != PackageManager.PERMISSION_GRANTED)) {
625 return false;
626 }
627 if (LocationManager.NETWORK_PROVIDER.equals(provider)
628 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
629 != PackageManager.PERMISSION_GRANTED)
630 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
631 != PackageManager.PERMISSION_GRANTED)) {
632 return false;
633 }
634
635 return true;
636 }
637
638 private String[] getPackageNames() {
639 // Since a single UID may correspond to multiple packages, this can only be used as an
640 // approximation for tracking
641 return mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
642 }
643
644 public List<String> getAllProviders() {
645 try {
646 return _getAllProviders();
647 } catch (SecurityException se) {
648 throw se;
649 } catch (Exception e) {
650 Log.e(TAG, "getAllProviders got exception:", e);
651 return null;
652 }
653 }
654
655 private List<String> _getAllProviders() {
656 if (Config.LOGD) {
657 Log.d(TAG, "getAllProviders");
658 }
659 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
660 ArrayList<String> out = new ArrayList<String>(providers.size());
661
662 for (LocationProviderImpl p : providers) {
663 out.add(p.getName());
664 }
665 return out;
666 }
667
668 public List<String> getProviders(boolean enabledOnly) {
669 try {
670 return _getProviders(enabledOnly);
671 } catch (SecurityException se) {
672 throw se;
673 } catch (Exception e) {
674 Log.e(TAG, "getProviders gotString exception:", e);
675 return null;
676 }
677 }
678
679 private List<String> _getProviders(boolean enabledOnly) {
680 if (Config.LOGD) {
681 Log.d(TAG, "getProviders");
682 }
683 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
684 ArrayList<String> out = new ArrayList<String>();
685
686 for (LocationProviderImpl p : providers) {
687 String name = p.getName();
688 if (isAllowedProvider(name)) {
689 if (enabledOnly && !isAllowedBySettings(name)) {
690 continue;
691 }
692 out.add(name);
693 }
694 }
695 return out;
696 }
697
698 public void updateProviders() {
699 for (LocationProviderImpl p : LocationProviderImpl.getProviders()) {
700 boolean isEnabled = p.isEnabled();
701 String name = p.getName();
702 boolean shouldBeEnabled = isAllowedBySettings(name);
703
704 // Collection is only allowed when network provider is being used
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800705 if (mCollector != null &&
706 p.getName().equals(LocationManager.NETWORK_PROVIDER)) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700707 mCollector.updateNetworkProviderStatus(shouldBeEnabled);
708 }
709
710 if (isEnabled && !shouldBeEnabled) {
711 updateProviderListeners(name, false);
712 } else if (!isEnabled && shouldBeEnabled) {
713 updateProviderListeners(name, true);
714 }
715
716 }
717 }
718
719 private void updateProviderListeners(String provider, boolean enabled) {
720 int listeners = 0;
721
722 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
723 if (p == null) {
724 return;
725 }
726
727 synchronized (mRecordsByProvider) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700728 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
729 if (records != null) {
730 for (UpdateRecord record : records) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800731 // Sends a notification message to the receiver
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700732 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800733 Receiver receiver = record.mReceiver;
734 if (receiver.isListener()) {
735 if (enabled) {
736 receiver.getListener().onProviderEnabled(provider);
737 } else {
738 receiver.getListener().onProviderDisabled(provider);
739 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700740 } else {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800741 PendingIntent intent = receiver.getPendingIntent();
742 Intent providerIntent = new Intent();
743 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
744 try {
745 receiver.getPendingIntent().send(mContext, 0,
746 providerIntent, null, null);
747 } catch (PendingIntent.CanceledException e) {
748 _removeUpdates(receiver);
749 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700750 }
751 } catch (RemoteException e) {
752 // The death link will clean this up.
753 }
754 listeners++;
755 }
756 }
757 }
758
759 if (enabled) {
760 p.enable();
761 if (listeners > 0) {
762 p.setMinTime(getMinTime(provider));
763 p.enableLocationTracking(true);
764 updateWakelockStatus(mScreenOn);
765 }
766 } else {
767 p.enableLocationTracking(false);
768 p.disable();
769 updateWakelockStatus(mScreenOn);
770 }
771
772 if (enabled && listeners > 0) {
773 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
774 Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
775 mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
776 } else {
777 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
778 }
779 }
780
781 private long getMinTime(String provider) {
782 long minTime = Long.MAX_VALUE;
783 synchronized (mRecordsByProvider) {
784 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
785 if (records != null) {
786 for (UpdateRecord r : records) {
787 minTime = Math.min(minTime, r.mMinTime);
788 }
789 }
790 }
791 return minTime;
792 }
793
794 private class UpdateRecord {
795 String mProvider;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800796 Receiver mReceiver;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700797 long mMinTime;
798 float mMinDistance;
799 String[] mPackages;
800
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800801 UpdateRecord(String provider, long minTime, float minDistance,
802 Receiver receiver, String[] packages) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700803 mProvider = provider;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800804 mReceiver = receiver;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700805 mMinTime = minTime;
806 mMinDistance = minDistance;
807 mPackages = packages;
808
809 synchronized (mRecordsByProvider) {
810 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
811 if (records == null) {
812 records = new HashSet<UpdateRecord>();
813 mRecordsByProvider.put(provider, records);
814 }
815 records.add(this);
816 }
817 }
818
819 /**
820 * Method to be called when a record will no longer be used. Calling this multiple times
821 * must have the same effect as calling it once.
822 */
823 public void dispose() {
824 synchronized (mRecordsByProvider) {
825 HashSet<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
826 records.remove(this);
827 }
828 }
829
830 /**
831 * Calls dispose().
832 */
833 @Override protected void finalize() {
834 dispose();
835 }
836 }
837
838 public void requestLocationUpdates(String provider,
839 long minTime, float minDistance, ILocationListener listener) {
840
841 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800842 _requestLocationUpdates(provider, minTime, minDistance,
843 new Receiver(listener));
844 } catch (SecurityException se) {
845 throw se;
846 } catch (Exception e) {
847 Log.e(TAG, "requestUpdates got exception:", e);
848 }
849 }
850
851 public void requestLocationUpdatesPI(String provider,
852 long minTime, float minDistance, PendingIntent intent) {
853 try {
854 _requestLocationUpdates(provider, minTime, minDistance,
855 new Receiver(intent));
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700856 } catch (SecurityException se) {
857 throw se;
858 } catch (Exception e) {
859 Log.e(TAG, "requestUpdates got exception:", e);
860 }
861 }
862
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800863 private synchronized void _requestLocationUpdates(String provider,
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800864 long minTime, float minDistance, Receiver receiver) {
865 Object key = receiver.getKey();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700866 if (Config.LOGD) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800867 Log.d(TAG, "_requestLocationUpdates: listener = " + key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700868 }
869
870 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
871 if (impl == null) {
872 throw new IllegalArgumentException("provider=" + provider);
873 }
874
875 checkPermissions(provider);
876
877 String[] packages = getPackageNames();
878
879 // so wakelock calls will succeed
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800880 mCallingUid = getCallingUid();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700881 long identity = Binder.clearCallingIdentity();
882 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800883 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, packages);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700884 synchronized (mLocationListeners) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800885 if (mListeners.get(key) == null) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700886 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800887 if (receiver.isListener()) {
888 receiver.getListener().asBinder().linkToDeath(receiver, 0);
889 }
890 mListeners.put(key, receiver);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700891 } catch (RemoteException e) {
892 return;
893 }
894 }
895
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800896 HashMap<String,UpdateRecord> records = mLocationListeners.get(key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700897 if (records == null) {
898 records = new HashMap<String,UpdateRecord>();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800899 mLocationListeners.put(key, records);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700900 }
901 UpdateRecord oldRecord = records.put(provider, r);
902 if (oldRecord != null) {
903 oldRecord.dispose();
904 }
905
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700906 boolean isProviderEnabled = isAllowedBySettings(provider);
907 if (isProviderEnabled) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800908 if (provider.equals(LocationManager.GPS_PROVIDER)) {
909 try {
910 mBatteryStats.noteRequestGpsOn(mCallingUid);
911 } catch (RemoteException e) {
912 Log.w(TAG, "Got RemoteException calling noteRequestGpsOff", e);
913 }
914 }
915
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700916 long minTimeForProvider = getMinTime(provider);
917 impl.setMinTime(minTimeForProvider);
918 impl.enableLocationTracking(true);
919 updateWakelockStatus(mScreenOn);
920
921 // Clear heartbeats if any before starting a new one
922 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
923 Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
924 mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700925 } else {
926 try {
927 // Notify the listener that updates are currently disabled
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800928 if (receiver.isListener()) {
929 receiver.getListener().onProviderDisabled(provider);
930 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700931 } catch(RemoteException e) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800932 Log.w(TAG, "RemoteException calling onProviderDisabled on " +
933 receiver.getListener());
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700934 }
935 }
936 }
937 } finally {
938 Binder.restoreCallingIdentity(identity);
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800939 mCallingUid = -1;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700940 }
941 }
942
943 public void removeUpdates(ILocationListener listener) {
944 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800945 _removeUpdates(new Receiver(listener));
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700946 } catch (SecurityException se) {
947 throw se;
948 } catch (Exception e) {
949 Log.e(TAG, "removeUpdates got exception:", e);
950 }
951 }
952
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800953 public void removeUpdatesPI(PendingIntent intent) {
954 try {
955 _removeUpdates(new Receiver(intent));
956 } catch (SecurityException se) {
957 throw se;
958 } catch (Exception e) {
959 Log.e(TAG, "removeUpdates got exception:", e);
960 }
961 }
962
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800963 private synchronized void _removeUpdates(Receiver receiver) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800964 Object key = receiver.getKey();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700965 if (Config.LOGD) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800966 Log.d(TAG, "_removeUpdates: listener = " + key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700967 }
968
969 // so wakelock calls will succeed
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800970 mCallingUid = getCallingUid();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700971 long identity = Binder.clearCallingIdentity();
972 try {
973 synchronized (mLocationListeners) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800974 Receiver myReceiver = mListeners.remove(key);
975 if ((myReceiver != null) && (myReceiver.isListener())) {
976 myReceiver.getListener().asBinder().unlinkToDeath(myReceiver, 0);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700977 }
978
979 // Record which providers were associated with this listener
980 HashSet<String> providers = new HashSet<String>();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800981 HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700982 if (oldRecords != null) {
983 // Call dispose() on the obsolete update records.
984 for (UpdateRecord record : oldRecords.values()) {
985 if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800986 if (mNetworkLocationInterface != null) {
987 mNetworkLocationInterface.removeListener(record.mPackages);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700988 }
989 }
990 record.dispose();
991 }
992 // Accumulate providers
993 providers.addAll(oldRecords.keySet());
994 }
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800995
996 if (providers.contains("gps")) {
997 try {
998 mBatteryStats.noteRequestGpsOff(mCallingUid);
999 } catch (RemoteException e) {
1000 Log.w(TAG, "Got RemoteException calling noteRequestGpsOff", e);
1001 }
1002 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001003
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001004 mLocationListeners.remove(key);
1005 mLastFixBroadcast.remove(key);
1006 mLastStatusBroadcast.remove(key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001007
1008 // See if the providers associated with this listener have any
1009 // other listeners; if one does, inform it of the new smallest minTime
1010 // value; if one does not, disable location tracking for it
1011 for (String provider : providers) {
1012 // If provider is already disabled, don't need to do anything
1013 if (!isAllowedBySettings(provider)) {
1014 continue;
1015 }
1016
1017 boolean hasOtherListener = false;
1018 synchronized (mRecordsByProvider) {
1019 HashSet<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1020 if (recordsForProvider != null && recordsForProvider.size() > 0) {
1021 hasOtherListener = true;
1022 }
1023 }
1024
1025 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1026 if (p != null) {
1027 if (hasOtherListener) {
1028 p.setMinTime(getMinTime(provider));
1029 } else {
1030 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
1031 p.enableLocationTracking(false);
1032 }
1033 }
1034 }
1035
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001036 updateWakelockStatus(mScreenOn);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001037 }
1038 } finally {
1039 Binder.restoreCallingIdentity(identity);
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001040 mCallingUid = -1;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001041 }
1042 }
1043
1044 public boolean addGpsStatusListener(IGpsStatusListener listener) {
1045 if (mGpsLocationProvider == null) {
1046 return false;
1047 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001048 if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1049 PackageManager.PERMISSION_GRANTED) {
1050 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1051 }
1052
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001053 try {
1054 mGpsLocationProvider.addGpsStatusListener(listener);
1055 } catch (RemoteException e) {
1056 Log.w(TAG, "RemoteException in addGpsStatusListener");
1057 return false;
1058 }
1059 return true;
1060 }
1061
1062 public void removeGpsStatusListener(IGpsStatusListener listener) {
1063 mGpsLocationProvider.removeGpsStatusListener(listener);
1064 }
1065
1066 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1067 // first check for permission to the provider
1068 checkPermissions(provider);
1069 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1070 if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1071 != PackageManager.PERMISSION_GRANTED)) {
1072 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1073 }
1074
1075 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
1076 if (provider == null) {
1077 return false;
1078 }
1079
1080 return impl.sendExtraCommand(command, extras);
1081 }
1082
1083 class ProximityAlert {
1084 double mLatitude;
1085 double mLongitude;
1086 float mRadius;
1087 long mExpiration;
1088 PendingIntent mIntent;
1089 Location mLocation;
1090
1091 public ProximityAlert(double latitude, double longitude,
1092 float radius, long expiration, PendingIntent intent) {
1093 mLatitude = latitude;
1094 mLongitude = longitude;
1095 mRadius = radius;
1096 mExpiration = expiration;
1097 mIntent = intent;
1098
1099 mLocation = new Location("");
1100 mLocation.setLatitude(latitude);
1101 mLocation.setLongitude(longitude);
1102 }
1103
1104 public long getExpiration() {
1105 return mExpiration;
1106 }
1107
1108 public PendingIntent getIntent() {
1109 return mIntent;
1110 }
1111
1112 public boolean isInProximity(double latitude, double longitude) {
1113 Location loc = new Location("");
1114 loc.setLatitude(latitude);
1115 loc.setLongitude(longitude);
1116
1117 double radius = loc.distanceTo(mLocation);
1118 return radius <= mRadius;
1119 }
1120 }
1121
1122 // Listener for receiving locations to trigger proximity alerts
1123 class ProximityListener extends ILocationListener.Stub {
1124
1125 boolean isGpsAvailable = false;
1126
1127 public void onLocationChanged(Location loc) {
1128
1129 // If Gps is available, then ignore updates from NetworkLocationProvider
1130 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1131 isGpsAvailable = true;
1132 }
1133 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1134 return;
1135 }
1136
1137 // Process proximity alerts
1138 long now = System.currentTimeMillis();
1139 double latitude = loc.getLatitude();
1140 double longitude = loc.getLongitude();
1141 ArrayList<PendingIntent> intentsToRemove = null;
1142
1143 for (ProximityAlert alert : mProximityAlerts.values()) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001144 PendingIntent intent = alert.getIntent();
1145 long expiration = alert.getExpiration();
1146
1147 if ((expiration == -1) || (now <= expiration)) {
1148 boolean entered = mProximitiesEntered.contains(alert);
1149 boolean inProximity =
1150 alert.isInProximity(latitude, longitude);
1151 if (!entered && inProximity) {
1152 if (Config.LOGD) {
1153 Log.i(TAG, "Entered alert");
1154 }
1155 mProximitiesEntered.add(alert);
1156 Intent enteredIntent = new Intent();
1157 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1158 try {
1159 intent.send(mContext, 0, enteredIntent, null, null);
1160 } catch (PendingIntent.CanceledException e) {
1161 if (Config.LOGD) {
1162 Log.i(TAG, "Canceled proximity alert: " + alert, e);
1163 }
1164 if (intentsToRemove == null) {
1165 intentsToRemove = new ArrayList<PendingIntent>();
1166 }
1167 intentsToRemove.add(intent);
1168 }
1169 } else if (entered && !inProximity) {
1170 if (Config.LOGD) {
1171 Log.i(TAG, "Exited alert");
1172 }
1173 mProximitiesEntered.remove(alert);
1174 Intent exitedIntent = new Intent();
1175 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1176 try {
1177 intent.send(mContext, 0, exitedIntent, null, null);
1178 } catch (PendingIntent.CanceledException e) {
1179 if (Config.LOGD) {
1180 Log.i(TAG, "Canceled proximity alert: " + alert, e);
1181 }
1182 if (intentsToRemove == null) {
1183 intentsToRemove = new ArrayList<PendingIntent>();
1184 }
1185 intentsToRemove.add(intent);
1186 }
1187 }
1188 } else {
1189 // Mark alert for expiration
1190 if (Config.LOGD) {
1191 Log.i(TAG, "Expiring proximity alert: " + alert);
1192 }
1193 if (intentsToRemove == null) {
1194 intentsToRemove = new ArrayList<PendingIntent>();
1195 }
1196 intentsToRemove.add(alert.getIntent());
1197 }
1198 }
1199
1200 // Remove expired alerts
1201 if (intentsToRemove != null) {
1202 for (PendingIntent i : intentsToRemove) {
1203 mProximityAlerts.remove(i);
1204 ProximityAlert alert = mProximityAlerts.get(i);
1205 mProximitiesEntered.remove(alert);
1206 }
1207 }
1208
1209 }
1210
1211 public void onProviderDisabled(String provider) {
1212 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1213 isGpsAvailable = false;
1214 }
1215 }
1216
1217 public void onProviderEnabled(String provider) {
1218 // ignore
1219 }
1220
1221 public void onStatusChanged(String provider, int status, Bundle extras) {
1222 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1223 (status != LocationProvider.AVAILABLE)) {
1224 isGpsAvailable = false;
1225 }
1226 }
1227 }
1228
1229 public void addProximityAlert(double latitude, double longitude,
1230 float radius, long expiration, PendingIntent intent) {
1231 try {
1232 _addProximityAlert(latitude, longitude, radius, expiration, intent);
1233 } catch (SecurityException se) {
1234 throw se;
1235 } catch (Exception e) {
1236 Log.e(TAG, "addProximityAlert got exception:", e);
1237 }
1238 }
1239
1240 private void _addProximityAlert(double latitude, double longitude,
1241 float radius, long expiration, PendingIntent intent) {
1242 if (Config.LOGD) {
1243 Log.d(TAG, "addProximityAlert: latitude = " + latitude +
1244 ", longitude = " + longitude +
1245 ", expiration = " + expiration +
1246 ", intent = " + intent);
1247 }
1248
1249 // Require ability to access all providers for now
1250 if (!isAllowedProvider(LocationManager.GPS_PROVIDER) ||
1251 !isAllowedProvider(LocationManager.NETWORK_PROVIDER)) {
1252 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1253 }
1254
1255 if (expiration != -1) {
1256 expiration += System.currentTimeMillis();
1257 }
1258 ProximityAlert alert = new ProximityAlert(latitude, longitude, radius, expiration, intent);
1259 mProximityAlerts.put(intent, alert);
1260
1261 if (mProximityListener == null) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001262 mProximityListener = new Receiver(new ProximityListener());
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001263
1264 LocationProvider provider = LocationProviderImpl.getProvider(
1265 LocationManager.GPS_PROVIDER);
1266 if (provider != null) {
1267 _requestLocationUpdates(provider.getName(), 1000L, 1.0f, mProximityListener);
1268 }
1269
1270 provider =
1271 LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER);
1272 if (provider != null) {
1273 _requestLocationUpdates(provider.getName(), 1000L, 1.0f, mProximityListener);
1274 }
1275 }
1276 }
1277
1278 public void removeProximityAlert(PendingIntent intent) {
1279 try {
1280 _removeProximityAlert(intent);
1281 } catch (SecurityException se) {
1282 throw se;
1283 } catch (Exception e) {
1284 Log.e(TAG, "removeProximityAlert got exception:", e);
1285 }
1286 }
1287
1288 private void _removeProximityAlert(PendingIntent intent) {
1289 if (Config.LOGD) {
1290 Log.d(TAG, "removeProximityAlert: intent = " + intent);
1291 }
1292
1293 mProximityAlerts.remove(intent);
1294 if (mProximityAlerts.size() == 0) {
1295 _removeUpdates(mProximityListener);
1296 mProximityListener = null;
1297 }
1298 }
1299
1300 /**
1301 * @return null if the provider does not exits
1302 * @throw SecurityException if the provider is not allowed to be
1303 * accessed by the caller
1304 */
1305 public Bundle getProviderInfo(String provider) {
1306 try {
1307 return _getProviderInfo(provider);
1308 } catch (SecurityException se) {
1309 throw se;
1310 } catch (Exception e) {
1311 Log.e(TAG, "_getProviderInfo got exception:", e);
1312 return null;
1313 }
1314 }
1315
1316 private Bundle _getProviderInfo(String provider) {
1317 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1318 if (p == null) {
1319 return null;
1320 }
1321
1322 checkPermissions(provider);
1323
1324 Bundle b = new Bundle();
1325 b.putBoolean("network", p.requiresNetwork());
1326 b.putBoolean("satellite", p.requiresSatellite());
1327 b.putBoolean("cell", p.requiresCell());
1328 b.putBoolean("cost", p.hasMonetaryCost());
1329 b.putBoolean("altitude", p.supportsAltitude());
1330 b.putBoolean("speed", p.supportsSpeed());
1331 b.putBoolean("bearing", p.supportsBearing());
1332 b.putInt("power", p.getPowerRequirement());
1333 b.putInt("accuracy", p.getAccuracy());
1334
1335 return b;
1336 }
1337
1338 public boolean isProviderEnabled(String provider) {
1339 try {
1340 return _isProviderEnabled(provider);
1341 } catch (SecurityException se) {
1342 throw se;
1343 } catch (Exception e) {
1344 Log.e(TAG, "isProviderEnabled got exception:", e);
1345 return false;
1346 }
1347 }
1348
1349 private boolean _isProviderEnabled(String provider) {
1350 checkPermissions(provider);
1351
1352 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1353 if (p == null) {
1354 throw new IllegalArgumentException("provider=" + provider);
1355 }
1356 return isAllowedBySettings(provider);
1357 }
1358
1359 public Location getLastKnownLocation(String provider) {
1360 try {
1361 return _getLastKnownLocation(provider);
1362 } catch (SecurityException se) {
1363 throw se;
1364 } catch (Exception e) {
1365 Log.e(TAG, "getLastKnownLocation got exception:", e);
1366 return null;
1367 }
1368 }
1369
1370 private Location _getLastKnownLocation(String provider) {
1371 checkPermissions(provider);
1372
1373 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1374 if (p == null) {
1375 throw new IllegalArgumentException("provider=" + provider);
1376 }
1377
1378 if (!isAllowedBySettings(provider)) {
1379 return null;
1380 }
1381
1382 Location location = mLastKnownLocation.get(provider);
1383 if (location == null) {
1384 // Get the persistent last known location for the provider
1385 location = readLastKnownLocation(provider);
1386 if (location != null) {
1387 mLastKnownLocation.put(provider, location);
1388 }
1389 }
1390
1391 return location;
1392 }
1393
1394 private boolean shouldBroadcast(Location loc, Location lastLoc, UpdateRecord record) {
1395 // Always broadcast the first update
1396 if (lastLoc == null) {
1397 return true;
1398 }
1399
1400 // Don't broadcast same location again regardless of condition
1401 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1402 if (loc.getTime() == lastLoc.getTime()) {
1403 return false;
1404 }
1405
1406 // Check whether sufficient distance has been traveled
1407 double minDistance = record.mMinDistance;
1408 if (minDistance > 0.0) {
1409 if (loc.distanceTo(lastLoc) <= minDistance) {
1410 return false;
1411 }
1412 }
1413
1414 return true;
1415 }
1416
1417 private void handleLocationChanged(String provider) {
1418 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
1419 if (records == null || records.size() == 0) {
1420 return;
1421 }
1422
1423 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1424 if (p == null) {
1425 return;
1426 }
1427
1428 // Get location object
1429 Location loc = mLocationsByProvider.get(provider);
1430 if (loc == null) {
1431 loc = new Location(provider);
1432 mLocationsByProvider.put(provider, loc);
1433 } else {
1434 loc.reset();
1435 }
1436
1437 // Use the mock location if available
1438 Location mockLoc = mMockProviderLocation.get(provider);
1439 boolean locationValid;
1440 if (mockLoc != null) {
1441 locationValid = true;
1442 loc.set(mockLoc);
1443 } else {
1444 locationValid = p.getLocation(loc);
1445 }
1446
1447 // Update last known location for provider
1448 if (locationValid) {
1449 Location location = mLastKnownLocation.get(provider);
1450 if (location == null) {
1451 mLastKnownLocation.put(provider, new Location(loc));
1452 } else {
1453 location.set(loc);
1454 }
1455 writeLastKnownLocation(provider, loc);
1456
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001457 if (p instanceof INetworkLocationProvider) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001458 mWakeLockNetworkReceived = true;
1459 } else if (p instanceof GpsLocationProvider) {
1460 // Gps location received signal is in NetworkStateBroadcastReceiver
1461 }
1462 }
1463
1464 // Fetch latest status update time
1465 long newStatusUpdateTime = p.getStatusUpdateTime();
1466
1467 // Override real time with mock time if present
1468 Long mockStatusUpdateTime = mMockProviderStatusUpdateTime.get(provider);
1469 if (mockStatusUpdateTime != null) {
1470 newStatusUpdateTime = mockStatusUpdateTime.longValue();
1471 }
1472
1473 // Get latest status
1474 Bundle extras = new Bundle();
1475 int status = p.getStatus(extras);
1476
1477 // Override status with mock status if present
1478 Integer mockStatus = mMockProviderStatus.get(provider);
1479 if (mockStatus != null) {
1480 status = mockStatus.intValue();
1481 }
1482
1483 // Override extras with mock extras if present
1484 Bundle mockExtras = mMockProviderStatusExtras.get(provider);
1485 if (mockExtras != null) {
1486 extras.clear();
1487 extras.putAll(mockExtras);
1488 }
1489
1490 // Broadcast location or status to all listeners
1491 for (UpdateRecord r : records) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001492 Receiver receiver = r.mReceiver;
1493 Object key = receiver.getKey();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001494
1495 // Broadcast location only if it is valid
1496 if (locationValid) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001497 HashMap<String,Location> map = mLastFixBroadcast.get(key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001498 if (map == null) {
1499 map = new HashMap<String,Location>();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001500 mLastFixBroadcast.put(key, map);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001501 }
1502 Location lastLoc = map.get(provider);
1503 if ((lastLoc == null) || shouldBroadcast(loc, lastLoc, r)) {
1504 if (lastLoc == null) {
1505 lastLoc = new Location(loc);
1506 map.put(provider, lastLoc);
1507 } else {
1508 lastLoc.set(loc);
1509 }
1510 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001511 receiver.onLocationChanged(loc);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001512 } catch (RemoteException doe) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001513 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1514 _removeUpdates(receiver);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001515 }
1516 }
1517 }
1518
1519 // Broadcast status message
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001520 HashMap<String,Long> statusMap = mLastStatusBroadcast.get(key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001521 if (statusMap == null) {
1522 statusMap = new HashMap<String,Long>();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001523 mLastStatusBroadcast.put(key, statusMap);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001524 }
1525 long prevStatusUpdateTime =
1526 (statusMap.get(provider) != null) ? statusMap.get(provider) : 0;
1527
1528 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1529 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1530
1531 statusMap.put(provider, newStatusUpdateTime);
1532 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001533 receiver.onStatusChanged(provider, status, extras);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001534 } catch (RemoteException doe) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001535 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1536 _removeUpdates(receiver);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001537 }
1538 }
1539 }
1540 }
1541
1542 private class LocationWorkerHandler extends Handler {
1543
1544 @Override
1545 public void handleMessage(Message msg) {
1546 try {
1547 if (msg.what == MESSAGE_HEARTBEAT) {
1548 // log("LocationWorkerHandler: Heartbeat!");
1549
1550 synchronized (mRecordsByProvider) {
1551 String provider = (String) msg.obj;
1552 if (!isAllowedBySettings(provider)) {
1553 return;
1554 }
1555
1556 // Process the location fix if the screen is on or we're holding a wakelock
1557 if (mScreenOn || (mWakeLockAcquireTime != 0)) {
1558 handleLocationChanged(provider);
1559 }
1560
1561 // If it continues to have listeners
1562 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
1563 if (records != null && records.size() > 0) {
1564 Message m = Message.obtain(this, MESSAGE_HEARTBEAT, provider);
1565 sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
1566 }
1567 }
1568
1569 if ((mWakeLockAcquireTime != 0) &&
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001570 (SystemClock.elapsedRealtime() - mWakeLockAcquireTime
1571 > MAX_TIME_FOR_WAKE_LOCK)) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001572
1573 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1574 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1575
1576 log("LocationWorkerHandler: Exceeded max time for wake lock");
1577 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1578 sendMessageAtFrontOfQueue(m);
1579
1580 } else if (mWakeLockAcquireTime != 0 &&
1581 mWakeLockGpsReceived && mWakeLockNetworkReceived) {
1582
1583 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1584 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1585
1586 log("LocationWorkerHandler: Locations received.");
1587 mWakeLockAcquireTime = 0;
1588 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1589 sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK);
1590 }
1591
1592 } else if (msg.what == MESSAGE_ACQUIRE_WAKE_LOCK) {
1593 log("LocationWorkerHandler: Acquire");
1594 acquireWakeLock();
1595 } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) {
1596 log("LocationWorkerHandler: Release");
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001597
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001598 // Update wakelock status so the next alarm is set before releasing wakelock
1599 updateWakelockStatus(mScreenOn);
1600 releaseWakeLock();
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001601 } else if (msg.what == MESSAGE_SET_NETWORK_LOCATION_PROVIDER) {
1602 synchronized (LocationManagerService.class) {
1603 Log.d(TAG, "adding network location provider");
1604 mNetworkLocationInterface =
1605 (INetworkLocationProvider)msg.obj;
1606 mNetworkLocationInterface.addListener(getPackageNames());
1607 mNetworkLocationProvider =
1608 (LocationProviderImpl)mNetworkLocationInterface;
1609 LocationProviderImpl.addProvider(mNetworkLocationProvider);
1610 updateProviders();
1611 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001612 }
1613 } catch (Exception e) {
1614 // Log, don't crash!
1615 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1616 }
1617 }
1618 }
1619
1620 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001621
1622 private CellState mLastCellState = null;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001623 @Override
1624 public void onCellLocationChanged(CellLocation cellLocation) {
1625 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001626 int asu = mSignalStrength;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001627
1628 // Gets cell state
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001629 mLastCellState = new CellState(mTelephonyManager, cellLocation, asu);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001630
1631 // Notify collector
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001632 if (mCollector != null) {
1633 mCollector.updateCellState(mLastCellState);
1634 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001635
1636 // Updates providers
1637 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1638 for (LocationProviderImpl provider : providers) {
1639 if (provider.requiresCell()) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001640 provider.updateCellState(mLastCellState);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001641 }
1642 }
1643 } catch (Exception e) {
1644 Log.e(TAG, "Exception in PhoneStateListener.onCellLocationCahnged:", e);
1645 }
1646 }
1647
1648 @Override
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001649 public void onSignalStrengthChanged(int asu) {
1650 mSignalStrength = asu;
1651
1652 if (mLastCellState != null) {
1653 mLastCellState.updateSignalStrength(asu);
1654 }
1655 }
1656
1657 @Override
1658 public void onDataConnectionStateChanged(int state) {
1659 if (mLastCellState != null) {
1660 mLastCellState.updateRadioType(mTelephonyManager);
1661 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001662 }
1663 };
1664
1665 private class PowerStateBroadcastReceiver extends BroadcastReceiver {
1666 @Override public void onReceive(Context context, Intent intent) {
1667 String action = intent.getAction();
1668
1669 if (action.equals(ALARM_INTENT)) {
1670 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1671 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1672
1673 log("PowerStateBroadcastReceiver: Alarm received");
1674 Message m = mLocationHandler.obtainMessage(MESSAGE_ACQUIRE_WAKE_LOCK);
1675 mLocationHandler.sendMessageAtFrontOfQueue(m);
1676
1677 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1678 log("PowerStateBroadcastReceiver: Screen off");
1679 updateWakelockStatus(false);
1680
1681 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1682 log("PowerStateBroadcastReceiver: Screen on");
1683 updateWakelockStatus(true);
1684
1685 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
1686 log("PowerStateBroadcastReceiver: Battery changed");
1687 int scale = intent.getIntExtra(BATTERY_EXTRA_SCALE, 100);
1688 int level = intent.getIntExtra(BATTERY_EXTRA_LEVEL, 0);
1689 boolean plugged = intent.getIntExtra(BATTERY_EXTRA_PLUGGED, 0) != 0;
1690
1691 // Notify collector battery state
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001692 if (mCollector != null) {
1693 mCollector.updateBatteryState(scale, level, plugged);
1694 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001695 }
1696 }
1697 }
1698
1699 private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
1700 @Override public void onReceive(Context context, Intent intent) {
1701 String action = intent.getAction();
1702
1703 if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
1704
1705 List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
1706
1707 if (wifiScanResults == null) {
1708 return;
1709 }
1710
1711 // Notify provider and collector of Wifi scan results
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001712 if (mCollector != null) {
1713 mCollector.updateWifiScanResults(wifiScanResults);
1714 }
1715 if (mNetworkLocationInterface != null) {
1716 mNetworkLocationInterface.updateWifiScanResults(wifiScanResults);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001717 }
1718
1719 } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
1720 int networkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
1721
1722 boolean noConnectivity =
1723 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1724 if (!noConnectivity) {
1725 networkState = LocationProvider.AVAILABLE;
1726 }
1727
1728 // Notify location providers of current network state
1729 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1730 for (LocationProviderImpl provider : providers) {
1731 if (provider.requiresNetwork()) {
1732 provider.updateNetworkState(networkState);
1733 }
1734 }
1735
1736 } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
1737 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
1738 WifiManager.WIFI_STATE_UNKNOWN);
1739
1740 boolean enabled;
1741 if (state == WifiManager.WIFI_STATE_ENABLED) {
1742 enabled = true;
1743 } else if (state == WifiManager.WIFI_STATE_DISABLED) {
1744 enabled = false;
1745 } else {
1746 return;
1747 }
1748
1749 // Notify network provider of current wifi enabled state
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001750 if (mNetworkLocationInterface != null) {
1751 mNetworkLocationInterface.updateWifiEnabledState(enabled);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001752 }
1753
1754 } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
1755
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001756 final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED,
1757 false);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001758
1759 if (!enabled) {
1760 // When GPS is disabled, we are OK to release wake-lock
1761 mWakeLockGpsReceived = true;
1762 }
1763 }
1764
1765 }
1766 }
1767
1768 // Wake locks
1769
1770 private void updateWakelockStatus(boolean screenOn) {
1771 log("updateWakelockStatus(): " + screenOn);
1772
1773 boolean needsLock = false;
1774 long minTime = Integer.MAX_VALUE;
1775
1776 if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) {
1777 needsLock = true;
1778 minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime);
1779 }
1780
1781 if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) {
1782 needsLock = true;
1783 minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime);
1784 if (screenOn) {
1785 startGps();
1786 } else if (mScreenOn && !screenOn) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001787
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001788 // We just turned the screen off so stop navigating
1789 stopGps();
1790 }
1791 }
1792
1793 mScreenOn = screenOn;
1794
1795 PendingIntent sender =
1796 PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0);
1797
1798 // Cancel existing alarm
1799 log("Cancelling existing alarm");
1800 mAlarmManager.cancel(sender);
1801
1802 if (needsLock && !mScreenOn) {
1803 long now = SystemClock.elapsedRealtime();
1804 mAlarmManager.set(
1805 AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender);
1806 mAlarmInterval = minTime;
1807 log("Creating a new wakelock alarm with minTime = " + minTime);
1808 } else {
1809 log("No need for alarm");
1810 mAlarmInterval = -1;
1811
1812 // Clear out existing wakelocks
1813 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1814 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1815 releaseWakeLock();
1816 }
1817 }
1818
1819 private void acquireWakeLock() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001820 try {
1821 acquireWakeLockX();
1822 } catch (Exception e) {
1823 // This is to catch a runtime exception thrown when we try to release an
1824 // already released lock.
1825 Log.e(TAG, "exception in acquireWakeLock()", e);
1826 }
1827 }
1828
1829 private void acquireWakeLockX() {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001830 if (mWakeLock.isHeld()) {
1831 log("Must release wakelock before acquiring");
1832 mWakeLockAcquireTime = 0;
1833 mWakeLock.release();
1834 }
1835
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001836 boolean networkActive = (mNetworkLocationProvider != null)
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001837 && mNetworkLocationProvider.isLocationTracking();
1838 boolean gpsActive = (mGpsLocationProvider != null)
1839 && mGpsLocationProvider.isLocationTracking();
1840
1841 boolean needsLock = networkActive || gpsActive;
1842 if (!needsLock) {
1843 log("No need for Lock!");
1844 return;
1845 }
1846
1847 mWakeLockGpsReceived = !gpsActive;
1848 mWakeLockNetworkReceived = !networkActive;
1849
1850 // Acquire wake lock
1851 mWakeLock.acquire();
1852 mWakeLockAcquireTime = SystemClock.elapsedRealtime();
1853 log("Acquired wakelock");
1854
1855 // Start the gps provider
1856 startGps();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001857
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001858 // Acquire cell lock
1859 if (mCellWakeLockAcquired) {
1860 // Lock is already acquired
1861 } else if (!mWakeLockNetworkReceived) {
1862 mTelephonyManager.enableLocationUpdates();
1863 mCellWakeLockAcquired = true;
1864 } else {
1865 mCellWakeLockAcquired = false;
1866 }
1867
1868 // Notify NetworkLocationProvider
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001869 if (mNetworkLocationInterface != null) {
1870 mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001871 }
1872
1873 // Acquire wifi lock
1874 WifiManager.WifiLock wifiLock = getWifiWakelock();
1875 if (wifiLock != null) {
1876 if (mWifiWakeLockAcquired) {
1877 // Lock is already acquired
1878 } else if (mWifiManager.isWifiEnabled() && !mWakeLockNetworkReceived) {
1879 wifiLock.acquire();
1880 mWifiWakeLockAcquired = true;
1881 } else {
1882 mWifiWakeLockAcquired = false;
1883 Log.w(TAG, "acquireWakeLock(): Unable to get WiFi lock");
1884 }
1885 }
1886 }
1887
1888 private void startGps() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001889 boolean gpsActive = (mGpsLocationProvider != null)
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001890 && mGpsLocationProvider.isLocationTracking();
1891 if (gpsActive) {
1892 mGpsLocationProvider.startNavigating();
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001893 long identity = Binder.clearCallingIdentity();
1894 try {
1895 mBatteryStats.noteStartGps(mCallingUid == -1 ? getCallingUid() : mCallingUid);
1896 } catch (RemoteException e) {
1897 Log.w(TAG, "RemoteException calling noteStartGps on BatteryStatsService", e);
1898 } finally {
1899 Binder.restoreCallingIdentity(identity);
1900 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001901 }
1902 }
1903
1904 private void stopGps() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001905 boolean gpsActive = mGpsLocationProvider != null
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001906 && mGpsLocationProvider.isLocationTracking();
1907 if (gpsActive) {
1908 mGpsLocationProvider.stopNavigating();
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001909 long identity = Binder.clearCallingIdentity();
1910 try {
1911 mBatteryStats.noteStopGps(mCallingUid == -1 ? getCallingUid() : mCallingUid);
1912 } catch (RemoteException e) {
1913 Log.w(TAG, "RemoteException calling noteStopGps on BatteryStatsService", e);
1914 } finally {
1915 Binder.restoreCallingIdentity(identity);
1916 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001917 }
1918 }
1919
1920 private void releaseWakeLock() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001921 try {
1922 releaseWakeLockX();
1923 } catch (Exception e) {
1924 // This is to catch a runtime exception thrown when we try to release an
1925 // already released lock.
1926 Log.e(TAG, "exception in releaseWakeLock()", e);
1927 }
1928 }
1929
1930 private void releaseWakeLockX() {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001931 // Release wifi lock
1932 WifiManager.WifiLock wifiLock = getWifiWakelock();
1933 if (wifiLock != null) {
1934 if (mWifiWakeLockAcquired) {
1935 wifiLock.release();
1936 mWifiWakeLockAcquired = false;
1937 }
1938 }
1939
1940 if (!mScreenOn) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001941 // Stop the gps
1942 stopGps();
1943 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001944
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001945 // Release cell lock
1946 if (mCellWakeLockAcquired) {
1947 mTelephonyManager.disableLocationUpdates();
1948 mCellWakeLockAcquired = false;
1949 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001950
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001951 // Notify NetworkLocationProvider
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001952 if (mNetworkLocationInterface != null) {
1953 mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001954 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001955
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001956 // Release wake lock
1957 mWakeLockAcquireTime = 0;
1958 if (mWakeLock.isHeld()) {
1959 log("Released wakelock");
1960 mWakeLock.release();
1961 } else {
1962 log("Can't release wakelock again!");
1963 }
1964 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001965
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001966 // Geocoder
1967
1968 public String getFromLocation(double latitude, double longitude, int maxResults,
1969 String language, String country, String variant, String appName, List<Address> addrs) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001970 if (mNetworkLocationInterface != null) {
1971 return mNetworkLocationInterface.getFromLocation(latitude, longitude, maxResults,
1972 language, country, variant, appName, addrs);
1973 } else {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001974 return null;
1975 }
1976 }
1977
1978 public String getFromLocationName(String locationName,
1979 double lowerLeftLatitude, double lowerLeftLongitude,
1980 double upperRightLatitude, double upperRightLongitude, int maxResults,
1981 String language, String country, String variant, String appName, List<Address> addrs) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001982 if (mNetworkLocationInterface != null) {
1983 return mNetworkLocationInterface.getFromLocationName(locationName, lowerLeftLatitude,
1984 lowerLeftLongitude, upperRightLatitude, upperRightLongitude, maxResults,
1985 language, country, variant, appName, addrs);
1986 } else {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001987 return null;
1988 }
1989 }
1990
1991 // Mock Providers
1992
1993 class MockProvider extends LocationProviderImpl {
1994 boolean mRequiresNetwork;
1995 boolean mRequiresSatellite;
1996 boolean mRequiresCell;
1997 boolean mHasMonetaryCost;
1998 boolean mSupportsAltitude;
1999 boolean mSupportsSpeed;
2000 boolean mSupportsBearing;
2001 int mPowerRequirement;
2002 int mAccuracy;
2003
2004 public MockProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
2005 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
2006 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
2007 super(name);
2008
2009 mRequiresNetwork = requiresNetwork;
2010 mRequiresSatellite = requiresSatellite;
2011 mRequiresCell = requiresCell;
2012 mHasMonetaryCost = hasMonetaryCost;
2013 mSupportsAltitude = supportsAltitude;
2014 mSupportsBearing = supportsBearing;
2015 mSupportsSpeed = supportsSpeed;
2016 mPowerRequirement = powerRequirement;
2017 mAccuracy = accuracy;
2018 }
2019
2020 @Override
2021 public void disable() {
2022 String name = getName();
2023 mEnabledProviders.remove(name);
2024 mDisabledProviders.add(name);
2025 }
2026
2027 @Override
2028 public void enable() {
2029 String name = getName();
2030 mEnabledProviders.add(name);
2031 mDisabledProviders.remove(name);
2032 }
2033
2034 @Override
2035 public boolean getLocation(Location l) {
2036 Location loc = mMockProviderLocation.get(getName());
2037 if (loc == null) {
2038 return false;
2039 }
2040 l.set(loc);
2041 return true;
2042 }
2043
2044 @Override
2045 public int getStatus(Bundle extras) {
2046 String name = getName();
2047 Integer s = mMockProviderStatus.get(name);
2048 int status = (s == null) ? AVAILABLE : s.intValue();
2049 Bundle newExtras = mMockProviderStatusExtras.get(name);
2050 if (newExtras != null) {
2051 extras.clear();
2052 extras.putAll(newExtras);
2053 }
2054 return status;
2055 }
2056
2057 @Override
2058 public boolean isEnabled() {
2059 return mEnabledProviders.contains(getName());
2060 }
2061
2062 @Override
2063 public int getAccuracy() {
2064 return mAccuracy;
2065 }
2066
2067 @Override
2068 public int getPowerRequirement() {
2069 return mPowerRequirement;
2070 }
2071
2072 @Override
2073 public boolean hasMonetaryCost() {
2074 return mHasMonetaryCost;
2075 }
2076
2077 @Override
2078 public boolean requiresCell() {
2079 return mRequiresCell;
2080 }
2081
2082 @Override
2083 public boolean requiresNetwork() {
2084 return mRequiresNetwork;
2085 }
2086
2087 @Override
2088 public boolean requiresSatellite() {
2089 return mRequiresSatellite;
2090 }
2091
2092 @Override
2093 public boolean supportsAltitude() {
2094 return mSupportsAltitude;
2095 }
2096
2097 @Override
2098 public boolean supportsBearing() {
2099 return mSupportsBearing;
2100 }
2101
2102 @Override
2103 public boolean supportsSpeed() {
2104 return mSupportsSpeed;
2105 }
2106 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002107
2108 private void checkMockPermissions() {
2109 boolean allowMocks = false;
2110 try {
2111 allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
2112 Settings.Secure.ALLOW_MOCK_LOCATION) == 1;
2113 } catch (SettingNotFoundException e) {
2114 // Do nothing
2115 }
2116 if (!allowMocks) {
2117 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
2118 }
2119
2120 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
2121 PackageManager.PERMISSION_GRANTED) {
2122 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
2123 }
2124 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002125
2126 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
2127 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
2128 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002129 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002130
2131 MockProvider provider = new MockProvider(name, requiresNetwork, requiresSatellite,
2132 requiresCell, hasMonetaryCost, supportsAltitude,
2133 supportsSpeed, supportsBearing, powerRequirement, accuracy);
2134 if (LocationProviderImpl.getProvider(name) != null) {
2135 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
2136 }
2137 LocationProviderImpl.addProvider(provider);
2138 updateProviders();
2139 }
2140
2141 public void removeTestProvider(String provider) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002142 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002143 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
2144 if (p == null) {
2145 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2146 }
2147 LocationProviderImpl.removeProvider(p);
2148 updateProviders();
2149 }
2150
2151 public void setTestProviderLocation(String provider, Location loc) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002152 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002153 if (LocationProviderImpl.getProvider(provider) == null) {
2154 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2155 }
2156 mMockProviderLocation.put(provider, loc);
2157 }
2158
2159 public void clearTestProviderLocation(String provider) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002160 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002161 if (LocationProviderImpl.getProvider(provider) == null) {
2162 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2163 }
2164 mMockProviderLocation.remove(provider);
2165 }
2166
2167 public void setTestProviderEnabled(String provider, boolean enabled) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002168 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002169 if (LocationProviderImpl.getProvider(provider) == null) {
2170 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2171 }
2172 if (enabled) {
2173 mEnabledProviders.add(provider);
2174 mDisabledProviders.remove(provider);
2175 } else {
2176 mEnabledProviders.remove(provider);
2177 mDisabledProviders.add(provider);
2178 }
2179 updateProviders();
2180 }
2181
2182 public void clearTestProviderEnabled(String provider) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002183 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002184 if (LocationProviderImpl.getProvider(provider) == null) {
2185 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2186 }
2187 mEnabledProviders.remove(provider);
2188 mDisabledProviders.remove(provider);
2189 updateProviders();
2190 }
2191
2192 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002193 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002194 if (LocationProviderImpl.getProvider(provider) == null) {
2195 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2196 }
2197 mMockProviderStatus.put(provider, new Integer(status));
2198 mMockProviderStatusExtras.put(provider, extras);
2199 mMockProviderStatusUpdateTime.put(provider, new Long(updateTime));
2200 }
2201
2202 public void clearTestProviderStatus(String provider) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002203 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002204 if (LocationProviderImpl.getProvider(provider) == null) {
2205 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2206 }
2207 mMockProviderStatus.remove(provider);
2208 mMockProviderStatusExtras.remove(provider);
2209 mMockProviderStatusUpdateTime.remove(provider);
2210 }
2211
2212 private void log(String log) {
2213 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2214 Log.d(TAG, log);
2215 }
2216 }
2217}
2218