summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Qiu <zqiu@google.com>2016-01-20 13:42:06 -0800
committerNingyuan Wang <nywang@google.com>2016-06-28 10:40:32 -0700
commitc083e644c805754ae5f4d1cbef72f6d438d71c22 (patch)
tree957b19eb3c0ac4facc51a92e2e79430b0d9779b6
parentc3385bb331965d4adacae39338bfe9bd6046e740 (diff)
downloadwifi-n-iot-preview-2.tar.gz
Wifi watchdog is not being used anymore, the associated setting ("Settings -> Wi-Fi -> Advanced -> Avoid poor internet connection") had already been deleted from UI. So remove it. While there, remove the unused state VerifyingLinkState in WifiStateMachine, which is only triggered by WifiWatchdogStateMachine. BUG=29533054 BUG=26254553 TEST=compile Change-Id: I77dd1bc2b90cc0f3f94619e7b7df8293b068fcce
-rw-r--r--service/java/com/android/server/wifi/WifiServiceImpl.java6
-rw-r--r--service/java/com/android/server/wifi/WifiStateMachine.java52
-rw-r--r--service/java/com/android/server/wifi/WifiWatchdogStateMachine.java1210
3 files changed, 0 insertions, 1268 deletions
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index 55c604070..d7f7277af 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -313,8 +313,6 @@ public final class WifiServiceImpl extends IWifiManager.Stub {
WifiStateMachineHandler mWifiStateMachineHandler;
- private WifiWatchdogStateMachine mWifiWatchdogStateMachine;
-
private WifiController mWifiController;
public WifiServiceImpl(Context context) {
@@ -396,9 +394,6 @@ public final class WifiServiceImpl extends IWifiManager.Stub {
// If we are already disabled (could be due to airplane mode), avoid changing persist
// state here
if (wifiEnabled) setWifiEnabled(wifiEnabled);
-
- mWifiWatchdogStateMachine = WifiWatchdogStateMachine.
- makeWifiWatchdogStateMachine(mContext, mWifiStateMachine.getMessenger());
}
/**
@@ -1537,7 +1532,6 @@ public final class WifiServiceImpl extends IWifiManager.Stub {
pw.println(l);
}
- mWifiWatchdogStateMachine.dump(fd, pw, args);
pw.println();
mWifiStateMachine.dump(fd, pw, args);
pw.println();
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index adfc049ea..2fb3d6404 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -969,8 +969,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno
private State mL2ConnectedState = new L2ConnectedState();
/* fetching IP after connection to access point (assoc+auth complete) */
private State mObtainingIpState = new ObtainingIpState();
- /* Waiting for link quality verification to be complete */
- private State mVerifyingLinkState = new VerifyingLinkState();
/* Connected with IP addr */
private State mConnectedState = new ConnectedState();
/* Roaming */
@@ -1323,7 +1321,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno
addState(mConnectModeState, mDriverStartedState);
addState(mL2ConnectedState, mConnectModeState);
addState(mObtainingIpState, mL2ConnectedState);
- addState(mVerifyingLinkState, mL2ConnectedState);
addState(mConnectedState, mL2ConnectedState);
addState(mRoamingState, mL2ConnectedState);
addState(mDisconnectingState, mConnectModeState);
@@ -5626,8 +5623,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno
case CMD_SET_AP_CONFIG_COMPLETED:
case CMD_REQUEST_AP_CONFIG:
case CMD_RESPONSE_AP_CONFIG:
- case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
- case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
case CMD_NO_NETWORKS_PERIODIC_SCAN:
case CMD_DISABLE_P2P_RSP:
case WifiMonitor.SUP_REQUEST_IDENTITY:
@@ -6953,12 +6948,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno
case CMD_DISABLE_P2P_REQ:
s = "CMD_DISABLE_P2P_REQ";
break;
- case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
- s = "GOOD_LINK_DETECTED";
- break;
- case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
- s = "POOR_LINK_DETECTED";
- break;
case WifiP2pServiceImpl.GROUP_CREATING_TIMED_OUT:
s = "GROUP_CREATING_TIMED_OUT";
break;
@@ -8730,40 +8719,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno
}
}
- // Note: currently, this state is never used, because WifiWatchdogStateMachine unconditionally
- // sets mPoorNetworkDetectionEnabled to false.
- class VerifyingLinkState extends State {
- @Override
- public void enter() {
- log(getName() + " enter");
- setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
- mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
- sendNetworkStateChangeBroadcast(mLastBssid);
- // End roaming
- mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
- }
- @Override
- public boolean processMessage(Message message) {
- logStateAndMessage(message, this);
-
- switch (message.what) {
- case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
- // Stay here
- log(getName() + " POOR_LINK_DETECTED: no transition");
- break;
- case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
- log(getName() + " GOOD_LINK_DETECTED: transition to CONNECTED");
- sendConnectedState();
- transitionTo(mConnectedState);
- break;
- default:
- if (DBG) log(getName() + " what=" + message.what + " NOT_HANDLED");
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
private void sendConnectedState() {
// If this network was explicitly selected by the user, evaluate whether to call
// explicitlySelected() so the system can treat it appropriately.
@@ -8819,9 +8774,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno
WifiConfiguration.ROAMING_FAILURE_IP_CONFIG);
}
return NOT_HANDLED;
- case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
- if (DBG) log("Roaming and Watchdog reports poor link -> ignore");
- return HANDLED;
case CMD_UNWANTED_NETWORK:
if (DBG) log("Roaming and CS doesnt want the network -> ignore");
return HANDLED;
@@ -9016,10 +8968,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiPno
case CMD_UPDATE_ASSOCIATED_SCAN_PERMISSION:
updateAssociatedScanPermission();
break;
- case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
- if (DBG) log("Watchdog reports poor link");
- transitionTo(mVerifyingLinkState);
- break;
case CMD_UNWANTED_NETWORK:
if (message.arg1 == NETWORK_STATUS_UNWANTED_DISCONNECT) {
mWifiConfigStore.handleBadNetworkDisconnectReport(mLastNetworkId, mWifiInfo);
diff --git a/service/java/com/android/server/wifi/WifiWatchdogStateMachine.java b/service/java/com/android/server/wifi/WifiWatchdogStateMachine.java
deleted file mode 100644
index 0ef18e6ae..000000000
--- a/service/java/com/android/server/wifi/WifiWatchdogStateMachine.java
+++ /dev/null
@@ -1,1210 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * 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 com.android.server.wifi;
-
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.net.ConnectivityManager;
-import android.net.LinkProperties;
-import android.net.NetworkInfo;
-import android.net.wifi.RssiPacketCountInfo;
-import android.net.wifi.SupplicantState;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.LruCache;
-
-import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.Protocol;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.text.DecimalFormat;
-
-/**
- * WifiWatchdogStateMachine monitors the connection to a WiFi network. When WiFi
- * connects at L2 layer, the beacons from access point reach the device and it
- * can maintain a connection, but the application connectivity can be flaky (due
- * to bigger packet size exchange).
- * <p>
- * We now monitor the quality of the last hop on WiFi using packet loss ratio as
- * an indicator to decide if the link is good enough to switch to Wi-Fi as the
- * uplink.
- * <p>
- * When WiFi is connected, the WiFi watchdog keeps sampling the RSSI and the
- * instant packet loss, and record it as per-AP loss-to-rssi statistics. When
- * the instant packet loss is higher than a threshold, the WiFi watchdog sends a
- * poor link notification to avoid WiFi connection temporarily.
- * <p>
- * While WiFi is being avoided, the WiFi watchdog keep watching the RSSI to
- * bring the WiFi connection back. Once the RSSI is high enough to achieve a
- * lower packet loss, a good link detection is sent such that the WiFi
- * connection become available again.
- * <p>
- * BSSID roaming has been taken into account. When user is moving across
- * multiple APs, the WiFi watchdog will detect that and keep watching the
- * currently connected AP.
- * <p>
- * Power impact should be minimal since much of the measurement relies on
- * passive statistics already being tracked at the driver and the polling is
- * done when screen is turned on and the RSSI is in a certain range.
- *
- * @hide
- */
-public class WifiWatchdogStateMachine extends StateMachine {
-
- private static final boolean DBG = false;
-
- private static final int BASE = Protocol.BASE_WIFI_WATCHDOG;
-
- /* Internal events */
- private static final int EVENT_WATCHDOG_TOGGLED = BASE + 1;
- private static final int EVENT_NETWORK_STATE_CHANGE = BASE + 2;
- private static final int EVENT_RSSI_CHANGE = BASE + 3;
- private static final int EVENT_SUPPLICANT_STATE_CHANGE = BASE + 4;
- private static final int EVENT_WIFI_RADIO_STATE_CHANGE = BASE + 5;
- private static final int EVENT_WATCHDOG_SETTINGS_CHANGE = BASE + 6;
- private static final int EVENT_BSSID_CHANGE = BASE + 7;
- private static final int EVENT_SCREEN_ON = BASE + 8;
- private static final int EVENT_SCREEN_OFF = BASE + 9;
-
- /* Internal messages */
- private static final int CMD_RSSI_FETCH = BASE + 11;
-
- /* Notifications from/to WifiStateMachine */
- static final int POOR_LINK_DETECTED = BASE + 21;
- static final int GOOD_LINK_DETECTED = BASE + 22;
-
- /*
- * RSSI levels as used by notification icon
- * Level 4 -55 <= RSSI
- * Level 3 -66 <= RSSI < -55
- * Level 2 -77 <= RSSI < -67
- * Level 1 -88 <= RSSI < -78
- * Level 0 RSSI < -88
- */
-
- /**
- * WiFi link statistics is monitored and recorded actively below this threshold.
- * <p>
- * Larger threshold is more adaptive but increases sampling cost.
- */
- private static final int LINK_MONITOR_LEVEL_THRESHOLD = WifiManager.RSSI_LEVELS - 1;
-
- /**
- * Remember packet loss statistics of how many BSSIDs.
- * <p>
- * Larger size is usually better but requires more space.
- */
- private static final int BSSID_STAT_CACHE_SIZE = 20;
-
- /**
- * RSSI range of a BSSID statistics.
- * Within the range, (RSSI -> packet loss %) mappings are stored.
- * <p>
- * Larger range is usually better but requires more space.
- */
- private static final int BSSID_STAT_RANGE_LOW_DBM = -105;
-
- /**
- * See {@link #BSSID_STAT_RANGE_LOW_DBM}.
- */
- private static final int BSSID_STAT_RANGE_HIGH_DBM = -45;
-
- /**
- * How many consecutive empty data point to trigger a empty-cache detection.
- * In this case, a preset/default loss value (function on RSSI) is used.
- * <p>
- * In normal uses, some RSSI values may never be seen due to channel randomness.
- * However, the size of such empty RSSI chunk in normal use is generally 1~2.
- */
- private static final int BSSID_STAT_EMPTY_COUNT = 3;
-
- /**
- * Sample interval for packet loss statistics, in msec.
- * <p>
- * Smaller interval is more accurate but increases sampling cost (battery consumption).
- */
- private static final long LINK_SAMPLING_INTERVAL_MS = 1 * 1000;
-
- /**
- * Coefficients (alpha) for moving average for packet loss tracking.
- * Must be within (0.0, 1.0).
- * <p>
- * Equivalent number of samples: N = 2 / alpha - 1 .
- * We want the historic loss to base on more data points to be statistically reliable.
- * We want the current instant loss to base on less data points to be responsive.
- */
- private static final double EXP_COEFFICIENT_RECORD = 0.1;
-
- /**
- * See {@link #EXP_COEFFICIENT_RECORD}.
- */
- private static final double EXP_COEFFICIENT_MONITOR = 0.5;
-
- /**
- * Thresholds for sending good/poor link notifications, in packet loss %.
- * Good threshold must be smaller than poor threshold.
- * Use smaller poor threshold to avoid WiFi more aggressively.
- * Use smaller good threshold to bring back WiFi more conservatively.
- * <p>
- * When approaching the boundary, loss ratio jumps significantly within a few dBs.
- * 50% loss threshold is a good balance between accuracy and reponsiveness.
- * <=10% good threshold is a safe value to avoid jumping back to WiFi too easily.
- */
- private static final double POOR_LINK_LOSS_THRESHOLD = 0.5;
-
- /**
- * See {@link #POOR_LINK_LOSS_THRESHOLD}.
- */
- private static final double GOOD_LINK_LOSS_THRESHOLD = 0.1;
-
- /**
- * Number of samples to confirm before sending a poor link notification.
- * Response time = confirm_count * sample_interval .
- * <p>
- * A smaller threshold improves response speed but may suffer from randomness.
- * According to experiments, 3~5 are good values to achieve a balance.
- * These parameters should be tuned along with {@link #LINK_SAMPLING_INTERVAL_MS}.
- */
- private static final int POOR_LINK_SAMPLE_COUNT = 3;
-
- /**
- * Minimum volume (converted from pkt/sec) to detect a poor link, to avoid randomness.
- * <p>
- * According to experiments, 1pkt/sec is too sensitive but 3pkt/sec is slightly unresponsive.
- */
- private static final double POOR_LINK_MIN_VOLUME = 2.0 * LINK_SAMPLING_INTERVAL_MS / 1000.0;
-
- /**
- * When a poor link is detected, we scan over this range (based on current
- * poor link RSSI) for a target RSSI that satisfies a target packet loss.
- * Refer to {@link #GOOD_LINK_TARGET}.
- * <p>
- * We want range_min not too small to avoid jumping back to WiFi too easily.
- */
- private static final int GOOD_LINK_RSSI_RANGE_MIN = 3;
-
- /**
- * See {@link #GOOD_LINK_RSSI_RANGE_MIN}.
- */
- private static final int GOOD_LINK_RSSI_RANGE_MAX = 20;
-
- /**
- * Adaptive good link target to avoid flapping.
- * When a poor link is detected, a good link target is calculated as follows:
- * <p>
- * targetRSSI = min { rssi | loss(rssi) < GOOD_LINK_LOSS_THRESHOLD } + rssi_adj[i],
- * where rssi is within the above GOOD_LINK_RSSI_RANGE.
- * targetCount = sample_count[i] .
- * <p>
- * While WiFi is being avoided, we keep monitoring its signal strength.
- * Good link notification is sent when we see current RSSI >= targetRSSI
- * for targetCount consecutive times.
- * <p>
- * Index i is incremented each time after a poor link detection.
- * Index i is decreased to at most k if the last poor link was at lease reduce_time[k] ago.
- * <p>
- * Intuitively, larger index i makes it more difficult to get back to WiFi, avoiding flapping.
- * In experiments, (+9 dB / 30 counts) makes it quite difficult to achieve.
- * Avoid using it unless flapping is really bad (say, last poor link is < 1 min ago).
- */
- private static final GoodLinkTarget[] GOOD_LINK_TARGET = {
- /* rssi_adj, sample_count, reduce_time */
- new GoodLinkTarget( 0, 3, 30 * 60000 ),
- new GoodLinkTarget( 3, 5, 5 * 60000 ),
- new GoodLinkTarget( 6, 10, 1 * 60000 ),
- new GoodLinkTarget( 9, 30, 0 * 60000 ),
- };
-
- /**
- * The max time to avoid a BSSID, to prevent avoiding forever.
- * If current RSSI is at least min_rssi[i], the max avoidance time is at most max_time[i]
- * <p>
- * It is unusual to experience high packet loss at high RSSI. Something unusual must be
- * happening (e.g. strong interference). For higher signal strengths, we set the avoidance
- * time to be low to allow for quick turn around from temporary interference.
- * <p>
- * See {@link BssidStatistics#poorLinkDetected}.
- */
- private static final MaxAvoidTime[] MAX_AVOID_TIME = {
- /* max_time, min_rssi */
- new MaxAvoidTime( 30 * 60000, -200 ),
- new MaxAvoidTime( 5 * 60000, -70 ),
- new MaxAvoidTime( 0 * 60000, -55 ),
- };
-
- /* Framework related */
- private Context mContext;
- private ContentResolver mContentResolver;
- private WifiManager mWifiManager;
- private IntentFilter mIntentFilter;
- private BroadcastReceiver mBroadcastReceiver;
- private AsyncChannel mWsmChannel = new AsyncChannel();
- private WifiInfo mWifiInfo;
- private LinkProperties mLinkProperties;
-
- /* System settingss related */
- private static boolean sWifiOnly = false;
- private boolean mPoorNetworkDetectionEnabled;
-
- /* Poor link detection related */
- private LruCache<String, BssidStatistics> mBssidCache =
- new LruCache<String, BssidStatistics>(BSSID_STAT_CACHE_SIZE);
- private int mRssiFetchToken = 0;
- private int mCurrentSignalLevel;
- private BssidStatistics mCurrentBssid;
- private VolumeWeightedEMA mCurrentLoss;
- private boolean mIsScreenOn = true;
- private static double sPresetLoss[];
-
- /* WiFi watchdog state machine related */
- private DefaultState mDefaultState = new DefaultState();
- private WatchdogDisabledState mWatchdogDisabledState = new WatchdogDisabledState();
- private WatchdogEnabledState mWatchdogEnabledState = new WatchdogEnabledState();
- private NotConnectedState mNotConnectedState = new NotConnectedState();
- private VerifyingLinkState mVerifyingLinkState = new VerifyingLinkState();
- private ConnectedState mConnectedState = new ConnectedState();
- private OnlineWatchState mOnlineWatchState = new OnlineWatchState();
- private LinkMonitoringState mLinkMonitoringState = new LinkMonitoringState();
- private OnlineState mOnlineState = new OnlineState();
-
- /**
- * STATE MAP
- * Default
- * / \
- * Disabled Enabled
- * / \ \
- * NotConnected Verifying Connected
- * /---------\
- * (all other states)
- */
- private WifiWatchdogStateMachine(Context context, Messenger dstMessenger) {
- super("WifiWatchdogStateMachine");
- mContext = context;
- mContentResolver = context.getContentResolver();
- mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-
- mWsmChannel.connectSync(mContext, getHandler(), dstMessenger);
-
- setupNetworkReceiver();
-
- // the content observer to listen needs a handler
- registerForSettingsChanges();
- registerForWatchdogToggle();
- addState(mDefaultState);
- addState(mWatchdogDisabledState, mDefaultState);
- addState(mWatchdogEnabledState, mDefaultState);
- addState(mNotConnectedState, mWatchdogEnabledState);
- addState(mVerifyingLinkState, mWatchdogEnabledState);
- addState(mConnectedState, mWatchdogEnabledState);
- addState(mOnlineWatchState, mConnectedState);
- addState(mLinkMonitoringState, mConnectedState);
- addState(mOnlineState, mConnectedState);
-
- if (isWatchdogEnabled()) {
- setInitialState(mNotConnectedState);
- } else {
- setInitialState(mWatchdogDisabledState);
- }
- setLogRecSize(25);
- setLogOnlyTransitions(true);
- updateSettings();
- }
-
- public static WifiWatchdogStateMachine makeWifiWatchdogStateMachine(Context context, Messenger dstMessenger) {
- ContentResolver contentResolver = context.getContentResolver();
-
- ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- sWifiOnly = (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
-
- // Watchdog is always enabled. Poor network detection can be seperately turned on/off
- // TODO: Remove this setting & clean up state machine since we always have
- // watchdog in an enabled state
- putSettingsGlobalBoolean(contentResolver, Settings.Global.WIFI_WATCHDOG_ON, true);
-
- WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context, dstMessenger);
- wwsm.start();
- return wwsm;
- }
-
- private void setupNetworkReceiver() {
- mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
- obtainMessage(EVENT_RSSI_CHANGE,
- intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200), 0).sendToTarget();
- } else if (action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)) {
- sendMessage(EVENT_SUPPLICANT_STATE_CHANGE, intent);
- } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
- sendMessage(EVENT_NETWORK_STATE_CHANGE, intent);
- } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
- sendMessage(EVENT_SCREEN_ON);
- } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
- sendMessage(EVENT_SCREEN_OFF);
- } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
- sendMessage(EVENT_WIFI_RADIO_STATE_CHANGE,intent.getIntExtra(
- WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN));
- }
- }
- };
-
- mIntentFilter = new IntentFilter();
- mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
- mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
- mIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
- mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
- mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
- }
-
- /**
- * Observes the watchdog on/off setting, and takes action when changed.
- */
- private void registerForWatchdogToggle() {
- ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
- @Override
- public void onChange(boolean selfChange) {
- sendMessage(EVENT_WATCHDOG_TOGGLED);
- }
- };
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.WIFI_WATCHDOG_ON),
- false, contentObserver);
- }
-
- /**
- * Observes watchdogs secure setting changes.
- */
- private void registerForSettingsChanges() {
- ContentObserver contentObserver = new ContentObserver(this.getHandler()) {
- @Override
- public void onChange(boolean selfChange) {
- sendMessage(EVENT_WATCHDOG_SETTINGS_CHANGE);
- }
- };
-
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED),
- false, contentObserver);
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- super.dump(fd, pw, args);
- pw.println("mWifiInfo: [" + mWifiInfo + "]");
- pw.println("mLinkProperties: [" + mLinkProperties + "]");
- pw.println("mCurrentSignalLevel: [" + mCurrentSignalLevel + "]");
- pw.println("mPoorNetworkDetectionEnabled: [" + mPoorNetworkDetectionEnabled + "]");
- }
-
- private boolean isWatchdogEnabled() {
- boolean ret = getSettingsGlobalBoolean(
- mContentResolver, Settings.Global.WIFI_WATCHDOG_ON, true);
- if (DBG) logd("Watchdog enabled " + ret);
- return ret;
- }
-
- private void updateSettings() {
- if (DBG) logd("Updating secure settings");
-
- // Unconditionally disable poor network avoidance, since this mechanism is obsolete
- mPoorNetworkDetectionEnabled = false;
- }
-
- /**
- * Default state, guard for unhandled messages.
- */
- class DefaultState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case EVENT_WATCHDOG_SETTINGS_CHANGE:
- updateSettings();
- if (DBG) logd("Updating wifi-watchdog secure settings");
- break;
- case EVENT_RSSI_CHANGE:
- mCurrentSignalLevel = calculateSignalLevel(msg.arg1);
- break;
- case EVENT_WIFI_RADIO_STATE_CHANGE:
- case EVENT_NETWORK_STATE_CHANGE:
- case EVENT_SUPPLICANT_STATE_CHANGE:
- case EVENT_BSSID_CHANGE:
- case CMD_RSSI_FETCH:
- case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
- case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
- // ignore
- break;
- case EVENT_SCREEN_ON:
- mIsScreenOn = true;
- break;
- case EVENT_SCREEN_OFF:
- mIsScreenOn = false;
- break;
- default:
- loge("Unhandled message " + msg + " in state " + getCurrentState().getName());
- break;
- }
- return HANDLED;
- }
- }
-
- /**
- * WiFi watchdog is disabled by the setting.
- */
- class WatchdogDisabledState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case EVENT_WATCHDOG_TOGGLED:
- if (isWatchdogEnabled())
- transitionTo(mNotConnectedState);
- return HANDLED;
- case EVENT_NETWORK_STATE_CHANGE:
- Intent intent = (Intent) msg.obj;
- NetworkInfo networkInfo = (NetworkInfo)
- intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-
- switch (networkInfo.getDetailedState()) {
- case VERIFYING_POOR_LINK:
- if (DBG) logd("Watchdog disabled, verify link");
- sendLinkStatusNotification(true);
- break;
- default:
- break;
- }
- break;
- }
- return NOT_HANDLED;
- }
- }
-
- /**
- * WiFi watchdog is enabled by the setting.
- */
- class WatchdogEnabledState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- }
-
- @Override
- public boolean processMessage(Message msg) {
- Intent intent;
- switch (msg.what) {
- case EVENT_WATCHDOG_TOGGLED:
- if (!isWatchdogEnabled())
- transitionTo(mWatchdogDisabledState);
- break;
-
- case EVENT_NETWORK_STATE_CHANGE:
- intent = (Intent) msg.obj;
- NetworkInfo networkInfo =
- (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
- if (DBG) logd("Network state change " + networkInfo.getDetailedState());
-
- mWifiInfo = (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
- updateCurrentBssid(mWifiInfo != null ? mWifiInfo.getBSSID() : null);
-
- switch (networkInfo.getDetailedState()) {
- case VERIFYING_POOR_LINK:
- mLinkProperties = (LinkProperties) intent.getParcelableExtra(
- WifiManager.EXTRA_LINK_PROPERTIES);
- if (mPoorNetworkDetectionEnabled) {
- if (mWifiInfo == null || mCurrentBssid == null) {
- loge("Ignore, wifiinfo " + mWifiInfo +" bssid " + mCurrentBssid);
- sendLinkStatusNotification(true);
- } else {
- transitionTo(mVerifyingLinkState);
- }
- } else {
- sendLinkStatusNotification(true);
- }
- break;
- case CONNECTED:
- transitionTo(mOnlineWatchState);
- break;
- default:
- transitionTo(mNotConnectedState);
- break;
- }
- break;
-
- case EVENT_SUPPLICANT_STATE_CHANGE:
- intent = (Intent) msg.obj;
- SupplicantState supplicantState = (SupplicantState) intent.getParcelableExtra(
- WifiManager.EXTRA_NEW_STATE);
- if (supplicantState == SupplicantState.COMPLETED) {
- mWifiInfo = mWifiManager.getConnectionInfo();
- updateCurrentBssid(mWifiInfo.getBSSID());
- }
- break;
-
- case EVENT_WIFI_RADIO_STATE_CHANGE:
- if (msg.arg1 == WifiManager.WIFI_STATE_DISABLING) {
- transitionTo(mNotConnectedState);
- }
- break;
-
- default:
- return NOT_HANDLED;
- }
-
- return HANDLED;
- }
- }
-
- /**
- * WiFi is disconnected.
- */
- class NotConnectedState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- }
- }
-
- /**
- * WiFi is connected, but waiting for good link detection message.
- */
- class VerifyingLinkState extends State {
-
- private int mSampleCount;
-
- @Override
- public void enter() {
- if (DBG) logd(getName());
- mSampleCount = 0;
- if (mCurrentBssid != null) mCurrentBssid.newLinkDetected();
- sendMessage(obtainMessage(CMD_RSSI_FETCH, ++mRssiFetchToken, 0));
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case EVENT_WATCHDOG_SETTINGS_CHANGE:
- updateSettings();
- if (!mPoorNetworkDetectionEnabled) {
- sendLinkStatusNotification(true);
- }
- break;
-
- case EVENT_BSSID_CHANGE:
- transitionTo(mVerifyingLinkState);
- break;
-
- case CMD_RSSI_FETCH:
- if (msg.arg1 == mRssiFetchToken) {
- mWsmChannel.sendMessage(WifiManager.RSSI_PKTCNT_FETCH);
- sendMessageDelayed(obtainMessage(CMD_RSSI_FETCH, ++mRssiFetchToken, 0),
- LINK_SAMPLING_INTERVAL_MS);
- }
- break;
-
- case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
- if (mCurrentBssid == null || msg.obj == null) {
- break;
- }
- RssiPacketCountInfo info = (RssiPacketCountInfo) msg.obj;
- int rssi = info.rssi;
- if (DBG) logd("Fetch RSSI succeed, rssi=" + rssi);
-
- long time = mCurrentBssid.mBssidAvoidTimeMax - SystemClock.elapsedRealtime();
- if (time <= 0) {
- // max avoidance time is met
- if (DBG) logd("Max avoid time elapsed");
- sendLinkStatusNotification(true);
- } else {
- if (rssi >= mCurrentBssid.mGoodLinkTargetRssi) {
- if (++mSampleCount >= mCurrentBssid.mGoodLinkTargetCount) {
- // link is good again
- if (DBG) logd("Good link detected, rssi=" + rssi);
- mCurrentBssid.mBssidAvoidTimeMax = 0;
- sendLinkStatusNotification(true);
- }
- } else {
- mSampleCount = 0;
- if (DBG) logd("Link is still poor, time left=" + time);
- }
- }
- break;
-
- case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
- if (DBG) logd("RSSI_FETCH_FAILED");
- break;
-
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- /**
- * WiFi is connected and link is verified.
- */
- class ConnectedState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case EVENT_WATCHDOG_SETTINGS_CHANGE:
- updateSettings();
- if (mPoorNetworkDetectionEnabled) {
- transitionTo(mOnlineWatchState);
- } else {
- transitionTo(mOnlineState);
- }
- return HANDLED;
- }
- return NOT_HANDLED;
- }
- }
-
- /**
- * RSSI is high enough and don't need link monitoring.
- */
- class OnlineWatchState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- if (mPoorNetworkDetectionEnabled) {
- // treat entry as an rssi change
- handleRssiChange();
- } else {
- transitionTo(mOnlineState);
- }
- }
-
- private void handleRssiChange() {
- if (mCurrentSignalLevel <= LINK_MONITOR_LEVEL_THRESHOLD && mCurrentBssid != null) {
- transitionTo(mLinkMonitoringState);
- } else {
- // stay here
- }
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case EVENT_RSSI_CHANGE:
- mCurrentSignalLevel = calculateSignalLevel(msg.arg1);
- handleRssiChange();
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- /**
- * Keep sampling the link and monitor any poor link situation.
- */
- class LinkMonitoringState extends State {
-
- private int mSampleCount;
-
- private int mLastRssi;
- private int mLastTxGood;
- private int mLastTxBad;
-
- @Override
- public void enter() {
- if (DBG) logd(getName());
- mSampleCount = 0;
- mCurrentLoss = new VolumeWeightedEMA(EXP_COEFFICIENT_MONITOR);
- sendMessage(obtainMessage(CMD_RSSI_FETCH, ++mRssiFetchToken, 0));
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case EVENT_RSSI_CHANGE:
- mCurrentSignalLevel = calculateSignalLevel(msg.arg1);
- if (mCurrentSignalLevel <= LINK_MONITOR_LEVEL_THRESHOLD) {
- // stay here;
- } else {
- // we don't need frequent RSSI monitoring any more
- transitionTo(mOnlineWatchState);
- }
- break;
-
- case EVENT_BSSID_CHANGE:
- transitionTo(mLinkMonitoringState);
- break;
-
- case CMD_RSSI_FETCH:
- if (!mIsScreenOn) {
- transitionTo(mOnlineState);
- } else if (msg.arg1 == mRssiFetchToken) {
- mWsmChannel.sendMessage(WifiManager.RSSI_PKTCNT_FETCH);
- sendMessageDelayed(obtainMessage(CMD_RSSI_FETCH, ++mRssiFetchToken, 0),
- LINK_SAMPLING_INTERVAL_MS);
- }
- break;
-
- case WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED:
- if (mCurrentBssid == null) {
- break;
- }
- RssiPacketCountInfo info = (RssiPacketCountInfo) msg.obj;
- int rssi = info.rssi;
- int mrssi = (mLastRssi + rssi) / 2;
- int txbad = info.txbad;
- int txgood = info.txgood;
- if (DBG) logd("Fetch RSSI succeed, rssi=" + rssi + " mrssi=" + mrssi + " txbad="
- + txbad + " txgood=" + txgood);
-
- // skip the first data point as we want incremental values
- long now = SystemClock.elapsedRealtime();
- if (now - mCurrentBssid.mLastTimeSample < LINK_SAMPLING_INTERVAL_MS * 2) {
-
- // update packet loss statistics
- int dbad = txbad - mLastTxBad;
- int dgood = txgood - mLastTxGood;
- int dtotal = dbad + dgood;
-
- if (dtotal > 0) {
- // calculate packet loss in the last sampling interval
- double loss = ((double) dbad) / ((double) dtotal);
-
- mCurrentLoss.update(loss, dtotal);
-
- if (DBG) {
- DecimalFormat df = new DecimalFormat("#.##");
- logd("Incremental loss=" + dbad + "/" + dtotal + " Current loss="
- + df.format(mCurrentLoss.mValue * 100) + "% volume="
- + df.format(mCurrentLoss.mVolume));
- }
-
- mCurrentBssid.updateLoss(mrssi, loss, dtotal);
-
- // check for high packet loss and send poor link notification
- if (mCurrentLoss.mValue > POOR_LINK_LOSS_THRESHOLD
- && mCurrentLoss.mVolume > POOR_LINK_MIN_VOLUME) {
- if (++mSampleCount >= POOR_LINK_SAMPLE_COUNT)
- if (mCurrentBssid.poorLinkDetected(rssi)) {
- sendLinkStatusNotification(false);
- ++mRssiFetchToken;
- }
- } else {
- mSampleCount = 0;
- }
- }
- }
-
- mCurrentBssid.mLastTimeSample = now;
- mLastTxBad = txbad;
- mLastTxGood = txgood;
- mLastRssi = rssi;
- break;
-
- case WifiManager.RSSI_PKTCNT_FETCH_FAILED:
- // can happen if we are waiting to get a disconnect notification
- if (DBG) logd("RSSI_FETCH_FAILED");
- break;
-
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- /**
- * Child state of ConnectedState indicating that we are online and there is nothing to do.
- */
- class OnlineState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case EVENT_SCREEN_ON:
- mIsScreenOn = true;
- if (mPoorNetworkDetectionEnabled)
- transitionTo(mOnlineWatchState);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- private void updateCurrentBssid(String bssid) {
- if (DBG) logd("Update current BSSID to " + (bssid != null ? bssid : "null"));
-
- // if currently not connected, then set current BSSID to null
- if (bssid == null) {
- if (mCurrentBssid == null) return;
- mCurrentBssid = null;
- if (DBG) logd("BSSID changed");
- sendMessage(EVENT_BSSID_CHANGE);
- return;
- }
-
- // if it is already the current BSSID, then done
- if (mCurrentBssid != null && bssid.equals(mCurrentBssid.mBssid)) return;
-
- // search for the new BSSID in the cache, add to cache if not found
- mCurrentBssid = mBssidCache.get(bssid);
- if (mCurrentBssid == null) {
- mCurrentBssid = new BssidStatistics(bssid);
- mBssidCache.put(bssid, mCurrentBssid);
- }
-
- // send BSSID change notification
- if (DBG) logd("BSSID changed");
- sendMessage(EVENT_BSSID_CHANGE);
- }
-
- private int calculateSignalLevel(int rssi) {
- int signalLevel = WifiManager.calculateSignalLevel(rssi, WifiManager.RSSI_LEVELS);
- if (DBG)
- logd("RSSI current: " + mCurrentSignalLevel + " new: " + rssi + ", " + signalLevel);
- return signalLevel;
- }
-
- private void sendLinkStatusNotification(boolean isGood) {
- if (DBG) logd("########################################");
- if (isGood) {
- mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
- if (mCurrentBssid != null) {
- mCurrentBssid.mLastTimeGood = SystemClock.elapsedRealtime();
- }
- if (DBG) logd("Good link notification is sent");
- } else {
- mWsmChannel.sendMessage(POOR_LINK_DETECTED);
- if (mCurrentBssid != null) {
- mCurrentBssid.mLastTimePoor = SystemClock.elapsedRealtime();
- }
- logd("Poor link notification is sent");
- }
- }
-
- /**
- * Convenience function for retrieving a single secure settings value as a
- * boolean. Note that internally setting values are always stored as
- * strings; this function converts the string to a boolean for you. The
- * default value will be returned if the setting is not defined or not a
- * valid boolean.
- *
- * @param cr The ContentResolver to access.
- * @param name The name of the setting to retrieve.
- * @param def Value to return if the setting is not defined.
- * @return The setting's current value, or 'def' if it is not defined or not
- * a valid boolean.
- */
- private static boolean getSettingsGlobalBoolean(ContentResolver cr, String name, boolean def) {
- return Settings.Global.getInt(cr, name, def ? 1 : 0) == 1;
- }
-
- /**
- * Convenience function for updating a single settings value as an integer.
- * This will either create a new entry in the table if the given name does
- * not exist, or modify the value of the existing row with that name. Note
- * that internally setting values are always stored as strings, so this
- * function converts the given value to a string before storing it.
- *
- * @param cr The ContentResolver to access.
- * @param name The name of the setting to modify.
- * @param value The new value for the setting.
- * @return true if the value was set, false on database errors
- */
- private static boolean putSettingsGlobalBoolean(ContentResolver cr, String name, boolean value) {
- return Settings.Global.putInt(cr, name, value ? 1 : 0);
- }
-
- /**
- * Bundle of good link count parameters
- */
- private static class GoodLinkTarget {
- public final int RSSI_ADJ_DBM;
- public final int SAMPLE_COUNT;
- public final int REDUCE_TIME_MS;
- public GoodLinkTarget(int adj, int count, int time) {
- RSSI_ADJ_DBM = adj;
- SAMPLE_COUNT = count;
- REDUCE_TIME_MS = time;
- }
- }
-
- /**
- * Bundle of max avoidance time parameters
- */
- private static class MaxAvoidTime {
- public final int TIME_MS;
- public final int MIN_RSSI_DBM;
- public MaxAvoidTime(int time, int rssi) {
- TIME_MS = time;
- MIN_RSSI_DBM = rssi;
- }
- }
-
- /**
- * Volume-weighted Exponential Moving Average (V-EMA)
- * - volume-weighted: each update has its own weight (number of packets)
- * - exponential: O(1) time and O(1) space for both update and query
- * - moving average: reflect most recent results and expire old ones
- */
- private class VolumeWeightedEMA {
- private double mValue;
- private double mVolume;
- private double mProduct;
- private final double mAlpha;
-
- public VolumeWeightedEMA(double coefficient) {
- mValue = 0.0;
- mVolume = 0.0;
- mProduct = 0.0;
- mAlpha = coefficient;
- }
-
- public void update(double newValue, int newVolume) {
- if (newVolume <= 0) return;
- // core update formulas
- double newProduct = newValue * newVolume;
- mProduct = mAlpha * newProduct + (1 - mAlpha) * mProduct;
- mVolume = mAlpha * newVolume + (1 - mAlpha) * mVolume;
- mValue = mProduct / mVolume;
- }
- }
-
- /**
- * Record (RSSI -> pakce loss %) mappings of one BSSID
- */
- private class BssidStatistics {
-
- /* MAC address of this BSSID */
- private final String mBssid;
-
- /* RSSI -> packet loss % mappings */
- private VolumeWeightedEMA[] mEntries;
- private int mRssiBase;
- private int mEntriesSize;
-
- /* Target to send good link notification, set when poor link is detected */
- private int mGoodLinkTargetRssi;
- private int mGoodLinkTargetCount;
-
- /* Index of GOOD_LINK_TARGET array */
- private int mGoodLinkTargetIndex;
-
- /* Timestamps of some last events */
- private long mLastTimeSample;
- private long mLastTimeGood;
- private long mLastTimePoor;
-
- /* Max time to avoid this BSSID */
- private long mBssidAvoidTimeMax;
-
- /**
- * Constructor
- *
- * @param bssid is the address of this BSSID
- */
- public BssidStatistics(String bssid) {
- this.mBssid = bssid;
- mRssiBase = BSSID_STAT_RANGE_LOW_DBM;
- mEntriesSize = BSSID_STAT_RANGE_HIGH_DBM - BSSID_STAT_RANGE_LOW_DBM + 1;
- mEntries = new VolumeWeightedEMA[mEntriesSize];
- for (int i = 0; i < mEntriesSize; i++)
- mEntries[i] = new VolumeWeightedEMA(EXP_COEFFICIENT_RECORD);
- }
-
- /**
- * Update this BSSID cache
- *
- * @param rssi is the RSSI
- * @param value is the new instant loss value at this RSSI
- * @param volume is the volume for this single update
- */
- public void updateLoss(int rssi, double value, int volume) {
- if (volume <= 0) return;
- int index = rssi - mRssiBase;
- if (index < 0 || index >= mEntriesSize) return;
- mEntries[index].update(value, volume);
- if (DBG) {
- DecimalFormat df = new DecimalFormat("#.##");
- logd("Cache updated: loss[" + rssi + "]=" + df.format(mEntries[index].mValue * 100)
- + "% volume=" + df.format(mEntries[index].mVolume));
- }
- }
-
- /**
- * Get preset loss if the cache has insufficient data, observed from experiments.
- *
- * @param rssi is the input RSSI
- * @return preset loss of the given RSSI
- */
- public double presetLoss(int rssi) {
- if (rssi <= -90) return 1.0;
- if (rssi > 0) return 0.0;
-
- if (sPresetLoss == null) {
- // pre-calculate all preset losses only once, then reuse them
- final int size = 90;
- sPresetLoss = new double[size];
- for (int i = 0; i < size; i++) sPresetLoss[i] = 1.0 / Math.pow(90 - i, 1.5);
- }
- return sPresetLoss[-rssi];
- }
-
- /**
- * A poor link is detected, calculate a target RSSI to bring WiFi back.
- *
- * @param rssi is the current RSSI
- * @return true iff the current BSSID should be avoided
- */
- public boolean poorLinkDetected(int rssi) {
- if (DBG) logd("Poor link detected, rssi=" + rssi);
-
- long now = SystemClock.elapsedRealtime();
- long lastGood = now - mLastTimeGood;
- long lastPoor = now - mLastTimePoor;
-
- // reduce the difficulty of good link target if last avoidance was long time ago
- while (mGoodLinkTargetIndex > 0
- && lastPoor >= GOOD_LINK_TARGET[mGoodLinkTargetIndex - 1].REDUCE_TIME_MS)
- mGoodLinkTargetIndex--;
- mGoodLinkTargetCount = GOOD_LINK_TARGET[mGoodLinkTargetIndex].SAMPLE_COUNT;
-
- // scan for a target RSSI at which the link is good
- int from = rssi + GOOD_LINK_RSSI_RANGE_MIN;
- int to = rssi + GOOD_LINK_RSSI_RANGE_MAX;
- mGoodLinkTargetRssi = findRssiTarget(from, to, GOOD_LINK_LOSS_THRESHOLD);
- mGoodLinkTargetRssi += GOOD_LINK_TARGET[mGoodLinkTargetIndex].RSSI_ADJ_DBM;
- if (mGoodLinkTargetIndex < GOOD_LINK_TARGET.length - 1) mGoodLinkTargetIndex++;
-
- // calculate max avoidance time to prevent avoiding forever
- int p = 0, pmax = MAX_AVOID_TIME.length - 1;
- while (p < pmax && rssi >= MAX_AVOID_TIME[p + 1].MIN_RSSI_DBM) p++;
- long avoidMax = MAX_AVOID_TIME[p].TIME_MS;
-
- // don't avoid if max avoidance time is 0 (RSSI is super high)
- if (avoidMax <= 0) return false;
-
- // set max avoidance time, send poor link notification
- mBssidAvoidTimeMax = now + avoidMax;
-
- if (DBG) logd("goodRssi=" + mGoodLinkTargetRssi + " goodCount=" + mGoodLinkTargetCount
- + " lastGood=" + lastGood + " lastPoor=" + lastPoor + " avoidMax=" + avoidMax);
-
- return true;
- }
-
- /**
- * A new BSSID is connected, recalculate target RSSI threshold
- */
- public void newLinkDetected() {
- // if this BSSID is currently being avoided, the reuse those values
- if (mBssidAvoidTimeMax > 0) {
- if (DBG) logd("Previous avoidance still in effect, rssi=" + mGoodLinkTargetRssi
- + " count=" + mGoodLinkTargetCount);
- return;
- }
-
- // calculate a new RSSI threshold for new link verifying
- int from = BSSID_STAT_RANGE_LOW_DBM;
- int to = BSSID_STAT_RANGE_HIGH_DBM;
- mGoodLinkTargetRssi = findRssiTarget(from, to, GOOD_LINK_LOSS_THRESHOLD);
- mGoodLinkTargetCount = 1;
- mBssidAvoidTimeMax = SystemClock.elapsedRealtime() + MAX_AVOID_TIME[0].TIME_MS;
- if (DBG) logd("New link verifying target set, rssi=" + mGoodLinkTargetRssi + " count="
- + mGoodLinkTargetCount);
- }
-
- /**
- * Return the first RSSI within the range where loss[rssi] < threshold
- *
- * @param from start scanning from this RSSI
- * @param to stop scanning at this RSSI
- * @param threshold target threshold for scanning
- * @return target RSSI
- */
- public int findRssiTarget(int from, int to, double threshold) {
- from -= mRssiBase;
- to -= mRssiBase;
- int emptyCount = 0;
- int d = from < to ? 1 : -1;
- for (int i = from; i != to; i += d)
- // don't use a data point if it volume is too small (statistically unreliable)
- if (i >= 0 && i < mEntriesSize && mEntries[i].mVolume > 1.0) {
- emptyCount = 0;
- if (mEntries[i].mValue < threshold) {
- // scan target found
- int rssi = mRssiBase + i;
- if (DBG) {
- DecimalFormat df = new DecimalFormat("#.##");
- logd("Scan target found: rssi=" + rssi + " threshold="
- + df.format(threshold * 100) + "% value="
- + df.format(mEntries[i].mValue * 100) + "% volume="
- + df.format(mEntries[i].mVolume));
- }
- return rssi;
- }
- } else if (++emptyCount >= BSSID_STAT_EMPTY_COUNT) {
- // cache has insufficient data around this RSSI, use preset loss instead
- int rssi = mRssiBase + i;
- double lossPreset = presetLoss(rssi);
- if (lossPreset < threshold) {
- if (DBG) {
- DecimalFormat df = new DecimalFormat("#.##");
- logd("Scan target found: rssi=" + rssi + " threshold="
- + df.format(threshold * 100) + "% value="
- + df.format(lossPreset * 100) + "% volume=preset");
- }
- return rssi;
- }
- }
-
- return mRssiBase + to;
- }
- }
-}