summaryrefslogtreecommitdiffstats
path: root/wifi/java/android/net
diff options
context:
space:
mode:
authorIrfan Sheriff <isheriff@google.com>2012-01-27 21:00:19 -0800
committerIrfan Sheriff <isheriff@google.com>2012-02-29 17:33:36 -0800
commit07573b32494acbabd21979d8b9584c1ed3f7a6ad (patch)
treead3a4a15c889b4b175717cd38a28a92601ed82ae /wifi/java/android/net
parent6b48f088b1e0bed242118422c88fea8d714307e0 (diff)
downloadframeworks_base-07573b32494acbabd21979d8b9584c1ed3f7a6ad.zip
frameworks_base-07573b32494acbabd21979d8b9584c1ed3f7a6ad.tar.gz
frameworks_base-07573b32494acbabd21979d8b9584c1ed3f7a6ad.tar.bz2
Improve Wi-Fi hand-off
When Wi-fi connects at L2 layer, the beacons reach and the device can maintain a connection to the access point, but the application connectivity can be flaky (due to bigger packet size exchange). We now use Watchdog to monitor the quality of the last hop on Wi-Fi using signal strength and ARP connectivity as indicators to decide if the link is good enough to switch to Wi-Fi as the uplink. ARP pings are useful for link validation but can still get through when the application traffic fails to go through and thus not best indicator real packet loss since they are tiny packets (28 bytes) and have much low chance of packet corruption than the regular data packets. Signal strength and ARP used together ends up working well in tests. The goal is to switch to Wi-Fi after validating ARP transfer and RSSI and then switching out of Wi-Fi when we hit a low signal strength threshold and waiting until the signal strength improves and validating ARP transfer. Change-Id: Ica593291ec7772da892f03cf45b649635b730c47
Diffstat (limited to 'wifi/java/android/net')
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl4
-rw-r--r--wifi/java/android/net/wifi/SupplicantState.java4
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java21
-rw-r--r--wifi/java/android/net/wifi/WifiStateMachine.java291
-rw-r--r--wifi/java/android/net/wifi/WifiWatchdogStateMachine.java993
5 files changed, 532 insertions, 781 deletions
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 61dfebf..6b08074 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -101,7 +101,9 @@ interface IWifiManager
void clearBlacklist();
- Messenger getMessenger();
+ Messenger getWifiServiceMessenger();
+
+ Messenger getWifiStateMachineMessenger();
String getConfigFile();
}
diff --git a/wifi/java/android/net/wifi/SupplicantState.java b/wifi/java/android/net/wifi/SupplicantState.java
index 509b02c..4a2037d 100644
--- a/wifi/java/android/net/wifi/SupplicantState.java
+++ b/wifi/java/android/net/wifi/SupplicantState.java
@@ -171,8 +171,8 @@ public enum SupplicantState implements Parcelable {
}
- /* Supplicant associating or authenticating is considered a handshake state */
- static boolean isHandshakeState(SupplicantState state) {
+ /** Supplicant associating or authenticating is considered a handshake state {@hide} */
+ public static boolean isHandshakeState(SupplicantState state) {
switch(state) {
case AUTHENTICATING:
case ASSOCIATING:
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 1a0e0da..1acfd3a 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1096,7 +1096,7 @@ public class WifiManager {
* @hide
*/
public void asyncConnect(Context srcContext, Handler srcHandler) {
- mAsyncChannel.connect(srcContext, srcHandler, getMessenger());
+ mAsyncChannel.connect(srcContext, srcHandler, getWifiServiceMessenger());
}
/**
@@ -1197,15 +1197,30 @@ public class WifiManager {
* @return Messenger pointing to the WifiService handler
* @hide
*/
- public Messenger getMessenger() {
+ public Messenger getWifiServiceMessenger() {
try {
- return mService.getMessenger();
+ return mService.getWifiServiceMessenger();
} catch (RemoteException e) {
return null;
}
}
/**
+ * Get a reference to WifiStateMachine handler.
+ * @return Messenger pointing to the WifiService handler
+ * @hide
+ */
+ public Messenger getWifiStateMachineMessenger() {
+ try {
+ return mService.getWifiStateMachineMessenger();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+
+
+ /**
* Returns the file in which IP and proxy configuration data is stored
* @hide
*/
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 1b64f3e..e140d80 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -463,8 +463,12 @@ public class WifiStateMachine extends StateMachine {
private State mScanModeState = new ScanModeState();
/* Connecting to an access point */
private State mConnectModeState = new ConnectModeState();
- /* Fetching IP after network connection (assoc+auth complete) */
- private State mConnectingState = new ConnectingState();
+ /* Connected at 802.11 (L2) level */
+ 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();
/* disconnect issued, waiting for network disconnect confirmation */
@@ -629,8 +633,10 @@ public class WifiStateMachine extends StateMachine {
addState(mDriverStartedState, mSupplicantStartedState);
addState(mScanModeState, mDriverStartedState);
addState(mConnectModeState, mDriverStartedState);
- addState(mConnectingState, mConnectModeState);
- addState(mConnectedState, mConnectModeState);
+ addState(mL2ConnectedState, mConnectModeState);
+ addState(mObtainingIpState, mL2ConnectedState);
+ addState(mVerifyingLinkState, mL2ConnectedState);
+ addState(mConnectedState, mL2ConnectedState);
addState(mDisconnectingState, mConnectModeState);
addState(mDisconnectedState, mConnectModeState);
addState(mWaitForWpsCompletionState, mConnectModeState);
@@ -655,6 +661,9 @@ public class WifiStateMachine extends StateMachine {
* Methods exposed for public use
********************************************************/
+ public Messenger getMessenger() {
+ return new Messenger(getHandler());
+ }
/**
* TODO: doc
*/
@@ -1543,12 +1552,14 @@ public class WifiStateMachine extends StateMachine {
Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties (mLinkProperties));
if (bssid != null)
intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
- if (mNetworkInfo.getState() == NetworkInfo.State.CONNECTED)
+ if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK ||
+ mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) {
intent.putExtra(WifiManager.EXTRA_WIFI_INFO, new WifiInfo(mWifiInfo));
+ }
mContext.sendStickyBroadcast(intent);
}
@@ -1740,9 +1751,6 @@ public class WifiStateMachine extends StateMachine {
}
} else {
configureLinkProperties();
- setNetworkDetailedState(DetailedState.CONNECTED);
- mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
- sendNetworkStateChangeBroadcast(mLastBssid);
}
}
@@ -1890,6 +1898,8 @@ public class WifiStateMachine extends StateMachine {
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:
break;
case WifiMonitor.DRIVER_HUNG_EVENT:
setWifiEnabled(false);
@@ -2885,7 +2895,7 @@ public class WifiStateMachine extends StateMachine {
/* send event to CM & network change broadcast */
setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
sendNetworkStateChangeBroadcast(mLastBssid);
- transitionTo(mConnectingState);
+ transitionTo(mObtainingIpState);
break;
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
if (DBG) log("Network connection lost");
@@ -2900,122 +2910,18 @@ public class WifiStateMachine extends StateMachine {
}
}
- class ConnectingState extends State {
-
- @Override
- public void enter() {
- if (DBG) log(getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- try {
- mNwService.enableIpv6(mInterfaceName);
- } catch (RemoteException re) {
- loge("Failed to enable IPv6: " + re);
- } catch (IllegalStateException e) {
- loge("Failed to enable IPv6: " + e);
- }
-
- if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
- //start DHCP
- mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
- mContext, WifiStateMachine.this, mInterfaceName);
- mDhcpStateMachine.registerForPreDhcpNotification();
- mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
- } else {
- DhcpInfoInternal dhcpInfoInternal = mWifiConfigStore.getIpConfiguration(
- mLastNetworkId);
- InterfaceConfiguration ifcg = new InterfaceConfiguration();
- ifcg.setLinkAddress(dhcpInfoInternal.makeLinkAddress());
- ifcg.setInterfaceUp();
- try {
- mNwService.setInterfaceConfig(mInterfaceName, ifcg);
- if (DBG) log("Static IP configuration succeeded");
- sendMessage(CMD_STATIC_IP_SUCCESS, dhcpInfoInternal);
- } catch (RemoteException re) {
- loge("Static IP configuration failed: " + re);
- sendMessage(CMD_STATIC_IP_FAILURE);
- } catch (IllegalStateException e) {
- loge("Static IP configuration failed: " + e);
- sendMessage(CMD_STATIC_IP_FAILURE);
- }
- }
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) log(getName() + message.toString() + "\n");
-
- switch(message.what) {
- case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
- handlePreDhcpSetup();
- mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
- break;
- case DhcpStateMachine.CMD_POST_DHCP_ACTION:
- handlePostDhcpSetup();
- if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
- handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
- transitionTo(mConnectedState);
- } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
- handleFailedIpConfiguration();
- transitionTo(mDisconnectingState);
- }
- break;
- case CMD_STATIC_IP_SUCCESS:
- handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
- transitionTo(mConnectedState);
- break;
- case CMD_STATIC_IP_FAILURE:
- handleFailedIpConfiguration();
- transitionTo(mDisconnectingState);
- break;
- case CMD_DISCONNECT:
- mWifiNative.disconnect();
- transitionTo(mDisconnectingState);
- break;
- /* Ignore connection to same network */
- case CMD_CONNECT_NETWORK:
- int netId = message.arg1;
- if (mWifiInfo.getNetworkId() == netId) {
- break;
- }
- return NOT_HANDLED;
- case CMD_SAVE_NETWORK:
- deferMessage(message);
- break;
- /* Ignore */
- case WifiMonitor.NETWORK_CONNECTION_EVENT:
- break;
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- }
- break;
- /* Defer scan when IP is being fetched */
- case CMD_START_SCAN:
- deferMessage(message);
- break;
- /* Defer any power mode changes since we must keep active power mode at DHCP */
- case CMD_SET_HIGH_PERF_MODE:
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
- class ConnectedState extends State {
+ class L2ConnectedState extends State {
@Override
public void enter() {
if (DBG) log(getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
mRssiPollToken++;
if (mEnableRssiPolling) {
- sendMessage(obtainMessage(WifiStateMachine.CMD_RSSI_POLL, mRssiPollToken, 0));
+ sendMessage(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0));
}
}
+
@Override
public boolean processMessage(Message message) {
if (DBG) log(getName() + message.toString() + "\n");
@@ -3028,8 +2934,11 @@ public class WifiStateMachine extends StateMachine {
case DhcpStateMachine.CMD_POST_DHCP_ACTION:
handlePostDhcpSetup();
if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
+ if (DBG) log("DHCP successful");
handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
+ transitionTo(mVerifyingLinkState);
} else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
+ if (DBG) log("DHCP failed");
handleFailedIpConfiguration();
transitionTo(mDisconnectingState);
}
@@ -3067,7 +2976,7 @@ public class WifiStateMachine extends StateMachine {
if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
if (result.hasIpChanged()) {
log("Reconfiguring IP on connection");
- transitionTo(mConnectingState);
+ transitionTo(mObtainingIpState);
}
if (result.hasProxyChanged()) {
log("Reconfiguring proxy on connection");
@@ -3084,7 +2993,7 @@ public class WifiStateMachine extends StateMachine {
if (message.arg1 == mRssiPollToken) {
// Get Info and continue polling
fetchRssiAndLinkSpeedNative();
- sendMessageDelayed(obtainMessage(WifiStateMachine.CMD_RSSI_POLL,
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
} else {
// Polling has completed
@@ -3096,25 +3005,22 @@ public class WifiStateMachine extends StateMachine {
if (mEnableRssiPolling) {
// first poll
fetchRssiAndLinkSpeedNative();
- sendMessageDelayed(obtainMessage(WifiStateMachine.CMD_RSSI_POLL,
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL,
mRssiPollToken, 0), POLL_RSSI_INTERVAL_MSECS);
}
break;
default:
return NOT_HANDLED;
}
+
if (eventLoggingEnabled) {
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
}
return HANDLED;
}
+
@Override
public void exit() {
-
- /* Request a CS wakelock during transition to mobile */
- checkAndSetConnectivityInstance();
- mCm.requestNetworkTransitionWakelock(TAG);
-
/* If a scan result is pending in connected state, the supplicant
* is in SCAN_ONLY_MODE. Restore CONNECT_MODE on exit
*/
@@ -3124,6 +3030,141 @@ public class WifiStateMachine extends StateMachine {
}
}
+ class ObtainingIpState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
+ //start DHCP
+ mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
+ mContext, WifiStateMachine.this, mInterfaceName);
+ mDhcpStateMachine.registerForPreDhcpNotification();
+ mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
+ } else {
+ DhcpInfoInternal dhcpInfoInternal = mWifiConfigStore.getIpConfiguration(
+ mLastNetworkId);
+ InterfaceConfiguration ifcg = new InterfaceConfiguration();
+ ifcg.setLinkAddress(dhcpInfoInternal.makeLinkAddress());
+ ifcg.setInterfaceUp();
+ try {
+ mNwService.setInterfaceConfig(mInterfaceName, ifcg);
+ if (DBG) log("Static IP configuration succeeded");
+ sendMessage(CMD_STATIC_IP_SUCCESS, dhcpInfoInternal);
+ } catch (RemoteException re) {
+ loge("Static IP configuration failed: " + re);
+ sendMessage(CMD_STATIC_IP_FAILURE);
+ } catch (IllegalStateException e) {
+ loge("Static IP configuration failed: " + e);
+ sendMessage(CMD_STATIC_IP_FAILURE);
+ }
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) log(getName() + message.toString() + "\n");
+ switch(message.what) {
+ case CMD_STATIC_IP_SUCCESS:
+ handleSuccessfulIpConfiguration((DhcpInfoInternal) message.obj);
+ transitionTo(mVerifyingLinkState);
+ break;
+ case CMD_STATIC_IP_FAILURE:
+ handleFailedIpConfiguration();
+ transitionTo(mDisconnectingState);
+ break;
+ case CMD_SAVE_NETWORK:
+ deferMessage(message);
+ break;
+ /* Defer any power mode changes since we must keep active power mode at DHCP */
+ case CMD_SET_HIGH_PERF_MODE:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class VerifyingLinkState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
+ mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch (message.what) {
+ case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
+ //stay here
+ break;
+ case WifiWatchdogStateMachine.GOOD_LINK_DETECTED:
+ try {
+ mNwService.enableIpv6(mInterfaceName);
+ } catch (RemoteException re) {
+ loge("Failed to enable IPv6: " + re);
+ } catch (IllegalStateException e) {
+ loge("Failed to enable IPv6: " + e);
+ }
+
+ setNetworkDetailedState(DetailedState.CONNECTED);
+ mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+ transitionTo(mConnectedState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class ConnectedState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) log(getName() + message.toString() + "\n");
+ switch (message.what) {
+ case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
+ if (DBG) log("Watchdog reports poor link");
+ try {
+ mNwService.disableIpv6(mInterfaceName);
+ } catch (RemoteException re) {
+ loge("Failed to disable IPv6: " + re);
+ } catch (IllegalStateException e) {
+ loge("Failed to disable IPv6: " + e);
+ }
+ /* Report a disconnect */
+ setNetworkDetailedState(DetailedState.DISCONNECTED);
+ mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+
+ transitionTo(mVerifyingLinkState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ @Override
+ public void exit() {
+ /* Request a CS wakelock during transition to mobile */
+ checkAndSetConnectivityInstance();
+ mCm.requestNetworkTransitionWakelock(TAG);
+ }
+ }
+
class DisconnectingState extends State {
@Override
public void enter() {
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 0ca3852..a2f6343 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -26,9 +26,12 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.net.arp.ArpPeer;
import android.net.ConnectivityManager;
-import android.net.DnsPinger;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
import android.net.NetworkInfo;
+import android.net.RouteInfo;
import android.net.Uri;
import android.os.Message;
import android.os.SystemClock;
@@ -38,6 +41,7 @@ import android.provider.Settings.Secure;
import android.util.Log;
import com.android.internal.R;
+import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -46,49 +50,66 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.InetAddress;
+import java.net.SocketException;
import java.net.URL;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
/**
- * {@link WifiWatchdogStateMachine} monitors the initial connection to a Wi-Fi
- * network with multiple access points. After the framework successfully
- * connects to an access point, the watchdog verifies connectivity by 'pinging'
- * the configured DNS server using {@link DnsPinger}.
- * <p>
- * On DNS check failure, the BSSID is blacklisted if it is reasonably likely
- * that another AP might have internet access; otherwise the SSID is disabled.
- * <p>
- * On DNS success, the WatchdogService initiates a walled garden check via an
- * http get. A browser window is activated if a walled garden is detected.
+ * WifiWatchdogStateMachine monitors the connection to a Wi-Fi
+ * network. After the framework notifies that it has connected to an
+ * acccess point and is waiting for link to be verified, the watchdog
+ * takes over and verifies if the link is good by doing ARP pings to
+ * the gateway using {@link ArpPeer}.
+ *
+ * Upon successful verification, the watchdog notifies and continues
+ * to monitor the link afterwards when the RSSI level falls below
+ * a certain threshold.
+
+ * When Wi-fi 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).
+ *
+ * We now monitor the quality of the last hop on
+ * Wi-Fi using signal strength and ARP connectivity as indicators
+ * to decide if the link is good enough to switch to Wi-Fi as the uplink.
+ *
+ * ARP pings are useful for link validation but can still get through
+ * when the application traffic fails to go through and are thus not
+ * the best indicator of real packet loss since they are tiny packets
+ * (28 bytes) and have a much low chance of packet corruption than the
+ * regular data packets.
+ *
+ * When signal strength and ARP are used together, it ends up working well in tests.
+ * The goal is to switch to Wi-Fi after validating ARP transfer
+ * and RSSI and then switching out of Wi-Fi when we hit a low
+ * signal strength threshold and then waiting until the signal strength
+ * improves and validating ARP transfer.
*
* @hide
*/
public class WifiWatchdogStateMachine extends StateMachine {
- private static final boolean DBG = false;
+ /* STOPSHIP: Keep this configurable for debugging until ship */
+ private static boolean DBG = false;
private static final String TAG = "WifiWatchdogStateMachine";
- private static final String DISABLED_NETWORK_NOTIFICATION_ID = "WifiWatchdog.networkdisabled";
private static final String WALLED_GARDEN_NOTIFICATION_ID = "WifiWatchdog.walledgarden";
- private static final int WIFI_SIGNAL_LEVELS = 4;
- /**
- * Low signal is defined as less than or equal to cut off
- */
- private static final int LOW_SIGNAL_CUTOFF = 0;
+ /* Wi-fi connection is considered poor below this
+ RSSI level threshold and the watchdog report it
+ to the WifiStateMachine */
+ private static final int RSSI_LEVEL_CUTOFF = 1;
+ /* Wi-fi connection is monitored actively below this
+ threshold */
+ private static final int RSSI_LEVEL_MONITOR = 2;
- private static final long DEFAULT_DNS_CHECK_SHORT_INTERVAL_MS = 2 * 60 * 1000;
- private static final long DEFAULT_DNS_CHECK_LONG_INTERVAL_MS = 60 * 60 * 1000;
- private static final long DEFAULT_WALLED_GARDEN_INTERVAL_MS = 30 * 60 * 1000;
+ private int mCurrentSignalLevel;
- private static final int DEFAULT_MAX_SSID_BLACKLISTS = 7;
- private static final int DEFAULT_NUM_DNS_PINGS = 5; // Multiple pings to detect setup issues
- private static final int DEFAULT_MIN_DNS_RESPONSES = 1;
+ private static final long DEFAULT_ARP_CHECK_INTERVAL_MS = 2 * 60 * 1000;
+ private static final long DEFAULT_WALLED_GARDEN_INTERVAL_MS = 30 * 60 * 1000;
- private static final int DEFAULT_DNS_PING_TIMEOUT_MS = 2000;
+ private static final int DEFAULT_NUM_ARP_PINGS = 5;
+ private static final int DEFAULT_MIN_ARP_RESPONSES = 1;
- private static final long DEFAULT_BLACKLIST_FOLLOWUP_INTERVAL_MS = 15 * 1000;
+ private static final int DEFAULT_ARP_PING_TIMEOUT_MS = 100;
// See http://go/clientsdns for usage approval
private static final String DEFAULT_WALLED_GARDEN_URL =
@@ -102,10 +123,6 @@ public class WifiWatchdogStateMachine extends StateMachine {
*/
private static final int WALLED_GARDEN_START_DELAY_MS = 3000;
- private static final int DNS_INTRATEST_PING_INTERVAL_MS = 200;
- /* With some router setups, it takes a few hunder milli-seconds before connection is active */
- private static final int DNS_START_DELAY_MS = 1000;
-
private static final int BASE = Protocol.BASE_WIFI_WATCHDOG;
/**
@@ -118,99 +135,76 @@ public class WifiWatchdogStateMachine extends StateMachine {
* which has a non-null networkInfo object
*/
private static final int EVENT_NETWORK_STATE_CHANGE = BASE + 2;
- /**
- * Indicates the signal has changed. Passed with arg1
- * {@link #mNetEventCounter} and arg2 [raw signal strength]
- */
+ /* Passed with RSSI information */
private static final int EVENT_RSSI_CHANGE = BASE + 3;
- private static final int EVENT_SCAN_RESULTS_AVAILABLE = 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 MESSAGE_HANDLE_WALLED_GARDEN = BASE + 100;
- private static final int MESSAGE_HANDLE_BAD_AP = BASE + 101;
- /**
- * arg1 == mOnlineWatchState.checkCount
- */
- private static final int MESSAGE_SINGLE_DNS_CHECK = BASE + 102;
- private static final int MESSAGE_NETWORK_FOLLOWUP = BASE + 103;
- private static final int MESSAGE_DELAYED_WALLED_GARDEN_CHECK = BASE + 104;
+ /* Internal messages */
+ private static final int CMD_ARP_CHECK = BASE + 11;
+ private static final int CMD_DELAYED_WALLED_GARDEN_CHECK = BASE + 12;
+
+ /* Notifications to WifiStateMachine */
+ static final int POOR_LINK_DETECTED = BASE + 21;
+ static final int GOOD_LINK_DETECTED = BASE + 22;
+
+ private static final int SINGLE_ARP_CHECK = 0;
+ private static final int FULL_ARP_CHECK = 1;
private Context mContext;
private ContentResolver mContentResolver;
private WifiManager mWifiManager;
- private DnsPinger mDnsPinger;
private IntentFilter mIntentFilter;
private BroadcastReceiver mBroadcastReceiver;
+ private AsyncChannel mWsmChannel = new AsyncChannel();;
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 DnsCheckingState mDnsCheckingState = new DnsCheckingState();
+ private WalledGardenCheckState mWalledGardenCheckState = new WalledGardenCheckState();
+ /* Online and watching link connectivity */
private OnlineWatchState mOnlineWatchState = new OnlineWatchState();
+ /* Online and doing nothing */
private OnlineState mOnlineState = new OnlineState();
- private DnsCheckFailureState mDnsCheckFailureState = new DnsCheckFailureState();
- private DelayWalledGardenState mDelayWalledGardenState = new DelayWalledGardenState();
- private WalledGardenState mWalledGardenState = new WalledGardenState();
- private BlacklistedApState mBlacklistedApState = new BlacklistedApState();
- private long mDnsCheckShortIntervalMs;
- private long mDnsCheckLongIntervalMs;
+ private int mArpToken = 0;
+ private long mArpCheckIntervalMs;
private long mWalledGardenIntervalMs;
- private int mMaxSsidBlacklists;
- private int mNumDnsPings;
- private int mMinDnsResponses;
- private int mDnsPingTimeoutMs;
- private long mBlacklistFollowupIntervalMs;
+ private int mNumArpPings;
+ private int mMinArpResponses;
+ private int mArpPingTimeoutMs;
private boolean mPoorNetworkDetectionEnabled;
private boolean mWalledGardenTestEnabled;
private String mWalledGardenUrl;
- private boolean mShowDisabledNotification;
- /**
- * The {@link WifiInfo} object passed to WWSM on network broadcasts
- */
- private WifiInfo mConnectionInfo;
- private int mNetEventCounter = 0;
-
- /**
- * Currently maintained but not used, TODO
- */
- private HashSet<String> mBssids = new HashSet<String>();
- private int mNumCheckFailures = 0;
+ private WifiInfo mWifiInfo;
+ private LinkProperties mLinkProperties;
- private Long mLastWalledGardenCheckTime = null;
+ private long mLastWalledGardenCheckTime = 0;
- /**
- * This is set by the blacklisted state and reset when connected to a new AP.
- * It triggers a disableNetwork call if a DNS check fails.
- */
- public boolean mDisableAPNextFailure = false;
private static boolean sWifiOnly = false;
- private boolean mDisabledNotificationShown;
private boolean mWalledGardenNotificationShown;
- public boolean mHasConnectedWifiManager = false;
/**
* STATE MAP
* Default
* / \
- * Disabled Enabled
- * / \
- * NotConnected Connected
- * /---------\
- * (all other states)
+ * Disabled Enabled
+ * / \ \
+ * NotConnected Verifying Connected
+ * /---------\
+ * (all other states)
*/
private WifiWatchdogStateMachine(Context context) {
super(TAG);
mContext = context;
mContentResolver = context.getContentResolver();
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- mDnsPinger = new DnsPinger(mContext, "WifiWatchdogStateMachine.DnsPinger",
- this.getHandler().getLooper(), this.getHandler(),
- ConnectivityManager.TYPE_WIFI);
+ mWsmChannel.connectSync(mContext, getHandler(),
+ mWifiManager.getWifiStateMachineMessenger());
setupNetworkReceiver();
@@ -221,16 +215,17 @@ public class WifiWatchdogStateMachine extends StateMachine {
addState(mWatchdogDisabledState, mDefaultState);
addState(mWatchdogEnabledState, mDefaultState);
addState(mNotConnectedState, mWatchdogEnabledState);
+ addState(mVerifyingLinkState, mWatchdogEnabledState);
addState(mConnectedState, mWatchdogEnabledState);
- addState(mDnsCheckingState, mConnectedState);
- addState(mDnsCheckFailureState, mConnectedState);
- addState(mDelayWalledGardenState, mConnectedState);
- addState(mWalledGardenState, mConnectedState);
- addState(mBlacklistedApState, mConnectedState);
+ addState(mWalledGardenCheckState, mConnectedState);
addState(mOnlineWatchState, mConnectedState);
addState(mOnlineState, mConnectedState);
- setInitialState(mWatchdogDisabledState);
+ if (isWatchdogEnabled()) {
+ setInitialState(mNotConnectedState);
+ } else {
+ setInitialState(mWatchdogDisabledState);
+ }
updateSettings();
}
@@ -242,19 +237,15 @@ public class WifiWatchdogStateMachine extends StateMachine {
sWifiOnly = (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
// Disable for wifi only devices.
- if (Settings.Secure.getString(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON) == null &&
- sWifiOnly) {
+ if (Settings.Secure.getString(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON) == null
+ && sWifiOnly) {
putSettingsBoolean(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON, false);
}
WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context);
wwsm.start();
- wwsm.sendMessage(EVENT_WATCHDOG_TOGGLED);
return wwsm;
}
- /**
- *
- */
private void setupNetworkReceiver() {
mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -263,10 +254,8 @@ public class WifiWatchdogStateMachine extends StateMachine {
if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
sendMessage(EVENT_NETWORK_STATE_CHANGE, intent);
} else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
- obtainMessage(EVENT_RSSI_CHANGE, mNetEventCounter,
- intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200)).sendToTarget();
- } else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
- sendMessage(EVENT_SCAN_RESULTS_AVAILABLE);
+ obtainMessage(EVENT_RSSI_CHANGE,
+ intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200), 0).sendToTarget();
} else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
sendMessage(EVENT_WIFI_RADIO_STATE_CHANGE,
intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
@@ -279,7 +268,7 @@ public class WifiWatchdogStateMachine extends StateMachine {
mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
mIntentFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
- mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
}
/**
@@ -311,39 +300,29 @@ public class WifiWatchdogStateMachine extends StateMachine {
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(
- Settings.Secure.WIFI_WATCHDOG_DNS_CHECK_SHORT_INTERVAL_MS),
+ Settings.Secure.WIFI_WATCHDOG_ARP_CHECK_INTERVAL_MS),
false, contentObserver);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_DNS_CHECK_LONG_INTERVAL_MS),
- false, contentObserver);
- mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_INTERVAL_MS),
false, contentObserver);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_MAX_SSID_BLACKLISTS),
+ Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_NUM_ARP_PINGS),
false, contentObserver);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_NUM_DNS_PINGS),
+ Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_MIN_ARP_RESPONSES),
false, contentObserver);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_MIN_DNS_RESPONSES),
+ Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ARP_PING_TIMEOUT_MS),
false, contentObserver);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_DNS_PING_TIMEOUT_MS),
+ Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED),
false, contentObserver);
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(
- Settings.Secure.WIFI_WATCHDOG_BLACKLIST_FOLLOWUP_INTERVAL_MS),
- false, contentObserver);
- mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED),
false, contentObserver);
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL),
false, contentObserver);
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_SHOW_DISABLED_NETWORK_POPUP)
- , false, contentObserver);
}
/**
@@ -375,17 +354,20 @@ public class WifiWatchdogStateMachine extends StateMachine {
}
}
- private boolean rssiStrengthAboveCutoff(int rssi) {
- return WifiManager.calculateSignalLevel(rssi, WIFI_SIGNAL_LEVELS) > LOW_SIGNAL_CUTOFF;
- }
-
public void dump(PrintWriter pw) {
pw.print("WatchdogStatus: ");
- pw.print("State " + getCurrentState());
- pw.println(", network [" + mConnectionInfo + "]");
- pw.print("checkFailures " + mNumCheckFailures);
- pw.println(", bssids: " + mBssids);
- pw.println("lastSingleCheck: " + mOnlineWatchState.lastCheckTime);
+ pw.print("State: " + getCurrentState());
+ pw.println("mWifiInfo: [" + mWifiInfo + "]");
+ pw.println("mLinkProperties: [" + mLinkProperties + "]");
+ pw.println("mCurrentSignalLevel: [" + mCurrentSignalLevel + "]");
+ pw.println("mArpCheckIntervalMs: [" + mArpCheckIntervalMs+ "]");
+ pw.println("mWalledGardenIntervalMs: [" + mWalledGardenIntervalMs + "]");
+ pw.println("mNumArpPings: [" + mNumArpPings + "]");
+ pw.println("mMinArpResponses: [" + mMinArpResponses + "]");
+ pw.println("mArpPingTimeoutMs: [" + mArpPingTimeoutMs + "]");
+ pw.println("mPoorNetworkDetectionEnabled: [" + mPoorNetworkDetectionEnabled + "]");
+ pw.println("mWalledGardenTestEnabled: [" + mWalledGardenTestEnabled + "]");
+ pw.println("mWalledGardenUrl: [" + mWalledGardenUrl + "]");
}
private boolean isWatchdogEnabled() {
@@ -393,31 +375,22 @@ public class WifiWatchdogStateMachine extends StateMachine {
}
private void updateSettings() {
- mDnsCheckShortIntervalMs = Secure.getLong(mContentResolver,
- Secure.WIFI_WATCHDOG_DNS_CHECK_SHORT_INTERVAL_MS,
- DEFAULT_DNS_CHECK_SHORT_INTERVAL_MS);
- mDnsCheckLongIntervalMs = Secure.getLong(mContentResolver,
- Secure.WIFI_WATCHDOG_DNS_CHECK_LONG_INTERVAL_MS,
- DEFAULT_DNS_CHECK_LONG_INTERVAL_MS);
- mMaxSsidBlacklists = Secure.getInt(mContentResolver,
- Secure.WIFI_WATCHDOG_MAX_SSID_BLACKLISTS,
- DEFAULT_MAX_SSID_BLACKLISTS);
- mNumDnsPings = Secure.getInt(mContentResolver,
- Secure.WIFI_WATCHDOG_NUM_DNS_PINGS,
- DEFAULT_NUM_DNS_PINGS);
- mMinDnsResponses = Secure.getInt(mContentResolver,
- Secure.WIFI_WATCHDOG_MIN_DNS_RESPONSES,
- DEFAULT_MIN_DNS_RESPONSES);
- mDnsPingTimeoutMs = Secure.getInt(mContentResolver,
- Secure.WIFI_WATCHDOG_DNS_PING_TIMEOUT_MS,
- DEFAULT_DNS_PING_TIMEOUT_MS);
- mBlacklistFollowupIntervalMs = Secure.getLong(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_BLACKLIST_FOLLOWUP_INTERVAL_MS,
- DEFAULT_BLACKLIST_FOLLOWUP_INTERVAL_MS);
- //TODO: enable this by default after changing watchdog behavior
- //Also, update settings description
+ if (DBG) log("Updating secure settings");
+
+ mArpCheckIntervalMs = Secure.getLong(mContentResolver,
+ Secure.WIFI_WATCHDOG_ARP_CHECK_INTERVAL_MS,
+ DEFAULT_ARP_CHECK_INTERVAL_MS);
+ mNumArpPings = Secure.getInt(mContentResolver,
+ Secure.WIFI_WATCHDOG_NUM_ARP_PINGS,
+ DEFAULT_NUM_ARP_PINGS);
+ mMinArpResponses = Secure.getInt(mContentResolver,
+ Secure.WIFI_WATCHDOG_MIN_ARP_RESPONSES,
+ DEFAULT_MIN_ARP_RESPONSES);
+ mArpPingTimeoutMs = Secure.getInt(mContentResolver,
+ Secure.WIFI_WATCHDOG_ARP_PING_TIMEOUT_MS,
+ DEFAULT_ARP_PING_TIMEOUT_MS);
mPoorNetworkDetectionEnabled = getSettingsBoolean(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, false);
+ Settings.Secure.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, true);
mWalledGardenTestEnabled = getSettingsBoolean(mContentResolver,
Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, true);
mWalledGardenUrl = getSettingsStr(mContentResolver,
@@ -426,69 +399,6 @@ public class WifiWatchdogStateMachine extends StateMachine {
mWalledGardenIntervalMs = Secure.getLong(mContentResolver,
Secure.WIFI_WATCHDOG_WALLED_GARDEN_INTERVAL_MS,
DEFAULT_WALLED_GARDEN_INTERVAL_MS);
- mShowDisabledNotification = getSettingsBoolean(mContentResolver,
- Settings.Secure.WIFI_WATCHDOG_SHOW_DISABLED_NETWORK_POPUP, true);
- }
-
- /**
- * Helper to return wait time left given a min interval and last run
- *
- * @param interval minimum wait interval
- * @param lastTime last time action was performed in
- * SystemClock.elapsedRealtime(). Null if never.
- * @return non negative time to wait
- */
- private static long waitTime(long interval, Long lastTime) {
- if (lastTime == null)
- return 0;
- long wait = interval + lastTime - SystemClock.elapsedRealtime();
- return wait > 0 ? wait : 0;
- }
-
- private static String wifiInfoToStr(WifiInfo wifiInfo) {
- if (wifiInfo == null)
- return "null";
- return "(" + wifiInfo.getSSID() + ", " + wifiInfo.getBSSID() + ")";
- }
-
- /**
- * Uses {@link #mConnectionInfo}.
- */
- private void updateBssids() {
- String curSsid = mConnectionInfo.getSSID();
- List<ScanResult> results = mWifiManager.getScanResults();
- int oldNumBssids = mBssids.size();
-
- if (results == null) {
- if (DBG) {
- log("updateBssids: Got null scan results!");
- }
- return;
- }
-
- for (ScanResult result : results) {
- if (result == null || result.SSID == null) {
- if (DBG) {
- log("Received invalid scan result: " + result);
- }
- continue;
- }
- if (curSsid.equals(result.SSID))
- mBssids.add(result.BSSID);
- }
- }
-
- private void resetWatchdogState() {
- if (DBG) {
- log("Resetting watchdog state...");
- }
- mConnectionInfo = null;
- mDisableAPNextFailure = false;
- mLastWalledGardenCheckTime = null;
- mNumCheckFailures = 0;
- mBssids.clear();
- setDisabledNetworkNotificationVisible(false);
- setWalledGardenNotificationVisible(false);
}
private void setWalledGardenNotificationVisible(boolean visible) {
@@ -507,7 +417,7 @@ public class WifiWatchdogStateMachine extends StateMachine {
CharSequence title = r.getString(R.string.wifi_available_sign_in, 0);
CharSequence details = r.getString(R.string.wifi_available_sign_in_detailed,
- mConnectionInfo.getSSID());
+ mWifiInfo.getSSID());
Notification notification = new Notification();
notification.when = 0;
@@ -524,41 +434,6 @@ public class WifiWatchdogStateMachine extends StateMachine {
mWalledGardenNotificationShown = visible;
}
- private void setDisabledNetworkNotificationVisible(boolean visible) {
- // If it should be hidden and it is already hidden, then noop
- if (!visible && !mDisabledNotificationShown) {
- return;
- }
-
- Resources r = Resources.getSystem();
- NotificationManager notificationManager = (NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE);
-
- if (visible) {
- CharSequence title = r.getText(R.string.wifi_watchdog_network_disabled);
- String msg = mConnectionInfo.getSSID() +
- r.getText(R.string.wifi_watchdog_network_disabled_detailed);
-
- Notification wifiDisabledWarning = new Notification.Builder(mContext)
- .setSmallIcon(R.drawable.stat_sys_warning)
- .setDefaults(Notification.DEFAULT_ALL)
- .setTicker(title)
- .setContentTitle(title)
- .setContentText(msg)
- .setContentIntent(PendingIntent.getActivity(mContext, 0,
- new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0))
- .setWhen(System.currentTimeMillis())
- .setAutoCancel(true)
- .getNotification();
-
- notificationManager.notify(DISABLED_NETWORK_NOTIFICATION_ID, 1, wifiDisabledWarning);
- } else {
- notificationManager.cancel(DISABLED_NETWORK_NOTIFICATION_ID, 1);
- }
- mDisabledNotificationShown = visible;
- }
-
class DefaultState extends State {
@Override
public boolean processMessage(Message msg) {
@@ -568,11 +443,20 @@ public class WifiWatchdogStateMachine extends StateMachine {
if (DBG) {
log("Updating wifi-watchdog secure settings");
}
- return HANDLED;
- }
- if (DBG) {
- log("Caught message " + msg.what + " in state " +
- getCurrentState().getName());
+ break;
+ case EVENT_RSSI_CHANGE:
+ mCurrentSignalLevel = WifiManager.calculateSignalLevel(msg.arg1,
+ WifiManager.RSSI_LEVELS);
+ break;
+ case EVENT_WIFI_RADIO_STATE_CHANGE:
+ case EVENT_NETWORK_STATE_CHANGE:
+ case CMD_ARP_CHECK:
+ case CMD_DELAYED_WALLED_GARDEN_CHECK:
+ //ignore
+ break;
+ default:
+ log("Unhandled message " + msg + " in state " + getCurrentState().getName());
+ break;
}
return HANDLED;
}
@@ -586,6 +470,20 @@ public class WifiWatchdogStateMachine extends StateMachine {
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) log("Watchdog disabled, verify link");
+ mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
+ break;
+ default:
+ break;
+ }
+ break;
}
return NOT_HANDLED;
}
@@ -594,10 +492,8 @@ public class WifiWatchdogStateMachine extends StateMachine {
class WatchdogEnabledState extends State {
@Override
public void enter() {
- resetWatchdogState();
- mContext.registerReceiver(mBroadcastReceiver, mIntentFilter);
if (DBG) log("WifiWatchdogService enabled");
- }
+ }
@Override
public boolean processMessage(Message msg) {
@@ -605,77 +501,57 @@ public class WifiWatchdogStateMachine extends StateMachine {
case EVENT_WATCHDOG_TOGGLED:
if (!isWatchdogEnabled())
transitionTo(mWatchdogDisabledState);
- return HANDLED;
+ break;
case EVENT_NETWORK_STATE_CHANGE:
- Intent stateChangeIntent = (Intent) msg.obj;
+ Intent intent = (Intent) msg.obj;
NetworkInfo networkInfo = (NetworkInfo)
- stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-
- setDisabledNetworkNotificationVisible(false);
- setWalledGardenNotificationVisible(false);
- switch (networkInfo.getState()) {
- case CONNECTED:
- WifiInfo wifiInfo = (WifiInfo)
- stateChangeIntent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
- if (wifiInfo == null) {
- loge("Connected --> WifiInfo object null!");
- return HANDLED;
- }
-
- if (wifiInfo.getSSID() == null || wifiInfo.getBSSID() == null) {
- loge("Received wifiInfo object with null elts: "
- + wifiInfoToStr(wifiInfo));
- return HANDLED;
- }
-
- initConnection(wifiInfo);
- mConnectionInfo = wifiInfo;
- mNetEventCounter++;
+ intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+
+ switch (networkInfo.getDetailedState()) {
+ case VERIFYING_POOR_LINK:
+ mLinkProperties = (LinkProperties) intent.getParcelableExtra(
+ WifiManager.EXTRA_LINK_PROPERTIES);
+ mWifiInfo = (WifiInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_WIFI_INFO);
if (mPoorNetworkDetectionEnabled) {
- updateBssids();
- transitionTo(mDnsCheckingState);
+ if (mWifiInfo == null) {
+ log("Ignoring link verification, mWifiInfo is NULL");
+ mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
+ } else {
+ transitionTo(mVerifyingLinkState);
+ }
} else {
- transitionTo(mDelayWalledGardenState);
+ mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
+ }
+ break;
+ case CONNECTED:
+ if (shouldCheckWalledGarden()) {
+ transitionTo(mWalledGardenCheckState);
+ } else {
+ transitionTo(mOnlineWatchState);
}
break;
default:
- mNetEventCounter++;
transitionTo(mNotConnectedState);
break;
}
- return HANDLED;
+ break;
case EVENT_WIFI_RADIO_STATE_CHANGE:
if ((Integer) msg.obj == WifiManager.WIFI_STATE_DISABLING) {
if (DBG) log("WifiStateDisabling -- Resetting WatchdogState");
- resetWatchdogState();
- mNetEventCounter++;
transitionTo(mNotConnectedState);
}
- return HANDLED;
- }
-
- return NOT_HANDLED;
- }
-
- /**
- * @param wifiInfo Info object with non-null ssid and bssid
- */
- private void initConnection(WifiInfo wifiInfo) {
- if (DBG) {
- log("Connected:: old " + wifiInfoToStr(mConnectionInfo) +
- " ==> new " + wifiInfoToStr(wifiInfo));
+ break;
+ default:
+ return NOT_HANDLED;
}
- if (mConnectionInfo == null || !wifiInfo.getSSID().equals(mConnectionInfo.getSSID())) {
- resetWatchdogState();
- } else if (!wifiInfo.getBSSID().equals(mConnectionInfo.getBSSID())) {
- mDisableAPNextFailure = false;
- }
+ setWalledGardenNotificationVisible(false);
+ return HANDLED;
}
@Override
public void exit() {
- mContext.unregisterReceiver(mBroadcastReceiver);
if (DBG) log("WifiWatchdogService disabled");
}
}
@@ -683,423 +559,240 @@ public class WifiWatchdogStateMachine extends StateMachine {
class NotConnectedState extends State {
}
- class ConnectedState extends State {
+ class VerifyingLinkState extends State {
+ @Override
+ public void enter() {
+ if (DBG) log(getName() + "\n");
+ //Treat entry as an rssi change
+ handleRssiChange();
+ }
+
+ private void handleRssiChange() {
+ if (mCurrentSignalLevel <= RSSI_LEVEL_CUTOFF) {
+ //stay here
+ if (DBG) log("enter VerifyingLinkState, stay level: " + mCurrentSignalLevel);
+ } else {
+ if (DBG) log("enter VerifyingLinkState, arp check level: " + mCurrentSignalLevel);
+ sendMessage(obtainMessage(CMD_ARP_CHECK, ++mArpToken, 0));
+ }
+ }
+
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
- case EVENT_SCAN_RESULTS_AVAILABLE:
- if (mPoorNetworkDetectionEnabled) {
- updateBssids();
- }
- return HANDLED;
case EVENT_WATCHDOG_SETTINGS_CHANGE:
updateSettings();
- if (mPoorNetworkDetectionEnabled) {
- transitionTo(mOnlineWatchState);
- } else {
- transitionTo(mOnlineState);
+ if (!mPoorNetworkDetectionEnabled) {
+ mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
}
- return HANDLED;
+ break;
+ case EVENT_RSSI_CHANGE:
+ int signalLevel = WifiManager.calculateSignalLevel(msg.arg1,
+ WifiManager.RSSI_LEVELS);
+ if (DBG) log("RSSI change old: " + mCurrentSignalLevel + "new: " + signalLevel);
+ mCurrentSignalLevel = signalLevel;
+
+ handleRssiChange();
+ break;
+ case CMD_ARP_CHECK:
+ if (msg.arg1 == mArpToken) {
+ if (doArpTest(FULL_ARP_CHECK) == true) {
+ if (DBG) log("Notify link is good " + mCurrentSignalLevel);
+ mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
+ } else {
+ if (DBG) log("Continue ARP check, rssi level: " + mCurrentSignalLevel);
+ sendMessageDelayed(obtainMessage(CMD_ARP_CHECK, ++mArpToken, 0),
+ mArpCheckIntervalMs);
+ }
+ }
+ break;
+ default:
+ return NOT_HANDLED;
}
- return NOT_HANDLED;
+ return HANDLED;
}
}
- class DnsCheckingState extends State {
- List<InetAddress> mDnsList;
- int[] dnsCheckSuccesses;
- String dnsCheckLogStr;
- String[] dnsResponseStrs;
- /** Keeps track of active dns pings. Map is from pingID to index in mDnsList */
- HashMap<Integer, Integer> idDnsMap = new HashMap<Integer, Integer>();
-
+ class ConnectedState extends State {
@Override
public void enter() {
- mDnsList = mDnsPinger.getDnsList();
- int numDnses = mDnsList.size();
- dnsCheckSuccesses = new int[numDnses];
- dnsResponseStrs = new String[numDnses];
- for (int i = 0; i < numDnses; i++)
- dnsResponseStrs[i] = "";
-
- if (DBG) {
- dnsCheckLogStr = String.format("Pinging %s on ssid [%s]: ",
- mDnsList, mConnectionInfo.getSSID());
- log(dnsCheckLogStr);
- }
-
- idDnsMap.clear();
- for (int i=0; i < mNumDnsPings; i++) {
- for (int j = 0; j < numDnses; j++) {
- idDnsMap.put(mDnsPinger.pingDnsAsync(mDnsList.get(j), mDnsPingTimeoutMs,
- DNS_START_DELAY_MS + DNS_INTRATEST_PING_INTERVAL_MS * i), j);
- }
- }
+ if (DBG) log(getName() + "\n");
}
-
@Override
public boolean processMessage(Message msg) {
- if (msg.what != DnsPinger.DNS_PING_RESULT) {
- return NOT_HANDLED;
- }
-
- int pingID = msg.arg1;
- int pingResponseTime = msg.arg2;
-
- Integer dnsServerId = idDnsMap.get(pingID);
- if (dnsServerId == null) {
- loge("Received a Dns response with unknown ID!");
- return HANDLED;
- }
-
- idDnsMap.remove(pingID);
- if (pingResponseTime >= 0)
- dnsCheckSuccesses[dnsServerId]++;
-
- if (DBG) {
- if (pingResponseTime >= 0) {
- dnsResponseStrs[dnsServerId] += "|" + pingResponseTime;
- } else {
- dnsResponseStrs[dnsServerId] += "|x";
- }
- }
-
- /**
- * After a full ping count, if we have more responses than this
- * cutoff, the outcome is success; else it is 'failure'.
- */
-
- /**
- * Our final success count will be at least this big, so we're
- * guaranteed to succeed.
- */
- if (dnsCheckSuccesses[dnsServerId] >= mMinDnsResponses) {
- // DNS CHECKS OK, NOW WALLED GARDEN
- if (DBG) {
- log(makeLogString() + " SUCCESS");
- }
+ switch (msg.what) {
+ case EVENT_WATCHDOG_SETTINGS_CHANGE:
+ updateSettings();
+ //STOPSHIP: Remove this at ship
+ DBG = true;
+ if (DBG) log("Updated secure settings and turned debug on");
- if (!shouldCheckWalledGarden()) {
- transitionTo(mOnlineWatchState);
+ if (mPoorNetworkDetectionEnabled) {
+ transitionTo(mOnlineWatchState);
+ } else {
+ transitionTo(mOnlineState);
+ }
return HANDLED;
- }
-
- transitionTo(mDelayWalledGardenState);
- return HANDLED;
- }
-
- if (idDnsMap.isEmpty()) {
- if (DBG) {
- log(makeLogString() + " FAILURE");
- }
- transitionTo(mDnsCheckFailureState);
- return HANDLED;
}
-
- return HANDLED;
- }
-
- private String makeLogString() {
- String logStr = dnsCheckLogStr;
- for (String respStr : dnsResponseStrs)
- logStr += " [" + respStr + "]";
- return logStr;
- }
-
- @Override
- public void exit() {
- mDnsPinger.cancelPings();
- }
-
- private boolean shouldCheckWalledGarden() {
- if (!mWalledGardenTestEnabled) {
- if (DBG)
- log("Skipping walled garden check - disabled");
- return false;
- }
- long waitTime = waitTime(mWalledGardenIntervalMs,
- mLastWalledGardenCheckTime);
- if (waitTime > 0) {
- if (DBG) {
- log("Skipping walled garden check - wait " +
- waitTime + " ms.");
- }
- return false;
- }
- return true;
+ return NOT_HANDLED;
}
}
- class DelayWalledGardenState extends State {
+ class WalledGardenCheckState extends State {
+ private int mWalledGardenToken = 0;
@Override
public void enter() {
- sendMessageDelayed(MESSAGE_DELAYED_WALLED_GARDEN_CHECK, WALLED_GARDEN_START_DELAY_MS);
+ if (DBG) log(getName() + "\n");
+ sendMessageDelayed(obtainMessage(CMD_DELAYED_WALLED_GARDEN_CHECK,
+ ++mWalledGardenToken, 0), WALLED_GARDEN_START_DELAY_MS);
}
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
- case MESSAGE_DELAYED_WALLED_GARDEN_CHECK:
- mLastWalledGardenCheckTime = SystemClock.elapsedRealtime();
- if (isWalledGardenConnection()) {
- if (DBG) log("Walled garden test complete - walled garden detected");
- transitionTo(mWalledGardenState);
- } else {
- if (DBG) log("Walled garden test complete - online");
- if (mPoorNetworkDetectionEnabled) {
- transitionTo(mOnlineWatchState);
- } else {
- transitionTo(mOnlineState);
+ case CMD_DELAYED_WALLED_GARDEN_CHECK:
+ if (msg.arg1 == mWalledGardenToken) {
+ mLastWalledGardenCheckTime = SystemClock.elapsedRealtime();
+ if (isWalledGardenConnection()) {
+ if (DBG) log("Walled garden detected");
+ setWalledGardenNotificationVisible(true);
}
+ transitionTo(mOnlineWatchState);
}
- return HANDLED;
+ break;
default:
return NOT_HANDLED;
}
+ return HANDLED;
}
}
class OnlineWatchState extends State {
- /**
- * Signals a short-wait message is enqueued for the current 'guard' counter
- */
- boolean unstableSignalChecks = false;
-
- /**
- * The signal is unstable. We should enqueue a short-wait check, if one is enqueued
- * already
- */
- boolean signalUnstable = false;
-
- /**
- * A monotonic counter to ensure that at most one check message will be processed from any
- * set of check messages currently enqueued. Avoids duplicate checks when a low-signal
- * event is observed.
- */
- int checkGuard = 0;
- Long lastCheckTime = null;
-
- /** Keeps track of dns pings. Map is from pingID to InetAddress used for ping */
- HashMap<Integer, InetAddress> pingInfoMap = new HashMap<Integer, InetAddress>();
-
- @Override
public void enter() {
- lastCheckTime = SystemClock.elapsedRealtime();
- signalUnstable = false;
- checkGuard++;
- unstableSignalChecks = false;
- pingInfoMap.clear();
- triggerSingleDnsCheck();
+ if (DBG) log(getName() + "\n");
+ if (mPoorNetworkDetectionEnabled) {
+ //Treat entry as an rssi change
+ handleRssiChange();
+ } else {
+ transitionTo(mOnlineState);
+ }
+ }
+
+ private void handleRssiChange() {
+ if (mCurrentSignalLevel <= RSSI_LEVEL_CUTOFF) {
+ if (DBG) log("Transition out, below cut off level: " + mCurrentSignalLevel);
+ mWsmChannel.sendMessage(POOR_LINK_DETECTED);
+ } else if (mCurrentSignalLevel <= RSSI_LEVEL_MONITOR) {
+ if (DBG) log("Start monitoring, level: " + mCurrentSignalLevel);
+ sendMessage(obtainMessage(CMD_ARP_CHECK, ++mArpToken, 0));
+ }
}
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
case EVENT_RSSI_CHANGE:
- if (msg.arg1 != mNetEventCounter) {
- if (DBG) {
- log("Rssi change message out of sync, ignoring");
- }
- return HANDLED;
- }
- int newRssi = msg.arg2;
- signalUnstable = !rssiStrengthAboveCutoff(newRssi);
- if (DBG) {
- log("OnlineWatchState:: new rssi " + newRssi + " --> level " +
- WifiManager.calculateSignalLevel(newRssi, WIFI_SIGNAL_LEVELS));
- }
-
- if (signalUnstable && !unstableSignalChecks) {
- if (DBG) {
- log("Sending triggered check msg");
- }
- triggerSingleDnsCheck();
- }
- return HANDLED;
- case MESSAGE_SINGLE_DNS_CHECK:
- if (msg.arg1 != checkGuard) {
- if (DBG) {
- log("Single check msg out of sync, ignoring.");
- }
- return HANDLED;
- }
- lastCheckTime = SystemClock.elapsedRealtime();
- pingInfoMap.clear();
- for (InetAddress curDns: mDnsPinger.getDnsList()) {
- pingInfoMap.put(mDnsPinger.pingDnsAsync(curDns, mDnsPingTimeoutMs, 0),
- curDns);
- }
- return HANDLED;
- case DnsPinger.DNS_PING_RESULT:
- InetAddress curDnsServer = pingInfoMap.get(msg.arg1);
- if (curDnsServer == null) {
- return HANDLED;
- }
- pingInfoMap.remove(msg.arg1);
- int responseTime = msg.arg2;
- if (responseTime >= 0) {
- if (DBG) {
- log("Single DNS ping OK. Response time: "
- + responseTime + " from DNS " + curDnsServer);
+ int signalLevel = WifiManager.calculateSignalLevel(msg.arg1,
+ WifiManager.RSSI_LEVELS);
+ if (DBG) log("RSSI change old: " + mCurrentSignalLevel + "new: " + signalLevel);
+ mCurrentSignalLevel = signalLevel;
+
+ handleRssiChange();
+
+ break;
+ case CMD_ARP_CHECK:
+ if (msg.arg1 == mArpToken) {
+ if (doArpTest(SINGLE_ARP_CHECK) != true) {
+ if (DBG) log("single ARP fail, full ARP check");
+ //do a full test
+ if (doArpTest(FULL_ARP_CHECK) != true) {
+ if (DBG) log("notify full ARP fail, level: " + mCurrentSignalLevel);
+ mWsmChannel.sendMessage(POOR_LINK_DETECTED);
+ }
}
- pingInfoMap.clear();
- checkGuard++;
- unstableSignalChecks = false;
- triggerSingleDnsCheck();
- } else {
- if (pingInfoMap.isEmpty()) {
- if (DBG) {
- log("Single dns ping failure. All dns servers failed, "
- + "starting full checks.");
- }
- transitionTo(mDnsCheckingState);
+ if (mCurrentSignalLevel <= RSSI_LEVEL_MONITOR) {
+ if (DBG) log("Continue ARP check, rssi level: " + mCurrentSignalLevel);
+ sendMessageDelayed(obtainMessage(CMD_ARP_CHECK, ++mArpToken, 0),
+ mArpCheckIntervalMs);
}
}
- return HANDLED;
- }
- return NOT_HANDLED;
- }
-
- @Override
- public void exit() {
- mDnsPinger.cancelPings();
- }
-
- /**
- * Times a dns check with an interval based on {@link #signalUnstable}
- */
- private void triggerSingleDnsCheck() {
- long waitInterval;
- if (signalUnstable) {
- waitInterval = mDnsCheckShortIntervalMs;
- unstableSignalChecks = true;
- } else {
- waitInterval = mDnsCheckLongIntervalMs;
+ break;
+ default:
+ return NOT_HANDLED;
}
- sendMessageDelayed(obtainMessage(MESSAGE_SINGLE_DNS_CHECK, checkGuard, 0),
- waitTime(waitInterval, lastCheckTime));
+ return HANDLED;
}
}
-
/* Child state of ConnectedState indicating that we are online
* and there is nothing to do
*/
class OnlineState extends State {
}
- class DnsCheckFailureState extends State {
-
- @Override
- public void enter() {
- mNumCheckFailures++;
- obtainMessage(MESSAGE_HANDLE_BAD_AP, mNetEventCounter, 0).sendToTarget();
+ private boolean shouldCheckWalledGarden() {
+ if (!mWalledGardenTestEnabled) {
+ if (DBG) log("Skipping walled garden check - disabled");
+ return false;
}
- @Override
- public boolean processMessage(Message msg) {
- if (msg.what != MESSAGE_HANDLE_BAD_AP) {
- return NOT_HANDLED;
- }
-
- if (msg.arg1 != mNetEventCounter) {
- if (DBG) {
- log("Msg out of sync, ignoring...");
- }
- return HANDLED;
- }
+ long waitTime = (mWalledGardenIntervalMs + mLastWalledGardenCheckTime)
+ - SystemClock.elapsedRealtime();
- if (mDisableAPNextFailure || mNumCheckFailures >= mBssids.size()
- || mNumCheckFailures >= mMaxSsidBlacklists) {
- if (sWifiOnly) {
- log("Would disable bad network, but device has no mobile data!" +
- " Going idle...");
- // This state should be called idle -- will be changing flow.
- transitionTo(mNotConnectedState);
- return HANDLED;
- }
-
- // TODO : Unban networks if they had low signal ?
- log("Disabling current SSID " + wifiInfoToStr(mConnectionInfo)
- + ". " + "numCheckFailures " + mNumCheckFailures
- + ", numAPs " + mBssids.size());
- int networkId = mConnectionInfo.getNetworkId();
- if (!mHasConnectedWifiManager) {
- mWifiManager.asyncConnect(mContext, getHandler());
- mHasConnectedWifiManager = true;
- }
- mWifiManager.disableNetwork(networkId, WifiConfiguration.DISABLED_DNS_FAILURE);
- if (mShowDisabledNotification) {
- setDisabledNetworkNotificationVisible(true);
- }
- transitionTo(mNotConnectedState);
- } else {
- log("Blacklisting current BSSID. " + wifiInfoToStr(mConnectionInfo)
- + "numCheckFailures " + mNumCheckFailures + ", numAPs " + mBssids.size());
-
- mWifiManager.addToBlacklist(mConnectionInfo.getBSSID());
- mWifiManager.reassociate();
- transitionTo(mBlacklistedApState);
+ if (mLastWalledGardenCheckTime != 0 && waitTime > 0) {
+ if (DBG) {
+ log("Skipping walled garden check - wait " +
+ waitTime + " ms.");
}
- return HANDLED;
+ return false;
}
+ return true;
}
- class WalledGardenState extends State {
- @Override
- public void enter() {
- obtainMessage(MESSAGE_HANDLE_WALLED_GARDEN, mNetEventCounter, 0).sendToTarget();
- }
+ private boolean doArpTest(int type) {
+ boolean success;
- @Override
- public boolean processMessage(Message msg) {
- if (msg.what != MESSAGE_HANDLE_WALLED_GARDEN) {
- return NOT_HANDLED;
- }
+ String iface = mLinkProperties.getInterfaceName();
+ String mac = mWifiInfo.getMacAddress();
+ InetAddress inetAddress = null;
+ InetAddress gateway = null;
- if (msg.arg1 != mNetEventCounter) {
- if (DBG) {
- log("WalledGardenState::Msg out of sync, ignoring...");
- }
- return HANDLED;
- }
- setWalledGardenNotificationVisible(true);
- if (mPoorNetworkDetectionEnabled) {
- transitionTo(mOnlineWatchState);
- } else {
- transitionTo(mOnlineState);
- }
- return HANDLED;
+ for (LinkAddress la : mLinkProperties.getLinkAddresses()) {
+ inetAddress = la.getAddress();
+ break;
}
- }
- class BlacklistedApState extends State {
- @Override
- public void enter() {
- mDisableAPNextFailure = true;
- sendMessageDelayed(obtainMessage(MESSAGE_NETWORK_FOLLOWUP, mNetEventCounter, 0),
- mBlacklistFollowupIntervalMs);
+ for (RouteInfo route : mLinkProperties.getRoutes()) {
+ gateway = route.getGateway();
+ break;
}
- @Override
- public boolean processMessage(Message msg) {
- if (msg.what != MESSAGE_NETWORK_FOLLOWUP) {
- return NOT_HANDLED;
- }
+ if (DBG) log("ARP " + iface + "addr: " + inetAddress + "mac: " + mac + "gw: " + gateway);
- if (msg.arg1 != mNetEventCounter) {
- if (DBG) {
- log("BlacklistedApState::Msg out of sync, ignoring...");
+ try {
+ ArpPeer peer = new ArpPeer(iface, inetAddress, mac, gateway);
+ if (type == SINGLE_ARP_CHECK) {
+ success = (peer.doArp(mArpPingTimeoutMs) != null);
+ if (DBG) log("single ARP test result: " + success);
+ } else {
+ int responses = 0;
+ for (int i=0; i < mNumArpPings; i++) {
+ if(peer.doArp(mArpPingTimeoutMs) != null) responses++;
}
- return HANDLED;
+ if (DBG) log("full ARP test result: " + responses + "/" + mNumArpPings);
+ success = (responses >= mMinArpResponses);
}
-
- transitionTo(mDnsCheckingState);
- return HANDLED;
+ peer.close();
+ } catch (SocketException se) {
+ //Consider an Arp socket creation issue as a successful Arp
+ //test to avoid any wifi connectivity issues
+ loge("ARP test initiation failure: " + se);
+ success = true;
}
- }
+ return success;
+ }
/**
* Convenience function for retrieving a single secure settings value