package net.wigle.wigleandroid.listener; import static android.location.LocationManager.GPS_PROVIDER; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.regex.Matcher; import net.wigle.wigleandroid.model.ConcurrentLinkedHashMap; import net.wigle.wigleandroid.DashboardFragment; import net.wigle.wigleandroid.DatabaseHelper; import net.wigle.wigleandroid.ListFragment; import net.wigle.wigleandroid.MainActivity; import net.wigle.wigleandroid.model.Network; import net.wigle.wigleandroid.NetworkListAdapter; import net.wigle.wigleandroid.model.NetworkType; import net.wigle.wigleandroid.FilterMatcher; import net.wigle.wigleandroid.R; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.location.Location; import android.net.wifi.ScanResult; import android.net.wifi.WifiManager; import android.os.BatteryManager; import android.os.Handler; import android.telephony.CellLocation; import android.telephony.TelephonyManager; import android.telephony.gsm.GsmCellLocation; import android.widget.Toast; import com.google.android.gms.maps.model.LatLng; public class WifiReceiver extends BroadcastReceiver { private MainActivity mainActivity; private final DatabaseHelper dbHelper; private NetworkListAdapter listAdapter; private final SimpleDateFormat timeFormat; private final NumberFormat numberFormat1; private final SsidSpeaker ssidSpeaker; private Handler wifiTimer; private Location prevGpsLocation; private long scanRequestTime = Long.MIN_VALUE; private long lastScanResponseTime = Long.MIN_VALUE; private long lastWifiUnjamTime = 0; private long lastSaveLocationTime = 0; private long lastHaveLocationTime = 0; private int pendingWifiCount = 0; private int pendingCellCount = 0; private final long constructionTime = System.currentTimeMillis(); private long previousTalkTime = System.currentTimeMillis(); private final Set<String> runNetworks = new HashSet<>(); private long prevNewNetCount; private long prevScanPeriod; private boolean scanInFlight = false; public static final int SIGNAL_COMPARE = 10; public static final int CHANNEL_COMPARE = 11; public static final int CRYPTO_COMPARE = 12; public static final int FIND_TIME_COMPARE = 13; public static final int SSID_COMPARE = 14; private static final Comparator<Network> signalCompare = new Comparator<Network>() { @Override public int compare( Network a, Network b ) { return b.getLevel() - a.getLevel(); } }; private static final Comparator<Network> channelCompare = new Comparator<Network>() { @Override public int compare( Network a, Network b ) { return a.getFrequency() - b.getFrequency(); } }; private static final Comparator<Network> cryptoCompare = new Comparator<Network>() { @Override public int compare( Network a, Network b ) { return b.getCrypto() - a.getCrypto(); } }; private static final Comparator<Network> findTimeCompare = new Comparator<Network>() { @Override public int compare( Network a, Network b ) { return (int) (b.getConstructionTime() - a.getConstructionTime()); } }; private static final Comparator<Network> ssidCompare = new Comparator<Network>() { @Override public int compare( Network a, Network b ) { return a.getSsid().compareTo( b.getSsid() ); } }; public WifiReceiver( final MainActivity mainActivity, final DatabaseHelper dbHelper ) { this.mainActivity = mainActivity; this.dbHelper = dbHelper; prevScanPeriod = mainActivity.getLocationSetPeriod(); ListFragment.lameStatic.runNetworks = runNetworks; ssidSpeaker = new SsidSpeaker( mainActivity ); // formats for speech timeFormat = new SimpleDateFormat( "h mm aa", Locale.US ); numberFormat1 = NumberFormat.getNumberInstance( Locale.US ); if ( numberFormat1 instanceof DecimalFormat ) { numberFormat1.setMaximumFractionDigits(1); } } public void setMainActivity( final MainActivity mainActivity ) { this.mainActivity = mainActivity; this.ssidSpeaker.setListActivity( mainActivity ); if (mainActivity != null) { prevScanPeriod = mainActivity.getLocationSetPeriod(); MainActivity.info("WifiReceiver setting prevScanPeriod: " + prevScanPeriod); } } public void setListAdapter( final NetworkListAdapter listAdapter ) { this.listAdapter = listAdapter; } public int getRunNetworkCount() { return runNetworks.size(); } public void updateLastScanResponseTime() { lastHaveLocationTime = System.currentTimeMillis(); } @SuppressWarnings("ConstantConditions") @Override public void onReceive( final Context context, final Intent intent ) { scanInFlight = false; final long now = System.currentTimeMillis(); lastScanResponseTime = now; // final long start = now; final WifiManager wifiManager = (WifiManager) mainActivity.getApplicationContext().getSystemService(Context.WIFI_SERVICE); List<ScanResult> results = null; try { results = wifiManager.getScanResults(); // return can be null! } catch (final SecurityException ex) { MainActivity.info("security exception getting scan results: " + ex, ex); } catch (final Exception ex) { // ignore, happens on some vm's MainActivity.info("exception getting scan results: " + ex, ex); } long nonstopScanRequestTime = Long.MIN_VALUE; final SharedPreferences prefs = mainActivity.getSharedPreferences( ListFragment.SHARED_PREFS, 0 ); final long period = getScanPeriod(); if ( period == 0 ) { // treat as "continuous", so request scan in here doWifiScan(); nonstopScanRequestTime = now; } final long setPeriod = mainActivity.getLocationSetPeriod(); if ( setPeriod != prevScanPeriod && mainActivity.isScanning() ) { // update our location scanning speed MainActivity.info("setting location updates to: " + setPeriod); mainActivity.setLocationUpdates(setPeriod, 0f); prevScanPeriod = setPeriod; } // have the gps listener to a self-check, in case it isn't getting updates anymore final GPSListener gpsListener = mainActivity.getGPSListener(); Location location = null; if (gpsListener != null) { gpsListener.checkLocationOK(); location = gpsListener.getLocation(); } // save the location every minute, for later runs, or viewing map during loss of location. if (now - lastSaveLocationTime > 60000L && location != null) { mainActivity.getGPSListener().saveLocation(); lastSaveLocationTime = now; } if (location != null) { lastHaveLocationTime = now; } // MainActivity.info("now minus haveloctime: " + (now-lastHaveLocationTime) // + " lastHaveLocationTime: " + lastHaveLocationTime); if (now - lastHaveLocationTime > 30000L) { // no location in a while, make sure we're subscribed to updates MainActivity.info("no location for a while, setting location update period: " + setPeriod); mainActivity.setLocationUpdates(setPeriod, 0f); // don't do this until another period has passed lastHaveLocationTime = now; } final boolean showCurrent = prefs.getBoolean( ListFragment.PREF_SHOW_CURRENT, true ); if ( showCurrent && listAdapter != null ) { listAdapter.clear(); } final int preQueueSize = dbHelper.getQueueSize(); final boolean fastMode = dbHelper.isFastMode(); final ConcurrentLinkedHashMap<String,Network> networkCache = MainActivity.getNetworkCache(); boolean somethingAdded = false; int resultSize = 0; int newWifiForRun = 0; final boolean ssidSpeak = prefs.getBoolean( ListFragment.PREF_SPEAK_SSID, false ) && ! mainActivity.isMuted(); final Matcher ssidMatcher = FilterMatcher.getFilterMatcher( prefs, ListFragment.FILTER_PREF_PREFIX ); // can be null on shutdown if ( results != null ) { resultSize = results.size(); for ( ScanResult result : results ) { Network network = networkCache.get( result.BSSID ); if ( network == null ) { network = new Network( result ); networkCache.put( network.getBssid(), network ); } else { // cache hit, just set the level network.setLevel( result.level ); } final boolean added = runNetworks.add( result.BSSID ); if ( added ) { newWifiForRun++; if ( ssidSpeak ) { ssidSpeaker.add( network.getSsid() ); } } somethingAdded |= added; if ( location != null && (added || network.getLatLng() == null) ) { // set the LatLng for mapping final LatLng LatLng = new LatLng( location.getLatitude(), location.getLongitude() ); network.setLatLng( LatLng ); MainActivity.addNetworkToMap(network); } // if we're showing current, or this was just added, put on the list if ( showCurrent || added ) { if ( FilterMatcher.isOk( ssidMatcher, prefs, ListFragment.FILTER_PREF_PREFIX, network ) ) { if (listAdapter != null) { listAdapter.add( network ); } } // load test // for ( int i = 0; i< 10; i++) { // listAdapter.add( network ); // } } else if (listAdapter != null){ // not showing current, and not a new thing, go find the network and update the level // this is O(n), ohwell, that's why showCurrent is the default config. for ( int index = 0; index < listAdapter.getCount(); index++ ) { final Network testNet = listAdapter.getItem(index); if ( testNet.getBssid().equals( network.getBssid() ) ) { testNet.setLevel( result.level ); } } } if ( location != null ) { // if in fast mode, only add new-for-run stuff to the db queue if ( fastMode && ! added ) { MainActivity.info( "in fast mode, not adding seen-this-run: " + network.getBssid() ); } else { // loop for stress-testing // for ( int i = 0; i < 10; i++ ) { dbHelper.addObservation( network, location, added ); // } } } else { // no location dbHelper.pendingObservation( network, added ); } } } // check if there are more "New" nets final long newNetCount = dbHelper.getNewNetworkCount(); final long newWifiCount = dbHelper.getNewWifiCount(); final long newNetDiff = newWifiCount - prevNewNetCount; prevNewNetCount = newWifiCount; // check for "New" cell towers final long newCellCount = dbHelper.getNewCellCount(); if ( ! mainActivity.isMuted() ) { final boolean playRun = prefs.getBoolean( ListFragment.PREF_FOUND_SOUND, true ); final boolean playNew = prefs.getBoolean( ListFragment.PREF_FOUND_NEW_SOUND, true ); if ( newNetDiff > 0 && playNew ) { mainActivity.playNewNetSound(); } else if ( somethingAdded && playRun ) { mainActivity.playRunNetSound(); } } if ( mainActivity.getPhoneState().isPhoneActive() ) { // a phone call is active, make sure we aren't speaking anything mainActivity.interruptSpeak(); } // check cell tower info final int preCellForRun = runNetworks.size(); int newCellForRun = 0; final Network cellNetwork = recordCellInfo(location); if ( cellNetwork != null ) { resultSize++; if ( showCurrent && listAdapter != null && FilterMatcher.isOk( ssidMatcher, prefs, ListFragment.FILTER_PREF_PREFIX, cellNetwork ) ) { listAdapter.add(cellNetwork); } if ( runNetworks.size() > preCellForRun ) { newCellForRun++; } } final int sort = prefs.getInt(ListFragment.PREF_LIST_SORT, SIGNAL_COMPARE); Comparator<Network> comparator = signalCompare; switch ( sort ) { case SIGNAL_COMPARE: comparator = signalCompare; break; case CHANNEL_COMPARE: comparator = channelCompare; break; case CRYPTO_COMPARE: comparator = cryptoCompare; break; case FIND_TIME_COMPARE: comparator = findTimeCompare; break; case SSID_COMPARE: comparator = ssidCompare; break; } if (listAdapter != null) { listAdapter.sort( comparator ); } final long dbNets = dbHelper.getNetworkCount(); final long dbLocs = dbHelper.getLocationCount(); // update stat mainActivity.setNetCountUI(); // set the statics for the map ListFragment.lameStatic.runNets = runNetworks.size(); ListFragment.lameStatic.newNets = newNetCount; ListFragment.lameStatic.newWifi = newWifiCount; ListFragment.lameStatic.newCells = newCellCount; ListFragment.lameStatic.currNets = resultSize; ListFragment.lameStatic.preQueueSize = preQueueSize; ListFragment.lameStatic.dbNets = dbNets; ListFragment.lameStatic.dbLocs = dbLocs; // do this if trail is empty, so as soon as we get first gps location it gets triggered // and will show up on map if ( newWifiForRun > 0 || newCellForRun > 0 || ListFragment.lameStatic.networkCache.isEmpty() ) { if ( location == null ) { // save for later pendingWifiCount += newWifiForRun; pendingCellCount += newCellForRun; // MainActivity.info("pendingCellCount: " + pendingCellCount); } else { // add any pendings // don't go crazy if ( pendingWifiCount > 25 ) { pendingWifiCount = 25; } pendingWifiCount = 0; if ( pendingCellCount > 25 ) { pendingCellCount = 25; } pendingCellCount = 0; } } // info( savedStats ); // notify if (listAdapter != null) { listAdapter.notifyDataSetChanged(); } if ( scanRequestTime <= 0 ) { // wasn't set, set to now scanRequestTime = now; } final String status = resultSize + " " + mainActivity.getString(R.string.scanned_in) + " " + (now - scanRequestTime) + mainActivity.getString(R.string.ms_short) + ". " + mainActivity.getString(R.string.dash_db_queue) + " " + preQueueSize; mainActivity.setStatusUI( status ); // we've shown it, reset it to the nonstop time above, or min_value if nonstop wasn't set. scanRequestTime = nonstopScanRequestTime; // do lerp if need be if ( location == null ) { if ( prevGpsLocation != null ) { dbHelper.lastLocation( prevGpsLocation ); // MainActivity.info("set last location for lerping"); } } else { dbHelper.recoverLocations( location ); } // do distance calcs if ( location != null && GPS_PROVIDER.equals( location.getProvider() ) && location.getAccuracy() <= ListFragment.MIN_DISTANCE_ACCURACY ) { if ( prevGpsLocation != null ) { float dist = location.distanceTo( prevGpsLocation ); // info( "dist: " + dist ); if ( dist > 0f ) { final Editor edit = prefs.edit(); edit.putFloat( ListFragment.PREF_DISTANCE_RUN, dist + prefs.getFloat( ListFragment.PREF_DISTANCE_RUN, 0f ) ); edit.putFloat( ListFragment.PREF_DISTANCE_TOTAL, dist + prefs.getFloat( ListFragment.PREF_DISTANCE_TOTAL, 0f ) ); edit.apply(); } } // set for next time prevGpsLocation = location; } if ( somethingAdded && ssidSpeak ) { ssidSpeaker.speak(); } final long speechPeriod = prefs.getLong( ListFragment.PREF_SPEECH_PERIOD, MainActivity.DEFAULT_SPEECH_PERIOD ); if ( speechPeriod != 0 && now - previousTalkTime > speechPeriod * 1000L ) { doAnnouncement( preQueueSize, newWifiCount, newCellCount, now ); } } public String getNetworkTypeName() { TelephonyManager tele = (TelephonyManager) mainActivity.getSystemService( Context.TELEPHONY_SERVICE ); if ( tele == null ) { return null; } switch (tele.getNetworkType()) { case TelephonyManager.NETWORK_TYPE_GPRS: return "GPRS"; case TelephonyManager.NETWORK_TYPE_EDGE: return "EDGE"; case TelephonyManager.NETWORK_TYPE_UMTS: return "UMTS"; case 4: return "CDMA"; case 5: return "CDMA - EvDo rev. 0"; case 6: return "CDMA - EvDo rev. A"; case 7: return "CDMA - 1xRTT"; case 8: return "HSDPA"; case 9: return "HSUPA"; case 10: return "HSPA"; case 11: return "IDEN"; case 12: return "CDMA - EvDo rev. B"; default: return "UNKNOWN"; } } private Network recordCellInfo(final Location location) { TelephonyManager tele = (TelephonyManager) mainActivity.getSystemService( Context.TELEPHONY_SERVICE ); Network network = null; if ( tele != null ) { /* List<NeighboringCellInfo> list = tele.getNeighboringCellInfo(); for (final NeighboringCellInfo cell : list ) { MainActivity.info("neigh cell: " + cell + " class: " + cell.getClass().getCanonicalName() ); MainActivity.info("cid: " + cell.getCid()); // api level 5!!!! MainActivity.info("lac: " + cell.getLac() ); MainActivity.info("psc: " + cell.getPsc() ); MainActivity.info("net type: " + cell.getNetworkType() ); MainActivity.info("nettypename: " + getNetworkTypeName() ); } */ String bssid = null; NetworkType type = null; CellLocation cellLocation = null; try { cellLocation = tele.getCellLocation(); } catch ( NullPointerException ex ) { // bug in Archos7 can NPE there, just ignore } catch (final SecurityException ex) { MainActivity.info("Security exception tele.getCellLocation: " + ex); } //noinspection StatementWithEmptyBody if ( cellLocation == null ) { // ignore } else if ( cellLocation.getClass().getSimpleName().equals("CdmaCellLocation") ) { try { final int systemId = (Integer) cellLocation.getClass().getMethod("getSystemId").invoke(cellLocation); final int networkId = (Integer) cellLocation.getClass().getMethod("getNetworkId").invoke(cellLocation); final int baseStationId = (Integer) cellLocation.getClass().getMethod("getBaseStationId").invoke(cellLocation); if ( systemId > 0 && networkId >= 0 && baseStationId >= 0 ) { bssid = systemId + "_" + networkId + "_" + baseStationId; type = NetworkType.CDMA; } } catch ( Exception ex ) { MainActivity.error("cdma reflection exception: " + ex); } } else if ( cellLocation instanceof GsmCellLocation ) { GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation; if ( gsmCellLocation.getLac() >= 0 && gsmCellLocation.getCid() >= 0 ) { bssid = tele.getNetworkOperator() + "_" + gsmCellLocation.getLac() + "_" + gsmCellLocation.getCid(); type = NetworkType.GSM; } } if ( bssid != null ) { final String ssid = tele.getNetworkOperatorName(); final String networkType = getNetworkTypeName(); final String capabilities = networkType + ";" + tele.getNetworkCountryIso(); int strength = 0; PhoneState phoneState = mainActivity.getPhoneState(); if (phoneState != null) { strength = phoneState.getStrength(); } if ( NetworkType.GSM.equals(type) ) { strength = gsmRssiMagicDecoderRing( strength ); } // MainActivity.info( "bssid: " + bssid ); // MainActivity.info( "strength: " + strength ); // MainActivity.info( "ssid: " + ssid ); // MainActivity.info( "capabilities: " + capabilities ); // MainActivity.info( "networkType: " + networkType ); // MainActivity.info( "location: " + location ); final ConcurrentLinkedHashMap<String,Network> networkCache = MainActivity.getNetworkCache(); final boolean newForRun = runNetworks.add( bssid ); network = networkCache.get( bssid ); if ( network == null ) { network = new Network( bssid, ssid, 0, capabilities, strength, type ); networkCache.put( network.getBssid(), network ); } else { network.setLevel(strength); } if ( location != null && (newForRun || network.getLatLng() == null) ) { // set the LatLng for mapping final LatLng LatLng = new LatLng( location.getLatitude(), location.getLongitude() ); network.setLatLng( LatLng ); } if ( location != null ) { dbHelper.addObservation(network, location, newForRun); } } } return network; } private int gsmRssiMagicDecoderRing( int strength ) { int retval; if ( strength == 99 ) { // unknown retval = -113; } else { retval = ((strength - 31) * 2) - 51; } // MainActivity.info("strength: " + strength + " retval: " + retval); return retval; } private void doAnnouncement( int preQueueSize, long newWifiCount, long newCellCount, long now ) { final SharedPreferences prefs = mainActivity.getSharedPreferences( ListFragment.SHARED_PREFS, 0 ); StringBuilder builder = new StringBuilder(); if ( mainActivity.getGPSListener().getLocation() == null && prefs.getBoolean( ListFragment.PREF_SPEECH_GPS, true ) ) { builder.append(mainActivity.getString(R.string.tts_no_gps_fix)).append(", "); } // run, new, queue, miles, time, battery if ( prefs.getBoolean( ListFragment.PREF_SPEAK_RUN, true ) ) { builder.append(mainActivity.getString(R.string.run)).append(" ") .append(runNetworks.size()).append( ", " ); } if ( prefs.getBoolean( ListFragment.PREF_SPEAK_NEW_WIFI, true ) ) { builder.append(mainActivity.getString(R.string.tts_new_wifi)).append(" ") .append(newWifiCount).append( ", " ); } if ( prefs.getBoolean( ListFragment.PREF_SPEAK_NEW_CELL, true ) ) { builder.append(mainActivity.getString(R.string.tts_new_cell)).append(" ") .append(newCellCount).append( ", " ); } if ( preQueueSize > 0 && prefs.getBoolean( ListFragment.PREF_SPEAK_QUEUE, true ) ) { builder.append(mainActivity.getString(R.string.tts_queue)).append(" ") .append(preQueueSize).append( ", " ); } if ( prefs.getBoolean( ListFragment.PREF_SPEAK_MILES, true ) ) { final float dist = prefs.getFloat( ListFragment.PREF_DISTANCE_RUN, 0f ); final String distString = DashboardFragment.metersToString( numberFormat1, mainActivity, dist, false ); builder.append(mainActivity.getString(R.string.tts_from)).append(" ") .append(distString).append( ", " ); } if ( prefs.getBoolean( ListFragment.PREF_SPEAK_TIME, true ) ) { String time = timeFormat.format( new Date() ); // time is hard to say. time = time.replace(" 00", " " + mainActivity.getString(R.string.tts_o_clock)); time = time.replace(" 0", " " + mainActivity.getString(R.string.tts_o) + " "); builder.append( time ).append( ", " ); } final int batteryLevel = mainActivity.getBatteryLevelReceiver().getBatteryLevel(); if ( batteryLevel >= 0 && prefs.getBoolean( ListFragment.PREF_SPEAK_BATTERY, true ) ) { builder.append(mainActivity.getString(R.string.tts_battery)).append(" ").append(batteryLevel).append(" ").append(mainActivity.getString(R.string.tts_percent)).append(", "); } final String speak = builder.toString(); MainActivity.info( "speak: " + speak ); if (! "".equals(speak)) { mainActivity.speak( builder.toString() ); } previousTalkTime = now; } public void setupWifiTimer( final boolean turnedWifiOn ) { MainActivity.info( "create wifi timer" ); if ( wifiTimer == null ) { wifiTimer = new Handler(); final Runnable mUpdateTimeTask = new Runnable() { @Override public void run() { // make sure the app isn't trying to finish if ( ! mainActivity.isFinishing() ) { // info( "timer start scan" ); doWifiScan(); if ( scanRequestTime <= 0 ) { scanRequestTime = System.currentTimeMillis(); } long period = getScanPeriod(); // check if set to "continuous" if ( period == 0L ) { // set to default here, as a scan will also be requested on the scan result listener period = MainActivity.SCAN_DEFAULT; } // info("wifitimer: " + period ); wifiTimer.postDelayed( this, period ); } else { MainActivity.info( "finishing timer" ); } } }; wifiTimer.removeCallbacks( mUpdateTimeTask ); wifiTimer.postDelayed( mUpdateTimeTask, 100 ); if ( turnedWifiOn ) { MainActivity.info( "not immediately running wifi scan, since it was just turned on" + " it will block for a few seconds and fail anyway"); } else { MainActivity.info( "start first wifi scan"); // starts scan, sends event when done final boolean scanOK = doWifiScan(); if ( scanRequestTime <= 0 ) { scanRequestTime = System.currentTimeMillis(); } MainActivity.info( "startup finished. wifi scanOK: " + scanOK ); } } } public long getScanPeriod() { final SharedPreferences prefs = mainActivity.getSharedPreferences( ListFragment.SHARED_PREFS, 0 ); String scanPref = ListFragment.PREF_SCAN_PERIOD; long defaultRate = MainActivity.SCAN_DEFAULT; // if over 5 mph Location location = null; final GPSListener gpsListener = mainActivity.getGPSListener(); if (gpsListener != null) { location = gpsListener.getLocation(); } if ( location != null && location.getSpeed() >= 2.2352f ) { scanPref = ListFragment.PREF_SCAN_PERIOD_FAST; defaultRate = MainActivity.SCAN_FAST_DEFAULT; } else if ( location == null || location.getSpeed() < 0.1f ) { scanPref = ListFragment.PREF_SCAN_PERIOD_STILL; defaultRate = MainActivity.SCAN_STILL_DEFAULT; } return prefs.getLong( scanPref, defaultRate ); } public void scheduleScan() { wifiTimer.post(new Runnable() { @Override public void run() { doWifiScan(); } }); } /** * only call this from a Handler * @return true if startScan success */ private boolean doWifiScan() { // MainActivity.info("do wifi scan. lastScanTime: " + lastScanResponseTime); final WifiManager wifiManager = (WifiManager) mainActivity.getApplicationContext().getSystemService(Context.WIFI_SERVICE); boolean success = false; if ( mainActivity.isTransferring() ) { MainActivity.info( "transferring, not scanning for now" ); // reset this lastScanResponseTime = Long.MIN_VALUE; } else if (mainActivity.isScanning()) { if ( ! scanInFlight ) { try { success = wifiManager.startScan(); } catch (Exception ex) { MainActivity.warn("exception starting scan: " + ex, ex); } if ( success ) { scanInFlight = true; } } final long now = System.currentTimeMillis(); if ( lastScanResponseTime < 0 ) { // use now, since we made a request lastScanResponseTime = now; } else { final long sinceLastScan = now - lastScanResponseTime; final SharedPreferences prefs = mainActivity.getSharedPreferences( ListFragment.SHARED_PREFS, 0 ); final long resetWifiPeriod = prefs.getLong( ListFragment.PREF_RESET_WIFI_PERIOD, MainActivity.DEFAULT_RESET_WIFI_PERIOD ); if ( resetWifiPeriod > 0 && sinceLastScan > resetWifiPeriod ) { MainActivity.warn("Time since last scan: " + sinceLastScan + " milliseconds"); if ( now - lastWifiUnjamTime > resetWifiPeriod ) { final boolean disableToast = prefs.getBoolean(ListFragment.PREF_DISABLE_TOAST, false); if (!disableToast) { Toast.makeText( mainActivity, mainActivity.getString(R.string.wifi_jammed), Toast.LENGTH_LONG ).show(); } scanInFlight = false; try { wifiManager.setWifiEnabled(false); wifiManager.setWifiEnabled(true); } catch (SecurityException ex) { MainActivity.info("exception resetting wifi: " + ex, ex); } lastWifiUnjamTime = now; if (prefs.getBoolean(ListFragment.PREF_SPEAK_WIFI_RESTART, true)) { mainActivity.speak(mainActivity.getString(R.string.wifi_restart_1) + " " + (sinceLastScan / 1000L) + " " + mainActivity.getString(R.string.wifi_restart_2)); } } } } } else { // scanning is off. since we're the only timer, update the UI mainActivity.setNetCountUI(); mainActivity.setLocationUI(); mainActivity.setStatusUI("Scanning Turned Off" ); // keep the scan times from getting huge scanRequestTime = System.currentTimeMillis(); // reset this lastScanResponseTime = Long.MIN_VALUE; } // battery kill if ( ! mainActivity.isTransferring() ) { final SharedPreferences prefs = mainActivity.getSharedPreferences( ListFragment.SHARED_PREFS, 0 ); long batteryKill = prefs.getLong( ListFragment.PREF_BATTERY_KILL_PERCENT, MainActivity.DEFAULT_BATTERY_KILL_PERCENT); if ( mainActivity.getBatteryLevelReceiver() != null ) { final int batteryLevel = mainActivity.getBatteryLevelReceiver().getBatteryLevel(); final int batteryStatus = mainActivity.getBatteryLevelReceiver().getBatteryStatus(); // MainActivity.info("batteryStatus: " + batteryStatus); // give some time since starting up to change this configuration if ( batteryKill > 0 && batteryLevel > 0 && batteryLevel <= batteryKill && batteryStatus != BatteryManager.BATTERY_STATUS_CHARGING && (System.currentTimeMillis() - constructionTime) > 30000L) { final String text = mainActivity.getString(R.string.battery_at) + " " + batteryLevel + " " + mainActivity.getString(R.string.battery_postfix); Toast.makeText( mainActivity, text, Toast.LENGTH_LONG ).show(); MainActivity.warn("low battery, shutting down"); mainActivity.speak( text ); MainActivity.sleep(5000L); mainActivity.finish(); } } } return success; } }