Bug 1036977: Handle socket setup for |Connect| in BluetoothInterface (under bluetooth2/), r=btian
authorThomas Zimmermann <tdz@users.sourceforge.net>
Wed, 16 Jul 2014 10:24:33 +0200
changeset 194437 bea73261c79229a3fa07694529f52037bfa1b304
parent 194436 b709235dd6b7a8a04edebfa0695c34e4ee86f789
child 194438 60eabd556f3bae0a313e800ec10b8b9d4ea29180
push id27146
push userkwierso@gmail.com
push dateThu, 17 Jul 2014 00:23:21 +0000
treeherdermozilla-central@a74600665875 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbtian
bugs1036977
milestone33.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 1036977: Handle socket setup for |Connect| in BluetoothInterface (under bluetooth2/), r=btian Bluedroid's internal socket setup transfers 2 messages and possibly a file descriptor as the first data of a socket connection. This patch moves the socket-setup code for the |Connect| call to the implementation of BluetoothInterface. BluetoothSocket only handles the socket setup of |Listen|, and general socket state.
dom/bluetooth2/bluedroid/BluetoothInterface.cpp
dom/bluetooth2/bluedroid/BluetoothInterface.h
dom/bluetooth2/bluedroid/BluetoothSocket.cpp
dom/bluetooth2/bluedroid/BluetoothSocket.h
--- a/dom/bluetooth2/bluedroid/BluetoothInterface.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothInterface.cpp
@@ -1,17 +1,22 @@
 /* -*- 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 <errno.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include "base/message_loop.h"
 #include "BluetoothInterface.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 template<class T>
 struct interface_traits
 { };
 
 //
@@ -64,16 +69,50 @@ public:
   }
 
 private:
   nsRefPtr<Obj> mObj;
   void (Obj::*mMethod)(Arg1);
   Arg1 mArg1;
 };
 
+template <typename Obj, typename Res,
+          typename Arg1, typename Arg2, typename Arg3>
+class BluetoothInterfaceRunnable3 : public nsRunnable
+{
+public:
+  BluetoothInterfaceRunnable3(Obj* aObj,
+                              Res (Obj::*aMethod)(Arg1, Arg2, Arg3),
+                              const Arg1& aArg1, const Arg2& aArg2,
+                              const Arg3& aArg3)
+  : mObj(aObj)
+  , mMethod(aMethod)
+  , mArg1(aArg1)
+  , mArg2(aArg2)
+  , mArg3(aArg3)
+  {
+    MOZ_ASSERT(mObj);
+    MOZ_ASSERT(mMethod);
+  }
+
+  NS_METHOD
+  Run() MOZ_OVERRIDE
+  {
+    ((*mObj).*mMethod)(mArg1, mArg2, mArg3);
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<Obj> mObj;
+  void (Obj::*mMethod)(Arg1, Arg2, Arg3);
+  Arg1 mArg1;
+  Arg2 mArg2;
+  Arg3 mArg3;
+};
+
 //
 // Socket Interface
 //
 
 template<>
 struct interface_traits<BluetoothSocketInterface>
 {
   typedef const btsock_interface_t const_interface_type;
@@ -84,16 +123,21 @@ struct interface_traits<BluetoothSocketI
   }
 };
 
 typedef
   BluetoothInterfaceRunnable1<BluetoothSocketResultHandler, void, int>
   BluetoothSocketIntResultRunnable;
 
 typedef
+  BluetoothInterfaceRunnable3<BluetoothSocketResultHandler,
+                              void, int, const nsAString_internal&, int>
+  BluetoothSocketIntStringIntResultRunnable;
+
+typedef
   BluetoothInterfaceRunnable1<BluetoothSocketResultHandler, void, bt_status_t>
   BluetoothSocketErrorRunnable;
 
 static nsresult
 DispatchBluetoothSocketResult(BluetoothSocketResultHandler* aRes,
                               void (BluetoothSocketResultHandler::*aMethod)(int),
                               int aArg, bt_status_t aStatus)
 {
@@ -109,16 +153,41 @@ DispatchBluetoothSocketResult(BluetoothS
   }
   nsresult rv = NS_DispatchToMainThread(runnable);
   if (NS_FAILED(rv)) {
     BT_WARNING("NS_DispatchToMainThread failed: %X", rv);
   }
   return rv;
 }
 
+static nsresult
+DispatchBluetoothSocketResult(
+  BluetoothSocketResultHandler* aRes,
+  void (BluetoothSocketResultHandler::*aMethod)(int, const nsAString&, int),
+  int aArg1, const nsAString& aArg2, int aArg3, bt_status_t aStatus)
+{
+  MOZ_ASSERT(aRes);
+
+  nsRunnable* runnable;
+
+  if (aStatus == BT_STATUS_SUCCESS) {
+    runnable = new BluetoothSocketIntStringIntResultRunnable(aRes, aMethod,
+                                                             aArg1, aArg2,
+                                                             aArg3);
+  } else {
+    runnable = new BluetoothSocketErrorRunnable(aRes,
+      &BluetoothSocketResultHandler::OnError, aStatus);
+  }
+  nsresult rv = NS_DispatchToMainThread(runnable);
+  if (NS_FAILED(rv)) {
+    BT_WARNING("NS_DispatchToMainThread failed: %X", rv);
+  }
+  return rv;
+}
+
 void
 BluetoothSocketInterface::Listen(btsock_type_t aType,
                                  const char* aServiceName,
                                  const uint8_t* aServiceUuid,
                                  int aChannel, int aFlags,
                                  BluetoothSocketResultHandler* aRes)
 {
   int fd;
@@ -126,30 +195,321 @@ BluetoothSocketInterface::Listen(btsock_
   bt_status_t status = mInterface->listen(aType, aServiceName, aServiceUuid,
                                           aChannel, &fd, aFlags);
   if (aRes) {
     DispatchBluetoothSocketResult(aRes, &BluetoothSocketResultHandler::Listen,
                                   fd, status);
   }
 }
 
+#define CMSGHDR_CONTAINS_FD(_cmsghdr) \
+    ( ((_cmsghdr)->cmsg_level == SOL_SOCKET) && \
+      ((_cmsghdr)->cmsg_type == SCM_RIGHTS) )
+
+/* |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)
+  : mFd(aFd)
+  , mClientFd(-1)
+  , mLen(0)
+  { }
+
+  virtual ~SocketMessageWatcher()
+  { }
+
+  virtual void Proceed(bt_status_t aStatus) = 0;
+
+  void OnFileCanReadWithoutBlocking(int aFd) MOZ_OVERRIDE
+  {
+    bt_status_t status;
+
+    switch (mLen) {
+      case 0:
+        status = RecvMsg1();
+        break;
+      case MSG1_SIZE:
+        status = RecvMsg2();
+        break;
+      default:
+        /* message-size error */
+        status = BT_STATUS_FAIL;
+        break;
+    }
+
+    if (IsComplete() || status != BT_STATUS_SUCCESS) {
+      mWatcher.StopWatchingFileDescriptor();
+      Proceed(status);
+    }
+  }
+
+  void OnFileCanWriteWithoutBlocking(int aFd) MOZ_OVERRIDE
+  { }
+
+  void Watch()
+  {
+    MessageLoopForIO::current()->WatchFileDescriptor(
+      mFd,
+      true,
+      MessageLoopForIO::WATCH_READ,
+      &mWatcher,
+      this);
+  }
+
+  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;
+  }
+
+private:
+  bt_status_t 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 BT_STATUS_FAIL;
+    }
+
+    mLen += res;
+
+    return BT_STATUS_SUCCESS;
+  }
+
+  bt_status_t 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 BT_STATUS_FAIL;
+    }
+
+    mLen += res;
+
+    if (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) {
+      return BT_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 BT_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
+  {
+    char str[18];
+    sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x",
+            mBuf[aOffset + 0], mBuf[aOffset + 1], mBuf[aOffset + 2],
+            mBuf[aOffset + 3], mBuf[aOffset + 4], mBuf[aOffset + 5]);
+    aBdAddress.AssignLiteral(str);
+  }
+
+  MessageLoopForIO::FileDescriptorWatcher mWatcher;
+  int mFd;
+  int mClientFd;
+  unsigned char mLen;
+  uint8_t mBuf[MSG1_SIZE + MSG2_SIZE];
+};
+
+/* |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)
+  { }
+
+  void Run() MOZ_OVERRIDE
+  {
+    mPtr = nullptr;
+  }
+
+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
+{
+public:
+  ConnectWatcher(int aFd, BluetoothSocketResultHandler* aRes)
+  : SocketMessageWatcher(aFd)
+  , mRes(aRes)
+  { }
+
+  void Proceed(bt_status_t aStatus) MOZ_OVERRIDE
+  {
+    if (mRes) {
+      DispatchBluetoothSocketResult(mRes,
+                                   &BluetoothSocketResultHandler::Connect,
+                                    GetFd(), GetBdAddress(),
+                                    GetConnectionStatus(), aStatus);
+    }
+    MessageLoopForIO::current()->PostTask(
+      FROM_HERE, new DeleteTask<ConnectWatcher>(this));
+  }
+
+private:
+  nsRefPtr<BluetoothSocketResultHandler> mRes;
+};
+
 void
 BluetoothSocketInterface::Connect(const bt_bdaddr_t* aBdAddr,
                                   btsock_type_t aType, const uint8_t* aUuid,
                                   int aChannel, int aFlags,
                                   BluetoothSocketResultHandler* aRes)
 {
   int fd;
 
   bt_status_t status = mInterface->connect(aBdAddr, aType, aUuid, aChannel,
                                            &fd, aFlags);
-  if (aRes) {
-    DispatchBluetoothSocketResult(aRes,
-                                  &BluetoothSocketResultHandler::Connect,
-                                  fd, status);
+  if (status == BT_STATUS_SUCCESS) {
+    /* receive Bluedroid's socket-setup messages */
+    Task* t = new SocketMessageWatcherTask(new ConnectWatcher(fd, aRes));
+    XRE_GetIOMessageLoop()->PostTask(FROM_HERE, t);
+  } else if (aRes) {
+      DispatchBluetoothSocketResult(aRes,
+                                    &BluetoothSocketResultHandler::Connect,
+                                    -1, EmptyString(), 0, status);
   }
 }
 
 BluetoothSocketInterface::BluetoothSocketInterface(
   const btsock_interface_t* aInterface)
 : mInterface(aInterface)
 {
   MOZ_ASSERT(mInterface);
--- a/dom/bluetooth2/bluedroid/BluetoothInterface.h
+++ b/dom/bluetooth2/bluedroid/BluetoothInterface.h
@@ -32,17 +32,18 @@ public:
   virtual ~BluetoothSocketResultHandler() { }
 
   virtual void OnError(bt_status_t aStatus)
   {
     BT_WARNING("Received error code %d", (int)aStatus);
   }
 
   virtual void Listen(int aSockFd) { }
-  virtual void Connect(int aSockFd) { }
+  virtual void Connect(int aSockFd, const nsAString& aBdAddress,
+                       int aConnectionState) { }
 };
 
 class BluetoothSocketInterface
 {
 public:
   friend BluetoothInterface;
 
   // Init and Cleanup is handled by BluetoothInterface
--- a/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
@@ -258,16 +258,80 @@ public:
 
     return NS_OK;
   }
 
 private:
   T* mInstance;
 };
 
