Bug 960865 - Part 4: dun support implementation in NetworkManager. r=vchang

This commit is contained in:
Jessica Jong 2014-02-28 18:05:06 +08:00
parent b6005b9988
commit 44d60b81be

View File

@ -9,6 +9,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/systemlibs.js");
const NETWORKMANAGER_CONTRACTID = "@mozilla.org/network/manager;1";
const NETWORKMANAGER_CID =
@ -78,6 +79,9 @@ const SETTINGS_USB_DNS2 = "tethering.usb.dns2";
const SETTINGS_WIFI_DHCPSERVER_STARTIP = "tethering.wifi.dhcpserver.startip";
const SETTINGS_WIFI_DHCPSERVER_ENDIP = "tethering.wifi.dhcpserver.endip";
// Settings DB patch for dun required setting.
const SETTINGS_DUN_REQUIRED = "tethering.dun.required";
// Default value for USB tethering.
const DEFAULT_USB_IP = "192.168.0.1";
const DEFAULT_USB_PREFIX = "24";
@ -90,6 +94,15 @@ const DEFAULT_DNS2 = "8.8.4.4";
const DEFAULT_WIFI_DHCPSERVER_STARTIP = "192.168.1.10";
const DEFAULT_WIFI_DHCPSERVER_ENDIP = "192.168.1.30";
const IPV4_ADDRESS_ANY = "0.0.0.0";
const IPV6_ADDRESS_ANY = "::0";
const IPV4_MAX_PREFIX_LENGTH = 32;
const IPV6_MAX_PREFIX_LENGTH = 128;
const PREF_DATA_DEFAULT_SERVICE_ID = "ril.data.defaultServiceId";
const DEBUG = false;
function defineLazyRegExp(obj, name, pattern) {
@ -198,6 +211,11 @@ NetworkManager.prototype = {
}
// Add extra host route. For example, mms proxy or mmsc.
this.setExtraHostRoute(network);
// Dun type is a special case where we add the default route to a
// secondary table.
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
this.setSecondaryDefaultRoute(network);
}
#endif
// Remove pre-created default route and let setAndConfigureActive()
// to set default route only on preferred network
@ -226,6 +244,10 @@ NetworkManager.prototype = {
}
// Remove extra host route. For example, mms proxy or mmsc.
this.removeExtraHostRoute(network);
// Remove secondary default route for dun.
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
this.removeSecondaryDefaultRoute(network);
}
#endif
// Remove routing table in /proc/net/route
if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
@ -256,11 +278,20 @@ NetworkManager.prototype = {
let regNetwork = subject.QueryInterface(Ci.nsINetworkInterface);
// Add extra host route. For example, mms proxy or mmsc.
this.setExtraHostRoute(regNetwork);
// Dun type is a special case where we add the default route to a
// secondary table.
if (regNetwork.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
this.setSecondaryDefaultRoute(regNetwork);
}
break;
case TOPIC_INTERFACE_UNREGISTERED:
let unregNetwork = subject.QueryInterface(Ci.nsINetworkInterface);
// Remove extra host route. For example, mms proxy or mmsc.
this.removeExtraHostRoute(unregNetwork);
// Remove secondary default route for dun.
if (unregNetwork.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
this.removeSecondaryDefaultRoute(unregNetwork);
}
break;
#endif
case TOPIC_MOZSETTINGS_CHANGED:
@ -291,6 +322,7 @@ NetworkManager.prototype = {
let excludeMms = aMsg.json.excludeMms;
let excludeSupl = aMsg.json.excludeSupl;
let excludeIms = aMsg.json.excludeIms;
let excludeDun = aMsg.json.excludeDun;
#endif
let interfaces = [];
@ -298,7 +330,8 @@ NetworkManager.prototype = {
#ifdef MOZ_B2G_RIL
if ((i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS && excludeMms) ||
(i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL && excludeSupl) ||
(i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_IMS && excludeIms)) {
(i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_IMS && excludeIms) ||
(i.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN && excludeDun)) {
continue;
}
#endif
@ -456,6 +489,39 @@ NetworkManager.prototype = {
gNetworkService.removeHostRouteWithResolve(network, mmsHosts);
}
},
setSecondaryDefaultRoute: function(network) {
// First, we need to add a host route to the gateway in the secondary
// routing table to make the gateway reachable. Host route takes the max
// prefix and gateway address 'any'.
let route = {
ip: network.gateway,
prefix: IPV4_MAX_PREFIX_LENGTH,
gateway: IPV4_ADDRESS_ANY
};
gNetworkService.addSecondaryRoute(network.name, route);
// Now we can add the default route through gateway. Default route takes the
// min prefix and destination ip 'any'.
route.ip = IPV4_ADDRESS_ANY;
route.prefix = 0;
route.gateway = network.gateway;
gNetworkService.addSecondaryRoute(network.name, route);
},
removeSecondaryDefaultRoute: function(network) {
// Remove both host route and default route.
let route = {
ip: network.gateway,
prefix: IPV4_MAX_PREFIX_LENGTH,
gateway: IPV4_ADDRESS_ANY
};
gNetworkService.removeSecondaryRoute(network.name, route);
route.ip = IPV4_ADDRESS_ANY;
route.prefix = "0";
route.gateway = network.gateway;
gNetworkService.removeSecondaryRoute(network.name, route);
},
#endif // MOZ_B2G_RIL
/**
@ -586,6 +652,11 @@ NetworkManager.prototype = {
this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP] = DEFAULT_WIFI_DHCPSERVER_STARTIP;
this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP] = DEFAULT_WIFI_DHCPSERVER_ENDIP;
#ifdef MOZ_B2G_RIL
this.tetheringSettings[SETTINGS_DUN_REQUIRED] =
libcutils.property_get("ro.tethering.dun_required") === "1";
#endif
},
_requestCount: 0,
@ -674,6 +745,33 @@ NetworkManager.prototype = {
}
},
#ifdef MOZ_B2G_RIL
_pendingTetheringRequests: [],
handleDunConnection: function(enable, callback) {
debug("handleDunConnection: " + enable);
let dun = this.getNetworkInterface(
Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN);
if (!enable) {
if (dun && (dun.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED)) {
this.mRil.getRadioInterface(this.gDataDefaultServiceId)
.deactivateDataCallByType("dun");
}
return;
}
if (!dun || (dun.state != Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED)) {
debug("DUN data call inactive, setup dun data call!")
this._pendingTetheringRequests.push(callback);
this.mRil.getRadioInterface(this.gDataDefaultServiceId)
.setupDataCallByType("dun");
return;
}
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = dun.name;
callback(dun);
},
#endif
handleUSBTetheringToggle: function(enable) {
if (!enable) {
this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
@ -681,15 +779,26 @@ NetworkManager.prototype = {
return;
}
this.tetheringSettings[SETTINGS_USB_ENABLED] = true;
#ifdef MOZ_B2G_RIL
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
this.handleDunConnection(true, function(network) {
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = network.name;
gNetworkService.enableUsbRndis(true, this.enableUsbRndisResult.bind(this));
}.bind(this));
return;
}
#endif
if (this.active) {
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = this.active.name
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = this.active.name;
} else {
let mobile = this.getNetworkInterface(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE);
if (mobile) {
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = mobile.name;
}
}
this.tetheringSettings[SETTINGS_USB_ENABLED] = true;
gNetworkService.enableUsbRndis(true, this.enableUsbRndisResult.bind(this));
},
@ -753,8 +862,28 @@ NetworkManager.prototype = {
}
},
enableWifiTethering: function(enable, config, callback) {
// Fill in config's required fields.
config.ifname = this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface;
config.internalIfname = this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface;
config.externalIfname = this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface;
gNetworkService.setWifiTethering(enable, config, (function(error) {
#ifdef MOZ_B2G_RIL
// Disconnect dun on error or when wifi tethering is disabled.
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] &&
(!enable || error)) {
this.handleDunConnection(false);
}
#endif
let resetSettings = error;
this.notifyError(resetSettings, callback, error);
}).bind(this));
},
// Enable/disable WiFi tethering by sending commands to netd.
setWifiTethering: function(enable, network, config, callback) {
debug("setWifiTethering: " + enable);
if (!network) {
this.notifyError(true, callback, "invalid network information");
return;
@ -765,22 +894,30 @@ NetworkManager.prototype = {
return;
}
if (!enable) {
this.enableWifiTethering(false, config, callback);
return;
}
this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface = network.name;
#ifdef MOZ_B2G_RIL
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
this.handleDunConnection(true, function(config, callback, network) {
this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface = network.name;
this.enableWifiTethering(true, config, callback);
}.bind(this, config, callback));
return;
}
#endif
let mobile = this.getNetworkInterface(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE);
// Update the real interface name
if (mobile) {
this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface = mobile.name;
}
config.ifname = this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface;
config.internalIfname = this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface;
config.externalIfname = this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface;
gNetworkService.setWifiTethering(enable, config, (function(error) {
let resetSettings = error;
this.notifyError(resetSettings, callback, error);
}).bind(this));
this.enableWifiTethering(true, config, callback);
},
// Enable/disable USB tethering by sending commands to netd.
@ -816,7 +953,11 @@ NetworkManager.prototype = {
enableUsbRndisResult: function(success, enable) {
if (success) {
this._tetheringInterface[TETHERING_TYPE_USB].internalInterface = this.getUsbInterface();
// If enable is false, don't find usb interface cause it is already down,
// just use the internal interface in settings.
if (enable) {
this._tetheringInterface[TETHERING_TYPE_USB].internalInterface = this.getUsbInterface();
}
this.setUSBTethering(enable,
this._tetheringInterface[TETHERING_TYPE_USB],
this.usbTetheringResultReport.bind(this));
@ -835,8 +976,19 @@ NetworkManager.prototype = {
this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
settingsLock.set("tethering.usb.enabled", false, null);
// Skip others request when we found an error.
#ifdef MOZ_B2G_RIL
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
this.handleDunConnection(false);
}
#endif
this._requestCount = 0;
} else {
#ifdef MOZ_B2G_RIL
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] &&
!this.tetheringSettings[SETTINGS_USB_ENABLED]) {
this.handleDunConnection(false);
}
#endif
this.handleLastRequest();
}
},
@ -857,11 +1009,42 @@ NetworkManager.prototype = {
return;
}
#ifdef MOZ_B2G_RIL
// We can not use network.type only to check if it's dun, cause if it is
// shared with default, the returned type would always be default, see bug
// 939046. In most cases, if dun is required, it should not be shared with
// default.
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] &&
(network.type === Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN ||
this.mRil.getRadioInterface(this.gDataDefaultServiceId)
.getDataCallStateByType("dun") ===
Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED)) {
debug("DUN data call connected, process callbacks.");
while (this._pendingTetheringRequests.length > 0) {
let callback = this._pendingTetheringRequests.shift();
if (typeof callback === 'function') {
callback(network);
}
}
return;
}
#endif
if (!this.tetheringSettings[SETTINGS_USB_ENABLED]) {
debug("Usb tethering settings is not enabled");
return;
}
#ifdef MOZ_B2G_RIL
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] &&
network.type === Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN &&
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface ===
network.name) {
debug("Dun required and dun interface is the same");
return;
}
#endif
if (this._tetheringInterface[TETHERING_TYPE_USB].externalInterface ===
this.active.name) {
debug("The active interface is the same");
@ -973,6 +1156,15 @@ let CaptivePortalDetectionHelper = (function() {
XPCOMUtils.defineLazyServiceGetter(NetworkManager.prototype, "mRil",
"@mozilla.org/ril;1",
"nsIRadioInterfaceLayer");
XPCOMUtils.defineLazyGetter(NetworkManager.prototype,
"gDataDefaultServiceId", function() {
try {
return Services.prefs.getIntPref(PREF_DATA_DEFAULT_SERVICE_ID);
} catch(e) {}
return 0;
});
#endif
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkManager]);