Bug 989717 - Handle enabling and disabling state properly. r=vchang
authorChuck Lee <chulee@mozilla.com>
Tue, 15 Apr 2014 11:43:51 +0800
changeset 179214 092bbc509ddcc9278bda088ec5a0aed459620155
parent 179213 82047628d463ff1a7fd8ce782e69742f9f46db70
child 179215 565eed5660a6f6323b231bd721026a7f9d5c8808
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersvchang
bugs989717
milestone31.0a1
Bug 989717 - Handle enabling and disabling state properly. r=vchang
dom/wifi/WifiWorker.js
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -885,23 +885,23 @@ var WifiManager = (function() {
   function createWaitForDriverReadyTimer(onTimeout) {
     waitForDriverReadyTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
     waitForDriverReadyTimer.initWithCallback(onTimeout,
                                              manager.driverDelay,
                                              Ci.nsITimer.TYPE_ONE_SHOT);
   };
 
   // Public interface of the wifi service.
-  manager.setWifiEnabled = function(enable, callback) {
-    if (enable === manager.enabled) {
+  manager.setWifiEnabled = function(enabled, callback) {
+    if (enabled === manager.isWifiEnabled(manager.state)) {
       callback("no change");
       return;
     }
 
-    if (enable) {
+    if (enabled) {
       manager.state = "INITIALIZING";
       // Register as network interface.
       WifiNetworkInterface.name = manager.ifname;
       if (!WifiNetworkInterface.registered) {
         gNetworkManager.registerNetworkInterface(WifiNetworkInterface);
         WifiNetworkInterface.registered = true;
       }
       WifiNetworkInterface.state = Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED;
@@ -947,16 +947,20 @@ var WifiManager = (function() {
               cancelWaitForDriverReadyTimer();
 
               if (!manager.connectToSupplicant) {
                 startSupplicantInternal();
                 return;
               }
               wifiCommand.closeSupplicantConnection(function () {
                 manager.connectToSupplicant = false;
+                // closeSupplicantConnection() will trigger onsupplicantlost
+                // and set manager.state to "UNINITIALIZED", we have to
+                // restore it here.
+                manager.state = "INITIALIZING";
                 startSupplicantInternal();
               });
             }
             // 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);
@@ -989,16 +993,21 @@ var WifiManager = (function() {
       } else {
         doDisableWifi();
       }
     }
   }
 
   // Get wifi interface and load wifi driver when enable Ap mode.
   manager.setWifiApEnabled = function(enabled, configuration, callback) {
+    if (enabled === manager.isWifiTetheringEnabled(manager.tetheringState)) {
+      callback("no change");
+      return;
+    }
+
     if (enabled) {
       manager.tetheringState = "INITIALIZING";
       loadDriver(function (status) {
         if (status < 0) {
           callback();
           manager.tetheringState = "UNINITIALIZED";
           return;
         }
@@ -1253,16 +1262,36 @@ var WifiManager = (function() {
       case "SCANNING":
       case "UNINITIALIZED":
       case "INVALID":
       case "CONNECTED":
       default:
         return false;
     }
   }
+
+  manager.isWifiEnabled = function(state) {
+    switch (state) {
+      case "UNINITIALIZED":
+      case "DISABLING":
+        return false;
+      default:
+        return true;
+    }
+  }
+
+  manager.isWifiTetheringEnabled = function(state) {
+    switch (state) {
+      case "UNINITIALIZED":
+        return false;
+      default:
+        return true;
+    }
+  }
+
   manager.syncDebug = syncDebug;
   manager.stateOrdinal = function(state) {
     return supplicantStatesMap.indexOf(state);
   }
   manager.supplicantLoopDetection = function(prevState, state) {
     var isPrevStateInHandShake = manager.isHandShakeState(prevState);
     var isStateInHandShake = manager.isHandShakeState(state);
 
@@ -1797,30 +1826,34 @@ function WifiWorker() {
     } catch (e) {
       self._allowWpaEap = false;
     }
 
     // Notify everybody, even if they didn't ask us to come up.
     WifiManager.getMacAddress(function (mac) {
       self.macAddress = mac;
       debug("Got mac: " + mac);
+      self._ignoreNextWifiEnabledSetting = true;
       self._fireEvent("wifiUp", { macAddress: mac });
+      self.requestDone();
     });
 
     if (WifiManager.state === "SCANNING")
       startScanStuckTimer();
   };
 
   WifiManager.onsupplicantlost = function() {
     WifiManager.enabled = WifiManager.supplicantStarted = false;
     WifiManager.state = "UNINITIALIZED";
     debug("Supplicant died!");
 
     // Notify everybody, even if they didn't ask us to come up.
+    self._ignoreNextWifiDisabledSetting = true;
     self._fireEvent("wifiDown", {});
+    self.requestDone();
   };
 
   WifiManager.onpasswordmaybeincorrect = function() {
     WifiManager.authenticationFailuresCount++;
   };
 
   WifiManager.ondisconnected = function() {
     // We may fail to establish the connection, re-enable the
@@ -2239,16 +2272,23 @@ WifiWorker.prototype = {
   disconnectedByWifi: false,
 
   disconnectedByWifiTethering: false,
 
   _wifiTetheringSettingsToRead: [],
 
   _oldWifiTetheringEnabledState: null,
 
+  // 930355: Workaround before bug 930355 is landed.
+  // Current system app will set settings value "wifi.enabled" after receiving
+  // wifi enable/disable event, this will cause infinite loop between gaia
+  // and gecko, so now use this variable to cut the loop.
+  _ignoreNextWifiDisabledSetting: false,
+  _ignoreNextWifiEnabledSetting: false,
+
   tetheringSettings: {},
 
   initTetheringSettings: function initTetheringSettings() {
     this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] = null;
     this.tetheringSettings[SETTINGS_WIFI_SSID] = DEFAULT_WIFI_SSID;
     this.tetheringSettings[SETTINGS_WIFI_SECURITY_TYPE] = DEFAULT_WIFI_SECURITY_TYPE;
     this.tetheringSettings[SETTINGS_WIFI_SECURITY_PASSWORD] = DEFAULT_WIFI_SECURITY_PASSWORD;
     this.tetheringSettings[SETTINGS_WIFI_IP] = DEFAULT_WIFI_IP;
@@ -2684,23 +2724,26 @@ WifiWorker.prototype = {
         networks.push(netToDOM(this.configuredNetworks[networkKey]));
       }
 
       this._sendMessage(message, true, networks, msg);
     }).bind(this));
   },
 
   _setWifiEnabledCallback: function(status) {
+    if (status !== 0) {
+      this.requestDone();
+      return;
+    }
+
     // If we're enabling ourselves, then wait until we've connected to the
     // supplicant to notify. If we're disabling, we take care of this in
     // supplicantlost.
     if (WifiManager.supplicantStarted)
       WifiManager.start();
-
-    this.requestDone();
   },
 
   setWifiEnabled: function(enabled, callback) {
     // Reply error to pending requests.
     if (!enabled) {
       this._clearPendingRequest();
     }
 
@@ -3102,24 +3145,21 @@ WifiWorker.prototype = {
         handleError: function(aErrorMessage) {
           self.requestDone();
         }
       },
       "fromInternalSetting");
   },
 
   handleWifiEnabled: function(enabled) {
-    if (WifiManager.enabled === enabled) {
-      return;
-    }
     // Make sure Wifi hotspot is idle before switching to Wifi mode.
     if (enabled) {
       this.queueRequest(false, function(data) {
         if (this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] ||
-            WifiManager.tetheringState != "UNINITIALIZED") {
+            WifiManager.isWifiTetheringEnabled(WifiManager.tetheringState)) {
           this.disconnectedByWifi = true;
           this.setWifiApEnabled(false, this.notifyTetheringOff.bind(this));
         } else {
           this.requestDone();
         }
       }.bind(this));
     }
 
@@ -3138,27 +3178,27 @@ WifiWorker.prototype = {
       }.bind(this));
     }
   },
 
   handleWifiTetheringEnabled: function(enabled) {
     // Make sure Wifi is idle before switching to Wifi hotspot mode.
     if (enabled) {
       this.queueRequest(false, function(data) {
-        if (WifiManager.enabled || WifiManager.state != "UNINITIALIZED") {
+        if (WifiManager.isWifiEnabled(WifiManager.state)) {
           this.disconnectedByWifiTethering = true;
           this.setWifiEnabled(false, this._setWifiEnabledCallback.bind(this));
         } else {
           this.requestDone();
         }
       }.bind(this));
     }
 
     this.queueRequest(enabled, function(data) {
-      this.setWifiApEnabled(data, this.requestDone.bind(this));
+      this.setWifiApEnabled(enabled, this.requestDone.bind(this));
     }.bind(this));
 
     if (!enabled) {
       this.queueRequest(true, function(data) {
         if (this.disconnectedByWifiTethering) {
           this.setWifiEnabled(true, this._setWifiEnabledCallback.bind(this));
         } else {
           this.requestDone();
@@ -3186,16 +3226,24 @@ WifiWorker.prototype = {
     }
 
     this.handle(setting.key, setting.value);
   },
 
   handle: function handle(aName, aResult) {
     switch(aName) {
       case SETTINGS_WIFI_ENABLED:
+        if (this._ignoreNextWifiEnabledSetting && aResult) {
+          this._ignoreNextWifiEnabledSetting = false;
+          break;
+        }
+        if (this._ignoreNextWifiDisabledSetting && !aResult) {
+          this._ignoreNextWifiDisabledSetting = false;
+          break;
+        }
         this.handleWifiEnabled(aResult)
         break;
       case SETTINGS_WIFI_DEBUG_ENABLED:
         if (aResult === null)
           aResult = false;
         DEBUG = aResult;
         updateDebug();
         break;