Bug 1084342: Move |SocketMessageWatcher| to separate file, r=shawnjohnjr
authorThomas Zimmermann <tdz@users.sourceforge.net>
Tue, 28 Oct 2014 10:34:09 +0100
changeset 237036 5f3eeddf3de79ec900321c574d96788e95ced83c
parent 237035 e5fab638f15e203b9fddc73190a2fc5a0f0c6b92
child 237037 7ed6cd12215e630a24ae967de194905f41775153
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshawnjohnjr
bugs1084342
milestone36.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 1084342: Move |SocketMessageWatcher| to separate file, r=shawnjohnjr This patch moves |SocketMessageWatcher| into a separate file. The new Bluetooth daemon will require it's functionality for OPP profiles.
CLOBBER
dom/bluetooth/bluedroid/BluetoothSocketHALInterface.cpp
dom/bluetooth/bluedroid/BluetoothSocketHALInterface.h
dom/bluetooth/bluedroid/BluetoothSocketMessageWatcher.cpp
dom/bluetooth/bluedroid/BluetoothSocketMessageWatcher.h
dom/bluetooth/moz.build
dom/bluetooth2/bluedroid/BluetoothSocketHALInterface.cpp
dom/bluetooth2/bluedroid/BluetoothSocketHALInterface.h
dom/bluetooth2/bluedroid/BluetoothSocketMessageWatcher.cpp
dom/bluetooth2/bluedroid/BluetoothSocketMessageWatcher.h
dom/bluetooth2/moz.build
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,12 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 946065 needs a CLOBBER
+Updating CLOBBER for Bug 1084342
+
+There are some refactored files in in dom/bluetooth. Updating CLOBBER to not
+leave artifacts from older builds.
--- a/dom/bluetooth/bluedroid/BluetoothSocketHALInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothSocketHALInterface.cpp
@@ -1,19 +1,17 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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 "BluetoothSocketHALInterface.h"
-#include <errno.h>
-#include <unistd.h>
-#include <sys/socket.h>
 #include "BluetoothHALHelpers.h"
+#include "BluetoothSocketMessageWatcher.h"
 #include "mozilla/FileUtils.h"
 #include "nsClassHashtable.h"
 #include "nsXULAppAPI.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 typedef
   BluetoothHALInterfaceRunnable1<BluetoothSocketResultHandler, void,
@@ -102,304 +100,16 @@ BluetoothSocketHALInterface::Listen(Blue
 
   if (aRes) {
     DispatchBluetoothSocketHALResult(
       aRes, &BluetoothSocketResultHandler::Listen, fd,
       ConvertDefault(status, STATUS_FAIL));
   }
 }
 
-#define CMSGHDR_CONTAINS_FD(_cmsghdr) \
-    ( ((_cmsghdr)->cmsg_level == SOL_SOCKET) && \
-      ((_cmsghdr)->cmsg_type == SCM_RIGHTS) )
-
-class SocketMessageWatcher;
-
-/* |SocketMessageWatcherWrapper| wraps SocketMessageWatcher to keep it from
- * being released by hash table's Remove() method.
- */
-class SocketMessageWatcherWrapper
-{
-public:
-  SocketMessageWatcherWrapper(SocketMessageWatcher* aSocketMessageWatcher)
-  : mSocketMessageWatcher(aSocketMessageWatcher)
-  {
-    MOZ_ASSERT(mSocketMessageWatcher);
-  }
-
-  SocketMessageWatcher* GetSocketMessageWatcher()
-  {
-    return mSocketMessageWatcher;
-  }
-
-private:
-  SocketMessageWatcher* mSocketMessageWatcher;
-};
-
-/* |sWatcherHashTable| maps result handlers to corresponding watchers */
-static nsClassHashtable<nsRefPtrHashKey<BluetoothSocketResultHandler>,
-                        SocketMessageWatcherWrapper>
-  sWatcherHashtable;
-
-/* |SocketMessageWatcher| receives Bluedroid's socket setup
- * messages on the I/O thread. You need to inherit from this
- * class to make use of it.
- *
- * Bluedroid sends two socket info messages (20 bytes) at
- * the beginning of a connection to both peers.
- *
- *   - 1st message: [channel:4]
- *   - 2nd message: [size:2][bd address:6][channel:4][connection status:4]
- *
- * On the server side, the second message will contain a
- * socket file descriptor for the connection. The client
- * uses the original file descriptor.
- */
-class SocketMessageWatcher : public MessageLoopForIO::Watcher
-{
-public:
-  static const unsigned char MSG1_SIZE = 4;
-  static const unsigned char MSG2_SIZE = 16;
-
-  static const unsigned char OFF_CHANNEL1 = 0;
-  static const unsigned char OFF_SIZE = 4;
-  static const unsigned char OFF_BDADDRESS = 6;
-  static const unsigned char OFF_CHANNEL2 = 12;
-  static const unsigned char OFF_STATUS = 16;
-
-  SocketMessageWatcher(int aFd, BluetoothSocketResultHandler* aRes)
-  : mFd(aFd)
-  , mClientFd(-1)
-  , mLen(0)
-  , mRes(aRes)
-  {
-    MOZ_ASSERT(mRes);
-  }
-
-  virtual ~SocketMessageWatcher()
-  { }
-
-  virtual void Proceed(BluetoothStatus aStatus) = 0;
-
-  void OnFileCanReadWithoutBlocking(int aFd) MOZ_OVERRIDE
-  {
-    BluetoothStatus status;
-
-    switch (mLen) {
-      case 0:
-        status = RecvMsg1();
-        break;
-      case MSG1_SIZE:
-        status = RecvMsg2();
-        break;
-      default:
-        /* message-size error */
-        status = STATUS_FAIL;
-        break;
-    }
-
-    if (IsComplete() || status != STATUS_SUCCESS) {
-      StopWatching();
-      Proceed(status);
-    }
-  }
-
-  void OnFileCanWriteWithoutBlocking(int aFd) MOZ_OVERRIDE
-  { }
-
-  void Watch()
-  {
-    // add this watcher and its result handler to hash table
-    sWatcherHashtable.Put(mRes, new SocketMessageWatcherWrapper(this));
-
-    MessageLoopForIO::current()->WatchFileDescriptor(
-      mFd,
-      true,
-      MessageLoopForIO::WATCH_READ,
-      &mWatcher,
-      this);
-  }
-
-  void StopWatching()
-  {
-    mWatcher.StopWatchingFileDescriptor();
-
-    // remove this watcher and its result handler from hash table
-    sWatcherHashtable.Remove(mRes);
-  }
-
-  bool IsComplete() const
-  {
-    return mLen == (MSG1_SIZE + MSG2_SIZE);
-  }
-
-  int GetFd() const
-  {
-    return mFd;
-  }
-
-  int32_t GetChannel1() const
-  {
-    return ReadInt32(OFF_CHANNEL1);
-  }
-
-  int32_t GetSize() const
-  {
-    return ReadInt16(OFF_SIZE);
-  }
-
-  nsString GetBdAddress() const
-  {
-    nsString bdAddress;
-    ReadBdAddress(OFF_BDADDRESS, bdAddress);
-    return bdAddress;
-  }
-
-  int32_t GetChannel2() const
-  {
-    return ReadInt32(OFF_CHANNEL2);
-  }
-
-  int32_t GetConnectionStatus() const
-  {
-    return ReadInt32(OFF_STATUS);
-  }
-
-  int GetClientFd() const
-  {
-    return mClientFd;
-  }
-
-  BluetoothSocketResultHandler* GetResultHandler() const
-  {
-    return mRes;
-  }
-
-private:
-  BluetoothStatus RecvMsg1()
-  {
-    struct iovec iv;
-    memset(&iv, 0, sizeof(iv));
-    iv.iov_base = mBuf;
-    iv.iov_len = MSG1_SIZE;
-
-    struct msghdr msg;
-    memset(&msg, 0, sizeof(msg));
-    msg.msg_iov = &iv;
-    msg.msg_iovlen = 1;
-
-    ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
-    if (res <= 0) {
-      return STATUS_FAIL;
-    }
-
-    mLen += res;
-
-    return STATUS_SUCCESS;
-  }
-
-  BluetoothStatus RecvMsg2()
-  {
-    struct iovec iv;
-    memset(&iv, 0, sizeof(iv));
-    iv.iov_base = mBuf + MSG1_SIZE;
-    iv.iov_len = MSG2_SIZE;
-
-    struct msghdr msg;
-    struct cmsghdr cmsgbuf[2 * sizeof(cmsghdr) + 0x100];
-    memset(&msg, 0, sizeof(msg));
-    msg.msg_iov = &iv;
-    msg.msg_iovlen = 1;
-    msg.msg_control = cmsgbuf;
-    msg.msg_controllen = sizeof(cmsgbuf);
-
-    ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
-    if (res <= 0) {
-      return STATUS_FAIL;
-    }
-
-    mLen += res;
-
-    if (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) {
-      return STATUS_FAIL;
-    }
-
-    struct cmsghdr *cmsgptr = CMSG_FIRSTHDR(&msg);
-
-    // Extract client fd from message header
-    for (; cmsgptr; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
-      if (CMSGHDR_CONTAINS_FD(cmsgptr)) {
-        // if multiple file descriptors have been sent, we close
-        // all but the final one.
-        if (mClientFd != -1) {
-          TEMP_FAILURE_RETRY(close(mClientFd));
-        }
-        // retrieve sent client fd
-        mClientFd = *(static_cast<int*>(CMSG_DATA(cmsgptr)));
-      }
-    }
-
-    return STATUS_SUCCESS;
-  }
-
-  int16_t ReadInt16(unsigned long aOffset) const
-  {
-    /* little-endian buffer */
-    return (static_cast<int16_t>(mBuf[aOffset + 1]) << 8) |
-            static_cast<int16_t>(mBuf[aOffset]);
-  }
-
-  int32_t ReadInt32(unsigned long aOffset) const
-  {
-    /* little-endian buffer */
-    return (static_cast<int32_t>(mBuf[aOffset + 3]) << 24) |
-           (static_cast<int32_t>(mBuf[aOffset + 2]) << 16) |
-           (static_cast<int32_t>(mBuf[aOffset + 1]) << 8) |
-            static_cast<int32_t>(mBuf[aOffset]);
-  }
-
-  void ReadBdAddress(unsigned long aOffset, nsAString& aBdAddress) const
-  {
-    const bt_bdaddr_t* bdAddress =
-      reinterpret_cast<const bt_bdaddr_t*>(mBuf+aOffset);
-
-    if (NS_FAILED(Convert(*bdAddress, aBdAddress))) {
-      aBdAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
-    }
-  }
-
-  MessageLoopForIO::FileDescriptorWatcher mWatcher;
-  int mFd;
-  int mClientFd;
-  unsigned char mLen;
-  uint8_t mBuf[MSG1_SIZE + MSG2_SIZE];
-  nsRefPtr<BluetoothSocketResultHandler> mRes;
-};
-
-/* |SocketMessageWatcherTask| starts a SocketMessageWatcher
- * on the I/O task
- */
-class SocketMessageWatcherTask MOZ_FINAL : public Task
-{
-public:
-  SocketMessageWatcherTask(SocketMessageWatcher* aWatcher)
-  : mWatcher(aWatcher)
-  {
-    MOZ_ASSERT(mWatcher);
-  }
-
-  void Run() MOZ_OVERRIDE
-  {
-    mWatcher->Watch();
-  }
-
-private:
-  SocketMessageWatcher* mWatcher;
-};
-
 /* |DeleteTask| deletes a class instance on the I/O thread
  */
 template <typename T>
 class DeleteTask MOZ_FINAL : public Task
 {
 public:
   DeleteTask(T* aPtr)
   : mPtr(aPtr)
@@ -414,21 +124,22 @@ private:
   nsAutoPtr<T> mPtr;
 };
 
 /* |ConnectWatcher| specializes SocketMessageWatcher for
  * connect operations by reading the socket messages from
  * Bluedroid and forwarding the connected socket to the
  * resource handler.
  */
