Merge m-c to inbound. DONTBUILD
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 28 Aug 2012 16:02:29 -0400
changeset 103700 1f93692fea1ebc066e7df9119da5d8f5d16d3565
parent 103695 d63bb4e02b3dfd4e864ea18a5ba38699ed3d4a0b (current diff)
parent 103699 61faabceedd4d18ac34178fca4f5e4fd16b4f26e (diff)
child 103701 020c834a8991eb38904817fd64e3089f03d236a4
push id14120
push userryanvm@gmail.com
push dateTue, 28 Aug 2012 20:02:35 +0000
treeherdermozilla-inbound@1f93692fea1e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound. DONTBUILD
dom/wifi/WifiWorker.js
--- a/dom/wifi/DOMWifiManager.js
+++ b/dom/wifi/DOMWifiManager.js
@@ -78,17 +78,18 @@ DOMWifiManager.prototype = {
                       "WifiManager:getNetworks:Return:OK", "WifiManager:getNetworks:Return:NO",
                       "WifiManager:associate:Return:OK", "WifiManager:associate:Return:NO",
                       "WifiManager:forget:Return:OK", "WifiManager:forget:Return:NO",
                       "WifiManager:wps:Return:OK", "WifiManager:wps:Return:NO",
                       "WifiManager:wifiDown", "WifiManager:wifiUp",
                       "WifiManager:onconnecting", "WifiManager:onassociate",
                       "WifiManager:onconnect", "WifiManager:ondisconnect",
                       "WifiManager:onwpstimeout", "WifiManager:onwpsfail",
-                      "WifiManager:onwpsoverlap", "WifiManager:connectionInfoUpdate"];
+                      "WifiManager:onwpsoverlap", "WifiManager:connectionInfoUpdate",
+                      "WifiManager:onconnectingfailed"];
     this.initHelper(aWindow, messages);
     this._mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
 
     var state = this._mm.sendSyncMessage("WifiManager:getState")[0];
     if (state) {
       this._currentNetwork = state.network;
       if (this._currentNetwork)
         exposeCurrentNetwork(this._currentNetwork);
@@ -236,16 +237,22 @@ DOMWifiManager.prototype = {
         this._lastConnectionInfo = null;
         this._fireStatusChangeEvent();
         break;
 
       case "WifiManager:connectionInfoUpdate":
         this._lastConnectionInfo = msg;
         this._fireConnectionInfoUpdate(msg);
         break;
+      case "WifiManager:onconnectingfailed":
+        this._currentNetwork = null;
+        this._connectionStatus = "connectingfailed";
+        this._lastConnectionInfo = null;
+        this._fireStatusChangeEvent();
+        break;
     }
   },
 
   _fireStatusChangeEvent: function StatusChangeEvent() {
     if (this._onStatusChange) {
       debug("StatusChangeEvent");
       var event = new this._window.MozWifiStatusChangeEvent("statusChangeEvent",
                                                             { network: this._currentNetwork,
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -15,16 +15,19 @@ const DEBUG = false; // set to true to s
 
 const WIFIWORKER_CONTRACTID = "@mozilla.org/wifi/worker;1";
 const WIFIWORKER_CID        = Components.ID("{a14e8977-d259-433a-a88d-58dd44657e5b}");
 
 const WIFIWORKER_WORKER     = "resource://gre/modules/wifi_worker.js";
 
 const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
 
+const MAX_RETRIES_ON_AUTHENTICATION_FAILURE = 2;
+const MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
+
 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
                                    "@mozilla.org/network/manager;1",
                                    "nsINetworkManager");
 
 // A note about errors and error handling in this file:
 // The libraries that we use in this file are intended for C code. For
 // C code, it is natural to return -1 for errors and 0 for success.
 // Therefore, the code that interacts directly with the worker uses this
@@ -48,17 +51,17 @@ var WifiManager = (function() {
         }
         c_property_get(key, cbuf, defaultValue);
         return cbuf.readString();
       }
       return { sdkVersion: parseInt(property_get("ro.build.version.sdk")),
                device: property_get("ro.product.device") };
     } catch(e) {
       // Eat it.  Hopefully we're on a non-Gonk system ...
-      // 
+      //
       // XXX we should check that
       return 0;
     }
   }
 
   let { sdkVersion, device } = getSdkVersionAndDevice();
 
   var controlWorker = new ChromeWorker(WIFIWORKER_WORKER);
@@ -584,16 +587,18 @@ var WifiManager = (function() {
         fields.state !== "INTERFACE_DISABLED" &&
         fields.state !== "INACTIVE" &&
         fields.state !== "SCANNING") {
       return false;
     }
     fields.prevState = manager.state;
     manager.state = fields.state;
 
+    // Detect wpa_supplicant's loop iterations.
+    manager.supplicantLoopDetection(fields.prevState, fields.state);
     notify("statechange", fields);
     return true;
   }
 
   function parseStatus(status, reconnected) {
     if (status === null) {
       debug("Unable to get wpa supplicant's status");
       return;
@@ -822,19 +827,25 @@ var WifiManager = (function() {
       if (eventData.indexOf("recv error") !== -1 && ++recvErrors < 10)
         return true;
 
       notifyStateChange({ state: "DISCONNECTED", BSSID: null, id: -1 });
       notify("supplicantlost");
       return false;
     }
     if (eventData.indexOf("CTRL-EVENT-DISCONNECTED") === 0) {
+      var token = event.split(" ")[1];
+      var bssid = token.split("=")[1];
       manager.connectionInfo.bssid = null;
       manager.connectionInfo.ssid = null;
       manager.connectionInfo.id = -1;
+      if (manager.authenticationFailuresCount > MAX_RETRIES_ON_AUTHENTICATION_FAILURE) {
+        notify("disconnected", {BSSID: bssid});
+        manager.authenticationFailuresCount = 0;
+      }
       return true;
     }
     if (eventData.indexOf("CTRL-EVENT-CONNECTED") === 0) {
       // Format: CTRL-EVENT-CONNECTED - Connection to 00:1e:58:ec:d5:6d completed (reauth) [id=1 id_str=]
       var bssid = eventData.split(" ")[4];
       var id = eventData.substr(eventData.indexOf("id=")).split(" ")[0];
 
       // Don't call onconnected if we ignored this state change (since we were
@@ -942,16 +953,18 @@ var WifiManager = (function() {
     });
   }
 
   // Initial state
   manager.state = "UNINITIALIZED";
   manager.enabled = false;
   manager.supplicantStarted = false;
   manager.connectionInfo = { ssid: null, bssid: null, id: -1 };
+  manager.authenticationFailuresCount = 0;
+  manager.loopDetectionCount = 0;
 
   // Public interface of the wifi service
   manager.setWifiEnabled = function(enable, callback) {
     if (enable === manager.enabled) {
       callback("no change");
       return;
     }
 
@@ -1184,16 +1197,64 @@ var WifiManager = (function() {
   manager.wpsPin = wpsPinCommand;
   manager.wpsCancel = wpsCancelCommand;
   manager.getRssiApprox = getRssiApproxCommand;
   manager.getLinkSpeed = getLinkSpeedCommand;
   manager.getDhcpInfo = function() { return dhcpInfo; }
   manager.getConnectionInfo = (sdkVersion >= 15)
                               ? getConnectionInfoICS
                               : getConnectionInfoGB;
+
+  manager.isHandShakeState = function(state) {
+    switch (state) {
+      case "AUTHENTICATING":
+      case "ASSOCIATING":
+      case "ASSOCIATED":
+      case "FOUR_WAY_HANDSHAKE":
+      case "GROUP_HANDSHAKE":
+        return true;
+      case "DORMANT":
+      case "COMPLETED":
+      case "DISCONNECTED":
+      case "INTERFACE_DISABLED":
+      case "INACTIVE":
+      case "SCANNING":
+      case "UNINITIALIZED":
+      case "INVALID":
+      case "CONNECTED":
+      default:
+        return false;
+    }
+  }
+  manager.stateOrdinal = function(state) {
+    return supplicantStatesMap.indexOf(state);
+  }
+  manager.supplicantLoopDetection = function(prevState, state) {
+    var isPrevStateInHandShake = manager.isHandShakeState(prevState);
+    var isStateInHandShake = manager.isHandShakeState(state);
+
+    if (isPrevStateInHandShake) {
+      if (isStateInHandShake) {
+        // Increase the count only if we are in the loop.
+        if (manager.stateOrdinal(state) > manager.stateOrdinal(prevState)) {
+          manager.loopDetectionCount++;
+        }
+        if (manager.loopDetectionCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
+          notify("disconnected");
+          manager.loopDetectionCount = 0;
+        }
+      }
+    } else {
+      // From others state to HandShake state. Reset the count.
+      if (isStateInHandShake) {
+        manager.loopDetectionCount = 0;
+      }
+    }
+  }
+
   return manager;
 })();
 
 function getKeyManagement(flags) {
   var types = [];
   if (!flags)
     return types;
 
@@ -1463,16 +1524,27 @@ function WifiWorker() {
     WifiManager.enabled = WifiManager.supplicantStarted = false;
     WifiManager.state = "UNINITIALIZED";
     debug("Couldn't connect to supplicant");
 
     if (self._stateRequests.length > 0)
       self._notifyAfterStateChange(false, false);
   }
 
+  WifiManager.onpasswordmaybeincorrect = function() {
+    WifiManager.authenticationFailuresCount++;
+  }
+  WifiManager.ondisconnected = function() {
+    var currentNetwork = self.currentNetwork;
+    if (currentNetwork) {
+      WifiManager.disableNetwork(currentNetwork.netId, function() {});
+      self._fireEvent("onconnectingfailed", {network: currentNetwork});
+    }
+  }
+
   WifiManager.onstatechange = function() {
     debug("State change: " + this.prevState + " -> " + this.state);
 
     if (self._connectionInfoTimer &&
         this.state !== "CONNECTED" &&
         this.state !== "COMPLETED") {
       self._stopConnectionInfoTimer();
     }
@@ -1521,16 +1593,19 @@ function WifiWorker() {
           // In this case, we connected to an already-connected wpa_supplicant,
           // because of that we need to gather information about the current
           // network here.
           self.currentNetwork = { ssid: quote(WifiManager.connectionInfo.ssid),
                                   netId: WifiManager.connectionInfo.id };
           WifiManager.getNetworkConfiguration(self.currentNetwork, function(){});
         }
 
+        // The full authentication process is completed, reset the count.
+        WifiManager.authenticationFailuresCount = 0;
+        WifiManager.loopDetectionCount = 0;
         self._startConnectionInfoTimer();
         self._fireEvent("onassociate", { network: netToDOM(self.currentNetwork) });
         break;
       case "CONNECTED":
         break;
       case "DISCONNECTED":
         self._fireEvent("ondisconnect", {});
         self.currentNetwork = null;
@@ -1580,17 +1655,17 @@ function WifiWorker() {
       WifiNetworkInterface.dns1 = this.info.dns1_str;
       WifiNetworkInterface.dns2 = this.info.dns2_str;
       Services.obs.notifyObservers(WifiNetworkInterface,
                                    kNetworkInterfaceStateChangedTopic,
                                    null);
 
       self._fireEvent("onconnect", { network: netToDOM(self.currentNetwork) });
     } else {
-      WifiManager.disconnect(function(){});
+      WifiManager.reassociate(function(){});
     }
   };
 
   WifiManager.onscanresultsavailable = function() {
     if (self.wantScanResults.length === 0) {
       debug("Scan results available, but we don't need them");
       return;
     }
@@ -1945,20 +2020,33 @@ WifiWorker.prototype = {
 
   getNetworks: function(msg) {
     const message = "WifiManager:getNetworks:Return";
     if (!WifiManager.enabled) {
       this._sendMessage(message, false, "Wifi is disabled", msg);
       return;
     }
 
-    this.waitForScan((function (networks) {
+    let callback = (function (networks) {
       this._sendMessage(message, networks !== null, networks, msg);
+    }).bind(this);
+    this.waitForScan(callback);
+
+    WifiManager.scan(true, (function(ok) {
+      // If the scan command succeeded, we're done.
+      if (ok)
+        return;
+
+      // Avoid sending multiple responses.
+      this.wantScanResults.splice(this.wantScanResults.indexOf(callback), 1);
+
+      // Otherwise, let the client know that it failed, it's responsible for
+      // trying again in a few seconds.
+      this._sendMessage(message, false, "ScanFailed", msg);
     }).bind(this));
-    WifiManager.scan(true, function() {});
   },
 
   _notifyAfterStateChange: function(success, newState) {
     // First, notify all of the requests that were trying to make this change.
     let state = this._stateRequests[0].enabled;
 
     // If the new state is not the same as state, then we weren't processing
     // the first request (we were racing somehow) so don't notify.