1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/system/gonk/NetworkService.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,562 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; 1.11 + 1.12 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.13 +Cu.import("resource://gre/modules/Services.jsm"); 1.14 +Cu.import("resource://gre/modules/NetUtil.jsm"); 1.15 +Cu.import("resource://gre/modules/FileUtils.jsm"); 1.16 + 1.17 +const NETWORKSERVICE_CONTRACTID = "@mozilla.org/network/service;1"; 1.18 +const NETWORKSERVICE_CID = Components.ID("{baec696c-c78d-42db-8b44-603f8fbfafb4}"); 1.19 + 1.20 +XPCOMUtils.defineLazyServiceGetter(this, "gNetworkWorker", 1.21 + "@mozilla.org/network/worker;1", 1.22 + "nsINetworkWorker"); 1.23 + 1.24 +// 1xx - Requested action is proceeding 1.25 +const NETD_COMMAND_PROCEEDING = 100; 1.26 +// 2xx - Requested action has been successfully completed 1.27 +const NETD_COMMAND_OKAY = 200; 1.28 +// 4xx - The command is accepted but the requested action didn't 1.29 +// take place. 1.30 +const NETD_COMMAND_FAIL = 400; 1.31 +// 5xx - The command syntax or parameters error 1.32 +const NETD_COMMAND_ERROR = 500; 1.33 +// 6xx - Unsolicited broadcasts 1.34 +const NETD_COMMAND_UNSOLICITED = 600; 1.35 + 1.36 +const WIFI_CTRL_INTERFACE = "wl0.1"; 1.37 + 1.38 +const MANUAL_PROXY_CONFIGURATION = 1; 1.39 + 1.40 +let DEBUG = false; 1.41 + 1.42 +// Read debug setting from pref. 1.43 +try { 1.44 + let debugPref = Services.prefs.getBoolPref("network.debugging.enabled"); 1.45 + DEBUG = DEBUG || debugPref; 1.46 +} catch (e) {} 1.47 + 1.48 +function netdResponseType(code) { 1.49 + return Math.floor(code / 100) * 100; 1.50 +} 1.51 + 1.52 +function isError(code) { 1.53 + let type = netdResponseType(code); 1.54 + return (type !== NETD_COMMAND_PROCEEDING && type !== NETD_COMMAND_OKAY); 1.55 +} 1.56 + 1.57 +function debug(msg) { 1.58 + dump("-*- NetworkService: " + msg + "\n"); 1.59 +} 1.60 + 1.61 +/** 1.62 + * This component watches for network interfaces changing state and then 1.63 + * adjusts routes etc. accordingly. 1.64 + */ 1.65 +function NetworkService() { 1.66 + if(DEBUG) debug("Starting net_worker."); 1.67 + 1.68 + let self = this; 1.69 + 1.70 + if (gNetworkWorker) { 1.71 + let networkListener = { 1.72 + onEvent: function(event) { 1.73 + self.handleWorkerMessage(event); 1.74 + } 1.75 + }; 1.76 + gNetworkWorker.start(networkListener); 1.77 + } 1.78 + // Callbacks to invoke when a reply arrives from the net_worker. 1.79 + this.controlCallbacks = Object.create(null); 1.80 + 1.81 + this.shutdown = false; 1.82 + Services.obs.addObserver(this, "xpcom-shutdown", false); 1.83 +} 1.84 + 1.85 +NetworkService.prototype = { 1.86 + classID: NETWORKSERVICE_CID, 1.87 + classInfo: XPCOMUtils.generateCI({classID: NETWORKSERVICE_CID, 1.88 + contractID: NETWORKSERVICE_CONTRACTID, 1.89 + classDescription: "Network Service", 1.90 + interfaces: [Ci.nsINetworkService]}), 1.91 + QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkService]), 1.92 + 1.93 + // Helpers 1.94 + 1.95 + idgen: 0, 1.96 + controlMessage: function(params, callback) { 1.97 + if (this.shutdown) { 1.98 + return; 1.99 + } 1.100 + 1.101 + if (callback) { 1.102 + let id = this.idgen++; 1.103 + params.id = id; 1.104 + this.controlCallbacks[id] = callback; 1.105 + } 1.106 + if (gNetworkWorker) { 1.107 + gNetworkWorker.postMessage(params); 1.108 + } 1.109 + }, 1.110 + 1.111 + handleWorkerMessage: function(response) { 1.112 + if(DEBUG) debug("NetworkManager received message from worker: " + JSON.stringify(response)); 1.113 + let id = response.id; 1.114 + if (response.broadcast === true) { 1.115 + Services.obs.notifyObservers(null, response.topic, response.reason); 1.116 + return; 1.117 + } 1.118 + let callback = this.controlCallbacks[id]; 1.119 + if (callback) { 1.120 + callback.call(this, response); 1.121 + delete this.controlCallbacks[id]; 1.122 + } 1.123 + }, 1.124 + 1.125 + // nsINetworkService 1.126 + 1.127 + getNetworkInterfaceStats: function(networkName, callback) { 1.128 + if(DEBUG) debug("getNetworkInterfaceStats for " + networkName); 1.129 + 1.130 + if (this.shutdown) { 1.131 + return; 1.132 + } 1.133 + 1.134 + let file = new FileUtils.File("/proc/net/dev"); 1.135 + if (!file) { 1.136 + callback.networkStatsAvailable(false, -1, -1, new Date()); 1.137 + return; 1.138 + } 1.139 + 1.140 + NetUtil.asyncFetch(file, function(inputStream, status) { 1.141 + let result = { 1.142 + success: true, // netd always return success even interface doesn't exist. 1.143 + rxBytes: 0, 1.144 + txBytes: 0 1.145 + }; 1.146 + result.date = new Date(); 1.147 + 1.148 + if (Components.isSuccessCode(status)) { 1.149 + // Find record for corresponding interface. 1.150 + let statExpr = /(\S+): +(\d+) +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +(\d+) +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+/; 1.151 + let data = NetUtil.readInputStreamToString(inputStream, 1.152 + inputStream.available()).split("\n"); 1.153 + for (let i = 2; i < data.length; i++) { 1.154 + let parseResult = statExpr.exec(data[i]); 1.155 + if (parseResult && parseResult[1] === networkName) { 1.156 + result.rxBytes = parseInt(parseResult[2], 10); 1.157 + result.txBytes = parseInt(parseResult[3], 10); 1.158 + break; 1.159 + } 1.160 + } 1.161 + } 1.162 + 1.163 + callback.networkStatsAvailable(result.success, result.rxBytes, 1.164 + result.txBytes, result.date); 1.165 + }); 1.166 + }, 1.167 + 1.168 + setNetworkInterfaceAlarm: function(networkName, threshold, callback) { 1.169 + if (!networkName) { 1.170 + callback.networkUsageAlarmResult(-1); 1.171 + return; 1.172 + } 1.173 + 1.174 + let self = this; 1.175 + this._disableNetworkInterfaceAlarm(networkName, function(result) { 1.176 + if (threshold < 0) { 1.177 + if (!isError(result.resultCode)) { 1.178 + callback.networkUsageAlarmResult(null); 1.179 + return; 1.180 + } 1.181 + callback.networkUsageAlarmResult(result.reason); 1.182 + return 1.183 + } 1.184 + 1.185 + self._setNetworkInterfaceAlarm(networkName, threshold, callback); 1.186 + }); 1.187 + }, 1.188 + 1.189 + _setNetworkInterfaceAlarm: function(networkName, threshold, callback) { 1.190 + if(DEBUG) debug("setNetworkInterfaceAlarm for " + networkName + " at " + threshold + "bytes"); 1.191 + 1.192 + let params = { 1.193 + cmd: "setNetworkInterfaceAlarm", 1.194 + ifname: networkName, 1.195 + threshold: threshold 1.196 + }; 1.197 + 1.198 + params.report = true; 1.199 + params.isAsync = true; 1.200 + 1.201 + this.controlMessage(params, function(result) { 1.202 + if (!isError(result.resultCode)) { 1.203 + callback.networkUsageAlarmResult(null); 1.204 + return; 1.205 + } 1.206 + 1.207 + this._enableNetworkInterfaceAlarm(networkName, threshold, callback); 1.208 + }); 1.209 + }, 1.210 + 1.211 + _enableNetworkInterfaceAlarm: function(networkName, threshold, callback) { 1.212 + if(DEBUG) debug("enableNetworkInterfaceAlarm for " + networkName + " at " + threshold + "bytes"); 1.213 + 1.214 + let params = { 1.215 + cmd: "enableNetworkInterfaceAlarm", 1.216 + ifname: networkName, 1.217 + threshold: threshold 1.218 + }; 1.219 + 1.220 + params.report = true; 1.221 + params.isAsync = true; 1.222 + 1.223 + this.controlMessage(params, function(result) { 1.224 + if (!isError(result.resultCode)) { 1.225 + callback.networkUsageAlarmResult(null); 1.226 + return; 1.227 + } 1.228 + callback.networkUsageAlarmResult(result.reason); 1.229 + }); 1.230 + }, 1.231 + 1.232 + _disableNetworkInterfaceAlarm: function(networkName, callback) { 1.233 + if(DEBUG) debug("disableNetworkInterfaceAlarm for " + networkName); 1.234 + 1.235 + let params = { 1.236 + cmd: "disableNetworkInterfaceAlarm", 1.237 + ifname: networkName, 1.238 + }; 1.239 + 1.240 + params.report = true; 1.241 + params.isAsync = true; 1.242 + 1.243 + this.controlMessage(params, function(result) { 1.244 + callback(result); 1.245 + }); 1.246 + }, 1.247 + 1.248 + setWifiOperationMode: function(interfaceName, mode, callback) { 1.249 + if(DEBUG) debug("setWifiOperationMode on " + interfaceName + " to " + mode); 1.250 + 1.251 + let params = { 1.252 + cmd: "setWifiOperationMode", 1.253 + ifname: interfaceName, 1.254 + mode: mode 1.255 + }; 1.256 + 1.257 + params.report = true; 1.258 + params.isAsync = true; 1.259 + 1.260 + this.controlMessage(params, function(result) { 1.261 + if (isError(result.resultCode)) { 1.262 + callback.wifiOperationModeResult("netd command error"); 1.263 + } else { 1.264 + callback.wifiOperationModeResult(null); 1.265 + } 1.266 + }); 1.267 + }, 1.268 + 1.269 + resetRoutingTable: function(network) { 1.270 + let ips = {}; 1.271 + let prefixLengths = {}; 1.272 + let length = network.getAddresses(ips, prefixLengths); 1.273 + 1.274 + for (let i = 0; i < length; i++) { 1.275 + let ip = ips.value[i]; 1.276 + let prefixLength = prefixLengths.value[i]; 1.277 + 1.278 + let options = { 1.279 + cmd: "removeNetworkRoute", 1.280 + ifname: network.name, 1.281 + ip: ip, 1.282 + prefixLength: prefixLength 1.283 + }; 1.284 + this.controlMessage(options); 1.285 + } 1.286 + }, 1.287 + 1.288 + setDNS: function(networkInterface) { 1.289 + if(DEBUG) debug("Going DNS to " + networkInterface.name); 1.290 + let dnses = networkInterface.getDnses(); 1.291 + let options = { 1.292 + cmd: "setDNS", 1.293 + ifname: networkInterface.name, 1.294 + domain: "mozilla." + networkInterface.name + ".doman", 1.295 + dnses: dnses 1.296 + }; 1.297 + this.controlMessage(options); 1.298 + }, 1.299 + 1.300 + setDefaultRouteAndDNS: function(network, oldInterface) { 1.301 + if(DEBUG) debug("Going to change route and DNS to " + network.name); 1.302 + let gateways = network.getGateways(); 1.303 + let dnses = network.getDnses(); 1.304 + let options = { 1.305 + cmd: "setDefaultRouteAndDNS", 1.306 + ifname: network.name, 1.307 + oldIfname: (oldInterface && oldInterface !== network) ? oldInterface.name : null, 1.308 + gateways: gateways, 1.309 + domain: "mozilla." + network.name + ".doman", 1.310 + dnses: dnses 1.311 + }; 1.312 + this.controlMessage(options); 1.313 + this.setNetworkProxy(network); 1.314 + }, 1.315 + 1.316 + removeDefaultRoute: function(network) { 1.317 + if(DEBUG) debug("Remove default route for " + network.name); 1.318 + let gateways = network.getGateways(); 1.319 + let options = { 1.320 + cmd: "removeDefaultRoute", 1.321 + ifname: network.name, 1.322 + gateways: gateways 1.323 + }; 1.324 + this.controlMessage(options); 1.325 + }, 1.326 + 1.327 + addHostRoute: function(network) { 1.328 + if(DEBUG) debug("Going to add host route on " + network.name); 1.329 + let gateways = network.getGateways(); 1.330 + let dnses = network.getDnses(); 1.331 + let options = { 1.332 + cmd: "addHostRoute", 1.333 + ifname: network.name, 1.334 + gateways: gateways, 1.335 + hostnames: dnses.concat(network.httpProxyHost) 1.336 + }; 1.337 + this.controlMessage(options); 1.338 + }, 1.339 + 1.340 + removeHostRoute: function(network) { 1.341 + if(DEBUG) debug("Going to remove host route on " + network.name); 1.342 + let gateways = network.getGateways(); 1.343 + let dnses = network.getDnses(); 1.344 + let options = { 1.345 + cmd: "removeHostRoute", 1.346 + ifname: network.name, 1.347 + gateways: gateways, 1.348 + hostnames: dnses.concat(network.httpProxyHost) 1.349 + }; 1.350 + this.controlMessage(options); 1.351 + }, 1.352 + 1.353 + removeHostRoutes: function(ifname) { 1.354 + if(DEBUG) debug("Going to remove all host routes on " + ifname); 1.355 + let options = { 1.356 + cmd: "removeHostRoutes", 1.357 + ifname: ifname, 1.358 + }; 1.359 + this.controlMessage(options); 1.360 + }, 1.361 + 1.362 + addHostRouteWithResolve: function(network, hosts) { 1.363 + if(DEBUG) debug("Going to add host route after dns resolution on " + network.name); 1.364 + let gateways = network.getGateways(); 1.365 + let options = { 1.366 + cmd: "addHostRoute", 1.367 + ifname: network.name, 1.368 + gateways: gateways, 1.369 + hostnames: hosts 1.370 + }; 1.371 + this.controlMessage(options); 1.372 + }, 1.373 + 1.374 + removeHostRouteWithResolve: function(network, hosts) { 1.375 + if(DEBUG) debug("Going to remove host route after dns resolution on " + network.name); 1.376 + let gateways = network.getGateways(); 1.377 + let options = { 1.378 + cmd: "removeHostRoute", 1.379 + ifname: network.name, 1.380 + gateways: gateways, 1.381 + hostnames: hosts 1.382 + }; 1.383 + this.controlMessage(options); 1.384 + }, 1.385 + 1.386 + addSecondaryRoute: function(ifname, route) { 1.387 + if(DEBUG) debug("Going to add route to secondary table on " + ifname); 1.388 + let options = { 1.389 + cmd: "addSecondaryRoute", 1.390 + ifname: ifname, 1.391 + ip: route.ip, 1.392 + prefix: route.prefix, 1.393 + gateway: route.gateway 1.394 + }; 1.395 + this.controlMessage(options); 1.396 + }, 1.397 + 1.398 + removeSecondaryRoute: function(ifname, route) { 1.399 + if(DEBUG) debug("Going to remove route from secondary table on " + ifname); 1.400 + let options = { 1.401 + cmd: "removeSecondaryRoute", 1.402 + ifname: ifname, 1.403 + ip: route.ip, 1.404 + prefix: route.prefix, 1.405 + gateway: route.gateway 1.406 + }; 1.407 + this.controlMessage(options); 1.408 + }, 1.409 + 1.410 + setNetworkProxy: function(network) { 1.411 + try { 1.412 + if (!network.httpProxyHost || network.httpProxyHost === "") { 1.413 + // Sets direct connection to internet. 1.414 + Services.prefs.clearUserPref("network.proxy.type"); 1.415 + Services.prefs.clearUserPref("network.proxy.share_proxy_settings"); 1.416 + Services.prefs.clearUserPref("network.proxy.http"); 1.417 + Services.prefs.clearUserPref("network.proxy.http_port"); 1.418 + Services.prefs.clearUserPref("network.proxy.ssl"); 1.419 + Services.prefs.clearUserPref("network.proxy.ssl_port"); 1.420 + if(DEBUG) debug("No proxy support for " + network.name + " network interface."); 1.421 + return; 1.422 + } 1.423 + 1.424 + if(DEBUG) debug("Going to set proxy settings for " + network.name + " network interface."); 1.425 + // Sets manual proxy configuration. 1.426 + Services.prefs.setIntPref("network.proxy.type", MANUAL_PROXY_CONFIGURATION); 1.427 + // Do not use this proxy server for all protocols. 1.428 + Services.prefs.setBoolPref("network.proxy.share_proxy_settings", false); 1.429 + Services.prefs.setCharPref("network.proxy.http", network.httpProxyHost); 1.430 + Services.prefs.setCharPref("network.proxy.ssl", network.httpProxyHost); 1.431 + let port = network.httpProxyPort === 0 ? 8080 : network.httpProxyPort; 1.432 + Services.prefs.setIntPref("network.proxy.http_port", port); 1.433 + Services.prefs.setIntPref("network.proxy.ssl_port", port); 1.434 + } catch(ex) { 1.435 + if(DEBUG) debug("Exception " + ex + ". Unable to set proxy setting for " + 1.436 + network.name + " network interface."); 1.437 + } 1.438 + }, 1.439 + 1.440 + // Enable/Disable DHCP server. 1.441 + setDhcpServer: function(enabled, config, callback) { 1.442 + if (null === config) { 1.443 + config = {}; 1.444 + } 1.445 + 1.446 + config.cmd = "setDhcpServer"; 1.447 + config.isAsync = true; 1.448 + config.enabled = enabled; 1.449 + 1.450 + this.controlMessage(config, function setDhcpServerResult(response) { 1.451 + if (!response.success) { 1.452 + callback.dhcpServerResult('Set DHCP server error'); 1.453 + return; 1.454 + } 1.455 + callback.dhcpServerResult(null); 1.456 + }); 1.457 + }, 1.458 + 1.459 + // Enable/disable WiFi tethering by sending commands to netd. 1.460 + setWifiTethering: function(enable, config, callback) { 1.461 + // config should've already contained: 1.462 + // .ifname 1.463 + // .internalIfname 1.464 + // .externalIfname 1.465 + config.wifictrlinterfacename = WIFI_CTRL_INTERFACE; 1.466 + config.cmd = "setWifiTethering"; 1.467 + 1.468 + // The callback function in controlMessage may not be fired immediately. 1.469 + config.isAsync = true; 1.470 + this.controlMessage(config, function setWifiTetheringResult(data) { 1.471 + let code = data.resultCode; 1.472 + let reason = data.resultReason; 1.473 + let enable = data.enable; 1.474 + let enableString = enable ? "Enable" : "Disable"; 1.475 + 1.476 + if(DEBUG) debug(enableString + " Wifi tethering result: Code " + code + " reason " + reason); 1.477 + 1.478 + if (isError(code)) { 1.479 + callback.wifiTetheringEnabledChange("netd command error"); 1.480 + } else { 1.481 + callback.wifiTetheringEnabledChange(null); 1.482 + } 1.483 + }); 1.484 + }, 1.485 + 1.486 + // Enable/disable USB tethering by sending commands to netd. 1.487 + setUSBTethering: function(enable, config, callback) { 1.488 + config.cmd = "setUSBTethering"; 1.489 + // The callback function in controlMessage may not be fired immediately. 1.490 + config.isAsync = true; 1.491 + this.controlMessage(config, function setUsbTetheringResult(data) { 1.492 + let code = data.resultCode; 1.493 + let reason = data.resultReason; 1.494 + let enable = data.enable; 1.495 + let enableString = enable ? "Enable" : "Disable"; 1.496 + 1.497 + if(DEBUG) debug(enableString + " USB tethering result: Code " + code + " reason " + reason); 1.498 + 1.499 + if (isError(code)) { 1.500 + callback.usbTetheringEnabledChange("netd command error"); 1.501 + } else { 1.502 + callback.usbTetheringEnabledChange(null); 1.503 + } 1.504 + }); 1.505 + }, 1.506 + 1.507 + // Switch usb function by modifying property of persist.sys.usb.config. 1.508 + enableUsbRndis: function(enable, callback) { 1.509 + if(DEBUG) debug("enableUsbRndis: " + enable); 1.510 + 1.511 + let params = { 1.512 + cmd: "enableUsbRndis", 1.513 + enable: enable 1.514 + }; 1.515 + // Ask net work to report the result when this value is set to true. 1.516 + if (callback) { 1.517 + params.report = true; 1.518 + } else { 1.519 + params.report = false; 1.520 + } 1.521 + 1.522 + // The callback function in controlMessage may not be fired immediately. 1.523 + params.isAsync = true; 1.524 + //this._usbTetheringAction = TETHERING_STATE_ONGOING; 1.525 + this.controlMessage(params, function(data) { 1.526 + callback.enableUsbRndisResult(data.result, data.enable); 1.527 + }); 1.528 + }, 1.529 + 1.530 + updateUpStream: function(previous, current, callback) { 1.531 + let params = { 1.532 + cmd: "updateUpStream", 1.533 + isAsync: true, 1.534 + preInternalIfname: previous.internalIfname, 1.535 + preExternalIfname: previous.externalIfname, 1.536 + curInternalIfname: current.internalIfname, 1.537 + curExternalIfname: current.externalIfname 1.538 + }; 1.539 + 1.540 + this.controlMessage(params, function(data) { 1.541 + let code = data.resultCode; 1.542 + let reason = data.resultReason; 1.543 + if(DEBUG) debug("updateUpStream result: Code " + code + " reason " + reason); 1.544 + callback.updateUpStreamResult(!isError(code), data.curExternalIfname); 1.545 + }); 1.546 + }, 1.547 + 1.548 + shutdown: false, 1.549 + 1.550 + observe: function observe(aSubject, aTopic, aData) { 1.551 + switch (aTopic) { 1.552 + case "xpcom-shutdown": 1.553 + debug("NetworkService shutdown"); 1.554 + this.shutdown = true; 1.555 + Services.obs.removeObserver(this, "xpcom-shutdown"); 1.556 + if (gNetworkWorker) { 1.557 + gNetworkWorker.shutdown(); 1.558 + gNetworkWorker = null; 1.559 + } 1.560 + break; 1.561 + } 1.562 + }, 1.563 +}; 1.564 + 1.565 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkService]);