blob: e2332bfe4b8aa054ba7e85e5c098d97d4bd9fb25 [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.locationtracker;
import com.android.locationtracker.data.TrackerDataHelper;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.telephony.CellLocation;
import android.telephony.PhoneStateListener;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.gsm.GsmCellLocation;
import android.util.Log;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Location Tracking service
*
* Records location updates for all registered location providers, and cell
* location updates
*/
public class TrackerService extends Service {
private List<LocationTrackingListener> mListeners;
private static final String LOG_TAG = TrackerActivity.LOG_TAG;
// controls which location providers to track
private Set<String> mTrackedProviders;
private TrackerDataHelper mTrackerData;
private TelephonyManager mTelephonyManager;
private Location mNetworkLocation;
// Handlers and Receivers for phone and network state
private NetworkStateBroadcastReceiver mNetwork;
private static final String CELL_PROVIDER_TAG = "cell";
// signal strength updates
private static final String SIGNAL_PROVIDER_TAG = "signal";
private static final String WIFI_PROVIDER_TAG = "wifi";
// tracking tag for data connectivity issues
private static final String DATA_CONN_PROVIDER_TAG = "data";
// preference constants
private static final String MIN_TIME_PREF = "mintime_preference";
private static final String MIN_DIS_PREF = "mindistance_preference";
private static final String GPS_PREF = "gps_preference";
private static final String NETWORK_PREF = "network_preference";
private static final String SIGNAL_PREF = "signal_preference";
private static final String DEBUG_PREF = "advanced_log_preference";
private PreferenceListener mPrefListener;
public TrackerService() {
}
@Override
public IBinder onBind(Intent intent) {
// ignore - nothing to do
return null;
}
/**
* registers location listeners
*
* @param intent
* @param startId
*/
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
mNetworkLocation = null;
initLocationListeners();
Toast.makeText(this, "Tracking service started", Toast.LENGTH_SHORT);
}
private synchronized void initLocationListeners() {
mTrackerData = new TrackerDataHelper(this);
LocationManager lm = getLocationManager();
mTrackedProviders = getTrackedProviders();
List<String> locationProviders = lm.getAllProviders();
mListeners = new ArrayList<LocationTrackingListener>(
locationProviders.size());
long minUpdateTime = getLocationUpdateTime();
float minDistance = getLocationMinDistance();
for (String providerName : locationProviders) {
if (mTrackedProviders.contains(providerName)) {
Log.d(LOG_TAG, "Adding location listener for provider " +
providerName);
if (doDebugLogging()) {
mTrackerData.writeEntry("init", String.format(
"start listening to %s : %d ms; %f meters",
providerName, minUpdateTime, minDistance));
}
LocationTrackingListener listener =
new LocationTrackingListener();
lm.requestLocationUpdates(providerName, minUpdateTime,
minDistance, listener);
mListeners.add(listener);
}
}
mTelephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
if (doDebugLogging()) {
// register for cell location updates
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CELL_LOCATION);
// Register for Network (Wifi or Mobile) updates
mNetwork = new NetworkStateBroadcastReceiver();
IntentFilter mIntentFilter;
mIntentFilter = new IntentFilter();
mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
mIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
Log.d(LOG_TAG, "registering receiver");
registerReceiver(mNetwork, mIntentFilter);
}
if (trackSignalStrength()) {
mTelephonyManager.listen(mPhoneStateListener,
PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
// register for preference changes, so we can restart listeners on
// pref changes
mPrefListener = new PreferenceListener();
getPreferences().registerOnSharedPreferenceChangeListener(mPrefListener);
}
private Set<String> getTrackedProviders() {
Set<String> providerSet = new HashSet<String>();
if (trackGPS()) {
providerSet.add(LocationManager.GPS_PROVIDER);
}
if (trackNetwork()) {
providerSet.add(LocationManager.NETWORK_PROVIDER);
}
return providerSet;
}
private SharedPreferences getPreferences() {
return PreferenceManager.getDefaultSharedPreferences(this);
}
private boolean trackNetwork() {
return getPreferences().getBoolean(NETWORK_PREF, true);
}
private boolean trackGPS() {
return getPreferences().getBoolean(GPS_PREF, true);
}
private boolean doDebugLogging() {
return getPreferences().getBoolean(DEBUG_PREF, false);
}
private boolean trackSignalStrength() {
return getPreferences().getBoolean(SIGNAL_PREF, false);
}
private float getLocationMinDistance() {
try {
String disString = getPreferences().getString(MIN_DIS_PREF, "0");
return Float.parseFloat(disString);
}
catch (NumberFormatException e) {
Log.e(LOG_TAG, "Invalid preference for location min distance", e);
}
return 0;
}
private long getLocationUpdateTime() {
try {
String timeString = getPreferences().getString(MIN_TIME_PREF, "0");
long secondsTime = Long.valueOf(timeString);
return secondsTime * 1000;
}
catch (NumberFormatException e) {
Log.e(LOG_TAG, "Invalid preference for location min time", e);
}
return 0;
}
/**
* Shuts down this service
*/
@Override
public void onDestroy() {
super.onDestroy();
Log.d(LOG_TAG, "Removing location listeners");
stopListeners();
Toast.makeText(this, "Tracking service stopped", Toast.LENGTH_SHORT);
}
/**
* De-registers all location listeners, closes persistent storage
*/
protected synchronized void stopListeners() {
LocationManager lm = getLocationManager();
if (mListeners != null) {
for (LocationTrackingListener listener : mListeners) {
lm.removeUpdates(listener);
}
mListeners.clear();
}
mListeners = null;
// stop cell state listener
if (mTelephonyManager != null) {
mTelephonyManager.listen(mPhoneStateListener, 0);
}
// stop network/wifi listener
if (mNetwork != null) {
unregisterReceiver(mNetwork);
}
mNetwork = null;
mTrackerData = null;
if (mPrefListener != null) {
getPreferences().unregisterOnSharedPreferenceChangeListener(mPrefListener);
mPrefListener = null;
}
}
private LocationManager getLocationManager() {
return (LocationManager) getSystemService(Context.LOCATION_SERVICE);
}
/**
* Determine the current distance from given location to the last
* approximated network location
*
* @param location - new location
*
* @return float distance in meters
*/
private synchronized float getDistanceFromNetwork(Location location) {
float value = 0;
if (mNetworkLocation != null) {
value = location.distanceTo(mNetworkLocation);
}
if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) {
mNetworkLocation = location;
}
return value;
}
private class LocationTrackingListener implements LocationListener {
/**
* Writes details of location update to tracking file, including
* recording the distance between this location update and the last
* network location update
*
* @param location - new location
*/
public void onLocationChanged(Location location) {
if (location == null) {
return;
}
float distance = getDistanceFromNetwork(location);
mTrackerData.writeEntry(location, distance);
}
/**
* Writes update to tracking file
*
* @param provider - name of disabled provider
*/
public void onProviderDisabled(String provider) {
if (doDebugLogging()) {
mTrackerData.writeEntry(provider, "provider disabled");
}
}
/**
* Writes update to tracking file
*
* @param provider - name of enabled provider
*/
public void onProviderEnabled(String provider) {
if (doDebugLogging()) {
mTrackerData.writeEntry(provider, "provider enabled");
}
}
/**
* Writes update to tracking file
*
* @param provider - name of provider whose status changed
* @param status - new status
* @param extras - optional set of extra status messages
*/
public void onStatusChanged(String provider, int status, Bundle extras) {
if (doDebugLogging()) {
mTrackerData.writeEntry(provider, "status change: " + status);
}
}
}
PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onCellLocationChanged(CellLocation location) {
try {
if (location instanceof GsmCellLocation) {
GsmCellLocation cellLocation = (GsmCellLocation)location;
String updateMsg = "cid=" + cellLocation.getCid() +
", lac=" + cellLocation.getLac();
mTrackerData.writeEntry(CELL_PROVIDER_TAG, updateMsg);
} else if (location instanceof CdmaCellLocation) {
CdmaCellLocation cellLocation = (CdmaCellLocation)location;
String updateMsg = "BID=" + cellLocation.getBaseStationId() +
", SID=" + cellLocation.getSystemId() +
", NID=" + cellLocation.getNetworkId() +
", lat=" + cellLocation.getBaseStationLatitude() +
", long=" + cellLocation.getBaseStationLongitude() +
", SID=" + cellLocation.getSystemId() +
", NID=" + cellLocation.getNetworkId();
mTrackerData.writeEntry(CELL_PROVIDER_TAG, updateMsg);
}
} catch (Exception e) {
Log.e(LOG_TAG, "Exception in CellStateHandler.handleMessage:", e);
}
}
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
String updateMsg = "cdma dBM=" + signalStrength.getCdmaDbm();
mTrackerData.writeEntry(SIGNAL_PROVIDER_TAG, updateMsg);
} else if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
String updateMsg = "gsm signal=" + signalStrength.getGsmSignalStrength();
mTrackerData.writeEntry(SIGNAL_PROVIDER_TAG, updateMsg);
}
}
};
/**
* Listener + recorder for mobile or wifi updates
*/
private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
WifiManager wifiManager =
(WifiManager) context.getSystemService(Context.WIFI_SERVICE);
List<ScanResult> wifiScanResults = wifiManager.getScanResults();
String updateMsg = "num scan results=" +
(wifiScanResults == null ? "0" : wifiScanResults.size());
mTrackerData.writeEntry(WIFI_PROVIDER_TAG, updateMsg);
} else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
String updateMsg;
boolean noConnectivity =
intent.getBooleanExtra(
ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
if (noConnectivity) {
updateMsg = "no connectivity";
}
else {
updateMsg = "connection available";
}
mTrackerData.writeEntry(DATA_CONN_PROVIDER_TAG, updateMsg);
} else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN);
String stateString = "unknown";
switch (state) {
case WifiManager.WIFI_STATE_DISABLED:
stateString = "disabled";
break;
case WifiManager.WIFI_STATE_DISABLING:
stateString = "disabling";
break;
case WifiManager.WIFI_STATE_ENABLED:
stateString = "enabled";
break;
case WifiManager.WIFI_STATE_ENABLING:
stateString = "enabling";
break;
}
mTrackerData.writeEntry(WIFI_PROVIDER_TAG,
"state = " + stateString);
}
}
}
private class PreferenceListener implements OnSharedPreferenceChangeListener {
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
Log.d(LOG_TAG, "restarting listeners due to preference change");
synchronized (TrackerService.this) {
stopListeners();
initLocationListeners();
}
}
}
}