gecko-dev/dom/system/gonk/TetheringService.js
Tooru Fujisawa 24ab66bc98 Bug 1217093 - Remove for-each from dom/. r=smaug
--HG--
extra : commitid : DcjxshJqlKg
extra : rebase_source : 23bc9a985f1e6d13e13837e31bb9b88b9be24d55
2015-10-19 02:00:50 +09:00

892 lines
32 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
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 TETHERINGSERVICE_CONTRACTID = "@mozilla.org/tethering/service;1";
const TETHERINGSERVICE_CID =
Components.ID("{527a4121-ee5a-4651-be9c-f46f59cf7c01}");
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
"@mozilla.org/network/manager;1",
"nsINetworkManager");
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
"@mozilla.org/network/service;1",
"nsINetworkService");
XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
"@mozilla.org/settingsService;1",
"nsISettingsService");
XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
"@mozilla.org/mobileconnection/mobileconnectionservice;1",
"nsIMobileConnectionService");
XPCOMUtils.defineLazyGetter(this, "gRil", function() {
try {
return Cc["@mozilla.org/ril;1"].getService(Ci.nsIRadioInterfaceLayer);
} catch (e) {}
return null;
});
const TOPIC_MOZSETTINGS_CHANGED = "mozsettings-changed";
const TOPIC_CONNECTION_STATE_CHANGED = "network-connection-state-changed";
const TOPIC_PREF_CHANGED = "nsPref:changed";
const TOPIC_XPCOM_SHUTDOWN = "xpcom-shutdown";
const PREF_MANAGE_OFFLINE_STATUS = "network.gonk.manage-offline-status";
const PREF_NETWORK_DEBUG_ENABLED = "network.debugging.enabled";
const POSSIBLE_USB_INTERFACE_NAME = "rndis0,usb0";
const DEFAULT_USB_INTERFACE_NAME = "rndis0";
const DEFAULT_3G_INTERFACE_NAME = "rmnet0";
const DEFAULT_WIFI_INTERFACE_NAME = "wlan0";
// The kernel's proc entry for network lists.
const KERNEL_NETWORK_ENTRY = "/sys/class/net";
const TETHERING_TYPE_WIFI = "WiFi";
const TETHERING_TYPE_USB = "USB";
const WIFI_FIRMWARE_AP = "AP";
const WIFI_FIRMWARE_STATION = "STA";
const WIFI_SECURITY_TYPE_NONE = "open";
const WIFI_SECURITY_TYPE_WPA_PSK = "wpa-psk";
const WIFI_SECURITY_TYPE_WPA2_PSK = "wpa2-psk";
const WIFI_CTRL_INTERFACE = "wl0.1";
const NETWORK_INTERFACE_UP = "up";
const NETWORK_INTERFACE_DOWN = "down";
const TETHERING_STATE_ONGOING = "ongoing";
const TETHERING_STATE_IDLE = "idle";
const TETHERING_STATE_ACTIVE = "active";
// Settings DB path for USB tethering.
const SETTINGS_USB_ENABLED = "tethering.usb.enabled";
const SETTINGS_USB_IP = "tethering.usb.ip";
const SETTINGS_USB_PREFIX = "tethering.usb.prefix";
const SETTINGS_USB_DHCPSERVER_STARTIP = "tethering.usb.dhcpserver.startip";
const SETTINGS_USB_DHCPSERVER_ENDIP = "tethering.usb.dhcpserver.endip";
const SETTINGS_USB_DNS1 = "tethering.usb.dns1";
const SETTINGS_USB_DNS2 = "tethering.usb.dns2";
// Settings DB path for WIFI tethering.
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";
const DEFAULT_USB_DHCPSERVER_STARTIP = "192.168.0.10";
const DEFAULT_USB_DHCPSERVER_ENDIP = "192.168.0.30";
const DEFAULT_DNS1 = "8.8.8.8";
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 SETTINGS_DATA_DEFAULT_SERVICE_ID = "ril.data.defaultServiceId";
const MOBILE_DUN_CONNECT_TIMEOUT = 30000;
const MOBILE_DUN_RETRY_INTERVAL = 5000;
const MOBILE_DUN_MAX_RETRIES = 5;
var debug;
function updateDebug() {
let debugPref = false; // set default value here.
try {
debugPref = debugPref || Services.prefs.getBoolPref(PREF_NETWORK_DEBUG_ENABLED);
} catch (e) {}
if (debugPref) {
debug = function(s) {
dump("-*- TetheringService: " + s + "\n");
};
} else {
debug = function(s) {};
}
}
updateDebug();
function TetheringService() {
Services.obs.addObserver(this, TOPIC_XPCOM_SHUTDOWN, false);
Services.obs.addObserver(this, TOPIC_MOZSETTINGS_CHANGED, false);
Services.obs.addObserver(this, TOPIC_CONNECTION_STATE_CHANGED, false);
Services.prefs.addObserver(PREF_NETWORK_DEBUG_ENABLED, this, false);
Services.prefs.addObserver(PREF_MANAGE_OFFLINE_STATUS, this, false);
try {
this._manageOfflineStatus =
Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS);
} catch(ex) {
// Ignore.
}
this._dataDefaultServiceId = 0;
// Possible usb tethering interfaces for different gonk platform.
this.possibleInterface = POSSIBLE_USB_INTERFACE_NAME.split(",");
// Default values for internal and external interfaces.
this._tetheringInterface = {};
this._tetheringInterface[TETHERING_TYPE_USB] = {
externalInterface: DEFAULT_3G_INTERFACE_NAME,
internalInterface: DEFAULT_USB_INTERFACE_NAME
};
this._tetheringInterface[TETHERING_TYPE_WIFI] = {
externalInterface: DEFAULT_3G_INTERFACE_NAME,
internalInterface: DEFAULT_WIFI_INTERFACE_NAME
};
this.tetheringSettings = {};
this.initTetheringSettings();
let settingsLock = gSettingsService.createLock();
// Read the default service id for data call.
settingsLock.get(SETTINGS_DATA_DEFAULT_SERVICE_ID, this);
// Read usb tethering data from settings DB.
settingsLock.get(SETTINGS_USB_IP, this);
settingsLock.get(SETTINGS_USB_PREFIX, this);
settingsLock.get(SETTINGS_USB_DHCPSERVER_STARTIP, this);
settingsLock.get(SETTINGS_USB_DHCPSERVER_ENDIP, this);
settingsLock.get(SETTINGS_USB_DNS1, this);
settingsLock.get(SETTINGS_USB_DNS2, this);
settingsLock.get(SETTINGS_USB_ENABLED, this);
// Read wifi tethering data from settings DB.
settingsLock.get(SETTINGS_WIFI_DHCPSERVER_STARTIP, this);
settingsLock.get(SETTINGS_WIFI_DHCPSERVER_ENDIP, this);
this._usbTetheringSettingsToRead = [SETTINGS_USB_IP,
SETTINGS_USB_PREFIX,
SETTINGS_USB_DHCPSERVER_STARTIP,
SETTINGS_USB_DHCPSERVER_ENDIP,
SETTINGS_USB_DNS1,
SETTINGS_USB_DNS2,
SETTINGS_USB_ENABLED,
SETTINGS_WIFI_DHCPSERVER_STARTIP,
SETTINGS_WIFI_DHCPSERVER_ENDIP];
this.wantConnectionEvent = null;
this.dunConnectTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.dunRetryTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._pendingTetheringRequests = [];
}
TetheringService.prototype = {
classID: TETHERINGSERVICE_CID,
classInfo: XPCOMUtils.generateCI({classID: TETHERINGSERVICE_CID,
contractID: TETHERINGSERVICE_CONTRACTID,
classDescription: "Tethering Service",
interfaces: [Ci.nsITetheringService]}),
QueryInterface: XPCOMUtils.generateQI([Ci.nsITetheringService,
Ci.nsISupportsWeakReference,
Ci.nsIObserver,
Ci.nsISettingsServiceCallback]),
// Flag to record the default client id for data call.
_dataDefaultServiceId: null,
// Number of usb tehering requests to be processed.
_usbTetheringRequestCount: 0,
// Usb tethering state.
_usbTetheringAction: TETHERING_STATE_IDLE,
// Tethering settings.
tetheringSettings: null,
// Tethering settings need to be read from settings DB.
_usbTetheringSettingsToRead: null,
// Previous usb tethering enabled state.
_oldUsbTetheringEnabledState: null,
// External and internal interface name.
_tetheringInterface: null,
// Dun connection timer.
dunConnectTimer: null,
// Dun connection retry times.
dunRetryTimes: 0,
// Dun retry timer.
dunRetryTimer: null,
// Pending tethering request to handle after dun is connected.
_pendingTetheringRequests: null,
// Flag to indicate wether wifi tethering is being processed.
_wifiTetheringRequestOngoing: false,
// Arguments for pending wifi tethering request.
_pendingWifiTetheringRequestArgs: null,
// The state of tethering.
state: Ci.nsITetheringService.TETHERING_STATE_INACTIVE,
// Flag to check if we can modify the Services.io.offline.
_manageOfflineStatus: true,
// nsIObserver
observe: function(aSubject, aTopic, aData) {
let network;
switch(aTopic) {
case TOPIC_PREF_CHANGED:
if (aData === PREF_NETWORK_DEBUG_ENABLED) {
updateDebug();
}
break;
case TOPIC_MOZSETTINGS_CHANGED:
if ("wrappedJSObject" in aSubject) {
aSubject = aSubject.wrappedJSObject;
}
this.handle(aSubject.key, aSubject.value);
break;
case TOPIC_CONNECTION_STATE_CHANGED:
network = aSubject.QueryInterface(Ci.nsINetworkInfo);
debug("Network " + network.type + "/" + network.name +
" changed state to " + network.state);
this.onConnectionChanged(network);
break;
case TOPIC_XPCOM_SHUTDOWN:
Services.obs.removeObserver(this, TOPIC_XPCOM_SHUTDOWN);
Services.obs.removeObserver(this, TOPIC_MOZSETTINGS_CHANGED);
Services.obs.removeObserver(this, TOPIC_CONNECTION_STATE_CHANGED);
Services.prefs.removeObserver(PREF_NETWORK_DEBUG_ENABLED, this);
Services.prefs.removeObserver(PREF_MANAGE_OFFLINE_STATUS, this);
this.dunConnectTimer.cancel();
this.dunRetryTimer.cancel();
break;
case PREF_MANAGE_OFFLINE_STATUS:
try {
this._manageOfflineStatus =
Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS);
} catch(ex) {
// Ignore.
}
break;
}
},
// nsISettingsServiceCallback
handle: function(aName, aResult) {
switch(aName) {
case SETTINGS_DATA_DEFAULT_SERVICE_ID:
this._dataDefaultServiceId = aResult || 0;
debug("'_dataDefaultServiceId' is now " + this._dataDefaultServiceId);
break;
case SETTINGS_USB_ENABLED:
this._oldUsbTetheringEnabledState = this.tetheringSettings[SETTINGS_USB_ENABLED];
case SETTINGS_USB_IP:
case SETTINGS_USB_PREFIX:
case SETTINGS_USB_DHCPSERVER_STARTIP:
case SETTINGS_USB_DHCPSERVER_ENDIP:
case SETTINGS_USB_DNS1:
case SETTINGS_USB_DNS2:
case SETTINGS_WIFI_DHCPSERVER_STARTIP:
case SETTINGS_WIFI_DHCPSERVER_ENDIP:
if (aResult !== null) {
this.tetheringSettings[aName] = aResult;
}
debug("'" + aName + "'" + " is now " + this.tetheringSettings[aName]);
let index = this._usbTetheringSettingsToRead.indexOf(aName);
if (index != -1) {
this._usbTetheringSettingsToRead.splice(index, 1);
}
if (this._usbTetheringSettingsToRead.length) {
debug("We haven't read completely the usb Tethering data from settings db.");
break;
}
if (this._oldUsbTetheringEnabledState === this.tetheringSettings[SETTINGS_USB_ENABLED]) {
debug("No changes for SETTINGS_USB_ENABLED flag. Nothing to do.");
this.handlePendingWifiTetheringRequest();
break;
}
this._usbTetheringRequestCount++;
if (this._usbTetheringRequestCount === 1) {
if (this._wifiTetheringRequestOngoing) {
debug('USB tethering request is blocked by ongoing wifi tethering request.');
} else {
this.handleLastUsbTetheringRequest();
}
}
break;
};
},
handleError: function(aErrorMessage) {
debug("There was an error while reading Tethering settings.");
this.tetheringSettings = {};
this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
},
initTetheringSettings: function() {
this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
this.tetheringSettings[SETTINGS_USB_IP] = DEFAULT_USB_IP;
this.tetheringSettings[SETTINGS_USB_PREFIX] = DEFAULT_USB_PREFIX;
this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP] = DEFAULT_USB_DHCPSERVER_STARTIP;
this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP] = DEFAULT_USB_DHCPSERVER_ENDIP;
this.tetheringSettings[SETTINGS_USB_DNS1] = DEFAULT_DNS1;
this.tetheringSettings[SETTINGS_USB_DNS2] = DEFAULT_DNS2;
this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP] = DEFAULT_WIFI_DHCPSERVER_STARTIP;
this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP] = DEFAULT_WIFI_DHCPSERVER_ENDIP;
this.tetheringSettings[SETTINGS_DUN_REQUIRED] =
libcutils.property_get("ro.tethering.dun_required") === "1";
},
getNetworkInfo: function(aType, aServiceId) {
for (let networkId in gNetworkManager.allNetworkInfo) {
let networkInfo = gNetworkManager.allNetworkInfo[networkId];
if (networkInfo.type == aType) {
try {
if (networkInfo instanceof Ci.nsIRilNetworkInfo) {
let rilNetwork = networkInfo.QueryInterface(Ci.nsIRilNetworkInfo);
if (rilNetwork.serviceId != aServiceId) {
continue;
}
}
} catch (e) {}
return networkInfo;
}
}
return null;
},
handleLastUsbTetheringRequest: function() {
debug('handleLastUsbTetheringRequest... ' + this._usbTetheringRequestCount);
if (this._usbTetheringRequestCount === 0) {
if (this.wantConnectionEvent) {
if (this.tetheringSettings[SETTINGS_USB_ENABLED]) {
this.wantConnectionEvent.call(this);
}
this.wantConnectionEvent = null;
}
this.handlePendingWifiTetheringRequest();
return;
}
// Cancel the accumlated count to 1 since we only care about the
// last state.
this._usbTetheringRequestCount = 1;
this.handleUSBTetheringToggle(this.tetheringSettings[SETTINGS_USB_ENABLED]);
this.wantConnectionEvent = null;
},
handlePendingWifiTetheringRequest: function() {
if (this._pendingWifiTetheringRequestArgs) {
this.setWifiTethering.apply(this, this._pendingWifiTetheringRequestArgs);
this._pendingWifiTetheringRequestArgs = null;
}
},
/**
* Callback when dun connection fails to connect within timeout.
*/
onDunConnectTimerTimeout: function() {
while (this._pendingTetheringRequests.length > 0) {
debug("onDunConnectTimerTimeout: callback without network info.");
let callback = this._pendingTetheringRequests.shift();
if (typeof callback === 'function') {
callback();
}
}
},
setupDunConnection: function() {
this.dunRetryTimer.cancel();
let connection =
gMobileConnectionService.getItemByServiceId(this._dataDefaultServiceId);
let data = connection && connection.data;
if (data && data.state === "registered") {
let ril = gRil.getRadioInterface(this._dataDefaultServiceId);
this.dunRetryTimes = 0;
ril.setupDataCallByType(Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN);
this.dunConnectTimer.cancel();
this.dunConnectTimer.
initWithCallback(this.onDunConnectTimerTimeout.bind(this),
MOBILE_DUN_CONNECT_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT);
return;
}
if (this.dunRetryTimes++ >= this.MOBILE_DUN_MAX_RETRIES) {
debug("setupDunConnection: max retries reached.");
this.dunRetryTimes = 0;
// same as dun connect timeout.
this.onDunConnectTimerTimeout();
return;
}
debug("Data not ready, retry dun after " + MOBILE_DUN_RETRY_INTERVAL + " ms.");
this.dunRetryTimer.
initWithCallback(this.setupDunConnection.bind(this),
MOBILE_DUN_RETRY_INTERVAL, Ci.nsITimer.TYPE_ONE_SHOT);
},
_dunActiveUsers: 0,
handleDunConnection: function(aEnable, aCallback) {
debug("handleDunConnection: " + aEnable);
let dun = this.getNetworkInfo(
Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN, this._dataDefaultServiceId);
if (!aEnable) {
this._dunActiveUsers--;
if (this._dunActiveUsers > 0) {
debug("Dun still needed by others, do not disconnect.")
return;
}
this.dunRetryTimes = 0;
this.dunRetryTimer.cancel();
this.dunConnectTimer.cancel();
this._pendingTetheringRequests = [];
if (dun && (dun.state == Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED)) {
gRil.getRadioInterface(this._dataDefaultServiceId)
.deactivateDataCallByType(Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN);
}
return;
}
this._dunActiveUsers++;
if (!dun || (dun.state != Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED)) {
debug("DUN data call inactive, setup dun data call!")
this._pendingTetheringRequests.push(aCallback);
this.dunRetryTimes = 0;
this.setupDunConnection();
return;
}
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = dun.name;
aCallback(dun);
},
handleUSBTetheringToggle: function(aEnable) {
debug("handleUSBTetheringToggle: " + aEnable);
if (aEnable &&
(this._usbTetheringAction === TETHERING_STATE_ONGOING ||
this._usbTetheringAction === TETHERING_STATE_ACTIVE)) {
debug("Usb tethering already connecting/connected.");
this._usbTetheringRequestCount = 0;
this.handlePendingWifiTetheringRequest();
return;
}
if (!aEnable &&
this._usbTetheringAction === TETHERING_STATE_IDLE) {
debug("Usb tethering already disconnected.");
this._usbTetheringRequestCount = 0;
this.handlePendingWifiTetheringRequest();
return;
}
if (!aEnable) {
this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
gNetworkService.enableUsbRndis(false, this.enableUsbRndisResult.bind(this));
return;
}
this.tetheringSettings[SETTINGS_USB_ENABLED] = true;
this._usbTetheringAction = TETHERING_STATE_ONGOING;
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
this.handleDunConnection(true, (aNetworkInfo) => {
if (!aNetworkInfo){
this.usbTetheringResultReport(aEnable, "Dun connection failed");
return;
}
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface =
aNetworkInfo.name;
gNetworkService.enableUsbRndis(true, this.enableUsbRndisResult.bind(this));
});
return;
}
if (gNetworkManager.activeNetworkInfo) {
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface =
gNetworkManager.activeNetworkInfo.name;
} else {
let mobile = this.getNetworkInfo(
Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, this._dataDefaultServiceId);
if (mobile && mobile.name) {
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface = mobile.name;
}
}
gNetworkService.enableUsbRndis(true, this.enableUsbRndisResult.bind(this));
},
getUSBTetheringParameters: function(aEnable, aTetheringInterface) {
let interfaceIp = this.tetheringSettings[SETTINGS_USB_IP];
let prefix = this.tetheringSettings[SETTINGS_USB_PREFIX];
let wifiDhcpStartIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_STARTIP];
let wifiDhcpEndIp = this.tetheringSettings[SETTINGS_WIFI_DHCPSERVER_ENDIP];
let usbDhcpStartIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_STARTIP];
let usbDhcpEndIp = this.tetheringSettings[SETTINGS_USB_DHCPSERVER_ENDIP];
let dns1 = this.tetheringSettings[SETTINGS_USB_DNS1];
let dns2 = this.tetheringSettings[SETTINGS_USB_DNS2];
let internalInterface = aTetheringInterface.internalInterface;
let externalInterface = aTetheringInterface.externalInterface;
// Using the default values here until application support these settings.
if (interfaceIp == "" || prefix == "" ||
wifiDhcpStartIp == "" || wifiDhcpEndIp == "" ||
usbDhcpStartIp == "" || usbDhcpEndIp == "") {
debug("Invalid subnet information.");
return null;
}
return {
ifname: internalInterface,
ip: interfaceIp,
prefix: prefix,
wifiStartIp: wifiDhcpStartIp,
wifiEndIp: wifiDhcpEndIp,
usbStartIp: usbDhcpStartIp,
usbEndIp: usbDhcpEndIp,
dns1: dns1,
dns2: dns2,
internalIfname: internalInterface,
externalIfname: externalInterface,
enable: aEnable,
link: aEnable ? NETWORK_INTERFACE_UP : NETWORK_INTERFACE_DOWN
};
},
notifyError: function(aResetSettings, aCallback, aMsg) {
if (aResetSettings) {
let settingsLock = gSettingsService.createLock();
// Disable wifi tethering with a useful error message for the user.
settingsLock.set("tethering.wifi.enabled", false, null, aMsg);
}
debug("setWifiTethering: " + (aMsg ? aMsg : "success"));
if (aCallback) {
// Callback asynchronously to avoid netsted toggling.
Services.tm.currentThread.dispatch(() => {
aCallback.wifiTetheringEnabledChange(aMsg);
}, Ci.nsIThread.DISPATCH_NORMAL);
}
},
enableWifiTethering: function(aEnable, aConfig, aCallback) {
// Fill in config's required fields.
aConfig.ifname = this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface;
aConfig.internalIfname = this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface;
aConfig.externalIfname = this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface;
this._wifiTetheringRequestOngoing = true;
gNetworkService.setWifiTethering(aEnable, aConfig, (aError) => {
// Change the tethering state to WIFI if there is no error.
if (aEnable && !aError) {
this.state = Ci.nsITetheringService.TETHERING_STATE_WIFI;
} else {
// If wifi thethering is disable, or any error happens,
// then consider the following statements.
// Check whether the state is USB now or not. If no then just change
// it to INACTIVE, if yes then just keep it.
// It means that don't let the disable or error of WIFI affect
// the original active state.
if (this.state != Ci.nsITetheringService.TETHERING_STATE_USB) {
this.state = Ci.nsITetheringService.TETHERING_STATE_INACTIVE;
}
// Disconnect dun on error or when wifi tethering is disabled.
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
this.handleDunConnection(false);
}
}
if (this._manageOfflineStatus) {
Services.io.offline = !this.isAnyConnected() &&
(this.state ===
Ci.nsITetheringService.TETHERING_STATE_INACTIVE);
}
let resetSettings = aError;
debug('gNetworkService.setWifiTethering finished');
this.notifyError(resetSettings, aCallback, aError);
this._wifiTetheringRequestOngoing = false;
if (this._usbTetheringRequestCount > 0) {
debug('Perform pending USB tethering requests.');
this.handleLastUsbTetheringRequest();
}
});
},
// Enable/disable WiFi tethering by sending commands to netd.
setWifiTethering: function(aEnable, aInterfaceName, aConfig, aCallback) {
debug("setWifiTethering: " + aEnable);
if (!aInterfaceName) {
this.notifyError(true, aCallback, "invalid network interface name");
return;
}
if (!aConfig) {
this.notifyError(true, aCallback, "invalid configuration");
return;
}
if (this._usbTetheringRequestCount > 0) {
// If there's still pending usb tethering request, save
// the request params and redo |setWifiTethering| on
// usb tethering task complete.
debug('USB tethering request is being processed. Queue this wifi tethering request.');
this._pendingWifiTetheringRequestArgs = Array.prototype.slice.call(arguments);
debug('Pending args: ' + JSON.stringify(this._pendingWifiTetheringRequestArgs));
return;
}
// Re-check again, test cases set this property later.
this.tetheringSettings[SETTINGS_DUN_REQUIRED] =
libcutils.property_get("ro.tethering.dun_required") === "1";
if (!aEnable) {
this.enableWifiTethering(false, aConfig, aCallback);
return;
}
this._tetheringInterface[TETHERING_TYPE_WIFI].internalInterface =
aInterfaceName;
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
this.handleDunConnection(true, (aNetworkInfo) => {
if (!aNetworkInfo) {
this.notifyError(true, aCallback, "Dun connection failed");
return;
}
this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface =
aNetworkInfo.name;
this.enableWifiTethering(true, aConfig, aCallback);
});
return;
}
let mobile = this.getNetworkInfo(
Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE, this._dataDefaultServiceId);
// Update the real interface name
if (mobile && mobile.name) {
this._tetheringInterface[TETHERING_TYPE_WIFI].externalInterface = mobile.name;
}
this.enableWifiTethering(true, aConfig, aCallback);
},
// Enable/disable USB tethering by sending commands to netd.
setUSBTethering: function(aEnable, aTetheringInterface, aCallback) {
let params = this.getUSBTetheringParameters(aEnable, aTetheringInterface);
if (params === null) {
gNetworkService.enableUsbRndis(false, function() {
this.usbTetheringResultReport(aEnable, "Invalid parameters");
});
return;
}
gNetworkService.setUSBTethering(aEnable, params, aCallback);
},
getUsbInterface: function() {
// Find the rndis interface.
for (let i = 0; i < this.possibleInterface.length; i++) {
try {
let file = new FileUtils.File(KERNEL_NETWORK_ENTRY + "/" +
this.possibleInterface[i]);
if (file.exists()) {
return this.possibleInterface[i];
}
} catch (e) {
debug("Not " + this.possibleInterface[i] + " interface.");
}
}
debug("Can't find rndis interface in possible lists.");
return DEFAULT_USB_INTERFACE_NAME;
},
enableUsbRndisResult: function(aSuccess, aEnable) {
if (aSuccess) {
// If enable is false, don't find usb interface cause it is already down,
// just use the internal interface in settings.
if (aEnable) {
this._tetheringInterface[TETHERING_TYPE_USB].internalInterface =
this.getUsbInterface();
}
this.setUSBTethering(aEnable,
this._tetheringInterface[TETHERING_TYPE_USB],
this.usbTetheringResultReport.bind(this, aEnable));
} else {
this.usbTetheringResultReport(aEnable, "enableUsbRndisResult failure");
throw new Error("failed to set USB Function to adb");
}
},
usbTetheringResultReport: function(aEnable, aError) {
this._usbTetheringRequestCount--;
let settingsLock = gSettingsService.createLock();
debug('usbTetheringResultReport callback. enable: ' + aEnable +
', error: ' + aError);
// Disable tethering settings when fail to enable it.
if (aError) {
this.tetheringSettings[SETTINGS_USB_ENABLED] = false;
settingsLock.set("tethering.usb.enabled", false, null);
// Skip others request when we found an error.
this._usbTetheringRequestCount = 0;
this._usbTetheringAction = TETHERING_STATE_IDLE;
// If the thethering state is WIFI now, then just keep it,
// if not, just change the state to INACTIVE.
// It means that don't let the error of USB affect the original active state.
if (this.state != Ci.nsITetheringService.TETHERING_STATE_WIFI) {
this.state = Ci.nsITetheringService.TETHERING_STATE_INACTIVE;
}
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
this.handleDunConnection(false);
}
} else {
if (aEnable) {
this._usbTetheringAction = TETHERING_STATE_ACTIVE;
this.state = Ci.nsITetheringService.TETHERING_STATE_USB;
} else {
this._usbTetheringAction = TETHERING_STATE_IDLE;
// If the state is now WIFI, don't let the disable of USB affect it.
if (this.state != Ci.nsITetheringService.TETHERING_STATE_WIFI) {
this.state = Ci.nsITetheringService.TETHERING_STATE_INACTIVE;
}
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED]) {
this.handleDunConnection(false);
}
}
if (this._manageOfflineStatus) {
Services.io.offline = !this.isAnyConnected() &&
(this.state ===
Ci.nsITetheringService.TETHERING_STATE_INACTIVE);
}
this.handleLastUsbTetheringRequest();
}
},
onConnectionChangedReport: function(aSuccess, aExternalIfname) {
debug("onConnectionChangedReport result: success " + aSuccess);
if (aSuccess) {
// Update the external interface.
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface =
aExternalIfname;
debug("Change the interface name to " + aExternalIfname);
}
},
onConnectionChanged: function(aNetworkInfo) {
if (aNetworkInfo.state != Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
debug("We are only interested in CONNECTED event");
return;
}
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] &&
aNetworkInfo.type === Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN) {
this.dunConnectTimer.cancel();
debug("DUN data call connected, process callbacks.");
while (this._pendingTetheringRequests.length > 0) {
let callback = this._pendingTetheringRequests.shift();
if (typeof callback === 'function') {
callback(aNetworkInfo);
}
}
return;
}
if (!this.tetheringSettings[SETTINGS_USB_ENABLED]) {
debug("Usb tethering settings is not enabled");
return;
}
if (this.tetheringSettings[SETTINGS_DUN_REQUIRED] &&
aNetworkInfo.type === Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE_DUN &&
this._tetheringInterface[TETHERING_TYPE_USB].externalInterface ===
aNetworkInfo.name) {
debug("Dun required and dun interface is the same");
return;
}
if (this._tetheringInterface[TETHERING_TYPE_USB].externalInterface ===
gNetworkManager.activeNetworkInfo.name) {
debug("The active interface is the same");
return;
}
let previous = {
internalIfname: this._tetheringInterface[TETHERING_TYPE_USB].internalInterface,
externalIfname: this._tetheringInterface[TETHERING_TYPE_USB].externalInterface
};
let current = {
internalIfname: this._tetheringInterface[TETHERING_TYPE_USB].internalInterface,
externalIfname: aNetworkInfo.name
};
let callback = (() => {
// Update external network interface.
debug("Update upstream interface to " + aNetworkInfo.name);
gNetworkService.updateUpStream(previous, current,
this.onConnectionChangedReport.bind(this));
});
if (this._usbTetheringAction === TETHERING_STATE_ONGOING) {
debug("Postpone the event and handle it when state is idle.");
this.wantConnectionEvent = callback;
return;
}
this.wantConnectionEvent = null;
callback.call(this);
},
isAnyConnected: function() {
let allNetworkInfo = gNetworkManager.allNetworkInfo;
for (let networkId in allNetworkInfo) {
if (allNetworkInfo.hasOwnProperty(networkId) &&
allNetworkInfo[networkId].state === Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED) {
return true;
}
}
return false;
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TetheringService]);