+class DroidSocketImplRunnable : public nsRunnable
+{
+public:
+  DroidSocketImpl* GetImpl() const
+  {
+    return mImpl;
+  }
+
+protected:
+  DroidSocketImplRunnable(DroidSocketImpl* aImpl)
+  : mImpl(aImpl)
+  {
+    MOZ_ASSERT(aImpl);
+  }
+
+  virtual ~DroidSocketImplRunnable()
+  { }
+
+private:
+  DroidSocketImpl* mImpl;
+};
+
+class OnSocketEventRunnable : public DroidSocketImplRunnable
+{
+public:
+  enum SocketEvent {
+    CONNECT_SUCCESS,
+    CONNECT_ERROR,
+    DISCONNECT
+  };
+
+  OnSocketEventRunnable(DroidSocketImpl* aImpl, SocketEvent e)
+  : DroidSocketImplRunnable(aImpl)
+  , mEvent(e)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    DroidSocketImpl* impl = GetImpl();
+
+    if (impl->IsShutdownOnMainThread()) {
+      NS_WARNING("CloseSocket has already been called!");
+      // Since we've already explicitly closed and the close happened before
+      // this, this isn't really an error. Since we've warned, return OK.
+      return NS_OK;
+    }
+    if (mEvent == CONNECT_SUCCESS) {
+      impl->mConsumer->NotifySuccess();
+    } else if (mEvent == CONNECT_ERROR) {
+      impl->mConsumer->NotifyError();
+    } else if (mEvent == DISCONNECT) {
+      impl->mConsumer->NotifyDisconnect();
+    }
+    return NS_OK;
+  }
+
+private:
+  SocketEvent mEvent;
+};
+
 class RequestClosingSocketTask : public nsRunnable
 {
 public:
   RequestClosingSocketTask(DroidSocketImpl* aImpl) : mImpl(aImpl)
   {
     MOZ_ASSERT(aImpl);
   }
 
@@ -462,17 +526,17 @@ DroidSocketImpl::Connect(int aFd)
   if (!(flags & O_NONBLOCK)) {
     int res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags | O_NONBLOCK));
     NS_ENSURE_TRUE_VOID(!res);
   }
 
   SetFd(aFd);
   mConnectionStatus = SOCKET_IS_CONNECTING;
 
