1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/system/gonk/NetworkManager.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1342 @@ 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/FileUtils.jsm"); 1.15 +Cu.import("resource://gre/modules/systemlibs.js"); 1.16 + 1.17 +const NETWORKMANAGER_CONTRACTID = "@mozilla.org/network/manager;1"; 1.18 +const NETWORKMANAGER_CID = 1.19 + Components.ID("{33901e46-33b8-11e1-9869-f46d04d25bcc}"); 1.20 + 1.21 +const DEFAULT_PREFERRED_NETWORK_TYPE = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI; 1.22 + 1.23 +XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService", 1.24 + "@mozilla.org/settingsService;1", 1.25 + "nsISettingsService"); 1.26 +XPCOMUtils.defineLazyGetter(this, "ppmm", function() { 1.27 + return Cc["@mozilla.org/parentprocessmessagemanager;1"] 1.28 + .getService(Ci.nsIMessageBroadcaster); 1.29 +}); 1.30 + 1.31 +XPCOMUtils.defineLazyServiceGetter(this, "gDNSService", 1.32 + "@mozilla.org/network/dns-service;1", 1.33 + "nsIDNSService"); 1.34 + 1.35 +XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService", 1.36 + "@mozilla.org/network/service;1", 1.37 + "nsINetworkService"); 1.38 + 1.39 +const TOPIC_INTERFACE_STATE_CHANGED = "network-interface-state-changed"; 1.40 +const TOPIC_INTERFACE_REGISTERED = "network-interface-registered"; 1.41 +const TOPIC_INTERFACE_UNREGISTERED = "network-interface-unregistered"; 1.42 +const TOPIC_ACTIVE_CHANGED = "network-active-changed"; 1.43 +const TOPIC_MOZSETTINGS_CHANGED = "mozsettings-changed"; 1.44 +const TOPIC_PREF_CHANGED = "nsPref:changed"; 1.45 +const TOPIC_XPCOM_SHUTDOWN = "xpcom-shutdown"; 1.46 +const TOPIC_CONNECTION_STATE_CHANGED = "network-connection-state-changed"; 1.47 +const PREF_MANAGE_OFFLINE_STATUS = "network.gonk.manage-offline-status"; 1.48 + 1.49 +const POSSIBLE_USB_INTERFACE_NAME = "rndis0,usb0"; 1.50 +const DEFAULT_USB_INTERFACE_NAME = "rndis0"; 1.51 +const DEFAULT_3G_INTERFACE_NAME = "rmnet0"; 1.52 +const DEFAULT_WIFI_INTERFACE_NAME = "wlan0"; 1.53 + 1.54 +// The kernel's proc entry for network lists. 1.55 +const KERNEL_NETWORK_ENTRY = "/sys/class/net"; 1.56 + 1.57 +const TETHERING_TYPE_WIFI = "WiFi"; 1.58 +const TETHERING_TYPE_USB = "USB"; 1.59 + 1.60 +const WIFI_FIRMWARE_AP = "AP"; 1.61 +const WIFI_FIRMWARE_STATION = "STA"; 1.62 +const WIFI_SECURITY_TYPE_NONE = "open"; 1.63 +const WIFI_SECURITY_TYPE_WPA_PSK = "wpa-psk"; 1.64 +const WIFI_SECURITY_TYPE_WPA2_PSK = "wpa2-psk"; 1.65 +const WIFI_CTRL_INTERFACE = "wl0.1"; 1.66 + 1.67 +const NETWORK_INTERFACE_UP = "up"; 1.68 +const NETWORK_INTERFACE_DOWN = "down"; 1.69 + 1.70 +const TETHERING_STATE_ONGOING = "ongoing"; 1.71 +const TETHERING_STATE_IDLE = "idle"; 1.72 +const TETHERING_STATE_ACTIVE = "active"; 1.73 + 1.74 +// Settings DB path for USB tethering. 1.75 +const SETTINGS_USB_ENABLED = "tethering.usb.enabled"; 1.76 +const SETTINGS_USB_IP = "tethering.usb.ip"; 1.77 +const SETTINGS_USB_PREFIX = "tethering.usb.prefix"; 1.78 +const SETTINGS_USB_DHCPSERVER_STARTIP = "tethering.usb.dhcpserver.startip"; 1.79 +const SETTINGS_USB_DHCPSERVER_ENDIP = "tethering.usb.dhcpserver.endip"; 1.80 +const SETTINGS_USB_DNS1 = "tethering.usb.dns1"; 1.81 +const SETTINGS_USB_DNS2 = "tethering.usb.dns2"; 1.82 + 1.83 +// Settings DB path for WIFI tethering. 1.84 +const SETTINGS_WIFI_DHCPSERVER_STARTIP = "tethering.wifi.dhcpserver.startip"; 1.85 +const SETTINGS_WIFI_DHCPSERVER_ENDIP = "tethering.wifi.dhcpserver.endip"; 1.86 + 1.87 +// Settings DB patch for dun required setting. 1.88 +const SETTINGS_DUN_REQUIRED = "tethering.dun.required"; 1.89 + 1.90 +// Default value for USB tethering. 1.91 +const DEFAULT_USB_IP = "192.168.0.1"; 1.92 +const DEFAULT_USB_PREFIX = "24"; 1.93 +const DEFAULT_USB_DHCPSERVER_STARTIP = "192.168.0.10"; 1.94 +const DEFAULT_USB_DHCPSERVER_ENDIP = "192.168.0.30"; 1.95 + 1.96 +const DEFAULT_DNS1 = "8.8.8.8"; 1.97 +const DEFAULT_DNS2 = "8.8.4.4"; 1.98 + 1.99 +const DEFAULT_WIFI_DHCPSERVER_STARTIP = "192.168.1.10"; 1.100 +const DEFAULT_WIFI_DHCPSERVER_ENDIP = "192.168.1.30"; 1.101 + 1.102 +const IPV4_ADDRESS_ANY = "0.0.0.0"; 1.103 +const IPV6_ADDRESS_ANY = "::0"; 1.104 + 1.105 +const IPV4_MAX_PREFIX_LENGTH = 32; 1.106 +const IPV6_MAX_PREFIX_LENGTH = 128; 1.107 + 1.108 +const PREF_DATA_DEFAULT_SERVICE_ID = "ril.data.defaultServiceId"; 1.109 +const MOBILE_DUN_CONNECT_TIMEOUT = 30000; 1.110 +const MOBILE_DUN_RETRY_INTERVAL = 5000; 1.111 +const MOBILE_DUN_MAX_RETRIES = 5; 1.112 + 1.113 +// Connection Type for Network Information API 1.114 +const CONNECTION_TYPE_CULLULAR = 0; 1.115 +const CONNECTION_TYPE_BLUETOOTH = 1; 1.116 +const CONNECTION_TYPE_ETHERNET = 2; 1.117 +const CONNECTION_TYPE_WIFI = 3; 1.118 +const CONNECTION_TYPE_OTHER = 4; 1.119 +const CONNECTION_TYPE_NONE = 5; 1.120 + 1.121 +let DEBUG = false; 1.122 + 1.123 +// Read debug setting from pref. 1.124 +try { 1.125 + let debugPref = Services.prefs.getBoolPref("network.debugging.enabled"); 1.126 + DEBUG = DEBUG || debugPref; 1.127 +} catch (e) {} 1.128 + 1.129 +function defineLazyRegExp(obj, name, pattern) { 1.130 + obj.__defineGetter__(name, function() { 1.131 + delete obj[name]; 1.132 + return obj[name] = new RegExp(pattern); 1.133 + }); 1.134 +} 1.135 + 1.136 +/** 1.137 + * This component watches for network interfaces changing state and then 1.138 + * adjusts routes etc. accordingly. 1.139 + */ 1.140 +function NetworkManager() { 1.141 + this.networkInterfaces = {}; 1.142 + Services.obs.addObserver(this, TOPIC_INTERFACE_STATE_CHANGED, true); 1.143 +#ifdef MOZ_B2G_RIL 1.144 + Services.obs.addObserver(this, TOPIC_INTERFACE_REGISTERED, true); 1.145 + Services.obs.addObserver(this, TOPIC_INTERFACE_UNREGISTERED, true); 1.146 +#endif 1.147 + Services.obs.addObserver(this, TOPIC_XPCOM_SHUTDOWN, false); 1.148 + Services.obs.addObserver(this, TOPIC_MOZSETTINGS_CHANGED, false); 1.149 + 1.150 + try { 1.151 + this._manageOfflineStatus = 1.152 + Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS); 1.153 + } catch(ex) { 1.154 + // Ignore. 1.155 + } 1.156 + Services.prefs.addObserver(PREF_MANAGE_OFFLINE_STATUS, this, false); 1.157 + 1.158 + // Possible usb tethering interfaces for different gonk platform. 1.159 + this.possibleInterface = POSSIBLE_USB_INTERFACE_NAME.split(","); 1.160 + 1.161 + // Default values for internal and external interfaces. 1.162 + this._tetheringInterface = Object.create(null); 1.163 + this._tetheringInterface[TETHERING_TYPE_USB] = { 1.164 + externalInterface: DEFAULT_3G_INTERFACE_NAME, 1.165 + internalInterface: DEFAULT_USB_INTERFACE_NAME 1.166 + }; 1.167 + this._tetheringInterface[TETHERING_TYPE_WIFI] = { 1.168 + externalInterface: DEFAULT_3G_INTERFACE_NAME, 1.169 + internalInterface: DEFAULT_WIFI_INTERFACE_NAME 1.170 + }; 1.171 + 1.172 + this.initTetheringSettings(); 1.173 + 1.174 + let settingsLock = gSettingsService.createLock(); 1.175 + // Read usb tethering data from settings DB. 1.176 + settingsLock.get(SETTINGS_USB_IP, this); 1.177 + settingsLock.get(SETTINGS_USB_PREFIX, this); 1.178 + settingsLock.get(SETTINGS_USB_DHCPSERVER_STARTIP, this); 1.179 + settingsLock.get(SETTINGS_USB_DHCPSERVER_ENDIP, this); 1.180 + settingsLock.get(SETTINGS_USB_DNS1, this); 1.181 + settingsLock.get(SETTINGS_USB_DNS2, this); 1.182 + settingsLock.get(SETTINGS_USB_ENABLED, this); 1.183 + 1.184 + // Read wifi tethering data from settings DB. 1.185 + settingsLock.get(SETTINGS_WIFI_DHCPSERVER_STARTIP, this); 1.186 + settingsLock.get(SETTINGS_WIFI_DHCPSERVER_ENDIP, this); 1.187 + 1.188 + this._usbTetheringSettingsToRead = [SETTINGS_USB_IP, 1.189 + SETTINGS_USB_PREFIX, 1.190 + SETTINGS_USB_DHCPSERVER_STARTIP, 1.191 + SETTINGS_USB_DHCPSERVER_ENDIP, 1.192 + SETTINGS_USB_DNS1, 1.193 + SETTINGS_USB_DNS2, 1.194 + SETTINGS_USB_ENABLED, 1.195 + SETTINGS_WIFI_DHCPSERVER_STARTIP, 1.196 + SETTINGS_WIFI_DHCPSERVER_ENDIP]; 1.197 + 1.198 + this.wantConnectionEvent = null; 1.199 + this.setAndConfigureActive(); 1.200 + 1.201 + ppmm.addMessageListener('NetworkInterfaceList:ListInterface', this); 1.202 + 1.203 + // Used in resolveHostname(). 1.204 + defineLazyRegExp(this, "REGEXP_IPV4", "^\\d{1,3}(?:\\.\\d{1,3}){3}$"); 1.205 + defineLazyRegExp(this, "REGEXP_IPV6", "^[\\da-fA-F]{4}(?::[\\da-fA-F]{4}){7}$"); 1.206 +} 1.207 +NetworkManager.prototype = { 1.208 + classID: NETWORKMANAGER_CID, 1.209 + classInfo: XPCOMUtils.generateCI({classID: NETWORKMANAGER_CID, 1.210 + contractID: NETWORKMANAGER_CONTRACTID, 1.211 + classDescription: "Network Manager", 1.212 + interfaces: [Ci.nsINetworkManager]}), 1.213 + QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkManager, 1.214 + Ci.nsISupportsWeakReference, 1.215 + Ci.nsIObserver, 1.216 + Ci.nsISettingsServiceCallback]), 1.217 + 1.218 + // nsIObserver 1.219 + 1.220 + observe: function(subject, topic, data) { 1.221 + switch (topic) { 1.222 + case TOPIC_INTERFACE_STATE_CHANGED: 1.223 + let network = subject.QueryInterface(Ci.nsINetworkInterface); 1.224 + debug("Network " + network.name + " changed state to " + network.state); 1.225 + switch (network.state) { 1.226 + case Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED: 1.227 +#ifdef MOZ_B2G_RIL 1.228 + // Add host route for data calls 1.229 + if (this.isNetworkTypeMobile(network.type)) { 1.230 + gNetworkService.removeHostRoutes(network.name); 1.231 + gNetworkService.addHostRoute(network); 1.232 + } 1.233 + // Add extra host route. For example, mms proxy or mmsc. 1.234 + this.setExtraHostRoute(network); 1.235 + // Dun type is a special case where we add the default route to a 1.236 + // secondary table. 1.237 + if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) { 1.238 + this.setSecondaryDefaultRoute(network); 1.239 + } 1.240 +#endif 1.241 + // Remove pre-created default route and let setAndConfigureActive() 1.242 + // to set default route only on preferred network 1.243 + gNetworkService.removeDefaultRoute(network); 1.244 + this.setAndConfigureActive(); 1.245 +#ifdef MOZ_B2G_RIL 1.246 + // Update data connection when Wifi connected/disconnected 1.247 + if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) { 1.248 + for (let i = 0; i < this.mRil.numRadioInterfaces; i++) { 1.249 + this.mRil.getRadioInterface(i).updateRILNetworkInterface(); 1.250 + } 1.251 + } 1.252 +#endif 1.253 + 1.254 + this.onConnectionChanged(network); 1.255 + 1.256 + // Probing the public network accessibility after routing table is ready 1.257 + CaptivePortalDetectionHelper 1.258 + .notify(CaptivePortalDetectionHelper.EVENT_CONNECT, this.active); 1.259 + break; 1.260 + case Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED: 1.261 +#ifdef MOZ_B2G_RIL 1.262 + // Remove host route for data calls 1.263 + if (this.isNetworkTypeMobile(network.type)) { 1.264 + gNetworkService.removeHostRoute(network); 1.265 + } 1.266 + // Remove extra host route. For example, mms proxy or mmsc. 1.267 + this.removeExtraHostRoute(network); 1.268 + // Remove secondary default route for dun. 1.269 + if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) { 1.270 + this.removeSecondaryDefaultRoute(network); 1.271 + } 1.272 +#endif 1.273 + // Remove routing table in /proc/net/route 1.274 + if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) { 1.275 + gNetworkService.resetRoutingTable(network); 1.276 +#ifdef MOZ_B2G_RIL 1.277 + } else if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) { 1.278 + gNetworkService.removeDefaultRoute(network); 1.279 +#endif 1.280 + } 1.281 + 1.282 + // Abort ongoing captive portal detection on the wifi interface 1.283 + CaptivePortalDetectionHelper 1.284 + .notify(CaptivePortalDetectionHelper.EVENT_DISCONNECT, network); 1.285 + this.setAndConfigureActive(); 1.286 +#ifdef MOZ_B2G_RIL 1.287 + // Update data connection when Wifi connected/disconnected 1.288 + if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) { 1.289 + for (let i = 0; i < this.mRil.numRadioInterfaces; i++) { 1.290 + this.mRil.getRadioInterface(i).updateRILNetworkInterface(); 1.291 + } 1.292 + } 1.293 +#endif 1.294 + break; 1.295 + } 1.296 +#ifdef MOZ_B2G_RIL 1.297 + // Notify outer modules like MmsService to start the transaction after 1.298 + // the configuration of the network interface is done. 1.299 + Services.obs.notifyObservers(network, TOPIC_CONNECTION_STATE_CHANGED, 1.300 + this.convertConnectionType(network)); 1.301 +#endif 1.302 + break; 1.303 +#ifdef MOZ_B2G_RIL 1.304 + case TOPIC_INTERFACE_REGISTERED: 1.305 + let regNetwork = subject.QueryInterface(Ci.nsINetworkInterface); 1.306 + // Add extra host route. For example, mms proxy or mmsc. 1.307 + this.setExtraHostRoute(regNetwork); 1.308 + // Dun type is a special case where we add the default route to a 1.309 + // secondary table. 1.310 + if (regNetwork.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) { 1.311 + this.setSecondaryDefaultRoute(regNetwork); 1.312 + } 1.313 + break; 1.314 + case TOPIC_INTERFACE_UNREGISTERED: 1.315 + let unregNetwork = subject.QueryInterface(Ci.nsINetworkInterface); 1.316 + // Remove extra host route. For example, mms proxy or mmsc. 1.317 + this.removeExtraHostRoute(unregNetwork); 1.318 + // Remove secondary default route for dun. 1.319 + if (unregNetwork.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) { 1.320 + this.removeSecondaryDefaultRoute(unregNetwork); 1.321 + } 1.322 + break; 1.323 +#endif 1.324 + case TOPIC_MOZSETTINGS_CHANGED: 1.325 + let setting = JSON.parse(data); 1.326 + this.handle(setting.key, setting.value); 1.327 + break; 1.328 + case TOPIC_PREF_CHANGED: 1.329 + this._manageOfflineStatus = 1.330 + Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS); 1.331 + debug(PREF_MANAGE_OFFLINE_STATUS + " has changed to " + this._manageOfflineStatus); 1.332 + break; 1.333 + case TOPIC_XPCOM_SHUTDOWN: 1.334 + Services.obs.removeObserver(this, TOPIC_XPCOM_SHUTDOWN); 1.335 + Services.obs.removeObserver(this, TOPIC_MOZSETTINGS_CHANGED); 1.336 +#ifdef MOZ_B2G_RIL 1.337 + Services.obs.removeObserver(this, TOPIC_INTERFACE_REGISTERED); 1.338 + Services.obs.removeObserver(this, TOPIC_INTERFACE_UNREGISTERED); 1.339 +#endif 1.340 + Services.obs.removeObserver(this, TOPIC_INTERFACE_STATE_CHANGED); 1.341 +#ifdef MOZ_B2G_RIL 1.342 + this.dunConnectTimer.cancel(); 1.343 + this.dunRetryTimer.cancel(); 1.344 +#endif 1.345 + break; 1.346 + } 1.347 + }, 1.348 + 1.349 + receiveMessage: function(aMsg) { 1.350 + switch (aMsg.name) { 1.351 + case "NetworkInterfaceList:ListInterface": { 1.352 +#ifdef MOZ_B2G_RIL 1.353 + let excludeMms = aMsg.json.excludeMms; 1.354 + let excludeSupl = aMsg.json.excludeSupl; 1.355 + let excludeIms = aMsg.json.excludeIms; 1.356 + let excludeDun = aMsg.json.excludeDun; 1.357 +#endif 1.358 + let interfaces = []; 1.359 + 1.360 + for each (let i in this.networkInterfaces) { 1.361 +#ifdef MOZ_B2G_RIL 1.362 + if ((i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS && excludeMms) || 1.363 + (i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL && excludeSupl) || 1.364 + (i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_IMS && excludeIms) || 1.365 + (i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN && excludeDun)) { 1.366 + continue; 1.367 + } 1.368 +#endif 1.369 + let ips = {}; 1.370 + let prefixLengths = {}; 1.371 + i.getAddresses(ips, prefixLengths); 1.372 + 1.373 + interfaces.push({ 1.374 + state: i.state, 1.375 + type: i.type, 1.376 + name: i.name, 1.377 + ips: ips.value, 1.378 + prefixLengths: prefixLengths.value, 1.379 + gateways: i.getGateways(), 1.380 + dnses: i.getDnses(), 1.381 + httpProxyHost: i.httpProxyHost, 1.382 + httpProxyPort: i.httpProxyPort 1.383 + }); 1.384 + } 1.385 + return interfaces; 1.386 + } 1.387 + } 1.388 + }, 1.389 + 1.390 + // nsINetworkManager 1.391 + 1.392 + registerNetworkInterface: function(network) { 1.393 + if (!(network instanceof Ci.nsINetworkInterface)) { 1.394 + throw Components.Exception("Argument must be nsINetworkInterface.", 1.395 + Cr.NS_ERROR_INVALID_ARG); 1.396 + } 1.397 + if (network.name in this.networkInterfaces) { 1.398 + throw Components.Exception("Network with that name already registered!", 1.399 + Cr.NS_ERROR_INVALID_ARG); 1.400 + } 1.401 + this.networkInterfaces[network.name] = network; 1.402 +#ifdef MOZ_B2G_RIL 1.403 + // Add host route for data calls 1.404 + if (this.isNetworkTypeMobile(network.type)) { 1.405 + gNetworkService.addHostRoute(network); 1.406 + } 1.407 +#endif 1.408 + // Remove pre-created default route and let setAndConfigureActive() 1.409 + // to set default route only on preferred network 1.410 + gNetworkService.removeDefaultRoute(network); 1.411 + this.setAndConfigureActive(); 1.412 + Services.obs.notifyObservers(network, TOPIC_INTERFACE_REGISTERED, null); 1.413 + debug("Network '" + network.name + "' registered."); 1.414 + }, 1.415 + 1.416 + unregisterNetworkInterface: function(network) { 1.417 + if (!(network instanceof Ci.nsINetworkInterface)) { 1.418 + throw Components.Exception("Argument must be nsINetworkInterface.", 1.419 + Cr.NS_ERROR_INVALID_ARG); 1.420 + } 1.421 + if (!(network.name in this.networkInterfaces)) { 1.422 + throw Components.Exception("No network with that name registered.", 1.423 + Cr.NS_ERROR_INVALID_ARG); 1.424 + } 1.425 + delete this.networkInterfaces[network.name]; 1.426 +#ifdef MOZ_B2G_RIL 1.427 + // Remove host route for data calls 1.428 + if (this.isNetworkTypeMobile(network.type)) { 1.429 + gNetworkService.removeHostRoute(network); 1.430 + } 1.431 +#endif 1.432 + this.setAndConfigureActive(); 1.433 + Services.obs.notifyObservers(network, TOPIC_INTERFACE_UNREGISTERED, null); 1.434 + debug("Network '" + network.name + "' unregistered."); 1.435 + }, 1.436 + 1.437 + _manageOfflineStatus: true, 1.438 + 1.439 + networkInterfaces: null, 1.440 + 1.441 + _preferredNetworkType: DEFAULT_PREFERRED_NETWORK_TYPE, 1.442 + get preferredNetworkType() { 1.443 + return this._preferredNetworkType; 1.444 + }, 1.445 + set preferredNetworkType(val) { 1.446 +#ifdef MOZ_B2G_RIL 1.447 + if ([Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, 1.448 + Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE].indexOf(val) == -1) { 1.449 +#else 1.450 + if (val != Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) { 1.451 +#endif 1.452 + throw "Invalid network type"; 1.453 + } 1.454 + this._preferredNetworkType = val; 1.455 + }, 1.456 + 1.457 + active: null, 1.458 + _overriddenActive: null, 1.459 + 1.460 + overrideActive: function(network) { 1.461 +#ifdef MOZ_B2G_RIL 1.462 + if (this.isNetworkTypeSecondaryMobile(network.type)) { 1.463 + throw "Invalid network type"; 1.464 + } 1.465 +#endif 1.466 + this._overriddenActive = network; 1.467 + this.setAndConfigureActive(); 1.468 + }, 1.469 + 1.470 +#ifdef MOZ_B2G_RIL 1.471 + isNetworkTypeSecondaryMobile: function(type) { 1.472 + return (type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS || 1.473 + type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL || 1.474 + type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_IMS || 1.475 + type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN); 1.476 + }, 1.477 + 1.478 + isNetworkTypeMobile: function(type) { 1.479 + return (type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE || 1.480 + this.isNetworkTypeSecondaryMobile(type)); 1.481 + }, 1.482 + 1.483 + setExtraHostRoute: function(network) { 1.484 + if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS) { 1.485 + if (!(network instanceof Ci.nsIRilNetworkInterface)) { 1.486 + debug("Network for MMS must be an instance of nsIRilNetworkInterface"); 1.487 + return; 1.488 + } 1.489 + 1.490 + network = network.QueryInterface(Ci.nsIRilNetworkInterface); 1.491 + 1.492 + debug("Network '" + network.name + "' registered, " + 1.493 + "adding mmsproxy and/or mmsc route"); 1.494 + 1.495 + let hostToResolve = network.mmsProxy; 1.496 + // Workaround an xpconnect issue with undefined string objects. 1.497 + // See bug 808220 1.498 + if (!hostToResolve || hostToResolve === "undefined") { 1.499 + hostToResolve = network.mmsc; 1.500 + } 1.501 + 1.502 + let mmsHosts = this.resolveHostname([hostToResolve]); 1.503 + if (mmsHosts.length == 0) { 1.504 + debug("No valid hostnames can be added. Stop adding host route."); 1.505 + return; 1.506 + } 1.507 + 1.508 + gNetworkService.addHostRouteWithResolve(network, mmsHosts); 1.509 + } 1.510 + }, 1.511 + 1.512 + removeExtraHostRoute: function(network) { 1.513 + if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS) { 1.514 + if (!(network instanceof Ci.nsIRilNetworkInterface)) { 1.515 + debug("Network for MMS must be an instance of nsIRilNetworkInterface"); 1.516 + return; 1.517 + } 1.518 + 1.519 + network = network.QueryInterface(Ci.nsIRilNetworkInterface); 1.520 + 1.521 + debug("Network '" + network.name + "' unregistered, " + 1.522 + "removing mmsproxy and/or mmsc route"); 1.523 + 1.524 + let hostToResolve = network.mmsProxy; 1.525 + // Workaround an xpconnect issue with undefined string objects. 1.526 + // See bug 808220 1.527 + if (!hostToResolve || hostToResolve === "undefined") { 1.528 + hostToResolve = network.mmsc; 1.529 + } 1.530 + 1.531 + let mmsHosts = this.resolveHostname([hostToResolve]); 1.532 + if (mmsHosts.length == 0) { 1.533 + debug("No valid hostnames can be removed. Stop removing host route."); 1.534 + return; 1.535 + } 1.536 + 1.537 + gNetworkService.removeHostRouteWithResolve(network, mmsHosts); 1.538 + } 1.539 + }, 1.540 + 1.541 + setSecondaryDefaultRoute: function(network) { 1.542 + let gateways = network.getGateways(); 1.543 + for (let i = 0; i < gateways.length; i++) { 1.544 + let isIPv6 = (gateways[i].indexOf(":") != -1) ? true : false; 1.545 + // First, we need to add a host route to the gateway in the secondary 1.546 + // routing table to make the gateway reachable. Host route takes the max 1.547 + // prefix and gateway address 'any'. 1.548 + let route = { 1.549 + ip: gateways[i], 1.550 + prefix: isIPv6 ? IPV6_MAX_PREFIX_LENGTH : IPV4_MAX_PREFIX_LENGTH, 1.551 + gateway: isIPv6 ? IPV6_ADDRESS_ANY : IPV4_ADDRESS_ANY 1.552 + }; 1.553 + gNetworkService.addSecondaryRoute(network.name, route); 1.554 + // Now we can add the default route through gateway. Default route takes the 1.555 + // min prefix and destination ip 'any'. 1.556 + route.ip = isIPv6 ? IPV6_ADDRESS_ANY : IPV4_ADDRESS_ANY; 1.557 + route.prefix = 0; 1.558 + route.gateway = gateways[i]; 1.559 + gNetworkService.addSecondaryRoute(network.name, route); 1.560 + } 1.561 + }, 1.562 + 1.563 + removeSecondaryDefaultRoute: function(network) { 1.564 + let gateways = network.getGateways(); 1.565 + for (let i = 0; i < gateways.length; i++) { 1.566 + let isIPv6 = (gateways[i].indexOf(":") != -1) ? true : false; 1.567 + // Remove both default route and host route. 1.568 + let route = { 1.569 + ip: isIPv6 ? IPV6_ADDRESS_ANY : IPV4_ADDRESS_ANY, 1.570 + prefix: 0, 1.571 + gateway: gateways[i] 1.572 + }; 1.573 + gNetworkService.removeSecondaryRoute(network.name, route); 1.574 + 1.575 + route.ip = gateways[i]; 1.576 + route.prefix = isIPv6 ? IPV6_MAX_PREFIX_LENGTH : IPV4_MAX_PREFIX_LENGTH; 1.577 + route.gateway = isIPv6 ? IPV6_ADDRESS_ANY : IPV4_ADDRESS_ANY; 1.578 + gNetworkService.removeSecondaryRoute(network.name, route); 1.579 + } 1.580 + }, 1.581 +#endif // MOZ_B2G_RIL 1.582 + 1.583 + /** 1.584 + * Determine the active interface and configure it. 1.585 + */ 1.586 + setAndConfigureActive: function() { 1.587 + debug("Evaluating whether active network needs to be changed."); 1.588 + let oldActive = this.active; 1.589 + 1.590 + if (this._overriddenActive) { 1.591 + debug("We have an override for the active network: " + 1.592 + this._overriddenActive.name); 1.593 + // The override was just set, so reconfigure the network. 1.594 + if (this.active != this._overriddenActive) { 1.595 + this.active = this._overriddenActive; 1.596 + gNetworkService.setDefaultRouteAndDNS(this.active, oldActive); 1.597 + Services.obs.notifyObservers(this.active, TOPIC_ACTIVE_CHANGED, null); 1.598 + } 1.599 + return; 1.600 + } 1.601 + 1.602 + // The active network is already our preferred type. 1.603 + if (this.active && 1.604 + this.active.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED && 1.605 + this.active.type == this._preferredNetworkType) { 1.606 + debug("Active network is already our preferred type."); 1.607 + gNetworkService.setDefaultRouteAndDNS(this.active, oldActive); 1.608 + return; 1.609 + } 1.610 + 1.611 + // Find a suitable network interface to activate. 1.612 + this.active = null; 1.613 +#ifdef MOZ_B2G_RIL 1.614 + let defaultDataNetwork; 1.615 +#endif 1.616 + for each (let network in this.networkInterfaces) { 1.617 + if (network.state != Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) { 1.618 + continue; 1.619 + } 1.620 +#ifdef MOZ_B2G_RIL 1.621 + if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) { 1.622 + defaultDataNetwork = network; 1.623 + } 1.624 +#endif 1.625 + this.active = network; 1.626 + if (network.type == this.preferredNetworkType) { 1.627 + debug("Found our preferred type of network: " + network.name); 1.628 + break; 1.629 + } 1.630 + } 1.631 + if (this.active) { 1.632 +#ifdef MOZ_B2G_RIL 1.633 + // Give higher priority to default data APN than seconary APN. 1.634 + // If default data APN is not connected, we still set default route 1.635 + // and DNS on seconary APN. 1.636 + if (defaultDataNetwork && 1.637 + this.isNetworkTypeSecondaryMobile(this.active.type) && 1.638 + this.active.type != this.preferredNetworkType) { 1.639 + this.active = defaultDataNetwork; 1.640 + } 1.641 + // Don't set default route on secondary APN 1.642 + if (this.isNetworkTypeSecondaryMobile(this.active.type)) { 1.643 + gNetworkService.setDNS(this.active); 1.644 + } else { 1.645 +#endif // MOZ_B2G_RIL 1.646 + gNetworkService.setDefaultRouteAndDNS(this.active, oldActive); 1.647 +#ifdef MOZ_B2G_RIL 1.648 + } 1.649 +#endif 1.650 + } 1.651 + 1.652 + if (this.active != oldActive) { 1.653 + Services.obs.notifyObservers(this.active, TOPIC_ACTIVE_CHANGED, null); 1.654 + } 1.655 + 1.656 + if (this._manageOfflineStatus) { 1.657 + Services.io.offline = !this.active; 1.658 + } 1.659 + }, 1.660 + 1.661 +#ifdef MOZ_B2G_RIL 1.662 + resolveHostname: function(hosts) { 1.663 + let retval = []; 1.664 + 1.665 + for (let hostname of hosts) { 1.666 + // Sanity check for null, undefined and empty string... etc. 1.667 + if (!hostname) { 1.668 + continue; 1.669 + } 1.670 + 1.671 + try { 1.672 + let uri = Services.io.newURI(hostname, null, null); 1.673 + hostname = uri.host; 1.674 + } catch (e) {} 1.675 + 1.676 + // An extra check for hostnames that cannot be made by newURI(...). 1.677 + // For example, an IP address like "10.1.1.1". 1.678 + if (hostname.match(this.REGEXP_IPV4) || 1.679 + hostname.match(this.REGEXP_IPV6)) { 1.680 + retval.push(hostname); 1.681 + continue; 1.682 + } 1.683 + 1.684 + try { 1.685 + let hostnameIps = gDNSService.resolve(hostname, 0); 1.686 + while (hostnameIps.hasMore()) { 1.687 + retval.push(hostnameIps.getNextAddrAsString()); 1.688 + debug("Found IP at: " + JSON.stringify(retval)); 1.689 + } 1.690 + } catch (e) {} 1.691 + } 1.692 + 1.693 + return retval; 1.694 + }, 1.695 +#endif 1.696 + 1.697 + convertConnectionType: function(network) { 1.698 + // If there is internal interface change (e.g., MOBILE_MMS, MOBILE_SUPL), 1.699 + // the function will return null so that it won't trigger type change event 1.700 + // in NetworkInformation API. 1.701 + if (network.type != Ci.nsINetworkInterface.NETWORK_TYPE_WIFI && 1.702 + network.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) { 1.703 + return null; 1.704 + } 1.705 + 1.706 + if (network.state == Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED) { 1.707 + return CONNECTION_TYPE_NONE; 1.708 + } 1.709 + 1.710 + switch (network.type) { 1.711 + case Ci.nsINetworkInterface.NETWORK_TYPE_WIFI: 1.712 + return CONNECTION_TYPE_WIFI; 1.713 + case Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE: 1.714 + return CONNECTION_TYPE_CULLULAR; 1.715 + } 1.716 + }, 1.717 + 1.718 + // nsISettingsServiceCallback 1.719 + 1.720 + tetheringSettings: {}, 1.721 + 1.722 + initTetheringSettings: function() { 1.723 + this.tetheringSettings[SETTINGS_USB_ENABLED] = false; 1.724 + this.tetheringSettings[SETTINGS_USB_IP] = DEFAULT_USB_IP; 1.725 + this.tetheringSettings[SETTINGS_USB_PREFIX] = DEFAULT_USB_PREFIX; 1.726 + this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP] = DEFAULT_USB_DHCPSERVER_STARTIP; 1.727 + this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP] = DEFAULT_USB_DHCPSERVER_ENDIP; 1.728 + this.tetheringSettings[SETTINGS_USB_DNS1] = DEFAULT_DNS1; 1.729 + this.tetheringSettings[SETTINGS_USB_DNS2] = DEFAULT_DNS2; 1.730 + 1.731 + this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP] = DEFAULT_WIFI_DHCPSERVER_STARTIP; 1.732 + this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP] = DEFAULT_WIFI_DHCPSERVER_ENDIP; 1.733 + 1.734 +#ifdef MOZ_B2G_RIL 1.735 + this.tetheringSettings[SETTINGS_DUN_REQUIRED] = 1.736 + libcutils.property_get("ro.tethering.dun_required") === "1"; 1.737 +#endif 1.738 + }, 1.739 + 1.740 + _requestCount: 0, 1.741 + 1.742 + handle: function(aName, aResult) { 1.743 + switch(aName) { 1.744 + case SETTINGS_USB_ENABLED: 1.745 + this._oldUsbTetheringEnabledState = this.tetheringSettings[SETTINGS_USB_ENABLED]; 1.746 + case SETTINGS_USB_IP: 1.747 + case SETTINGS_USB_PREFIX: 1.748 + case SETTINGS_USB_DHCPSERVER_STARTIP: 1.749 + case SETTINGS_USB_DHCPSERVER_ENDIP: 1.750 + case SETTINGS_USB_DNS1: 1.751 + case SETTINGS_USB_DNS2: 1.752 + case SETTINGS_WIFI_DHCPSERVER_STARTIP: 1.753 + case SETTINGS_WIFI_DHCPSERVER_ENDIP: 1.754 + if (aResult !== null) { 1.755 + this.tetheringSettings[aName] = aResult; 1.756 + } 1.757 + debug("'" + aName + "'" + " is now " + this.tetheringSettings[aName]); 1.758 + let index = this._usbTetheringSettingsToRead.indexOf(aName); 1.759 + 1.760 + if (index != -1) { 1.761 + this._usbTetheringSettingsToRead.splice(index, 1); 1.762 + } 1.763 + 1.764 + if (this._usbTetheringSettingsToRead.length) { 1.765 + debug("We haven't read completely the usb Tethering data from settings db."); 1.766 + break; 1.767 + } 1.768 + 1.769 + if (this._oldUsbTetheringEnabledState === this.tetheringSettings[SETTINGS_USB_ENABLED]) { 1.770 + debug("No changes for SETTINGS_USB_ENABLED flag. Nothing to do."); 1.771 + break; 1.772 + } 1.773 + 1.774 + this._requestCount++; 1.775 + if (this._requestCount === 1) { 1.776 + this.handleUSBTetheringToggle(aResult); 1.777 + } 1.778 + break; 1.779 + }; 1.780 + }, 1.781 + 1.782 + handleError: function(aErrorMessage) { 1.783 + debug("There was an error while reading Tethering settings."); 1.784 + this.tetheringSettings = {}; 1.785 + this.tetheringSettings[SETTINGS_USB_ENABLED] = false; 1.786 + }, 1.787 + 1.788 + getNetworkInterface: function(type) { 1.789 + for each (let network in this.networkInterfaces) { 1.790 + if (network.type == type) { 1.791 + return network; 1.792 + } 1.793 + } 1.794 + return null; 1.795 + }, 1.796 + 1.797 + _usbTetheringAction: TETHERING_STATE_IDLE, 1.798 + 1.799 + _usbTetheringSettingsToRead: [], 1.800 + 1.801 + _oldUsbTetheringEnabledState: null, 1.802 + 1.803 + // External and internal interface name. 1.804 + _tetheringInterface: null, 1.805 + 1.806 + handleLastRequest: function() { 1.807 + let count = this._requestCount; 1.808 + this._requestCount = 0; 1.809 + 1.810 + if (count === 1) { 1.811 + if (this.wantConnectionEvent) { 1.812 + if (this.tetheringSettings[SETTINGS_USB_ENABLED]) { 1.813 + this.wantConnectionEvent.call(this); 1.814 + } 1.815 + this.wantConnectionEvent = null; 1.816 + } 1.817 + return; 1.818 + } 1.819 + 1.820 + if (count > 1) { 1.821 + this.handleUSBTetheringToggle(this.tetheringSettings[SETTINGS_USB_ENABLED]); 1.822 + this.wantConnectionEvent = null; 1.823 + } 1.824 + }, 1.825 + 1.826 +#ifdef MOZ_B2G_RIL 1.827 + dunConnectTimer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer), 1.828 + /** 1.829 + * Callback when dun connection fails to connect within timeout. 1.830 + */ 1.831 + onDunConnectTimerTimeout: function() { 1.832 + while (this._pendingTetheringRequests.length > 0) { 1.833 + debug("onDunConnectTimerTimeout: callback without network info."); 1.834 + let callback = this._pendingTetheringRequests.shift(); 1.835 + if (typeof callback === 'function') { 1.836 + callback(); 1.837 + } 1.838 + } 1.839 + }, 1.840 + 1.841 + dunRetryTimes: 0, 1.842 + dunRetryTimer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer), 1.843 + setupDunConnection: function() { 1.844 + this.dunRetryTimer.cancel(); 1.845 + let ril = this.mRil.getRadioInterface(this.gDataDefaultServiceId); 1.846 + 1.847 + if (ril.rilContext && ril.rilContext.data && 1.848 + ril.rilContext.data.state === "registered") { 1.849 + this.dunRetryTimes = 0; 1.850 + ril.setupDataCallByType("dun"); 1.851 + this.dunConnectTimer.cancel(); 1.852 + this.dunConnectTimer. 1.853 + initWithCallback(this.onDunConnectTimerTimeout.bind(this), 1.854 + MOBILE_DUN_CONNECT_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT); 1.855 + return; 1.856 + } 1.857 + 1.858 + if (this.dunRetryTimes++ >= this.MOBILE_DUN_MAX_RETRIES) { 1.859 + debug("setupDunConnection: max retries reached."); 1.860 + this.dunRetryTimes = 0; 1.861 + // same as dun connect timeout. 1.862 + this.onDunConnectTimerTimeout(); 1.863 + return; 1.864 + } 1.865 + 1.866 + debug("Data not ready, retry dun after " + MOBILE_DUN_RETRY_INTERVAL + " ms."); 1.867 + this.dunRetryTimer. 1.868 + initWithCallback(this.setupDunConnection.bind(this), 1.869 + MOBILE_DUN_RETRY_INTERVAL, Ci.nsITimer.TYPE_ONE_SHOT); 1.870 + }, 1.871 + 1.872 + _pendingTetheringRequests: [], 1.873 + _dunActiveUsers: 0, 1.874 + handleDunConnection: function(enable, callback) { 1.875 + debug("handleDunConnection: " + enable); 1.876 + let dun = this.getNetworkInterface( 1.877 + Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN); 1.878 + 1.879 + if (!enable) { 1.880 + this._dunActiveUsers--; 1.881 + if (this._dunActiveUsers > 0) { 1.882 + debug("Dun still needed by others, do not disconnect.") 1.883 + return; 1.884 + } 1.885 + 1.886 + this.dunRetryTimes = 0; 1.887 + this.dunRetryTimer.cancel(); 1.888 + this.dunConnectTimer.cancel(); 1.889 + this._pendingTetheringRequests = []; 1.890 + 1.891 + if (dun && (dun.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED)) { 1.892 + this.mRil.getRadioInterface(this.gDataDefaultServiceId) 1.893 + .deactivateDataCallByType("dun"); 1.894 + } 1.895 + return; 1.896 + } 1.897 + 1.898 + this._dunActiveUsers++; 1.899 + if (!dun || (dun.state != Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED)) { 1.900 + debug("DUN data call inactive, setup dun data call!") 1.901 + this._pendingTetheringRequests.push(callback); 1.902 + this.dunRetryTimes = 0; 1.903 + this.setupDunConnection(); 1.904 + 1.905 + return; 1.906 + } 1.907 + this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = dun.name; 1.908 + callback(dun); 1.909 + }, 1.910 +#endif 1.911 + 1.912 + handleUSBTetheringToggle: function(enable) { 1.913 + debug("handleUSBTetheringToggle: " + enable); 1.914 + if (enable && 1.915 + (this._usbTetheringAction === TETHERING_STATE_ONGOING || 1.916 + this._usbTetheringAction === TETHERING_STATE_ACTIVE)) { 1.917 + debug("Usb tethering already connecting/connected."); 1.918 + return; 1.919 + } 1.920 + 1.921 + if (!enable && 1.922 + this._usbTetheringAction === TETHERING_STATE_IDLE) { 1.923 + debug("Usb tethering already disconnected."); 1.924 + return; 1.925 + } 1.926 + 1.927 + if (!enable) { 1.928 + this.tetheringSettings[SETTINGS_USB_ENABLED] = false; 1.929 + gNetworkService.enableUsbRndis(false, this.enableUsbRndisResult.bind(this)); 1.930 + return; 1.931 + } 1.932 + 1.933 + this.tetheringSettings[SETTINGS_USB_ENABLED] = true; 1.934 + this._usbTetheringAction = TETHERING_STATE_ONGOING; 1.935 + 1.936 +#ifdef MOZ_B2G_RIL 1.937 + if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) { 1.938 + this.handleDunConnection(true, function(network) { 1.939 + if (!network){ 1.940 + this.usbTetheringResultReport("Dun connection failed"); 1.941 + return; 1.942 + } 1.943 + this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = network.name; 1.944 + gNetworkService.enableUsbRndis(true, this.enableUsbRndisResult.bind(this)); 1.945 + }.bind(this)); 1.946 + return; 1.947 + } 1.948 +#endif 1.949 + 1.950 + if (this.active) { 1.951 + this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = this.active.name; 1.952 + } else { 1.953 + let mobile = this.getNetworkInterface(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE); 1.954 + if (mobile) { 1.955 + this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = mobile.name; 1.956 + } 1.957 + } 1.958 + gNetworkService.enableUsbRndis(true, this.enableUsbRndisResult.bind(this)); 1.959 + }, 1.960 + 1.961 + getUSBTetheringParameters: function(enable, tetheringinterface) { 1.962 + let interfaceIp; 1.963 + let prefix; 1.964 + let wifiDhcpStartIp; 1.965 + let wifiDhcpEndIp; 1.966 + let usbDhcpStartIp; 1.967 + let usbDhcpEndIp; 1.968 + let dns1; 1.969 + let dns2; 1.970 + let internalInterface = tetheringinterface.internalInterface; 1.971 + let externalInterface = tetheringinterface.externalInterface; 1.972 + 1.973 + interfaceIp = this.tetheringSettings[SETTINGS_USB_IP]; 1.974 + prefix = this.tetheringSettings[SETTINGS_USB_PREFIX]; 1.975 + wifiDhcpStartIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP]; 1.976 + wifiDhcpEndIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP]; 1.977 + usbDhcpStartIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP]; 1.978 + usbDhcpEndIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP]; 1.979 + dns1 = this.tetheringSettings[SETTINGS_USB_DNS1]; 1.980 + dns2 = this.tetheringSettings[SETTINGS_USB_DNS2]; 1.981 + 1.982 + // Using the default values here until application support these settings. 1.983 + if (interfaceIp == "" || prefix == "" || 1.984 + wifiDhcpStartIp == "" || wifiDhcpEndIp == "" || 1.985 + usbDhcpStartIp == "" || usbDhcpEndIp == "") { 1.986 + debug("Invalid subnet information."); 1.987 + return null; 1.988 + } 1.989 + 1.990 + return { 1.991 + ifname: internalInterface, 1.992 + ip: interfaceIp, 1.993 + prefix: prefix, 1.994 + wifiStartIp: wifiDhcpStartIp, 1.995 + wifiEndIp: wifiDhcpEndIp, 1.996 + usbStartIp: usbDhcpStartIp, 1.997 + usbEndIp: usbDhcpEndIp, 1.998 + dns1: dns1, 1.999 + dns2: dns2, 1.1000 + internalIfname: internalInterface, 1.1001 + externalIfname: externalInterface, 1.1002 + enable: enable, 1.1003 + link: enable ? NETWORK_INTERFACE_UP : NETWORK_INTERFACE_DOWN 1.1004 + }; 1.1005 + }, 1.1006 + 1.1007 + notifyError: function(resetSettings, callback, msg) { 1.1008 + if (resetSettings) { 1.1009 + let settingsLock = gSettingsService.createLock(); 1.1010 + // Disable wifi tethering with a useful error message for the user. 1.1011 + settingsLock.set("tethering.wifi.enabled", false, null, msg); 1.1012 + } 1.1013 + 1.1014 + debug("setWifiTethering: " + (msg ? msg : "success")); 1.1015 + 1.1016 + if (callback) { 1.1017 + callback.wifiTetheringEnabledChange(msg); 1.1018 + } 1.1019 + }, 1.1020 + 1.1021 + enableWifiTethering: function(enable, config, callback) { 1.1022 + // Fill in config's required fields. 1.1023 + config.ifname = this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface; 1.1024 + config.internalIfname = this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface; 1.1025 + config.externalIfname = this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface; 1.1026 + 1.1027 + gNetworkService.setWifiTethering(enable, config, (function(error) { 1.1028 +#ifdef MOZ_B2G_RIL 1.1029 + // Disconnect dun on error or when wifi tethering is disabled. 1.1030 + if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] && 1.1031 + (!enable || error)) { 1.1032 + this.handleDunConnection(false); 1.1033 + } 1.1034 +#endif 1.1035 + let resetSettings = error; 1.1036 + this.notifyError(resetSettings, callback, error); 1.1037 + }).bind(this)); 1.1038 + }, 1.1039 + 1.1040 + // Enable/disable WiFi tethering by sending commands to netd. 1.1041 + setWifiTethering: function(enable, network, config, callback) { 1.1042 + debug("setWifiTethering: " + enable); 1.1043 + if (!network) { 1.1044 + this.notifyError(true, callback, "invalid network information"); 1.1045 + return; 1.1046 + } 1.1047 + 1.1048 + if (!config) { 1.1049 + this.notifyError(true, callback, "invalid configuration"); 1.1050 + return; 1.1051 + } 1.1052 + 1.1053 + if (!enable) { 1.1054 + this.enableWifiTethering(false, config, callback); 1.1055 + return; 1.1056 + } 1.1057 + 1.1058 + this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface = network.name; 1.1059 + 1.1060 +#ifdef MOZ_B2G_RIL 1.1061 + if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) { 1.1062 + this.handleDunConnection(true, function(config, callback, network) { 1.1063 + if (!network) { 1.1064 + this.notifyError(true, callback, "Dun connection failed"); 1.1065 + return; 1.1066 + } 1.1067 + this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface = network.name; 1.1068 + this.enableWifiTethering(true, config, callback); 1.1069 + }.bind(this, config, callback)); 1.1070 + return; 1.1071 + } 1.1072 +#endif 1.1073 + 1.1074 + let mobile = this.getNetworkInterface(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE); 1.1075 + // Update the real interface name 1.1076 + if (mobile) { 1.1077 + this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface = mobile.name; 1.1078 + } 1.1079 + 1.1080 + this.enableWifiTethering(true, config, callback); 1.1081 + }, 1.1082 + 1.1083 + // Enable/disable USB tethering by sending commands to netd. 1.1084 + setUSBTethering: function(enable, tetheringInterface, callback) { 1.1085 + let params = this.getUSBTetheringParameters(enable, tetheringInterface); 1.1086 + 1.1087 + if (params === null) { 1.1088 + gNetworkService.enableUsbRndis(false, function() { 1.1089 + this.usbTetheringResultReport("Invalid parameters"); 1.1090 + }); 1.1091 + return; 1.1092 + } 1.1093 + 1.1094 + gNetworkService.setUSBTethering(enable, params, callback); 1.1095 + }, 1.1096 + 1.1097 + getUsbInterface: function() { 1.1098 + // Find the rndis interface. 1.1099 + for (let i = 0; i < this.possibleInterface.length; i++) { 1.1100 + try { 1.1101 + let file = new FileUtils.File(KERNEL_NETWORK_ENTRY + "/" + 1.1102 + this.possibleInterface[i]); 1.1103 + if (file.exists()) { 1.1104 + return this.possibleInterface[i]; 1.1105 + } 1.1106 + } catch (e) { 1.1107 + debug("Not " + this.possibleInterface[i] + " interface."); 1.1108 + } 1.1109 + } 1.1110 + debug("Can't find rndis interface in possible lists."); 1.1111 + return DEFAULT_USB_INTERFACE_NAME; 1.1112 + }, 1.1113 + 1.1114 + enableUsbRndisResult: function(success, enable) { 1.1115 + if (success) { 1.1116 + // If enable is false, don't find usb interface cause it is already down, 1.1117 + // just use the internal interface in settings. 1.1118 + if (enable) { 1.1119 + this._tetheringInterface[TETHERING_TYPE_USB].internalInterface = this.getUsbInterface(); 1.1120 + } 1.1121 + this.setUSBTethering(enable, 1.1122 + this._tetheringInterface[TETHERING_TYPE_USB], 1.1123 + this.usbTetheringResultReport.bind(this)); 1.1124 + } else { 1.1125 + this.usbTetheringResultReport("Failed to set usb function"); 1.1126 + throw new Error("failed to set USB Function to adb"); 1.1127 + } 1.1128 + }, 1.1129 + 1.1130 + usbTetheringResultReport: function(error) { 1.1131 + let settingsLock = gSettingsService.createLock(); 1.1132 + 1.1133 + // Disable tethering settings when fail to enable it. 1.1134 + if (error) { 1.1135 + this.tetheringSettings[SETTINGS_USB_ENABLED] = false; 1.1136 + settingsLock.set("tethering.usb.enabled", false, null); 1.1137 + // Skip others request when we found an error. 1.1138 + this._requestCount = 0; 1.1139 + this._usbTetheringAction = TETHERING_STATE_IDLE; 1.1140 +#ifdef MOZ_B2G_RIL 1.1141 + if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) { 1.1142 + this.handleDunConnection(false); 1.1143 + } 1.1144 +#endif 1.1145 + } else { 1.1146 + if (this.tetheringSettings[SETTINGS_USB_ENABLED]) { 1.1147 + this._usbTetheringAction = TETHERING_STATE_ACTIVE; 1.1148 + } else { 1.1149 + this._usbTetheringAction = TETHERING_STATE_IDLE; 1.1150 +#ifdef MOZ_B2G_RIL 1.1151 + if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) { 1.1152 + this.handleDunConnection(false); 1.1153 + } 1.1154 +#endif 1.1155 + } 1.1156 + this.handleLastRequest(); 1.1157 + } 1.1158 + }, 1.1159 + 1.1160 + onConnectionChangedReport: function(success, externalIfname) { 1.1161 + debug("onConnectionChangedReport result: success " + success); 1.1162 + 1.1163 + if (success) { 1.1164 + // Update the external interface. 1.1165 + this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = externalIfname; 1.1166 + debug("Change the interface name to " + externalIfname); 1.1167 + } 1.1168 + }, 1.1169 + 1.1170 + onConnectionChanged: function(network) { 1.1171 + if (network.state != Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) { 1.1172 + debug("We are only interested in CONNECTED event"); 1.1173 + return; 1.1174 + } 1.1175 + 1.1176 +#ifdef MOZ_B2G_RIL 1.1177 + // We can not use network.type only to check if it's dun, cause if it is 1.1178 + // shared with default, the returned type would always be default, see bug 1.1179 + // 939046. In most cases, if dun is required, it should not be shared with 1.1180 + // default. 1.1181 + if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] && 1.1182 + (network.type === Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN || 1.1183 + this.mRil.getRadioInterface(this.gDataDefaultServiceId) 1.1184 + .getDataCallStateByType("dun") === 1.1185 + Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED)) { 1.1186 + this.dunConnectTimer.cancel(); 1.1187 + debug("DUN data call connected, process callbacks."); 1.1188 + while (this._pendingTetheringRequests.length > 0) { 1.1189 + let callback = this._pendingTetheringRequests.shift(); 1.1190 + if (typeof callback === 'function') { 1.1191 + callback(network); 1.1192 + } 1.1193 + } 1.1194 + return; 1.1195 + } 1.1196 +#endif 1.1197 + 1.1198 + if (!this.tetheringSettings[SETTINGS_USB_ENABLED]) { 1.1199 + debug("Usb tethering settings is not enabled"); 1.1200 + return; 1.1201 + } 1.1202 + 1.1203 +#ifdef MOZ_B2G_RIL 1.1204 + if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] && 1.1205 + network.type === Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN && 1.1206 + this._tetheringInterface[TETHERING_TYPE_USB].externalInterface === 1.1207 + network.name) { 1.1208 + debug("Dun required and dun interface is the same"); 1.1209 + return; 1.1210 + } 1.1211 +#endif 1.1212 + 1.1213 + if (this._tetheringInterface[TETHERING_TYPE_USB].externalInterface === 1.1214 + this.active.name) { 1.1215 + debug("The active interface is the same"); 1.1216 + return; 1.1217 + } 1.1218 + 1.1219 + let previous = { 1.1220 + internalIfname: this._tetheringInterface[TETHERING_TYPE_USB].internalInterface, 1.1221 + externalIfname: this._tetheringInterface[TETHERING_TYPE_USB].externalInterface 1.1222 + }; 1.1223 + 1.1224 + let current = { 1.1225 + internalIfname: this._tetheringInterface[TETHERING_TYPE_USB].internalInterface, 1.1226 + externalIfname: network.name 1.1227 + }; 1.1228 + 1.1229 + let callback = (function() { 1.1230 + // Update external network interface. 1.1231 + debug("Update upstream interface to " + network.name); 1.1232 + gNetworkService.updateUpStream(previous, current, this.onConnectionChangedReport.bind(this)); 1.1233 + }).bind(this); 1.1234 + 1.1235 + if (this._usbTetheringAction === TETHERING_STATE_ONGOING) { 1.1236 + debug("Postpone the event and handle it when state is idle."); 1.1237 + this.wantConnectionEvent = callback; 1.1238 + return; 1.1239 + } 1.1240 + this.wantConnectionEvent = null; 1.1241 + 1.1242 + callback.call(this); 1.1243 + } 1.1244 +}; 1.1245 + 1.1246 +let CaptivePortalDetectionHelper = (function() { 1.1247 + 1.1248 + const EVENT_CONNECT = "Connect"; 1.1249 + const EVENT_DISCONNECT = "Disconnect"; 1.1250 + let _ongoingInterface = null; 1.1251 + let _available = ("nsICaptivePortalDetector" in Ci); 1.1252 + let getService = function() { 1.1253 + return Cc['@mozilla.org/toolkit/captive-detector;1'] 1.1254 + .getService(Ci.nsICaptivePortalDetector); 1.1255 + }; 1.1256 + 1.1257 + let _performDetection = function(interfaceName, callback) { 1.1258 + let capService = getService(); 1.1259 + let capCallback = { 1.1260 + QueryInterface: XPCOMUtils.generateQI([Ci.nsICaptivePortalCallback]), 1.1261 + prepare: function() { 1.1262 + capService.finishPreparation(interfaceName); 1.1263 + }, 1.1264 + complete: function(success) { 1.1265 + _ongoingInterface = null; 1.1266 + callback(success); 1.1267 + } 1.1268 + }; 1.1269 + 1.1270 + // Abort any unfinished captive portal detection. 1.1271 + if (_ongoingInterface != null) { 1.1272 + capService.abort(_ongoingInterface); 1.1273 + _ongoingInterface = null; 1.1274 + } 1.1275 + try { 1.1276 + capService.checkCaptivePortal(interfaceName, capCallback); 1.1277 + _ongoingInterface = interfaceName; 1.1278 + } catch (e) { 1.1279 + debug('Fail to detect captive portal due to: ' + e.message); 1.1280 + } 1.1281 + }; 1.1282 + 1.1283 + let _abort = function(interfaceName) { 1.1284 + if (_ongoingInterface !== interfaceName) { 1.1285 + return; 1.1286 + } 1.1287 + 1.1288 + let capService = getService(); 1.1289 + capService.abort(_ongoingInterface); 1.1290 + _ongoingInterface = null; 1.1291 + }; 1.1292 + 1.1293 + return { 1.1294 + EVENT_CONNECT: EVENT_CONNECT, 1.1295 + EVENT_DISCONNECT: EVENT_DISCONNECT, 1.1296 + notify: function(eventType, network) { 1.1297 + switch (eventType) { 1.1298 + case EVENT_CONNECT: 1.1299 + // perform captive portal detection on wifi interface 1.1300 + if (_available && network && 1.1301 + network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) { 1.1302 + _performDetection(network.name, function() { 1.1303 + // TODO: bug 837600 1.1304 + // We can disconnect wifi in here if user abort the login procedure. 1.1305 + }); 1.1306 + } 1.1307 + 1.1308 + break; 1.1309 + case EVENT_DISCONNECT: 1.1310 + if (_available && 1.1311 + network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) { 1.1312 + _abort(network.name); 1.1313 + } 1.1314 + break; 1.1315 + } 1.1316 + } 1.1317 + }; 1.1318 +}()); 1.1319 + 1.1320 +#ifdef MOZ_B2G_RIL 1.1321 +XPCOMUtils.defineLazyServiceGetter(NetworkManager.prototype, "mRil", 1.1322 + "@mozilla.org/ril;1", 1.1323 + "nsIRadioInterfaceLayer"); 1.1324 + 1.1325 +XPCOMUtils.defineLazyGetter(NetworkManager.prototype, 1.1326 + "gDataDefaultServiceId", function() { 1.1327 + try { 1.1328 + return Services.prefs.getIntPref(PREF_DATA_DEFAULT_SERVICE_ID); 1.1329 + } catch(e) {} 1.1330 + 1.1331 + return 0; 1.1332 +}); 1.1333 +#endif 1.1334 + 1.1335 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkManager]); 1.1336 + 1.1337 + 1.1338 +let debug; 1.1339 +if (DEBUG) { 1.1340 + debug = function(s) { 1.1341 + dump("-*- NetworkManager: " + s + "\n"); 1.1342 + }; 1.1343 +} else { 1.1344 + debug = function(s) {}; 1.1345 +}