Bug 897871 - Wifi - Improve wifi startup time. r=mrbkap

This commit is contained in:
Vincent Chang 2013-07-29 17:59:53 +08:00
parent 7703e594ae
commit 03db682e11

View File

@ -10,6 +10,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/systemlibs.js");
var DEBUG = false; // set to true to show debug messages. var DEBUG = false; // set to true to show debug messages.
@ -67,6 +68,15 @@ const WIFI_SECURITY_TYPE_WPA2_PSK = "wpa2-psk";
const NETWORK_INTERFACE_UP = "up"; const NETWORK_INTERFACE_UP = "up";
const NETWORK_INTERFACE_DOWN = "down"; const NETWORK_INTERFACE_DOWN = "down";
const DEFAULT_WLAN_INTERFACE = "wlan0";
const DRIVER_READY_WAIT = 2000;
const SUPP_PROP = "init.svc.wpa_supplicant";
const WPA_SUPPLICANT = "wpa_supplicant";
const DHCP_PROP = "init.svc.dhcpcd";
const DHCP = "dhcpcd";
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager", XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
"@mozilla.org/network/manager;1", "@mozilla.org/network/manager;1",
"nsINetworkManager"); "nsINetworkManager");
@ -84,21 +94,29 @@ XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
// expected results). // expected results).
var WifiManager = (function() { var WifiManager = (function() {
function getStartupPrefs() { function getStartupPrefs() {
Cu.import("resource://gre/modules/systemlibs.js");
return { return {
sdkVersion: parseInt(libcutils.property_get("ro.build.version.sdk"), 10), sdkVersion: parseInt(libcutils.property_get("ro.build.version.sdk"), 10),
unloadDriverEnabled: libcutils.property_get("ro.moz.wifi.unloaddriver") === "1", unloadDriverEnabled: libcutils.property_get("ro.moz.wifi.unloaddriver") === "1",
schedScanRecovery: libcutils.property_get("ro.moz.wifi.sched_scan_recover") === "false" ? false : true schedScanRecovery: libcutils.property_get("ro.moz.wifi.sched_scan_recover") === "false" ? false : true,
driverDelay: libcutils.property_get("ro.moz.wifi.driverDelay"),
ifname: libcutils.property_get("wifi.interface")
}; };
} }
let {sdkVersion, unloadDriverEnabled, schedScanRecovery} = getStartupPrefs(); let {sdkVersion, unloadDriverEnabled, schedScanRecovery, driverDelay, ifname} = getStartupPrefs();
var controlWorker = new ChromeWorker(WIFIWORKER_WORKER); var controlWorker = new ChromeWorker(WIFIWORKER_WORKER);
var eventWorker = new ChromeWorker(WIFIWORKER_WORKER); var eventWorker = new ChromeWorker(WIFIWORKER_WORKER);
var manager = {}; var manager = {};
manager.ifname = ifname;
// Emulator build runs to here.
// The debug() should only be used after WifiManager.
if (!ifname) {
manager.ifname = DEFAULT_WLAN_INTERFACE;
}
manager.schedScanRecovery = schedScanRecovery; manager.schedScanRecovery = schedScanRecovery;
manager.driverDelay = driverDelay ? parseInt(driverDelay, 10) : DRIVER_READY_WAIT;
// Callbacks to invoke when a reply arrives from the controlWorker. // Callbacks to invoke when a reply arrives from the controlWorker.
var controlCallbacks = Object.create(null); var controlCallbacks = Object.create(null);
@ -711,12 +729,38 @@ var WifiManager = (function() {
}); });
} }
function stopProcess(service, process, callback) {
var count = 0;
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
function tick() {
let result = libcutils.property_get(service);
if (result === null) {
callback();
return;
}
if (result === "stopped" || ++count >= 5) {
// Either we succeeded or ran out of time.
timer = null;
callback();
return;
}
// Else it's still running, continue waiting.
timer.initWithCallback(tick, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
}
setProperty("ctl.stop", process, tick);
}
function stopDhcp(ifname, callback) { function stopDhcp(ifname, callback) {
controlMessage({ cmd: "dhcp_stop", ifname: ifname }, function(data) { // This function does exactly what dhcp_stop does. Unforunately, if we call
dhcpInfo = null; // this function twice before the previous callback is returned. We may block
notify("dhcplost"); // our self waiting for the callback. It slows down the wifi startup procedure.
callback(!data.status); // Therefore, we have to roll our own version here.
}); let dhcpService = DHCP_PROP + "_" + ifname;
let suffix = (ifname.substr(0, 3) === "p2p") ? "p2p" : ifname;
let processName = DHCP + "_" + suffix;
stopProcess(dhcpService, processName, callback);
} }
function releaseDhcpLease(ifname, callback) { function releaseDhcpLease(ifname, callback) {
@ -1114,34 +1158,13 @@ var WifiManager = (function() {
return true; return true;
} }
const SUPP_PROP = "init.svc.wpa_supplicant";
function killSupplicant(callback) { function killSupplicant(callback) {
// It is interesting to note that this function does exactly what // It is interesting to note that this function does exactly what
// wifi_stop_supplicant does. Unforunately, on the Galaxy S2, Samsung // wifi_stop_supplicant does. Unforunately, on the Galaxy S2, Samsung
// changed that function in a way that means that it doesn't recognize // changed that function in a way that means that it doesn't recognize
// wpa_supplicant as already running. Therefore, we have to roll our own // wpa_supplicant as already running. Therefore, we have to roll our own
// version here. // version here.
var count = 0; stopProcess(SUPP_PROP, WPA_SUPPLICANT, callback);
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
function tick() {
getProperty(SUPP_PROP, "stopped", function (result) {
if (result === null) {
callback();
return;
}
if (result === "stopped" || ++count >= 5) {
// Either we succeeded or ran out of time.
timer = null;
callback();
return;
}
// Else it's still running, continue waiting.
timer.initWithCallback(tick, 1000, Ci.nsITimer.TYPE_ONE_SHOT);
});
}
setProperty("ctl.stop", "wpa_supplicant", tick);
} }
function didConnectSupplicant(callback) { function didConnectSupplicant(callback) {
@ -1159,10 +1182,24 @@ var WifiManager = (function() {
} }
function prepareForStartup(callback) { function prepareForStartup(callback) {
let status = libcutils.property_get(DHCP_PROP + "_" + manager.ifname);
if (status !== "running") {
tryStopSupplicant();
return;
}
manager.connectionDropped(function() { manager.connectionDropped(function() {
// Ignore any errors and kill any currently-running supplicants. On some tryStopSupplicant();
// phones, stopSupplicant won't work for a supplicant that we didn't });
// start, so we hand-roll it here.
// Ignore any errors and kill any currently-running supplicants. On some
// phones, stopSupplicant won't work for a supplicant that we didn't
// start, so we hand-roll it here.
function tryStopSupplicant () {
let status = libcutils.property_get(SUPP_PROP);
if (status !== "running") {
callback();
return;
}
suppressEvents = true; suppressEvents = true;
killSupplicant(function() { killSupplicant(function() {
disableInterface(manager.ifname, function (ok) { disableInterface(manager.ifname, function (ok) {
@ -1170,7 +1207,7 @@ var WifiManager = (function() {
callback(); callback();
}); });
}); });
}); }
} }
// Initial state. // Initial state.
@ -1182,7 +1219,6 @@ var WifiManager = (function() {
manager.authenticationFailuresCount = 0; manager.authenticationFailuresCount = 0;
manager.loopDetectionCount = 0; manager.loopDetectionCount = 0;
const DRIVER_READY_WAIT = 2000;
var waitForDriverReadyTimer = null; var waitForDriverReadyTimer = null;
function cancelWaitForDriverReadyTimer() { function cancelWaitForDriverReadyTimer() {
if (waitForDriverReadyTimer) { if (waitForDriverReadyTimer) {
@ -1193,7 +1229,7 @@ var WifiManager = (function() {
function createWaitForDriverReadyTimer(onTimeout) { function createWaitForDriverReadyTimer(onTimeout) {
waitForDriverReadyTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); waitForDriverReadyTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
waitForDriverReadyTimer.initWithCallback(onTimeout, waitForDriverReadyTimer.initWithCallback(onTimeout,
DRIVER_READY_WAIT, manager.driverDelay,
Ci.nsITimer.TYPE_ONE_SHOT); Ci.nsITimer.TYPE_ONE_SHOT);
}; };
@ -1206,71 +1242,64 @@ var WifiManager = (function() {
if (enable) { if (enable) {
manager.state = "INITIALIZING"; manager.state = "INITIALIZING";
// Kill any existing connections if necessary. // Register as network interface.
getProperty("wifi.interface", "tiwlan0", function (ifname) { WifiNetworkInterface.name = manager.ifname;
if (!ifname) { if (!WifiNetworkInterface.registered) {
callback(-1); gNetworkManager.registerNetworkInterface(WifiNetworkInterface);
manager.state = "UNINITIALIZED"; WifiNetworkInterface.registered = true;
return; }
} WifiNetworkInterface.state = Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED;
manager.ifname = ifname; WifiNetworkInterface.ip = null;
WifiNetworkInterface.netmask = null;
// Register as network interface. WifiNetworkInterface.broadcast = null;
WifiNetworkInterface.name = ifname; WifiNetworkInterface.gateway = null;
if (!WifiNetworkInterface.registered) { WifiNetworkInterface.dns1 = null;
gNetworkManager.registerNetworkInterface(WifiNetworkInterface); WifiNetworkInterface.dns2 = null;
WifiNetworkInterface.registered = true; Services.obs.notifyObservers(WifiNetworkInterface,
} kNetworkInterfaceStateChangedTopic,
WifiNetworkInterface.state = Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED; null);
WifiNetworkInterface.ip = null; prepareForStartup(function() {
WifiNetworkInterface.netmask = null; loadDriver(function (status) {
WifiNetworkInterface.broadcast = null; if (status < 0) {
WifiNetworkInterface.gateway = null; callback(status);
WifiNetworkInterface.dns1 = null; manager.state = "UNINITIALIZED";
WifiNetworkInterface.dns2 = null; return;
Services.obs.notifyObservers(WifiNetworkInterface, }
kNetworkInterfaceStateChangedTopic, gNetworkManager.setWifiOperationMode(manager.ifname,
null); WIFI_FIRMWARE_STATION,
function (status) {
prepareForStartup(function() {
loadDriver(function (status) {
if (status) { if (status) {
callback(status); callback(status);
manager.state = "UNINITIALIZED"; manager.state = "UNINITIALIZED";
return; return;
} }
gNetworkManager.setWifiOperationMode(ifname,
WIFI_FIRMWARE_STATION,
function (status) {
if (status < 0) {
callback(status);
manager.state = "UNINITIALIZED";
return;
}
function doStartSupplicant() { function doStartSupplicant() {
cancelWaitForDriverReadyTimer(); cancelWaitForDriverReadyTimer();
startSupplicant(function (status) { startSupplicant(function (status) {
if (status < 0) { if (status < 0) {
unloadDriver(function() { unloadDriver(function() {
callback(status); callback(status);
});
manager.state = "UNINITIALIZED";
return;
}
manager.supplicantStarted = true;
enableInterface(ifname, function (ok) {
callback(ok ? 0 : -1);
}); });
}); manager.state = "UNINITIALIZED";
} return;
}
// Driver startup on certain platforms takes longer than it takes for us manager.supplicantStarted = true;
// to return from loadDriver, so wait 2 seconds before starting enableInterface(manager.ifname, function (ok) {
// the supplicant to give it a chance to start. callback(ok ? 0 : -1);
});
});
}
// Driver startup on certain platforms takes longer than it takes for us
// to return from loadDriver, so wait 2 seconds before starting
// the supplicant to give it a chance to start.
if (manager.driverDelay > 0) {
createWaitForDriverReadyTimer(doStartSupplicant); createWaitForDriverReadyTimer(doStartSupplicant);
}); } else {
doStartSupplicant();
}
}); });
}); });
}); });
@ -1297,42 +1326,34 @@ var WifiManager = (function() {
manager.setWifiApEnabled = function(enabled, configuration, callback) { manager.setWifiApEnabled = function(enabled, configuration, callback) {
if (enabled) { if (enabled) {
manager.tetheringState = "INITIALIZING"; manager.tetheringState = "INITIALIZING";
getProperty("wifi.interface", "tiwlan0", function (ifname) { loadDriver(function (status) {
if (!ifname) { if (status < 0) {
callback(); callback();
manager.tetheringState = "UNINITIALIZED"; manager.tetheringState = "UNINITIALIZED";
return; return;
} }
manager.ifname = ifname;
loadDriver(function (status) { function doStartWifiTethering() {
if (status < 0) { cancelWaitForDriverReadyTimer();
WifiNetworkInterface.name = manager.ifname;
gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface,
configuration, function(result) {
if (result) {
manager.tetheringState = "UNINITIALIZED";
} else {
manager.tetheringState = "COMPLETED";
}
// Pop out current request.
callback(); callback();
manager.tetheringState = "UNINITIALIZED"; // Should we fire a dom event if we fail to set wifi tethering ?
return; debug("Enable Wifi tethering result: " + (result ? result : "successfully"));
} });
}
function doStartWifiTethering() { // Driver startup on certain platforms takes longer than it takes
cancelWaitForDriverReadyTimer(); // for us to return from loadDriver, so wait 2 seconds before
WifiNetworkInterface.name = manager.ifname; // turning on Wifi tethering.
gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface, createWaitForDriverReadyTimer(doStartWifiTethering);
configuration, function(result) {
if (result) {
manager.tetheringState = "UNINITIALIZED";
} else {
manager.tetheringState = "COMPLETED";
}
// Pop out current request.
callback();
// Should we fire a dom event if we fail to set wifi tethering ?
debug("Enable Wifi tethering result: " + (result ? result : "successfully"));
});
}
// Driver startup on certain platforms takes longer than it takes
// for us to return from loadDriver, so wait 2 seconds before
// turning on Wifi tethering.
createWaitForDriverReadyTimer(doStartWifiTethering);
});
}); });
} else { } else {
gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface, gNetworkManager.setWifiTethering(enabled, WifiNetworkInterface,
@ -2160,6 +2181,16 @@ function WifiWorker() {
self.currentNetwork.bssid = WifiManager.connectionInfo.bssid; self.currentNetwork.bssid = WifiManager.connectionInfo.bssid;
break; break;
case "DISCONNECTED": case "DISCONNECTED":
// wpa_supplicant may give us a "DISCONNECTED" event even if
// we are already in "DISCONNECTED" state.
if (this.prevState === "INITIALIZING" ||
this.prevState === "DISCONNECTED" ||
this.prevState === "INTERFACE_DISABLED" ||
this.prevState === "INACTIVE" ||
this.prevState === "UNINITIALIZED") {
return;
}
self._fireEvent("ondisconnect", {}); self._fireEvent("ondisconnect", {});
self.currentNetwork = null; self.currentNetwork = null;
self.ipAddress = ""; self.ipAddress = "";