-  AddWatchers(READ_WATCHER, true);
+  AddWatchers(WRITE_WATCHER, false);
 }
 
 void
 DroidSocketImpl::Listen(int aFd)
 {
   MOZ_ASSERT(aFd >= 0);
 
   int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL));
@@ -545,19 +609,16 @@ DroidSocketImpl::ReadMsg(int aFd, void *
 
 void
 DroidSocketImpl::OnFileCanReadWithoutBlocking(int aFd)
 {
   if (mConnectionStatus == SOCKET_IS_CONNECTED) {
     OnSocketCanReceiveWithoutBlocking(aFd);
   } else if (mConnectionStatus == SOCKET_IS_LISTENING) {
     OnSocketCanAcceptWithoutBlocking(aFd);
-  } else if (mConnectionStatus == SOCKET_IS_CONNECTING) {
-    /* TODO: remove this branch when connect is handled by BluetoothInterface */
-    OnSocketCanConnectWithoutBlocking(aFd);
   } else {
     NS_NOTREACHED("invalid connection state for reading");
   }
 }
 
 void
 DroidSocketImpl::OnSocketCanReceiveWithoutBlocking(int aFd)
 {
@@ -715,61 +776,30 @@ DroidSocketImpl::OnSocketCanSendWithoutB
 }
 
 void
 DroidSocketImpl::OnSocketCanConnectWithoutBlocking(int aFd)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(!mShuttingDownOnIOThread);
 
-  // Read all of the incoming data.
-  while (true) {
-    nsAutoPtr<UnixSocketRawData> incoming(new UnixSocketRawData(MAX_READ_SIZE));
+  /* We follow Posix behaviour here: Connect operations are
+   * complete once we can write to the connecting socket.
+   */
 
-    ssize_t ret;
-    if (!mReadMsgForClientFd) {
-      ret = read(aFd, incoming->mData, incoming->mSize);
-    } else {
-      ret = ReadMsg(aFd, incoming->mData, incoming->mSize);
-    }
-
-    if (ret <= 0) {
-      if (ret == -1) {
-        if (errno == EINTR) {
-          continue; // retry system call when interrupted
-        }
-        if (errno == EAGAIN || errno == EWOULDBLOCK) {
-          return; // no data available: return and re-poll
-        }
+  mConnectionStatus = SOCKET_IS_CONNECTED;
 
-        BT_WARNING("Cannot read from network");
-        // else fall through to error handling on other errno's
-      }
-
-      // We're done with our descriptors. Ensure that spurious events don't
-      // cause us to end up back here.
-      RemoveWatchers(READ_WATCHER | WRITE_WATCHER);
-      nsRefPtr<RequestClosingSocketTask> t = new RequestClosingSocketTask(this);
-      NS_DispatchToMainThread(t);
-      return;
-    }
+  nsRefPtr<OnSocketEventRunnable> r =
+    new OnSocketEventRunnable(this, OnSocketEventRunnable::CONNECT_SUCCESS);
+  NS_DispatchToMainThread(r);
 
-    incoming->mSize = ret;
-    nsRefPtr<SocketReceiveTask> t =
-      new SocketReceiveTask(this, incoming.forget());
-    NS_DispatchToMainThread(t);
-
-    // If ret is less than MAX_READ_SIZE, there's no
-    // more data in the socket for us to read now.
-    if (ret < ssize_t(MAX_READ_SIZE)) {
-      return;
-    }
+  AddWatchers(READ_WATCHER, true);
+  if (!mOutgoingQ.IsEmpty()) {
+    AddWatchers(WRITE_WATCHER, false);
   }
-
-  MOZ_CRASH("We returned early");
 }
 
 BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver,
                                  BluetoothSocketType aType,
                                  bool aAuth,
                                  bool aEncrypt)
   : mObserver(aObserver)
   , mImpl(nullptr)
