summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Irfan Sheriff <isheriff@google.com> 2012-02-29 19:33:06 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2012-02-29 19:33:06 -0800
commit43d8a95fa8dfd26ba8c56ac7489a8bc77c77034c (patch)
tree8aed3c6b7190073512833b461e4e28aa2a9fe657
parent7084e75282d6c800a1b889eff66794a8cd62e0c5 (diff)
parent07573b32494acbabd21979d8b9584c1ed3f7a6ad (diff)
Merge "Improve Wi-Fi hand-off"
-rw-r--r--api/current.txt1
-rw-r--r--core/java/android/net/NetworkInfo.java5
-rw-r--r--core/java/android/net/arp/ArpPeer.java132
-rw-r--r--core/java/android/provider/Settings.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java15
-rw-r--r--services/java/com/android/server/WifiService.java9
-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
11 files changed, 693 insertions, 837 deletions
diff --git a/api/current.txt b/api/current.txt
index 5babf232f709..cce28a48366f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11815,6 +11815,7 @@ package android.net {
enum_constant public static final android.net.NetworkInfo.DetailedState OBTAINING_IPADDR;
enum_constant public static final android.net.NetworkInfo.DetailedState SCANNING;
enum_constant public static final android.net.NetworkInfo.DetailedState SUSPENDED;
+ enum_constant public static final android.net.NetworkInfo.DetailedState VERIFYING_POOR_LINK;
}
public static final class NetworkInfo.State extends java.lang.Enum {
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 2f43cb87f6fc..0bc6b58aef33 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -77,7 +77,9 @@ public class NetworkInfo implements Parcelable {
/** Attempt to connect failed. */
FAILED,
/** Access to this network is blocked. */
- BLOCKED
+ BLOCKED,
+ /** Link has poor connectivity. */
+ VERIFYING_POOR_LINK
}
/**
@@ -94,6 +96,7 @@ public class NetworkInfo implements Parcelable {
stateMap.put(DetailedState.CONNECTING, State.CONNECTING);
stateMap.put(DetailedState.AUTHENTICATING, State.CONNECTING);
stateMap.put(DetailedState.OBTAINING_IPADDR, State.CONNECTING);
+ stateMap.put(DetailedState.VERIFYING_POOR_LINK, State.CONNECTING);
stateMap.put(DetailedState.CONNECTED, State.CONNECTED);
stateMap.put(DetailedState.SUSPENDED, State.SUSPENDED);
stateMap.put(DetailedState.DISCONNECTING, State.DISCONNECTING);
diff --git a/core/java/android/net/arp/ArpPeer.java b/core/java/android/net/arp/ArpPeer.java
new file mode 100644
index 000000000000..8e666bc275da
--- /dev/null
+++ b/core/java/android/net/arp/ArpPeer.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2012 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 android.net.arp;
+
+import android.os.SystemClock;
+import android.util.Log;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Inet6Address;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+import libcore.net.RawSocket;
+
+/**
+ * This class allows simple ARP exchanges over an uninitialized network
+ * interface.
+ *
+ * @hide
+ */
+public class ArpPeer {
+ private String mInterfaceName;
+ private final InetAddress mMyAddr;
+ private final byte[] mMyMac = new byte[6];
+ private final InetAddress mPeer;
+ private final RawSocket mSocket;
+ private final byte[] L2_BROADCAST; // TODO: refactor from DhcpClient.java
+ private static final int MAX_LENGTH = 1500; // refactor from DhcpPacket.java
+ private static final int ETHERNET_TYPE = 1;
+ private static final int ARP_LENGTH = 28;
+ private static final int MAC_ADDR_LENGTH = 6;
+ private static final int IPV4_LENGTH = 4;
+ private static final String TAG = "ArpPeer";
+
+ public ArpPeer(String interfaceName, InetAddress myAddr, String mac,
+ InetAddress peer) throws SocketException {
+ mInterfaceName = interfaceName;
+ mMyAddr = myAddr;
+
+ for (int i = 0; i < MAC_ADDR_LENGTH; i++) {
+ mMyMac[i] = (byte) Integer.parseInt(mac.substring(
+ i*3, (i*3) + 2), 16);
+ }
+
+ if (myAddr instanceof Inet6Address || peer instanceof Inet6Address) {
+ throw new IllegalArgumentException("IPv6 unsupported");
+ }
+
+ mPeer = peer;
+ L2_BROADCAST = new byte[MAC_ADDR_LENGTH];
+ Arrays.fill(L2_BROADCAST, (byte) 0xFF);
+
+ mSocket = new RawSocket(mInterfaceName, RawSocket.ETH_P_ARP);
+ }
+
+ /**
+ * Returns the MAC address (or null if timeout) for the requested
+ * peer.
+ */
+ public byte[] doArp(int timeoutMillis) {
+ ByteBuffer buf = ByteBuffer.allocate(MAX_LENGTH);
+ byte[] desiredIp = mPeer.getAddress();
+ long timeout = SystemClock.elapsedRealtime() + timeoutMillis;
+
+ // construct ARP request packet, using a ByteBuffer as a
+ // convenient container
+ buf.clear();
+ buf.order(ByteOrder.BIG_ENDIAN);
+
+ buf.putShort((short) ETHERNET_TYPE); // Ethernet type, 16 bits
+ buf.putShort(RawSocket.ETH_P_IP); // Protocol type IP, 16 bits
+ buf.put((byte)MAC_ADDR_LENGTH); // MAC address length, 6 bytes
+ buf.put((byte)IPV4_LENGTH); // IPv4 protocol size
+ buf.putShort((short) 1); // ARP opcode 1: 'request'
+ buf.put(mMyMac); // six bytes: sender MAC
+ buf.put(mMyAddr.getAddress()); // four bytes: sender IP address
+ buf.put(new byte[MAC_ADDR_LENGTH]); // target MAC address: unknown
+ buf.put(desiredIp); // target IP address, 4 bytes
+ buf.flip();
+ mSocket.write(L2_BROADCAST, buf.array(), 0, buf.limit());
+
+ byte[] recvBuf = new byte[MAX_LENGTH];
+
+ while (SystemClock.elapsedRealtime() < timeout) {
+ long duration = (long) timeout - SystemClock.elapsedRealtime();
+ int readLen = mSocket.read(recvBuf, 0, recvBuf.length, -1,
+ (int) duration);
+
+ // Verify packet details. see RFC 826
+ if ((readLen >= ARP_LENGTH) // trailing bytes at times
+ && (recvBuf[0] == 0) && (recvBuf[1] == ETHERNET_TYPE) // type Ethernet
+ && (recvBuf[2] == 8) && (recvBuf[3] == 0) // protocol IP
+ && (recvBuf[4] == MAC_ADDR_LENGTH) // mac length
+ && (recvBuf[5] == IPV4_LENGTH) // IPv4 protocol size
+ && (recvBuf[6] == 0) && (recvBuf[7] == 2) // ARP reply
+ // verify desired IP address
+ && (recvBuf[14] == desiredIp[0]) && (recvBuf[15] == desiredIp[1])
+ && (recvBuf[16] == desiredIp[2]) && (recvBuf[17] == desiredIp[3]))
+ {
+ // looks good. copy out the MAC
+ byte[] result = new byte[MAC_ADDR_LENGTH];
+ System.arraycopy(recvBuf, 8, result, 0, MAC_ADDR_LENGTH);
+ return result;
+ }
+ }
+
+ return null;
+ }
+
+ public void close() {
+ try {
+ mSocket.close();
+ } catch (IOException ex) {
+ }
+ }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0aad64a61be9..b42417add694 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3125,15 +3125,8 @@ public final class Settings {
* ms delay before rechecking an 'online' wifi connection when it is thought to be unstable.
* @hide
*/
- public static final String WIFI_WATCHDOG_DNS_CHECK_SHORT_INTERVAL_MS =
- "wifi_watchdog_dns_check_short_interval_ms";
-
- /**
- * ms delay before rechecking an 'online' wifi connection when it is thought to be stable.
- * @hide
- */
- public static final String WIFI_WATCHDOG_DNS_CHECK_LONG_INTERVAL_MS =
- "wifi_watchdog_dns_check_long_interval_ms";
+ public static final String WIFI_WATCHDOG_ARP_CHECK_INTERVAL_MS =
+ "wifi_watchdog_arp_interval_ms";
/**
* ms delay before rechecking a connect SSID for walled garden with a http download.
@@ -3143,44 +3136,28 @@ public final class Settings {
"wifi_watchdog_walled_garden_interval_ms";
/**
- * max blacklist calls on an SSID before full dns check failures disable the network.
+ * Number of ARP pings per check.
* @hide
*/
- public static final String WIFI_WATCHDOG_MAX_SSID_BLACKLISTS =
- "wifi_watchdog_max_ssid_blacklists";
+ public static final String WIFI_WATCHDOG_NUM_ARP_PINGS = "wifi_watchdog_num_arp_pings";
/**
- * Number of dns pings per check.
+ * Minimum number of responses to the arp pings to consider the test 'successful'.
* @hide
*/
- public static final String WIFI_WATCHDOG_NUM_DNS_PINGS = "wifi_watchdog_num_dns_pings";
+ public static final String WIFI_WATCHDOG_MIN_ARP_RESPONSES =
+ "wifi_watchdog_min_arp_responses";
/**
- * Minimum number of responses to the dns pings to consider the test 'successful'.
+ * Timeout on ARP pings
* @hide
*/
- public static final String WIFI_WATCHDOG_MIN_DNS_RESPONSES =
- "wifi_watchdog_min_dns_responses";
+ public static final String WIFI_WATCHDOG_ARP_PING_TIMEOUT_MS =
+ "wifi_watchdog_arp_ping_timeout_ms";
/**
- * Timeout on dns pings
- * @hide
- */
- public static final String WIFI_WATCHDOG_DNS_PING_TIMEOUT_MS =
- "wifi_watchdog_dns_ping_timeout_ms";
-
- /**
- * We consider action from a 'blacklist' call to have finished by the end of
- * this interval. If we are connected to the same AP with no network connection,
- * we are likely stuck on an SSID with no external connectivity.
- * @hide
- */
- public static final String WIFI_WATCHDOG_BLACKLIST_FOLLOWUP_INTERVAL_MS =
- "wifi_watchdog_blacklist_followup_interval_ms";
-
- /**
- * Setting to turn off poor network avoidance on Wi-Fi. Feature is disabled by default and
- * the setting needs to be set to 1 to enable it.
+ * Setting to turn off poor network avoidance on Wi-Fi. Feature is enabled by default and
+ * the setting needs to be set to 0 to disable it.
* @hide
*/
public static final String WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED =
@@ -3204,14 +3181,6 @@ public final class Settings {
"wifi_watchdog_walled_garden_url";
/**
- * Boolean to determine whether to notify on disabling a network. Secure setting used
- * to notify user only once.
- * @hide
- */
- public static final String WIFI_WATCHDOG_SHOW_DISABLED_NETWORK_POPUP =
- "wifi_watchdog_show_disabled_network_popup";
-
- /**
* The maximum number of times we will retry a connection to an access
* point for which we have failed in acquiring an IP address from DHCP.
* A value of N means that we will make N+1 connection attempts in all.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index c59290cc2453..95704f676f14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -203,7 +203,7 @@ public class NetworkController extends BroadcastReceiver {
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
Handler handler = new WifiHandler();
mWifiChannel = new AsyncChannel();
- Messenger wifiMessenger = mWifiManager.getMessenger();
+ Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger();
if (wifiMessenger != null) {
mWifiChannel.connect(mContext, handler, wifiMessenger);
}
@@ -767,17 +767,10 @@ public class NetworkController extends BroadcastReceiver {
} else if (!mWifiConnected) {
mWifiSsid = null;
}
- // Apparently the wifi level is not stable at this point even if we've just connected to
- // the network; we need to wait for an RSSI_CHANGED_ACTION for that. So let's just set
- // it to 0 for now
- mWifiLevel = 0;
- mWifiRssi = -200;
} else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
- if (mWifiConnected) {
- mWifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
- mWifiLevel = WifiManager.calculateSignalLevel(
- mWifiRssi, WifiIcons.WIFI_LEVEL_COUNT);
- }
+ mWifiRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
+ mWifiLevel = WifiManager.calculateSignalLevel(
+ mWifiRssi, WifiIcons.WIFI_LEVEL_COUNT);
}
updateWifiIcons();
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 52087854c07b..f09c43f51a84 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -914,7 +914,7 @@ public class WifiService extends IWifiManager.Stub {
* Get a reference to handler. This is used by a client to establish
* an AsyncChannel communication with WifiService
*/
- public Messenger getMessenger() {
+ public Messenger getWifiServiceMessenger() {
/* Enforce the highest permissions
TODO: when we consider exposing the asynchronous API, think about
how to provide both access and change permissions seperately
@@ -924,6 +924,13 @@ public class WifiService extends IWifiManager.Stub {
return new Messenger(mAsyncServiceHandler);
}
+ /** Get a reference to WifiStateMachine handler for AsyncChannel communication */
+ public Messenger getWifiStateMachineMessenger() {
+ enforceAccessPermission();
+ enforceChangePermission();
+ return mWifiStateMachine.getMessenger();
+ }
+
/**
* Get the IP and proxy configuration file
*/
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 61dfebf41344..6b0807414b91 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 509b02ca82d8..4a2037d1237d 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 1a0e0dad5f85..1acfd3a5ec21 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 1b64f3e3a2f5..e140d80a3263 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 0ca3852a0bdc..a2f63438c4fa 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