dom/system/gonk/NetworkService.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/NetUtil.jsm");
    12 Cu.import("resource://gre/modules/FileUtils.jsm");
    14 const NETWORKSERVICE_CONTRACTID = "@mozilla.org/network/service;1";
    15 const NETWORKSERVICE_CID = Components.ID("{baec696c-c78d-42db-8b44-603f8fbfafb4}");
    17 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkWorker",
    18                                    "@mozilla.org/network/worker;1",
    19                                    "nsINetworkWorker");
    21 // 1xx - Requested action is proceeding
    22 const NETD_COMMAND_PROCEEDING   = 100;
    23 // 2xx - Requested action has been successfully completed
    24 const NETD_COMMAND_OKAY         = 200;
    25 // 4xx - The command is accepted but the requested action didn't
    26 // take place.
    27 const NETD_COMMAND_FAIL         = 400;
    28 // 5xx - The command syntax or parameters error
    29 const NETD_COMMAND_ERROR        = 500;
    30 // 6xx - Unsolicited broadcasts
    31 const NETD_COMMAND_UNSOLICITED  = 600;
    33 const WIFI_CTRL_INTERFACE = "wl0.1";
    35 const MANUAL_PROXY_CONFIGURATION = 1;
    37 let DEBUG = false;
    39 // Read debug setting from pref.
    40 try {
    41   let debugPref = Services.prefs.getBoolPref("network.debugging.enabled");
    42   DEBUG = DEBUG || debugPref;
    43 } catch (e) {}
    45 function netdResponseType(code) {
    46   return Math.floor(code / 100) * 100;
    47 }
    49 function isError(code) {
    50   let type = netdResponseType(code);
    51   return (type !== NETD_COMMAND_PROCEEDING && type !== NETD_COMMAND_OKAY);
    52 }
    54 function debug(msg) {
    55   dump("-*- NetworkService: " + msg + "\n");
    56 }
    58 /**
    59  * This component watches for network interfaces changing state and then
    60  * adjusts routes etc. accordingly.
    61  */
    62 function NetworkService() {
    63   if(DEBUG) debug("Starting net_worker.");
    65   let self = this;
    67   if (gNetworkWorker) {
    68     let networkListener = {
    69       onEvent: function(event) {
    70         self.handleWorkerMessage(event);
    71       }
    72     };
    73     gNetworkWorker.start(networkListener);
    74   }
    75   // Callbacks to invoke when a reply arrives from the net_worker.
    76   this.controlCallbacks = Object.create(null);
    78   this.shutdown = false;
    79   Services.obs.addObserver(this, "xpcom-shutdown", false);
    80 }
    82 NetworkService.prototype = {
    83   classID:   NETWORKSERVICE_CID,
    84   classInfo: XPCOMUtils.generateCI({classID: NETWORKSERVICE_CID,
    85                                     contractID: NETWORKSERVICE_CONTRACTID,
    86                                     classDescription: "Network Service",
    87                                     interfaces: [Ci.nsINetworkService]}),
    88   QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkService]),
    90   // Helpers
    92   idgen: 0,
    93   controlMessage: function(params, callback) {
    94     if (this.shutdown) {
    95       return;
    96     }
    98     if (callback) {
    99       let id = this.idgen++;
   100       params.id = id;
   101       this.controlCallbacks[id] = callback;
   102     }
   103     if (gNetworkWorker) {
   104       gNetworkWorker.postMessage(params);
   105     }
   106   },
   108   handleWorkerMessage: function(response) {
   109     if(DEBUG) debug("NetworkManager received message from worker: " + JSON.stringify(response));
   110     let id = response.id;
   111     if (response.broadcast === true) {
   112       Services.obs.notifyObservers(null, response.topic, response.reason);
   113       return;
   114     }
   115     let callback = this.controlCallbacks[id];
   116     if (callback) {
   117       callback.call(this, response);
   118       delete this.controlCallbacks[id];
   119     }
   120   },
   122   // nsINetworkService
   124   getNetworkInterfaceStats: function(networkName, callback) {
   125     if(DEBUG) debug("getNetworkInterfaceStats for " + networkName);
   127     if (this.shutdown) {
   128       return;
   129     }
   131     let file = new FileUtils.File("/proc/net/dev");
   132     if (!file) {
   133       callback.networkStatsAvailable(false, -1, -1, new Date());
   134       return;
   135     }
   137     NetUtil.asyncFetch(file, function(inputStream, status) {
   138       let result = {
   139         success: true,  // netd always return success even interface doesn't exist.
   140         rxBytes: 0,
   141         txBytes: 0
   142       };
   143       result.date = new Date();
   145       if (Components.isSuccessCode(status)) {
   146         // Find record for corresponding interface.
   147         let statExpr = /(\S+): +(\d+) +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +(\d+) +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+/;
   148         let data = NetUtil.readInputStreamToString(inputStream,
   149                     inputStream.available()).split("\n");
   150         for (let i = 2; i < data.length; i++) {
   151           let parseResult = statExpr.exec(data[i]);
   152           if (parseResult && parseResult[1] === networkName) {
   153             result.rxBytes = parseInt(parseResult[2], 10);
   154             result.txBytes = parseInt(parseResult[3], 10);
   155             break;
   156           }
   157         }
   158       }
   160       callback.networkStatsAvailable(result.success, result.rxBytes,
   161                                      result.txBytes, result.date);
   162     });
   163   },
   165   setNetworkInterfaceAlarm: function(networkName, threshold, callback) {
   166     if (!networkName) {
   167       callback.networkUsageAlarmResult(-1);
   168       return;
   169     }
   171     let self = this;
   172     this._disableNetworkInterfaceAlarm(networkName, function(result) {
   173       if (threshold < 0) {
   174         if (!isError(result.resultCode)) {
   175           callback.networkUsageAlarmResult(null);
   176           return;
   177         }
   178         callback.networkUsageAlarmResult(result.reason);
   179         return
   180       }
   182       self._setNetworkInterfaceAlarm(networkName, threshold, callback);
   183     });
   184   },
   186   _setNetworkInterfaceAlarm: function(networkName, threshold, callback) {
   187     if(DEBUG) debug("setNetworkInterfaceAlarm for " + networkName + " at " + threshold + "bytes");
   189     let params = {
   190       cmd: "setNetworkInterfaceAlarm",
   191       ifname: networkName,
   192       threshold: threshold
   193     };
   195     params.report = true;
   196     params.isAsync = true;
   198     this.controlMessage(params, function(result) {
   199       if (!isError(result.resultCode)) {
   200         callback.networkUsageAlarmResult(null);
   201         return;
   202       }
   204       this._enableNetworkInterfaceAlarm(networkName, threshold, callback);
   205     });
   206   },
   208   _enableNetworkInterfaceAlarm: function(networkName, threshold, callback) {
   209     if(DEBUG) debug("enableNetworkInterfaceAlarm for " + networkName + " at " + threshold + "bytes");
   211     let params = {
   212       cmd: "enableNetworkInterfaceAlarm",
   213       ifname: networkName,
   214       threshold: threshold
   215     };
   217     params.report = true;
   218     params.isAsync = true;
   220     this.controlMessage(params, function(result) {
   221       if (!isError(result.resultCode)) {
   222         callback.networkUsageAlarmResult(null);
   223         return;
   224       }
   225       callback.networkUsageAlarmResult(result.reason);
   226     });
   227   },
   229   _disableNetworkInterfaceAlarm: function(networkName, callback) {
   230     if(DEBUG) debug("disableNetworkInterfaceAlarm for " + networkName);
   232     let params = {
   233       cmd: "disableNetworkInterfaceAlarm",
   234       ifname: networkName,
   235     };
   237     params.report = true;
   238     params.isAsync = true;
   240     this.controlMessage(params, function(result) {
   241       callback(result);
   242     });
   243   },
   245   setWifiOperationMode: function(interfaceName, mode, callback) {
   246     if(DEBUG) debug("setWifiOperationMode on " + interfaceName + " to " + mode);
   248     let params = {
   249       cmd: "setWifiOperationMode",
   250       ifname: interfaceName,
   251       mode: mode
   252     };
   254     params.report = true;
   255     params.isAsync = true;
   257     this.controlMessage(params, function(result) {
   258       if (isError(result.resultCode)) {
   259         callback.wifiOperationModeResult("netd command error");
   260       } else {
   261         callback.wifiOperationModeResult(null);
   262       }
   263     });
   264   },
   266   resetRoutingTable: function(network) {
   267     let ips = {};
   268     let prefixLengths = {};
   269     let length = network.getAddresses(ips, prefixLengths);
   271     for (let i = 0; i < length; i++) {
   272       let ip = ips.value[i];
   273       let prefixLength = prefixLengths.value[i];
   275       let options = {
   276         cmd: "removeNetworkRoute",
   277         ifname: network.name,
   278         ip: ip,
   279         prefixLength: prefixLength
   280       };
   281       this.controlMessage(options);
   282     }
   283   },
   285   setDNS: function(networkInterface) {
   286     if(DEBUG) debug("Going DNS to " + networkInterface.name);
   287     let dnses = networkInterface.getDnses();
   288     let options = {
   289       cmd: "setDNS",
   290       ifname: networkInterface.name,
   291       domain: "mozilla." + networkInterface.name + ".doman",
   292       dnses: dnses
   293     };
   294     this.controlMessage(options);
   295   },
   297   setDefaultRouteAndDNS: function(network, oldInterface) {
   298     if(DEBUG) debug("Going to change route and DNS to " + network.name);
   299     let gateways = network.getGateways();
   300     let dnses = network.getDnses();
   301     let options = {
   302       cmd: "setDefaultRouteAndDNS",
   303       ifname: network.name,
   304       oldIfname: (oldInterface && oldInterface !== network) ? oldInterface.name : null,
   305       gateways: gateways,
   306       domain: "mozilla." + network.name + ".doman",
   307       dnses: dnses
   308     };
   309     this.controlMessage(options);
   310     this.setNetworkProxy(network);
   311   },
   313   removeDefaultRoute: function(network) {
   314     if(DEBUG) debug("Remove default route for " + network.name);
   315     let gateways = network.getGateways();
   316     let options = {
   317       cmd: "removeDefaultRoute",
   318       ifname: network.name,
   319       gateways: gateways
   320     };
   321     this.controlMessage(options);
   322   },
   324   addHostRoute: function(network) {
   325     if(DEBUG) debug("Going to add host route on " + network.name);
   326     let gateways = network.getGateways();
   327     let dnses = network.getDnses();
   328     let options = {
   329       cmd: "addHostRoute",
   330       ifname: network.name,
   331       gateways: gateways,
   332       hostnames: dnses.concat(network.httpProxyHost)
   333     };
   334     this.controlMessage(options);
   335   },
   337   removeHostRoute: function(network) {
   338     if(DEBUG) debug("Going to remove host route on " + network.name);
   339     let gateways = network.getGateways();
   340     let dnses = network.getDnses();
   341     let options = {
   342       cmd: "removeHostRoute",
   343       ifname: network.name,
   344       gateways: gateways,
   345       hostnames: dnses.concat(network.httpProxyHost)
   346     };
   347     this.controlMessage(options);
   348   },
   350   removeHostRoutes: function(ifname) {
   351     if(DEBUG) debug("Going to remove all host routes on " + ifname);
   352     let options = {
   353       cmd: "removeHostRoutes",
   354       ifname: ifname,
   355     };
   356     this.controlMessage(options);
   357   },
   359   addHostRouteWithResolve: function(network, hosts) {
   360     if(DEBUG) debug("Going to add host route after dns resolution on " + network.name);
   361     let gateways = network.getGateways();
   362     let options = {
   363       cmd: "addHostRoute",
   364       ifname: network.name,
   365       gateways: gateways,
   366       hostnames: hosts
   367     };
   368     this.controlMessage(options);
   369   },
   371   removeHostRouteWithResolve: function(network, hosts) {
   372     if(DEBUG) debug("Going to remove host route after dns resolution on " + network.name);
   373     let gateways = network.getGateways();
   374     let options = {
   375       cmd: "removeHostRoute",
   376       ifname: network.name,
   377       gateways: gateways,
   378       hostnames: hosts
   379     };
   380     this.controlMessage(options);
   381   },
   383   addSecondaryRoute: function(ifname, route) {
   384     if(DEBUG) debug("Going to add route to secondary table on " + ifname);
   385     let options = {
   386       cmd: "addSecondaryRoute",
   387       ifname: ifname,
   388       ip: route.ip,
   389       prefix: route.prefix,
   390       gateway: route.gateway
   391     };
   392     this.controlMessage(options);
   393   },
   395   removeSecondaryRoute: function(ifname, route) {
   396     if(DEBUG) debug("Going to remove route from secondary table on " + ifname);
   397     let options = {
   398       cmd: "removeSecondaryRoute",
   399       ifname: ifname,
   400       ip: route.ip,
   401       prefix: route.prefix,
   402       gateway: route.gateway
   403     };
   404     this.controlMessage(options);
   405   },
   407   setNetworkProxy: function(network) {
   408     try {
   409       if (!network.httpProxyHost || network.httpProxyHost === "") {
   410         // Sets direct connection to internet.
   411         Services.prefs.clearUserPref("network.proxy.type");
   412         Services.prefs.clearUserPref("network.proxy.share_proxy_settings");
   413         Services.prefs.clearUserPref("network.proxy.http");
   414         Services.prefs.clearUserPref("network.proxy.http_port");
   415         Services.prefs.clearUserPref("network.proxy.ssl");
   416         Services.prefs.clearUserPref("network.proxy.ssl_port");
   417         if(DEBUG) debug("No proxy support for " + network.name + " network interface.");
   418         return;
   419       }
   421       if(DEBUG) debug("Going to set proxy settings for " + network.name + " network interface.");
   422       // Sets manual proxy configuration.
   423       Services.prefs.setIntPref("network.proxy.type", MANUAL_PROXY_CONFIGURATION);
   424       // Do not use this proxy server for all protocols.
   425       Services.prefs.setBoolPref("network.proxy.share_proxy_settings", false);
   426       Services.prefs.setCharPref("network.proxy.http", network.httpProxyHost);
   427       Services.prefs.setCharPref("network.proxy.ssl", network.httpProxyHost);
   428       let port = network.httpProxyPort === 0 ? 8080 : network.httpProxyPort;
   429       Services.prefs.setIntPref("network.proxy.http_port", port);
   430       Services.prefs.setIntPref("network.proxy.ssl_port", port);
   431     } catch(ex) {
   432         if(DEBUG) debug("Exception " + ex + ". Unable to set proxy setting for " +
   433                          network.name + " network interface.");
   434     }
   435   },
   437   // Enable/Disable DHCP server.
   438   setDhcpServer: function(enabled, config, callback) {
   439     if (null === config) {
   440       config = {};
   441     }
   443     config.cmd = "setDhcpServer";
   444     config.isAsync = true;
   445     config.enabled = enabled;
   447     this.controlMessage(config, function setDhcpServerResult(response) {
   448       if (!response.success) {
   449         callback.dhcpServerResult('Set DHCP server error');
   450         return;
   451       }
   452       callback.dhcpServerResult(null);
   453     });
   454   },
   456   // Enable/disable WiFi tethering by sending commands to netd.
   457   setWifiTethering: function(enable, config, callback) {
   458     // config should've already contained:
   459     //   .ifname
   460     //   .internalIfname
   461     //   .externalIfname
   462     config.wifictrlinterfacename = WIFI_CTRL_INTERFACE;
   463     config.cmd = "setWifiTethering";
   465     // The callback function in controlMessage may not be fired immediately.
   466     config.isAsync = true;
   467     this.controlMessage(config, function setWifiTetheringResult(data) {
   468       let code = data.resultCode;
   469       let reason = data.resultReason;
   470       let enable = data.enable;
   471       let enableString = enable ? "Enable" : "Disable";
   473       if(DEBUG) debug(enableString + " Wifi tethering result: Code " + code + " reason " + reason);
   475       if (isError(code)) {
   476         callback.wifiTetheringEnabledChange("netd command error");
   477       } else {
   478         callback.wifiTetheringEnabledChange(null);
   479       }
   480     });
   481   },
   483   // Enable/disable USB tethering by sending commands to netd.
   484   setUSBTethering: function(enable, config, callback) {
   485     config.cmd = "setUSBTethering";
   486     // The callback function in controlMessage may not be fired immediately.
   487     config.isAsync = true;
   488     this.controlMessage(config, function setUsbTetheringResult(data) {
   489       let code = data.resultCode;
   490       let reason = data.resultReason;
   491       let enable = data.enable;
   492       let enableString = enable ? "Enable" : "Disable";
   494       if(DEBUG) debug(enableString + " USB tethering result: Code " + code + " reason " + reason);
   496       if (isError(code)) {
   497         callback.usbTetheringEnabledChange("netd command error");
   498       } else {
   499         callback.usbTetheringEnabledChange(null);
   500       }
   501     });
   502   },
   504   // Switch usb function by modifying property of persist.sys.usb.config.
   505   enableUsbRndis: function(enable, callback) {
   506     if(DEBUG) debug("enableUsbRndis: " + enable);
   508     let params = {
   509       cmd: "enableUsbRndis",
   510       enable: enable
   511     };
   512     // Ask net work to report the result when this value is set to true.
   513     if (callback) {
   514       params.report = true;
   515     } else {
   516       params.report = false;
   517     }
   519     // The callback function in controlMessage may not be fired immediately.
   520     params.isAsync = true;
   521     //this._usbTetheringAction = TETHERING_STATE_ONGOING;
   522     this.controlMessage(params, function(data) {
   523       callback.enableUsbRndisResult(data.result, data.enable);
   524     });
   525   },
   527   updateUpStream: function(previous, current, callback) {
   528     let params = {
   529       cmd: "updateUpStream",
   530       isAsync: true,
   531       preInternalIfname: previous.internalIfname,
   532       preExternalIfname: previous.externalIfname,
   533       curInternalIfname: current.internalIfname,
   534       curExternalIfname: current.externalIfname
   535     };
   537     this.controlMessage(params, function(data) {
   538       let code = data.resultCode;
   539       let reason = data.resultReason;
   540       if(DEBUG) debug("updateUpStream result: Code " + code + " reason " + reason);
   541       callback.updateUpStreamResult(!isError(code), data.curExternalIfname);
   542     });
   543   },
   545   shutdown: false,
   547   observe: function observe(aSubject, aTopic, aData) {
   548     switch (aTopic) {
   549       case "xpcom-shutdown":
   550         debug("NetworkService shutdown");
   551         this.shutdown = true;
   552         Services.obs.removeObserver(this, "xpcom-shutdown");
   553         if (gNetworkWorker) {
   554           gNetworkWorker.shutdown();
   555           gNetworkWorker = null;
   556         }
   557         break;
   558     }
   559   },
   560 };
   562 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkService]);

mercurial