@@ -806,19 +836,24 @@ class ConnectResultHandler MOZ_FINAL : p
 {
 public:
   ConnectResultHandler(DroidSocketImpl* aImpl)
   : mImpl(aImpl)
   {
     MOZ_ASSERT(mImpl);
   }
 
-  void Connect(int aFd) MOZ_OVERRIDE
+  void Connect(int aFd, const nsAString& aBdAddress,
+               int aConnectionStatus) MOZ_OVERRIDE
   {
     MOZ_ASSERT(NS_IsMainThread());
+
+    if (!mImpl->IsShutdownOnMainThread()) {
+      mImpl->mConsumer->SetAddress(aBdAddress);
+    }
     XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
                                      new SocketConnectTask(mImpl, aFd));
   }
 
   void OnError(bt_status_t aStatus) MOZ_OVERRIDE
   {
     MOZ_ASSERT(NS_IsMainThread());
     BT_WARNING("Connect failed: %d", (int)aStatus);
@@ -959,17 +994,18 @@ BluetoothSocket::ReceiveSocketInfo(nsAut
   }
 
   return true;
 }
 
 void
 BluetoothSocket::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage)
 {
-  if (ReceiveSocketInfo(aMessage)) {
+  /* clients handle socket setup in BluetoothInterface::Connect */
+  if (mIsServer && ReceiveSocketInfo(aMessage)) {
     return;
   }
 
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mObserver);
   mObserver->ReceiveSocketData(this, aMessage);
 }
 
