blob: e4d21558b8f9152b833e5a984562da36f996fb62 [file] [log] [blame]
Mike Lockwoodc9d3be32010-02-22 23:42:02 -05001/*
Mike Lockwood8ab8b412010-02-24 12:59:14 -05002 * Copyright (C) 2008 The Android Open Source Project
Mike Lockwoodc9d3be32010-02-22 23:42:02 -05003 *
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
Mike Lockwood191d8982010-02-23 07:50:18 -050017package com.android.locationtracker;
Mike Lockwoodc9d3be32010-02-22 23:42:02 -050018
Victoria Lease1e47b322012-10-31 09:25:15 -070019import com.android.locationtracker.data.TrackerDataHelper;
20
Mike Lockwoodc9d3be32010-02-22 23:42:02 -050021import android.app.Service;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.SharedPreferences;
27import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
28import android.location.Location;
29import android.location.LocationListener;
30import android.location.LocationManager;
31import android.net.ConnectivityManager;
32import android.net.wifi.ScanResult;
33import android.net.wifi.WifiManager;
34import android.os.Bundle;
35import android.os.IBinder;
36import android.preference.PreferenceManager;
37import android.telephony.CellLocation;
38import android.telephony.PhoneStateListener;
39import android.telephony.SignalStrength;
40import android.telephony.TelephonyManager;
41import android.telephony.cdma.CdmaCellLocation;
42import android.telephony.gsm.GsmCellLocation;
43import android.util.Log;
44import android.widget.Toast;
45
Victoria Lease1e47b322012-10-31 09:25:15 -070046import java.util.ArrayList;
47import java.util.HashSet;
Mike Lockwoodc9d3be32010-02-22 23:42:02 -050048import java.util.List;
Victoria Lease1e47b322012-10-31 09:25:15 -070049import java.util.Set;
Mike Lockwoodc9d3be32010-02-22 23:42:02 -050050
51/**
52 * Location Tracking service
53 *
54 * Records location updates for all registered location providers, and cell
55 * location updates
56 */
57public class TrackerService extends Service {
58
Victoria Lease1e47b322012-10-31 09:25:15 -070059 private List<LocationTrackingListener> mListeners;
Mike Lockwoodc9d3be32010-02-22 23:42:02 -050060
61 private static final String LOG_TAG = TrackerActivity.LOG_TAG;
62
Victoria Lease1e47b322012-10-31 09:25:15 -070063 // controls which location providers to track
64 private Set<String> mTrackedProviders;
65
Mike Lockwoodc9d3be32010-02-22 23:42:02 -050066 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";
Victoria Lease1e47b322012-10-31 09:25:15 -070082 private static final String MIN_DIS_PREF = "mindistance_preference";
Mike Lockwoodc9d3be32010-02-22 23:42:02 -050083 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
Victoria Lease1e47b322012-10-31 09:25:15 -0700106 public void onStart(Intent intent, int startId) {
107 super.onStart(intent, startId);
Mike Lockwoodc9d3be32010-02-22 23:42:02 -0500108 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
Victoria Lease1e47b322012-10-31 09:25:15 -0700118 mTrackedProviders = getTrackedProviders();
119
120 List<String> locationProviders = lm.getAllProviders();
121 mListeners = new ArrayList<LocationTrackingListener>(
122 locationProviders.size());
123
Mike Lockwoodc9d3be32010-02-22 23:42:02 -0500124 long minUpdateTime = getLocationUpdateTime();
Victoria Lease1e47b322012-10-31 09:25:15 -0700125 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 }
Mike Lockwoodc9d3be32010-02-22 23:42:02 -0500142 }
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
Victoria Lease1e47b322012-10-31 09:25:15 -0700171 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
Mike Lockwoodc9d3be32010-02-22 23:42:02 -0500183 private SharedPreferences getPreferences() {
184 return PreferenceManager.getDefaultSharedPreferences(this);
185 }
186
Victoria Lease1e47b322012-10-31 09:25:15 -0700187 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
Mike Lockwoodc9d3be32010-02-22 23:42:02 -0500195 private boolean doDebugLogging() {
Mike Lockwood27a7a6c2010-03-23 12:27:38 -0400196 return getPreferences().getBoolean(DEBUG_PREF, false);
Mike Lockwoodc9d3be32010-02-22 23:42:02 -0500197 }
198
199 private boolean trackSignalStrength() {
Mike Lockwood27a7a6c2010-03-23 12:27:38 -0400200 return getPreferences().getBoolean(SIGNAL_PREF, false);
Mike Lockwoodc9d3be32010-02-22 23:42:02 -0500201 }
202
Victoria Lease1e47b322012-10-31 09:25:15 -0700203 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
Mike Lockwoodc9d3be32010-02-22 23:42:02 -0500214 private long getLocationUpdateTime() {
215 try {
216 String timeString = getPreferences().getString(MIN_TIME_PREF, "0");
Tobias Thierer28532d02016-04-21 14:52:10 +0100217 long secondsTime = Long.parseLong(timeString);
Mike Lockwoodc9d3be32010-02-22 23:42:02 -0500218 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();
Victoria Lease1e47b322012-10-31 09:25:15 -0700242 if (mListeners != null) {
243 for (LocationTrackingListener listener : mListeners) {
244 lm.removeUpdates(listener);
245 }
246 mListeners.clear();
Mike Lockwoodc9d3be32010-02-22 23:42:02 -0500247 }
Victoria Lease1e47b322012-10-31 09:25:15 -0700248 mListeners = null;
Mike Lockwoodc9d3be32010-02-22 23:42:02 -0500249
250 // stop cell state listener
251 if (mTelephonyManager != null) {
252 mTelephonyManager.listen(mPhoneStateListener, 0);
Victoria Lease1e47b322012-10-31 09:25:15 -0700253 }
254
Mike Lockwoodc9d3be32010-02-22 23:42:02 -0500255 // 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
Victoria Lease1e47b322012-10-31 09:25:15 -0700321 *
Mike Lockwoodc9d3be32010-02-22 23:42:02 -0500322 * @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 /**
Victoria Lease1e47b322012-10-31 09:25:15 -0700331 * Writes update to tracking file
332 *
Mike Lockwoodc9d3be32010-02-22 23:42:02 -0500333 * @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
Mike Lockwoodc9d3be32010-02-22 23:42:02 -0500369 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}