Bug 802418 - Turn on background scanning when we're disconnected to work around wpa_supplicant's sched_scan getting stuck. r=vchang
authorBlake Kaplan <mrbkap@gmail.com>
Thu, 25 Oct 2012 16:27:10 -0700
changeset 111860 17534a39a5a5d644945603c45ef9638f14be8c9e
parent 111859 471b0731ff8dcb5b3441fada7b21848690c0ab6d
child 111861 9809bdb2ad0f4ba9389d610551f8bbd61c915efb
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersvchang
bugs802418
milestone19.0a1
Bug 802418 - Turn on background scanning when we're disconnected to work around wpa_supplicant's sched_scan getting stuck. r=vchang
dom/wifi/WifiWorker.js
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -230,31 +230,57 @@ var WifiManager = (function() {
   function reconnectCommand(callback) {
     doBooleanCommand("RECONNECT", "OK", callback);
   }
 
   function reassociateCommand(callback) {
     doBooleanCommand("REASSOCIATE", "OK", callback);
   }
 
+  // A note about background scanning:
+  // Normally, background scanning shouldn't be necessary as wpa_supplicant
+  // has the capability to automatically schedule its own scans at appropriate
+  // intervals. However, with some drivers, this appears to get stuck after
+  // three scans, so we enable the driver's background scanning to work around
+  // that when we're not connected to any network. This ensures that we'll
+  // automatically reconnect to networks if one falls out of range.
+  var reEnableBackgroundScan = false;
+  var backgroundScanEnabled = false;
+  function setBackgroundScan(enable, callback) {
+    var doEnable = (enable === "ON");
+    if (doEnable === backgroundScanEnabled) {
+      callback(false, true);
+      return;
+    }
+
+    backgroundScanEnabled = doEnable;
+    doBooleanCommand("SET pno " + (backgroundScanEnabled ? "1" : "0"), "OK",
+                     function(ok) {
+                       callback(true, ok);
+                     });
+  }
+
   var scanModeActive = false;
 
   function doSetScanModeCommand(setActive, callback) {
     doBooleanCommand(setActive ? "DRIVER SCAN-ACTIVE" : "DRIVER SCAN-PASSIVE", "OK", callback);
   }
 
   function scanCommand(forceActive, callback) {
     if (forceActive && !scanModeActive) {
       // Note: we ignore errors from doSetScanMode.
       doSetScanModeCommand(true, function(ignore) {
-        doBooleanCommand("SCAN", "OK", function(ok) {
-          doSetScanModeCommand(false, function(ignore) {
-            // The result of scanCommand is the result of the actual SCAN
-            // request.
-            callback(ok);
+        setBackgroundScan("OFF", function(turned, ignore) {
+          reEnableBackgroundScan = turned;
+          doBooleanCommand("SCAN", "OK", function(ok) {
+            doSetScanModeCommand(false, function(ignore) {
+              // The result of scanCommand is the result of the actual SCAN
+              // request.
+              callback(ok);
+            });
           });
         });
       });
       return;
     }
     doBooleanCommand("SCAN", "OK", callback);
   }
 
@@ -570,16 +596,26 @@ var WifiManager = (function() {
     // do anything, so just ignore them.
     if (manager.state === "COMPLETED" &&
         fields.state !== "DISCONNECTED" &&
         fields.state !== "INTERFACE_DISABLED" &&
         fields.state !== "INACTIVE" &&
         fields.state !== "SCANNING") {
       return false;
     }
+
+    // Stop background scanning if we're trying to connect to a network.
+    if (backgroundScanEnabled &&
+        (fields.state === "ASSOCIATING" ||
+         fields.state === "ASSOCIATED" ||
+         fields.state === "FOUR_WAY_HANDSHAKE" ||
+         fields.state === "GROUP_HANDSHAKE" ||
+         fields.state === "COMPLETED")) {
+      setBackgroundScan("OFF", function() {});
+    }
     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;
   }
@@ -623,18 +659,34 @@ var WifiManager = (function() {
       manager.connectionInfo.ssid = ssid;
       manager.connectionInfo.id = id;
     }
 
     if (ip_address)
       dhcpInfo = { ip_address: ip_address };
 
     notifyStateChange({ state: state, fromStatus: true });
-    if (state === "COMPLETED")
-      onconnected();
+
+    // If we parse the status and the supplicant has already entered the
+    // COMPLETED state, then we need to set up DHCP right away. Otherwise, if
+    // we're not actively connecting to a network, we need to turn on
+    // background scanning.
+    switch (state) {
+      case "COMPLETED":
+        onconnected();
+        break;
+
+      case "DISCONNECTED":
+      case "INACTIVE":
+      case "SCANNING":
+        setBackgroundScan("ON", function(){});
+
+      default:
+        break;
+    }
   }
 
   // try to connect to the supplicant
   var connectTries = 0;
   var retryTimer = null;
   function connectCallback(ok) {
     if (ok === 0) {
       // Tell the event worker to start waiting for events.
@@ -806,16 +858,20 @@ var WifiManager = (function() {
       // Don't call onconnected if we ignored this state change (since we were
       // already connected).
       if (notifyStateChange({ state: "CONNECTED", BSSID: bssid, id: id }))
         onconnected();
       return true;
     }
     if (eventData.indexOf("CTRL-EVENT-SCAN-RESULTS") === 0) {
       debug("Notifying of scan results available");
+      if (reEnableBackgroundScan) {
+        reEnableBackgroundScan = false;
+        setBackgroundScan("ON", function() {});
+      }
       notify("scanresultsavailable");
       return true;
     }
     if (eventData.indexOf("WPS-TIMEOUT") === 0) {
       notifyStateChange({ state: "WPS_TIMEOUT", BSSID: null, id: -1 });
       return true;
     }
     if (eventData.indexOf("WPS-FAIL") === 0) {
@@ -1156,16 +1212,17 @@ var WifiManager = (function() {
   manager.disableNetwork = function(netId, callback) {
     disableNetworkCommand(netId, callback);
   }
   manager.getMacAddress = getMacAddressCommand;
   manager.getScanResults = scanResultsCommand;
   manager.setScanMode = function(mode, callback) {
     setScanModeCommand(mode === "active", callback);
   }
+  manager.setBackgroundScan = setBackgroundScan;
   manager.scan = scanCommand;
   manager.wpsPbc = wpsPbcCommand;
   manager.wpsPin = wpsPinCommand;
   manager.wpsCancel = wpsCancelCommand;
   manager.setPowerMode = setPowerModeCommand;
   manager.setSuspendOptimizations = setSuspendOptimizationsCommand;
   manager.getRssiApprox = getRssiApproxCommand;
   manager.getLinkSpeed = getLinkSpeedCommand;
@@ -1640,16 +1697,18 @@ function WifiWorker() {
           // We've disconnected from a network because of a call to forgetNetwork.
           // Reconnect to the next available network (if any).
           if (self._reconnectOnDisconnect) {
             self._reconnectOnDisconnect = false;
             WifiManager.reconnect(function(){});
           }
         });
 
+        WifiManager.setBackgroundScan("ON", function(){});
+
         WifiNetworkInterface.state =
           Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED;
         WifiNetworkInterface.ip = null;
         WifiNetworkInterface.netmask = null;
         WifiNetworkInterface.broadcast = null;
         WifiNetworkInterface.gateway = null;
         WifiNetworkInterface.dns1 = null;
         WifiNetworkInterface.dns2 = null;