|
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/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; |
|
8 |
|
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"); |
|
13 |
|
14 const NETWORKMANAGER_CONTRACTID = "@mozilla.org/network/manager;1"; |
|
15 const NETWORKMANAGER_CID = |
|
16 Components.ID("{33901e46-33b8-11e1-9869-f46d04d25bcc}"); |
|
17 |
|
18 const DEFAULT_PREFERRED_NETWORK_TYPE = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI; |
|
19 |
|
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 }); |
|
27 |
|
28 XPCOMUtils.defineLazyServiceGetter(this, "gDNSService", |
|
29 "@mozilla.org/network/dns-service;1", |
|
30 "nsIDNSService"); |
|
31 |
|
32 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService", |
|
33 "@mozilla.org/network/service;1", |
|
34 "nsINetworkService"); |
|
35 |
|
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"; |
|
45 |
|
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"; |
|
50 |
|
51 // The kernel's proc entry for network lists. |
|
52 const KERNEL_NETWORK_ENTRY = "/sys/class/net"; |
|
53 |
|
54 const TETHERING_TYPE_WIFI = "WiFi"; |
|
55 const TETHERING_TYPE_USB = "USB"; |
|
56 |
|
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"; |
|
63 |
|
64 const NETWORK_INTERFACE_UP = "up"; |
|
65 const NETWORK_INTERFACE_DOWN = "down"; |
|
66 |
|
67 const TETHERING_STATE_ONGOING = "ongoing"; |
|
68 const TETHERING_STATE_IDLE = "idle"; |
|
69 const TETHERING_STATE_ACTIVE = "active"; |
|
70 |
|
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"; |
|
79 |
|
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"; |
|
83 |
|
84 // Settings DB patch for dun required setting. |
|
85 const SETTINGS_DUN_REQUIRED = "tethering.dun.required"; |
|
86 |
|
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"; |
|
92 |
|
93 const DEFAULT_DNS1 = "8.8.8.8"; |
|
94 const DEFAULT_DNS2 = "8.8.4.4"; |
|
95 |
|
96 const DEFAULT_WIFI_DHCPSERVER_STARTIP = "192.168.1.10"; |
|
97 const DEFAULT_WIFI_DHCPSERVER_ENDIP = "192.168.1.30"; |
|
98 |
|
99 const IPV4_ADDRESS_ANY = "0.0.0.0"; |
|
100 const IPV6_ADDRESS_ANY = "::0"; |
|
101 |
|
102 const IPV4_MAX_PREFIX_LENGTH = 32; |
|
103 const IPV6_MAX_PREFIX_LENGTH = 128; |
|
104 |
|
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; |
|
109 |
|
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; |
|
117 |
|
118 let DEBUG = false; |
|
119 |
|
120 // Read debug setting from pref. |
|
121 try { |
|
122 let debugPref = Services.prefs.getBoolPref("network.debugging.enabled"); |
|
123 DEBUG = DEBUG || debugPref; |
|
124 } catch (e) {} |
|
125 |
|
126 function defineLazyRegExp(obj, name, pattern) { |
|
127 obj.__defineGetter__(name, function() { |
|
128 delete obj[name]; |
|
129 return obj[name] = new RegExp(pattern); |
|
130 }); |
|
131 } |
|
132 |
|
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); |
|
146 |
|
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); |
|
154 |
|
155 // Possible usb tethering interfaces for different gonk platform. |
|
156 this.possibleInterface = POSSIBLE_USB_INTERFACE_NAME.split(","); |
|
157 |
|
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 }; |
|
168 |
|
169 this.initTetheringSettings(); |
|
170 |
|
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); |
|
180 |
|
181 // Read wifi tethering data from settings DB. |
|
182 settingsLock.get(SETTINGS_WIFI_DHCPSERVER_STARTIP, this); |
|
183 settingsLock.get(SETTINGS_WIFI_DHCPSERVER_ENDIP, this); |
|
184 |
|
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]; |
|
194 |
|
195 this.wantConnectionEvent = null; |
|
196 this.setAndConfigureActive(); |
|
197 |
|
198 ppmm.addMessageListener('NetworkInterfaceList:ListInterface', this); |
|
199 |
|
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]), |
|
214 |
|
215 // nsIObserver |
|
216 |
|
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 |
|
250 |
|
251 this.onConnectionChanged(network); |
|
252 |
|
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 } |
|
278 |
|
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 }, |
|
345 |
|
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 = []; |
|
356 |
|
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); |
|
369 |
|
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 }, |
|
386 |
|
387 // nsINetworkManager |
|
388 |
|
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 }, |
|
412 |
|
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 }, |
|
433 |
|
434 _manageOfflineStatus: true, |
|
435 |
|
436 networkInterfaces: null, |
|
437 |
|
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 }, |
|
453 |
|
454 active: null, |
|
455 _overriddenActive: null, |
|
456 |
|
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 }, |
|
466 |
|
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 }, |
|
474 |
|
475 isNetworkTypeMobile: function(type) { |
|
476 return (type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE || |
|
477 this.isNetworkTypeSecondaryMobile(type)); |
|
478 }, |
|
479 |
|
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 } |
|
486 |
|
487 network = network.QueryInterface(Ci.nsIRilNetworkInterface); |
|
488 |
|
489 debug("Network '" + network.name + "' registered, " + |
|
490 "adding mmsproxy and/or mmsc route"); |
|
491 |
|
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 } |
|
498 |
|
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 } |
|
504 |
|
505 gNetworkService.addHostRouteWithResolve(network, mmsHosts); |
|
506 } |
|
507 }, |
|
508 |
|
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 } |
|
515 |
|
516 network = network.QueryInterface(Ci.nsIRilNetworkInterface); |
|
517 |
|
518 debug("Network '" + network.name + "' unregistered, " + |
|
519 "removing mmsproxy and/or mmsc route"); |
|
520 |
|
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 } |
|
527 |
|
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 } |
|
533 |
|
534 gNetworkService.removeHostRouteWithResolve(network, mmsHosts); |
|
535 } |
|
536 }, |
|
537 |
|
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 }, |
|
559 |
|
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); |
|
571 |
|
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 |
|
579 |
|
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; |
|
586 |
|
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 } |
|
598 |
|
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 } |
|
607 |
|
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 } |
|
648 |
|
649 if (this.active != oldActive) { |
|
650 Services.obs.notifyObservers(this.active, TOPIC_ACTIVE_CHANGED, null); |
|
651 } |
|
652 |
|
653 if (this._manageOfflineStatus) { |
|
654 Services.io.offline = !this.active; |
|
655 } |
|
656 }, |
|
657 |
|
658 #ifdef MOZ_B2G_RIL |
|
659 resolveHostname: function(hosts) { |
|
660 let retval = []; |
|
661 |
|
662 for (let hostname of hosts) { |
|
663 // Sanity check for null, undefined and empty string... etc. |
|
664 if (!hostname) { |
|
665 continue; |
|
666 } |
|
667 |
|
668 try { |
|
669 let uri = Services.io.newURI(hostname, null, null); |
|
670 hostname = uri.host; |
|
671 } catch (e) {} |
|
672 |
|
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 } |
|
680 |
|
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 } |
|
689 |
|
690 return retval; |
|
691 }, |
|
692 #endif |
|
693 |
|
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 } |
|
702 |
|
703 if (network.state == Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED) { |
|
704 return CONNECTION_TYPE_NONE; |
|
705 } |
|
706 |
|
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 }, |
|
714 |
|
715 // nsISettingsServiceCallback |
|
716 |
|
717 tetheringSettings: {}, |
|
718 |
|
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; |
|
727 |
|
728 this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP] = DEFAULT_WIFI_DHCPSERVER_STARTIP; |
|
729 this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP] = DEFAULT_WIFI_DHCPSERVER_ENDIP; |
|
730 |
|
731 #ifdef MOZ_B2G_RIL |
|
732 this.tetheringSettings[SETTINGS_DUN_REQUIRED] = |
|
733 libcutils.property_get("ro.tethering.dun_required") === "1"; |
|
734 #endif |
|
735 }, |
|
736 |
|
737 _requestCount: 0, |
|
738 |
|
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); |
|
756 |
|
757 if (index != -1) { |
|
758 this._usbTetheringSettingsToRead.splice(index, 1); |
|
759 } |
|
760 |
|
761 if (this._usbTetheringSettingsToRead.length) { |
|
762 debug("We haven't read completely the usb Tethering data from settings db."); |
|
763 break; |
|
764 } |
|
765 |
|
766 if (this._oldUsbTetheringEnabledState === this.tetheringSettings[SETTINGS_USB_ENABLED]) { |
|
767 debug("No changes for SETTINGS_USB_ENABLED flag. Nothing to do."); |
|
768 break; |
|
769 } |
|
770 |
|
771 this._requestCount++; |
|
772 if (this._requestCount === 1) { |
|
773 this.handleUSBTetheringToggle(aResult); |
|
774 } |
|
775 break; |
|
776 }; |
|
777 }, |
|
778 |
|
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 }, |
|
784 |
|
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 }, |
|
793 |
|
794 _usbTetheringAction: TETHERING_STATE_IDLE, |
|
795 |
|
796 _usbTetheringSettingsToRead: [], |
|
797 |
|
798 _oldUsbTetheringEnabledState: null, |
|
799 |
|
800 // External and internal interface name. |
|
801 _tetheringInterface: null, |
|
802 |
|
803 handleLastRequest: function() { |
|
804 let count = this._requestCount; |
|
805 this._requestCount = 0; |
|
806 |
|
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 } |
|
816 |
|
817 if (count > 1) { |
|
818 this.handleUSBTetheringToggle(this.tetheringSettings[SETTINGS_USB_ENABLED]); |
|
819 this.wantConnectionEvent = null; |
|
820 } |
|
821 }, |
|
822 |
|
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 }, |
|
837 |
|
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); |
|
843 |
|
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 } |
|
854 |
|
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 } |
|
862 |
|
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 }, |
|
868 |
|
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); |
|
875 |
|
876 if (!enable) { |
|
877 this._dunActiveUsers--; |
|
878 if (this._dunActiveUsers > 0) { |
|
879 debug("Dun still needed by others, do not disconnect.") |
|
880 return; |
|
881 } |
|
882 |
|
883 this.dunRetryTimes = 0; |
|
884 this.dunRetryTimer.cancel(); |
|
885 this.dunConnectTimer.cancel(); |
|
886 this._pendingTetheringRequests = []; |
|
887 |
|
888 if (dun && (dun.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED)) { |
|
889 this.mRil.getRadioInterface(this.gDataDefaultServiceId) |
|
890 .deactivateDataCallByType("dun"); |
|
891 } |
|
892 return; |
|
893 } |
|
894 |
|
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(); |
|
901 |
|
902 return; |
|
903 } |
|
904 this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = dun.name; |
|
905 callback(dun); |
|
906 }, |
|
907 #endif |
|
908 |
|
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 } |
|
917 |
|
918 if (!enable && |
|
919 this._usbTetheringAction === TETHERING_STATE_IDLE) { |
|
920 debug("Usb tethering already disconnected."); |
|
921 return; |
|
922 } |
|
923 |
|
924 if (!enable) { |
|
925 this.tetheringSettings[SETTINGS_USB_ENABLED] = false; |
|
926 gNetworkService.enableUsbRndis(false, this.enableUsbRndisResult.bind(this)); |
|
927 return; |
|
928 } |
|
929 |
|
930 this.tetheringSettings[SETTINGS_USB_ENABLED] = true; |
|
931 this._usbTetheringAction = TETHERING_STATE_ONGOING; |
|
932 |
|
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 |
|
946 |
|
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 }, |
|
957 |
|
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; |
|
969 |
|
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]; |
|
978 |
|
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 } |
|
986 |
|
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 }, |
|
1003 |
|
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); |
|
1009 } |
|
1010 |
|
1011 debug("setWifiTethering: " + (msg ? msg : "success")); |
|
1012 |
|
1013 if (callback) { |
|
1014 callback.wifiTetheringEnabledChange(msg); |
|
1015 } |
|
1016 }, |
|
1017 |
|
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; |
|
1023 |
|
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); |
|
1030 } |
|
1031 #endif |
|
1032 let resetSettings = error; |
|
1033 this.notifyError(resetSettings, callback, error); |
|
1034 }).bind(this)); |
|
1035 }, |
|
1036 |
|
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; |
|
1043 } |
|
1044 |
|
1045 if (!config) { |
|
1046 this.notifyError(true, callback, "invalid configuration"); |
|
1047 return; |
|
1048 } |
|
1049 |
|
1050 if (!enable) { |
|
1051 this.enableWifiTethering(false, config, callback); |
|
1052 return; |
|
1053 } |
|
1054 |
|
1055 this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface = network.name; |
|
1056 |
|
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; |
|
1063 } |
|
1064 this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface = network.name; |
|
1065 this.enableWifiTethering(true, config, callback); |
|
1066 }.bind(this, config, callback)); |
|
1067 return; |
|
1068 } |
|
1069 #endif |
|
1070 |
|
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; |
|
1075 } |
|
1076 |
|
1077 this.enableWifiTethering(true, config, callback); |
|
1078 }, |
|
1079 |
|
1080 // Enable/disable USB tethering by sending commands to netd. |
|
1081 setUSBTethering: function(enable, tetheringInterface, callback) { |
|
1082 let params = this.getUSBTetheringParameters(enable, tetheringInterface); |
|
1083 |
|
1084 if (params === null) { |
|
1085 gNetworkService.enableUsbRndis(false, function() { |
|
1086 this.usbTetheringResultReport("Invalid parameters"); |
|
1087 }); |
|
1088 return; |
|
1089 } |
|
1090 |
|
1091 gNetworkService.setUSBTethering(enable, params, callback); |
|
1092 }, |
|
1093 |
|
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]; |
|
1102 } |
|
1103 } catch (e) { |
|
1104 debug("Not " + this.possibleInterface[i] + " interface."); |
|
1105 } |
|
1106 } |
|
1107 debug("Can't find rndis interface in possible lists."); |
|
1108 return DEFAULT_USB_INTERFACE_NAME; |
|
1109 }, |
|
1110 |
|
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(); |
|
1117 } |
|
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"); |
|
1124 } |
|
1125 }, |
|
1126 |
|
1127 usbTetheringResultReport: function(error) { |
|
1128 let settingsLock = gSettingsService.createLock(); |
|
1129 |
|
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); |
|
1140 } |
|
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); |
|
1150 } |
|
1151 #endif |
|
1152 } |
|
1153 this.handleLastRequest(); |
|
1154 } |
|
1155 }, |
|
1156 |
|
1157 onConnectionChangedReport: function(success, externalIfname) { |
|
1158 debug("onConnectionChangedReport result: success " + success); |
|
1159 |
|
1160 if (success) { |
|
1161 // Update the external interface. |
|
1162 this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = externalIfname; |
|
1163 debug("Change the interface name to " + externalIfname); |
|
1164 } |
|
1165 }, |
|
1166 |
|
1167 onConnectionChanged: function(network) { |
|
1168 if (network.state != Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED) { |
|
1169 debug("We are only interested in CONNECTED event"); |
|
1170 return; |
|
1171 } |
|
1172 |
|
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); |
|
1189 } |
|
1190 } |
|
1191 return; |
|
1192 } |
|
1193 #endif |
|
1194 |
|
1195 if (!this.tetheringSettings[SETTINGS_USB_ENABLED]) { |
|
1196 debug("Usb tethering settings is not enabled"); |
|
1197 return; |
|
1198 } |
|
1199 |
|
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; |
|
1207 } |
|
1208 #endif |
|
1209 |
|
1210 if (this._tetheringInterface[TETHERING_TYPE_USB].externalInterface === |
|
1211 this.active.name) { |
|
1212 debug("The active interface is the same"); |
|
1213 return; |
|
1214 } |
|
1215 |
|
1216 let previous = { |
|
1217 internalIfname: this._tetheringInterface[TETHERING_TYPE_USB].internalInterface, |
|
1218 externalIfname: this._tetheringInterface[TETHERING_TYPE_USB].externalInterface |
|
1219 }; |
|
1220 |
|
1221 let current = { |
|
1222 internalIfname: this._tetheringInterface[TETHERING_TYPE_USB].internalInterface, |
|
1223 externalIfname: network.name |
|
1224 }; |
|
1225 |
|
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); |
|
1231 |
|
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; |
|
1236 } |
|
1237 this.wantConnectionEvent = null; |
|
1238 |
|
1239 callback.call(this); |
|
1240 } |
|
1241 }; |
|
1242 |
|
1243 let CaptivePortalDetectionHelper = (function() { |
|
1244 |
|
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 }; |
|
1253 |
|
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); |
|
1264 } |
|
1265 }; |
|
1266 |
|
1267 // Abort any unfinished captive portal detection. |
|
1268 if (_ongoingInterface != null) { |
|
1269 capService.abort(_ongoingInterface); |
|
1270 _ongoingInterface = null; |
|
1271 } |
|
1272 try { |
|
1273 capService.checkCaptivePortal(interfaceName, capCallback); |
|
1274 _ongoingInterface = interfaceName; |
|
1275 } catch (e) { |
|
1276 debug('Fail to detect captive portal due to: ' + e.message); |
|
1277 } |
|
1278 }; |
|
1279 |
|
1280 let _abort = function(interfaceName) { |
|
1281 if (_ongoingInterface !== interfaceName) { |
|
1282 return; |
|
1283 } |
|
1284 |
|
1285 let capService = getService(); |
|
1286 capService.abort(_ongoingInterface); |
|
1287 _ongoingInterface = null; |
|
1288 }; |
|
1289 |
|
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 }); |
|
1303 } |
|
1304 |
|
1305 break; |
|
1306 case EVENT_DISCONNECT: |
|
1307 if (_available && |
|
1308 network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) { |
|
1309 _abort(network.name); |
|
1310 } |
|
1311 break; |
|
1312 } |
|
1313 } |
|
1314 }; |
|
1315 }()); |
|
1316 |
|
1317 #ifdef MOZ_B2G_RIL |
|
1318 XPCOMUtils.defineLazyServiceGetter(NetworkManager.prototype, "mRil", |
|
1319 "@mozilla.org/ril;1", |
|
1320 "nsIRadioInterfaceLayer"); |
|
1321 |
|
1322 XPCOMUtils.defineLazyGetter(NetworkManager.prototype, |
|
1323 "gDataDefaultServiceId", function() { |
|
1324 try { |
|
1325 return Services.prefs.getIntPref(PREF_DATA_DEFAULT_SERVICE_ID); |
|
1326 } catch(e) {} |
|
1327 |
|
1328 return 0; |
|
1329 }); |
|
1330 #endif |
|
1331 |
|
1332 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkManager]); |
|
1333 |
|
1334 |
|
1335 let debug; |
|
1336 if (DEBUG) { |
|
1337 debug = function(s) { |
|
1338 dump("-*- NetworkManager: " + s + "\n"); |
|
1339 }; |
|
1340 } else { |
|
1341 debug = function(s) {}; |
|
1342 } |