1 /* 2 * Copyright (C) 2008 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 17 package com.android.locationtracker; 18 19 import com.android.locationtracker.data.TrackerDataHelper; 20 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 46 import java.util.ArrayList; 47 import java.util.HashSet; 48 import java.util.List; 49 import java.util.Set; 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 59 private List<LocationTrackingListener> mListeners; 60 61 private static final String LOG_TAG = TrackerActivity.LOG_TAG; 62 63 // controls which location providers to track 64 private Set<String> mTrackedProviders; 65 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"; 82 private static final String MIN_DIS_PREF = "mindistance_preference"; 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 106 public void onStart(Intent intent, int startId) { 107 super.onStart(intent, startId); 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 118 mTrackedProviders = getTrackedProviders(); 119 120 List<String> locationProviders = lm.getAllProviders(); 121 mListeners = new ArrayList<LocationTrackingListener>( 122 locationProviders.size()); 123 124 long minUpdateTime = getLocationUpdateTime(); 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 } 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 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 183 private SharedPreferences getPreferences() { 184 return PreferenceManager.getDefaultSharedPreferences(this); 185 } 186 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 195 private boolean doDebugLogging() { 196 return getPreferences().getBoolean(DEBUG_PREF, false); 197 } 198 199 private boolean trackSignalStrength() { 200 return getPreferences().getBoolean(SIGNAL_PREF, false); 201 } 202 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 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(); 242 if (mListeners != null) { 243 for (LocationTrackingListener listener : mListeners) { 244 lm.removeUpdates(listener); 245 } 246 mListeners.clear(); 247 } 248 mListeners = null; 249 250 // stop cell state listener 251 if (mTelephonyManager != null) { 252 mTelephonyManager.listen(mPhoneStateListener, 0); 253 } 254 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 321 * 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 /** 331 * Writes update to tracking file 332 * 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 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 } 446