/* * 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 mListeners; private static final String LOG_TAG = TrackerActivity.LOG_TAG; // controls which location providers to track private Set 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 locationProviders = lm.getAllProviders(); mListeners = new ArrayList( 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 getTrackedProviders() { Set providerSet = new HashSet(); 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 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(); } } } }