dom/wifi/WifiWorker.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial