Bug 912015 - Support multiple wap_supplicant connections. r=mrbkap
authorHenry Chang <hchang@mozilla.com>
Mon, 14 Oct 2013 16:33:59 +0800
changeset 167207 7e749f99448d948b49de2bc28e4847d2684bc201
parent 167206 1dd66283ff2690f058b8c5dba65a22cda0ae4b54
child 167208 9e0dbf6786543397dcc34d7664aa5094164d7a9c
child 167231 b9d18faa1a974138b0282e7ae5170ed73edfbce5
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs912015
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 912015 - Support multiple wap_supplicant connections. r=mrbkap
dom/wifi/WifiCommand.jsm
dom/wifi/WifiProxyService.cpp
dom/wifi/WifiProxyService.h
dom/wifi/WifiUtils.cpp
dom/wifi/WifiUtils.h
dom/wifi/WifiWorker.js
dom/wifi/nsIWifiService.idl
--- a/dom/wifi/WifiCommand.jsm
+++ b/dom/wifi/WifiCommand.jsm
@@ -10,17 +10,17 @@ this.EXPORTED_SYMBOLS = ["WifiCommand"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 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 = {};
 
   //-------------------------------------------------
   // General commands.
   //-------------------------------------------------
 
   command.loadDriver = function (callback) {
     voidControlMessage("load_driver", function(status) {
@@ -332,26 +332,27 @@ this.WifiCommand = function(controlMessa
     });
   };
 
   //--------------------------------------------------
   // Helper functions.
   //--------------------------------------------------
 
   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) {
     doCommand(request, function(data) {
       callback(data.status ? -1 : (data.reply|0));
     });
   }
 
--- a/dom/wifi/WifiProxyService.cpp
+++ b/dom/wifi/WifiProxyService.cpp
@@ -14,68 +14,76 @@
   { 0xc6c9be7e, 0x744f, 0x4222, {0xb2, 0x03, 0xcd, 0x55, 0xdf, 0xc8, 0xbc, 0x12} }
 
 using namespace mozilla;
 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;
 
 // Runnable used dispatch the WaitForEvent result on the main thread.
 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());
   }
 
   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());
   }
 
   NS_IMETHOD Run()
   {
     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());
 
     // XXX: is there a better way to copy webidl dictionnaries?
     // the copy constructor is private.
 #define COPY_FIELD(prop) mResult.prop = aResult.prop;
 
     COPY_FIELD(mId)
@@ -101,43 +109,48 @@ public:
     COPY_FIELD(mServer)
 
 #undef COPY_FIELD
   }
 
   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)
 
 WifiProxyService::WifiProxyService()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!gWifiProxyService);
@@ -165,102 +178,128 @@ WifiProxyService::FactoryCreate()
     ClearOnShutdown(&gWpaSupplicant);
   }
 
   nsRefPtr<WifiProxyService> service = gWifiProxyService.get();
   return service.forget();
 }
 
 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;
   }
 
   mListener = aListener;
 
   return NS_OK;
 }
 
 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;
 
   if (!options.Init(aCx,
                     JS::Handle<JS::Value>::fromMarkedLocation(&aOptions))) {
     NS_WARNING("Bad dictionary passed to WifiProxyService::SendCommand");
     return NS_ERROR_FAILURE;
   }
 
   // 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());
 
   mozilla::AutoSafeJSContext cx;
   JS::RootedValue val(cx);
 
   if (!aOptions.ToObject(cx, JS::NullPtr(), &val)) {
     return;
   }
 
   // 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,
                                          WifiProxyService::FactoryCreate)
 
 NS_DEFINE_NAMED_CID(NS_WIFIPROXYSERVICE_CID);
 
 static const mozilla::Module::CIDEntry kWifiProxyServiceCIDs[] = {
--- a/dom/wifi/WifiProxyService.h
+++ b/dom/wifi/WifiProxyService.h
@@ -4,35 +4,44 @@
 
 #ifndef WifiProxyService_h
 #define WifiProxyService_h
 
 #include "nsIWifiService.h"
 #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
 
   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;
 };
 
 } // namespace mozilla
 
 #endif // WifiProxyService_h
--- a/dom/wifi/WifiUtils.cpp
+++ b/dom/wifi/WifiUtils.cpp
@@ -216,22 +216,22 @@ WpaSupplicant::WpaSupplicant()
   if (NetUtils::SdkVersion() < 16) {
     mImpl = new ICSWpaSupplicantImpl();
   } else {
     mImpl = new JBWpaSupplicantImpl();
   }
   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);
 }
 
 #define GET_CHAR(prop) NS_ConvertUTF16toUTF8(aOptions.prop).get()
 
 /**
  * Make a subnet mask.
  */
