dom/wifi/WifiWorker.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 "use strict";
     9 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
    11 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    12 Cu.import("resource://gre/modules/Services.jsm");
    13 Cu.import("resource://gre/modules/systemlibs.js");
    14 Cu.import("resource://gre/modules/WifiCommand.jsm");
    15 Cu.import("resource://gre/modules/WifiNetUtil.jsm");
    16 Cu.import("resource://gre/modules/WifiP2pManager.jsm");
    17 Cu.import("resource://gre/modules/WifiP2pWorkerObserver.jsm");
    19 var DEBUG = false; // set to true to show debug messages.
    21 const WIFIWORKER_CONTRACTID = "@mozilla.org/wifi/worker;1";
    22 const WIFIWORKER_CID        = Components.ID("{a14e8977-d259-433a-a88d-58dd44657e5b}");
    24 const WIFIWORKER_WORKER     = "resource://gre/modules/wifi_worker.js";
    26 const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
    27 const kMozSettingsChangedObserverTopic   = "mozsettings-changed";
    29 const MAX_RETRIES_ON_AUTHENTICATION_FAILURE = 2;
    30 const MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
    31 const MAX_RETRIES_ON_DHCP_FAILURE = 2;
    33 // Settings DB path for wifi
    34 const SETTINGS_WIFI_ENABLED            = "wifi.enabled";
    35 const SETTINGS_WIFI_DEBUG_ENABLED      = "wifi.debugging.enabled";
    36 // Settings DB path for Wifi tethering.
    37 const SETTINGS_WIFI_TETHERING_ENABLED  = "tethering.wifi.enabled";
    38 const SETTINGS_WIFI_SSID               = "tethering.wifi.ssid";
    39 const SETTINGS_WIFI_SECURITY_TYPE      = "tethering.wifi.security.type";
    40 const SETTINGS_WIFI_SECURITY_PASSWORD  = "tethering.wifi.security.password";
    41 const SETTINGS_WIFI_IP                 = "tethering.wifi.ip";
    42 const SETTINGS_WIFI_PREFIX             = "tethering.wifi.prefix";
    43 const SETTINGS_WIFI_DHCPSERVER_STARTIP = "tethering.wifi.dhcpserver.startip";
    44 const SETTINGS_WIFI_DHCPSERVER_ENDIP   = "tethering.wifi.dhcpserver.endip";
    45 const SETTINGS_WIFI_DNS1               = "tethering.wifi.dns1";
    46 const SETTINGS_WIFI_DNS2               = "tethering.wifi.dns2";
    48 // Settings DB path for USB tethering.
    49 const SETTINGS_USB_DHCPSERVER_STARTIP  = "tethering.usb.dhcpserver.startip";
    50 const SETTINGS_USB_DHCPSERVER_ENDIP    = "tethering.usb.dhcpserver.endip";
    52 // Default value for WIFI tethering.
    53 const DEFAULT_WIFI_IP                  = "192.168.1.1";
    54 const DEFAULT_WIFI_PREFIX              = "24";
    55 const DEFAULT_WIFI_DHCPSERVER_STARTIP  = "192.168.1.10";
    56 const DEFAULT_WIFI_DHCPSERVER_ENDIP    = "192.168.1.30";
    57 const DEFAULT_WIFI_SSID                = "FirefoxHotspot";
    58 const DEFAULT_WIFI_SECURITY_TYPE       = "open";
    59 const DEFAULT_WIFI_SECURITY_PASSWORD   = "1234567890";
    60 const DEFAULT_DNS1                     = "8.8.8.8";
    61 const DEFAULT_DNS2                     = "8.8.4.4";
    63 // Default value for USB tethering.
    64 const DEFAULT_USB_DHCPSERVER_STARTIP   = "192.168.0.10";
    65 const DEFAULT_USB_DHCPSERVER_ENDIP     = "192.168.0.30";
    67 const WIFI_FIRMWARE_AP            = "AP";
    68 const WIFI_FIRMWARE_STATION       = "STA";
    69 const WIFI_SECURITY_TYPE_NONE     = "open";
    70 const WIFI_SECURITY_TYPE_WPA_PSK  = "wpa-psk";
    71 const WIFI_SECURITY_TYPE_WPA2_PSK = "wpa2-psk";
    73 const NETWORK_INTERFACE_UP   = "up";
    74 const NETWORK_INTERFACE_DOWN = "down";
    76 const DEFAULT_WLAN_INTERFACE = "wlan0";
    78 const DRIVER_READY_WAIT = 2000;
    80 const SUPP_PROP = "init.svc.wpa_supplicant";
    81 const WPA_SUPPLICANT = "wpa_supplicant";
    82 const DHCP_PROP = "init.svc.dhcpcd";
    83 const DHCP = "dhcpcd";
    85 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
    86                                    "@mozilla.org/network/manager;1",
    87                                    "nsINetworkManager");
    89 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
    90                                    "@mozilla.org/network/service;1",
    91                                    "nsINetworkService");
    93 XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
    94                                    "@mozilla.org/settingsService;1",
    95                                    "nsISettingsService");
    97 // A note about errors and error handling in this file:
    98 // The libraries that we use in this file are intended for C code. For
    99 // C code, it is natural to return -1 for errors and 0 for success.
   100 // Therefore, the code that interacts directly with the worker uses this
   101 // convention (note: command functions do get boolean results since the
   102 // command always succeeds and we do a string/boolean check for the
   103 // expected results).
   104 var WifiManager = (function() {
   105   var manager = {};
   107   function getStartupPrefs() {
   108     return {
   109       sdkVersion: parseInt(libcutils.property_get("ro.build.version.sdk"), 10),
   110       unloadDriverEnabled: libcutils.property_get("ro.moz.wifi.unloaddriver") === "1",
   111       schedScanRecovery: libcutils.property_get("ro.moz.wifi.sched_scan_recover") === "false" ? false : true,
   112       driverDelay: libcutils.property_get("ro.moz.wifi.driverDelay"),
   113       p2pSupported: libcutils.property_get("ro.moz.wifi.p2p_supported") === "1",
   114       eapSimSupported: libcutils.property_get("ro.moz.wifi.eapsim_supported") === "1",
   115       ifname: libcutils.property_get("wifi.interface")
   116     };
   117   }
   119   let {sdkVersion, unloadDriverEnabled, schedScanRecovery,
   120        driverDelay, p2pSupported, eapSimSupported, ifname} = getStartupPrefs();
   122   let capabilities = {
   123     eapSim: eapSimSupported
   124   };
   126   let wifiListener = {
   127     onWaitEvent: function(event, iface) {
   128       if (manager.ifname === iface && handleEvent(event)) {
   129         waitForEvent(iface);
   130       } else if (p2pSupported) {
   131         if (WifiP2pManager.INTERFACE_NAME === iface) {
   132           // If the connection is closed, wifi.c::wifi_wait_for_event()
   133           // will still return 'CTRL-EVENT-TERMINATING  - connection closed'
   134           // rather than blocking. So when we see this special event string,
   135           // just return immediately.
   136           const TERMINATED_EVENT = 'CTRL-EVENT-TERMINATING  - connection closed';
   137           if (-1 !== event.indexOf(TERMINATED_EVENT)) {
   138             return;
   139           }
   140           p2pManager.handleEvent(event);
   141           waitForEvent(iface);
   142         }
   143       }
   144     },
   146     onCommand: function(event, iface) {
   147       onmessageresult(event, iface);
   148     }
   149   }
   151   manager.ifname = ifname;
   152   manager.connectToSupplicant = false;
   153   // Emulator build runs to here.
   154   // The debug() should only be used after WifiManager.
   155   if (!ifname) {
   156     manager.ifname = DEFAULT_WLAN_INTERFACE;
   157   }
   158   manager.schedScanRecovery = schedScanRecovery;
   159   manager.driverDelay = driverDelay ? parseInt(driverDelay, 10) : DRIVER_READY_WAIT;
   161   // Regular Wifi stuff.
   162   var netUtil = WifiNetUtil(controlMessage);
   163   var wifiCommand = WifiCommand(controlMessage, manager.ifname);
   165   // Wifi P2P stuff
   166   var p2pManager;
   167   if (p2pSupported) {
   168     let p2pCommand = WifiCommand(controlMessage, WifiP2pManager.INTERFACE_NAME);
   169     p2pManager = WifiP2pManager(p2pCommand, netUtil);
   170   }
   172   let wifiService = Cc["@mozilla.org/wifi/service;1"];
   173   if (wifiService) {
   174     wifiService = wifiService.getService(Ci.nsIWifiProxyService);
   175     let interfaces = [manager.ifname];
   176     if (p2pSupported) {
   177       interfaces.push(WifiP2pManager.INTERFACE_NAME);
   178     }
   179     wifiService.start(wifiListener, interfaces, interfaces.length);
   180   } else {
   181     debug("No wifi service component available!");
   182   }
   184   // Callbacks to invoke when a reply arrives from the wifi service.
   185   var controlCallbacks = Object.create(null);
   186   var idgen = 0;
   188   function controlMessage(obj, callback) {
   189     var id = idgen++;
   190     obj.id = id;
   191     if (callback) {
   192       controlCallbacks[id] = callback;
   193     }
   194     wifiService.sendCommand(obj, obj.iface);
   195   }
   197   let onmessageresult = function(data, iface) {
   198     var id = data.id;
   199     var callback = controlCallbacks[id];
   200     if (callback) {
   201       callback(data);
   202       delete controlCallbacks[id];
   203     }
   204   }
   206   // Polling the status worker
   207   var recvErrors = 0;
   209   function waitForEvent(iface) {
   210     wifiService.waitForEvent(iface);
   211   }
   213   // Commands to the control worker.
   215   var driverLoaded = false;
   217   function loadDriver(callback) {
   218     if (driverLoaded) {
   219       callback(0);
   220       return;
   221     }
   223     wifiCommand.loadDriver(function (status) {
   224       driverLoaded = (status >= 0);
   225       callback(status)
   226     });
   227   }
   229   function unloadDriver(type, callback) {
   230     if (!unloadDriverEnabled) {
   231       // Unloading drivers is generally unnecessary and
   232       // can trigger bugs in some drivers.
   233       // On properly written drivers, bringing the interface
   234       // down powers down the interface.
   235       if (type === WIFI_FIRMWARE_STATION) {
   236         notify("supplicantlost", { success: true });
   237       }
   238       callback(0);
   239       return;
   240     }
   242     wifiCommand.unloadDriver(function(status) {
   243       driverLoaded = (status < 0);
   244       if (type === WIFI_FIRMWARE_STATION) {
   245         notify("supplicantlost", { success: true });
   246       }
   247       callback(status);
   248     });
   249   }
   251   // A note about background scanning:
   252   // Normally, background scanning shouldn't be necessary as wpa_supplicant
   253   // has the capability to automatically schedule its own scans at appropriate
   254   // intervals. However, with some drivers, this appears to get stuck after
   255   // three scans, so we enable the driver's background scanning to work around
   256   // that when we're not connected to any network. This ensures that we'll
   257   // automatically reconnect to networks if one falls out of range.
   258   var reEnableBackgroundScan = false;
   260   // NB: This is part of the internal API.
   261   manager.backgroundScanEnabled = false;
   262   function setBackgroundScan(enable, callback) {
   263     var doEnable = (enable === "ON");
   264     if (doEnable === manager.backgroundScanEnabled) {
   265       callback(false, true);
   266       return;
   267     }
   269     manager.backgroundScanEnabled = doEnable;
   270     wifiCommand.setBackgroundScan(manager.backgroundScanEnabled, callback);
   271   }
   273   var scanModeActive = false;
   275   function scan(forceActive, callback) {
   276     if (forceActive && !scanModeActive) {
   277       // Note: we ignore errors from doSetScanMode.
   278       wifiCommand.doSetScanMode(true, function(ignore) {
   279         setBackgroundScan("OFF", function(turned, ignore) {
   280           reEnableBackgroundScan = turned;
   281           manager.handlePreWifiScan();
   282           wifiCommand.scan(function(ok) {
   283             wifiCommand.doSetScanMode(false, function(ignore) {
   284               // The result of scanCommand is the result of the actual SCAN
   285               // request.
   286               callback(ok);
   287             });
   288           });
   289         });
   290       });
   291       return;
   292     }
   293     manager.handlePreWifiScan();
   294     wifiCommand.scan(callback);
   295   }
   297   var debugEnabled = false;
   299   function syncDebug() {
   300     if (debugEnabled !== DEBUG) {
   301       let wanted = DEBUG;
   302       wifiCommand.setLogLevel(wanted ? "DEBUG" : "INFO", function(ok) {
   303         if (ok)
   304           debugEnabled = wanted;
   305       });
   306       if (p2pSupported && p2pManager) {
   307         p2pManager.setDebug(DEBUG);
   308       }
   309     }
   310   }
   312   function getDebugEnabled(callback) {
   313     wifiCommand.getLogLevel(function(level) {
   314       if (level === null) {
   315         debug("Unable to get wpa_supplicant's log level");
   316         callback(false);
   317         return;
   318       }
   320       var lines = level.split("\n");
   321       for (let i = 0; i < lines.length; ++i) {
   322         let match = /Current level: (.*)/.exec(lines[i]);
   323         if (match) {
   324           debugEnabled = match[1].toLowerCase() === "debug";
   325           callback(true);
   326           return;
   327         }
   328       }
   330       // If we're here, we didn't get the current level.
   331       callback(false);
   332     });
   333   }
   335   function setScanMode(setActive, callback) {
   336     scanModeActive = setActive;
   337     wifiCommand.doSetScanMode(setActive, callback);
   338   }
   340   var httpProxyConfig = Object.create(null);
   342   /**
   343    * Given a network, configure http proxy when using wifi.
   344    * @param network A network object to update http proxy
   345    * @param info Info should have following field:
   346    *        - httpProxyHost ip address of http proxy.
   347    *        - httpProxyPort port of http proxy, set 0 to use default port 8080.
   348    * @param callback callback function.
   349    */
   350   function configureHttpProxy(network, info, callback) {
   351     if (!network)
   352       return;
   354     let networkKey = getNetworkKey(network);
   356     if (!info || info.httpProxyHost === "") {
   357       delete httpProxyConfig[networkKey];
   358     } else {
   359       httpProxyConfig[networkKey] = network;
   360       httpProxyConfig[networkKey].httpProxyHost = info.httpProxyHost;
   361       httpProxyConfig[networkKey].httpProxyPort = info.httpProxyPort;
   362     }
   364     callback(true);
   365   }
   367   function getHttpProxyNetwork(network) {
   368     if (!network)
   369       return null;
   371     let networkKey = getNetworkKey(network);
   372     return ((networkKey in httpProxyConfig) ? httpProxyConfig : null);
   373   }
   375   function setHttpProxy(network) {
   376     if (!network)
   377       return;
   379     gNetworkService.setNetworkProxy(network);
   380   }
   382   var staticIpConfig = Object.create(null);
   383   function setStaticIpMode(network, info, callback) {
   384     let setNetworkKey = getNetworkKey(network);
   385     let curNetworkKey = null;
   386     let currentNetwork = Object.create(null);
   387     currentNetwork.netId = manager.connectionInfo.id;
   389     manager.getNetworkConfiguration(currentNetwork, function (){
   390       curNetworkKey = getNetworkKey(currentNetwork);
   392       // Add additional information to static ip configuration
   393       // It is used to compatiable with information dhcp callback.
   394       info.ipaddr = stringToIp(info.ipaddr_str);
   395       info.gateway = stringToIp(info.gateway_str);
   396       info.mask_str = makeMask(info.maskLength);
   398       // Optional
   399       info.dns1 = stringToIp("dns1_str" in info ? info.dns1_str : "");
   400       info.dns2 = stringToIp("dns2_str" in info ? info.dns2_str : "");
   401       info.proxy = stringToIp("proxy_str" in info ? info.proxy_str : "");
   403       staticIpConfig[setNetworkKey] = info;
   405       // If the ssid of current connection is the same as configured ssid
   406       // It means we need update current connection to use static IP address.
   407       if (setNetworkKey == curNetworkKey) {
   408         // Use configureInterface directly doesn't work, the network iterface
   409         // and routing table is changed but still cannot connect to network
   410         // so the workaround here is disable interface the enable again to
   411         // trigger network reconnect with static ip.
   412         netUtil.disableInterface(manager.ifname, function (ok) {
   413           netUtil.enableInterface(manager.ifname, function (ok) {
   414           });
   415         });
   416       }
   417     });
   418   }
   420   var dhcpInfo = null;
   422   function runStaticIp(ifname, key) {
   423     debug("Run static ip");
   425     // Read static ip information from settings.
   426     let staticIpInfo;
   428     if (!(key in staticIpConfig))
   429       return;
   431     staticIpInfo = staticIpConfig[key];
   433     // Stop dhcpd when use static IP
   434     if (dhcpInfo != null) {
   435       netUtil.stopDhcp(manager.ifname, function() {});
   436     }
   438     // Set ip, mask length, gateway, dns to network interface
   439     netUtil.configureInterface( { ifname: ifname,
   440                                   ipaddr: staticIpInfo.ipaddr,
   441                                   mask: staticIpInfo.maskLength,
   442                                   gateway: staticIpInfo.gateway,
   443                                   dns1: staticIpInfo.dns1,
   444                                   dns2: staticIpInfo.dns2 }, function (data) {
   445       netUtil.runIpConfig(ifname, staticIpInfo, function(data) {
   446         dhcpInfo = data.info;
   447         notify("networkconnected", data);
   448       });
   449     });
   450   }
   452   var suppressEvents = false;
   453   function notify(eventName, eventObject) {
   454     if (suppressEvents)
   455       return;
   456     var handler = manager["on" + eventName];
   457     if (handler) {
   458       if (!eventObject)
   459         eventObject = ({});
   460       handler.call(eventObject);
   461     }
   462   }
   464   function notifyStateChange(fields) {
   465     // If we're already in the COMPLETED state, we might receive events from
   466     // the supplicant that tell us that we're re-authenticating or reminding
   467     // us that we're associated to a network. In those cases, we don't need to
   468     // do anything, so just ignore them.
   469     if (manager.state === "COMPLETED" &&
   470         fields.state !== "DISCONNECTED" &&
   471         fields.state !== "INTERFACE_DISABLED" &&
   472         fields.state !== "INACTIVE" &&
   473         fields.state !== "SCANNING") {
   474       return false;
   475     }
   477     // Stop background scanning if we're trying to connect to a network.
   478     if (manager.backgroundScanEnabled &&
   479         (fields.state === "ASSOCIATING" ||
   480          fields.state === "ASSOCIATED" ||
   481          fields.state === "FOUR_WAY_HANDSHAKE" ||
   482          fields.state === "GROUP_HANDSHAKE" ||
   483          fields.state === "COMPLETED")) {
   484       setBackgroundScan("OFF", function() {});
   485     }
   487     fields.prevState = manager.state;
   488     // Detect wpa_supplicant's loop iterations.
   489     manager.supplicantLoopDetection(fields.prevState, fields.state);
   490     notify("statechange", fields);
   492     // Don't update state when and after disabling.
   493     if (manager.state === "DISABLING" ||
   494         manager.state === "UNINITIALIZED") {
   495       return false;
   496     }
   498     manager.state = fields.state;
   499     return true;
   500   }
   502   function parseStatus(status) {
   503     if (status === null) {
   504       debug("Unable to get wpa supplicant's status");
   505       return;
   506     }
   508     var ssid;
   509     var bssid;
   510     var state;
   511     var ip_address;
   512     var id;
   514     var lines = status.split("\n");
   515     for (let i = 0; i < lines.length; ++i) {
   516       let [key, value] = lines[i].split("=");
   517       switch (key) {
   518         case "wpa_state":
   519           state = value;
   520           break;
   521         case "ssid":
   522           ssid = value;
   523           break;
   524         case "bssid":
   525           bssid = value;
   526           break;
   527         case "ip_address":
   528           ip_address = value;
   529           break;
   530         case "id":
   531           id = value;
   532           break;
   533       }
   534     }
   536     if (bssid && ssid) {
   537       manager.connectionInfo.bssid = bssid;
   538       manager.connectionInfo.ssid = ssid;
   539       manager.connectionInfo.id = id;
   540     }
   542     if (ip_address)
   543       dhcpInfo = { ip_address: ip_address };
   545     notifyStateChange({ state: state, fromStatus: true });
   547     // If we parse the status and the supplicant has already entered the
   548     // COMPLETED state, then we need to set up DHCP right away.
   549     if (state === "COMPLETED")
   550       onconnected();
   551   }
   553   // try to connect to the supplicant
   554   var connectTries = 0;
   555   var retryTimer = null;
   556   function connectCallback(ok) {
   557     if (ok === 0) {
   558       // Tell the event worker to start waiting for events.
   559       retryTimer = null;
   560       connectTries = 0;
   561       recvErrors = 0;
   562       manager.connectToSupplicant = true;
   563       didConnectSupplicant(function(){});
   564       return;
   565     }
   566     if (connectTries++ < 5) {
   567       // Try again in 1 seconds.
   568       if (!retryTimer)
   569         retryTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   571       retryTimer.initWithCallback(function(timer) {
   572         wifiCommand.connectToSupplicant(connectCallback);
   573       }, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
   574       return;
   575     }
   577     retryTimer = null;
   578     connectTries = 0;
   579     notify("supplicantlost", { success: false });
   580   }
   582   manager.connectionDropped = function(callback) {
   583     // Reset network interface when connection drop
   584     netUtil.configureInterface( { ifname: manager.ifname,
   585                                   ipaddr: 0,
   586                                   mask: 0,
   587                                   gateway: 0,
   588                                   dns1: 0,
   589                                   dns2: 0 }, function (data) {
   590     });
   592     // If we got disconnected, kill the DHCP client in preparation for
   593     // reconnection.
   594     netUtil.resetConnections(manager.ifname, function() {
   595       netUtil.stopDhcp(manager.ifname, function() {
   596         callback();
   597       });
   598     });
   599   }
   601   manager.start = function() {
   602     debug("detected SDK version " + sdkVersion);
   603     wifiCommand.connectToSupplicant(connectCallback);
   604   }
   606   function onconnected() {
   607     // For now we do our own DHCP. In the future, this should be handed
   608     // off to the Network Manager.
   609     let currentNetwork = Object.create(null);
   610     currentNetwork.netId = manager.connectionInfo.id;
   612     manager.getNetworkConfiguration(currentNetwork, function (){
   613       let key = getNetworkKey(currentNetwork);
   614       if (staticIpConfig  &&
   615           (key in staticIpConfig) &&
   616           staticIpConfig[key].enabled) {
   617           debug("Run static ip");
   618           runStaticIp(manager.ifname, key);
   619           return;
   620       }
   621       netUtil.runDhcp(manager.ifname, function(data) {
   622         dhcpInfo = data.info;
   623         if (!dhcpInfo) {
   624           if (++manager.dhcpFailuresCount >= MAX_RETRIES_ON_DHCP_FAILURE) {
   625             manager.dhcpFailuresCount = 0;
   626             notify("disconnected", {ssid: manager.connectionInfo.ssid});
   627             return;
   628           }
   629           // NB: We have to call disconnect first. Otherwise, we only reauth with
   630           // the existing AP and don't retrigger DHCP.
   631           manager.disconnect(function() {
   632             manager.reassociate(function(){});
   633           });
   634           return;
   635         }
   637         manager.dhcpFailuresCount = 0;
   638         notify("networkconnected", data);
   639       });
   640     });
   641   }
   643   var supplicantStatesMap = (sdkVersion >= 15) ?
   644     ["DISCONNECTED", "INTERFACE_DISABLED", "INACTIVE", "SCANNING",
   645      "AUTHENTICATING", "ASSOCIATING", "ASSOCIATED", "FOUR_WAY_HANDSHAKE",
   646      "GROUP_HANDSHAKE", "COMPLETED"]
   647     :
   648     ["DISCONNECTED", "INACTIVE", "SCANNING", "ASSOCIATING",
   649      "ASSOCIATED", "FOUR_WAY_HANDSHAKE", "GROUP_HANDSHAKE",
   650      "COMPLETED", "DORMANT", "UNINITIALIZED"];
   652   var driverEventMap = { STOPPED: "driverstopped", STARTED: "driverstarted", HANGED: "driverhung" };
   654   manager.getCurrentNetworkId = function (ssid, callback) {
   655     manager.getConfiguredNetworks(function(networks) {
   656       if (!networks) {
   657         debug("Unable to get configured networks");
   658         return callback(null);
   659       }
   660       for (let net in networks) {
   661         let network = networks[net];
   662         // Trying to get netId from
   663         // 1. CURRENT network.
   664         // 2. Trying to associate with SSID 'ssid' event
   665         if (network.status === "CURRENT" ||
   666             (ssid && ssid === dequote(network.ssid))) {
   667           return callback(net);
   668         }
   669       }
   670       callback(null);
   671     });
   672   }
   674   // Handle events sent to us by the event worker.
   675   function handleEvent(event) {
   676     debug("Event coming in: " + event);
   677     if (event.indexOf("CTRL-EVENT-") !== 0 && event.indexOf("WPS") !== 0) {
   678       // Handle connection fail exception on WEP-128, while password length
   679       // is not 5 nor 13 bytes.
   680       if (event.indexOf("Association request to the driver failed") !== -1) {
   681         notify("passwordmaybeincorrect");
   682         if (manager.authenticationFailuresCount > MAX_RETRIES_ON_AUTHENTICATION_FAILURE) {
   683           manager.authenticationFailuresCount = 0;
   684           notify("disconnected", {ssid: manager.connectionInfo.ssid});
   685         }
   686         return true;
   687       }
   689       if (event.indexOf("WPA:") == 0 &&
   690           event.indexOf("pre-shared key may be incorrect") != -1) {
   691         notify("passwordmaybeincorrect");
   692       }
   694       // This is ugly, but we need to grab the SSID here. BSSID is not guaranteed
   695       // to be provided, so don't grab BSSID here.
   696       var match = /Trying to associate with.*SSID[ =]'(.*)'/.exec(event);
   697       if (match) {
   698         debug("Matched: " + match[1] + "\n");
   699         manager.connectionInfo.ssid = match[1];
   700       }
   701       return true;
   702     }
   704     var space = event.indexOf(" ");
   705     var eventData = event.substr(0, space + 1);
   706     if (eventData.indexOf("CTRL-EVENT-STATE-CHANGE") === 0) {
   707       // Parse the event data.
   708       var fields = {};
   709       var tokens = event.substr(space + 1).split(" ");
   710       for (var n = 0; n < tokens.length; ++n) {
   711         var kv = tokens[n].split("=");
   712         if (kv.length === 2)
   713           fields[kv[0]] = kv[1];
   714       }
   715       if (!("state" in fields))
   716         return true;
   717       fields.state = supplicantStatesMap[fields.state];
   719       // The BSSID field is only valid in the ASSOCIATING and ASSOCIATED
   720       // states, except when we "reauth", except this seems to depend on the
   721       // driver, so simply check to make sure that we don't have a null BSSID.
   722       if (fields.BSSID !== "00:00:00:00:00:00")
   723         manager.connectionInfo.bssid = fields.BSSID;
   725       if (notifyStateChange(fields) && fields.state === "COMPLETED") {
   726         onconnected();
   727       }
   728       return true;
   729     }
   730     if (eventData.indexOf("CTRL-EVENT-DRIVER-STATE") === 0) {
   731       var handlerName = driverEventMap[eventData];
   732       if (handlerName)
   733         notify(handlerName);
   734       return true;
   735     }
   736     if (eventData.indexOf("CTRL-EVENT-TERMINATING") === 0) {
   737       // As long the monitor socket is not closed and we haven't seen too many
   738       // recv errors yet, we will keep going for a bit longer.
   739       if (event.indexOf("connection closed") === -1 &&
   740           event.indexOf("recv error") !== -1 && ++recvErrors < 10)
   741         return true;
   743       notifyStateChange({ state: "DISCONNECTED", BSSID: null, id: -1 });
   745       // If the supplicant is terminated as commanded, the supplicant lost
   746       // notification will be sent after driver unloaded. In such case, the
   747       // manager state will be "DISABLING" or "UNINITIALIZED".
   748       // So if supplicant terminated with incorrect manager state, implying
   749       // unexpected condition, we should notify supplicant lost here.
   750       if (manager.state !== "DISABLING" && manager.state !== "UNINITIALIZED") {
   751         notify("supplicantlost", { success: true });
   752       }
   753       wifiCommand.closeSupplicantConnection(function() {
   754         manager.connectToSupplicant = false;
   755       });
   756       return false;
   757     }
   758     if (eventData.indexOf("CTRL-EVENT-DISCONNECTED") === 0) {
   759       var token = event.split(" ")[1];
   760       var bssid = token.split("=")[1];
   761       if (manager.authenticationFailuresCount > MAX_RETRIES_ON_AUTHENTICATION_FAILURE) {
   762         manager.authenticationFailuresCount = 0;
   763         notify("disconnected", {ssid: manager.connectionInfo.ssid});
   764       }
   765       manager.connectionInfo.bssid = null;
   766       manager.connectionInfo.ssid = null;
   767       manager.connectionInfo.id = -1;
   768       return true;
   769     }
   770     // Association reject is triggered mostly on incorrect WEP key.
   771     if (eventData.indexOf("CTRL-EVENT-ASSOC-REJECT") === 0) {
   772       notify("passwordmaybeincorrect");
   773       if (manager.authenticationFailuresCount > MAX_RETRIES_ON_AUTHENTICATION_FAILURE) {
   774         manager.authenticationFailuresCount = 0;
   775         notify("disconnected", {ssid: manager.connectionInfo.ssid});
   776       }
   777       return true;
   778     }
   779     if (eventData.indexOf("CTRL-EVENT-EAP-FAILURE") === 0) {
   780       if (event.indexOf("EAP authentication failed") !== -1) {
   781         notify("passwordmaybeincorrect");
   782       }
   783       return true;
   784     }
   785     if (eventData.indexOf("CTRL-EVENT-CONNECTED") === 0) {
   786       // Format: CTRL-EVENT-CONNECTED - Connection to 00:1e:58:ec:d5:6d completed (reauth) [id=1 id_str=]
   787       var bssid = event.split(" ")[4];
   789       var keyword = "id=";
   790       var id = event.substr(event.indexOf(keyword) + keyword.length).split(" ")[0];
   791       // Read current BSSID here, it will always being provided.
   792       manager.connectionInfo.id = id;
   793       manager.connectionInfo.bssid = bssid;
   794       return true;
   795     }
   796     if (eventData.indexOf("CTRL-EVENT-SCAN-RESULTS") === 0) {
   797       debug("Notifying of scan results available");
   798       if (reEnableBackgroundScan) {
   799         reEnableBackgroundScan = false;
   800         setBackgroundScan("ON", function() {});
   801       }
   802       manager.handlePostWifiScan();
   803       notify("scanresultsavailable");
   804       return true;
   805     }
   806     if (eventData.indexOf("WPS-TIMEOUT") === 0) {
   807       notifyStateChange({ state: "WPS_TIMEOUT", BSSID: null, id: -1 });
   808       return true;
   809     }
   810     if (eventData.indexOf("WPS-FAIL") === 0) {
   811       notifyStateChange({ state: "WPS_FAIL", BSSID: null, id: -1 });
   812       return true;
   813     }
   814     if (eventData.indexOf("WPS-OVERLAP-DETECTED") === 0) {
   815       notifyStateChange({ state: "WPS_OVERLAP_DETECTED", BSSID: null, id: -1 });
   816       return true;
   817     }
   818     // Unknown event.
   819     return true;
   820   }
   822   function didConnectSupplicant(callback) {
   823     waitForEvent(manager.ifname);
   825     // Load up the supplicant state.
   826     getDebugEnabled(function(ok) {
   827       syncDebug();
   828     });
   829     wifiCommand.status(function(status) {
   830       parseStatus(status);
   831       notify("supplicantconnection");
   832       callback();
   833     });
   835     if (p2pSupported) {
   836       manager.enableP2p(function(success) {});
   837     }
   838   }
   840   function prepareForStartup(callback) {
   841     let status = libcutils.property_get(DHCP_PROP + "_" + manager.ifname);
   842     if (status !== "running") {
   843       tryStopSupplicant();
   844       return;
   845     }
   846     manager.connectionDropped(function() {
   847       tryStopSupplicant();
   848     });
   850     // Ignore any errors and kill any currently-running supplicants. On some
   851     // phones, stopSupplicant won't work for a supplicant that we didn't
   852     // start, so we hand-roll it here.
   853     function tryStopSupplicant () {
   854       let status = libcutils.property_get(SUPP_PROP);
   855       if (status !== "running") {
   856         callback();
   857         return;
   858       }
   859       suppressEvents = true;
   860       wifiCommand.killSupplicant(function() {
   861         netUtil.disableInterface(manager.ifname, function (ok) {
   862           suppressEvents = false;
   863           callback();
   864         });
   865       });
   866     }
   867   }
   869   // Initial state.
   870   manager.state = "UNINITIALIZED";
   871   manager.tetheringState = "UNINITIALIZED";
   872   manager.enabled = false;
   873   manager.supplicantStarted = false;
   874   manager.connectionInfo = { ssid: null, bssid: null, id: -1 };
   875   manager.authenticationFailuresCount = 0;
   876   manager.loopDetectionCount = 0;
   877   manager.dhcpFailuresCount = 0;
   879   var waitForDriverReadyTimer = null;
   880   function cancelWaitForDriverReadyTimer() {
   881     if (waitForDriverReadyTimer) {
   882       waitForDriverReadyTimer.cancel();
   883       waitForDriverReadyTimer = null;
   884     }
   885   };
   886   function createWaitForDriverReadyTimer(onTimeout) {
   887     waitForDriverReadyTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
   888     waitForDriverReadyTimer.initWithCallback(onTimeout,
   889                                              manager.driverDelay,
   890                                              Ci.nsITimer.TYPE_ONE_SHOT);
   891   };
   893   // Public interface of the wifi service.
   894   manager.setWifiEnabled = function(enabled, callback) {
   895     if (enabled === manager.isWifiEnabled(manager.state)) {
   896       callback("no change");
   897       return;
   898     }
   900     if (enabled) {
   901       manager.state = "INITIALIZING";
   902       // Register as network interface.
   903       WifiNetworkInterface.name = manager.ifname;
   904       if (!WifiNetworkInterface.registered) {
   905         gNetworkManager.registerNetworkInterface(WifiNetworkInterface);
   906         WifiNetworkInterface.registered = true;
   907       }
   908       WifiNetworkInterface.state = Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED;
   909       WifiNetworkInterface.ips = [];
   910       WifiNetworkInterface.prefixLengths = [];
   911       WifiNetworkInterface.gateways = [];
   912       WifiNetworkInterface.dnses = [];
   913       Services.obs.notifyObservers(WifiNetworkInterface,
   914                                    kNetworkInterfaceStateChangedTopic,
   915                                    null);
   916       prepareForStartup(function() {
   917         loadDriver(function (status) {
   918           if (status < 0) {
   919             callback(status);
   920             manager.state = "UNINITIALIZED";
   921             return;
   922           }
   923           // This command is mandatory for Nexus 4. But some devices like
   924           // Galaxy S2 don't support it. Continue to start wpa_supplicant
   925           // even if we fail to set wifi operation mode to station.
   926           gNetworkService.setWifiOperationMode(manager.ifname,
   927                                                WIFI_FIRMWARE_STATION,
   928                                                function (status) {
   930             function startSupplicantInternal() {
   931               wifiCommand.startSupplicant(function (status) {
   932                 if (status < 0) {
   933                   unloadDriver(WIFI_FIRMWARE_STATION, function() {
   934                     callback(status);
   935                   });
   936                   manager.state = "UNINITIALIZED";
   937                   return;
   938                 }
   940                 manager.supplicantStarted = true;
   941                 netUtil.enableInterface(manager.ifname, function (ok) {
   942                   callback(ok ? 0 : -1);
   943                 });
   944               });
   945             }
   947             function doStartSupplicant() {
   948               cancelWaitForDriverReadyTimer();
   950               if (!manager.connectToSupplicant) {
   951                 startSupplicantInternal();
   952                 return;
   953               }
   954               wifiCommand.closeSupplicantConnection(function () {
   955                 manager.connectToSupplicant = false;
   956                 // closeSupplicantConnection() will trigger onsupplicantlost
   957                 // and set manager.state to "UNINITIALIZED", we have to
   958                 // restore it here.
   959                 manager.state = "INITIALIZING";
   960                 startSupplicantInternal();
   961               });
   962             }
   963             // Driver startup on certain platforms takes longer than it takes for us
   964             // to return from loadDriver, so wait 2 seconds before starting
   965             // the supplicant to give it a chance to start.
   966             if (manager.driverDelay > 0) {
   967               createWaitForDriverReadyTimer(doStartSupplicant);
   968             } else {
   969               doStartSupplicant();
   970             }
   971           });
   972         });
   973       });
   974     } else {
   975       manager.state = "DISABLING";
   976       // Note these following calls ignore errors. If we fail to kill the
   977       // supplicant gracefully, then we need to continue telling it to die
   978       // until it does.
   979       let doDisableWifi = function() {
   980         wifiCommand.terminateSupplicant(function (ok) {
   981           manager.connectionDropped(function () {
   982             wifiCommand.stopSupplicant(function (status) {
   983               manager.state = "UNINITIALIZED";
   984               netUtil.disableInterface(manager.ifname, function (ok) {
   985                 unloadDriver(WIFI_FIRMWARE_STATION, callback);
   986               });
   987             });
   988           });
   989         });
   990       }
   992       if (p2pSupported) {
   993         p2pManager.setEnabled(false, { onDisabled: doDisableWifi });
   994       } else {
   995         doDisableWifi();
   996       }
   997     }
   998   }
  1000   // Get wifi interface and load wifi driver when enable Ap mode.
  1001   manager.setWifiApEnabled = function(enabled, configuration, callback) {
  1002     if (enabled === manager.isWifiTetheringEnabled(manager.tetheringState)) {
  1003       callback("no change");
  1004       return;
  1007     if (enabled) {
  1008       manager.tetheringState = "INITIALIZING";
  1009       loadDriver(function (status) {
  1010         if (status < 0) {
  1011           callback();
  1012           manager.tetheringState = "UNINITIALIZED";
  1013           return;
  1016         function doStartWifiTethering() {
  1017           cancelWaitForDriverReadyTimer();
  1018           WifiNetworkInterface.name = manager.ifname;
  1019           gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface,
  1020                                            configuration, function(result) {
  1021             if (result) {
  1022               manager.tetheringState = "UNINITIALIZED";
  1023             } else {
  1024               manager.tetheringState = "COMPLETED";
  1026             // Pop out current request.
  1027             callback();
  1028             // Should we fire a dom event if we fail to set wifi tethering  ?
  1029             debug("Enable Wifi tethering result: " + (result ? result : "successfully"));
  1030           });
  1033         // Driver startup on certain platforms takes longer than it takes
  1034         // for us to return from loadDriver, so wait 2 seconds before
  1035         // turning on Wifi tethering.
  1036         createWaitForDriverReadyTimer(doStartWifiTethering);
  1037       });
  1038     } else {
  1039       gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface,
  1040                                        configuration, function(result) {
  1041         // Should we fire a dom event if we fail to set wifi tethering  ?
  1042         debug("Disable Wifi tethering result: " + (result ? result : "successfully"));
  1043         // Unload wifi driver even if we fail to control wifi tethering.
  1044         unloadDriver(WIFI_FIRMWARE_AP, function(status) {
  1045           if (status < 0) {
  1046             debug("Fail to unload wifi driver");
  1048           manager.tetheringState = "UNINITIALIZED";
  1049           callback();
  1050         });
  1051       });
  1055   manager.disconnect = wifiCommand.disconnect;
  1056   manager.reconnect = wifiCommand.reconnect;
  1057   manager.reassociate = wifiCommand.reassociate;
  1059   var networkConfigurationFields = [
  1060     "ssid", "bssid", "psk", "wep_key0", "wep_key1", "wep_key2", "wep_key3",
  1061     "wep_tx_keyidx", "priority", "key_mgmt", "scan_ssid", "disabled",
  1062     "identity", "password", "auth_alg", "phase1", "phase2", "eap", "pin",
  1063     "pcsc"
  1064   ];
  1066   manager.getNetworkConfiguration = function(config, callback) {
  1067     var netId = config.netId;
  1068     var done = 0;
  1069     for (var n = 0; n < networkConfigurationFields.length; ++n) {
  1070       let fieldName = networkConfigurationFields[n];
  1071       wifiCommand.getNetworkVariable(netId, fieldName, function(value) {
  1072         if (value !== null)
  1073           config[fieldName] = value;
  1074         if (++done == networkConfigurationFields.length)
  1075           callback(config);
  1076       });
  1079   manager.setNetworkConfiguration = function(config, callback) {
  1080     var netId = config.netId;
  1081     var done = 0;
  1082     var errors = 0;
  1083     for (var n = 0; n < networkConfigurationFields.length; ++n) {
  1084       let fieldName = networkConfigurationFields[n];
  1085       if (!(fieldName in config) ||
  1086           // These fields are special: We can't retrieve them from the
  1087           // supplicant, and often we have a star in our config. In that case,
  1088           // we need to avoid overwriting the correct password with a *.
  1089           (fieldName === "password" ||
  1090            fieldName === "wep_key0" ||
  1091            fieldName === "psk") &&
  1092           config[fieldName] === '*') {
  1093         ++done;
  1094       } else {
  1095         wifiCommand.setNetworkVariable(netId, fieldName, config[fieldName], function(ok) {
  1096           if (!ok)
  1097             ++errors;
  1098           if (++done == networkConfigurationFields.length)
  1099             callback(errors == 0);
  1100         });
  1103     // If config didn't contain any of the fields we want, don't lose the error callback.
  1104     if (done == networkConfigurationFields.length)
  1105       callback(false);
  1107   manager.getConfiguredNetworks = function(callback) {
  1108     wifiCommand.listNetworks(function (reply) {
  1109       var networks = Object.create(null);
  1110       var lines = reply ? reply.split("\n") : 0;
  1111       if (lines.length <= 1) {
  1112         // We need to make sure we call the callback even if there are no
  1113         // configured networks.
  1114         callback(networks);
  1115         return;
  1118       var done = 0;
  1119       var errors = 0;
  1120       for (var n = 1; n < lines.length; ++n) {
  1121         var result = lines[n].split("\t");
  1122         var netId = result[0];
  1123         var config = networks[netId] = { netId: netId };
  1124         switch (result[3]) {
  1125         case "[CURRENT]":
  1126           config.status = "CURRENT";
  1127           break;
  1128         case "[DISABLED]":
  1129           config.status = "DISABLED";
  1130           break;
  1131         default:
  1132           config.status = "ENABLED";
  1133           break;
  1135         manager.getNetworkConfiguration(config, function (ok) {
  1136             if (!ok)
  1137               ++errors;
  1138             if (++done == lines.length - 1) {
  1139               if (errors) {
  1140                 // If an error occured, delete the new netId.
  1141                 wifiCommand.removeNetwork(netId, function() {
  1142                   callback(null);
  1143                 });
  1144               } else {
  1145                 callback(networks);
  1148         });
  1150     });
  1152   manager.addNetwork = function(config, callback) {
  1153     wifiCommand.addNetwork(function (netId) {
  1154       config.netId = netId;
  1155       manager.setNetworkConfiguration(config, function (ok) {
  1156         if (!ok) {
  1157           wifiCommand.removeNetwork(netId, function() { callback(false); });
  1158           return;
  1161         callback(ok);
  1162       });
  1163     });
  1165   manager.updateNetwork = function(config, callback) {
  1166     manager.setNetworkConfiguration(config, callback);
  1168   manager.removeNetwork = function(netId, callback) {
  1169     wifiCommand.removeNetwork(netId, callback);
  1172   function stringToIp(string) {
  1173     let ip = 0;
  1174     let start, end = -1;
  1175     for (let i = 0; i < 4; i++) {
  1176       start = end + 1;
  1177       end = string.indexOf(".", start);
  1178       if (end == -1) {
  1179         end = string.length;
  1181       let num = parseInt(string.slice(start, end), 10);
  1182       if (isNaN(num)) {
  1183         return 0;
  1185       ip |= num << (i * 8);
  1187     return ip;
  1190   function swap32(n) {
  1191     return (((n >> 24) & 0xFF) <<  0) |
  1192            (((n >> 16) & 0xFF) <<  8) |
  1193            (((n >>  8) & 0xFF) << 16) |
  1194            (((n >>  0) & 0xFF) << 24);
  1197   function ntohl(n) {
  1198     return swap32(n);
  1201   function makeMask(len) {
  1202     let mask = 0;
  1203     for (let i = 0; i < len; ++i) {
  1204       mask |= (0x80000000 >> i);
  1206     return ntohl(mask);
  1209   manager.saveConfig = function(callback) {
  1210     wifiCommand.saveConfig(callback);
  1212   manager.enableNetwork = function(netId, disableOthers, callback) {
  1213     if (p2pSupported) {
  1214       // We have to stop wifi direct scan before associating to an AP.
  1215       // Otherwise we will get a "REJECT" wpa supplicant event.
  1216       p2pManager.setScanEnabled(false, function(success) {});
  1218     wifiCommand.enableNetwork(netId, disableOthers, callback);
  1220   manager.disableNetwork = function(netId, callback) {
  1221     wifiCommand.disableNetwork(netId, callback);
  1223   manager.getMacAddress = wifiCommand.getMacAddress;
  1224   manager.getScanResults = wifiCommand.scanResults;
  1225   manager.setScanMode = function(mode, callback) {
  1226     setScanMode(mode === "active", callback); // Use our own version.
  1228   manager.setBackgroundScan = setBackgroundScan; // Use our own version.
  1229   manager.scan = scan; // Use our own version.
  1230   manager.wpsPbc = wifiCommand.wpsPbc;
  1231   manager.wpsPin = wifiCommand.wpsPin;
  1232   manager.wpsCancel = wifiCommand.wpsCancel;
  1233   manager.setPowerMode = (sdkVersion >= 16)
  1234                          ? wifiCommand.setPowerModeJB
  1235                          : wifiCommand.setPowerModeICS;
  1236   manager.getHttpProxyNetwork = getHttpProxyNetwork;
  1237   manager.setHttpProxy = setHttpProxy;
  1238   manager.configureHttpProxy = configureHttpProxy;
  1239   manager.setSuspendOptimizations = (sdkVersion >= 16)
  1240                                    ? wifiCommand.setSuspendOptimizationsJB
  1241                                    : wifiCommand.setSuspendOptimizationsICS;
  1242   manager.setStaticIpMode = setStaticIpMode;
  1243   manager.getRssiApprox = wifiCommand.getRssiApprox;
  1244   manager.getLinkSpeed = wifiCommand.getLinkSpeed;
  1245   manager.getDhcpInfo = function() { return dhcpInfo; }
  1246   manager.getConnectionInfo = (sdkVersion >= 15)
  1247                               ? wifiCommand.getConnectionInfoICS
  1248                               : wifiCommand.getConnectionInfoGB;
  1250   manager.isHandShakeState = function(state) {
  1251     switch (state) {
  1252       case "AUTHENTICATING":
  1253       case "ASSOCIATING":
  1254       case "ASSOCIATED":
  1255       case "FOUR_WAY_HANDSHAKE":
  1256       case "GROUP_HANDSHAKE":
  1257         return true;
  1258       case "DORMANT":
  1259       case "COMPLETED":
  1260       case "DISCONNECTED":
  1261       case "INTERFACE_DISABLED":
  1262       case "INACTIVE":
  1263       case "SCANNING":
  1264       case "UNINITIALIZED":
  1265       case "INVALID":
  1266       case "CONNECTED":
  1267       default:
  1268         return false;
  1272   manager.isWifiEnabled = function(state) {
  1273     switch (state) {
  1274       case "UNINITIALIZED":
  1275       case "DISABLING":
  1276         return false;
  1277       default:
  1278         return true;
  1282   manager.isWifiTetheringEnabled = function(state) {
  1283     switch (state) {
  1284       case "UNINITIALIZED":
  1285         return false;
  1286       default:
  1287         return true;
  1291   manager.syncDebug = syncDebug;
  1292   manager.stateOrdinal = function(state) {
  1293     return supplicantStatesMap.indexOf(state);
  1295   manager.supplicantLoopDetection = function(prevState, state) {
  1296     var isPrevStateInHandShake = manager.isHandShakeState(prevState);
  1297     var isStateInHandShake = manager.isHandShakeState(state);
  1299     if (isPrevStateInHandShake) {
  1300       if (isStateInHandShake) {
  1301         // Increase the count only if we are in the loop.
  1302         if (manager.stateOrdinal(state) > manager.stateOrdinal(prevState)) {
  1303           manager.loopDetectionCount++;
  1305         if (manager.loopDetectionCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
  1306           notify("disconnected", {ssid: manager.connectionInfo.ssid});
  1307           manager.loopDetectionCount = 0;
  1310     } else {
  1311       // From others state to HandShake state. Reset the count.
  1312       if (isStateInHandShake) {
  1313         manager.loopDetectionCount = 0;
  1318   manager.handlePreWifiScan = function() {
  1319     if (p2pSupported) {
  1320       // Before doing regular wifi scan, we have to disable wifi direct
  1321       // scan first. Otherwise we will never get the scan result.
  1322       p2pManager.blockScan();
  1324   };
  1326   manager.handlePostWifiScan = function() {
  1327     if (p2pSupported) {
  1328       // After regular wifi scanning, we should restore the restricted
  1329       // wifi direct scan.
  1330       p2pManager.unblockScan();
  1332   };
  1334   //
  1335   // Public APIs for P2P.
  1336   //
  1338   manager.p2pSupported = function() {
  1339     return p2pSupported;
  1340   };
  1342   manager.getP2pManager = function() {
  1343     return p2pManager;
  1344   };
  1346   manager.enableP2p = function(callback) {
  1347     p2pManager.setEnabled(true, {
  1348       onSupplicantConnected: function() {
  1349         waitForEvent(WifiP2pManager.INTERFACE_NAME);
  1350       },
  1352       onEnabled: function(success) {
  1353         callback(success);
  1355     });
  1356   };
  1358   manager.getCapabilities = function() {
  1359     return capabilities;
  1362   return manager;
  1363 })();
  1365 // Get unique key for a network, now the key is created by escape(SSID)+Security.
  1366 // So networks of same SSID but different security mode can be identified.
  1367 function getNetworkKey(network)
  1369   var ssid = "",
  1370       encryption = "OPEN";
  1372   if ("security" in network) {
  1373     // manager network object, represents an AP
  1374     // object structure
  1375     // {
  1376     //   .ssid           : SSID of AP
  1377     //   .security[]     : "WPA-PSK" for WPA-PSK
  1378     //                     "WPA-EAP" for WPA-EAP
  1379     //                     "WEP" for WEP
  1380     //                     "" for OPEN
  1381     //   other keys
  1382     // }
  1384     var security = network.security;
  1385     ssid = network.ssid;
  1387     for (let j = 0; j < security.length; j++) {
  1388       if (security[j] === "WPA-PSK") {
  1389         encryption = "WPA-PSK";
  1390         break;
  1391       } else if (security[j] === "WPA-EAP") {
  1392         encryption = "WPA-EAP";
  1393         break;
  1394       } else if (security[j] === "WEP") {
  1395         encryption = "WEP";
  1396         break;
  1399   } else if ("key_mgmt" in network) {
  1400     // configure network object, represents a network
  1401     // object structure
  1402     // {
  1403     //   .ssid           : SSID of network, quoted
  1404     //   .key_mgmt       : Encryption type
  1405     //                     "WPA-PSK" for WPA-PSK
  1406     //                     "WPA-EAP" for WPA-EAP
  1407     //                     "NONE" for WEP/OPEN
  1408     //   .auth_alg       : Encryption algorithm(WEP mode only)
  1409     //                     "OPEN_SHARED" for WEP
  1410     //   other keys
  1411     // }
  1412     var key_mgmt = network.key_mgmt,
  1413         auth_alg = network.auth_alg;
  1414     ssid = dequote(network.ssid);
  1416     if (key_mgmt == "WPA-PSK") {
  1417       encryption = "WPA-PSK";
  1418     } else if (key_mgmt == "WPA-EAP") {
  1419       encryption = "WPA-EAP";
  1420     } else if (key_mgmt == "NONE" && auth_alg === "OPEN SHARED") {
  1421       encryption = "WEP";
  1425   // ssid here must be dequoted, and it's safer to esacpe it.
  1426   // encryption won't be empty and always be assigned one of the followings :
  1427   // "OPEN"/"WEP"/"WPA-PSK"/"WPA-EAP".
  1428   // So for a invalid network object, the returned key will be "OPEN".
  1429   return escape(ssid) + encryption;
  1432 function getKeyManagement(flags) {
  1433   var types = [];
  1434   if (!flags)
  1435     return types;
  1437   if (/\[WPA2?-PSK/.test(flags))
  1438     types.push("WPA-PSK");
  1439   if (/\[WPA2?-EAP/.test(flags))
  1440     types.push("WPA-EAP");
  1441   if (/\[WEP/.test(flags))
  1442     types.push("WEP");
  1443   return types;
  1446 function getCapabilities(flags) {
  1447   var types = [];
  1448   if (!flags)
  1449     return types;
  1451   if (/\[WPS/.test(flags))
  1452     types.push("WPS");
  1453   return types;
  1456 // These constants shamelessly ripped from WifiManager.java
  1457 // strength is the value returned by scan_results. It is nominally in dB. We
  1458 // transform it into a percentage for clients looking to simply show a
  1459 // relative indication of the strength of a network.
  1460 const MIN_RSSI = -100;
  1461 const MAX_RSSI = -55;
  1463 function calculateSignal(strength) {
  1464   // Some wifi drivers represent their signal strengths as 8-bit integers, so
  1465   // in order to avoid negative numbers, they add 256 to the actual values.
  1466   // While we don't *know* that this is the case here, we make an educated
  1467   // guess.
  1468   if (strength > 0)
  1469     strength -= 256;
  1471   if (strength <= MIN_RSSI)
  1472     return 0;
  1473   if (strength >= MAX_RSSI)
  1474     return 100;
  1475   return Math.floor(((strength - MIN_RSSI) / (MAX_RSSI - MIN_RSSI)) * 100);
  1478 function Network(ssid, security, password, capabilities) {
  1479   this.ssid = ssid;
  1480   this.security = security;
  1482   if (typeof password !== "undefined")
  1483     this.password = password;
  1484   if (capabilities !== undefined)
  1485     this.capabilities = capabilities;
  1486   // TODO connected here as well?
  1488   this.__exposedProps__ = Network.api;
  1491 Network.api = {
  1492   ssid: "r",
  1493   security: "r",
  1494   capabilities: "r",
  1495   known: "r",
  1497   password: "rw",
  1498   keyManagement: "rw",
  1499   psk: "rw",
  1500   identity: "rw",
  1501   wep: "rw",
  1502   hidden: "rw",
  1503   eap: "rw",
  1504   pin: "rw",
  1505   phase1: "rw",
  1506   phase2: "rw"
  1507 };
  1509 // Note: We never use ScanResult.prototype, so the fact that it's unrelated to
  1510 // Network.prototype is OK.
  1511 function ScanResult(ssid, bssid, flags, signal) {
  1512   Network.call(this, ssid, getKeyManagement(flags), undefined,
  1513                getCapabilities(flags));
  1514   this.bssid = bssid;
  1515   this.signalStrength = signal;
  1516   this.relSignalStrength = calculateSignal(Number(signal));
  1518   this.__exposedProps__ = ScanResult.api;
  1521 // XXX This should probably live in the DOM-facing side, but it's hard to do
  1522 // there, so we stick this here.
  1523 ScanResult.api = {
  1524   bssid: "r",
  1525   signalStrength: "r",
  1526   relSignalStrength: "r",
  1527   connected: "r"
  1528 };
  1530 for (let i in Network.api) {
  1531   ScanResult.api[i] = Network.api[i];
  1534 function quote(s) {
  1535   return '"' + s + '"';
  1538 function dequote(s) {
  1539   if (s[0] != '"' || s[s.length - 1] != '"')
  1540     throw "Invalid argument, not a quoted string: " + s;
  1541   return s.substr(1, s.length - 2);
  1544 function isWepHexKey(s) {
  1545   if (s.length != 10 && s.length != 26 && s.length != 58)
  1546     return false;
  1547   return !/[^a-fA-F0-9]/.test(s);
  1551 let WifiNetworkInterface = {
  1553   QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterface]),
  1555   registered: false,
  1557   // nsINetworkInterface
  1559   NETWORK_STATE_UNKNOWN:       Ci.nsINetworkInterface.NETWORK_STATE_UNKNOWN,
  1560   NETWORK_STATE_CONNECTING:    Ci.nsINetworkInterface.CONNECTING,
  1561   NETWORK_STATE_CONNECTED:     Ci.nsINetworkInterface.CONNECTED,
  1562   NETWORK_STATE_DISCONNECTING: Ci.nsINetworkInterface.DISCONNECTING,
  1563   NETWORK_STATE_DISCONNECTED:  Ci.nsINetworkInterface.DISCONNECTED,
  1565   state: Ci.nsINetworkInterface.NETWORK_STATE_UNKNOWN,
  1567   NETWORK_TYPE_WIFI:        Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
  1568   NETWORK_TYPE_MOBILE:      Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
  1569   NETWORK_TYPE_MOBILE_MMS:  Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS,
  1570   NETWORK_TYPE_MOBILE_SUPL: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL,
  1572   type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
  1574   name: null,
  1576   ips: [],
  1578   prefixLengths: [],
  1580   dnses: [],
  1582   gateways: [],
  1584   httpProxyHost: null,
  1586   httpProxyPort: null,
  1588   getAddresses: function (ips, prefixLengths) {
  1589     ips.value = this.ips.slice();
  1590     prefixLengths.value = this.prefixLengths.slice();
  1592     return this.ips.length;
  1593   },
  1595   getGateways: function (count) {
  1596     if (count) {
  1597       count.value = this.gateways.length;
  1599     return this.gateways.slice();
  1600   },
  1602   getDnses: function (count) {
  1603     if (count) {
  1604       count.value = this.dnses.length;
  1606     return this.dnses.slice();
  1608 };
  1610 function WifiScanResult() {}
  1612 // TODO Make the difference between a DOM-based network object and our
  1613 // networks objects much clearer.
  1614 let netToDOM;
  1615 let netFromDOM;
  1617 function WifiWorker() {
  1618   var self = this;
  1620   this._mm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
  1621                .getService(Ci.nsIMessageListenerManager);
  1622   const messages = ["WifiManager:getNetworks", "WifiManager:getKnownNetworks",
  1623                     "WifiManager:associate", "WifiManager:forget",
  1624                     "WifiManager:wps", "WifiManager:getState",
  1625                     "WifiManager:setPowerSavingMode",
  1626                     "WifiManager:setHttpProxy",
  1627                     "WifiManager:setStaticIpMode",
  1628                     "child-process-shutdown"];
  1630   messages.forEach((function(msgName) {
  1631     this._mm.addMessageListener(msgName, this);
  1632   }).bind(this));
  1634   Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
  1636   this.wantScanResults = [];
  1638   this._allowWpaEap = false;
  1639   this._needToEnableNetworks = false;
  1640   this._highestPriority = -1;
  1642   // Networks is a map from SSID -> a scan result.
  1643   this.networks = Object.create(null);
  1645   // ConfiguredNetworks is a map from SSID -> our view of a network. It only
  1646   // lists networks known to the wpa_supplicant. The SSID field (and other
  1647   // fields) are quoted for ease of use with WifiManager commands.
  1648   // Note that we don't have to worry about escaping embedded quotes since in
  1649   // all cases, the supplicant will take the last quotation that we pass it as
  1650   // the end of the string.
  1651   this.configuredNetworks = Object.create(null);
  1652   this._addingNetworks = Object.create(null);
  1654   this.currentNetwork = null;
  1655   this.ipAddress = "";
  1656   this.macAddress = null;
  1658   this._lastConnectionInfo = null;
  1659   this._connectionInfoTimer = null;
  1660   this._reconnectOnDisconnect = false;
  1662   // Create p2pObserver and assign to p2pManager.
  1663   if (WifiManager.p2pSupported()) {
  1664     this._p2pObserver = WifiP2pWorkerObserver(WifiManager.getP2pManager());
  1665     WifiManager.getP2pManager().setObserver(this._p2pObserver);
  1667     // Add DOM message observerd by p2pObserver to the message listener as well.
  1668     this._p2pObserver.getObservedDOMMessages().forEach((function(msgName) {
  1669       this._mm.addMessageListener(msgName, this);
  1670     }).bind(this));
  1673   // Users of instances of nsITimer should keep a reference to the timer until
  1674   // it is no longer needed in order to assure the timer is fired.
  1675   this._callbackTimer = null;
  1677   // XXX On some phones (Otoro and Unagi) the wifi driver doesn't play nicely
  1678   // with the automatic scans that wpa_supplicant does (it appears that the
  1679   // driver forgets that it's returned scan results and then refuses to try to
  1680   // rescan. In order to detect this case we start a timer when we enter the
  1681   // SCANNING state and reset it whenever we either get scan results or leave
  1682   // the SCANNING state. If the timer fires, we assume that we are stuck and
  1683   // forceably try to unstick the supplican, also turning on background
  1684   // scanning to avoid having to constantly poke the supplicant.
  1686   // How long we wait is controlled by the SCAN_STUCK_WAIT constant.
  1687   const SCAN_STUCK_WAIT = 12000;
  1688   this._scanStuckTimer = null;
  1689   this._turnOnBackgroundScan = false;
  1691   function startScanStuckTimer() {
  1692     if (WifiManager.schedScanRecovery) {
  1693       self._scanStuckTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  1694       self._scanStuckTimer.initWithCallback(scanIsStuck, SCAN_STUCK_WAIT,
  1695                                             Ci.nsITimer.TYPE_ONE_SHOT);
  1699   function scanIsStuck() {
  1700     // Uh-oh, we've waited too long for scan results. Disconnect (which
  1701     // guarantees that we leave the SCANNING state and tells wpa_supplicant to
  1702     // wait for our next command) ensure that background scanning is on and
  1703     // then try again.
  1704     debug("Determined that scanning is stuck, turning on background scanning!");
  1705     WifiManager.handlePostWifiScan();
  1706     WifiManager.disconnect(function(ok) {});
  1707     self._turnOnBackgroundScan = true;
  1710   // A list of requests to turn wifi on or off.
  1711   this._stateRequests = [];
  1713   // Given a connection status network, takes a network from
  1714   // self.configuredNetworks and prepares it for the DOM.
  1715   netToDOM = function(net) {
  1716     var ssid = dequote(net.ssid);
  1717     var security = (net.key_mgmt === "NONE" && net.wep_key0) ? ["WEP"] :
  1718                    (net.key_mgmt && net.key_mgmt !== "NONE") ? [net.key_mgmt] :
  1719                    [];
  1720     var password;
  1721     if (("psk" in net && net.psk) ||
  1722         ("password" in net && net.password) ||
  1723         ("wep_key0" in net && net.wep_key0)) {
  1724       password = "*";
  1727     var pub = new Network(ssid, security, password);
  1728     if (net.identity)
  1729       pub.identity = dequote(net.identity);
  1730     if (net.netId)
  1731       pub.known = true;
  1732     if (net.scan_ssid === 1)
  1733       pub.hidden = true;
  1734     return pub;
  1735   };
  1737   netFromDOM = function(net, configured) {
  1738     // Takes a network from the DOM and makes it suitable for insertion into
  1739     // self.configuredNetworks (that is calling addNetwork will do the right
  1740     // thing).
  1741     // NB: Modifies net in place: safe since we don't share objects between
  1742     // the dom and the chrome code.
  1744     // Things that are useful for the UI but not to us.
  1745     delete net.bssid;
  1746     delete net.signalStrength;
  1747     delete net.relSignalStrength;
  1748     delete net.security;
  1749     delete net.capabilities;
  1751     if (!configured)
  1752       configured = {};
  1754     net.ssid = quote(net.ssid);
  1756     let wep = false;
  1757     if ("keyManagement" in net) {
  1758       if (net.keyManagement === "WEP") {
  1759         wep = true;
  1760         net.keyManagement = "NONE";
  1763       configured.key_mgmt = net.key_mgmt = net.keyManagement; // WPA2-PSK, WPA-PSK, etc.
  1764       delete net.keyManagement;
  1765     } else {
  1766       configured.key_mgmt = net.key_mgmt = "NONE";
  1769     if (net.hidden) {
  1770       configured.scan_ssid = net.scan_ssid = 1;
  1771       delete net.hidden;
  1774     function checkAssign(name, checkStar) {
  1775       if (name in net) {
  1776         let value = net[name];
  1777         if (!value || (checkStar && value === '*')) {
  1778           if (name in configured)
  1779             net[name] = configured[name];
  1780           else
  1781             delete net[name];
  1782         } else {
  1783           configured[name] = net[name] = quote(value);
  1788     checkAssign("psk", true);
  1789     checkAssign("identity", false);
  1790     checkAssign("password", true);
  1791     if (wep && net.wep && net.wep != '*') {
  1792       configured.wep_key0 = net.wep_key0 = isWepHexKey(net.wep) ? net.wep : quote(net.wep);
  1793       configured.auth_alg = net.auth_alg = "OPEN SHARED";
  1796     if ("pin" in net) {
  1797       net.pin = quote(net.pin);
  1800     if ("phase1" in net)
  1801       net.phase1 = quote(net.phase1);
  1803     if ("phase2" in net)
  1804       net.phase2 = quote(net.phase2);
  1806     return net;
  1807   };
  1809   WifiManager.onsupplicantconnection = function() {
  1810     debug("Connected to supplicant");
  1811     WifiManager.enabled = true;
  1812     self._reloadConfiguredNetworks(function(ok) {
  1813       // Prime this.networks.
  1814       if (!ok)
  1815         return;
  1817       self.waitForScan(function firstScan() {});
  1818       // The select network command we used in associate() disables others networks.
  1819       // Enable them here to make sure wpa_supplicant helps to connect to known
  1820       // network automatically.
  1821       self._enableAllNetworks();
  1822       WifiManager.saveConfig(function() {})
  1823     });
  1825     try {
  1826       self._allowWpaEap = WifiManager.getCapabilities().eapSim;
  1827     } catch (e) {
  1828       self._allowWpaEap = false;
  1831     // Notify everybody, even if they didn't ask us to come up.
  1832     WifiManager.getMacAddress(function (mac) {
  1833       self.macAddress = mac;
  1834       debug("Got mac: " + mac);
  1835       self._fireEvent("wifiUp", { macAddress: mac });
  1836       self.requestDone();
  1837     });
  1839     if (WifiManager.state === "SCANNING")
  1840       startScanStuckTimer();
  1841   };
  1843   WifiManager.onsupplicantlost = function() {
  1844     WifiManager.enabled = WifiManager.supplicantStarted = false;
  1845     WifiManager.state = "UNINITIALIZED";
  1846     debug("Supplicant died!");
  1848     // Notify everybody, even if they didn't ask us to come up.
  1849     self._fireEvent("wifiDown", {});
  1850     self.requestDone();
  1851   };
  1853   WifiManager.onpasswordmaybeincorrect = function() {
  1854     WifiManager.authenticationFailuresCount++;
  1855   };
  1857   WifiManager.ondisconnected = function() {
  1858     // We may fail to establish the connection, re-enable the
  1859     // rest of our networks.
  1860     if (self._needToEnableNetworks) {
  1861       self._enableAllNetworks();
  1862       self._needToEnableNetworks = false;
  1865     WifiManager.getCurrentNetworkId(this.ssid, function(netId) {
  1866       // Trying to get netId from current network.
  1867       if (!netId &&
  1868           self.currentNetwork &&
  1869           typeof self.currentNetwork.netId !== "undefined") {
  1870         netId = self.currentNetwork.netId;
  1872       if (netId) {
  1873         WifiManager.disableNetwork(netId, function() {});
  1875     });
  1876     self._fireEvent("onconnectingfailed", {network: self.currentNetwork});
  1879   WifiManager.onstatechange = function() {
  1880     debug("State change: " + this.prevState + " -> " + this.state);
  1882     if (self._connectionInfoTimer &&
  1883         this.state !== "CONNECTED" &&
  1884         this.state !== "COMPLETED") {
  1885       self._stopConnectionInfoTimer();
  1888     if (this.state !== "SCANNING" &&
  1889         self._scanStuckTimer) {
  1890       self._scanStuckTimer.cancel();
  1891       self._scanStuckTimer = null;
  1894     switch (this.state) {
  1895       case "DORMANT":
  1896         // The dormant state is a bad state to be in since we won't
  1897         // automatically connect. Try to knock us out of it. We only
  1898         // hit this state when we've failed to run DHCP, so trying
  1899         // again isn't the worst thing we can do. Eventually, we'll
  1900         // need to detect if we're looping in this state and bail out.
  1901         WifiManager.reconnect(function(){});
  1902         break;
  1903       case "ASSOCIATING":
  1904         // id has not yet been filled in, so we can only report the ssid and
  1905         // bssid.
  1906         self.currentNetwork =
  1907           { bssid: WifiManager.connectionInfo.bssid,
  1908             ssid: quote(WifiManager.connectionInfo.ssid) };
  1909         self._fireEvent("onconnecting", { network: netToDOM(self.currentNetwork) });
  1910         break;
  1911       case "ASSOCIATED":
  1912         if (!self.currentNetwork) {
  1913           self.currentNetwork =
  1914             { bssid: WifiManager.connectionInfo.bssid,
  1915               ssid: quote(WifiManager.connectionInfo.ssid) };
  1918         self.currentNetwork.netId = this.id;
  1919         WifiManager.getNetworkConfiguration(self.currentNetwork, function (){});
  1920         break;
  1921       case "COMPLETED":
  1922         // Now that we've successfully completed the connection, re-enable the
  1923         // rest of our networks.
  1924         // XXX Need to do this eventually if the user entered an incorrect
  1925         // password. For now, we require user interaction to break the loop and
  1926         // select a better network!
  1927         if (self._needToEnableNetworks) {
  1928           self._enableAllNetworks();
  1929           self._needToEnableNetworks = false;
  1932         // We get the ASSOCIATED event when we've associated but not connected, so
  1933         // wait until the handshake is complete.
  1934         if (this.fromStatus || !self.currentNetwork) {
  1935           // In this case, we connected to an already-connected wpa_supplicant,
  1936           // because of that we need to gather information about the current
  1937           // network here.
  1938           self.currentNetwork = { ssid: quote(WifiManager.connectionInfo.ssid),
  1939                                   netId: WifiManager.connectionInfo.id };
  1940           WifiManager.getNetworkConfiguration(self.currentNetwork, function(){});
  1943         // Update http proxy when connected to network.
  1944         let netConnect = WifiManager.getHttpProxyNetwork(self.currentNetwork);
  1945         if (netConnect)
  1946           WifiManager.setHttpProxy(netConnect);
  1948         // The full authentication process is completed, reset the count.
  1949         WifiManager.authenticationFailuresCount = 0;
  1950         WifiManager.loopDetectionCount = 0;
  1951         self._startConnectionInfoTimer();
  1952         self._fireEvent("onassociate", { network: netToDOM(self.currentNetwork) });
  1953         break;
  1954       case "CONNECTED":
  1955         // BSSID is read after connected, update it.
  1956         self.currentNetwork.bssid = WifiManager.connectionInfo.bssid;
  1957         break;
  1958       case "DISCONNECTED":
  1959         // wpa_supplicant may give us a "DISCONNECTED" event even if
  1960         // we are already in "DISCONNECTED" state.
  1961         if ((WifiNetworkInterface.state ===
  1962              Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED) &&
  1963              (this.prevState === "INITIALIZING" ||
  1964               this.prevState === "DISCONNECTED" ||
  1965               this.prevState === "INTERFACE_DISABLED" ||
  1966               this.prevState === "INACTIVE" ||
  1967               this.prevState === "UNINITIALIZED")) {
  1968           return;
  1971         self._fireEvent("ondisconnect", {});
  1973         // When disconnected, clear the http proxy setting if it exists.
  1974         // Temporarily set http proxy to empty and restore user setting after setHttpProxy.
  1975         let netDisconnect = WifiManager.getHttpProxyNetwork(self.currentNetwork);
  1976         if (netDisconnect) {
  1977           let prehttpProxyHostSetting = netDisconnect.httpProxyHost;
  1978           let prehttpProxyPortSetting = netDisconnect.httpProxyPort;
  1980           netDisconnect.httpProxyHost = "";
  1981           netDisconnect.httpProxyPort = 0;
  1983           WifiManager.setHttpProxy(netDisconnect);
  1985           netDisconnect.httpProxyHost = prehttpProxyHostSetting;
  1986           netDisconnect.httpProxyPort = prehttpProxyPortSetting;
  1989         self.currentNetwork = null;
  1990         self.ipAddress = "";
  1992         if (self._turnOnBackgroundScan) {
  1993           self._turnOnBackgroundScan = false;
  1994           WifiManager.setBackgroundScan("ON", function(did_something, ok) {
  1995             WifiManager.reassociate(function() {});
  1996           });
  1999         WifiManager.connectionDropped(function() {
  2000           // We've disconnected from a network because of a call to forgetNetwork.
  2001           // Reconnect to the next available network (if any).
  2002           if (self._reconnectOnDisconnect) {
  2003             self._reconnectOnDisconnect = false;
  2004             WifiManager.reconnect(function(){});
  2006         });
  2008         WifiNetworkInterface.state =
  2009           Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED;
  2010         WifiNetworkInterface.ips = [];
  2011         WifiNetworkInterface.prefixLengths = [];
  2012         WifiNetworkInterface.gateways = [];
  2013         WifiNetworkInterface.dnses = [];
  2014         Services.obs.notifyObservers(WifiNetworkInterface,
  2015                                      kNetworkInterfaceStateChangedTopic,
  2016                                      null);
  2018         break;
  2019       case "WPS_TIMEOUT":
  2020         self._fireEvent("onwpstimeout", {});
  2021         break;
  2022       case "WPS_FAIL":
  2023         self._fireEvent("onwpsfail", {});
  2024         break;
  2025       case "WPS_OVERLAP_DETECTED":
  2026         self._fireEvent("onwpsoverlap", {});
  2027         break;
  2028       case "SCANNING":
  2029         // If we're already scanning in the background, we don't need to worry
  2030         // about getting stuck while scanning.
  2031         if (!WifiManager.backgroundScanEnabled && WifiManager.enabled)
  2032           startScanStuckTimer();
  2033         break;
  2035   };
  2037   WifiManager.onnetworkconnected = function() {
  2038     if (!this.info || !this.info.ipaddr_str) {
  2039       debug("Network information is invalid.");
  2040       return;
  2043     let maskLength =
  2044       netHelpers.getMaskLength(netHelpers.stringToIP(this.info.mask_str));
  2045     if (!maskLength) {
  2046       maskLength = 32; // max prefix for IPv4.
  2048     WifiNetworkInterface.state =
  2049       Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED;
  2050     WifiNetworkInterface.ips = [this.info.ipaddr_str];
  2051     WifiNetworkInterface.prefixLengths = [maskLength];
  2052     WifiNetworkInterface.gateways = [this.info.gateway_str];
  2053     if (typeof this.info.dns1_str == "string" &&
  2054         this.info.dns1_str.length) {
  2055       WifiNetworkInterface.dnses.push(this.info.dns1_str);
  2057     if (typeof this.info.dns2_str == "string" &&
  2058         this.info.dns2_str.length) {
  2059       WifiNetworkInterface.dnses.push(this.info.dns2_str);
  2061     Services.obs.notifyObservers(WifiNetworkInterface,
  2062                                  kNetworkInterfaceStateChangedTopic,
  2063                                  null);
  2065     self.ipAddress = this.info.ipaddr_str;
  2067     // We start the connection information timer when we associate, but
  2068     // don't have our IP address until here. Make sure that we fire a new
  2069     // connectionInformation event with the IP address the next time the
  2070     // timer fires.
  2071     self._lastConnectionInfo = null;
  2072     self._fireEvent("onconnect", { network: netToDOM(self.currentNetwork) });
  2073   };
  2075   WifiManager.onscanresultsavailable = function() {
  2076     if (self._scanStuckTimer) {
  2077       // We got scan results! We must not be stuck for now, try again.
  2078       self._scanStuckTimer.cancel();
  2079       self._scanStuckTimer.initWithCallback(scanIsStuck, SCAN_STUCK_WAIT,
  2080                                             Ci.nsITimer.TYPE_ONE_SHOT);
  2083     if (self.wantScanResults.length === 0) {
  2084       debug("Scan results available, but we don't need them");
  2085       return;
  2088     debug("Scan results are available! Asking for them.");
  2089     WifiManager.getScanResults(function(r) {
  2090       // Failure.
  2091       if (!r) {
  2092         self.wantScanResults.forEach(function(callback) { callback(null) });
  2093         self.wantScanResults = [];
  2094         return;
  2097       // Now that we have scan results, there's no more need to continue
  2098       // scanning. Ignore any errors from this command.
  2099       WifiManager.setScanMode("inactive", function() {});
  2100       let lines = r.split("\n");
  2101       // NB: Skip the header line.
  2102       self.networksArray = [];
  2103       for (let i = 1; i < lines.length; ++i) {
  2104         // bssid / frequency / signal level / flags / ssid
  2105         var match = /([\S]+)\s+([\S]+)\s+([\S]+)\s+(\[[\S]+\])?\s(.*)/.exec(lines[i]);
  2107         if (match && match[5]) {
  2108           let ssid = match[5],
  2109               bssid = match[1],
  2110               signalLevel = match[3],
  2111               flags = match[4];
  2113           // Skip ad-hoc networks which aren't supported (bug 811635).
  2114           if (flags && flags.indexOf("[IBSS]") >= 0)
  2115             continue;
  2117           // If this is the first time that we've seen this SSID in the scan
  2118           // results, add it to the list along with any other information.
  2119           // Also, we use the highest signal strength that we see.
  2120           let network = new ScanResult(ssid, bssid, flags, signalLevel);
  2122           let networkKey = getNetworkKey(network);
  2123           let eapIndex = -1;
  2124           if (networkKey in self.configuredNetworks) {
  2125             let known = self.configuredNetworks[networkKey];
  2126             network.known = true;
  2128             if ("identity" in known && known.identity)
  2129               network.identity = dequote(known.identity);
  2131             // Note: we don't hand out passwords here! The * marks that there
  2132             // is a password that we're hiding.
  2133             if (("psk" in known && known.psk) ||
  2134                 ("password" in known && known.password) ||
  2135                 ("wep_key0" in known && known.wep_key0)) {
  2136               network.password = "*";
  2138           } else if (!self._allowWpaEap &&
  2139                      (eapIndex = network.security.indexOf("WPA-EAP")) >= 0) {
  2140             // Don't offer to connect to WPA-EAP networks unless one has been
  2141             // configured through other means (e.g. it was added directly to
  2142             // wpa_supplicant.conf). Here, we have an unknown WPA-EAP network,
  2143             // so we ignore it entirely if it only supports WPA-EAP, otherwise
  2144             // we take EAP out of the list and offer the rest of the
  2145             // security.
  2146             if (network.security.length === 1)
  2147               continue;
  2149             network.security.splice(eapIndex, 1);
  2152           self.networksArray.push(network);
  2153           if (network.bssid === WifiManager.connectionInfo.bssid)
  2154             network.connected = true;
  2156           let signal = calculateSignal(Number(match[3]));
  2157           if (signal > network.relSignalStrength)
  2158             network.relSignalStrength = signal;
  2159         } else if (!match) {
  2160           debug("Match didn't find anything for: " + lines[i]);
  2164       self.wantScanResults.forEach(function(callback) { callback(self.networksArray) });
  2165       self.wantScanResults = [];
  2166     });
  2167   };
  2169   // Read the 'wifi.enabled' setting in order to start with a known
  2170   // value at boot time. The handle() will be called after reading.
  2171   //
  2172   // nsISettingsServiceCallback implementation.
  2173   var initWifiEnabledCb = {
  2174     handle: function handle(aName, aResult) {
  2175       if (aName !== SETTINGS_WIFI_ENABLED)
  2176         return;
  2177       if (aResult === null)
  2178         aResult = true;
  2179       self.handleWifiEnabled(aResult);
  2180     },
  2181     handleError: function handleError(aErrorMessage) {
  2182       debug("Error reading the 'wifi.enabled' setting. Default to wifi on.");
  2183       self.handleWifiEnabled(true);
  2185   };
  2187   var initWifiDebuggingEnabledCb = {
  2188     handle: function handle(aName, aResult) {
  2189       if (aName !== SETTINGS_WIFI_DEBUG_ENABLED)
  2190         return;
  2191       if (aResult === null)
  2192         aResult = false;
  2193       DEBUG = aResult;
  2194       updateDebug();
  2195     },
  2196     handleError: function handleError(aErrorMessage) {
  2197       debug("Error reading the 'wifi.debugging.enabled' setting. Default to debugging off.");
  2198       DEBUG = false;
  2199       updateDebug();
  2201   };
  2203   this.initTetheringSettings();
  2205   let lock = gSettingsService.createLock();
  2206   lock.get(SETTINGS_WIFI_ENABLED, initWifiEnabledCb);
  2207   lock.get(SETTINGS_WIFI_DEBUG_ENABLED, initWifiDebuggingEnabledCb);
  2209   lock.get(SETTINGS_WIFI_SSID, this);
  2210   lock.get(SETTINGS_WIFI_SECURITY_TYPE, this);
  2211   lock.get(SETTINGS_WIFI_SECURITY_PASSWORD, this);
  2212   lock.get(SETTINGS_WIFI_IP, this);
  2213   lock.get(SETTINGS_WIFI_PREFIX, this);
  2214   lock.get(SETTINGS_WIFI_DHCPSERVER_STARTIP, this);
  2215   lock.get(SETTINGS_WIFI_DHCPSERVER_ENDIP, this);
  2216   lock.get(SETTINGS_WIFI_DNS1, this);
  2217   lock.get(SETTINGS_WIFI_DNS2, this);
  2218   lock.get(SETTINGS_WIFI_TETHERING_ENABLED, this);
  2220   lock.get(SETTINGS_USB_DHCPSERVER_STARTIP, this);
  2221   lock.get(SETTINGS_USB_DHCPSERVER_ENDIP, this);
  2223   this._wifiTetheringSettingsToRead = [SETTINGS_WIFI_SSID,
  2224                                        SETTINGS_WIFI_SECURITY_TYPE,
  2225                                        SETTINGS_WIFI_SECURITY_PASSWORD,
  2226                                        SETTINGS_WIFI_IP,
  2227                                        SETTINGS_WIFI_PREFIX,
  2228                                        SETTINGS_WIFI_DHCPSERVER_STARTIP,
  2229                                        SETTINGS_WIFI_DHCPSERVER_ENDIP,
  2230                                        SETTINGS_WIFI_DNS1,
  2231                                        SETTINGS_WIFI_DNS2,
  2232                                        SETTINGS_WIFI_TETHERING_ENABLED,
  2233                                        SETTINGS_USB_DHCPSERVER_STARTIP,
  2234                                        SETTINGS_USB_DHCPSERVER_ENDIP];
  2237 function translateState(state) {
  2238   switch (state) {
  2239     case "INTERFACE_DISABLED":
  2240     case "INACTIVE":
  2241     case "SCANNING":
  2242     case "DISCONNECTED":
  2243     default:
  2244       return "disconnected";
  2246     case "AUTHENTICATING":
  2247     case "ASSOCIATING":
  2248     case "ASSOCIATED":
  2249     case "FOUR_WAY_HANDSHAKE":
  2250     case "GROUP_HANDSHAKE":
  2251       return "connecting";
  2253     case "COMPLETED":
  2254       return WifiManager.getDhcpInfo() ? "connected" : "associated";
  2258 WifiWorker.prototype = {
  2259   classID:   WIFIWORKER_CID,
  2260   classInfo: XPCOMUtils.generateCI({classID: WIFIWORKER_CID,
  2261                                     contractID: WIFIWORKER_CONTRACTID,
  2262                                     classDescription: "WifiWorker",
  2263                                     interfaces: [Ci.nsIWorkerHolder,
  2264                                                  Ci.nsIWifi,
  2265                                                  Ci.nsIObserver]}),
  2267   QueryInterface: XPCOMUtils.generateQI([Ci.nsIWorkerHolder,
  2268                                          Ci.nsIWifi,
  2269                                          Ci.nsISettingsServiceCallback]),
  2271   disconnectedByWifi: false,
  2273   disconnectedByWifiTethering: false,
  2275   _wifiTetheringSettingsToRead: [],
  2277   _oldWifiTetheringEnabledState: null,
  2279   tetheringSettings: {},
  2281   initTetheringSettings: function initTetheringSettings() {
  2282     this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] = null;
  2283     this.tetheringSettings[SETTINGS_WIFI_SSID] = DEFAULT_WIFI_SSID;
  2284     this.tetheringSettings[SETTINGS_WIFI_SECURITY_TYPE] = DEFAULT_WIFI_SECURITY_TYPE;
  2285     this.tetheringSettings[SETTINGS_WIFI_SECURITY_PASSWORD] = DEFAULT_WIFI_SECURITY_PASSWORD;
  2286     this.tetheringSettings[SETTINGS_WIFI_IP] = DEFAULT_WIFI_IP;
  2287     this.tetheringSettings[SETTINGS_WIFI_PREFIX] = DEFAULT_WIFI_PREFIX;
  2288     this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP] = DEFAULT_WIFI_DHCPSERVER_STARTIP;
  2289     this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP] = DEFAULT_WIFI_DHCPSERVER_ENDIP;
  2290     this.tetheringSettings[SETTINGS_WIFI_DNS1] = DEFAULT_DNS1;
  2291     this.tetheringSettings[SETTINGS_WIFI_DNS2] = DEFAULT_DNS2;
  2293     this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP] = DEFAULT_USB_DHCPSERVER_STARTIP;
  2294     this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP] = DEFAULT_USB_DHCPSERVER_ENDIP;
  2295   },
  2297   // Internal methods.
  2298   waitForScan: function(callback) {
  2299     this.wantScanResults.push(callback);
  2300   },
  2302   // In order to select a specific network, we disable the rest of the
  2303   // networks known to us. However, in general, we want the supplicant to
  2304   // connect to which ever network it thinks is best, so when we select the
  2305   // proper network (or fail to), we need to re-enable the rest.
  2306   _enableAllNetworks: function() {
  2307     for each (let net in this.configuredNetworks) {
  2308       WifiManager.enableNetwork(net.netId, false, function(ok) {
  2309         net.disabled = ok ? 1 : 0;
  2310       });
  2312   },
  2314   _startConnectionInfoTimer: function() {
  2315     if (this._connectionInfoTimer)
  2316       return;
  2318     var self = this;
  2319     function getConnectionInformation() {
  2320       WifiManager.getConnectionInfo(function(connInfo) {
  2321         // See comments in calculateSignal for information about this.
  2322         if (!connInfo) {
  2323           self._lastConnectionInfo = null;
  2324           return;
  2327         let { rssi, linkspeed } = connInfo;
  2328         if (rssi > 0)
  2329           rssi -= 256;
  2330         if (rssi <= MIN_RSSI)
  2331           rssi = MIN_RSSI;
  2332         else if (rssi >= MAX_RSSI)
  2333           rssi = MAX_RSSI;
  2335         let info = { signalStrength: rssi,
  2336                      relSignalStrength: calculateSignal(rssi),
  2337                      linkSpeed: linkspeed,
  2338                      ipAddress: self.ipAddress };
  2339         let last = self._lastConnectionInfo;
  2341         // Only fire the event if the link speed changed or the signal
  2342         // strength changed by more than 10%.
  2343         function tensPlace(percent) ((percent / 10) | 0)
  2345         if (last && last.linkSpeed === info.linkSpeed &&
  2346             tensPlace(last.relSignalStrength) === tensPlace(info.relSignalStrength)) {
  2347           return;
  2350         self._lastConnectionInfo = info;
  2351         debug("Firing connectionInfoUpdate: " + uneval(info));
  2352         self._fireEvent("connectionInfoUpdate", info);
  2353       });
  2356     // Prime our _lastConnectionInfo immediately and fire the event at the
  2357     // same time.
  2358     getConnectionInformation();
  2360     // Now, set up the timer for regular updates.
  2361     this._connectionInfoTimer =
  2362       Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  2363     this._connectionInfoTimer.init(getConnectionInformation, 5000,
  2364                                    Ci.nsITimer.TYPE_REPEATING_SLACK);
  2365   },
  2367   _stopConnectionInfoTimer: function() {
  2368     if (!this._connectionInfoTimer)
  2369       return;
  2371     this._connectionInfoTimer.cancel();
  2372     this._connectionInfoTimer = null;
  2373     this._lastConnectionInfo = null;
  2374   },
  2376   _reloadConfiguredNetworks: function(callback) {
  2377     WifiManager.getConfiguredNetworks((function(networks) {
  2378       if (!networks) {
  2379         debug("Unable to get configured networks");
  2380         callback(false);
  2381         return;
  2384       this._highestPriority = -1;
  2386       // Convert between netId-based and ssid-based indexing.
  2387       for (let net in networks) {
  2388         let network = networks[net];
  2389         delete networks[net];
  2391         if (!network.ssid) {
  2392           WifiManager.removeNetwork(network.netId, function() {});
  2393           continue;
  2396         if (network.priority && network.priority > this._highestPriority)
  2397           this._highestPriority = network.priority;
  2399         let networkKey = getNetworkKey(network);
  2400         // Accept latest config of same network(same SSID and same security).
  2401         if (networks[networkKey]) {
  2402           WifiManager.removeNetwork(networks[networkKey].netId, function() {});
  2404         networks[networkKey] = network;
  2407       this.configuredNetworks = networks;
  2408       callback(true);
  2409     }).bind(this));
  2410   },
  2412   // Important side effect: calls WifiManager.saveConfig.
  2413   _reprioritizeNetworks: function(callback) {
  2414     // First, sort the networks in orer of their priority.
  2415     var ordered = Object.getOwnPropertyNames(this.configuredNetworks);
  2416     let self = this;
  2417     ordered.sort(function(a, b) {
  2418       var neta = self.configuredNetworks[a],
  2419           netb = self.configuredNetworks[b];
  2421       // Sort unsorted networks to the end of the list.
  2422       if (isNaN(neta.priority))
  2423         return isNaN(netb.priority) ? 0 : 1;
  2424       if (isNaN(netb.priority))
  2425         return -1;
  2426       return netb.priority - neta.priority;
  2427     });
  2429     // Skip unsorted networks.
  2430     let newPriority = 0, i;
  2431     for (i = ordered.length - 1; i >= 0; --i) {
  2432       if (!isNaN(this.configuredNetworks[ordered[i]].priority))
  2433         break;
  2436     // No networks we care about?
  2437     if (i < 0) {
  2438       WifiManager.saveConfig(callback);
  2439       return;
  2442     // Now assign priorities from 0 to length, starting with the smallest
  2443     // priority and heading towards the highest (note the dependency between
  2444     // total and i here).
  2445     let done = 0, errors = 0, total = i + 1;
  2446     for (; i >= 0; --i) {
  2447       let network = this.configuredNetworks[ordered[i]];
  2448       network.priority = newPriority++;
  2450       // Note: networkUpdated declared below since it happens logically after
  2451       // this loop.
  2452       WifiManager.updateNetwork(network, networkUpdated);
  2455     function networkUpdated(ok) {
  2456       if (!ok)
  2457         ++errors;
  2458       if (++done === total) {
  2459         if (errors > 0) {
  2460           callback(false);
  2461           return;
  2464         WifiManager.saveConfig(function(ok) {
  2465           if (!ok) {
  2466             callback(false);
  2467             return;
  2470           self._reloadConfiguredNetworks(function(ok) {
  2471             callback(ok);
  2472           });
  2473         });
  2476   },
  2478   // nsIWifi
  2480   _domManagers: [],
  2481   _fireEvent: function(message, data) {
  2482     this._domManagers.forEach(function(manager) {
  2483       // Note: We should never have a dead message manager here because we
  2484       // observe our child message managers shutting down, below.
  2485       manager.sendAsyncMessage("WifiManager:" + message, data);
  2486     });
  2487   },
  2489   _sendMessage: function(message, success, data, msg) {
  2490     try {
  2491       msg.manager.sendAsyncMessage(message + (success ? ":OK" : ":NO"),
  2492                                    { data: data, rid: msg.rid, mid: msg.mid });
  2493     } catch (e) {
  2494       debug("sendAsyncMessage error : " + e);
  2496     this._splicePendingRequest(msg);
  2497   },
  2499   _domRequest: [],
  2501   _splicePendingRequest: function(msg) {
  2502     for (let i = 0; i < this._domRequest.length; i++) {
  2503       if (this._domRequest[i].msg === msg) {
  2504         this._domRequest.splice(i, 1);
  2505         return;
  2508   },
  2510   _clearPendingRequest: function() {
  2511     if (this._domRequest.length === 0) return;
  2512     this._domRequest.forEach((function(req) {
  2513       this._sendMessage(req.name + ":Return", false, "Wifi is disabled", req.msg);
  2514     }).bind(this));
  2515   },
  2517   receiveMessage: function MessageManager_receiveMessage(aMessage) {
  2518     let msg = aMessage.data || {};
  2519     msg.manager = aMessage.target;
  2521     if (WifiManager.p2pSupported()) {
  2522       // If p2pObserver returns something truthy, return it!
  2523       // Otherwise, continue to do the rest of tasks.
  2524       var p2pRet = this._p2pObserver.onDOMMessage(aMessage);
  2525       if (p2pRet) {
  2526         return p2pRet;
  2530     // Note: By the time we receive child-process-shutdown, the child process
  2531     // has already forgotten its permissions so we do this before the
  2532     // permissions check.
  2533     if (aMessage.name === "child-process-shutdown") {
  2534       let i;
  2535       if ((i = this._domManagers.indexOf(msg.manager)) != -1) {
  2536         this._domManagers.splice(i, 1);
  2538       for (i = this._domRequest.length - 1; i >= 0; i--) {
  2539         if (this._domRequest[i].msg.manager === msg.manager) {
  2540           this._domRequest.splice(i, 1);
  2543       return;
  2546     if (!aMessage.target.assertPermission("wifi-manage")) {
  2547       return;
  2550     // We are interested in DOMRequests only.
  2551     if (aMessage.name != "WifiManager:getState") {
  2552       this._domRequest.push({name: aMessage.name, msg:msg});
  2555     switch (aMessage.name) {
  2556       case "WifiManager:getNetworks":
  2557         this.getNetworks(msg);
  2558         break;
  2559       case "WifiManager:getKnownNetworks":
  2560         this.getKnownNetworks(msg);
  2561         break;
  2562       case "WifiManager:associate":
  2563         this.associate(msg);
  2564         break;
  2565       case "WifiManager:forget":
  2566         this.forget(msg);
  2567         break;
  2568       case "WifiManager:wps":
  2569         this.wps(msg);
  2570         break;
  2571       case "WifiManager:setPowerSavingMode":
  2572         this.setPowerSavingMode(msg);
  2573         break;
  2574       case "WifiManager:setHttpProxy":
  2575         this.setHttpProxy(msg);
  2576         break;
  2577       case "WifiManager:setStaticIpMode":
  2578         this.setStaticIpMode(msg);
  2579         break;
  2580       case "WifiManager:getState": {
  2581         let i;
  2582         if ((i = this._domManagers.indexOf(msg.manager)) === -1) {
  2583           this._domManagers.push(msg.manager);
  2586         let net = this.currentNetwork ? netToDOM(this.currentNetwork) : null;
  2587         return { network: net,
  2588                  connectionInfo: this._lastConnectionInfo,
  2589                  enabled: WifiManager.enabled,
  2590                  status: translateState(WifiManager.state),
  2591                  macAddress: this.macAddress };
  2594   },
  2596   getNetworks: function(msg) {
  2597     const message = "WifiManager:getNetworks:Return";
  2598     if (!WifiManager.enabled) {
  2599       this._sendMessage(message, false, "Wifi is disabled", msg);
  2600       return;
  2603     let sent = false;
  2604     let callback = (function (networks) {
  2605       if (sent)
  2606         return;
  2607       sent = true;
  2608       this._sendMessage(message, networks !== null, networks, msg);
  2609     }).bind(this);
  2610     this.waitForScan(callback);
  2612     WifiManager.scan(true, (function(ok) {
  2613       // If the scan command succeeded, we're done.
  2614       if (ok)
  2615         return;
  2617       // Avoid sending multiple responses.
  2618       if (sent)
  2619         return;
  2621       // Otherwise, let the client know that it failed, it's responsible for
  2622       // trying again in a few seconds.
  2623       sent = true;
  2624       this._sendMessage(message, false, "ScanFailed", msg);
  2625     }).bind(this));
  2626   },
  2628   getWifiScanResults: function(callback) {
  2629     var count = 0;
  2630     var timer = null;
  2631     var self = this;
  2633     self.waitForScan(waitForScanCallback);
  2634     doScan();
  2635     function doScan() {
  2636       WifiManager.scan(true, (function (ok) {
  2637         if (!ok) {
  2638           if (!timer) {
  2639             count = 0;
  2640             timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  2643           if (count++ >= 3) {
  2644             timer = null;
  2645             self.wantScanResults.splice(self.wantScanResults.indexOf(waitForScanCallback), 1);
  2646             callback.onfailure();
  2647             return;
  2650           // Else it's still running, continue waiting.
  2651           timer.initWithCallback(doScan, 10000, Ci.nsITimer.TYPE_ONE_SHOT);
  2652           return;
  2654       }).bind(this));
  2657     function waitForScanCallback(networks) {
  2658       if (networks === null) {
  2659         callback.onfailure();
  2660         return;
  2663       var wifiScanResults = new Array();
  2664       var net;
  2665       for (let net in networks) {
  2666         let value = networks[net];
  2667         wifiScanResults.push(transformResult(value));
  2669       callback.onready(wifiScanResults.length, wifiScanResults);
  2672     function transformResult(element) {
  2673       var result = new WifiScanResult();
  2674       result.connected = false;
  2675       for (let id in element) {
  2676         if (id === "__exposedProps__") {
  2677           continue;
  2679         if (id === "security") {
  2680           result[id] = 0;
  2681           var security = element[id];
  2682           for (let j = 0; j < security.length; j++) {
  2683             if (security[j] === "WPA-PSK") {
  2684               result[id] |= Ci.nsIWifiScanResult.WPA_PSK;
  2685             } else if (security[j] === "WPA-EAP") {
  2686               result[id] |= Ci.nsIWifiScanResult.WPA_EAP;
  2687             } else if (security[j] === "WEP") {
  2688               result[id] |= Ci.nsIWifiScanResult.WEP;
  2689             } else {
  2690              result[id] = 0;
  2693         } else {
  2694           result[id] = element[id];
  2697       return result;
  2699   },
  2701   getKnownNetworks: function(msg) {
  2702     const message = "WifiManager:getKnownNetworks:Return";
  2703     if (!WifiManager.enabled) {
  2704       this._sendMessage(message, false, "Wifi is disabled", msg);
  2705       return;
  2708     this._reloadConfiguredNetworks((function(ok) {
  2709       if (!ok) {
  2710         this._sendMessage(message, false, "Failed", msg);
  2711         return;
  2714       var networks = [];
  2715       for (let networkKey in this.configuredNetworks) {
  2716         networks.push(netToDOM(this.configuredNetworks[networkKey]));
  2719       this._sendMessage(message, true, networks, msg);
  2720     }).bind(this));
  2721   },
  2723   _setWifiEnabledCallback: function(status) {
  2724     if (status !== 0) {
  2725       this.requestDone();
  2726       return;
  2729     // If we're enabling ourselves, then wait until we've connected to the
  2730     // supplicant to notify. If we're disabling, we take care of this in
  2731     // supplicantlost.
  2732     if (WifiManager.supplicantStarted)
  2733       WifiManager.start();
  2734   },
  2736   setWifiEnabled: function(enabled, callback) {
  2737     // Reply error to pending requests.
  2738     if (!enabled) {
  2739       this._clearPendingRequest();
  2742     WifiManager.setWifiEnabled(enabled, callback);
  2743   },
  2745   // requestDone() must be called to before callback complete(or error)
  2746   // so next queue in the request quene can be executed.
  2747   queueRequest: function(data, callback) {
  2748     if (!callback) {
  2749         throw "Try to enqueue a request without callback";
  2752     let optimizeCommandList = ["setWifiEnabled", "setWifiApEnabled"];
  2753     if (optimizeCommandList.indexOf(data.command) != -1) {
  2754       this._stateRequests = this._stateRequests.filter(function(element) {
  2755         return element.data.command !== data.command;
  2756       });
  2759     this._stateRequests.push({
  2760       data: data,
  2761       callback: callback
  2762     });
  2764     this.nextRequest();
  2765   },
  2767   getWifiTetheringParameters: function getWifiTetheringParameters(enable) {
  2768     let ssid;
  2769     let securityType;
  2770     let securityId;
  2771     let interfaceIp;
  2772     let prefix;
  2773     let wifiDhcpStartIp;
  2774     let wifiDhcpEndIp;
  2775     let usbDhcpStartIp;
  2776     let usbDhcpEndIp;
  2777     let dns1;
  2778     let dns2;
  2780     ssid = this.tetheringSettings[SETTINGS_WIFI_SSID];
  2781     securityType = this.tetheringSettings[SETTINGS_WIFI_SECURITY_TYPE];
  2782     securityId = this.tetheringSettings[SETTINGS_WIFI_SECURITY_PASSWORD];
  2783     interfaceIp = this.tetheringSettings[SETTINGS_WIFI_IP];
  2784     prefix = this.tetheringSettings[SETTINGS_WIFI_PREFIX];
  2785     wifiDhcpStartIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP];
  2786     wifiDhcpEndIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP];
  2787     usbDhcpStartIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP];
  2788     usbDhcpEndIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP];
  2789     dns1 = this.tetheringSettings[SETTINGS_WIFI_DNS1];
  2790     dns2 = this.tetheringSettings[SETTINGS_WIFI_DNS2];
  2792     // Check the format to prevent netd from crash.
  2793     if (!ssid || ssid == "") {
  2794       debug("Invalid SSID value.");
  2795       return null;
  2797     if (securityType != WIFI_SECURITY_TYPE_NONE &&
  2798         securityType != WIFI_SECURITY_TYPE_WPA_PSK &&
  2799         securityType != WIFI_SECURITY_TYPE_WPA2_PSK) {
  2801       debug("Invalid security type.");
  2802       return null;
  2804     if (securityType != WIFI_SECURITY_TYPE_NONE && !securityId) {
  2805       debug("Invalid security password.");
  2806       return null;
  2808     // Using the default values here until application supports these settings.
  2809     if (interfaceIp == "" || prefix == "" ||
  2810         wifiDhcpStartIp == "" || wifiDhcpEndIp == "" ||
  2811         usbDhcpStartIp == "" || usbDhcpEndIp == "") {
  2812       debug("Invalid subnet information.");
  2813       return null;
  2816     return {
  2817       ssid: ssid,
  2818       security: securityType,
  2819       key: securityId,
  2820       ip: interfaceIp,
  2821       prefix: prefix,
  2822       wifiStartIp: wifiDhcpStartIp,
  2823       wifiEndIp: wifiDhcpEndIp,
  2824       usbStartIp: usbDhcpStartIp,
  2825       usbEndIp: usbDhcpEndIp,
  2826       dns1: dns1,
  2827       dns2: dns2,
  2828       enable: enable,
  2829       mode: enable ? WIFI_FIRMWARE_AP : WIFI_FIRMWARE_STATION,
  2830       link: enable ? NETWORK_INTERFACE_UP : NETWORK_INTERFACE_DOWN
  2831     };
  2832   },
  2834   setWifiApEnabled: function(enabled, callback) {
  2835     let configuration = this.getWifiTetheringParameters(enabled);
  2837     if (!configuration) {
  2838       this.requestDone();
  2839       debug("Invalid Wifi Tethering configuration.");
  2840       return;
  2843     WifiManager.setWifiApEnabled(enabled, configuration, callback);
  2844   },
  2846   associate: function(msg) {
  2847     const MAX_PRIORITY = 9999;
  2848     const message = "WifiManager:associate:Return";
  2849     let network = msg.data;
  2851     let privnet = network;
  2852     let dontConnect = privnet.dontConnect;
  2853     delete privnet.dontConnect;
  2855     if (!WifiManager.enabled) {
  2856       this._sendMessage(message, false, "Wifi is disabled", msg);
  2857       return;
  2860     let self = this;
  2861     function networkReady() {
  2862       // saveConfig now before we disable most of the other networks.
  2863       function selectAndConnect() {
  2864         WifiManager.enableNetwork(privnet.netId, true, function (ok) {
  2865           if (ok)
  2866             self._needToEnableNetworks = true;
  2867           if (WifiManager.state === "DISCONNECTED" ||
  2868               WifiManager.state === "SCANNING") {
  2869             WifiManager.reconnect(function (ok) {
  2870               self._sendMessage(message, ok, ok, msg);
  2871             });
  2872           } else {
  2873             self._sendMessage(message, ok, ok, msg);
  2875         });
  2878       var selectAndConnectOrReturn = dontConnect ?
  2879         function() {
  2880           self._sendMessage(message, true, "Wifi has been recorded", msg);
  2881         } : selectAndConnect;
  2882       if (self._highestPriority >= MAX_PRIORITY) {
  2883         self._reprioritizeNetworks(selectAndConnectOrReturn);
  2884       } else {
  2885         WifiManager.saveConfig(selectAndConnectOrReturn);
  2889     let ssid = privnet.ssid;
  2890     let networkKey = getNetworkKey(privnet);
  2891     let configured;
  2893     if (networkKey in this._addingNetworks) {
  2894       this._sendMessage(message, false, "Racing associates");
  2895       return;
  2898     if (networkKey in this.configuredNetworks)
  2899       configured = this.configuredNetworks[networkKey];
  2901     netFromDOM(privnet, configured);
  2903     privnet.priority = ++this._highestPriority;
  2904     if (configured) {
  2905       privnet.netId = configured.netId;
  2906       WifiManager.updateNetwork(privnet, (function(ok) {
  2907         if (!ok) {
  2908           this._sendMessage(message, false, "Network is misconfigured", msg);
  2909           return;
  2912         networkReady();
  2913       }).bind(this));
  2914     } else {
  2915       // networkReady, above, calls saveConfig. We want to remember the new
  2916       // network as being enabled, which isn't the default, so we explicitly
  2917       // set it to being "enabled" before we add it and save the
  2918       // configuration.
  2919       privnet.disabled = 0;
  2920       this._addingNetworks[networkKey] = privnet;
  2921       WifiManager.addNetwork(privnet, (function(ok) {
  2922         delete this._addingNetworks[networkKey];
  2924         if (!ok) {
  2925           this._sendMessage(message, false, "Network is misconfigured", msg);
  2926           return;
  2929         this.configuredNetworks[networkKey] = privnet;
  2930         networkReady();
  2931       }).bind(this));
  2933   },
  2935   forget: function(msg) {
  2936     const message = "WifiManager:forget:Return";
  2937     let network = msg.data;
  2938     if (!WifiManager.enabled) {
  2939       this._sendMessage(message, false, "Wifi is disabled", msg);
  2940       return;
  2943     this._reloadConfiguredNetworks((function(ok) {
  2944       // Give it a chance to remove the network even if reload is failed.
  2945       if (!ok) {
  2946         debug("Warning !!! Failed to reload the configured networks");
  2949       let ssid = network.ssid;
  2950       let networkKey = getNetworkKey(network);
  2951       if (!(networkKey in this.configuredNetworks)) {
  2952         this._sendMessage(message, false, "Trying to forget an unknown network", msg);
  2953         return;
  2956       let self = this;
  2957       let configured = this.configuredNetworks[networkKey];
  2958       this._reconnectOnDisconnect = (this.currentNetwork &&
  2959                                     (this.currentNetwork.ssid === ssid));
  2960       WifiManager.removeNetwork(configured.netId, function(ok) {
  2961         if (!ok) {
  2962           self._sendMessage(message, false, "Unable to remove the network", msg);
  2963           self._reconnectOnDisconnect = false;
  2964           return;
  2967         WifiManager.saveConfig(function() {
  2968           self._reloadConfiguredNetworks(function() {
  2969             self._sendMessage(message, true, true, msg);
  2970           });
  2971         });
  2972       });
  2973     }).bind(this));
  2974   },
  2976   wps: function(msg) {
  2977     const message = "WifiManager:wps:Return";
  2978     let self = this;
  2979     let detail = msg.data;
  2980     if (detail.method === "pbc") {
  2981       WifiManager.wpsPbc(WifiManager.ifname, function(ok) {
  2982         if (ok)
  2983           self._sendMessage(message, true, true, msg);
  2984         else
  2985           self._sendMessage(message, false, "WPS PBC failed", msg);
  2986       });
  2987     } else if (detail.method === "pin") {
  2988       WifiManager.wpsPin(detail, function(pin) {
  2989         if (pin)
  2990           self._sendMessage(message, true, pin, msg);
  2991         else
  2992           self._sendMessage(message, false, "WPS PIN failed", msg);
  2993       });
  2994     } else if (detail.method === "cancel") {
  2995       WifiManager.wpsCancel(function(ok) {
  2996         if (ok)
  2997           self._sendMessage(message, true, true, msg);
  2998         else
  2999           self._sendMessage(message, false, "WPS Cancel failed", msg);
  3000       });
  3001     } else {
  3002       self._sendMessage(message, false, "Invalid WPS method=" + detail.method,
  3003                         msg);
  3005   },
  3007   setPowerSavingMode: function(msg) {
  3008     const message = "WifiManager:setPowerSavingMode:Return";
  3009     let self = this;
  3010     let enabled = msg.data;
  3011     let mode = enabled ? "AUTO" : "ACTIVE";
  3013     // Some wifi drivers may not implement this command. Set power mode
  3014     // even if suspend optimization command failed.
  3015     WifiManager.setSuspendOptimizations(enabled, function(ok) {
  3016       WifiManager.setPowerMode(mode, function(ok) {
  3017         if (ok) {
  3018           self._sendMessage(message, true, true, msg);
  3019         } else {
  3020           self._sendMessage(message, false, "Set power saving mode failed", msg);
  3022       });
  3023     });
  3024   },
  3026   setHttpProxy: function(msg) {
  3027     const message = "WifiManager:setHttpProxy:Return";
  3028     let self = this;
  3029     let network = msg.data.network;
  3030     let info = msg.data.info;
  3032     WifiManager.configureHttpProxy(network, info, function(ok) {
  3033       if (ok) {
  3034         // If configured network is current connected network
  3035         // need update http proxy immediately.
  3036         let setNetworkKey = getNetworkKey(network);
  3037         let curNetworkKey = self.currentNetwork ? getNetworkKey(self.currentNetwork) : null;
  3038         if (setNetworkKey === curNetworkKey)
  3039           WifiManager.setHttpProxy(network);
  3041         self._sendMessage(message, true, true, msg);
  3042       } else {
  3043         self._sendMessage(message, false, "Set http proxy failed", msg);
  3045     });
  3046   },
  3048   setStaticIpMode: function(msg) {
  3049     const message = "WifiManager:setStaticMode:Return";
  3050     let self = this;
  3051     let network = msg.data.network;
  3052     let info = msg.data.info;
  3054     // To compatiable with DHCP returned info structure, do translation here
  3055     info.ipaddr_str = info.ipaddr;
  3056     info.proxy_str = info.proxy;
  3057     info.gateway_str = info.gateway;
  3058     info.dns1_str = info.dns1;
  3059     info.dns2_str = info.dns2;
  3061     WifiManager.setStaticIpMode(network, info, function(ok) {
  3062       if (ok) {
  3063         self._sendMessage(message, true, true, msg);
  3064       } else {
  3065         self._sendMessage(message, false, "Set static ip mode failed", msg);
  3067     });
  3068   },
  3070   // This is a bit ugly, but works. In particular, this depends on the fact
  3071   // that RadioManager never actually tries to get the worker from us.
  3072   get worker() { throw "Not implemented"; },
  3074   shutdown: function() {
  3075     debug("shutting down ...");
  3076     this.queueRequest({command: "setWifiEnabled", value: false}, function(data) {
  3077       this.setWifiEnabled(false, this._setWifiEnabledCallback.bind(this));
  3078     }.bind(this));
  3079   },
  3081   requestProcessing: false,   // Hold while dequeue and execution a request.
  3082                               // Released upon the request is fully executed,
  3083                               // i.e, mostly after callback is done.
  3084   requestDone: function requestDone() {
  3085     this.requestProcessing = false;
  3086     this.nextRequest();
  3087   },
  3089   nextRequest: function nextRequest() {
  3090     // No request to process
  3091     if (this._stateRequests.length === 0) {
  3092       return;
  3095     // Handling request, wait for it.
  3096     if (this.requestProcessing) {
  3097       return;
  3100     // Hold processing lock
  3101     this.requestProcessing = true;
  3103     // Find next valid request
  3104     let request = this._stateRequests.shift();
  3106     request.callback(request.data);
  3107   },
  3109   notifyTetheringOn: function notifyTetheringOn() {
  3110     // It's really sad that we don't have an API to notify the wifi
  3111     // hotspot status. Toggle settings to let gaia know that wifi hotspot
  3112     // is enabled.
  3113     let self = this;
  3114     this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] = true;
  3115     this._oldWifiTetheringEnabledState = true;
  3116     gSettingsService.createLock().set(
  3117       SETTINGS_WIFI_TETHERING_ENABLED,
  3118       true,
  3120         handle: function(aName, aResult) {
  3121           self.requestDone();
  3122         },
  3123         handleError: function(aErrorMessage) {
  3124           self.requestDone();
  3126       },
  3127       "fromInternalSetting");
  3128   },
  3130   notifyTetheringOff: function notifyTetheringOff() {
  3131     // It's really sad that we don't have an API to notify the wifi
  3132     // hotspot status. Toggle settings to let gaia know that wifi hotspot
  3133     // is disabled.
  3134     let self = this;
  3135     this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] = false;
  3136     this._oldWifiTetheringEnabledState = false;
  3137     gSettingsService.createLock().set(
  3138       SETTINGS_WIFI_TETHERING_ENABLED,
  3139       false,
  3141         handle: function(aName, aResult) {
  3142           self.requestDone();
  3143         },
  3144         handleError: function(aErrorMessage) {
  3145           self.requestDone();
  3147       },
  3148       "fromInternalSetting");
  3149   },
  3151   handleWifiEnabled: function(enabled) {
  3152     // Make sure Wifi hotspot is idle before switching to Wifi mode.
  3153     if (enabled) {
  3154       this.queueRequest({command: "setWifiApEnabled", value: false}, function(data) {
  3155         if (this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] ||
  3156             WifiManager.isWifiTetheringEnabled(WifiManager.tetheringState)) {
  3157           this.disconnectedByWifi = true;
  3158           this.setWifiApEnabled(false, this.notifyTetheringOff.bind(this));
  3159         } else {
  3160           this.requestDone();
  3162       }.bind(this));
  3165     this.queueRequest({command: "setWifiEnabled", value: enabled}, function(data) {
  3166       this.setWifiEnabled(enabled, this._setWifiEnabledCallback.bind(this));
  3167     }.bind(this));
  3169     if (!enabled) {
  3170       this.queueRequest({command: "setWifiApEnabled", value: true}, function(data) {
  3171         if (this.disconnectedByWifi) {
  3172           this.setWifiApEnabled(true, this.notifyTetheringOn.bind(this));
  3173         } else {
  3174           this.requestDone();
  3176         this.disconnectedByWifi = false;
  3177       }.bind(this));
  3179   },
  3181   handleWifiTetheringEnabled: function(enabled) {
  3182     // Make sure Wifi is idle before switching to Wifi hotspot mode.
  3183     if (enabled) {
  3184       this.queueRequest({command: "setWifiEnabled", value: false}, function(data) {
  3185         if (WifiManager.isWifiEnabled(WifiManager.state)) {
  3186           this.disconnectedByWifiTethering = true;
  3187           this.setWifiEnabled(false, this._setWifiEnabledCallback.bind(this));
  3188         } else {
  3189           this.requestDone();
  3191       }.bind(this));
  3194     this.queueRequest({command: "setWifiApEnabled", value: enabled}, function(data) {
  3195       this.setWifiApEnabled(enabled, this.requestDone.bind(this));
  3196     }.bind(this));
  3198     if (!enabled) {
  3199       this.queueRequest({command: "setWifiEnabled", value: true}, function(data) {
  3200         if (this.disconnectedByWifiTethering) {
  3201           this.setWifiEnabled(true, this._setWifiEnabledCallback.bind(this));
  3202         } else {
  3203           this.requestDone();
  3205         this.disconnectedByWifiTethering = false;
  3206       }.bind(this));
  3208   },
  3210   // nsIObserver implementation
  3211   observe: function observe(subject, topic, data) {
  3212     // Note that this function gets called for any and all settings changes,
  3213     // so we need to carefully check if we have the one we're interested in.
  3214     // The string we're interested in will be a JSON string that looks like:
  3215     // {"key":"wifi.enabled","value":"true"}.
  3216     if (topic !== kMozSettingsChangedObserverTopic) {
  3217       return;
  3220     let setting = JSON.parse(data);
  3221     // To avoid WifiWorker setting the wifi again, don't need to deal with
  3222     // the "mozsettings-changed" event fired from internal setting.
  3223     if (setting.message && setting.message === "fromInternalSetting") {
  3224       return;
  3227     this.handle(setting.key, setting.value);
  3228   },
  3230   handle: function handle(aName, aResult) {
  3231     switch(aName) {
  3232       case SETTINGS_WIFI_ENABLED:
  3233         this.handleWifiEnabled(aResult)
  3234         break;
  3235       case SETTINGS_WIFI_DEBUG_ENABLED:
  3236         if (aResult === null)
  3237           aResult = false;
  3238         DEBUG = aResult;
  3239         updateDebug();
  3240         break;
  3241       case SETTINGS_WIFI_TETHERING_ENABLED:
  3242         this._oldWifiTetheringEnabledState = this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED];
  3243         // Fall through!
  3244       case SETTINGS_WIFI_SSID:
  3245       case SETTINGS_WIFI_SECURITY_TYPE:
  3246       case SETTINGS_WIFI_SECURITY_PASSWORD:
  3247       case SETTINGS_WIFI_IP:
  3248       case SETTINGS_WIFI_PREFIX:
  3249       case SETTINGS_WIFI_DHCPSERVER_STARTIP:
  3250       case SETTINGS_WIFI_DHCPSERVER_ENDIP:
  3251       case SETTINGS_WIFI_DNS1:
  3252       case SETTINGS_WIFI_DNS2:
  3253       case SETTINGS_USB_DHCPSERVER_STARTIP:
  3254       case SETTINGS_USB_DHCPSERVER_ENDIP:
  3255         if (aResult !== null) {
  3256           this.tetheringSettings[aName] = aResult;
  3258         debug("'" + aName + "'" + " is now " + this.tetheringSettings[aName]);
  3259         let index = this._wifiTetheringSettingsToRead.indexOf(aName);
  3261         if (index != -1) {
  3262           this._wifiTetheringSettingsToRead.splice(index, 1);
  3265         if (this._wifiTetheringSettingsToRead.length) {
  3266           debug("We haven't read completely the wifi Tethering data from settings db.");
  3267           break;
  3270         if (this._oldWifiTetheringEnabledState === this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED]) {
  3271           debug("No changes for SETTINGS_WIFI_TETHERING_ENABLED flag. Nothing to do.");
  3272           break;
  3275         if (this._oldWifiTetheringEnabledState === null &&
  3276             !this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED]) {
  3277           debug("Do nothing when initial settings for SETTINGS_WIFI_TETHERING_ENABLED flag is false.");
  3278           break;
  3281         this._oldWifiTetheringEnabledState = this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED];
  3282         this.handleWifiTetheringEnabled(aResult)
  3283         break;
  3284     };
  3285   },
  3287   handleError: function handleError(aErrorMessage) {
  3288     debug("There was an error while reading Tethering settings.");
  3289     this.tetheringSettings = {};
  3290     this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] = false;
  3291   },
  3292 };
  3294 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WifiWorker]);
  3296 let debug;
  3297 function updateDebug() {
  3298   if (DEBUG) {
  3299     debug = function (s) {
  3300       dump("-*- WifiWorker component: " + s + "\n");
  3301     };
  3302   } else {
  3303     debug = function (s) {};
  3305   WifiManager.syncDebug();
  3307 updateDebug();

mercurial