Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 1 | /* |
Mike Lockwood | 8ab8b41 | 2010-02-24 12:59:14 -0500 | [diff] [blame] | 2 | * Copyright (C) 2008 The Android Open Source Project |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 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 | |
Mike Lockwood | 191d898 | 2010-02-23 07:50:18 -0500 | [diff] [blame] | 17 | package com.android.locationtracker; |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 18 | |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 19 | import com.android.locationtracker.data.TrackerDataHelper; |
| 20 | |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 21 | import android.app.Service; |
| 22 | import android.content.BroadcastReceiver; |
| 23 | import android.content.Context; |
| 24 | import android.content.Intent; |
| 25 | import android.content.IntentFilter; |
| 26 | import android.content.SharedPreferences; |
| 27 | import android.content.SharedPreferences.OnSharedPreferenceChangeListener; |
| 28 | import android.location.Location; |
| 29 | import android.location.LocationListener; |
| 30 | import android.location.LocationManager; |
| 31 | import android.net.ConnectivityManager; |
| 32 | import android.net.wifi.ScanResult; |
| 33 | import android.net.wifi.WifiManager; |
| 34 | import android.os.Bundle; |
| 35 | import android.os.IBinder; |
| 36 | import android.preference.PreferenceManager; |
| 37 | import android.telephony.CellLocation; |
| 38 | import android.telephony.PhoneStateListener; |
| 39 | import android.telephony.SignalStrength; |
| 40 | import android.telephony.TelephonyManager; |
| 41 | import android.telephony.cdma.CdmaCellLocation; |
| 42 | import android.telephony.gsm.GsmCellLocation; |
| 43 | import android.util.Log; |
| 44 | import android.widget.Toast; |
| 45 | |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 46 | import java.util.ArrayList; |
| 47 | import java.util.HashSet; |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 48 | import java.util.List; |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 49 | import java.util.Set; |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 50 | |
| 51 | /** |
| 52 | * Location Tracking service |
| 53 | * |
| 54 | * Records location updates for all registered location providers, and cell |
| 55 | * location updates |
| 56 | */ |
| 57 | public class TrackerService extends Service { |
| 58 | |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 59 | private List<LocationTrackingListener> mListeners; |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 60 | |
| 61 | private static final String LOG_TAG = TrackerActivity.LOG_TAG; |
| 62 | |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 63 | // controls which location providers to track |
| 64 | private Set<String> mTrackedProviders; |
| 65 | |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 66 | private TrackerDataHelper mTrackerData; |
| 67 | |
| 68 | private TelephonyManager mTelephonyManager; |
| 69 | private Location mNetworkLocation; |
| 70 | |
| 71 | // Handlers and Receivers for phone and network state |
| 72 | private NetworkStateBroadcastReceiver mNetwork; |
| 73 | private static final String CELL_PROVIDER_TAG = "cell"; |
| 74 | // signal strength updates |
| 75 | private static final String SIGNAL_PROVIDER_TAG = "signal"; |
| 76 | private static final String WIFI_PROVIDER_TAG = "wifi"; |
| 77 | // tracking tag for data connectivity issues |
| 78 | private static final String DATA_CONN_PROVIDER_TAG = "data"; |
| 79 | |
| 80 | // preference constants |
| 81 | private static final String MIN_TIME_PREF = "mintime_preference"; |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 82 | private static final String MIN_DIS_PREF = "mindistance_preference"; |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 83 | private static final String GPS_PREF = "gps_preference"; |
| 84 | private static final String NETWORK_PREF = "network_preference"; |
| 85 | private static final String SIGNAL_PREF = "signal_preference"; |
| 86 | private static final String DEBUG_PREF = "advanced_log_preference"; |
| 87 | |
| 88 | private PreferenceListener mPrefListener; |
| 89 | |
| 90 | public TrackerService() { |
| 91 | } |
| 92 | |
| 93 | @Override |
| 94 | public IBinder onBind(Intent intent) { |
| 95 | // ignore - nothing to do |
| 96 | return null; |
| 97 | } |
| 98 | |
| 99 | /** |
| 100 | * registers location listeners |
| 101 | * |
| 102 | * @param intent |
| 103 | * @param startId |
| 104 | */ |
| 105 | @Override |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 106 | public void onStart(Intent intent, int startId) { |
| 107 | super.onStart(intent, startId); |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 108 | mNetworkLocation = null; |
| 109 | |
| 110 | initLocationListeners(); |
| 111 | Toast.makeText(this, "Tracking service started", Toast.LENGTH_SHORT); |
| 112 | } |
| 113 | |
| 114 | private synchronized void initLocationListeners() { |
| 115 | mTrackerData = new TrackerDataHelper(this); |
| 116 | LocationManager lm = getLocationManager(); |
| 117 | |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 118 | mTrackedProviders = getTrackedProviders(); |
| 119 | |
| 120 | List<String> locationProviders = lm.getAllProviders(); |
| 121 | mListeners = new ArrayList<LocationTrackingListener>( |
| 122 | locationProviders.size()); |
| 123 | |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 124 | long minUpdateTime = getLocationUpdateTime(); |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 125 | float minDistance = getLocationMinDistance(); |
| 126 | |
| 127 | for (String providerName : locationProviders) { |
| 128 | if (mTrackedProviders.contains(providerName)) { |
| 129 | Log.d(LOG_TAG, "Adding location listener for provider " + |
| 130 | providerName); |
| 131 | if (doDebugLogging()) { |
| 132 | mTrackerData.writeEntry("init", String.format( |
| 133 | "start listening to %s : %d ms; %f meters", |
| 134 | providerName, minUpdateTime, minDistance)); |
| 135 | } |
| 136 | LocationTrackingListener listener = |
| 137 | new LocationTrackingListener(); |
| 138 | lm.requestLocationUpdates(providerName, minUpdateTime, |
| 139 | minDistance, listener); |
| 140 | mListeners.add(listener); |
| 141 | } |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 142 | } |
| 143 | mTelephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); |
| 144 | |
| 145 | if (doDebugLogging()) { |
| 146 | // register for cell location updates |
| 147 | mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CELL_LOCATION); |
| 148 | |
| 149 | // Register for Network (Wifi or Mobile) updates |
| 150 | mNetwork = new NetworkStateBroadcastReceiver(); |
| 151 | IntentFilter mIntentFilter; |
| 152 | mIntentFilter = new IntentFilter(); |
| 153 | mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); |
| 154 | mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); |
| 155 | mIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); |
| 156 | Log.d(LOG_TAG, "registering receiver"); |
| 157 | registerReceiver(mNetwork, mIntentFilter); |
| 158 | } |
| 159 | |
| 160 | if (trackSignalStrength()) { |
| 161 | mTelephonyManager.listen(mPhoneStateListener, |
| 162 | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS); |
| 163 | } |
| 164 | |
| 165 | // register for preference changes, so we can restart listeners on |
| 166 | // pref changes |
| 167 | mPrefListener = new PreferenceListener(); |
| 168 | getPreferences().registerOnSharedPreferenceChangeListener(mPrefListener); |
| 169 | } |
| 170 | |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 171 | private Set<String> getTrackedProviders() { |
| 172 | Set<String> providerSet = new HashSet<String>(); |
| 173 | |
| 174 | if (trackGPS()) { |
| 175 | providerSet.add(LocationManager.GPS_PROVIDER); |
| 176 | } |
| 177 | if (trackNetwork()) { |
| 178 | providerSet.add(LocationManager.NETWORK_PROVIDER); |
| 179 | } |
| 180 | return providerSet; |
| 181 | } |
| 182 | |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 183 | private SharedPreferences getPreferences() { |
| 184 | return PreferenceManager.getDefaultSharedPreferences(this); |
| 185 | } |
| 186 | |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 187 | private boolean trackNetwork() { |
| 188 | return getPreferences().getBoolean(NETWORK_PREF, true); |
| 189 | } |
| 190 | |
| 191 | private boolean trackGPS() { |
| 192 | return getPreferences().getBoolean(GPS_PREF, true); |
| 193 | } |
| 194 | |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 195 | private boolean doDebugLogging() { |
Mike Lockwood | 27a7a6c | 2010-03-23 12:27:38 -0400 | [diff] [blame] | 196 | return getPreferences().getBoolean(DEBUG_PREF, false); |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 197 | } |
| 198 | |
| 199 | private boolean trackSignalStrength() { |
Mike Lockwood | 27a7a6c | 2010-03-23 12:27:38 -0400 | [diff] [blame] | 200 | return getPreferences().getBoolean(SIGNAL_PREF, false); |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 201 | } |
| 202 | |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 203 | private float getLocationMinDistance() { |
| 204 | try { |
| 205 | String disString = getPreferences().getString(MIN_DIS_PREF, "0"); |
| 206 | return Float.parseFloat(disString); |
| 207 | } |
| 208 | catch (NumberFormatException e) { |
| 209 | Log.e(LOG_TAG, "Invalid preference for location min distance", e); |
| 210 | } |
| 211 | return 0; |
| 212 | } |
| 213 | |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 214 | private long getLocationUpdateTime() { |
| 215 | try { |
| 216 | String timeString = getPreferences().getString(MIN_TIME_PREF, "0"); |
| 217 | long secondsTime = Long.valueOf(timeString); |
| 218 | return secondsTime * 1000; |
| 219 | } |
| 220 | catch (NumberFormatException e) { |
| 221 | Log.e(LOG_TAG, "Invalid preference for location min time", e); |
| 222 | } |
| 223 | return 0; |
| 224 | } |
| 225 | |
| 226 | /** |
| 227 | * Shuts down this service |
| 228 | */ |
| 229 | @Override |
| 230 | public void onDestroy() { |
| 231 | super.onDestroy(); |
| 232 | Log.d(LOG_TAG, "Removing location listeners"); |
| 233 | stopListeners(); |
| 234 | Toast.makeText(this, "Tracking service stopped", Toast.LENGTH_SHORT); |
| 235 | } |
| 236 | |
| 237 | /** |
| 238 | * De-registers all location listeners, closes persistent storage |
| 239 | */ |
| 240 | protected synchronized void stopListeners() { |
| 241 | LocationManager lm = getLocationManager(); |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 242 | if (mListeners != null) { |
| 243 | for (LocationTrackingListener listener : mListeners) { |
| 244 | lm.removeUpdates(listener); |
| 245 | } |
| 246 | mListeners.clear(); |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 247 | } |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 248 | mListeners = null; |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 249 | |
| 250 | // stop cell state listener |
| 251 | if (mTelephonyManager != null) { |
| 252 | mTelephonyManager.listen(mPhoneStateListener, 0); |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 253 | } |
| 254 | |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 255 | // stop network/wifi listener |
| 256 | if (mNetwork != null) { |
| 257 | unregisterReceiver(mNetwork); |
| 258 | } |
| 259 | mNetwork = null; |
| 260 | |
| 261 | mTrackerData = null; |
| 262 | if (mPrefListener != null) { |
| 263 | getPreferences().unregisterOnSharedPreferenceChangeListener(mPrefListener); |
| 264 | mPrefListener = null; |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | private LocationManager getLocationManager() { |
| 269 | return (LocationManager) getSystemService(Context.LOCATION_SERVICE); |
| 270 | } |
| 271 | |
| 272 | /** |
| 273 | * Determine the current distance from given location to the last |
| 274 | * approximated network location |
| 275 | * |
| 276 | * @param location - new location |
| 277 | * |
| 278 | * @return float distance in meters |
| 279 | */ |
| 280 | private synchronized float getDistanceFromNetwork(Location location) { |
| 281 | float value = 0; |
| 282 | if (mNetworkLocation != null) { |
| 283 | value = location.distanceTo(mNetworkLocation); |
| 284 | } |
| 285 | if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) { |
| 286 | mNetworkLocation = location; |
| 287 | } |
| 288 | return value; |
| 289 | } |
| 290 | |
| 291 | private class LocationTrackingListener implements LocationListener { |
| 292 | |
| 293 | /** |
| 294 | * Writes details of location update to tracking file, including |
| 295 | * recording the distance between this location update and the last |
| 296 | * network location update |
| 297 | * |
| 298 | * @param location - new location |
| 299 | */ |
| 300 | public void onLocationChanged(Location location) { |
| 301 | if (location == null) { |
| 302 | return; |
| 303 | } |
| 304 | float distance = getDistanceFromNetwork(location); |
| 305 | mTrackerData.writeEntry(location, distance); |
| 306 | } |
| 307 | |
| 308 | /** |
| 309 | * Writes update to tracking file |
| 310 | * |
| 311 | * @param provider - name of disabled provider |
| 312 | */ |
| 313 | public void onProviderDisabled(String provider) { |
| 314 | if (doDebugLogging()) { |
| 315 | mTrackerData.writeEntry(provider, "provider disabled"); |
| 316 | } |
| 317 | } |
| 318 | |
| 319 | /** |
| 320 | * Writes update to tracking file |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 321 | * |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 322 | * @param provider - name of enabled provider |
| 323 | */ |
| 324 | public void onProviderEnabled(String provider) { |
| 325 | if (doDebugLogging()) { |
| 326 | mTrackerData.writeEntry(provider, "provider enabled"); |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | /** |
Victoria Lease | 1e47b32 | 2012-10-31 09:25:15 -0700 | [diff] [blame] | 331 | * Writes update to tracking file |
| 332 | * |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 333 | * @param provider - name of provider whose status changed |
| 334 | * @param status - new status |
| 335 | * @param extras - optional set of extra status messages |
| 336 | */ |
| 337 | public void onStatusChanged(String provider, int status, Bundle extras) { |
| 338 | if (doDebugLogging()) { |
| 339 | mTrackerData.writeEntry(provider, "status change: " + status); |
| 340 | } |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | PhoneStateListener mPhoneStateListener = new PhoneStateListener() { |
| 345 | @Override |
| 346 | public void onCellLocationChanged(CellLocation location) { |
| 347 | try { |
| 348 | if (location instanceof GsmCellLocation) { |
| 349 | GsmCellLocation cellLocation = (GsmCellLocation)location; |
| 350 | String updateMsg = "cid=" + cellLocation.getCid() + |
| 351 | ", lac=" + cellLocation.getLac(); |
| 352 | mTrackerData.writeEntry(CELL_PROVIDER_TAG, updateMsg); |
| 353 | } else if (location instanceof CdmaCellLocation) { |
| 354 | CdmaCellLocation cellLocation = (CdmaCellLocation)location; |
| 355 | String updateMsg = "BID=" + cellLocation.getBaseStationId() + |
| 356 | ", SID=" + cellLocation.getSystemId() + |
| 357 | ", NID=" + cellLocation.getNetworkId() + |
| 358 | ", lat=" + cellLocation.getBaseStationLatitude() + |
| 359 | ", long=" + cellLocation.getBaseStationLongitude() + |
| 360 | ", SID=" + cellLocation.getSystemId() + |
| 361 | ", NID=" + cellLocation.getNetworkId(); |
| 362 | mTrackerData.writeEntry(CELL_PROVIDER_TAG, updateMsg); |
| 363 | } |
| 364 | } catch (Exception e) { |
| 365 | Log.e(LOG_TAG, "Exception in CellStateHandler.handleMessage:", e); |
| 366 | } |
| 367 | } |
| 368 | |
Mike Lockwood | c9d3be3 | 2010-02-22 23:42:02 -0500 | [diff] [blame] | 369 | public void onSignalStrengthsChanged(SignalStrength signalStrength) { |
| 370 | if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { |
| 371 | String updateMsg = "cdma dBM=" + signalStrength.getCdmaDbm(); |
| 372 | mTrackerData.writeEntry(SIGNAL_PROVIDER_TAG, updateMsg); |
| 373 | } else if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) { |
| 374 | String updateMsg = "gsm signal=" + signalStrength.getGsmSignalStrength(); |
| 375 | mTrackerData.writeEntry(SIGNAL_PROVIDER_TAG, updateMsg); |
| 376 | } |
| 377 | } |
| 378 | }; |
| 379 | |
| 380 | /** |
| 381 | * Listener + recorder for mobile or wifi updates |
| 382 | */ |
| 383 | private class NetworkStateBroadcastReceiver extends BroadcastReceiver { |
| 384 | @Override |
| 385 | public void onReceive(Context context, Intent intent) { |
| 386 | String action = intent.getAction(); |
| 387 | |
| 388 | if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { |
| 389 | WifiManager wifiManager = |
| 390 | (WifiManager) context.getSystemService(Context.WIFI_SERVICE); |
| 391 | List<ScanResult> wifiScanResults = wifiManager.getScanResults(); |
| 392 | String updateMsg = "num scan results=" + |
| 393 | (wifiScanResults == null ? "0" : wifiScanResults.size()); |
| 394 | mTrackerData.writeEntry(WIFI_PROVIDER_TAG, updateMsg); |
| 395 | |
| 396 | } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { |
| 397 | String updateMsg; |
| 398 | boolean noConnectivity = |
| 399 | intent.getBooleanExtra( |
| 400 | ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); |
| 401 | if (noConnectivity) { |
| 402 | updateMsg = "no connectivity"; |
| 403 | } |
| 404 | else { |
| 405 | updateMsg = "connection available"; |
| 406 | } |
| 407 | mTrackerData.writeEntry(DATA_CONN_PROVIDER_TAG, updateMsg); |
| 408 | |
| 409 | } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { |
| 410 | int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, |
| 411 | WifiManager.WIFI_STATE_UNKNOWN); |
| 412 | |
| 413 | String stateString = "unknown"; |
| 414 | switch (state) { |
| 415 | case WifiManager.WIFI_STATE_DISABLED: |
| 416 | stateString = "disabled"; |
| 417 | break; |
| 418 | case WifiManager.WIFI_STATE_DISABLING: |
| 419 | stateString = "disabling"; |
| 420 | break; |
| 421 | case WifiManager.WIFI_STATE_ENABLED: |
| 422 | stateString = "enabled"; |
| 423 | break; |
| 424 | case WifiManager.WIFI_STATE_ENABLING: |
| 425 | stateString = "enabling"; |
| 426 | break; |
| 427 | } |
| 428 | mTrackerData.writeEntry(WIFI_PROVIDER_TAG, |
| 429 | "state = " + stateString); |
| 430 | } |
| 431 | } |
| 432 | } |
| 433 | |
| 434 | private class PreferenceListener implements OnSharedPreferenceChangeListener { |
| 435 | |
| 436 | public void onSharedPreferenceChanged( |
| 437 | SharedPreferences sharedPreferences, String key) { |
| 438 | Log.d(LOG_TAG, "restarting listeners due to preference change"); |
| 439 | synchronized (TrackerService.this) { |
| 440 | stopListeners(); |
| 441 | initLocationListeners(); |
| 442 | } |
| 443 | } |
| 444 | } |
| 445 | } |