Merge for bug 899052
authorEddy Bruel <ejpbruel@mozilla.com>
Thu, 22 Aug 2013 19:55:40 +0200
changeset 143888 31ef01e1bb6145516e95ede1e67f1c0454877b06
parent 143887 fc9ca12061eac2290a7f989269602796d8eb0acd (current diff)
parent 143886 89294cd501d9a6e6767a5fe6abe088f19e93f477 (diff)
child 143889 ee85a2f2ecb15c5ca7ef9dda3e921d7068de75ba
push id32801
push userejpbruel@mozilla.com
push dateThu, 22 Aug 2013 17:49:38 +0000
treeherdermozilla-inbound@31ef01e1bb61 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs899052
milestone26.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 for bug 899052
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "e4f586d6a2c7a8ff3a676d2ddfecc5b2de94f429", 
+    "revision": "5de9f5b9b2f843825f13bd3667dc0fbf2e04e5f3", 
     "repo_path": "/integration/gaia-central"
 }
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -5,16 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/systemlibs.js");
 
 var DEBUG = false; // set to true to show debug messages.
 
 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";
 
@@ -62,16 +63,25 @@ const WIFI_FIRMWARE_AP            = "AP"
 const WIFI_FIRMWARE_STATION       = "STA";
 const WIFI_SECURITY_TYPE_NONE     = "open";
 const WIFI_SECURITY_TYPE_WPA_PSK  = "wpa-psk";
 const WIFI_SECURITY_TYPE_WPA2_PSK = "wpa2-psk";
 
 const NETWORK_INTERFACE_UP   = "up";
 const NETWORK_INTERFACE_DOWN = "down";
 
+const DEFAULT_WLAN_INTERFACE = "wlan0";
+
+const DRIVER_READY_WAIT = 2000;
+
+const SUPP_PROP = "init.svc.wpa_supplicant";
+const WPA_SUPPLICANT = "wpa_supplicant";
+const DHCP_PROP = "init.svc.dhcpcd";
+const DHCP = "dhcpcd";
+
 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
                                    "@mozilla.org/network/manager;1",
                                    "nsINetworkManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
                                    "@mozilla.org/settingsService;1",
                                    "nsISettingsService");
 
