Bug 831716 - [Settings] [Internet sharing][wifi] Tapping Wi-fi hotspot/wifi ON disable Wi-fi connection forever. r=mrbkap, r=dflanagan, a=tef+
authorVincent Chang <vchang@mozilla.com>
Thu, 17 Jan 2013 20:26:17 +0800
changeset 118312 715e2e7ce60aa0429ba871fe08587097bdb45618
parent 118311 ca138e4376f1a764952d98970323fd01e4d0c8e9
child 118313 0e76f133a7108f1161398016a04e122d80b5bca2
push id343
push userryanvm@gmail.com
push dateThu, 24 Jan 2013 18:55:32 +0000
reviewersmrbkap, dflanagan, tef
bugs831716
milestone18.0
Bug 831716 - [Settings] [Internet sharing][wifi] Tapping Wi-fi hotspot/wifi ON disable Wi-fi connection forever. r=mrbkap, r=dflanagan, a=tef+
dom/wifi/WifiWorker.js
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -1011,16 +1011,17 @@ var WifiManager = (function() {
           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;
@@ -1040,20 +1041,22 @@ var WifiManager = (function() {
   // 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);
@@ -1069,26 +1072,28 @@ var WifiManager = (function() {
         Services.obs.notifyObservers(WifiNetworkInterface,
                                      kNetworkInterfaceStateChangedTopic,
                                      null);
 
         prepareForStartup(function() {
           loadDriver(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);
                 });
               });
@@ -1118,57 +1123,64 @@ var WifiManager = (function() {
         });
       });
     }
   }
 
   // Get wifi interface and load wifi driver when enable Ap mode.
   manager.setWifiApEnabled = function(enabled, callback) {
     if (enabled) {
+      manager.tetheringState = "INITIALIZING";
       getProperty("wifi.interface", "tiwlan0", function (ifname) {
         if (!ifname) {
-          callback(enabled);
+          callback();
+          manager.tetheringState = "UNINITIALIZED";
           return;
         }
         manager.ifname = ifname;
         loadDriver(function (status) {
           if (status < 0) {
-            callback(enabled);
+            callback();
+            manager.tetheringState = "UNINITIALIZED";
             return;
           }
 
           function doStartWifiTethering() {
             cancelWaitForDriverReadyTimer();
             WifiNetworkInterface.name = manager.ifname;
-            manager.state = "WIFITETHERING";
             gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface, function(result) {
+              if (result) {
+                manager.tetheringState = "UNINITIALIZED";
+              } else {
+                manager.tetheringState = "COMPLETED";
+              }
               // Pop out current request.
-              callback(enabled);
+              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);
         });
       });
     } else {
-      manager.state = "UNINITIALIZED";
       gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface, 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) {
           if (status < 0) {
             debug("Fail to unload wifi driver");
           }
-          callback(enabled);
+          manager.tetheringState = "UNINITIALIZED";
+          callback();
         });
       });
     }
   }
 
   manager.disconnect = disconnectCommand;
   manager.reconnect = reconnectCommand;
   manager.reassociate = reassociateCommand;