--- a/dom/bluetooth2/bluedroid/BluetoothSocket.h
+++ b/dom/bluetooth2/bluedroid/BluetoothSocket.h
@@ -18,25 +18,16 @@ class DroidSocketImpl;
 class BluetoothSocket : public mozilla::ipc::UnixSocketConsumer
 {
 public:
   BluetoothSocket(BluetoothSocketObserver* aObserver,
                   BluetoothSocketType aType,
                   bool aAuth,
                   bool aEncrypt);
 
-  /**
-   * Connect to remote server as a client.
-   *
-   * The steps are as following:
-   * 1) BluetoothSocket acquires fd from bluedroid, and creates
-   *    a DroidSocketImpl to watch read/write of the fd.
-   * 2) DroidSocketImpl receives first 2 messages to get socket info.
-   * 3) Obex client session starts.
-   */
   bool Connect(const nsAString& aDeviceAddress, int aChannel);
 
   /**
    * Listen to incoming connection as a server.
    *
    * The steps are as following:
    * 1) BluetoothSocket acquires fd from bluedroid, and creates
    *    a DroidSocketImpl to watch read of the fd. DroidSocketImpl
@@ -60,16 +51,21 @@ public:
   virtual void ReceiveSocketData(
     nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aMessage) MOZ_OVERRIDE;
 
   inline void GetAddress(nsAString& aDeviceAddress)
   {
     aDeviceAddress = mDeviceAddress;
   }
 
+  inline void SetAddress(const nsAString& aDeviceAddress)
+  {
+    mDeviceAddress = aDeviceAddress;
+  }
+
   void CloseDroidSocket();
   bool SendDroidSocketData(mozilla::ipc::UnixSocketRawData* aData);
 
 private:
   BluetoothSocketObserver* mObserver;
   DroidSocketImpl* mImpl;
   nsString mDeviceAddress;
   bool mAuth;