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.

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

mercurial