@@ -2542,16 +2554,17 @@ WifiWorker.prototype = {
     if (!success || state === newState) {
       do {
         if (!("callback" in this._stateRequests[0])) {
           this._stateRequests.shift();
         }
         // Don't remove more than one request if the previous one failed.
       } while (success &&
                this._stateRequests.length &&
+               !("callback" in this._stateRequests[0]) &&
                this._stateRequests[0].enabled === state);
     }
 
     // If there were requests queued after this one, run them.
     if (this._stateRequests.length > 0) {
       let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
       let self = this;
       timer.initWithCallback(function(timer) {
@@ -2598,17 +2611,17 @@ WifiWorker.prototype = {
       if ("callback" in this._stateRequests[0]) {
         this._stateRequests[0].callback.call(this, msg.enabled);
       } else {
         WifiManager.setWifiEnabled(msg.enabled, this._setWifiEnabledCallback.bind(this));
       }
     }
   },
 
-  setWifiEnabledInternal: function(enabled, callback) {
+  queueRequest: function(enabled, callback) {
     this.setWifiEnabled({enabled: enabled, callback: callback});
   },
 
   setWifiApEnabled: function(enabled, callback) {
     WifiManager.setWifiApEnabled(enabled, callback);
   },
 
   associate: function(msg) {
@@ -2772,78 +2785,72 @@ WifiWorker.prototype = {
   // that RadioManager never actually tries to get the worker from us.
   get worker() { throw "Not implemented"; },
 
   shutdown: function() {
     debug("shutting down ...");
     this.setWifiEnabled({enabled: false});
   },
 
-  nextRequest: function nextRequest(state) {
+  nextRequest: function nextRequest() {
     if (this._stateRequests.length <= 0 ||
         !("callback" in this._stateRequests[0])) {
       return;
     }
-
-    do {
-      this._stateRequests.shift();
-    } while (this._stateRequests.length &&
-             this._stateRequests[0].enabled === state);
-
+    this._stateRequests.shift();
     // Serve the pending requests.
     if (this._stateRequests.length > 0) {
       if ("callback" in this._stateRequests[0]) {
         this._stateRequests[0].callback.call(this,
                                              this._stateRequests[0].enabled);
       } else {
         WifiManager.setWifiEnabled(this._stateRequests[0].enabled,
                                    this._setWifiEnabledCallback.bind(this));
       }
     }
   },
 
+  notifyTetheringOff: function notifyTetheringOff() {
+    // It's really sad that we don't have an API to notify the wifi
+    // hotspot status. Toggle settings to let gaia know that wifi hotspot
+    // is disabled.
+    gSettingsService.createLock().set(
+      "tethering.wifi.enabled", false, null, "fromInternalSetting");
+    // Check for the next request.
+    this.nextRequest();
+  },
+
   handleWifiEnabled: function(enabled) {
     if (WifiManager.enabled === enabled) {
       return;
     }
-    // Disable wifi tethering before enabling wifi.
-    if (gNetworkManager.wifiTetheringEnabled) {
-      this.setWifiEnabledInternal(false, function(data) {
-        this.setWifiApEnabled(data, this.nextRequest.bind(this));
+    // Make sure Wifi hotspot is idle before switching to Wifi mode.
+    if (enabled && (gNetworkManager.wifiTetheringEnabled ||
+         WifiManager.tetheringState != "UNINITIALIZED")) {
+      this.queueRequest(false, function(data) {
+        this.setWifiApEnabled(false, this.notifyTetheringOff.bind(this));
       }.bind(this));
-      // It's really sad that we don't have an API to notify the wifi
-      // hotspot status. Toggle settings to let gaia know that wifi hotspot
-      // is disalbed.
-      gSettingsService.createLock().set(
-        "tethering.wifi.enabled", false, null, "fromInternalSetting");
     }
     this.setWifiEnabled({enabled: enabled});
   },
 
   handleWifiTetheringEnabled: function(enabled) {
     if (gNetworkManager.wifiTetheringEnabled === enabled) {
       return;
     }
 
-    // Wifi is disabled
-    if (!WifiManager.enabled) {
-      this.setWifiEnabledInternal(enabled, function(data) {
-        this.setWifiApEnabled(data, this.nextRequest.bind(this));
-      }.bind(this));
-      return;
+    // Make sure Wifi is idle before switching to Wifi hotspot mode.
+    if (enabled && (WifiManager.enabled ||
+         WifiManager.state != "UNINITIALIZED")) {
+      this.setWifiEnabled({enabled: false});
     }
 
-    // Wifi is enabled, turn off it before switching to Ap mode.
-    if (enabled) {
-      // Turn off wifi first.
-      this.setWifiEnabled({enabled: false});
-      this.setWifiEnabledInternal(enabled, (function (data) {
-        this.setWifiApEnabled(data, this.nextRequest.bind(this));
-      }).bind(this));
-    }
+    this.queueRequest(enabled, function(data) {
+      this.setWifiApEnabled(data, this.nextRequest.bind(this));
+    }.bind(this));
   },
 
   // nsIObserver implementation
   observe: function observe(subject, topic, data) {
     // Note that this function gets called for any and all settings changes,
     // so we need to carefully check if we have the one we're interested in.
     // The string we're interested in will be a JSON string that looks like:
     // {"key":"wifi.enabled","value":"true"}.