Home | History | Annotate | Download | only in locationtracker
      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