/* * This source is part of the * _____ ___ ____ * __ / / _ \/ _ | / __/___ _______ _ * / // / , _/ __ |/ _/_/ _ \/ __/ _ `/ * \___/_/|_/_/ |_/_/ (_)___/_/ \_, / * /___/ * repository. * * Copyright (C) 2015 Rasmus Holm * Copyright (C) 2015 Carmen Alvarez * * 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 ca.rmen.android.networkmonitor.app.speedtest; import android.content.Context; import android.content.SharedPreferences; import android.database.Cursor; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.preference.PreferenceManager; import android.provider.BaseColumns; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import ca.rmen.android.networkmonitor.Constants; import ca.rmen.android.networkmonitor.provider.NetMonColumns; import ca.rmen.android.networkmonitor.util.DBUtil; import ca.rmen.android.networkmonitor.util.Log; import ca.rmen.android.networkmonitor.util.NetMonSignalStrength; import ca.rmen.android.networkmonitor.util.TelephonyUtil; /** * Determines if a speed test should be executed or not. */ public class SpeedTestExecutionDecider { private static final String TAG = Constants.TAG + SpeedTestExecutionDecider.class.getSimpleName(); private static final int SIGNAL_STRENGTH_VARIATION_THRESHOLD_DBM = 5; private final Context mContext; private final SpeedTestPreferences mPreferences; private final NetMonSignalStrength mNetMonSignalStrength; private int mCurrentCellSignalStrengthDbm; // For fetching data regarding the network such as signal strength, network type etc. private final TelephonyManager mTelephonyManager; private final WifiManager mWifiManager; private final ConnectivityManager mConnectivityManager; // Using this selection in a query will make the query only return results in which a speed test was performed. private static final String QUERY_FILTER_HAS_SPEED_TEST = "CAST(" + NetMonColumns.DOWNLOAD_SPEED + " AS REAL) > 0 OR CAST(" + NetMonColumns.UPLOAD_SPEED + " AS REAL) > 0"; public SpeedTestExecutionDecider(Context context) { Log.v(TAG, "onCreate"); mContext = context; mPreferences = SpeedTestPreferences.getInstance(context); mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); mNetMonSignalStrength = new NetMonSignalStrength(context); if (mPreferences.isEnabled() && mPreferences.getSpeedTestInterval() == SpeedTestPreferences.PREF_SPEED_TEST_INTERVAL_DBM_OR_NETWORK_CHANGE) { registerPhoneStateListener(); } PreferenceManager.getDefaultSharedPreferences(mContext).registerOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener); } // Need to make sure we do not listen after we are done public void onDestroy() { Log.v(TAG, "onDestroy"); unregisterPhoneStateListener(); PreferenceManager.getDefaultSharedPreferences(mContext).unregisterOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener); } /** * @return true if a speed test should be executed. */ public boolean shouldExecute() { Log.v(TAG, "shouldExecute"); if (!mPreferences.isEnabled()) return false; int speedTestInterval = mPreferences.getSpeedTestInterval(); if (speedTestInterval == SpeedTestPreferences.PREF_SPEED_TEST_INTERVAL_NETWORK_CHANGE) { // check for change in network return hasNetworkTypeChanged(); } else if (speedTestInterval == SpeedTestPreferences.PREF_SPEED_TEST_INTERVAL_DBM_OR_NETWORK_CHANGE) { // check for change in network and for a difference in dbm by 5 if (hasSignalStrengthChanged() || hasNetworkTypeChanged()) { return true; } } else if (isIntervalExceeded()) { return true; } return false; } /** * @return true if the current network type is different from the network type during the last speed test. */ private boolean hasNetworkTypeChanged() { String lastLoggedNetworkType = DBUtil.readLastLoggedValue(mContext, NetMonColumns.NETWORK_TYPE, QUERY_FILTER_HAS_SPEED_TEST); String currentNetworkType = TelephonyUtil.getNetworkType(mContext); if (currentNetworkType == null) return false; Log.v(TAG, "hasNetworkTypeChanged: from " + lastLoggedNetworkType + " to " + currentNetworkType); return !currentNetworkType.equals(lastLoggedNetworkType); } /** * @return true if the current wifi or mobile signal strength in dbm has changed significantly compared to the value during the last speed test. * Only checks wifi signal strength if we are on wifi, and cell signal strength, if we are on mobile data. */ private boolean hasSignalStrengthChanged() { NetworkInfo activeNetworkInfo = mConnectivityManager.getActiveNetworkInfo(); if (activeNetworkInfo == null) return false; if (activeNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI) { return hasWifiSignalStrengthChanged(); } else { return hasCellSignalStrengthChanged(); } } /** * @return true if the current cell signal strength has changed significantly compared to the cell signal strength during the last speed test. */ private boolean hasCellSignalStrengthChanged() { Log.v(TAG, "hasCellSignalStrengthChanged by: " + SIGNAL_STRENGTH_VARIATION_THRESHOLD_DBM + '?'); String lastLoggedCellSignalStrength = DBUtil.readLastLoggedValue(mContext, NetMonColumns.CELL_SIGNAL_STRENGTH_DBM, QUERY_FILTER_HAS_SPEED_TEST); return lastLoggedCellSignalStrength != null && signalStrengthChangeExceedsThreshold(Integer.valueOf(lastLoggedCellSignalStrength), mCurrentCellSignalStrengthDbm); } /** * @return true if the current wifi signal strength has changed significantly compared to the wifi signal strength during the last speed test. */ private boolean hasWifiSignalStrengthChanged() { Log.v(TAG, "hasWifiSignalStrengthChanged by: " + SIGNAL_STRENGTH_VARIATION_THRESHOLD_DBM + '?'); WifiInfo connectionInfo = mWifiManager.getConnectionInfo(); int currentWifiSignalStrengthDbm = connectionInfo.getRssi(); String lastLoggedWifiSignalStrength = DBUtil.readLastLoggedValue(mContext, NetMonColumns.WIFI_RSSI, QUERY_FILTER_HAS_SPEED_TEST); return lastLoggedWifiSignalStrength != null && signalStrengthChangeExceedsThreshold(Integer.valueOf(lastLoggedWifiSignalStrength), currentWifiSignalStrengthDbm); } /** * @return true if the difference between the two values exceeds the threshold defined by #SIGNAL_STRENGTH_VARIATION_THRESHOLD_DBM. */ private static boolean signalStrengthChangeExceedsThreshold(int previousValue, int currentValue) { Log.v(TAG, "signal strength has been changed from " + previousValue + " to " + currentValue); if (previousValue != NetMonSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) { if (currentValue != NetMonSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) { if (currentValue >= previousValue + SIGNAL_STRENGTH_VARIATION_THRESHOLD_DBM || currentValue <= previousValue - SIGNAL_STRENGTH_VARIATION_THRESHOLD_DBM) { return true; } } } return false; } /** * @return true if enough network monitor tests, without speed tests, have been logged. */ private boolean isIntervalExceeded() { int numberOfRecordsSinceLastSpeedTest = readNumberOfRecordsSinceLastSpeedTest(); Log.v(TAG, "isIntervalExceeded: numberOfRecordsSinceLastSpeedTest: " + numberOfRecordsSinceLastSpeedTest + " vs speed test interval: " + mPreferences.getSpeedTestInterval()); return (numberOfRecordsSinceLastSpeedTest < 0) || (numberOfRecordsSinceLastSpeedTest >= mPreferences.getSpeedTestInterval() - 1); } private int readNumberOfRecordsSinceLastSpeedTest() { String idOfLatestSpeedTest = DBUtil.readLastLoggedValue(mContext, BaseColumns._ID, QUERY_FILTER_HAS_SPEED_TEST); if (idOfLatestSpeedTest == null) return 0; String orderBy = BaseColumns._ID + " DESC"; String selection = BaseColumns._ID + " > " + idOfLatestSpeedTest; Cursor cursor = mContext.getContentResolver().query(NetMonColumns.CONTENT_URI, null, selection, null, orderBy); if (cursor != null) { try { return cursor.getCount(); } finally { cursor.close(); } } return 0; } private void registerPhoneStateListener() { Log.v(TAG, "registerPhoneStateListener"); mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS | PhoneStateListener.LISTEN_SERVICE_STATE); } private void unregisterPhoneStateListener() { Log.v(TAG, "unregisterPhoneStateListener"); mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); } private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { @Override public void onSignalStrengthsChanged(SignalStrength signalStrength) { Log.v(TAG, "onSignalStrengthsChanged: " + signalStrength); mCurrentCellSignalStrengthDbm = mNetMonSignalStrength.getDbm(signalStrength); } @Override public void onServiceStateChanged(ServiceState serviceState) { Log.v(TAG, "onServiceStateChanged " + serviceState); if (serviceState.getState() != ServiceState.STATE_IN_SERVICE) { mCurrentCellSignalStrengthDbm = NetMonSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; } } }; private final SharedPreferences.OnSharedPreferenceChangeListener mSharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (SpeedTestPreferences.PREF_SPEED_TEST_ENABLED.equals(key) || SpeedTestPreferences.PREF_SPEED_TEST_INTERVAL.equals(key)) { if (mPreferences.isEnabled() && mPreferences.getSpeedTestInterval() == SpeedTestPreferences.PREF_SPEED_TEST_INTERVAL_DBM_OR_NETWORK_CHANGE) { registerPhoneStateListener(); } else { unregisterPhoneStateListener(); } } } }; }