blob: db4daa561c1c069415b8f2fb557abfb547632c44 [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 Projectda996f32009-02-13 12:57:50 -0800135 private static final int MESSAGE_INSTALL_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 Projectda996f32009-02-13 12:57:50 -0800568 public void setInstallCallback(InstallCallback callback) {
569 mLocationHandler.removeMessages(MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER);
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800570 Message m = Message.obtain(mLocationHandler,
The Android Open Source Projectda996f32009-02-13 12:57:50 -0800571 MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER, callback);
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800572 mLocationHandler.sendMessageAtFrontOfQueue(m);
573 }
574
The Android Open Source Projectda996f32009-02-13 12:57:50 -0800575 public void setNetworkLocationProvider(INetworkLocationProvider provider) {
576 mNetworkLocationInterface = provider;
577 provider.addListener(getPackageNames());
578 mNetworkLocationProvider = (LocationProviderImpl)provider;
579 LocationProviderImpl.addProvider(mNetworkLocationProvider);
580 updateProviders();
581 }
582
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800583 public void setLocationCollector(ILocationCollector collector) {
584 mCollector = collector;
585 if (mGpsLocationProvider != null) {
586 mGpsLocationProvider.setLocationCollector(mCollector);
587 }
588 }
589
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700590 private WifiManager.WifiLock getWifiWakelock() {
591 if (mWifiLock == null && mWifiManager != null) {
The Android Open Source Projectb7986892009-01-09 17:51:23 -0800592 mWifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_SCAN_ONLY, WIFILOCK_KEY);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700593 mWifiLock.setReferenceCounted(false);
594 }
595 return mWifiLock;
596 }
597
598 private boolean isAllowedBySettings(String provider) {
599 if (mEnabledProviders.contains(provider)) {
600 return true;
601 }
602 if (mDisabledProviders.contains(provider)) {
603 return false;
604 }
605 // Use system settings
606 ContentResolver resolver = mContext.getContentResolver();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800607 String allowedProviders = Settings.Secure.getString(resolver,
608 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700609
610 return ((allowedProviders != null) && (allowedProviders.contains(provider)));
611 }
612
613 private void checkPermissions(String provider) {
614 if (LocationManager.GPS_PROVIDER.equals(provider)
615 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
616 != PackageManager.PERMISSION_GRANTED)) {
617 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
618 }
619 if (LocationManager.NETWORK_PROVIDER.equals(provider)
620 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
621 != PackageManager.PERMISSION_GRANTED)
622 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
623 != PackageManager.PERMISSION_GRANTED)) {
624 throw new SecurityException(
625 "Requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
626 }
627 }
628
629 private boolean isAllowedProvider(String provider) {
630 if (LocationManager.GPS_PROVIDER.equals(provider)
631 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
632 != PackageManager.PERMISSION_GRANTED)) {
633 return false;
634 }
635 if (LocationManager.NETWORK_PROVIDER.equals(provider)
636 && (mContext.checkCallingPermission(ACCESS_FINE_LOCATION)
637 != PackageManager.PERMISSION_GRANTED)
638 && (mContext.checkCallingPermission(ACCESS_COARSE_LOCATION)
639 != PackageManager.PERMISSION_GRANTED)) {
640 return false;
641 }
642
643 return true;
644 }
645
646 private String[] getPackageNames() {
647 // Since a single UID may correspond to multiple packages, this can only be used as an
648 // approximation for tracking
649 return mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
650 }
651
652 public List<String> getAllProviders() {
653 try {
654 return _getAllProviders();
655 } catch (SecurityException se) {
656 throw se;
657 } catch (Exception e) {
658 Log.e(TAG, "getAllProviders got exception:", e);
659 return null;
660 }
661 }
662
663 private List<String> _getAllProviders() {
664 if (Config.LOGD) {
665 Log.d(TAG, "getAllProviders");
666 }
667 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
668 ArrayList<String> out = new ArrayList<String>(providers.size());
669
670 for (LocationProviderImpl p : providers) {
671 out.add(p.getName());
672 }
673 return out;
674 }
675
676 public List<String> getProviders(boolean enabledOnly) {
677 try {
678 return _getProviders(enabledOnly);
679 } catch (SecurityException se) {
680 throw se;
681 } catch (Exception e) {
682 Log.e(TAG, "getProviders gotString exception:", e);
683 return null;
684 }
685 }
686
687 private List<String> _getProviders(boolean enabledOnly) {
688 if (Config.LOGD) {
689 Log.d(TAG, "getProviders");
690 }
691 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
692 ArrayList<String> out = new ArrayList<String>();
693
694 for (LocationProviderImpl p : providers) {
695 String name = p.getName();
696 if (isAllowedProvider(name)) {
697 if (enabledOnly && !isAllowedBySettings(name)) {
698 continue;
699 }
700 out.add(name);
701 }
702 }
703 return out;
704 }
705
706 public void updateProviders() {
707 for (LocationProviderImpl p : LocationProviderImpl.getProviders()) {
708 boolean isEnabled = p.isEnabled();
709 String name = p.getName();
710 boolean shouldBeEnabled = isAllowedBySettings(name);
711
712 // Collection is only allowed when network provider is being used
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800713 if (mCollector != null &&
714 p.getName().equals(LocationManager.NETWORK_PROVIDER)) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700715 mCollector.updateNetworkProviderStatus(shouldBeEnabled);
716 }
717
718 if (isEnabled && !shouldBeEnabled) {
719 updateProviderListeners(name, false);
720 } else if (!isEnabled && shouldBeEnabled) {
721 updateProviderListeners(name, true);
722 }
723
724 }
725 }
726
727 private void updateProviderListeners(String provider, boolean enabled) {
728 int listeners = 0;
729
730 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
731 if (p == null) {
732 return;
733 }
734
735 synchronized (mRecordsByProvider) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700736 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
737 if (records != null) {
738 for (UpdateRecord record : records) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800739 // Sends a notification message to the receiver
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700740 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800741 Receiver receiver = record.mReceiver;
742 if (receiver.isListener()) {
743 if (enabled) {
744 receiver.getListener().onProviderEnabled(provider);
745 } else {
746 receiver.getListener().onProviderDisabled(provider);
747 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700748 } else {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800749 PendingIntent intent = receiver.getPendingIntent();
750 Intent providerIntent = new Intent();
751 providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
752 try {
753 receiver.getPendingIntent().send(mContext, 0,
754 providerIntent, null, null);
755 } catch (PendingIntent.CanceledException e) {
756 _removeUpdates(receiver);
757 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700758 }
759 } catch (RemoteException e) {
760 // The death link will clean this up.
761 }
762 listeners++;
763 }
764 }
765 }
766
767 if (enabled) {
768 p.enable();
769 if (listeners > 0) {
770 p.setMinTime(getMinTime(provider));
771 p.enableLocationTracking(true);
772 updateWakelockStatus(mScreenOn);
773 }
774 } else {
775 p.enableLocationTracking(false);
776 p.disable();
777 updateWakelockStatus(mScreenOn);
778 }
779
780 if (enabled && listeners > 0) {
781 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
782 Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
783 mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
784 } else {
785 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
786 }
787 }
788
789 private long getMinTime(String provider) {
790 long minTime = Long.MAX_VALUE;
791 synchronized (mRecordsByProvider) {
792 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
793 if (records != null) {
794 for (UpdateRecord r : records) {
795 minTime = Math.min(minTime, r.mMinTime);
796 }
797 }
798 }
799 return minTime;
800 }
801
802 private class UpdateRecord {
803 String mProvider;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800804 Receiver mReceiver;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700805 long mMinTime;
806 float mMinDistance;
807 String[] mPackages;
808
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800809 UpdateRecord(String provider, long minTime, float minDistance,
810 Receiver receiver, String[] packages) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700811 mProvider = provider;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800812 mReceiver = receiver;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700813 mMinTime = minTime;
814 mMinDistance = minDistance;
815 mPackages = packages;
816
817 synchronized (mRecordsByProvider) {
818 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
819 if (records == null) {
820 records = new HashSet<UpdateRecord>();
821 mRecordsByProvider.put(provider, records);
822 }
823 records.add(this);
824 }
825 }
826
827 /**
828 * Method to be called when a record will no longer be used. Calling this multiple times
829 * must have the same effect as calling it once.
830 */
831 public void dispose() {
832 synchronized (mRecordsByProvider) {
833 HashSet<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
834 records.remove(this);
835 }
836 }
837
838 /**
839 * Calls dispose().
840 */
841 @Override protected void finalize() {
842 dispose();
843 }
844 }
845
846 public void requestLocationUpdates(String provider,
847 long minTime, float minDistance, ILocationListener listener) {
848
849 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800850 _requestLocationUpdates(provider, minTime, minDistance,
851 new Receiver(listener));
852 } catch (SecurityException se) {
853 throw se;
854 } catch (Exception e) {
855 Log.e(TAG, "requestUpdates got exception:", e);
856 }
857 }
858
859 public void requestLocationUpdatesPI(String provider,
860 long minTime, float minDistance, PendingIntent intent) {
861 try {
862 _requestLocationUpdates(provider, minTime, minDistance,
863 new Receiver(intent));
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700864 } catch (SecurityException se) {
865 throw se;
866 } catch (Exception e) {
867 Log.e(TAG, "requestUpdates got exception:", e);
868 }
869 }
870
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800871 private synchronized void _requestLocationUpdates(String provider,
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800872 long minTime, float minDistance, Receiver receiver) {
873 Object key = receiver.getKey();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700874 if (Config.LOGD) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800875 Log.d(TAG, "_requestLocationUpdates: listener = " + key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700876 }
877
878 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
879 if (impl == null) {
880 throw new IllegalArgumentException("provider=" + provider);
881 }
882
883 checkPermissions(provider);
884
885 String[] packages = getPackageNames();
886
887 // so wakelock calls will succeed
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800888 mCallingUid = getCallingUid();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700889 long identity = Binder.clearCallingIdentity();
890 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800891 UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, packages);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700892 synchronized (mLocationListeners) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800893 if (mListeners.get(key) == null) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700894 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800895 if (receiver.isListener()) {
896 receiver.getListener().asBinder().linkToDeath(receiver, 0);
897 }
898 mListeners.put(key, receiver);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700899 } catch (RemoteException e) {
900 return;
901 }
902 }
903
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800904 HashMap<String,UpdateRecord> records = mLocationListeners.get(key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700905 if (records == null) {
906 records = new HashMap<String,UpdateRecord>();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800907 mLocationListeners.put(key, records);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700908 }
909 UpdateRecord oldRecord = records.put(provider, r);
910 if (oldRecord != null) {
911 oldRecord.dispose();
912 }
913
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700914 boolean isProviderEnabled = isAllowedBySettings(provider);
915 if (isProviderEnabled) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800916 if (provider.equals(LocationManager.GPS_PROVIDER)) {
917 try {
918 mBatteryStats.noteRequestGpsOn(mCallingUid);
919 } catch (RemoteException e) {
920 Log.w(TAG, "Got RemoteException calling noteRequestGpsOff", e);
921 }
922 }
923
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700924 long minTimeForProvider = getMinTime(provider);
925 impl.setMinTime(minTimeForProvider);
926 impl.enableLocationTracking(true);
927 updateWakelockStatus(mScreenOn);
928
929 // Clear heartbeats if any before starting a new one
930 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
931 Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
932 mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700933 } else {
934 try {
935 // Notify the listener that updates are currently disabled
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800936 if (receiver.isListener()) {
937 receiver.getListener().onProviderDisabled(provider);
938 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700939 } catch(RemoteException e) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800940 Log.w(TAG, "RemoteException calling onProviderDisabled on " +
941 receiver.getListener());
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700942 }
943 }
944 }
945 } finally {
946 Binder.restoreCallingIdentity(identity);
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800947 mCallingUid = -1;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700948 }
949 }
950
951 public void removeUpdates(ILocationListener listener) {
952 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800953 _removeUpdates(new Receiver(listener));
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700954 } catch (SecurityException se) {
955 throw se;
956 } catch (Exception e) {
957 Log.e(TAG, "removeUpdates got exception:", e);
958 }
959 }
960
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800961 public void removeUpdatesPI(PendingIntent intent) {
962 try {
963 _removeUpdates(new Receiver(intent));
964 } catch (SecurityException se) {
965 throw se;
966 } catch (Exception e) {
967 Log.e(TAG, "removeUpdates got exception:", e);
968 }
969 }
970
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800971 private synchronized void _removeUpdates(Receiver receiver) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800972 Object key = receiver.getKey();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700973 if (Config.LOGD) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800974 Log.d(TAG, "_removeUpdates: listener = " + key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700975 }
976
977 // so wakelock calls will succeed
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800978 mCallingUid = getCallingUid();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700979 long identity = Binder.clearCallingIdentity();
980 try {
981 synchronized (mLocationListeners) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800982 Receiver myReceiver = mListeners.remove(key);
983 if ((myReceiver != null) && (myReceiver.isListener())) {
984 myReceiver.getListener().asBinder().unlinkToDeath(myReceiver, 0);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700985 }
986
987 // Record which providers were associated with this listener
988 HashSet<String> providers = new HashSet<String>();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800989 HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700990 if (oldRecords != null) {
991 // Call dispose() on the obsolete update records.
992 for (UpdateRecord record : oldRecords.values()) {
993 if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -0800994 if (mNetworkLocationInterface != null) {
995 mNetworkLocationInterface.removeListener(record.mPackages);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -0700996 }
997 }
998 record.dispose();
999 }
1000 // Accumulate providers
1001 providers.addAll(oldRecords.keySet());
1002 }
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001003
1004 if (providers.contains("gps")) {
1005 try {
1006 mBatteryStats.noteRequestGpsOff(mCallingUid);
1007 } catch (RemoteException e) {
1008 Log.w(TAG, "Got RemoteException calling noteRequestGpsOff", e);
1009 }
1010 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001011
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001012 mLocationListeners.remove(key);
1013 mLastFixBroadcast.remove(key);
1014 mLastStatusBroadcast.remove(key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001015
1016 // See if the providers associated with this listener have any
1017 // other listeners; if one does, inform it of the new smallest minTime
1018 // value; if one does not, disable location tracking for it
1019 for (String provider : providers) {
1020 // If provider is already disabled, don't need to do anything
1021 if (!isAllowedBySettings(provider)) {
1022 continue;
1023 }
1024
1025 boolean hasOtherListener = false;
1026 synchronized (mRecordsByProvider) {
1027 HashSet<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
1028 if (recordsForProvider != null && recordsForProvider.size() > 0) {
1029 hasOtherListener = true;
1030 }
1031 }
1032
1033 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1034 if (p != null) {
1035 if (hasOtherListener) {
1036 p.setMinTime(getMinTime(provider));
1037 } else {
1038 mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
1039 p.enableLocationTracking(false);
1040 }
1041 }
1042 }
1043
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001044 updateWakelockStatus(mScreenOn);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001045 }
1046 } finally {
1047 Binder.restoreCallingIdentity(identity);
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001048 mCallingUid = -1;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001049 }
1050 }
1051
1052 public boolean addGpsStatusListener(IGpsStatusListener listener) {
1053 if (mGpsLocationProvider == null) {
1054 return false;
1055 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001056 if (mContext.checkCallingPermission(ACCESS_FINE_LOCATION) !=
1057 PackageManager.PERMISSION_GRANTED) {
1058 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1059 }
1060
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001061 try {
1062 mGpsLocationProvider.addGpsStatusListener(listener);
1063 } catch (RemoteException e) {
1064 Log.w(TAG, "RemoteException in addGpsStatusListener");
1065 return false;
1066 }
1067 return true;
1068 }
1069
1070 public void removeGpsStatusListener(IGpsStatusListener listener) {
1071 mGpsLocationProvider.removeGpsStatusListener(listener);
1072 }
1073
1074 public boolean sendExtraCommand(String provider, String command, Bundle extras) {
1075 // first check for permission to the provider
1076 checkPermissions(provider);
1077 // and check for ACCESS_LOCATION_EXTRA_COMMANDS
1078 if ((mContext.checkCallingPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
1079 != PackageManager.PERMISSION_GRANTED)) {
1080 throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
1081 }
1082
1083 LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
1084 if (provider == null) {
1085 return false;
1086 }
1087
1088 return impl.sendExtraCommand(command, extras);
1089 }
1090
1091 class ProximityAlert {
1092 double mLatitude;
1093 double mLongitude;
1094 float mRadius;
1095 long mExpiration;
1096 PendingIntent mIntent;
1097 Location mLocation;
1098
1099 public ProximityAlert(double latitude, double longitude,
1100 float radius, long expiration, PendingIntent intent) {
1101 mLatitude = latitude;
1102 mLongitude = longitude;
1103 mRadius = radius;
1104 mExpiration = expiration;
1105 mIntent = intent;
1106
1107 mLocation = new Location("");
1108 mLocation.setLatitude(latitude);
1109 mLocation.setLongitude(longitude);
1110 }
1111
1112 public long getExpiration() {
1113 return mExpiration;
1114 }
1115
1116 public PendingIntent getIntent() {
1117 return mIntent;
1118 }
1119
1120 public boolean isInProximity(double latitude, double longitude) {
1121 Location loc = new Location("");
1122 loc.setLatitude(latitude);
1123 loc.setLongitude(longitude);
1124
1125 double radius = loc.distanceTo(mLocation);
1126 return radius <= mRadius;
1127 }
1128 }
1129
1130 // Listener for receiving locations to trigger proximity alerts
1131 class ProximityListener extends ILocationListener.Stub {
1132
1133 boolean isGpsAvailable = false;
1134
1135 public void onLocationChanged(Location loc) {
1136
1137 // If Gps is available, then ignore updates from NetworkLocationProvider
1138 if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
1139 isGpsAvailable = true;
1140 }
1141 if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
1142 return;
1143 }
1144
1145 // Process proximity alerts
1146 long now = System.currentTimeMillis();
1147 double latitude = loc.getLatitude();
1148 double longitude = loc.getLongitude();
1149 ArrayList<PendingIntent> intentsToRemove = null;
1150
1151 for (ProximityAlert alert : mProximityAlerts.values()) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001152 PendingIntent intent = alert.getIntent();
1153 long expiration = alert.getExpiration();
1154
1155 if ((expiration == -1) || (now <= expiration)) {
1156 boolean entered = mProximitiesEntered.contains(alert);
1157 boolean inProximity =
1158 alert.isInProximity(latitude, longitude);
1159 if (!entered && inProximity) {
1160 if (Config.LOGD) {
1161 Log.i(TAG, "Entered alert");
1162 }
1163 mProximitiesEntered.add(alert);
1164 Intent enteredIntent = new Intent();
1165 enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
1166 try {
1167 intent.send(mContext, 0, enteredIntent, null, null);
1168 } catch (PendingIntent.CanceledException e) {
1169 if (Config.LOGD) {
1170 Log.i(TAG, "Canceled proximity alert: " + alert, e);
1171 }
1172 if (intentsToRemove == null) {
1173 intentsToRemove = new ArrayList<PendingIntent>();
1174 }
1175 intentsToRemove.add(intent);
1176 }
1177 } else if (entered && !inProximity) {
1178 if (Config.LOGD) {
1179 Log.i(TAG, "Exited alert");
1180 }
1181 mProximitiesEntered.remove(alert);
1182 Intent exitedIntent = new Intent();
1183 exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
1184 try {
1185 intent.send(mContext, 0, exitedIntent, null, null);
1186 } catch (PendingIntent.CanceledException e) {
1187 if (Config.LOGD) {
1188 Log.i(TAG, "Canceled proximity alert: " + alert, e);
1189 }
1190 if (intentsToRemove == null) {
1191 intentsToRemove = new ArrayList<PendingIntent>();
1192 }
1193 intentsToRemove.add(intent);
1194 }
1195 }
1196 } else {
1197 // Mark alert for expiration
1198 if (Config.LOGD) {
1199 Log.i(TAG, "Expiring proximity alert: " + alert);
1200 }
1201 if (intentsToRemove == null) {
1202 intentsToRemove = new ArrayList<PendingIntent>();
1203 }
1204 intentsToRemove.add(alert.getIntent());
1205 }
1206 }
1207
1208 // Remove expired alerts
1209 if (intentsToRemove != null) {
1210 for (PendingIntent i : intentsToRemove) {
1211 mProximityAlerts.remove(i);
1212 ProximityAlert alert = mProximityAlerts.get(i);
1213 mProximitiesEntered.remove(alert);
1214 }
1215 }
1216
1217 }
1218
1219 public void onProviderDisabled(String provider) {
1220 if (provider.equals(LocationManager.GPS_PROVIDER)) {
1221 isGpsAvailable = false;
1222 }
1223 }
1224
1225 public void onProviderEnabled(String provider) {
1226 // ignore
1227 }
1228
1229 public void onStatusChanged(String provider, int status, Bundle extras) {
1230 if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
1231 (status != LocationProvider.AVAILABLE)) {
1232 isGpsAvailable = false;
1233 }
1234 }
1235 }
1236
1237 public void addProximityAlert(double latitude, double longitude,
1238 float radius, long expiration, PendingIntent intent) {
1239 try {
1240 _addProximityAlert(latitude, longitude, radius, expiration, intent);
1241 } catch (SecurityException se) {
1242 throw se;
1243 } catch (Exception e) {
1244 Log.e(TAG, "addProximityAlert got exception:", e);
1245 }
1246 }
1247
1248 private void _addProximityAlert(double latitude, double longitude,
1249 float radius, long expiration, PendingIntent intent) {
1250 if (Config.LOGD) {
1251 Log.d(TAG, "addProximityAlert: latitude = " + latitude +
1252 ", longitude = " + longitude +
1253 ", expiration = " + expiration +
1254 ", intent = " + intent);
1255 }
1256
1257 // Require ability to access all providers for now
1258 if (!isAllowedProvider(LocationManager.GPS_PROVIDER) ||
1259 !isAllowedProvider(LocationManager.NETWORK_PROVIDER)) {
1260 throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
1261 }
1262
1263 if (expiration != -1) {
1264 expiration += System.currentTimeMillis();
1265 }
1266 ProximityAlert alert = new ProximityAlert(latitude, longitude, radius, expiration, intent);
1267 mProximityAlerts.put(intent, alert);
1268
1269 if (mProximityListener == null) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001270 mProximityListener = new Receiver(new ProximityListener());
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001271
1272 LocationProvider provider = LocationProviderImpl.getProvider(
1273 LocationManager.GPS_PROVIDER);
1274 if (provider != null) {
1275 _requestLocationUpdates(provider.getName(), 1000L, 1.0f, mProximityListener);
1276 }
1277
1278 provider =
1279 LocationProviderImpl.getProvider(LocationManager.NETWORK_PROVIDER);
1280 if (provider != null) {
1281 _requestLocationUpdates(provider.getName(), 1000L, 1.0f, mProximityListener);
1282 }
1283 }
1284 }
1285
1286 public void removeProximityAlert(PendingIntent intent) {
1287 try {
1288 _removeProximityAlert(intent);
1289 } catch (SecurityException se) {
1290 throw se;
1291 } catch (Exception e) {
1292 Log.e(TAG, "removeProximityAlert got exception:", e);
1293 }
1294 }
1295
1296 private void _removeProximityAlert(PendingIntent intent) {
1297 if (Config.LOGD) {
1298 Log.d(TAG, "removeProximityAlert: intent = " + intent);
1299 }
1300
1301 mProximityAlerts.remove(intent);
1302 if (mProximityAlerts.size() == 0) {
1303 _removeUpdates(mProximityListener);
1304 mProximityListener = null;
1305 }
1306 }
1307
1308 /**
1309 * @return null if the provider does not exits
1310 * @throw SecurityException if the provider is not allowed to be
1311 * accessed by the caller
1312 */
1313 public Bundle getProviderInfo(String provider) {
1314 try {
1315 return _getProviderInfo(provider);
1316 } catch (SecurityException se) {
1317 throw se;
1318 } catch (Exception e) {
1319 Log.e(TAG, "_getProviderInfo got exception:", e);
1320 return null;
1321 }
1322 }
1323
1324 private Bundle _getProviderInfo(String provider) {
1325 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1326 if (p == null) {
1327 return null;
1328 }
1329
1330 checkPermissions(provider);
1331
1332 Bundle b = new Bundle();
1333 b.putBoolean("network", p.requiresNetwork());
1334 b.putBoolean("satellite", p.requiresSatellite());
1335 b.putBoolean("cell", p.requiresCell());
1336 b.putBoolean("cost", p.hasMonetaryCost());
1337 b.putBoolean("altitude", p.supportsAltitude());
1338 b.putBoolean("speed", p.supportsSpeed());
1339 b.putBoolean("bearing", p.supportsBearing());
1340 b.putInt("power", p.getPowerRequirement());
1341 b.putInt("accuracy", p.getAccuracy());
1342
1343 return b;
1344 }
1345
1346 public boolean isProviderEnabled(String provider) {
1347 try {
1348 return _isProviderEnabled(provider);
1349 } catch (SecurityException se) {
1350 throw se;
1351 } catch (Exception e) {
1352 Log.e(TAG, "isProviderEnabled got exception:", e);
1353 return false;
1354 }
1355 }
1356
1357 private boolean _isProviderEnabled(String provider) {
1358 checkPermissions(provider);
1359
1360 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1361 if (p == null) {
1362 throw new IllegalArgumentException("provider=" + provider);
1363 }
1364 return isAllowedBySettings(provider);
1365 }
1366
1367 public Location getLastKnownLocation(String provider) {
1368 try {
1369 return _getLastKnownLocation(provider);
1370 } catch (SecurityException se) {
1371 throw se;
1372 } catch (Exception e) {
1373 Log.e(TAG, "getLastKnownLocation got exception:", e);
1374 return null;
1375 }
1376 }
1377
1378 private Location _getLastKnownLocation(String provider) {
1379 checkPermissions(provider);
1380
1381 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1382 if (p == null) {
1383 throw new IllegalArgumentException("provider=" + provider);
1384 }
1385
1386 if (!isAllowedBySettings(provider)) {
1387 return null;
1388 }
1389
1390 Location location = mLastKnownLocation.get(provider);
1391 if (location == null) {
1392 // Get the persistent last known location for the provider
1393 location = readLastKnownLocation(provider);
1394 if (location != null) {
1395 mLastKnownLocation.put(provider, location);
1396 }
1397 }
1398
1399 return location;
1400 }
1401
1402 private boolean shouldBroadcast(Location loc, Location lastLoc, UpdateRecord record) {
1403 // Always broadcast the first update
1404 if (lastLoc == null) {
1405 return true;
1406 }
1407
1408 // Don't broadcast same location again regardless of condition
1409 // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
1410 if (loc.getTime() == lastLoc.getTime()) {
1411 return false;
1412 }
1413
1414 // Check whether sufficient distance has been traveled
1415 double minDistance = record.mMinDistance;
1416 if (minDistance > 0.0) {
1417 if (loc.distanceTo(lastLoc) <= minDistance) {
1418 return false;
1419 }
1420 }
1421
1422 return true;
1423 }
1424
1425 private void handleLocationChanged(String provider) {
1426 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
1427 if (records == null || records.size() == 0) {
1428 return;
1429 }
1430
1431 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
1432 if (p == null) {
1433 return;
1434 }
1435
1436 // Get location object
1437 Location loc = mLocationsByProvider.get(provider);
1438 if (loc == null) {
1439 loc = new Location(provider);
1440 mLocationsByProvider.put(provider, loc);
1441 } else {
1442 loc.reset();
1443 }
1444
1445 // Use the mock location if available
1446 Location mockLoc = mMockProviderLocation.get(provider);
1447 boolean locationValid;
1448 if (mockLoc != null) {
1449 locationValid = true;
1450 loc.set(mockLoc);
1451 } else {
1452 locationValid = p.getLocation(loc);
1453 }
1454
1455 // Update last known location for provider
1456 if (locationValid) {
1457 Location location = mLastKnownLocation.get(provider);
1458 if (location == null) {
1459 mLastKnownLocation.put(provider, new Location(loc));
1460 } else {
1461 location.set(loc);
1462 }
1463 writeLastKnownLocation(provider, loc);
1464
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001465 if (p instanceof INetworkLocationProvider) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001466 mWakeLockNetworkReceived = true;
1467 } else if (p instanceof GpsLocationProvider) {
1468 // Gps location received signal is in NetworkStateBroadcastReceiver
1469 }
1470 }
1471
1472 // Fetch latest status update time
1473 long newStatusUpdateTime = p.getStatusUpdateTime();
1474
1475 // Override real time with mock time if present
1476 Long mockStatusUpdateTime = mMockProviderStatusUpdateTime.get(provider);
1477 if (mockStatusUpdateTime != null) {
1478 newStatusUpdateTime = mockStatusUpdateTime.longValue();
1479 }
1480
1481 // Get latest status
1482 Bundle extras = new Bundle();
1483 int status = p.getStatus(extras);
1484
1485 // Override status with mock status if present
1486 Integer mockStatus = mMockProviderStatus.get(provider);
1487 if (mockStatus != null) {
1488 status = mockStatus.intValue();
1489 }
1490
1491 // Override extras with mock extras if present
1492 Bundle mockExtras = mMockProviderStatusExtras.get(provider);
1493 if (mockExtras != null) {
1494 extras.clear();
1495 extras.putAll(mockExtras);
1496 }
1497
1498 // Broadcast location or status to all listeners
1499 for (UpdateRecord r : records) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001500 Receiver receiver = r.mReceiver;
1501 Object key = receiver.getKey();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001502
1503 // Broadcast location only if it is valid
1504 if (locationValid) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001505 HashMap<String,Location> map = mLastFixBroadcast.get(key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001506 if (map == null) {
1507 map = new HashMap<String,Location>();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001508 mLastFixBroadcast.put(key, map);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001509 }
1510 Location lastLoc = map.get(provider);
1511 if ((lastLoc == null) || shouldBroadcast(loc, lastLoc, r)) {
1512 if (lastLoc == null) {
1513 lastLoc = new Location(loc);
1514 map.put(provider, lastLoc);
1515 } else {
1516 lastLoc.set(loc);
1517 }
1518 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001519 receiver.onLocationChanged(loc);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001520 } catch (RemoteException doe) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001521 Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
1522 _removeUpdates(receiver);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001523 }
1524 }
1525 }
1526
1527 // Broadcast status message
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001528 HashMap<String,Long> statusMap = mLastStatusBroadcast.get(key);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001529 if (statusMap == null) {
1530 statusMap = new HashMap<String,Long>();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001531 mLastStatusBroadcast.put(key, statusMap);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001532 }
1533 long prevStatusUpdateTime =
1534 (statusMap.get(provider) != null) ? statusMap.get(provider) : 0;
1535
1536 if ((newStatusUpdateTime > prevStatusUpdateTime) &&
1537 (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
1538
1539 statusMap.put(provider, newStatusUpdateTime);
1540 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001541 receiver.onStatusChanged(provider, status, extras);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001542 } catch (RemoteException doe) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001543 Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
1544 _removeUpdates(receiver);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001545 }
1546 }
1547 }
1548 }
1549
1550 private class LocationWorkerHandler extends Handler {
1551
1552 @Override
1553 public void handleMessage(Message msg) {
1554 try {
1555 if (msg.what == MESSAGE_HEARTBEAT) {
1556 // log("LocationWorkerHandler: Heartbeat!");
1557
1558 synchronized (mRecordsByProvider) {
1559 String provider = (String) msg.obj;
1560 if (!isAllowedBySettings(provider)) {
1561 return;
1562 }
1563
1564 // Process the location fix if the screen is on or we're holding a wakelock
1565 if (mScreenOn || (mWakeLockAcquireTime != 0)) {
1566 handleLocationChanged(provider);
1567 }
1568
1569 // If it continues to have listeners
1570 HashSet<UpdateRecord> records = mRecordsByProvider.get(provider);
1571 if (records != null && records.size() > 0) {
1572 Message m = Message.obtain(this, MESSAGE_HEARTBEAT, provider);
1573 sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
1574 }
1575 }
1576
1577 if ((mWakeLockAcquireTime != 0) &&
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001578 (SystemClock.elapsedRealtime() - mWakeLockAcquireTime
1579 > MAX_TIME_FOR_WAKE_LOCK)) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001580
1581 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1582 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1583
1584 log("LocationWorkerHandler: Exceeded max time for wake lock");
1585 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1586 sendMessageAtFrontOfQueue(m);
1587
1588 } else if (mWakeLockAcquireTime != 0 &&
1589 mWakeLockGpsReceived && mWakeLockNetworkReceived) {
1590
1591 removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1592 removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1593
1594 log("LocationWorkerHandler: Locations received.");
1595 mWakeLockAcquireTime = 0;
1596 Message m = Message.obtain(this, MESSAGE_RELEASE_WAKE_LOCK);
1597 sendMessageDelayed(m, TIME_AFTER_WAKE_LOCK);
1598 }
1599
1600 } else if (msg.what == MESSAGE_ACQUIRE_WAKE_LOCK) {
1601 log("LocationWorkerHandler: Acquire");
1602 acquireWakeLock();
1603 } else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) {
1604 log("LocationWorkerHandler: Release");
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001605
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001606 // Update wakelock status so the next alarm is set before releasing wakelock
1607 updateWakelockStatus(mScreenOn);
1608 releaseWakeLock();
The Android Open Source Projectda996f32009-02-13 12:57:50 -08001609 } else if (msg.what == MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001610 synchronized (LocationManagerService.class) {
The Android Open Source Projectda996f32009-02-13 12:57:50 -08001611 Log.d(TAG, "installing network location provider");
1612 INetworkLocationManager.InstallCallback callback =
1613 (INetworkLocationManager.InstallCallback)msg.obj;
1614 callback.installNetworkLocationProvider(mContext, LocationManagerService.this);
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001615 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001616 }
1617 } catch (Exception e) {
1618 // Log, don't crash!
1619 Log.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
1620 }
1621 }
1622 }
1623
1624 PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001625
1626 private CellState mLastCellState = null;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001627 @Override
1628 public void onCellLocationChanged(CellLocation cellLocation) {
1629 try {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001630 int asu = mSignalStrength;
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001631
1632 // Gets cell state
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001633 mLastCellState = new CellState(mTelephonyManager, cellLocation, asu);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001634
1635 // Notify collector
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001636 if (mCollector != null) {
1637 mCollector.updateCellState(mLastCellState);
1638 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001639
1640 // Updates providers
1641 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1642 for (LocationProviderImpl provider : providers) {
1643 if (provider.requiresCell()) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001644 provider.updateCellState(mLastCellState);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001645 }
1646 }
1647 } catch (Exception e) {
1648 Log.e(TAG, "Exception in PhoneStateListener.onCellLocationCahnged:", e);
1649 }
1650 }
1651
1652 @Override
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001653 public void onSignalStrengthChanged(int asu) {
1654 mSignalStrength = asu;
1655
1656 if (mLastCellState != null) {
1657 mLastCellState.updateSignalStrength(asu);
1658 }
1659 }
1660
1661 @Override
1662 public void onDataConnectionStateChanged(int state) {
1663 if (mLastCellState != null) {
1664 mLastCellState.updateRadioType(mTelephonyManager);
1665 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001666 }
1667 };
1668
1669 private class PowerStateBroadcastReceiver extends BroadcastReceiver {
1670 @Override public void onReceive(Context context, Intent intent) {
1671 String action = intent.getAction();
1672
1673 if (action.equals(ALARM_INTENT)) {
1674 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1675 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1676
1677 log("PowerStateBroadcastReceiver: Alarm received");
1678 Message m = mLocationHandler.obtainMessage(MESSAGE_ACQUIRE_WAKE_LOCK);
1679 mLocationHandler.sendMessageAtFrontOfQueue(m);
1680
1681 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
1682 log("PowerStateBroadcastReceiver: Screen off");
1683 updateWakelockStatus(false);
1684
1685 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
1686 log("PowerStateBroadcastReceiver: Screen on");
1687 updateWakelockStatus(true);
1688
1689 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
1690 log("PowerStateBroadcastReceiver: Battery changed");
1691 int scale = intent.getIntExtra(BATTERY_EXTRA_SCALE, 100);
1692 int level = intent.getIntExtra(BATTERY_EXTRA_LEVEL, 0);
1693 boolean plugged = intent.getIntExtra(BATTERY_EXTRA_PLUGGED, 0) != 0;
1694
1695 // Notify collector battery state
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001696 if (mCollector != null) {
1697 mCollector.updateBatteryState(scale, level, plugged);
1698 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001699 }
1700 }
1701 }
1702
1703 private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
1704 @Override public void onReceive(Context context, Intent intent) {
1705 String action = intent.getAction();
1706
1707 if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
1708
1709 List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
1710
1711 if (wifiScanResults == null) {
1712 return;
1713 }
1714
1715 // Notify provider and collector of Wifi scan results
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001716 if (mCollector != null) {
1717 mCollector.updateWifiScanResults(wifiScanResults);
1718 }
1719 if (mNetworkLocationInterface != null) {
1720 mNetworkLocationInterface.updateWifiScanResults(wifiScanResults);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001721 }
1722
1723 } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
1724 int networkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
1725
1726 boolean noConnectivity =
1727 intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
1728 if (!noConnectivity) {
1729 networkState = LocationProvider.AVAILABLE;
1730 }
1731
1732 // Notify location providers of current network state
1733 List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
1734 for (LocationProviderImpl provider : providers) {
1735 if (provider.requiresNetwork()) {
1736 provider.updateNetworkState(networkState);
1737 }
1738 }
1739
1740 } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
1741 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
1742 WifiManager.WIFI_STATE_UNKNOWN);
1743
1744 boolean enabled;
1745 if (state == WifiManager.WIFI_STATE_ENABLED) {
1746 enabled = true;
1747 } else if (state == WifiManager.WIFI_STATE_DISABLED) {
1748 enabled = false;
1749 } else {
1750 return;
1751 }
1752
1753 // Notify network provider of current wifi enabled state
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001754 if (mNetworkLocationInterface != null) {
1755 mNetworkLocationInterface.updateWifiEnabledState(enabled);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001756 }
1757
1758 } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION)) {
1759
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001760 final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED,
1761 false);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001762
1763 if (!enabled) {
1764 // When GPS is disabled, we are OK to release wake-lock
1765 mWakeLockGpsReceived = true;
1766 }
1767 }
1768
1769 }
1770 }
1771
1772 // Wake locks
1773
1774 private void updateWakelockStatus(boolean screenOn) {
1775 log("updateWakelockStatus(): " + screenOn);
1776
1777 boolean needsLock = false;
1778 long minTime = Integer.MAX_VALUE;
1779
1780 if (mNetworkLocationProvider != null && mNetworkLocationProvider.isLocationTracking()) {
1781 needsLock = true;
1782 minTime = Math.min(mNetworkLocationProvider.getMinTime(), minTime);
1783 }
1784
1785 if (mGpsLocationProvider != null && mGpsLocationProvider.isLocationTracking()) {
1786 needsLock = true;
1787 minTime = Math.min(mGpsLocationProvider.getMinTime(), minTime);
1788 if (screenOn) {
1789 startGps();
1790 } else if (mScreenOn && !screenOn) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001791
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001792 // We just turned the screen off so stop navigating
1793 stopGps();
1794 }
1795 }
1796
1797 mScreenOn = screenOn;
1798
1799 PendingIntent sender =
1800 PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_INTENT), 0);
1801
1802 // Cancel existing alarm
1803 log("Cancelling existing alarm");
1804 mAlarmManager.cancel(sender);
1805
1806 if (needsLock && !mScreenOn) {
1807 long now = SystemClock.elapsedRealtime();
1808 mAlarmManager.set(
1809 AlarmManager.ELAPSED_REALTIME_WAKEUP, now + minTime, sender);
1810 mAlarmInterval = minTime;
1811 log("Creating a new wakelock alarm with minTime = " + minTime);
1812 } else {
1813 log("No need for alarm");
1814 mAlarmInterval = -1;
1815
1816 // Clear out existing wakelocks
1817 mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
1818 mLocationHandler.removeMessages(MESSAGE_RELEASE_WAKE_LOCK);
1819 releaseWakeLock();
1820 }
1821 }
1822
1823 private void acquireWakeLock() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001824 try {
1825 acquireWakeLockX();
1826 } catch (Exception e) {
1827 // This is to catch a runtime exception thrown when we try to release an
1828 // already released lock.
1829 Log.e(TAG, "exception in acquireWakeLock()", e);
1830 }
1831 }
1832
1833 private void acquireWakeLockX() {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001834 if (mWakeLock.isHeld()) {
1835 log("Must release wakelock before acquiring");
1836 mWakeLockAcquireTime = 0;
1837 mWakeLock.release();
1838 }
1839
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001840 boolean networkActive = (mNetworkLocationProvider != null)
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001841 && mNetworkLocationProvider.isLocationTracking();
1842 boolean gpsActive = (mGpsLocationProvider != null)
1843 && mGpsLocationProvider.isLocationTracking();
1844
1845 boolean needsLock = networkActive || gpsActive;
1846 if (!needsLock) {
1847 log("No need for Lock!");
1848 return;
1849 }
1850
1851 mWakeLockGpsReceived = !gpsActive;
1852 mWakeLockNetworkReceived = !networkActive;
1853
1854 // Acquire wake lock
1855 mWakeLock.acquire();
1856 mWakeLockAcquireTime = SystemClock.elapsedRealtime();
1857 log("Acquired wakelock");
1858
1859 // Start the gps provider
1860 startGps();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001861
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001862 // Acquire cell lock
1863 if (mCellWakeLockAcquired) {
1864 // Lock is already acquired
1865 } else if (!mWakeLockNetworkReceived) {
1866 mTelephonyManager.enableLocationUpdates();
1867 mCellWakeLockAcquired = true;
1868 } else {
1869 mCellWakeLockAcquired = false;
1870 }
1871
1872 // Notify NetworkLocationProvider
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001873 if (mNetworkLocationInterface != null) {
1874 mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001875 }
1876
1877 // Acquire wifi lock
1878 WifiManager.WifiLock wifiLock = getWifiWakelock();
1879 if (wifiLock != null) {
1880 if (mWifiWakeLockAcquired) {
1881 // Lock is already acquired
1882 } else if (mWifiManager.isWifiEnabled() && !mWakeLockNetworkReceived) {
1883 wifiLock.acquire();
1884 mWifiWakeLockAcquired = true;
1885 } else {
1886 mWifiWakeLockAcquired = false;
1887 Log.w(TAG, "acquireWakeLock(): Unable to get WiFi lock");
1888 }
1889 }
1890 }
1891
1892 private void startGps() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001893 boolean gpsActive = (mGpsLocationProvider != null)
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001894 && mGpsLocationProvider.isLocationTracking();
1895 if (gpsActive) {
1896 mGpsLocationProvider.startNavigating();
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001897 long identity = Binder.clearCallingIdentity();
1898 try {
1899 mBatteryStats.noteStartGps(mCallingUid == -1 ? getCallingUid() : mCallingUid);
1900 } catch (RemoteException e) {
1901 Log.w(TAG, "RemoteException calling noteStartGps on BatteryStatsService", e);
1902 } finally {
1903 Binder.restoreCallingIdentity(identity);
1904 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001905 }
1906 }
1907
1908 private void stopGps() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001909 boolean gpsActive = mGpsLocationProvider != null
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001910 && mGpsLocationProvider.isLocationTracking();
1911 if (gpsActive) {
1912 mGpsLocationProvider.stopNavigating();
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001913 long identity = Binder.clearCallingIdentity();
1914 try {
1915 mBatteryStats.noteStopGps(mCallingUid == -1 ? getCallingUid() : mCallingUid);
1916 } catch (RemoteException e) {
1917 Log.w(TAG, "RemoteException calling noteStopGps on BatteryStatsService", e);
1918 } finally {
1919 Binder.restoreCallingIdentity(identity);
1920 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001921 }
1922 }
1923
1924 private void releaseWakeLock() {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001925 try {
1926 releaseWakeLockX();
1927 } catch (Exception e) {
1928 // This is to catch a runtime exception thrown when we try to release an
1929 // already released lock.
1930 Log.e(TAG, "exception in releaseWakeLock()", e);
1931 }
1932 }
1933
1934 private void releaseWakeLockX() {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001935 // Release wifi lock
1936 WifiManager.WifiLock wifiLock = getWifiWakelock();
1937 if (wifiLock != null) {
1938 if (mWifiWakeLockAcquired) {
1939 wifiLock.release();
1940 mWifiWakeLockAcquired = false;
1941 }
1942 }
1943
1944 if (!mScreenOn) {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001945 // Stop the gps
1946 stopGps();
1947 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001948
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001949 // Release cell lock
1950 if (mCellWakeLockAcquired) {
1951 mTelephonyManager.disableLocationUpdates();
1952 mCellWakeLockAcquired = false;
1953 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001954
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001955 // Notify NetworkLocationProvider
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001956 if (mNetworkLocationInterface != null) {
1957 mNetworkLocationInterface.updateCellLockStatus(mCellWakeLockAcquired);
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001958 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001959
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001960 // Release wake lock
1961 mWakeLockAcquireTime = 0;
1962 if (mWakeLock.isHeld()) {
1963 log("Released wakelock");
1964 mWakeLock.release();
1965 } else {
1966 log("Can't release wakelock again!");
1967 }
1968 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001969
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001970 // Geocoder
1971
1972 public String getFromLocation(double latitude, double longitude, int maxResults,
1973 String language, String country, String variant, String appName, List<Address> addrs) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001974 if (mNetworkLocationInterface != null) {
1975 return mNetworkLocationInterface.getFromLocation(latitude, longitude, maxResults,
1976 language, country, variant, appName, addrs);
1977 } else {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001978 return null;
1979 }
1980 }
1981
1982 public String getFromLocationName(String locationName,
1983 double lowerLeftLatitude, double lowerLeftLongitude,
1984 double upperRightLatitude, double upperRightLongitude, int maxResults,
1985 String language, String country, String variant, String appName, List<Address> addrs) {
The Android Open Source Projectd24b8182009-02-10 15:44:00 -08001986 if (mNetworkLocationInterface != null) {
1987 return mNetworkLocationInterface.getFromLocationName(locationName, lowerLeftLatitude,
1988 lowerLeftLongitude, upperRightLatitude, upperRightLongitude, maxResults,
1989 language, country, variant, appName, addrs);
1990 } else {
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07001991 return null;
1992 }
1993 }
1994
1995 // Mock Providers
1996
1997 class MockProvider extends LocationProviderImpl {
1998 boolean mRequiresNetwork;
1999 boolean mRequiresSatellite;
2000 boolean mRequiresCell;
2001 boolean mHasMonetaryCost;
2002 boolean mSupportsAltitude;
2003 boolean mSupportsSpeed;
2004 boolean mSupportsBearing;
2005 int mPowerRequirement;
2006 int mAccuracy;
2007
2008 public MockProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
2009 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
2010 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
2011 super(name);
2012
2013 mRequiresNetwork = requiresNetwork;
2014 mRequiresSatellite = requiresSatellite;
2015 mRequiresCell = requiresCell;
2016 mHasMonetaryCost = hasMonetaryCost;
2017 mSupportsAltitude = supportsAltitude;
2018 mSupportsBearing = supportsBearing;
2019 mSupportsSpeed = supportsSpeed;
2020 mPowerRequirement = powerRequirement;
2021 mAccuracy = accuracy;
2022 }
2023
2024 @Override
2025 public void disable() {
2026 String name = getName();
2027 mEnabledProviders.remove(name);
2028 mDisabledProviders.add(name);
2029 }
2030
2031 @Override
2032 public void enable() {
2033 String name = getName();
2034 mEnabledProviders.add(name);
2035 mDisabledProviders.remove(name);
2036 }
2037
2038 @Override
2039 public boolean getLocation(Location l) {
2040 Location loc = mMockProviderLocation.get(getName());
2041 if (loc == null) {
2042 return false;
2043 }
2044 l.set(loc);
2045 return true;
2046 }
2047
2048 @Override
2049 public int getStatus(Bundle extras) {
2050 String name = getName();
2051 Integer s = mMockProviderStatus.get(name);
2052 int status = (s == null) ? AVAILABLE : s.intValue();
2053 Bundle newExtras = mMockProviderStatusExtras.get(name);
2054 if (newExtras != null) {
2055 extras.clear();
2056 extras.putAll(newExtras);
2057 }
2058 return status;
2059 }
2060
2061 @Override
2062 public boolean isEnabled() {
2063 return mEnabledProviders.contains(getName());
2064 }
2065
2066 @Override
2067 public int getAccuracy() {
2068 return mAccuracy;
2069 }
2070
2071 @Override
2072 public int getPowerRequirement() {
2073 return mPowerRequirement;
2074 }
2075
2076 @Override
2077 public boolean hasMonetaryCost() {
2078 return mHasMonetaryCost;
2079 }
2080
2081 @Override
2082 public boolean requiresCell() {
2083 return mRequiresCell;
2084 }
2085
2086 @Override
2087 public boolean requiresNetwork() {
2088 return mRequiresNetwork;
2089 }
2090
2091 @Override
2092 public boolean requiresSatellite() {
2093 return mRequiresSatellite;
2094 }
2095
2096 @Override
2097 public boolean supportsAltitude() {
2098 return mSupportsAltitude;
2099 }
2100
2101 @Override
2102 public boolean supportsBearing() {
2103 return mSupportsBearing;
2104 }
2105
2106 @Override
2107 public boolean supportsSpeed() {
2108 return mSupportsSpeed;
2109 }
2110 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002111
2112 private void checkMockPermissions() {
2113 boolean allowMocks = false;
2114 try {
2115 allowMocks = Settings.Secure.getInt(mContext.getContentResolver(),
2116 Settings.Secure.ALLOW_MOCK_LOCATION) == 1;
2117 } catch (SettingNotFoundException e) {
2118 // Do nothing
2119 }
2120 if (!allowMocks) {
2121 throw new SecurityException("Requires ACCESS_MOCK_LOCATION secure setting");
2122 }
2123
2124 if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
2125 PackageManager.PERMISSION_GRANTED) {
2126 throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
2127 }
2128 }
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002129
2130 public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
2131 boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
2132 boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002133 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002134
2135 MockProvider provider = new MockProvider(name, requiresNetwork, requiresSatellite,
2136 requiresCell, hasMonetaryCost, supportsAltitude,
2137 supportsSpeed, supportsBearing, powerRequirement, accuracy);
2138 if (LocationProviderImpl.getProvider(name) != null) {
2139 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
2140 }
2141 LocationProviderImpl.addProvider(provider);
2142 updateProviders();
2143 }
2144
2145 public void removeTestProvider(String provider) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002146 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002147 LocationProviderImpl p = LocationProviderImpl.getProvider(provider);
2148 if (p == null) {
2149 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2150 }
2151 LocationProviderImpl.removeProvider(p);
2152 updateProviders();
2153 }
2154
2155 public void setTestProviderLocation(String provider, Location loc) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002156 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002157 if (LocationProviderImpl.getProvider(provider) == null) {
2158 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2159 }
2160 mMockProviderLocation.put(provider, loc);
2161 }
2162
2163 public void clearTestProviderLocation(String provider) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002164 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002165 if (LocationProviderImpl.getProvider(provider) == null) {
2166 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2167 }
2168 mMockProviderLocation.remove(provider);
2169 }
2170
2171 public void setTestProviderEnabled(String provider, boolean enabled) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002172 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002173 if (LocationProviderImpl.getProvider(provider) == null) {
2174 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2175 }
2176 if (enabled) {
2177 mEnabledProviders.add(provider);
2178 mDisabledProviders.remove(provider);
2179 } else {
2180 mEnabledProviders.remove(provider);
2181 mDisabledProviders.add(provider);
2182 }
2183 updateProviders();
2184 }
2185
2186 public void clearTestProviderEnabled(String provider) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002187 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002188 if (LocationProviderImpl.getProvider(provider) == null) {
2189 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2190 }
2191 mEnabledProviders.remove(provider);
2192 mDisabledProviders.remove(provider);
2193 updateProviders();
2194 }
2195
2196 public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002197 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002198 if (LocationProviderImpl.getProvider(provider) == null) {
2199 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2200 }
2201 mMockProviderStatus.put(provider, new Integer(status));
2202 mMockProviderStatusExtras.put(provider, extras);
2203 mMockProviderStatusUpdateTime.put(provider, new Long(updateTime));
2204 }
2205
2206 public void clearTestProviderStatus(String provider) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08002207 checkMockPermissions();
The Android Open Source Project54b6cfa2008-10-21 07:00:00 -07002208 if (LocationProviderImpl.getProvider(provider) == null) {
2209 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
2210 }
2211 mMockProviderStatus.remove(provider);
2212 mMockProviderStatusExtras.remove(provider);
2213 mMockProviderStatusUpdateTime.remove(provider);
2214 }
2215
2216 private void log(String log) {
2217 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2218 Log.d(TAG, log);
2219 }
2220 }
2221}
2222