dom/wifi/WifiWorker.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/wifi/WifiWorker.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3307 @@
     1.4 +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +"use strict";
    1.11 +
    1.12 +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
    1.13 +
    1.14 +Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    1.15 +Cu.import("resource://gre/modules/Services.jsm");
    1.16 +Cu.import("resource://gre/modules/systemlibs.js");
    1.17 +Cu.import("resource://gre/modules/WifiCommand.jsm");
    1.18 +Cu.import("resource://gre/modules/WifiNetUtil.jsm");
    1.19 +Cu.import("resource://gre/modules/WifiP2pManager.jsm");
    1.20 +Cu.import("resource://gre/modules/WifiP2pWorkerObserver.jsm");
    1.21 +
    1.22 +var DEBUG = false; // set to true to show debug messages.
    1.23 +
    1.24 +const WIFIWORKER_CONTRACTID = "@mozilla.org/wifi/worker;1";
    1.25 +const WIFIWORKER_CID        = Components.ID("{a14e8977-d259-433a-a88d-58dd44657e5b}");
    1.26 +
    1.27 +const WIFIWORKER_WORKER     = "resource://gre/modules/wifi_worker.js";
    1.28 +
    1.29 +const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
    1.30 +const kMozSettingsChangedObserverTopic   = "mozsettings-changed";
    1.31 +
    1.32 +const MAX_RETRIES_ON_AUTHENTICATION_FAILURE = 2;
    1.33 +const MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
    1.34 +const MAX_RETRIES_ON_DHCP_FAILURE = 2;
    1.35 +
    1.36 +// Settings DB path for wifi
    1.37 +const SETTINGS_WIFI_ENABLED            = "wifi.enabled";
    1.38 +const SETTINGS_WIFI_DEBUG_ENABLED      = "wifi.debugging.enabled";
    1.39 +// Settings DB path for Wifi tethering.
    1.40 +const SETTINGS_WIFI_TETHERING_ENABLED  = "tethering.wifi.enabled";
    1.41 +const SETTINGS_WIFI_SSID               = "tethering.wifi.ssid";
    1.42 +const SETTINGS_WIFI_SECURITY_TYPE      = "tethering.wifi.security.type";
    1.43 +const SETTINGS_WIFI_SECURITY_PASSWORD  = "tethering.wifi.security.password";
    1.44 +const SETTINGS_WIFI_IP                 = "tethering.wifi.ip";
    1.45 +const SETTINGS_WIFI_PREFIX             = "tethering.wifi.prefix";
    1.46 +const SETTINGS_WIFI_DHCPSERVER_STARTIP = "tethering.wifi.dhcpserver.startip";
    1.47 +const SETTINGS_WIFI_DHCPSERVER_ENDIP   = "tethering.wifi.dhcpserver.endip";
    1.48 +const SETTINGS_WIFI_DNS1               = "tethering.wifi.dns1";
    1.49 +const SETTINGS_WIFI_DNS2               = "tethering.wifi.dns2";
    1.50 +
    1.51 +// Settings DB path for USB tethering.
    1.52 +const SETTINGS_USB_DHCPSERVER_STARTIP  = "tethering.usb.dhcpserver.startip";
    1.53 +const SETTINGS_USB_DHCPSERVER_ENDIP    = "tethering.usb.dhcpserver.endip";
    1.54 +
    1.55 +// Default value for WIFI tethering.
    1.56 +const DEFAULT_WIFI_IP                  = "192.168.1.1";
    1.57 +const DEFAULT_WIFI_PREFIX              = "24";
    1.58 +const DEFAULT_WIFI_DHCPSERVER_STARTIP  = "192.168.1.10";
    1.59 +const DEFAULT_WIFI_DHCPSERVER_ENDIP    = "192.168.1.30";
    1.60 +const DEFAULT_WIFI_SSID                = "FirefoxHotspot";
    1.61 +const DEFAULT_WIFI_SECURITY_TYPE       = "open";
    1.62 +const DEFAULT_WIFI_SECURITY_PASSWORD   = "1234567890";
    1.63 +const DEFAULT_DNS1                     = "8.8.8.8";
    1.64 +const DEFAULT_DNS2                     = "8.8.4.4";
    1.65 +
    1.66 +// Default value for USB tethering.
    1.67 +const DEFAULT_USB_DHCPSERVER_STARTIP   = "192.168.0.10";
    1.68 +const DEFAULT_USB_DHCPSERVER_ENDIP     = "192.168.0.30";
    1.69 +
    1.70 +const WIFI_FIRMWARE_AP            = "AP";
    1.71 +const WIFI_FIRMWARE_STATION       = "STA";
    1.72 +const WIFI_SECURITY_TYPE_NONE     = "open";
    1.73 +const WIFI_SECURITY_TYPE_WPA_PSK  = "wpa-psk";
    1.74 +const WIFI_SECURITY_TYPE_WPA2_PSK = "wpa2-psk";
    1.75 +
    1.76 +const NETWORK_INTERFACE_UP   = "up";
    1.77 +const NETWORK_INTERFACE_DOWN = "down";
    1.78 +
    1.79 +const DEFAULT_WLAN_INTERFACE = "wlan0";
    1.80 +
    1.81 +const DRIVER_READY_WAIT = 2000;
    1.82 +
    1.83 +const SUPP_PROP = "init.svc.wpa_supplicant";
    1.84 +const WPA_SUPPLICANT = "wpa_supplicant";
    1.85 +const DHCP_PROP = "init.svc.dhcpcd";
    1.86 +const DHCP = "dhcpcd";
    1.87 +
    1.88 +XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
    1.89 +                                   "@mozilla.org/network/manager;1",
    1.90 +                                   "nsINetworkManager");
    1.91 +
    1.92 +XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
    1.93 +                                   "@mozilla.org/network/service;1",
    1.94 +                                   "nsINetworkService");
    1.95 +
    1.96 +XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
    1.97 +                                   "@mozilla.org/settingsService;1",
    1.98 +                                   "nsISettingsService");
    1.99 +
   1.100 +// A note about errors and error handling in this file:
   1.101 +// The libraries that we use in this file are intended for C code. For
   1.102 +// C code, it is natural to return -1 for errors and 0 for success.
   1.103 +// Therefore, the code that interacts directly with the worker uses this
   1.104 +// convention (note: command functions do get boolean results since the
   1.105 +// command always succeeds and we do a string/boolean check for the
   1.106 +// expected results).
   1.107 +var WifiManager = (function() {
   1.108 +  var manager = {};
   1.109 +
   1.110 +  function getStartupPrefs() {
   1.111 +    return {
   1.112 +      sdkVersion: parseInt(libcutils.property_get("ro.build.version.sdk"), 10),
   1.113 +      unloadDriverEnabled: libcutils.property_get("ro.moz.wifi.unloaddriver") === "1",
   1.114 +      schedScanRecovery: libcutils.property_get("ro.moz.wifi.sched_scan_recover") === "false" ? false : true,
   1.115 +      driverDelay: libcutils.property_get("ro.moz.wifi.driverDelay"),
   1.116 +      p2pSupported: libcutils.property_get("ro.moz.wifi.p2p_supported") === "1",
   1.117 +      eapSimSupported: libcutils.property_get("ro.moz.wifi.eapsim_supported") === "1",
   1.118 +      ifname: libcutils.property_get("wifi.interface")
   1.119 +    };
   1.120 +  }
   1.121 +
   1.122 +  let {sdkVersion, unloadDriverEnabled, schedScanRecovery,
   1.123 +       driverDelay, p2pSupported, eapSimSupported, ifname} = getStartupPrefs();
   1.124 +
   1.125 +  let capabilities = {
   1.126 +    eapSim: eapSimSupported
   1.127 +  };
   1.128 +
   1.129 +  let wifiListener = {
   1.130 +    onWaitEvent: function(event, iface) {
   1.131 +      if (manager.ifname === iface && handleEvent(event)) {
   1.132 +        waitForEvent(iface);
   1.133 +      } else if (p2pSupported) {
   1.134 +        if (WifiP2pManager.INTERFACE_NAME === iface) {
   1.135 +          // If the connection is closed, wifi.c::wifi_wait_for_event()
   1.136 +          // will still return 'CTRL-EVENT-TERMINATING  - connection closed'
   1.137 +          // rather than blocking. So when we see this special event string,
   1.138 +          // just return immediately.
   1.139 +          const TERMINATED_EVENT = 'CTRL-EVENT-TERMINATING  - connection closed';
   1.140 +          if (-1 !== event.indexOf(TERMINATED_EVENT)) {
   1.141 +            return;
   1.142 +          }
   1.143 +          p2pManager.handleEvent(event);
   1.144 +          waitForEvent(iface);
   1.145 +        }
   1.146 +      }
   1.147 +    },
   1.148 +
   1.149 +    onCommand: function(event, iface) {
   1.150 +      onmessageresult(event, iface);
   1.151 +    }
   1.152 +  }
   1.153 +
   1.154 +  manager.ifname = ifname;
   1.155 +  manager.connectToSupplicant = false;
   1.156 +  // Emulator build runs to here.
   1.157 +  // The debug() should only be used after WifiManager.
   1.158 +  if (!ifname) {
   1.159 +    manager.ifname = DEFAULT_WLAN_INTERFACE;
   1.160 +  }
   1.161 +  manager.schedScanRecovery = schedScanRecovery;
   1.162 +  manager.driverDelay = driverDelay ? parseInt(driverDelay, 10) : DRIVER_READY_WAIT;
   1.163 +
   1.164 +  // Regular Wifi stuff.
   1.165 +  var netUtil = WifiNetUtil(controlMessage);
   1.166 +  var wifiCommand = WifiCommand(controlMessage, manager.ifname);
   1.167 +
   1.168 +  // Wifi P2P stuff
   1.169 +  var p2pManager;
   1.170 +  if (p2pSupported) {
   1.171 +    let p2pCommand = WifiCommand(controlMessage, WifiP2pManager.INTERFACE_NAME);
   1.172 +    p2pManager = WifiP2pManager(p2pCommand, netUtil);
   1.173 +  }
   1.174 +
   1.175 +  let wifiService = Cc["@mozilla.org/wifi/service;1"];
   1.176 +  if (wifiService) {
   1.177 +    wifiService = wifiService.getService(Ci.nsIWifiProxyService);
   1.178 +    let interfaces = [manager.ifname];
   1.179 +    if (p2pSupported) {
   1.180 +      interfaces.push(WifiP2pManager.INTERFACE_NAME);
   1.181 +    }
   1.182 +    wifiService.start(wifiListener, interfaces, interfaces.length);
   1.183 +  } else {
   1.184 +    debug("No wifi service component available!");
   1.185 +  }
   1.186 +
   1.187 +  // Callbacks to invoke when a reply arrives from the wifi service.
   1.188 +  var controlCallbacks = Object.create(null);
   1.189 +  var idgen = 0;
   1.190 +
   1.191 +  function controlMessage(obj, callback) {
   1.192 +    var id = idgen++;
   1.193 +    obj.id = id;
   1.194 +    if (callback) {
   1.195 +      controlCallbacks[id] = callback;
   1.196 +    }
   1.197 +    wifiService.sendCommand(obj, obj.iface);
   1.198 +  }
   1.199 +
   1.200 +  let onmessageresult = function(data, iface) {
   1.201 +    var id = data.id;
   1.202 +    var callback = controlCallbacks[id];
   1.203 +    if (callback) {
   1.204 +      callback(data);
   1.205 +      delete controlCallbacks[id];
   1.206 +    }
   1.207 +  }
   1.208 +
   1.209 +  // Polling the status worker
   1.210 +  var recvErrors = 0;
   1.211 +
   1.212 +  function waitForEvent(iface) {
   1.213 +    wifiService.waitForEvent(iface);
   1.214 +  }
   1.215 +
   1.216 +  // Commands to the control worker.
   1.217 +
   1.218 +  var driverLoaded = false;
   1.219 +
   1.220 +  function loadDriver(callback) {
   1.221 +    if (driverLoaded) {
   1.222 +      callback(0);
   1.223 +      return;
   1.224 +    }
   1.225 +
   1.226 +    wifiCommand.loadDriver(function (status) {
   1.227 +      driverLoaded = (status >= 0);
   1.228 +      callback(status)
   1.229 +    });
   1.230 +  }
   1.231 +
   1.232 +  function unloadDriver(type, callback) {
   1.233 +    if (!unloadDriverEnabled) {
   1.234 +      // Unloading drivers is generally unnecessary and
   1.235 +      // can trigger bugs in some drivers.
   1.236 +      // On properly written drivers, bringing the interface
   1.237 +      // down powers down the interface.
   1.238 +      if (type === WIFI_FIRMWARE_STATION) {
   1.239 +        notify("supplicantlost", { success: true });
   1.240 +      }
   1.241 +      callback(0);
   1.242 +      return;
   1.243 +    }
   1.244 +
   1.245 +    wifiCommand.unloadDriver(function(status) {
   1.246 +      driverLoaded = (status < 0);
   1.247 +      if (type === WIFI_FIRMWARE_STATION) {
   1.248 +        notify("supplicantlost", { success: true });
   1.249 +      }
   1.250 +      callback(status);
   1.251 +    });
   1.252 +  }
   1.253 +
   1.254 +  // A note about background scanning:
   1.255 +  // Normally, background scanning shouldn't be necessary as wpa_supplicant
   1.256 +  // has the capability to automatically schedule its own scans at appropriate
   1.257 +  // intervals. However, with some drivers, this appears to get stuck after
   1.258 +  // three scans, so we enable the driver's background scanning to work around
   1.259 +  // that when we're not connected to any network. This ensures that we'll
   1.260 +  // automatically reconnect to networks if one falls out of range.
   1.261 +  var reEnableBackgroundScan = false;
   1.262 +
   1.263 +  // NB: This is part of the internal API.
   1.264 +  manager.backgroundScanEnabled = false;
   1.265 +  function setBackgroundScan(enable, callback) {
   1.266 +    var doEnable = (enable === "ON");
   1.267 +    if (doEnable === manager.backgroundScanEnabled) {
   1.268 +      callback(false, true);
   1.269 +      return;
   1.270 +    }
   1.271 +
   1.272 +    manager.backgroundScanEnabled = doEnable;
   1.273 +    wifiCommand.setBackgroundScan(manager.backgroundScanEnabled, callback);
   1.274 +  }
   1.275 +
   1.276 +  var scanModeActive = false;
   1.277 +
   1.278 +  function scan(forceActive, callback) {
   1.279 +    if (forceActive && !scanModeActive) {
   1.280 +      // Note: we ignore errors from doSetScanMode.
   1.281 +      wifiCommand.doSetScanMode(true, function(ignore) {
   1.282 +        setBackgroundScan("OFF", function(turned, ignore) {
   1.283 +          reEnableBackgroundScan = turned;
   1.284 +          manager.handlePreWifiScan();
   1.285 +          wifiCommand.scan(function(ok) {
   1.286 +            wifiCommand.doSetScanMode(false, function(ignore) {
   1.287 +              // The result of scanCommand is the result of the actual SCAN
   1.288 +              // request.
   1.289 +              callback(ok);
   1.290 +            });
   1.291 +          });
   1.292 +        });
   1.293 +      });
   1.294 +      return;
   1.295 +    }
   1.296 +    manager.handlePreWifiScan();
   1.297 +    wifiCommand.scan(callback);
   1.298 +  }
   1.299 +
   1.300 +  var debugEnabled = false;
   1.301 +
   1.302 +  function syncDebug() {
   1.303 +    if (debugEnabled !== DEBUG) {
   1.304 +      let wanted = DEBUG;
   1.305 +      wifiCommand.setLogLevel(wanted ? "DEBUG" : "INFO", function(ok) {
   1.306 +        if (ok)
   1.307 +          debugEnabled = wanted;
   1.308 +      });
   1.309 +      if (p2pSupported && p2pManager) {
   1.310 +        p2pManager.setDebug(DEBUG);
   1.311 +      }
   1.312 +    }
   1.313 +  }
   1.314 +
   1.315 +  function getDebugEnabled(callback) {
   1.316 +    wifiCommand.getLogLevel(function(level) {
   1.317 +      if (level === null) {
   1.318 +        debug("Unable to get wpa_supplicant's log level");
   1.319 +        callback(false);
   1.320 +        return;
   1.321 +      }
   1.322 +
   1.323 +      var lines = level.split("\n");
   1.324 +      for (let i = 0; i < lines.length; ++i) {
   1.325 +        let match = /Current level: (.*)/.exec(lines[i]);
   1.326 +        if (match) {
   1.327 +          debugEnabled = match[1].toLowerCase() === "debug";
   1.328 +          callback(true);
   1.329 +          return;
   1.330 +        }
   1.331 +      }
   1.332 +
   1.333 +      // If we're here, we didn't get the current level.
   1.334 +      callback(false);
   1.335 +    });
   1.336 +  }
   1.337 +
   1.338 +  function setScanMode(setActive, callback) {
   1.339 +    scanModeActive = setActive;
   1.340 +    wifiCommand.doSetScanMode(setActive, callback);
   1.341 +  }
   1.342 +
   1.343 +  var httpProxyConfig = Object.create(null);
   1.344 +
   1.345 +  /**
   1.346 +   * Given a network, configure http proxy when using wifi.
   1.347 +   * @param network A network object to update http proxy
   1.348 +   * @param info Info should have following field:
   1.349 +   *        - httpProxyHost ip address of http proxy.
   1.350 +   *        - httpProxyPort port of http proxy, set 0 to use default port 8080.
   1.351 +   * @param callback callback function.
   1.352 +   */
   1.353 +  function configureHttpProxy(network, info, callback) {
   1.354 +    if (!network)
   1.355 +      return;
   1.356 +
   1.357 +    let networkKey = getNetworkKey(network);
   1.358 +
   1.359 +    if (!info || info.httpProxyHost === "") {
   1.360 +      delete httpProxyConfig[networkKey];
   1.361 +    } else {
   1.362 +      httpProxyConfig[networkKey] = network;
   1.363 +      httpProxyConfig[networkKey].httpProxyHost = info.httpProxyHost;
   1.364 +      httpProxyConfig[networkKey].httpProxyPort = info.httpProxyPort;
   1.365 +    }
   1.366 +
   1.367 +    callback(true);
   1.368 +  }
   1.369 +
   1.370 +  function getHttpProxyNetwork(network) {
   1.371 +    if (!network)
   1.372 +      return null;
   1.373 +
   1.374 +    let networkKey = getNetworkKey(network);
   1.375 +    return ((networkKey in httpProxyConfig) ? httpProxyConfig : null);
   1.376 +  }
   1.377 +
   1.378 +  function setHttpProxy(network) {
   1.379 +    if (!network)
   1.380 +      return;
   1.381 +
   1.382 +    gNetworkService.setNetworkProxy(network);
   1.383 +  }
   1.384 +
   1.385 +  var staticIpConfig = Object.create(null);
   1.386 +  function setStaticIpMode(network, info, callback) {
   1.387 +    let setNetworkKey = getNetworkKey(network);
   1.388 +    let curNetworkKey = null;
   1.389 +    let currentNetwork = Object.create(null);
   1.390 +    currentNetwork.netId = manager.connectionInfo.id;
   1.391 +
   1.392 +    manager.getNetworkConfiguration(currentNetwork, function (){
   1.393 +      curNetworkKey = getNetworkKey(currentNetwork);
   1.394 +
   1.395 +      // Add additional information to static ip configuration
   1.396 +      // It is used to compatiable with information dhcp callback.
   1.397 +      info.ipaddr = stringToIp(info.ipaddr_str);
   1.398 +      info.gateway = stringToIp(info.gateway_str);
   1.399 +      info.mask_str = makeMask(info.maskLength);
   1.400 +
   1.401 +      // Optional
   1.402 +      info.dns1 = stringToIp("dns1_str" in info ? info.dns1_str : "");
   1.403 +      info.dns2 = stringToIp("dns2_str" in info ? info.dns2_str : "");
   1.404 +      info.proxy = stringToIp("proxy_str" in info ? info.proxy_str : "");
   1.405 +
   1.406 +      staticIpConfig[setNetworkKey] = info;
   1.407 +
   1.408 +      // If the ssid of current connection is the same as configured ssid
   1.409 +      // It means we need update current connection to use static IP address.
   1.410 +      if (setNetworkKey == curNetworkKey) {
   1.411 +        // Use configureInterface directly doesn't work, the network iterface
   1.412 +        // and routing table is changed but still cannot connect to network
   1.413 +        // so the workaround here is disable interface the enable again to
   1.414 +        // trigger network reconnect with static ip.
   1.415 +        netUtil.disableInterface(manager.ifname, function (ok) {
   1.416 +          netUtil.enableInterface(manager.ifname, function (ok) {
   1.417 +          });
   1.418 +        });
   1.419 +      }
   1.420 +    });
   1.421 +  }
   1.422 +
   1.423 +  var dhcpInfo = null;
   1.424 +
   1.425 +  function runStaticIp(ifname, key) {
   1.426 +    debug("Run static ip");
   1.427 +
   1.428 +    // Read static ip information from settings.
   1.429 +    let staticIpInfo;
   1.430 +
   1.431 +    if (!(key in staticIpConfig))
   1.432 +      return;
   1.433 +
   1.434 +    staticIpInfo = staticIpConfig[key];
   1.435 +
   1.436 +    // Stop dhcpd when use static IP
   1.437 +    if (dhcpInfo != null) {
   1.438 +      netUtil.stopDhcp(manager.ifname, function() {});
   1.439 +    }
   1.440 +
   1.441 +    // Set ip, mask length, gateway, dns to network interface
   1.442 +    netUtil.configureInterface( { ifname: ifname,
   1.443 +                                  ipaddr: staticIpInfo.ipaddr,
   1.444 +                                  mask: staticIpInfo.maskLength,
   1.445 +                                  gateway: staticIpInfo.gateway,
   1.446 +                                  dns1: staticIpInfo.dns1,
   1.447 +                                  dns2: staticIpInfo.dns2 }, function (data) {
   1.448 +      netUtil.runIpConfig(ifname, staticIpInfo, function(data) {
   1.449 +        dhcpInfo = data.info;
   1.450 +        notify("networkconnected", data);
   1.451 +      });
   1.452 +    });
   1.453 +  }
   1.454 +
   1.455 +  var suppressEvents = false;
   1.456 +  function notify(eventName, eventObject) {
   1.457 +    if (suppressEvents)
   1.458 +      return;
   1.459 +    var handler = manager["on" + eventName];
   1.460 +    if (handler) {
   1.461 +      if (!eventObject)
   1.462 +        eventObject = ({});
   1.463 +      handler.call(eventObject);
   1.464 +    }
   1.465 +  }
   1.466 +
   1.467 +  function notifyStateChange(fields) {
   1.468 +    // If we're already in the COMPLETED state, we might receive events from
   1.469 +    // the supplicant that tell us that we're re-authenticating or reminding
   1.470 +    // us that we're associated to a network. In those cases, we don't need to
   1.471 +    // do anything, so just ignore them.
   1.472 +    if (manager.state === "COMPLETED" &&
   1.473 +        fields.state !== "DISCONNECTED" &&
   1.474 +        fields.state !== "INTERFACE_DISABLED" &&
   1.475 +        fields.state !== "INACTIVE" &&
   1.476 +        fields.state !== "SCANNING") {
   1.477 +      return false;
   1.478 +    }
   1.479 +
   1.480 +    // Stop background scanning if we're trying to connect to a network.
   1.481 +    if (manager.backgroundScanEnabled &&
   1.482 +        (fields.state === "ASSOCIATING" ||
   1.483 +         fields.state === "ASSOCIATED" ||
   1.484 +         fields.state === "FOUR_WAY_HANDSHAKE" ||
   1.485 +         fields.state === "GROUP_HANDSHAKE" ||
   1.486 +         fields.state === "COMPLETED")) {
   1.487 +      setBackgroundScan("OFF", function() {});
   1.488 +    }
   1.489 +
   1.490 +    fields.prevState = manager.state;
   1.491 +    // Detect wpa_supplicant's loop iterations.
   1.492 +    manager.supplicantLoopDetection(fields.prevState, fields.state);
   1.493 +    notify("statechange", fields);
   1.494 +
   1.495 +    // Don't update state when and after disabling.
   1.496 +    if (manager.state === "DISABLING" ||
   1.497 +        manager.state === "UNINITIALIZED") {
   1.498 +      return false;
   1.499 +    }
   1.500 +
   1.501 +    manager.state = fields.state;
   1.502 +    return true;
   1.503 +  }
   1.504 +
   1.505 +  function parseStatus(status) {
   1.506 +    if (status === null) {
   1.507 +      debug("Unable to get wpa supplicant's status");
   1.508 +      return;
   1.509 +    }
   1.510 +
   1.511 +    var ssid;
   1.512 +    var bssid;
   1.513 +    var state;
   1.514 +    var ip_address;
   1.515 +    var id;
   1.516 +
   1.517 +    var lines = status.split("\n");
   1.518 +    for (let i = 0; i < lines.length; ++i) {
   1.519 +      let [key, value] = lines[i].split("=");
   1.520 +      switch (key) {
   1.521 +        case "wpa_state":
   1.522 +          state = value;
   1.523 +          break;
   1.524 +        case "ssid":
   1.525 +          ssid = value;
   1.526 +          break;
   1.527 +        case "bssid":
   1.528 +          bssid = value;
   1.529 +          break;
   1.530 +        case "ip_address":
   1.531 +          ip_address = value;
   1.532 +          break;
   1.533 +        case "id":
   1.534 +          id = value;
   1.535 +          break;
   1.536 +      }
   1.537 +    }
   1.538 +
   1.539 +    if (bssid && ssid) {
   1.540 +      manager.connectionInfo.bssid = bssid;
   1.541 +      manager.connectionInfo.ssid = ssid;
   1.542 +      manager.connectionInfo.id = id;
   1.543 +    }
   1.544 +
   1.545 +    if (ip_address)
   1.546 +      dhcpInfo = { ip_address: ip_address };
   1.547 +
   1.548 +    notifyStateChange({ state: state, fromStatus: true });
   1.549 +
   1.550 +    // If we parse the status and the supplicant has already entered the
   1.551 +    // COMPLETED state, then we need to set up DHCP right away.
   1.552 +    if (state === "COMPLETED")
   1.553 +      onconnected();
   1.554 +  }
   1.555 +
   1.556 +  // try to connect to the supplicant
   1.557 +  var connectTries = 0;
   1.558 +  var retryTimer = null;
   1.559 +  function connectCallback(ok) {
   1.560 +    if (ok === 0) {
   1.561 +      // Tell the event worker to start waiting for events.
   1.562 +      retryTimer = null;
   1.563 +      connectTries = 0;
   1.564 +      recvErrors = 0;
   1.565 +      manager.connectToSupplicant = true;
   1.566 +      didConnectSupplicant(function(){});
   1.567 +      return;
   1.568 +    }
   1.569 +    if (connectTries++ < 5) {
   1.570 +      // Try again in 1 seconds.
   1.571 +      if (!retryTimer)
   1.572 +        retryTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   1.573 +
   1.574 +      retryTimer.initWithCallback(function(timer) {
   1.575 +        wifiCommand.connectToSupplicant(connectCallback);
   1.576 +      }, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
   1.577 +      return;
   1.578 +    }
   1.579 +
   1.580 +    retryTimer = null;
   1.581 +    connectTries = 0;
   1.582 +    notify("supplicantlost", { success: false });
   1.583 +  }
   1.584 +
   1.585 +  manager.connectionDropped = function(callback) {
   1.586 +    // Reset network interface when connection drop
   1.587 +    netUtil.configureInterface( { ifname: manager.ifname,
   1.588 +                                  ipaddr: 0,
   1.589 +                                  mask: 0,
   1.590 +                                  gateway: 0,
   1.591 +                                  dns1: 0,
   1.592 +                                  dns2: 0 }, function (data) {
   1.593 +    });
   1.594 +
   1.595 +    // If we got disconnected, kill the DHCP client in preparation for
   1.596 +    // reconnection.
   1.597 +    netUtil.resetConnections(manager.ifname, function() {
   1.598 +      netUtil.stopDhcp(manager.ifname, function() {
   1.599 +        callback();
   1.600 +      });
   1.601 +    });
   1.602 +  }
   1.603 +
   1.604 +  manager.start = function() {
   1.605 +    debug("detected SDK version " + sdkVersion);
   1.606 +    wifiCommand.connectToSupplicant(connectCallback);
   1.607 +  }
   1.608 +
   1.609 +  function onconnected() {
   1.610 +    // For now we do our own DHCP. In the future, this should be handed
   1.611 +    // off to the Network Manager.
   1.612 +    let currentNetwork = Object.create(null);
   1.613 +    currentNetwork.netId = manager.connectionInfo.id;
   1.614 +
   1.615 +    manager.getNetworkConfiguration(currentNetwork, function (){
   1.616 +      let key = getNetworkKey(currentNetwork);
   1.617 +      if (staticIpConfig  &&
   1.618 +          (key in staticIpConfig) &&
   1.619 +          staticIpConfig[key].enabled) {
   1.620 +          debug("Run static ip");
   1.621 +          runStaticIp(manager.ifname, key);
   1.622 +          return;
   1.623 +      }
   1.624 +      netUtil.runDhcp(manager.ifname, function(data) {
   1.625 +        dhcpInfo = data.info;
   1.626 +        if (!dhcpInfo) {
   1.627 +          if (++manager.dhcpFailuresCount >= MAX_RETRIES_ON_DHCP_FAILURE) {
   1.628 +            manager.dhcpFailuresCount = 0;
   1.629 +            notify("disconnected", {ssid: manager.connectionInfo.ssid});
   1.630 +            return;
   1.631 +          }
   1.632 +          // NB: We have to call disconnect first. Otherwise, we only reauth with
   1.633 +          // the existing AP and don't retrigger DHCP.
   1.634 +          manager.disconnect(function() {
   1.635 +            manager.reassociate(function(){});
   1.636 +          });
   1.637 +          return;
   1.638 +        }
   1.639 +
   1.640 +        manager.dhcpFailuresCount = 0;
   1.641 +        notify("networkconnected", data);
   1.642 +      });
   1.643 +    });
   1.644 +  }
   1.645 +
   1.646 +  var supplicantStatesMap = (sdkVersion >= 15) ?
   1.647 +    ["DISCONNECTED", "INTERFACE_DISABLED", "INACTIVE", "SCANNING",
   1.648 +     "AUTHENTICATING", "ASSOCIATING", "ASSOCIATED", "FOUR_WAY_HANDSHAKE",
   1.649 +     "GROUP_HANDSHAKE", "COMPLETED"]
   1.650 +    :
   1.651 +    ["DISCONNECTED", "INACTIVE", "SCANNING", "ASSOCIATING",
   1.652 +     "ASSOCIATED", "FOUR_WAY_HANDSHAKE", "GROUP_HANDSHAKE",
   1.653 +     "COMPLETED", "DORMANT", "UNINITIALIZED"];
   1.654 +
   1.655 +  var driverEventMap = { STOPPED: "driverstopped", STARTED: "driverstarted", HANGED: "driverhung" };
   1.656 +
   1.657 +  manager.getCurrentNetworkId = function (ssid, callback) {
   1.658 +    manager.getConfiguredNetworks(function(networks) {
   1.659 +      if (!networks) {
   1.660 +        debug("Unable to get configured networks");
   1.661 +        return callback(null);
   1.662 +      }
   1.663 +      for (let net in networks) {
   1.664 +        let network = networks[net];
   1.665 +        // Trying to get netId from
   1.666 +        // 1. CURRENT network.
   1.667 +        // 2. Trying to associate with SSID 'ssid' event
   1.668 +        if (network.status === "CURRENT" ||
   1.669 +            (ssid && ssid === dequote(network.ssid))) {
   1.670 +          return callback(net);
   1.671 +        }
   1.672 +      }
   1.673 +      callback(null);
   1.674 +    });
   1.675 +  }
   1.676 +
   1.677 +  // Handle events sent to us by the event worker.
   1.678 +  function handleEvent(event) {
   1.679 +    debug("Event coming in: " + event);
   1.680 +    if (event.indexOf("CTRL-EVENT-") !== 0 && event.indexOf("WPS") !== 0) {
   1.681 +      // Handle connection fail exception on WEP-128, while password length
   1.682 +      // is not 5 nor 13 bytes.
   1.683 +      if (event.indexOf("Association request to the driver failed") !== -1) {
   1.684 +        notify("passwordmaybeincorrect");
   1.685 +        if (manager.authenticationFailuresCount > MAX_RETRIES_ON_AUTHENTICATION_FAILURE) {
   1.686 +          manager.authenticationFailuresCount = 0;
   1.687 +          notify("disconnected", {ssid: manager.connectionInfo.ssid});
   1.688 +        }
   1.689 +        return true;
   1.690 +      }
   1.691 +
   1.692 +      if (event.indexOf("WPA:") == 0 &&
   1.693 +          event.indexOf("pre-shared key may be incorrect") != -1) {
   1.694 +        notify("passwordmaybeincorrect");
   1.695 +      }
   1.696 +
   1.697 +      // This is ugly, but we need to grab the SSID here. BSSID is not guaranteed
   1.698 +      // to be provided, so don't grab BSSID here.
   1.699 +      var match = /Trying to associate with.*SSID[ =]'(.*)'/.exec(event);
   1.700 +      if (match) {
   1.701 +        debug("Matched: " + match[1] + "\n");
   1.702 +        manager.connectionInfo.ssid = match[1];
   1.703 +      }
   1.704 +      return true;
   1.705 +    }
   1.706 +
   1.707 +    var space = event.indexOf(" ");
   1.708 +    var eventData = event.substr(0, space + 1);
   1.709 +    if (eventData.indexOf("CTRL-EVENT-STATE-CHANGE") === 0) {
   1.710 +      // Parse the event data.
   1.711 +      var fields = {};
   1.712 +      var tokens = event.substr(space + 1).split(" ");
   1.713 +      for (var n = 0; n < tokens.length; ++n) {
   1.714 +        var kv = tokens[n].split("=");
   1.715 +        if (kv.length === 2)
   1.716 +          fields[kv[0]] = kv[1];
   1.717 +      }
   1.718 +      if (!("state" in fields))
   1.719 +        return true;
   1.720 +      fields.state = supplicantStatesMap[fields.state];
   1.721 +
   1.722 +      // The BSSID field is only valid in the ASSOCIATING and ASSOCIATED
   1.723 +      // states, except when we "reauth", except this seems to depend on the
   1.724 +      // driver, so simply check to make sure that we don't have a null BSSID.
   1.725 +      if (fields.BSSID !== "00:00:00:00:00:00")
   1.726 +        manager.connectionInfo.bssid = fields.BSSID;
   1.727 +
   1.728 +      if (notifyStateChange(fields) && fields.state === "COMPLETED") {
   1.729 +        onconnected();
   1.730 +      }
   1.731 +      return true;
   1.732 +    }
   1.733 +    if (eventData.indexOf("CTRL-EVENT-DRIVER-STATE") === 0) {
   1.734 +      var handlerName = driverEventMap[eventData];
   1.735 +      if (handlerName)
   1.736 +        notify(handlerName);
   1.737 +      return true;
   1.738 +    }
   1.739 +    if (eventData.indexOf("CTRL-EVENT-TERMINATING") === 0) {
   1.740 +      // As long the monitor socket is not closed and we haven't seen too many
   1.741 +      // recv errors yet, we will keep going for a bit longer.
   1.742 +      if (event.indexOf("connection closed") === -1 &&
   1.743 +          event.indexOf("recv error") !== -1 && ++recvErrors < 10)
   1.744 +        return true;
   1.745 +
   1.746 +      notifyStateChange({ state: "DISCONNECTED", BSSID: null, id: -1 });
   1.747 +
   1.748 +      // If the supplicant is terminated as commanded, the supplicant lost
   1.749 +      // notification will be sent after driver unloaded. In such case, the
   1.750 +      // manager state will be "DISABLING" or "UNINITIALIZED".
   1.751 +      // So if supplicant terminated with incorrect manager state, implying
   1.752 +      // unexpected condition, we should notify supplicant lost here.
   1.753 +      if (manager.state !== "DISABLING" && manager.state !== "UNINITIALIZED") {
   1.754 +        notify("supplicantlost", { success: true });
   1.755 +      }
   1.756 +      wifiCommand.closeSupplicantConnection(function() {
   1.757 +        manager.connectToSupplicant = false;
   1.758 +      });
   1.759 +      return false;
   1.760 +    }
   1.761 +    if (eventData.indexOf("CTRL-EVENT-DISCONNECTED") === 0) {
   1.762 +      var token = event.split(" ")[1];
   1.763 +      var bssid = token.split("=")[1];
   1.764 +      if (manager.authenticationFailuresCount > MAX_RETRIES_ON_AUTHENTICATION_FAILURE) {
   1.765 +        manager.authenticationFailuresCount = 0;
   1.766 +        notify("disconnected", {ssid: manager.connectionInfo.ssid});
   1.767 +      }
   1.768 +      manager.connectionInfo.bssid = null;
   1.769 +      manager.connectionInfo.ssid = null;
   1.770 +      manager.connectionInfo.id = -1;
   1.771 +      return true;
   1.772 +    }
   1.773 +    // Association reject is triggered mostly on incorrect WEP key.
   1.774 +    if (eventData.indexOf("CTRL-EVENT-ASSOC-REJECT") === 0) {
   1.775 +      notify("passwordmaybeincorrect");
   1.776 +      if (manager.authenticationFailuresCount > MAX_RETRIES_ON_AUTHENTICATION_FAILURE) {
   1.777 +        manager.authenticationFailuresCount = 0;
   1.778 +        notify("disconnected", {ssid: manager.connectionInfo.ssid});
   1.779 +      }
   1.780 +      return true;
   1.781 +    }
   1.782 +    if (eventData.indexOf("CTRL-EVENT-EAP-FAILURE") === 0) {
   1.783 +      if (event.indexOf("EAP authentication failed") !== -1) {
   1.784 +        notify("passwordmaybeincorrect");
   1.785 +      }
   1.786 +      return true;
   1.787 +    }
   1.788 +    if (eventData.indexOf("CTRL-EVENT-CONNECTED") === 0) {
   1.789 +      // Format: CTRL-EVENT-CONNECTED - Connection to 00:1e:58:ec:d5:6d completed (reauth) [id=1 id_str=]
   1.790 +      var bssid = event.split(" ")[4];
   1.791 +
   1.792 +      var keyword = "id=";
   1.793 +      var id = event.substr(event.indexOf(keyword) + keyword.length).split(" ")[0];
   1.794 +      // Read current BSSID here, it will always being provided.
   1.795 +      manager.connectionInfo.id = id;
   1.796 +      manager.connectionInfo.bssid = bssid;
   1.797 +      return true;
   1.798 +    }
   1.799 +    if (eventData.indexOf("CTRL-EVENT-SCAN-RESULTS") === 0) {
   1.800 +      debug("Notifying of scan results available");
   1.801 +      if (reEnableBackgroundScan) {
   1.802 +        reEnableBackgroundScan = false;
   1.803 +        setBackgroundScan("ON", function() {});
   1.804 +      }
   1.805 +      manager.handlePostWifiScan();
   1.806 +      notify("scanresultsavailable");
   1.807 +      return true;
   1.808 +    }
   1.809 +    if (eventData.indexOf("WPS-TIMEOUT") === 0) {
   1.810 +      notifyStateChange({ state: "WPS_TIMEOUT", BSSID: null, id: -1 });
   1.811 +      return true;
   1.812 +    }
   1.813 +    if (eventData.indexOf("WPS-FAIL") === 0) {
   1.814 +      notifyStateChange({ state: "WPS_FAIL", BSSID: null, id: -1 });
   1.815 +      return true;
   1.816 +    }
   1.817 +    if (eventData.indexOf("WPS-OVERLAP-DETECTED") === 0) {
   1.818 +      notifyStateChange({ state: "WPS_OVERLAP_DETECTED", BSSID: null, id: -1 });
   1.819 +      return true;
   1.820 +    }
   1.821 +    // Unknown event.
   1.822 +    return true;
   1.823 +  }
   1.824 +
   1.825 +  function didConnectSupplicant(callback) {
   1.826 +    waitForEvent(manager.ifname);
   1.827 +
   1.828 +    // Load up the supplicant state.
   1.829 +    getDebugEnabled(function(ok) {
   1.830 +      syncDebug();
   1.831 +    });
   1.832 +    wifiCommand.status(function(status) {
   1.833 +      parseStatus(status);
   1.834 +      notify("supplicantconnection");
   1.835 +      callback();
   1.836 +    });
   1.837 +
   1.838 +    if (p2pSupported) {
   1.839 +      manager.enableP2p(function(success) {});
   1.840 +    }
   1.841 +  }
   1.842 +
   1.843 +  function prepareForStartup(callback) {
   1.844 +    let status = libcutils.property_get(DHCP_PROP + "_" + manager.ifname);
   1.845 +    if (status !== "running") {
   1.846 +      tryStopSupplicant();
   1.847 +      return;
   1.848 +    }
   1.849 +    manager.connectionDropped(function() {
   1.850 +      tryStopSupplicant();
   1.851 +    });
   1.852 +
   1.853 +    // Ignore any errors and kill any currently-running supplicants. On some
   1.854 +    // phones, stopSupplicant won't work for a supplicant that we didn't
   1.855 +    // start, so we hand-roll it here.
   1.856 +    function tryStopSupplicant () {
   1.857 +      let status = libcutils.property_get(SUPP_PROP);
   1.858 +      if (status !== "running") {
   1.859 +        callback();
   1.860 +        return;
   1.861 +      }
   1.862 +      suppressEvents = true;
   1.863 +      wifiCommand.killSupplicant(function() {
   1.864 +        netUtil.disableInterface(manager.ifname, function (ok) {
   1.865 +          suppressEvents = false;
   1.866 +          callback();
   1.867 +        });
   1.868 +      });
   1.869 +    }
   1.870 +  }
   1.871 +
   1.872 +  // Initial state.
   1.873 +  manager.state = "UNINITIALIZED";
   1.874 +  manager.tetheringState = "UNINITIALIZED";
   1.875 +  manager.enabled = false;
   1.876 +  manager.supplicantStarted = false;
   1.877 +  manager.connectionInfo = { ssid: null, bssid: null, id: -1 };
   1.878 +  manager.authenticationFailuresCount = 0;
   1.879 +  manager.loopDetectionCount = 0;
   1.880 +  manager.dhcpFailuresCount = 0;
   1.881 +
   1.882 +  var waitForDriverReadyTimer = null;
   1.883 +  function cancelWaitForDriverReadyTimer() {
   1.884 +    if (waitForDriverReadyTimer) {
   1.885 +      waitForDriverReadyTimer.cancel();
   1.886 +      waitForDriverReadyTimer = null;
   1.887 +    }
   1.888 +  };
   1.889 +  function createWaitForDriverReadyTimer(onTimeout) {
   1.890 +    waitForDriverReadyTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   1.891 +    waitForDriverReadyTimer.initWithCallback(onTimeout,
   1.892 +                                             manager.driverDelay,
   1.893 +                                             Ci.nsITimer.TYPE_ONE_SHOT);
   1.894 +  };
   1.895 +
   1.896 +  // Public interface of the wifi service.
   1.897 +  manager.setWifiEnabled = function(enabled, callback) {
   1.898 +    if (enabled === manager.isWifiEnabled(manager.state)) {
   1.899 +      callback("no change");
   1.900 +      return;
   1.901 +    }
   1.902 +
   1.903 +    if (enabled) {
   1.904 +      manager.state = "INITIALIZING";
   1.905 +      // Register as network interface.
   1.906 +      WifiNetworkInterface.name = manager.ifname;
   1.907 +      if (!WifiNetworkInterface.registered) {
   1.908 +        gNetworkManager.registerNetworkInterface(WifiNetworkInterface);
   1.909 +        WifiNetworkInterface.registered = true;
   1.910 +      }
   1.911 +      WifiNetworkInterface.state = Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED;
   1.912 +      WifiNetworkInterface.ips = [];
   1.913 +      WifiNetworkInterface.prefixLengths = [];
   1.914 +      WifiNetworkInterface.gateways = [];
   1.915 +      WifiNetworkInterface.dnses = [];
   1.916 +      Services.obs.notifyObservers(WifiNetworkInterface,
   1.917 +                                   kNetworkInterfaceStateChangedTopic,
   1.918 +                                   null);
   1.919 +      prepareForStartup(function() {
   1.920 +        loadDriver(function (status) {
   1.921 +          if (status < 0) {
   1.922 +            callback(status);
   1.923 +            manager.state = "UNINITIALIZED";
   1.924 +            return;
   1.925 +          }
   1.926 +          // This command is mandatory for Nexus 4. But some devices like
   1.927 +          // Galaxy S2 don't support it. Continue to start wpa_supplicant
   1.928 +          // even if we fail to set wifi operation mode to station.
   1.929 +          gNetworkService.setWifiOperationMode(manager.ifname,
   1.930 +                                               WIFI_FIRMWARE_STATION,
   1.931 +                                               function (status) {
   1.932 +
   1.933 +            function startSupplicantInternal() {
   1.934 +              wifiCommand.startSupplicant(function (status) {
   1.935 +                if (status < 0) {
   1.936 +                  unloadDriver(WIFI_FIRMWARE_STATION, function() {
   1.937 +                    callback(status);
   1.938 +                  });
   1.939 +                  manager.state = "UNINITIALIZED";
   1.940 +                  return;
   1.941 +                }
   1.942 +
   1.943 +                manager.supplicantStarted = true;
   1.944 +                netUtil.enableInterface(manager.ifname, function (ok) {
   1.945 +                  callback(ok ? 0 : -1);
   1.946 +                });
   1.947 +              });
   1.948 +            }
   1.949 +
   1.950 +            function doStartSupplicant() {
   1.951 +              cancelWaitForDriverReadyTimer();
   1.952 +
   1.953 +              if (!manager.connectToSupplicant) {
   1.954 +                startSupplicantInternal();
   1.955 +                return;
   1.956 +              }
   1.957 +              wifiCommand.closeSupplicantConnection(function () {
   1.958 +                manager.connectToSupplicant = false;
   1.959 +                // closeSupplicantConnection() will trigger onsupplicantlost
   1.960 +                // and set manager.state to "UNINITIALIZED", we have to
   1.961 +                // restore it here.
   1.962 +                manager.state = "INITIALIZING";
   1.963 +                startSupplicantInternal();
   1.964 +              });
   1.965 +            }
   1.966 +            // Driver startup on certain platforms takes longer than it takes for us
   1.967 +            // to return from loadDriver, so wait 2 seconds before starting
   1.968 +            // the supplicant to give it a chance to start.
   1.969 +            if (manager.driverDelay > 0) {
   1.970 +              createWaitForDriverReadyTimer(doStartSupplicant);
   1.971 +            } else {
   1.972 +              doStartSupplicant();
   1.973 +            }
   1.974 +          });
   1.975 +        });
   1.976 +      });
   1.977 +    } else {
   1.978 +      manager.state = "DISABLING";
   1.979 +      // Note these following calls ignore errors. If we fail to kill the
   1.980 +      // supplicant gracefully, then we need to continue telling it to die
   1.981 +      // until it does.
   1.982 +      let doDisableWifi = function() {
   1.983 +        wifiCommand.terminateSupplicant(function (ok) {
   1.984 +          manager.connectionDropped(function () {
   1.985 +            wifiCommand.stopSupplicant(function (status) {
   1.986 +              manager.state = "UNINITIALIZED";
   1.987 +              netUtil.disableInterface(manager.ifname, function (ok) {
   1.988 +                unloadDriver(WIFI_FIRMWARE_STATION, callback);
   1.989 +              });
   1.990 +            });
   1.991 +          });
   1.992 +        });
   1.993 +      }
   1.994 +
   1.995 +      if (p2pSupported) {
   1.996 +        p2pManager.setEnabled(false, { onDisabled: doDisableWifi });
   1.997 +      } else {
   1.998 +        doDisableWifi();
   1.999 +      }
  1.1000 +    }
  1.1001 +  }
  1.1002 +
  1.1003 +  // Get wifi interface and load wifi driver when enable Ap mode.
  1.1004 +  manager.setWifiApEnabled = function(enabled, configuration, callback) {
  1.1005 +    if (enabled === manager.isWifiTetheringEnabled(manager.tetheringState)) {
  1.1006 +      callback("no change");
  1.1007 +      return;
  1.1008 +    }
  1.1009 +
  1.1010 +    if (enabled) {
  1.1011 +      manager.tetheringState = "INITIALIZING";
  1.1012 +      loadDriver(function (status) {
  1.1013 +        if (status < 0) {
  1.1014 +          callback();
  1.1015 +          manager.tetheringState = "UNINITIALIZED";
  1.1016 +          return;
  1.1017 +        }
  1.1018 +
  1.1019 +        function doStartWifiTethering() {
  1.1020 +          cancelWaitForDriverReadyTimer();
  1.1021 +          WifiNetworkInterface.name = manager.ifname;
  1.1022 +          gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface,
  1.1023 +                                           configuration, function(result) {
  1.1024 +            if (result) {
  1.1025 +              manager.tetheringState = "UNINITIALIZED";
  1.1026 +            } else {
  1.1027 +              manager.tetheringState = "COMPLETED";
  1.1028 +            }
  1.1029 +            // Pop out current request.
  1.1030 +            callback();
  1.1031 +            // Should we fire a dom event if we fail to set wifi tethering  ?
  1.1032 +            debug("Enable Wifi tethering result: " + (result ? result : "successfully"));
  1.1033 +          });
  1.1034 +        }
  1.1035 +
  1.1036 +        // Driver startup on certain platforms takes longer than it takes
  1.1037 +        // for us to return from loadDriver, so wait 2 seconds before
  1.1038 +        // turning on Wifi tethering.
  1.1039 +        createWaitForDriverReadyTimer(doStartWifiTethering);
  1.1040 +      });
  1.1041 +    } else {
  1.1042 +      gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface,
  1.1043 +                                       configuration, function(result) {
  1.1044 +        // Should we fire a dom event if we fail to set wifi tethering  ?
  1.1045 +        debug("Disable Wifi tethering result: " + (result ? result : "successfully"));
  1.1046 +        // Unload wifi driver even if we fail to control wifi tethering.
  1.1047 +        unloadDriver(WIFI_FIRMWARE_AP, function(status) {
  1.1048 +          if (status < 0) {
  1.1049 +            debug("Fail to unload wifi driver");
  1.1050 +          }
  1.1051 +          manager.tetheringState = "UNINITIALIZED";
  1.1052 +          callback();
  1.1053 +        });
  1.1054 +      });
  1.1055 +    }
  1.1056 +  }
  1.1057 +
  1.1058 +  manager.disconnect = wifiCommand.disconnect;
  1.1059 +  manager.reconnect = wifiCommand.reconnect;
  1.1060 +  manager.reassociate = wifiCommand.reassociate;
  1.1061 +
  1.1062 +  var networkConfigurationFields = [
  1.1063 +    "ssid", "bssid", "psk", "wep_key0", "wep_key1", "wep_key2", "wep_key3",
  1.1064 +    "wep_tx_keyidx", "priority", "key_mgmt", "scan_ssid", "disabled",
  1.1065 +    "identity", "password", "auth_alg", "phase1", "phase2", "eap", "pin",
  1.1066 +    "pcsc"
  1.1067 +  ];
  1.1068 +
  1.1069 +  manager.getNetworkConfiguration = function(config, callback) {
  1.1070 +    var netId = config.netId;
  1.1071 +    var done = 0;
  1.1072 +    for (var n = 0; n < networkConfigurationFields.length; ++n) {
  1.1073 +      let fieldName = networkConfigurationFields[n];
  1.1074 +      wifiCommand.getNetworkVariable(netId, fieldName, function(value) {
  1.1075 +        if (value !== null)
  1.1076 +          config[fieldName] = value;
  1.1077 +        if (++done == networkConfigurationFields.length)
  1.1078 +          callback(config);
  1.1079 +      });
  1.1080 +    }
  1.1081 +  }
  1.1082 +  manager.setNetworkConfiguration = function(config, callback) {
  1.1083 +    var netId = config.netId;
  1.1084 +    var done = 0;
  1.1085 +    var errors = 0;
  1.1086 +    for (var n = 0; n < networkConfigurationFields.length; ++n) {
  1.1087 +      let fieldName = networkConfigurationFields[n];
  1.1088 +      if (!(fieldName in config) ||
  1.1089 +          // These fields are special: We can't retrieve them from the
  1.1090 +          // supplicant, and often we have a star in our config. In that case,
  1.1091 +          // we need to avoid overwriting the correct password with a *.
  1.1092 +          (fieldName === "password" ||
  1.1093 +           fieldName === "wep_key0" ||
  1.1094 +           fieldName === "psk") &&
  1.1095 +          config[fieldName] === '*') {
  1.1096 +        ++done;
  1.1097 +      } else {
  1.1098 +        wifiCommand.setNetworkVariable(netId, fieldName, config[fieldName], function(ok) {
  1.1099 +          if (!ok)
  1.1100 +            ++errors;
  1.1101 +          if (++done == networkConfigurationFields.length)
  1.1102 +            callback(errors == 0);
  1.1103 +        });
  1.1104 +      }
  1.1105 +    }
  1.1106 +    // If config didn't contain any of the fields we want, don't lose the error callback.
  1.1107 +    if (done == networkConfigurationFields.length)
  1.1108 +      callback(false);
  1.1109 +  }
  1.1110 +  manager.getConfiguredNetworks = function(callback) {
  1.1111 +    wifiCommand.listNetworks(function (reply) {
  1.1112 +      var networks = Object.create(null);
  1.1113 +      var lines = reply ? reply.split("\n") : 0;
  1.1114 +      if (lines.length <= 1) {
  1.1115 +        // We need to make sure we call the callback even if there are no
  1.1116 +        // configured networks.
  1.1117 +        callback(networks);
  1.1118 +        return;
  1.1119 +      }
  1.1120 +
  1.1121 +      var done = 0;
  1.1122 +      var errors = 0;
  1.1123 +      for (var n = 1; n < lines.length; ++n) {
  1.1124 +        var result = lines[n].split("\t");
  1.1125 +        var netId = result[0];
  1.1126 +        var config = networks[netId] = { netId: netId };
  1.1127 +        switch (result[3]) {
  1.1128 +        case "[CURRENT]":
  1.1129 +          config.status = "CURRENT";
  1.1130 +          break;
  1.1131 +        case "[DISABLED]":
  1.1132 +          config.status = "DISABLED";
  1.1133 +          break;
  1.1134 +        default:
  1.1135 +          config.status = "ENABLED";
  1.1136 +          break;
  1.1137 +        }
  1.1138 +        manager.getNetworkConfiguration(config, function (ok) {
  1.1139 +            if (!ok)
  1.1140 +              ++errors;
  1.1141 +            if (++done == lines.length - 1) {
  1.1142 +              if (errors) {
  1.1143 +                // If an error occured, delete the new netId.
  1.1144 +                wifiCommand.removeNetwork(netId, function() {
  1.1145 +                  callback(null);
  1.1146 +                });
  1.1147 +              } else {
  1.1148 +                callback(networks);
  1.1149 +              }
  1.1150 +            }
  1.1151 +        });
  1.1152 +      }
  1.1153 +    });
  1.1154 +  }
  1.1155 +  manager.addNetwork = function(config, callback) {
  1.1156 +    wifiCommand.addNetwork(function (netId) {
  1.1157 +      config.netId = netId;
  1.1158 +      manager.setNetworkConfiguration(config, function (ok) {
  1.1159 +        if (!ok) {
  1.1160 +          wifiCommand.removeNetwork(netId, function() { callback(false); });
  1.1161 +          return;
  1.1162 +        }
  1.1163 +
  1.1164 +        callback(ok);
  1.1165 +      });
  1.1166 +    });
  1.1167 +  }
  1.1168 +  manager.updateNetwork = function(config, callback) {
  1.1169 +    manager.setNetworkConfiguration(config, callback);
  1.1170 +  }
  1.1171 +  manager.removeNetwork = function(netId, callback) {
  1.1172 +    wifiCommand.removeNetwork(netId, callback);
  1.1173 +  }
  1.1174 +
  1.1175 +  function stringToIp(string) {
  1.1176 +    let ip = 0;
  1.1177 +    let start, end = -1;
  1.1178 +    for (let i = 0; i < 4; i++) {
  1.1179 +      start = end + 1;
  1.1180 +      end = string.indexOf(".", start);
  1.1181 +      if (end == -1) {
  1.1182 +        end = string.length;
  1.1183 +      }
  1.1184 +      let num = parseInt(string.slice(start, end), 10);
  1.1185 +      if (isNaN(num)) {
  1.1186 +        return 0;
  1.1187 +      }
  1.1188 +      ip |= num << (i * 8);
  1.1189 +    }
  1.1190 +    return ip;
  1.1191 +  }
  1.1192 +
  1.1193 +  function swap32(n) {
  1.1194 +    return (((n >> 24) & 0xFF) <<  0) |
  1.1195 +           (((n >> 16) & 0xFF) <<  8) |
  1.1196 +           (((n >>  8) & 0xFF) << 16) |
  1.1197 +           (((n >>  0) & 0xFF) << 24);
  1.1198 +  }
  1.1199 +
  1.1200 +  function ntohl(n) {
  1.1201 +    return swap32(n);
  1.1202 +  }
  1.1203 +
  1.1204 +  function makeMask(len) {
  1.1205 +    let mask = 0;
  1.1206 +    for (let i = 0; i < len; ++i) {
  1.1207 +      mask |= (0x80000000 >> i);
  1.1208 +    }
  1.1209 +    return ntohl(mask);
  1.1210 +  }
  1.1211 +
  1.1212 +  manager.saveConfig = function(callback) {
  1.1213 +    wifiCommand.saveConfig(callback);
  1.1214 +  }
  1.1215 +  manager.enableNetwork = function(netId, disableOthers, callback) {
  1.1216 +    if (p2pSupported) {
  1.1217 +      // We have to stop wifi direct scan before associating to an AP.
  1.1218 +      // Otherwise we will get a "REJECT" wpa supplicant event.
  1.1219 +      p2pManager.setScanEnabled(false, function(success) {});
  1.1220 +    }
  1.1221 +    wifiCommand.enableNetwork(netId, disableOthers, callback);
  1.1222 +  }
  1.1223 +  manager.disableNetwork = function(netId, callback) {
  1.1224 +    wifiCommand.disableNetwork(netId, callback);
  1.1225 +  }
  1.1226 +  manager.getMacAddress = wifiCommand.getMacAddress;
  1.1227 +  manager.getScanResults = wifiCommand.scanResults;
  1.1228 +  manager.setScanMode = function(mode, callback) {
  1.1229 +    setScanMode(mode === "active", callback); // Use our own version.
  1.1230 +  }
  1.1231 +  manager.setBackgroundScan = setBackgroundScan; // Use our own version.
  1.1232 +  manager.scan = scan; // Use our own version.
  1.1233 +  manager.wpsPbc = wifiCommand.wpsPbc;
  1.1234 +  manager.wpsPin = wifiCommand.wpsPin;
  1.1235 +  manager.wpsCancel = wifiCommand.wpsCancel;
  1.1236 +  manager.setPowerMode = (sdkVersion >= 16)
  1.1237 +                         ? wifiCommand.setPowerModeJB
  1.1238 +                         : wifiCommand.setPowerModeICS;
  1.1239 +  manager.getHttpProxyNetwork = getHttpProxyNetwork;
  1.1240 +  manager.setHttpProxy = setHttpProxy;
  1.1241 +  manager.configureHttpProxy = configureHttpProxy;
  1.1242 +  manager.setSuspendOptimizations = (sdkVersion >= 16)
  1.1243 +                                   ? wifiCommand.setSuspendOptimizationsJB
  1.1244 +                                   : wifiCommand.setSuspendOptimizationsICS;
  1.1245 +  manager.setStaticIpMode = setStaticIpMode;
  1.1246 +  manager.getRssiApprox = wifiCommand.getRssiApprox;
  1.1247 +  manager.getLinkSpeed = wifiCommand.getLinkSpeed;
  1.1248 +  manager.getDhcpInfo = function() { return dhcpInfo; }
  1.1249 +  manager.getConnectionInfo = (sdkVersion >= 15)
  1.1250 +                              ? wifiCommand.getConnectionInfoICS
  1.1251 +                              : wifiCommand.getConnectionInfoGB;
  1.1252 +
  1.1253 +  manager.isHandShakeState = function(state) {
  1.1254 +    switch (state) {
  1.1255 +      case "AUTHENTICATING":
  1.1256 +      case "ASSOCIATING":
  1.1257 +      case "ASSOCIATED":
  1.1258 +      case "FOUR_WAY_HANDSHAKE":
  1.1259 +      case "GROUP_HANDSHAKE":
  1.1260 +        return true;
  1.1261 +      case "DORMANT":
  1.1262 +      case "COMPLETED":
  1.1263 +      case "DISCONNECTED":
  1.1264 +      case "INTERFACE_DISABLED":
  1.1265 +      case "INACTIVE":
  1.1266 +      case "SCANNING":
  1.1267 +      case "UNINITIALIZED":
  1.1268 +      case "INVALID":
  1.1269 +      case "CONNECTED":
  1.1270 +      default:
  1.1271 +        return false;
  1.1272 +    }
  1.1273 +  }
  1.1274 +
  1.1275 +  manager.isWifiEnabled = function(state) {
  1.1276 +    switch (state) {
  1.1277 +      case "UNINITIALIZED":
  1.1278 +      case "DISABLING":
  1.1279 +        return false;
  1.1280 +      default:
  1.1281 +        return true;
  1.1282 +    }
  1.1283 +  }
  1.1284 +
  1.1285 +  manager.isWifiTetheringEnabled = function(state) {
  1.1286 +    switch (state) {
  1.1287 +      case "UNINITIALIZED":
  1.1288 +        return false;
  1.1289 +      default:
  1.1290 +        return true;
  1.1291 +    }
  1.1292 +  }
  1.1293 +
  1.1294 +  manager.syncDebug = syncDebug;
  1.1295 +  manager.stateOrdinal = function(state) {
  1.1296 +    return supplicantStatesMap.indexOf(state);
  1.1297 +  }
  1.1298 +  manager.supplicantLoopDetection = function(prevState, state) {
  1.1299 +    var isPrevStateInHandShake = manager.isHandShakeState(prevState);
  1.1300 +    var isStateInHandShake = manager.isHandShakeState(state);
  1.1301 +
  1.1302 +    if (isPrevStateInHandShake) {
  1.1303 +      if (isStateInHandShake) {
  1.1304 +        // Increase the count only if we are in the loop.
  1.1305 +        if (manager.stateOrdinal(state) > manager.stateOrdinal(prevState)) {
  1.1306 +          manager.loopDetectionCount++;
  1.1307 +        }
  1.1308 +        if (manager.loopDetectionCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
  1.1309 +          notify("disconnected", {ssid: manager.connectionInfo.ssid});
  1.1310 +          manager.loopDetectionCount = 0;
  1.1311 +        }
  1.1312 +      }
  1.1313 +    } else {
  1.1314 +      // From others state to HandShake state. Reset the count.
  1.1315 +      if (isStateInHandShake) {
  1.1316 +        manager.loopDetectionCount = 0;
  1.1317 +      }
  1.1318 +    }
  1.1319 +  }
  1.1320 +
  1.1321 +  manager.handlePreWifiScan = function() {
  1.1322 +    if (p2pSupported) {
  1.1323 +      // Before doing regular wifi scan, we have to disable wifi direct
  1.1324 +      // scan first. Otherwise we will never get the scan result.
  1.1325 +      p2pManager.blockScan();
  1.1326 +    }
  1.1327 +  };
  1.1328 +
  1.1329 +  manager.handlePostWifiScan = function() {
  1.1330 +    if (p2pSupported) {
  1.1331 +      // After regular wifi scanning, we should restore the restricted
  1.1332 +      // wifi direct scan.
  1.1333 +      p2pManager.unblockScan();
  1.1334 +    }
  1.1335 +  };
  1.1336 +
  1.1337 +  //
  1.1338 +  // Public APIs for P2P.
  1.1339 +  //
  1.1340 +
  1.1341 +  manager.p2pSupported = function() {
  1.1342 +    return p2pSupported;
  1.1343 +  };
  1.1344 +
  1.1345 +  manager.getP2pManager = function() {
  1.1346 +    return p2pManager;
  1.1347 +  };
  1.1348 +
  1.1349 +  manager.enableP2p = function(callback) {
  1.1350 +    p2pManager.setEnabled(true, {
  1.1351 +      onSupplicantConnected: function() {
  1.1352 +        waitForEvent(WifiP2pManager.INTERFACE_NAME);
  1.1353 +      },
  1.1354 +
  1.1355 +      onEnabled: function(success) {
  1.1356 +        callback(success);
  1.1357 +      }
  1.1358 +    });
  1.1359 +  };
  1.1360 +
  1.1361 +  manager.getCapabilities = function() {
  1.1362 +    return capabilities;
  1.1363 +  }
  1.1364 +
  1.1365 +  return manager;
  1.1366 +})();
  1.1367 +
  1.1368 +// Get unique key for a network, now the key is created by escape(SSID)+Security.
  1.1369 +// So networks of same SSID but different security mode can be identified.
  1.1370 +function getNetworkKey(network)
  1.1371 +{
  1.1372 +  var ssid = "",
  1.1373 +      encryption = "OPEN";
  1.1374 +
  1.1375 +  if ("security" in network) {
  1.1376 +    // manager network object, represents an AP
  1.1377 +    // object structure
  1.1378 +    // {
  1.1379 +    //   .ssid           : SSID of AP
  1.1380 +    //   .security[]     : "WPA-PSK" for WPA-PSK
  1.1381 +    //                     "WPA-EAP" for WPA-EAP
  1.1382 +    //                     "WEP" for WEP
  1.1383 +    //                     "" for OPEN
  1.1384 +    //   other keys
  1.1385 +    // }
  1.1386 +
  1.1387 +    var security = network.security;
  1.1388 +    ssid = network.ssid;
  1.1389 +
  1.1390 +    for (let j = 0; j < security.length; j++) {
  1.1391 +      if (security[j] === "WPA-PSK") {
  1.1392 +        encryption = "WPA-PSK";
  1.1393 +        break;
  1.1394 +      } else if (security[j] === "WPA-EAP") {
  1.1395 +        encryption = "WPA-EAP";
  1.1396 +        break;
  1.1397 +      } else if (security[j] === "WEP") {
  1.1398 +        encryption = "WEP";
  1.1399 +        break;
  1.1400 +      }
  1.1401 +    }
  1.1402 +  } else if ("key_mgmt" in network) {
  1.1403 +    // configure network object, represents a network
  1.1404 +    // object structure
  1.1405 +    // {
  1.1406 +    //   .ssid           : SSID of network, quoted
  1.1407 +    //   .key_mgmt       : Encryption type
  1.1408 +    //                     "WPA-PSK" for WPA-PSK
  1.1409 +    //                     "WPA-EAP" for WPA-EAP
  1.1410 +    //                     "NONE" for WEP/OPEN
  1.1411 +    //   .auth_alg       : Encryption algorithm(WEP mode only)
  1.1412 +    //                     "OPEN_SHARED" for WEP
  1.1413 +    //   other keys
  1.1414 +    // }
  1.1415 +    var key_mgmt = network.key_mgmt,
  1.1416 +        auth_alg = network.auth_alg;
  1.1417 +    ssid = dequote(network.ssid);
  1.1418 +
  1.1419 +    if (key_mgmt == "WPA-PSK") {
  1.1420 +      encryption = "WPA-PSK";
  1.1421 +    } else if (key_mgmt == "WPA-EAP") {
  1.1422 +      encryption = "WPA-EAP";
  1.1423 +    } else if (key_mgmt == "NONE" && auth_alg === "OPEN SHARED") {
  1.1424 +      encryption = "WEP";
  1.1425 +    }
  1.1426 +  }
  1.1427 +
  1.1428 +  // ssid here must be dequoted, and it's safer to esacpe it.
  1.1429 +  // encryption won't be empty and always be assigned one of the followings :
  1.1430 +  // "OPEN"/"WEP"/"WPA-PSK"/"WPA-EAP".
  1.1431 +  // So for a invalid network object, the returned key will be "OPEN".
  1.1432 +  return escape(ssid) + encryption;
  1.1433 +}
  1.1434 +
  1.1435 +function getKeyManagement(flags) {
  1.1436 +  var types = [];
  1.1437 +  if (!flags)
  1.1438 +    return types;
  1.1439 +
  1.1440 +  if (/\[WPA2?-PSK/.test(flags))
  1.1441 +    types.push("WPA-PSK");
  1.1442 +  if (/\[WPA2?-EAP/.test(flags))
  1.1443 +    types.push("WPA-EAP");
  1.1444 +  if (/\[WEP/.test(flags))
  1.1445 +    types.push("WEP");
  1.1446 +  return types;
  1.1447 +}
  1.1448 +
  1.1449 +function getCapabilities(flags) {
  1.1450 +  var types = [];
  1.1451 +  if (!flags)
  1.1452 +    return types;
  1.1453 +
  1.1454 +  if (/\[WPS/.test(flags))
  1.1455 +    types.push("WPS");
  1.1456 +  return types;
  1.1457 +}
  1.1458 +
  1.1459 +// These constants shamelessly ripped from WifiManager.java
  1.1460 +// strength is the value returned by scan_results. It is nominally in dB. We
  1.1461 +// transform it into a percentage for clients looking to simply show a
  1.1462 +// relative indication of the strength of a network.
  1.1463 +const MIN_RSSI = -100;
  1.1464 +const MAX_RSSI = -55;
  1.1465 +
  1.1466 +function calculateSignal(strength) {
  1.1467 +  // Some wifi drivers represent their signal strengths as 8-bit integers, so
  1.1468 +  // in order to avoid negative numbers, they add 256 to the actual values.
  1.1469 +  // While we don't *know* that this is the case here, we make an educated
  1.1470 +  // guess.
  1.1471 +  if (strength > 0)
  1.1472 +    strength -= 256;
  1.1473 +
  1.1474 +  if (strength <= MIN_RSSI)
  1.1475 +    return 0;
  1.1476 +  if (strength >= MAX_RSSI)
  1.1477 +    return 100;
  1.1478 +  return Math.floor(((strength - MIN_RSSI) / (MAX_RSSI - MIN_RSSI)) * 100);
  1.1479 +}
  1.1480 +
  1.1481 +function Network(ssid, security, password, capabilities) {
  1.1482 +  this.ssid = ssid;
  1.1483 +  this.security = security;
  1.1484 +
  1.1485 +  if (typeof password !== "undefined")
  1.1486 +    this.password = password;
  1.1487 +  if (capabilities !== undefined)
  1.1488 +    this.capabilities = capabilities;
  1.1489 +  // TODO connected here as well?
  1.1490 +
  1.1491 +  this.__exposedProps__ = Network.api;
  1.1492 +}
  1.1493 +
  1.1494 +Network.api = {
  1.1495 +  ssid: "r",
  1.1496 +  security: "r",
  1.1497 +  capabilities: "r",
  1.1498 +  known: "r",
  1.1499 +
  1.1500 +  password: "rw",
  1.1501 +  keyManagement: "rw",
  1.1502 +  psk: "rw",
  1.1503 +  identity: "rw",
  1.1504 +  wep: "rw",
  1.1505 +  hidden: "rw",
  1.1506 +  eap: "rw",
  1.1507 +  pin: "rw",
  1.1508 +  phase1: "rw",
  1.1509 +  phase2: "rw"
  1.1510 +};
  1.1511 +
  1.1512 +// Note: We never use ScanResult.prototype, so the fact that it's unrelated to
  1.1513 +// Network.prototype is OK.
  1.1514 +function ScanResult(ssid, bssid, flags, signal) {
  1.1515 +  Network.call(this, ssid, getKeyManagement(flags), undefined,
  1.1516 +               getCapabilities(flags));
  1.1517 +  this.bssid = bssid;
  1.1518 +  this.signalStrength = signal;
  1.1519 +  this.relSignalStrength = calculateSignal(Number(signal));
  1.1520 +
  1.1521 +  this.__exposedProps__ = ScanResult.api;
  1.1522 +}
  1.1523 +
  1.1524 +// XXX This should probably live in the DOM-facing side, but it's hard to do
  1.1525 +// there, so we stick this here.
  1.1526 +ScanResult.api = {
  1.1527 +  bssid: "r",
  1.1528 +  signalStrength: "r",
  1.1529 +  relSignalStrength: "r",
  1.1530 +  connected: "r"
  1.1531 +};
  1.1532 +
  1.1533 +for (let i in Network.api) {
  1.1534 +  ScanResult.api[i] = Network.api[i];
  1.1535 +}
  1.1536 +
  1.1537 +function quote(s) {
  1.1538 +  return '"' + s + '"';
  1.1539 +}
  1.1540 +
  1.1541 +function dequote(s) {
  1.1542 +  if (s[0] != '"' || s[s.length - 1] != '"')
  1.1543 +    throw "Invalid argument, not a quoted string: " + s;
  1.1544 +  return s.substr(1, s.length - 2);
  1.1545 +}
  1.1546 +
  1.1547 +function isWepHexKey(s) {
  1.1548 +  if (s.length != 10 && s.length != 26 && s.length != 58)
  1.1549 +    return false;
  1.1550 +  return !/[^a-fA-F0-9]/.test(s);
  1.1551 +}
  1.1552 +
  1.1553 +
  1.1554 +let WifiNetworkInterface = {
  1.1555 +
  1.1556 +  QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterface]),
  1.1557 +
  1.1558 +  registered: false,
  1.1559 +
  1.1560 +  // nsINetworkInterface
  1.1561 +
  1.1562 +  NETWORK_STATE_UNKNOWN:       Ci.nsINetworkInterface.NETWORK_STATE_UNKNOWN,
  1.1563 +  NETWORK_STATE_CONNECTING:    Ci.nsINetworkInterface.CONNECTING,
  1.1564 +  NETWORK_STATE_CONNECTED:     Ci.nsINetworkInterface.CONNECTED,
  1.1565 +  NETWORK_STATE_DISCONNECTING: Ci.nsINetworkInterface.DISCONNECTING,
  1.1566 +  NETWORK_STATE_DISCONNECTED:  Ci.nsINetworkInterface.DISCONNECTED,
  1.1567 +
  1.1568 +  state: Ci.nsINetworkInterface.NETWORK_STATE_UNKNOWN,
  1.1569 +
  1.1570 +  NETWORK_TYPE_WIFI:        Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
  1.1571 +  NETWORK_TYPE_MOBILE:      Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
  1.1572 +  NETWORK_TYPE_MOBILE_MMS:  Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS,
  1.1573 +  NETWORK_TYPE_MOBILE_SUPL: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL,
  1.1574 +
  1.1575 +  type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
  1.1576 +
  1.1577 +  name: null,
  1.1578 +
  1.1579 +  ips: [],
  1.1580 +
  1.1581 +  prefixLengths: [],
  1.1582 +
  1.1583 +  dnses: [],
  1.1584 +
  1.1585 +  gateways: [],
  1.1586 +
  1.1587 +  httpProxyHost: null,
  1.1588 +
  1.1589 +  httpProxyPort: null,
  1.1590 +
  1.1591 +  getAddresses: function (ips, prefixLengths) {
  1.1592 +    ips.value = this.ips.slice();
  1.1593 +    prefixLengths.value = this.prefixLengths.slice();
  1.1594 +
  1.1595 +    return this.ips.length;
  1.1596 +  },
  1.1597 +
  1.1598 +  getGateways: function (count) {
  1.1599 +    if (count) {
  1.1600 +      count.value = this.gateways.length;
  1.1601 +    }
  1.1602 +    return this.gateways.slice();
  1.1603 +  },
  1.1604 +
  1.1605 +  getDnses: function (count) {
  1.1606 +    if (count) {
  1.1607 +      count.value = this.dnses.length;
  1.1608 +    }
  1.1609 +    return this.dnses.slice();
  1.1610 +  }
  1.1611 +};
  1.1612 +
  1.1613 +function WifiScanResult() {}
  1.1614 +
  1.1615 +// TODO Make the difference between a DOM-based network object and our
  1.1616 +// networks objects much clearer.
  1.1617 +let netToDOM;
  1.1618 +let netFromDOM;
  1.1619 +
  1.1620 +function WifiWorker() {
  1.1621 +  var self = this;
  1.1622 +
  1.1623 +  this._mm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
  1.1624 +               .getService(Ci.nsIMessageListenerManager);
  1.1625 +  const messages = ["WifiManager:getNetworks", "WifiManager:getKnownNetworks",
  1.1626 +                    "WifiManager:associate", "WifiManager:forget",
  1.1627 +                    "WifiManager:wps", "WifiManager:getState",
  1.1628 +                    "WifiManager:setPowerSavingMode",
  1.1629 +                    "WifiManager:setHttpProxy",
  1.1630 +                    "WifiManager:setStaticIpMode",
  1.1631 +                    "child-process-shutdown"];
  1.1632 +
  1.1633 +  messages.forEach((function(msgName) {
  1.1634 +    this._mm.addMessageListener(msgName, this);
  1.1635 +  }).bind(this));
  1.1636 +
  1.1637 +  Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
  1.1638 +
  1.1639 +  this.wantScanResults = [];
  1.1640 +
  1.1641 +  this._allowWpaEap = false;
  1.1642 +  this._needToEnableNetworks = false;
  1.1643 +  this._highestPriority = -1;
  1.1644 +
  1.1645 +  // Networks is a map from SSID -> a scan result.
  1.1646 +  this.networks = Object.create(null);
  1.1647 +
  1.1648 +  // ConfiguredNetworks is a map from SSID -> our view of a network. It only
  1.1649 +  // lists networks known to the wpa_supplicant. The SSID field (and other
  1.1650 +  // fields) are quoted for ease of use with WifiManager commands.
  1.1651 +  // Note that we don't have to worry about escaping embedded quotes since in
  1.1652 +  // all cases, the supplicant will take the last quotation that we pass it as
  1.1653 +  // the end of the string.
  1.1654 +  this.configuredNetworks = Object.create(null);
  1.1655 +  this._addingNetworks = Object.create(null);
  1.1656 +
  1.1657 +  this.currentNetwork = null;
  1.1658 +  this.ipAddress = "";
  1.1659 +  this.macAddress = null;
  1.1660 +
  1.1661 +  this._lastConnectionInfo = null;
  1.1662 +  this._connectionInfoTimer = null;
  1.1663 +  this._reconnectOnDisconnect = false;
  1.1664 +
  1.1665 +  // Create p2pObserver and assign to p2pManager.
  1.1666 +  if (WifiManager.p2pSupported()) {
  1.1667 +    this._p2pObserver = WifiP2pWorkerObserver(WifiManager.getP2pManager());
  1.1668 +    WifiManager.getP2pManager().setObserver(this._p2pObserver);
  1.1669 +
  1.1670 +    // Add DOM message observerd by p2pObserver to the message listener as well.
  1.1671 +    this._p2pObserver.getObservedDOMMessages().forEach((function(msgName) {
  1.1672 +      this._mm.addMessageListener(msgName, this);
  1.1673 +    }).bind(this));
  1.1674 +  }
  1.1675 +
  1.1676 +  // Users of instances of nsITimer should keep a reference to the timer until
  1.1677 +  // it is no longer needed in order to assure the timer is fired.
  1.1678 +  this._callbackTimer = null;
  1.1679 +
  1.1680 +  // XXX On some phones (Otoro and Unagi) the wifi driver doesn't play nicely
  1.1681 +  // with the automatic scans that wpa_supplicant does (it appears that the
  1.1682 +  // driver forgets that it's returned scan results and then refuses to try to
  1.1683 +  // rescan. In order to detect this case we start a timer when we enter the
  1.1684 +  // SCANNING state and reset it whenever we either get scan results or leave
  1.1685 +  // the SCANNING state. If the timer fires, we assume that we are stuck and
  1.1686 +  // forceably try to unstick the supplican, also turning on background
  1.1687 +  // scanning to avoid having to constantly poke the supplicant.
  1.1688 +
  1.1689 +  // How long we wait is controlled by the SCAN_STUCK_WAIT constant.
  1.1690 +  const SCAN_STUCK_WAIT = 12000;
  1.1691 +  this._scanStuckTimer = null;
  1.1692 +  this._turnOnBackgroundScan = false;
  1.1693 +
  1.1694 +  function startScanStuckTimer() {
  1.1695 +    if (WifiManager.schedScanRecovery) {
  1.1696 +      self._scanStuckTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  1.1697 +      self._scanStuckTimer.initWithCallback(scanIsStuck, SCAN_STUCK_WAIT,
  1.1698 +                                            Ci.nsITimer.TYPE_ONE_SHOT);
  1.1699 +    }
  1.1700 +  }
  1.1701 +
  1.1702 +  function scanIsStuck() {
  1.1703 +    // Uh-oh, we've waited too long for scan results. Disconnect (which
  1.1704 +    // guarantees that we leave the SCANNING state and tells wpa_supplicant to
  1.1705 +    // wait for our next command) ensure that background scanning is on and
  1.1706 +    // then try again.
  1.1707 +    debug("Determined that scanning is stuck, turning on background scanning!");
  1.1708 +    WifiManager.handlePostWifiScan();
  1.1709 +    WifiManager.disconnect(function(ok) {});
  1.1710 +    self._turnOnBackgroundScan = true;
  1.1711 +  }
  1.1712 +
  1.1713 +  // A list of requests to turn wifi on or off.
  1.1714 +  this._stateRequests = [];
  1.1715 +
  1.1716 +  // Given a connection status network, takes a network from
  1.1717 +  // self.configuredNetworks and prepares it for the DOM.
  1.1718 +  netToDOM = function(net) {
  1.1719 +    var ssid = dequote(net.ssid);
  1.1720 +    var security = (net.key_mgmt === "NONE" && net.wep_key0) ? ["WEP"] :
  1.1721 +                   (net.key_mgmt && net.key_mgmt !== "NONE") ? [net.key_mgmt] :
  1.1722 +                   [];
  1.1723 +    var password;
  1.1724 +    if (("psk" in net && net.psk) ||
  1.1725 +        ("password" in net && net.password) ||
  1.1726 +        ("wep_key0" in net && net.wep_key0)) {
  1.1727 +      password = "*";
  1.1728 +    }
  1.1729 +
  1.1730 +    var pub = new Network(ssid, security, password);
  1.1731 +    if (net.identity)
  1.1732 +      pub.identity = dequote(net.identity);
  1.1733 +    if (net.netId)
  1.1734 +      pub.known = true;
  1.1735 +    if (net.scan_ssid === 1)
  1.1736 +      pub.hidden = true;
  1.1737 +    return pub;
  1.1738 +  };
  1.1739 +
  1.1740 +  netFromDOM = function(net, configured) {
  1.1741 +    // Takes a network from the DOM and makes it suitable for insertion into
  1.1742 +    // self.configuredNetworks (that is calling addNetwork will do the right
  1.1743 +    // thing).
  1.1744 +    // NB: Modifies net in place: safe since we don't share objects between
  1.1745 +    // the dom and the chrome code.
  1.1746 +
  1.1747 +    // Things that are useful for the UI but not to us.
  1.1748 +    delete net.bssid;
  1.1749 +    delete net.signalStrength;
  1.1750 +    delete net.relSignalStrength;
  1.1751 +    delete net.security;
  1.1752 +    delete net.capabilities;
  1.1753 +
  1.1754 +    if (!configured)
  1.1755 +      configured = {};
  1.1756 +
  1.1757 +    net.ssid = quote(net.ssid);
  1.1758 +
  1.1759 +    let wep = false;
  1.1760 +    if ("keyManagement" in net) {
  1.1761 +      if (net.keyManagement === "WEP") {
  1.1762 +        wep = true;
  1.1763 +        net.keyManagement = "NONE";
  1.1764 +      }
  1.1765 +
  1.1766 +      configured.key_mgmt = net.key_mgmt = net.keyManagement; // WPA2-PSK, WPA-PSK, etc.
  1.1767 +      delete net.keyManagement;
  1.1768 +    } else {
  1.1769 +      configured.key_mgmt = net.key_mgmt = "NONE";
  1.1770 +    }
  1.1771 +
  1.1772 +    if (net.hidden) {
  1.1773 +      configured.scan_ssid = net.scan_ssid = 1;
  1.1774 +      delete net.hidden;
  1.1775 +    }
  1.1776 +
  1.1777 +    function checkAssign(name, checkStar) {
  1.1778 +      if (name in net) {
  1.1779 +        let value = net[name];
  1.1780 +        if (!value || (checkStar && value === '*')) {
  1.1781 +          if (name in configured)
  1.1782 +            net[name] = configured[name];
  1.1783 +          else
  1.1784 +            delete net[name];
  1.1785 +        } else {
  1.1786 +          configured[name] = net[name] = quote(value);
  1.1787 +        }
  1.1788 +      }
  1.1789 +    }
  1.1790 +
  1.1791 +    checkAssign("psk", true);
  1.1792 +    checkAssign("identity", false);
  1.1793 +    checkAssign("password", true);
  1.1794 +    if (wep && net.wep && net.wep != '*') {
  1.1795 +      configured.wep_key0 = net.wep_key0 = isWepHexKey(net.wep) ? net.wep : quote(net.wep);
  1.1796 +      configured.auth_alg = net.auth_alg = "OPEN SHARED";
  1.1797 +    }
  1.1798 +
  1.1799 +    if ("pin" in net) {
  1.1800 +      net.pin = quote(net.pin);
  1.1801 +    }
  1.1802 +
  1.1803 +    if ("phase1" in net)
  1.1804 +      net.phase1 = quote(net.phase1);
  1.1805 +
  1.1806 +    if ("phase2" in net)
  1.1807 +      net.phase2 = quote(net.phase2);
  1.1808 +
  1.1809 +    return net;
  1.1810 +  };
  1.1811 +
  1.1812 +  WifiManager.onsupplicantconnection = function() {
  1.1813 +    debug("Connected to supplicant");
  1.1814 +    WifiManager.enabled = true;
  1.1815 +    self._reloadConfiguredNetworks(function(ok) {
  1.1816 +      // Prime this.networks.
  1.1817 +      if (!ok)
  1.1818 +        return;
  1.1819 +
  1.1820 +      self.waitForScan(function firstScan() {});
  1.1821 +      // The select network command we used in associate() disables others networks.
  1.1822 +      // Enable them here to make sure wpa_supplicant helps to connect to known
  1.1823 +      // network automatically.
  1.1824 +      self._enableAllNetworks();
  1.1825 +      WifiManager.saveConfig(function() {})
  1.1826 +    });
  1.1827 +
  1.1828 +    try {
  1.1829 +      self._allowWpaEap = WifiManager.getCapabilities().eapSim;
  1.1830 +    } catch (e) {
  1.1831 +      self._allowWpaEap = false;
  1.1832 +    }
  1.1833 +
  1.1834 +    // Notify everybody, even if they didn't ask us to come up.
  1.1835 +    WifiManager.getMacAddress(function (mac) {
  1.1836 +      self.macAddress = mac;
  1.1837 +      debug("Got mac: " + mac);
  1.1838 +      self._fireEvent("wifiUp", { macAddress: mac });
  1.1839 +      self.requestDone();
  1.1840 +    });
  1.1841 +
  1.1842 +    if (WifiManager.state === "SCANNING")
  1.1843 +      startScanStuckTimer();
  1.1844 +  };
  1.1845 +
  1.1846 +  WifiManager.onsupplicantlost = function() {
  1.1847 +    WifiManager.enabled = WifiManager.supplicantStarted = false;
  1.1848 +    WifiManager.state = "UNINITIALIZED";
  1.1849 +    debug("Supplicant died!");
  1.1850 +
  1.1851 +    // Notify everybody, even if they didn't ask us to come up.
  1.1852 +    self._fireEvent("wifiDown", {});
  1.1853 +    self.requestDone();
  1.1854 +  };
  1.1855 +
  1.1856 +  WifiManager.onpasswordmaybeincorrect = function() {
  1.1857 +    WifiManager.authenticationFailuresCount++;
  1.1858 +  };
  1.1859 +
  1.1860 +  WifiManager.ondisconnected = function() {
  1.1861 +    // We may fail to establish the connection, re-enable the
  1.1862 +    // rest of our networks.
  1.1863 +    if (self._needToEnableNetworks) {
  1.1864 +      self._enableAllNetworks();
  1.1865 +      self._needToEnableNetworks = false;
  1.1866 +    }
  1.1867 +
  1.1868 +    WifiManager.getCurrentNetworkId(this.ssid, function(netId) {
  1.1869 +      // Trying to get netId from current network.
  1.1870 +      if (!netId &&
  1.1871 +          self.currentNetwork &&
  1.1872 +          typeof self.currentNetwork.netId !== "undefined") {
  1.1873 +        netId = self.currentNetwork.netId;
  1.1874 +      }
  1.1875 +      if (netId) {
  1.1876 +        WifiManager.disableNetwork(netId, function() {});
  1.1877 +      }
  1.1878 +    });
  1.1879 +    self._fireEvent("onconnectingfailed", {network: self.currentNetwork});
  1.1880 +  }
  1.1881 +
  1.1882 +  WifiManager.onstatechange = function() {
  1.1883 +    debug("State change: " + this.prevState + " -> " + this.state);
  1.1884 +
  1.1885 +    if (self._connectionInfoTimer &&
  1.1886 +        this.state !== "CONNECTED" &&
  1.1887 +        this.state !== "COMPLETED") {
  1.1888 +      self._stopConnectionInfoTimer();
  1.1889 +    }
  1.1890 +
  1.1891 +    if (this.state !== "SCANNING" &&
  1.1892 +        self._scanStuckTimer) {
  1.1893 +      self._scanStuckTimer.cancel();
  1.1894 +      self._scanStuckTimer = null;
  1.1895 +    }
  1.1896 +
  1.1897 +    switch (this.state) {
  1.1898 +      case "DORMANT":
  1.1899 +        // The dormant state is a bad state to be in since we won't
  1.1900 +        // automatically connect. Try to knock us out of it. We only
  1.1901 +        // hit this state when we've failed to run DHCP, so trying
  1.1902 +        // again isn't the worst thing we can do. Eventually, we'll
  1.1903 +        // need to detect if we're looping in this state and bail out.
  1.1904 +        WifiManager.reconnect(function(){});
  1.1905 +        break;
  1.1906 +      case "ASSOCIATING":
  1.1907 +        // id has not yet been filled in, so we can only report the ssid and
  1.1908 +        // bssid.
  1.1909 +        self.currentNetwork =
  1.1910 +          { bssid: WifiManager.connectionInfo.bssid,
  1.1911 +            ssid: quote(WifiManager.connectionInfo.ssid) };
  1.1912 +        self._fireEvent("onconnecting", { network: netToDOM(self.currentNetwork) });
  1.1913 +        break;
  1.1914 +      case "ASSOCIATED":
  1.1915 +        if (!self.currentNetwork) {
  1.1916 +          self.currentNetwork =
  1.1917 +            { bssid: WifiManager.connectionInfo.bssid,
  1.1918 +              ssid: quote(WifiManager.connectionInfo.ssid) };
  1.1919 +        }
  1.1920 +
  1.1921 +        self.currentNetwork.netId = this.id;
  1.1922 +        WifiManager.getNetworkConfiguration(self.currentNetwork, function (){});
  1.1923 +        break;
  1.1924 +      case "COMPLETED":
  1.1925 +        // Now that we've successfully completed the connection, re-enable the
  1.1926 +        // rest of our networks.
  1.1927 +        // XXX Need to do this eventually if the user entered an incorrect
  1.1928 +        // password. For now, we require user interaction to break the loop and
  1.1929 +        // select a better network!
  1.1930 +        if (self._needToEnableNetworks) {
  1.1931 +          self._enableAllNetworks();
  1.1932 +          self._needToEnableNetworks = false;
  1.1933 +        }
  1.1934 +
  1.1935 +        // We get the ASSOCIATED event when we've associated but not connected, so
  1.1936 +        // wait until the handshake is complete.
  1.1937 +        if (this.fromStatus || !self.currentNetwork) {
  1.1938 +          // In this case, we connected to an already-connected wpa_supplicant,
  1.1939 +          // because of that we need to gather information about the current
  1.1940 +          // network here.
  1.1941 +          self.currentNetwork = { ssid: quote(WifiManager.connectionInfo.ssid),
  1.1942 +                                  netId: WifiManager.connectionInfo.id };
  1.1943 +          WifiManager.getNetworkConfiguration(self.currentNetwork, function(){});
  1.1944 +        }
  1.1945 +
  1.1946 +        // Update http proxy when connected to network.
  1.1947 +        let netConnect = WifiManager.getHttpProxyNetwork(self.currentNetwork);
  1.1948 +        if (netConnect)
  1.1949 +          WifiManager.setHttpProxy(netConnect);
  1.1950 +
  1.1951 +        // The full authentication process is completed, reset the count.
  1.1952 +        WifiManager.authenticationFailuresCount = 0;
  1.1953 +        WifiManager.loopDetectionCount = 0;
  1.1954 +        self._startConnectionInfoTimer();
  1.1955 +        self._fireEvent("onassociate", { network: netToDOM(self.currentNetwork) });
  1.1956 +        break;
  1.1957 +      case "CONNECTED":
  1.1958 +        // BSSID is read after connected, update it.
  1.1959 +        self.currentNetwork.bssid = WifiManager.connectionInfo.bssid;
  1.1960 +        break;
  1.1961 +      case "DISCONNECTED":
  1.1962 +        // wpa_supplicant may give us a "DISCONNECTED" event even if
  1.1963 +        // we are already in "DISCONNECTED" state.
  1.1964 +        if ((WifiNetworkInterface.state ===
  1.1965 +             Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED) &&
  1.1966 +             (this.prevState === "INITIALIZING" ||
  1.1967 +              this.prevState === "DISCONNECTED" ||
  1.1968 +              this.prevState === "INTERFACE_DISABLED" ||
  1.1969 +              this.prevState === "INACTIVE" ||
  1.1970 +              this.prevState === "UNINITIALIZED")) {
  1.1971 +          return;
  1.1972 +        }
  1.1973 +
  1.1974 +        self._fireEvent("ondisconnect", {});
  1.1975 +
  1.1976 +        // When disconnected, clear the http proxy setting if it exists.
  1.1977 +        // Temporarily set http proxy to empty and restore user setting after setHttpProxy.
  1.1978 +        let netDisconnect = WifiManager.getHttpProxyNetwork(self.currentNetwork);
  1.1979 +        if (netDisconnect) {
  1.1980 +          let prehttpProxyHostSetting = netDisconnect.httpProxyHost;
  1.1981 +          let prehttpProxyPortSetting = netDisconnect.httpProxyPort;
  1.1982 +
  1.1983 +          netDisconnect.httpProxyHost = "";
  1.1984 +          netDisconnect.httpProxyPort = 0;
  1.1985 +
  1.1986 +          WifiManager.setHttpProxy(netDisconnect);
  1.1987 +
  1.1988 +          netDisconnect.httpProxyHost = prehttpProxyHostSetting;
  1.1989 +          netDisconnect.httpProxyPort = prehttpProxyPortSetting;
  1.1990 +        }
  1.1991 +
  1.1992 +        self.currentNetwork = null;
  1.1993 +        self.ipAddress = "";
  1.1994 +
  1.1995 +        if (self._turnOnBackgroundScan) {
  1.1996 +          self._turnOnBackgroundScan = false;
  1.1997 +          WifiManager.setBackgroundScan("ON", function(did_something, ok) {
  1.1998 +            WifiManager.reassociate(function() {});
  1.1999 +          });
  1.2000 +        }
  1.2001 +
  1.2002 +        WifiManager.connectionDropped(function() {
  1.2003 +          // We've disconnected from a network because of a call to forgetNetwork.
  1.2004 +          // Reconnect to the next available network (if any).
  1.2005 +          if (self._reconnectOnDisconnect) {
  1.2006 +            self._reconnectOnDisconnect = false;
  1.2007 +            WifiManager.reconnect(function(){});
  1.2008 +          }
  1.2009 +        });
  1.2010 +
  1.2011 +        WifiNetworkInterface.state =
  1.2012 +          Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED;
  1.2013 +        WifiNetworkInterface.ips = [];
  1.2014 +        WifiNetworkInterface.prefixLengths = [];
  1.2015 +        WifiNetworkInterface.gateways = [];
  1.2016 +        WifiNetworkInterface.dnses = [];
  1.2017 +        Services.obs.notifyObservers(WifiNetworkInterface,
  1.2018 +                                     kNetworkInterfaceStateChangedTopic,
  1.2019 +                                     null);
  1.2020 +
  1.2021 +        break;
  1.2022 +      case "WPS_TIMEOUT":
  1.2023 +        self._fireEvent("onwpstimeout", {});
  1.2024 +        break;
  1.2025 +      case "WPS_FAIL":
  1.2026 +        self._fireEvent("onwpsfail", {});
  1.2027 +        break;
  1.2028 +      case "WPS_OVERLAP_DETECTED":
  1.2029 +        self._fireEvent("onwpsoverlap", {});
  1.2030 +        break;
  1.2031 +      case "SCANNING":
  1.2032 +        // If we're already scanning in the background, we don't need to worry
  1.2033 +        // about getting stuck while scanning.
  1.2034 +        if (!WifiManager.backgroundScanEnabled && WifiManager.enabled)
  1.2035 +          startScanStuckTimer();
  1.2036 +        break;
  1.2037 +    }
  1.2038 +  };
  1.2039 +
  1.2040 +  WifiManager.onnetworkconnected = function() {
  1.2041 +    if (!this.info || !this.info.ipaddr_str) {
  1.2042 +      debug("Network information is invalid.");
  1.2043 +      return;
  1.2044 +    }
  1.2045 +
  1.2046 +    let maskLength =
  1.2047 +      netHelpers.getMaskLength(netHelpers.stringToIP(this.info.mask_str));
  1.2048 +    if (!maskLength) {
  1.2049 +      maskLength = 32; // max prefix for IPv4.
  1.2050 +    }
  1.2051 +    WifiNetworkInterface.state =
  1.2052 +      Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED;
  1.2053 +    WifiNetworkInterface.ips = [this.info.ipaddr_str];
  1.2054 +    WifiNetworkInterface.prefixLengths = [maskLength];
  1.2055 +    WifiNetworkInterface.gateways = [this.info.gateway_str];
  1.2056 +    if (typeof this.info.dns1_str == "string" &&
  1.2057 +        this.info.dns1_str.length) {
  1.2058 +      WifiNetworkInterface.dnses.push(this.info.dns1_str);
  1.2059 +    }
  1.2060 +    if (typeof this.info.dns2_str == "string" &&
  1.2061 +        this.info.dns2_str.length) {
  1.2062 +      WifiNetworkInterface.dnses.push(this.info.dns2_str);
  1.2063 +    }
  1.2064 +    Services.obs.notifyObservers(WifiNetworkInterface,
  1.2065 +                                 kNetworkInterfaceStateChangedTopic,
  1.2066 +                                 null);
  1.2067 +
  1.2068 +    self.ipAddress = this.info.ipaddr_str;
  1.2069 +
  1.2070 +    // We start the connection information timer when we associate, but
  1.2071 +    // don't have our IP address until here. Make sure that we fire a new
  1.2072 +    // connectionInformation event with the IP address the next time the
  1.2073 +    // timer fires.
  1.2074 +    self._lastConnectionInfo = null;
  1.2075 +    self._fireEvent("onconnect", { network: netToDOM(self.currentNetwork) });
  1.2076 +  };
  1.2077 +
  1.2078 +  WifiManager.onscanresultsavailable = function() {
  1.2079 +    if (self._scanStuckTimer) {
  1.2080 +      // We got scan results! We must not be stuck for now, try again.
  1.2081 +      self._scanStuckTimer.cancel();
  1.2082 +      self._scanStuckTimer.initWithCallback(scanIsStuck, SCAN_STUCK_WAIT,
  1.2083 +                                            Ci.nsITimer.TYPE_ONE_SHOT);
  1.2084 +    }
  1.2085 +
  1.2086 +    if (self.wantScanResults.length === 0) {
  1.2087 +      debug("Scan results available, but we don't need them");
  1.2088 +      return;
  1.2089 +    }
  1.2090 +
  1.2091 +    debug("Scan results are available! Asking for them.");
  1.2092 +    WifiManager.getScanResults(function(r) {
  1.2093 +      // Failure.
  1.2094 +      if (!r) {
  1.2095 +        self.wantScanResults.forEach(function(callback) { callback(null) });
  1.2096 +        self.wantScanResults = [];
  1.2097 +        return;
  1.2098 +      }
  1.2099 +
  1.2100 +      // Now that we have scan results, there's no more need to continue
  1.2101 +      // scanning. Ignore any errors from this command.
  1.2102 +      WifiManager.setScanMode("inactive", function() {});
  1.2103 +      let lines = r.split("\n");
  1.2104 +      // NB: Skip the header line.
  1.2105 +      self.networksArray = [];
  1.2106 +      for (let i = 1; i < lines.length; ++i) {
  1.2107 +        // bssid / frequency / signal level / flags / ssid
  1.2108 +        var match = /([\S]+)\s+([\S]+)\s+([\S]+)\s+(\[[\S]+\])?\s(.*)/.exec(lines[i]);
  1.2109 +
  1.2110 +        if (match && match[5]) {
  1.2111 +          let ssid = match[5],
  1.2112 +              bssid = match[1],
  1.2113 +              signalLevel = match[3],
  1.2114 +              flags = match[4];
  1.2115 +
  1.2116 +          // Skip ad-hoc networks which aren't supported (bug 811635).
  1.2117 +          if (flags && flags.indexOf("[IBSS]") >= 0)
  1.2118 +            continue;
  1.2119 +
  1.2120 +          // If this is the first time that we've seen this SSID in the scan
  1.2121 +          // results, add it to the list along with any other information.
  1.2122 +          // Also, we use the highest signal strength that we see.
  1.2123 +          let network = new ScanResult(ssid, bssid, flags, signalLevel);
  1.2124 +
  1.2125 +          let networkKey = getNetworkKey(network);
  1.2126 +          let eapIndex = -1;
  1.2127 +          if (networkKey in self.configuredNetworks) {
  1.2128 +            let known = self.configuredNetworks[networkKey];
  1.2129 +            network.known = true;
  1.2130 +
  1.2131 +            if ("identity" in known && known.identity)
  1.2132 +              network.identity = dequote(known.identity);
  1.2133 +
  1.2134 +            // Note: we don't hand out passwords here! The * marks that there
  1.2135 +            // is a password that we're hiding.
  1.2136 +            if (("psk" in known && known.psk) ||
  1.2137 +                ("password" in known && known.password) ||
  1.2138 +                ("wep_key0" in known && known.wep_key0)) {
  1.2139 +              network.password = "*";
  1.2140 +            }
  1.2141 +          } else if (!self._allowWpaEap &&
  1.2142 +                     (eapIndex = network.security.indexOf("WPA-EAP")) >= 0) {
  1.2143 +            // Don't offer to connect to WPA-EAP networks unless one has been
  1.2144 +            // configured through other means (e.g. it was added directly to
  1.2145 +            // wpa_supplicant.conf). Here, we have an unknown WPA-EAP network,
  1.2146 +            // so we ignore it entirely if it only supports WPA-EAP, otherwise
  1.2147 +            // we take EAP out of the list and offer the rest of the
  1.2148 +            // security.
  1.2149 +            if (network.security.length === 1)
  1.2150 +              continue;
  1.2151 +
  1.2152 +            network.security.splice(eapIndex, 1);
  1.2153 +          }
  1.2154 +
  1.2155 +          self.networksArray.push(network);
  1.2156 +          if (network.bssid === WifiManager.connectionInfo.bssid)
  1.2157 +            network.connected = true;
  1.2158 +
  1.2159 +          let signal = calculateSignal(Number(match[3]));
  1.2160 +          if (signal > network.relSignalStrength)
  1.2161 +            network.relSignalStrength = signal;
  1.2162 +        } else if (!match) {
  1.2163 +          debug("Match didn't find anything for: " + lines[i]);
  1.2164 +        }
  1.2165 +      }
  1.2166 +
  1.2167 +      self.wantScanResults.forEach(function(callback) { callback(self.networksArray) });
  1.2168 +      self.wantScanResults = [];
  1.2169 +    });
  1.2170 +  };
  1.2171 +
  1.2172 +  // Read the 'wifi.enabled' setting in order to start with a known
  1.2173 +  // value at boot time. The handle() will be called after reading.
  1.2174 +  //
  1.2175 +  // nsISettingsServiceCallback implementation.
  1.2176 +  var initWifiEnabledCb = {
  1.2177 +    handle: function handle(aName, aResult) {
  1.2178 +      if (aName !== SETTINGS_WIFI_ENABLED)
  1.2179 +        return;
  1.2180 +      if (aResult === null)
  1.2181 +        aResult = true;
  1.2182 +      self.handleWifiEnabled(aResult);
  1.2183 +    },
  1.2184 +    handleError: function handleError(aErrorMessage) {
  1.2185 +      debug("Error reading the 'wifi.enabled' setting. Default to wifi on.");
  1.2186 +      self.handleWifiEnabled(true);
  1.2187 +    }
  1.2188 +  };
  1.2189 +
  1.2190 +  var initWifiDebuggingEnabledCb = {
  1.2191 +    handle: function handle(aName, aResult) {
  1.2192 +      if (aName !== SETTINGS_WIFI_DEBUG_ENABLED)
  1.2193 +        return;
  1.2194 +      if (aResult === null)
  1.2195 +        aResult = false;
  1.2196 +      DEBUG = aResult;
  1.2197 +      updateDebug();
  1.2198 +    },
  1.2199 +    handleError: function handleError(aErrorMessage) {
  1.2200 +      debug("Error reading the 'wifi.debugging.enabled' setting. Default to debugging off.");
  1.2201 +      DEBUG = false;
  1.2202 +      updateDebug();
  1.2203 +    }
  1.2204 +  };
  1.2205 +
  1.2206 +  this.initTetheringSettings();
  1.2207 +
  1.2208 +  let lock = gSettingsService.createLock();
  1.2209 +  lock.get(SETTINGS_WIFI_ENABLED, initWifiEnabledCb);
  1.2210 +  lock.get(SETTINGS_WIFI_DEBUG_ENABLED, initWifiDebuggingEnabledCb);
  1.2211 +
  1.2212 +  lock.get(SETTINGS_WIFI_SSID, this);
  1.2213 +  lock.get(SETTINGS_WIFI_SECURITY_TYPE, this);
  1.2214 +  lock.get(SETTINGS_WIFI_SECURITY_PASSWORD, this);
  1.2215 +  lock.get(SETTINGS_WIFI_IP, this);
  1.2216 +  lock.get(SETTINGS_WIFI_PREFIX, this);
  1.2217 +  lock.get(SETTINGS_WIFI_DHCPSERVER_STARTIP, this);
  1.2218 +  lock.get(SETTINGS_WIFI_DHCPSERVER_ENDIP, this);
  1.2219 +  lock.get(SETTINGS_WIFI_DNS1, this);
  1.2220 +  lock.get(SETTINGS_WIFI_DNS2, this);
  1.2221 +  lock.get(SETTINGS_WIFI_TETHERING_ENABLED, this);
  1.2222 +
  1.2223 +  lock.get(SETTINGS_USB_DHCPSERVER_STARTIP, this);
  1.2224 +  lock.get(SETTINGS_USB_DHCPSERVER_ENDIP, this);
  1.2225 +
  1.2226 +  this._wifiTetheringSettingsToRead = [SETTINGS_WIFI_SSID,
  1.2227 +                                       SETTINGS_WIFI_SECURITY_TYPE,
  1.2228 +                                       SETTINGS_WIFI_SECURITY_PASSWORD,
  1.2229 +                                       SETTINGS_WIFI_IP,
  1.2230 +                                       SETTINGS_WIFI_PREFIX,
  1.2231 +                                       SETTINGS_WIFI_DHCPSERVER_STARTIP,
  1.2232 +                                       SETTINGS_WIFI_DHCPSERVER_ENDIP,
  1.2233 +                                       SETTINGS_WIFI_DNS1,
  1.2234 +                                       SETTINGS_WIFI_DNS2,
  1.2235 +                                       SETTINGS_WIFI_TETHERING_ENABLED,
  1.2236 +                                       SETTINGS_USB_DHCPSERVER_STARTIP,
  1.2237 +                                       SETTINGS_USB_DHCPSERVER_ENDIP];
  1.2238 +}
  1.2239 +
  1.2240 +function translateState(state) {
  1.2241 +  switch (state) {
  1.2242 +    case "INTERFACE_DISABLED":
  1.2243 +    case "INACTIVE":
  1.2244 +    case "SCANNING":
  1.2245 +    case "DISCONNECTED":
  1.2246 +    default:
  1.2247 +      return "disconnected";
  1.2248 +
  1.2249 +    case "AUTHENTICATING":
  1.2250 +    case "ASSOCIATING":
  1.2251 +    case "ASSOCIATED":
  1.2252 +    case "FOUR_WAY_HANDSHAKE":
  1.2253 +    case "GROUP_HANDSHAKE":
  1.2254 +      return "connecting";
  1.2255 +
  1.2256 +    case "COMPLETED":
  1.2257 +      return WifiManager.getDhcpInfo() ? "connected" : "associated";
  1.2258 +  }
  1.2259 +}
  1.2260 +
  1.2261 +WifiWorker.prototype = {
  1.2262 +  classID:   WIFIWORKER_CID,
  1.2263 +  classInfo: XPCOMUtils.generateCI({classID: WIFIWORKER_CID,
  1.2264 +                                    contractID: WIFIWORKER_CONTRACTID,
  1.2265 +                                    classDescription: "WifiWorker",
  1.2266 +                                    interfaces: [Ci.nsIWorkerHolder,
  1.2267 +                                                 Ci.nsIWifi,
  1.2268 +                                                 Ci.nsIObserver]}),
  1.2269 +
  1.2270 +  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWorkerHolder,
  1.2271 +                                         Ci.nsIWifi,
  1.2272 +                                         Ci.nsISettingsServiceCallback]),
  1.2273 +
  1.2274 +  disconnectedByWifi: false,
  1.2275 +
  1.2276 +  disconnectedByWifiTethering: false,
  1.2277 +
  1.2278 +  _wifiTetheringSettingsToRead: [],
  1.2279 +
  1.2280 +  _oldWifiTetheringEnabledState: null,
  1.2281 +
  1.2282 +  tetheringSettings: {},
  1.2283 +
  1.2284 +  initTetheringSettings: function initTetheringSettings() {
  1.2285 +    this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] = null;
  1.2286 +    this.tetheringSettings[SETTINGS_WIFI_SSID] = DEFAULT_WIFI_SSID;
  1.2287 +    this.tetheringSettings[SETTINGS_WIFI_SECURITY_TYPE] = DEFAULT_WIFI_SECURITY_TYPE;
  1.2288 +    this.tetheringSettings[SETTINGS_WIFI_SECURITY_PASSWORD] = DEFAULT_WIFI_SECURITY_PASSWORD;
  1.2289 +    this.tetheringSettings[SETTINGS_WIFI_IP] = DEFAULT_WIFI_IP;
  1.2290 +    this.tetheringSettings[SETTINGS_WIFI_PREFIX] = DEFAULT_WIFI_PREFIX;
  1.2291 +    this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP] = DEFAULT_WIFI_DHCPSERVER_STARTIP;
  1.2292 +    this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP] = DEFAULT_WIFI_DHCPSERVER_ENDIP;
  1.2293 +    this.tetheringSettings[SETTINGS_WIFI_DNS1] = DEFAULT_DNS1;
  1.2294 +    this.tetheringSettings[SETTINGS_WIFI_DNS2] = DEFAULT_DNS2;
  1.2295 +
  1.2296 +    this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP] = DEFAULT_USB_DHCPSERVER_STARTIP;
  1.2297 +    this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP] = DEFAULT_USB_DHCPSERVER_ENDIP;
  1.2298 +  },
  1.2299 +
  1.2300 +  // Internal methods.
  1.2301 +  waitForScan: function(callback) {
  1.2302 +    this.wantScanResults.push(callback);
  1.2303 +  },
  1.2304 +
  1.2305 +  // In order to select a specific network, we disable the rest of the
  1.2306 +  // networks known to us. However, in general, we want the supplicant to
  1.2307 +  // connect to which ever network it thinks is best, so when we select the
  1.2308 +  // proper network (or fail to), we need to re-enable the rest.
  1.2309 +  _enableAllNetworks: function() {
  1.2310 +    for each (let net in this.configuredNetworks) {
  1.2311 +      WifiManager.enableNetwork(net.netId, false, function(ok) {
  1.2312 +        net.disabled = ok ? 1 : 0;
  1.2313 +      });
  1.2314 +    }
  1.2315 +  },
  1.2316 +
  1.2317 +  _startConnectionInfoTimer: function() {
  1.2318 +    if (this._connectionInfoTimer)
  1.2319 +      return;
  1.2320 +
  1.2321 +    var self = this;
  1.2322 +    function getConnectionInformation() {
  1.2323 +      WifiManager.getConnectionInfo(function(connInfo) {
  1.2324 +        // See comments in calculateSignal for information about this.
  1.2325 +        if (!connInfo) {
  1.2326 +          self._lastConnectionInfo = null;
  1.2327 +          return;
  1.2328 +        }
  1.2329 +
  1.2330 +        let { rssi, linkspeed } = connInfo;
  1.2331 +        if (rssi > 0)
  1.2332 +          rssi -= 256;
  1.2333 +        if (rssi <= MIN_RSSI)
  1.2334 +          rssi = MIN_RSSI;
  1.2335 +        else if (rssi >= MAX_RSSI)
  1.2336 +          rssi = MAX_RSSI;
  1.2337 +
  1.2338 +        let info = { signalStrength: rssi,
  1.2339 +                     relSignalStrength: calculateSignal(rssi),
  1.2340 +                     linkSpeed: linkspeed,
  1.2341 +                     ipAddress: self.ipAddress };
  1.2342 +        let last = self._lastConnectionInfo;
  1.2343 +
  1.2344 +        // Only fire the event if the link speed changed or the signal
  1.2345 +        // strength changed by more than 10%.
  1.2346 +        function tensPlace(percent) ((percent / 10) | 0)
  1.2347 +
  1.2348 +        if (last && last.linkSpeed === info.linkSpeed &&
  1.2349 +            tensPlace(last.relSignalStrength) === tensPlace(info.relSignalStrength)) {
  1.2350 +          return;
  1.2351 +        }
  1.2352 +
  1.2353 +        self._lastConnectionInfo = info;
  1.2354 +        debug("Firing connectionInfoUpdate: " + uneval(info));
  1.2355 +        self._fireEvent("connectionInfoUpdate", info);
  1.2356 +      });
  1.2357 +    }
  1.2358 +
  1.2359 +    // Prime our _lastConnectionInfo immediately and fire the event at the
  1.2360 +    // same time.
  1.2361 +    getConnectionInformation();
  1.2362 +
  1.2363 +    // Now, set up the timer for regular updates.
  1.2364 +    this._connectionInfoTimer =
  1.2365 +      Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  1.2366 +    this._connectionInfoTimer.init(getConnectionInformation, 5000,
  1.2367 +                                   Ci.nsITimer.TYPE_REPEATING_SLACK);
  1.2368 +  },
  1.2369 +
  1.2370 +  _stopConnectionInfoTimer: function() {
  1.2371 +    if (!this._connectionInfoTimer)
  1.2372 +      return;
  1.2373 +
  1.2374 +    this._connectionInfoTimer.cancel();
  1.2375 +    this._connectionInfoTimer = null;
  1.2376 +    this._lastConnectionInfo = null;
  1.2377 +  },
  1.2378 +
  1.2379 +  _reloadConfiguredNetworks: function(callback) {
  1.2380 +    WifiManager.getConfiguredNetworks((function(networks) {
  1.2381 +      if (!networks) {
  1.2382 +        debug("Unable to get configured networks");
  1.2383 +        callback(false);
  1.2384 +        return;
  1.2385 +      }
  1.2386 +
  1.2387 +      this._highestPriority = -1;
  1.2388 +
  1.2389 +      // Convert between netId-based and ssid-based indexing.
  1.2390 +      for (let net in networks) {
  1.2391 +        let network = networks[net];
  1.2392 +        delete networks[net];
  1.2393 +
  1.2394 +        if (!network.ssid) {
  1.2395 +          WifiManager.removeNetwork(network.netId, function() {});
  1.2396 +          continue;
  1.2397 +        }
  1.2398 +
  1.2399 +        if (network.priority && network.priority > this._highestPriority)
  1.2400 +          this._highestPriority = network.priority;
  1.2401 +
  1.2402 +        let networkKey = getNetworkKey(network);
  1.2403 +        // Accept latest config of same network(same SSID and same security).
  1.2404 +        if (networks[networkKey]) {
  1.2405 +          WifiManager.removeNetwork(networks[networkKey].netId, function() {});
  1.2406 +        }
  1.2407 +        networks[networkKey] = network;
  1.2408 +      }
  1.2409 +
  1.2410 +      this.configuredNetworks = networks;
  1.2411 +      callback(true);
  1.2412 +    }).bind(this));
  1.2413 +  },
  1.2414 +
  1.2415 +  // Important side effect: calls WifiManager.saveConfig.
  1.2416 +  _reprioritizeNetworks: function(callback) {
  1.2417 +    // First, sort the networks in orer of their priority.
  1.2418 +    var ordered = Object.getOwnPropertyNames(this.configuredNetworks);
  1.2419 +    let self = this;
  1.2420 +    ordered.sort(function(a, b) {
  1.2421 +      var neta = self.configuredNetworks[a],
  1.2422 +          netb = self.configuredNetworks[b];
  1.2423 +
  1.2424 +      // Sort unsorted networks to the end of the list.
  1.2425 +      if (isNaN(neta.priority))
  1.2426 +        return isNaN(netb.priority) ? 0 : 1;
  1.2427 +      if (isNaN(netb.priority))
  1.2428 +        return -1;
  1.2429 +      return netb.priority - neta.priority;
  1.2430 +    });
  1.2431 +
  1.2432 +    // Skip unsorted networks.
  1.2433 +    let newPriority = 0, i;
  1.2434 +    for (i = ordered.length - 1; i >= 0; --i) {
  1.2435 +      if (!isNaN(this.configuredNetworks[ordered[i]].priority))
  1.2436 +        break;
  1.2437 +    }
  1.2438 +
  1.2439 +    // No networks we care about?
  1.2440 +    if (i < 0) {
  1.2441 +      WifiManager.saveConfig(callback);
  1.2442 +      return;
  1.2443 +    }
  1.2444 +
  1.2445 +    // Now assign priorities from 0 to length, starting with the smallest
  1.2446 +    // priority and heading towards the highest (note the dependency between
  1.2447 +    // total and i here).
  1.2448 +    let done = 0, errors = 0, total = i + 1;
  1.2449 +    for (; i >= 0; --i) {
  1.2450 +      let network = this.configuredNetworks[ordered[i]];
  1.2451 +      network.priority = newPriority++;
  1.2452 +
  1.2453 +      // Note: networkUpdated declared below since it happens logically after
  1.2454 +      // this loop.
  1.2455 +      WifiManager.updateNetwork(network, networkUpdated);
  1.2456 +    }
  1.2457 +
  1.2458 +    function networkUpdated(ok) {
  1.2459 +      if (!ok)
  1.2460 +        ++errors;
  1.2461 +      if (++done === total) {
  1.2462 +        if (errors > 0) {
  1.2463 +          callback(false);
  1.2464 +          return;
  1.2465 +        }
  1.2466 +
  1.2467 +        WifiManager.saveConfig(function(ok) {
  1.2468 +          if (!ok) {
  1.2469 +            callback(false);
  1.2470 +            return;
  1.2471 +          }
  1.2472 +
  1.2473 +          self._reloadConfiguredNetworks(function(ok) {
  1.2474 +            callback(ok);
  1.2475 +          });
  1.2476 +        });
  1.2477 +      }
  1.2478 +    }
  1.2479 +  },
  1.2480 +
  1.2481 +  // nsIWifi
  1.2482 +
  1.2483 +  _domManagers: [],
  1.2484 +  _fireEvent: function(message, data) {
  1.2485 +    this._domManagers.forEach(function(manager) {
  1.2486 +      // Note: We should never have a dead message manager here because we
  1.2487 +      // observe our child message managers shutting down, below.
  1.2488 +      manager.sendAsyncMessage("WifiManager:" + message, data);
  1.2489 +    });
  1.2490 +  },
  1.2491 +
  1.2492 +  _sendMessage: function(message, success, data, msg) {
  1.2493 +    try {
  1.2494 +      msg.manager.sendAsyncMessage(message + (success ? ":OK" : ":NO"),
  1.2495 +                                   { data: data, rid: msg.rid, mid: msg.mid });
  1.2496 +    } catch (e) {
  1.2497 +      debug("sendAsyncMessage error : " + e);
  1.2498 +    }
  1.2499 +    this._splicePendingRequest(msg);
  1.2500 +  },
  1.2501 +
  1.2502 +  _domRequest: [],
  1.2503 +
  1.2504 +  _splicePendingRequest: function(msg) {
  1.2505 +    for (let i = 0; i < this._domRequest.length; i++) {
  1.2506 +      if (this._domRequest[i].msg === msg) {
  1.2507 +        this._domRequest.splice(i, 1);
  1.2508 +        return;
  1.2509 +      }
  1.2510 +    }
  1.2511 +  },
  1.2512 +
  1.2513 +  _clearPendingRequest: function() {
  1.2514 +    if (this._domRequest.length === 0) return;
  1.2515 +    this._domRequest.forEach((function(req) {
  1.2516 +      this._sendMessage(req.name + ":Return", false, "Wifi is disabled", req.msg);
  1.2517 +    }).bind(this));
  1.2518 +  },
  1.2519 +
  1.2520 +  receiveMessage: function MessageManager_receiveMessage(aMessage) {
  1.2521 +    let msg = aMessage.data || {};
  1.2522 +    msg.manager = aMessage.target;
  1.2523 +
  1.2524 +    if (WifiManager.p2pSupported()) {
  1.2525 +      // If p2pObserver returns something truthy, return it!
  1.2526 +      // Otherwise, continue to do the rest of tasks.
  1.2527 +      var p2pRet = this._p2pObserver.onDOMMessage(aMessage);
  1.2528 +      if (p2pRet) {
  1.2529 +        return p2pRet;
  1.2530 +      }
  1.2531 +    }
  1.2532 +
  1.2533 +    // Note: By the time we receive child-process-shutdown, the child process
  1.2534 +    // has already forgotten its permissions so we do this before the
  1.2535 +    // permissions check.
  1.2536 +    if (aMessage.name === "child-process-shutdown") {
  1.2537 +      let i;
  1.2538 +      if ((i = this._domManagers.indexOf(msg.manager)) != -1) {
  1.2539 +        this._domManagers.splice(i, 1);
  1.2540 +      }
  1.2541 +      for (i = this._domRequest.length - 1; i >= 0; i--) {
  1.2542 +        if (this._domRequest[i].msg.manager === msg.manager) {
  1.2543 +          this._domRequest.splice(i, 1);
  1.2544 +        }
  1.2545 +      }
  1.2546 +      return;
  1.2547 +    }
  1.2548 +
  1.2549 +    if (!aMessage.target.assertPermission("wifi-manage")) {
  1.2550 +      return;
  1.2551 +    }
  1.2552 +
  1.2553 +    // We are interested in DOMRequests only.
  1.2554 +    if (aMessage.name != "WifiManager:getState") {
  1.2555 +      this._domRequest.push({name: aMessage.name, msg:msg});
  1.2556 +    }
  1.2557 +
  1.2558 +    switch (aMessage.name) {
  1.2559 +      case "WifiManager:getNetworks":
  1.2560 +        this.getNetworks(msg);
  1.2561 +        break;
  1.2562 +      case "WifiManager:getKnownNetworks":
  1.2563 +        this.getKnownNetworks(msg);
  1.2564 +        break;
  1.2565 +      case "WifiManager:associate":
  1.2566 +        this.associate(msg);
  1.2567 +        break;
  1.2568 +      case "WifiManager:forget":
  1.2569 +        this.forget(msg);
  1.2570 +        break;
  1.2571 +      case "WifiManager:wps":
  1.2572 +        this.wps(msg);
  1.2573 +        break;
  1.2574 +      case "WifiManager:setPowerSavingMode":
  1.2575 +        this.setPowerSavingMode(msg);
  1.2576 +        break;
  1.2577 +      case "WifiManager:setHttpProxy":
  1.2578 +        this.setHttpProxy(msg);
  1.2579 +        break;
  1.2580 +      case "WifiManager:setStaticIpMode":
  1.2581 +        this.setStaticIpMode(msg);
  1.2582 +        break;
  1.2583 +      case "WifiManager:getState": {
  1.2584 +        let i;
  1.2585 +        if ((i = this._domManagers.indexOf(msg.manager)) === -1) {
  1.2586 +          this._domManagers.push(msg.manager);
  1.2587 +        }
  1.2588 +
  1.2589 +        let net = this.currentNetwork ? netToDOM(this.currentNetwork) : null;
  1.2590 +        return { network: net,
  1.2591 +                 connectionInfo: this._lastConnectionInfo,
  1.2592 +                 enabled: WifiManager.enabled,
  1.2593 +                 status: translateState(WifiManager.state),
  1.2594 +                 macAddress: this.macAddress };
  1.2595 +      }
  1.2596 +    }
  1.2597 +  },
  1.2598 +
  1.2599 +  getNetworks: function(msg) {
  1.2600 +    const message = "WifiManager:getNetworks:Return";
  1.2601 +    if (!WifiManager.enabled) {
  1.2602 +      this._sendMessage(message, false, "Wifi is disabled", msg);
  1.2603 +      return;
  1.2604 +    }
  1.2605 +
  1.2606 +    let sent = false;
  1.2607 +    let callback = (function (networks) {
  1.2608 +      if (sent)
  1.2609 +        return;
  1.2610 +      sent = true;
  1.2611 +      this._sendMessage(message, networks !== null, networks, msg);
  1.2612 +    }).bind(this);
  1.2613 +    this.waitForScan(callback);
  1.2614 +
  1.2615 +    WifiManager.scan(true, (function(ok) {
  1.2616 +      // If the scan command succeeded, we're done.
  1.2617 +      if (ok)
  1.2618 +        return;
  1.2619 +
  1.2620 +      // Avoid sending multiple responses.
  1.2621 +      if (sent)
  1.2622 +        return;
  1.2623 +
  1.2624 +      // Otherwise, let the client know that it failed, it's responsible for
  1.2625 +      // trying again in a few seconds.
  1.2626 +      sent = true;
  1.2627 +      this._sendMessage(message, false, "ScanFailed", msg);
  1.2628 +    }).bind(this));
  1.2629 +  },
  1.2630 +
  1.2631 +  getWifiScanResults: function(callback) {
  1.2632 +    var count = 0;
  1.2633 +    var timer = null;
  1.2634 +    var self = this;
  1.2635 +
  1.2636 +    self.waitForScan(waitForScanCallback);
  1.2637 +    doScan();
  1.2638 +    function doScan() {
  1.2639 +      WifiManager.scan(true, (function (ok) {
  1.2640 +        if (!ok) {
  1.2641 +          if (!timer) {
  1.2642 +            count = 0;
  1.2643 +            timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  1.2644 +          }
  1.2645 +
  1.2646 +          if (count++ >= 3) {
  1.2647 +            timer = null;
  1.2648 +            self.wantScanResults.splice(self.wantScanResults.indexOf(waitForScanCallback), 1);
  1.2649 +            callback.onfailure();
  1.2650 +            return;
  1.2651 +          }
  1.2652 +
  1.2653 +          // Else it's still running, continue waiting.
  1.2654 +          timer.initWithCallback(doScan, 10000, Ci.nsITimer.TYPE_ONE_SHOT);
  1.2655 +          return;
  1.2656 +        }
  1.2657 +      }).bind(this));
  1.2658 +    }
  1.2659 +
  1.2660 +    function waitForScanCallback(networks) {
  1.2661 +      if (networks === null) {
  1.2662 +        callback.onfailure();
  1.2663 +        return;
  1.2664 +      }
  1.2665 +
  1.2666 +      var wifiScanResults = new Array();
  1.2667 +      var net;
  1.2668 +      for (let net in networks) {
  1.2669 +        let value = networks[net];
  1.2670 +        wifiScanResults.push(transformResult(value));
  1.2671 +      }
  1.2672 +      callback.onready(wifiScanResults.length, wifiScanResults);
  1.2673 +    }
  1.2674 +
  1.2675 +    function transformResult(element) {
  1.2676 +      var result = new WifiScanResult();
  1.2677 +      result.connected = false;
  1.2678 +      for (let id in element) {
  1.2679 +        if (id === "__exposedProps__") {
  1.2680 +          continue;
  1.2681 +        }
  1.2682 +        if (id === "security") {
  1.2683 +          result[id] = 0;
  1.2684 +          var security = element[id];
  1.2685 +          for (let j = 0; j < security.length; j++) {
  1.2686 +            if (security[j] === "WPA-PSK") {
  1.2687 +              result[id] |= Ci.nsIWifiScanResult.WPA_PSK;
  1.2688 +            } else if (security[j] === "WPA-EAP") {
  1.2689 +              result[id] |= Ci.nsIWifiScanResult.WPA_EAP;
  1.2690 +            } else if (security[j] === "WEP") {
  1.2691 +              result[id] |= Ci.nsIWifiScanResult.WEP;
  1.2692 +            } else {
  1.2693 +             result[id] = 0;
  1.2694 +            }
  1.2695 +          }
  1.2696 +        } else {
  1.2697 +          result[id] = element[id];
  1.2698 +        }
  1.2699 +      }
  1.2700 +      return result;
  1.2701 +    }
  1.2702 +  },
  1.2703 +
  1.2704 +  getKnownNetworks: function(msg) {
  1.2705 +    const message = "WifiManager:getKnownNetworks:Return";
  1.2706 +    if (!WifiManager.enabled) {
  1.2707 +      this._sendMessage(message, false, "Wifi is disabled", msg);
  1.2708 +      return;
  1.2709 +    }
  1.2710 +
  1.2711 +    this._reloadConfiguredNetworks((function(ok) {
  1.2712 +      if (!ok) {
  1.2713 +        this._sendMessage(message, false, "Failed", msg);
  1.2714 +        return;
  1.2715 +      }
  1.2716 +
  1.2717 +      var networks = [];
  1.2718 +      for (let networkKey in this.configuredNetworks) {
  1.2719 +        networks.push(netToDOM(this.configuredNetworks[networkKey]));
  1.2720 +      }
  1.2721 +
  1.2722 +      this._sendMessage(message, true, networks, msg);
  1.2723 +    }).bind(this));
  1.2724 +  },
  1.2725 +
  1.2726 +  _setWifiEnabledCallback: function(status) {
  1.2727 +    if (status !== 0) {
  1.2728 +      this.requestDone();
  1.2729 +      return;
  1.2730 +    }
  1.2731 +
  1.2732 +    // If we're enabling ourselves, then wait until we've connected to the
  1.2733 +    // supplicant to notify. If we're disabling, we take care of this in
  1.2734 +    // supplicantlost.
  1.2735 +    if (WifiManager.supplicantStarted)
  1.2736 +      WifiManager.start();
  1.2737 +  },
  1.2738 +
  1.2739 +  setWifiEnabled: function(enabled, callback) {
  1.2740 +    // Reply error to pending requests.
  1.2741 +    if (!enabled) {
  1.2742 +      this._clearPendingRequest();
  1.2743 +    }
  1.2744 +
  1.2745 +    WifiManager.setWifiEnabled(enabled, callback);
  1.2746 +  },
  1.2747 +
  1.2748 +  // requestDone() must be called to before callback complete(or error)
  1.2749 +  // so next queue in the request quene can be executed.
  1.2750 +  queueRequest: function(data, callback) {
  1.2751 +    if (!callback) {
  1.2752 +        throw "Try to enqueue a request without callback";
  1.2753 +    }
  1.2754 +
  1.2755 +    let optimizeCommandList = ["setWifiEnabled", "setWifiApEnabled"];
  1.2756 +    if (optimizeCommandList.indexOf(data.command) != -1) {
  1.2757 +      this._stateRequests = this._stateRequests.filter(function(element) {
  1.2758 +        return element.data.command !== data.command;
  1.2759 +      });
  1.2760 +    }
  1.2761 +
  1.2762 +    this._stateRequests.push({
  1.2763 +      data: data,
  1.2764 +      callback: callback
  1.2765 +    });
  1.2766 +
  1.2767 +    this.nextRequest();
  1.2768 +  },
  1.2769 +
  1.2770 +  getWifiTetheringParameters: function getWifiTetheringParameters(enable) {
  1.2771 +    let ssid;
  1.2772 +    let securityType;
  1.2773 +    let securityId;
  1.2774 +    let interfaceIp;
  1.2775 +    let prefix;
  1.2776 +    let wifiDhcpStartIp;
  1.2777 +    let wifiDhcpEndIp;
  1.2778 +    let usbDhcpStartIp;
  1.2779 +    let usbDhcpEndIp;
  1.2780 +    let dns1;
  1.2781 +    let dns2;
  1.2782 +
  1.2783 +    ssid = this.tetheringSettings[SETTINGS_WIFI_SSID];
  1.2784 +    securityType = this.tetheringSettings[SETTINGS_WIFI_SECURITY_TYPE];
  1.2785 +    securityId = this.tetheringSettings[SETTINGS_WIFI_SECURITY_PASSWORD];
  1.2786 +    interfaceIp = this.tetheringSettings[SETTINGS_WIFI_IP];
  1.2787 +    prefix = this.tetheringSettings[SETTINGS_WIFI_PREFIX];
  1.2788 +    wifiDhcpStartIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP];
  1.2789 +    wifiDhcpEndIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP];
  1.2790 +    usbDhcpStartIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP];
  1.2791 +    usbDhcpEndIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP];
  1.2792 +    dns1 = this.tetheringSettings[SETTINGS_WIFI_DNS1];
  1.2793 +    dns2 = this.tetheringSettings[SETTINGS_WIFI_DNS2];
  1.2794 +
  1.2795 +    // Check the format to prevent netd from crash.
  1.2796 +    if (!ssid || ssid == "") {
  1.2797 +      debug("Invalid SSID value.");
  1.2798 +      return null;
  1.2799 +    }
  1.2800 +    if (securityType != WIFI_SECURITY_TYPE_NONE &&
  1.2801 +        securityType != WIFI_SECURITY_TYPE_WPA_PSK &&
  1.2802 +        securityType != WIFI_SECURITY_TYPE_WPA2_PSK) {
  1.2803 +
  1.2804 +      debug("Invalid security type.");
  1.2805 +      return null;
  1.2806 +    }
  1.2807 +    if (securityType != WIFI_SECURITY_TYPE_NONE && !securityId) {
  1.2808 +      debug("Invalid security password.");
  1.2809 +      return null;
  1.2810 +    }
  1.2811 +    // Using the default values here until application supports these settings.
  1.2812 +    if (interfaceIp == "" || prefix == "" ||
  1.2813 +        wifiDhcpStartIp == "" || wifiDhcpEndIp == "" ||
  1.2814 +        usbDhcpStartIp == "" || usbDhcpEndIp == "") {
  1.2815 +      debug("Invalid subnet information.");
  1.2816 +      return null;
  1.2817 +    }
  1.2818 +
  1.2819 +    return {
  1.2820 +      ssid: ssid,
  1.2821 +      security: securityType,
  1.2822 +      key: securityId,
  1.2823 +      ip: interfaceIp,
  1.2824 +      prefix: prefix,
  1.2825 +      wifiStartIp: wifiDhcpStartIp,
  1.2826 +      wifiEndIp: wifiDhcpEndIp,
  1.2827 +      usbStartIp: usbDhcpStartIp,
  1.2828 +      usbEndIp: usbDhcpEndIp,
  1.2829 +      dns1: dns1,
  1.2830 +      dns2: dns2,
  1.2831 +      enable: enable,
  1.2832 +      mode: enable ? WIFI_FIRMWARE_AP : WIFI_FIRMWARE_STATION,
  1.2833 +      link: enable ? NETWORK_INTERFACE_UP : NETWORK_INTERFACE_DOWN
  1.2834 +    };
  1.2835 +  },
  1.2836 +
  1.2837 +  setWifiApEnabled: function(enabled, callback) {
  1.2838 +    let configuration = this.getWifiTetheringParameters(enabled);
  1.2839 +
  1.2840 +    if (!configuration) {
  1.2841 +      this.requestDone();
  1.2842 +      debug("Invalid Wifi Tethering configuration.");
  1.2843 +      return;
  1.2844 +    }
  1.2845 +
  1.2846 +    WifiManager.setWifiApEnabled(enabled, configuration, callback);
  1.2847 +  },
  1.2848 +
  1.2849 +  associate: function(msg) {
  1.2850 +    const MAX_PRIORITY = 9999;
  1.2851 +    const message = "WifiManager:associate:Return";
  1.2852 +    let network = msg.data;
  1.2853 +
  1.2854 +    let privnet = network;
  1.2855 +    let dontConnect = privnet.dontConnect;
  1.2856 +    delete privnet.dontConnect;
  1.2857 +
  1.2858 +    if (!WifiManager.enabled) {
  1.2859 +      this._sendMessage(message, false, "Wifi is disabled", msg);
  1.2860 +      return;
  1.2861 +    }
  1.2862 +
  1.2863 +    let self = this;
  1.2864 +    function networkReady() {
  1.2865 +      // saveConfig now before we disable most of the other networks.
  1.2866 +      function selectAndConnect() {
  1.2867 +        WifiManager.enableNetwork(privnet.netId, true, function (ok) {
  1.2868 +          if (ok)
  1.2869 +            self._needToEnableNetworks = true;
  1.2870 +          if (WifiManager.state === "DISCONNECTED" ||
  1.2871 +              WifiManager.state === "SCANNING") {
  1.2872 +            WifiManager.reconnect(function (ok) {
  1.2873 +              self._sendMessage(message, ok, ok, msg);
  1.2874 +            });
  1.2875 +          } else {
  1.2876 +            self._sendMessage(message, ok, ok, msg);
  1.2877 +          }
  1.2878 +        });
  1.2879 +      }
  1.2880 +
  1.2881 +      var selectAndConnectOrReturn = dontConnect ?
  1.2882 +        function() {
  1.2883 +          self._sendMessage(message, true, "Wifi has been recorded", msg);
  1.2884 +        } : selectAndConnect;
  1.2885 +      if (self._highestPriority >= MAX_PRIORITY) {
  1.2886 +        self._reprioritizeNetworks(selectAndConnectOrReturn);
  1.2887 +      } else {
  1.2888 +        WifiManager.saveConfig(selectAndConnectOrReturn);
  1.2889 +      }
  1.2890 +    }
  1.2891 +
  1.2892 +    let ssid = privnet.ssid;
  1.2893 +    let networkKey = getNetworkKey(privnet);
  1.2894 +    let configured;
  1.2895 +
  1.2896 +    if (networkKey in this._addingNetworks) {
  1.2897 +      this._sendMessage(message, false, "Racing associates");
  1.2898 +      return;
  1.2899 +    }
  1.2900 +
  1.2901 +    if (networkKey in this.configuredNetworks)
  1.2902 +      configured = this.configuredNetworks[networkKey];
  1.2903 +
  1.2904 +    netFromDOM(privnet, configured);
  1.2905 +
  1.2906 +    privnet.priority = ++this._highestPriority;
  1.2907 +    if (configured) {
  1.2908 +      privnet.netId = configured.netId;
  1.2909 +      WifiManager.updateNetwork(privnet, (function(ok) {
  1.2910 +        if (!ok) {
  1.2911 +          this._sendMessage(message, false, "Network is misconfigured", msg);
  1.2912 +          return;
  1.2913 +        }
  1.2914 +
  1.2915 +        networkReady();
  1.2916 +      }).bind(this));
  1.2917 +    } else {
  1.2918 +      // networkReady, above, calls saveConfig. We want to remember the new
  1.2919 +      // network as being enabled, which isn't the default, so we explicitly
  1.2920 +      // set it to being "enabled" before we add it and save the
  1.2921 +      // configuration.
  1.2922 +      privnet.disabled = 0;
  1.2923 +      this._addingNetworks[networkKey] = privnet;
  1.2924 +      WifiManager.addNetwork(privnet, (function(ok) {
  1.2925 +        delete this._addingNetworks[networkKey];
  1.2926 +
  1.2927 +        if (!ok) {
  1.2928 +          this._sendMessage(message, false, "Network is misconfigured", msg);
  1.2929 +          return;
  1.2930 +        }
  1.2931 +
  1.2932 +        this.configuredNetworks[networkKey] = privnet;
  1.2933 +        networkReady();
  1.2934 +      }).bind(this));
  1.2935 +    }
  1.2936 +  },
  1.2937 +
  1.2938 +  forget: function(msg) {
  1.2939 +    const message = "WifiManager:forget:Return";
  1.2940 +    let network = msg.data;
  1.2941 +    if (!WifiManager.enabled) {
  1.2942 +      this._sendMessage(message, false, "Wifi is disabled", msg);
  1.2943 +      return;
  1.2944 +    }
  1.2945 +
  1.2946 +    this._reloadConfiguredNetworks((function(ok) {
  1.2947 +      // Give it a chance to remove the network even if reload is failed.
  1.2948 +      if (!ok) {
  1.2949 +        debug("Warning !!! Failed to reload the configured networks");
  1.2950 +      }
  1.2951 +
  1.2952 +      let ssid = network.ssid;
  1.2953 +      let networkKey = getNetworkKey(network);
  1.2954 +      if (!(networkKey in this.configuredNetworks)) {
  1.2955 +        this._sendMessage(message, false, "Trying to forget an unknown network", msg);
  1.2956 +        return;
  1.2957 +      }
  1.2958 +
  1.2959 +      let self = this;
  1.2960 +      let configured = this.configuredNetworks[networkKey];
  1.2961 +      this._reconnectOnDisconnect = (this.currentNetwork &&
  1.2962 +                                    (this.currentNetwork.ssid === ssid));
  1.2963 +      WifiManager.removeNetwork(configured.netId, function(ok) {
  1.2964 +        if (!ok) {
  1.2965 +          self._sendMessage(message, false, "Unable to remove the network", msg);
  1.2966 +          self._reconnectOnDisconnect = false;
  1.2967 +          return;
  1.2968 +        }
  1.2969 +
  1.2970 +        WifiManager.saveConfig(function() {
  1.2971 +          self._reloadConfiguredNetworks(function() {
  1.2972 +            self._sendMessage(message, true, true, msg);
  1.2973 +          });
  1.2974 +        });
  1.2975 +      });
  1.2976 +    }).bind(this));
  1.2977 +  },
  1.2978 +
  1.2979 +  wps: function(msg) {
  1.2980 +    const message = "WifiManager:wps:Return";
  1.2981 +    let self = this;
  1.2982 +    let detail = msg.data;
  1.2983 +    if (detail.method === "pbc") {
  1.2984 +      WifiManager.wpsPbc(WifiManager.ifname, function(ok) {
  1.2985 +        if (ok)
  1.2986 +          self._sendMessage(message, true, true, msg);
  1.2987 +        else
  1.2988 +          self._sendMessage(message, false, "WPS PBC failed", msg);
  1.2989 +      });
  1.2990 +    } else if (detail.method === "pin") {
  1.2991 +      WifiManager.wpsPin(detail, function(pin) {
  1.2992 +        if (pin)
  1.2993 +          self._sendMessage(message, true, pin, msg);
  1.2994 +        else
  1.2995 +          self._sendMessage(message, false, "WPS PIN failed", msg);
  1.2996 +      });
  1.2997 +    } else if (detail.method === "cancel") {
  1.2998 +      WifiManager.wpsCancel(function(ok) {
  1.2999 +        if (ok)
  1.3000 +          self._sendMessage(message, true, true, msg);
  1.3001 +        else
  1.3002 +          self._sendMessage(message, false, "WPS Cancel failed", msg);
  1.3003 +      });
  1.3004 +    } else {
  1.3005 +      self._sendMessage(message, false, "Invalid WPS method=" + detail.method,
  1.3006 +                        msg);
  1.3007 +    }
  1.3008 +  },
  1.3009 +
  1.3010 +  setPowerSavingMode: function(msg) {
  1.3011 +    const message = "WifiManager:setPowerSavingMode:Return";
  1.3012 +    let self = this;
  1.3013 +    let enabled = msg.data;
  1.3014 +    let mode = enabled ? "AUTO" : "ACTIVE";
  1.3015 +
  1.3016 +    // Some wifi drivers may not implement this command. Set power mode
  1.3017 +    // even if suspend optimization command failed.
  1.3018 +    WifiManager.setSuspendOptimizations(enabled, function(ok) {
  1.3019 +      WifiManager.setPowerMode(mode, function(ok) {
  1.3020 +        if (ok) {
  1.3021 +          self._sendMessage(message, true, true, msg);
  1.3022 +        } else {
  1.3023 +          self._sendMessage(message, false, "Set power saving mode failed", msg);
  1.3024 +        }
  1.3025 +      });
  1.3026 +    });
  1.3027 +  },
  1.3028 +
  1.3029 +  setHttpProxy: function(msg) {
  1.3030 +    const message = "WifiManager:setHttpProxy:Return";
  1.3031 +    let self = this;
  1.3032 +    let network = msg.data.network;
  1.3033 +    let info = msg.data.info;
  1.3034 +
  1.3035 +    WifiManager.configureHttpProxy(network, info, function(ok) {
  1.3036 +      if (ok) {
  1.3037 +        // If configured network is current connected network
  1.3038 +        // need update http proxy immediately.
  1.3039 +        let setNetworkKey = getNetworkKey(network);
  1.3040 +        let curNetworkKey = self.currentNetwork ? getNetworkKey(self.currentNetwork) : null;
  1.3041 +        if (setNetworkKey === curNetworkKey)
  1.3042 +          WifiManager.setHttpProxy(network);
  1.3043 +
  1.3044 +        self._sendMessage(message, true, true, msg);
  1.3045 +      } else {
  1.3046 +        self._sendMessage(message, false, "Set http proxy failed", msg);
  1.3047 +      }
  1.3048 +    });
  1.3049 +  },
  1.3050 +
  1.3051 +  setStaticIpMode: function(msg) {
  1.3052 +    const message = "WifiManager:setStaticMode:Return";
  1.3053 +    let self = this;
  1.3054 +    let network = msg.data.network;
  1.3055 +    let info = msg.data.info;
  1.3056 +
  1.3057 +    // To compatiable with DHCP returned info structure, do translation here
  1.3058 +    info.ipaddr_str = info.ipaddr;
  1.3059 +    info.proxy_str = info.proxy;
  1.3060 +    info.gateway_str = info.gateway;
  1.3061 +    info.dns1_str = info.dns1;
  1.3062 +    info.dns2_str = info.dns2;
  1.3063 +
  1.3064 +    WifiManager.setStaticIpMode(network, info, function(ok) {
  1.3065 +      if (ok) {
  1.3066 +        self._sendMessage(message, true, true, msg);
  1.3067 +      } else {
  1.3068 +        self._sendMessage(message, false, "Set static ip mode failed", msg);
  1.3069 +      }
  1.3070 +    });
  1.3071 +  },
  1.3072 +
  1.3073 +  // This is a bit ugly, but works. In particular, this depends on the fact
  1.3074 +  // that RadioManager never actually tries to get the worker from us.
  1.3075 +  get worker() { throw "Not implemented"; },
  1.3076 +
  1.3077 +  shutdown: function() {
  1.3078 +    debug("shutting down ...");
  1.3079 +    this.queueRequest({command: "setWifiEnabled", value: false}, function(data) {
  1.3080 +      this.setWifiEnabled(false, this._setWifiEnabledCallback.bind(this));
  1.3081 +    }.bind(this));
  1.3082 +  },
  1.3083 +
  1.3084 +  requestProcessing: false,   // Hold while dequeue and execution a request.
  1.3085 +                              // Released upon the request is fully executed,
  1.3086 +                              // i.e, mostly after callback is done.
  1.3087 +  requestDone: function requestDone() {
  1.3088 +    this.requestProcessing = false;
  1.3089 +    this.nextRequest();
  1.3090 +  },
  1.3091 +
  1.3092 +  nextRequest: function nextRequest() {
  1.3093 +    // No request to process
  1.3094 +    if (this._stateRequests.length === 0) {
  1.3095 +      return;
  1.3096 +    }
  1.3097 +
  1.3098 +    // Handling request, wait for it.
  1.3099 +    if (this.requestProcessing) {
  1.3100 +      return;
  1.3101 +    }
  1.3102 +
  1.3103 +    // Hold processing lock
  1.3104 +    this.requestProcessing = true;
  1.3105 +
  1.3106 +    // Find next valid request
  1.3107 +    let request = this._stateRequests.shift();
  1.3108 +
  1.3109 +    request.callback(request.data);
  1.3110 +  },
  1.3111 +
  1.3112 +  notifyTetheringOn: function notifyTetheringOn() {
  1.3113 +    // It's really sad that we don't have an API to notify the wifi
  1.3114 +    // hotspot status. Toggle settings to let gaia know that wifi hotspot
  1.3115 +    // is enabled.
  1.3116 +    let self = this;
  1.3117 +    this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] = true;
  1.3118 +    this._oldWifiTetheringEnabledState = true;
  1.3119 +    gSettingsService.createLock().set(
  1.3120 +      SETTINGS_WIFI_TETHERING_ENABLED,
  1.3121 +      true,
  1.3122 +      {
  1.3123 +        handle: function(aName, aResult) {
  1.3124 +          self.requestDone();
  1.3125 +        },
  1.3126 +        handleError: function(aErrorMessage) {
  1.3127 +          self.requestDone();
  1.3128 +        }
  1.3129 +      },
  1.3130 +      "fromInternalSetting");
  1.3131 +  },
  1.3132 +
  1.3133 +  notifyTetheringOff: function notifyTetheringOff() {
  1.3134 +    // It's really sad that we don't have an API to notify the wifi
  1.3135 +    // hotspot status. Toggle settings to let gaia know that wifi hotspot
  1.3136 +    // is disabled.
  1.3137 +    let self = this;
  1.3138 +    this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] = false;
  1.3139 +    this._oldWifiTetheringEnabledState = false;
  1.3140 +    gSettingsService.createLock().set(
  1.3141 +      SETTINGS_WIFI_TETHERING_ENABLED,
  1.3142 +      false,
  1.3143 +      {
  1.3144 +        handle: function(aName, aResult) {
  1.3145 +          self.requestDone();
  1.3146 +        },
  1.3147 +        handleError: function(aErrorMessage) {
  1.3148 +          self.requestDone();
  1.3149 +        }
  1.3150 +      },
  1.3151 +      "fromInternalSetting");
  1.3152 +  },
  1.3153 +
  1.3154 +  handleWifiEnabled: function(enabled) {
  1.3155 +    // Make sure Wifi hotspot is idle before switching to Wifi mode.
  1.3156 +    if (enabled) {
  1.3157 +      this.queueRequest({command: "setWifiApEnabled", value: false}, function(data) {
  1.3158 +        if (this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] ||
  1.3159 +            WifiManager.isWifiTetheringEnabled(WifiManager.tetheringState)) {
  1.3160 +          this.disconnectedByWifi = true;
  1.3161 +          this.setWifiApEnabled(false, this.notifyTetheringOff.bind(this));
  1.3162 +        } else {
  1.3163 +          this.requestDone();
  1.3164 +        }
  1.3165 +      }.bind(this));
  1.3166 +    }
  1.3167 +
  1.3168 +    this.queueRequest({command: "setWifiEnabled", value: enabled}, function(data) {
  1.3169 +      this.setWifiEnabled(enabled, this._setWifiEnabledCallback.bind(this));
  1.3170 +    }.bind(this));
  1.3171 +
  1.3172 +    if (!enabled) {
  1.3173 +      this.queueRequest({command: "setWifiApEnabled", value: true}, function(data) {
  1.3174 +        if (this.disconnectedByWifi) {
  1.3175 +          this.setWifiApEnabled(true, this.notifyTetheringOn.bind(this));
  1.3176 +        } else {
  1.3177 +          this.requestDone();
  1.3178 +        }
  1.3179 +        this.disconnectedByWifi = false;
  1.3180 +      }.bind(this));
  1.3181 +    }
  1.3182 +  },
  1.3183 +
  1.3184 +  handleWifiTetheringEnabled: function(enabled) {
  1.3185 +    // Make sure Wifi is idle before switching to Wifi hotspot mode.
  1.3186 +    if (enabled) {
  1.3187 +      this.queueRequest({command: "setWifiEnabled", value: false}, function(data) {
  1.3188 +        if (WifiManager.isWifiEnabled(WifiManager.state)) {
  1.3189 +          this.disconnectedByWifiTethering = true;
  1.3190 +          this.setWifiEnabled(false, this._setWifiEnabledCallback.bind(this));
  1.3191 +        } else {
  1.3192 +          this.requestDone();
  1.3193 +        }
  1.3194 +      }.bind(this));
  1.3195 +    }
  1.3196 +
  1.3197 +    this.queueRequest({command: "setWifiApEnabled", value: enabled}, function(data) {
  1.3198 +      this.setWifiApEnabled(enabled, this.requestDone.bind(this));
  1.3199 +    }.bind(this));
  1.3200 +
  1.3201 +    if (!enabled) {
  1.3202 +      this.queueRequest({command: "setWifiEnabled", value: true}, function(data) {
  1.3203 +        if (this.disconnectedByWifiTethering) {
  1.3204 +          this.setWifiEnabled(true, this._setWifiEnabledCallback.bind(this));
  1.3205 +        } else {
  1.3206 +          this.requestDone();
  1.3207 +        }
  1.3208 +        this.disconnectedByWifiTethering = false;
  1.3209 +      }.bind(this));
  1.3210 +    }
  1.3211 +  },
  1.3212 +
  1.3213 +  // nsIObserver implementation
  1.3214 +  observe: function observe(subject, topic, data) {
  1.3215 +    // Note that this function gets called for any and all settings changes,
  1.3216 +    // so we need to carefully check if we have the one we're interested in.
  1.3217 +    // The string we're interested in will be a JSON string that looks like:
  1.3218 +    // {"key":"wifi.enabled","value":"true"}.
  1.3219 +    if (topic !== kMozSettingsChangedObserverTopic) {
  1.3220 +      return;
  1.3221 +    }
  1.3222 +
  1.3223 +    let setting = JSON.parse(data);
  1.3224 +    // To avoid WifiWorker setting the wifi again, don't need to deal with
  1.3225 +    // the "mozsettings-changed" event fired from internal setting.
  1.3226 +    if (setting.message && setting.message === "fromInternalSetting") {
  1.3227 +      return;
  1.3228 +    }
  1.3229 +
  1.3230 +    this.handle(setting.key, setting.value);
  1.3231 +  },
  1.3232 +
  1.3233 +  handle: function handle(aName, aResult) {
  1.3234 +    switch(aName) {
  1.3235 +      case SETTINGS_WIFI_ENABLED:
  1.3236 +        this.handleWifiEnabled(aResult)
  1.3237 +        break;
  1.3238 +      case SETTINGS_WIFI_DEBUG_ENABLED:
  1.3239 +        if (aResult === null)
  1.3240 +          aResult = false;
  1.3241 +        DEBUG = aResult;
  1.3242 +        updateDebug();
  1.3243 +        break;
  1.3244 +      case SETTINGS_WIFI_TETHERING_ENABLED:
  1.3245 +        this._oldWifiTetheringEnabledState = this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED];
  1.3246 +        // Fall through!
  1.3247 +      case SETTINGS_WIFI_SSID:
  1.3248 +      case SETTINGS_WIFI_SECURITY_TYPE:
  1.3249 +      case SETTINGS_WIFI_SECURITY_PASSWORD:
  1.3250 +      case SETTINGS_WIFI_IP:
  1.3251 +      case SETTINGS_WIFI_PREFIX:
  1.3252 +      case SETTINGS_WIFI_DHCPSERVER_STARTIP:
  1.3253 +      case SETTINGS_WIFI_DHCPSERVER_ENDIP:
  1.3254 +      case SETTINGS_WIFI_DNS1:
  1.3255 +      case SETTINGS_WIFI_DNS2:
  1.3256 +      case SETTINGS_USB_DHCPSERVER_STARTIP:
  1.3257 +      case SETTINGS_USB_DHCPSERVER_ENDIP:
  1.3258 +        if (aResult !== null) {
  1.3259 +          this.tetheringSettings[aName] = aResult;
  1.3260 +        }
  1.3261 +        debug("'" + aName + "'" + " is now " + this.tetheringSettings[aName]);
  1.3262 +        let index = this._wifiTetheringSettingsToRead.indexOf(aName);
  1.3263 +
  1.3264 +        if (index != -1) {
  1.3265 +          this._wifiTetheringSettingsToRead.splice(index, 1);
  1.3266 +        }
  1.3267 +
  1.3268 +        if (this._wifiTetheringSettingsToRead.length) {
  1.3269 +          debug("We haven't read completely the wifi Tethering data from settings db.");
  1.3270 +          break;
  1.3271 +        }
  1.3272 +
  1.3273 +        if (this._oldWifiTetheringEnabledState === this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED]) {
  1.3274 +          debug("No changes for SETTINGS_WIFI_TETHERING_ENABLED flag. Nothing to do.");
  1.3275 +          break;
  1.3276 +        }
  1.3277 +
  1.3278 +        if (this._oldWifiTetheringEnabledState === null &&
  1.3279 +            !this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED]) {
  1.3280 +          debug("Do nothing when initial settings for SETTINGS_WIFI_TETHERING_ENABLED flag is false.");
  1.3281 +          break;
  1.3282 +        }
  1.3283 +
  1.3284 +        this._oldWifiTetheringEnabledState = this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED];
  1.3285 +        this.handleWifiTetheringEnabled(aResult)
  1.3286 +        break;
  1.3287 +    };
  1.3288 +  },
  1.3289 +
  1.3290 +  handleError: function handleError(aErrorMessage) {
  1.3291 +    debug("There was an error while reading Tethering settings.");
  1.3292 +    this.tetheringSettings = {};
  1.3293 +    this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] = false;
  1.3294 +  },
  1.3295 +};
  1.3296 +
  1.3297 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WifiWorker]);
  1.3298 +
  1.3299 +let debug;
  1.3300 +function updateDebug() {
  1.3301 +  if (DEBUG) {
  1.3302 +    debug = function (s) {
  1.3303 +      dump("-*- WifiWorker component: " + s + "\n");
  1.3304 +    };
  1.3305 +  } else {
  1.3306 +    debug = function (s) {};
  1.3307 +  }
  1.3308 +  WifiManager.syncDebug();
  1.3309 +}
  1.3310 +updateDebug();

mercurial