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