19d10b341a0ba46f108cb96e46691197d778cbc06San Mehat/*
29d10b341a0ba46f108cb96e46691197d778cbc06San Mehat * Copyright (C) 2008 The Android Open Source Project
39d10b341a0ba46f108cb96e46691197d778cbc06San Mehat *
49d10b341a0ba46f108cb96e46691197d778cbc06San Mehat * Licensed under the Apache License, Version 2.0 (the "License");
59d10b341a0ba46f108cb96e46691197d778cbc06San Mehat * you may not use this file except in compliance with the License.
69d10b341a0ba46f108cb96e46691197d778cbc06San Mehat * You may obtain a copy of the License at
79d10b341a0ba46f108cb96e46691197d778cbc06San Mehat *
89d10b341a0ba46f108cb96e46691197d778cbc06San Mehat *      http://www.apache.org/licenses/LICENSE-2.0
99d10b341a0ba46f108cb96e46691197d778cbc06San Mehat *
109d10b341a0ba46f108cb96e46691197d778cbc06San Mehat * Unless required by applicable law or agreed to in writing, software
119d10b341a0ba46f108cb96e46691197d778cbc06San Mehat * distributed under the License is distributed on an "AS IS" BASIS,
129d10b341a0ba46f108cb96e46691197d778cbc06San Mehat * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139d10b341a0ba46f108cb96e46691197d778cbc06San Mehat * See the License for the specific language governing permissions and
149d10b341a0ba46f108cb96e46691197d778cbc06San Mehat * limitations under the License.
159d10b341a0ba46f108cb96e46691197d778cbc06San Mehat */
169d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
179d10b341a0ba46f108cb96e46691197d778cbc06San Mehatpackage com.android.locationtracker;
189d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1918737845d3c6a60edd6f75ac441a1b3fed6d66a7San Mehatimport com.android.locationtracker.data.TrackerDataHelper;
2018737845d3c6a60edd6f75ac441a1b3fed6d66a7San Mehat
219d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.app.Service;
229d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.content.BroadcastReceiver;
2318737845d3c6a60edd6f75ac441a1b3fed6d66a7San Mehatimport android.content.Context;
2418737845d3c6a60edd6f75ac441a1b3fed6d66a7San Mehatimport android.content.Intent;
2518737845d3c6a60edd6f75ac441a1b3fed6d66a7San Mehatimport android.content.IntentFilter;
269d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.content.SharedPreferences;
279d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.content.SharedPreferences.OnSharedPreferenceChangeListener;
289d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.location.Location;
299d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.location.LocationListener;
309d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.location.LocationManager;
319d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.net.ConnectivityManager;
329d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.net.wifi.ScanResult;
339d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.net.wifi.WifiManager;
349d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.os.Bundle;
359d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.os.IBinder;
369d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.preference.PreferenceManager;
379d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.telephony.CellLocation;
389d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.telephony.PhoneStateListener;
399d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.telephony.SignalStrength;
409d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.telephony.TelephonyManager;
419d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.telephony.cdma.CdmaCellLocation;
429d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.telephony.gsm.GsmCellLocation;
439d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.util.Log;
449d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport android.widget.Toast;
459d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
469d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport java.util.ArrayList;
479d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport java.util.HashSet;
489d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport java.util.List;
499d10b341a0ba46f108cb96e46691197d778cbc06San Mehatimport java.util.Set;
509d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
519d10b341a0ba46f108cb96e46691197d778cbc06San Mehat/**
529d10b341a0ba46f108cb96e46691197d778cbc06San Mehat * Location Tracking service
539d10b341a0ba46f108cb96e46691197d778cbc06San Mehat *
549d10b341a0ba46f108cb96e46691197d778cbc06San Mehat * Records location updates for all registered location providers, and cell
559d10b341a0ba46f108cb96e46691197d778cbc06San Mehat * location updates
569d10b341a0ba46f108cb96e46691197d778cbc06San Mehat */
579d10b341a0ba46f108cb96e46691197d778cbc06San Mehatpublic class TrackerService extends Service {
589d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
599d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private List<LocationTrackingListener> mListeners;
609d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
619d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private static final String LOG_TAG = TrackerActivity.LOG_TAG;
629d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
639d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    // controls which location providers to track
649d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private Set<String> mTrackedProviders;
659d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
669d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private TrackerDataHelper mTrackerData;
679d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
689d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private TelephonyManager mTelephonyManager;
699d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private Location mNetworkLocation;
709d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
719d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    // Handlers and Receivers for phone and network state
729d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private NetworkStateBroadcastReceiver mNetwork;
739d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private static final String CELL_PROVIDER_TAG = "cell";
749d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    // signal strength updates
759d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private static final String SIGNAL_PROVIDER_TAG = "signal";
769d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private static final String WIFI_PROVIDER_TAG = "wifi";
779d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    // tracking tag for data connectivity issues
789d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private static final String DATA_CONN_PROVIDER_TAG = "data";
799d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
809d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    // preference constants
819d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private static final String MIN_TIME_PREF = "mintime_preference";
829d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private static final String MIN_DIS_PREF = "mindistance_preference";
839d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private static final String GPS_PREF = "gps_preference";
849d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private static final String NETWORK_PREF = "network_preference";
859d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private static final String SIGNAL_PREF = "signal_preference";
869d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private static final String DEBUG_PREF = "advanced_log_preference";
879d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
889d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private PreferenceListener mPrefListener;
899d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
909d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    public TrackerService() {
919d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    }
929d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
939d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    @Override
949d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    public IBinder onBind(Intent intent) {
959d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        // ignore - nothing to do
969d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        return null;
979d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    }
989d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
999d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    /**
1009d10b341a0ba46f108cb96e46691197d778cbc06San Mehat     * registers location listeners
1019d10b341a0ba46f108cb96e46691197d778cbc06San Mehat     *
1029d10b341a0ba46f108cb96e46691197d778cbc06San Mehat     * @param intent
1039d10b341a0ba46f108cb96e46691197d778cbc06San Mehat     * @param startId
1049d10b341a0ba46f108cb96e46691197d778cbc06San Mehat     */
1059d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    @Override
1069d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    public void onStart(Intent intent, int startId) {
1079d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        super.onStart(intent, startId);
1089d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        mNetworkLocation = null;
1099d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1109d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        initLocationListeners();
1119d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        Toast.makeText(this, "Tracking service started", Toast.LENGTH_SHORT);
1129d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    }
1139d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1149d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private synchronized void initLocationListeners() {
1159d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        mTrackerData = new TrackerDataHelper(this);
1169d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        LocationManager lm = getLocationManager();
1179d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1189d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        mTrackedProviders = getTrackedProviders();
1199d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1209d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        List<String> locationProviders = lm.getAllProviders();
1219d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        mListeners = new ArrayList<LocationTrackingListener>(
1229d10b341a0ba46f108cb96e46691197d778cbc06San Mehat                locationProviders.size());
1239d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1249d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        long minUpdateTime = getLocationUpdateTime();
1259d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        float minDistance = getLocationMinDistance();
1269d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1279d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        for (String providerName : locationProviders) {
1289d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            if (mTrackedProviders.contains(providerName)) {
1299d10b341a0ba46f108cb96e46691197d778cbc06San Mehat                Log.d(LOG_TAG, "Adding location listener for provider " +
1309d10b341a0ba46f108cb96e46691197d778cbc06San Mehat                        providerName);
1319d10b341a0ba46f108cb96e46691197d778cbc06San Mehat                if (doDebugLogging()) {
1329d10b341a0ba46f108cb96e46691197d778cbc06San Mehat                    mTrackerData.writeEntry("init", String.format(
1339d10b341a0ba46f108cb96e46691197d778cbc06San Mehat                            "start listening to %s : %d ms; %f meters",
1349d10b341a0ba46f108cb96e46691197d778cbc06San Mehat                            providerName, minUpdateTime, minDistance));
1359d10b341a0ba46f108cb96e46691197d778cbc06San Mehat                }
1369d10b341a0ba46f108cb96e46691197d778cbc06San Mehat                LocationTrackingListener listener =
1379d10b341a0ba46f108cb96e46691197d778cbc06San Mehat                    new LocationTrackingListener();
1389d10b341a0ba46f108cb96e46691197d778cbc06San Mehat                lm.requestLocationUpdates(providerName, minUpdateTime,
1399d10b341a0ba46f108cb96e46691197d778cbc06San Mehat                        minDistance, listener);
1409d10b341a0ba46f108cb96e46691197d778cbc06San Mehat                mListeners.add(listener);
1419d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            }
1429d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        }
1439d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        mTelephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
1449d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1459d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        if (doDebugLogging()) {
1469d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            // register for cell location updates
1479d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CELL_LOCATION);
1489d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1499d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            // Register for Network (Wifi or Mobile) updates
1509d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            mNetwork = new NetworkStateBroadcastReceiver();
1519d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            IntentFilter mIntentFilter;
1529d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            mIntentFilter = new IntentFilter();
1539d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
1549d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
1559d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            mIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
1569d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            Log.d(LOG_TAG, "registering receiver");
1579d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            registerReceiver(mNetwork, mIntentFilter);
1589d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        }
15918737845d3c6a60edd6f75ac441a1b3fed6d66a7San Mehat
1609d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        if (trackSignalStrength()) {
1619d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            mTelephonyManager.listen(mPhoneStateListener,
1629d10b341a0ba46f108cb96e46691197d778cbc06San Mehat                    PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
16318737845d3c6a60edd6f75ac441a1b3fed6d66a7San Mehat        }
1649d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1659d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        // register for preference changes, so we can restart listeners on
1669d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        // pref changes
1679d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        mPrefListener = new PreferenceListener();
1689d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        getPreferences().registerOnSharedPreferenceChangeListener(mPrefListener);
1699d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    }
1709d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1719d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private Set<String> getTrackedProviders() {
1729d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        Set<String> providerSet = new HashSet<String>();
1739d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1749d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        if (trackGPS()) {
1759d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            providerSet.add(LocationManager.GPS_PROVIDER);
1769d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        }
1779d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        if (trackNetwork()) {
1789d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            providerSet.add(LocationManager.NETWORK_PROVIDER);
1799d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        }
1809d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        return providerSet;
1819d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    }
1829d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1839d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private SharedPreferences getPreferences() {
1849d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        return PreferenceManager.getDefaultSharedPreferences(this);
1859d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    }
1869d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1879d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private boolean trackNetwork() {
1889d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        return getPreferences().getBoolean(NETWORK_PREF, true);
1899d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    }
1909d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1919d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private boolean trackGPS() {
1929d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        return getPreferences().getBoolean(GPS_PREF, true);
1939d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    }
1949d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1959d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private boolean doDebugLogging() {
1969d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        return getPreferences().getBoolean(DEBUG_PREF, false);
1979d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    }
1989d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
1999d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private boolean trackSignalStrength() {
2009d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        return getPreferences().getBoolean(SIGNAL_PREF, false);
2019d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    }
2029d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
2039d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private float getLocationMinDistance() {
2049d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        try {
2059d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            String disString = getPreferences().getString(MIN_DIS_PREF, "0");
2069d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            return Float.parseFloat(disString);
2079d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        }
2089d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        catch (NumberFormatException e) {
2099d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            Log.e(LOG_TAG, "Invalid preference for location min distance", e);
2109d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        }
2119d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        return 0;
2129d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    }
2139d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
2149d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    private long getLocationUpdateTime() {
2159d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        try {
2169d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            String timeString = getPreferences().getString(MIN_TIME_PREF, "0");
2179d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            long secondsTime = Long.valueOf(timeString);
2189d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            return secondsTime * 1000;
2199d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        }
2209d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        catch (NumberFormatException e) {
2219d10b341a0ba46f108cb96e46691197d778cbc06San Mehat            Log.e(LOG_TAG, "Invalid preference for location min time", e);
2229d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        }
2239d10b341a0ba46f108cb96e46691197d778cbc06San Mehat        return 0;
2249d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    }
2259d10b341a0ba46f108cb96e46691197d778cbc06San Mehat
2269d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    /**
2279d10b341a0ba46f108cb96e46691197d778cbc06San Mehat     * Shuts down this service
2289d10b341a0ba46f108cb96e46691197d778cbc06San Mehat     */
2299d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    @Override
2309d10b341a0ba46f108cb96e46691197d778cbc06San Mehat    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