dom/system/gonk/NetworkManager.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 "use strict";
michael@0 6
michael@0 7 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
michael@0 8
michael@0 9 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 10 Cu.import("resource://gre/modules/Services.jsm");
michael@0 11 Cu.import("resource://gre/modules/FileUtils.jsm");
michael@0 12 Cu.import("resource://gre/modules/systemlibs.js");
michael@0 13
michael@0 14 const NETWORKMANAGER_CONTRACTID = "@mozilla.org/network/manager;1";
michael@0 15 const NETWORKMANAGER_CID =
michael@0 16 Components.ID("{33901e46-33b8-11e1-9869-f46d04d25bcc}");
michael@0 17
michael@0 18 const DEFAULT_PREFERRED_NETWORK_TYPE = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI;
michael@0 19
michael@0 20 XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
michael@0 21 "@mozilla.org/settingsService;1",
michael@0 22 "nsISettingsService");
michael@0 23 XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
michael@0 24 return Cc["@mozilla.org/parentprocessmessagemanager;1"]
michael@0 25 .getService(Ci.nsIMessageBroadcaster);
michael@0 26 });
michael@0 27
michael@0 28 XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
michael@0 29 "@mozilla.org/network/dns-service;1",
michael@0 30 "nsIDNSService");
michael@0 31
michael@0 32 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
michael@0 33 "@mozilla.org/network/service;1",
michael@0 34 "nsINetworkService");
michael@0 35
michael@0 36 const TOPIC_INTERFACE_STATE_CHANGED = "network-interface-state-changed";
michael@0 37 const TOPIC_INTERFACE_REGISTERED = "network-interface-registered";
michael@0 38 const TOPIC_INTERFACE_UNREGISTERED = "network-interface-unregistered";
michael@0 39 const TOPIC_ACTIVE_CHANGED = "network-active-changed";
michael@0 40 const TOPIC_MOZSETTINGS_CHANGED = "mozsettings-changed";
michael@0 41 const TOPIC_PREF_CHANGED = "nsPref:changed";
michael@0 42 const TOPIC_XPCOM_SHUTDOWN = "xpcom-shutdown";
michael@0 43 const TOPIC_CONNECTION_STATE_CHANGED = "network-connection-state-changed";
michael@0 44 const PREF_MANAGE_OFFLINE_STATUS = "network.gonk.manage-offline-status";
michael@0 45
michael@0 46 const POSSIBLE_USB_INTERFACE_NAME = "rndis0,usb0";
michael@0 47 const DEFAULT_USB_INTERFACE_NAME = "rndis0";
michael@0 48 const DEFAULT_3G_INTERFACE_NAME = "rmnet0";
michael@0 49 const DEFAULT_WIFI_INTERFACE_NAME = "wlan0";
michael@0 50
michael@0 51 // The kernel's proc entry for network lists.
michael@0 52 const KERNEL_NETWORK_ENTRY = "/sys/class/net";
michael@0 53
michael@0 54 const TETHERING_TYPE_WIFI = "WiFi";
michael@0 55 const TETHERING_TYPE_USB = "USB";
michael@0 56
michael@0 57 const WIFI_FIRMWARE_AP = "AP";
michael@0 58 const WIFI_FIRMWARE_STATION = "STA";
michael@0 59 const WIFI_SECURITY_TYPE_NONE = "open";
michael@0 60 const WIFI_SECURITY_TYPE_WPA_PSK = "wpa-psk";
michael@0 61 const WIFI_SECURITY_TYPE_WPA2_PSK = "wpa2-psk";
michael@0 62 const WIFI_CTRL_INTERFACE = "wl0.1";
michael@0 63
michael@0 64 const NETWORK_INTERFACE_UP = "up";
michael@0 65 const NETWORK_INTERFACE_DOWN = "down";
michael@0 66
michael@0 67 const TETHERING_STATE_ONGOING = "ongoing";
michael@0 68 const TETHERING_STATE_IDLE = "idle";
michael@0 69 const TETHERING_STATE_ACTIVE = "active";
michael@0 70
michael@0 71 // Settings DB path for USB tethering.
michael@0 72 const SETTINGS_USB_ENABLED = "tethering.usb.enabled";
michael@0 73 const SETTINGS_USB_IP = "tethering.usb.ip";
michael@0 74 const SETTINGS_USB_PREFIX = "tethering.usb.prefix";
michael@0 75 const SETTINGS_USB_DHCPSERVER_STARTIP = "tethering.usb.dhcpserver.startip";
michael@0 76 const SETTINGS_USB_DHCPSERVER_ENDIP = "tethering.usb.dhcpserver.endip";
michael@0 77 const SETTINGS_USB_DNS1 = "tethering.usb.dns1";
michael@0 78 const SETTINGS_USB_DNS2 = "tethering.usb.dns2";
michael@0 79
michael@0 80 // Settings DB path for WIFI tethering.
michael@0 81 const SETTINGS_WIFI_DHCPSERVER_STARTIP = "tethering.wifi.dhcpserver.startip";
michael@0 82 const SETTINGS_WIFI_DHCPSERVER_ENDIP = "tethering.wifi.dhcpserver.endip";
michael@0 83
michael@0 84 // Settings DB patch for dun required setting.
michael@0 85 const SETTINGS_DUN_REQUIRED = "tethering.dun.required";
michael@0 86
michael@0 87 // Default value for USB tethering.
michael@0 88 const DEFAULT_USB_IP = "192.168.0.1";
michael@0 89 const DEFAULT_USB_PREFIX = "24";
michael@0 90 const DEFAULT_USB_DHCPSERVER_STARTIP = "192.168.0.10";
michael@0 91 const DEFAULT_USB_DHCPSERVER_ENDIP = "192.168.0.30";
michael@0 92
michael@0 93 const DEFAULT_DNS1 = "8.8.8.8";
michael@0 94 const DEFAULT_DNS2 = "8.8.4.4";
michael@0 95
michael@0 96 const DEFAULT_WIFI_DHCPSERVER_STARTIP = "192.168.1.10";
michael@0 97 const DEFAULT_WIFI_DHCPSERVER_ENDIP = "192.168.1.30";
michael@0 98
michael@0 99 const IPV4_ADDRESS_ANY = "0.0.0.0";
michael@0 100 const IPV6_ADDRESS_ANY = "::0";
michael@0 101
michael@0 102 const IPV4_MAX_PREFIX_LENGTH = 32;
michael@0 103 const IPV6_MAX_PREFIX_LENGTH = 128;
michael@0 104
michael@0 105 const PREF_DATA_DEFAULT_SERVICE_ID = "ril.data.defaultServiceId";
michael@0 106 const MOBILE_DUN_CONNECT_TIMEOUT = 30000;
michael@0 107 const MOBILE_DUN_RETRY_INTERVAL = 5000;
michael@0 108 const MOBILE_DUN_MAX_RETRIES = 5;
michael@0 109
michael@0 110 // Connection Type for Network Information API
michael@0 111 const CONNECTION_TYPE_CULLULAR = 0;
michael@0 112 const CONNECTION_TYPE_BLUETOOTH = 1;
michael@0 113 const CONNECTION_TYPE_ETHERNET = 2;
michael@0 114 const CONNECTION_TYPE_WIFI = 3;
michael@0 115 const CONNECTION_TYPE_OTHER = 4;
michael@0 116 const CONNECTION_TYPE_NONE = 5;
michael@0 117
michael@0 118 let DEBUG = false;
michael@0 119
michael@0 120 // Read debug setting from pref.
michael@0 121 try {
michael@0 122 let debugPref = Services.prefs.getBoolPref("network.debugging.enabled");
michael@0 123 DEBUG = DEBUG || debugPref;
michael@0 124 } catch (e) {}
michael@0 125
michael@0 126 function defineLazyRegExp(obj, name, pattern) {
michael@0 127 obj.__defineGetter__(name, function() {
michael@0 128 delete obj[name];
michael@0 129 return obj[name] = new RegExp(pattern);
michael@0 130 });
michael@0 131 }
michael@0 132
michael@0 133 /**
michael@0 134 * This component watches for network interfaces changing state and then
michael@0 135 * adjusts routes etc. accordingly.
michael@0 136 */
michael@0 137 function NetworkManager() {
michael@0 138 this.networkInterfaces = {};
michael@0 139 Services.obs.addObserver(this, TOPIC_INTERFACE_STATE_CHANGED, true);
michael@0 140 #ifdef MOZ_B2G_RIL
michael@0 141 Services.obs.addObserver(this, TOPIC_INTERFACE_REGISTERED, true);
michael@0 142 Services.obs.addObserver(this, TOPIC_INTERFACE_UNREGISTERED, true);
michael@0 143 #endif
michael@0 144 Services.obs.addObserver(this, TOPIC_XPCOM_SHUTDOWN, false);
michael@0 145 Services.obs.addObserver(this, TOPIC_MOZSETTINGS_CHANGED, false);
michael@0 146
michael@0 147 try {
michael@0 148 this._manageOfflineStatus =
michael@0 149 Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS);
michael@0 150 } catch(ex) {
michael@0 151 // Ignore.
michael@0 152 }
michael@0 153 Services.prefs.addObserver(PREF_MANAGE_OFFLINE_STATUS, this, false);
michael@0 154
michael@0 155 // Possible usb tethering interfaces for different gonk platform.
michael@0 156 this.possibleInterface = POSSIBLE_USB_INTERFACE_NAME.split(",");
michael@0 157
michael@0 158 // Default values for internal and external interfaces.
michael@0 159 this._tetheringInterface = Object.create(null);
michael@0 160 this._tetheringInterface[TETHERING_TYPE_USB] = {
michael@0 161 externalInterface: DEFAULT_3G_INTERFACE_NAME,
michael@0 162 internalInterface: DEFAULT_USB_INTERFACE_NAME
michael@0 163 };
michael@0 164 this._tetheringInterface[TETHERING_TYPE_WIFI] = {
michael@0 165 externalInterface: DEFAULT_3G_INTERFACE_NAME,
michael@0 166 internalInterface: DEFAULT_WIFI_INTERFACE_NAME
michael@0 167 };
michael@0 168
michael@0 169 this.initTetheringSettings();
michael@0 170
michael@0 171 let settingsLock = gSettingsService.createLock();
michael@0 172 // Read usb tethering data from settings DB.
michael@0 173 settingsLock.get(SETTINGS_USB_IP, this);
michael@0 174 settingsLock.get(SETTINGS_USB_PREFIX, this);
michael@0 175 settingsLock.get(SETTINGS_USB_DHCPSERVER_STARTIP, this);
michael@0 176 settingsLock.get(SETTINGS_USB_DHCPSERVER_ENDIP, this);
michael@0 177 settingsLock.get(SETTINGS_USB_DNS1, this);
michael@0 178 settingsLock.get(SETTINGS_USB_DNS2, this);
michael@0 179 settingsLock.get(SETTINGS_USB_ENABLED, this);
michael@0 180
michael@0 181 // Read wifi tethering data from settings DB.
michael@0 182 settingsLock.get(SETTINGS_WIFI_DHCPSERVER_STARTIP, this);
michael@0 183 settingsLock.get(SETTINGS_WIFI_DHCPSERVER_ENDIP, this);
michael@0 184
michael@0 185 this._usbTetheringSettingsToRead = [SETTINGS_USB_IP,
michael@0 186 SETTINGS_USB_PREFIX,
michael@0 187 SETTINGS_USB_DHCPSERVER_STARTIP,
michael@0 188 SETTINGS_USB_DHCPSERVER_ENDIP,
michael@0 189 SETTINGS_USB_DNS1,
michael@0 190 SETTINGS_USB_DNS2,
michael@0 191 SETTINGS_USB_ENABLED,
michael@0 192 SETTINGS_WIFI_DHCPSERVER_STARTIP,
michael@0 193 SETTINGS_WIFI_DHCPSERVER_ENDIP];
michael@0 194
michael@0 195 this.wantConnectionEvent = null;
michael@0 196 this.setAndConfigureActive();
michael@0 197
michael@0 198 ppmm.addMessageListener('NetworkInterfaceList:ListInterface', this);
michael@0 199
michael@0 200 // Used in resolveHostname().
michael@0 201 defineLazyRegExp(this, "REGEXP_IPV4", "^\\d{1,3}(?:\\.\\d{1,3}){3}$");
michael@0 202 defineLazyRegExp(this, "REGEXP_IPV6", "^[\\da-fA-F]{4}(?::[\\da-fA-F]{4}){7}$");
michael@0 203 }
michael@0 204 NetworkManager.prototype = {
michael@0 205 classID: NETWORKMANAGER_CID,
michael@0 206 classInfo: XPCOMUtils.generateCI({classID: NETWORKMANAGER_CID,
michael@0 207 contractID: NETWORKMANAGER_CONTRACTID,
michael@0 208 classDescription: "Network Manager",
michael@0 209 interfaces: [Ci.nsINetworkManager]}),
michael@0 210 QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkManager,
michael@0 211 Ci.nsISupportsWeakReference,
michael@0 212 Ci.nsIObserver,
michael@0 213 Ci.nsISettingsServiceCallback]),
michael@0 214
michael@0 215 // nsIObserver
michael@0 216
michael@0 217 observe: function(subject, topic, data) {
michael@0 218 switch (topic) {
michael@0 219 case TOPIC_INTERFACE_STATE_CHANGED:
michael@0 220 let network = subject.QueryInterface(Ci.nsINetworkInterface);
michael@0 221 debug("Network " + network.name + " changed state to " + network.state);
michael@0 222 switch (network.state) {
michael@0 223 case Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED:
michael@0 224 #ifdef MOZ_B2G_RIL
michael@0 225 // Add host route for data calls
michael@0 226 if (this.isNetworkTypeMobile(network.type)) {
michael@0 227 gNetworkService.removeHostRoutes(network.name);
michael@0 228 gNetworkService.addHostRoute(network);
michael@0 229 }
michael@0 230 // Add extra host route. For example, mms proxy or mmsc.
michael@0 231 this.setExtraHostRoute(network);
michael@0 232 // Dun type is a special case where we add the default route to a
michael@0 233 // secondary table.
michael@0 234 if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
michael@0 235 this.setSecondaryDefaultRoute(network);
michael@0 236 }
michael@0 237 #endif
michael@0 238 // Remove pre-created default route and let setAndConfigureActive()
michael@0 239 // to set default route only on preferred network
michael@0 240 gNetworkService.removeDefaultRoute(network);
michael@0 241 this.setAndConfigureActive();
michael@0 242 #ifdef MOZ_B2G_RIL
michael@0 243 // Update data connection when Wifi connected/disconnected
michael@0 244 if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
michael@0 245 for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
michael@0 246 this.mRil.getRadioInterface(i).updateRILNetworkInterface();
michael@0 247 }
michael@0 248 }
michael@0 249 #endif
michael@0 250
michael@0 251 this.onConnectionChanged(network);
michael@0 252
michael@0 253 // Probing the public network accessibility after routing table is ready
michael@0 254 CaptivePortalDetectionHelper
michael@0 255 .notify(CaptivePortalDetectionHelper.EVENT_CONNECT, this.active);
michael@0 256 break;
michael@0 257 case Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED:
michael@0 258 #ifdef MOZ_B2G_RIL
michael@0 259 // Remove host route for data calls
michael@0 260 if (this.isNetworkTypeMobile(network.type)) {
michael@0 261 gNetworkService.removeHostRoute(network);
michael@0 262 }
michael@0 263 // Remove extra host route. For example, mms proxy or mmsc.
michael@0 264 this.removeExtraHostRoute(network);
michael@0 265 // Remove secondary default route for dun.
michael@0 266 if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
michael@0 267 this.removeSecondaryDefaultRoute(network);
michael@0 268 }
michael@0 269 #endif
michael@0 270 // Remove routing table in /proc/net/route
michael@0 271 if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
michael@0 272 gNetworkService.resetRoutingTable(network);
michael@0 273 #ifdef MOZ_B2G_RIL
michael@0 274 } else if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
michael@0 275 gNetworkService.removeDefaultRoute(network);
michael@0 276 #endif
michael@0 277 }
michael@0 278
michael@0 279 // Abort ongoing captive portal detection on the wifi interface
michael@0 280 CaptivePortalDetectionHelper
michael@0 281 .notify(CaptivePortalDetectionHelper.EVENT_DISCONNECT, network);
michael@0 282 this.setAndConfigureActive();
michael@0 283 #ifdef MOZ_B2G_RIL
michael@0 284 // Update data connection when Wifi connected/disconnected
michael@0 285 if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
michael@0 286 for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
michael@0 287 this.mRil.getRadioInterface(i).updateRILNetworkInterface();
michael@0 288 }
michael@0 289 }
michael@0 290 #endif
michael@0 291 break;
michael@0 292 }
michael@0 293 #ifdef MOZ_B2G_RIL
michael@0 294 // Notify outer modules like MmsService to start the transaction after
michael@0 295 // the configuration of the network interface is done.
michael@0 296 Services.obs.notifyObservers(network, TOPIC_CONNECTION_STATE_CHANGED,
michael@0 297 this.convertConnectionType(network));
michael@0 298 #endif
michael@0 299 break;
michael@0 300 #ifdef MOZ_B2G_RIL
michael@0 301 case TOPIC_INTERFACE_REGISTERED:
michael@0 302 let regNetwork = subject.QueryInterface(Ci.nsINetworkInterface);
michael@0 303 // Add extra host route. For example, mms proxy or mmsc.
michael@0 304 this.setExtraHostRoute(regNetwork);
michael@0 305 // Dun type is a special case where we add the default route to a
michael@0 306 // secondary table.
michael@0 307 if (regNetwork.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
michael@0 308 this.setSecondaryDefaultRoute(regNetwork);
michael@0 309 }
michael@0 310 break;
michael@0 311 case TOPIC_INTERFACE_UNREGISTERED:
michael@0 312 let unregNetwork = subject.QueryInterface(Ci.nsINetworkInterface);
michael@0 313 // Remove extra host route. For example, mms proxy or mmsc.
michael@0 314 this.removeExtraHostRoute(unregNetwork);
michael@0 315 // Remove secondary default route for dun.
michael@0 316 if (unregNetwork.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
michael@0 317 this.removeSecondaryDefaultRoute(unregNetwork);
michael@0 318 }
michael@0 319 break;
michael@0 320 #endif
michael@0 321 case TOPIC_MOZSETTINGS_CHANGED:
michael@0 322 let setting = JSON.parse(data);
michael@0 323 this.handle(setting.key, setting.value);
michael@0 324 break;
michael@0 325 case TOPIC_PREF_CHANGED:
michael@0 326 this._manageOfflineStatus =
michael@0 327 Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS);
michael@0 328 debug(PREF_MANAGE_OFFLINE_STATUS + " has changed to " + this._manageOfflineStatus);
michael@0 329 break;
michael@0 330 case TOPIC_XPCOM_SHUTDOWN:
michael@0 331 Services.obs.removeObserver(this, TOPIC_XPCOM_SHUTDOWN);
michael@0 332 Services.obs.removeObserver(this, TOPIC_MOZSETTINGS_CHANGED);
michael@0 333 #ifdef MOZ_B2G_RIL
michael@0 334 Services.obs.removeObserver(this, TOPIC_INTERFACE_REGISTERED);
michael@0 335 Services.obs.removeObserver(this, TOPIC_INTERFACE_UNREGISTERED);
michael@0 336 #endif
michael@0 337 Services.obs.removeObserver(this, TOPIC_INTERFACE_STATE_CHANGED);
michael@0 338 #ifdef MOZ_B2G_RIL
michael@0 339 this.dunConnectTimer.cancel();
michael@0 340 this.dunRetryTimer.cancel();
michael@0 341 #endif
michael@0 342 break;
michael@0 343 }
michael@0 344 },
michael@0 345
michael@0 346 receiveMessage: function(aMsg) {
michael@0 347 switch (aMsg.name) {
michael@0 348 case "NetworkInterfaceList:ListInterface": {
michael@0 349 #ifdef MOZ_B2G_RIL
michael@0 350 let excludeMms = aMsg.json.excludeMms;
michael@0 351 let excludeSupl = aMsg.json.excludeSupl;
michael@0 352 let excludeIms = aMsg.json.excludeIms;
michael@0 353 let excludeDun = aMsg.json.excludeDun;
michael@0 354 #endif
michael@0 355 let interfaces = [];
michael@0 356
michael@0 357 for each (let i in this.networkInterfaces) {
michael@0 358 #ifdef MOZ_B2G_RIL
michael@0 359 if ((i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS && excludeMms) ||
michael@0 360 (i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL && excludeSupl) ||
michael@0 361 (i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_IMS && excludeIms) ||
michael@0 362 (i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN && excludeDun)) {
michael@0 363 continue;
michael@0 364 }
michael@0 365 #endif
michael@0 366 let ips = {};
michael@0 367 let prefixLengths = {};
michael@0 368 i.getAddresses(ips, prefixLengths);
michael@0 369
michael@0 370 interfaces.push({
michael@0 371 state: i.state,
michael@0 372 type: i.type,
michael@0 373 name: i.name,
michael@0 374 ips: ips.value,
michael@0 375 prefixLengths: prefixLengths.value,
michael@0 376 gateways: i.getGateways(),
michael@0 377 dnses: i.getDnses(),
michael@0 378 httpProxyHost: i.httpProxyHost,
michael@0 379 httpProxyPort: i.httpProxyPort
michael@0 380 });
michael@0 381 }
michael@0 382 return interfaces;
michael@0 383 }
michael@0 384 }
michael@0 385 },
michael@0 386
michael@0 387 // nsINetworkManager
michael@0 388
michael@0 389 registerNetworkInterface: function(network) {
michael@0 390 if (!(network instanceof Ci.nsINetworkInterface)) {
michael@0 391 throw Components.Exception("Argument must be nsINetworkInterface.",
michael@0 392 Cr.NS_ERROR_INVALID_ARG);
michael@0 393 }
michael@0 394 if (network.name in this.networkInterfaces) {
michael@0 395 throw Components.Exception("Network with that name already registered!",
michael@0 396 Cr.NS_ERROR_INVALID_ARG);
michael@0 397 }
michael@0 398 this.networkInterfaces[network.name] = network;
michael@0 399 #ifdef MOZ_B2G_RIL
michael@0 400 // Add host route for data calls
michael@0 401 if (this.isNetworkTypeMobile(network.type)) {
michael@0 402 gNetworkService.addHostRoute(network);
michael@0 403 }
michael@0 404 #endif
michael@0 405 // Remove pre-created default route and let setAndConfigureActive()
michael@0 406 // to set default route only on preferred network
michael@0 407 gNetworkService.removeDefaultRoute(network);
michael@0 408 this.setAndConfigureActive();
michael@0 409 Services.obs.notifyObservers(network, TOPIC_INTERFACE_REGISTERED, null);
michael@0 410 debug("Network '" + network.name + "' registered.");
michael@0 411 },
michael@0 412
michael@0 413 unregisterNetworkInterface: function(network) {
michael@0 414 if (!(network instanceof Ci.nsINetworkInterface)) {
michael@0 415 throw Components.Exception("Argument must be nsINetworkInterface.",
michael@0 416 Cr.NS_ERROR_INVALID_ARG);
michael@0 417 }
michael@0 418 if (!(network.name in this.networkInterfaces)) {
michael@0 419 throw Components.Exception("No network with that name registered.",
michael@0 420 Cr.NS_ERROR_INVALID_ARG);
michael@0 421 }
michael@0 422 delete this.networkInterfaces[network.name];
michael@0 423 #ifdef MOZ_B2G_RIL
michael@0 424 // Remove host route for data calls
michael@0 425 if (this.isNetworkTypeMobile(network.type)) {
michael@0 426 gNetworkService.removeHostRoute(network);
michael@0 427 }
michael@0 428 #endif
michael@0 429 this.setAndConfigureActive();
michael@0 430 Services.obs.notifyObservers(network, TOPIC_INTERFACE_UNREGISTERED, null);
michael@0 431 debug("Network '" + network.name + "' unregistered.");
michael@0 432 },
michael@0 433
michael@0 434 _manageOfflineStatus: true,
michael@0 435
michael@0 436 networkInterfaces: null,
michael@0 437
michael@0 438 _preferredNetworkType: DEFAULT_PREFERRED_NETWORK_TYPE,
michael@0 439 get preferredNetworkType() {
michael@0 440 return this._preferredNetworkType;
michael@0 441 },
michael@0 442 set preferredNetworkType(val) {
michael@0 443 #ifdef MOZ_B2G_RIL
michael@0 444 if ([Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
michael@0 445 Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE].indexOf(val) == -1) {
michael@0 446 #else
michael@0 447 if (val != Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
michael@0 448 #endif
michael@0 449 throw "Invalid network type";
michael@0 450 }
michael@0 451 this._preferredNetworkType = val;
michael@0 452 },
michael@0 453
michael@0 454 active: null,
michael@0 455 _overriddenActive: null,
michael@0 456
michael@0 457 overrideActive: function(network) {
michael@0 458 #ifdef MOZ_B2G_RIL
michael@0 459 if (this.isNetworkTypeSecondaryMobile(network.type)) {
michael@0 460 throw "Invalid network type";
michael@0 461 }
michael@0 462 #endif
michael@0 463 this._overriddenActive = network;
michael@0 464 this.setAndConfigureActive();
michael@0 465 },
michael@0 466
michael@0 467 #ifdef MOZ_B2G_RIL
michael@0 468 isNetworkTypeSecondaryMobile: function(type) {
michael@0 469 return (type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS ||
michael@0 470 type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL ||
michael@0 471 type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_IMS ||
michael@0 472 type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN);
michael@0 473 },
michael@0 474
michael@0 475 isNetworkTypeMobile: function(type) {
michael@0 476 return (type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE ||
michael@0 477 this.isNetworkTypeSecondaryMobile(type));
michael@0 478 },
michael@0 479
michael@0 480 setExtraHostRoute: function(network) {
michael@0 481 if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS) {
michael@0 482 if (!(network instanceof Ci.nsIRilNetworkInterface)) {
michael@0 483 debug("Network for MMS must be an instance of nsIRilNetworkInterface");
michael@0 484 return;
michael@0 485 }
michael@0 486
michael@0 487 network = network.QueryInterface(Ci.nsIRilNetworkInterface);
michael@0 488
michael@0 489 debug("Network '" + network.name + "' registered, " +
michael@0 490 "adding mmsproxy and/or mmsc route");
michael@0 491
michael@0 492 let hostToResolve = network.mmsProxy;
michael@0 493 // Workaround an xpconnect issue with undefined string objects.
michael@0 494 // See bug 808220
michael@0 495 if (!hostToResolve || hostToResolve === "undefined") {
michael@0 496 hostToResolve = network.mmsc;
michael@0 497 }
michael@0 498
michael@0 499 let mmsHosts = this.resolveHostname([hostToResolve]);
michael@0 500 if (mmsHosts.length == 0) {
michael@0 501 debug("No valid hostnames can be added. Stop adding host route.");
michael@0 502 return;
michael@0 503 }
michael@0 504
michael@0 505 gNetworkService.addHostRouteWithResolve(network, mmsHosts);
michael@0 506 }
michael@0 507 },
michael@0 508
michael@0 509 removeExtraHostRoute: function(network) {
michael@0 510 if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS) {
michael@0 511 if (!(network instanceof Ci.nsIRilNetworkInterface)) {
michael@0 512 debug("Network for MMS must be an instance of nsIRilNetworkInterface");
michael@0 513 return;
michael@0 514 }
michael@0 515
michael@0 516 network = network.QueryInterface(Ci.nsIRilNetworkInterface);
michael@0 517
michael@0 518 debug("Network '" + network.name + "' unregistered, " +
michael@0 519 "removing mmsproxy and/or mmsc route");
michael@0 520
michael@0 521 let hostToResolve = network.mmsProxy;
michael@0 522 // Workaround an xpconnect issue with undefined string objects.
michael@0 523 // See bug 808220
michael@0 524 if (!hostToResolve || hostToResolve === "undefined") {
michael@0 525 hostToResolve = network.mmsc;
michael@0 526 }
michael@0 527
michael@0 528 let mmsHosts = this.resolveHostname([hostToResolve]);
michael@0 529 if (mmsHosts.length == 0) {
michael@0 530 debug("No valid hostnames can be removed. Stop removing host route.");
michael@0 531 return;
michael@0 532 }
michael@0 533
michael@0 534 gNetworkService.removeHostRouteWithResolve(network, mmsHosts);
michael@0 535 }
michael@0 536 },
michael@0 537
michael@0 538 setSecondaryDefaultRoute: function(network) {
michael@0 539 let gateways = network.getGateways();
michael@0 540 for (let i = 0; i < gateways.length; i++) {
michael@0 541 let isIPv6 = (gateways[i].indexOf(":") != -1) ? true : false;
michael@0 542 // First, we need to add a host route to the gateway in the secondary
michael@0 543 // routing table to make the gateway reachable. Host route takes the max
michael@0 544 // prefix and gateway address 'any'.
michael@0 545 let route = {
michael@0 546 ip: gateways[i],
michael@0 547 prefix: isIPv6 ? IPV6_MAX_PREFIX_LENGTH : IPV4_MAX_PREFIX_LENGTH,
michael@0 548 gateway: isIPv6 ? IPV6_ADDRESS_ANY : IPV4_ADDRESS_ANY
michael@0 549 };
michael@0 550 gNetworkService.addSecondaryRoute(network.name, route);
michael@0 551 // Now we can add the default route through gateway. Default route takes the
michael@0 552 // min prefix and destination ip 'any'.
michael@0 553 route.ip = isIPv6 ? IPV6_ADDRESS_ANY : IPV4_ADDRESS_ANY;
michael@0 554 route.prefix = 0;
michael@0 555 route.gateway = gateways[i];
michael@0 556 gNetworkService.addSecondaryRoute(network.name, route);
michael@0 557 }
michael@0 558 },
michael@0 559
michael@0 560 removeSecondaryDefaultRoute: function(network) {
michael@0 561 let gateways = network.getGateways();
michael@0 562 for (let i = 0; i < gateways.length; i++) {
michael@0 563 let isIPv6 = (gateways[i].indexOf(":") != -1) ? true : false;
michael@0 564 // Remove both default route and host route.
michael@0 565 let route = {
michael@0 566 ip: isIPv6 ? IPV6_ADDRESS_ANY : IPV4_ADDRESS_ANY,
michael@0 567 prefix: 0,
michael@0 568 gateway: gateways[i]
michael@0 569 };
michael@0 570 gNetworkService.removeSecondaryRoute(network.name, route);
michael@0 571
michael@0 572 route.ip = gateways[i];
michael@0 573 route.prefix = isIPv6 ? IPV6_MAX_PREFIX_LENGTH : IPV4_MAX_PREFIX_LENGTH;
michael@0 574 route.gateway = isIPv6 ? IPV6_ADDRESS_ANY : IPV4_ADDRESS_ANY;
michael@0 575 gNetworkService.removeSecondaryRoute(network.name, route);
michael@0 576 }
michael@0 577 },
michael@0 578 #endif // MOZ_B2G_RIL
michael@0 579
michael@0 580 /**
michael@0 581 * Determine the active interface and configure it.
michael@0 582 */
michael@0 583 setAndConfigureActive: function() {
michael@0 584 debug("Evaluating whether active network needs to be changed.");
michael@0 585 let oldActive = this.active;
michael@0 586
michael@0 587 if (this._overriddenActive) {
michael@0 588 debug("We have an override for the active network: " +
michael@0 589 this._overriddenActive.name);
michael@0 590 // The override was just set, so reconfigure the network.
michael@0 591 if (this.active != this._overriddenActive) {
michael@0 592 this.active = this._overriddenActive;
michael@0 593 gNetworkService.setDefaultRouteAndDNS(this.active, oldActive);
michael@0 594 Services.obs.notifyObservers(this.active, TOPIC_ACTIVE_CHANGED, null);
michael@0 595 }
michael@0 596 return;
michael@0 597 }
michael@0 598
michael@0 599 // The active network is already our preferred type.
michael@0 600 if (this.active &&
michael@0 601 this.active.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED &&
michael@0 602 this.active.type == this._preferredNetworkType) {
michael@0 603 debug("Active network is already our preferred type.");
michael@0 604 gNetworkService.setDefaultRouteAndDNS(this.active, oldActive);
michael@0 605 return;
michael@0 606 }
michael@0 607
michael@0 608 // Find a suitable network interface to activate.
michael@0 609 this.active = null;
michael@0 610 #ifdef MOZ_B2G_RIL
michael@0 611 let defaultDataNetwork;
michael@0 612 #endif
michael@0 613 for each (let network in this.networkInterfaces) {
michael@0 614 if (network.state != Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) {
michael@0 615 continue;
michael@0 616 }
michael@0 617 #ifdef MOZ_B2G_RIL
michael@0 618 if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
michael@0 619 defaultDataNetwork = network;
michael@0 620 }
michael@0 621 #endif
michael@0 622 this.active = network;
michael@0 623 if (network.type == this.preferredNetworkType) {
michael@0 624 debug("Found our preferred type of network: " + network.name);
michael@0 625 break;
michael@0 626 }
michael@0 627 }
michael@0 628 if (this.active) {
michael@0 629 #ifdef MOZ_B2G_RIL
michael@0 630 // Give higher priority to default data APN than seconary APN.
michael@0 631 // If default data APN is not connected, we still set default route
michael@0 632 // and DNS on seconary APN.
michael@0 633 if (defaultDataNetwork &&
michael@0 634 this.isNetworkTypeSecondaryMobile(this.active.type) &&
michael@0 635 this.active.type != this.preferredNetworkType) {
michael@0 636 this.active = defaultDataNetwork;
michael@0 637 }
michael@0 638 // Don't set default route on secondary APN
michael@0 639 if (this.isNetworkTypeSecondaryMobile(this.active.type)) {
michael@0 640 gNetworkService.setDNS(this.active);
michael@0 641 } else {
michael@0 642 #endif // MOZ_B2G_RIL
michael@0 643 gNetworkService.setDefaultRouteAndDNS(this.active, oldActive);
michael@0 644 #ifdef MOZ_B2G_RIL
michael@0 645 }
michael@0 646 #endif
michael@0 647 }
michael@0 648
michael@0 649 if (this.active != oldActive) {
michael@0 650 Services.obs.notifyObservers(this.active, TOPIC_ACTIVE_CHANGED, null);
michael@0 651 }
michael@0 652
michael@0 653 if (this._manageOfflineStatus) {
michael@0 654 Services.io.offline = !this.active;
michael@0 655 }
michael@0 656 },
michael@0 657
michael@0 658 #ifdef MOZ_B2G_RIL
michael@0 659 resolveHostname: function(hosts) {
michael@0 660 let retval = [];
michael@0 661
michael@0 662 for (let hostname of hosts) {
michael@0 663 // Sanity check for null, undefined and empty string... etc.
michael@0 664 if (!hostname) {
michael@0 665 continue;
michael@0 666 }
michael@0 667
michael@0 668 try {
michael@0 669 let uri = Services.io.newURI(hostname, null, null);
michael@0 670 hostname = uri.host;
michael@0 671 } catch (e) {}
michael@0 672
michael@0 673 // An extra check for hostnames that cannot be made by newURI(...).
michael@0 674 // For example, an IP address like "10.1.1.1".
michael@0 675 if (hostname.match(this.REGEXP_IPV4) ||
michael@0 676 hostname.match(this.REGEXP_IPV6)) {
michael@0 677 retval.push(hostname);
michael@0 678 continue;
michael@0 679 }
michael@0 680
michael@0 681 try {
michael@0 682 let hostnameIps = gDNSService.resolve(hostname, 0);
michael@0 683 while (hostnameIps.hasMore()) {
michael@0 684 retval.push(hostnameIps.getNextAddrAsString());
michael@0 685 debug("Found IP at: " + JSON.stringify(retval));
michael@0 686 }
michael@0 687 } catch (e) {}
michael@0 688 }
michael@0 689
michael@0 690 return retval;
michael@0 691 },
michael@0 692 #endif
michael@0 693
michael@0 694 convertConnectionType: function(network) {
michael@0 695 // If there is internal interface change (e.g., MOBILE_MMS, MOBILE_SUPL),
michael@0 696 // the function will return null so that it won't trigger type change event
michael@0 697 // in NetworkInformation API.
michael@0 698 if (network.type != Ci.nsINetworkInterface.NETWORK_TYPE_WIFI &&
michael@0 699 network.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
michael@0 700 return null;
michael@0 701 }
michael@0 702
michael@0 703 if (network.state == Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED) {
michael@0 704 return CONNECTION_TYPE_NONE;
michael@0 705 }
michael@0 706
michael@0 707 switch (network.type) {
michael@0 708 case Ci.nsINetworkInterface.NETWORK_TYPE_WIFI:
michael@0 709 return CONNECTION_TYPE_WIFI;
michael@0 710 case Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE:
michael@0 711 return CONNECTION_TYPE_CULLULAR;
michael@0 712 }
michael@0 713 },
michael@0 714
michael@0 715 // nsISettingsServiceCallback
michael@0 716
michael@0 717 tetheringSettings: {},
michael@0 718
michael@0 719 initTetheringSettings: function() {
michael@0 720 this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
michael@0 721 this.tetheringSettings[SETTINGS_USB_IP] = DEFAULT_USB_IP;
michael@0 722 this.tetheringSettings[SETTINGS_USB_PREFIX] = DEFAULT_USB_PREFIX;
michael@0 723 this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP] = DEFAULT_USB_DHCPSERVER_STARTIP;
michael@0 724 this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP] = DEFAULT_USB_DHCPSERVER_ENDIP;
michael@0 725 this.tetheringSettings[SETTINGS_USB_DNS1] = DEFAULT_DNS1;
michael@0 726 this.tetheringSettings[SETTINGS_USB_DNS2] = DEFAULT_DNS2;
michael@0 727
michael@0 728 this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP] = DEFAULT_WIFI_DHCPSERVER_STARTIP;
michael@0 729 this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP] = DEFAULT_WIFI_DHCPSERVER_ENDIP;
michael@0 730
michael@0 731 #ifdef MOZ_B2G_RIL
michael@0 732 this.tetheringSettings[SETTINGS_DUN_REQUIRED] =
michael@0 733 libcutils.property_get("ro.tethering.dun_required") === "1";
michael@0 734 #endif
michael@0 735 },
michael@0 736
michael@0 737 _requestCount: 0,
michael@0 738
michael@0 739 handle: function(aName, aResult) {
michael@0 740 switch(aName) {
michael@0 741 case SETTINGS_USB_ENABLED:
michael@0 742 this._oldUsbTetheringEnabledState = this.tetheringSettings[SETTINGS_USB_ENABLED];
michael@0 743 case SETTINGS_USB_IP:
michael@0 744 case SETTINGS_USB_PREFIX:
michael@0 745 case SETTINGS_USB_DHCPSERVER_STARTIP:
michael@0 746 case SETTINGS_USB_DHCPSERVER_ENDIP:
michael@0 747 case SETTINGS_USB_DNS1:
michael@0 748 case SETTINGS_USB_DNS2:
michael@0 749 case SETTINGS_WIFI_DHCPSERVER_STARTIP:
michael@0 750 case SETTINGS_WIFI_DHCPSERVER_ENDIP:
michael@0 751 if (aResult !== null) {
michael@0 752 this.tetheringSettings[aName] = aResult;
michael@0 753 }
michael@0 754 debug("'" + aName + "'" + " is now " + this.tetheringSettings[aName]);
michael@0 755 let index = this._usbTetheringSettingsToRead.indexOf(aName);
michael@0 756
michael@0 757 if (index != -1) {
michael@0 758 this._usbTetheringSettingsToRead.splice(index, 1);
michael@0 759 }
michael@0 760
michael@0 761 if (this._usbTetheringSettingsToRead.length) {
michael@0 762 debug("We haven't read completely the usb Tethering data from settings db.");
michael@0 763 break;
michael@0 764 }
michael@0 765
michael@0 766 if (this._oldUsbTetheringEnabledState === this.tetheringSettings[SETTINGS_USB_ENABLED]) {
michael@0 767 debug("No changes for SETTINGS_USB_ENABLED flag. Nothing to do.");
michael@0 768 break;
michael@0 769 }
michael@0 770
michael@0 771 this._requestCount++;
michael@0 772 if (this._requestCount === 1) {
michael@0 773 this.handleUSBTetheringToggle(aResult);
michael@0 774 }
michael@0 775 break;
michael@0 776 };
michael@0 777 },
michael@0 778
michael@0 779 handleError: function(aErrorMessage) {
michael@0 780 debug("There was an error while reading Tethering settings.");
michael@0 781 this.tetheringSettings = {};
michael@0 782 this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
michael@0 783 },
michael@0 784
michael@0 785 getNetworkInterface: function(type) {
michael@0 786 for each (let network in this.networkInterfaces) {
michael@0 787 if (network.type == type) {
michael@0 788 return network;
michael@0 789 }
michael@0 790 }
michael@0 791 return null;
michael@0 792 },
michael@0 793
michael@0 794 _usbTetheringAction: TETHERING_STATE_IDLE,
michael@0 795
michael@0 796 _usbTetheringSettingsToRead: [],
michael@0 797
michael@0 798 _oldUsbTetheringEnabledState: null,
michael@0 799
michael@0 800 // External and internal interface name.
michael@0 801 _tetheringInterface: null,
michael@0 802
michael@0 803 handleLastRequest: function() {
michael@0 804 let count = this._requestCount;
michael@0 805 this._requestCount = 0;
michael@0 806
michael@0 807 if (count === 1) {
michael@0 808 if (this.wantConnectionEvent) {
michael@0 809 if (this.tetheringSettings[SETTINGS_USB_ENABLED]) {
michael@0 810 this.wantConnectionEvent.call(this);
michael@0 811 }
michael@0 812 this.wantConnectionEvent = null;
michael@0 813 }
michael@0 814 return;
michael@0 815 }
michael@0 816
michael@0 817 if (count > 1) {
michael@0 818 this.handleUSBTetheringToggle(this.tetheringSettings[SETTINGS_USB_ENABLED]);
michael@0 819 this.wantConnectionEvent = null;
michael@0 820 }
michael@0 821 },
michael@0 822
michael@0 823 #ifdef MOZ_B2G_RIL
michael@0 824 dunConnectTimer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
michael@0 825 /**
michael@0 826 * Callback when dun connection fails to connect within timeout.
michael@0 827 */
michael@0 828 onDunConnectTimerTimeout: function() {
michael@0 829 while (this._pendingTetheringRequests.length > 0) {
michael@0 830 debug("onDunConnectTimerTimeout: callback without network info.");
michael@0 831 let callback = this._pendingTetheringRequests.shift();
michael@0 832 if (typeof callback === 'function') {
michael@0 833 callback();
michael@0 834 }
michael@0 835 }
michael@0 836 },
michael@0 837
michael@0 838 dunRetryTimes: 0,
michael@0 839 dunRetryTimer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
michael@0 840 setupDunConnection: function() {
michael@0 841 this.dunRetryTimer.cancel();
michael@0 842 let ril = this.mRil.getRadioInterface(this.gDataDefaultServiceId);
michael@0 843
michael@0 844 if (ril.rilContext && ril.rilContext.data &&
michael@0 845 ril.rilContext.data.state === "registered") {
michael@0 846 this.dunRetryTimes = 0;
michael@0 847 ril.setupDataCallByType("dun");
michael@0 848 this.dunConnectTimer.cancel();
michael@0 849 this.dunConnectTimer.
michael@0 850 initWithCallback(this.onDunConnectTimerTimeout.bind(this),
michael@0 851 MOBILE_DUN_CONNECT_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT);
michael@0 852 return;
michael@0 853 }
michael@0 854
michael@0 855 if (this.dunRetryTimes++ >= this.MOBILE_DUN_MAX_RETRIES) {
michael@0 856 debug("setupDunConnection: max retries reached.");
michael@0 857 this.dunRetryTimes = 0;
michael@0 858 // same as dun connect timeout.
michael@0 859 this.onDunConnectTimerTimeout();
michael@0 860 return;
michael@0 861 }
michael@0 862
michael@0 863 debug("Data not ready, retry dun after " + MOBILE_DUN_RETRY_INTERVAL + " ms.");
michael@0 864 this.dunRetryTimer.
michael@0 865 initWithCallback(this.setupDunConnection.bind(this),
michael@0 866 MOBILE_DUN_RETRY_INTERVAL, Ci.nsITimer.TYPE_ONE_SHOT);
michael@0 867 },
michael@0 868
michael@0 869 _pendingTetheringRequests: [],
michael@0 870 _dunActiveUsers: 0,
michael@0 871 handleDunConnection: function(enable, callback) {
michael@0 872 debug("handleDunConnection: " + enable);
michael@0 873 let dun = this.getNetworkInterface(
michael@0 874 Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN);
michael@0 875
michael@0 876 if (!enable) {
michael@0 877 this._dunActiveUsers--;
michael@0 878 if (this._dunActiveUsers > 0) {
michael@0 879 debug("Dun still needed by others, do not disconnect.")
michael@0 880 return;
michael@0 881 }
michael@0 882
michael@0 883 this.dunRetryTimes = 0;
michael@0 884 this.dunRetryTimer.cancel();
michael@0 885 this.dunConnectTimer.cancel();
michael@0 886 this._pendingTetheringRequests = [];
michael@0 887
michael@0 888 if (dun && (dun.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED)) {
michael@0 889 this.mRil.getRadioInterface(this.gDataDefaultServiceId)
michael@0 890 .deactivateDataCallByType("dun");
michael@0 891 }
michael@0 892 return;
michael@0 893 }
michael@0 894
michael@0 895 this._dunActiveUsers++;
michael@0 896 if (!dun || (dun.state != Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED)) {
michael@0 897 debug("DUN data call inactive, setup dun data call!")
michael@0 898 this._pendingTetheringRequests.push(callback);
michael@0 899 this.dunRetryTimes = 0;
michael@0 900 this.setupDunConnection();
michael@0 901
michael@0 902 return;
michael@0 903 }
michael@0 904 this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = dun.name;
michael@0 905 callback(dun);
michael@0 906 },
michael@0 907 #endif
michael@0 908
michael@0 909 handleUSBTetheringToggle: function(enable) {
michael@0 910 debug("handleUSBTetheringToggle: " + enable);
michael@0 911 if (enable &&
michael@0 912 (this._usbTetheringAction === TETHERING_STATE_ONGOING ||
michael@0 913 this._usbTetheringAction === TETHERING_STATE_ACTIVE)) {
michael@0 914 debug("Usb tethering already connecting/connected.");
michael@0 915 return;
michael@0 916 }
michael@0 917
michael@0 918 if (!enable &&
michael@0 919 this._usbTetheringAction === TETHERING_STATE_IDLE) {
michael@0 920 debug("Usb tethering already disconnected.");
michael@0 921 return;
michael@0 922 }
michael@0 923
michael@0 924 if (!enable) {
michael@0 925 this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
michael@0 926 gNetworkService.enableUsbRndis(false, this.enableUsbRndisResult.bind(this));
michael@0 927 return;
michael@0 928 }
michael@0 929
michael@0 930 this.tetheringSettings[SETTINGS_USB_ENABLED] = true;
michael@0 931 this._usbTetheringAction = TETHERING_STATE_ONGOING;
michael@0 932
michael@0 933 #ifdef MOZ_B2G_RIL
michael@0 934 if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
michael@0 935 this.handleDunConnection(true, function(network) {
michael@0 936 if (!network){
michael@0 937 this.usbTetheringResultReport("Dun connection failed");
michael@0 938 return;
michael@0 939 }
michael@0 940 this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = network.name;
michael@0 941 gNetworkService.enableUsbRndis(true, this.enableUsbRndisResult.bind(this));
michael@0 942 }.bind(this));
michael@0 943 return;
michael@0 944 }
michael@0 945 #endif
michael@0 946
michael@0 947 if (this.active) {
michael@0 948 this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = this.active.name;
michael@0 949 } else {
michael@0 950 let mobile = this.getNetworkInterface(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE);
michael@0 951 if (mobile) {
michael@0 952 this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = mobile.name;
michael@0 953 }
michael@0 954 }
michael@0 955 gNetworkService.enableUsbRndis(true, this.enableUsbRndisResult.bind(this));
michael@0 956 },
michael@0 957
michael@0 958 getUSBTetheringParameters: function(enable, tetheringinterface) {
michael@0 959 let interfaceIp;
michael@0 960 let prefix;
michael@0 961 let wifiDhcpStartIp;
michael@0 962 let wifiDhcpEndIp;
michael@0 963 let usbDhcpStartIp;
michael@0 964 let usbDhcpEndIp;
michael@0 965 let dns1;
michael@0 966 let dns2;
michael@0 967 let internalInterface = tetheringinterface.internalInterface;
michael@0 968 let externalInterface = tetheringinterface.externalInterface;
michael@0 969
michael@0 970 interfaceIp = this.tetheringSettings[SETTINGS_USB_IP];
michael@0 971 prefix = this.tetheringSettings[SETTINGS_USB_PREFIX];
michael@0 972 wifiDhcpStartIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP];
michael@0 973 wifiDhcpEndIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP];
michael@0 974 usbDhcpStartIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP];
michael@0 975 usbDhcpEndIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP];
michael@0 976 dns1 = this.tetheringSettings[SETTINGS_USB_DNS1];
michael@0 977 dns2 = this.tetheringSettings[SETTINGS_USB_DNS2];
michael@0 978
michael@0 979 // Using the default values here until application support these settings.
michael@0 980 if (interfaceIp == "" || prefix == "" ||
michael@0 981 wifiDhcpStartIp == "" || wifiDhcpEndIp == "" ||
michael@0 982 usbDhcpStartIp == "" || usbDhcpEndIp == "") {
michael@0 983 debug("Invalid subnet information.");
michael@0 984 return null;
michael@0 985 }
michael@0 986
michael@0 987 return {
michael@0 988 ifname: internalInterface,
michael@0 989 ip: interfaceIp,
michael@0 990 prefix: prefix,
michael@0 991 wifiStartIp: wifiDhcpStartIp,
michael@0 992 wifiEndIp: wifiDhcpEndIp,
michael@0 993 usbStartIp: usbDhcpStartIp,
michael@0 994 usbEndIp: usbDhcpEndIp,
michael@0 995 dns1: dns1,
michael@0 996 dns2: dns2,
michael@0 997 internalIfname: internalInterface,
michael@0 998 externalIfname: externalInterface,
michael@0 999 enable: enable,
michael@0 1000 link: enable ? NETWORK_INTERFACE_UP : NETWORK_INTERFACE_DOWN
michael@0 1001 };
michael@0 1002 },
michael@0 1003
michael@0 1004 notifyError: function(resetSettings, callback, msg) {
michael@0 1005 if (resetSettings) {
michael@0 1006 let settingsLock = gSettingsService.createLock();
michael@0 1007 // Disable wifi tethering with a useful error message for the user.
michael@0 1008 settingsLock.set("tethering.wifi.enabled", false, null, msg);
michael@0 1009 }
michael@0 1010
michael@0 1011 debug("setWifiTethering: " + (msg ? msg : "success"));
michael@0 1012
michael@0 1013 if (callback) {
michael@0 1014 callback.wifiTetheringEnabledChange(msg);
michael@0 1015 }
michael@0 1016 },
michael@0 1017
michael@0 1018 enableWifiTethering: function(enable, config, callback) {
michael@0 1019 // Fill in config's required fields.
michael@0 1020 config.ifname = this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface;
michael@0 1021 config.internalIfname = this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface;
michael@0 1022 config.externalIfname = this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface;
michael@0 1023
michael@0 1024 gNetworkService.setWifiTethering(enable, config, (function(error) {
michael@0 1025 #ifdef MOZ_B2G_RIL
michael@0 1026 // Disconnect dun on error or when wifi tethering is disabled.
michael@0 1027 if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] &&
michael@0 1028 (!enable || error)) {
michael@0 1029 this.handleDunConnection(false);
michael@0 1030 }
michael@0 1031 #endif
michael@0 1032 let resetSettings = error;
michael@0 1033 this.notifyError(resetSettings, callback, error);
michael@0 1034 }).bind(this));
michael@0 1035 },
michael@0 1036
michael@0 1037 // Enable/disable WiFi tethering by sending commands to netd.
michael@0 1038 setWifiTethering: function(enable, network, config, callback) {
michael@0 1039 debug("setWifiTethering: " + enable);
michael@0 1040 if (!network) {
michael@0 1041 this.notifyError(true, callback, "invalid network information");
michael@0 1042 return;
michael@0 1043 }
michael@0 1044
michael@0 1045 if (!config) {
michael@0 1046 this.notifyError(true, callback, "invalid configuration");
michael@0 1047 return;
michael@0 1048 }
michael@0 1049
michael@0 1050 if (!enable) {
michael@0 1051 this.enableWifiTethering(false, config, callback);
michael@0 1052 return;
michael@0 1053 }
michael@0 1054
michael@0 1055 this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface = network.name;
michael@0 1056
michael@0 1057 #ifdef MOZ_B2G_RIL
michael@0 1058 if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
michael@0 1059 this.handleDunConnection(true, function(config, callback, network) {
michael@0 1060 if (!network) {
michael@0 1061 this.notifyError(true, callback, "Dun connection failed");
michael@0 1062 return;
michael@0 1063 }
michael@0 1064 this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface = network.name;
michael@0 1065 this.enableWifiTethering(true, config, callback);
michael@0 1066 }.bind(this, config, callback));
michael@0 1067 return;
michael@0 1068 }
michael@0 1069 #endif
michael@0 1070
michael@0 1071 let mobile = this.getNetworkInterface(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE);
michael@0 1072 // Update the real interface name
michael@0 1073 if (mobile) {
michael@0 1074 this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface = mobile.name;
michael@0 1075 }
michael@0 1076
michael@0 1077 this.enableWifiTethering(true, config, callback);
michael@0 1078 },
michael@0 1079
michael@0 1080 // Enable/disable USB tethering by sending commands to netd.
michael@0 1081 setUSBTethering: function(enable, tetheringInterface, callback) {
michael@0 1082 let params = this.getUSBTetheringParameters(enable, tetheringInterface);
michael@0 1083
michael@0 1084 if (params === null) {
michael@0 1085 gNetworkService.enableUsbRndis(false, function() {
michael@0 1086 this.usbTetheringResultReport("Invalid parameters");
michael@0 1087 });
michael@0 1088 return;
michael@0 1089 }
michael@0 1090
michael@0 1091 gNetworkService.setUSBTethering(enable, params, callback);
michael@0 1092 },
michael@0 1093
michael@0 1094 getUsbInterface: function() {
michael@0 1095 // Find the rndis interface.
michael@0 1096 for (let i = 0; i < this.possibleInterface.length; i++) {
michael@0 1097 try {
michael@0 1098 let file = new FileUtils.File(KERNEL_NETWORK_ENTRY + "/" +
michael@0 1099 this.possibleInterface[i]);
michael@0 1100 if (file.exists()) {
michael@0 1101 return this.possibleInterface[i];
michael@0 1102 }
michael@0 1103 } catch (e) {
michael@0 1104 debug("Not " + this.possibleInterface[i] + " interface.");
michael@0 1105 }
michael@0 1106 }
michael@0 1107 debug("Can't find rndis interface in possible lists.");
michael@0 1108 return DEFAULT_USB_INTERFACE_NAME;
michael@0 1109 },
michael@0 1110
michael@0 1111 enableUsbRndisResult: function(success, enable) {
michael@0 1112 if (success) {
michael@0 1113 // If enable is false, don't find usb interface cause it is already down,
michael@0 1114 // just use the internal interface in settings.
michael@0 1115 if (enable) {
michael@0 1116 this._tetheringInterface[TETHERING_TYPE_USB].internalInterface = this.getUsbInterface();
michael@0 1117 }
michael@0 1118 this.setUSBTethering(enable,
michael@0 1119 this._tetheringInterface[TETHERING_TYPE_USB],
michael@0 1120 this.usbTetheringResultReport.bind(this));
michael@0 1121 } else {
michael@0 1122 this.usbTetheringResultReport("Failed to set usb function");
michael@0 1123 throw new Error("failed to set USB Function to adb");
michael@0 1124 }
michael@0 1125 },
michael@0 1126
michael@0 1127 usbTetheringResultReport: function(error) {
michael@0 1128 let settingsLock = gSettingsService.createLock();
michael@0 1129
michael@0 1130 // Disable tethering settings when fail to enable it.
michael@0 1131 if (error) {
michael@0 1132 this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
michael@0 1133 settingsLock.set("tethering.usb.enabled", false, null);
michael@0 1134 // Skip others request when we found an error.
michael@0 1135 this._requestCount = 0;
michael@0 1136 this._usbTetheringAction = TETHERING_STATE_IDLE;
michael@0 1137 #ifdef MOZ_B2G_RIL
michael@0 1138 if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
michael@0 1139 this.handleDunConnection(false);
michael@0 1140 }
michael@0 1141 #endif
michael@0 1142 } else {
michael@0 1143 if (this.tetheringSettings[SETTINGS_USB_ENABLED]) {
michael@0 1144 this._usbTetheringAction = TETHERING_STATE_ACTIVE;
michael@0 1145 } else {
michael@0 1146 this._usbTetheringAction = TETHERING_STATE_IDLE;
michael@0 1147 #ifdef MOZ_B2G_RIL
michael@0 1148 if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
michael@0 1149 this.handleDunConnection(false);
michael@0 1150 }
michael@0 1151 #endif
michael@0 1152 }
michael@0 1153 this.handleLastRequest();
michael@0 1154 }
michael@0 1155 },
michael@0 1156
michael@0 1157 onConnectionChangedReport: function(success, externalIfname) {
michael@0 1158 debug("onConnectionChangedReport result: success " + success);
michael@0 1159
michael@0 1160 if (success) {
michael@0 1161 // Update the external interface.
michael@0 1162 this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = externalIfname;
michael@0 1163 debug("Change the interface name to " + externalIfname);
michael@0 1164 }
michael@0 1165 },
michael@0 1166
michael@0 1167 onConnectionChanged: function(network) {
michael@0 1168 if (network.state != Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) {
michael@0 1169 debug("We are only interested in CONNECTED event");
michael@0 1170 return;
michael@0 1171 }
michael@0 1172
michael@0 1173 #ifdef MOZ_B2G_RIL
michael@0 1174 // We can not use network.type only to check if it's dun, cause if it is
michael@0 1175 // shared with default, the returned type would always be default, see bug
michael@0 1176 // 939046. In most cases, if dun is required, it should not be shared with
michael@0 1177 // default.
michael@0 1178 if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] &&
michael@0 1179 (network.type === Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN ||
michael@0 1180 this.mRil.getRadioInterface(this.gDataDefaultServiceId)
michael@0 1181 .getDataCallStateByType("dun") ===
michael@0 1182 Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED)) {
michael@0 1183 this.dunConnectTimer.cancel();
michael@0 1184 debug("DUN data call connected, process callbacks.");
michael@0 1185 while (this._pendingTetheringRequests.length > 0) {
michael@0 1186 let callback = this._pendingTetheringRequests.shift();
michael@0 1187 if (typeof callback === 'function') {
michael@0 1188 callback(network);
michael@0 1189 }
michael@0 1190 }
michael@0 1191 return;
michael@0 1192 }
michael@0 1193 #endif
michael@0 1194
michael@0 1195 if (!this.tetheringSettings[SETTINGS_USB_ENABLED]) {
michael@0 1196 debug("Usb tethering settings is not enabled");
michael@0 1197 return;
michael@0 1198 }
michael@0 1199
michael@0 1200 #ifdef MOZ_B2G_RIL
michael@0 1201 if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] &&
michael@0 1202 network.type === Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN &&
michael@0 1203 this._tetheringInterface[TETHERING_TYPE_USB].externalInterface ===
michael@0 1204 network.name) {
michael@0 1205 debug("Dun required and dun interface is the same");
michael@0 1206 return;
michael@0 1207 }
michael@0 1208 #endif
michael@0 1209
michael@0 1210 if (this._tetheringInterface[TETHERING_TYPE_USB].externalInterface ===
michael@0 1211 this.active.name) {
michael@0 1212 debug("The active interface is the same");
michael@0 1213 return;
michael@0 1214 }
michael@0 1215
michael@0 1216 let previous = {
michael@0 1217 internalIfname: this._tetheringInterface[TETHERING_TYPE_USB].internalInterface,
michael@0 1218 externalIfname: this._tetheringInterface[TETHERING_TYPE_USB].externalInterface
michael@0 1219 };
michael@0 1220
michael@0 1221 let current = {
michael@0 1222 internalIfname: this._tetheringInterface[TETHERING_TYPE_USB].internalInterface,
michael@0 1223 externalIfname: network.name
michael@0 1224 };
michael@0 1225
michael@0 1226 let callback = (function() {
michael@0 1227 // Update external network interface.
michael@0 1228 debug("Update upstream interface to " + network.name);
michael@0 1229 gNetworkService.updateUpStream(previous, current, this.onConnectionChangedReport.bind(this));
michael@0 1230 }).bind(this);
michael@0 1231
michael@0 1232 if (this._usbTetheringAction === TETHERING_STATE_ONGOING) {
michael@0 1233 debug("Postpone the event and handle it when state is idle.");
michael@0 1234 this.wantConnectionEvent = callback;
michael@0 1235 return;
michael@0 1236 }
michael@0 1237 this.wantConnectionEvent = null;
michael@0 1238
michael@0 1239 callback.call(this);
michael@0 1240 }
michael@0 1241 };
michael@0 1242
michael@0 1243 let CaptivePortalDetectionHelper = (function() {
michael@0 1244
michael@0 1245 const EVENT_CONNECT = "Connect";
michael@0 1246 const EVENT_DISCONNECT = "Disconnect";
michael@0 1247 let _ongoingInterface = null;
michael@0 1248 let _available = ("nsICaptivePortalDetector" in Ci);
michael@0 1249 let getService = function() {
michael@0 1250 return Cc['@mozilla.org/toolkit/captive-detector;1']
michael@0 1251 .getService(Ci.nsICaptivePortalDetector);
michael@0 1252 };
michael@0 1253
michael@0 1254 let _performDetection = function(interfaceName, callback) {
michael@0 1255 let capService = getService();
michael@0 1256 let capCallback = {
michael@0 1257 QueryInterface: XPCOMUtils.generateQI([Ci.nsICaptivePortalCallback]),
michael@0 1258 prepare: function() {
michael@0 1259 capService.finishPreparation(interfaceName);
michael@0 1260 },
michael@0 1261 complete: function(success) {
michael@0 1262 _ongoingInterface = null;
michael@0 1263 callback(success);
michael@0 1264 }
michael@0 1265 };
michael@0 1266
michael@0 1267 // Abort any unfinished captive portal detection.
michael@0 1268 if (_ongoingInterface != null) {
michael@0 1269 capService.abort(_ongoingInterface);
michael@0 1270 _ongoingInterface = null;
michael@0 1271 }
michael@0 1272 try {
michael@0 1273 capService.checkCaptivePortal(interfaceName, capCallback);
michael@0 1274 _ongoingInterface = interfaceName;
michael@0 1275 } catch (e) {
michael@0 1276 debug('Fail to detect captive portal due to: ' + e.message);
michael@0 1277 }
michael@0 1278 };
michael@0 1279
michael@0 1280 let _abort = function(interfaceName) {
michael@0 1281 if (_ongoingInterface !== interfaceName) {
michael@0 1282 return;
michael@0 1283 }
michael@0 1284
michael@0 1285 let capService = getService();
michael@0 1286 capService.abort(_ongoingInterface);
michael@0 1287 _ongoingInterface = null;
michael@0 1288 };
michael@0 1289
michael@0 1290 return {
michael@0 1291 EVENT_CONNECT: EVENT_CONNECT,
michael@0 1292 EVENT_DISCONNECT: EVENT_DISCONNECT,
michael@0 1293 notify: function(eventType, network) {
michael@0 1294 switch (eventType) {
michael@0 1295 case EVENT_CONNECT:
michael@0 1296 // perform captive portal detection on wifi interface
michael@0 1297 if (_available && network &&
michael@0 1298 network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
michael@0 1299 _performDetection(network.name, function() {
michael@0 1300 // TODO: bug 837600
michael@0 1301 // We can disconnect wifi in here if user abort the login procedure.
michael@0 1302 });
michael@0 1303 }
michael@0 1304
michael@0 1305 break;
michael@0 1306 case EVENT_DISCONNECT:
michael@0 1307 if (_available &&
michael@0 1308 network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
michael@0 1309 _abort(network.name);
michael@0 1310 }
michael@0 1311 break;
michael@0 1312 }
michael@0 1313 }
michael@0 1314 };
michael@0 1315 }());
michael@0 1316
michael@0 1317 #ifdef MOZ_B2G_RIL
michael@0 1318 XPCOMUtils.defineLazyServiceGetter(NetworkManager.prototype, "mRil",
michael@0 1319 "@mozilla.org/ril;1",
michael@0 1320 "nsIRadioInterfaceLayer");
michael@0 1321
michael@0 1322 XPCOMUtils.defineLazyGetter(NetworkManager.prototype,
michael@0 1323 "gDataDefaultServiceId", function() {
michael@0 1324 try {
michael@0 1325 return Services.prefs.getIntPref(PREF_DATA_DEFAULT_SERVICE_ID);
michael@0 1326 } catch(e) {}
michael@0 1327
michael@0 1328 return 0;
michael@0 1329 });
michael@0 1330 #endif
michael@0 1331
michael@0 1332 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkManager]);
michael@0 1333
michael@0 1334
michael@0 1335 let debug;
michael@0 1336 if (DEBUG) {
michael@0 1337 debug = function(s) {
michael@0 1338 dump("-*- NetworkManager: " + s + "\n");
michael@0 1339 };
michael@0 1340 } else {
michael@0 1341 debug = function(s) {};
michael@0 1342 }

mercurial