-class ConnectWatcher MOZ_FINAL : public SocketMessageWatcher
+class BluetoothSocketHALInterface::ConnectWatcher MOZ_FINAL
+  : public SocketMessageWatcher
 {
 public:
   ConnectWatcher(int aFd, BluetoothSocketResultHandler* aRes)
-  : SocketMessageWatcher(aFd, aRes)
+    : SocketMessageWatcher(aFd, aRes)
   { }
 
   void Proceed(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
     DispatchBluetoothSocketHALResult(
       GetResultHandler(), &BluetoothSocketResultHandler::Connect,
       GetFd(), GetBdAddress(), GetConnectionStatus(), aStatus);
 
@@ -472,21 +183,22 @@ BluetoothSocketHALInterface::Connect(con
 
 /* |AcceptWatcher| specializes SocketMessageWatcher for Accept
  * operations by reading the socket messages from Bluedroid and
  * forwarding the received client socket to the resource handler.
  * The first message is received immediately. When there's a new
  * connection, Bluedroid sends the 2nd message with the socket
  * info and socket file descriptor.
  */
-class AcceptWatcher MOZ_FINAL : public SocketMessageWatcher
+class BluetoothSocketHALInterface::AcceptWatcher MOZ_FINAL
+  : public SocketMessageWatcher
 {
 public:
   AcceptWatcher(int aFd, BluetoothSocketResultHandler* aRes)
-  : SocketMessageWatcher(aFd, aRes)
+    : SocketMessageWatcher(aFd, aRes)
   { }
 
   void Proceed(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
     if ((aStatus != STATUS_SUCCESS) && (GetClientFd() != -1)) {
       mozilla::ScopedClose(GetClientFd()); // Close received socket fd on error
     }
 
@@ -503,46 +215,16 @@ void
 BluetoothSocketHALInterface::Accept(int aFd,
                                     BluetoothSocketResultHandler* aRes)
 {
   /* receive Bluedroid's socket-setup messages and client fd */
   Task* t = new SocketMessageWatcherTask(new AcceptWatcher(aFd, aRes));
   XRE_GetIOMessageLoop()->PostTask(FROM_HERE, t);
 }
 
-/* |DeleteSocketMessageWatcherTask| deletes a watching SocketMessageWatcher
- * on the I/O task
- */
-class DeleteSocketMessageWatcherTask MOZ_FINAL : public Task
-{
-public:
-  DeleteSocketMessageWatcherTask(BluetoothSocketResultHandler* aRes)
-  : mRes(aRes)
-  {
-    MOZ_ASSERT(mRes);
-  }
-
-  void Run() MOZ_OVERRIDE
-  {
-    // look up hash table for the watcher corresponding to |mRes|
-    SocketMessageWatcherWrapper* wrapper = sWatcherHashtable.Get(mRes);
-    if (!wrapper) {
-      return;
-    }
-
-    // stop the watcher if it exists
-    SocketMessageWatcher* watcher = wrapper->GetSocketMessageWatcher();
-    watcher->StopWatching();
-    watcher->Proceed(STATUS_DONE);
-  }
-
-private:
-  BluetoothSocketResultHandler* mRes;
-};
-
 void
 BluetoothSocketHALInterface::Close(BluetoothSocketResultHandler* aRes)
 {
   MOZ_ASSERT(aRes);
 
   /* stop the watcher corresponding to |aRes| */
   Task* t = new DeleteSocketMessageWatcherTask(aRes);
   XRE_GetIOMessageLoop()->PostTask(FROM_HERE, t);
--- a/dom/bluetooth/bluedroid/BluetoothSocketHALInterface.h
+++ b/dom/bluetooth/bluedroid/BluetoothSocketHALInterface.h
@@ -15,16 +15,19 @@
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothHALInterface;
 
 class BluetoothSocketHALInterface MOZ_FINAL
   : public BluetoothSocketInterface
 {
 public:
+  class ConnectWatcher;
+  class AcceptWatcher;
+
   friend class BluetoothHALInterface;
 
   void Listen(BluetoothSocketType aType,
               const nsAString& aServiceName,
               const uint8_t aServiceUuid[16],
               int aChannel, bool aEncrypt, bool aAuth,
               BluetoothSocketResultHandler* aRes);
 
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/bluedroid/BluetoothSocketMessageWatcher.cpp
@@ -0,0 +1,325 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "BluetoothSocketMessageWatcher.h"
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include "BluetoothInterface.h"
+#include "nsClassHashtable.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+//
+// SocketMessageWatcherWrapper
+//
+
+/* |SocketMessageWatcherWrapper| wraps SocketMessageWatcher to keep it from
+ * being released by hash table's Remove() method.
+ */
+class SocketMessageWatcherWrapper
+{
+public:
+  SocketMessageWatcherWrapper(SocketMessageWatcher* aSocketMessageWatcher)
+    : mSocketMessageWatcher(aSocketMessageWatcher)
+  {
+    MOZ_ASSERT(mSocketMessageWatcher);
+  }
+
+  SocketMessageWatcher* GetSocketMessageWatcher()
+  {
+    return mSocketMessageWatcher;
+  }
+
+private:
+  SocketMessageWatcher* mSocketMessageWatcher;
+};
+
+/* |sWatcherHashTable| maps result handlers to corresponding watchers */
+static nsClassHashtable<nsRefPtrHashKey<BluetoothSocketResultHandler>,
+                        SocketMessageWatcherWrapper>
+  sWatcherHashtable;
+
+//
+// SocketMessageWatcher
+//
+
+SocketMessageWatcher::SocketMessageWatcher(
+  int aFd, BluetoothSocketResultHandler* aRes)
+  : mFd(aFd)
+  , mClientFd(-1)
+  , mLen(0)
+  , mRes(aRes)
+{
+  MOZ_ASSERT(mRes);
+}
+
+SocketMessageWatcher::~SocketMessageWatcher()
+{ }
+
+void
+SocketMessageWatcher::OnFileCanReadWithoutBlocking(int aFd)
+{
+  BluetoothStatus status;
+
+  switch (mLen) {
+    case 0:
+      status = RecvMsg1();
+      break;
+    case MSG1_SIZE:
+      status = RecvMsg2();
+      break;
+    default:
+      /* message-size error */
+      status = STATUS_FAIL;
+      break;
+  }
+
+  if (IsComplete() || status != STATUS_SUCCESS) {
+    StopWatching();
+    Proceed(status);
+  }
+}
+
+void
+SocketMessageWatcher::OnFileCanWriteWithoutBlocking(int aFd)
+{ }
+
+void
+SocketMessageWatcher::Watch()
+{
+  // add this watcher and its result handler to hash table
+  sWatcherHashtable.Put(mRes, new SocketMessageWatcherWrapper(this));
+
+  MessageLoopForIO::current()->WatchFileDescriptor(
+    mFd,
+    true,
+    MessageLoopForIO::WATCH_READ,
+    &mWatcher,
+    this);
+}
+
+void
+SocketMessageWatcher::StopWatching()
+{
+  mWatcher.StopWatchingFileDescriptor();
+
+  // remove this watcher and its result handler from hash table
+  sWatcherHashtable.Remove(mRes);
+}
+
+bool
+SocketMessageWatcher::IsComplete() const
+{
+  return mLen == (MSG1_SIZE + MSG2_SIZE);
+}
+
+int
+SocketMessageWatcher::GetFd() const
+{
+  return mFd;
+}
+
+int32_t
+SocketMessageWatcher::GetChannel1() const
+{
+  return ReadInt32(OFF_CHANNEL1);
+}
+
+int32_t
+SocketMessageWatcher::GetSize() const
+{
+  return ReadInt16(OFF_SIZE);
+}
+
+nsString
+SocketMessageWatcher::GetBdAddress() const
+{
+  nsString bdAddress;
+  ReadBdAddress(OFF_BDADDRESS, bdAddress);
+  return bdAddress;
+}
+
+int32_t
+SocketMessageWatcher::GetChannel2() const
+{
+  return ReadInt32(OFF_CHANNEL2);
+}
+
+int32_t
+SocketMessageWatcher::GetConnectionStatus() const
+{
+  return ReadInt32(OFF_STATUS);
+}
+
+int
+SocketMessageWatcher::GetClientFd() const
+{
+  return mClientFd;
+}
+
+BluetoothSocketResultHandler*
+SocketMessageWatcher::GetResultHandler() const
+{
+  return mRes;
+}
+
+BluetoothStatus
+SocketMessageWatcher::RecvMsg1()
+{
+  struct iovec iv;
+  memset(&iv, 0, sizeof(iv));
+  iv.iov_base = mBuf;
+  iv.iov_len = MSG1_SIZE;
+
+  struct msghdr msg;
+  memset(&msg, 0, sizeof(msg));
+  msg.msg_iov = &iv;
+  msg.msg_iovlen = 1;
+
+  ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
+  if (res <= 0) {
+    return STATUS_FAIL;
+  }
+
+  mLen += res;
+
+  return STATUS_SUCCESS;
+}
+
+#define CMSGHDR_CONTAINS_FD(_cmsghdr) \
+    ( ((_cmsghdr)->cmsg_level == SOL_SOCKET) && \
+      ((_cmsghdr)->cmsg_type == SCM_RIGHTS) )
+
+BluetoothStatus
+SocketMessageWatcher::RecvMsg2()
+{
+  struct iovec iv;
+  memset(&iv, 0, sizeof(iv));
+  iv.iov_base = mBuf + MSG1_SIZE;
+  iv.iov_len = MSG2_SIZE;
+
+  struct msghdr msg;
+  struct cmsghdr cmsgbuf[2 * sizeof(cmsghdr) + 0x100];
+  memset(&msg, 0, sizeof(msg));
+  msg.msg_iov = &iv;
+  msg.msg_iovlen = 1;
+  msg.msg_control = cmsgbuf;
+  msg.msg_controllen = sizeof(cmsgbuf);
+
+  ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
+  if (res <= 0) {
+    return STATUS_FAIL;
+  }
+
+  mLen += res;
+
+  if (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) {
+    return STATUS_FAIL;
+  }
+
+  struct cmsghdr *cmsgptr = CMSG_FIRSTHDR(&msg);
+
+  // Extract client fd from message header
+  for (; cmsgptr; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
+    if (CMSGHDR_CONTAINS_FD(cmsgptr)) {
+      // if multiple file descriptors have been sent, we close
+      // all but the final one.
+      if (mClientFd != -1) {
+        TEMP_FAILURE_RETRY(close(mClientFd));
+      }
+      // retrieve sent client fd
+      mClientFd = *(static_cast<int*>(CMSG_DATA(cmsgptr)));
+    }
+  }
+
+  return STATUS_SUCCESS;
+}
+
+int16_t
+SocketMessageWatcher::ReadInt16(unsigned long aOffset) const
+{
+  /* little-endian buffer */
+  return (static_cast<int16_t>(mBuf[aOffset + 1]) << 8) |
+          static_cast<int16_t>(mBuf[aOffset]);
+}
+
+int32_t
+SocketMessageWatcher::ReadInt32(unsigned long aOffset) const
+{
+  /* little-endian buffer */
+  return (static_cast<int32_t>(mBuf[aOffset + 3]) << 24) |
+         (static_cast<int32_t>(mBuf[aOffset + 2]) << 16) |
+         (static_cast<int32_t>(mBuf[aOffset + 1]) << 8) |
+          static_cast<int32_t>(mBuf[aOffset]);
+}
+
+void
+SocketMessageWatcher::ReadBdAddress(unsigned long aOffset,
+                                    nsAString& aBdAddress) const
+{
+  char str[BLUETOOTH_ADDRESS_LENGTH + 1];
+
+  int res = snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x",
+                     static_cast<int>(mBuf[aOffset + 0]),
+                     static_cast<int>(mBuf[aOffset + 1]),
+                     static_cast<int>(mBuf[aOffset + 2]),
+                     static_cast<int>(mBuf[aOffset + 3]),
+                     static_cast<int>(mBuf[aOffset + 4]),
+                     static_cast<int>(mBuf[aOffset + 5]));
+  if (res < 0) {
+    aBdAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
+  } else if ((size_t)res >= sizeof(str)) { /* string buffer too small */
+    aBdAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
+  } else {
+    aBdAddress = NS_ConvertUTF8toUTF16(str);
+  }
+}
+
+//
+// SocketMessageWatcherTask
+//
+
+SocketMessageWatcherTask::SocketMessageWatcherTask(
+  SocketMessageWatcher* aWatcher)
+  : mWatcher(aWatcher)
+{
+  MOZ_ASSERT(mWatcher);
+}
+
+void
+SocketMessageWatcherTask::Run()
+{
+  mWatcher->Watch();
+}
+
+//
+// DeleteSocketMessageWatcherTask
+//
+
+DeleteSocketMessageWatcherTask::DeleteSocketMessageWatcherTask(
+  BluetoothSocketResultHandler* aRes)
+  : mRes(aRes)
+{
+  MOZ_ASSERT(mRes);
+}
+
+void
+DeleteSocketMessageWatcherTask::Run()
+{
+  // look up hash table for the watcher corresponding to |mRes|
+  SocketMessageWatcherWrapper* wrapper = sWatcherHashtable.Get(mRes);
+  if (!wrapper) {
+    return;
+  }
+
+  // stop the watcher if it exists
+  SocketMessageWatcher* watcher = wrapper->GetSocketMessageWatcher();
+  watcher->StopWatching();
+  watcher->Proceed(STATUS_DONE);
+}
+
+END_BLUETOOTH_NAMESPACE
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/bluedroid/BluetoothSocketMessageWatcher.h
@@ -0,0 +1,109 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "base/message_loop.h"
+#include "BluetoothCommon.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothSocketResultHandler;
+
+/* |SocketMessageWatcher| receives Bluedroid's socket setup
+ * messages on the I/O thread. You need to inherit from this
+ * class to make use of it.
+ *
+ * Bluedroid sends two socket info messages (20 bytes) at
+ * the beginning of a connection to both peers.
+ *
+ *   - 1st message: [channel:4]
+ *   - 2nd message: [size:2][bd address:6][channel:4][connection status:4]
+ *
+ * On the server side, the second message will contain a
+ * socket file descriptor for the connection. The client
+ * uses the original file descriptor.
+ */
+class SocketMessageWatcher : public MessageLoopForIO::Watcher
+{
+public:
+  static const unsigned char MSG1_SIZE = 4;
+  static const unsigned char MSG2_SIZE = 16;
+
+  static const unsigned char OFF_CHANNEL1 = 0;
+  static const unsigned char OFF_SIZE = 4;
+  static const unsigned char OFF_BDADDRESS = 6;
+  static const unsigned char OFF_CHANNEL2 = 12;
+  static const unsigned char OFF_STATUS = 16;
+
+  virtual ~SocketMessageWatcher();
+
+  virtual void Proceed(BluetoothStatus aStatus) = 0;
+
+  void OnFileCanReadWithoutBlocking(int aFd) MOZ_OVERRIDE;
+  void OnFileCanWriteWithoutBlocking(int aFd) MOZ_OVERRIDE;
+
+  void Watch();
+  void StopWatching();
+
+  bool IsComplete() const;
+
+  int      GetFd() const;
+  int32_t  GetChannel1() const;
+  int32_t  GetSize() const;
+  nsString GetBdAddress() const;
+  int32_t  GetChannel2() const;
+  int32_t  GetConnectionStatus() const;
+  int      GetClientFd() const;
+
+  BluetoothSocketResultHandler* GetResultHandler() const;
+
+protected:
+  SocketMessageWatcher(int aFd, BluetoothSocketResultHandler* aRes);
+
+private:
+  BluetoothStatus RecvMsg1();
+  BluetoothStatus RecvMsg2();
+
+  int16_t ReadInt16(unsigned long aOffset) const;
+  int32_t ReadInt32(unsigned long aOffset) const;
+  void    ReadBdAddress(unsigned long aOffset, nsAString& aBdAddress) const;
+
+  MessageLoopForIO::FileDescriptorWatcher mWatcher;
+  int mFd;
+  int mClientFd;
+  unsigned char mLen;
+  uint8_t mBuf[MSG1_SIZE + MSG2_SIZE];
+  nsRefPtr<BluetoothSocketResultHandler> mRes;
+};
+
+/* |SocketMessageWatcherTask| starts a SocketMessageWatcher
+ * on the I/O task
+ */
+class SocketMessageWatcherTask MOZ_FINAL : public Task
+{
+public:
+  SocketMessageWatcherTask(SocketMessageWatcher* aWatcher);
+
+  void Run() MOZ_OVERRIDE;
+
+private:
+  SocketMessageWatcher* mWatcher;
+};
+
+/* |DeleteSocketMessageWatcherTask| deletes a watching SocketMessageWatcher
+ * on the I/O task
+ */
+class DeleteSocketMessageWatcherTask MOZ_FINAL : public Task
+{
+public:
+  DeleteSocketMessageWatcherTask(BluetoothSocketResultHandler* aRes);
+
+  void Run() MOZ_OVERRIDE;
+
+private:
+  BluetoothSocketResultHandler* mRes;
+};
+
+END_BLUETOOTH_NAMESPACE
--- a/dom/bluetooth/moz.build
+++ b/dom/bluetooth/moz.build
@@ -50,16 +50,17 @@ if CONFIG['MOZ_B2G_BT']:
                 'bluedroid/BluetoothAvrcpHALInterface.cpp',
                 'bluedroid/BluetoothHALHelpers.cpp',
                 'bluedroid/BluetoothHALInterface.cpp',
                 'bluedroid/BluetoothHandsfreeHALInterface.cpp',
                 'bluedroid/BluetoothOppManager.cpp',
                 'bluedroid/BluetoothServiceBluedroid.cpp',
                 'bluedroid/BluetoothSocket.cpp',
                 'bluedroid/BluetoothSocketHALInterface.cpp',
+                'bluedroid/BluetoothSocketMessageWatcher.cpp',
                 'bluedroid/BluetoothUtils.cpp',
             ]
             LOCAL_INCLUDES += [
                 'bluedroid',
             ]
 
             if CONFIG['MOZ_B2G_RIL']:
                 SOURCES += [
--- a/dom/bluetooth2/bluedroid/BluetoothSocketHALInterface.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothSocketHALInterface.cpp
@@ -1,19 +1,17 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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 "BluetoothSocketHALInterface.h"
-#include <errno.h>
-#include <sys/socket.h>
-#include <unistd.h>
 #include "BluetoothHALHelpers.h"
+#include "BluetoothSocketMessageWatcher.h"
 #include "mozilla/FileUtils.h"
 #include "nsClassHashtable.h"
 #include "nsXULAppAPI.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 typedef
   BluetoothHALInterfaceRunnable1<BluetoothSocketResultHandler, void,
@@ -102,304 +100,16 @@ BluetoothSocketHALInterface::Listen(Blue
 
   if (aRes) {
     DispatchBluetoothSocketHALResult(
       aRes, &BluetoothSocketResultHandler::Listen, fd,
       ConvertDefault(status, STATUS_FAIL));
   }
 }
 
-#define CMSGHDR_CONTAINS_FD(_cmsghdr) \
-    ( ((_cmsghdr)->cmsg_level == SOL_SOCKET) && \
-      ((_cmsghdr)->cmsg_type == SCM_RIGHTS) )
-
-class SocketMessageWatcher;
-
-/* |SocketMessageWatcherWrapper| wraps SocketMessageWatcher to keep it from
- * being released by hash table's Remove() method.
- */
-class SocketMessageWatcherWrapper
-{
-public:
-  SocketMessageWatcherWrapper(SocketMessageWatcher* aSocketMessageWatcher)
-  : mSocketMessageWatcher(aSocketMessageWatcher)
-  {
-    MOZ_ASSERT(mSocketMessageWatcher);
-  }
-
-  SocketMessageWatcher* GetSocketMessageWatcher()
-  {
-    return mSocketMessageWatcher;
-  }
-
-private:
-  SocketMessageWatcher* mSocketMessageWatcher;
-};
-
-/* |sWatcherHashTable| maps result handlers to corresponding watchers */
-static nsClassHashtable<nsRefPtrHashKey<BluetoothSocketResultHandler>,
-                        SocketMessageWatcherWrapper>
-  sWatcherHashtable;
-
-/* |SocketMessageWatcher| receives Bluedroid's socket setup
- * messages on the I/O thread. You need to inherit from this
- * class to make use of it.
- *
- * Bluedroid sends two socket info messages (20 bytes) at
- * the beginning of a connection to both peers.
- *
- *   - 1st message: [channel:4]
- *   - 2nd message: [size:2][bd address:6][channel:4][connection status:4]
- *
- * On the server side, the second message will contain a
- * socket file descriptor for the connection. The client
- * uses the original file descriptor.
- */
-class SocketMessageWatcher : public MessageLoopForIO::Watcher
-{
-public:
-  static const unsigned char MSG1_SIZE = 4;
-  static const unsigned char MSG2_SIZE = 16;
-
-  static const unsigned char OFF_CHANNEL1 = 0;
-  static const unsigned char OFF_SIZE = 4;
-  static const unsigned char OFF_BDADDRESS = 6;
-  static const unsigned char OFF_CHANNEL2 = 12;
-  static const unsigned char OFF_STATUS = 16;
-
-  SocketMessageWatcher(int aFd, BluetoothSocketResultHandler* aRes)
-  : mFd(aFd)
-  , mClientFd(-1)
-  , mLen(0)
-  , mRes(aRes)
-  {
-    MOZ_ASSERT(mRes);
-  }
-
-  virtual ~SocketMessageWatcher()
-  { }
-
-  virtual void Proceed(BluetoothStatus aStatus) = 0;
-
-  void OnFileCanReadWithoutBlocking(int aFd) MOZ_OVERRIDE
-  {
-    BluetoothStatus status;
-
-    switch (mLen) {
-      case 0:
-        status = RecvMsg1();
-        break;
-      case MSG1_SIZE:
-        status = RecvMsg2();
-        break;
-      default:
-        /* message-size error */
-        status = STATUS_FAIL;
-        break;
-    }
-
-    if (IsComplete() || status != STATUS_SUCCESS) {
-      StopWatching();
-      Proceed(status);
-    }
-  }
-
-  void OnFileCanWriteWithoutBlocking(int aFd) MOZ_OVERRIDE
-  { }
-
-  void Watch()
-  {
-    // add this watcher and its result handler to hash table
-    sWatcherHashtable.Put(mRes, new SocketMessageWatcherWrapper(this));
-
-    MessageLoopForIO::current()->WatchFileDescriptor(
-      mFd,
-      true,
-      MessageLoopForIO::WATCH_READ,
-      &mWatcher,
-      this);
-  }
-
-  void StopWatching()
-  {
-    mWatcher.StopWatchingFileDescriptor();
-
-    // remove this watcher and its result handler from hash table
-    sWatcherHashtable.Remove(mRes);
-  }
-
-  bool IsComplete() const
-  {
-    return mLen == (MSG1_SIZE + MSG2_SIZE);
-  }
-
-  int GetFd() const
-  {
-    return mFd;
-  }
-
-  int32_t GetChannel1() const
-  {
-    return ReadInt32(OFF_CHANNEL1);
-  }
-
-  int32_t GetSize() const
-  {
-    return ReadInt16(OFF_SIZE);
-  }
-
-  nsString GetBdAddress() const
-  {
-    nsString bdAddress;
-    ReadBdAddress(OFF_BDADDRESS, bdAddress);
-    return bdAddress;
-  }
-
-  int32_t GetChannel2() const
-  {
-    return ReadInt32(OFF_CHANNEL2);
-  }
-
-  int32_t GetConnectionStatus() const
-  {
-    return ReadInt32(OFF_STATUS);
-  }
-
-  int GetClientFd() const
-  {
-    return mClientFd;
-  }
-
-  BluetoothSocketResultHandler* GetResultHandler() const
-  {
-    return mRes;
-  }
-
-private:
-  BluetoothStatus RecvMsg1()
-  {
-    struct iovec iv;
-    memset(&iv, 0, sizeof(iv));
-    iv.iov_base = mBuf;
-    iv.iov_len = MSG1_SIZE;
-
-    struct msghdr msg;
-    memset(&msg, 0, sizeof(msg));
-    msg.msg_iov = &iv;
-    msg.msg_iovlen = 1;
-
-    ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
-    if (res <= 0) {
-      return STATUS_FAIL;
-    }
-
-    mLen += res;
-
-    return STATUS_SUCCESS;
-  }
-
-  BluetoothStatus RecvMsg2()
-  {
-    struct iovec iv;
-    memset(&iv, 0, sizeof(iv));
-    iv.iov_base = mBuf + MSG1_SIZE;
-    iv.iov_len = MSG2_SIZE;
-
-    struct msghdr msg;
-    struct cmsghdr cmsgbuf[2 * sizeof(cmsghdr) + 0x100];
-    memset(&msg, 0, sizeof(msg));
-    msg.msg_iov = &iv;
-    msg.msg_iovlen = 1;
-    msg.msg_control = cmsgbuf;
-    msg.msg_controllen = sizeof(cmsgbuf);
-
-    ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
-    if (res <= 0) {
-      return STATUS_FAIL;
-    }
-
-    mLen += res;
-
-    if (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) {
-      return STATUS_FAIL;
-    }
-
-    struct cmsghdr *cmsgptr = CMSG_FIRSTHDR(&msg);
-
-    // Extract client fd from message header
-    for (; cmsgptr; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
-      if (CMSGHDR_CONTAINS_FD(cmsgptr)) {
-        // if multiple file descriptors have been sent, we close
-        // all but the final one.
-        if (mClientFd != -1) {
-          TEMP_FAILURE_RETRY(close(mClientFd));
-        }
-        // retrieve sent client fd
-        mClientFd = *(static_cast<int*>(CMSG_DATA(cmsgptr)));
-      }
-    }
-
-    return STATUS_SUCCESS;
-  }
-
-  int16_t ReadInt16(unsigned long aOffset) const
-  {
-    /* little-endian buffer */
-    return (static_cast<int16_t>(mBuf[aOffset + 1]) << 8) |
-            static_cast<int16_t>(mBuf[aOffset]);
-  }
-
-  int32_t ReadInt32(unsigned long aOffset) const
-  {
-    /* little-endian buffer */
-    return (static_cast<int32_t>(mBuf[aOffset + 3]) << 24) |
-           (static_cast<int32_t>(mBuf[aOffset + 2]) << 16) |
-           (static_cast<int32_t>(mBuf[aOffset + 1]) << 8) |
-            static_cast<int32_t>(mBuf[aOffset]);
-  }
-
-  void ReadBdAddress(unsigned long aOffset, nsAString& aBdAddress) const
-  {
-    const bt_bdaddr_t* bdAddress =
-      reinterpret_cast<const bt_bdaddr_t*>(mBuf+aOffset);
-
-    if (NS_FAILED(Convert(*bdAddress, aBdAddress))) {
-      aBdAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
-    }
-  }
-
-  MessageLoopForIO::FileDescriptorWatcher mWatcher;
-  int mFd;
-  int mClientFd;
-  unsigned char mLen;
-  uint8_t mBuf[MSG1_SIZE + MSG2_SIZE];
-  nsRefPtr<BluetoothSocketResultHandler> mRes;
-};
-
-/* |SocketMessageWatcherTask| starts a SocketMessageWatcher
- * on the I/O task
- */
-class SocketMessageWatcherTask MOZ_FINAL : public Task
-{
-public:
-  SocketMessageWatcherTask(SocketMessageWatcher* aWatcher)
-  : mWatcher(aWatcher)
-  {
-    MOZ_ASSERT(mWatcher);
-  }
-
-  void Run() MOZ_OVERRIDE
-  {
-    mWatcher->Watch();
-  }
-
-private:
-  SocketMessageWatcher* mWatcher;
-};
-
 /* |DeleteTask| deletes a class instance on the I/O thread
  */
 template <typename T>
 class DeleteTask MOZ_FINAL : public Task
 {
 public:
   DeleteTask(T* aPtr)
   : mPtr(aPtr)
@@ -414,21 +124,22 @@ private:
   nsAutoPtr<T> mPtr;
 };
 
 /* |ConnectWatcher| specializes SocketMessageWatcher for
  * connect operations by reading the socket messages from
  * Bluedroid and forwarding the connected socket to the
  * resource handler.
  */
-class ConnectWatcher MOZ_FINAL : public SocketMessageWatcher
+class BluetoothSocketHALInterface::ConnectWatcher MOZ_FINAL
+  : public SocketMessageWatcher
 {
 public:
   ConnectWatcher(int aFd, BluetoothSocketResultHandler* aRes)
-  : SocketMessageWatcher(aFd, aRes)
+    : SocketMessageWatcher(aFd, aRes)
   { }
 
   void Proceed(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
     DispatchBluetoothSocketHALResult(
       GetResultHandler(), &BluetoothSocketResultHandler::Connect,
       GetFd(), GetBdAddress(), GetConnectionStatus(), aStatus);
 
@@ -472,21 +183,22 @@ BluetoothSocketHALInterface::Connect(con
 
 /* |AcceptWatcher| specializes |SocketMessageWatcher| for accept
  * operations by reading the socket messages from Bluedroid and
  * forwarding the received client socket to the resource handler.
  * The first message is received immediately. When there's a new
  * connection, Bluedroid sends the 2nd message with the socket
  * info and socket file descriptor.
  */
-class AcceptWatcher MOZ_FINAL : public SocketMessageWatcher
+class BluetoothSocketHALInterface::AcceptWatcher MOZ_FINAL
+  : public SocketMessageWatcher
 {
 public:
   AcceptWatcher(int aFd, BluetoothSocketResultHandler* aRes)
-  : SocketMessageWatcher(aFd, aRes)
+    : SocketMessageWatcher(aFd, aRes)
   { }
 
   void Proceed(BluetoothStatus aStatus) MOZ_OVERRIDE
   {
     if ((aStatus != STATUS_SUCCESS) && (GetClientFd() != -1)) {
       mozilla::ScopedClose(GetClientFd()); // Close received socket fd on error
     }
 
@@ -503,46 +215,16 @@ void
 BluetoothSocketHALInterface::Accept(int aFd,
                                     BluetoothSocketResultHandler* aRes)
 {
   /* receive Bluedroid's socket-setup messages and client fd */
   Task* t = new SocketMessageWatcherTask(new AcceptWatcher(aFd, aRes));
   XRE_GetIOMessageLoop()->PostTask(FROM_HERE, t);
 }
 
-/* |DeleteSocketMessageWatcherTask| deletes a watching SocketMessageWatcher
- * on the I/O task
- */
-class DeleteSocketMessageWatcherTask MOZ_FINAL : public Task
-{
-public:
-  DeleteSocketMessageWatcherTask(BluetoothSocketResultHandler* aRes)
-  : mRes(aRes)
-  {
-    MOZ_ASSERT(mRes);
-  }
-
-  void Run() MOZ_OVERRIDE
-  {
-    // look up hash table for the watcher corresponding to |mRes|
-    SocketMessageWatcherWrapper* wrapper = sWatcherHashtable.Get(mRes);
-    if (!wrapper) {
-      return;
-    }
-
-    // stop the watcher if it exists
-    SocketMessageWatcher* watcher = wrapper->GetSocketMessageWatcher();
-    watcher->StopWatching();
-    watcher->Proceed(STATUS_DONE);
-  }
-
-private:
-  BluetoothSocketResultHandler* mRes;
-};
-
 void
 BluetoothSocketHALInterface::Close(BluetoothSocketResultHandler* aRes)
 {
   MOZ_ASSERT(aRes);
 
   /* stop the watcher corresponding to |aRes| */
   Task* t = new DeleteSocketMessageWatcherTask(aRes);
   XRE_GetIOMessageLoop()->PostTask(FROM_HERE, t);
--- a/dom/bluetooth2/bluedroid/BluetoothSocketHALInterface.h
+++ b/dom/bluetooth2/bluedroid/BluetoothSocketHALInterface.h
@@ -15,16 +15,19 @@
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothHALInterface;
 
 class BluetoothSocketHALInterface MOZ_FINAL
   : public BluetoothSocketInterface
 {
 public:
+  class ConnectWatcher;
+  class AcceptWatcher;
+
   friend class BluetoothHALInterface;
 
   void Listen(BluetoothSocketType aType,
               const nsAString& aServiceName,
               const uint8_t aServiceUuid[16],
               int aChannel, bool aEncrypt, bool aAuth,
               BluetoothSocketResultHandler* aRes);
 
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/bluedroid/BluetoothSocketMessageWatcher.cpp
@@ -0,0 +1,325 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "BluetoothSocketMessageWatcher.h"
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include "BluetoothInterface.h"
+#include "nsClassHashtable.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+//
+// SocketMessageWatcherWrapper
+//
+
+/* |SocketMessageWatcherWrapper| wraps SocketMessageWatcher to keep it from
+ * being released by hash table's Remove() method.
+ */
+class SocketMessageWatcherWrapper
+{
+public:
+  SocketMessageWatcherWrapper(SocketMessageWatcher* aSocketMessageWatcher)
+    : mSocketMessageWatcher(aSocketMessageWatcher)
+  {
+    MOZ_ASSERT(mSocketMessageWatcher);
+  }
+
+  SocketMessageWatcher* GetSocketMessageWatcher()
+  {
+    return mSocketMessageWatcher;
+  }
+
+private:
+  SocketMessageWatcher* mSocketMessageWatcher;
+};
+
+/* |sWatcherHashTable| maps result handlers to corresponding watchers */
+static nsClassHashtable<nsRefPtrHashKey<BluetoothSocketResultHandler>,
+                        SocketMessageWatcherWrapper>
+  sWatcherHashtable;
+
+//
+// SocketMessageWatcher
+//
+
+SocketMessageWatcher::SocketMessageWatcher(
+  int aFd, BluetoothSocketResultHandler* aRes)
+  : mFd(aFd)
+  , mClientFd(-1)
+  , mLen(0)
+  , mRes(aRes)
+{
+  MOZ_ASSERT(mRes);
+}
+
+SocketMessageWatcher::~SocketMessageWatcher()
+{ }
+
+void
+SocketMessageWatcher::OnFileCanReadWithoutBlocking(int aFd)
+{
+  BluetoothStatus status;
+
+  switch (mLen) {
+    case 0:
+      status = RecvMsg1();
+      break;
+    case MSG1_SIZE:
+      status = RecvMsg2();
+      break;
+    default:
+      /* message-size error */
+      status = STATUS_FAIL;
+      break;
+  }
+
+  if (IsComplete() || status != STATUS_SUCCESS) {
+    StopWatching();
+    Proceed(status);
+  }
+}
+
+void
+SocketMessageWatcher::OnFileCanWriteWithoutBlocking(int aFd)
+{ }
+
+void
+SocketMessageWatcher::Watch()
+{
+  // add this watcher and its result handler to hash table
+  sWatcherHashtable.Put(mRes, new SocketMessageWatcherWrapper(this));
+
+  MessageLoopForIO::current()->WatchFileDescriptor(
+    mFd,
+    true,
+    MessageLoopForIO::WATCH_READ,
+    &mWatcher,
+    this);
+}
+
+void
+SocketMessageWatcher::StopWatching()
+{
+  mWatcher.StopWatchingFileDescriptor();
+
+  // remove this watcher and its result handler from hash table
+  sWatcherHashtable.Remove(mRes);
+}
+
+bool
+SocketMessageWatcher::IsComplete() const
+{
+  return mLen == (MSG1_SIZE + MSG2_SIZE);
+}
+
+int
+SocketMessageWatcher::GetFd() const
+{
+  return mFd;
+}
+
+int32_t
+SocketMessageWatcher::GetChannel1() const
+{
+  return ReadInt32(OFF_CHANNEL1);
+}
+
+int32_t
+SocketMessageWatcher::GetSize() const
+{
+  return ReadInt16(OFF_SIZE);
+}
+
+nsString
+SocketMessageWatcher::GetBdAddress() const
+{
+  nsString bdAddress;
+  ReadBdAddress(OFF_BDADDRESS, bdAddress);
+  return bdAddress;
+}
+
+int32_t
+SocketMessageWatcher::GetChannel2() const
+{
+  return ReadInt32(OFF_CHANNEL2);
+}
+
+int32_t
+SocketMessageWatcher::GetConnectionStatus() const
+{
+  return ReadInt32(OFF_STATUS);
+}
+
+int
+SocketMessageWatcher::GetClientFd() const
+{
+  return mClientFd;
+}
+
+BluetoothSocketResultHandler*
+SocketMessageWatcher::GetResultHandler() const
+{
+  return mRes;
+}
+
+BluetoothStatus
+SocketMessageWatcher::RecvMsg1()
+{
+  struct iovec iv;
+  memset(&iv, 0, sizeof(iv));
+  iv.iov_base = mBuf;
+  iv.iov_len = MSG1_SIZE;
+
+  struct msghdr msg;
+  memset(&msg, 0, sizeof(msg));
+  msg.msg_iov = &iv;
+  msg.msg_iovlen = 1;
+
+  ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
+  if (res <= 0) {
+    return STATUS_FAIL;
+  }
+
+  mLen += res;
+
+  return STATUS_SUCCESS;
+}
+
+#define CMSGHDR_CONTAINS_FD(_cmsghdr) \
+    ( ((_cmsghdr)->cmsg_level == SOL_SOCKET) && \
+      ((_cmsghdr)->cmsg_type == SCM_RIGHTS) )
+
+BluetoothStatus
+SocketMessageWatcher::RecvMsg2()
+{
+  struct iovec iv;
+  memset(&iv, 0, sizeof(iv));
+  iv.iov_base = mBuf + MSG1_SIZE;
+  iv.iov_len = MSG2_SIZE;
+
+  struct msghdr msg;
+  struct cmsghdr cmsgbuf[2 * sizeof(cmsghdr) + 0x100];
+  memset(&msg, 0, sizeof(msg));
+  msg.msg_iov = &iv;
+  msg.msg_iovlen = 1;
+  msg.msg_control = cmsgbuf;
+  msg.msg_controllen = sizeof(cmsgbuf);
+
+  ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
+  if (res <= 0) {
+    return STATUS_FAIL;
+  }
+
+  mLen += res;
+
+  if (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) {
+    return STATUS_FAIL;
+  }
+
+  struct cmsghdr *cmsgptr = CMSG_FIRSTHDR(&msg);
+
+  // Extract client fd from message header
+  for (; cmsgptr; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
+    if (CMSGHDR_CONTAINS_FD(cmsgptr)) {
+      // if multiple file descriptors have been sent, we close
+      // all but the final one.
+      if (mClientFd != -1) {
+        TEMP_FAILURE_RETRY(close(mClientFd));
+      }
+      // retrieve sent client fd
+      mClientFd = *(static_cast<int*>(CMSG_DATA(cmsgptr)));
+    }
+  }
+
+  return STATUS_SUCCESS;
+}
+
+int16_t
+SocketMessageWatcher::ReadInt16(unsigned long aOffset) const
+{
+  /* little-endian buffer */
+  return (static_cast<int16_t>(mBuf[aOffset + 1]) << 8) |
+          static_cast<int16_t>(mBuf[aOffset]);
+}
+
+int32_t
+SocketMessageWatcher::ReadInt32(unsigned long aOffset) const
+{
+  /* little-endian buffer */
+  return (static_cast<int32_t>(mBuf[aOffset + 3]) << 24) |
+         (static_cast<int32_t>(mBuf[aOffset + 2]) << 16) |
+         (static_cast<int32_t>(mBuf[aOffset + 1]) << 8) |
+          static_cast<int32_t>(mBuf[aOffset]);
+}
+
+void
+SocketMessageWatcher::ReadBdAddress(unsigned long aOffset,
+                                    nsAString& aBdAddress) const
+{
+  char str[BLUETOOTH_ADDRESS_LENGTH + 1];
+
+  int res = snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x",
+                     static_cast<int>(mBuf[aOffset + 0]),
+                     static_cast<int>(mBuf[aOffset + 1]),
+                     static_cast<int>(mBuf[aOffset + 2]),
+                     static_cast<int>(mBuf[aOffset + 3]),
+                     static_cast<int>(mBuf[aOffset + 4]),
+                     static_cast<int>(mBuf[aOffset + 5]));
+  if (res < 0) {
+    aBdAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
+  } else if ((size_t)res >= sizeof(str)) { /* string buffer too small */
+    aBdAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
+  } else {
+    aBdAddress = NS_ConvertUTF8toUTF16(str);
+  }
+}
+
+//
+// SocketMessageWatcherTask
+//
+
+SocketMessageWatcherTask::SocketMessageWatcherTask(
+  SocketMessageWatcher* aWatcher)
+  : mWatcher(aWatcher)
+{
+  MOZ_ASSERT(mWatcher);
+}
+
+void
+SocketMessageWatcherTask::Run()
+{
+  mWatcher->Watch();
+}
+
+//
+// DeleteSocketMessageWatcherTask
+//
+
+DeleteSocketMessageWatcherTask::DeleteSocketMessageWatcherTask(
+  BluetoothSocketResultHandler* aRes)
+  : mRes(aRes)
+{
+  MOZ_ASSERT(mRes);
+}
+
+void
+DeleteSocketMessageWatcherTask::Run()
+{
+  // look up hash table for the watcher corresponding to |mRes|
+  SocketMessageWatcherWrapper* wrapper = sWatcherHashtable.Get(mRes);
+  if (!wrapper) {
+    return;
+  }
+
+  // stop the watcher if it exists
+  SocketMessageWatcher* watcher = wrapper->GetSocketMessageWatcher();
+  watcher->StopWatching();
+  watcher->Proceed(STATUS_DONE);
+}
+
+END_BLUETOOTH_NAMESPACE
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/bluedroid/BluetoothSocketMessageWatcher.h
@@ -0,0 +1,109 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "base/message_loop.h"
+#include "BluetoothCommon.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothSocketResultHandler;
+
+/* |SocketMessageWatcher| receives Bluedroid's socket setup
+ * messages on the I/O thread. You need to inherit from this
+ * class to make use of it.
+ *
+ * Bluedroid sends two socket info messages (20 bytes) at
+ * the beginning of a connection to both peers.
+ *
+ *   - 1st message: [channel:4]
+ *   - 2nd message: [size:2][bd address:6][channel:4][connection status:4]
+ *
+ * On the server side, the second message will contain a
+ * socket file descriptor for the connection. The client
+ * uses the original file descriptor.
+ */
+class SocketMessageWatcher : public MessageLoopForIO::Watcher
+{
+public:
+  static const unsigned char MSG1_SIZE = 4;
+  static const unsigned char MSG2_SIZE = 16;
+
+  static const unsigned char OFF_CHANNEL1 = 0;
+  static const unsigned char OFF_SIZE = 4;
+  static const unsigned char OFF_BDADDRESS = 6;
+  static const unsigned char OFF_CHANNEL2 = 12;
+  static const unsigned char OFF_STATUS = 16;
+
+  virtual ~SocketMessageWatcher();
+
+  virtual void Proceed(BluetoothStatus aStatus) = 0;
+
+  void OnFileCanReadWithoutBlocking(int aFd) MOZ_OVERRIDE;
+  void OnFileCanWriteWithoutBlocking(int aFd) MOZ_OVERRIDE;
+
+  void Watch();
+  void StopWatching();
+
+  bool IsComplete() const;
+
+  int      GetFd() const;
+  int32_t  GetChannel1() const;
+  int32_t  GetSize() const;
+  nsString GetBdAddress() const;
+  int32_t  GetChannel2() const;
+  int32_t  GetConnectionStatus() const;
+  int      GetClientFd() const;
+
+  BluetoothSocketResultHandler* GetResultHandler() const;
+
+protected:
+  SocketMessageWatcher(int aFd, BluetoothSocketResultHandler* aRes);
+
+private:
+  BluetoothStatus RecvMsg1();
+  BluetoothStatus RecvMsg2();
+
+  int16_t ReadInt16(unsigned long aOffset) const;
+  int32_t ReadInt32(unsigned long aOffset) const;
+  void    ReadBdAddress(unsigned long aOffset, nsAString& aBdAddress) const;
+
+  MessageLoopForIO::FileDescriptorWatcher mWatcher;
+  int mFd;
+  int mClientFd;
+  unsigned char mLen;
+  uint8_t mBuf[MSG1_SIZE + MSG2_SIZE];
+  nsRefPtr<BluetoothSocketResultHandler> mRes;
+};
+
+/* |SocketMessageWatcherTask| starts a SocketMessageWatcher
+ * on the I/O task
+ */
+class SocketMessageWatcherTask MOZ_FINAL : public Task
+{
+public:
+  SocketMessageWatcherTask(SocketMessageWatcher* aWatcher);
+
+  void Run() MOZ_OVERRIDE;
+
+private:
+  SocketMessageWatcher* mWatcher;
+};
+
+/* |DeleteSocketMessageWatcherTask| deletes a watching SocketMessageWatcher
+ * on the I/O task
+ */
+class DeleteSocketMessageWatcherTask MOZ_FINAL : public Task
+{
+public:
+  DeleteSocketMessageWatcherTask(BluetoothSocketResultHandler* aRes);
+
+  void Run() MOZ_OVERRIDE;
+
+private:
+  BluetoothSocketResultHandler* mRes;
+};
+
+END_BLUETOOTH_NAMESPACE
--- a/dom/bluetooth2/moz.build
+++ b/dom/bluetooth2/moz.build
@@ -55,16 +55,17 @@ if CONFIG['MOZ_B2G_BT']:
                 'bluedroid/BluetoothGattManager.cpp',
                 'bluedroid/BluetoothHALHelpers.cpp',
                 'bluedroid/BluetoothHALInterface.cpp',
                 'bluedroid/BluetoothHandsfreeHALInterface.cpp',
                 'bluedroid/BluetoothOppManager.cpp',
                 'bluedroid/BluetoothServiceBluedroid.cpp',
                 'bluedroid/BluetoothSocket.cpp',
                 'bluedroid/BluetoothSocketHALInterface.cpp',
+                'bluedroid/BluetoothSocketMessageWatcher.cpp',
                 'bluedroid/BluetoothUtils.cpp',
             ]
             LOCAL_INCLUDES += [
                 'bluedroid',
             ]
 
             if CONFIG['MOZ_B2G_RIL']:
                 SOURCES += [