@@ -79,31 +89,39 @@ XPCOMUtils.defineLazyServiceGetter(this,
 // 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
 // convention (note: command functions do get boolean results since the
 // command always succeeds and we do a string/boolean check for the
 // expected results).
 var WifiManager = (function() {
   function getStartupPrefs() {
-    Cu.import("resource://gre/modules/systemlibs.js");
     return {
       sdkVersion: parseInt(libcutils.property_get("ro.build.version.sdk"), 10),
       unloadDriverEnabled: libcutils.property_get("ro.moz.wifi.unloaddriver") === "1",
-      schedScanRecovery: libcutils.property_get("ro.moz.wifi.sched_scan_recover") === "false" ? false : true
+      schedScanRecovery: libcutils.property_get("ro.moz.wifi.sched_scan_recover") === "false" ? false : true,
+      driverDelay: libcutils.property_get("ro.moz.wifi.driverDelay"),
+      ifname: libcutils.property_get("wifi.interface")
     };
   }
 
-  let {sdkVersion, unloadDriverEnabled, schedScanRecovery} = getStartupPrefs();
+  let {sdkVersion, unloadDriverEnabled, schedScanRecovery, driverDelay, ifname} = getStartupPrefs();
 
   var controlWorker = new ChromeWorker(WIFIWORKER_WORKER);
   var eventWorker = new ChromeWorker(WIFIWORKER_WORKER);
 
   var manager = {};
+  manager.ifname = ifname;
+  // Emulator build runs to here.
+  // The debug() should only be used after WifiManager.
+  if (!ifname) {
+    manager.ifname = DEFAULT_WLAN_INTERFACE;
+  }
   manager.schedScanRecovery = schedScanRecovery;
+  manager.driverDelay = driverDelay ? parseInt(driverDelay, 10) : DRIVER_READY_WAIT;
 
   // Callbacks to invoke when a reply arrives from the controlWorker.
   var controlCallbacks = Object.create(null);
   var idgen = 0;
 
   function controlMessage(obj, callback) {
     var id = idgen++;
     obj.id = id;
@@ -706,22 +724,48 @@ var WifiManager = (function() {
                        staticIpInfo.maskLength,
                        staticIpInfo.gateway,
                        staticIpInfo.dns1,
                        staticIpInfo.dns2, function (data) {
       runIpConfig(ifname, staticIpInfo);
     });
   }
 
+  function stopProcess(service, process, callback) {
+    var count = 0;
+    var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    function tick() {
+      let result = libcutils.property_get(service);
+      if (result === null) {
+        callback();
+        return;
+      }
+      if (result === "stopped" || ++count >= 5) {
+        // Either we succeeded or ran out of time.
+        timer = null;
+        callback();
+        return;
+      }
+
+      // Else it's still running, continue waiting.
+      timer.initWithCallback(tick, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
+    }
+
+    setProperty("ctl.stop", process, tick);
+  }
+
   function stopDhcp(ifname, callback) {
-    controlMessage({ cmd: "dhcp_stop", ifname: ifname }, function(data) {
-      dhcpInfo = null;
-      notify("dhcplost");
-      callback(!data.status);
-    });
+    // This function does exactly what dhcp_stop does. Unforunately, if we call
+    // this function twice before the previous callback is returned. We may block
+    // our self waiting for the callback. It slows down the wifi startup procedure.
+    // Therefore, we have to roll our own version here.
+    let dhcpService = DHCP_PROP + "_" + ifname;
+    let suffix = (ifname.substr(0, 3) === "p2p") ? "p2p" : ifname;
+    let processName = DHCP + "_" + suffix;
+    stopProcess(dhcpService, processName, callback);
   }
 
   function releaseDhcpLease(ifname, callback) {
     controlMessage({ cmd: "dhcp_release_lease", ifname: ifname }, function(data) {
       dhcpInfo = null;
       notify("dhcplost");
       callback(!data.status);
     });
@@ -1109,44 +1153,23 @@ var WifiManager = (function() {
     if (eventData.indexOf("WPS-OVERLAP-DETECTED") === 0) {
       notifyStateChange({ state: "WPS_OVERLAP_DETECTED", BSSID: null, id: -1 });
       return true;
     }
     // Unknown event.
     return true;
   }
 
-  const SUPP_PROP = "init.svc.wpa_supplicant";
   function killSupplicant(callback) {
     // It is interesting to note that this function does exactly what
     // wifi_stop_supplicant does. Unforunately, on the Galaxy S2, Samsung
     // changed that function in a way that means that it doesn't recognize
     // wpa_supplicant as already running. Therefore, we have to roll our own
     // version here.
-    var count = 0;
-    var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    function tick() {
-      getProperty(SUPP_PROP, "stopped", function (result) {
-        if (result === null) {
-          callback();
-          return;
-        }
-        if (result === "stopped" || ++count >= 5) {
-          // Either we succeeded or ran out of time.
-          timer = null;
-          callback();
-          return;
-        }
-
-        // Else it's still running, continue waiting.
-        timer.initWithCallback(tick, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
-      });
-    }
-
-    setProperty("ctl.stop", "wpa_supplicant", tick);
+    stopProcess(SUPP_PROP, WPA_SUPPLICANT, callback);
   }
 
   function didConnectSupplicant(callback) {
     waitForEvent();
 
     // Load up the supplicant state.
     getDebugEnabled(function(ok) {
       syncDebug();
@@ -1154,128 +1177,134 @@ var WifiManager = (function() {
     statusCommand(function(status) {
       parseStatus(status);
       notify("supplicantconnection");
       callback();
     });
   }
 
   function prepareForStartup(callback) {
+    let status = libcutils.property_get(DHCP_PROP + "_" + manager.ifname);
+    if (status !== "running") {
+      tryStopSupplicant();
+      return;
+    }
     manager.connectionDropped(function() {
-      // Ignore any errors and kill any currently-running supplicants. On some
-      // phones, stopSupplicant won't work for a supplicant that we didn't
-      // start, so we hand-roll it here.
+      tryStopSupplicant();
+    });
+
+    // Ignore any errors and kill any currently-running supplicants. On some
+    // phones, stopSupplicant won't work for a supplicant that we didn't
+    // start, so we hand-roll it here.
+    function tryStopSupplicant () {
+      let status = libcutils.property_get(SUPP_PROP);
+      if (status !== "running") {
+        callback();
+        return;
+      }
       suppressEvents = true;
       killSupplicant(function() {
         disableInterface(manager.ifname, function (ok) {
           suppressEvents = false;
           callback();
         });
       });
-    });
+    }
   }
 
   // Initial state.
   manager.state = "UNINITIALIZED";
   manager.tetheringState = "UNINITIALIZED";
   manager.enabled = false;
   manager.supplicantStarted = false;
   manager.connectionInfo = { ssid: null, bssid: null, id: -1 };
   manager.authenticationFailuresCount = 0;
   manager.loopDetectionCount = 0;
 
-  const DRIVER_READY_WAIT = 2000;
   var waitForDriverReadyTimer = null;
   function cancelWaitForDriverReadyTimer() {
     if (waitForDriverReadyTimer) {
       waitForDriverReadyTimer.cancel();
       waitForDriverReadyTimer = null;
     }
   };
   function createWaitForDriverReadyTimer(onTimeout) {
     waitForDriverReadyTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
     waitForDriverReadyTimer.initWithCallback(onTimeout,
-                                             DRIVER_READY_WAIT,
+                                             manager.driverDelay,
                                              Ci.nsITimer.TYPE_ONE_SHOT);
   };
 
   // Public interface of the wifi service.
   manager.setWifiEnabled = function(enable, callback) {
     if (enable === manager.enabled) {
       callback("no change");
       return;
     }
 
     if (enable) {
       manager.state = "INITIALIZING";
-      // Kill any existing connections if necessary.
-      getProperty("wifi.interface", "tiwlan0", function (ifname) {
-        if (!ifname) {
-          callback(-1);
-          manager.state = "UNINITIALIZED";
-          return;
-        }
-        manager.ifname = ifname;
-
-        // Register as network interface.
-        WifiNetworkInterface.name = ifname;
-        if (!WifiNetworkInterface.registered) {
-          gNetworkManager.registerNetworkInterface(WifiNetworkInterface);
-          WifiNetworkInterface.registered = true;
-        }
-        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;
-        Services.obs.notifyObservers(WifiNetworkInterface,
-                                     kNetworkInterfaceStateChangedTopic,
-                                     null);
-
-        prepareForStartup(function() {
-          loadDriver(function (status) {
+      // Register as network interface.
+      WifiNetworkInterface.name = manager.ifname;
+      if (!WifiNetworkInterface.registered) {
+        gNetworkManager.registerNetworkInterface(WifiNetworkInterface);
+        WifiNetworkInterface.registered = true;
+      }
+      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;
+      Services.obs.notifyObservers(WifiNetworkInterface,
+                                   kNetworkInterfaceStateChangedTopic,
+                                   null);
+      prepareForStartup(function() {
+        loadDriver(function (status) {
+          if (status < 0) {
+            callback(status);
+            manager.state = "UNINITIALIZED";
+            return;
+          }
+          gNetworkManager.setWifiOperationMode(manager.ifname,
+                                               WIFI_FIRMWARE_STATION,
+                                               function (status) {
             if (status) {
               callback(status);
               manager.state = "UNINITIALIZED";
               return;
             }
-            gNetworkManager.setWifiOperationMode(ifname,
-                                                 WIFI_FIRMWARE_STATION,
-                                                 function (status) {
-              if (status < 0) {
-                callback(status);
-                manager.state = "UNINITIALIZED";
-                return;
-              }
-
-              function doStartSupplicant() {
-                cancelWaitForDriverReadyTimer();
-                startSupplicant(function (status) {
-                  if (status < 0) {
-                    unloadDriver(function() {
-                      callback(status);
-                    });
-                    manager.state = "UNINITIALIZED";
-                    return;
-                  }
-
-                  manager.supplicantStarted = true;
-                  enableInterface(ifname, function (ok) {
-                    callback(ok ? 0 : -1);
+
+            function doStartSupplicant() {
+              cancelWaitForDriverReadyTimer();
+              startSupplicant(function (status) {
+                if (status < 0) {
+                  unloadDriver(function() {
+                    callback(status);
+
                   });
+                  manager.state = "UNINITIALIZED";
+                  return;
+                }
+
+                manager.supplicantStarted = true;
+                enableInterface(manager.ifname, function (ok) {
+                  callback(ok ? 0 : -1);
                 });
-              }
-
-              // Driver startup on certain platforms takes longer than it takes for us
-              // to return from loadDriver, so wait 2 seconds before starting
-              // the supplicant to give it a chance to start.
+              });
+            }
+            // Driver startup on certain platforms takes longer than it takes for us
+            // to return from loadDriver, so wait 2 seconds before starting
+            // the supplicant to give it a chance to start.
+            if (manager.driverDelay > 0) {
               createWaitForDriverReadyTimer(doStartSupplicant);
-            });
+            } else {
+              doStartSupplicant();
+            }
           });
         });
       });
     } else {
       // Note these following calls ignore errors. If we fail to kill the
       // supplicant gracefully, then we need to continue telling it to die
       // until it does.
       terminateSupplicant(function (ok) {
@@ -1292,52 +1321,44 @@ var WifiManager = (function() {
       });
     }
   }
 
   // Get wifi interface and load wifi driver when enable Ap mode.
   manager.setWifiApEnabled = function(enabled, configuration, callback) {
     if (enabled) {
       manager.tetheringState = "INITIALIZING";
-      getProperty("wifi.interface", "tiwlan0", function (ifname) {
-        if (!ifname) {
+      loadDriver(function (status) {
+        if (status < 0) {
           callback();
           manager.tetheringState = "UNINITIALIZED";
           return;
         }
-        manager.ifname = ifname;
-        loadDriver(function (status) {
-          if (status < 0) {
+
+        function doStartWifiTethering() {
+          cancelWaitForDriverReadyTimer();
+          WifiNetworkInterface.name = manager.ifname;
+          gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface,
+                                           configuration, function(result) {
+            if (result) {
+              manager.tetheringState = "UNINITIALIZED";
+            } else {
+              manager.tetheringState = "COMPLETED";
+            }
+            // Pop out current request.
             callback();
-            manager.tetheringState = "UNINITIALIZED";
-            return;
-          }
-
-          function doStartWifiTethering() {
-            cancelWaitForDriverReadyTimer();
-            WifiNetworkInterface.name = manager.ifname;
-            gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface,
-                                             configuration, function(result) {
-              if (result) {
-                manager.tetheringState = "UNINITIALIZED";
-              } else {
-                manager.tetheringState = "COMPLETED";
-              }
-              // Pop out current request.
-              callback();
-              // Should we fire a dom event if we fail to set wifi tethering  ?
-              debug("Enable Wifi tethering result: " + (result ? result : "successfully"));
-            });
-          }
-
-          // Driver startup on certain platforms takes longer than it takes
-          // for us to return from loadDriver, so wait 2 seconds before
-          // turning on Wifi tethering.
-          createWaitForDriverReadyTimer(doStartWifiTethering);
-        });
+            // Should we fire a dom event if we fail to set wifi tethering  ?
+            debug("Enable Wifi tethering result: " + (result ? result : "successfully"));
+          });
+        }
+
+        // Driver startup on certain platforms takes longer than it takes
+        // for us to return from loadDriver, so wait 2 seconds before
+        // turning on Wifi tethering.
+        createWaitForDriverReadyTimer(doStartWifiTethering);
       });
     } else {
       gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface,
                                        configuration, function(result) {
         // Should we fire a dom event if we fail to set wifi tethering  ?
         debug("Disable Wifi tethering result: " + (result ? result : "successfully"));
         // Unload wifi driver even if we fail to control wifi tethering.
         unloadDriver(function(status) {
@@ -2155,16 +2176,26 @@ function WifiWorker() {
         self._startConnectionInfoTimer();
         self._fireEvent("onassociate", { network: netToDOM(self.currentNetwork) });
         break;
       case "CONNECTED":
         // BSSID is read after connected, update it.
         self.currentNetwork.bssid = WifiManager.connectionInfo.bssid;
         break;
       case "DISCONNECTED":
+        // wpa_supplicant may give us a "DISCONNECTED" event even if
+        // we are already in "DISCONNECTED" state.
+        if (this.prevState === "INITIALIZING" ||
+          this.prevState === "DISCONNECTED" ||
+          this.prevState === "INTERFACE_DISABLED" ||
+          this.prevState === "INACTIVE" ||
+          this.prevState === "UNINITIALIZED") {
+          return;
+        }
+
         self._fireEvent("ondisconnect", {});
         self.currentNetwork = null;
         self.ipAddress = "";
 
         if (self._turnOnBackgroundScan) {
           self._turnOnBackgroundScan = false;
           WifiManager.setBackgroundScan("ON", function(did_something, ok) {
             WifiManager.reassociate(function() {});