@@ -239,52 +239,53 @@ uint32_t WpaSupplicant::MakeMask(uint32_
   uint32_t mask = 0;
   for (uint32_t i = 0; i < len; ++i) {
     mask |= (0x80000000 >> i);
   }
   return ntohl(mask);
 }
 
 bool WpaSupplicant::ExecuteCommand(CommandOptions aOptions,
-                                   WifiResultOptions& aResult)
+                                   WifiResultOptions& aResult,
+                                   const nsCString& aInterface)
 {
   CHECK_HWLIB(false)
   if (!mNetUtils->GetSharedLibrary()) {
     return false;
   }
 
   // Always correlate the opaque ids.
   aResult.mId = aOptions.mId;
 
   if (aOptions.mCmd.EqualsLiteral("command")) {
     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.
         len--;
       }
       buffer[len] = '\0';
       CheckBuffer(buffer, len, value);
     }
     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")) {
     aResult.mStatus = mImpl->do_wifi_unload_driver();
   } else if (aOptions.mCmd.EqualsLiteral("start_supplicant")) {
     aResult.mStatus = mImpl->do_wifi_start_supplicant(0);
   } 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")) {
     aResult.mStatus = mNetUtils->do_ifc_disable(GET_CHAR(mIfname));
   } else if (aOptions.mCmd.EqualsLiteral("ifc_configure")) {
     aResult.mStatus = mNetUtils->do_ifc_configure(
       GET_CHAR(mIfname), aOptions.mIpaddr, aOptions.mMask,
       aOptions.mGateway, aOptions.mDns1, aOptions.mDns2
--- a/dom/wifi/WifiUtils.h
+++ b/dom/wifi/WifiUtils.h
@@ -81,16 +81,19 @@ public:
 };
 
 // Abstract class that exposes libhardware_legacy functions we need for
 // wifi management.
 // We use the ICS signatures here since they are likely more future-proof.
 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
 
   virtual int32_t
   do_wifi_command(const char* iface, const char* cmd, char* buff, size_t* len) = 0; // ICS != JB
 
   virtual int32_t
   do_wifi_load_driver() = 0;
@@ -112,19 +115,23 @@ public:
 };
 
 // Concrete class to use to access the wpa supplicant.
 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;
   nsAutoPtr<NetUtils> mNetUtils;
 
 protected:
   void CheckBuffer(char* buffer, int32_t length, nsAString& aEvent);
   uint32_t MakeMask(uint32_t len);
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -91,87 +91,90 @@ XPCOMUtils.defineLazyServiceGetter(this,
 // A note about errors and error handling in this file:
 // The libraries that we use in this file are intended for C code. For
 // C code, it is natural to return -1 for errors and 0 for success.
 // Therefore, the code that interacts directly with the worker uses this
 // convention (note: command functions do get boolean results since the
 // 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),
       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"),
       ifname: libcutils.property_get("wifi.interface")
     };
   }
 
   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.
   if (!ifname) {
     manager.ifname = DEFAULT_WLAN_INTERFACE;
   }
   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.
   var controlCallbacks = Object.create(null);
   var idgen = 0;
 
   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) {
       callback(data);
       delete controlCallbacks[id];
     }
   }
 
   // Polling the status worker
   var recvErrors = 0;
 
-  function waitForEvent() {
-    wifiService.waitForEvent();
+  function waitForEvent(iface) {
+    wifiService.waitForEvent(iface);
   }
 
   // Commands to the control worker.
 
   var driverLoaded = false;
 
   function loadDriver(callback) {
     if (driverLoaded) {
@@ -750,17 +753,17 @@ var WifiManager = (function() {
       notifyStateChange({ state: "WPS_OVERLAP_DETECTED", BSSID: null, id: -1 });
       return true;
     }
     // Unknown event.
     return true;
   }
 
   function didConnectSupplicant(callback) {
-    waitForEvent();
+    waitForEvent(manager.ifname);
 
     // Load up the supplicant state.
     getDebugEnabled(function(ok) {
       syncDebug();
     });
     wifiCommand.status(function(status) {
       parseStatus(status);
       notify("supplicantconnection");
--- a/dom/wifi/nsIWifiService.idl
+++ b/dom/wifi/nsIWifiService.idl
@@ -1,20 +1,22 @@
 /* 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/. */
 
 #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);
 };