Bug 811635 - Part 4: Wifi Direct core implementation. r=vchang

This commit is contained in:
Henry Chang 2013-11-28 16:02:44 +08:00
parent 5a2911852e
commit 8ba8195b75
9 changed files with 2354 additions and 100 deletions

View File

@ -402,6 +402,8 @@
#ifdef MOZ_WIDGET_GONK
@BINPATH@/components/DOMWifiManager.js
@BINPATH@/components/DOMWifiManager.manifest
@BINPATH@/components/DOMWifiP2pManager.js
@BINPATH@/components/DOMWifiP2pManager.manifest
@BINPATH@/components/NetworkInterfaceListService.js
@BINPATH@/components/NetworkInterfaceListService.manifest
@BINPATH@/components/NetworkManager.js

View File

@ -9,7 +9,7 @@ interface nsIWifiTetheringCallback;
/**
* Information about networks that is exposed to network manager API consumers.
*/
[scriptable, uuid(f4cf9d88-f962-4d29-9baa-fb295dad387b)]
[scriptable, uuid(e2f5c6e0-4203-11e3-aa6e-0800200c9a66)]
interface nsINetworkInterface : nsISupports
{
const long NETWORK_STATE_UNKNOWN = -1;
@ -31,6 +31,7 @@ interface nsINetworkInterface : nsISupports
const long NETWORK_TYPE_MOBILE = 1;
const long NETWORK_TYPE_MOBILE_MMS = 2;
const long NETWORK_TYPE_MOBILE_SUPL = 3;
const long NETWORK_TYPE_WIFI_P2P = 4;
/**
* Network type. One of the NETWORK_TYPE_* constants.

205
dom/wifi/StateMachine.jsm Normal file
View File

@ -0,0 +1,205 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* 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/Services.jsm");
this.EXPORTED_SYMBOLS = ["StateMachine"];
const DEBUG = true;
this.StateMachine = function(aDebugTag) {
function debug(aMsg) {
dump('-------------- StateMachine:' + aDebugTag + ': ' + aMsg);
}
var sm = {};
var _initialState;
var _curState;
var _prevState;
var _paused;
var _eventQueue = [];
var _deferredEventQueue = [];
var _defaultEventHandler;
// Public interfaces.
sm.setDefaultEventHandler = function(aDefaultEventHandler) {
_defaultEventHandler = aDefaultEventHandler;
};
sm.start = function(aInitialState) {
_initialState = aInitialState;
sm.gotoState(_initialState);
};
sm.sendEvent = function (aEvent) {
if (!_initialState) {
if (DEBUG) {
debug('StateMachine is not running. Call StateMachine.start() first.');
}
return;
}
_eventQueue.push(aEvent);
asyncCall(handleFirstEvent);
};
sm.getPreviousState = function() {
return _prevState;
};
sm.getCurrentState = function() {
return _curState;
};
// State object maker.
// @param aName string for this state's name.
// @param aDelegate object:
// .handleEvent: required.
// .enter: called before entering this state (optional).
// .exit: called before exiting this state (optional).
sm.makeState = function (aName, aDelegate) {
if (!aDelegate.handleEvent) {
throw "handleEvent is a required delegate function.";
}
var nop = function() {};
return {
name: aName,
enter: (aDelegate.enter || nop),
exit: (aDelegate.exit || nop),
handleEvent: aDelegate.handleEvent
};
};
sm.deferEvent = function (aEvent) {
// The definition of a 'deferred event' is:
// We are not able to handle this event now but after receiving
// certain event or entering a new state, we might be able to handle
// it. For example, we couldn't handle CONNECT_EVENT in the
// diconnecting state. But once we finish doing "disconnecting", we
// could then handle CONNECT_EVENT!
//
// So, the deferred event may be handled in the following cases:
// 1. Once we entered a new state.
// 2. Once we handled a regular event.
if (DEBUG) {
debug('Deferring event: ' + JSON.stringify(aEvent));
}
_deferredEventQueue.push(aEvent);
};
// Goto the new state. If the current state is null, the exit
// function won't be called.
sm.gotoState = function (aNewState) {
if (_curState) {
if (DEBUG) {
debug("exiting state: " + _curState.name);
}
_curState.exit();
}
_prevState = _curState;
_curState = aNewState;
if (DEBUG) {
debug("entering state: " + _curState.name);
}
_curState.enter();
// We are in the new state now. We got a chance to handle the
// deferred events.
handleDeferredEvents();
sm.resume();
};
// No incoming event will be handled after you call pause().
// (But they will be queued.)
sm.pause = function() {
_paused = true;
};
// Continue to handle incoming events.
sm.resume = function() {
_paused = false;
asyncCall(handleFirstEvent);
};
//----------------------------------------------------------
// Private stuff
//----------------------------------------------------------
function asyncCall(f) {
Services.tm.currentThread.dispatch(f, Ci.nsIThread.DISPATCH_NORMAL);
}
function handleFirstEvent() {
var hadDeferredEvents;
if (0 === _eventQueue.length) {
return;
}
if (_paused) {
return; // The state machine is paused now.
}
hadDeferredEvents = _deferredEventQueue.length > 0;
handleOneEvent(_eventQueue.shift()); // The handler may defer this event.
// We've handled one event. If we had deferred events before, now is
// a good chance to handle them.
if (hadDeferredEvents) {
handleDeferredEvents();
}
// Continue to handle the next regular event.
handleFirstEvent();
}
function handleDeferredEvents() {
if (_deferredEventQueue.length && DEBUG) {
debug('Handle deferred events: ' + _deferredEventQueue.length);
}
for (let i = 0; i < _deferredEventQueue.length; i++) {
handleOneEvent(_deferredEventQueue.shift());
}
}
function handleOneEvent(aEvent)
{
if (DEBUG) {
debug('Handling event: ' + JSON.stringify(aEvent));
}
var handled = _curState.handleEvent(aEvent);
if (undefined === handled) {
throw "handleEvent returns undefined: " + _curState.name;
}
if (!handled) {
// Event is not handled in the current state. Try handleEventCommon().
handled = (_defaultEventHandler ? _defaultEventHandler(aEvent) : handled);
}
if (undefined === handled) {
throw "handleEventCommon returns undefined: " + _curState.name;
}
if (!handled) {
if (DEBUG) {
debug('!!!!!!!!! FIXME !!!!!!!!! Event not handled: ' + JSON.stringify(aEvent));
}
}
return handled;
}
return sm;
};

View File

@ -14,8 +14,15 @@ Cu.import("resource://gre/modules/systemlibs.js");
const SUPP_PROP = "init.svc.wpa_supplicant";
const WPA_SUPPLICANT = "wpa_supplicant";
const DEBUG = false;
this.WifiCommand = function(aControlMessage, aInterface) {
function debug(msg) {
if (DEBUG) {
dump('-------------- WifiCommand: ' + msg);
}
}
var command = {};
//-------------------------------------------------
@ -135,14 +142,16 @@ this.WifiCommand = function(aControlMessage, aInterface) {
doStringCommand("LOG_LEVEL", callback);
};
command.wpsPbc = function (callback) {
doBooleanCommand("WPS_PBC", "OK", callback);
command.wpsPbc = function (iface, callback) {
doBooleanCommand("WPS_PBC" + (iface ? (" interface=" + iface) : ""),
"OK", callback);
};
command.wpsPin = function (detail, callback) {
doStringCommand("WPS_PIN " +
(detail.bssid === undefined ? "any" : detail.bssid) +
(detail.pin === undefined ? "" : (" " + detail.pin)),
(detail.pin === undefined ? "" : (" " + detail.pin)) +
(detail.iface ? (" interface=" + detail.iface) : ""),
callback);
};
@ -332,9 +341,89 @@ this.WifiCommand = function(aControlMessage, aInterface) {
});
};
//--------------------------------------------------
// Helper functions.
//--------------------------------------------------
command.setDeviceName = function(deviceName, callback) {
doBooleanCommand("SET device_name " + deviceName, "OK", callback);
};
//-------------------------------------------------
// P2P commands.
//-------------------------------------------------
command.p2pProvDiscovery = function(address, wpsMethod, callback) {
var command = "P2P_PROV_DISC " + address + " " + wpsMethod;
doBooleanCommand(command, "OK", callback);
};
command.p2pConnect = function(config, callback) {
var command = "P2P_CONNECT " + config.address + " " + config.wpsMethodWithPin + " ";
if (config.joinExistingGroup) {
command += "join";
} else {
command += "go_intent=" + config.goIntent;
}
debug('P2P connect command: ' + command);
doBooleanCommand(command, "OK", callback);
};
command.p2pGroupRemove = function(iface, callback) {
debug("groupRemove()");
doBooleanCommand("P2P_GROUP_REMOVE " + iface, "OK", callback);
};
command.p2pEnable = function(detail, callback) {
var commandChain = ["SET device_name " + detail.deviceName,
"SET device_type " + detail.deviceType,
"SET config_methods " + detail.wpsMethods,
"P2P_SET conc_pref sta",
"P2P_FLUSH"];
doBooleanCommandChain(commandChain, callback);
};
command.p2pDisable = function(callback) {
doBooleanCommand("P2P_SET disabled 1", "OK", callback);
};
command.p2pEnableScan = function(timeout, callback) {
doBooleanCommand("P2P_FIND " + timeout, "OK", callback);
};
command.p2pDisableScan = function(callback) {
doBooleanCommand("P2P_STOP_FIND", "OK", callback);
};
command.p2pGetGroupCapab = function(address, callback) {
command.p2pPeer(address, function(reply) {
debug('p2p_peer reply: ' + reply);
if (!reply) {
callback(0);
return;
}
var capab = /group_capab=0x([0-9a-fA-F]+)/.exec(reply)[1];
if (!capab) {
callback(0);
} else {
callback(parseInt(capab, 16));
}
});
};
command.p2pPeer = function(address, callback) {
doStringCommand("P2P_PEER " + address, callback);
};
command.p2pGroupAdd = function(netId, callback) {
doBooleanCommand("P2P_GROUP_ADD persistent=" + netId, callback);
};
command.p2pReinvoke = function(netId, address, callback) {
doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + address, "OK", callback);
};
//----------------------------------------------------------
// Private stuff.
//----------------------------------------------------------
function voidControlMessage(cmd, callback) {
aControlMessage({ cmd: cmd, iface: aInterface }, function (data) {
@ -386,6 +475,10 @@ this.WifiCommand = function(aControlMessage, aInterface) {
});
}
//--------------------------------------------------
// Helper functions.
//--------------------------------------------------
function stopProcess(service, process, callback) {
var count = 0;
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);

View File

@ -11,16 +11,23 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/systemlibs.js");
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
"@mozilla.org/network/manager;1",
"nsINetworkManager");
XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
"@mozilla.org/network/service;1",
"nsINetworkService");
this.EXPORTED_SYMBOLS = ["WifiNetUtil"];
const DHCP_PROP = "init.svc.dhcpcd";
const DHCP = "dhcpcd";
const DEBUG = false;
this.WifiNetUtil = function(controlMessage) {
function debug(msg) {
if (DEBUG) {
dump('-------------- NetUtil: ' + msg);
}
}
var util = {};
util.configureInterface = function(cfg, callback) {
@ -67,14 +74,14 @@ this.WifiNetUtil = function(controlMessage) {
});
};
util.startDhcpServer = function (range, callback) {
gNetworkManager.setDhcpServer(true, range, function (error) {
util.startDhcpServer = function (config, callback) {
gNetworkService.setDhcpServer(true, config, function (error) {
callback(!error);
});
};
util.stopDhcpServer = function (callback) {
gNetworkManager.setDhcpServer(false, null, function (error) {
gNetworkService.setDhcpServer(false, null, function (error) {
callback(!error);
});
};
@ -135,6 +142,7 @@ this.WifiNetUtil = function(controlMessage) {
util.runIpConfig = function (name, data, callback) {
if (!data) {
debug("IP config failed to run");
callback({ info: data });
return;
}
@ -142,16 +150,19 @@ this.WifiNetUtil = function(controlMessage) {
setProperty("net." + name + ".dns1", ipToString(data.dns1),
function(ok) {
if (!ok) {
debug("Unable to set net.<ifname>.dns1");
return;
}
setProperty("net." + name + ".dns2", ipToString(data.dns2),
function(ok) {
if (!ok) {
debug("Unable to set net.<ifname>.dns2");
return;
}
setProperty("net." + name + ".gw", ipToString(data.gateway),
function(ok) {
if (!ok) {
debug("Unable to set net.<ifname>.gw");
return;
}
callback({ info: data });

1597
dom/wifi/WifiP2pManager.jsm Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,303 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* 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;
const CONNECTION_STATUS_DISCONNECTED = "disconnected";
const CONNECTION_STATUS_CONNECTING = "connecting";
const CONNECTION_STATUS_CONNECTED = "connected";
const CONNECTION_STATUS_DISCONNECTING = "disconnecting";
const DEBUG = false;
this.EXPORTED_SYMBOLS = ["WifiP2pWorkerObserver"];
// WifiP2pWorkerObserver resides in WifiWorker to handle DOM message
// by either 1) returning internally maintained information or
// 2) delegating to aDomMsgResponder. It is also responsible
// for observing events from WifiP2pManager and dispatch to DOM.
//
// @param aDomMsgResponder handles DOM messages, including
// - setScanEnabled
// - connect
// - disconnect
// - setPairingConfirmation
// The instance is actually WifiP2pManager.
this.WifiP2pWorkerObserver = function(aDomMsgResponder) {
function debug(aMsg) {
if (DEBUG) {
dump('-------------- WifiP2pWorkerObserver: ' + aMsg);
}
}
// Private member variables.
let _localDevice;
let _peerList = {}; // List of P2pDevice.
let _domManagers = [];
// Constructor of P2pDevice. It will be exposed to DOM.
//
// @param aPeer object representing a P2P device:
// .name: string for the device name.
// .address: Mac address.
// .isGroupOwner: boolean to indicate if this device is the group owner.
// .wpsCapabilities: array of string of {"pbc", "display", "keypad"}.
function P2pDevice(aPeer) {
this.address = aPeer.address;
this.name = (aPeer.name ? aPeer.name : aPeer.address);
this.isGroupOwner = aPeer.isGroupOwner;
this.wpsCapabilities = aPeer.wpsCapabilities;
this.connectionStatus = CONNECTION_STATUS_DISCONNECTED;
// Since this object will be exposed to web, defined the exposed
// properties here.
this.__exposedProps__ = {
address: "r",
name: "r",
isGroupOwner: "r",
wpsCapabilities: "r",
connectionStatus: "r"
};
}
// Constructor of P2pGroupOwner.
//
// @param aGroupOwner:
// .macAddress
// .ipAddress
// .passphrase
// .ssid
// .freq
// .isLocal
function P2pGroupOwner(aGroupOwner) {
this.macAddress = aGroupOwner.macAddress; // The identifier to get further information.
this.ipAddress = aGroupOwner.ipAddress;
this.passphrase = aGroupOwner.passphrase;
this.ssid = aGroupOwner.ssid; // e.g. DIRECT-xy.
this.freq = aGroupOwner.freq;
this.isLocal = aGroupOwner.isLocal;
let detail = _peerList[aGroupOwner.macAddress];
if (detail) {
this.name = detail.name;
this.wpsCapabilities = detail.wpsCapabilities;
} else if (_localDevice.address === this.macAddress) {
this.name = _localDevice.name;
this.wpsCapabilities = _localDevice.wpsCapabilities;
} else {
debug("We don't know this group owner: " + aGroupOwner.macAddress);
this.name = aGroupOwner.macAddress;
this.wpsCapabilities = [];
}
}
function fireEvent(aMessage, aData) {
debug('domManager: ' + JSON.stringify(_domManagers));
_domManagers.forEach(function(manager) {
// Note: We should never have a dead message manager here because we
// observe our child message managers shutting down below.
manager.sendAsyncMessage("WifiP2pManager:" + aMessage, aData);
});
}
function addDomManager(aMsg) {
if (-1 === _domManagers.indexOf(aMsg.manager)) {
_domManagers.push(aMsg.manager);
}
}
function returnMessage(aMessage, aSuccess, aData, aMsg) {
let rMsg = aMessage + ":Return:" + (aSuccess ? "OK" : "NO");
aMsg.manager.sendAsyncMessage(rMsg,
{ data: aData, rid: aMsg.rid, mid: aMsg.mid });
}
function handlePeerListUpdated() {
fireEvent("onpeerinfoupdate", {});
}
// Return a literal object as the constructed object.
return {
onLocalDeviceChanged: function(aDevice) {
_localDevice = aDevice;
debug('Local device updated to: ' + JSON.stringify(_localDevice));
},
onEnabled: function() {
_peerList = [];
fireEvent("p2pUp", {});
},
onDisbaled: function() {
fireEvent("p2pDown", {});
},
onPeerFound: function(aPeer) {
let newFoundPeer = new P2pDevice(aPeer);
let origianlPeer = _peerList[aPeer.address];
_peerList[aPeer.address] = newFoundPeer;
if (origianlPeer) {
newFoundPeer.connectionStatus = origianlPeer.connectionStatus;
}
handlePeerListUpdated();
},
onPeerLost: function(aPeer) {
let lostPeer = _peerList[aPeer.address];
if (!lostPeer) {
debug('Unknown peer lost: ' + aPeer.address);
return;
}
delete _peerList[aPeer.address];
handlePeerListUpdated();
},
onConnecting: function(aPeer) {
let peer = _peerList[aPeer.address];
if (!peer) {
debug('Unknown peer connecting: ' + aPeer.address);
peer = new P2pDevice(aPeer);
_peerList[aPeer.address] = peer;
handlePeerListUpdated();
}
peer.connectionStatus = CONNECTION_STATUS_CONNECTING;
fireEvent('onconnecting', { peer: peer });
},
onConnected: function(aGroupOwner, aPeer) {
let go = new P2pGroupOwner(aGroupOwner);
let peer = _peerList[aPeer.address];
if (!peer) {
debug('Unknown peer connected: ' + aPeer.address);
peer = new P2pDevice(aPeer);
_peerList[aPeer.address] = peer;
handlePeerListUpdated();
}
peer.connectionStatus = CONNECTION_STATUS_CONNECTED;
peer.isGroupOwner = (aPeer.address === aGroupOwner.address);
fireEvent('onconnected', { groupOwner: go, peer: peer });
},
onDisconnected: function(aPeer) {
let peer = _peerList[aPeer.address];
if (!peer) {
debug('Unknown peer disconnected: ' + aPeer.address);
return;
}
peer.connectionStatus = CONNECTION_STATUS_DISCONNECTED;
fireEvent('ondisconnected', { peer: peer });
},
getObservedDOMMessages: function() {
return [
"WifiP2pManager:getState",
"WifiP2pManager:getPeerList",
"WifiP2pManager:setScanEnabled",
"WifiP2pManager:connect",
"WifiP2pManager:disconnect",
"WifiP2pManager:setPairingConfirmation",
"WifiP2pManager:setDeviceName"
];
},
onDOMMessage: function(aMessage) {
let msg = aMessage.data || {};
msg.manager = aMessage.target;
if ("child-process-shutdown" === aMessage.name) {
let i;
if (-1 !== (i = _domManagers.indexOf(msg.manager))) {
_domManagers.splice(i, 1);
}
return;
}
if (!aMessage.target.assertPermission("wifi-manage")) {
return;
}
switch (aMessage.name) {
case "WifiP2pManager:getState": // A new DOM manager is created.
addDomManager(msg);
return { peerList: _peerList, }; // Synchronous call. Simply return it.
case "WifiP2pManager:setScanEnabled":
{
let enabled = msg.data;
aDomMsgResponder.setScanEnabled(enabled, function(success) {
returnMessage(aMessage.name, success, (success ? true : "ERROR"), msg);
});
}
break;
case "WifiP2pManager:getPeerList":
{
// Convert the object to an array.
let peerArray = [];
for (let key in _peerList) {
if (_peerList.hasOwnProperty(key)) {
peerArray.push(_peerList[key]);
}
}
returnMessage(aMessage.name, true, peerArray, msg);
}
break;
case "WifiP2pManager:connect":
{
let peer = msg.data;
let onDoConnect = function(success) {
returnMessage(aMessage.name, success, (success ? true : "ERROR"), msg);
};
aDomMsgResponder.connect(peer.address, peer.wpsMethod,
peer.goIntent, onDoConnect);
}
break;
case "WifiP2pManager:disconnect":
{
let address = msg.data;
aDomMsgResponder.disconnect(address, function(success) {
returnMessage(aMessage.name, success, (success ? true : "ERROR"), msg);
});
}
break;
case "WifiP2pManager:setPairingConfirmation":
{
let result = msg.data;
aDomMsgResponder.setPairingConfirmation(result);
returnMessage(aMessage.name, true, true, msg);
}
break;
case "WifiP2pManager:setDeviceName":
{
let newDeviceName = msg.data;
aDomMsgResponder.setDeviceName(newDeviceName, function(success) {
returnMessage(aMessage.name, success, (success ? true : "ERROR"), msg);
});
}
break;
default:
if (0 === aMessage.name.indexOf("WifiP2pManager:")) {
debug("DOM WifiP2pManager message not handled: " + aMessage.name);
}
} // End of switch.
}
};
};

View File

@ -13,6 +13,8 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/systemlibs.js");
Cu.import("resource://gre/modules/WifiCommand.jsm");
Cu.import("resource://gre/modules/WifiNetUtil.jsm");
Cu.import("resource://gre/modules/WifiP2pManager.jsm");
Cu.import("resource://gre/modules/WifiP2pWorkerObserver.jsm");
var DEBUG = false; // set to true to show debug messages.
@ -108,16 +110,30 @@ var WifiManager = (function() {
unloadDriverEnabled: libcutils.property_get("ro.moz.wifi.unloaddriver") === "1",
schedScanRecovery: libcutils.property_get("ro.moz.wifi.sched_scan_recover") === "false" ? false : true,
driverDelay: libcutils.property_get("ro.moz.wifi.driverDelay"),
p2pSupported: libcutils.property_get("ro.moz.wifi.p2p_supported") === "1",
ifname: libcutils.property_get("wifi.interface")
};
}
let {sdkVersion, unloadDriverEnabled, schedScanRecovery, driverDelay, ifname} = getStartupPrefs();
let {sdkVersion, unloadDriverEnabled, schedScanRecovery, driverDelay, p2pSupported, ifname} = getStartupPrefs();
let wifiListener = {
onWaitEvent: function(event, iface) {
if (manager.ifname === iface && handleEvent(event)) {
waitForEvent(iface);
} else if (p2pSupported) {
if (WifiP2pManager.INTERFACE_NAME === iface) {
// If the connection is closed, wifi.c::wifi_wait_for_event()
// will still return 'CTRL-EVENT-TERMINATING - connection closed'
// rather than blocking. So when we see this special event string,
// just return immediately.
const TERMINATED_EVENT = 'CTRL-EVENT-TERMINATING - connection closed';
if (-1 !== event.indexOf(TERMINATED_EVENT)) {
return;
}
p2pManager.handleEvent(event);
waitForEvent(iface);
}
}
},
@ -135,18 +151,29 @@ var WifiManager = (function() {
manager.schedScanRecovery = schedScanRecovery;
manager.driverDelay = driverDelay ? parseInt(driverDelay, 10) : DRIVER_READY_WAIT;
// Regular Wifi stuff.
var netUtil = WifiNetUtil(controlMessage);
var wifiCommand = WifiCommand(controlMessage, manager.ifname);
// Wifi P2P stuff
var p2pManager;
if (p2pSupported) {
let p2pCommand = WifiCommand(controlMessage, WifiP2pManager.INTERFACE_NAME);
p2pManager = WifiP2pManager(p2pCommand, netUtil);
}
let wifiService = Cc["@mozilla.org/wifi/service;1"];
if (wifiService) {
wifiService = wifiService.getService(Ci.nsIWifiProxyService);
let interfaces = [manager.ifname];
if (p2pSupported) {
interfaces.push(WifiP2pManager.INTERFACE_NAME);
}
wifiService.start(wifiListener, interfaces, interfaces.length);
} else {
debug("No wifi service component available!");
}
var wifiCommand = WifiCommand(controlMessage, manager.ifname);
var netUtil = WifiNetUtil(controlMessage);
// Callbacks to invoke when a reply arrives from the wifi service.
var controlCallbacks = Object.create(null);
var idgen = 0;
@ -240,6 +267,7 @@ var WifiManager = (function() {
wifiCommand.doSetScanMode(true, function(ignore) {
setBackgroundScan("OFF", function(turned, ignore) {
reEnableBackgroundScan = turned;
manager.handlePreWifiScan();
wifiCommand.scan(function(ok) {
wifiCommand.doSetScanMode(false, function(ignore) {
// The result of scanCommand is the result of the actual SCAN
@ -251,6 +279,7 @@ var WifiManager = (function() {
});
return;
}
manager.handlePreWifiScan();
wifiCommand.scan(callback);
}
@ -263,6 +292,7 @@ var WifiManager = (function() {
if (ok)
debugEnabled = wanted;
});
p2pManager.setDebug(DEBUG);
}
}
@ -751,6 +781,7 @@ var WifiManager = (function() {
reEnableBackgroundScan = false;
setBackgroundScan("ON", function() {});
}
manager.handlePostWifiScan();
notify("scanresultsavailable");
return true;
}
@ -782,6 +813,10 @@ var WifiManager = (function() {
notify("supplicantconnection");
callback();
});
if (p2pSupported) {
manager.enableP2p(function(success) {});
}
}
function prepareForStartup(callback) {
@ -910,6 +945,7 @@ var WifiManager = (function() {
// Note these following calls ignore errors. If we fail to kill the
// supplicant gracefully, then we need to continue telling it to die
// until it does.
let doDisableWifi = function() {
manager.state = "DISABLING";
wifiCommand.terminateSupplicant(function (ok) {
manager.connectionDropped(function () {
@ -924,6 +960,13 @@ var WifiManager = (function() {
});
});
}
if (p2pSupported) {
p2pManager.setEnabled(false, { onDisabled: doDisableWifi });
} else {
doDisableWifi();
}
}
}
// Get wifi interface and load wifi driver when enable Ap mode.
@ -1134,6 +1177,11 @@ var WifiManager = (function() {
wifiCommand.saveConfig(callback);
}
manager.enableNetwork = function(netId, disableOthers, callback) {
if (p2pSupported) {
// We have to stop wifi direct scan before associating to an AP.
// Otherwise we will get a "REJECT" wpa supplicant event.
p2pManager.setScanEnabled(false, function(success) {});
}
wifiCommand.enableNetwork(netId, disableOthers, callback);
}
manager.disableNetwork = function(netId, callback) {
@ -1212,6 +1260,46 @@ var WifiManager = (function() {
}
}
manager.handlePreWifiScan = function() {
if (p2pSupported) {
// Before doing regular wifi scan, we have to disable wifi direct
// scan first. Otherwise we will never get the scan result.
p2pManager.blockScan();
}
};
manager.handlePostWifiScan = function() {
if (p2pSupported) {
// After regular wifi scanning, we should restore the restricted
// wifi direct scan.
p2pManager.unblockScan();
}
};
//
// Public APIs for P2P.
//
manager.p2pSupported = function() {
return p2pSupported;
};
manager.getP2pManager = function() {
return p2pManager;
};
manager.enableP2p = function(callback) {
p2pManager.setEnabled(true, {
onSupplicantConnected: function() {
wifiService.waitForEvent(WifiP2pManager.INTERFACE_NAME);
},
onEnabled: function(success) {
callback(success);
}
});
};
return manager;
})();
@ -1494,6 +1582,17 @@ function WifiWorker() {
this._connectionInfoTimer = null;
this._reconnectOnDisconnect = false;
// Create p2pObserver and assign to p2pManager.
if (WifiManager.p2pSupported()) {
this._p2pObserver = WifiP2pWorkerObserver(WifiManager.getP2pManager());
WifiManager.getP2pManager().setObserver(this._p2pObserver);
// Add DOM message observerd by p2pObserver to the message listener as well.
this._p2pObserver.getObservedDOMMessages().forEach((function(msgName) {
this._mm.addMessageListener(msgName, this);
}).bind(this));
}
// Users of instances of nsITimer should keep a reference to the timer until
// it is no longer needed in order to assure the timer is fired.
this._callbackTimer = null;
@ -1526,6 +1625,7 @@ function WifiWorker() {
// wait for our next command) ensure that background scanning is on and
// then try again.
debug("Determined that scanning is stuck, turning on background scanning!");
WifiManager.handlePostWifiScan();
WifiManager.disconnect(function(ok) {});
self._turnOnBackgroundScan = true;
}
@ -2301,6 +2401,15 @@ WifiWorker.prototype = {
let msg = aMessage.data || {};
msg.manager = aMessage.target;
if (WifiManager.p2pSupported()) {
// If p2pObserver returns something truthy, return it!
// Otherwise, continue to do the rest of tasks.
var p2pRet = this._p2pObserver.onDOMMessage(aMessage);
if (p2pRet) {
return p2pRet;
}
}
// Note: By the time we receive child-process-shutdown, the child process
// has already forgotten its permissions so we do this before the
// permissions check.
@ -2390,79 +2499,6 @@ WifiWorker.prototype = {
}).bind(this));
},
getWifiScanResults: function(callback) {
var count = 0;
var timer = null;
var self = this;
self.waitForScan(waitForScanCallback);
doScan();
function doScan() {
WifiManager.scan(true, function (ok) {
if (!ok) {
if (!timer) {
count = 0;
timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
}
if (count++ >= 3) {
timer = null;
this.wantScanResults.splice(this.wantScanResults.indexOf(waitForScanCallback), 1);
callback.onfailure();
return;
}
// Else it's still running, continue waiting.
timer.initWithCallback(doScan, 10000, Ci.nsITimer.TYPE_ONE_SHOT);
return;
}
});
}
function waitForScanCallback(networks) {
if (networks === null) {
callback.onfailure();
return;
}
var wifiScanResults = new Array();
var net;
for (let net in networks) {
let value = networks[net];
wifiScanResults.push(transformResult(value));
}
callback.onready(wifiScanResults.length, wifiScanResults);
}
function transformResult(element) {
var result = new WifiScanResult();
result.connected = false;
for (let id in element) {
if (id === "__exposedProps__") {
continue;
}
if (id === "security") {
result[id] = 0;
var security = element[id];
for (let j = 0; j < security.length; j++) {
if (security[j] === "WPA-PSK") {
result[id] |= Ci.nsIWifiScanResult.WPA_PSK;
} else if (security[j] === "WPA-EAP") {
result[id] |= Ci.nsIWifiScanResult.WPA_EAP;
} else if (security[j] === "WEP") {
result[id] |= Ci.nsIWifiScanResult.WEP;
} else {
result[id] = 0;
}
}
} else {
result[id] = element[id];
}
}
return result;
}
},
getKnownNetworks: function(msg) {
const message = "WifiManager:getKnownNetworks:Return";
if (!WifiManager.enabled) {
@ -2719,7 +2755,7 @@ WifiWorker.prototype = {
let self = this;
let detail = msg.data;
if (detail.method === "pbc") {
WifiManager.wpsPbc(function(ok) {
WifiManager.wpsPbc(null, function(ok) {
if (ok)
self._sendMessage(message, true, true, msg);
else

View File

@ -6,6 +6,7 @@
XPIDL_SOURCES += [
'nsIDOMMozWifiConnectionInfoEvent.idl',
'nsIDOMMozWifiP2pStatusChangeEvent.idl',
'nsIDOMMozWifiStatusChangeEvent.idl',
'nsIWifi.idl',
'nsIWifiService.idl',
@ -16,13 +17,18 @@ XPIDL_MODULE = 'dom_wifi'
EXTRA_COMPONENTS += [
'DOMWifiManager.js',
'DOMWifiManager.manifest',
'DOMWifiP2pManager.js',
'DOMWifiP2pManager.manifest',
'WifiWorker.js',
'WifiWorker.manifest',
]
EXTRA_JS_MODULES += [
'StateMachine.jsm',
'WifiCommand.jsm',
'WifiNetUtil.jsm',
'WifiP2pManager.jsm',
'WifiP2pWorkerObserver.jsm',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':