1 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
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/FileUtils.jsm");
15 Cu.import("resource://gre/modules/WifiCommand.jsm");
16 Cu.import("resource://gre/modules/WifiNetUtil.jsm");
17 Cu.import("resource://gre/modules/WifiP2pManager.jsm");
18 Cu.import("resource://gre/modules/WifiP2pWorkerObserver.jsm");
20 var DEBUG = false; // set to true to show debug messages.
22 const WIFIWORKER_CONTRACTID = "@mozilla.org/wifi/worker;1";
23 const WIFIWORKER_CID = Components.ID("{a14e8977-d259-433a-a88d-58dd44657e5b}");
25 const WIFIWORKER_WORKER = "resource://gre/modules/wifi_worker.js";
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",
89 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
90 "@mozilla.org/network/service;1",
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() {
107 function getStartupPrefs() {
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")
119 let {sdkVersion, unloadDriverEnabled, schedScanRecovery,
120 driverDelay, p2pSupported, eapSimSupported, ifname} = getStartupPrefs();
123 security: ["OPEN", "WEP", "WPA-PSK", "WPA-EAP"],
124 eapMethod: ["PEAP", "TTLS"],
125 eapPhase2: ["MSCHAPV2"],
126 certificate: ["SERVER"]
128 if (eapSimSupported) {
129 capabilities.eapMethod.unshift("SIM");
133 onWaitEvent: function(event, iface) {
134 if (manager.ifname === iface && handleEvent(event)) {
136 } else if (p2pSupported) {
137 if (WifiP2pManager.INTERFACE_NAME === iface) {
138 // If the connection is closed, wifi.c::wifi_wait_for_event()
139 // will still return 'CTRL-EVENT-TERMINATING - connection closed'
140 // rather than blocking. So when we see this special event string,
141 // just return immediately.
142 const TERMINATED_EVENT = 'CTRL-EVENT-TERMINATING - connection closed';
143 if (-1 !== event.indexOf(TERMINATED_EVENT)) {
146 p2pManager.handleEvent(event);
152 onCommand: function(event, iface) {
153 onmessageresult(event, iface);
157 manager.ifname = ifname;
158 manager.connectToSupplicant = false;
159 // Emulator build runs to here.
160 // The debug() should only be used after WifiManager.
162 manager.ifname = DEFAULT_WLAN_INTERFACE;
164 manager.schedScanRecovery = schedScanRecovery;
165 manager.driverDelay = driverDelay ? parseInt(driverDelay, 10) : DRIVER_READY_WAIT;
167 // Regular Wifi stuff.
168 var netUtil = WifiNetUtil(controlMessage);
169 var wifiCommand = WifiCommand(controlMessage, manager.ifname, sdkVersion);
174 let p2pCommand = WifiCommand(controlMessage, WifiP2pManager.INTERFACE_NAME, sdkVersion);
175 p2pManager = WifiP2pManager(p2pCommand, netUtil);
178 let wifiService = Cc["@mozilla.org/wifi/service;1"];
180 wifiService = wifiService.getService(Ci.nsIWifiProxyService);
181 let interfaces = [manager.ifname];
183 interfaces.push(WifiP2pManager.INTERFACE_NAME);
185 wifiService.start(wifiListener, interfaces, interfaces.length);
187 debug("No wifi service component available!");
190 // Callbacks to invoke when a reply arrives from the wifi service.
191 var controlCallbacks = Object.create(null);
194 function controlMessage(obj, callback) {
198 controlCallbacks[id] = callback;
200 wifiService.sendCommand(obj, obj.iface);
203 let onmessageresult = function(data, iface) {
205 var callback = controlCallbacks[id];
208 delete controlCallbacks[id];
212 // Polling the status worker
215 function waitForEvent(iface) {
216 wifiService.waitForEvent(iface);
219 // Commands to the control worker.
221 var driverLoaded = false;
223 function loadDriver(callback) {
229 wifiCommand.loadDriver(function (status) {
230 driverLoaded = (status >= 0);
235 function unloadDriver(type, callback) {
236 if (!unloadDriverEnabled) {
237 // Unloading drivers is generally unnecessary and
238 // can trigger bugs in some drivers.
239 // On properly written drivers, bringing the interface
240 // down powers down the interface.
241 if (type === WIFI_FIRMWARE_STATION) {
242 notify("supplicantlost", { success: true });
248 wifiCommand.unloadDriver(function(status) {
249 driverLoaded = (status < 0);
250 if (type === WIFI_FIRMWARE_STATION) {
251 notify("supplicantlost", { success: true });
257 // A note about background scanning:
258 // Normally, background scanning shouldn't be necessary as wpa_supplicant
259 // has the capability to automatically schedule its own scans at appropriate
260 // intervals. However, with some drivers, this appears to get stuck after
261 // three scans, so we enable the driver's background scanning to work around
262 // that when we're not connected to any network. This ensures that we'll
263 // automatically reconnect to networks if one falls out of range.
264 var reEnableBackgroundScan = false;
266 // NB: This is part of the internal API.
267 manager.backgroundScanEnabled = false;
268 function setBackgroundScan(enable, callback) {
269 var doEnable = (enable === "ON");
270 if (doEnable === manager.backgroundScanEnabled) {
271 callback(false, true);
275 manager.backgroundScanEnabled = doEnable;
276 wifiCommand.setBackgroundScan(manager.backgroundScanEnabled, callback);
279 var scanModeActive = false;
281 function scan(forceActive, callback) {
282 if (forceActive && !scanModeActive) {
283 // Note: we ignore errors from doSetScanMode.
284 wifiCommand.doSetScanMode(true, function(ignore) {
285 setBackgroundScan("OFF", function(turned, ignore) {
286 reEnableBackgroundScan = turned;
287 manager.handlePreWifiScan();
288 wifiCommand.scan(function(ok) {
289 wifiCommand.doSetScanMode(false, function(ignore) {
290 // The result of scanCommand is the result of the actual SCAN
299 manager.handlePreWifiScan();
300 wifiCommand.scan(callback);
303 var debugEnabled = false;
305 function syncDebug() {
306 if (debugEnabled !== DEBUG) {
308 wifiCommand.setLogLevel(wanted ? "DEBUG" : "INFO", function(ok) {
310 debugEnabled = wanted;
312 if (p2pSupported && p2pManager) {
313 p2pManager.setDebug(DEBUG);
318 function getDebugEnabled(callback) {
319 wifiCommand.getLogLevel(function(level) {
320 if (level === null) {
321 debug("Unable to get wpa_supplicant's log level");
326 var lines = level.split("\n");
327 for (let i = 0; i < lines.length; ++i) {
328 let match = /Current level: (.*)/.exec(lines[i]);
330 debugEnabled = match[1].toLowerCase() === "debug";
336 // If we're here, we didn't get the current level.
341 function setScanMode(setActive, callback) {
342 scanModeActive = setActive;
343 wifiCommand.doSetScanMode(setActive, callback);
346 var httpProxyConfig = Object.create(null);
349 * Given a network, configure http proxy when using wifi.
350 * @param network A network object to update http proxy
351 * @param info Info should have following field:
352 * - httpProxyHost ip address of http proxy.
353 * - httpProxyPort port of http proxy, set 0 to use default port 8080.
354 * @param callback callback function.
356 function configureHttpProxy(network, info, callback) {
360 let networkKey = getNetworkKey(network);
362 if (!info || info.httpProxyHost === "") {
363 delete httpProxyConfig[networkKey];
365 httpProxyConfig[networkKey] = network;
366 httpProxyConfig[networkKey].httpProxyHost = info.httpProxyHost;
367 httpProxyConfig[networkKey].httpProxyPort = info.httpProxyPort;
373 function getHttpProxyNetwork(network) {
377 let networkKey = getNetworkKey(network);
378 return ((networkKey in httpProxyConfig) ? httpProxyConfig : null);
381 function setHttpProxy(network) {
385 gNetworkService.setNetworkProxy(network);
388 var staticIpConfig = Object.create(null);
389 function setStaticIpMode(network, info, callback) {
390 let setNetworkKey = getNetworkKey(network);
391 let curNetworkKey = null;
392 let currentNetwork = Object.create(null);
393 currentNetwork.netId = manager.connectionInfo.id;
395 manager.getNetworkConfiguration(currentNetwork, function (){
396 curNetworkKey = getNetworkKey(currentNetwork);
398 // Add additional information to static ip configuration
399 // It is used to compatiable with information dhcp callback.
400 info.ipaddr = netHelpers.stringToIP(info.ipaddr_str);
401 info.gateway = netHelpers.stringToIP(info.gateway_str);
402 info.mask_str = netHelpers.ipToString(netHelpers.makeMask(info.maskLength));
405 info.dns1 = netHelpers.stringToIP(info.dns1_str);
406 info.dns2 = netHelpers.stringToIP(info.dns2_str);
407 info.proxy = netHelpers.stringToIP(info.proxy_str);
409 staticIpConfig[setNetworkKey] = info;
411 // If the ssid of current connection is the same as configured ssid
412 // It means we need update current connection to use static IP address.
413 if (setNetworkKey == curNetworkKey) {
414 // Use configureInterface directly doesn't work, the network iterface
415 // and routing table is changed but still cannot connect to network
416 // so the workaround here is disable interface the enable again to
417 // trigger network reconnect with static ip.
418 netUtil.disableInterface(manager.ifname, function (ok) {
419 netUtil.enableInterface(manager.ifname, function (ok) {
428 function runStaticIp(ifname, key) {
429 debug("Run static ip");
431 // Read static ip information from settings.
434 if (!(key in staticIpConfig))
437 staticIpInfo = staticIpConfig[key];
439 // Stop dhcpd when use static IP
440 if (dhcpInfo != null) {
441 netUtil.stopDhcp(manager.ifname, function() {});
444 // Set ip, mask length, gateway, dns to network interface
445 netUtil.configureInterface( { ifname: ifname,
446 ipaddr: staticIpInfo.ipaddr,
447 mask: staticIpInfo.maskLength,
448 gateway: staticIpInfo.gateway,
449 dns1: staticIpInfo.dns1,
450 dns2: staticIpInfo.dns2 }, function (data) {
451 netUtil.runIpConfig(ifname, staticIpInfo, function(data) {
452 dhcpInfo = data.info;
453 notify("networkconnected", data);
458 var suppressEvents = false;
459 function notify(eventName, eventObject) {
462 var handler = manager["on" + eventName];
466 handler.call(eventObject);
470 function notifyStateChange(fields) {
471 // If we're already in the COMPLETED state, we might receive events from
472 // the supplicant that tell us that we're re-authenticating or reminding
473 // us that we're associated to a network. In those cases, we don't need to
474 // do anything, so just ignore them.
475 if (manager.state === "COMPLETED" &&
476 fields.state !== "DISCONNECTED" &&
477 fields.state !== "INTERFACE_DISABLED" &&
478 fields.state !== "INACTIVE" &&
479 fields.state !== "SCANNING") {
483 // Stop background scanning if we're trying to connect to a network.
484 if (manager.backgroundScanEnabled &&
485 (fields.state === "ASSOCIATING" ||
486 fields.state === "ASSOCIATED" ||
487 fields.state === "FOUR_WAY_HANDSHAKE" ||
488 fields.state === "GROUP_HANDSHAKE" ||
489 fields.state === "COMPLETED")) {
490 setBackgroundScan("OFF", function() {});
493 fields.prevState = manager.state;
494 // Detect wpa_supplicant's loop iterations.
495 manager.supplicantLoopDetection(fields.prevState, fields.state);
496 notify("statechange", fields);
498 // Don't update state when and after disabling.
499 if (manager.state === "DISABLING" ||
500 manager.state === "UNINITIALIZED") {
504 manager.state = fields.state;
508 function parseStatus(status) {
509 if (status === null) {
510 debug("Unable to get wpa supplicant's status");
520 var lines = status.split("\n");
521 for (let i = 0; i < lines.length; ++i) {
522 let [key, value] = lines[i].split("=");
543 manager.connectionInfo.bssid = bssid;
544 manager.connectionInfo.ssid = ssid;
545 manager.connectionInfo.id = id;
549 dhcpInfo = { ip_address: ip_address };
551 notifyStateChange({ state: state, fromStatus: true });
553 // If we parse the status and the supplicant has already entered the
554 // COMPLETED state, then we need to set up DHCP right away.
555 if (state === "COMPLETED")
559 // try to connect to the supplicant
560 var connectTries = 0;
561 var retryTimer = null;
562 function connectCallback(ok) {
564 // Tell the event worker to start waiting for events.
568 manager.connectToSupplicant = true;
569 didConnectSupplicant(function(){});
572 if (connectTries++ < 5) {
573 // Try again in 1 seconds.
575 retryTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
577 retryTimer.initWithCallback(function(timer) {
578 wifiCommand.connectToSupplicant(connectCallback);
579 }, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
585 notify("supplicantlost", { success: false });
588 manager.connectionDropped = function(callback) {
589 // Reset network interface when connection drop
590 netUtil.configureInterface( { ifname: manager.ifname,
595 dns2: 0 }, function (data) {
598 // If we got disconnected, kill the DHCP client in preparation for
600 netUtil.resetConnections(manager.ifname, function() {
601 netUtil.stopDhcp(manager.ifname, function() {
607 manager.start = function() {
608 debug("detected SDK version " + sdkVersion);
609 wifiCommand.connectToSupplicant(connectCallback);
612 function onconnected() {
613 // For now we do our own DHCP. In the future, this should be handed
614 // off to the Network Manager.
615 let currentNetwork = Object.create(null);
616 currentNetwork.netId = manager.connectionInfo.id;
618 manager.getNetworkConfiguration(currentNetwork, function (){
619 let key = getNetworkKey(currentNetwork);
620 if (staticIpConfig &&
621 (key in staticIpConfig) &&
622 staticIpConfig[key].enabled) {
623 debug("Run static ip");
624 runStaticIp(manager.ifname, key);
627 netUtil.runDhcp(manager.ifname, function(data) {
628 dhcpInfo = data.info;
630 if (++manager.dhcpFailuresCount >= MAX_RETRIES_ON_DHCP_FAILURE) {
631 manager.dhcpFailuresCount = 0;
632 notify("disconnected", {connectionInfo: manager.connectionInfo});
635 // NB: We have to call disconnect first. Otherwise, we only reauth with
636 // the existing AP and don't retrigger DHCP.
637 manager.disconnect(function() {
638 manager.reassociate(function(){});
643 manager.dhcpFailuresCount = 0;
644 notify("networkconnected", data);
649 var supplicantStatesMap = (sdkVersion >= 15) ?
650 ["DISCONNECTED", "INTERFACE_DISABLED", "INACTIVE", "SCANNING",
651 "AUTHENTICATING", "ASSOCIATING", "ASSOCIATED", "FOUR_WAY_HANDSHAKE",
652 "GROUP_HANDSHAKE", "COMPLETED"]
654 ["DISCONNECTED", "INACTIVE", "SCANNING", "ASSOCIATING",
655 "ASSOCIATED", "FOUR_WAY_HANDSHAKE", "GROUP_HANDSHAKE",
656 "COMPLETED", "DORMANT", "UNINITIALIZED"];
658 var driverEventMap = { STOPPED: "driverstopped", STARTED: "driverstarted", HANGED: "driverhung" };
660 manager.getNetworkId = function (ssid, callback) {
661 manager.getConfiguredNetworks(function(networks) {
663 debug("Unable to get configured networks");
664 return callback(null);
666 for (let net in networks) {
667 let network = networks[net];
668 // Trying to get netId from
669 // 1. network matching SSID if SSID is provided.
670 // 2. current network if no SSID is provided, it's not guaranteed that
671 // current network matches requested SSID.
672 if ((!ssid && network.status === "CURRENT") ||
673 (ssid && network.ssid && ssid === dequote(network.ssid))) {
674 return callback(net);
681 function handleWpaEapEvents(event) {
682 if (event.indexOf("CTRL-EVENT-EAP-FAILURE") !== -1) {
683 if (event.indexOf("EAP authentication failed") !== -1) {
684 notify("passwordmaybeincorrect");
685 if (manager.authenticationFailuresCount > MAX_RETRIES_ON_AUTHENTICATION_FAILURE) {
686 manager.authenticationFailuresCount = 0;
687 notify("disconnected", {connectionInfo: manager.connectionInfo});
692 if (event.indexOf("CTRL-EVENT-EAP-TLS-CERT-ERROR") !== -1) {
694 notify("passwordmaybeincorrect");
695 if (manager.authenticationFailuresCount > MAX_RETRIES_ON_AUTHENTICATION_FAILURE) {
696 manager.authenticationFailuresCount = 0;
697 notify("disconnected", {connectionInfo: manager.connectionInfo});
701 if (event.indexOf("CTRL-EVENT-EAP-STARTED") !== -1) {
702 notifyStateChange({ state: "AUTHENTICATING" });
708 // Handle events sent to us by the event worker.
709 function handleEvent(event) {
710 debug("Event coming in: " + event);
711 if (event.indexOf("CTRL-EVENT-") !== 0 && event.indexOf("WPS") !== 0) {
712 // Handle connection fail exception on WEP-128, while password length
713 // is not 5 nor 13 bytes.
714 if (event.indexOf("Association request to the driver failed") !== -1) {
715 notify("passwordmaybeincorrect");
716 if (manager.authenticationFailuresCount > MAX_RETRIES_ON_AUTHENTICATION_FAILURE) {
717 manager.authenticationFailuresCount = 0;
718 notify("disconnected", {connectionInfo: manager.connectionInfo});
723 if (event.indexOf("WPA:") == 0 &&
724 event.indexOf("pre-shared key may be incorrect") != -1) {
725 notify("passwordmaybeincorrect");
728 // This is ugly, but we need to grab the SSID here. BSSID is not guaranteed
729 // to be provided, so don't grab BSSID here.
730 var match = /Trying to associate with.*SSID[ =]'(.*)'/.exec(event);
732 debug("Matched: " + match[1] + "\n");
733 manager.connectionInfo.ssid = match[1];
738 var space = event.indexOf(" ");
739 var eventData = event.substr(0, space + 1);
740 if (eventData.indexOf("CTRL-EVENT-STATE-CHANGE") === 0) {
741 // Parse the event data.
743 var tokens = event.substr(space + 1).split(" ");
744 for (var n = 0; n < tokens.length; ++n) {
745 var kv = tokens[n].split("=");
747 fields[kv[0]] = kv[1];
749 if (!("state" in fields))
751 fields.state = supplicantStatesMap[fields.state];
753 // The BSSID field is only valid in the ASSOCIATING and ASSOCIATED
754 // states, except when we "reauth", except this seems to depend on the
755 // driver, so simply check to make sure that we don't have a null BSSID.
756 if (fields.BSSID !== "00:00:00:00:00:00")
757 manager.connectionInfo.bssid = fields.BSSID;
759 if (notifyStateChange(fields) && fields.state === "COMPLETED") {
764 if (eventData.indexOf("CTRL-EVENT-DRIVER-STATE") === 0) {
765 var handlerName = driverEventMap[eventData];
770 if (eventData.indexOf("CTRL-EVENT-TERMINATING") === 0) {
771 // As long the monitor socket is not closed and we haven't seen too many
772 // recv errors yet, we will keep going for a bit longer.
773 if (event.indexOf("connection closed") === -1 &&
774 event.indexOf("recv error") !== -1 && ++recvErrors < 10)
777 notifyStateChange({ state: "DISCONNECTED", BSSID: null, id: -1 });
779 // If the supplicant is terminated as commanded, the supplicant lost
780 // notification will be sent after driver unloaded. In such case, the
781 // manager state will be "DISABLING" or "UNINITIALIZED".
782 // So if supplicant terminated with incorrect manager state, implying
783 // unexpected condition, we should notify supplicant lost here.
784 if (manager.state !== "DISABLING" && manager.state !== "UNINITIALIZED") {
785 notify("supplicantlost", { success: true });
788 if (manager.stopSupplicantCallback) {
789 cancelWaitForTerminateEventTimer();
790 // It's possible that the terminating event triggered by timer comes
791 // earlier than the event from wpa_supplicant. Since
792 // stopSupplicantCallback contains async. callbacks, swap it to local
793 // to prevent calling the callback twice.
794 let stopSupplicantCallback = manager.stopSupplicantCallback.bind(manager);
795 manager.stopSupplicantCallback = null;
796 stopSupplicantCallback();
797 stopSupplicantCallback = null;
801 if (eventData.indexOf("CTRL-EVENT-DISCONNECTED") === 0) {
802 var token = event.split(" ")[1];
803 var bssid = token.split("=")[1];
804 if (manager.authenticationFailuresCount > MAX_RETRIES_ON_AUTHENTICATION_FAILURE) {
805 manager.authenticationFailuresCount = 0;
806 notify("disconnected", {connectionInfo: manager.connectionInfo});
808 manager.connectionInfo.bssid = null;
809 manager.connectionInfo.ssid = null;
810 manager.connectionInfo.id = -1;
813 if (eventData.indexOf("CTRL-EVENT-CONNECTED") === 0) {
814 // Format: CTRL-EVENT-CONNECTED - Connection to 00:1e:58:ec:d5:6d completed (reauth) [id=1 id_str=]
815 var bssid = event.split(" ")[4];
818 var id = event.substr(event.indexOf(keyword) + keyword.length).split(" ")[0];
819 // Read current BSSID here, it will always being provided.
820 manager.connectionInfo.id = id;
821 manager.connectionInfo.bssid = bssid;
824 if (eventData.indexOf("CTRL-EVENT-SCAN-RESULTS") === 0) {
825 debug("Notifying of scan results available");
826 if (reEnableBackgroundScan) {
827 reEnableBackgroundScan = false;
828 setBackgroundScan("ON", function() {});
830 manager.handlePostWifiScan();
831 notify("scanresultsavailable");
834 if (eventData.indexOf("CTRL-EVENT-EAP") === 0) {
835 return handleWpaEapEvents(event);
837 if (eventData.indexOf("WPS-TIMEOUT") === 0) {
838 notifyStateChange({ state: "WPS_TIMEOUT", BSSID: null, id: -1 });
841 if (eventData.indexOf("WPS-FAIL") === 0) {
842 notifyStateChange({ state: "WPS_FAIL", BSSID: null, id: -1 });
845 if (eventData.indexOf("WPS-OVERLAP-DETECTED") === 0) {
846 notifyStateChange({ state: "WPS_OVERLAP_DETECTED", BSSID: null, id: -1 });
853 function didConnectSupplicant(callback) {
854 waitForEvent(manager.ifname);
856 // Load up the supplicant state.
857 getDebugEnabled(function(ok) {
860 wifiCommand.status(function(status) {
862 notify("supplicantconnection");
867 manager.enableP2p(function(success) {});
871 function prepareForStartup(callback) {
872 let status = libcutils.property_get(DHCP_PROP + "_" + manager.ifname);
873 if (status !== "running") {
877 manager.connectionDropped(function() {
881 // Ignore any errors and kill any currently-running supplicants. On some
882 // phones, stopSupplicant won't work for a supplicant that we didn't
883 // start, so we hand-roll it here.
884 function tryStopSupplicant () {
885 let status = libcutils.property_get(SUPP_PROP);
886 if (status !== "running") {
890 suppressEvents = true;
891 wifiCommand.killSupplicant(function() {
892 netUtil.disableInterface(manager.ifname, function (ok) {
893 suppressEvents = false;
901 manager.state = "UNINITIALIZED";
902 manager.tetheringState = "UNINITIALIZED";
903 manager.supplicantStarted = false;
904 manager.connectionInfo = { ssid: null, bssid: null, id: -1 };
905 manager.authenticationFailuresCount = 0;
906 manager.loopDetectionCount = 0;
907 manager.dhcpFailuresCount = 0;
908 manager.stopSupplicantCallback = null;
910 manager.__defineGetter__("enabled", function() {
911 switch (manager.state) {
912 case "UNINITIALIZED":
921 var waitForTerminateEventTimer = null;
922 function cancelWaitForTerminateEventTimer() {
923 if (waitForTerminateEventTimer) {
924 waitForTerminateEventTimer.cancel();
925 waitForTerminateEventTimer = null;
928 function createWaitForTerminateEventTimer(onTimeout) {
929 if (waitForTerminateEventTimer) {
932 waitForTerminateEventTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
933 waitForTerminateEventTimer.initWithCallback(onTimeout,
935 Ci.nsITimer.TYPE_ONE_SHOT);
938 var waitForDriverReadyTimer = null;
939 function cancelWaitForDriverReadyTimer() {
940 if (waitForDriverReadyTimer) {
941 waitForDriverReadyTimer.cancel();
942 waitForDriverReadyTimer = null;
945 function createWaitForDriverReadyTimer(onTimeout) {
946 waitForDriverReadyTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
947 waitForDriverReadyTimer.initWithCallback(onTimeout,
949 Ci.nsITimer.TYPE_ONE_SHOT);
952 // Public interface of the wifi service.
953 manager.setWifiEnabled = function(enabled, callback) {
954 if (enabled === manager.isWifiEnabled(manager.state)) {
955 callback("no change");
960 manager.state = "INITIALIZING";
961 // Register as network interface.
962 WifiNetworkInterface.name = manager.ifname;
963 if (!WifiNetworkInterface.registered) {
964 gNetworkManager.registerNetworkInterface(WifiNetworkInterface);
965 WifiNetworkInterface.registered = true;
967 WifiNetworkInterface.state = Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED;
968 WifiNetworkInterface.ips = [];
969 WifiNetworkInterface.prefixLengths = [];
970 WifiNetworkInterface.gateways = [];
971 WifiNetworkInterface.dnses = [];
972 gNetworkManager.updateNetworkInterface(WifiNetworkInterface);
974 prepareForStartup(function() {
975 loadDriver(function (status) {
978 manager.state = "UNINITIALIZED";
981 // This command is mandatory for Nexus 4. But some devices like
982 // Galaxy S2 don't support it. Continue to start wpa_supplicant
983 // even if we fail to set wifi operation mode to station.
984 gNetworkService.setWifiOperationMode(manager.ifname,
985 WIFI_FIRMWARE_STATION,
988 function startSupplicantInternal() {
989 wifiCommand.startSupplicant(function (status) {
991 unloadDriver(WIFI_FIRMWARE_STATION, function() {
994 manager.state = "UNINITIALIZED";
998 manager.supplicantStarted = true;
999 netUtil.enableInterface(manager.ifname, function (ok) {
1000 callback(ok ? 0 : -1);
1005 function doStartSupplicant() {
1006 cancelWaitForDriverReadyTimer();
1008 if (!manager.connectToSupplicant) {
1009 startSupplicantInternal();
1012 wifiCommand.closeSupplicantConnection(function () {
1013 manager.connectToSupplicant = false;
1014 // closeSupplicantConnection() will trigger onsupplicantlost
1015 // and set manager.state to "UNINITIALIZED", we have to
1017 manager.state = "INITIALIZING";
1018 startSupplicantInternal();
1021 // Driver startup on certain platforms takes longer than it takes for us
1022 // to return from loadDriver, so wait 2 seconds before starting
1023 // the supplicant to give it a chance to start.
1024 if (manager.driverDelay > 0) {
1025 createWaitForDriverReadyTimer(doStartSupplicant);
1027 doStartSupplicant();
1033 manager.state = "DISABLING";
1034 // Note these following calls ignore errors. If we fail to kill the
1035 // supplicant gracefully, then we need to continue telling it to die
1037 let doDisableWifi = function() {
1038 manager.stopSupplicantCallback = (function () {
1039 wifiCommand.stopSupplicant(function (status) {
1040 wifiCommand.closeSupplicantConnection(function() {
1041 manager.connectToSupplicant = false;
1042 manager.state = "UNINITIALIZED";
1043 netUtil.disableInterface(manager.ifname, function (ok) {
1044 unloadDriver(WIFI_FIRMWARE_STATION, callback);
1050 let terminateEventCallback = (function() {
1051 handleEvent("CTRL-EVENT-TERMINATING");
1053 createWaitForTerminateEventTimer(terminateEventCallback);
1055 // We are going to terminate the connection between wpa_supplicant.
1056 // Stop the polling timer immediately to prevent connection info update
1057 // command blocking in control thread until socket timeout.
1058 notify("stopconnectioninfotimer");
1060 wifiCommand.terminateSupplicant(function (ok) {
1061 manager.connectionDropped(function () {
1067 p2pManager.setEnabled(false, { onDisabled: doDisableWifi });
1074 var wifiHotspotStatusTimer = null;
1075 function cancelWifiHotspotStatusTimer() {
1076 if (wifiHotspotStatusTimer) {
1077 wifiHotspotStatusTimer.cancel();
1078 wifiHotspotStatusTimer = null;
1082 function createWifiHotspotStatusTimer(onTimeout) {
1083 wifiHotspotStatusTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
1084 wifiHotspotStatusTimer.init(onTimeout, 5000, Ci.nsITimer.TYPE_REPEATING_SLACK);
1087 // Get wifi interface and load wifi driver when enable Ap mode.
1088 manager.setWifiApEnabled = function(enabled, configuration, callback) {
1089 if (enabled === manager.isWifiTetheringEnabled(manager.tetheringState)) {
1090 callback("no change");
1095 manager.tetheringState = "INITIALIZING";
1096 loadDriver(function (status) {
1099 manager.tetheringState = "UNINITIALIZED";
1100 if (wifiHotspotStatusTimer) {
1101 cancelWifiHotspotStatusTimer();
1102 wifiCommand.closeHostapdConnection(function(result) {
1108 function getWifiHotspotStatus() {
1109 wifiCommand.hostapdGetStations(function(result) {
1110 notify("stationinfoupdate", {station: result});
1114 function doStartWifiTethering() {
1115 cancelWaitForDriverReadyTimer();
1116 WifiNetworkInterface.name = libcutils.property_get("wifi.tethering.interface", manager.ifname);
1117 gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface,
1118 configuration, function(result) {
1120 manager.tetheringState = "UNINITIALIZED";
1122 manager.tetheringState = "COMPLETED";
1123 wifiCommand.connectToHostapd(function(result) {
1127 // Create a timer to track the connection status.
1128 createWifiHotspotStatusTimer(getWifiHotspotStatus);
1131 // Pop out current request.
1133 // Should we fire a dom event if we fail to set wifi tethering ?
1134 debug("Enable Wifi tethering result: " + (result ? result : "successfully"));
1138 // Driver startup on certain platforms takes longer than it takes
1139 // for us to return from loadDriver, so wait 2 seconds before
1140 // turning on Wifi tethering.
1141 createWaitForDriverReadyTimer(doStartWifiTethering);
1144 cancelWifiHotspotStatusTimer();
1145 gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface,
1146 configuration, function(result) {
1147 // Should we fire a dom event if we fail to set wifi tethering ?
1148 debug("Disable Wifi tethering result: " + (result ? result : "successfully"));
1149 // Unload wifi driver even if we fail to control wifi tethering.
1150 unloadDriver(WIFI_FIRMWARE_AP, function(status) {
1152 debug("Fail to unload wifi driver");
1154 manager.tetheringState = "UNINITIALIZED";
1161 manager.disconnect = wifiCommand.disconnect;
1162 manager.reconnect = wifiCommand.reconnect;
1163 manager.reassociate = wifiCommand.reassociate;
1165 var networkConfigurationFields = [
1166 {name: "ssid", type: "string"},
1167 {name: "bssid", type: "string"},
1168 {name: "psk", type: "string"},
1169 {name: "wep_key0", type: "string"},
1170 {name: "wep_key1", type: "string"},
1171 {name: "wep_key2", type: "string"},
1172 {name: "wep_key3", type: "string"},
1173 {name: "wep_tx_keyidx", type: "integer"},
1174 {name: "priority", type: "integer"},
1175 {name: "key_mgmt", type: "string"},
1176 {name: "scan_ssid", type: "string"},
1177 {name: "disabled", type: "integer"},
1178 {name: "identity", type: "string"},
1179 {name: "password", type: "string"},
1180 {name: "auth_alg", type: "string"},
1181 {name: "phase1", type: "string"},
1182 {name: "phase2", type: "string"},
1183 {name: "eap", type: "string"},
1184 {name: "pin", type: "string"},
1185 {name: "pcsc", type: "string"},
1186 {name: "ca_cert", type: "string"},
1187 {name: "subject_match", type: "string"}
1190 manager.getNetworkConfiguration = function(config, callback) {
1191 var netId = config.netId;
1193 for (var n = 0; n < networkConfigurationFields.length; ++n) {
1194 let fieldName = networkConfigurationFields[n].name;
1195 let fieldType = networkConfigurationFields[n].type;
1196 wifiCommand.getNetworkVariable(netId, fieldName, function(value) {
1197 if (value !== null) {
1198 if (fieldType === "integer") {
1199 config[fieldName] = parseInt(value, 10);
1200 } else if ( fieldName == "ssid" && value[0] != '"' ) {
1201 // SET_NETWORK will set a quoted ssid to wpa_supplicant.
1202 // But if ssid contains non-ascii char, it will be converted into utf-8.
1203 // For example: "Testçš„wifi" --> 54657374e79a8477696669
1204 // When GET_NETWORK receive a un-quoted utf-8 ssid, it must be decoded and quoted.
1205 config[fieldName] = quote(decodeURIComponent(value.replace(/[0-9a-f]{2}/g, '%$&')));
1207 // value is string type by default.
1208 config[fieldName] = value;
1211 if (++done == networkConfigurationFields.length)
1216 manager.setNetworkConfiguration = function(config, callback) {
1217 var netId = config.netId;
1221 function hasValidProperty(name) {
1222 return ((name in config) &&
1223 config[name] != null &&
1224 (["password", "wep_key0", "psk"].indexOf(name) === -1 ||
1225 config[name] !== '*'));
1228 for (var n = 0; n < networkConfigurationFields.length; ++n) {
1229 let fieldName = networkConfigurationFields[n].name;
1230 if (!hasValidProperty(fieldName)) {
1233 wifiCommand.setNetworkVariable(netId, fieldName, config[fieldName], function(ok) {
1236 if (++done == networkConfigurationFields.length)
1237 callback(errors == 0);
1241 // If config didn't contain any of the fields we want, don't lose the error callback.
1242 if (done == networkConfigurationFields.length)
1245 manager.getConfiguredNetworks = function(callback) {
1246 wifiCommand.listNetworks(function (reply) {
1247 var networks = Object.create(null);
1248 var lines = reply ? reply.split("\n") : 0;
1249 if (lines.length <= 1) {
1250 // We need to make sure we call the callback even if there are no
1251 // configured networks.
1258 for (var n = 1; n < lines.length; ++n) {
1259 var result = lines[n].split("\t");
1260 var netId = parseInt(result[0], 10);
1261 var config = networks[netId] = { netId: netId };
1262 switch (result[3]) {
1264 config.status = "CURRENT";
1267 config.status = "DISABLED";
1270 config.status = "ENABLED";
1273 manager.getNetworkConfiguration(config, function (ok) {
1276 if (++done == lines.length - 1) {
1278 // If an error occured, delete the new netId.
1279 wifiCommand.removeNetwork(netId, function() {
1290 manager.addNetwork = function(config, callback) {
1291 wifiCommand.addNetwork(function (netId) {
1292 config.netId = netId;
1293 manager.setNetworkConfiguration(config, function (ok) {
1295 wifiCommand.removeNetwork(netId, function() { callback(false); });
1303 manager.updateNetwork = function(config, callback) {
1304 manager.setNetworkConfiguration(config, callback);
1306 manager.removeNetwork = function(netId, callback) {
1307 wifiCommand.removeNetwork(netId, callback);
1310 manager.saveConfig = function(callback) {
1311 wifiCommand.saveConfig(callback);
1313 manager.enableNetwork = function(netId, disableOthers, callback) {
1315 // We have to stop wifi direct scan before associating to an AP.
1316 // Otherwise we will get a "REJECT" wpa supplicant event.
1317 p2pManager.setScanEnabled(false, function(success) {});
1319 wifiCommand.enableNetwork(netId, disableOthers, callback);
1321 manager.disableNetwork = function(netId, callback) {
1322 wifiCommand.disableNetwork(netId, callback);
1324 manager.getMacAddress = wifiCommand.getMacAddress;
1325 manager.getScanResults = wifiCommand.scanResults;
1326 manager.setScanMode = function(mode, callback) {
1327 setScanMode(mode === "active", callback); // Use our own version.
1329 manager.setBackgroundScan = setBackgroundScan; // Use our own version.
1330 manager.scan = scan; // Use our own version.
1331 manager.wpsPbc = wifiCommand.wpsPbc;
1332 manager.wpsPin = wifiCommand.wpsPin;
1333 manager.wpsCancel = wifiCommand.wpsCancel;
1334 manager.setPowerMode = (sdkVersion >= 16)
1335 ? wifiCommand.setPowerModeJB
1336 : wifiCommand.setPowerModeICS;
1337 manager.getHttpProxyNetwork = getHttpProxyNetwork;
1338 manager.setHttpProxy = setHttpProxy;
1339 manager.configureHttpProxy = configureHttpProxy;
1340 manager.setSuspendOptimizations = (sdkVersion >= 16)
1341 ? wifiCommand.setSuspendOptimizationsJB
1342 : wifiCommand.setSuspendOptimizationsICS;
1343 manager.setStaticIpMode = setStaticIpMode;
1344 manager.getRssiApprox = wifiCommand.getRssiApprox;
1345 manager.getLinkSpeed = wifiCommand.getLinkSpeed;
1346 manager.getDhcpInfo = function() { return dhcpInfo; }
1347 manager.getConnectionInfo = (sdkVersion >= 15)
1348 ? wifiCommand.getConnectionInfoICS
1349 : wifiCommand.getConnectionInfoGB;
1351 manager.isHandShakeState = function(state) {
1353 case "AUTHENTICATING":
1356 case "FOUR_WAY_HANDSHAKE":
1357 case "GROUP_HANDSHAKE":
1361 case "DISCONNECTED":
1362 case "INTERFACE_DISABLED":
1365 case "UNINITIALIZED":
1373 manager.isWifiEnabled = function(state) {
1375 case "UNINITIALIZED":
1383 manager.isWifiTetheringEnabled = function(state) {
1385 case "UNINITIALIZED":
1392 manager.syncDebug = syncDebug;
1393 manager.stateOrdinal = function(state) {
1394 return supplicantStatesMap.indexOf(state);
1396 manager.supplicantLoopDetection = function(prevState, state) {
1397 var isPrevStateInHandShake = manager.isHandShakeState(prevState);
1398 var isStateInHandShake = manager.isHandShakeState(state);
1400 if (isPrevStateInHandShake) {
1401 if (isStateInHandShake) {
1402 // Increase the count only if we are in the loop.
1403 if (manager.stateOrdinal(state) > manager.stateOrdinal(prevState)) {
1404 manager.loopDetectionCount++;
1406 if (manager.loopDetectionCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
1407 notify("disconnected", {connectionInfo: manager.connectionInfo});
1408 manager.loopDetectionCount = 0;
1412 // From others state to HandShake state. Reset the count.
1413 if (isStateInHandShake) {
1414 manager.loopDetectionCount = 0;
1419 manager.handlePreWifiScan = function() {
1421 // Before doing regular wifi scan, we have to disable wifi direct
1422 // scan first. Otherwise we will never get the scan result.
1423 p2pManager.blockScan();
1427 manager.handlePostWifiScan = function() {
1429 // After regular wifi scanning, we should restore the restricted
1430 // wifi direct scan.
1431 p2pManager.unblockScan();
1436 // Public APIs for P2P.
1439 manager.p2pSupported = function() {
1440 return p2pSupported;
1443 manager.getP2pManager = function() {
1447 manager.enableP2p = function(callback) {
1448 p2pManager.setEnabled(true, {
1449 onSupplicantConnected: function() {
1450 waitForEvent(WifiP2pManager.INTERFACE_NAME);
1453 onEnabled: function(success) {
1459 manager.getCapabilities = function() {
1460 return capabilities;
1464 let wifiCertService = Cc["@mozilla.org/wifi/certservice;1"];
1465 if (wifiCertService) {
1466 wifiCertService = wifiCertService.getService(Ci.nsIWifiCertService);
1467 wifiCertService.start(wifiListener);
1469 debug("No wifi CA service component available");
1472 manager.importCert = function(caInfo, callback) {
1475 controlCallbacks[id] = callback;
1478 wifiCertService.importCert(id, caInfo.certBlob, caInfo.certPassword,
1479 caInfo.certNickname);
1482 manager.deleteCert = function(caInfo, callback) {
1485 controlCallbacks[id] = callback;
1488 wifiCertService.deleteCert(id, caInfo.certNickname);
1494 // Get unique key for a network, now the key is created by escape(SSID)+Security.
1495 // So networks of same SSID but different security mode can be identified.
1496 function getNetworkKey(network)
1499 encryption = "OPEN";
1501 if ("security" in network) {
1502 // manager network object, represents an AP
1505 // .ssid : SSID of AP
1506 // .security[] : "WPA-PSK" for WPA-PSK
1507 // "WPA-EAP" for WPA-EAP
1513 var security = network.security;
1514 ssid = network.ssid;
1516 for (let j = 0; j < security.length; j++) {
1517 if (security[j] === "WPA-PSK") {
1518 encryption = "WPA-PSK";
1520 } else if (security[j] === "WPA-EAP") {
1521 encryption = "WPA-EAP";
1523 } else if (security[j] === "WEP") {
1528 } else if ("key_mgmt" in network) {
1529 // configure network object, represents a network
1532 // .ssid : SSID of network, quoted
1533 // .key_mgmt : Encryption type
1534 // "WPA-PSK" for WPA-PSK
1535 // "WPA-EAP" for WPA-EAP
1536 // "NONE" for WEP/OPEN
1537 // .auth_alg : Encryption algorithm(WEP mode only)
1538 // "OPEN_SHARED" for WEP
1541 var key_mgmt = network.key_mgmt,
1542 auth_alg = network.auth_alg;
1543 ssid = dequote(network.ssid);
1545 if (key_mgmt == "WPA-PSK") {
1546 encryption = "WPA-PSK";
1547 } else if (key_mgmt.indexOf("WPA-EAP") != -1) {
1548 encryption = "WPA-EAP";
1549 } else if (key_mgmt == "NONE" && auth_alg === "OPEN SHARED") {
1554 // ssid here must be dequoted, and it's safer to esacpe it.
1555 // encryption won't be empty and always be assigned one of the followings :
1556 // "OPEN"/"WEP"/"WPA-PSK"/"WPA-EAP".
1557 // So for a invalid network object, the returned key will be "OPEN".
1558 return escape(ssid) + encryption;
1561 function getKeyManagement(flags) {
1566 if (/\[WPA2?-PSK/.test(flags))
1567 types.push("WPA-PSK");
1568 if (/\[WPA2?-EAP/.test(flags))
1569 types.push("WPA-EAP");
1570 if (/\[WEP/.test(flags))
1575 function getCapabilities(flags) {
1580 if (/\[WPS/.test(flags))
1585 // These constants shamelessly ripped from WifiManager.java
1586 // strength is the value returned by scan_results. It is nominally in dB. We
1587 // transform it into a percentage for clients looking to simply show a
1588 // relative indication of the strength of a network.
1589 const MIN_RSSI = -100;
1590 const MAX_RSSI = -55;
1592 function calculateSignal(strength) {
1593 // Some wifi drivers represent their signal strengths as 8-bit integers, so
1594 // in order to avoid negative numbers, they add 256 to the actual values.
1595 // While we don't *know* that this is the case here, we make an educated
1600 if (strength <= MIN_RSSI)
1602 if (strength >= MAX_RSSI)
1604 return Math.floor(((strength - MIN_RSSI) / (MAX_RSSI - MIN_RSSI)) * 100);
1607 function Network(ssid, security, password, capabilities) {
1609 this.security = security;
1611 if (typeof password !== "undefined")
1612 this.password = password;
1613 if (capabilities !== undefined)
1614 this.capabilities = capabilities;
1615 // TODO connected here as well?
1617 this.__exposedProps__ = Network.api;
1627 keyManagement: "rw",
1636 serverCertificate: "rw"
1639 // Note: We never use ScanResult.prototype, so the fact that it's unrelated to
1640 // Network.prototype is OK.
1641 function ScanResult(ssid, bssid, flags, signal) {
1642 Network.call(this, ssid, getKeyManagement(flags), undefined,
1643 getCapabilities(flags));
1645 this.signalStrength = signal;
1646 this.relSignalStrength = calculateSignal(Number(signal));
1648 this.__exposedProps__ = ScanResult.api;
1651 // XXX This should probably live in the DOM-facing side, but it's hard to do
1652 // there, so we stick this here.
1655 signalStrength: "r",
1656 relSignalStrength: "r",
1660 for (let i in Network.api) {
1661 ScanResult.api[i] = Network.api[i];
1665 return '"' + s + '"';
1668 function dequote(s) {
1669 if (s[0] != '"' || s[s.length - 1] != '"')
1670 throw "Invalid argument, not a quoted string: " + s;
1671 return s.substr(1, s.length - 2);
1674 function isWepHexKey(s) {
1675 if (s.length != 10 && s.length != 26 && s.length != 58)
1677 return !/[^a-fA-F0-9]/.test(s);
1681 let WifiNetworkInterface = {
1683 QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkInterface]),
1687 // nsINetworkInterface
1689 NETWORK_STATE_UNKNOWN: Ci.nsINetworkInterface.NETWORK_STATE_UNKNOWN,
1690 NETWORK_STATE_CONNECTING: Ci.nsINetworkInterface.CONNECTING,
1691 NETWORK_STATE_CONNECTED: Ci.nsINetworkInterface.CONNECTED,
1692 NETWORK_STATE_DISCONNECTING: Ci.nsINetworkInterface.DISCONNECTING,
1693 NETWORK_STATE_DISCONNECTED: Ci.nsINetworkInterface.DISCONNECTED,
1695 state: Ci.nsINetworkInterface.NETWORK_STATE_UNKNOWN,
1697 NETWORK_TYPE_WIFI: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
1698 NETWORK_TYPE_MOBILE: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
1699 NETWORK_TYPE_MOBILE_MMS: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS,
1700 NETWORK_TYPE_MOBILE_SUPL: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL,
1702 type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
1714 httpProxyHost: null,
1716 httpProxyPort: null,
1718 getAddresses: function (ips, prefixLengths) {
1719 ips.value = this.ips.slice();
1720 prefixLengths.value = this.prefixLengths.slice();
1722 return this.ips.length;
1725 getGateways: function (count) {
1727 count.value = this.gateways.length;
1729 return this.gateways.slice();
1732 getDnses: function (count) {
1734 count.value = this.dnses.length;
1736 return this.dnses.slice();
1740 function WifiScanResult() {}
1742 // TODO Make the difference between a DOM-based network object and our
1743 // networks objects much clearer.
1747 function WifiWorker() {
1750 this._mm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
1751 .getService(Ci.nsIMessageListenerManager);
1752 const messages = ["WifiManager:getNetworks", "WifiManager:getKnownNetworks",
1753 "WifiManager:associate", "WifiManager:forget",
1754 "WifiManager:wps", "WifiManager:getState",
1755 "WifiManager:setPowerSavingMode",
1756 "WifiManager:setHttpProxy",
1757 "WifiManager:setStaticIpMode",
1758 "WifiManager:importCert",
1759 "WifiManager:getImportedCerts",
1760 "WifiManager:deleteCert",
1761 "WifiManager:setWifiEnabled",
1762 "WifiManager:setWifiTethering",
1763 "child-process-shutdown"];
1765 messages.forEach((function(msgName) {
1766 this._mm.addMessageListener(msgName, this);
1769 Services.obs.addObserver(this, kMozSettingsChangedObserverTopic, false);
1770 Services.obs.addObserver(this, "xpcom-shutdown", false);
1772 this.wantScanResults = [];
1774 this._needToEnableNetworks = false;
1775 this._highestPriority = -1;
1777 // Networks is a map from SSID -> a scan result.
1778 this.networks = Object.create(null);
1780 // ConfiguredNetworks is a map from SSID -> our view of a network. It only
1781 // lists networks known to the wpa_supplicant. The SSID field (and other
1782 // fields) are quoted for ease of use with WifiManager commands.
1783 // Note that we don't have to worry about escaping embedded quotes since in
1784 // all cases, the supplicant will take the last quotation that we pass it as
1785 // the end of the string.
1786 this.configuredNetworks = Object.create(null);
1787 this._addingNetworks = Object.create(null);
1789 this.currentNetwork = null;
1790 this.ipAddress = "";
1791 this.macAddress = null;
1793 this._lastConnectionInfo = null;
1794 this._connectionInfoTimer = null;
1795 this._reconnectOnDisconnect = false;
1797 // Create p2pObserver and assign to p2pManager.
1798 if (WifiManager.p2pSupported()) {
1799 this._p2pObserver = WifiP2pWorkerObserver(WifiManager.getP2pManager());
1800 WifiManager.getP2pManager().setObserver(this._p2pObserver);
1802 // Add DOM message observerd by p2pObserver to the message listener as well.
1803 this._p2pObserver.getObservedDOMMessages().forEach((function(msgName) {
1804 this._mm.addMessageListener(msgName, this);
1808 // Users of instances of nsITimer should keep a reference to the timer until
1809 // it is no longer needed in order to assure the timer is fired.
1810 this._callbackTimer = null;
1812 // XXX On some phones (Otoro and Unagi) the wifi driver doesn't play nicely
1813 // with the automatic scans that wpa_supplicant does (it appears that the
1814 // driver forgets that it's returned scan results and then refuses to try to
1815 // rescan. In order to detect this case we start a timer when we enter the
1816 // SCANNING state and reset it whenever we either get scan results or leave
1817 // the SCANNING state. If the timer fires, we assume that we are stuck and
1818 // forceably try to unstick the supplican, also turning on background
1819 // scanning to avoid having to constantly poke the supplicant.
1821 // How long we wait is controlled by the SCAN_STUCK_WAIT constant.
1822 const SCAN_STUCK_WAIT = 12000;
1823 this._scanStuckTimer = null;
1824 this._turnOnBackgroundScan = false;
1826 function startScanStuckTimer() {
1827 if (WifiManager.schedScanRecovery) {
1828 self._scanStuckTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
1829 self._scanStuckTimer.initWithCallback(scanIsStuck, SCAN_STUCK_WAIT,
1830 Ci.nsITimer.TYPE_ONE_SHOT);
1834 function scanIsStuck() {
1835 // Uh-oh, we've waited too long for scan results. Disconnect (which
1836 // guarantees that we leave the SCANNING state and tells wpa_supplicant to
1837 // wait for our next command) ensure that background scanning is on and
1839 debug("Determined that scanning is stuck, turning on background scanning!");
1840 WifiManager.handlePostWifiScan();
1841 WifiManager.disconnect(function(ok) {});
1842 self._turnOnBackgroundScan = true;
1845 // A list of requests to turn wifi on or off.
1846 this._stateRequests = [];
1848 // Given a connection status network, takes a network from
1849 // self.configuredNetworks and prepares it for the DOM.
1850 netToDOM = function(net) {
1851 var ssid = dequote(net.ssid);
1852 var security = (net.key_mgmt === "NONE" && net.wep_key0) ? ["WEP"] :
1853 (net.key_mgmt && net.key_mgmt !== "NONE") ? [net.key_mgmt.split(" ")[0]] :
1856 if (("psk" in net && net.psk) ||
1857 ("password" in net && net.password) ||
1858 ("wep_key0" in net && net.wep_key0)) {
1862 var pub = new Network(ssid, security, password);
1864 pub.identity = dequote(net.identity);
1867 if (net.scan_ssid === 1)
1869 if ("ca_cert" in net && net.ca_cert &&
1870 net.ca_cert.indexOf("keystore://WIFI_SERVERCERT_" === 0)) {
1871 pub.serverCertificate = net.ca_cert.substr(27);
1873 if(net.subject_match) {
1874 pub.subjectMatch = net.subject_match;
1879 netFromDOM = function(net, configured) {
1880 // Takes a network from the DOM and makes it suitable for insertion into
1881 // self.configuredNetworks (that is calling addNetwork will do the right
1883 // NB: Modifies net in place: safe since we don't share objects between
1884 // the dom and the chrome code.
1886 // Things that are useful for the UI but not to us.
1888 delete net.signalStrength;
1889 delete net.relSignalStrength;
1890 delete net.security;
1891 delete net.capabilities;
1896 net.ssid = quote(net.ssid);
1899 if ("keyManagement" in net) {
1900 if (net.keyManagement === "WEP") {
1902 net.keyManagement = "NONE";
1903 } else if (net.keyManagement === "WPA-EAP") {
1904 net.keyManagement += " IEEE8021X";
1907 configured.key_mgmt = net.key_mgmt = net.keyManagement; // WPA2-PSK, WPA-PSK, etc.
1908 delete net.keyManagement;
1910 configured.key_mgmt = net.key_mgmt = "NONE";
1914 configured.scan_ssid = net.scan_ssid = 1;
1918 function checkAssign(name, checkStar) {
1920 let value = net[name];
1921 if (!value || (checkStar && value === '*')) {
1922 if (name in configured)
1923 net[name] = configured[name];
1927 configured[name] = net[name] = quote(value);
1932 checkAssign("psk", true);
1933 checkAssign("identity", false);
1934 checkAssign("password", true);
1935 if (wep && net.wep && net.wep != '*') {
1936 configured.wep_key0 = net.wep_key0 = isWepHexKey(net.wep) ? net.wep : quote(net.wep);
1937 configured.auth_alg = net.auth_alg = "OPEN SHARED";
1940 function hasValidProperty(name) {
1941 return ((name in net) && net[name] != null);
1944 if (hasValidProperty("eap")) {
1945 if (hasValidProperty("pin")) {
1946 net.pin = quote(net.pin);
1949 if (hasValidProperty("phase1"))
1950 net.phase1 = quote(net.phase1);
1952 if (hasValidProperty("phase2")) {
1953 if (net.phase2 === "TLS") {
1954 net.phase2 = quote("autheap=" + net.phase2);
1955 } else { // PAP, MSCHAPV2, etc.
1956 net.phase2 = quote("auth=" + net.phase2);
1960 if (hasValidProperty("serverCertificate"))
1961 net.ca_cert = quote("keystore://WIFI_SERVERCERT_" + net.serverCertificate);
1963 if (hasValidProperty("subjectMatch"))
1964 net.subject_match = quote(net.subjectMatch);
1970 WifiManager.onsupplicantconnection = function() {
1971 debug("Connected to supplicant");
1972 // Give it a state other than UNINITIALIZED, INITIALIZING or DISABLING
1973 // defined in getter function of WifiManager.enabled. It implies that
1974 // we are ready to accept dom request from applications.
1975 WifiManager.state = "DISCONNECTED";
1976 self._reloadConfiguredNetworks(function(ok) {
1977 // Prime this.networks.
1981 // The select network command we used in associate() disables others networks.
1982 // Enable them here to make sure wpa_supplicant helps to connect to known
1983 // network automatically.
1984 self._enableAllNetworks();
1985 WifiManager.saveConfig(function() {})
1988 // Notify everybody, even if they didn't ask us to come up.
1989 WifiManager.getMacAddress(function (mac) {
1990 self.macAddress = mac;
1991 debug("Got mac: " + mac);
1992 self._fireEvent("wifiUp", { macAddress: mac });
1996 if (WifiManager.state === "SCANNING")
1997 startScanStuckTimer();
2000 WifiManager.onsupplicantlost = function() {
2001 WifiManager.supplicantStarted = false;
2002 WifiManager.state = "UNINITIALIZED";
2003 debug("Supplicant died!");
2005 // Notify everybody, even if they didn't ask us to come up.
2006 self._fireEvent("wifiDown", {});
2010 WifiManager.onpasswordmaybeincorrect = function() {
2011 WifiManager.authenticationFailuresCount++;
2014 WifiManager.ondisconnected = function() {
2015 // We may fail to establish the connection, re-enable the
2016 // rest of our networks.
2017 if (self._needToEnableNetworks) {
2018 self._enableAllNetworks();
2019 self._needToEnableNetworks = false;
2022 let connectionInfo = this.connectionInfo;
2023 WifiManager.getNetworkId(connectionInfo.ssid, function(netId) {
2024 // Trying to get netId from current network.
2026 self.currentNetwork && self.currentNetwork.ssid &&
2027 dequote(self.currentNetwork.ssid) == connectionInfo.ssid &&
2028 typeof self.currentNetwork.netId !== "undefined") {
2029 netId = self.currentNetwork.netId;
2032 WifiManager.disableNetwork(netId, function() {});
2035 self._fireEvent("onconnectingfailed", {network: self.currentNetwork});
2038 WifiManager.onstatechange = function() {
2039 debug("State change: " + this.prevState + " -> " + this.state);
2041 if (self._connectionInfoTimer &&
2042 this.state !== "CONNECTED" &&
2043 this.state !== "COMPLETED") {
2044 self._stopConnectionInfoTimer();
2047 if (this.state !== "SCANNING" &&
2048 self._scanStuckTimer) {
2049 self._scanStuckTimer.cancel();
2050 self._scanStuckTimer = null;
2053 switch (this.state) {
2055 // The dormant state is a bad state to be in since we won't
2056 // automatically connect. Try to knock us out of it. We only
2057 // hit this state when we've failed to run DHCP, so trying
2058 // again isn't the worst thing we can do. Eventually, we'll
2059 // need to detect if we're looping in this state and bail out.
2060 WifiManager.reconnect(function(){});
2063 // id has not yet been filled in, so we can only report the ssid and
2065 self.currentNetwork =
2066 { bssid: WifiManager.connectionInfo.bssid,
2067 ssid: quote(WifiManager.connectionInfo.ssid) };
2068 self._fireEvent("onconnecting", { network: netToDOM(self.currentNetwork) });
2071 if (!self.currentNetwork) {
2072 self.currentNetwork =
2073 { bssid: WifiManager.connectionInfo.bssid,
2074 ssid: quote(WifiManager.connectionInfo.ssid) };
2077 self.currentNetwork.netId = this.id;
2078 WifiManager.getNetworkConfiguration(self.currentNetwork, function (){});
2081 // Now that we've successfully completed the connection, re-enable the
2082 // rest of our networks.
2083 // XXX Need to do this eventually if the user entered an incorrect
2084 // password. For now, we require user interaction to break the loop and
2085 // select a better network!
2086 if (self._needToEnableNetworks) {
2087 self._enableAllNetworks();
2088 self._needToEnableNetworks = false;
2091 // We get the ASSOCIATED event when we've associated but not connected, so
2092 // wait until the handshake is complete.
2093 if (this.fromStatus || !self.currentNetwork) {
2094 // In this case, we connected to an already-connected wpa_supplicant,
2095 // because of that we need to gather information about the current
2097 self.currentNetwork = { ssid: quote(WifiManager.connectionInfo.ssid),
2098 netId: WifiManager.connectionInfo.id };
2099 WifiManager.getNetworkConfiguration(self.currentNetwork, function(){});
2102 // Update http proxy when connected to network.
2103 let netConnect = WifiManager.getHttpProxyNetwork(self.currentNetwork);
2105 WifiManager.setHttpProxy(netConnect);
2107 // The full authentication process is completed, reset the count.
2108 WifiManager.authenticationFailuresCount = 0;
2109 WifiManager.loopDetectionCount = 0;
2110 self._startConnectionInfoTimer();
2111 self._fireEvent("onassociate", { network: netToDOM(self.currentNetwork) });
2114 // BSSID is read after connected, update it.
2115 self.currentNetwork.bssid = WifiManager.connectionInfo.bssid;
2117 case "DISCONNECTED":
2118 // wpa_supplicant may give us a "DISCONNECTED" event even if
2119 // we are already in "DISCONNECTED" state.
2120 if ((WifiNetworkInterface.state ===
2121 Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED) &&
2122 (this.prevState === "INITIALIZING" ||
2123 this.prevState === "DISCONNECTED" ||
2124 this.prevState === "INTERFACE_DISABLED" ||
2125 this.prevState === "INACTIVE" ||
2126 this.prevState === "UNINITIALIZED")) {
2130 self._fireEvent("ondisconnect", {});
2132 // When disconnected, clear the http proxy setting if it exists.
2133 // Temporarily set http proxy to empty and restore user setting after setHttpProxy.
2134 let netDisconnect = WifiManager.getHttpProxyNetwork(self.currentNetwork);
2135 if (netDisconnect) {
2136 let prehttpProxyHostSetting = netDisconnect.httpProxyHost;
2137 let prehttpProxyPortSetting = netDisconnect.httpProxyPort;
2139 netDisconnect.httpProxyHost = "";
2140 netDisconnect.httpProxyPort = 0;
2142 WifiManager.setHttpProxy(netDisconnect);
2144 netDisconnect.httpProxyHost = prehttpProxyHostSetting;
2145 netDisconnect.httpProxyPort = prehttpProxyPortSetting;
2148 self.currentNetwork = null;
2149 self.ipAddress = "";
2151 if (self._turnOnBackgroundScan) {
2152 self._turnOnBackgroundScan = false;
2153 WifiManager.setBackgroundScan("ON", function(did_something, ok) {
2154 WifiManager.reassociate(function() {});
2158 WifiManager.connectionDropped(function() {
2159 // We've disconnected from a network because of a call to forgetNetwork.
2160 // Reconnect to the next available network (if any).
2161 if (self._reconnectOnDisconnect) {
2162 self._reconnectOnDisconnect = false;
2163 WifiManager.reconnect(function(){});
2167 WifiNetworkInterface.state =
2168 Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED;
2169 WifiNetworkInterface.ips = [];
2170 WifiNetworkInterface.prefixLengths = [];
2171 WifiNetworkInterface.gateways = [];
2172 WifiNetworkInterface.dnses = [];
2173 gNetworkManager.updateNetworkInterface(WifiNetworkInterface);
2177 self._fireEvent("onwpstimeout", {});
2180 self._fireEvent("onwpsfail", {});
2182 case "WPS_OVERLAP_DETECTED":
2183 self._fireEvent("onwpsoverlap", {});
2185 case "AUTHENTICATING":
2186 self._fireEvent("onauthenticating", {network: netToDOM(self.currentNetwork)});
2189 // If we're already scanning in the background, we don't need to worry
2190 // about getting stuck while scanning.
2191 if (!WifiManager.backgroundScanEnabled && WifiManager.enabled)
2192 startScanStuckTimer();
2197 WifiManager.onnetworkconnected = function() {
2198 if (!this.info || !this.info.ipaddr_str) {
2199 debug("Network information is invalid.");
2204 netHelpers.getMaskLength(netHelpers.stringToIP(this.info.mask_str));
2206 maskLength = 32; // max prefix for IPv4.
2208 WifiNetworkInterface.state =
2209 Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED;
2210 WifiNetworkInterface.ips = [this.info.ipaddr_str];
2211 WifiNetworkInterface.prefixLengths = [maskLength];
2212 WifiNetworkInterface.gateways = [this.info.gateway_str];
2213 if (typeof this.info.dns1_str == "string" &&
2214 this.info.dns1_str.length) {
2215 WifiNetworkInterface.dnses.push(this.info.dns1_str);
2217 if (typeof this.info.dns2_str == "string" &&
2218 this.info.dns2_str.length) {
2219 WifiNetworkInterface.dnses.push(this.info.dns2_str);
2221 gNetworkManager.updateNetworkInterface(WifiNetworkInterface);
2223 self.ipAddress = this.info.ipaddr_str;
2225 // We start the connection information timer when we associate, but
2226 // don't have our IP address until here. Make sure that we fire a new
2227 // connectionInformation event with the IP address the next time the
2229 self._lastConnectionInfo = null;
2230 self._fireEvent("onconnect", { network: netToDOM(self.currentNetwork) });
2233 WifiManager.onscanresultsavailable = function() {
2234 if (self._scanStuckTimer) {
2235 // We got scan results! We must not be stuck for now, try again.
2236 self._scanStuckTimer.cancel();
2237 self._scanStuckTimer.initWithCallback(scanIsStuck, SCAN_STUCK_WAIT,
2238 Ci.nsITimer.TYPE_ONE_SHOT);
2241 if (self.wantScanResults.length === 0) {
2242 debug("Scan results available, but we don't need them");
2246 debug("Scan results are available! Asking for them.");
2247 WifiManager.getScanResults(function(r) {
2250 self.wantScanResults.forEach(function(callback) { callback(null) });
2251 self.wantScanResults = [];
2255 // Now that we have scan results, there's no more need to continue
2256 // scanning. Ignore any errors from this command.
2257 WifiManager.setScanMode("inactive", function() {});
2258 let lines = r.split("\n");
2259 // NB: Skip the header line.
2260 self.networksArray = [];
2261 for (let i = 1; i < lines.length; ++i) {
2262 // bssid / frequency / signal level / flags / ssid
2263 var match = /([\S]+)\s+([\S]+)\s+([\S]+)\s+(\[[\S]+\])?\s(.*)/.exec(lines[i]);
2265 if (match && match[5]) {
2266 let ssid = match[5],
2268 signalLevel = match[3],
2271 // Skip ad-hoc networks which aren't supported (bug 811635).
2272 if (flags && flags.indexOf("[IBSS]") >= 0)
2275 // If this is the first time that we've seen this SSID in the scan
2276 // results, add it to the list along with any other information.
2277 // Also, we use the highest signal strength that we see.
2278 let network = new ScanResult(ssid, bssid, flags, signalLevel);
2280 let networkKey = getNetworkKey(network);
2282 if (networkKey in self.configuredNetworks) {
2283 let known = self.configuredNetworks[networkKey];
2284 network.known = true;
2286 if ("identity" in known && known.identity)
2287 network.identity = dequote(known.identity);
2289 // Note: we don't hand out passwords here! The * marks that there
2290 // is a password that we're hiding.
2291 if (("psk" in known && known.psk) ||
2292 ("password" in known && known.password) ||
2293 ("wep_key0" in known && known.wep_key0)) {
2294 network.password = "*";
2298 self.networksArray.push(network);
2299 if (network.bssid === WifiManager.connectionInfo.bssid)
2300 network.connected = true;
2302 let signal = calculateSignal(Number(match[3]));
2303 if (signal > network.relSignalStrength)
2304 network.relSignalStrength = signal;
2305 } else if (!match) {
2306 debug("Match didn't find anything for: " + lines[i]);
2310 self.wantScanResults.forEach(function(callback) { callback(self.networksArray) });
2311 self.wantScanResults = [];
2315 WifiManager.onstationinfoupdate = function() {
2316 self._fireEvent("stationinfoupdate", { station: this.station });
2319 WifiManager.onstopconnectioninfotimer = function() {
2320 self._stopConnectionInfoTimer();
2323 // Read the 'wifi.enabled' setting in order to start with a known
2324 // value at boot time. The handle() will be called after reading.
2326 // nsISettingsServiceCallback implementation.
2327 var initWifiEnabledCb = {
2328 handle: function handle(aName, aResult) {
2329 if (aName !== SETTINGS_WIFI_ENABLED)
2331 if (aResult === null)
2333 self.handleWifiEnabled(aResult);
2335 handleError: function handleError(aErrorMessage) {
2336 debug("Error reading the 'wifi.enabled' setting. Default to wifi on.");
2337 self.handleWifiEnabled(true);
2341 var initWifiDebuggingEnabledCb = {
2342 handle: function handle(aName, aResult) {
2343 if (aName !== SETTINGS_WIFI_DEBUG_ENABLED)
2345 if (aResult === null)
2350 handleError: function handleError(aErrorMessage) {
2351 debug("Error reading the 'wifi.debugging.enabled' setting. Default to debugging off.");
2357 this.initTetheringSettings();
2359 let lock = gSettingsService.createLock();
2360 lock.get(SETTINGS_WIFI_ENABLED, initWifiEnabledCb);
2361 lock.get(SETTINGS_WIFI_DEBUG_ENABLED, initWifiDebuggingEnabledCb);
2363 lock.get(SETTINGS_WIFI_SSID, this);
2364 lock.get(SETTINGS_WIFI_SECURITY_TYPE, this);
2365 lock.get(SETTINGS_WIFI_SECURITY_PASSWORD, this);
2366 lock.get(SETTINGS_WIFI_IP, this);
2367 lock.get(SETTINGS_WIFI_PREFIX, this);
2368 lock.get(SETTINGS_WIFI_DHCPSERVER_STARTIP, this);
2369 lock.get(SETTINGS_WIFI_DHCPSERVER_ENDIP, this);
2370 lock.get(SETTINGS_WIFI_DNS1, this);
2371 lock.get(SETTINGS_WIFI_DNS2, this);
2372 lock.get(SETTINGS_WIFI_TETHERING_ENABLED, this);
2374 lock.get(SETTINGS_USB_DHCPSERVER_STARTIP, this);
2375 lock.get(SETTINGS_USB_DHCPSERVER_ENDIP, this);
2377 this._wifiTetheringSettingsToRead = [SETTINGS_WIFI_SSID,
2378 SETTINGS_WIFI_SECURITY_TYPE,
2379 SETTINGS_WIFI_SECURITY_PASSWORD,
2381 SETTINGS_WIFI_PREFIX,
2382 SETTINGS_WIFI_DHCPSERVER_STARTIP,
2383 SETTINGS_WIFI_DHCPSERVER_ENDIP,
2386 SETTINGS_WIFI_TETHERING_ENABLED,
2387 SETTINGS_USB_DHCPSERVER_STARTIP,
2388 SETTINGS_USB_DHCPSERVER_ENDIP];
2391 function translateState(state) {
2393 case "INTERFACE_DISABLED":
2396 case "DISCONNECTED":
2398 return "disconnected";
2400 case "AUTHENTICATING":
2403 case "FOUR_WAY_HANDSHAKE":
2404 case "GROUP_HANDSHAKE":
2405 return "connecting";
2408 return WifiManager.getDhcpInfo() ? "connected" : "associated";
2412 WifiWorker.prototype = {
2413 classID: WIFIWORKER_CID,
2414 classInfo: XPCOMUtils.generateCI({classID: WIFIWORKER_CID,
2415 contractID: WIFIWORKER_CONTRACTID,
2416 classDescription: "WifiWorker",
2417 interfaces: [Ci.nsIWorkerHolder,
2421 QueryInterface: XPCOMUtils.generateQI([Ci.nsIWorkerHolder,
2424 Ci.nsISettingsServiceCallback]),
2426 disconnectedByWifi: false,
2428 disconnectedByWifiTethering: false,
2430 _wifiTetheringSettingsToRead: [],
2432 _oldWifiTetheringEnabledState: null,
2434 tetheringSettings: {},
2436 initTetheringSettings: function initTetheringSettings() {
2437 this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] = null;
2438 this.tetheringSettings[SETTINGS_WIFI_SSID] = DEFAULT_WIFI_SSID;
2439 this.tetheringSettings[SETTINGS_WIFI_SECURITY_TYPE] = DEFAULT_WIFI_SECURITY_TYPE;
2440 this.tetheringSettings[SETTINGS_WIFI_SECURITY_PASSWORD] = DEFAULT_WIFI_SECURITY_PASSWORD;
2441 this.tetheringSettings[SETTINGS_WIFI_IP] = DEFAULT_WIFI_IP;
2442 this.tetheringSettings[SETTINGS_WIFI_PREFIX] = DEFAULT_WIFI_PREFIX;
2443 this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP] = DEFAULT_WIFI_DHCPSERVER_STARTIP;
2444 this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP] = DEFAULT_WIFI_DHCPSERVER_ENDIP;
2445 this.tetheringSettings[SETTINGS_WIFI_DNS1] = DEFAULT_DNS1;
2446 this.tetheringSettings[SETTINGS_WIFI_DNS2] = DEFAULT_DNS2;
2448 this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP] = DEFAULT_USB_DHCPSERVER_STARTIP;
2449 this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP] = DEFAULT_USB_DHCPSERVER_ENDIP;
2452 // Internal methods.
2453 waitForScan: function(callback) {
2454 this.wantScanResults.push(callback);
2457 // In order to select a specific network, we disable the rest of the
2458 // networks known to us. However, in general, we want the supplicant to
2459 // connect to which ever network it thinks is best, so when we select the
2460 // proper network (or fail to), we need to re-enable the rest.
2461 _enableAllNetworks: function() {
2462 for each (let net in this.configuredNetworks) {
2463 WifiManager.enableNetwork(net.netId, false, function(ok) {
2464 net.disabled = ok ? 1 : 0;
2469 _startConnectionInfoTimer: function() {
2470 if (this._connectionInfoTimer)
2474 function getConnectionInformation() {
2475 WifiManager.getConnectionInfo(function(connInfo) {
2476 // See comments in calculateSignal for information about this.
2478 self._lastConnectionInfo = null;
2482 let { rssi, linkspeed } = connInfo;
2485 if (rssi <= MIN_RSSI)
2487 else if (rssi >= MAX_RSSI)
2490 let info = { signalStrength: rssi,
2491 relSignalStrength: calculateSignal(rssi),
2492 linkSpeed: linkspeed,
2493 ipAddress: self.ipAddress };
2494 let last = self._lastConnectionInfo;
2496 // Only fire the event if the link speed changed or the signal
2497 // strength changed by more than 10%.
2498 function tensPlace(percent) ((percent / 10) | 0)
2500 if (last && last.linkSpeed === info.linkSpeed &&
2501 last.ipAddress === info.ipAddress &&
2502 tensPlace(last.relSignalStrength) === tensPlace(info.relSignalStrength)) {
2506 self._lastConnectionInfo = info;
2507 debug("Firing connectioninfoupdate: " + uneval(info));
2508 self._fireEvent("connectioninfoupdate", info);
2512 // Prime our _lastConnectionInfo immediately and fire the event at the
2514 getConnectionInformation();
2516 // Now, set up the timer for regular updates.
2517 this._connectionInfoTimer =
2518 Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
2519 this._connectionInfoTimer.init(getConnectionInformation, 5000,
2520 Ci.nsITimer.TYPE_REPEATING_SLACK);
2523 _stopConnectionInfoTimer: function() {
2524 if (!this._connectionInfoTimer)
2527 this._connectionInfoTimer.cancel();
2528 this._connectionInfoTimer = null;
2529 this._lastConnectionInfo = null;
2532 _reloadConfiguredNetworks: function(callback) {
2533 WifiManager.getConfiguredNetworks((function(networks) {
2535 debug("Unable to get configured networks");
2540 this._highestPriority = -1;
2542 // Convert between netId-based and ssid-based indexing.
2543 for (let net in networks) {
2544 let network = networks[net];
2545 delete networks[net];
2547 if (!network.ssid) {
2548 WifiManager.removeNetwork(network.netId, function() {});
2552 if (network.hasOwnProperty("priority") &&
2553 network.priority > this._highestPriority) {
2554 this._highestPriority = network.priority;
2557 let networkKey = getNetworkKey(network);
2558 // Accept latest config of same network(same SSID and same security).
2559 if (networks[networkKey]) {
2560 WifiManager.removeNetwork(networks[networkKey].netId, function() {});
2562 networks[networkKey] = network;
2565 this.configuredNetworks = networks;
2570 // Important side effect: calls WifiManager.saveConfig.
2571 _reprioritizeNetworks: function(callback) {
2572 // First, sort the networks in orer of their priority.
2573 var ordered = Object.getOwnPropertyNames(this.configuredNetworks);
2575 ordered.sort(function(a, b) {
2576 var neta = self.configuredNetworks[a],
2577 netb = self.configuredNetworks[b];
2579 // Sort unsorted networks to the end of the list.
2580 if (isNaN(neta.priority))
2581 return isNaN(netb.priority) ? 0 : 1;
2582 if (isNaN(netb.priority))
2584 return netb.priority - neta.priority;
2587 // Skip unsorted networks.
2588 let newPriority = 0, i;
2589 for (i = ordered.length - 1; i >= 0; --i) {
2590 if (!isNaN(this.configuredNetworks[ordered[i]].priority))
2594 // No networks we care about?
2596 WifiManager.saveConfig(callback);
2600 // Now assign priorities from 0 to length, starting with the smallest
2601 // priority and heading towards the highest (note the dependency between
2602 // total and i here).
2603 let done = 0, errors = 0, total = i + 1;
2604 for (; i >= 0; --i) {
2605 let network = this.configuredNetworks[ordered[i]];
2606 network.priority = newPriority++;
2608 // Note: networkUpdated declared below since it happens logically after
2610 WifiManager.updateNetwork(network, networkUpdated);
2613 function networkUpdated(ok) {
2616 if (++done === total) {
2622 WifiManager.saveConfig(function(ok) {
2628 self._reloadConfiguredNetworks(function(ok) {
2639 _fireEvent: function(message, data) {
2640 this._domManagers.forEach(function(manager) {
2641 // Note: We should never have a dead message manager here because we
2642 // observe our child message managers shutting down, below.
2643 manager.sendAsyncMessage("WifiManager:" + message, data);
2647 _sendMessage: function(message, success, data, msg) {
2649 msg.manager.sendAsyncMessage(message + (success ? ":OK" : ":NO"),
2650 { data: data, rid: msg.rid, mid: msg.mid });
2652 debug("sendAsyncMessage error : " + e);
2654 this._splicePendingRequest(msg);
2659 _splicePendingRequest: function(msg) {
2660 for (let i = 0; i < this._domRequest.length; i++) {
2661 if (this._domRequest[i].msg === msg) {
2662 this._domRequest.splice(i, 1);
2668 _clearPendingRequest: function() {
2669 if (this._domRequest.length === 0) return;
2670 this._domRequest.forEach((function(req) {
2671 this._sendMessage(req.name + ":Return", false, "Wifi is disabled", req.msg);
2675 receiveMessage: function MessageManager_receiveMessage(aMessage) {
2676 let msg = aMessage.data || {};
2677 msg.manager = aMessage.target;
2679 if (WifiManager.p2pSupported()) {
2680 // If p2pObserver returns something truthy, return it!
2681 // Otherwise, continue to do the rest of tasks.
2682 var p2pRet = this._p2pObserver.onDOMMessage(aMessage);
2688 // Note: By the time we receive child-process-shutdown, the child process
2689 // has already forgotten its permissions so we do this before the
2690 // permissions check.
2691 if (aMessage.name === "child-process-shutdown") {
2693 if ((i = this._domManagers.indexOf(msg.manager)) != -1) {
2694 this._domManagers.splice(i, 1);
2696 for (i = this._domRequest.length - 1; i >= 0; i--) {
2697 if (this._domRequest[i].msg.manager === msg.manager) {
2698 this._domRequest.splice(i, 1);
2704 if (!aMessage.target.assertPermission("wifi-manage")) {
2708 // We are interested in DOMRequests only.
2709 if (aMessage.name != "WifiManager:getState") {
2710 this._domRequest.push({name: aMessage.name, msg:msg});
2713 switch (aMessage.name) {
2714 case "WifiManager:setWifiEnabled":
2715 this.setWifiEnabled(msg);
2717 case "WifiManager:getNetworks":
2718 this.getNetworks(msg);
2720 case "WifiManager:getKnownNetworks":
2721 this.getKnownNetworks(msg);
2723 case "WifiManager:associate":
2724 this.associate(msg);
2726 case "WifiManager:forget":
2729 case "WifiManager:wps":
2732 case "WifiManager:setPowerSavingMode":
2733 this.setPowerSavingMode(msg);
2735 case "WifiManager:setHttpProxy":
2736 this.setHttpProxy(msg);
2738 case "WifiManager:setStaticIpMode":
2739 this.setStaticIpMode(msg);
2741 case "WifiManager:importCert":
2742 this.importCert(msg);
2744 case "WifiManager:getImportedCerts":
2745 this.getImportedCerts(msg);
2747 case "WifiManager:deleteCert":
2748 this.deleteCert(msg);
2750 case "WifiManager:setWifiTethering":
2751 this.setWifiTethering(msg);
2753 case "WifiManager:getState": {
2755 if ((i = this._domManagers.indexOf(msg.manager)) === -1) {
2756 this._domManagers.push(msg.manager);
2759 let net = this.currentNetwork ? netToDOM(this.currentNetwork) : null;
2760 return { network: net,
2761 connectionInfo: this._lastConnectionInfo,
2762 enabled: WifiManager.enabled,
2763 status: translateState(WifiManager.state),
2764 macAddress: this.macAddress,
2765 capabilities: WifiManager.getCapabilities()};
2770 getNetworks: function(msg) {
2771 const message = "WifiManager:getNetworks:Return";
2772 if (!WifiManager.enabled) {
2773 this._sendMessage(message, false, "Wifi is disabled", msg);
2778 let callback = (function (networks) {
2782 this._sendMessage(message, networks !== null, networks, msg);
2784 this.waitForScan(callback);
2786 WifiManager.scan(true, (function(ok) {
2787 // If the scan command succeeded, we're done.
2791 // Avoid sending multiple responses.
2795 // Otherwise, let the client know that it failed, it's responsible for
2796 // trying again in a few seconds.
2798 this._sendMessage(message, false, "ScanFailed", msg);
2802 getWifiScanResults: function(callback) {
2807 if (!WifiManager.enabled) {
2808 callback.onfailure();
2812 self.waitForScan(waitForScanCallback);
2815 WifiManager.scan(true, (function (ok) {
2819 timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
2824 self.wantScanResults.splice(self.wantScanResults.indexOf(waitForScanCallback), 1);
2825 callback.onfailure();
2829 // Else it's still running, continue waiting.
2830 timer.initWithCallback(doScan, 10000, Ci.nsITimer.TYPE_ONE_SHOT);
2836 function waitForScanCallback(networks) {
2837 if (networks === null) {
2838 callback.onfailure();
2842 var wifiScanResults = new Array();
2844 for (let net in networks) {
2845 let value = networks[net];
2846 wifiScanResults.push(transformResult(value));
2848 callback.onready(wifiScanResults.length, wifiScanResults);
2851 function transformResult(element) {
2852 var result = new WifiScanResult();
2853 result.connected = false;
2854 for (let id in element) {
2855 if (id === "__exposedProps__") {
2858 if (id === "security") {
2860 var security = element[id];
2861 for (let j = 0; j < security.length; j++) {
2862 if (security[j] === "WPA-PSK") {
2863 result[id] |= Ci.nsIWifiScanResult.WPA_PSK;
2864 } else if (security[j] === "WPA-EAP") {
2865 result[id] |= Ci.nsIWifiScanResult.WPA_EAP;
2866 } else if (security[j] === "WEP") {
2867 result[id] |= Ci.nsIWifiScanResult.WEP;
2873 result[id] = element[id];
2880 getKnownNetworks: function(msg) {
2881 const message = "WifiManager:getKnownNetworks:Return";
2882 if (!WifiManager.enabled) {
2883 this._sendMessage(message, false, "Wifi is disabled", msg);
2887 this._reloadConfiguredNetworks((function(ok) {
2889 this._sendMessage(message, false, "Failed", msg);
2894 for (let networkKey in this.configuredNetworks) {
2895 networks.push(netToDOM(this.configuredNetworks[networkKey]));
2898 this._sendMessage(message, true, networks, msg);
2902 _setWifiEnabledCallback: function(status) {
2908 // If we're enabling ourselves, then wait until we've connected to the
2909 // supplicant to notify. If we're disabling, we take care of this in
2911 if (WifiManager.supplicantStarted)
2912 WifiManager.start();
2916 * Compatibility flags for detecting if Gaia is controlling wifi by settings
2917 * or API, once API is called, gecko will no longer accept wifi enable
2918 * control from settings.
2919 * This is used to deal with compatibility issue while Gaia adopted to use
2920 * API but gecko doesn't remove the settings code in time.
2921 * TODO: Remove this flag in Bug 1050147
2923 ignoreWifiEnabledFromSettings: false,
2924 setWifiEnabled: function(msg) {
2925 const message = "WifiManager:setWifiEnabled:Return";
2927 let enabled = msg.data;
2929 self.ignoreWifiEnabledFromSettings = true;
2931 if (enabled === WifiManager.enabled) {
2932 this._sendMessage(message, true, true, msg);
2936 // Can't enable wifi while hotspot mode is enabled.
2937 if (enabled && (this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] ||
2938 WifiManager.isWifiTetheringEnabled(WifiManager.tetheringState))) {
2939 self._sendMessage(message, false, "Can't enable Wifi while hotspot mode is enabled", msg);
2943 WifiManager.setWifiEnabled(enabled, function(ok) {
2944 if (ok === 0 || ok === "no change") {
2945 self._sendMessage(message, true, true, msg);
2947 // Reply error to pending requests.
2949 self._clearPendingRequest();
2951 WifiManager.start();
2954 self._sendMessage(message, false, "Set wifi enabled failed", msg);
2959 _setWifiEnabled: function(enabled, callback) {
2960 // Reply error to pending requests.
2962 this._clearPendingRequest();
2965 WifiManager.setWifiEnabled(enabled, callback);
2968 // requestDone() must be called to before callback complete(or error)
2969 // so next queue in the request quene can be executed.
2970 // TODO: Remove command queue in Bug 1050147
2971 queueRequest: function(data, callback) {
2973 throw "Try to enqueue a request without callback";
2976 let optimizeCommandList = ["setWifiEnabled", "setWifiApEnabled"];
2977 if (optimizeCommandList.indexOf(data.command) != -1) {
2978 this._stateRequests = this._stateRequests.filter(function(element) {
2979 return element.data.command !== data.command;
2983 this._stateRequests.push({
2991 getWifiTetheringParameters: function getWifiTetheringParameters(enable) {
2992 if (this.useTetheringAPI) {
2993 return this.getWifiTetheringConfiguration(enable);
2995 return this.getWifiTetheringParametersBySetting(enable);
2999 getWifiTetheringConfiguration: function getWifiTetheringConfiguration(enable) {
3001 let params = this.tetheringConfig;
3003 let check = function(field, _default) {
3004 config[field] = field in params ? params[field] : _default;
3007 check("ssid", DEFAULT_WIFI_SSID);
3008 check("security", DEFAULT_WIFI_SECURITY_TYPE);
3009 check("key", DEFAULT_WIFI_SECURITY_PASSWORD);
3010 check("ip", DEFAULT_WIFI_IP);
3011 check("prefix", DEFAULT_WIFI_PREFIX);
3012 check("wifiStartIp", DEFAULT_WIFI_DHCPSERVER_STARTIP);
3013 check("wifiEndIp", DEFAULT_WIFI_DHCPSERVER_ENDIP);
3014 check("usbStartIp", DEFAULT_USB_DHCPSERVER_STARTIP);
3015 check("usbEndIp", DEFAULT_USB_DHCPSERVER_ENDIP);
3016 check("dns1", DEFAULT_DNS1);
3017 check("dns2", DEFAULT_DNS2);
3019 config.enable = enable;
3020 config.mode = enable ? WIFI_FIRMWARE_AP : WIFI_FIRMWARE_STATION;
3021 config.link = enable ? NETWORK_INTERFACE_UP : NETWORK_INTERFACE_DOWN;
3023 // Check the format to prevent netd from crash.
3024 if (enable && (!config.ssid || config.ssid == "")) {
3025 debug("Invalid SSID value.");
3029 if (enable && (config.security != WIFI_SECURITY_TYPE_NONE && !config.key)) {
3030 debug("Invalid security password.");
3034 // Using the default values here until application supports these settings.
3035 if (config.ip == "" || config.prefix == "" ||
3036 config.wifiStartIp == "" || config.wifiEndIp == "" ||
3037 config.usbStartIp == "" || config.usbEndIp == "") {
3038 debug("Invalid subnet information.");
3045 getWifiTetheringParametersBySetting: function getWifiTetheringParametersBySetting(enable) {
3051 let wifiDhcpStartIp;
3058 ssid = this.tetheringSettings[SETTINGS_WIFI_SSID];
3059 securityType = this.tetheringSettings[SETTINGS_WIFI_SECURITY_TYPE];
3060 securityId = this.tetheringSettings[SETTINGS_WIFI_SECURITY_PASSWORD];
3061 interfaceIp = this.tetheringSettings[SETTINGS_WIFI_IP];
3062 prefix = this.tetheringSettings[SETTINGS_WIFI_PREFIX];
3063 wifiDhcpStartIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP];
3064 wifiDhcpEndIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP];
3065 usbDhcpStartIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP];
3066 usbDhcpEndIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP];
3067 dns1 = this.tetheringSettings[SETTINGS_WIFI_DNS1];
3068 dns2 = this.tetheringSettings[SETTINGS_WIFI_DNS2];
3070 // Check the format to prevent netd from crash.
3071 if (!ssid || ssid == "") {
3072 debug("Invalid SSID value.");
3075 // Truncate ssid if its length of encoded to utf8 is longer than 32.
3076 while (unescape(encodeURIComponent(ssid)).length > 32)
3078 ssid = ssid.substring(0, ssid.length-1);
3081 if (securityType != WIFI_SECURITY_TYPE_NONE &&
3082 securityType != WIFI_SECURITY_TYPE_WPA_PSK &&
3083 securityType != WIFI_SECURITY_TYPE_WPA2_PSK) {
3085 debug("Invalid security type.");
3088 if (securityType != WIFI_SECURITY_TYPE_NONE && !securityId) {
3089 debug("Invalid security password.");
3092 // Using the default values here until application supports these settings.
3093 if (interfaceIp == "" || prefix == "" ||
3094 wifiDhcpStartIp == "" || wifiDhcpEndIp == "" ||
3095 usbDhcpStartIp == "" || usbDhcpEndIp == "") {
3096 debug("Invalid subnet information.");
3102 security: securityType,
3106 wifiStartIp: wifiDhcpStartIp,
3107 wifiEndIp: wifiDhcpEndIp,
3108 usbStartIp: usbDhcpStartIp,
3109 usbEndIp: usbDhcpEndIp,
3113 mode: enable ? WIFI_FIRMWARE_AP : WIFI_FIRMWARE_STATION,
3114 link: enable ? NETWORK_INTERFACE_UP : NETWORK_INTERFACE_DOWN
3118 setWifiApEnabled: function(enabled, callback) {
3119 let configuration = this.getWifiTetheringParameters(enabled);
3121 if (!configuration) {
3123 debug("Invalid Wifi Tethering configuration.");
3127 WifiManager.setWifiApEnabled(enabled, configuration, callback);
3130 associate: function(msg) {
3131 const MAX_PRIORITY = 9999;
3132 const message = "WifiManager:associate:Return";
3133 let network = msg.data;
3135 let privnet = network;
3136 let dontConnect = privnet.dontConnect;
3137 delete privnet.dontConnect;
3139 if (!WifiManager.enabled) {
3140 this._sendMessage(message, false, "Wifi is disabled", msg);
3145 function networkReady() {
3146 // saveConfig now before we disable most of the other networks.
3147 function selectAndConnect() {
3148 WifiManager.enableNetwork(privnet.netId, true, function (ok) {
3150 self._needToEnableNetworks = true;
3151 if (WifiManager.state === "DISCONNECTED" ||
3152 WifiManager.state === "SCANNING") {
3153 WifiManager.reconnect(function (ok) {
3154 self._sendMessage(message, ok, ok, msg);
3157 self._sendMessage(message, ok, ok, msg);
3162 var selectAndConnectOrReturn = dontConnect ?
3164 self._sendMessage(message, true, "Wifi has been recorded", msg);
3165 } : selectAndConnect;
3166 if (self._highestPriority >= MAX_PRIORITY) {
3167 self._reprioritizeNetworks(selectAndConnectOrReturn);
3169 WifiManager.saveConfig(selectAndConnectOrReturn);
3173 let ssid = privnet.ssid;
3174 let networkKey = getNetworkKey(privnet);
3177 if (networkKey in this._addingNetworks) {
3178 this._sendMessage(message, false, "Racing associates");
3182 if (networkKey in this.configuredNetworks)
3183 configured = this.configuredNetworks[networkKey];
3185 netFromDOM(privnet, configured);
3187 privnet.priority = ++this._highestPriority;
3189 privnet.netId = configured.netId;
3190 // Sync priority back to configured so if priority reaches MAX_PRIORITY,
3191 // it can be sorted correctly in _reprioritizeNetworks() because the
3192 // function sort network based on priority in configure list.
3193 configured.priority = privnet.priority;
3194 WifiManager.updateNetwork(privnet, (function(ok) {
3196 this._sendMessage(message, false, "Network is misconfigured", msg);
3203 // networkReady, above, calls saveConfig. We want to remember the new
3204 // network as being enabled, which isn't the default, so we explicitly
3205 // set it to being "enabled" before we add it and save the
3207 privnet.disabled = 0;
3208 this._addingNetworks[networkKey] = privnet;
3209 WifiManager.addNetwork(privnet, (function(ok) {
3210 delete this._addingNetworks[networkKey];
3213 this._sendMessage(message, false, "Network is misconfigured", msg);
3217 this.configuredNetworks[networkKey] = privnet;
3223 forget: function(msg) {
3224 const message = "WifiManager:forget:Return";
3225 let network = msg.data;
3226 if (!WifiManager.enabled) {
3227 this._sendMessage(message, false, "Wifi is disabled", msg);
3231 this._reloadConfiguredNetworks((function(ok) {
3232 // Give it a chance to remove the network even if reload is failed.
3234 debug("Warning !!! Failed to reload the configured networks");
3237 let ssid = network.ssid;
3238 let networkKey = getNetworkKey(network);
3239 if (!(networkKey in this.configuredNetworks)) {
3240 this._sendMessage(message, false, "Trying to forget an unknown network", msg);
3245 let configured = this.configuredNetworks[networkKey];
3246 this._reconnectOnDisconnect = (this.currentNetwork &&
3247 (this.currentNetwork.ssid === ssid));
3248 WifiManager.removeNetwork(configured.netId, function(ok) {
3249 if (self._needToEnableNetworks) {
3250 self._enableAllNetworks();
3251 self._needToEnableNetworks = false;
3255 self._sendMessage(message, false, "Unable to remove the network", msg);
3256 self._reconnectOnDisconnect = false;
3260 WifiManager.saveConfig(function() {
3261 self._reloadConfiguredNetworks(function() {
3262 self._sendMessage(message, true, true, msg);
3269 wps: function(msg) {
3270 const message = "WifiManager:wps:Return";
3272 let detail = msg.data;
3274 if (!WifiManager.enabled) {
3275 this._sendMessage(message, false, "Wifi is disabled", msg);
3279 if (detail.method === "pbc") {
3280 WifiManager.wpsPbc(function(ok) {
3282 self._sendMessage(message, true, true, msg);
3284 self._sendMessage(message, false, "WPS PBC failed", msg);
3286 } else if (detail.method === "pin") {
3287 WifiManager.wpsPin(detail, function(pin) {
3289 self._sendMessage(message, true, pin, msg);
3291 self._sendMessage(message, false, "WPS PIN failed", msg);
3293 } else if (detail.method === "cancel") {
3294 WifiManager.wpsCancel(function(ok) {
3296 self._sendMessage(message, true, true, msg);
3298 self._sendMessage(message, false, "WPS Cancel failed", msg);
3301 self._sendMessage(message, false, "Invalid WPS method=" + detail.method,
3306 setPowerSavingMode: function(msg) {
3307 const message = "WifiManager:setPowerSavingMode:Return";
3309 let enabled = msg.data;
3310 let mode = enabled ? "AUTO" : "ACTIVE";
3312 if (!WifiManager.enabled) {
3313 this._sendMessage(message, false, "Wifi is disabled", msg);
3317 // Some wifi drivers may not implement this command. Set power mode
3318 // even if suspend optimization command failed.
3319 WifiManager.setSuspendOptimizations(enabled, function(ok) {
3320 WifiManager.setPowerMode(mode, function(ok) {
3322 self._sendMessage(message, true, true, msg);
3324 self._sendMessage(message, false, "Set power saving mode failed", msg);
3330 setHttpProxy: function(msg) {
3331 const message = "WifiManager:setHttpProxy:Return";
3333 let network = msg.data.network;
3334 let info = msg.data.info;
3336 if (!WifiManager.enabled) {
3337 this._sendMessage(message, false, "Wifi is disabled", msg);
3341 WifiManager.configureHttpProxy(network, info, function(ok) {
3343 // If configured network is current connected network
3344 // need update http proxy immediately.
3345 let setNetworkKey = getNetworkKey(network);
3346 let curNetworkKey = self.currentNetwork ? getNetworkKey(self.currentNetwork) : null;
3347 if (setNetworkKey === curNetworkKey)
3348 WifiManager.setHttpProxy(network);
3350 self._sendMessage(message, true, true, msg);
3352 self._sendMessage(message, false, "Set http proxy failed", msg);
3357 setStaticIpMode: function(msg) {
3358 const message = "WifiManager:setStaticMode:Return";
3360 let network = msg.data.network;
3361 let info = msg.data.info;
3363 if (!WifiManager.enabled) {
3364 this._sendMessage(message, false, "Wifi is disabled", msg);
3368 // To compatiable with DHCP returned info structure, do translation here
3369 info.ipaddr_str = info.ipaddr;
3370 info.proxy_str = info.proxy;
3371 info.gateway_str = info.gateway;
3372 info.dns1_str = info.dns1;
3373 info.dns2_str = info.dns2;
3375 WifiManager.setStaticIpMode(network, info, function(ok) {
3377 self._sendMessage(message, true, true, msg);
3379 self._sendMessage(message, false, "Set static ip mode failed", msg);
3384 importCert: function importCert(msg) {
3385 const message = "WifiManager:importCert:Return";
3388 if (!WifiManager.enabled) {
3389 this._sendMessage(message, false, "Wifi is disabled", msg);
3393 WifiManager.importCert(msg.data, function(data) {
3394 if (data.status === 0) {
3395 let usageString = ["ServerCert"];
3396 let usageArray = [];
3397 for (let i = 0; i < usageString.length; i++) {
3398 if (data.usageFlag & (0x01 << i)) {
3399 usageArray.push(usageString[i]);
3403 self._sendMessage(message, true, {
3404 nickname: data.nickname,
3408 self._sendMessage(message, false, "Import Cert failed", msg);
3413 getImportedCerts: function getImportedCerts(msg) {
3414 const message = "WifiManager:getImportedCerts:Return";
3417 if (!WifiManager.enabled) {
3418 this._sendMessage(message, false, "Wifi is disabled", msg);
3422 let certDB = Cc["@mozilla.org/security/x509certdb;1"]
3423 .getService(Ci.nsIX509CertDB);
3425 self._sendMessage(message, false, "Failed to query NSS DB service", msg);
3428 let certList = certDB.getCerts();
3430 self._sendMessage(message, false, "Failed to get certificate List", msg);
3433 let certListEnum = certList.getEnumerator();
3434 if (!certListEnum) {
3435 self._sendMessage(message, false, "Failed to get certificate List enumerator", msg);
3437 let importedCerts = {
3440 let UsageMapping = {
3441 SERVERCERT: "ServerCert",
3444 while (certListEnum.hasMoreElements()) {
3445 let certInfo = certListEnum.getNext().QueryInterface(Ci.nsIX509Cert);
3446 let certNicknameInfo = /WIFI\_([A-Z]*)\_(.*)/.exec(certInfo.nickname);
3447 if (!certNicknameInfo) {
3450 importedCerts[UsageMapping[certNicknameInfo[1]]].push(certNicknameInfo[2]);
3453 self._sendMessage(message, true, importedCerts, msg);
3456 deleteCert: function deleteCert(msg) {
3457 const message = "WifiManager:deleteCert:Return";
3460 if (!WifiManager.enabled) {
3461 this._sendMessage(message, false, "Wifi is disabled", msg);
3465 WifiManager.deleteCert(msg.data, function(data) {
3466 self._sendMessage(message, data.status === 0, "Delete Cert failed", msg);
3470 // TODO : These two variables should be removed once GAIA uses tethering API.
3471 useTetheringAPI : false,
3472 tetheringConfig : {},
3474 setWifiTethering: function setWifiTethering(msg) {
3475 const message = "WifiManager:setWifiTethering:Return";
3477 let enabled = msg.data.enabled;
3479 this.useTetheringAPI = true;
3480 this.tetheringConfig = msg.data.config;
3482 if (WifiManager.enabled) {
3483 this._sendMessage(message, false, "Wifi is enabled", msg);
3487 this.setWifiApEnabled(enabled, function() {
3488 if ((enabled && WifiManager.tetheringState == "COMPLETED") ||
3489 (!enabled && WifiManager.tetheringState == "UNINITIALIZED")) {
3490 self._sendMessage(message, true, msg.data, msg);
3492 msg.data.reason = enabled ?
3493 "Enable WIFI tethering faild" : "Disable WIFI tethering faild";
3494 self._sendMessage(message, false, msg.data, msg);
3499 // This is a bit ugly, but works. In particular, this depends on the fact
3500 // that RadioManager never actually tries to get the worker from us.
3501 get worker() { throw "Not implemented"; },
3503 shutdown: function() {
3504 debug("shutting down ...");
3505 this.queueRequest({command: "setWifiEnabled", value: false}, function(data) {
3506 this._setWifiEnabled(false, this._setWifiEnabledCallback.bind(this));
3510 // TODO: Remove command queue in Bug 1050147.
3511 requestProcessing: false, // Hold while dequeue and execution a request.
3512 // Released upon the request is fully executed,
3513 // i.e, mostly after callback is done.
3514 requestDone: function requestDone() {
3515 this.requestProcessing = false;
3519 nextRequest: function nextRequest() {
3520 // No request to process
3521 if (this._stateRequests.length === 0) {
3525 // Handling request, wait for it.
3526 if (this.requestProcessing) {
3530 // Hold processing lock
3531 this.requestProcessing = true;
3533 // Find next valid request
3534 let request = this._stateRequests.shift();
3536 request.callback(request.data);
3539 notifyTetheringOn: function notifyTetheringOn() {
3540 // It's really sad that we don't have an API to notify the wifi
3541 // hotspot status. Toggle settings to let gaia know that wifi hotspot
3544 this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] = true;
3545 this._oldWifiTetheringEnabledState = true;
3546 gSettingsService.createLock().set(
3547 SETTINGS_WIFI_TETHERING_ENABLED,
3550 handle: function(aName, aResult) {
3553 handleError: function(aErrorMessage) {
3559 notifyTetheringOff: function notifyTetheringOff() {
3560 // It's really sad that we don't have an API to notify the wifi
3561 // hotspot status. Toggle settings to let gaia know that wifi hotspot
3564 this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] = false;
3565 this._oldWifiTetheringEnabledState = false;
3566 gSettingsService.createLock().set(
3567 SETTINGS_WIFI_TETHERING_ENABLED,
3570 handle: function(aName, aResult) {
3573 handleError: function(aErrorMessage) {
3579 handleWifiEnabled: function(enabled) {
3580 if (this.ignoreWifiEnabledFromSettings) {
3584 // Make sure Wifi hotspot is idle before switching to Wifi mode.
3586 this.queueRequest({command: "setWifiApEnabled", value: false}, function(data) {
3587 if (this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] ||
3588 WifiManager.isWifiTetheringEnabled(WifiManager.tetheringState)) {
3589 this.disconnectedByWifi = true;
3590 this.setWifiApEnabled(false, this.notifyTetheringOff.bind(this));
3597 this.queueRequest({command: "setWifiEnabled", value: enabled}, function(data) {
3598 this._setWifiEnabled(enabled, this._setWifiEnabledCallback.bind(this));
3602 this.queueRequest({command: "setWifiApEnabled", value: true}, function(data) {
3603 if (this.disconnectedByWifi) {
3604 this.setWifiApEnabled(true, this.notifyTetheringOn.bind(this));
3608 this.disconnectedByWifi = false;
3613 handleWifiTetheringEnabled: function(enabled) {
3614 // Make sure Wifi is idle before switching to Wifi hotspot mode.
3616 this.queueRequest({command: "setWifiEnabled", value: false}, function(data) {
3617 if (WifiManager.isWifiEnabled(WifiManager.state)) {
3618 this.disconnectedByWifiTethering = true;
3619 this._setWifiEnabled(false, this._setWifiEnabledCallback.bind(this));
3626 this.queueRequest({command: "setWifiApEnabled", value: enabled}, function(data) {
3627 this.setWifiApEnabled(enabled, this.requestDone.bind(this));
3631 this.queueRequest({command: "setWifiEnabled", value: true}, function(data) {
3632 if (this.disconnectedByWifiTethering) {
3633 this._setWifiEnabled(true, this._setWifiEnabledCallback.bind(this));
3637 this.disconnectedByWifiTethering = false;
3642 // nsIObserver implementation
3643 observe: function observe(subject, topic, data) {
3645 case kMozSettingsChangedObserverTopic:
3646 // The string we're interested in will be a JSON string that looks like:
3647 // {"key":"wifi.enabled","value":"true"}.
3649 let setting = JSON.parse(data);
3650 // To avoid WifiWorker setting the wifi again, don't need to deal with
3651 // the "mozsettings-changed" event fired from internal setting.
3652 if (setting.isInternalChange) {
3656 this.handle(setting.key, setting.value);
3659 case "xpcom-shutdown":
3660 let wifiService = Cc["@mozilla.org/wifi/service;1"].getService(Ci.nsIWifiProxyService);
3661 wifiService.shutdown();
3662 let wifiCertService = Cc["@mozilla.org/wifi/certservice;1"].getService(Ci.nsIWifiCertService);
3663 wifiCertService.shutdown();
3668 handle: function handle(aName, aResult) {
3670 // TODO: Remove function call in Bug 1050147.
3671 case SETTINGS_WIFI_ENABLED:
3672 this.handleWifiEnabled(aResult)
3674 case SETTINGS_WIFI_DEBUG_ENABLED:
3675 if (aResult === null)
3680 case SETTINGS_WIFI_TETHERING_ENABLED:
3681 this._oldWifiTetheringEnabledState = this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED];
3683 case SETTINGS_WIFI_SSID:
3684 case SETTINGS_WIFI_SECURITY_TYPE:
3685 case SETTINGS_WIFI_SECURITY_PASSWORD:
3686 case SETTINGS_WIFI_IP:
3687 case SETTINGS_WIFI_PREFIX:
3688 case SETTINGS_WIFI_DHCPSERVER_STARTIP:
3689 case SETTINGS_WIFI_DHCPSERVER_ENDIP:
3690 case SETTINGS_WIFI_DNS1:
3691 case SETTINGS_WIFI_DNS2:
3692 case SETTINGS_USB_DHCPSERVER_STARTIP:
3693 case SETTINGS_USB_DHCPSERVER_ENDIP:
3694 // TODO: code related to wifi-tethering setting should be removed after GAIA
3695 // use tethering API
3696 if (this.useTetheringAPI) {
3700 if (aResult !== null) {
3701 this.tetheringSettings[aName] = aResult;
3703 debug("'" + aName + "'" + " is now " + this.tetheringSettings[aName]);
3704 let index = this._wifiTetheringSettingsToRead.indexOf(aName);
3707 this._wifiTetheringSettingsToRead.splice(index, 1);
3710 if (this._wifiTetheringSettingsToRead.length) {
3711 debug("We haven't read completely the wifi Tethering data from settings db.");
3715 if (this._oldWifiTetheringEnabledState === this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED]) {
3716 debug("No changes for SETTINGS_WIFI_TETHERING_ENABLED flag. Nothing to do.");
3720 if (this._oldWifiTetheringEnabledState === null &&
3721 !this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED]) {
3722 debug("Do nothing when initial settings for SETTINGS_WIFI_TETHERING_ENABLED flag is false.");
3726 this._oldWifiTetheringEnabledState = this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED];
3727 this.handleWifiTetheringEnabled(aResult)
3732 handleError: function handleError(aErrorMessage) {
3733 debug("There was an error while reading Tethering settings.");
3734 this.tetheringSettings = {};
3735 this.tetheringSettings[SETTINGS_WIFI_TETHERING_ENABLED] = false;
3739 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WifiWorker]);
3742 function updateDebug() {
3744 debug = function (s) {
3745 dump("-*- WifiWorker component: " + s + "\n");
3748 debug = function (s) {};
3750 WifiManager.syncDebug();