Bug 912015 - Support multiple wap_supplicant connections. r=mrbkap

This commit is contained in:
Henry Chang 2013-10-14 16:33:59 +08:00
parent 11e739f7c6
commit 4b2cee8645
7 changed files with 138 additions and 76 deletions

View File

@ -15,7 +15,7 @@ Cu.import("resource://gre/modules/systemlibs.js");
const SUPP_PROP = "init.svc.wpa_supplicant";
const WPA_SUPPLICANT = "wpa_supplicant";
this.WifiCommand = function(controlMessage) {
this.WifiCommand = function(aControlMessage, aInterface) {
var command = {};
//-------------------------------------------------
@ -337,16 +337,17 @@ this.WifiCommand = function(controlMessage) {
//--------------------------------------------------
function voidControlMessage(cmd, callback) {
controlMessage({ cmd: cmd }, function (data) {
aControlMessage({ cmd: cmd, iface: aInterface }, function (data) {
callback(data.status);
});
}
function doCommand(request, callback) {
var msg = { cmd: "command",
request: request };
request: request,
iface: aInterface };
controlMessage(msg, callback);
aControlMessage(msg, callback);
}
function doIntCommand(request, callback) {

View File

@ -19,7 +19,7 @@ using namespace mozilla::dom;
namespace mozilla {
// The singleton Wifi service, to be used on the main thread.
StaticRefPtr<WifiProxyService> gWifiProxyService;
static StaticRefPtr<WifiProxyService> gWifiProxyService;
// The singleton supplicant class, that can be used on any thread.
static nsAutoPtr<WpaSupplicant> gWpaSupplicant;
@ -28,7 +28,9 @@ static nsAutoPtr<WpaSupplicant> gWpaSupplicant;
class WifiEventDispatcher : public nsRunnable
{
public:
WifiEventDispatcher(nsAString& aEvent): mEvent(aEvent)
WifiEventDispatcher(const nsAString& aEvent, const nsACString& aInterface)
: mEvent(aEvent)
, mInterface(aInterface)
{
MOZ_ASSERT(!NS_IsMainThread());
}
@ -36,19 +38,21 @@ public:
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
gWifiProxyService->DispatchWifiEvent(mEvent);
gWifiProxyService->DispatchWifiEvent(mEvent, mInterface);
return NS_OK;
}
private:
nsString mEvent;
nsCString mInterface;
};
// Runnable used to call WaitForEvent on the event thread.
class EventRunnable : public nsRunnable
{
public:
EventRunnable()
EventRunnable(const nsACString& aInterface)
: mInterface(aInterface)
{
MOZ_ASSERT(NS_IsMainThread());
}
@ -57,20 +61,24 @@ public:
{
MOZ_ASSERT(!NS_IsMainThread());
nsAutoString event;
gWpaSupplicant->WaitForEvent(event);
gWpaSupplicant->WaitForEvent(event, mInterface);
if (!event.IsEmpty()) {
nsCOMPtr<nsIRunnable> runnable = new WifiEventDispatcher(event);
nsCOMPtr<nsIRunnable> runnable = new WifiEventDispatcher(event, mInterface);
NS_DispatchToMainThread(runnable);
}
return NS_OK;
}
private:
nsCString mInterface;
};
// Runnable used dispatch the Command result on the main thread.
class WifiResultDispatcher : public nsRunnable
{
public:
WifiResultDispatcher(WifiResultOptions& aResult)
WifiResultDispatcher(WifiResultOptions& aResult, const nsACString& aInterface)
: mInterface(aInterface)
{
MOZ_ASSERT(!NS_IsMainThread());
@ -106,33 +114,38 @@ public:
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
gWifiProxyService->DispatchWifiResult(mResult);
gWifiProxyService->DispatchWifiResult(mResult, mInterface);
return NS_OK;
}
private:
WifiResultOptions mResult;
nsCString mInterface;
};
// Runnable used to call SendCommand on the control thread.
class ControlRunnable : public nsRunnable
{
public:
ControlRunnable(CommandOptions aOptions) : mOptions(aOptions) {
ControlRunnable(CommandOptions aOptions, const nsACString& aInterface)
: mOptions(aOptions)
, mInterface(aInterface)
{
MOZ_ASSERT(NS_IsMainThread());
}
NS_IMETHOD Run()
{
WifiResultOptions result;
if (gWpaSupplicant->ExecuteCommand(mOptions, result)) {
nsCOMPtr<nsIRunnable> runnable = new WifiResultDispatcher(result);
if (gWpaSupplicant->ExecuteCommand(mOptions, result, mInterface)) {
nsCOMPtr<nsIRunnable> runnable = new WifiResultDispatcher(result, mInterface);
NS_DispatchToMainThread(runnable);
}
return NS_OK;
}
private:
CommandOptions mOptions;
nsCString mInterface;
};
NS_IMPL_ISUPPORTS1(WifiProxyService, nsIWifiProxyService)
@ -170,23 +183,33 @@ WifiProxyService::FactoryCreate()
}
NS_IMETHODIMP
WifiProxyService::Start(nsIWifiEventListener* aListener)
WifiProxyService::Start(nsIWifiEventListener* aListener,
const char ** aInterfaces,
uint32_t aNumOfInterfaces)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aListener);
nsresult rv = NS_NewThread(getter_AddRefs(mEventThread));
if (NS_FAILED(rv)) {
NS_WARNING("Can't create wifi event thread");
return NS_ERROR_FAILURE;
nsresult rv;
// Since EventRunnable runs in the manner of blocking, we have to
// spin a thread for each interface.
// (See the WpaSupplicant::WaitForEvent)
mEventThreadList.SetLength(aNumOfInterfaces);
for (uint32_t i = 0; i < aNumOfInterfaces; i++) {
mEventThreadList[i].mInterface = aInterfaces[i];
rv = NS_NewThread(getter_AddRefs(mEventThreadList[i].mThread));
if (NS_FAILED(rv)) {
NS_WARNING("Can't create wifi event thread");
Shutdown();
return NS_ERROR_FAILURE;
}
}
rv = NS_NewThread(getter_AddRefs(mControlThread));
if (NS_FAILED(rv)) {
NS_WARNING("Can't create wifi control thread");
// Shutdown the event thread.
mEventThread->Shutdown();
mEventThread = nullptr;
Shutdown();
return NS_ERROR_FAILURE;
}
@ -199,15 +222,23 @@ NS_IMETHODIMP
WifiProxyService::Shutdown()
{
MOZ_ASSERT(NS_IsMainThread());
mEventThread->Shutdown();
mEventThread = nullptr;
mControlThread->Shutdown();
mControlThread = nullptr;
for (size_t i = 0; i < mEventThreadList.Length(); i++) {
if (mEventThreadList[i].mThread) {
mEventThreadList[i].mThread->Shutdown();
mEventThreadList[i].mThread = nullptr;
}
}
mEventThreadList.Clear();
if (mControlThread) {
mControlThread->Shutdown();
mControlThread = nullptr;
}
return NS_OK;
}
NS_IMETHODIMP
WifiProxyService::SendCommand(const JS::Value& aOptions, JSContext* aCx)
WifiProxyService::SendCommand(const JS::Value& aOptions, const nsACString& aInterface,
JSContext* aCx)
{
MOZ_ASSERT(NS_IsMainThread());
WifiCommandOptions options;
@ -220,22 +251,30 @@ WifiProxyService::SendCommand(const JS::Value& aOptions, JSContext* aCx)
// Dispatch the command to the control thread.
CommandOptions commandOptions(options);
nsCOMPtr<nsIRunnable> runnable = new ControlRunnable(commandOptions);
nsCOMPtr<nsIRunnable> runnable = new ControlRunnable(commandOptions, aInterface);
mControlThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
return NS_OK;
}
NS_IMETHODIMP
WifiProxyService::WaitForEvent()
WifiProxyService::WaitForEvent(const nsACString& aInterface)
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIRunnable> runnable = new EventRunnable();
mEventThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
return NS_OK;
// Dispatch to the event thread which has the given interface name
for (size_t i = 0; i < mEventThreadList.Length(); i++) {
if (mEventThreadList[i].mInterface.Equals(aInterface)) {
nsCOMPtr<nsIRunnable> runnable = new EventRunnable(aInterface);
mEventThreadList[i].mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
return NS_OK;
}
}
return NS_ERROR_FAILURE;
}
void
WifiProxyService::DispatchWifiResult(const WifiResultOptions& aOptions)
WifiProxyService::DispatchWifiResult(const WifiResultOptions& aOptions, const nsACString& aInterface)
{
MOZ_ASSERT(NS_IsMainThread());
@ -247,15 +286,15 @@ WifiProxyService::DispatchWifiResult(const WifiResultOptions& aOptions)
}
// Call the listener with a JS value.
mListener->OnCommand(val);
mListener->OnCommand(val, aInterface);
}
void
WifiProxyService::DispatchWifiEvent(const nsAString& aEvent)
WifiProxyService::DispatchWifiEvent(const nsAString& aEvent, const nsACString& aInterface)
{
MOZ_ASSERT(NS_IsMainThread());
// Call the listener.
mListener->OnWaitEvent(aEvent);
mListener->OnWaitEvent(aEvent, aInterface);
}
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WifiProxyService,

View File

@ -9,11 +9,19 @@
#include "nsCOMPtr.h"
#include "nsThread.h"
#include "mozilla/dom/WifiOptionsBinding.h"
#include "nsTArray.h"
namespace mozilla {
class WifiProxyService MOZ_FINAL : public nsIWifiProxyService
{
private:
struct EventThreadListEntry
{
nsCOMPtr<nsIThread> mThread;
nsCString mInterface;
};
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIWIFIPROXYSERVICE
@ -21,14 +29,15 @@ public:
static already_AddRefed<WifiProxyService>
FactoryCreate();
void DispatchWifiEvent(const nsAString& aEvent);
void DispatchWifiResult(const mozilla::dom::WifiResultOptions& aOptions);
void DispatchWifiEvent(const nsAString& aEvent, const nsACString& aInterface);
void DispatchWifiResult(const mozilla::dom::WifiResultOptions& aOptions,
const nsACString& aInterface);
private:
WifiProxyService();
~WifiProxyService();
nsCOMPtr<nsIThread> mEventThread;
nsTArray<EventThreadListEntry> mEventThreadList;
nsCOMPtr<nsIThread> mControlThread;
nsCOMPtr<nsIWifiEventListener> mListener;
};

View File

@ -221,12 +221,12 @@ WpaSupplicant::WpaSupplicant()
mNetUtils = new NetUtils();
};
void WpaSupplicant::WaitForEvent(nsAString& aEvent)
void WpaSupplicant::WaitForEvent(nsAString& aEvent, const nsCString& aInterface)
{
CHECK_HWLIB()
char buffer[BUFFER_SIZE];
int32_t ret = mImpl->do_wifi_wait_for_event("wlan0", buffer, BUFFER_SIZE);
int32_t ret = mImpl->do_wifi_wait_for_event(aInterface.get(), buffer, BUFFER_SIZE);
CheckBuffer(buffer, ret, aEvent);
}
@ -244,7 +244,8 @@ uint32_t WpaSupplicant::MakeMask(uint32_t len) {
}
bool WpaSupplicant::ExecuteCommand(CommandOptions aOptions,
WifiResultOptions& aResult)
WifiResultOptions& aResult,
const nsCString& aInterface)
{
CHECK_HWLIB(false)
if (!mNetUtils->GetSharedLibrary()) {
@ -258,7 +259,7 @@ bool WpaSupplicant::ExecuteCommand(CommandOptions aOptions,
size_t len = BUFFER_SIZE - 1;
char buffer[BUFFER_SIZE];
NS_ConvertUTF16toUTF8 request(aOptions.mRequest);
aResult.mStatus = mImpl->do_wifi_command("wlan0", request.get(), buffer, &len);
aResult.mStatus = mImpl->do_wifi_command(aInterface.get(), request.get(), buffer, &len);
nsString value;
if (aResult.mStatus == 0) {
if (buffer[len - 1] == '\n') { // remove trailing new lines.
@ -269,7 +270,7 @@ bool WpaSupplicant::ExecuteCommand(CommandOptions aOptions,
}
aResult.mReply = value;
} else if (aOptions.mCmd.EqualsLiteral("close_supplicant_connection")) {
mImpl->do_wifi_close_supplicant_connection("wlan0");
mImpl->do_wifi_close_supplicant_connection(aInterface.get());
} else if (aOptions.mCmd.EqualsLiteral("load_driver")) {
aResult.mStatus = mImpl->do_wifi_load_driver();
} else if (aOptions.mCmd.EqualsLiteral("unload_driver")) {
@ -279,7 +280,7 @@ bool WpaSupplicant::ExecuteCommand(CommandOptions aOptions,
} else if (aOptions.mCmd.EqualsLiteral("stop_supplicant")) {
aResult.mStatus = mImpl->do_wifi_stop_supplicant();
} else if (aOptions.mCmd.EqualsLiteral("connect_to_supplicant")) {
aResult.mStatus = mImpl->do_wifi_connect_to_supplicant("wlan0");
aResult.mStatus = mImpl->do_wifi_connect_to_supplicant(aInterface.get());
} else if (aOptions.mCmd.EqualsLiteral("ifc_enable")) {
aResult.mStatus = mNetUtils->do_ifc_enable(GET_CHAR(mIfname));
} else if (aOptions.mCmd.EqualsLiteral("ifc_disable")) {

View File

@ -86,6 +86,9 @@ public:
class WpaSupplicantImpl
{
public:
// Suppress warning from nsAutoPtr
virtual ~WpaSupplicantImpl() {}
virtual int32_t
do_wifi_wait_for_event(const char *iface, char *buf, size_t len) = 0; // ICS != JB
@ -117,9 +120,13 @@ class WpaSupplicant MOZ_FINAL
public:
WpaSupplicant();
void WaitForEvent(nsAString& aEvent);
// Use nsCString as the type of aInterface to guarantee it's
// null-terminated so that we can pass it to c API without
// conversion
void WaitForEvent(nsAString& aEvent, const nsCString& aInterface);
bool ExecuteCommand(CommandOptions aOptions,
mozilla::dom::WifiResultOptions& result);
mozilla::dom::WifiResultOptions& result,
const nsCString& aInterface);
private:
nsAutoPtr<WpaSupplicantImpl> mImpl;

View File

@ -96,6 +96,8 @@ XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
// command always succeeds and we do a string/boolean check for the
// expected results).
var WifiManager = (function() {
var manager = {};
function getStartupPrefs() {
return {
sdkVersion: parseInt(libcutils.property_get("ro.build.version.sdk"), 10),
@ -109,26 +111,17 @@ var WifiManager = (function() {
let {sdkVersion, unloadDriverEnabled, schedScanRecovery, driverDelay, ifname} = getStartupPrefs();
let wifiListener = {
onWaitEvent: function(event) {
if (handleEvent(event)) {
waitForEvent();
onWaitEvent: function(event, iface) {
if (manager.ifname === iface && handleEvent(event)) {
waitForEvent(iface);
}
},
onCommand: function(event) {
onmessageresult(event);
onCommand: function(event, iface) {
onmessageresult(event, iface);
}
}
let wifiService = Cc["@mozilla.org/wifi/service;1"];
if (wifiService) {
wifiService = wifiService.getService(Ci.nsIWifiProxyService);
wifiService.start(wifiListener);
} else {
debug("No wifi service component available!");
}
var manager = {};
manager.ifname = ifname;
// Emulator build runs to here.
// The debug() should only be used after WifiManager.
@ -138,7 +131,16 @@ var WifiManager = (function() {
manager.schedScanRecovery = schedScanRecovery;
manager.driverDelay = driverDelay ? parseInt(driverDelay, 10) : DRIVER_READY_WAIT;
var wifiCommand = WifiCommand(controlMessage);
let wifiService = Cc["@mozilla.org/wifi/service;1"];
if (wifiService) {
wifiService = wifiService.getService(Ci.nsIWifiProxyService);
let interfaces = [manager.ifname];
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.
@ -148,12 +150,13 @@ var WifiManager = (function() {
function controlMessage(obj, callback) {
var id = idgen++;
obj.id = id;
if (callback)
if (callback) {
controlCallbacks[id] = callback;
wifiService.sendCommand(obj);
}
wifiService.sendCommand(obj, obj.iface);
}
let onmessageresult = function(data) {
let onmessageresult = function(data, iface) {
var id = data.id;
var callback = controlCallbacks[id];
if (callback) {
@ -165,8 +168,8 @@ var WifiManager = (function() {
// Polling the status worker
var recvErrors = 0;
function waitForEvent() {
wifiService.waitForEvent();
function waitForEvent(iface) {
wifiService.waitForEvent(iface);
}
// Commands to the control worker.
@ -755,7 +758,7 @@ var WifiManager = (function() {
}
function didConnectSupplicant(callback) {
waitForEvent();
waitForEvent(manager.ifname);
// Load up the supplicant state.
getDebugEnabled(function(ok) {

View File

@ -4,17 +4,19 @@
#include "nsISupports.idl"
[scriptable, uuid(e3d54c8d-b1c7-4ed5-b760-e26d3075e3e5)]
[scriptable, uuid(4d4389e0-1547-11e3-8ffd-0800200c9a66)]
interface nsIWifiEventListener : nsISupports {
void onWaitEvent(in AString event);
void onCommand(in jsval result);
void onWaitEvent(in AString event, in ACString aInterface);
void onCommand(in jsval result, in ACString aInterface);
};
[scriptable, uuid(ac5ebae6-ec72-4212-89cb-cd25ed5a1b46)]
[scriptable, uuid(5e2bd8c0-1547-11e3-8ffd-0800200c9a66)]
interface nsIWifiProxyService : nsISupports {
void start(in nsIWifiEventListener listener);
void start(in nsIWifiEventListener listener,
[array, size_is(aNumOfInterface)] in string aInterfaces,
in unsigned long aNumOfInterface);
void shutdown();
[implicit_jscontext]
void sendCommand(in jsval parameters);
void waitForEvent();
void sendCommand(in jsval parameters, in ACString aInterface);
void waitForEvent(in ACString aInterface);
};