summaryrefslogtreecommitdiffstats
path: root/service/java/com/android/server
diff options
context:
space:
mode:
authorvandwalle <vandwalle@google.com>2014-06-10 20:47:51 -0700
committerPierre Vandwalle <vandwalle@google.com>2014-06-11 23:34:35 +0000
commit4dc6f3a322806b25d50039614cde1b94fe91ab17 (patch)
treeb6dc0b7dec3f8fd096d57cd6ce855c5c8736c19a /service/java/com/android/server
parentf62893bfeaebeaa0f6a840b29f1b7f33493c8894 (diff)
downloadandroid_frameworks_opt_net_wifi-4dc6f3a322806b25d50039614cde1b94fe91ab17.tar.gz
android_frameworks_opt_net_wifi-4dc6f3a322806b25d50039614cde1b94fe91ab17.tar.bz2
android_frameworks_opt_net_wifi-4dc6f3a322806b25d50039614cde1b94fe91ab17.zip
auto-roam initial implementation
Bug:15553951 Change-Id: I04ebca4a4f5a9b408893ce1e9874f08aeff50ba2
Diffstat (limited to 'service/java/com/android/server')
-rw-r--r--service/java/com/android/server/wifi/WifiAutoJoinController.java313
-rw-r--r--service/java/com/android/server/wifi/WifiConfigStore.java69
-rw-r--r--service/java/com/android/server/wifi/WifiStateMachine.java404
-rw-r--r--service/java/com/android/server/wifi/WifiTrafficPoller.java9
4 files changed, 653 insertions, 142 deletions
diff --git a/service/java/com/android/server/wifi/WifiAutoJoinController.java b/service/java/com/android/server/wifi/WifiAutoJoinController.java
index 3d8bbfe3f..95dfbbb45 100644
--- a/service/java/com/android/server/wifi/WifiAutoJoinController.java
+++ b/service/java/com/android/server/wifi/WifiAutoJoinController.java
@@ -64,10 +64,9 @@ public class WifiAutoJoinController {
new HashMap<String, ScanResult>();
//lose the non-auth failure blacklisting after 8 hours
- private final static long loseBlackListHardMilly = 1000 * 60 * 60 * 8;
+ private final static long loseBlackListHardMilli = 1000 * 60 * 60 * 8;
//lose some temporary blacklisting after 30 minutes
- private final static long loseBlackListSoftMilly = 1000 * 60 * 30;
-
+ private final static long loseBlackListSoftMilli = 1000 * 60 * 30;
WifiAutoJoinController(Context c, WifiStateMachine w, WifiConfigStore s,
WifiTrafficPoller t, WifiNative n) {
@@ -454,6 +453,7 @@ public class WifiAutoJoinController {
}
}
}
+
}
return found;
}
@@ -461,7 +461,6 @@ public class WifiAutoJoinController {
int compareWifiConfigurationsRSSI(WifiConfiguration a, WifiConfiguration b) {
int order = 0;
int boost5 = 25;
-
WifiConfiguration.Visibility astatus = a.visibility;
WifiConfiguration.Visibility bstatus = b.visibility;
if (astatus == null || bstatus == null) {
@@ -470,12 +469,12 @@ public class WifiAutoJoinController {
return 0;
}
if ((astatus.rssi5 > -70) && (bstatus.rssi5 == WifiConfiguration.INVALID_RSSI)
- && ((astatus.rssi5+boost5) > (bstatus.rssi24))) {
+ && ((astatus.rssi5 + boost5) > (bstatus.rssi24))) {
//a is seen on 5GHz with good RSSI, greater rssi than b
//a is of higher priority - descending
order = -1;
} else if ((bstatus.rssi5 > -70) && (astatus.rssi5 == WifiConfiguration.INVALID_RSSI)
- && ((bstatus.rssi5+boost5) > (bstatus.rssi24))) {
+ && ((bstatus.rssi5 + boost5) > (bstatus.rssi24))) {
//b is seen on 5GHz with good RSSI, greater rssi than a
//a is of lower priority - ascending
order = 1;
@@ -483,6 +482,7 @@ public class WifiAutoJoinController {
return order;
}
+
int compareWifiConfigurations(WifiConfiguration a, WifiConfiguration b) {
int order = 0;
String lastSelectedConfiguration = mWifiConfigStore.getLastSelectedConfiguration();
@@ -512,7 +512,8 @@ public class WifiAutoJoinController {
return -1; //a is of higher priority - descending
}
- int boost5 = 25;
+ int aRssiBoost5 = 0;
+ int bRssiBoost5 = 0;
//apply Hysteresis: boost the RSSI value of the currently connected configuration
int aRssiBoost = 0;
int bRssiBoost = 0;
@@ -524,6 +525,8 @@ public class WifiAutoJoinController {
}
}
if (linked) {
+ int ascore;
+ int bscore;
// then we try prefer 5GHz, and try to ignore user's choice
WifiConfiguration.Visibility astatus = a.visibility;
WifiConfiguration.Visibility bstatus = b.visibility;
@@ -540,38 +543,65 @@ public class WifiAutoJoinController {
+ Integer.toString(bstatus.rssi24));
}
- if ((astatus.rssi5 > -70) && (bstatus.rssi5 <= WifiConfiguration.INVALID_RSSI)
- && (astatus.rssi5+boost5+aRssiBoost) > (bstatus.rssi24+bRssiBoost)) {
- //in this case: a has 5GHz and b doesn't have 5GHz
- //compare a's 5GHz RSSI to b's 5GHz RSSI
-
- //a is seen on 5GHz with good RSSI, greater rssi than b
- //a is of higher priority - descending
- order = -10;
+ //Boost RSSI value of 5GHz bands iff the base value is better than -65
+ //This implements band preference where we prefer 5GHz if RSSI5 is good enough, whereas
+ //we prefer 2.4GHz otherwise.
+ //Note that 2.4GHz doesn't need a boost since at equal power the RSSI is 6-10 dB higher
+ if ((astatus.rssi5+aRssiBoost) > WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD) {
+ aRssiBoost5 = 25;
+ }
+ if ((bstatus.rssi5+bRssiBoost) > WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD) {
+ bRssiBoost5 = 25;
+ }
+ if (astatus.rssi5+aRssiBoost5 > astatus.rssi24) {
+ //prefer a's 5GHz
+ ascore = astatus.rssi5 + aRssiBoost5 + aRssiBoost;
+ } else {
+ //prefer a's 2.4GHz
+ ascore = astatus.rssi24 + aRssiBoost;
+ }
+ if (bstatus.rssi5+bRssiBoost5 > bstatus.rssi24) {
+ //prefer b's 5GHz
+ bscore = bstatus.rssi5 + bRssiBoost5 + bRssiBoost;
+ } else {
+ //prefer b's 2.4GHz
+ bscore = bstatus.rssi24 + bRssiBoost;
+ }
+ if (ascore > bscore) {
+ //a is seen on 5GHz with good RSSI, greater rssi than b
+ //a is of higher priority - descending
+ order = -10;
if (VDBG) {
logDbg("compareWifiConfigurations linked and prefers " + a.configKey()
+ + " rssi=(" + a.visibility.rssi24
+ + "," + a.visibility.rssi5
+ + ") num=(" + a.visibility.num24
+ + "," + a.visibility.num5 + ")"
+ " over " + b.configKey()
- + " due to 5GHz RSSI " + Integer.toString(astatus.rssi5)
- + " over: 5=" + Integer.toString(bstatus.rssi5)
- + ", 2.4=" + Integer.toString(bstatus.rssi5));
+ + " rssi=(" + b.visibility.rssi24
+ + "," + b.visibility.rssi5
+ + ") num=(" + b.visibility.num24
+ + "," + b.visibility.num5 + ")"
+ + " due to RSSI");
}
- } else if ((bstatus.rssi5 > -70) && (astatus.rssi5 <= WifiConfiguration.INVALID_RSSI)
- && ((bstatus.rssi5+boost5+bRssiBoost) > (astatus.rssi24+aRssiBoost))) {
- //in this case: b has 5GHz and a doesn't have 5GHz
-
- //b is seen on 5GHz with good RSSI, greater rssi than a
- //a is of lower priority - ascending
- if (VDBG) {
+ } else if (bscore > ascore) {
+ //b is seen on 5GHz with good RSSI, greater rssi than a
+ //a is of lower priority - ascending
+ order = 10;
+ if (VDBG) {
logDbg("compareWifiConfigurations linked and prefers " + b.configKey()
- + " over " + a.configKey() + " due to 5GHz RSSI "
- + Integer.toString(astatus.rssi5) + " over: 5="
- + Integer.toString(bstatus.rssi5) + ", 2.4="
- + Integer.toString(bstatus.rssi5));
+ + " rssi=(" + b.visibility.rssi24
+ + "," + b.visibility.rssi5
+ + ") num=(" + b.visibility.num24
+ + "," + b.visibility.num5 + ")"
+ + " over " + a.configKey()
+ + " rssi=(" + a.visibility.rssi24
+ + "," + a.visibility.rssi5
+ + ") num=(" + a.visibility.num24
+ + "," + a.visibility.num5 + ")"
+ + " due to RSSI");
}
- order = 10;
- } else {
- //TODO: handle cases where configurations are dual band
}
}
@@ -612,21 +642,27 @@ public class WifiAutoJoinController {
if ((lastSelectedConfiguration != null)
&& a.configKey().equals(lastSelectedConfiguration)) {
- // a is the last selected configuration, so keep it above connect choices
- //by giving a -4 (whereas connect choice preference gives +2)
- order = order - 4;
+ // a is the last selected configuration, so keep it above connect choices (+/-2) and
+ // above RSSI based selection of linked configuration (+/- 11)
+ // by giving a -11
+ // Additional other factors like BAD RSSI (still to do) and ASSOC_REJECTION high counts will then still
+ // tip the auto-join to roam
+ order = order - 11;
if (VDBG) {
- logDbg("compareWifiConfigurations prefers -4 " + a.configKey()
+ logDbg("compareWifiConfigurations prefers -11 " + a.configKey()
+ " over " + b.configKey() + " because a is the last selected -> "
+ Integer.toString(order));
}
} else if ((lastSelectedConfiguration != null)
&& b.configKey().equals(lastSelectedConfiguration)) {
- // b is the last selected configuration, so keep it above connect choices
- //by giving a +4 (whereas connect choice preference gives -2)
- order = order + 4;
+ // b is the last selected configuration, so keep it above connect choices (+/-2) and
+ // above RSSI based selection of linked configuration (+/- 11)
+ // by giving a +11
+ // Additional other factors like BAD RSSI (still to do) and ASSOC_REJECTION high counts will then still
+ // tip the auto-join to roam
+ order = order + 11;
if (VDBG) {
- logDbg("compareWifiConfigurations prefers +4 " + a.configKey()
+ logDbg("compareWifiConfigurations prefers +11 " + a.configKey()
+ " over " + b.configKey() + " because b is the last selected -> "
+ Integer.toString(order));
}
@@ -677,7 +713,86 @@ public class WifiAutoJoinController {
}
/* attemptAutoJoin function implement the core of the a network switching algorithm */
+ ScanResult attemptRoam(WifiConfiguration candidate, int age) {
+ ScanResult a = null;
+ String currentBSSID = mWifiStateMachine.getCurrentBSSID();
+ if (candidate == null) {
+ return null;
+ }
+ if (candidate.scanResultCache == null) {
+ return null;
+ }
+ if (candidate.scanResultCache.size() > 4) {
+ //implement same SSID roaming only for configuration that have less than 4 BSSIDs
+ return null;
+ }
+ if (candidate.visibility.num5 == 0) {
+ //implement same SSID roaming only for configuration that have 5GHz BSSIDs
+ return null;
+ }
+
+ //determine which BSSID we want to associate to, taking account relative strength of 5 and 2.4 GHz BSSIDs
+ long now_ms = System.currentTimeMillis();
+ int bRssiBoost5 = 0;
+ int aRssiBoost5 = 0;
+ int bRssiBoost = 0;
+ int aRssiBoost = 0;
+ for (ScanResult b : candidate.scanResultCache.values()) {
+
+ if (b.seen == 0)
+ continue;
+
+ if (b.BSSID == null)
+ continue;
+
+ if ((now_ms - b.seen) > age) continue;
+
+ //pick first one
+ if (a == null) {
+ a = b;
+ continue;
+ }
+
+ if (currentBSSID != null && currentBSSID.equals(b.BSSID)) {
+ bRssiBoost = +10;
+ }
+ if (currentBSSID != null && currentBSSID.equals(a.BSSID)) {
+ aRssiBoost = +10;
+ }
+ if (b.is5GHz() && (b.level+bRssiBoost) > WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD) {
+ bRssiBoost5 = 25;
+ }
+ if (a.is5GHz() && (a.level+aRssiBoost) > WifiConfiguration.A_BAND_PREFERENCE_RSSI_THRESHOLD) {
+ aRssiBoost5 = 25;
+ }
+ if (b.level + bRssiBoost + bRssiBoost5 > a.level +aRssiBoost + aRssiBoost5) {
+ //b is the better BSSID
+ a = b;
+ }
+
+ if (VDBG) {
+ logDbg("attemptRoam: "
+ + b.BSSID + "rssi=" + b.level + " freq=" + b.frequency + " versus "
+ + a.BSSID + "rssi=" + a.level + " freq=" + a.frequency
+ );
+ }
+ }
+ if (VDBG) {
+ logDbg("attemptRoam: Found "
+ + a.BSSID + "rssi=" + a.level + " freq=" + a.frequency
+ + " Current: " + currentBSSID);
+ }
+ if (currentBSSID!= null && currentBSSID.equals(a.BSSID)) {
+ return null;
+ } else {
+ return a;
+ }
+ }
+
+ /* attemptAutoJoin function implement the core of the a network switching algorithm */
void attemptAutoJoin() {
+ int isRoaming = 0;
+
String lastSelectedConfiguration = mWifiConfigStore.getLastSelectedConfiguration();
// reset the currentConfiguration Key, and set it only if WifiStateMachine and
@@ -740,7 +855,9 @@ public class WifiAutoJoinController {
}
}
- /* select Best Network candidate from known WifiConfigurations */
+ /* run thru all visible configurations without looking at the one we are currently associated to
+ * select Best Network candidate from known WifiConfigurations
+ * */
for (WifiConfiguration config : list) {
if ((config.status == WifiConfiguration.Status.DISABLED)
&& (config.disableReason == WifiConfiguration.DISABLED_AUTH_FAILURE)) {
@@ -758,7 +875,7 @@ public class WifiAutoJoinController {
if (config.autoJoinStatus >=
WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
//avoid temporarily disabled networks altogether
- //TODO: implement a better logic which will reenable the network after some time
+ //TODO: implement a better logic which will re-enable the network after some time
if (DBG) {
logDbg("attemptAutoJoin skip candidate due to auto join status "
+ Integer.toString(config.autoJoinStatus) + " key "
@@ -776,10 +893,10 @@ public class WifiAutoJoinController {
//this event should be rare enough so that we still want to lose the black list
config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
} else {
- if ((now - config.blackListTimestamp) > loseBlackListHardMilly) {
+ if ((now - config.blackListTimestamp) > loseBlackListHardMilli) {
//reenable it after 18 hours, i.e. next day
config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
- } else if ((now - config.blackListTimestamp) > loseBlackListSoftMilly) {
+ } else if ((now - config.blackListTimestamp) > loseBlackListSoftMilli) {
//lose blacklisting due to bad link
config.setAutoJoinStatus(config.autoJoinStatus - 8);
}
@@ -791,12 +908,12 @@ public class WifiAutoJoinController {
&& config.visibility.rssi24 < WifiConfiguration.UNBLACKLIST_THRESHOLD_24_SOFT) {
if (DBG) {
logDbg("attemptAutoJoin skip candidate due to auto join status "
- + Integer.toString(config.autoJoinStatus) + " key "
- + config.configKey(true)
- + " rssi=" + config.visibility.rssi24
- + ", " + config.visibility.rssi5
- + " num=" + config.visibility.num24
- + ", " + config.visibility.num5);
+ + config.autoJoinStatus
+ + " key " + config.configKey(true)
+ + " rssi=(" + config.visibility.rssi24
+ + "," + config.visibility.rssi5
+ + ") num=(" + config.visibility.num24
+ + "," + config.visibility.num5 + ")");
}
} else if (config.visibility.rssi5 < WifiConfiguration.UNBLACKLIST_THRESHOLD_5_HARD
&& config.visibility.rssi24 < WifiConfiguration.UNBLACKLIST_THRESHOLD_24_HARD) {
@@ -806,24 +923,34 @@ public class WifiAutoJoinController {
if (DBG) {
logDbg("attemptAutoJoin good candidate seen, bumped soft -> status="
+ config.autoJoinStatus
- + ", " + config.visibility.rssi5
- + " num=" + config.visibility.num24
- + ", " + config.visibility.num5);
+ + " key " + config.configKey(true) + " rssi=("
+ + config.visibility.rssi24 + "," + config.visibility.rssi5
+ + ") num=(" + config.visibility.num24
+ + "," + config.visibility.num5 + ")");
}
} else {
config.setAutoJoinStatus(config.autoJoinStatus - 2);
if (DBG) {
logDbg("attemptAutoJoin good candidate seen, bumped hard -> status="
+ config.autoJoinStatus
- + ", " + config.visibility.rssi5
- + " num=" + config.visibility.num24
- + ", " + config.visibility.num5);
+ + " key " + config.configKey(true) + " rssi=("
+ + config.visibility.rssi24 + "," + config.visibility.rssi5
+ + ") num=(" + config.visibility.num24
+ + "," + config.visibility.num5 + ")");
}
}
if (config.autoJoinStatus >=
WifiConfiguration.AUTO_JOIN_TEMPORARY_DISABLED) {
//network is blacklisted, skip
+ if (DBG) {
+ logDbg("attemptAutoJoin skip blacklisted -> status="
+ + config.autoJoinStatus
+ + " key " + config.configKey(true) + " rssi=("
+ + config.visibility.rssi24 + "," + config.visibility.rssi5
+ + ") num=(" + config.visibility.num24
+ + "," + config.visibility.num5 + ")");
+ }
continue;
}
if (config.networkId == currentNetId) {
@@ -843,13 +970,22 @@ public class WifiAutoJoinController {
}
if (config.visibility.rssi5 < WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_5
&& config.visibility.rssi24 < WifiConfiguration.INITIAL_AUTO_JOIN_ATTEMPT_MIN_24) {
+ if (DBG) {
+ logDbg("attemptAutoJoin gskip due to low visibility -> status="
+ + config.autoJoinStatus
+ + " key " + config.configKey(true) + " rssi="
+ + config.visibility.rssi24 + ", " + config.visibility.rssi5
+ + " num=" + config.visibility.num24
+ + ", " + config.visibility.num5);
+ }
continue;
}
}
if (DBG) {
logDbg("attemptAutoJoin trying candidate id=" + config.networkId + " "
- + config.SSID + " key " + config.configKey(true));
+ + config.SSID + " key " + config.configKey(true)
+ + " status=" + config.autoJoinStatus);
}
if (candidate == null) {
@@ -857,7 +993,7 @@ public class WifiAutoJoinController {
} else {
if (VDBG) {
logDbg("attemptAutoJoin will compare candidate " + candidate.configKey()
- + " with " + config.configKey() + " key " + config.configKey(true));
+ + " with " + config.configKey());
}
int order = compareWifiConfigurations(candidate, config);
@@ -889,7 +1025,7 @@ public class WifiAutoJoinController {
if (score > 0) {
// try any arbitrary formula for now, adding apple and oranges,
// i.e. adding network score and "dBm over noise"
- if (result.frequency < 4000) {
+ if (result.is24GHz()) {
if ((result.level + score) > (rssi24 -40)) {
// force it as open, TBD should we otherwise verify that this
// BSSID only supports open??
@@ -918,29 +1054,68 @@ public class WifiAutoJoinController {
}
}
if (candidate != null) {
- /* if candidate is found, check the state of the connection so as
- to decide if we should be acting on this candidate and switching over */
+ ScanResult roamCandidate = null;
+ /* if candidate is found, check the state of the connection so as
+ to decide if we should be acting on this candidate and switching over */
+ if (currentConfiguration != null && currentConfiguration.isLinked(candidate)) {
+ isRoaming = 2;
+ }
int networkDelta = compareNetwork(candidate);
if (DBG && (networkDelta > 0)) {
+ String roam = "";
+ if (isRoaming == 1)
+ roam = "roaming";
+ if (isRoaming == 2)
+ roam = "extended-roaming";
logDbg("attemptAutoJoin did find candidate " + candidate.configKey()
- + " for delta " + Integer.toString(networkDelta));
+ + " for delta " + Integer.toString(networkDelta)
+ + roam);
}
+
+ if (networkDelta <= 0) {
+ roamCandidate = attemptRoam(currentConfiguration, 3000);
+ if (roamCandidate != null)
+ networkDelta = 10; //TODO: adjust this based on RSSI difference?
+ }
+
/* ASK traffic poller permission to switch:
for instance,
if user is currently streaming voice traffic,
then don’t switch regardless of the delta */
- if (mWifiTrafficPoller.shouldSwitchNetwork(networkDelta)) {
+ if (mWifiStateMachine.shouldSwitchNetwork(networkDelta)) {
if (mStaStaSupported) {
logDbg("mStaStaSupported --> error do nothing now ");
} else {
- if (DBG) {
- logDbg("AutoJoin auto connect with netId "
- + Integer.toString(candidate.networkId)
- + " to " + candidate.configKey());
+ if (roamCandidate != null) {
+ if (DBG) {
+ logDbg("AutoJoin auto roam with netId "
+ + Integer.toString(currentConfiguration.networkId)
+ + " " + candidate.configKey() + " to BSSID="
+ + roamCandidate.BSSID + " freq=" + roamCandidate.frequency
+ + " RSSI=" + roamCandidate.frequency);
+ }
+ if (roamCandidate.is5GHz()) {
+ mWifiStateMachine.sendMessage(WifiStateMachine.CMD_AUTO_ROAM,
+ candidate.networkId, 2, roamCandidate.BSSID);
+ } else {
+ //if we want to autoroam to 2.4GHz then force reassociate without locking the
+ //BSSID, the wifi chipset should normally select a 2.4GHz BSSID as RSSI will be stronger,
+ //or otherwise fall back normally thru the firmware roam.
+ //Hence, roaming between 2.4GHz BSSIDs will be handled by firmware
+ //whereas roaming onto 5GHz BSSIDs will be handled by framework
+ mWifiStateMachine.sendMessage(WifiStateMachine.CMD_AUTO_ROAM,
+ candidate.networkId, 2, "any");
+ }
+ } else {
+ if (DBG) {
+ logDbg("AutoJoin auto connect with netId "
+ + Integer.toString(candidate.networkId)
+ + " to " + candidate.configKey());
+ }
+ mWifiStateMachine.sendMessage(WifiStateMachine.CMD_AUTO_CONNECT,
+ candidate.networkId, isRoaming, candidate);
}
- mWifiStateMachine.sendMessage(WifiStateMachine.CMD_AUTO_CONNECT,
- candidate.networkId);
}
}
}
diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java
index 495876c35..8dd7f3442 100644
--- a/service/java/com/android/server/wifi/WifiConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiConfigStore.java
@@ -75,13 +75,7 @@ import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.text.SimpleDateFormat;
import java.text.DateFormat;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
/**
* This class provides the API to manage configured
@@ -1959,6 +1953,54 @@ public class WifiConfigStore extends IpConfigStore {
return config;
}
+ public Set<Integer> makeChannelList(WifiConfiguration config, int age) {
+ if (config == null)
+ return null;
+ long now_ms = System.currentTimeMillis();
+
+ HashSet<Integer> channels = new HashSet<Integer>();
+
+ //get channels for this configuration, if there are at least 2 BSSIDs
+ if (config.scanResultCache == null && config.linkedConfigurations == null) {
+ return null;
+ }
+
+ if (VDBG) {
+ StringBuilder dbg = new StringBuilder();
+ dbg.append("makeChannelList for " + config.configKey());
+ if (config.scanResultCache != null) {
+ dbg.append(" bssids=" + config.scanResultCache.size());
+ }
+ if (config.linkedConfigurations != null) {
+ dbg.append(" linked=" + config.linkedConfigurations.size());
+ }
+ loge(dbg.toString());
+ }
+ if (config.scanResultCache != null && config.scanResultCache.size() > 0) {
+ for (ScanResult result : config.scanResultCache.values()) {
+ if ((now_ms - result.seen) < age) {
+ channels.add(result.frequency);
+ }
+ }
+ }
+ //get channels for linked configurations
+ if (config.linkedConfigurations != null) {
+ for (String key : config.linkedConfigurations.keySet()) {
+ WifiConfiguration linked = getWifiConfiguration(key);
+ if (linked == null)
+ continue;
+ if (linked.scanResultCache == null) {
+ return null;
+ }
+ for (ScanResult result : linked.scanResultCache.values()) {
+ if ((now_ms - result.seen) < age) {
+ channels.add(result.frequency);
+ }
+ }
+ }
+ }
+ return channels;
+ }
public WifiConfiguration updateSavedNetworkHistory(ScanResult scanResult) {
WifiConfiguration found = null;
@@ -2551,6 +2593,8 @@ public class WifiConfigStore extends IpConfigStore {
loge("SSID re-enabled for " + config.configKey() +
" had autoJoinStatus=" + Integer.toString(config.autoJoinStatus)
+ " self added " + config.selfAdded + " ephemeral " + config.ephemeral);
+ //TODO: really I don't know if re-enabling is right but we should err on the side of trying to connect
+ //TODO: even if the attempt will fail
if (config.autoJoinStatus == WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_ENABLED);
}
@@ -2561,14 +2605,13 @@ public class WifiConfigStore extends IpConfigStore {
if (message != null) {
loge(" wpa_supplicant message=" + message);
}
- if (config.selfAdded) {
- //this is a network we self added, so as auto-join can opportunistically try it
- //the user did not create this network and entered its credentials, so we want
+ if (config.selfAdded && config.lastConnected == 0) {
+ //this is a network we self added, and we never succeeded,
+ //the user did not create this network and never entered its credentials, so we want
//to be very aggressive in disabling it completely.
disableNetwork(config.networkId, WifiConfiguration.DISABLED_AUTH_FAILURE);
config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
config.disableReason = WifiConfiguration.DISABLED_AUTH_FAILURE;
-
} else {
if (message != null) {
if (message.contains("WRONG_KEY")
@@ -2585,7 +2628,9 @@ public class WifiConfigStore extends IpConfigStore {
//TODO: blacklisting hard
if (config.autoJoinStatus <=
WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE) {
- config.setAutoJoinStatus(3 + config.autoJoinStatus * 2);
+ //4 auth failure will reach 128 and disable permanently
+ //autoJoinStatus: 0 -> 4 -> 20 -> 84 -> 128
+ config.setAutoJoinStatus(4 + config.autoJoinStatus * 4);
if (config.autoJoinStatus > WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE)
config.setAutoJoinStatus(WifiConfiguration.AUTO_JOIN_DISABLED_ON_AUTH_FAILURE);
}
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index 242bd0fac..03aa8d329 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -42,20 +42,8 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
-import android.net.ConnectivityManager;
-import android.net.DhcpResults;
-import android.net.DhcpStateMachine;
-import android.net.InterfaceConfiguration;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.NetworkAgent;
-import android.net.NetworkCapabilities;
-import android.net.NetworkFactory;
-import android.net.NetworkInfo;
+import android.net.*;
import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkRequest;
-import android.net.NetworkUtils;
-import android.net.RouteInfo;
import android.net.wifi.BatchedScanResult;
import android.net.wifi.BatchedScanSettings;
import android.net.wifi.RssiPacketCountInfo;
@@ -107,14 +95,9 @@ import com.android.server.wifi.passpoint.WifiPasspointStateMachine;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Queue;
+import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.Iterator;
import java.util.regex.Pattern;
import java.io.FileReader;
import java.io.BufferedReader;
@@ -212,6 +195,8 @@ public class WifiStateMachine extends StateMachine {
private final Queue<Message> mBufferedScanMsg = new LinkedList<Message>();
private WorkSource mScanWorkSource = null;
private static final int UNKNOWN_SCAN_SOURCE = -1;
+ private static final int SCAN_ALARM_SOURCE = -2;
+
private static final int SCAN_REQUEST_BUFFER_MAX_SIZE = 10;
private static final String CUSTOMIZED_SCAN_SETTING = "customized_scan_settings";
private static final String CUSTOMIZED_SCAN_WORKSOURCE = "customized_scan_worksource";
@@ -293,10 +278,21 @@ public class WifiStateMachine extends StateMachine {
private DhcpStateMachine mDhcpStateMachine;
private boolean mDhcpActive = false;
- private boolean mWifiLinkLayerStatsSupported = true;
+ private int mWifiLinkLayerStatsSupported = 4;
private final AtomicInteger mCountryCodeSequence = new AtomicInteger();
+ //whether the state machine goes thru the Disconnecting->Disconnected->ObtainingIpAddress
+ private enum AutoRoaming {
+ IDLE,
+ ROAMING,
+ EXTENDED_ROAMING
+ };
+
+ AutoRoaming mAutoRoaming = AutoRoaming.IDLE;
+
+ static private final int ONE_HOUR_MILLI = 1000 * 60 * 60;
+
private class InterfaceObserver extends BaseNetworkObserver {
private WifiStateMachine mWifiStateMachine;
@@ -513,6 +509,9 @@ public class WifiStateMachine extends StateMachine {
static final int CMD_UNWANTED_NETWORK = BASE + 144;
+ static final int CMD_AUTO_ROAM = BASE + 145;
+
+
/* Wifi state machine modes of operation */
/* CONNECT_MODE - connect to any 'known' AP when it becomes available */
public static final int CONNECT_MODE = 1;
@@ -595,9 +594,6 @@ public class WifiStateMachine extends StateMachine {
// currently connected network), so we save the country code here to avoid redundency
private String mLastSetCountryCode;
- private static final int MIN_RSSI = -200;
- private static final int MAX_RSSI = 256;
-
/* Default parent state */
private State mDefaultState = new DefaultState();
/* Temporary initial state */
@@ -633,6 +629,8 @@ public class WifiStateMachine extends StateMachine {
private State mVerifyingLinkState = new VerifyingLinkState();
/* Connected with IP addr */
private State mConnectedState = new ConnectedState();
+ /* Roaming */
+ private State mRoamingState = new RoamingState();
/* disconnect issued, waiting for network disconnect confirmation */
private State mDisconnectingState = new DisconnectingState();
/* Network is not connected, supplicant assoc+auth is not complete */
@@ -821,7 +819,7 @@ public class WifiStateMachine extends StateMachine {
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- startScan(UNKNOWN_SCAN_SOURCE, null, null);
+ startScan(SCAN_ALARM_SOURCE, null, null);
if (VDBG)
loge("WiFiStateMachine SCAN ALARM");
}
@@ -907,6 +905,7 @@ public class WifiStateMachine extends StateMachine {
addState(mObtainingIpState, mL2ConnectedState);
addState(mVerifyingLinkState, mL2ConnectedState);
addState(mConnectedState, mL2ConnectedState);
+ addState(mRoamingState, mL2ConnectedState);
addState(mDisconnectingState, mConnectModeState);
addState(mDisconnectedState, mConnectModeState);
addState(mWpsRunningState, mConnectModeState);
@@ -1401,7 +1400,8 @@ public class WifiStateMachine extends StateMachine {
loge(ts + " noteScanstart no scan source");
}
}
- if (mScanWorkSource == null && (callingUid != UNKNOWN_SCAN_SOURCE || workSource != null)) {
+ if (mScanWorkSource == null && ((callingUid != UNKNOWN_SCAN_SOURCE && callingUid != SCAN_ALARM_SOURCE)
+ || workSource != null)) {
mScanWorkSource = workSource != null ? workSource : new WorkSource(callingUid);
try {
mBatteryStats.noteWifiScanStartedFromSource(mScanWorkSource);
@@ -2415,7 +2415,7 @@ public class WifiStateMachine extends StateMachine {
+ Integer.toString(newLinkSpeed));
}
- if (newRssi != -1 && MIN_RSSI < newRssi && newRssi < MAX_RSSI) { // screen out invalid values
+ if (newRssi > WifiInfo.INVALID_RSSI && newRssi < WifiInfo.MAX_RSSI) { // screen out invalid values
/* some implementations avoid negative values by adding 256
* so we need to adjust for that here.
*/
@@ -2437,7 +2437,7 @@ public class WifiStateMachine extends StateMachine {
}
mLastSignalLevel = newSignalLevel;
} else {
- mWifiInfo.setRssi(MIN_RSSI);
+ mWifiInfo.setRssi(WifiInfo.INVALID_RSSI);
}
if (newLinkSpeed != -1) {
@@ -2448,10 +2448,49 @@ public class WifiStateMachine extends StateMachine {
}
}
+ /* determine if we need to switch network:
+ * - the delta determine the urgency to switch and/or or the expected evilness of the disruption
+ * - match the uregncy of the switch versus the packet usage at the interface
+ */
+ boolean shouldSwitchNetwork(int networkDelta) {
+ int delta;
+ if (networkDelta < 0) {
+ networkDelta = -1 * networkDelta;
+ }
+ delta = networkDelta;
+ if (mWifiInfo != null) {
+ //TODO: look at per AC packet count, do not switch if VO/VI traffic is present at the interface
+ //TODO: discriminate between ucast and mcast, since the rxSuccessRate include all the bonjour and Ipv6
+ //TODO: broadcasts
+ if ((mWifiInfo.txSuccessRate > 20) || (mWifiInfo.rxSuccessRate > 80)) {
+ delta -= 1000;
+ } else if ((mWifiInfo.txSuccessRate > 5) || (mWifiInfo.rxSuccessRate > 30)) {
+ delta -= 6;
+ }
+ loge("WifiStateMachine shouldSwitchNetwork "
+ + " txSuccessRate=" + String.format( "%.2f", mWifiInfo.txSuccessRate)
+ + " rxSuccessRate=" +String.format( "%.2f", mWifiInfo.rxSuccessRate)
+ + " delta " + networkDelta + " -> " + delta);
+ } else {
+ loge("WifiStateMachine shouldSwitchNetwork "
+ + " delta " + networkDelta + " -> " + delta);
+ }
+ if (delta > 0) {
+ return true;
+ }
+ return false;
+ }
private void calculateWifiScore(WifiLinkLayerStats stats) {
- mWifiInfo.updatePacketRates(stats);
+ if (stats == null || mWifiLinkLayerStatsSupported <= 0) {
+ long mTxPkts = TrafficStats.getTxPackets(mInterfaceName);
+ long mRxPkts = TrafficStats.getRxPackets(mInterfaceName);
+ mWifiInfo.updatePacketRates(mTxPkts, mRxPkts);
+
+ } else {
+ mWifiInfo.updatePacketRates(stats);
+ }
int score = 56; //starting score, temporarily hardcoded in between 50 and 60
boolean isBadLinkspeed = (mWifiInfo.is24GHz()
&& mWifiInfo.getLinkSpeed() <= 6)
@@ -2496,10 +2535,7 @@ public class WifiStateMachine extends StateMachine {
score += 4; //so as bad rssi alone dont kill us
}
-
-
if (isBadRSSI) {
-
if (mWifiInfo.badRssiCount < 7)
mWifiInfo.badRssiCount += 1;
} else if (isLowRSSI) {
@@ -2762,7 +2798,13 @@ public class WifiStateMachine extends StateMachine {
if (state != mNetworkInfo.getDetailedState()) {
mNetworkInfo.setDetailedState(state, null, mWifiInfo.getSSID());
- if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ if (mNetworkAgent != null) {
+ if (mAutoRoaming == AutoRoaming.IDLE ||
+ (state != DetailedState.DISCONNECTED && state != DetailedState.DISCONNECTING) ) {
+ //don't tell the Network agent if we are doing a disconnect-roam
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ }
+ }
}
}
@@ -2799,7 +2841,11 @@ public class WifiStateMachine extends StateMachine {
* using the interface, stopping DHCP & disabling interface
*/
private void handleNetworkDisconnect() {
- if (DBG) log("handleNetworkDisconnect: Stopping DHCP and clearing IP");
+ if (DBG) log("handleNetworkDisconnect: Stopping DHCP and clearing IP"
+ + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
+ +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName()
+ +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName()
+ +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName());
stopDhcp();
@@ -2811,16 +2857,7 @@ public class WifiStateMachine extends StateMachine {
}
/* Reset data structures */
- // TODO: use a WifiInfo.reset(), although it would require moving the
- // MIN_RSSI to WifiInfo.
- mWifiInfo.setInetAddress(null);
- mWifiInfo.setBSSID(null);
- mWifiInfo.setSSID(null);
- mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
- mWifiInfo.setRssi(MIN_RSSI);
- mWifiInfo.setLinkSpeed(-1);
- mWifiInfo.setFrequency(-1);
- mWifiInfo.setMeteredHint(false);
+ mWifiInfo.reset();
setNetworkDetailedState(DetailedState.DISCONNECTED);
if (mNetworkAgent != null) {
@@ -2836,6 +2873,7 @@ public class WifiStateMachine extends StateMachine {
sendNetworkStateChangeBroadcast(mLastBssid);
mLastBssid= null;
+ registerDisconnected();
mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
}
@@ -2903,6 +2941,16 @@ public class WifiStateMachine extends StateMachine {
mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
}
+ void renewDhcp() {
+ if (mDhcpStateMachine == null) {
+ mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(
+ mContext, WifiStateMachine.this, mInterfaceName);
+
+ }
+ mDhcpStateMachine.registerForPreDhcpNotification();
+ mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_RENEW_DHCP);
+ }
+
void stopDhcp() {
if (mDhcpStateMachine != null) {
/* In case we were in middle of DHCP operation restore back powermode */
@@ -2950,6 +2998,24 @@ public class WifiStateMachine extends StateMachine {
if (addrs.hasNext()) {
addr = addrs.next();
}
+
+ if (mAutoRoaming != mAutoRoaming.IDLE) {
+ if (addr instanceof Inet4Address) {
+ int previousAddress = mWifiInfo.getIpAddress();
+ int newAddress = NetworkUtils.inetAddressToInt((Inet4Address)addr);
+ if (previousAddress != newAddress) {
+ loge("handleSuccessfulIpConfiguration, roaming and address changed" +
+ mWifiInfo + " got: " + addr);
+ } else {
+
+ }
+ } else {
+ loge("handleSuccessfulIpConfiguration, roaming and didnt get an IPv4 address" +
+ addr.toString());
+
+
+ }
+ }
mWifiInfo.setInetAddress(addr);
mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());
updateLinkProperties();
@@ -4161,6 +4227,9 @@ public class WifiStateMachine extends StateMachine {
case CMD_AUTO_CONNECT:
s = "CMD_AUTO_CONNECT";
break;
+ case CMD_AUTO_ROAM:
+ s = "CMD_AUTO_ROAM";
+ break;
case CMD_BOOT_COMPLETED:
s = "CMD_BOOT_COMPLETED";
break;
@@ -4312,6 +4381,28 @@ public class WifiStateMachine extends StateMachine {
return s;
}
+ void registerConnected() {
+ if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
+ long now_ms = System.currentTimeMillis();
+ //we are switching away from this configuration, hence record the time we were connected last
+ WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
+ if (config != null) {
+ config.lastConnected = System.currentTimeMillis();
+ }
+ }
+ }
+
+ void registerDisconnected() {
+ if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
+ long now_ms = System.currentTimeMillis();
+ //we are switching away from this configuration, hence record the time we were connected last
+ WifiConfiguration config = mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
+ if (config != null) {
+ config.lastDisconnected = System.currentTimeMillis();
+ }
+ }
+ }
+
WifiConfiguration getCurrentWifiConfiguration() {
if (mLastNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
return null;
@@ -4319,6 +4410,10 @@ public class WifiStateMachine extends StateMachine {
return mWifiConfigStore.getWifiConfiguration(mLastNetworkId);
}
+ String getCurrentBSSID() {
+ return mLastBssid;
+ }
+
class ConnectModeState extends State {
@Override
public boolean processMessage(Message message) {
@@ -4465,6 +4560,8 @@ public class WifiStateMachine extends StateMachine {
mWifiNative.reconnect();
}
break;
+ case CMD_AUTO_ROAM:
+ return HANDLED;
case CMD_AUTO_CONNECT:
/* Work Around: wpa_supplicant can get in a bad state where it returns a non
* associated status thus the STATUS command but somehow-someplace still thinks
@@ -4479,11 +4576,16 @@ public class WifiStateMachine extends StateMachine {
/* connect command coming from auto-join */
config = (WifiConfiguration) message.obj;
netId = message.arg1;
+ int roam = message.arg2;
loge("CMD_AUTO_CONNECT sup state "
+ mSupplicantStateTracker.getSupplicantStateName()
+ " my state " + getCurrentState().getName()
- + " nid=" + Integer.toString(netId));
+ + " nid=" + Integer.toString(netId)
+ + " roam=" + Integer.toString(roam));
+
+ /* make sure we cancel any previous roam request */
+ config.BSSID = "any";
/* Save the network config */
if (config != null) {
@@ -4507,8 +4609,14 @@ public class WifiStateMachine extends StateMachine {
/* The state tracker handles enabling networks upon completion/failure */
mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
//replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
- /* Expect a disconnection from the old connection */
- transitionTo(mDisconnectingState);
+ if (roam > 0) {
+ mAutoRoaming = AutoRoaming.EXTENDED_ROAMING;
+ }
+ if (mAutoRoaming != AutoRoaming.IDLE) {
+ transitionTo(mRoamingState);
+ } else {
+ transitionTo(mDisconnectingState);
+ }
} else {
loge("Failed to connect config: " + config + " netId: " + netId);
replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
@@ -4539,6 +4647,8 @@ public class WifiStateMachine extends StateMachine {
/* Save the network config */
if (config != null) {
+ /* make sure we don't lock the BSSID, TODO: allow it if it was not previously set by autojoin */
+ config.BSSID = "any";
NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
netId = result.getNetworkId();
}
@@ -4740,8 +4850,54 @@ public class WifiStateMachine extends StateMachine {
deferMessage(message);
break;
case CMD_START_SCAN:
- /* Do not attempt to connect when we are already connected */
- handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
+ if (DBG) {
+ loge("WifiStateMachine CMD_START_SCAN source " + message.arg1);
+ }
+ if (message.arg1 == SCAN_ALARM_SOURCE) {
+ if (mWifiInfo != null) {
+ //don't scan if lots of packets are being sent
+ //TODO add stats from TrafficPoller
+ if (mWifiInfo.txSuccessRate > 25 || mWifiInfo.rxSuccessRate > 80) {
+ if (DBG) {
+ loge("WifiStateMachine CMD_START_SCAN source " + message.arg1
+ + " and ignore scans "
+ + "tx=" + String.format( "%.2f", mWifiInfo.txSuccessRate)
+ + "rx=" + String.format( "%.2f", mWifiInfo.rxSuccessRate));
+ }
+ return HANDLED;
+ }
+ }
+ WifiConfiguration currentConfiguration = getCurrentWifiConfiguration();
+ if (currentConfiguration != null) {
+ Set<Integer> channels = mWifiConfigStore.makeChannelList(currentConfiguration,
+ ONE_HOUR_MILLI);
+ if (channels != null && channels.size() != 0) {
+ StringBuilder freqs = new StringBuilder();
+ boolean first = true;
+ for (Integer channel : channels) {
+ if (!first)
+ freqs.append(",");
+ freqs.append(channel.toString());
+ first = false;
+ }
+ if (DBG) {
+ loge("WifiStateMachine starting scan with " + freqs);
+ }
+ // call wifi native to start the scan
+ if (startScanNative(SCAN_ONLY_MODE, freqs.toString())) {
+ // only count battery consumption if scan request is accepted
+ noteScanStart(SCAN_ALARM_SOURCE, null);
+ }
+ } else {
+ if (DBG) {
+ loge("WifiStateMachine starting scan, did not find channels");
+ }
+ handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
+ }
+ }
+ } else {
+ handleScanRequest(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP, message);
+ }
break;
/* Ignore connection to same network */
case WifiManager.CONNECT_NETWORK:
@@ -4804,8 +4960,13 @@ public class WifiStateMachine extends StateMachine {
case CMD_RSSI_POLL:
if (message.arg1 == mRssiPollToken) {
WifiLinkLayerStats stats = null;
- if (mWifiLinkLayerStatsSupported) {
+ //try a reading L2 stats a couple of time, allow for a few failures
+ //in case the HAL/drivers are not completely initialized once we get there
+ if (mWifiLinkLayerStatsSupported > 0) {
stats = mWifiNative.getWifiLinkLayerStats();
+ if (stats == null && mWifiLinkLayerStatsSupported > 0) {
+ mWifiLinkLayerStatsSupported -= 1;
+ }
}
// Get Info and continue polling
fetchRssiLinkSpeedAndFrequencyNative();
@@ -4855,7 +5016,11 @@ public class WifiStateMachine extends StateMachine {
if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
// TODO: If we're switching between static IP configuration and DHCP, remove the
// static configuration first.
- startDhcp();
+ if (mAutoRoaming != AutoRoaming.IDLE) {
+ renewDhcp();
+ } else {
+ startDhcp();
+ }
} else {
// stop any running dhcp before assigning static IP
stopDhcp();
@@ -4922,6 +5087,7 @@ public class WifiStateMachine extends StateMachine {
setNetworkDetailedState(DetailedState.VERIFYING_POOR_LINK);
mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.VERIFYING_POOR_LINK);
sendNetworkStateChangeBroadcast(mLastBssid);
+ mAutoRoaming = AutoRoaming.IDLE;
}
@Override
public boolean processMessage(Message message) {
@@ -4968,6 +5134,70 @@ public class WifiStateMachine extends StateMachine {
}
}
+ class RoamingState extends State {
+ @Override
+ public void enter() {
+ if (DBG) {
+ log("RoamingState Enter"
+ + " mScreenOn=" + mScreenOn );
+ }
+ setScanAlarm(false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ logStateAndMessage(message, getClass().getSimpleName());
+
+ switch (message.what) {
+ case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
+ if (DBG) log("Roaming and Watchdog reports poor link -> ignore");
+ return HANDLED;
+ case CMD_UNWANTED_NETWORK:
+ if (DBG) log("Roaming and CS doesnt want the network -> ignore");
+ return HANDLED;
+ case CMD_SET_OPERATIONAL_MODE:
+ if (message.arg1 != CONNECT_MODE) {
+ deferMessage(message);
+ }
+ break;
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
+ /* If we get a SUPPLICANT_STATE_CHANGE_EVENT before NETWORK_DISCONNECTION_EVENT
+ * we have missed the network disconnection, transition to mDisconnectedState
+ * and handle the rest of the events there
+ */
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+ setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
+ break;
+ case WifiMonitor.NETWORK_CONNECTION_EVENT:
+ if (DBG) log("roaming and Network connection established");
+ mLastNetworkId = message.arg1;
+ mLastBssid = (String) message.obj;
+
+ mWifiInfo.setBSSID(mLastBssid);
+ mWifiInfo.setNetworkId(mLastNetworkId);
+ /* send event to CM & network change broadcast */
+ setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+ transitionTo(mObtainingIpState);
+ break;
+ case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
+ //throw away but only if it correspond to the network we're roaming from , so, how to do that?
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ loge("WifiStateMachine: Leaving Roaming state");
+
+ /* Request a CS wakelock during transition to mobile */
+ //checkAndSetConnectivityInstance();
+ //mCm.requestNetworkTransitionWakelock(getName());
+ }
+ }
+
class ConnectedState extends State {
@Override
public void enter() {
@@ -4984,13 +5214,14 @@ public class WifiStateMachine extends StateMachine {
} else {
mCurrentScanAlarmMs = 0;
}
+ registerConnected();
}
@Override
public boolean processMessage(Message message) {
logStateAndMessage(message, getClass().getSimpleName());
switch (message.what) {
- case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
+ case WifiWatchdogStateMachine.POOR_LINK_DETECTED:
if (DBG) log("Watchdog reports poor link");
try {
mNwService.disableIpv6(mInterfaceName);
@@ -5014,6 +5245,64 @@ public class WifiStateMachine extends StateMachine {
//low wifi score threshold
sendMessage(CMD_DISCONNECT);
return HANDLED;
+ case CMD_AUTO_ROAM:
+ /* this will happen similarly to an Auto_CONNECT, except we specify the BSSID */
+ /* Work Around: wpa_supplicant can get in a bad state where it returns a non
+ * associated status thus the STATUS command but somehow-someplace still thinks
+ * it is associated and thus will ignore select/reconnect command with
+ * following message:
+ * "Already associated with the selected network - do nothing"
+ *
+ * Hence, sends a disconnect to supplicant first.
+ */
+ mWifiNative.disconnect();
+
+ /* connect command coming from auto-join */
+ String bssid = (String) message.obj;
+ int netId = mLastNetworkId;
+ int roam = message.arg2;
+ WifiConfiguration config = getCurrentWifiConfiguration();
+
+ loge("CMD_AUTO_ROAM sup state "
+ + mSupplicantStateTracker.getSupplicantStateName()
+ + " my state " + getCurrentState().getName()
+ + " nid=" + Integer.toString(netId)
+ + " roam=" + Integer.toString(roam)
+ + bssid);
+
+ /* save the BSSID so as to lock it @ firmware */
+ config.BSSID = bssid;
+ /* Save the network config */
+ if (config != null) {
+ loge("CMD_AUTO_ROAM will save config -> " + config.SSID
+ + " nid=" + Integer.toString(netId));
+ NetworkUpdateResult result = mWifiConfigStore.saveNetwork(config);
+ netId = result.getNetworkId();
+ loge("CMD_AUTO_ROAM did save config -> "
+ + " nid=" + Integer.toString(netId));
+ }
+
+ if (mWifiConfigStore.selectNetwork(netId) &&
+ mWifiNative.reconnect()) {
+ // we selected a better config, maybe because we could not see the last user
+ // selection, then forget it. We will remember the selection
+ // only if it was persisted.
+ // mWifiConfigStore.
+ // setLastSelectedConfiguration(WifiConfiguration.INVALID_NETWORK_ID);
+
+ /* The state tracker handles enabling networks upon completion/failure */
+ mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
+ //replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
+ mAutoRoaming = AutoRoaming.ROAMING;
+ transitionTo(mRoamingState);
+
+ } else {
+ loge("Failed to connect config: " + config + " netId: " + netId);
+ replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
+ WifiManager.ERROR);
+ break;
+ }
+ break;
default:
return NOT_HANDLED;
}
@@ -5028,6 +5317,8 @@ public class WifiStateMachine extends StateMachine {
/* Request a CS wakelock during transition to mobile */
checkAndSetConnectivityInstance();
mCm.requestNetworkTransitionWakelock(getName());
+ loge("WifiStateMachine: Left Connected state");
+
}
}
@@ -5107,11 +5398,20 @@ public class WifiStateMachine extends StateMachine {
}
if (PDBG) {
+ String roamstr = "";
+ if (mAutoRoaming==AutoRoaming.EXTENDED_ROAMING)
+ roamstr = "Extended-Roaming";
+ if (mAutoRoaming==AutoRoaming.ROAMING)
+ roamstr = "Roaming";
loge(" Enter disconnected State scan interval " + mFrameworkScanIntervalMs
+ " mEnableBackgroundScan= " + mEnableBackgroundScan
- + " screenOn=" + mScreenOn);
+ + " screenOn=" + mScreenOn
+ + roamstr);
}
+ /** clear the roaming state, if we were roaming, we failed */
+ mAutoRoaming = AutoRoaming.IDLE;
+
/*
* mFrameworkAutoJoin is False: We initiate background scanning if it is enabled,
* otherwise we initiate an infrequent scan that wakes up the device to ensure
diff --git a/service/java/com/android/server/wifi/WifiTrafficPoller.java b/service/java/com/android/server/wifi/WifiTrafficPoller.java
index 5da7ffa28..20e7221ad 100644
--- a/service/java/com/android/server/wifi/WifiTrafficPoller.java
+++ b/service/java/com/android/server/wifi/WifiTrafficPoller.java
@@ -104,15 +104,6 @@ final class WifiTrafficPoller {
Message.obtain(mTrafficHandler, REMOVE_CLIENT, client).sendToTarget();
}
- boolean shouldSwitchNetwork(int networkDelta) {
- if (networkDelta > 100)
- return true;
-
-
-
- return false;
- }
-
void enableVerboseLogging(int verbose) {
if (verbose > 0 ) {
DBG = true;