Bug 864932 - Rewrite wifi workers in c++ r=mrbkap
authorFabrice Desré <fabrice@mozilla.com>
Tue, 27 Aug 2013 13:54:51 -0700
changeset 158318 f3c5e1c6ab9e8648a7344df8cde575f1eac82c67
parent 158317 42b78e557764ac4d337a5ab83adfba1cfa7d38bb
child 158319 49e21f2559fec1e09cfa8120f45c58d40d18ede0
push id407
push userlsblakk@mozilla.com
push dateTue, 03 Dec 2013 03:32:50 +0000
treeherdermozilla-release@babf8c9ebc52 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs864932
milestone26.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 864932 - Rewrite wifi workers in c++ r=mrbkap
dom/system/gonk/systemlibs.js
dom/webidl/DummyBinding.webidl
dom/webidl/WebIDL.mk
dom/webidl/WifiOptions.webidl
dom/wifi/Makefile.in
dom/wifi/NetUtils.cpp
dom/wifi/NetUtils.h
dom/wifi/WifiProxyService.cpp
dom/wifi/WifiProxyService.h
dom/wifi/WifiUtils.cpp
dom/wifi/WifiUtils.h
dom/wifi/WifiWorker.js
dom/wifi/libhardware_legacy.js
dom/wifi/moz.build
dom/wifi/nsIWifiService.idl
dom/wifi/wifi_worker.js
js/src/vm/CharacterEncoding.cpp
toolkit/library/Makefile.in
toolkit/library/nsStaticXULComponents.cpp
--- a/dom/system/gonk/systemlibs.js
+++ b/dom/system/gonk/systemlibs.js
@@ -204,17 +204,17 @@ this.libnetutils = (function () {
     let serverbuf = ctypes.char.array(4096)();
     let lease = ctypes.int();
     let vendorbuf = ctypes.char.array(4096)();
     let domainbuf = ctypes.char.array(4096)();
     let c_dhcp_do_request;
     let c_dhcp_do_request_renew;
 
     // also changed for 16 and 18
-    if (sdkVersion >= 18) {
+    if (sdkVersion >= 18) { // 18 == JB 4.3
       dnslistbuf[0] = dns1buf;
       dnslistbuf[1] = dns2buf;
       c_dhcp_do_request =
         library.declare("dhcp_do_request", ctypes.default_abi,
                         ctypes.int,       // return value
                         ctypes.char.ptr,  // ifname
                         ctypes.char.ptr,  // ipaddr
                         ctypes.char.ptr,  // gateway
@@ -231,30 +231,30 @@ this.libnetutils = (function () {
                         ctypes.char.ptr,  // ipaddr
                         ctypes.char.ptr,  // gateway
                         ctypes.int.ptr,   // prefixlen
                         ctypes.char.ptr.array(),  // dns
                         ctypes.char.ptr,  // server
                         ctypes.int.ptr,   // lease
                         ctypes.char.ptr,  // vendorinfo
                         ctypes.char.ptr); // domain
-    } else if (sdkVersion >= 16) {
+    } else if (sdkVersion >= 16) { // 16 == JB 4.1
       c_dhcp_do_request =
         library.declare("dhcp_do_request", ctypes.default_abi,
                         ctypes.int,       // return value
                         ctypes.char.ptr,  // ifname
                         ctypes.char.ptr,  // ipaddr
                         ctypes.char.ptr,  // gateway
                         ctypes.int.ptr,   // prefixlen
                         ctypes.char.ptr,  // dns1
                         ctypes.char.ptr,  // dns2
                         ctypes.char.ptr,  // server
                         ctypes.int.ptr,   // lease
                         ctypes.char.ptr); // vendorinfo
-    } else {
+    } else { // ICS
       c_dhcp_do_request =
         library.declare("dhcp_do_request", ctypes.default_abi,
                         ctypes.int,      // return value
                         ctypes.char.ptr, // ifname
                         ctypes.char.ptr, // ipaddr
                         ctypes.char.ptr, // gateway
                         ctypes.int.ptr,  // prefixlen
                         ctypes.char.ptr, // dns1
@@ -332,17 +332,17 @@ this.libnetutils = (function () {
       library.declare("ifc_reset_connections",
                       ctypes.default_abi,
                       ctypes.int,
                       ctypes.char.ptr,
                       ctypes.int);
     iface.ifc_reset_connections = function(ifname, reset_mask) {
       return c_ifc_reset_connections(ifname, reset_mask) | 0;
     }
-  } else {
+  } else { // version < 15 - we don't care anymore.
     let ints = ctypes.int.array(8)();
     let c_dhcp_do_request =
       library.declare("dhcp_do_request", ctypes.default_abi,
                       ctypes.int,      // return value
                       ctypes.char.ptr, // ifname
                       ctypes.int.ptr,  // ipaddr
                       ctypes.int.ptr,  // gateway
                       ctypes.int.ptr,  // mask
--- a/dom/webidl/DummyBinding.webidl
+++ b/dom/webidl/DummyBinding.webidl
@@ -18,13 +18,15 @@ interface DummyInterface {
   void funcDNSCacheDict(optional DNSCacheDict arg);
   void funcDNSLookupDict(optional DNSLookupDict arg);
   void funcConnStatusDict(optional ConnStatusDict arg);
   void frameRequestCallback(FrameRequestCallback arg);
   void MmsParameters(optional MmsParameters arg);
   void MmsAttachment(optional MmsAttachment arg);
   void AsyncScrollEventDetail(optional AsyncScrollEventDetail arg);
   void OpenWindowEventDetail(optional OpenWindowEventDetail arg);
+  void WifiOptions(optional WifiCommandOptions arg1,
+                   optional WifiResultOptions arg2);
 };
 
 interface DummyInterfaceWorkers {
   BlobPropertyBag blobBag();
 };
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -379,16 +379,17 @@ webidl_files = \
   URLUtilsReadOnly.webidl \
   ValidityState.webidl \
   VideoPlaybackQuality.webidl \
   VideoStreamTrack.webidl \
   WaveShaperNode.webidl \
   WebComponents.webidl \
   WebSocket.webidl \
   WheelEvent.webidl \
+  WifiOptions.webidl \
   Window.webidl \
   WorkerLocation.webidl \
   WorkerNavigator.webidl \
   XMLDocument.webidl \
   XMLHttpRequest.webidl \
   XMLHttpRequestEventTarget.webidl \
   XMLHttpRequestUpload.webidl \
   XMLSerializer.webidl \
new file mode 100644
--- /dev/null
+++ b/dom/webidl/WifiOptions.webidl
@@ -0,0 +1,59 @@
+/* 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/. */
+
+/**
+  * This dictionnary holds the parameters sent to the wifi service.
+  */
+dictionary WifiCommandOptions
+{
+  long      id = 0;       // opaque id.
+  DOMString cmd = "";     // the command name.
+  DOMString request;      // for "command"
+  DOMString ifname;       // for "ifc_reset_connections", "ifc_enable",
+                          // "ifc_disable", "ifc_remove_host_routes",
+                          // "ifc_remove_default_route", "dhcp_stop",
+                          // "dhcp_release_lease", "ifc_get_default_route",
+                          // "ifc_add_host_route", "ifc_set_default_route",
+                          // "ifc_configure", "dhcp_do_request",
+                          // "dhcp_do_request_renew".
+  long route;             // for "ifc_add_host_route", "ifc_set_default_route".
+  long ipaddr;            // for "ifc_configure".
+  long mask;              // for "ifc_configure".
+  long gateway;           // for "ifc_configure".
+  long dns1;              // for "ifc_configure".
+  long dns2;              // for "ifc_configure".
+  DOMString key;          // for "property_get", "property_set".
+  DOMString value;        // for "property_set".
+  DOMString defaultValue; // for "property_get".
+};
+
+/**
+  * This dictionnary holds the parameters sent back to WifiWorker.js
+  */
+dictionary WifiResultOptions
+{
+  long      id = 0;             // opaque id.
+  long      status = 0;         // the return status of the command.
+                                // Used by most commands.
+  DOMString reply = "";         // for "command".
+  DOMString route = "";         // for "ifc_get_default_route".
+  DOMString error = "";         // for "dhcp_get_errmsg".
+  DOMString value = "";         // for "property_get".
+  DOMString ipaddr_str = "";    // The following are for the result of
+                                // dhcp_do_request.
+  DOMString gateway_str = "";
+  DOMString broadcast_str = "";
+  DOMString dns1_str = "";
+  DOMString dns2_str = "";
+  DOMString mask_str = "";
+  DOMString server_str = "";
+  DOMString vendor_str = "";
+  long      lease = 0;
+  long      mask = 0;
+  long      ipaddr = 0;
+  long      gateway = 0;
+  long      dns1 = 0;
+  long      dns2 = 0;
+  long      server = 0;
+};
--- a/dom/wifi/Makefile.in
+++ b/dom/wifi/Makefile.in
@@ -5,13 +5,15 @@
 DEPTH            = @DEPTH@
 topsrcdir        = @top_srcdir@
 srcdir           = @srcdir@
 VPATH            = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 LIBRARY_NAME     = domwifi_s
+EXPORT_LIBRARY   = 1
+
 include $(topsrcdir)/dom/dom-config.mk
 
 include $(topsrcdir)/config/rules.mk
 
 
new file mode 100644
--- /dev/null
+++ b/dom/wifi/NetUtils.cpp
@@ -0,0 +1,139 @@
+/* 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 "NetUtils.h"
+#include <dlfcn.h>
+#include <errno.h>
+#include <cutils/properties.h>
+#include "prinit.h"
+#include "mozilla/Assertions.h"
+#include "nsDebug.h"
+
+static void* sNetUtilsLib;
+static PRCallOnceType sInitNetUtilsLib;
+
+static PRStatus
+InitNetUtilsLib()
+{
+  sNetUtilsLib = dlopen("/system/lib/libnetutils.so", RTLD_LAZY);
+  // We might fail to open the hardware lib. That's OK.
+  return PR_SUCCESS;
+}
+
+static void*
+GetNetUtilsLibHandle()
+{
+  PR_CallOnce(&sInitNetUtilsLib, InitNetUtilsLib);
+  return sNetUtilsLib;
+}
+
+// static
+void*
+NetUtils::GetSharedLibrary()
+{
+  void* netLib = GetNetUtilsLibHandle();
+  if (!netLib) {
+    NS_WARNING("No /system/lib/libnetutils.so");
+  }
+  return netLib;
+}
+
+// static
+int32_t
+NetUtils::SdkVersion()
+{
+  char propVersion[PROPERTY_VALUE_MAX];
+  property_get("ro.build.version.sdk", propVersion, "0");
+  int32_t version = strtol(propVersion, nullptr, 10);
+  return version;
+}
+
+DEFINE_DLFUNC(ifc_enable, int32_t, const char*)
+DEFINE_DLFUNC(ifc_disable, int32_t, const char*)
+DEFINE_DLFUNC(ifc_configure, int32_t, const char*, in_addr_t, uint32_t,
+              in_addr_t, in_addr_t, in_addr_t)
+DEFINE_DLFUNC(ifc_reset_connections, int32_t, const char*, const int32_t)
+DEFINE_DLFUNC(dhcp_stop, int32_t, const char*)
+
+int32_t NetUtils::do_ifc_enable(const char *ifname)
+{
+  USE_DLFUNC(ifc_enable)
+  return ifc_enable(ifname);
+}
+
+int32_t NetUtils::do_ifc_disable(const char *ifname)
+{
+  USE_DLFUNC(ifc_disable)
+  return ifc_disable(ifname);
+}
+
+int32_t NetUtils::do_ifc_configure(const char *ifname,
+                                       in_addr_t address,
+                                       uint32_t prefixLength,
+                                       in_addr_t gateway,
+                                       in_addr_t dns1,
+                                       in_addr_t dns2)
+{
+  USE_DLFUNC(ifc_configure)
+  int32_t ret = ifc_configure(ifname, address, prefixLength, gateway, dns1, dns2);
+  return ret;
+}
+
+int32_t NetUtils::do_ifc_reset_connections(const char *ifname,
+                                               const int32_t resetMask)
+{
+  USE_DLFUNC(ifc_reset_connections)
+  return ifc_reset_connections(ifname, resetMask);
+}
+
+int32_t NetUtils::do_dhcp_stop(const char *ifname)
+{
+  USE_DLFUNC(dhcp_stop)
+  return dhcp_stop(ifname);
+}
+
+int32_t NetUtils::do_dhcp_do_request(const char *ifname,
+                                         char *ipaddr,
+                                         char *gateway,
+                                         uint32_t *prefixLength,
+                                         char *dns1,
+                                         char *dns2,
+                                         char *server,
+                                         uint32_t  *lease,
+                                         char* vendorinfo)
+{
+  int32_t ret = -1;
+  uint32_t sdkVersion = SdkVersion();
+
+  if (sdkVersion == 15) {
+    // ICS
+    // http://androidxref.com/4.0.4/xref/system/core/libnetutils/dhcp_utils.c#149
+    DEFINE_DLFUNC(dhcp_do_request, int32_t, const char*, char*, char*,  uint32_t*, char*, char*, char*, uint32_t*)
+    USE_DLFUNC(dhcp_do_request)
+    vendorinfo[0] = '\0';
+
+    ret = dhcp_do_request(ifname, ipaddr, gateway, prefixLength, dns1, dns2,
+                          server, lease);
+  } else if (sdkVersion == 16 || sdkVersion == 17) {
+    // JB 4.1 and 4.2
+    // http://androidxref.com/4.1.2/xref/system/core/libnetutils/dhcp_utils.c#175
+    // http://androidxref.com/4.2.2_r1/xref/system/core/include/netutils/dhcp.h#26
+    DEFINE_DLFUNC(dhcp_do_request, int32_t, const char*, char*, char*,  uint32_t*, char*, char*, char*, uint32_t*, char*)
+    USE_DLFUNC(dhcp_do_request)
+    ret = dhcp_do_request(ifname, ipaddr, gateway, prefixLength, dns1, dns2,
+                          server, lease, vendorinfo);
+  } else if (sdkVersion == 18) {
+    // JB 4.3
+    // http://androidxref.com/4.3_r2.1/xref/system/core/libnetutils/dhcp_utils.c#181
+    DEFINE_DLFUNC(dhcp_do_request, int32_t, const char*, char*, char*,  uint32_t*, char**, char*, uint32_t*, char*, char*)
+    USE_DLFUNC(dhcp_do_request)
+    char *dns[3] = {dns1, dns2, NULL};
+    char domains[PROPERTY_VALUE_MAX];
+    ret = dhcp_do_request(ifname, ipaddr, gateway, prefixLength, dns,
+                          server, lease, vendorinfo, domains);
+  } else {
+    NS_WARNING("Unable to perform do_dhcp_request: unsupported sdk version!");
+  }
+  return ret;
+}
new file mode 100644
--- /dev/null
+++ b/dom/wifi/NetUtils.h
@@ -0,0 +1,60 @@
+/* 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/. */
+
+/**
+ * Abstraction on top of the network support from libnetutils that we
+ * use to set up network connections.
+ */
+
+#ifndef NetUtils_h
+#define NetUtils_h
+
+#include "arpa/inet.h"
+
+// Copied from ifc.h
+#define RESET_IPV4_ADDRESSES 0x01
+#define RESET_IPV6_ADDRESSES 0x02
+#define RESET_ALL_ADDRESSES  (RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES)
+
+// Implements netutils functions. No need for an abstract class here since we
+// only have a one sdk specific method (dhcp_do_request)
+class NetUtils
+{
+public:
+  static void* GetSharedLibrary();
+
+  int32_t do_ifc_enable(const char *ifname);
+  int32_t do_ifc_disable(const char *ifname);
+  int32_t do_ifc_configure(const char *ifname,
+                           in_addr_t address,
+                           uint32_t prefixLength,
+                           in_addr_t gateway,
+                           in_addr_t dns1,
+                           in_addr_t dns2);
+  int32_t do_ifc_reset_connections(const char *ifname, const int32_t resetMask);
+  int32_t do_dhcp_stop(const char *ifname);
+  int32_t do_dhcp_do_request(const char *ifname,
+                             char *ipaddr,
+                             char *gateway,
+                             uint32_t *prefixLength,
+                             char *dns1,
+                             char *dns2,
+                             char *server,
+                             uint32_t  *lease,
+                             char* vendorinfo);
+
+  static int32_t SdkVersion();
+};
+
+// Defines a function type with the right arguments and return type.
+#define DEFINE_DLFUNC(name, ret, args...) typedef ret (*FUNC##name)(args);
+
+// Set up a dlsymed function ready to use.
+#define USE_DLFUNC(name)                                                      \
+  FUNC##name name = (FUNC##name) dlsym(GetSharedLibrary(), #name);            \
+  if (!name) {                                                                \
+    MOZ_ASSERT("Symbol not found in shared library : " #name);                \
+  }
+
+#endif // NetUtils_h
new file mode 100644
--- /dev/null
+++ b/dom/wifi/WifiProxyService.cpp
@@ -0,0 +1,286 @@
+/* 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 "WifiProxyService.h"
+#include "nsServiceManagerUtils.h"
+#include "mozilla/ModuleUtils.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "nsXULAppAPI.h"
+#include "WifiUtils.h"
+#include "jsapi.h"
+#include "nsCxPusher.h"
+
+#define NS_WIFIPROXYSERVICE_CID \
+  { 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;
+
+// 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)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    gWifiProxyService->DispatchWifiEvent(mEvent);
+    return NS_OK;
+  }
+
+private:
+  nsString mEvent;
+};
+
+// Runnable used to call WaitForEvent on the event thread.
+class EventRunnable : public nsRunnable
+{
+public:
+  EventRunnable()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    nsAutoString event;
+    gWpaSupplicant->WaitForEvent(event);
+    if (!event.IsEmpty()) {
+      nsCOMPtr<nsIRunnable> runnable = new WifiEventDispatcher(event);
+      NS_DispatchToMainThread(runnable);
+    }
+    return NS_OK;
+  }
+};
+
+// Runnable used dispatch the Command result on the main thread.
+class WifiResultDispatcher : public nsRunnable
+{
+public:
+  WifiResultDispatcher(WifiResultOptions& aResult)
+  {
+    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)
+    COPY_FIELD(mStatus)
+    COPY_FIELD(mReply)
+    COPY_FIELD(mRoute)
+    COPY_FIELD(mError)
+    COPY_FIELD(mValue)
+    COPY_FIELD(mIpaddr_str)
+    COPY_FIELD(mGateway_str)
+    COPY_FIELD(mBroadcast_str)
+    COPY_FIELD(mDns1_str)
+    COPY_FIELD(mDns2_str)
+    COPY_FIELD(mMask_str)
+    COPY_FIELD(mServer_str)
+    COPY_FIELD(mVendor_str)
+    COPY_FIELD(mLease)
+    COPY_FIELD(mMask)
+    COPY_FIELD(mIpaddr)
+    COPY_FIELD(mGateway)
+    COPY_FIELD(mDns1)
+    COPY_FIELD(mDns2)
+    COPY_FIELD(mServer)
+
+#undef COPY_FIELD
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    gWifiProxyService->DispatchWifiResult(mResult);
+    return NS_OK;
+  }
+
+private:
+  WifiResultOptions mResult;
+};
+
+// Runnable used to call SendCommand on the control thread.
+class ControlRunnable : public nsRunnable
+{
+public:
+  ControlRunnable(CommandOptions aOptions) : mOptions(aOptions) {
+    MOZ_ASSERT(!NS_IsMainThread());
+  }
+
+  NS_IMETHOD Run()
+  {
+    WifiResultOptions result;
+    if (gWpaSupplicant->ExecuteCommand(mOptions, result)) {
+      nsCOMPtr<nsIRunnable> runnable = new WifiResultDispatcher(result);
+      NS_DispatchToMainThread(runnable);
+    }
+    return NS_OK;
+  }
+private:
+   CommandOptions mOptions;
+};
+
+NS_IMPL_ISUPPORTS1(WifiProxyService, nsIWifiProxyService)
+
+WifiProxyService::WifiProxyService()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!gWifiProxyService);
+}
+
+WifiProxyService::~WifiProxyService()
+{
+  MOZ_ASSERT(!gWifiProxyService);
+}
+
+already_AddRefed<WifiProxyService>
+WifiProxyService::FactoryCreate()
+{
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    return nullptr;
+  }
+
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!gWifiProxyService) {
+    gWifiProxyService = new WifiProxyService();
+    ClearOnShutdown(&gWifiProxyService);
+
+    gWpaSupplicant = new WpaSupplicant();
+    ClearOnShutdown(&gWpaSupplicant);
+  }
+
+  nsRefPtr<WifiProxyService> service = gWifiProxyService.get();
+  return service.forget();
+}
+
+NS_IMETHODIMP
+WifiProxyService::Start(nsIWifiEventListener* aListener)
+{
+  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;
+  }
+
+  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;
+    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;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WifiProxyService::SendCommand(const JS::Value& aOptions, 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);
+  mControlThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WifiProxyService::WaitForEvent()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsIRunnable> runnable = new EventRunnable();
+  mEventThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
+  return NS_OK;
+}
+
+void
+WifiProxyService::DispatchWifiResult(const WifiResultOptions& aOptions)
+{
+  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);
+}
+
+void
+WifiProxyService::DispatchWifiEvent(const nsAString& aEvent)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  // Call the listener.
+  mListener->OnWaitEvent(aEvent);
+}
+
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WifiProxyService,
+                                         WifiProxyService::FactoryCreate)
+
+NS_DEFINE_NAMED_CID(NS_WIFIPROXYSERVICE_CID);
+
+static const mozilla::Module::CIDEntry kWifiProxyServiceCIDs[] = {
+  { &kNS_WIFIPROXYSERVICE_CID, false, nullptr, WifiProxyServiceConstructor },
+  { nullptr }
+};
+
+static const mozilla::Module::ContractIDEntry kWifiProxyServiceContracts[] = {
+  { "@mozilla.org/wifi/service;1", &kNS_WIFIPROXYSERVICE_CID },
+  { nullptr }
+};
+
+static const mozilla::Module kWifiProxyServiceModule = {
+  mozilla::Module::kVersion,
+  kWifiProxyServiceCIDs,
+  kWifiProxyServiceContracts,
+  nullptr
+};
+
+} // namespace mozilla
+
+NSMODULE_DEFN(WifiProxyServiceModule) = &kWifiProxyServiceModule;
new file mode 100644
--- /dev/null
+++ b/dom/wifi/WifiProxyService.h
@@ -0,0 +1,38 @@
+/* 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/. */
+
+#ifndef WifiProxyService_h
+#define WifiProxyService_h
+
+#include "nsIWifiService.h"
+#include "nsCOMPtr.h"
+#include "nsThread.h"
+#include "mozilla/dom/WifiOptionsBinding.h"
+
+namespace mozilla {
+
+class WifiProxyService MOZ_FINAL : public nsIWifiProxyService
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIWIFIPROXYSERVICE
+
+  static already_AddRefed<WifiProxyService>
+  FactoryCreate();
+
+  void DispatchWifiEvent(const nsAString& aEvent);
+  void DispatchWifiResult(const mozilla::dom::WifiResultOptions& aOptions);
+
+private:
+  WifiProxyService();
+  ~WifiProxyService();
+
+  nsCOMPtr<nsIThread> mEventThread;
+  nsCOMPtr<nsIThread> mControlThread;
+  nsCOMPtr<nsIWifiEventListener> mListener;
+};
+
+} // namespace mozilla
+
+#endif // WifiProxyService_h
new file mode 100644
--- /dev/null
+++ b/dom/wifi/WifiUtils.cpp
@@ -0,0 +1,393 @@
+/* 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 "WifiUtils.h"
+#include <cutils/properties.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include "prinit.h"
+#include "js/CharacterEncoding.h"
+#include "NetUtils.h"
+
+using namespace mozilla::dom;
+
+#define BUFFER_SIZE 4096
+
+// Intentionally not trying to dlclose() this handle. That's playing
+// Russian roulette with security bugs.
+static void* sWifiLib;
+static PRCallOnceType sInitWifiLib;
+
+static PRStatus
+InitWifiLib()
+{
+  sWifiLib = dlopen("/system/lib/libhardware_legacy.so", RTLD_LAZY);
+  // We might fail to open the hardware lib. That's OK.
+  return PR_SUCCESS;
+}
+
+static void*
+GetSharedLibrary()
+{
+  PR_CallOnce(&sInitWifiLib, InitWifiLib);
+  return sWifiLib;
+}
+
+// This is the same algorithm as in InflateUTF8StringToBuffer with Copy and
+// while ignoring invalids.
+// https://mxr.mozilla.org/mozilla-central/source/js/src/vm/CharacterEncoding.cpp#231
+
+static const uint32_t REPLACE_UTF8 = 0xFFFD;
+
+void LossyConvertUTF8toUTF16(const char* aInput, uint32_t aLength, nsAString& aOut)
+{
+  JS::UTF8Chars src(aInput, aLength);
+
+  PRUnichar dst[aLength]; // Allocating for worst case.
+
+  // First, count how many jschars need to be in the inflated string.
+  // |i| is the index into |src|, and |j| is the the index into |dst|.
+  size_t srclen = src.length();
+  uint32_t j = 0;
+  for (uint32_t i = 0; i < srclen; i++, j++) {
+    uint32_t v = uint32_t(src[i]);
+    if (!(v & 0x80)) {
+      // ASCII code unit.  Simple copy.
+      dst[j] = PRUnichar(v);
+    } else {
+      // Non-ASCII code unit.  Determine its length in bytes (n).
+      uint32_t n = 1;
+      while (v & (0x80 >> n))
+        n++;
+
+  #define INVALID(report, arg, n2)                          \
+      do {                                                  \
+        n = n2;                                             \
+        goto invalidMultiByteCodeUnit;                      \
+      } while (0)
+
+      // Check the leading byte.
+      if (n < 2 || n > 4)
+        INVALID(ReportInvalidCharacter, i, 1);
+
+      // Check that |src| is large enough to hold an n-byte code unit.
+      if (i + n > srclen)
+        INVALID(ReportBufferTooSmall, /* dummy = */ 0, 1);
+
+      // Check the second byte.  From Unicode Standard v6.2, Table 3-7
+      // Well-Formed UTF-8 Byte Sequences.
+      if ((v == 0xE0 && ((uint8_t)src[i + 1] & 0xE0) != 0xA0) ||  // E0 A0~BF
+        (v == 0xED && ((uint8_t)src[i + 1] & 0xE0) != 0x80) ||  // ED 80~9F
+        (v == 0xF0 && ((uint8_t)src[i + 1] & 0xF0) == 0x80) ||  // F0 90~BF
+        (v == 0xF4 && ((uint8_t)src[i + 1] & 0xF0) != 0x80))    // F4 80~8F
+      {
+        INVALID(ReportInvalidCharacter, i, 1);
+      }
+
+      // Check the continuation bytes.
+      for (uint32_t m = 1; m < n; m++)
+        if ((src[i + m] & 0xC0) != 0x80)
+          INVALID(ReportInvalidCharacter, i, m);
+
+      // Determine the code unit's length in jschars and act accordingly.
+      v = JS::Utf8ToOneUcs4Char((uint8_t *)&src[i], n);
+      if (v < 0x10000) {
+        // The n-byte UTF8 code unit will fit in a single jschar.
+        dst[j] = jschar(v);
+      } else {
+        v -= 0x10000;
+        if (v <= 0xFFFFF) {
+          // The n-byte UTF8 code unit will fit in two jschars.
+          dst[j] = jschar((v >> 10) + 0xD800);
+          j++;
+          dst[j] = jschar((v & 0x3FF) + 0xDC00);
+        } else {
+          // The n-byte UTF8 code unit won't fit in two jschars.
+          INVALID(ReportTooBigCharacter, v, 1);
+        }
+      }
+
+    invalidMultiByteCodeUnit:
+      // Move i to the last byte of the multi-byte code unit;  the loop
+      // header will do the final i++ to move to the start of the next
+      // code unit.
+      i += n - 1;
+    }
+  }
+
+  dst[j] = 0;
+  aOut = dst;
+}
+
+// Helper to check we have loaded the hardware shared library.
+#define CHECK_HWLIB(ret)                                                      \
+  void* hwLib = GetSharedLibrary();                                           \
+  if (!hwLib) {                                                               \
+    NS_WARNING("No /system/lib/libhardware_legacy.so");                       \
+    return ret;                                                               \
+  }
+
+#define DEFAULT_IMPL(name, ret, args...) \
+  DEFINE_DLFUNC(name, ret, args...)      \
+  ret do_##name(args) {                  \
+    USE_DLFUNC(name)                     \
+    return name(args);                   \
+  }
+
+// ICS implementation.
+class ICSWpaSupplicantImpl : public WpaSupplicantImpl
+{
+public:
+  DEFAULT_IMPL(wifi_load_driver, int32_t, )
+  DEFAULT_IMPL(wifi_unload_driver, int32_t, )
+  DEFAULT_IMPL(wifi_stop_supplicant, int32_t, )
+
+  DEFINE_DLFUNC(wifi_wait_for_event, int32_t, char*, size_t)
+  int32_t do_wifi_wait_for_event(const char *iface, char *buf, size_t len) {
+    USE_DLFUNC(wifi_wait_for_event)
+    return wifi_wait_for_event(buf, len);
+  }
+
+  DEFINE_DLFUNC(wifi_command, int32_t, const char*, char*, size_t*)
+  int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) {
+    USE_DLFUNC(wifi_command)
+    return wifi_command(cmd, buf, len);
+  }
+
+  DEFINE_DLFUNC(wifi_start_supplicant, int32_t, )
+  int32_t do_wifi_start_supplicant(int32_t) {
+    USE_DLFUNC(wifi_start_supplicant)
+    return wifi_start_supplicant();
+  }
+
+  DEFINE_DLFUNC(wifi_connect_to_supplicant, int32_t, )
+  int32_t do_wifi_connect_to_supplicant(const char* iface) {
+    USE_DLFUNC(wifi_connect_to_supplicant)
+    return wifi_connect_to_supplicant();
+  }
+
+  DEFINE_DLFUNC(wifi_close_supplicant_connection, void, )
+  void do_wifi_close_supplicant_connection(const char* iface) {
+    USE_DLFUNC(wifi_close_supplicant_connection)
+    return wifi_close_supplicant_connection();
+  }
+};
+
+// JB implementation.
+// We only redefine the methods that have a different signature than on ICS.
+class JBWpaSupplicantImpl : public ICSWpaSupplicantImpl
+{
+public:
+  DEFINE_DLFUNC(wifi_wait_for_event, int32_t, const char*, char*, size_t)
+  int32_t do_wifi_wait_for_event(const char* iface, char* buf, size_t len) {
+    USE_DLFUNC(wifi_wait_for_event)
+    return wifi_wait_for_event(iface, buf, len);
+  }
+
+  DEFINE_DLFUNC(wifi_command, int32_t, const char*, const char*, char*, size_t*)
+  int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) {
+    USE_DLFUNC(wifi_command)
+    return wifi_command(iface, cmd, buf, len);
+  }
+
+  DEFINE_DLFUNC(wifi_start_supplicant, int32_t, int32_t)
+  int32_t do_wifi_start_supplicant(int32_t arg) {
+    USE_DLFUNC(wifi_start_supplicant)
+    return wifi_start_supplicant(arg);
+  }
+
+  DEFINE_DLFUNC(wifi_connect_to_supplicant, int32_t, const char*)
+  int32_t do_wifi_connect_to_supplicant(const char* iface) {
+    USE_DLFUNC(wifi_connect_to_supplicant)
+    return wifi_connect_to_supplicant(iface);
+  }
+
+  DEFINE_DLFUNC(wifi_close_supplicant_connection, void, const char*)
+  void do_wifi_close_supplicant_connection(const char* iface) {
+    USE_DLFUNC(wifi_close_supplicant_connection)
+    wifi_close_supplicant_connection(iface);
+  }
+};
+
+// Concrete class to use to access the wpa supplicant.
+WpaSupplicant::WpaSupplicant()
+{
+  if (NetUtils::SdkVersion() < 16) {
+    mImpl = new ICSWpaSupplicantImpl();
+  } else {
+    mImpl = new JBWpaSupplicantImpl();
+  }
+  mNetUtils = new NetUtils();
+};
+
+void WpaSupplicant::WaitForEvent(nsAString& aEvent)
+{
+  CHECK_HWLIB()
+
+  char buffer[BUFFER_SIZE];
+  int32_t ret = mImpl->do_wifi_wait_for_event("wlan0", buffer, BUFFER_SIZE);
+  CheckBuffer(buffer, ret, aEvent);
+}
+
+#define GET_CHAR(prop) NS_ConvertUTF16toUTF8(aOptions.prop).get()
+
+/**
+ * Make a subnet mask.
+ */
+uint32_t WpaSupplicant::MakeMask(uint32_t len) {
+  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)
+{
+  CHECK_HWLIB(false)
+  if (!mNetUtils->GetSharedLibrary()) {
+    return false;
+  }
+
+  // Always correlate the opaque ids.
+  aResult.mId = aOptions.mId;
+
+  if (aOptions.mCmd.EqualsLiteral("property_get")) {
+    char propValue[PROPERTY_VALUE_MAX];
+    NS_ConvertUTF16toUTF8 propName(aOptions.mKey);
+    NS_ConvertUTF16toUTF8 defaultValue(aOptions.mDefaultValue);
+    aResult.mStatus = property_get(propName.get(), propValue,
+                                   defaultValue.get());
+    aResult.mValue = NS_ConvertUTF8toUTF16(propValue);
+  } else if (aOptions.mCmd.EqualsLiteral("property_set")) {
+    NS_ConvertUTF16toUTF8 propName(aOptions.mKey);
+    NS_ConvertUTF16toUTF8 propValue(aOptions.mValue);
+    aResult.mStatus = property_set(propName.get(), propValue.get());
+  } else 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);
+    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");
+  } 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");
+  } 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
+    );
+  } else if (aOptions.mCmd.EqualsLiteral("ifc_reset_connections")) {
+    aResult.mStatus = mNetUtils->do_ifc_reset_connections(
+      GET_CHAR(mIfname), RESET_ALL_ADDRESSES
+    );
+  } else if (aOptions.mCmd.EqualsLiteral("dhcp_stop")) {
+    aResult.mStatus = mNetUtils->do_dhcp_stop(GET_CHAR(mIfname));
+  } else if (aOptions.mCmd.EqualsLiteral("dhcp_do_request")) {
+    char ipaddr[PROPERTY_VALUE_MAX];
+    char gateway[PROPERTY_VALUE_MAX];
+    uint32_t prefixLength;
+    char dns1[PROPERTY_VALUE_MAX];
+    char dns2[PROPERTY_VALUE_MAX];
+    char server[PROPERTY_VALUE_MAX];
+    uint32_t lease;
+    char vendorinfo[PROPERTY_VALUE_MAX];
+    aResult.mStatus =
+      mNetUtils->do_dhcp_do_request(GET_CHAR(mIfname),
+                                    ipaddr,
+                                    gateway,
+                                    &prefixLength,
+                                    dns1,
+                                    dns2,
+                                    server,
+                                    &lease,
+                                    vendorinfo);
+
+    if (aResult.mStatus == -1) {
+      // Early return since we failed.
+      return true;
+    }
+
+    aResult.mIpaddr_str = NS_ConvertUTF8toUTF16(ipaddr);
+    aResult.mGateway_str = NS_ConvertUTF8toUTF16(gateway);
+    aResult.mDns1_str = NS_ConvertUTF8toUTF16(dns1);
+    aResult.mDns2_str = NS_ConvertUTF8toUTF16(dns2);
+    aResult.mServer_str = NS_ConvertUTF8toUTF16(server);
+    aResult.mVendor_str = NS_ConvertUTF8toUTF16(vendorinfo);
+    aResult.mLease = lease;
+    aResult.mMask = MakeMask(prefixLength);
+
+    uint32_t inet4; // only support IPv4 for now.
+
+#define INET_PTON(var, field)                                                 \
+  PR_BEGIN_MACRO                                                              \
+    inet_pton(AF_INET, var, &inet4);                                          \
+    aResult.field = inet4;                                                    \
+  PR_END_MACRO
+
+    INET_PTON(ipaddr, mIpaddr);
+    INET_PTON(gateway, mGateway);
+
+    if (dns1[0] != '\0') {
+      INET_PTON(dns1, mDns1);
+    }
+
+    if (dns2[0] != '\0') {
+      INET_PTON(dns2, mDns2);
+    }
+
+    INET_PTON(server, mServer);
+
+    //aResult.mask_str = netHelpers.ipToString(obj.mask);
+    char inet_str[64];
+    if (inet_ntop(AF_INET, &aResult.mMask, inet_str, sizeof(inet_str))) {
+      aResult.mMask_str = NS_ConvertUTF8toUTF16(inet_str);
+    }
+
+    uint32_t broadcast = (aResult.mIpaddr & aResult.mMask) + ~aResult.mMask;
+    if (inet_ntop(AF_INET, &broadcast, inet_str, sizeof(inet_str))) {
+      aResult.mBroadcast_str = NS_ConvertUTF8toUTF16(inet_str);
+    }
+  } else {
+    NS_WARNING("WpaSupplicant::ExecuteCommand : Unknown command");
+    printf_stderr("WpaSupplicant::ExecuteCommand : Unknown command: %s",
+      NS_ConvertUTF16toUTF8(aOptions.mCmd).get());
+    return false;
+  }
+
+  return true;
+}
+
+// Checks the buffer and do the utf processing.
+void
+WpaSupplicant::CheckBuffer(char* buffer, int32_t length,
+                           nsAString& aEvent)
+{
+  if (length > 0 && length < BUFFER_SIZE) {
+    buffer[length] = 0;
+    LossyConvertUTF8toUTF16(buffer, length, aEvent);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/wifi/WifiUtils.h
@@ -0,0 +1,133 @@
+/* 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/. */
+
+/**
+ * Abstraction on top of the wifi support from libhardware_legacy that we
+ * use to talk to the wpa_supplicant.
+ */
+
+#ifndef WifiUtils_h
+#define WifiUtils_h
+
+#include "nsString.h"
+#include "nsAutoPtr.h"
+#include "mozilla/dom/WifiOptionsBinding.h"
+#include "NetUtils.h"
+#include "nsCxPusher.h"
+
+// Needed to add a copy constructor to WifiCommandOptions.
+struct CommandOptions
+{
+public:
+  CommandOptions(const CommandOptions& aOther) {
+    mId = aOther.mId;
+    mCmd = aOther.mCmd;
+    mRequest = aOther.mRequest;
+    mIfname = aOther.mIfname;
+    mRoute = aOther.mRoute;
+    mIpaddr = aOther.mIpaddr;
+    mMask = aOther.mMask;
+    mGateway = aOther.mGateway;
+    mDns1 = aOther.mDns1;
+    mDns2 = aOther.mDns2;
+    mKey = aOther.mKey;
+    mValue = aOther.mValue;
+    mDefaultValue = aOther.mDefaultValue;
+  }
+
+  CommandOptions(const mozilla::dom::WifiCommandOptions& aOther) {
+
+#define COPY_OPT_FIELD(prop, defaultValue)            \
+    if (aOther.prop.WasPassed()) {                    \
+      prop = aOther.prop.Value();                     \
+    } else {                                          \
+      prop = defaultValue;                            \
+    }
+
+#define COPY_FIELD(prop) prop = aOther.prop;
+    COPY_FIELD(mId)
+    COPY_FIELD(mCmd)
+    COPY_OPT_FIELD(mRequest, EmptyString())
+    COPY_OPT_FIELD(mIfname, EmptyString())
+    COPY_OPT_FIELD(mIpaddr, 0)
+    COPY_OPT_FIELD(mRoute, 0)
+    COPY_OPT_FIELD(mMask, 0)
+    COPY_OPT_FIELD(mGateway, 0)
+    COPY_OPT_FIELD(mDns1, 0)
+    COPY_OPT_FIELD(mDns2, 0)
+    COPY_OPT_FIELD(mKey, EmptyString())
+    COPY_OPT_FIELD(mValue, EmptyString())
+    COPY_OPT_FIELD(mDefaultValue, EmptyString())
+
+#undef COPY_OPT_FIELD
+#undef COPY_FIELD
+  }
+
+  // All the fields, not Optional<> anymore to get copy constructors.
+  nsString mCmd;
+  nsString mDefaultValue;
+  int32_t mDns1;
+  int32_t mDns2;
+  int32_t mGateway;
+  int32_t mId;
+  nsString mIfname;
+  int32_t mIpaddr;
+  nsString mKey;
+  int32_t mMask;
+  nsString mRequest;
+  int32_t mRoute;
+  nsString mValue;
+};
+
+// 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:
+  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;
+
+  virtual int32_t
+  do_wifi_unload_driver() = 0;
+
+  virtual int32_t
+  do_wifi_start_supplicant(int32_t) = 0; // ICS != JB
+
+  virtual int32_t
+  do_wifi_stop_supplicant() = 0;
+
+  virtual int32_t
+  do_wifi_connect_to_supplicant(const char* iface) = 0; // ICS != JB
+
+  virtual void
+  do_wifi_close_supplicant_connection(const char* iface) = 0; // ICS != JB
+};
+
+// Concrete class to use to access the wpa supplicant.
+class WpaSupplicant MOZ_FINAL
+{
+public:
+  WpaSupplicant();
+
+  void WaitForEvent(nsAString& aEvent);
+  bool ExecuteCommand(CommandOptions aOptions,
+                      mozilla::dom::WifiResultOptions& result);
+
+private:
+  nsAutoPtr<WpaSupplicantImpl> mImpl;
+  nsAutoPtr<NetUtils> mNetUtils;
+
+protected:
+  void CheckBuffer(char* buffer, int32_t length, nsAString& aEvent);
+  uint32_t MakeMask(uint32_t len);
+};
+
+#endif // WifiUtils_h
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -100,81 +100,72 @@ var WifiManager = (function() {
       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();
 
-  var controlWorker = new ChromeWorker(WIFIWORKER_WORKER);
-  var eventWorker = new ChromeWorker(WIFIWORKER_WORKER);
+  let wifiListener = {
+    onWaitEvent: function(event) {
+      if (handleEvent(event)) {
+        waitForEvent();
+      }
+    },
+
+    onCommand: function(event) {
+      onmessageresult(event);
+    }
+  }
+
+  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;
 
-  // Callbacks to invoke when a reply arrives from the controlWorker.
+  // 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)
       controlCallbacks[id] = callback;
-    controlWorker.postMessage(obj);
+    wifiService.sendCommand(obj);
   }
 
-  function onerror(e) {
-    // It is very important to call preventDefault on the event here.
-    // If an exception is thrown on the worker, it bubbles out to the
-    // component that created it. If that component doesn't have an
-    // onerror handler, the worker will try to call the error reporter
-    // on the context it was created on. However, That doesn't work
-    // for component contexts and can result in crashes. This onerror
-    // handler has to make sure that it calls preventDefault on the
-    // incoming event.
-    e.preventDefault();
-
-    var worker = (this === controlWorker) ? "control" : "event";
-
-    debug("Got an error from the " + worker + " worker: " + e.filename +
-          ":" + e.lineno + ": " + e.message + "\n");
-  }
-
-  controlWorker.onerror = onerror;
-  eventWorker.onerror = onerror;
-
-  controlWorker.onmessage = function(e) {
-    var data = e.data;
+  let onmessageresult = function(data) {
     var id = data.id;
     var callback = controlCallbacks[id];
     if (callback) {
       callback(data);
       delete controlCallbacks[id];
     }
-  };
+  }
 
   // Polling the status worker
   var recvErrors = 0;
-  eventWorker.onmessage = function(e) {
-    // Process the event and tell the event worker to listen for more events.
-    if (handleEvent(e.data.event))
-      waitForEvent();
-  };
 
   function waitForEvent() {
-    eventWorker.postMessage({ cmd: "wait_for_event" });
+    wifiService.waitForEvent();
   }
 
   // Commands to the control worker.
 
   function voidControlMessage(cmd, callback) {
     controlMessage({ cmd: cmd }, function (data) {
       callback(data.status);
     });
@@ -773,19 +764,20 @@ var WifiManager = (function() {
 
   function getDhcpError(callback) {
     controlMessage({ cmd: "dhcp_get_errmsg" }, function(data) {
       callback(data.error);
     });
   }
 
   function configureInterface(ifname, ipaddr, mask, gateway, dns1, dns2, callback) {
-    controlMessage({ cmd: "ifc_configure", ifname: ifname,
+    let message = { cmd: "ifc_configure", ifname: ifname,
                      ipaddr: ipaddr, mask: mask, gateway: gateway,
-                     dns1: dns1, dns2: dns2}, function(data) {
+                     dns1: dns1, dns2: dns2};
+    controlMessage(message, function(data) {
       callback(!data.status);
     });
   }
 
   function runDhcpRenew(ifname, callback) {
     controlMessage({ cmd: "dhcp_do_request", ifname: ifname }, function(data) {
       if (!data.status)
         dhcpInfo = data;
deleted file mode 100644
--- a/dom/wifi/libhardware_legacy.js
+++ /dev/null
@@ -1,96 +0,0 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
-
-"use strict";
-
-let libhardware_legacy = (function () {
-  let library = ctypes.open("libhardware_legacy.so");
-  let sdkVersion = libcutils.property_get("ro.build.version.sdk", "0");
-  sdkVersion = parseInt(sdkVersion, 10);
-
-  let iface = {
-    // Load wifi driver, 0 on success, < 0 on failure.
-    load_driver: library.declare("wifi_load_driver", ctypes.default_abi, ctypes.int),
-
-    // Unload wifi driver, 0 on success, < 0 on failure.
-    unload_driver: library.declare("wifi_unload_driver", ctypes.default_abi, ctypes.int),
-
-    // Start supplicant, 0 on success, < 0 on failure.
-    start_supplicant: library.declare("wifi_start_supplicant", ctypes.default_abi, ctypes.int),
-
-    // Stop supplicant, 0 on success, < 0 on failure.
-    stop_supplicant: library.declare("wifi_stop_supplicant", ctypes.default_abi, ctypes.int),
-
-    // Open a connection to the supplicant, 0 on success, < 0 on failure.
-    connect_to_supplicant: library.declare("wifi_connect_to_supplicant", ctypes.default_abi, ctypes.int),
-
-    // Close connection to connection to the supplicant, 0 on success, < 0 on failure.
-    close_supplicant_connection: library.declare("wifi_close_supplicant_connection", ctypes.default_abi, ctypes.void_t),
-
-    // Block until a wifi event is returned, buf is the buffer, len is the max length of the buffer.
-    // Return value is number of bytes in buffer, or 0 if no event (no connection for instance), and < 0 on failure.
-    wait_for_event: library.declare("wifi_wait_for_event", ctypes.default_abi, ctypes.int, ctypes.char.ptr, ctypes.size_t),
-
-    // Issue a command to the wifi driver. command is the command string, reply will hold the reply, reply_len contains
-    // the maximum reply length initially and is updated with the actual length. 0 is returned on success, < 0 on failure.
-    command: library.declare("wifi_command", ctypes.default_abi, ctypes.int, ctypes.char.ptr, ctypes.char.ptr, ctypes.size_t.ptr),
-  };
-
-  if (sdkVersion >= 16) {
-    let c_start_supplicant =
-      library.declare("wifi_start_supplicant",
-                      ctypes.default_abi,
-                      ctypes.int,
-                      ctypes.int);
-    iface.start_supplicant = function () {
-      return c_start_supplicant(0);
-    };
-
-    let c_connect_to_supplicant =
-      library.declare("wifi_connect_to_supplicant",
-                      ctypes.default_abi,
-                      ctypes.int,
-                      ctypes.char.ptr);
-    iface.connect_to_supplicant = function () {
-      return c_connect_to_supplicant("wlan0");
-    };
-
-    let c_close_supplicant_connection =
-      library.declare("wifi_close_supplicant_connection",
-                      ctypes.default_abi,
-                      ctypes.void_t,
-                      ctypes.char.ptr);
-    iface.close_supplicant_connection = function () {
-      c_close_supplicant_connection("wlan0");
-    };
-
-    let c_wait_for_event =
-      library.declare("wifi_wait_for_event",
-                      ctypes.default_abi,
-                      ctypes.int,
-                      ctypes.char.ptr,
-                      ctypes.char.ptr,
-                      ctypes.size_t);
-    iface.wait_for_event = function (cbuf, len) {
-      return c_wait_for_event("wlan0", cbuf, len);
-    };
-
-    let c_command =
-      library.declare("wifi_command",
-                      ctypes.default_abi,
-                      ctypes.int,
-                      ctypes.char.ptr,
-                      ctypes.char.ptr,
-                      ctypes.char.ptr,
-                      ctypes.size_t.ptr);
-    iface.command = function (message, cbuf, len) {
-      return c_command("wlan0", message, cbuf, len);
-    };
-  }
-
-  return iface;
-})();
--- a/dom/wifi/moz.build
+++ b/dom/wifi/moz.build
@@ -3,28 +3,31 @@
 # 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/.
 
 XPIDL_SOURCES += [
     'nsIDOMMozWifiConnectionInfoEvent.idl',
     'nsIDOMMozWifiStatusChangeEvent.idl',
     'nsIWifi.idl',
+    'nsIWifiService.idl',
 ]
 
 XPIDL_MODULE = 'dom_wifi'
 
 MODULE = 'dom'
 
 EXTRA_COMPONENTS += [
     'DOMWifiManager.js',
     'DOMWifiManager.manifest',
     'WifiWorker.js',
     'WifiWorker.manifest',
 ]
 
-EXTRA_JS_MODULES += [
-    'libhardware_legacy.js',
-    'wifi_worker.js',
-]
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+    CPP_SOURCES = [
+        'NetUtils.cpp',
+        'WifiProxyService.cpp',
+        'WifiUtils.cpp',
+    ]
 
 LIBXUL_LIBRARY = True
 
new file mode 100644
--- /dev/null
+++ b/dom/wifi/nsIWifiService.idl
@@ -0,0 +1,20 @@
+/* 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)]
+interface nsIWifiEventListener : nsISupports {
+  void onWaitEvent(in AString event);
+  void onCommand(in jsval result);
+};
+
+[scriptable, uuid(ac5ebae6-ec72-4212-89cb-cd25ed5a1b46)]
+interface nsIWifiProxyService : nsISupports {
+  void start(in nsIWifiEventListener listener);
+  void shutdown();
+  [implicit_jscontext]
+  void sendCommand(in jsval parameters);
+  void waitForEvent();
+};
deleted file mode 100644
--- a/dom/wifi/wifi_worker.js
+++ /dev/null
@@ -1,138 +0,0 @@
-/* -*- 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";
-
-importScripts("systemlibs.js", "libhardware_legacy.js");
-
-var cbuf = ctypes.char.array(4096)();
-var hwaddr = ctypes.uint8_t.array(6)();
-var len = ctypes.size_t();
-var ints = ctypes.int.array(8)();
-
-let DEBUG = false;
-
-let debug;
-if (DEBUG) {
-  debug = function (s) {
-    dump("-*- WifiWorker component: " + s + "\n");
-  };
-} else {
-  debug = function (s) {};
-}
-
-// TODO: consolidate with implementation in systemlibs.js
-let libcutils = (function () {
-  let library = ctypes.open("libcutils.so");
-
-  return {
-    property_get: library.declare("property_get", ctypes.default_abi, ctypes.int, ctypes.char.ptr, ctypes.char.ptr, ctypes.char.ptr),
-    property_set: library.declare("property_set", ctypes.default_abi, ctypes.int, ctypes.char.ptr, ctypes.char.ptr)
-  };
-})();
-
-self.onmessage = function(e) {
-  var data = e.data;
-  var id = data.id;
-  var cmd = data.cmd;
-
-  switch (cmd) {
-  case "command":
-    len.value = 4096;
-    var ret = libhardware_legacy.command(data.request, cbuf, len.address());
-    var reply = "";
-    if (!ret) {
-      // The return value from libhardware_legacy.command is not guaranteed to
-      // be null-terminated. At the same time we want to make sure that we
-      // don't return a response with a trailing newline, so handle both cases
-      // here. Note that if we wrote 4096 characters to cbuf, we don't have to
-      // null-terminate the buffer, as ctypes has the maximum size already.
-      // Note also that len.value is an object. We can ignore the high 32 bits
-      // because we know that the maximum that it can be is 4096.
-      var reply_len = ctypes.UInt64.lo(len.value);
-      if (reply_len !== 0) {
-        if (cbuf[reply_len - 1] === 10)
-          cbuf[--reply_len] = 0;
-        else if (reply_len !== 4096)
-          cbuf[reply_len] = 0;
-
-        reply = cbuf.readStringReplaceMalformed();
-      }
-
-      // Else if reply_len was 0, use the empty reply, set above.
-    }
-    postMessage({ id: id, status: ret, reply: reply });
-    break;
-  case "wait_for_event":
-    var ret = libhardware_legacy.wait_for_event(cbuf, 4096);
-    // Check the array index.
-    if (ret > 0 && ret < 4096) {
-      // Make sure the string buffer is null terminated.
-      cbuf[ret] = 0;
-      // Use readStringReplaceMalformed() to handle non-UTF8-encoded string.
-      var event = cbuf.readStringReplaceMalformed();
-      postMessage({ id: id, event: event });
-    }
-    break;
-  case "ifc_reset_connections":
-    var ret = libnetutils.ifc_reset_connections(data.ifname,
-                                                libnetutils.RESET_ALL_ADDRESSES);
-    postMessage({ id: id, status: ret });
-    break;
-  case "ifc_enable":
-  case "ifc_disable":
-  case "ifc_remove_host_routes":
-  case "ifc_remove_default_route":
-  case "dhcp_stop":
-  case "dhcp_release_lease":
-    var ret = libnetutils[cmd](data.ifname);
-    postMessage({ id: id, status: ret });
-    break;
-  case "ifc_get_default_route":
-    var route = libnetutils.ifc_get_default_route(data.ifname);
-    postMessage({ id: id, route: route });
-    break;
-  case "ifc_add_host_route":
-  case "ifc_set_default_route":
-    var ret = libnetutils[cmd](data.ifname, data.route);
-    postMessage({ id: id, status: ret });
-    break;
-  case "ifc_configure":
-    debug("WIFI: data: " + uneval(data) + "\n");
-    var ret = libnetutils.ifc_configure(data.ifname, data.ipaddr, data.mask, data.gateway, data.dns1, data.dns2);
-    postMessage({ id: id, status: ret });
-    break;
-  case "dhcp_get_errmsg":
-    var error = libnetutils.get_dhcp_get_errmsg();
-    postMessage({ id: id, error: error.readString() });
-    break;
-  case "dhcp_do_request":
-  case "dhcp_do_request_renew":
-    var out = libnetutils[cmd](data.ifname);
-    out.id = id;
-    out.status = out.ret;
-    postMessage(out);
-    break;
-  case "property_get":
-    var ret = libcutils.property_get(data.key, cbuf, data.defaultValue);
-    postMessage({ id: id, status: ret, value: cbuf.readString() });
-    break;
-  case "property_set":
-    var ret = libcutils.property_set(data.key, data.value);
-    postMessage({ id: id, status: ret });
-    break;
-  case "close_supplicant_connection":
-    libhardware_legacy.close_supplicant_connection();
-    postMessage({ id: id, status: ret });
-    break;
-  default:
-    var f = libhardware_legacy[cmd] || libnetutils[cmd];
-    var ret = f();
-    debug("WIFI: " + cmd + " returned: " + ret);
-    postMessage({ id: id, status: ret });
-    break;
-  }
-}
--- a/js/src/vm/CharacterEncoding.cpp
+++ b/js/src/vm/CharacterEncoding.cpp
@@ -221,16 +221,18 @@ ReportTooBigCharacter(JSContext *cx, uin
 enum InflateUTF8Action {
     CountAndReportInvalids,
     CountAndIgnoreInvalids,
     Copy
 };
 
 static const uint32_t REPLACE_UTF8 = 0xFFFD;
 
+// If making changes to this algorithm, make sure to also update
+// LossyConvertUTF8toUTF16() in dom/wifi/WifiUtils.cpp
 template <InflateUTF8Action action>
 static bool
 InflateUTF8StringToBuffer(JSContext *cx, const UTF8Chars src, jschar *dst, size_t *dstlenp,
                           bool *isAsciip)
 {
     *isAsciip = true;
 
     // First, count how many jschars need to be in the inflated string.
--- a/toolkit/library/Makefile.in
+++ b/toolkit/library/Makefile.in
@@ -173,16 +173,22 @@ COMPONENT_LIBS += \
   jsinspector \
   jsdebugger \
   storagecomps \
   rdf \
   windowds \
   diskspacewatcher \
   $(NULL)
 
+ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
+COMPONENT_LIBS += \
+  domwifi_s \
+  $(NULL)
+endif
+
 ifdef BUILD_CTYPES
 COMPONENT_LIBS += \
   jsctypes \
   $(NULL)
 endif
 
 COMPONENT_LIBS += \
   jsperf \
--- a/toolkit/library/nsStaticXULComponents.cpp
+++ b/toolkit/library/nsStaticXULComponents.cpp
@@ -235,19 +235,26 @@
     MODULE(DiskSpaceWatcherModule)           \
     /* end of list */
 
 #define MODULE(_name) \
   NSMODULE_DECL(_name);
 
 XUL_MODULES
 
+#ifdef MOZ_WIDGET_GONK
+MODULE(WifiProxyServiceModule)
+#endif
+
 #undef MODULE
 
 #define MODULE(_name) \
     &NSMODULE_NAME(_name),
 
 extern const mozilla::Module *const *const kPStaticModules[] = {
   XUL_MODULES
+#ifdef MOZ_WIDGET_GONK
+MODULE(WifiProxyServiceModule)
+#endif
   NULL
 };
 
 #undef MODULE