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